summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/Makefile2
-rw-r--r--drivers/acpi/acpi_configfs.c25
-rw-r--r--drivers/acpi/acpi_lpss.c2
-rw-r--r--drivers/acpi/bus.c3
-rw-r--r--drivers/acpi/cppc_acpi.c9
-rw-r--r--drivers/acpi/device_sysfs.c6
-rw-r--r--drivers/acpi/nfit/core.c193
-rw-r--r--drivers/acpi/nfit/nfit.h17
-rw-r--r--drivers/acpi/numa.c1
-rw-r--r--drivers/acpi/pptt.c3
-rw-r--r--drivers/acpi/scan.c1
-rw-r--r--drivers/acpi/sysfs.c21
-rw-r--r--drivers/acpi/utils.c1
-rw-r--r--drivers/amba/bus.c45
-rw-r--r--drivers/android/binder.c3
-rw-r--r--drivers/android/binder_alloc.c18
-rw-r--r--drivers/ata/Kconfig12
-rw-r--r--drivers/ata/Makefile1
-rw-r--r--drivers/ata/libahci.c3
-rw-r--r--drivers/ata/libata-scsi.c9
-rw-r--r--drivers/ata/libata-zpodd.c34
-rw-r--r--drivers/ata/pata_buddha.c257
-rw-r--r--drivers/ata/pata_macio.c2
-rw-r--r--drivers/ata/pata_of_platform.c6
-rw-r--r--drivers/ata/pata_platform.c24
-rw-r--r--drivers/ata/pata_samsung_cf.c8
-rw-r--r--drivers/auxdisplay/Kconfig38
-rw-r--r--drivers/auxdisplay/Makefile2
-rw-r--r--drivers/auxdisplay/charlcd.c55
-rw-r--r--drivers/auxdisplay/hd44780.c4
-rw-r--r--drivers/auxdisplay/panel.c4
-rw-r--r--drivers/base/Kconfig77
-rw-r--r--drivers/base/memory.c1
-rw-r--r--drivers/base/platform.c20
-rw-r--r--drivers/base/power/domain.c33
-rw-r--r--drivers/base/power/domain_governor.c1
-rw-r--r--drivers/base/power/main.c21
-rw-r--r--drivers/base/power/power.h1
-rw-r--r--drivers/base/power/qos.c2
-rw-r--r--drivers/base/power/runtime.c16
-rw-r--r--drivers/base/power/sysfs.c12
-rw-r--r--drivers/base/power/trace.c2
-rw-r--r--drivers/base/power/wakeup.c30
-rw-r--r--drivers/base/swnode.c4
-rw-r--r--drivers/block/floppy.c3
-rw-r--r--drivers/block/loop.c50
-rw-r--r--drivers/block/mtip32xx/mtip32xx.c37
-rw-r--r--drivers/block/nbd.c5
-rw-r--r--drivers/block/null_blk_main.c3
-rw-r--r--drivers/block/paride/pcd.c6
-rw-r--r--drivers/block/paride/pf.c16
-rw-r--r--drivers/block/rbd.c428
-rw-r--r--drivers/block/skd_main.c1
-rw-r--r--drivers/block/virtio_blk.c10
-rw-r--r--drivers/block/xen-blkback/xenbus.c99
-rw-r--r--drivers/block/xen-blkfront.c2
-rw-r--r--drivers/block/zram/zram_drv.c34
-rw-r--r--drivers/cdrom/cdrom.c7
-rw-r--r--drivers/char/ipmi/Kconfig5
-rw-r--r--drivers/char/ipmi/Makefile1
-rw-r--r--drivers/char/ipmi/ipmi_devintf.c6
-rw-r--r--drivers/char/ipmi/ipmi_dmi.c139
-rw-r--r--drivers/char/ipmi/ipmi_dmi.h2
-rw-r--r--drivers/char/ipmi/ipmi_msghandler.c159
-rw-r--r--drivers/char/ipmi/ipmi_plat_data.c121
-rw-r--r--drivers/char/ipmi/ipmi_plat_data.h22
-rw-r--r--drivers/char/ipmi/ipmi_si.h14
-rw-r--r--drivers/char/ipmi/ipmi_si_hardcode.c149
-rw-r--r--drivers/char/ipmi/ipmi_si_hotmod.c222
-rw-r--r--drivers/char/ipmi/ipmi_si_intf.c102
-rw-r--r--drivers/char/ipmi/ipmi_si_mem_io.c5
-rw-r--r--drivers/char/ipmi/ipmi_si_parisc.c2
-rw-r--r--drivers/char/ipmi/ipmi_si_pci.c4
-rw-r--r--drivers/char/ipmi/ipmi_si_platform.c73
-rw-r--r--drivers/char/ipmi/ipmi_si_port_io.c5
-rw-r--r--drivers/char/ipmi/ipmi_si_sm.h14
-rw-r--r--drivers/char/ipmi/ipmi_ssif.c188
-rw-r--r--drivers/char/ipmi/kcs_bmc.c5
-rw-r--r--drivers/char/tpm/eventlog/tpm1.c41
-rw-r--r--drivers/char/tpm/eventlog/tpm2.c12
-rw-r--r--drivers/char/tpm/st33zp24/i2c.c2
-rw-r--r--drivers/char/tpm/st33zp24/spi.c2
-rw-r--r--drivers/char/tpm/st33zp24/st33zp24.c2
-rw-r--r--drivers/char/tpm/st33zp24/st33zp24.h4
-rw-r--r--drivers/char/tpm/tpm-chip.c124
-rw-r--r--drivers/char/tpm/tpm-dev-common.c44
-rw-r--r--drivers/char/tpm/tpm-interface.c327
-rw-r--r--drivers/char/tpm/tpm-sysfs.c138
-rw-r--r--drivers/char/tpm/tpm.h180
-rw-r--r--drivers/char/tpm/tpm1-cmd.c43
-rw-r--r--drivers/char/tpm/tpm2-cmd.c208
-rw-r--r--drivers/char/tpm/tpm2-space.c90
-rw-r--r--drivers/char/tpm/tpm_atmel.c2
-rw-r--r--drivers/char/tpm/tpm_crb.c22
-rw-r--r--drivers/char/tpm/tpm_i2c_atmel.c15
-rw-r--r--drivers/char/tpm/tpm_i2c_infineon.c17
-rw-r--r--drivers/char/tpm/tpm_i2c_nuvoton.c18
-rw-r--r--drivers/char/tpm/tpm_ibmvtpm.c8
-rw-r--r--drivers/char/tpm/tpm_infineon.c2
-rw-r--r--drivers/char/tpm/tpm_nsc.c2
-rw-r--r--drivers/char/tpm/tpm_ppi.c78
-rw-r--r--drivers/char/tpm/tpm_tis_core.c21
-rw-r--r--drivers/char/tpm/tpm_vtpm_proxy.c15
-rw-r--r--drivers/char/tpm/xen-tpmfront.c4
-rw-r--r--drivers/clk/Kconfig6
-rw-r--r--drivers/clk/Makefile3
-rw-r--r--drivers/clk/actions/Kconfig5
-rw-r--r--drivers/clk/actions/Makefile1
-rw-r--r--drivers/clk/actions/owl-pll.c2
-rw-r--r--drivers/clk/actions/owl-pll.h30
-rw-r--r--drivers/clk/actions/owl-s500.c525
-rw-r--r--drivers/clk/at91/clk-audio-pll.c9
-rw-r--r--drivers/clk/at91/clk-programmable.c3
-rw-r--r--drivers/clk/at91/sama5d2.c3
-rw-r--r--drivers/clk/clk-clps711x.c61
-rw-r--r--drivers/clk/clk-devres.c11
-rw-r--r--drivers/clk/clk-fixed-mmio.c101
-rw-r--r--drivers/clk/clk-fractional-divider.c2
-rw-r--r--drivers/clk/clk-gpio.c39
-rw-r--r--drivers/clk/clk-highbank.c1
-rw-r--r--drivers/clk/clk-max77686.c28
-rw-r--r--drivers/clk/clk-qoriq.c5
-rw-r--r--drivers/clk/clk-stm32mp1.c37
-rw-r--r--drivers/clk/clk-twl6040.c53
-rw-r--r--drivers/clk/clk.c262
-rw-r--r--drivers/clk/clk.h23
-rw-r--r--drivers/clk/clkdev.c231
-rw-r--r--drivers/clk/imx/Kconfig6
-rw-r--r--drivers/clk/imx/Makefile4
-rw-r--r--drivers/clk/imx/clk-composite-8m.c2
-rw-r--r--drivers/clk/imx/clk-imx51-imx53.c1
-rw-r--r--drivers/clk/imx/clk-imx6q.c1
-rw-r--r--drivers/clk/imx/clk-imx6sx.c1
-rw-r--r--drivers/clk/imx/clk-imx7d.c1
-rw-r--r--drivers/clk/imx/clk-imx7ulp.c16
-rw-r--r--drivers/clk/imx/clk-imx8mm.c675
-rw-r--r--drivers/clk/imx/clk-imx8mq.c254
-rw-r--r--drivers/clk/imx/clk-imx8qxp.c1
-rw-r--r--drivers/clk/imx/clk-pll14xx.c392
-rw-r--r--drivers/clk/imx/clk-sccg-pll.c514
-rw-r--r--drivers/clk/imx/clk-scu.c123
-rw-r--r--drivers/clk/imx/clk-scu.h16
-rw-r--r--drivers/clk/imx/clk-vf610.c1
-rw-r--r--drivers/clk/imx/clk.h38
-rw-r--r--drivers/clk/ingenic/cgu.c13
-rw-r--r--drivers/clk/ingenic/cgu.h2
-rw-r--r--drivers/clk/ingenic/jz4740-cgu.c2
-rw-r--r--drivers/clk/mediatek/clk-gate.c4
-rw-r--r--drivers/clk/mediatek/clk-gate.h3
-rw-r--r--drivers/clk/mediatek/clk-mt2701.c4
-rw-r--r--drivers/clk/mediatek/clk-mt2712.c9
-rw-r--r--drivers/clk/mediatek/clk-mt6797.c68
-rw-r--r--drivers/clk/mediatek/clk-mt8173.c4
-rw-r--r--drivers/clk/mediatek/clk-mtk.c4
-rw-r--r--drivers/clk/mediatek/clk-mtk.h29
-rw-r--r--drivers/clk/meson/Kconfig101
-rw-r--r--drivers/clk/meson/Makefile29
-rw-r--r--drivers/clk/meson/axg-aoclk.c193
-rw-r--r--drivers/clk/meson/axg-aoclk.h13
-rw-r--r--drivers/clk/meson/axg-audio.c5
-rw-r--r--drivers/clk/meson/axg.c69
-rw-r--r--drivers/clk/meson/clk-dualdiv.c138
-rw-r--r--drivers/clk/meson/clk-dualdiv.h33
-rw-r--r--drivers/clk/meson/clk-input.c7
-rw-r--r--drivers/clk/meson/clk-input.h19
-rw-r--r--drivers/clk/meson/clk-mpll.c12
-rw-r--r--drivers/clk/meson/clk-mpll.h30
-rw-r--r--drivers/clk/meson/clk-phase.c75
-rw-r--r--drivers/clk/meson/clk-phase.h26
-rw-r--r--drivers/clk/meson/clk-pll.c216
-rw-r--r--drivers/clk/meson/clk-pll.h49
-rw-r--r--drivers/clk/meson/clk-regmap.c5
-rw-r--r--drivers/clk/meson/clk-regmap.h20
-rw-r--r--drivers/clk/meson/clk-triphase.c68
-rw-r--r--drivers/clk/meson/clkc.h127
-rw-r--r--drivers/clk/meson/g12a-aoclk.c454
-rw-r--r--drivers/clk/meson/g12a-aoclk.h34
-rw-r--r--drivers/clk/meson/g12a.c2359
-rw-r--r--drivers/clk/meson/g12a.h175
-rw-r--r--drivers/clk/meson/gxbb-aoclk-32k.c193
-rw-r--r--drivers/clk/meson/gxbb-aoclk.c268
-rw-r--r--drivers/clk/meson/gxbb-aoclk.h20
-rw-r--r--drivers/clk/meson/gxbb.c296
-rw-r--r--drivers/clk/meson/meson-aoclk.c54
-rw-r--r--drivers/clk/meson/meson-aoclk.h13
-rw-r--r--drivers/clk/meson/meson-eeclk.c63
-rw-r--r--drivers/clk/meson/meson-eeclk.h25
-rw-r--r--drivers/clk/meson/meson8b.c374
-rw-r--r--drivers/clk/meson/meson8b.h11
-rw-r--r--drivers/clk/meson/parm.h46
-rw-r--r--drivers/clk/meson/sclk-div.c10
-rw-r--r--drivers/clk/meson/sclk-div.h (renamed from drivers/clk/meson/clkc-audio.h)16
-rw-r--r--drivers/clk/meson/vid-pll-div.c10
-rw-r--r--drivers/clk/meson/vid-pll-div.h20
-rw-r--r--drivers/clk/mmp/clk-of-mmp2.c5
-rw-r--r--drivers/clk/mvebu/armada-370.c4
-rw-r--r--drivers/clk/mvebu/armada-xp.c4
-rw-r--r--drivers/clk/mvebu/dove.c8
-rw-r--r--drivers/clk/mvebu/kirkwood.c2
-rw-r--r--drivers/clk/mvebu/mv98dx3236.c4
-rw-r--r--drivers/clk/qcom/clk-rcg.h5
-rw-r--r--drivers/clk/qcom/clk-rcg2.c24
-rw-r--r--drivers/clk/qcom/clk-rpmh.c146
-rw-r--r--drivers/clk/qcom/clk-smd-rpm.c63
-rw-r--r--drivers/clk/qcom/common.c8
-rw-r--r--drivers/clk/qcom/common.h2
-rw-r--r--drivers/clk/qcom/gcc-ipq8074.c10
-rw-r--r--drivers/clk/qcom/gcc-mdm9615.c11
-rw-r--r--drivers/clk/qcom/gcc-msm8996.c10
-rw-r--r--drivers/clk/qcom/gcc-msm8998.c61
-rw-r--r--drivers/clk/qcom/gcc-qcs404.c10
-rw-r--r--drivers/clk/qcom/gcc-sdm660.c11
-rw-r--r--drivers/clk/qcom/gcc-sdm845.c5
-rw-r--r--drivers/clk/qcom/mmcc-msm8996.c10
-rw-r--r--drivers/clk/renesas/r8a774a1-cpg-mssr.c4
-rw-r--r--drivers/clk/renesas/r8a774c0-cpg-mssr.c15
-rw-r--r--drivers/clk/renesas/r8a77980-cpg-mssr.c8
-rw-r--r--drivers/clk/renesas/rcar-gen3-cpg.c147
-rw-r--r--drivers/clk/renesas/rcar-gen3-cpg.h4
-rw-r--r--drivers/clk/rockchip/clk-rk3188.c4
-rw-r--r--drivers/clk/rockchip/clk-rk3328.c12
-rw-r--r--drivers/clk/samsung/clk-exynos4.c1
-rw-r--r--drivers/clk/samsung/clk-exynos5-subcmu.c13
-rw-r--r--drivers/clk/samsung/clk-exynos5433.c38
-rw-r--r--drivers/clk/samsung/clk-s3c2443.c2
-rw-r--r--drivers/clk/samsung/clk.h2
-rw-r--r--drivers/clk/socfpga/clk-gate.c22
-rw-r--r--drivers/clk/socfpga/clk-pll-a10.c1
-rw-r--r--drivers/clk/socfpga/clk-pll.c1
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun8i-a23.c2
-rw-r--r--drivers/clk/tegra/clk-dfll.c18
-rw-r--r--drivers/clk/ti/adpll.c2
-rw-r--r--drivers/clk/ti/apll.c4
-rw-r--r--drivers/clk/ti/autoidle.c101
-rw-r--r--drivers/clk/ti/clk.c83
-rw-r--r--drivers/clk/ti/clkctrl.c4
-rw-r--r--drivers/clk/ti/clock.h5
-rw-r--r--drivers/clk/ti/clockdomain.c2
-rw-r--r--drivers/clk/ti/divider.c2
-rw-r--r--drivers/clk/ti/dpll.c11
-rw-r--r--drivers/clk/ti/dpll3xxx.c2
-rw-r--r--drivers/clk/ti/gate.c2
-rw-r--r--drivers/clk/ti/interface.c4
-rw-r--r--drivers/clk/ti/mux.c2
-rw-r--r--drivers/clk/uniphier/clk-uniphier-cpugear.c2
-rw-r--r--drivers/clk/x86/clk-lpt.c2
-rw-r--r--drivers/clk/x86/clk-st.c3
-rw-r--r--drivers/clocksource/arm_arch_timer.c11
-rw-r--r--drivers/clocksource/clps711x-timer.c44
-rw-r--r--drivers/clocksource/mips-gic-timer.c2
-rw-r--r--drivers/clocksource/tcb_clksrc.c4
-rw-r--r--drivers/clocksource/timer-riscv.c5
-rw-r--r--drivers/clocksource/timer-ti-dm.c4
-rw-r--r--drivers/connector/cn_proc.c22
-rw-r--r--drivers/cpufreq/cpufreq.c24
-rw-r--r--drivers/cpufreq/intel_pstate.c7
-rw-r--r--drivers/cpufreq/pxa2xx-cpufreq.c4
-rw-r--r--drivers/cpufreq/scpi-cpufreq.c2
-rw-r--r--drivers/cpuidle/governor.c1
-rw-r--r--drivers/cpuidle/governors/menu.c2
-rw-r--r--drivers/crypto/caam/caamalg.c12
-rw-r--r--drivers/crypto/caam/caamalg_qi.c11
-rw-r--r--drivers/crypto/caam/caamhash.c18
-rw-r--r--drivers/crypto/caam/caampkc.c14
-rw-r--r--drivers/crypto/caam/caamrng.c22
-rw-r--r--drivers/crypto/s5p-sss.c2
-rw-r--r--drivers/dax/Kconfig28
-rw-r--r--drivers/dax/Makefile6
-rw-r--r--drivers/dax/bus.c503
-rw-r--r--drivers/dax/bus.h61
-rw-r--r--drivers/dax/dax-private.h34
-rw-r--r--drivers/dax/dax.h18
-rw-r--r--drivers/dax/device-dax.h25
-rw-r--r--drivers/dax/device.c363
-rw-r--r--drivers/dax/kmem.c108
-rw-r--r--drivers/dax/pmem.c153
-rw-r--r--drivers/dax/pmem/Makefile7
-rw-r--r--drivers/dax/pmem/compat.c73
-rw-r--r--drivers/dax/pmem/core.c71
-rw-r--r--drivers/dax/pmem/pmem.c40
-rw-r--r--drivers/dax/super.c79
-rw-r--r--drivers/dma/Kconfig14
-rw-r--r--drivers/dma/Makefile1
-rw-r--r--drivers/dma/at_hdmac.c5
-rw-r--r--drivers/dma/bcm2835-dma.c27
-rw-r--r--drivers/dma/dma-axi-dmac.c3
-rw-r--r--drivers/dma/dma-jz4780.c5
-rw-r--r--drivers/dma/dmatest.c269
-rw-r--r--drivers/dma/dw-axi-dmac/dw-axi-dmac.h2
-rw-r--r--drivers/dma/dw/Kconfig2
-rw-r--r--drivers/dma/dw/Makefile2
-rw-r--r--drivers/dma/dw/core.c245
-rw-r--r--drivers/dma/dw/dw.c138
-rw-r--r--drivers/dma/dw/idma32.c160
-rw-r--r--drivers/dma/dw/internal.h15
-rw-r--r--drivers/dma/dw/pci.c53
-rw-r--r--drivers/dma/dw/platform.c22
-rw-r--r--drivers/dma/dw/regs.h30
-rw-r--r--drivers/dma/fsl-edma-common.c70
-rw-r--r--drivers/dma/fsl-edma-common.h4
-rw-r--r--drivers/dma/fsl-edma.c1
-rw-r--r--drivers/dma/fsl-qdma.c1259
-rw-r--r--drivers/dma/fsldma.c16
-rw-r--r--drivers/dma/fsldma.h68
-rw-r--r--drivers/dma/imx-dma.c8
-rw-r--r--drivers/dma/imx-sdma.c49
-rw-r--r--drivers/dma/ioat/dma.c12
-rw-r--r--drivers/dma/ioat/dma.h2
-rw-r--r--drivers/dma/ioat/hw.h3
-rw-r--r--drivers/dma/ioat/init.c40
-rw-r--r--drivers/dma/ioat/registers.h24
-rw-r--r--drivers/dma/k3dma.c61
-rw-r--r--drivers/dma/mcf-edma.c1
-rw-r--r--drivers/dma/mv_xor.c7
-rw-r--r--drivers/dma/pch_dma.c1
-rw-r--r--drivers/dma/pl330.c1
-rw-r--r--drivers/dma/qcom/bam_dma.c4
-rw-r--r--drivers/dma/qcom/hidma.c19
-rw-r--r--drivers/dma/qcom/hidma_mgmt.c3
-rw-r--r--drivers/dma/sa11x0-dma.c2
-rw-r--r--drivers/dma/sh/usb-dmac.c2
-rw-r--r--drivers/dma/sprd-dma.c19
-rw-r--r--drivers/dma/st_fdma.c6
-rw-r--r--drivers/dma/stm32-dma.c71
-rw-r--r--drivers/dma/stm32-dmamux.c58
-rw-r--r--drivers/dma/stm32-mdma.c52
-rw-r--r--drivers/dma/tegra20-apb-dma.c45
-rw-r--r--drivers/dma/tegra210-adma.c5
-rw-r--r--drivers/dma/timb_dma.c4
-rw-r--r--drivers/dma/xilinx/xilinx_dma.c170
-rw-r--r--drivers/edac/Kconfig35
-rw-r--r--drivers/edac/Makefile8
-rw-r--r--drivers/edac/altera_edac.c72
-rw-r--r--drivers/edac/aspeed_edac.c421
-rw-r--r--drivers/edac/debugfs.c48
-rw-r--r--drivers/edac/edac_module.h8
-rw-r--r--drivers/edac/i10nm_base.c275
-rw-r--r--drivers/edac/mce_amd.c291
-rw-r--r--drivers/edac/skx_base.c650
-rw-r--r--drivers/edac/skx_common.c691
-rw-r--r--drivers/edac/skx_common.h152
-rw-r--r--drivers/edac/skx_edac.c1358
-rw-r--r--drivers/firmware/efi/arm-runtime.c6
-rw-r--r--drivers/firmware/efi/runtime-wrappers.c17
-rw-r--r--drivers/firmware/iscsi_ibft.c1
-rw-r--r--drivers/firmware/iscsi_ibft_find.c2
-rw-r--r--drivers/firmware/memmap.c2
-rw-r--r--drivers/gpio/Kconfig24
-rw-r--r--drivers/gpio/Makefile3
-rw-r--r--drivers/gpio/gpio-adnp.c6
-rw-r--r--drivers/gpio/gpio-adp5588.c234
-rw-r--r--drivers/gpio/gpio-altera-a10sr.c17
-rw-r--r--drivers/gpio/gpio-altera.c4
-rw-r--r--drivers/gpio/gpio-amd-fch.c194
-rw-r--r--drivers/gpio/gpio-aspeed.c2
-rw-r--r--drivers/gpio/gpio-crystalcove.c30
-rw-r--r--drivers/gpio/gpio-davinci.c4
-rw-r--r--drivers/gpio/gpio-eic-sprd.c1
-rw-r--r--drivers/gpio/gpio-exar.c2
-rw-r--r--drivers/gpio/gpio-f7188x.c24
-rw-r--r--drivers/gpio/gpio-ftgpio010.c20
-rw-r--r--drivers/gpio/gpio-gw-pld.c137
-rw-r--r--drivers/gpio/gpio-hlwd.c192
-rw-r--r--drivers/gpio/gpio-madera.c2
-rw-r--r--drivers/gpio/gpio-ml-ioh.c2
-rw-r--r--drivers/gpio/gpio-mockup.c185
-rw-r--r--drivers/gpio/gpio-msic.c34
-rw-r--r--drivers/gpio/gpio-mvebu.c11
-rw-r--r--drivers/gpio/gpio-mxc.c5
-rw-r--r--drivers/gpio/gpio-omap.c14
-rw-r--r--drivers/gpio/gpio-pca953x.c190
-rw-r--r--drivers/gpio/gpio-pcf857x.c15
-rw-r--r--drivers/gpio/gpio-pch.c1
-rw-r--r--drivers/gpio/gpio-pmic-eic-sprd.c3
-rw-r--r--drivers/gpio/gpio-rcar.c12
-rw-r--r--drivers/gpio/gpio-sama5d2-piobu.c20
-rw-r--r--drivers/gpio/gpio-tegra.c25
-rw-r--r--drivers/gpio/gpio-tegra186.c70
-rw-r--r--drivers/gpio/gpio-tqmx86.c332
-rw-r--r--drivers/gpio/gpio-wcove.c29
-rw-r--r--drivers/gpio/gpio-zynq.c24
-rw-r--r--drivers/gpio/gpiolib-acpi.c7
-rw-r--r--drivers/gpio/gpiolib-of.c26
-rw-r--r--drivers/gpio/gpiolib.c83
-rw-r--r--drivers/gpio/gpiolib.h2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c13
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c1
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/psp_v3_1.c4
-rw-r--r--drivers/gpu/drm/amd/amdgpu/soc15.c1
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_cik.c52
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c47
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c1
-rw-r--r--drivers/gpu/drm/amd/display/dc/calcs/dcn_calcs.c8
-rw-r--r--drivers/gpu/drm/amd/display/dc/core/dc_link.c16
-rw-r--r--drivers/gpu/drm/amd/display/modules/freesync/freesync.c7
-rw-r--r--drivers/gpu/drm/amd/display/modules/inc/mod_freesync.h2
-rw-r--r--drivers/gpu/drm/amd/powerplay/hwmgr/pp_psm.c3
-rw-r--r--drivers/gpu/drm/amd/powerplay/hwmgr/ppatomfwctrl.c30
-rw-r--r--drivers/gpu/drm/amd/powerplay/hwmgr/ppatomfwctrl.h4
-rw-r--r--drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.c6
-rw-r--r--drivers/gpu/drm/amd/powerplay/hwmgr/vega10_hwmgr.c8
-rw-r--r--drivers/gpu/drm/amd/powerplay/hwmgr/vega12_hwmgr.c4
-rw-r--r--drivers/gpu/drm/amd/powerplay/hwmgr/vega20_hwmgr.c222
-rw-r--r--drivers/gpu/drm/amd/powerplay/hwmgr/vega20_hwmgr.h7
-rw-r--r--drivers/gpu/drm/amd/powerplay/hwmgr/vega20_processpptables.c17
-rw-r--r--drivers/gpu/drm/amd/powerplay/smumgr/polaris10_smumgr.c2
-rw-r--r--drivers/gpu/drm/amd/powerplay/smumgr/smu9_smumgr.c6
-rw-r--r--drivers/gpu/drm/amd/powerplay/smumgr/vega20_smumgr.c6
-rw-r--r--drivers/gpu/drm/drm_atomic_helper.c59
-rw-r--r--drivers/gpu/drm/drm_drv.c6
-rw-r--r--drivers/gpu/drm/drm_fb_helper.c2
-rw-r--r--drivers/gpu/drm/drm_file.c6
-rw-r--r--drivers/gpu/drm/drm_ioc32.c6
-rw-r--r--drivers/gpu/drm/etnaviv/Kconfig1
-rw-r--r--drivers/gpu/drm/etnaviv/etnaviv_cmdbuf.h2
-rw-r--r--drivers/gpu/drm/etnaviv/etnaviv_dump.c2
-rw-r--r--drivers/gpu/drm/etnaviv/etnaviv_gem.h1
-rw-r--r--drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c2
-rw-r--r--drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c2
-rw-r--r--drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.c4
-rw-r--r--drivers/gpu/drm/etnaviv/etnaviv_perfmon.c6
-rw-r--r--drivers/gpu/drm/etnaviv/etnaviv_sched.c2
-rw-r--r--drivers/gpu/drm/exynos/exynos_mixer.c110
-rw-r--r--drivers/gpu/drm/i915/gvt/cmd_parser.c2
-rw-r--r--drivers/gpu/drm/i915/gvt/gtt.c14
-rw-r--r--drivers/gpu/drm/i915/gvt/gtt.h1
-rw-r--r--drivers/gpu/drm/i915/gvt/mmio_context.c1
-rw-r--r--drivers/gpu/drm/i915/gvt/scheduler.c28
-rw-r--r--drivers/gpu/drm/i915/i915_active.c36
-rw-r--r--drivers/gpu/drm/i915/i915_drv.c35
-rw-r--r--drivers/gpu/drm/i915/i915_drv.h3
-rw-r--r--drivers/gpu/drm/i915/i915_gem.c18
-rw-r--r--drivers/gpu/drm/i915/i915_gpu_error.c2
-rw-r--r--drivers/gpu/drm/i915/i915_reg.h4
-rw-r--r--drivers/gpu/drm/i915/i915_scheduler.c27
-rw-r--r--drivers/gpu/drm/i915/intel_bios.c1
-rw-r--r--drivers/gpu/drm/i915/intel_breadcrumbs.c18
-rw-r--r--drivers/gpu/drm/i915/intel_ddi.c14
-rw-r--r--drivers/gpu/drm/i915/selftests/i915_gem_context.c69
-rw-r--r--drivers/gpu/drm/i915/selftests/i915_gem_evict.c2
-rw-r--r--drivers/gpu/drm/meson/meson_drv.c9
-rw-r--r--drivers/gpu/drm/meson/meson_dw_hdmi.c3
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_debugfs.c2
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_dmem.c14
-rw-r--r--drivers/gpu/drm/qxl/qxl_drv.c5
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_vop.c18
-rw-r--r--drivers/gpu/drm/tegra/hub.c4
-rw-r--r--drivers/gpu/drm/tegra/vic.c2
-rw-r--r--drivers/gpu/drm/udl/udl_connector.c72
-rw-r--r--drivers/gpu/drm/udl/udl_gem.c2
-rw-r--r--drivers/gpu/drm/vgem/vgem_drv.c6
-rw-r--r--drivers/gpu/drm/vkms/vkms_gem.c5
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_fb.c12
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c2
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c8
-rw-r--r--drivers/gpu/ipu-v3/ipu-cpmem.c26
-rw-r--r--drivers/gpu/ipu-v3/ipu-csi.c126
-rw-r--r--drivers/gpu/vga/vgaarb.c49
-rw-r--r--drivers/hid/Kconfig18
-rw-r--r--drivers/hid/Makefile2
-rw-r--r--drivers/hid/hid-core.c59
-rw-r--r--drivers/hid/hid-debug.c5
-rw-r--r--drivers/hid/hid-ids.h5
-rw-r--r--drivers/hid/hid-input.c82
-rw-r--r--drivers/hid/hid-lg.c2
-rw-r--r--drivers/hid/hid-logitech-dj.c1142
-rw-r--r--drivers/hid/hid-logitech-hidpp.c747
-rw-r--r--drivers/hid/hid-picolcd_core.c18
-rw-r--r--drivers/hid/hid-quirks.c17
-rw-r--r--drivers/hid/hid-sensor-custom.c12
-rw-r--r--drivers/hid/hid-steam.c26
-rw-r--r--drivers/hid/hid-u2fzero.c374
-rw-r--r--drivers/hid/hid-uclogic-params.c4
-rw-r--r--drivers/hid/intel-ish-hid/Kconfig15
-rw-r--r--drivers/hid/intel-ish-hid/Makefile3
-rw-r--r--drivers/hid/intel-ish-hid/ipc/hw-ish.h1
-rw-r--r--drivers/hid/intel-ish-hid/ipc/pci-ish.c1
-rw-r--r--drivers/hid/intel-ish-hid/ishtp-fw-loader.c1085
-rw-r--r--drivers/hid/intel-ish-hid/ishtp-hid-client.c168
-rw-r--r--drivers/hid/intel-ish-hid/ishtp-hid.c49
-rw-r--r--drivers/hid/intel-ish-hid/ishtp-hid.h14
-rw-r--r--drivers/hid/intel-ish-hid/ishtp/bus.c96
-rw-r--r--drivers/hid/intel-ish-hid/ishtp/bus.h37
-rw-r--r--drivers/hid/intel-ish-hid/ishtp/client.c60
-rw-r--r--drivers/hid/intel-ish-hid/ishtp/client.h24
-rw-r--r--drivers/hid/intel-ish-hid/ishtp/ishtp-dev.h31
-rw-r--r--drivers/hsi/controllers/omap_ssi_port.c6
-rw-r--r--drivers/hwmon/Kconfig1
-rw-r--r--drivers/hwmon/ntc_thermistor.c2
-rw-r--r--drivers/hwmon/occ/common.c6
-rw-r--r--drivers/hwtracing/coresight/coresight-etm3x.c44
-rw-r--r--drivers/hwtracing/coresight/coresight-etm4x.c21
-rw-r--r--drivers/hwtracing/coresight/coresight-priv.h40
-rw-r--r--drivers/hwtracing/coresight/coresight-stm.c14
-rw-r--r--drivers/hwtracing/coresight/coresight-tmc.c30
-rw-r--r--drivers/i2c/algos/i2c-algo-bit.c25
-rw-r--r--drivers/i2c/busses/Kconfig1
-rw-r--r--drivers/i2c/busses/i2c-aspeed.c120
-rw-r--r--drivers/i2c/busses/i2c-brcmstb.c13
-rw-r--r--drivers/i2c/busses/i2c-cbus-gpio.c80
-rw-r--r--drivers/i2c/busses/i2c-designware-core.h2
-rw-r--r--drivers/i2c/busses/i2c-designware-master.c6
-rw-r--r--drivers/i2c/busses/i2c-designware-pcidrv.c7
-rw-r--r--drivers/i2c/busses/i2c-designware-platdrv.c10
-rw-r--r--drivers/i2c/busses/i2c-eg20t.c1
-rw-r--r--drivers/i2c/busses/i2c-exynos5.c11
-rw-r--r--drivers/i2c/busses/i2c-gpio.c134
-rw-r--r--drivers/i2c/busses/i2c-i801.c4
-rw-r--r--drivers/i2c/busses/i2c-imx.c37
-rw-r--r--drivers/i2c/busses/i2c-iop3xx.c8
-rw-r--r--drivers/i2c/busses/i2c-mt65xx.c14
-rw-r--r--drivers/i2c/busses/i2c-ocores.c308
-rw-r--r--drivers/i2c/busses/i2c-rcar.c64
-rw-r--r--drivers/i2c/busses/i2c-s3c2410.c8
-rw-r--r--drivers/i2c/busses/i2c-sh_mobile.c121
-rw-r--r--drivers/i2c/busses/i2c-sis630.c4
-rw-r--r--drivers/i2c/busses/i2c-sprd.c34
-rw-r--r--drivers/i2c/busses/i2c-stm32f7.c2
-rw-r--r--drivers/i2c/busses/i2c-synquacer.c5
-rw-r--r--drivers/i2c/busses/i2c-tegra.c740
-rw-r--r--drivers/i2c/busses/i2c-zx2967.c8
-rw-r--r--drivers/i2c/i2c-core-base.c21
-rw-r--r--drivers/i2c/i2c-core-of.c14
-rw-r--r--drivers/i2c/i2c-core-smbus.c2
-rw-r--r--drivers/i2c/i2c-dev.c2
-rw-r--r--drivers/ide/Kconfig26
-rw-r--r--drivers/ide/hpt366.c4
-rw-r--r--drivers/ide/ide-floppy.c2
-rw-r--r--drivers/iio/adc/Kconfig7
-rw-r--r--drivers/iio/adc/Makefile1
-rw-r--r--drivers/iio/adc/stmpe-adc.c363
-rw-r--r--drivers/infiniband/Kconfig15
-rw-r--r--drivers/infiniband/core/Makefile4
-rw-r--r--drivers/infiniband/core/cache.c118
-rw-r--r--drivers/infiniband/core/cgroup.c5
-rw-r--r--drivers/infiniband/core/cm.c3
-rw-r--r--drivers/infiniband/core/cma.c139
-rw-r--r--drivers/infiniband/core/cma_priv.h4
-rw-r--r--drivers/infiniband/core/core_priv.h35
-rw-r--r--drivers/infiniband/core/device.c1313
-rw-r--r--drivers/infiniband/core/iwcm.c13
-rw-r--r--drivers/infiniband/core/iwpm_msg.c232
-rw-r--r--drivers/infiniband/core/iwpm_util.c86
-rw-r--r--drivers/infiniband/core/iwpm_util.h12
-rw-r--r--drivers/infiniband/core/mad.c4
-rw-r--r--drivers/infiniband/core/netlink.c4
-rw-r--r--drivers/infiniband/core/nldev.c492
-rw-r--r--drivers/infiniband/core/rdma_core.c42
-rw-r--r--drivers/infiniband/core/restrack.c193
-rw-r--r--drivers/infiniband/core/restrack.h28
-rw-r--r--drivers/infiniband/core/rw.c12
-rw-r--r--drivers/infiniband/core/sa_query.c4
-rw-r--r--drivers/infiniband/core/security.c96
-rw-r--r--drivers/infiniband/core/sysfs.c93
-rw-r--r--drivers/infiniband/core/ucma.c7
-rw-r--r--drivers/infiniband/core/umem.c60
-rw-r--r--drivers/infiniband/core/umem_odp.c21
-rw-r--r--drivers/infiniband/core/user_mad.c52
-rw-r--r--drivers/infiniband/core/uverbs_cmd.c69
-rw-r--r--drivers/infiniband/core/uverbs_ioctl.c3
-rw-r--r--drivers/infiniband/core/uverbs_main.c2
-rw-r--r--drivers/infiniband/core/uverbs_std_types.c2
-rw-r--r--drivers/infiniband/core/uverbs_uapi.c15
-rw-r--r--drivers/infiniband/core/verbs.c73
-rw-r--r--drivers/infiniband/hw/bnxt_re/Kconfig1
-rw-r--r--drivers/infiniband/hw/bnxt_re/Makefile2
-rw-r--r--drivers/infiniband/hw/bnxt_re/bnxt_re.h1
-rw-r--r--drivers/infiniband/hw/bnxt_re/ib_verbs.c268
-rw-r--r--drivers/infiniband/hw/bnxt_re/ib_verbs.h16
-rw-r--r--drivers/infiniband/hw/bnxt_re/main.c134
-rw-r--r--drivers/infiniband/hw/bnxt_re/qplib_fp.c193
-rw-r--r--drivers/infiniband/hw/bnxt_re/qplib_fp.h47
-rw-r--r--drivers/infiniband/hw/bnxt_re/qplib_rcfw.c40
-rw-r--r--drivers/infiniband/hw/bnxt_re/qplib_rcfw.h45
-rw-r--r--drivers/infiniband/hw/bnxt_re/qplib_res.c22
-rw-r--r--drivers/infiniband/hw/bnxt_re/qplib_res.h30
-rw-r--r--drivers/infiniband/hw/bnxt_re/qplib_sp.c3
-rw-r--r--drivers/infiniband/hw/bnxt_re/roce_hsi.h160
-rw-r--r--drivers/infiniband/hw/cxgb3/Makefile2
-rw-r--r--drivers/infiniband/hw/cxgb3/iwch.c2
-rw-r--r--drivers/infiniband/hw/cxgb3/iwch_provider.c95
-rw-r--r--drivers/infiniband/hw/cxgb4/Makefile4
-rw-r--r--drivers/infiniband/hw/cxgb4/cm.c199
-rw-r--r--drivers/infiniband/hw/cxgb4/device.c10
-rw-r--r--drivers/infiniband/hw/cxgb4/iw_cxgb4.h16
-rw-r--r--drivers/infiniband/hw/cxgb4/mem.c36
-rw-r--r--drivers/infiniband/hw/cxgb4/provider.c85
-rw-r--r--drivers/infiniband/hw/cxgb4/qp.c33
-rw-r--r--drivers/infiniband/hw/cxgb4/t4.h1
-rw-r--r--drivers/infiniband/hw/hfi1/Makefile1
-rw-r--r--drivers/infiniband/hw/hfi1/chip.c13
-rw-r--r--drivers/infiniband/hw/hfi1/chip.h4
-rw-r--r--drivers/infiniband/hw/hfi1/common.h4
-rw-r--r--drivers/infiniband/hw/hfi1/debugfs.c58
-rw-r--r--drivers/infiniband/hw/hfi1/debugfs.h12
-rw-r--r--drivers/infiniband/hw/hfi1/driver.c58
-rw-r--r--drivers/infiniband/hw/hfi1/fault.c53
-rw-r--r--drivers/infiniband/hw/hfi1/hfi.h24
-rw-r--r--drivers/infiniband/hw/hfi1/init.c35
-rw-r--r--drivers/infiniband/hw/hfi1/iowait.c34
-rw-r--r--drivers/infiniband/hw/hfi1/iowait.h99
-rw-r--r--drivers/infiniband/hw/hfi1/opfn.c323
-rw-r--r--drivers/infiniband/hw/hfi1/opfn.h85
-rw-r--r--drivers/infiniband/hw/hfi1/pio.c18
-rw-r--r--drivers/infiniband/hw/hfi1/qp.c76
-rw-r--r--drivers/infiniband/hw/hfi1/qp.h7
-rw-r--r--drivers/infiniband/hw/hfi1/rc.c1141
-rw-r--r--drivers/infiniband/hw/hfi1/rc.h51
-rw-r--r--drivers/infiniband/hw/hfi1/ruc.c48
-rw-r--r--drivers/infiniband/hw/hfi1/sdma.c24
-rw-r--r--drivers/infiniband/hw/hfi1/sdma_txreq.h1
-rw-r--r--drivers/infiniband/hw/hfi1/sysfs.c16
-rw-r--r--drivers/infiniband/hw/hfi1/tid_rdma.c5418
-rw-r--r--drivers/infiniband/hw/hfi1/tid_rdma.h311
-rw-r--r--drivers/infiniband/hw/hfi1/trace.c118
-rw-r--r--drivers/infiniband/hw/hfi1/trace.h1
-rw-r--r--drivers/infiniband/hw/hfi1/trace_ibhdrs.h8
-rw-r--r--drivers/infiniband/hw/hfi1/trace_rc.h48
-rw-r--r--drivers/infiniband/hw/hfi1/trace_rx.h107
-rw-r--r--drivers/infiniband/hw/hfi1/trace_tid.h1610
-rw-r--r--drivers/infiniband/hw/hfi1/trace_tx.h18
-rw-r--r--drivers/infiniband/hw/hfi1/uc.c3
-rw-r--r--drivers/infiniband/hw/hfi1/ud.c24
-rw-r--r--drivers/infiniband/hw/hfi1/user_exp_rcv.h1
-rw-r--r--drivers/infiniband/hw/hfi1/user_pages.c12
-rw-r--r--drivers/infiniband/hw/hfi1/user_sdma.c9
-rw-r--r--drivers/infiniband/hw/hfi1/verbs.c210
-rw-r--r--drivers/infiniband/hw/hfi1/verbs.h104
-rw-r--r--drivers/infiniband/hw/hfi1/verbs_txreq.h1
-rw-r--r--drivers/infiniband/hw/hfi1/vnic_sdma.c6
-rw-r--r--drivers/infiniband/hw/hns/Kconfig1
-rw-r--r--drivers/infiniband/hw/hns/Makefile2
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_cmd.c32
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_cmd.h12
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_cq.c9
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_db.c6
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_device.h63
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_hem.c68
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_hem.h3
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_hw_v1.c36
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_hw_v2.c596
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_hw_v2.h92
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_main.c88
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_mr.c95
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_pd.c25
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_qp.c92
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_srq.c16
-rw-r--r--drivers/infiniband/hw/i40iw/Makefile2
-rw-r--r--drivers/infiniband/hw/i40iw/i40iw_utils.c13
-rw-r--r--drivers/infiniband/hw/i40iw/i40iw_verbs.c137
-rw-r--r--drivers/infiniband/hw/mlx4/Kconfig1
-rw-r--r--drivers/infiniband/hw/mlx4/alias_GUID.c2
-rw-r--r--drivers/infiniband/hw/mlx4/cm.c2
-rw-r--r--drivers/infiniband/hw/mlx4/cq.c19
-rw-r--r--drivers/infiniband/hw/mlx4/doorbell.c6
-rw-r--r--drivers/infiniband/hw/mlx4/main.c77
-rw-r--r--drivers/infiniband/hw/mlx4/mlx4_ib.h3
-rw-r--r--drivers/infiniband/hw/mlx4/mr.c13
-rw-r--r--drivers/infiniband/hw/mlx4/qp.c84
-rw-r--r--drivers/infiniband/hw/mlx4/srq.c12
-rw-r--r--drivers/infiniband/hw/mlx5/Kconfig1
-rw-r--r--drivers/infiniband/hw/mlx5/cong.c15
-rw-r--r--drivers/infiniband/hw/mlx5/cq.c15
-rw-r--r--drivers/infiniband/hw/mlx5/devx.c493
-rw-r--r--drivers/infiniband/hw/mlx5/doorbell.c6
-rw-r--r--drivers/infiniband/hw/mlx5/ib_rep.c6
-rw-r--r--drivers/infiniband/hw/mlx5/main.c256
-rw-r--r--drivers/infiniband/hw/mlx5/mem.c5
-rw-r--r--drivers/infiniband/hw/mlx5/mlx5_ib.h41
-rw-r--r--drivers/infiniband/hw/mlx5/mr.c126
-rw-r--r--drivers/infiniband/hw/mlx5/odp.c316
-rw-r--r--drivers/infiniband/hw/mlx5/qp.c312
-rw-r--r--drivers/infiniband/hw/mlx5/srq.c11
-rw-r--r--drivers/infiniband/hw/mlx5/srq.h2
-rw-r--r--drivers/infiniband/hw/mlx5/srq_cmd.c16
-rw-r--r--drivers/infiniband/hw/mthca/mthca_main.c2
-rw-r--r--drivers/infiniband/hw/mthca/mthca_provider.c139
-rw-r--r--drivers/infiniband/hw/mthca/mthca_qp.c13
-rw-r--r--drivers/infiniband/hw/mthca/mthca_srq.c21
-rw-r--r--drivers/infiniband/hw/nes/Kconfig2
-rw-r--r--drivers/infiniband/hw/nes/nes_verbs.c313
-rw-r--r--drivers/infiniband/hw/nes/nes_verbs.h1
-rw-r--r--drivers/infiniband/hw/ocrdma/Makefile2
-rw-r--r--drivers/infiniband/hw/ocrdma/ocrdma_main.c12
-rw-r--r--drivers/infiniband/hw/ocrdma/ocrdma_stats.c67
-rw-r--r--drivers/infiniband/hw/ocrdma/ocrdma_verbs.c189
-rw-r--r--drivers/infiniband/hw/ocrdma/ocrdma_verbs.h11
-rw-r--r--drivers/infiniband/hw/qedr/main.c9
-rw-r--r--drivers/infiniband/hw/qedr/qedr_iw_cm.c2
-rw-r--r--drivers/infiniband/hw/qedr/verbs.c192
-rw-r--r--drivers/infiniband/hw/qedr/verbs.h10
-rw-r--r--drivers/infiniband/hw/qib/qib_debugfs.c27
-rw-r--r--drivers/infiniband/hw/qib/qib_rc.c7
-rw-r--r--drivers/infiniband/hw/qib/qib_sdma.c26
-rw-r--r--drivers/infiniband/hw/qib/qib_sysfs.c18
-rw-r--r--drivers/infiniband/hw/qib/qib_ud.c6
-rw-r--r--drivers/infiniband/hw/qib/qib_user_pages.c75
-rw-r--r--drivers/infiniband/hw/qib/qib_verbs.c20
-rw-r--r--drivers/infiniband/hw/usnic/Makefile2
-rw-r--r--drivers/infiniband/hw/usnic/usnic_debugfs.c26
-rw-r--r--drivers/infiniband/hw/usnic/usnic_ib_main.c57
-rw-r--r--drivers/infiniband/hw/usnic/usnic_ib_sysfs.c26
-rw-r--r--drivers/infiniband/hw/usnic/usnic_ib_verbs.c114
-rw-r--r--drivers/infiniband/hw/usnic/usnic_ib_verbs.h28
-rw-r--r--drivers/infiniband/hw/usnic/usnic_uiom.c65
-rw-r--r--drivers/infiniband/hw/usnic/usnic_uiom.h1
-rw-r--r--drivers/infiniband/hw/vmw_pvrdma/pvrdma_cq.c2
-rw-r--r--drivers/infiniband/hw/vmw_pvrdma/pvrdma_dev_api.h15
-rw-r--r--drivers/infiniband/hw/vmw_pvrdma/pvrdma_main.c12
-rw-r--r--drivers/infiniband/hw/vmw_pvrdma/pvrdma_misc.c21
-rw-r--r--drivers/infiniband/hw/vmw_pvrdma/pvrdma_mr.c3
-rw-r--r--drivers/infiniband/hw/vmw_pvrdma/pvrdma_qp.c6
-rw-r--r--drivers/infiniband/hw/vmw_pvrdma/pvrdma_srq.c4
-rw-r--r--drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.c98
-rw-r--r--drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.h12
-rw-r--r--drivers/infiniband/sw/rdmavt/mr.c21
-rw-r--r--drivers/infiniband/sw/rdmavt/pd.c29
-rw-r--r--drivers/infiniband/sw/rdmavt/pd.h7
-rw-r--r--drivers/infiniband/sw/rdmavt/qp.c104
-rw-r--r--drivers/infiniband/sw/rdmavt/rc.c13
-rw-r--r--drivers/infiniband/sw/rdmavt/srq.c5
-rw-r--r--drivers/infiniband/sw/rdmavt/trace_cq.h10
-rw-r--r--drivers/infiniband/sw/rdmavt/vt.c34
-rw-r--r--drivers/infiniband/sw/rxe/rxe.c67
-rw-r--r--drivers/infiniband/sw/rxe/rxe.h16
-rw-r--r--drivers/infiniband/sw/rxe/rxe_av.c7
-rw-r--r--drivers/infiniband/sw/rxe/rxe_comp.c6
-rw-r--r--drivers/infiniband/sw/rxe/rxe_loc.h9
-rw-r--r--drivers/infiniband/sw/rxe/rxe_mr.c15
-rw-r--r--drivers/infiniband/sw/rxe/rxe_net.c97
-rw-r--r--drivers/infiniband/sw/rxe/rxe_net.h2
-rw-r--r--drivers/infiniband/sw/rxe/rxe_param.h3
-rw-r--r--drivers/infiniband/sw/rxe/rxe_pool.c77
-rw-r--r--drivers/infiniband/sw/rxe/rxe_pool.h4
-rw-r--r--drivers/infiniband/sw/rxe/rxe_qp.c15
-rw-r--r--drivers/infiniband/sw/rxe/rxe_recv.c12
-rw-r--r--drivers/infiniband/sw/rxe/rxe_resp.c3
-rw-r--r--drivers/infiniband/sw/rxe/rxe_sysfs.c40
-rw-r--r--drivers/infiniband/sw/rxe/rxe_verbs.c103
-rw-r--r--drivers/infiniband/sw/rxe/rxe_verbs.h9
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib.h4
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_fs.c7
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_main.c14
-rw-r--r--drivers/infiniband/ulp/iser/iscsi_iser.h2
-rw-r--r--drivers/infiniband/ulp/iser/iser_memory.c19
-rw-r--r--drivers/infiniband/ulp/isert/Makefile1
-rw-r--r--drivers/infiniband/ulp/isert/ib_isert.c2
-rw-r--r--drivers/infiniband/ulp/srp/ib_srp.c26
-rw-r--r--drivers/infiniband/ulp/srpt/Makefile1
-rw-r--r--drivers/infiniband/ulp/srpt/ib_srpt.c80
-rw-r--r--drivers/infiniband/ulp/srpt/ib_srpt.h4
-rw-r--r--drivers/input/joystick/db9.c2
-rw-r--r--drivers/input/keyboard/gpio_keys.c10
-rw-r--r--drivers/input/keyboard/mcs_touchkey.c5
-rw-r--r--drivers/input/keyboard/mtk-pmic-keys.c13
-rw-r--r--drivers/input/keyboard/qt2160.c9
-rw-r--r--drivers/input/keyboard/tca6416-keypad.c4
-rw-r--r--drivers/input/keyboard/tm2-touchkey.c136
-rw-r--r--drivers/input/misc/Kconfig21
-rw-r--r--drivers/input/misc/Makefile3
-rw-r--r--drivers/input/misc/ims-pcu.c27
-rw-r--r--drivers/input/misc/msm-vibrator.c281
-rw-r--r--drivers/input/misc/soc_button_array.c6
-rw-r--r--drivers/input/misc/stpmic1_onkey.c198
-rw-r--r--drivers/input/mouse/elan_i2c_core.c1
-rw-r--r--drivers/input/mouse/synaptics_i2c.c22
-rw-r--r--drivers/input/serio/i8042-sparcio.h21
-rw-r--r--drivers/input/tablet/wacom_serial4.c2
-rw-r--r--drivers/input/touchscreen/Kconfig7
-rw-r--r--drivers/input/touchscreen/ad7879.c11
-rw-r--r--drivers/input/touchscreen/edt-ft5x06.c110
-rw-r--r--drivers/input/touchscreen/goodix.c6
-rw-r--r--drivers/input/touchscreen/ili210x.c321
-rw-r--r--drivers/input/touchscreen/st1232.c154
-rw-r--r--drivers/input/touchscreen/stmfts.c30
-rw-r--r--drivers/input/touchscreen/stmpe-ts.c66
-rw-r--r--drivers/input/touchscreen/sx8654.c255
-rw-r--r--drivers/input/touchscreen/ti_am335x_tsc.c4
-rw-r--r--drivers/iommu/Kconfig17
-rw-r--r--drivers/iommu/Makefile1
-rw-r--r--drivers/iommu/amd_iommu.c52
-rw-r--r--drivers/iommu/amd_iommu_init.c27
-rw-r--r--drivers/iommu/amd_iommu_types.h2
-rw-r--r--drivers/iommu/amd_iommu_v2.c24
-rw-r--r--drivers/iommu/arm-smmu-v3.c3
-rw-r--r--drivers/iommu/arm-smmu.c2
-rw-r--r--drivers/iommu/dma-iommu.c3
-rw-r--r--drivers/iommu/hyperv-iommu.c196
-rw-r--r--drivers/iommu/intel-iommu.c163
-rw-r--r--drivers/iommu/intel-pasid.c2
-rw-r--r--drivers/iommu/intel-svm.c88
-rw-r--r--drivers/iommu/intel_irq_remapping.c32
-rw-r--r--drivers/iommu/io-pgtable-arm-v7s.c25
-rw-r--r--drivers/iommu/io-pgtable-arm.c3
-rw-r--r--drivers/iommu/io-pgtable.c5
-rw-r--r--drivers/iommu/io-pgtable.h213
-rw-r--r--drivers/iommu/iommu-debugfs.c23
-rw-r--r--drivers/iommu/iommu.c24
-rw-r--r--drivers/iommu/iova.c5
-rw-r--r--drivers/iommu/ipmmu-vmsa.c3
-rw-r--r--drivers/iommu/irq_remapping.c3
-rw-r--r--drivers/iommu/irq_remapping.h1
-rw-r--r--drivers/iommu/msm_iommu.c10
-rw-r--r--drivers/iommu/mtk_iommu.h3
-rw-r--r--drivers/iommu/mtk_iommu_v1.c2
-rw-r--r--drivers/iommu/qcom_iommu.c2
-rw-r--r--drivers/iommu/tegra-gart.c473
-rw-r--r--drivers/iommu/tegra-smmu.c4
-rw-r--r--drivers/irqchip/irq-brcmstb-l2.c4
-rw-r--r--drivers/irqchip/irq-gic-v3-its.c2
-rw-r--r--drivers/irqchip/irq-gic-v3.c265
-rw-r--r--drivers/irqchip/irq-gic.c45
-rw-r--r--drivers/irqchip/irq-imx-irqsteer.c8
-rw-r--r--drivers/irqchip/irq-mbigen.c3
-rw-r--r--drivers/irqchip/irq-mmp.c2
-rw-r--r--drivers/irqchip/irq-mvebu-sei.c2
-rw-r--r--drivers/irqchip/irq-stm32-exti.c10
-rw-r--r--drivers/isdn/hardware/mISDN/hfcmulti.c3
-rw-r--r--drivers/isdn/hardware/mISDN/hfcpci.c9
-rw-r--r--drivers/isdn/hardware/mISDN/mISDNinfineon.c5
-rw-r--r--drivers/isdn/isdnloop/isdnloop.c2
-rw-r--r--drivers/leds/leds-mlxreg.c19
-rw-r--r--drivers/leds/leds-pca9532.c8
-rw-r--r--drivers/leds/trigger/ledtrig-netdev.c16
-rw-r--r--drivers/lightnvm/pblk-core.c8
-rw-r--r--drivers/lightnvm/pblk-gc.c20
-rw-r--r--drivers/lightnvm/pblk-init.c4
-rw-r--r--drivers/lightnvm/pblk-map.c1
-rw-r--r--drivers/lightnvm/pblk-rb.c26
-rw-r--r--drivers/lightnvm/pblk-recovery.c64
-rw-r--r--drivers/lightnvm/pblk-rl.c10
-rw-r--r--drivers/lightnvm/pblk-trace.h2
-rw-r--r--drivers/lightnvm/pblk-write.c1
-rw-r--r--drivers/lightnvm/pblk.h17
-rw-r--r--drivers/macintosh/smu.c5
-rw-r--r--drivers/mailbox/Kconfig11
-rw-r--r--drivers/mailbox/Makefile2
-rw-r--r--drivers/mailbox/imx-mailbox.c4
-rw-r--r--drivers/mailbox/mailbox-test.c26
-rw-r--r--drivers/mailbox/stm32-ipcc.c4
-rw-r--r--drivers/mailbox/tegra-hsp.c2
-rw-r--r--drivers/mailbox/zynqmp-ipi-mailbox.c725
-rw-r--r--drivers/md/Kconfig12
-rw-r--r--drivers/md/Makefile4
-rw-r--r--drivers/md/bcache/btree.c3
-rw-r--r--drivers/md/bcache/extents.c13
-rw-r--r--drivers/md/bcache/request.c7
-rw-r--r--drivers/md/bcache/stats.c2
-rw-r--r--drivers/md/bcache/super.c30
-rw-r--r--drivers/md/bcache/sysfs.c81
-rw-r--r--drivers/md/bcache/sysfs.h23
-rw-r--r--drivers/md/bcache/util.c6
-rw-r--r--drivers/md/bcache/writeback.h3
-rw-r--r--drivers/md/dm-cache-target.c127
-rw-r--r--drivers/md/dm-crypt.c3
-rw-r--r--drivers/md/dm-init.c303
-rw-r--r--drivers/md/dm-integrity.c10
-rw-r--r--drivers/md/dm-ioctl.c103
-rw-r--r--drivers/md/dm-raid.c14
-rw-r--r--drivers/md/dm-rq.c18
-rw-r--r--drivers/md/dm-rq.h16
-rw-r--r--drivers/md/dm-snap.c8
-rw-r--r--drivers/md/dm-switch.c3
-rw-r--r--drivers/md/dm-table.c13
-rw-r--r--drivers/md/dm-thin.c14
-rw-r--r--drivers/md/dm-verity-fec.c6
-rw-r--r--drivers/md/dm-writecache.c2
-rw-r--r--drivers/md/dm-zoned-target.c1
-rw-r--r--drivers/md/dm.c139
-rw-r--r--drivers/md/md-linear.c3
-rw-r--r--drivers/md/persistent-data/dm-block-manager.c8
-rw-r--r--drivers/md/raid1.c9
-rw-r--r--drivers/md/raid10.c3
-rw-r--r--drivers/md/raid5-log.h1
-rw-r--r--drivers/md/raid5-ppl.c69
-rw-r--r--drivers/md/raid5.c90
-rw-r--r--drivers/md/raid5.h9
-rw-r--r--drivers/media/cec/cec-api.c2
-rw-r--r--drivers/media/common/saa7146/saa7146_fops.c2
-rw-r--r--drivers/media/common/saa7146/saa7146_i2c.c5
-rw-r--r--drivers/media/common/saa7146/saa7146_video.c2
-rw-r--r--drivers/media/common/siano/sms-cards.c2
-rw-r--r--drivers/media/common/siano/smscoreapi.h2
-rw-r--r--drivers/media/common/v4l2-tpg/v4l2-tpg-core.c12
-rw-r--r--drivers/media/common/videobuf2/videobuf2-core.c53
-rw-r--r--drivers/media/common/videobuf2/videobuf2-dma-contig.c41
-rw-r--r--drivers/media/common/videobuf2/videobuf2-dma-sg.c4
-rw-r--r--drivers/media/common/videobuf2/videobuf2-memops.c2
-rw-r--r--drivers/media/common/videobuf2/videobuf2-v4l2.c30
-rw-r--r--drivers/media/dvb-core/dmxdev.c8
-rw-r--r--drivers/media/dvb-core/dvb_ca_en50221.c5
-rw-r--r--drivers/media/dvb-core/dvb_frontend.c2
-rw-r--r--drivers/media/dvb-core/dvbdev.c2
-rw-r--r--drivers/media/dvb-frontends/cxd2841er.c2
-rw-r--r--drivers/media/dvb-frontends/dib0090.c2
-rw-r--r--drivers/media/dvb-frontends/dib7000m.c4
-rw-r--r--drivers/media/dvb-frontends/dib7000p.c8
-rw-r--r--drivers/media/dvb-frontends/dib8000.c12
-rw-r--r--drivers/media/dvb-frontends/dib9000.c4
-rw-r--r--drivers/media/dvb-frontends/drx39xyj/drx_dap_fasi.h8
-rw-r--r--drivers/media/dvb-frontends/drx39xyj/drx_driver.h8
-rw-r--r--drivers/media/dvb-frontends/drx39xyj/drxj.c48
-rw-r--r--drivers/media/dvb-frontends/drx39xyj/drxj.h12
-rw-r--r--drivers/media/dvb-frontends/drxd_firm.c2
-rw-r--r--drivers/media/dvb-frontends/drxd_hard.c30
-rw-r--r--drivers/media/dvb-frontends/drxk.h2
-rw-r--r--drivers/media/dvb-frontends/drxk_hard.c8
-rw-r--r--drivers/media/dvb-frontends/ds3000.c4
-rw-r--r--drivers/media/dvb-frontends/isl6421.c2
-rw-r--r--drivers/media/dvb-frontends/lgdt3306a.c5
-rw-r--r--drivers/media/dvb-frontends/lgdt330x.c2
-rw-r--r--drivers/media/dvb-frontends/m88rs2000.c2
-rw-r--r--drivers/media/dvb-frontends/mt312.c4
-rw-r--r--drivers/media/dvb-frontends/nxt200x.c4
-rw-r--r--drivers/media/dvb-frontends/or51211.c2
-rw-r--r--drivers/media/dvb-frontends/rtl2832_sdr.c2
-rw-r--r--drivers/media/dvb-frontends/s5h1409.c2
-rw-r--r--drivers/media/dvb-frontends/sp8870.c4
-rw-r--r--drivers/media/dvb-frontends/stb0899_algo.c6
-rw-r--r--drivers/media/dvb-frontends/stv0367_defs.h2
-rw-r--r--drivers/media/dvb-frontends/stv0900_core.c4
-rw-r--r--drivers/media/dvb-frontends/stv0910.c4
-rw-r--r--drivers/media/dvb-frontends/stv6110.c2
-rw-r--r--drivers/media/dvb-frontends/tda1004x.h2
-rw-r--r--drivers/media/dvb-frontends/tda10086.c2
-rw-r--r--drivers/media/dvb-frontends/tda18271c2dd.c6
-rw-r--r--drivers/media/i2c/Kconfig36
-rw-r--r--drivers/media/i2c/Makefile4
-rw-r--r--drivers/media/i2c/adv7175.c2
-rw-r--r--drivers/media/i2c/adv748x/adv748x-afe.c2
-rw-r--r--drivers/media/i2c/adv748x/adv748x-core.c335
-rw-r--r--drivers/media/i2c/adv748x/adv748x-csi2.c64
-rw-r--r--drivers/media/i2c/adv748x/adv748x-hdmi.c2
-rw-r--r--drivers/media/i2c/adv748x/adv748x.h28
-rw-r--r--drivers/media/i2c/adv7842.c10
-rw-r--r--drivers/media/i2c/bt819.c4
-rw-r--r--drivers/media/i2c/cx25840/cx25840-core.c3
-rw-r--r--drivers/media/i2c/cx25840/cx25840-core.h3
-rw-r--r--drivers/media/i2c/cx25840/cx25840-ir.c4
-rw-r--r--drivers/media/i2c/dw9714.c2
-rw-r--r--drivers/media/i2c/et8ek8/et8ek8_mode.c2
-rw-r--r--drivers/media/i2c/imx214.c2
-rw-r--r--drivers/media/i2c/imx274.c24
-rw-r--r--drivers/media/i2c/lm3560.c2
-rw-r--r--drivers/media/i2c/lm3646.c2
-rw-r--r--drivers/media/i2c/m5mols/m5mols.h2
-rw-r--r--drivers/media/i2c/m5mols/m5mols_core.c2
-rw-r--r--drivers/media/i2c/msp3400-driver.c2
-rw-r--r--drivers/media/i2c/mt9m001.c (renamed from drivers/media/i2c/soc_camera/soc_mt9m001.c)457
-rw-r--r--drivers/media/i2c/mt9m111.c39
-rw-r--r--drivers/media/i2c/mt9t112.c2
-rw-r--r--drivers/media/i2c/ov2640.c45
-rw-r--r--drivers/media/i2c/ov5640.c159
-rw-r--r--drivers/media/i2c/ov6650.c4
-rw-r--r--drivers/media/i2c/ov7670.c201
-rw-r--r--drivers/media/i2c/ov7740.c9
-rw-r--r--drivers/media/i2c/ov8856.c1268
-rw-r--r--drivers/media/i2c/ov9640.c (renamed from drivers/media/i2c/soc_camera/soc_ov9640.c)123
-rw-r--r--drivers/media/i2c/ov9640.h (renamed from drivers/media/i2c/soc_camera/ov9640.h)7
-rw-r--r--drivers/media/i2c/ov9650.c4
-rw-r--r--drivers/media/i2c/s5c73m3/s5c73m3-core.c2
-rw-r--r--drivers/media/i2c/s5k4ecgx.c2
-rw-r--r--drivers/media/i2c/s5k6aa.c2
-rw-r--r--drivers/media/i2c/saa7115.c2
-rw-r--r--drivers/media/i2c/saa717x.c2
-rw-r--r--drivers/media/i2c/soc_camera/Makefile10
-rw-r--r--drivers/media/i2c/soc_camera/soc_mt9t112.c1157
-rw-r--r--drivers/media/i2c/soc_camera/soc_ov772x.c1123
-rw-r--r--drivers/media/i2c/soc_camera/soc_rj54n1cb0c.c1415
-rw-r--r--drivers/media/i2c/soc_camera/soc_tw9910.c999
-rw-r--r--drivers/media/i2c/tda1997x.c4
-rw-r--r--drivers/media/i2c/tda1997x_regs.h2
-rw-r--r--drivers/media/i2c/tda9840.c2
-rw-r--r--drivers/media/i2c/tea6415c.c2
-rw-r--r--drivers/media/i2c/tea6420.c2
-rw-r--r--drivers/media/i2c/tvaudio.c4
-rw-r--r--drivers/media/i2c/tvp514x.c2
-rw-r--r--drivers/media/i2c/tw9910.c29
-rw-r--r--drivers/media/i2c/video-i2c.c110
-rw-r--r--drivers/media/media-request.c3
-rw-r--r--drivers/media/pci/bt8xx/bttv-audio-hook.c2
-rw-r--r--drivers/media/pci/bt8xx/bttv-audio-hook.h2
-rw-r--r--drivers/media/pci/bt8xx/bttv-cards.c12
-rw-r--r--drivers/media/pci/bt8xx/bttv-driver.c12
-rw-r--r--drivers/media/pci/bt8xx/bttv-risc.c2
-rw-r--r--drivers/media/pci/bt8xx/bttv.h2
-rw-r--r--drivers/media/pci/bt8xx/dst.c22
-rw-r--r--drivers/media/pci/cobalt/cobalt-v4l2.c2
-rw-r--r--drivers/media/pci/cx18/cx18-cards.h2
-rw-r--r--drivers/media/pci/cx18/cx18-dvb.c6
-rw-r--r--drivers/media/pci/cx18/cx18-fileops.c2
-rw-r--r--drivers/media/pci/cx18/cx18-io.h2
-rw-r--r--drivers/media/pci/cx18/cx18-mailbox.c2
-rw-r--r--drivers/media/pci/cx18/cx18-vbi.c2
-rw-r--r--drivers/media/pci/cx18/cx23418.h2
-rw-r--r--drivers/media/pci/cx23885/cx23885-417.c2
-rw-r--r--drivers/media/pci/cx23885/cx23885-alsa.c2
-rw-r--r--drivers/media/pci/cx23885/cx23885-core.c6
-rw-r--r--drivers/media/pci/cx23885/cx23885.h2
-rw-r--r--drivers/media/pci/cx23885/cx23888-ir.c4
-rw-r--r--drivers/media/pci/cx25821/cx25821-alsa.c2
-rw-r--r--drivers/media/pci/cx25821/cx25821-sram.h2
-rw-r--r--drivers/media/pci/cx25821/cx25821.h2
-rw-r--r--drivers/media/pci/dm1105/dm1105.c2
-rw-r--r--drivers/media/pci/intel/ipu3/ipu3-cio2.c11
-rw-r--r--drivers/media/pci/ivtv/Kconfig23
-rw-r--r--drivers/media/pci/ivtv/ivtv-yuv.c2
-rw-r--r--drivers/media/pci/ivtv/ivtvfb.c16
-rw-r--r--drivers/media/pci/meye/meye.c8
-rw-r--r--drivers/media/pci/meye/meye.h4
-rw-r--r--drivers/media/pci/ngene/ngene-core.c2
-rw-r--r--drivers/media/pci/pt1/pt1.c54
-rw-r--r--drivers/media/pci/pt3/pt3.h2
-rw-r--r--drivers/media/pci/saa7134/saa7134-cards.c2
-rw-r--r--drivers/media/pci/saa7146/mxb.c4
-rw-r--r--drivers/media/pci/saa7164/saa7164-api.c2
-rw-r--r--drivers/media/pci/saa7164/saa7164-cards.c4
-rw-r--r--drivers/media/pci/saa7164/saa7164-core.c4
-rw-r--r--drivers/media/pci/saa7164/saa7164-dvb.c2
-rw-r--r--drivers/media/pci/saa7164/saa7164-fw.c2
-rw-r--r--drivers/media/pci/smipcie/smipcie-ir.c132
-rw-r--r--drivers/media/pci/smipcie/smipcie.h1
-rw-r--r--drivers/media/pci/solo6x10/solo6x10-disp.c4
-rw-r--r--drivers/media/pci/sta2x11/sta2x11_vip.c2
-rw-r--r--drivers/media/pci/ttpci/av7110.c6
-rw-r--r--drivers/media/pci/tw68/tw68-video.c2
-rw-r--r--drivers/media/platform/Kconfig5
-rw-r--r--drivers/media/platform/Makefile2
-rw-r--r--drivers/media/platform/aspeed-video.c1
-rw-r--r--drivers/media/platform/atmel/atmel-isi.c4
-rw-r--r--drivers/media/platform/coda/coda-bit.c24
-rw-r--r--drivers/media/platform/coda/coda-common.c13
-rw-r--r--drivers/media/platform/coda/coda-jpeg.c2
-rw-r--r--drivers/media/platform/coda/coda.h2
-rw-r--r--drivers/media/platform/davinci/isif.c4
-rw-r--r--drivers/media/platform/davinci/vpbe.c2
-rw-r--r--drivers/media/platform/davinci/vpfe_capture.c2
-rw-r--r--drivers/media/platform/davinci/vpif.c2
-rw-r--r--drivers/media/platform/davinci/vpif_display.c4
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is-command.h2
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is-param.h2
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is.c16
-rw-r--r--drivers/media/platform/exynos4-is/fimc-isp-video.c4
-rw-r--r--drivers/media/platform/exynos4-is/media-dev.h2
-rw-r--r--drivers/media/platform/exynos4-is/mipi-csis.c4
-rw-r--r--drivers/media/platform/fsl-viu.c2
-rw-r--r--drivers/media/platform/imx-pxp.c16
-rw-r--r--drivers/media/platform/marvell-ccic/mmp-driver.c4
-rw-r--r--drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c40
-rw-r--r--drivers/media/platform/mtk-mdp/mtk_mdp_core.h6
-rw-r--r--drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c20
-rw-r--r--drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c64
-rw-r--r--drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.c163
-rw-r--r--drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h35
-rw-r--r--drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c74
-rw-r--r--drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c104
-rw-r--r--drivers/media/platform/mtk-vcodec/vdec/vdec_h264_if.c4
-rw-r--r--drivers/media/platform/mtk-vcodec/vdec/vdec_vp8_if.c2
-rw-r--r--drivers/media/platform/mtk-vcodec/vdec_drv_if.h2
-rw-r--r--drivers/media/platform/mtk-vcodec/vdec_vpu_if.h2
-rw-r--r--drivers/media/platform/mx2_emmaprp.c6
-rw-r--r--drivers/media/platform/omap/omap_vout.c16
-rw-r--r--drivers/media/platform/omap/omap_voutdef.h4
-rw-r--r--drivers/media/platform/omap3isp/isp.c2
-rw-r--r--drivers/media/platform/omap3isp/ispccdc.c4
-rw-r--r--drivers/media/platform/omap3isp/ispcsi2.c2
-rw-r--r--drivers/media/platform/pxa_camera.c10
-rw-r--r--drivers/media/platform/qcom/venus/core.c12
-rw-r--r--drivers/media/platform/qcom/venus/core.h3
-rw-r--r--drivers/media/platform/qcom/venus/firmware.c53
-rw-r--r--drivers/media/platform/qcom/venus/helpers.c3
-rw-r--r--drivers/media/platform/rcar-vin/rcar-core.c26
-rw-r--r--drivers/media/platform/rcar-vin/rcar-csi2.c66
-rw-r--r--drivers/media/platform/rcar-vin/rcar-dma.c4
-rw-r--r--drivers/media/platform/rcar-vin/rcar-v4l2.c4
-rw-r--r--drivers/media/platform/rockchip/rga/rga-hw.c6
-rw-r--r--drivers/media/platform/rockchip/rga/rga.c6
-rw-r--r--drivers/media/platform/s3c-camif/camif-core.h2
-rw-r--r--drivers/media/platform/s5p-g2d/g2d.c6
-rw-r--r--drivers/media/platform/s5p-jpeg/jpeg-core.c63
-rw-r--r--drivers/media/platform/s5p-jpeg/jpeg-core.h6
-rw-r--r--drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.c2
-rw-r--r--drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.h2
-rw-r--r--drivers/media/platform/s5p-jpeg/jpeg-regs.h2
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc.c8
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_common.h2
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c2
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_dec.c2
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c2
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c4
-rw-r--r--drivers/media/platform/seco-cec/seco-cec.h2
-rw-r--r--drivers/media/platform/sh_veu.c4
-rw-r--r--drivers/media/platform/soc_camera/Kconfig26
-rw-r--r--drivers/media/platform/soc_camera/Makefile9
-rw-r--r--drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c1810
-rw-r--r--drivers/media/platform/soc_camera/soc_camera_platform.c188
-rw-r--r--drivers/media/platform/soc_camera/soc_scale_crop.c426
-rw-r--r--drivers/media/platform/soc_camera/soc_scale_crop.h47
-rw-r--r--drivers/media/platform/sti/bdisp/bdisp-debug.c34
-rw-r--r--drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.h2
-rw-r--r--drivers/media/platform/sti/delta/delta.h2
-rw-r--r--drivers/media/platform/sti/hva/hva-debugfs.c36
-rw-r--r--drivers/media/platform/sti/hva/hva-h264.c2
-rw-r--r--drivers/media/platform/stm32/stm32-dcmi.c2
-rw-r--r--drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c39
-rw-r--r--drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h5
-rw-r--r--drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c3
-rw-r--r--drivers/media/platform/ti-vpe/vpdma.c14
-rw-r--r--drivers/media/platform/ti-vpe/vpe.c2
-rw-r--r--drivers/media/platform/vicodec/codec-fwht.c148
-rw-r--r--drivers/media/platform/vicodec/codec-fwht.h30
-rw-r--r--drivers/media/platform/vicodec/codec-v4l2-fwht.c394
-rw-r--r--drivers/media/platform/vicodec/codec-v4l2-fwht.h15
-rw-r--r--drivers/media/platform/vicodec/vicodec-core.c658
-rw-r--r--drivers/media/platform/video-mux.c20
-rw-r--r--drivers/media/platform/vim2m.c675
-rw-r--r--drivers/media/platform/vimc/Makefile3
-rw-r--r--drivers/media/platform/vimc/vimc-capture.c26
-rw-r--r--drivers/media/platform/vimc/vimc-common.c35
-rw-r--r--drivers/media/platform/vimc/vimc-common.h17
-rw-r--r--drivers/media/platform/vimc/vimc-core.c5
-rw-r--r--drivers/media/platform/vimc/vimc-debayer.c26
-rw-r--r--drivers/media/platform/vimc/vimc-scaler.c28
-rw-r--r--drivers/media/platform/vimc/vimc-sensor.c51
-rw-r--r--drivers/media/platform/vimc/vimc-streamer.c188
-rw-r--r--drivers/media/platform/vimc/vimc-streamer.h38
-rw-r--r--drivers/media/platform/vivid/vivid-core.c26
-rw-r--r--drivers/media/platform/vivid/vivid-vid-cap.c10
-rw-r--r--drivers/media/platform/vivid/vivid-vid-common.c30
-rw-r--r--drivers/media/platform/vivid/vivid-vid-out.c57
-rw-r--r--drivers/media/platform/vsp1/vsp1_brx.c4
-rw-r--r--drivers/media/platform/vsp1/vsp1_drm.c6
-rw-r--r--drivers/media/platform/vsp1/vsp1_video.c2
-rw-r--r--drivers/media/platform/xilinx/xilinx-vip.c2
-rw-r--r--drivers/media/radio/radio-si476x.c2
-rw-r--r--drivers/media/radio/si470x/radio-si470x-i2c.c52
-rw-r--r--drivers/media/radio/si470x/radio-si470x.h1
-rw-r--r--drivers/media/radio/wl128x/fmdrv.h4
-rw-r--r--drivers/media/radio/wl128x/fmdrv_common.c4
-rw-r--r--drivers/media/rc/Kconfig17
-rw-r--r--drivers/media/rc/Makefile1
-rw-r--r--drivers/media/rc/ati_remote.c2
-rw-r--r--drivers/media/rc/ene_ir.c2
-rw-r--r--drivers/media/rc/ene_ir.h2
-rw-r--r--drivers/media/rc/fintek-cir.h2
-rw-r--r--drivers/media/rc/ir-rc6-decoder.c2
-rw-r--r--drivers/media/rc/ir-rcmm-decoder.c254
-rw-r--r--drivers/media/rc/ir-xmp-decoder.c2
-rw-r--r--drivers/media/rc/ite-cir.c2
-rw-r--r--drivers/media/rc/keymaps/rc-behold-columbus.c4
-rw-r--r--drivers/media/rc/keymaps/rc-behold.c2
-rw-r--r--drivers/media/rc/keymaps/rc-manli.c2
-rw-r--r--drivers/media/rc/keymaps/rc-powercolor-real-angel.c2
-rw-r--r--drivers/media/rc/mceusb.c2
-rw-r--r--drivers/media/rc/rc-core-priv.h5
-rw-r--r--drivers/media/rc/rc-ir-raw.c2
-rw-r--r--drivers/media/rc/rc-main.c34
-rw-r--r--drivers/media/rc/redrat3.c2
-rw-r--r--drivers/media/spi/cxd2880-spi.c8
-rw-r--r--drivers/media/tuners/mxl5005s.c2
-rw-r--r--drivers/media/tuners/qm1d1b0004.h2
-rw-r--r--drivers/media/tuners/r820t.c4
-rw-r--r--drivers/media/tuners/tda18271-common.c10
-rw-r--r--drivers/media/tuners/tda18271-fe.c2
-rw-r--r--drivers/media/tuners/tda18271.h4
-rw-r--r--drivers/media/tuners/xc4000.c4
-rw-r--r--drivers/media/usb/au0828/au0828-core.c2
-rw-r--r--drivers/media/usb/au0828/au0828-dvb.c2
-rw-r--r--drivers/media/usb/au0828/au0828.h2
-rw-r--r--drivers/media/usb/cpia2/cpia2.h2
-rw-r--r--drivers/media/usb/cpia2/cpia2_usb.c2
-rw-r--r--drivers/media/usb/cpia2/cpia2_v4l.c11
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-417.c4
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-avcore.c2
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-pcb-cfg.h2
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-vbi.c2
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-video.c2
-rw-r--r--drivers/media/usb/cx231xx/cx231xx.h2
-rw-r--r--drivers/media/usb/dvb-usb-v2/dvb_usb.h2
-rw-r--r--drivers/media/usb/dvb-usb-v2/lmedm04.c8
-rw-r--r--drivers/media/usb/dvb-usb-v2/mxl111sf.c4
-rw-r--r--drivers/media/usb/dvb-usb/af9005.c2
-rw-r--r--drivers/media/usb/dvb-usb/cinergyT2-fe.c2
-rw-r--r--drivers/media/usb/dvb-usb/cxusb.c2
-rw-r--r--drivers/media/usb/dvb-usb/dvb-usb-init.c2
-rw-r--r--drivers/media/usb/dvb-usb/dvb-usb.h2
-rw-r--r--drivers/media/usb/dvb-usb/pctv452e.c4
-rw-r--r--drivers/media/usb/em28xx/em28xx-i2c.c4
-rw-r--r--drivers/media/usb/em28xx/em28xx-reg.h2
-rw-r--r--drivers/media/usb/gspca/Kconfig2
-rw-r--r--drivers/media/usb/gspca/autogain_functions.c2
-rw-r--r--drivers/media/usb/gspca/benq.c4
-rw-r--r--drivers/media/usb/gspca/cpia1.c14
-rw-r--r--drivers/media/usb/gspca/gspca.c18
-rw-r--r--drivers/media/usb/gspca/m5602/m5602_mt9m111.c8
-rw-r--r--drivers/media/usb/gspca/m5602/m5602_po1030.c8
-rw-r--r--drivers/media/usb/gspca/mr97310a.c10
-rw-r--r--drivers/media/usb/gspca/ov519.c4
-rw-r--r--drivers/media/usb/gspca/ov534.c153
-rw-r--r--drivers/media/usb/gspca/pac_common.h2
-rw-r--r--drivers/media/usb/gspca/sn9c20x.c2
-rw-r--r--drivers/media/usb/gspca/sonixb.c4
-rw-r--r--drivers/media/usb/gspca/sonixj.c2
-rw-r--r--drivers/media/usb/gspca/spca501.c2
-rw-r--r--drivers/media/usb/gspca/sq905.c2
-rw-r--r--drivers/media/usb/gspca/sunplus.c4
-rw-r--r--drivers/media/usb/gspca/t613.c2
-rw-r--r--drivers/media/usb/gspca/touptek.c4
-rw-r--r--drivers/media/usb/gspca/w996Xcf.c2
-rw-r--r--drivers/media/usb/gspca/zc3xx-reg.h2
-rw-r--r--drivers/media/usb/gspca/zc3xx.c8
-rw-r--r--drivers/media/usb/hdpvr/hdpvr-i2c.c14
-rw-r--r--drivers/media/usb/hdpvr/hdpvr.h2
-rw-r--r--drivers/media/usb/pwc/pwc-dec23.c4
-rw-r--r--drivers/media/usb/pwc/pwc-if.c71
-rw-r--r--drivers/media/usb/pwc/pwc-misc.c2
-rw-r--r--drivers/media/usb/siano/smsusb.c2
-rw-r--r--drivers/media/usb/stk1160/stk1160-core.c4
-rw-r--r--drivers/media/usb/stk1160/stk1160-reg.h4
-rw-r--r--drivers/media/usb/stkwebcam/stk-webcam.c4
-rw-r--r--drivers/media/usb/tm6000/tm6000-alsa.c2
-rw-r--r--drivers/media/usb/tm6000/tm6000-core.c4
-rw-r--r--drivers/media/usb/tm6000/tm6000-dvb.c2
-rw-r--r--drivers/media/usb/tm6000/tm6000-i2c.c2
-rw-r--r--drivers/media/usb/tm6000/tm6000-stds.c2
-rw-r--r--drivers/media/usb/tm6000/tm6000-video.c4
-rw-r--r--drivers/media/usb/ttusb-budget/dvb-ttusb-budget.c2
-rw-r--r--drivers/media/usb/ttusb-dec/ttusb_dec.c2
-rw-r--r--drivers/media/usb/usbvision/usbvision-core.c10
-rw-r--r--drivers/media/usb/usbvision/usbvision-video.c4
-rw-r--r--drivers/media/usb/usbvision/usbvision.h10
-rw-r--r--drivers/media/usb/uvc/uvc_ctrl.c2
-rw-r--r--drivers/media/usb/uvc/uvc_driver.c16
-rw-r--r--drivers/media/usb/uvc/uvc_video.c10
-rw-r--r--drivers/media/usb/uvc/uvcvideo.h6
-rw-r--r--drivers/media/usb/zr364xx/zr364xx.c6
-rw-r--r--drivers/media/v4l2-core/v4l2-common.c10
-rw-r--r--drivers/media/v4l2-core/v4l2-ctrls.c16
-rw-r--r--drivers/media/v4l2-core/v4l2-event.c19
-rw-r--r--drivers/media/v4l2-core/v4l2-fwnode.c16
-rw-r--r--drivers/media/v4l2-core/v4l2-ioctl.c20
-rw-r--r--drivers/media/v4l2-core/v4l2-mem2mem.c52
-rw-r--r--drivers/media/v4l2-core/videobuf-core.c12
-rw-r--r--drivers/media/v4l2-core/videobuf-dma-contig.c2
-rw-r--r--drivers/media/v4l2-core/videobuf-vmalloc.c22
-rw-r--r--drivers/memory/tegra/mc.c118
-rw-r--r--drivers/memory/tegra/mc.h10
-rw-r--r--drivers/mfd/Kconfig47
-rw-r--r--drivers/mfd/Makefile4
-rw-r--r--drivers/mfd/aat2870-core.c40
-rw-r--r--drivers/mfd/adp5520.c30
-rw-r--r--drivers/mfd/as3711.c14
-rw-r--r--drivers/mfd/at91-usart.c24
-rw-r--r--drivers/mfd/cros_ec.c14
-rw-r--r--drivers/mfd/cros_ec_dev.c91
-rw-r--r--drivers/mfd/cros_ec_dev.h6
-rw-r--r--drivers/mfd/db8500-prcmu.c10
-rw-r--r--drivers/mfd/htc-i2cpld.c18
-rw-r--r--drivers/mfd/intel-lpss-acpi.c1
-rw-r--r--drivers/mfd/intel-lpss-pci.c1
-rw-r--r--drivers/mfd/intel-lpss.h2
-rw-r--r--drivers/mfd/lochnagar-i2c.c398
-rw-r--r--drivers/mfd/max8925-core.c7
-rw-r--r--drivers/mfd/mxs-lradc.c2
-rw-r--r--drivers/mfd/qcom-pm8xxx.c75
-rw-r--r--drivers/mfd/rc5t583.c14
-rw-r--r--drivers/mfd/sec-core.c16
-rw-r--r--drivers/mfd/sm501.c6
-rw-r--r--drivers/mfd/sta2x11-mfd.c10
-rw-r--r--drivers/mfd/stmpe.c68
-rw-r--r--drivers/mfd/stpmic1.c213
-rw-r--r--drivers/mfd/syscon.c12
-rw-r--r--drivers/mfd/tps65090.c30
-rw-r--r--drivers/mfd/tps65218.c89
-rw-r--r--drivers/mfd/tps65910.c18
-rw-r--r--drivers/mfd/tps68470.c1
-rw-r--r--drivers/mfd/tps80031.c37
-rw-r--r--drivers/mfd/tqmx86.c281
-rw-r--r--drivers/mfd/wm831x-core.c15
-rw-r--r--drivers/mfd/wm831x-i2c.c20
-rw-r--r--drivers/mfd/wm831x-spi.c24
-rw-r--r--drivers/mfd/wm8350-core.c30
-rw-r--r--drivers/mfd/wm8350-i2c.c24
-rw-r--r--drivers/mfd/wm8400-core.c18
-rw-r--r--drivers/misc/eeprom/at24.c169
-rw-r--r--drivers/misc/habanalabs/command_submission.c6
-rw-r--r--drivers/misc/habanalabs/debugfs.c7
-rw-r--r--drivers/misc/habanalabs/device.c71
-rw-r--r--drivers/misc/habanalabs/goya/goya.c65
-rw-r--r--drivers/misc/habanalabs/habanalabs.h21
-rw-r--r--drivers/misc/habanalabs/hw_queue.c5
-rw-r--r--drivers/misc/habanalabs/memory.c38
-rw-r--r--drivers/misc/habanalabs/mmu.c6
-rw-r--r--drivers/misc/mic/scif/scif_rma.c38
-rw-r--r--drivers/misc/pch_phub.c1
-rw-r--r--drivers/misc/pci_endpoint_test.c1
-rw-r--r--drivers/mmc/core/queue.c3
-rw-r--r--drivers/mmc/host/alcor.c25
-rw-r--r--drivers/mmc/host/davinci_mmc.c2
-rw-r--r--drivers/mmc/host/mxcmmc.c16
-rw-r--r--drivers/mmc/host/pxamci.c2
-rw-r--r--drivers/mmc/host/renesas_sdhi_core.c8
-rw-r--r--drivers/mmc/host/sdhci-omap.c3
-rw-r--r--drivers/mtd/ubi/cdev.c30
-rw-r--r--drivers/mtd/ubi/ubi.h1
-rw-r--r--drivers/mtd/ubi/wl.c174
-rw-r--r--drivers/net/Kconfig4
-rw-r--r--drivers/net/can/usb/peak_usb/pcan_usb_pro.c2
-rw-r--r--drivers/net/dsa/mv88e6xxx/chip.c11
-rw-r--r--drivers/net/dsa/mv88e6xxx/chip.h3
-rw-r--r--drivers/net/dsa/mv88e6xxx/port.c24
-rw-r--r--drivers/net/dsa/mv88e6xxx/port.h4
-rw-r--r--drivers/net/dsa/qca8k.c174
-rw-r--r--drivers/net/dsa/qca8k.h13
-rw-r--r--drivers/net/ethernet/3com/3c515.c2
-rw-r--r--drivers/net/ethernet/8390/mac8390.c19
-rw-r--r--drivers/net/ethernet/8390/pcnet_cs.c10
-rw-r--r--drivers/net/ethernet/aquantia/atlantic/aq_ring.c5
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c3
-rw-r--r--drivers/net/ethernet/cadence/macb_main.c10
-rw-r--r--drivers/net/ethernet/cavium/liquidio/lio_core.c10
-rw-r--r--drivers/net/ethernet/cavium/liquidio/lio_main.c5
-rw-r--r--drivers/net/ethernet/cavium/thunder/nicvf_main.c6
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c2
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/sge.c2
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/t4_msg.h8
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/t4_tcb.h12
-rw-r--r--drivers/net/ethernet/cisco/enic/enic_main.c6
-rw-r--r--drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c15
-rw-r--r--drivers/net/ethernet/fujitsu/fmvj18x_cs.c5
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hnae3.h1
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3_enet.c23
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3_enet.h1
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_err.c9
-rw-r--r--drivers/net/ethernet/ibm/ehea/ehea_main.c1
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/cmd.c9
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/resource_tracker.c6
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c3
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_rep.c13
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_rx.c11
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/eswitch.c17
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/fs_core.c161
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/fs_core.h1
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lag.c21
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/main.c85
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h2
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/qp.c72
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/core_env.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/core_thermal.c21
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/minimal.c18
-rw-r--r--drivers/net/ethernet/micrel/ks8851.c42
-rw-r--r--drivers/net/ethernet/micrel/ks8851.h93
-rw-r--r--drivers/net/ethernet/micrel/ks8851_mll.c317
-rw-r--r--drivers/net/ethernet/microchip/lan743x_main.c55
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h2
-rw-r--r--drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c7
-rw-r--r--drivers/net/ethernet/qlogic/qede/qede_ethtool.c7
-rw-r--r--drivers/net/ethernet/qlogic/qla3xxx.c6
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c2
-rw-r--r--drivers/net/ethernet/qlogic/qlge/qlge_main.c5
-rw-r--r--drivers/net/ethernet/realtek/8139too.c1
-rw-r--r--drivers/net/ethernet/realtek/atp.c2
-rw-r--r--drivers/net/ethernet/realtek/r8169.c8
-rw-r--r--drivers/net/ethernet/renesas/ravb_main.c2
-rw-r--r--drivers/net/ethernet/renesas/sh_eth.c6
-rw-r--r--drivers/net/ethernet/rocker/rocker_main.c5
-rw-r--r--drivers/net/ethernet/sis/sis900.c10
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c135
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c5
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/ring_mode.c13
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_main.c20
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c3
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c2
-rw-r--r--drivers/net/ethernet/sun/niu.c1
-rw-r--r--drivers/net/ethernet/ti/davinci_emac.c4
-rw-r--r--drivers/net/ethernet/ti/netcp_ethss.c8
-rw-r--r--drivers/net/ethernet/xilinx/xilinx_axienet_main.c2
-rw-r--r--drivers/net/ieee802154/adf7242.c4
-rw-r--r--drivers/net/ieee802154/mac802154_hwsim.c2
-rw-r--r--drivers/net/phy/Kconfig3
-rw-r--r--drivers/net/phy/broadcom.c13
-rw-r--r--drivers/net/phy/dp83822.c34
-rw-r--r--drivers/net/phy/meson-gxl.c6
-rw-r--r--drivers/net/phy/phy_device.c2
-rw-r--r--drivers/net/ppp/pptp.c1
-rw-r--r--drivers/net/tun.c16
-rw-r--r--drivers/net/usb/aqc111.c15
-rw-r--r--drivers/net/usb/cdc_ether.c8
-rw-r--r--drivers/net/vxlan.c22
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c4
-rw-r--r--drivers/net/wireless/mediatek/mt76/dma.c7
-rw-r--r--drivers/net/wireless/mediatek/mt76/mac80211.c18
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76.h4
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7603/beacon.c3
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7603/dma.c17
-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/main.c16
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7603/mcu.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7603/soc.c4
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x0/initvals.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x0/usb.c10
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02.h11
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_debugfs.c27
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_dfs.c3
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_mac.c67
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_mac.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c82
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_phy.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_usb_core.c3
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_util.c14
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/init.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/mt76x2.h1
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/pci_init.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/pci_mcu.c21
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/phy.c30
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/usb.c7
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/usb_mac.c1
-rw-r--r--drivers/net/wireless/mediatek/mt76/tx.c11
-rw-r--r--drivers/net/wireless/mediatek/mt76/usb.c6
-rw-r--r--drivers/net/wireless/mediatek/mt7601u/usb.c4
-rw-r--r--drivers/ntb/hw/intel/ntb_hw_gen1.c25
-rw-r--r--drivers/ntb/hw/intel/ntb_hw_gen1.h5
-rw-r--r--drivers/ntb/hw/intel/ntb_hw_gen3.c33
-rw-r--r--drivers/ntb/hw/mscc/ntb_hw_switchtec.c20
-rw-r--r--drivers/ntb/ntb_transport.c31
-rw-r--r--drivers/nvdimm/btt.c33
-rw-r--r--drivers/nvdimm/btt.h2
-rw-r--r--drivers/nvdimm/btt_devs.c8
-rw-r--r--drivers/nvdimm/dimm_devs.c7
-rw-r--r--drivers/nvdimm/e820.c1
-rw-r--r--drivers/nvdimm/label.c26
-rw-r--r--drivers/nvdimm/namespace_devs.c18
-rw-r--r--drivers/nvdimm/nd.h2
-rw-r--r--drivers/nvdimm/of_pmem.c2
-rw-r--r--drivers/nvdimm/pfn_devs.c24
-rw-r--r--drivers/nvdimm/region_devs.c8
-rw-r--r--drivers/nvme/host/core.c130
-rw-r--r--drivers/nvme/host/fabrics.c11
-rw-r--r--drivers/nvme/host/fabrics.h10
-rw-r--r--drivers/nvme/host/fault_inject.c2
-rw-r--r--drivers/nvme/host/fc.c50
-rw-r--r--drivers/nvme/host/lightnvm.c16
-rw-r--r--drivers/nvme/host/multipath.c101
-rw-r--r--drivers/nvme/host/nvme.h26
-rw-r--r--drivers/nvme/host/pci.c15
-rw-r--r--drivers/nvme/host/rdma.c26
-rw-r--r--drivers/nvme/host/tcp.c42
-rw-r--r--drivers/nvme/host/trace.c24
-rw-r--r--drivers/nvme/host/trace.h12
-rw-r--r--drivers/nvme/target/admin-cmd.c10
-rw-r--r--drivers/nvme/target/configfs.c10
-rw-r--r--drivers/nvme/target/core.c34
-rw-r--r--drivers/nvme/target/discovery.c12
-rw-r--r--drivers/nvme/target/fabrics-cmd.c10
-rw-r--r--drivers/nvme/target/fc.c56
-rw-r--r--drivers/nvme/target/fcloop.c13
-rw-r--r--drivers/nvme/target/io-cmd-bdev.c18
-rw-r--r--drivers/nvme/target/io-cmd-file.c22
-rw-r--r--drivers/nvme/target/loop.c10
-rw-r--r--drivers/nvme/target/nvmet.h10
-rw-r--r--drivers/nvme/target/rdma.c10
-rw-r--r--drivers/of/Kconfig6
-rw-r--r--drivers/of/fdt.c8
-rw-r--r--drivers/of/of_reserved_mem.c24
-rw-r--r--drivers/of/unittest.c23
-rw-r--r--drivers/opp/core.c2
-rw-r--r--drivers/opp/of.c16
-rw-r--r--drivers/parisc/ccio-dma.c4
-rw-r--r--drivers/parport/daisy.c32
-rw-r--r--drivers/parport/probe.c2
-rw-r--r--drivers/parport/share.c10
-rw-r--r--drivers/pci/ats.c57
-rw-r--r--drivers/pci/controller/Kconfig2
-rw-r--r--drivers/pci/controller/dwc/Kconfig4
-rw-r--r--drivers/pci/controller/dwc/Makefile2
-rw-r--r--drivers/pci/controller/dwc/pci-dra7xx.c94
-rw-r--r--drivers/pci/controller/dwc/pci-imx6.c224
-rw-r--r--drivers/pci/controller/dwc/pci-layerscape-ep.c156
-rw-r--r--drivers/pci/controller/dwc/pcie-designware-ep.c16
-rw-r--r--drivers/pci/controller/dwc/pcie-designware-host.c115
-rw-r--r--drivers/pci/controller/dwc/pcie-designware-plat.c19
-rw-r--r--drivers/pci/controller/dwc/pcie-designware.c6
-rw-r--r--drivers/pci/controller/dwc/pcie-designware.h60
-rw-r--r--drivers/pci/controller/dwc/pcie-qcom.c2
-rw-r--r--drivers/pci/controller/pci-aardvark.c4
-rw-r--r--drivers/pci/controller/pci-hyperv.c61
-rw-r--r--drivers/pci/controller/pci-mvebu.c2
-rw-r--r--drivers/pci/controller/pcie-altera.c270
-rw-r--r--drivers/pci/controller/pcie-cadence-ep.c25
-rw-r--r--drivers/pci/controller/pcie-mediatek.c13
-rw-r--r--drivers/pci/controller/pcie-rockchip-ep.c16
-rw-r--r--drivers/pci/controller/vmd.c15
-rw-r--r--drivers/pci/endpoint/functions/pci-epf-test.c97
-rw-r--r--drivers/pci/endpoint/pci-epc-core.c53
-rw-r--r--drivers/pci/endpoint/pci-epf-core.c4
-rw-r--r--drivers/pci/hotplug/ibmphp.h1
-rw-r--r--drivers/pci/hotplug/ibmphp_core.c2
-rw-r--r--drivers/pci/hotplug/ibmphp_hpc.c47
-rw-r--r--drivers/pci/hotplug/pciehp_hpc.c21
-rw-r--r--drivers/pci/of.c2
-rw-r--r--drivers/pci/pci-bridge-emul.c86
-rw-r--r--drivers/pci/pci-bridge-emul.h13
-rw-r--r--drivers/pci/pci-driver.c4
-rw-r--r--drivers/pci/pci.c136
-rw-r--r--drivers/pci/pci.h1
-rw-r--r--drivers/pci/pcie/Kconfig7
-rw-r--r--drivers/pci/pcie/Makefile1
-rw-r--r--drivers/pci/pcie/aer.c9
-rw-r--r--drivers/pci/pcie/bw_notification.c121
-rw-r--r--drivers/pci/pcie/dpc.c27
-rw-r--r--drivers/pci/pcie/pme.c48
-rw-r--r--drivers/pci/pcie/portdrv.h6
-rw-r--r--drivers/pci/pcie/portdrv_core.c17
-rw-r--r--drivers/pci/pcie/portdrv_pci.c9
-rw-r--r--drivers/pci/probe.c122
-rw-r--r--drivers/pci/quirks.c4
-rw-r--r--drivers/pci/setup-bus.c63
-rw-r--r--drivers/perf/xgene_pmu.c2
-rw-r--r--drivers/phy/allwinner/phy-sun4i-usb.c5
-rw-r--r--drivers/pinctrl/bcm/pinctrl-bcm2835.c4
-rw-r--r--drivers/pinctrl/berlin/pinctrl-as370.c58
-rw-r--r--drivers/pinctrl/cirrus/pinctrl-madera-core.c1
-rw-r--r--drivers/pinctrl/freescale/Kconfig16
-rw-r--r--drivers/pinctrl/freescale/Makefile2
-rw-r--r--drivers/pinctrl/freescale/pinctrl-imx8mm.c348
-rw-r--r--drivers/pinctrl/freescale/pinctrl-imx8qm.c326
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mtk-common-v2.c8
-rw-r--r--drivers/pinctrl/meson/pinctrl-meson.c24
-rw-r--r--drivers/pinctrl/meson/pinctrl-meson.h1
-rw-r--r--drivers/pinctrl/meson/pinctrl-meson8b.c6
-rw-r--r--drivers/pinctrl/mvebu/pinctrl-armada-37xx.c15
-rw-r--r--drivers/pinctrl/nomadik/pinctrl-nomadik.c17
-rw-r--r--drivers/pinctrl/pinconf.c222
-rw-r--r--drivers/pinctrl/pinctrl-amd.c2
-rw-r--r--drivers/pinctrl/pinctrl-at91.c134
-rw-r--r--drivers/pinctrl/pinctrl-at91.h3
-rw-r--r--drivers/pinctrl/pinctrl-ingenic.c367
-rw-r--r--drivers/pinctrl/pinctrl-mcp23s08.c31
-rw-r--r--drivers/pinctrl/qcom/Kconfig2
-rw-r--r--drivers/pinctrl/qcom/pinctrl-qcs404.c25
-rw-r--r--drivers/pinctrl/qcom/pinctrl-spmi-gpio.c143
-rw-r--r--drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c157
-rw-r--r--drivers/pinctrl/samsung/pinctrl-exynos.c12
-rw-r--r--drivers/pinctrl/sh-pfc/pfc-emev2.c20
-rw-r--r--drivers/pinctrl/sh-pfc/pfc-r8a7778.c6
-rw-r--r--drivers/pinctrl/sh-pfc/pfc-r8a7790.c17
-rw-r--r--drivers/pinctrl/sh-pfc/pfc-r8a7791.c70
-rw-r--r--drivers/pinctrl/sh-pfc/pfc-r8a7792.c1
-rw-r--r--drivers/pinctrl/sh-pfc/pfc-r8a7794.c16
-rw-r--r--drivers/pinctrl/sh-pfc/pfc-r8a7795.c90
-rw-r--r--drivers/pinctrl/sh-pfc/pfc-r8a7796.c90
-rw-r--r--drivers/pinctrl/sh-pfc/pfc-r8a77965.c404
-rw-r--r--drivers/pinctrl/sh-pfc/pfc-r8a77970.c128
-rw-r--r--drivers/pinctrl/sh-pfc/pfc-r8a77980.c64
-rw-r--r--drivers/pinctrl/sh-pfc/pfc-r8a77990.c388
-rw-r--r--drivers/pinctrl/sh-pfc/pfc-r8a77995.c11
-rw-r--r--drivers/pinctrl/sh-pfc/pfc-sh73a0.c3
-rw-r--r--drivers/pinctrl/sh-pfc/pinctrl.c2
-rw-r--r--drivers/pinctrl/sh-pfc/sh_pfc.h3
-rw-r--r--drivers/pinctrl/sirf/pinctrl-atlas7.c4
-rw-r--r--drivers/pinctrl/sirf/pinctrl-sirf.c2
-rw-r--r--drivers/pinctrl/stm32/pinctrl-stm32.c70
-rw-r--r--drivers/pinctrl/sunxi/pinctrl-sun9i-a80-r.c1
-rw-r--r--drivers/pinctrl/sunxi/pinctrl-sun9i-a80.c1
-rw-r--r--drivers/pinctrl/sunxi/pinctrl-sunxi.c41
-rw-r--r--drivers/pinctrl/sunxi/pinctrl-sunxi.h12
-rw-r--r--drivers/pinctrl/ti/pinctrl-ti-iodelay.c7
-rw-r--r--drivers/platform/chrome/Kconfig49
-rw-r--r--drivers/platform/chrome/Makefile9
-rw-r--r--drivers/platform/chrome/chromeos_pstore.c17
-rw-r--r--drivers/platform/chrome/cros_ec_debugfs.c86
-rw-r--r--drivers/platform/chrome/cros_ec_i2c.c32
-rw-r--r--drivers/platform/chrome/cros_ec_lightbar.c150
-rw-r--r--drivers/platform/chrome/cros_ec_lpc.c38
-rw-r--r--drivers/platform/chrome/cros_ec_lpc_mec.c78
-rw-r--r--drivers/platform/chrome/cros_ec_lpc_mec.h63
-rw-r--r--drivers/platform/chrome/cros_ec_lpc_reg.c73
-rw-r--r--drivers/platform/chrome/cros_ec_lpc_reg.h20
-rw-r--r--drivers/platform/chrome/cros_ec_proto.c19
-rw-r--r--drivers/platform/chrome/cros_ec_spi.c31
-rw-r--r--drivers/platform/chrome/cros_ec_sysfs.c62
-rw-r--r--drivers/platform/chrome/cros_ec_vbc.c83
-rw-r--r--drivers/platform/chrome/cros_kbd_led_backlight.c19
-rw-r--r--drivers/platform/chrome/wilco_ec/Kconfig20
-rw-r--r--drivers/platform/chrome/wilco_ec/Makefile6
-rw-r--r--drivers/platform/chrome/wilco_ec/core.c136
-rw-r--r--drivers/platform/chrome/wilco_ec/debugfs.c238
-rw-r--r--drivers/platform/chrome/wilco_ec/mailbox.c237
-rw-r--r--drivers/platform/mellanox/mlxreg-hotplug.c28
-rw-r--r--drivers/platform/x86/Kconfig14
-rw-r--r--drivers/platform/x86/Makefile1
-rw-r--r--drivers/platform/x86/asus-wmi.c9
-rw-r--r--drivers/platform/x86/dell-smbios-wmi.c2
-rw-r--r--drivers/platform/x86/dell-wmi-descriptor.c2
-rw-r--r--drivers/platform/x86/dell-wmi.c7
-rw-r--r--drivers/platform/x86/dell_rbu.c50
-rw-r--r--drivers/platform/x86/huawei-wmi.c3
-rw-r--r--drivers/platform/x86/i2c-multi-instantiate.c9
-rw-r--r--drivers/platform/x86/ideapad-laptop.c37
-rw-r--r--drivers/platform/x86/intel-hid.c7
-rw-r--r--drivers/platform/x86/intel-wmi-thunderbolt.c2
-rw-r--r--drivers/platform/x86/intel_int0002_vgpio.c32
-rw-r--r--drivers/platform/x86/intel_pmc_core.c159
-rw-r--r--drivers/platform/x86/intel_pmc_core.h14
-rw-r--r--drivers/platform/x86/mlx-platform.c105
-rw-r--r--drivers/platform/x86/pcengines-apuv2.c260
-rw-r--r--drivers/platform/x86/touchscreen_dmi.c79
-rw-r--r--drivers/platform/x86/wmi-bmof.c2
-rw-r--r--drivers/platform/x86/wmi.c5
-rw-r--r--drivers/power/reset/at91-reset.c13
-rw-r--r--drivers/power/supply/axp288_fuel_gauge.c14
-rw-r--r--drivers/power/supply/bq25890_charger.c4
-rw-r--r--drivers/power/supply/bq27xxx_battery.c20
-rw-r--r--drivers/power/supply/charger-manager.c18
-rw-r--r--drivers/power/supply/cpcap-charger.c1
-rw-r--r--drivers/power/supply/ds2782_battery.c8
-rw-r--r--drivers/power/supply/goldfish_battery.c88
-rw-r--r--drivers/power/supply/isp1704_charger.c60
-rw-r--r--drivers/power/supply/max17042_battery.c10
-rw-r--r--drivers/power/supply/power_supply_core.c5
-rw-r--r--drivers/power/supply/sc27xx_fuel_gauge.c95
-rw-r--r--drivers/power/supply/twl4030_charger.c4
-rw-r--r--drivers/pwm/Kconfig17
-rw-r--r--drivers/pwm/Makefile3
-rw-r--r--drivers/pwm/core.c10
-rw-r--r--drivers/pwm/pwm-atmel.c111
-rw-r--r--drivers/pwm/pwm-bcm-kona.c16
-rw-r--r--drivers/pwm/pwm-hibvt.c44
-rw-r--r--drivers/pwm/pwm-imx1.c199
-rw-r--r--drivers/pwm/pwm-imx27.c (renamed from drivers/pwm/pwm-imx.c)224
-rw-r--r--drivers/pwm/pwm-mtk-disp.c11
-rw-r--r--drivers/pwm/pwm-rcar.c99
-rw-r--r--drivers/ras/ras.c2
-rw-r--r--drivers/remoteproc/qcom_q6v5_adsp.c6
-rw-r--r--drivers/remoteproc/qcom_q6v5_mss.c209
-rw-r--r--drivers/remoteproc/qcom_q6v5_pas.c13
-rw-r--r--drivers/remoteproc/qcom_sysmon.c82
-rw-r--r--drivers/remoteproc/qcom_wcnss.c6
-rw-r--r--drivers/remoteproc/remoteproc_core.c160
-rw-r--r--drivers/remoteproc/remoteproc_debugfs.c47
-rw-r--r--drivers/remoteproc/remoteproc_internal.h12
-rw-r--r--drivers/remoteproc/remoteproc_virtio.c61
-rw-r--r--drivers/remoteproc/st_remoteproc.c91
-rw-r--r--drivers/rpmsg/virtio_rpmsg_bus.c24
-rw-r--r--drivers/rtc/Kconfig69
-rw-r--r--drivers/rtc/Makefile6
-rw-r--r--drivers/rtc/dev.c5
-rw-r--r--drivers/rtc/lib.c6
-rw-r--r--drivers/rtc/rtc-88pm80x.c21
-rw-r--r--drivers/rtc/rtc-88pm860x.c21
-rw-r--r--drivers/rtc/rtc-ab-eoz9.c465
-rw-r--r--drivers/rtc/rtc-abx80x.c71
-rw-r--r--drivers/rtc/rtc-cadence.c423
-rw-r--r--drivers/rtc/rtc-coh901331.c6
-rw-r--r--drivers/rtc/rtc-ds1307.c707
-rw-r--r--drivers/rtc/rtc-ds1672.c3
-rw-r--r--drivers/rtc/rtc-hym8563.c2
-rw-r--r--drivers/rtc/rtc-imx-sc.c21
-rw-r--r--drivers/rtc/rtc-isl1208.c167
-rw-r--r--drivers/rtc/rtc-mc146818-lib.c2
-rw-r--r--drivers/rtc/rtc-meson.c407
-rw-r--r--drivers/rtc/rtc-pcf85063.c46
-rw-r--r--drivers/rtc/rtc-pcf8523.c29
-rw-r--r--drivers/rtc/rtc-pic32.c34
-rw-r--r--drivers/rtc/rtc-pm8xxx.c6
-rw-r--r--drivers/rtc/rtc-rs5c372.c76
-rw-r--r--drivers/rtc/rtc-rv3028.c732
-rw-r--r--drivers/rtc/rtc-rv8803.c15
-rw-r--r--drivers/rtc/rtc-rx8581.c114
-rw-r--r--drivers/rtc/rtc-s3c.c87
-rw-r--r--drivers/rtc/rtc-sd3078.c231
-rw-r--r--drivers/rtc/rtc-snvs.c6
-rw-r--r--drivers/rtc/rtc-tx4939.c26
-rw-r--r--drivers/rtc/rtc-wilco-ec.c177
-rw-r--r--drivers/rtc/rtc-zynqmp.c18
-rw-r--r--drivers/s390/cio/chsc.c50
-rw-r--r--drivers/s390/cio/chsc.h1
-rw-r--r--drivers/s390/cio/vfio_ccw_drv.c8
-rw-r--r--drivers/s390/crypto/ap_bus.c19
-rw-r--r--drivers/s390/crypto/ap_bus.h2
-rw-r--r--drivers/s390/crypto/ap_queue.c26
-rw-r--r--drivers/s390/crypto/zcrypt_api.c30
-rw-r--r--drivers/s390/net/qeth_core_main.c5
-rw-r--r--drivers/s390/net/qeth_l2_main.c7
-rw-r--r--drivers/s390/net/qeth_l3_main.c8
-rw-r--r--drivers/s390/scsi/zfcp_erp.c17
-rw-r--r--drivers/s390/scsi/zfcp_ext.h2
-rw-r--r--drivers/s390/scsi/zfcp_fc.c21
-rw-r--r--drivers/s390/scsi/zfcp_scsi.c4
-rw-r--r--drivers/s390/virtio/virtio_ccw.c12
-rw-r--r--drivers/scsi/Kconfig6
-rw-r--r--drivers/scsi/Makefile1
-rw-r--r--drivers/scsi/aacraid/Makefile2
-rw-r--r--drivers/scsi/aacraid/aachba.c2
-rw-r--r--drivers/scsi/aacraid/aacraid.h11
-rw-r--r--drivers/scsi/aacraid/commctrl.c2
-rw-r--r--drivers/scsi/aacraid/commsup.c34
-rw-r--r--drivers/scsi/aacraid/linit.c22
-rw-r--r--drivers/scsi/aacraid/src.c2
-rw-r--r--drivers/scsi/aic7xxx/Makefile1
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_core.c14
-rw-r--r--drivers/scsi/arcmsr/arcmsr.h13
-rw-r--r--drivers/scsi/arcmsr/arcmsr_hba.c348
-rw-r--r--drivers/scsi/bfa/bfa_fcs_lport.c8
-rw-r--r--drivers/scsi/bfa/bfa_fcs_rport.c19
-rw-r--r--drivers/scsi/bfa/bfa_ioc.c9
-rw-r--r--drivers/scsi/bfa/bfad_debugfs.c18
-rw-r--r--drivers/scsi/bnx2fc/bnx2fc_fcoe.c2
-rw-r--r--drivers/scsi/bnx2i/bnx2i_iscsi.c2
-rw-r--r--drivers/scsi/csiostor/csio_attr.c3
-rw-r--r--drivers/scsi/csiostor/csio_init.c6
-rw-r--r--drivers/scsi/csiostor/csio_scsi.c8
-rw-r--r--drivers/scsi/cxgbi/Makefile2
-rw-r--r--drivers/scsi/cxgbi/cxgb4i/cxgb4i.c6
-rw-r--r--drivers/scsi/cxgbi/libcxgbi.c13
-rw-r--r--drivers/scsi/cxlflash/common.h3
-rw-r--r--drivers/scsi/cxlflash/main.c2
-rw-r--r--drivers/scsi/cxlflash/superpipe.c12
-rw-r--r--drivers/scsi/dpt_i2o.c73
-rw-r--r--drivers/scsi/esas2r/esas2r.h4
-rw-r--r--drivers/scsi/esas2r/esas2r_init.c3
-rw-r--r--drivers/scsi/esas2r/esas2r_ioctl.c16
-rw-r--r--drivers/scsi/esas2r/esas2r_main.c2
-rw-r--r--drivers/scsi/fcoe/fcoe.c2
-rw-r--r--drivers/scsi/fcoe/fcoe_ctlr.c7
-rw-r--r--drivers/scsi/fcoe/fcoe_sysfs.c15
-rw-r--r--drivers/scsi/fcoe/fcoe_transport.c3
-rw-r--r--drivers/scsi/fnic/fnic.h6
-rw-r--r--drivers/scsi/fnic/fnic_debugfs.c88
-rw-r--r--drivers/scsi/fnic/fnic_fcs.c10
-rw-r--r--drivers/scsi/fnic/fnic_io.h3
-rw-r--r--drivers/scsi/fnic/fnic_isr.c6
-rw-r--r--drivers/scsi/fnic/fnic_main.c37
-rw-r--r--drivers/scsi/fnic/fnic_scsi.c40
-rw-r--r--drivers/scsi/fnic/fnic_stats.h6
-rw-r--r--drivers/scsi/fnic/fnic_trace.c28
-rw-r--r--drivers/scsi/fnic/fnic_trace.h4
-rw-r--r--drivers/scsi/fnic/vnic_dev.c270
-rw-r--r--drivers/scsi/fnic/vnic_dev.h2
-rw-r--r--drivers/scsi/fnic/vnic_devcmd.h160
-rw-r--r--drivers/scsi/fnic/vnic_resource.h7
-rw-r--r--drivers/scsi/fnic/vnic_rq.c5
-rw-r--r--drivers/scsi/fnic/vnic_wq.c69
-rw-r--r--drivers/scsi/fnic/vnic_wq.h8
-rw-r--r--drivers/scsi/gdth.c1286
-rw-r--r--drivers/scsi/gdth.h30
-rw-r--r--drivers/scsi/gdth_ioctl.h89
-rw-r--r--drivers/scsi/gdth_proc.c113
-rw-r--r--drivers/scsi/gdth_proc.h3
-rw-r--r--drivers/scsi/hisi_sas/hisi_sas.h110
-rw-r--r--drivers/scsi/hisi_sas/hisi_sas_main.c901
-rw-r--r--drivers/scsi/hisi_sas/hisi_sas_v1_hw.c6
-rw-r--r--drivers/scsi/hisi_sas/hisi_sas_v2_hw.c26
-rw-r--r--drivers/scsi/hisi_sas/hisi_sas_v3_hw.c468
-rw-r--r--drivers/scsi/hpsa.c19
-rw-r--r--drivers/scsi/ibmvscsi/ibmvfc.c39
-rw-r--r--drivers/scsi/ibmvscsi/ibmvfc.h7
-rw-r--r--drivers/scsi/ibmvscsi/ibmvscsi.c23
-rw-r--r--drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c6
-rw-r--r--drivers/scsi/ipr.c3
-rw-r--r--drivers/scsi/iscsi_tcp.c9
-rw-r--r--drivers/scsi/libiscsi.c86
-rw-r--r--drivers/scsi/libiscsi_tcp.c19
-rw-r--r--drivers/scsi/libsas/sas_expander.c38
-rw-r--r--drivers/scsi/libsas/sas_scsi_host.c2
-rw-r--r--drivers/scsi/lpfc/lpfc.h97
-rw-r--r--drivers/scsi/lpfc/lpfc_attr.c469
-rw-r--r--drivers/scsi/lpfc/lpfc_bsg.c4
-rw-r--r--drivers/scsi/lpfc/lpfc_crtn.h36
-rw-r--r--drivers/scsi/lpfc/lpfc_ct.c18
-rw-r--r--drivers/scsi/lpfc/lpfc_debugfs.c1207
-rw-r--r--drivers/scsi/lpfc/lpfc_debugfs.h73
-rw-r--r--drivers/scsi/lpfc/lpfc_els.c6
-rw-r--r--drivers/scsi/lpfc/lpfc_hbadisc.c40
-rw-r--r--drivers/scsi/lpfc/lpfc_hw4.h16
-rw-r--r--drivers/scsi/lpfc/lpfc_init.c2291
-rw-r--r--drivers/scsi/lpfc/lpfc_mbox.c4
-rw-r--r--drivers/scsi/lpfc/lpfc_nportdisc.c10
-rw-r--r--drivers/scsi/lpfc/lpfc_nvme.c745
-rw-r--r--drivers/scsi/lpfc/lpfc_nvme.h66
-rw-r--r--drivers/scsi/lpfc/lpfc_nvmet.c448
-rw-r--r--drivers/scsi/lpfc/lpfc_nvmet.h4
-rw-r--r--drivers/scsi/lpfc/lpfc_scsi.c894
-rw-r--r--drivers/scsi/lpfc/lpfc_scsi.h63
-rw-r--r--drivers/scsi/lpfc/lpfc_sli.c2293
-rw-r--r--drivers/scsi/lpfc/lpfc_sli.h89
-rw-r--r--drivers/scsi/lpfc/lpfc_sli4.h304
-rw-r--r--drivers/scsi/lpfc/lpfc_version.h4
-rw-r--r--drivers/scsi/lpfc/lpfc_vport.c27
-rw-r--r--drivers/scsi/megaraid/megaraid_sas.h54
-rw-r--r--drivers/scsi/megaraid/megaraid_sas_base.c408
-rw-r--r--drivers/scsi/megaraid/megaraid_sas_fusion.c27
-rw-r--r--drivers/scsi/megaraid/megaraid_sas_fusion.h1
-rw-r--r--drivers/scsi/mpt3sas/mpi/mpi2_cnfg.h3
-rw-r--r--drivers/scsi/mpt3sas/mpt3sas_base.c10
-rw-r--r--drivers/scsi/mpt3sas/mpt3sas_base.h7
-rw-r--r--drivers/scsi/mpt3sas/mpt3sas_scsih.c28
-rw-r--r--drivers/scsi/mvumi.c5
-rw-r--r--drivers/scsi/nsp32.c1
-rw-r--r--drivers/scsi/osd/Kbuild20
-rw-r--r--drivers/scsi/osd/Kconfig49
-rw-r--r--drivers/scsi/osd/osd_debug.h30
-rw-r--r--drivers/scsi/osd/osd_initiator.c2076
-rw-r--r--drivers/scsi/osd/osd_uld.c571
-rw-r--r--drivers/scsi/osst.c2
-rw-r--r--drivers/scsi/pcmcia/Makefile2
-rw-r--r--drivers/scsi/pcmcia/nsp_cs.c3
-rw-r--r--drivers/scsi/qedf/qedf_debugfs.c18
-rw-r--r--drivers/scsi/qedf/qedf_io.c6
-rw-r--r--drivers/scsi/qedf/qedf_main.c2
-rw-r--r--drivers/scsi/qedi/qedi_debugfs.c17
-rw-r--r--drivers/scsi/qedi/qedi_fw.c7
-rw-r--r--drivers/scsi/qla2xxx/qla_attr.c115
-rw-r--r--drivers/scsi/qla2xxx/qla_def.h43
-rw-r--r--drivers/scsi/qla2xxx/qla_dfs.c45
-rw-r--r--drivers/scsi/qla2xxx/qla_gbl.h8
-rw-r--r--drivers/scsi/qla2xxx/qla_gs.c77
-rw-r--r--drivers/scsi/qla2xxx/qla_init.c199
-rw-r--r--drivers/scsi/qla2xxx/qla_iocb.c395
-rw-r--r--drivers/scsi/qla2xxx/qla_isr.c25
-rw-r--r--drivers/scsi/qla2xxx/qla_mbx.c96
-rw-r--r--drivers/scsi/qla2xxx/qla_nvme.c47
-rw-r--r--drivers/scsi/qla2xxx/qla_nvme.h2
-rw-r--r--drivers/scsi/qla2xxx/qla_os.c279
-rw-r--r--drivers/scsi/qla2xxx/qla_target.c28
-rw-r--r--drivers/scsi/qla2xxx/qla_target.h2
-rw-r--r--drivers/scsi/qla2xxx/qla_tmpl.c237
-rw-r--r--drivers/scsi/qla2xxx/qla_tmpl.h26
-rw-r--r--drivers/scsi/qla2xxx/qla_version.h2
-rw-r--r--drivers/scsi/qla2xxx/tcm_qla2xxx.c31
-rw-r--r--drivers/scsi/qla4xxx/ql4_os.c4
-rw-r--r--drivers/scsi/qlogicpti.c3
-rw-r--r--drivers/scsi/qlogicpti.h3
-rw-r--r--drivers/scsi/scsi.c16
-rw-r--r--drivers/scsi/scsi_debug.c186
-rw-r--r--drivers/scsi/scsi_error.c3
-rw-r--r--drivers/scsi/scsi_lib.c84
-rw-r--r--drivers/scsi/scsi_scan.c6
-rw-r--r--drivers/scsi/scsi_sysfs.c6
-rw-r--r--drivers/scsi/scsi_transport_iscsi.c2
-rw-r--r--drivers/scsi/scsi_transport_sas.c1
-rw-r--r--drivers/scsi/sd.c548
-rw-r--r--drivers/scsi/sd.h64
-rw-r--r--drivers/scsi/smartpqi/Makefile1
-rw-r--r--drivers/scsi/smartpqi/smartpqi_init.c9
-rw-r--r--drivers/scsi/snic/snic_debugfs.c133
-rw-r--r--drivers/scsi/snic/snic_main.c14
-rw-r--r--drivers/scsi/snic/snic_stats.h2
-rw-r--r--drivers/scsi/snic/snic_trc.c12
-rw-r--r--drivers/scsi/snic/snic_trc.h4
-rw-r--r--drivers/scsi/sr.c1
-rw-r--r--drivers/scsi/st.c6
-rw-r--r--drivers/scsi/ufs/Kconfig1
-rw-r--r--drivers/scsi/ufs/ufs-hisi.c124
-rw-r--r--drivers/scsi/ufs/ufs-hisi.h4
-rw-r--r--drivers/scsi/ufs/ufs.h1
-rw-r--r--drivers/scsi/ufs/ufs_bsg.c63
-rw-r--r--drivers/scsi/ufs/ufs_quirks.h29
-rw-r--r--drivers/scsi/ufs/ufshcd-dwc.c4
-rw-r--r--drivers/scsi/ufs/ufshcd-pltfrm.c2
-rw-r--r--drivers/scsi/ufs/ufshcd-pltfrm.h2
-rw-r--r--drivers/scsi/ufs/ufshcd.c90
-rw-r--r--drivers/scsi/ufs/ufshcd.h2
-rw-r--r--drivers/scsi/virtio_scsi.c16
-rw-r--r--drivers/soc/bcm/bcm2835-power.c49
-rw-r--r--drivers/spi/spi-topcliff-pch.c1
-rw-r--r--drivers/spmi/Kconfig2
-rw-r--r--drivers/spmi/spmi-pmic-arb.c67
-rw-r--r--drivers/staging/Kconfig2
-rw-r--r--drivers/staging/Makefile1
-rw-r--r--drivers/staging/axis-fifo/Kconfig1
-rw-r--r--drivers/staging/comedi/comedidev.h2
-rw-r--r--drivers/staging/comedi/drivers.c33
-rw-r--r--drivers/staging/comedi/drivers/ni_mio_common.c10
-rw-r--r--drivers/staging/erofs/data.c3
-rw-r--r--drivers/staging/erofs/dir.c45
-rw-r--r--drivers/staging/erofs/unzip_vle.c48
-rw-r--r--drivers/staging/erofs/unzip_vle_lz4.c7
-rw-r--r--drivers/staging/media/Kconfig6
-rw-r--r--drivers/staging/media/Makefile3
-rw-r--r--drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.c2
-rw-r--r--drivers/staging/media/davinci_vpfe/dm365_isif.c4
-rw-r--r--drivers/staging/media/davinci_vpfe/dm365_resizer.c4
-rw-r--r--drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c2
-rw-r--r--drivers/staging/media/imx/Kconfig9
-rw-r--r--drivers/staging/media/imx/Makefile4
-rw-r--r--drivers/staging/media/imx/TODO9
-rw-r--r--drivers/staging/media/imx/imx-ic-common.c6
-rw-r--r--drivers/staging/media/imx/imx-ic-prp.c25
-rw-r--r--drivers/staging/media/imx/imx-ic-prpencvf.c91
-rw-r--r--drivers/staging/media/imx/imx-media-capture.c119
-rw-r--r--drivers/staging/media/imx/imx-media-csi.c230
-rw-r--r--drivers/staging/media/imx/imx-media-dev-common.c90
-rw-r--r--drivers/staging/media/imx/imx-media-dev.c122
-rw-r--r--drivers/staging/media/imx/imx-media-internal-sd.c20
-rw-r--r--drivers/staging/media/imx/imx-media-of.c6
-rw-r--r--drivers/staging/media/imx/imx-media-utils.c47
-rw-r--r--drivers/staging/media/imx/imx-media-vdic.c21
-rw-r--r--drivers/staging/media/imx/imx-media.h45
-rw-r--r--drivers/staging/media/imx/imx7-media-csi.c1369
-rw-r--r--drivers/staging/media/imx/imx7-mipi-csis.c1160
-rw-r--r--drivers/staging/media/imx074/Kconfig5
-rw-r--r--drivers/staging/media/imx074/Makefile1
-rw-r--r--drivers/staging/media/imx074/TODO5
-rw-r--r--drivers/staging/media/ipu3/Makefile6
-rw-r--r--drivers/staging/media/ipu3/TODO7
-rw-r--r--drivers/staging/media/ipu3/include/intel-ipu3.h10
-rw-r--r--drivers/staging/media/ipu3/ipu3-abi.h2
-rw-r--r--drivers/staging/media/ipu3/ipu3-css-fw.c18
-rw-r--r--drivers/staging/media/ipu3/ipu3-css-fw.h8
-rw-r--r--drivers/staging/media/ipu3/ipu3-css-params.c271
-rw-r--r--drivers/staging/media/ipu3/ipu3-css-params.h8
-rw-r--r--drivers/staging/media/ipu3/ipu3-css-pool.c32
-rw-r--r--drivers/staging/media/ipu3/ipu3-css-pool.h30
-rw-r--r--drivers/staging/media/ipu3/ipu3-css.c460
-rw-r--r--drivers/staging/media/ipu3/ipu3-css.h92
-rw-r--r--drivers/staging/media/ipu3/ipu3-dmamap.c43
-rw-r--r--drivers/staging/media/ipu3/ipu3-dmamap.h14
-rw-r--r--drivers/staging/media/ipu3/ipu3-mmu.c125
-rw-r--r--drivers/staging/media/ipu3/ipu3-mmu.h18
-rw-r--r--drivers/staging/media/ipu3/ipu3-tables.c50
-rw-r--r--drivers/staging/media/ipu3/ipu3-tables.h54
-rw-r--r--drivers/staging/media/ipu3/ipu3-v4l2.c299
-rw-r--r--drivers/staging/media/ipu3/ipu3.c97
-rw-r--r--drivers/staging/media/ipu3/ipu3.h20
-rw-r--r--drivers/staging/media/omap4iss/iss_csi2.c2
-rw-r--r--drivers/staging/media/rockchip/vpu/rk3288_vpu_hw_jpeg_enc.c6
-rw-r--r--drivers/staging/media/rockchip/vpu/rk3399_vpu_hw_jpeg_enc.c6
-rw-r--r--drivers/staging/media/soc_camera/Kconfig (renamed from drivers/media/i2c/soc_camera/Kconfig)46
-rw-r--r--drivers/staging/media/soc_camera/Makefile7
-rw-r--r--drivers/staging/media/soc_camera/imx074.c (renamed from drivers/staging/media/imx074/imx074.c)0
-rw-r--r--drivers/staging/media/soc_camera/mt9t031.c (renamed from drivers/staging/media/mt9t031/mt9t031.c)0
-rw-r--r--drivers/staging/media/soc_camera/soc_camera.c (renamed from drivers/media/platform/soc_camera/soc_camera.c)4
-rw-r--r--drivers/staging/media/soc_camera/soc_mediabus.c (renamed from drivers/media/platform/soc_camera/soc_mediabus.c)0
-rw-r--r--drivers/staging/media/soc_camera/soc_mt9v022.c (renamed from drivers/media/i2c/soc_camera/soc_mt9v022.c)0
-rw-r--r--drivers/staging/media/soc_camera/soc_ov5642.c (renamed from drivers/media/i2c/soc_camera/soc_ov5642.c)0
-rw-r--r--drivers/staging/media/soc_camera/soc_ov9740.c (renamed from drivers/media/i2c/soc_camera/soc_ov9740.c)0
-rw-r--r--drivers/staging/media/sunxi/cedrus/TODO5
-rw-r--r--drivers/staging/media/sunxi/cedrus/cedrus.h9
-rw-r--r--drivers/staging/media/sunxi/cedrus/cedrus_dec.c2
-rw-r--r--drivers/staging/media/sunxi/cedrus/cedrus_dec.h6
-rw-r--r--drivers/staging/media/sunxi/cedrus/cedrus_hw.c28
-rw-r--r--drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c23
-rw-r--r--drivers/staging/media/sunxi/cedrus/cedrus_video.c19
-rw-r--r--drivers/staging/media/zoran/zoran.h2
-rw-r--r--drivers/staging/media/zoran/zoran_card.c2
-rw-r--r--drivers/staging/media/zoran/zoran_device.c6
-rw-r--r--drivers/staging/media/zoran/zoran_driver.c4
-rw-r--r--drivers/staging/mt7621-dts/gbpc1.dts29
-rw-r--r--drivers/staging/mt7621-dts/mt7621.dtsi73
-rw-r--r--drivers/staging/mt7621-eth/Documentation/devicetree/bindings/net/mediatek-net-gsw.txt48
-rw-r--r--drivers/staging/mt7621-eth/Kconfig39
-rw-r--r--drivers/staging/mt7621-eth/Makefile14
-rw-r--r--drivers/staging/mt7621-eth/TODO13
-rw-r--r--drivers/staging/mt7621-eth/ethtool.c250
-rw-r--r--drivers/staging/mt7621-eth/ethtool.h15
-rw-r--r--drivers/staging/mt7621-eth/gsw_mt7620.h277
-rw-r--r--drivers/staging/mt7621-eth/gsw_mt7621.c297
-rw-r--r--drivers/staging/mt7621-eth/mdio.c275
-rw-r--r--drivers/staging/mt7621-eth/mdio.h27
-rw-r--r--drivers/staging/mt7621-eth/mdio_mt7620.c173
-rw-r--r--drivers/staging/mt7621-eth/mtk_eth_soc.c2176
-rw-r--r--drivers/staging/mt7621-eth/mtk_eth_soc.h716
-rw-r--r--drivers/staging/mt7621-eth/soc_mt7621.c161
-rw-r--r--drivers/staging/mt7621-pci/Kconfig1
-rw-r--r--drivers/staging/octeon/ethernet-mdio.c2
-rw-r--r--drivers/staging/octeon/ethernet.c40
-rw-r--r--drivers/staging/octeon/octeon-ethernet.h4
-rw-r--r--drivers/staging/olpc_dcon/olpc_dcon_xo_1.c2
-rw-r--r--drivers/staging/rtl8188eu/core/rtw_xmit.c9
-rw-r--r--drivers/staging/rtl8188eu/include/rtw_xmit.h2
-rw-r--r--drivers/staging/rtl8712/rtl8712_cmd.c10
-rw-r--r--drivers/staging/rtl8712/rtl8712_cmd.h2
-rw-r--r--drivers/staging/rtl8723bs/core/rtw_xmit.c14
-rw-r--r--drivers/staging/rtl8723bs/include/rtw_xmit.h2
-rw-r--r--drivers/staging/rtlwifi/phydm/rtl_phydm.c2
-rw-r--r--drivers/staging/rtlwifi/rtl8822be/fw.c2
-rw-r--r--drivers/staging/speakup/speakup_soft.c16
-rw-r--r--drivers/staging/speakup/spk_priv.h1
-rw-r--r--drivers/staging/speakup/synth.c6
-rw-r--r--drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c8
-rw-r--r--drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c8
-rw-r--r--drivers/staging/vt6655/device_main.c11
-rw-r--r--drivers/target/iscsi/cxgbit/cxgbit.h2
-rw-r--r--drivers/target/iscsi/cxgbit/cxgbit_ddp.c2
-rw-r--r--drivers/target/iscsi/cxgbit/cxgbit_main.c2
-rw-r--r--drivers/target/iscsi/cxgbit/cxgbit_target.c2
-rw-r--r--drivers/target/iscsi/iscsi_target.c47
-rw-r--r--drivers/target/iscsi/iscsi_target.h2
-rw-r--r--drivers/target/iscsi/iscsi_target_configfs.c13
-rw-r--r--drivers/target/iscsi/iscsi_target_erl0.c5
-rw-r--r--drivers/target/iscsi/iscsi_target_erl1.c59
-rw-r--r--drivers/target/iscsi/iscsi_target_util.c23
-rw-r--r--drivers/target/loopback/tcm_loop.c21
-rw-r--r--drivers/target/sbp/sbp_target.c6
-rw-r--r--drivers/target/target_core_alua.c5
-rw-r--r--drivers/target/target_core_configfs.c4
-rw-r--r--drivers/target/target_core_device.c6
-rw-r--r--drivers/target/target_core_pr.c15
-rw-r--r--drivers/target/target_core_tmr.c39
-rw-r--r--drivers/target/target_core_transport.c49
-rw-r--r--drivers/target/target_core_user.c19
-rw-r--r--drivers/target/target_core_xcopy.c6
-rw-r--r--drivers/target/tcm_fc/tcm_fc.h1
-rw-r--r--drivers/target/tcm_fc/tfc_cmd.c7
-rw-r--r--drivers/target/tcm_fc/tfc_conf.c1
-rw-r--r--drivers/thermal/Kconfig3
-rw-r--r--drivers/thermal/broadcom/Kconfig9
-rw-r--r--drivers/thermal/broadcom/Makefile1
-rw-r--r--drivers/thermal/broadcom/bcm2835_thermal.c9
-rw-r--r--drivers/thermal/broadcom/sr-thermal.c121
-rw-r--r--drivers/thermal/cpu_cooling.c3
-rw-r--r--drivers/thermal/intel/int340x_thermal/int3400_thermal.c21
-rw-r--r--drivers/thermal/intel/intel_powerclamp.c4
-rw-r--r--drivers/thermal/mtk_thermal.c323
-rw-r--r--drivers/thermal/qcom/tsens-common.c33
-rw-r--r--drivers/thermal/qoriq_thermal.c104
-rw-r--r--drivers/thermal/rcar_gen3_thermal.c19
-rw-r--r--drivers/thermal/samsung/exynos_tmu.c2
-rw-r--r--drivers/thermal/tegra/soctherm.c38
-rw-r--r--drivers/tty/serial/8250/8250_lpss.c1
-rw-r--r--drivers/tty/serial/ar933x_uart.c24
-rw-r--r--drivers/tty/serial/atmel_serial.c52
-rw-r--r--drivers/tty/serial/kgdboc.c4
-rw-r--r--drivers/tty/serial/max310x.c2
-rw-r--r--drivers/tty/serial/mvebu-uart.c3
-rw-r--r--drivers/tty/serial/mxs-auart.c4
-rw-r--r--drivers/tty/serial/pch_uart.c2
-rw-r--r--drivers/tty/serial/qcom_geni_serial.c2
-rw-r--r--drivers/tty/serial/sc16is7xx.c12
-rw-r--r--drivers/tty/serial/sh-sci.c12
-rw-r--r--drivers/tty/tty_port.c10
-rw-r--r--drivers/usb/class/cdc-acm.c4
-rw-r--r--drivers/usb/common/common.c2
-rw-r--r--drivers/usb/core/hcd.c3
-rw-r--r--drivers/usb/dwc3/dwc3-pci.c4
-rw-r--r--drivers/usb/early/xhci-dbc.c2
-rw-r--r--drivers/usb/gadget/function/f_hid.c6
-rw-r--r--drivers/usb/gadget/function/f_tcm.c9
-rw-r--r--drivers/usb/gadget/udc/net2272.c1
-rw-r--r--drivers/usb/gadget/udc/net2280.c8
-rw-r--r--drivers/usb/gadget/udc/pch_udc.c1
-rw-r--r--drivers/usb/host/ohci-sm501.c3
-rw-r--r--drivers/usb/host/ohci-tmio.c2
-rw-r--r--drivers/usb/host/u132-hcd.c3
-rw-r--r--drivers/usb/host/xhci-dbgcap.c5
-rw-r--r--drivers/usb/host/xhci-hub.c19
-rw-r--r--drivers/usb/host/xhci-rcar.c1
-rw-r--r--drivers/usb/host/xhci-ring.c9
-rw-r--r--drivers/usb/host/xhci.h8
-rw-r--r--drivers/usb/misc/usb251xb.c4
-rw-r--r--drivers/usb/mtu3/Kconfig1
-rw-r--r--drivers/usb/serial/cp210x.c1
-rw-r--r--drivers/usb/serial/ftdi_sio.c2
-rw-r--r--drivers/usb/serial/ftdi_sio_ids.h4
-rw-r--r--drivers/usb/serial/mos7720.c4
-rw-r--r--drivers/usb/serial/option.c17
-rw-r--r--drivers/usb/storage/uas.c15
-rw-r--r--drivers/usb/typec/tcpm/tcpm.c27
-rw-r--r--drivers/usb/typec/tcpm/wcove.c9
-rw-r--r--drivers/vhost/scsi.c6
-rw-r--r--drivers/vhost/vhost.c2
-rw-r--r--drivers/video/backlight/pwm_bl.c2
-rw-r--r--drivers/video/fbdev/aty/radeon_pm.c6
-rw-r--r--drivers/video/fbdev/cg14.c4
-rw-r--r--drivers/video/fbdev/cg3.c2
-rw-r--r--drivers/video/fbdev/chipsfb.c3
-rw-r--r--drivers/video/fbdev/core/fb_cmdline.c23
-rw-r--r--drivers/video/fbdev/core/fbcon.c14
-rw-r--r--drivers/video/fbdev/core/fbmem.c3
-rw-r--r--drivers/video/fbdev/core/fbmon.c2
-rw-r--r--drivers/video/fbdev/ffb.c2
-rw-r--r--drivers/video/fbdev/geode/gxfb_core.c13
-rw-r--r--drivers/video/fbdev/geode/lxfb_core.c13
-rw-r--r--drivers/video/fbdev/imsttfb.c4
-rw-r--r--drivers/video/fbdev/mbx/mbxdebugfs.c40
-rw-r--r--drivers/video/fbdev/mbx/mbxfb.c2
-rw-r--r--drivers/video/fbdev/offb.c4
-rw-r--r--drivers/video/fbdev/omap2/omapfb/dss/core.c34
-rw-r--r--drivers/video/fbdev/omap2/omapfb/dss/dss-of.c4
-rw-r--r--drivers/video/fbdev/omap2/omapfb/dss/dss.h2
-rw-r--r--drivers/video/fbdev/omap2/omapfb/dss/hdmi4_core.c2
-rw-r--r--drivers/video/fbdev/ssd1307fb.c4
-rw-r--r--drivers/video/fbdev/via/viafbdev.c2
-rw-r--r--drivers/virt/vboxguest/vboxguest_core.c106
-rw-r--r--drivers/virt/vboxguest/vboxguest_core.h15
-rw-r--r--drivers/virt/vboxguest/vboxguest_linux.c26
-rw-r--r--drivers/virt/vboxguest/vboxguest_utils.c32
-rw-r--r--drivers/virt/vboxguest/vboxguest_version.h9
-rw-r--r--drivers/virt/vboxguest/vmmdev.h8
-rw-r--r--drivers/virtio/virtio.c2
-rw-r--r--drivers/virtio/virtio_balloon.c6
-rw-r--r--drivers/virtio/virtio_ring.c11
-rw-r--r--drivers/watchdog/Kconfig37
-rw-r--r--drivers/watchdog/Makefile2
-rw-r--r--drivers/watchdog/dw_wdt.c2
-rw-r--r--drivers/watchdog/mlx_wdt.c290
-rw-r--r--drivers/watchdog/pc87413_wdt.c2
-rw-r--r--drivers/watchdog/pika_wdt.c2
-rw-r--r--drivers/watchdog/qcom-wdt.c23
-rw-r--r--drivers/watchdog/sbc60xxwdt.c2
-rw-r--r--drivers/watchdog/sc1200wdt.c2
-rw-r--r--drivers/watchdog/sc520_wdt.c2
-rw-r--r--drivers/watchdog/smsc37b787_wdt.c2
-rw-r--r--drivers/watchdog/stpmic1_wdt.c139
-rw-r--r--drivers/watchdog/w83877f_wdt.c2
-rw-r--r--drivers/xen/Makefile1
-rw-r--r--drivers/xen/balloon.c5
-rw-r--r--drivers/xen/cpu_hotplug.c2
-rw-r--r--drivers/xen/fallback.c81
-rw-r--r--drivers/xen/gntdev-dmabuf.c21
-rw-r--r--drivers/xen/gntdev-dmabuf.h2
-rw-r--r--drivers/xen/gntdev.c2
-rw-r--r--drivers/xen/swiotlb-xen.c7
-rw-r--r--drivers/xen/xen-acpi-processor.c22
-rw-r--r--drivers/xen/xen-balloon.c11
-rw-r--r--drivers/xen/xen-pciback/pciback_ops.c2
-rw-r--r--drivers/xen/xen-pciback/xenbus.c2
-rw-r--r--drivers/xen/xen-scsiback.c8
2121 files changed, 81443 insertions, 46326 deletions
diff --git a/drivers/Makefile b/drivers/Makefile
index bb15b9d0e793..c61cde554340 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -56,7 +56,7 @@ obj-y += tty/
obj-y += char/
# iommu/ comes before gpu as gpu are using iommu controllers
-obj-$(CONFIG_IOMMU_SUPPORT) += iommu/
+obj-y += iommu/
# gpu/ comes after char for AGP vs DRM startup and after iommu
obj-y += gpu/
diff --git a/drivers/acpi/acpi_configfs.c b/drivers/acpi/acpi_configfs.c
index b58850389094..81bfc6197293 100644
--- a/drivers/acpi/acpi_configfs.c
+++ b/drivers/acpi/acpi_configfs.c
@@ -97,12 +97,12 @@ static ssize_t acpi_table_aml_read(struct config_item *cfg,
CONFIGFS_BIN_ATTR(acpi_table_, aml, NULL, MAX_ACPI_TABLE_SIZE);
-struct configfs_bin_attribute *acpi_table_bin_attrs[] = {
+static struct configfs_bin_attribute *acpi_table_bin_attrs[] = {
&acpi_table_attr_aml,
NULL,
};
-ssize_t acpi_table_signature_show(struct config_item *cfg, char *str)
+static ssize_t acpi_table_signature_show(struct config_item *cfg, char *str)
{
struct acpi_table_header *h = get_header(cfg);
@@ -112,7 +112,7 @@ ssize_t acpi_table_signature_show(struct config_item *cfg, char *str)
return sprintf(str, "%.*s\n", ACPI_NAME_SIZE, h->signature);
}
-ssize_t acpi_table_length_show(struct config_item *cfg, char *str)
+static ssize_t acpi_table_length_show(struct config_item *cfg, char *str)
{
struct acpi_table_header *h = get_header(cfg);
@@ -122,7 +122,7 @@ ssize_t acpi_table_length_show(struct config_item *cfg, char *str)
return sprintf(str, "%d\n", h->length);
}
-ssize_t acpi_table_revision_show(struct config_item *cfg, char *str)
+static ssize_t acpi_table_revision_show(struct config_item *cfg, char *str)
{
struct acpi_table_header *h = get_header(cfg);
@@ -132,7 +132,7 @@ ssize_t acpi_table_revision_show(struct config_item *cfg, char *str)
return sprintf(str, "%d\n", h->revision);
}
-ssize_t acpi_table_oem_id_show(struct config_item *cfg, char *str)
+static ssize_t acpi_table_oem_id_show(struct config_item *cfg, char *str)
{
struct acpi_table_header *h = get_header(cfg);
@@ -142,7 +142,7 @@ ssize_t acpi_table_oem_id_show(struct config_item *cfg, char *str)
return sprintf(str, "%.*s\n", ACPI_OEM_ID_SIZE, h->oem_id);
}
-ssize_t acpi_table_oem_table_id_show(struct config_item *cfg, char *str)
+static ssize_t acpi_table_oem_table_id_show(struct config_item *cfg, char *str)
{
struct acpi_table_header *h = get_header(cfg);
@@ -152,7 +152,7 @@ ssize_t acpi_table_oem_table_id_show(struct config_item *cfg, char *str)
return sprintf(str, "%.*s\n", ACPI_OEM_TABLE_ID_SIZE, h->oem_table_id);
}
-ssize_t acpi_table_oem_revision_show(struct config_item *cfg, char *str)
+static ssize_t acpi_table_oem_revision_show(struct config_item *cfg, char *str)
{
struct acpi_table_header *h = get_header(cfg);
@@ -162,7 +162,8 @@ ssize_t acpi_table_oem_revision_show(struct config_item *cfg, char *str)
return sprintf(str, "%d\n", h->oem_revision);
}
-ssize_t acpi_table_asl_compiler_id_show(struct config_item *cfg, char *str)
+static ssize_t acpi_table_asl_compiler_id_show(struct config_item *cfg,
+ char *str)
{
struct acpi_table_header *h = get_header(cfg);
@@ -172,8 +173,8 @@ ssize_t acpi_table_asl_compiler_id_show(struct config_item *cfg, char *str)
return sprintf(str, "%.*s\n", ACPI_NAME_SIZE, h->asl_compiler_id);
}
-ssize_t acpi_table_asl_compiler_revision_show(struct config_item *cfg,
- char *str)
+static ssize_t acpi_table_asl_compiler_revision_show(struct config_item *cfg,
+ char *str)
{
struct acpi_table_header *h = get_header(cfg);
@@ -192,7 +193,7 @@ CONFIGFS_ATTR_RO(acpi_table_, oem_revision);
CONFIGFS_ATTR_RO(acpi_table_, asl_compiler_id);
CONFIGFS_ATTR_RO(acpi_table_, asl_compiler_revision);
-struct configfs_attribute *acpi_table_attrs[] = {
+static struct configfs_attribute *acpi_table_attrs[] = {
&acpi_table_attr_signature,
&acpi_table_attr_length,
&acpi_table_attr_revision,
@@ -232,7 +233,7 @@ static void acpi_table_drop_item(struct config_group *group,
acpi_tb_unload_table(table->index);
}
-struct configfs_group_operations acpi_table_group_ops = {
+static struct configfs_group_operations acpi_table_group_ops = {
.make_item = acpi_table_make_item,
.drop_item = acpi_table_drop_item,
};
diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c
index 5f94c35d165f..1e2a10a06b9d 100644
--- a/drivers/acpi/acpi_lpss.c
+++ b/drivers/acpi/acpi_lpss.c
@@ -18,7 +18,7 @@
#include <linux/mutex.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
-#include <linux/platform_data/clk-lpss.h>
+#include <linux/platform_data/x86/clk-lpss.h>
#include <linux/platform_data/x86/pmc_atom.h>
#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
index 6ecbbabf1233..eec263c9019e 100644
--- a/drivers/acpi/bus.c
+++ b/drivers/acpi/bus.c
@@ -1043,9 +1043,6 @@ void __init acpi_early_init(void)
acpi_permanent_mmap = true;
- /* Initialize debug output. Linux does not use ACPICA defaults */
- acpi_dbg_level = ACPI_LV_INFO | ACPI_LV_REPAIR;
-
#ifdef CONFIG_X86
/*
* If the machine falls into the DMI check table,
diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index 1b207fca1420..d4244e7d0e38 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -1150,8 +1150,13 @@ int cppc_get_perf_caps(int cpunum, struct cppc_perf_caps *perf_caps)
cpc_read(cpunum, nominal_reg, &nom);
perf_caps->nominal_perf = nom;
- cpc_read(cpunum, guaranteed_reg, &guaranteed);
- perf_caps->guaranteed_perf = guaranteed;
+ if (guaranteed_reg->type != ACPI_TYPE_BUFFER ||
+ IS_NULL_REG(&guaranteed_reg->cpc_entry.reg)) {
+ perf_caps->guaranteed_perf = 0;
+ } else {
+ cpc_read(cpunum, guaranteed_reg, &guaranteed);
+ perf_caps->guaranteed_perf = guaranteed;
+ }
cpc_read(cpunum, lowest_non_linear_reg, &min_nonlinear);
perf_caps->lowest_nonlinear_perf = min_nonlinear;
diff --git a/drivers/acpi/device_sysfs.c b/drivers/acpi/device_sysfs.c
index 545e91420cde..8940054d6250 100644
--- a/drivers/acpi/device_sysfs.c
+++ b/drivers/acpi/device_sysfs.c
@@ -202,11 +202,15 @@ static int create_of_modalias(struct acpi_device *acpi_dev, char *modalias,
{
struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER };
const union acpi_object *of_compatible, *obj;
+ acpi_status status;
int len, count;
int i, nval;
char *c;
- acpi_get_name(acpi_dev->handle, ACPI_SINGLE_NAME, &buf);
+ status = acpi_get_name(acpi_dev->handle, ACPI_SINGLE_NAME, &buf);
+ if (ACPI_FAILURE(status))
+ return -ENODEV;
+
/* DT strings are all in lower case */
for (c = buf.pointer; *c != '\0'; c++)
*c = tolower(*c);
diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c
index e18ade5d74e9..5a389a4f4f65 100644
--- a/drivers/acpi/nfit/core.c
+++ b/drivers/acpi/nfit/core.c
@@ -55,6 +55,10 @@ static bool no_init_ars;
module_param(no_init_ars, bool, 0644);
MODULE_PARM_DESC(no_init_ars, "Skip ARS run at nfit init time");
+static bool force_labels;
+module_param(force_labels, bool, 0444);
+MODULE_PARM_DESC(force_labels, "Opt-in to labels despite missing methods");
+
LIST_HEAD(acpi_descs);
DEFINE_MUTEX(acpi_desc_lock);
@@ -415,7 +419,7 @@ static int cmd_to_func(struct nfit_mem *nfit_mem, unsigned int cmd,
if (call_pkg) {
int i;
- if (nfit_mem->family != call_pkg->nd_family)
+ if (nfit_mem && nfit_mem->family != call_pkg->nd_family)
return -ENOTTY;
for (i = 0; i < ARRAY_SIZE(call_pkg->nd_reserved2); i++)
@@ -424,6 +428,10 @@ static int cmd_to_func(struct nfit_mem *nfit_mem, unsigned int cmd,
return call_pkg->nd_command;
}
+ /* In the !call_pkg case, bus commands == bus functions */
+ if (!nfit_mem)
+ return cmd;
+
/* Linux ND commands == NVDIMM_FAMILY_INTEL function numbers */
if (nfit_mem->family == NVDIMM_FAMILY_INTEL)
return cmd;
@@ -454,17 +462,18 @@ int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm,
if (cmd_rc)
*cmd_rc = -EINVAL;
+ if (cmd == ND_CMD_CALL)
+ call_pkg = buf;
+ func = cmd_to_func(nfit_mem, cmd, call_pkg);
+ if (func < 0)
+ return func;
+
if (nvdimm) {
struct acpi_device *adev = nfit_mem->adev;
if (!adev)
return -ENOTTY;
- if (cmd == ND_CMD_CALL)
- call_pkg = buf;
- func = cmd_to_func(nfit_mem, cmd, call_pkg);
- if (func < 0)
- return func;
dimm_name = nvdimm_name(nvdimm);
cmd_name = nvdimm_cmd_name(cmd);
cmd_mask = nvdimm_cmd_mask(nvdimm);
@@ -475,12 +484,9 @@ int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm,
} else {
struct acpi_device *adev = to_acpi_dev(acpi_desc);
- func = cmd;
cmd_name = nvdimm_bus_cmd_name(cmd);
cmd_mask = nd_desc->cmd_mask;
- dsm_mask = cmd_mask;
- if (cmd == ND_CMD_CALL)
- dsm_mask = nd_desc->bus_dsm_mask;
+ dsm_mask = nd_desc->bus_dsm_mask;
desc = nd_cmd_bus_desc(cmd);
guid = to_nfit_uuid(NFIT_DEV_BUS);
handle = adev->handle;
@@ -554,6 +560,13 @@ int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm,
return -EINVAL;
}
+ if (out_obj->type != ACPI_TYPE_BUFFER) {
+ dev_dbg(dev, "%s unexpected output object type cmd: %s type: %d\n",
+ dimm_name, cmd_name, out_obj->type);
+ rc = -EINVAL;
+ goto out;
+ }
+
if (call_pkg) {
call_pkg->nd_fw_size = out_obj->buffer.length;
memcpy(call_pkg->nd_payload + call_pkg->nd_size_in,
@@ -572,13 +585,6 @@ int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm,
return 0;
}
- if (out_obj->package.type != ACPI_TYPE_BUFFER) {
- dev_dbg(dev, "%s unexpected output object type cmd: %s type: %d\n",
- dimm_name, cmd_name, out_obj->type);
- rc = -EINVAL;
- goto out;
- }
-
dev_dbg(dev, "%s cmd: %s output length: %d\n", dimm_name,
cmd_name, out_obj->buffer.length);
print_hex_dump_debug(cmd_name, DUMP_PREFIX_OFFSET, 4, 4,
@@ -1317,19 +1323,30 @@ static ssize_t scrub_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct nvdimm_bus_descriptor *nd_desc;
+ struct acpi_nfit_desc *acpi_desc;
ssize_t rc = -ENXIO;
+ bool busy;
device_lock(dev);
nd_desc = dev_get_drvdata(dev);
- if (nd_desc) {
- struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
+ if (!nd_desc) {
+ device_unlock(dev);
+ return rc;
+ }
+ acpi_desc = to_acpi_desc(nd_desc);
- mutex_lock(&acpi_desc->init_mutex);
- rc = sprintf(buf, "%d%s", acpi_desc->scrub_count,
- acpi_desc->scrub_busy
- && !acpi_desc->cancel ? "+\n" : "\n");
- mutex_unlock(&acpi_desc->init_mutex);
+ mutex_lock(&acpi_desc->init_mutex);
+ busy = test_bit(ARS_BUSY, &acpi_desc->scrub_flags)
+ && !test_bit(ARS_CANCEL, &acpi_desc->scrub_flags);
+ rc = sprintf(buf, "%d%s", acpi_desc->scrub_count, busy ? "+\n" : "\n");
+ /* Allow an admin to poll the busy state at a higher rate */
+ if (busy && capable(CAP_SYS_RAWIO) && !test_and_set_bit(ARS_POLL,
+ &acpi_desc->scrub_flags)) {
+ acpi_desc->scrub_tmo = 1;
+ mod_delayed_work(nfit_wq, &acpi_desc->dwork, HZ);
}
+
+ mutex_unlock(&acpi_desc->init_mutex);
device_unlock(dev);
return rc;
}
@@ -1759,14 +1776,14 @@ static bool acpi_nvdimm_has_method(struct acpi_device *adev, char *method)
__weak void nfit_intel_shutdown_status(struct nfit_mem *nfit_mem)
{
+ struct device *dev = &nfit_mem->adev->dev;
struct nd_intel_smart smart = { 0 };
union acpi_object in_buf = {
- .type = ACPI_TYPE_BUFFER,
- .buffer.pointer = (char *) &smart,
- .buffer.length = sizeof(smart),
+ .buffer.type = ACPI_TYPE_BUFFER,
+ .buffer.length = 0,
};
union acpi_object in_obj = {
- .type = ACPI_TYPE_PACKAGE,
+ .package.type = ACPI_TYPE_PACKAGE,
.package.count = 1,
.package.elements = &in_buf,
};
@@ -1781,8 +1798,15 @@ __weak void nfit_intel_shutdown_status(struct nfit_mem *nfit_mem)
return;
out_obj = acpi_evaluate_dsm(handle, guid, revid, func, &in_obj);
- if (!out_obj)
+ if (!out_obj || out_obj->type != ACPI_TYPE_BUFFER
+ || out_obj->buffer.length < sizeof(smart)) {
+ dev_dbg(dev->parent, "%s: failed to retrieve initial health\n",
+ dev_name(dev));
+ ACPI_FREE(out_obj);
return;
+ }
+ memcpy(&smart, out_obj->buffer.pointer, sizeof(smart));
+ ACPI_FREE(out_obj);
if (smart.flags & ND_INTEL_SMART_SHUTDOWN_VALID) {
if (smart.shutdown_state)
@@ -1793,7 +1817,6 @@ __weak void nfit_intel_shutdown_status(struct nfit_mem *nfit_mem)
set_bit(NFIT_MEM_DIRTY_COUNT, &nfit_mem->flags);
nfit_mem->dirty_shutdown = smart.shutdown_count;
}
- ACPI_FREE(out_obj);
}
static void populate_shutdown_status(struct nfit_mem *nfit_mem)
@@ -1861,9 +1884,17 @@ static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc,
dev_set_drvdata(&adev_dimm->dev, nfit_mem);
/*
- * Until standardization materializes we need to consider 4
- * different command sets. Note, that checking for function0 (bit0)
- * tells us if any commands are reachable through this GUID.
+ * There are 4 "legacy" NVDIMM command sets
+ * (NVDIMM_FAMILY_{INTEL,MSFT,HPE1,HPE2}) that were created before
+ * an EFI working group was established to constrain this
+ * proliferation. The nfit driver probes for the supported command
+ * set by GUID. Note, if you're a platform developer looking to add
+ * a new command set to this probe, consider using an existing set,
+ * or otherwise seek approval to publish the command set at
+ * http://www.uefi.org/RFIC_LIST.
+ *
+ * Note, that checking for function0 (bit0) tells us if any commands
+ * are reachable through this GUID.
*/
for (i = 0; i <= NVDIMM_FAMILY_MAX; i++)
if (acpi_check_dsm(adev_dimm->handle, to_nfit_uuid(i), 1, 1))
@@ -1886,6 +1917,8 @@ static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc,
dsm_mask &= ~(1 << 8);
} else if (nfit_mem->family == NVDIMM_FAMILY_MSFT) {
dsm_mask = 0xffffffff;
+ } else if (nfit_mem->family == NVDIMM_FAMILY_HYPERV) {
+ dsm_mask = 0x1f;
} else {
dev_dbg(dev, "unknown dimm command family\n");
nfit_mem->family = -1;
@@ -1915,18 +1948,32 @@ static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc,
| 1 << ND_CMD_SET_CONFIG_DATA;
if (family == NVDIMM_FAMILY_INTEL
&& (dsm_mask & label_mask) == label_mask)
- return 0;
+ /* skip _LS{I,R,W} enabling */;
+ else {
+ if (acpi_nvdimm_has_method(adev_dimm, "_LSI")
+ && acpi_nvdimm_has_method(adev_dimm, "_LSR")) {
+ dev_dbg(dev, "%s: has _LSR\n", dev_name(&adev_dimm->dev));
+ set_bit(NFIT_MEM_LSR, &nfit_mem->flags);
+ }
- if (acpi_nvdimm_has_method(adev_dimm, "_LSI")
- && acpi_nvdimm_has_method(adev_dimm, "_LSR")) {
- dev_dbg(dev, "%s: has _LSR\n", dev_name(&adev_dimm->dev));
- set_bit(NFIT_MEM_LSR, &nfit_mem->flags);
- }
+ if (test_bit(NFIT_MEM_LSR, &nfit_mem->flags)
+ && acpi_nvdimm_has_method(adev_dimm, "_LSW")) {
+ dev_dbg(dev, "%s: has _LSW\n", dev_name(&adev_dimm->dev));
+ set_bit(NFIT_MEM_LSW, &nfit_mem->flags);
+ }
- if (test_bit(NFIT_MEM_LSR, &nfit_mem->flags)
- && acpi_nvdimm_has_method(adev_dimm, "_LSW")) {
- dev_dbg(dev, "%s: has _LSW\n", dev_name(&adev_dimm->dev));
- set_bit(NFIT_MEM_LSW, &nfit_mem->flags);
+ /*
+ * Quirk read-only label configurations to preserve
+ * access to label-less namespaces by default.
+ */
+ if (!test_bit(NFIT_MEM_LSW, &nfit_mem->flags)
+ && !force_labels) {
+ dev_dbg(dev, "%s: No _LSW, disable labels\n",
+ dev_name(&adev_dimm->dev));
+ clear_bit(NFIT_MEM_LSR, &nfit_mem->flags);
+ } else
+ dev_dbg(dev, "%s: Force enable labels\n",
+ dev_name(&adev_dimm->dev));
}
populate_shutdown_status(nfit_mem);
@@ -2027,6 +2074,10 @@ static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc)
cmd_mask |= nfit_mem->dsm_mask & NVDIMM_STANDARD_CMDMASK;
}
+ /* Quirk to ignore LOCAL for labels on HYPERV DIMMs */
+ if (nfit_mem->family == NVDIMM_FAMILY_HYPERV)
+ set_bit(NDD_NOBLK, &flags);
+
if (test_bit(NFIT_MEM_LSR, &nfit_mem->flags)) {
set_bit(ND_CMD_GET_CONFIG_SIZE, &cmd_mask);
set_bit(ND_CMD_GET_CONFIG_DATA, &cmd_mask);
@@ -2050,7 +2101,7 @@ static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc)
if ((mem_flags & ACPI_NFIT_MEM_FAILED_MASK) == 0)
continue;
- dev_info(acpi_desc->dev, "%s flags:%s%s%s%s%s\n",
+ dev_err(acpi_desc->dev, "Error found in NVDIMM %s flags:%s%s%s%s%s\n",
nvdimm_name(nvdimm),
mem_flags & ACPI_NFIT_MEM_SAVE_FAILED ? " save_fail" : "",
mem_flags & ACPI_NFIT_MEM_RESTORE_FAILED ? " restore_fail":"",
@@ -2641,7 +2692,10 @@ static int ars_start(struct acpi_nfit_desc *acpi_desc,
if (rc < 0)
return rc;
- return cmd_rc;
+ if (cmd_rc < 0)
+ return cmd_rc;
+ set_bit(ARS_VALID, &acpi_desc->scrub_flags);
+ return 0;
}
static int ars_continue(struct acpi_nfit_desc *acpi_desc)
@@ -2651,11 +2705,11 @@ static int ars_continue(struct acpi_nfit_desc *acpi_desc)
struct nvdimm_bus_descriptor *nd_desc = &acpi_desc->nd_desc;
struct nd_cmd_ars_status *ars_status = acpi_desc->ars_status;
- memset(&ars_start, 0, sizeof(ars_start));
- ars_start.address = ars_status->restart_address;
- ars_start.length = ars_status->restart_length;
- ars_start.type = ars_status->type;
- ars_start.flags = acpi_desc->ars_start_flags;
+ ars_start = (struct nd_cmd_ars_start) {
+ .address = ars_status->restart_address,
+ .length = ars_status->restart_length,
+ .type = ars_status->type,
+ };
rc = nd_desc->ndctl(nd_desc, NULL, ND_CMD_ARS_START, &ars_start,
sizeof(ars_start), &cmd_rc);
if (rc < 0)
@@ -2734,6 +2788,17 @@ static int ars_status_process_records(struct acpi_nfit_desc *acpi_desc)
*/
if (ars_status->out_length < 44)
return 0;
+
+ /*
+ * Ignore potentially stale results that are only refreshed
+ * after a start-ARS event.
+ */
+ if (!test_and_clear_bit(ARS_VALID, &acpi_desc->scrub_flags)) {
+ dev_dbg(acpi_desc->dev, "skip %d stale records\n",
+ ars_status->num_records);
+ return 0;
+ }
+
for (i = 0; i < ars_status->num_records; i++) {
/* only process full records */
if (ars_status->out_length
@@ -2891,11 +2956,15 @@ static int acpi_nfit_register_region(struct acpi_nfit_desc *acpi_desc,
ndr_desc->res = &res;
ndr_desc->provider_data = nfit_spa;
ndr_desc->attr_groups = acpi_nfit_region_attribute_groups;
- if (spa->flags & ACPI_NFIT_PROXIMITY_VALID)
+ if (spa->flags & ACPI_NFIT_PROXIMITY_VALID) {
ndr_desc->numa_node = acpi_map_pxm_to_online_node(
spa->proximity_domain);
- else
+ ndr_desc->target_node = acpi_map_pxm_to_node(
+ spa->proximity_domain);
+ } else {
ndr_desc->numa_node = NUMA_NO_NODE;
+ ndr_desc->target_node = NUMA_NO_NODE;
+ }
/*
* Persistence domain bits are hierarchical, if
@@ -3004,14 +3073,16 @@ static int ars_register(struct acpi_nfit_desc *acpi_desc,
{
int rc;
- if (no_init_ars || test_bit(ARS_FAILED, &nfit_spa->ars_state))
+ if (test_bit(ARS_FAILED, &nfit_spa->ars_state))
return acpi_nfit_register_region(acpi_desc, nfit_spa);
set_bit(ARS_REQ_SHORT, &nfit_spa->ars_state);
- set_bit(ARS_REQ_LONG, &nfit_spa->ars_state);
+ if (!no_init_ars)
+ set_bit(ARS_REQ_LONG, &nfit_spa->ars_state);
switch (acpi_nfit_query_poison(acpi_desc)) {
case 0:
+ case -ENOSPC:
case -EAGAIN:
rc = ars_start(acpi_desc, nfit_spa, ARS_REQ_SHORT);
/* shouldn't happen, try again later */
@@ -3036,7 +3107,6 @@ static int ars_register(struct acpi_nfit_desc *acpi_desc,
break;
case -EBUSY:
case -ENOMEM:
- case -ENOSPC:
/*
* BIOS was using ARS, wait for it to complete (or
* resources to become available) and then perform our
@@ -3071,7 +3141,7 @@ static unsigned int __acpi_nfit_scrub(struct acpi_nfit_desc *acpi_desc,
lockdep_assert_held(&acpi_desc->init_mutex);
- if (acpi_desc->cancel)
+ if (test_bit(ARS_CANCEL, &acpi_desc->scrub_flags))
return 0;
if (query_rc == -EBUSY) {
@@ -3145,7 +3215,7 @@ static void __sched_ars(struct acpi_nfit_desc *acpi_desc, unsigned int tmo)
{
lockdep_assert_held(&acpi_desc->init_mutex);
- acpi_desc->scrub_busy = 1;
+ set_bit(ARS_BUSY, &acpi_desc->scrub_flags);
/* note this should only be set from within the workqueue */
if (tmo)
acpi_desc->scrub_tmo = tmo;
@@ -3161,7 +3231,7 @@ static void notify_ars_done(struct acpi_nfit_desc *acpi_desc)
{
lockdep_assert_held(&acpi_desc->init_mutex);
- acpi_desc->scrub_busy = 0;
+ clear_bit(ARS_BUSY, &acpi_desc->scrub_flags);
acpi_desc->scrub_count++;
if (acpi_desc->scrub_count_state)
sysfs_notify_dirent(acpi_desc->scrub_count_state);
@@ -3182,6 +3252,7 @@ static void acpi_nfit_scrub(struct work_struct *work)
else
notify_ars_done(acpi_desc);
memset(acpi_desc->ars_status, 0, acpi_desc->max_ars);
+ clear_bit(ARS_POLL, &acpi_desc->scrub_flags);
mutex_unlock(&acpi_desc->init_mutex);
}
@@ -3216,6 +3287,7 @@ static int acpi_nfit_register_regions(struct acpi_nfit_desc *acpi_desc)
struct nfit_spa *nfit_spa;
int rc;
+ set_bit(ARS_VALID, &acpi_desc->scrub_flags);
list_for_each_entry(nfit_spa, &acpi_desc->spas, list) {
switch (nfit_spa_type(nfit_spa->spa)) {
case NFIT_SPA_VOLATILE:
@@ -3450,7 +3522,7 @@ int acpi_nfit_ars_rescan(struct acpi_nfit_desc *acpi_desc,
struct nfit_spa *nfit_spa;
mutex_lock(&acpi_desc->init_mutex);
- if (acpi_desc->cancel) {
+ if (test_bit(ARS_CANCEL, &acpi_desc->scrub_flags)) {
mutex_unlock(&acpi_desc->init_mutex);
return 0;
}
@@ -3529,7 +3601,7 @@ void acpi_nfit_shutdown(void *data)
mutex_unlock(&acpi_desc_lock);
mutex_lock(&acpi_desc->init_mutex);
- acpi_desc->cancel = 1;
+ set_bit(ARS_CANCEL, &acpi_desc->scrub_flags);
cancel_delayed_work_sync(&acpi_desc->dwork);
mutex_unlock(&acpi_desc->init_mutex);
@@ -3729,6 +3801,7 @@ static __init int nfit_init(void)
guid_parse(UUID_NFIT_DIMM_N_HPE1, &nfit_uuid[NFIT_DEV_DIMM_N_HPE1]);
guid_parse(UUID_NFIT_DIMM_N_HPE2, &nfit_uuid[NFIT_DEV_DIMM_N_HPE2]);
guid_parse(UUID_NFIT_DIMM_N_MSFT, &nfit_uuid[NFIT_DEV_DIMM_N_MSFT]);
+ guid_parse(UUID_NFIT_DIMM_N_HYPERV, &nfit_uuid[NFIT_DEV_DIMM_N_HYPERV]);
nfit_wq = create_singlethread_workqueue("nfit");
if (!nfit_wq)
diff --git a/drivers/acpi/nfit/nfit.h b/drivers/acpi/nfit/nfit.h
index 33691aecfcee..2f8cf2a11e3b 100644
--- a/drivers/acpi/nfit/nfit.h
+++ b/drivers/acpi/nfit/nfit.h
@@ -34,11 +34,14 @@
/* https://msdn.microsoft.com/library/windows/hardware/mt604741 */
#define UUID_NFIT_DIMM_N_MSFT "1ee68b36-d4bd-4a1a-9a16-4f8e53d46e05"
+/* http://www.uefi.org/RFIC_LIST (see "Virtual NVDIMM 0x1901") */
+#define UUID_NFIT_DIMM_N_HYPERV "5746c5f2-a9a2-4264-ad0e-e4ddc9e09e80"
+
#define ACPI_NFIT_MEM_FAILED_MASK (ACPI_NFIT_MEM_SAVE_FAILED \
| ACPI_NFIT_MEM_RESTORE_FAILED | ACPI_NFIT_MEM_FLUSH_FAILED \
| ACPI_NFIT_MEM_NOT_ARMED | ACPI_NFIT_MEM_MAP_FAILED)
-#define NVDIMM_FAMILY_MAX NVDIMM_FAMILY_MSFT
+#define NVDIMM_FAMILY_MAX NVDIMM_FAMILY_HYPERV
#define NVDIMM_STANDARD_CMDMASK \
(1 << ND_CMD_SMART | 1 << ND_CMD_SMART_THRESHOLD | 1 << ND_CMD_DIMM_FLAGS \
@@ -94,6 +97,7 @@ enum nfit_uuids {
NFIT_DEV_DIMM_N_HPE1 = NVDIMM_FAMILY_HPE1,
NFIT_DEV_DIMM_N_HPE2 = NVDIMM_FAMILY_HPE2,
NFIT_DEV_DIMM_N_MSFT = NVDIMM_FAMILY_MSFT,
+ NFIT_DEV_DIMM_N_HYPERV = NVDIMM_FAMILY_HYPERV,
NFIT_SPA_VOLATILE,
NFIT_SPA_PM,
NFIT_SPA_DCR,
@@ -210,6 +214,13 @@ struct nfit_mem {
int family;
};
+enum scrub_flags {
+ ARS_BUSY,
+ ARS_CANCEL,
+ ARS_VALID,
+ ARS_POLL,
+};
+
struct acpi_nfit_desc {
struct nvdimm_bus_descriptor nd_desc;
struct acpi_table_header acpi_header;
@@ -223,7 +234,6 @@ struct acpi_nfit_desc {
struct list_head idts;
struct nvdimm_bus *nvdimm_bus;
struct device *dev;
- u8 ars_start_flags;
struct nd_cmd_ars_status *ars_status;
struct nfit_spa *scrub_spa;
struct delayed_work dwork;
@@ -232,8 +242,7 @@ struct acpi_nfit_desc {
unsigned int max_ars;
unsigned int scrub_count;
unsigned int scrub_mode;
- unsigned int scrub_busy:1;
- unsigned int cancel:1;
+ unsigned long scrub_flags;
unsigned long dimm_cmd_force_en;
unsigned long bus_cmd_force_en;
unsigned long bus_nfit_cmd_force_en;
diff --git a/drivers/acpi/numa.c b/drivers/acpi/numa.c
index 7bbbf8256a41..867f6e3f2b4f 100644
--- a/drivers/acpi/numa.c
+++ b/drivers/acpi/numa.c
@@ -84,6 +84,7 @@ int acpi_map_pxm_to_node(int pxm)
return node;
}
+EXPORT_SYMBOL(acpi_map_pxm_to_node);
/**
* acpi_map_pxm_to_online_node - Map proximity ID to online node
diff --git a/drivers/acpi/pptt.c b/drivers/acpi/pptt.c
index ad31c50de3be..065c4fc245d1 100644
--- a/drivers/acpi/pptt.c
+++ b/drivers/acpi/pptt.c
@@ -209,6 +209,9 @@ static int acpi_pptt_leaf_node(struct acpi_table_header *table_hdr,
struct acpi_pptt_processor *cpu_node;
u32 proc_sz;
+ if (table_hdr->revision > 1)
+ return (node->flags & ACPI_PPTT_ACPI_LEAF_NODE);
+
table_end = (unsigned long)table_hdr + table_hdr->length;
node_entry = ACPI_PTR_DIFF(node, table_hdr);
entry = ACPI_ADD_PTR(struct acpi_subtable_header, table_hdr,
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index 5efd4219f112..446c959a8f08 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -1545,6 +1545,7 @@ static bool acpi_device_enumeration_by_parent(struct acpi_device *device)
*/
static const struct acpi_device_id i2c_multi_instantiate_ids[] = {
{"BSG1160", },
+ {"BSG2150", },
{"INT33FE", },
{"INT3515", },
{}
diff --git a/drivers/acpi/sysfs.c b/drivers/acpi/sysfs.c
index 41324f0b1bee..fa76f5e41b5c 100644
--- a/drivers/acpi/sysfs.c
+++ b/drivers/acpi/sysfs.c
@@ -648,26 +648,29 @@ static void acpi_global_event_handler(u32 event_type, acpi_handle device,
}
}
-static int get_status(u32 index, acpi_event_status *status,
+static int get_status(u32 index, acpi_event_status *ret,
acpi_handle *handle)
{
- int result;
+ acpi_status status;
if (index >= num_gpes + ACPI_NUM_FIXED_EVENTS)
return -EINVAL;
if (index < num_gpes) {
- result = acpi_get_gpe_device(index, handle);
- if (result) {
+ status = acpi_get_gpe_device(index, handle);
+ if (ACPI_FAILURE(status)) {
ACPI_EXCEPTION((AE_INFO, AE_NOT_FOUND,
"Invalid GPE 0x%x", index));
- return result;
+ return -ENXIO;
}
- result = acpi_get_gpe_status(*handle, index, status);
- } else if (index < (num_gpes + ACPI_NUM_FIXED_EVENTS))
- result = acpi_get_event_status(index - num_gpes, status);
+ status = acpi_get_gpe_status(*handle, index, ret);
+ } else {
+ status = acpi_get_event_status(index - num_gpes, ret);
+ }
+ if (ACPI_FAILURE(status))
+ return -EIO;
- return result;
+ return 0;
}
static ssize_t counter_show(struct kobject *kobj,
diff --git a/drivers/acpi/utils.c b/drivers/acpi/utils.c
index 78db97687f26..c4b06cc075f9 100644
--- a/drivers/acpi/utils.c
+++ b/drivers/acpi/utils.c
@@ -800,6 +800,7 @@ bool acpi_dev_present(const char *hid, const char *uid, s64 hrv)
match.hrv = hrv;
dev = bus_find_device(&acpi_bus_type, NULL, &match, acpi_dev_match_cb);
+ put_device(dev);
return !!dev;
}
EXPORT_SYMBOL(acpi_dev_present);
diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c
index 41b706403ef7..b4dae624b9af 100644
--- a/drivers/amba/bus.c
+++ b/drivers/amba/bus.c
@@ -26,19 +26,36 @@
#define to_amba_driver(d) container_of(d, struct amba_driver, drv)
-static const struct amba_id *
-amba_lookup(const struct amba_id *table, struct amba_device *dev)
+/* called on periphid match and class 0x9 coresight device. */
+static int
+amba_cs_uci_id_match(const struct amba_id *table, struct amba_device *dev)
{
int ret = 0;
+ struct amba_cs_uci_id *uci;
+
+ uci = table->data;
+ /* no table data or zero mask - return match on periphid */
+ if (!uci || (uci->devarch_mask == 0))
+ return 1;
+
+ /* test against read devtype and masked devarch value */
+ ret = (dev->uci.devtype == uci->devtype) &&
+ ((dev->uci.devarch & uci->devarch_mask) == uci->devarch);
+ return ret;
+}
+
+static const struct amba_id *
+amba_lookup(const struct amba_id *table, struct amba_device *dev)
+{
while (table->mask) {
- ret = (dev->periphid & table->mask) == table->id;
- if (ret)
- break;
+ if (((dev->periphid & table->mask) == table->id) &&
+ ((dev->cid != CORESIGHT_CID) ||
+ (amba_cs_uci_id_match(table, dev))))
+ return table;
table++;
}
-
- return ret ? table : NULL;
+ return NULL;
}
static int amba_match(struct device *dev, struct device_driver *drv)
@@ -399,10 +416,22 @@ static int amba_device_try_add(struct amba_device *dev, struct resource *parent)
cid |= (readl(tmp + size - 0x10 + 4 * i) & 255) <<
(i * 8);
+ if (cid == CORESIGHT_CID) {
+ /* set the base to the start of the last 4k block */
+ void __iomem *csbase = tmp + size - 4096;
+
+ dev->uci.devarch =
+ readl(csbase + UCI_REG_DEVARCH_OFFSET);
+ dev->uci.devtype =
+ readl(csbase + UCI_REG_DEVTYPE_OFFSET) & 0xff;
+ }
+
amba_put_disable_pclk(dev);
- if (cid == AMBA_CID || cid == CORESIGHT_CID)
+ if (cid == AMBA_CID || cid == CORESIGHT_CID) {
dev->periphid = pid;
+ dev->cid = cid;
+ }
if (!dev->periphid)
ret = -ENODEV;
diff --git a/drivers/android/binder.c b/drivers/android/binder.c
index 8685882da64c..4b9c7ca492e6 100644
--- a/drivers/android/binder.c
+++ b/drivers/android/binder.c
@@ -2057,7 +2057,8 @@ static size_t binder_get_object(struct binder_proc *proc,
size_t object_size = 0;
read_size = min_t(size_t, sizeof(*object), buffer->data_size - offset);
- if (read_size < sizeof(*hdr) || !IS_ALIGNED(offset, sizeof(u32)))
+ if (offset > buffer->data_size || read_size < sizeof(*hdr) ||
+ !IS_ALIGNED(offset, sizeof(u32)))
return 0;
binder_alloc_copy_from_buffer(&proc->alloc, object, buffer,
offset, read_size);
diff --git a/drivers/android/binder_alloc.c b/drivers/android/binder_alloc.c
index 6389467670a0..195f120c4e8c 100644
--- a/drivers/android/binder_alloc.c
+++ b/drivers/android/binder_alloc.c
@@ -927,14 +927,13 @@ enum lru_status binder_alloc_free_page(struct list_head *item,
index = page - alloc->pages;
page_addr = (uintptr_t)alloc->buffer + index * PAGE_SIZE;
+
+ mm = alloc->vma_vm_mm;
+ if (!mmget_not_zero(mm))
+ goto err_mmget;
+ if (!down_write_trylock(&mm->mmap_sem))
+ goto err_down_write_mmap_sem_failed;
vma = binder_alloc_get_vma(alloc);
- if (vma) {
- if (!mmget_not_zero(alloc->vma_vm_mm))
- goto err_mmget;
- mm = alloc->vma_vm_mm;
- if (!down_read_trylock(&mm->mmap_sem))
- goto err_down_write_mmap_sem_failed;
- }
list_lru_isolate(lru, item);
spin_unlock(lock);
@@ -945,10 +944,9 @@ enum lru_status binder_alloc_free_page(struct list_head *item,
zap_page_range(vma, page_addr, PAGE_SIZE);
trace_binder_unmap_user_end(alloc, index);
-
- up_read(&mm->mmap_sem);
- mmput(mm);
}
+ up_write(&mm->mmap_sem);
+ mmput(mm);
trace_binder_unmap_kernel_start(alloc, index);
diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
index 8218db17ebdb..a6beb2c5a692 100644
--- a/drivers/ata/Kconfig
+++ b/drivers/ata/Kconfig
@@ -963,6 +963,18 @@ config PATA_GAYLE
If unsure, say N.
+config PATA_BUDDHA
+ tristate "Buddha/Catweasel/X-Surf PATA support"
+ depends on ZORRO
+ help
+ This option enables support for the IDE interfaces
+ on the Buddha, Catweasel and X-Surf expansion boards
+ on the Zorro expansion bus. It supports up to two
+ interfaces on the Buddha, three on the Catweasel and
+ two on the X-Surf.
+
+ If unsure, say N.
+
config PATA_ISAPNP
tristate "ISA Plug and Play PATA support"
depends on ISAPNP
diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile
index d21cdd83f7ab..d8cc2e04a6c7 100644
--- a/drivers/ata/Makefile
+++ b/drivers/ata/Makefile
@@ -98,6 +98,7 @@ obj-$(CONFIG_PATA_WINBOND) += pata_sl82c105.o
obj-$(CONFIG_PATA_CMD640_PCI) += pata_cmd640.o
obj-$(CONFIG_PATA_FALCON) += pata_falcon.o
obj-$(CONFIG_PATA_GAYLE) += pata_gayle.o
+obj-$(CONFIG_PATA_BUDDHA) += pata_buddha.o
obj-$(CONFIG_PATA_ISAPNP) += pata_isapnp.o
obj-$(CONFIG_PATA_IXP4XX_CF) += pata_ixp4xx_cf.o
obj-$(CONFIG_PATA_MPIIX) += pata_mpiix.o
diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c
index b5f57c69c487..692782dddc0f 100644
--- a/drivers/ata/libahci.c
+++ b/drivers/ata/libahci.c
@@ -2599,7 +2599,8 @@ int ahci_host_activate(struct ata_host *host, struct scsi_host_template *sht)
int rc;
if (hpriv->flags & AHCI_HFLAG_MULTI_MSI) {
- if (hpriv->irq_handler)
+ if (hpriv->irq_handler &&
+ hpriv->irq_handler != ahci_single_level_irq_intr)
dev_warn(host->dev,
"both AHCI_HFLAG_MULTI_MSI flag set and custom irq handler implemented\n");
if (!hpriv->get_irq_vector) {
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index 3d4887d0e84a..c10ee2391031 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -778,7 +778,7 @@ static int ata_ioc32(struct ata_port *ap)
}
int ata_sas_scsi_ioctl(struct ata_port *ap, struct scsi_device *scsidev,
- int cmd, void __user *arg)
+ unsigned int cmd, void __user *arg)
{
unsigned long val;
int rc = -EINVAL;
@@ -829,7 +829,8 @@ int ata_sas_scsi_ioctl(struct ata_port *ap, struct scsi_device *scsidev,
}
EXPORT_SYMBOL_GPL(ata_sas_scsi_ioctl);
-int ata_scsi_ioctl(struct scsi_device *scsidev, int cmd, void __user *arg)
+int ata_scsi_ioctl(struct scsi_device *scsidev, unsigned int cmd,
+ void __user *arg)
{
return ata_sas_scsi_ioctl(ata_shost_to_port(scsidev->host),
scsidev, cmd, arg);
@@ -1318,8 +1319,6 @@ static int ata_scsi_dev_config(struct scsi_device *sdev,
scsi_change_queue_depth(sdev, depth);
}
- blk_queue_flush_queueable(q, false);
-
if (dev->flags & ATA_DFLAG_TRUSTED)
sdev->security_supported = 1;
@@ -2990,7 +2989,7 @@ static unsigned int atapi_xlat(struct ata_queued_cmd *qc)
* This inconsistency confuses several controllers which
* perform PIO using DMA such as Intel AHCIs and sil3124/32.
* These controllers use actual number of transferred bytes to
- * update DMA poitner and transfer of 4n+2 bytes make those
+ * update DMA pointer and transfer of 4n+2 bytes make those
* controller push DMA pointer by 4n+4 bytes because SATA data
* FISes are aligned to 4 bytes. This causes data corruption
* and buffer overrun.
diff --git a/drivers/ata/libata-zpodd.c b/drivers/ata/libata-zpodd.c
index b3ed8f9953a8..173e6f2dd9af 100644
--- a/drivers/ata/libata-zpodd.c
+++ b/drivers/ata/libata-zpodd.c
@@ -52,38 +52,52 @@ static int eject_tray(struct ata_device *dev)
/* Per the spec, only slot type and drawer type ODD can be supported */
static enum odd_mech_type zpodd_get_mech_type(struct ata_device *dev)
{
- char buf[16];
+ char *buf;
unsigned int ret;
- struct rm_feature_desc *desc = (void *)(buf + 8);
+ struct rm_feature_desc *desc;
struct ata_taskfile tf;
static const char cdb[] = { GPCMD_GET_CONFIGURATION,
2, /* only 1 feature descriptor requested */
0, 3, /* 3, removable medium feature */
0, 0, 0,/* reserved */
- 0, sizeof(buf),
+ 0, 16,
0, 0, 0,
};
+ buf = kzalloc(16, GFP_KERNEL);
+ if (!buf)
+ return ODD_MECH_TYPE_UNSUPPORTED;
+ desc = (void *)(buf + 8);
+
ata_tf_init(dev, &tf);
tf.flags = ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
tf.command = ATA_CMD_PACKET;
tf.protocol = ATAPI_PROT_PIO;
- tf.lbam = sizeof(buf);
+ tf.lbam = 16;
ret = ata_exec_internal(dev, &tf, cdb, DMA_FROM_DEVICE,
- buf, sizeof(buf), 0);
- if (ret)
+ buf, 16, 0);
+ if (ret) {
+ kfree(buf);
return ODD_MECH_TYPE_UNSUPPORTED;
+ }
- if (be16_to_cpu(desc->feature_code) != 3)
+ if (be16_to_cpu(desc->feature_code) != 3) {
+ kfree(buf);
return ODD_MECH_TYPE_UNSUPPORTED;
+ }
- if (desc->mech_type == 0 && desc->load == 0 && desc->eject == 1)
+ if (desc->mech_type == 0 && desc->load == 0 && desc->eject == 1) {
+ kfree(buf);
return ODD_MECH_TYPE_SLOT;
- else if (desc->mech_type == 1 && desc->load == 0 && desc->eject == 1)
+ } else if (desc->mech_type == 1 && desc->load == 0 &&
+ desc->eject == 1) {
+ kfree(buf);
return ODD_MECH_TYPE_DRAWER;
- else
+ } else {
+ kfree(buf);
return ODD_MECH_TYPE_UNSUPPORTED;
+ }
}
/* Test if ODD is zero power ready by sense code */
diff --git a/drivers/ata/pata_buddha.c b/drivers/ata/pata_buddha.c
new file mode 100644
index 000000000000..11a8044ff633
--- /dev/null
+++ b/drivers/ata/pata_buddha.c
@@ -0,0 +1,257 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Buddha, Catweasel and X-Surf PATA controller driver
+ *
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Based on buddha.c:
+ *
+ * Copyright (C) 1997, 2001 by Geert Uytterhoeven and others
+ */
+
+#include <linux/ata.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/libata.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/zorro.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_host.h>
+
+#include <asm/amigahw.h>
+#include <asm/amigaints.h>
+#include <asm/ide.h>
+#include <asm/setup.h>
+
+#define DRV_NAME "pata_buddha"
+#define DRV_VERSION "0.1.0"
+
+#define BUDDHA_BASE1 0x800
+#define BUDDHA_BASE2 0xa00
+#define BUDDHA_BASE3 0xc00
+#define XSURF_BASE1 0xb000 /* 2.5" interface */
+#define XSURF_BASE2 0xd000 /* 3.5" interface */
+#define BUDDHA_CONTROL 0x11a
+#define BUDDHA_IRQ 0xf00
+#define XSURF_IRQ 0x7e
+#define BUDDHA_IRQ_MR 0xfc0 /* master interrupt enable */
+
+enum {
+ BOARD_BUDDHA = 0,
+ BOARD_CATWEASEL,
+ BOARD_XSURF
+};
+
+static unsigned int buddha_bases[3] __initdata = {
+ BUDDHA_BASE1, BUDDHA_BASE2, BUDDHA_BASE3
+};
+
+static unsigned int xsurf_bases[2] __initdata = {
+ XSURF_BASE1, XSURF_BASE2
+};
+
+static struct scsi_host_template pata_buddha_sht = {
+ ATA_PIO_SHT(DRV_NAME),
+};
+
+/* FIXME: is this needed? */
+static unsigned int pata_buddha_data_xfer(struct ata_queued_cmd *qc,
+ unsigned char *buf,
+ unsigned int buflen, int rw)
+{
+ struct ata_device *dev = qc->dev;
+ struct ata_port *ap = dev->link->ap;
+ void __iomem *data_addr = ap->ioaddr.data_addr;
+ unsigned int words = buflen >> 1;
+
+ /* Transfer multiple of 2 bytes */
+ if (rw == READ)
+ raw_insw((u16 *)data_addr, (u16 *)buf, words);
+ else
+ raw_outsw((u16 *)data_addr, (u16 *)buf, words);
+
+ /* Transfer trailing byte, if any. */
+ if (unlikely(buflen & 0x01)) {
+ unsigned char pad[2] = { };
+
+ /* Point buf to the tail of buffer */
+ buf += buflen - 1;
+
+ if (rw == READ) {
+ raw_insw((u16 *)data_addr, (u16 *)pad, 1);
+ *buf = pad[0];
+ } else {
+ pad[0] = *buf;
+ raw_outsw((u16 *)data_addr, (u16 *)pad, 1);
+ }
+ words++;
+ }
+
+ return words << 1;
+}
+
+/*
+ * Provide our own set_mode() as we don't want to change anything that has
+ * already been configured..
+ */
+static int pata_buddha_set_mode(struct ata_link *link,
+ struct ata_device **unused)
+{
+ struct ata_device *dev;
+
+ ata_for_each_dev(dev, link, ENABLED) {
+ /* We don't really care */
+ dev->pio_mode = dev->xfer_mode = XFER_PIO_0;
+ dev->xfer_shift = ATA_SHIFT_PIO;
+ dev->flags |= ATA_DFLAG_PIO;
+ ata_dev_info(dev, "configured for PIO\n");
+ }
+ return 0;
+}
+
+static bool pata_buddha_irq_check(struct ata_port *ap)
+{
+ u8 ch;
+
+ ch = z_readb((unsigned long)ap->private_data);
+
+ return !!(ch & 0x80);
+}
+
+static void pata_xsurf_irq_clear(struct ata_port *ap)
+{
+ z_writeb(0, (unsigned long)ap->private_data);
+}
+
+static struct ata_port_operations pata_buddha_ops = {
+ .inherits = &ata_sff_port_ops,
+ .sff_data_xfer = pata_buddha_data_xfer,
+ .sff_irq_check = pata_buddha_irq_check,
+ .cable_detect = ata_cable_unknown,
+ .set_mode = pata_buddha_set_mode,
+};
+
+static struct ata_port_operations pata_xsurf_ops = {
+ .inherits = &ata_sff_port_ops,
+ .sff_data_xfer = pata_buddha_data_xfer,
+ .sff_irq_check = pata_buddha_irq_check,
+ .sff_irq_clear = pata_xsurf_irq_clear,
+ .cable_detect = ata_cable_unknown,
+ .set_mode = pata_buddha_set_mode,
+};
+
+static int __init pata_buddha_init_one(void)
+{
+ struct zorro_dev *z = NULL;
+
+ while ((z = zorro_find_device(ZORRO_WILDCARD, z))) {
+ static const char *board_name[]
+ = { "Buddha", "Catweasel", "X-Surf" };
+ struct ata_host *host;
+ void __iomem *buddha_board;
+ unsigned long board;
+ unsigned int type, nr_ports = 2;
+ int i;
+
+ if (z->id == ZORRO_PROD_INDIVIDUAL_COMPUTERS_BUDDHA) {
+ type = BOARD_BUDDHA;
+ } else if (z->id == ZORRO_PROD_INDIVIDUAL_COMPUTERS_CATWEASEL) {
+ type = BOARD_CATWEASEL;
+ nr_ports++;
+ } else if (z->id == ZORRO_PROD_INDIVIDUAL_COMPUTERS_X_SURF) {
+ type = BOARD_XSURF;
+ } else
+ continue;
+
+ dev_info(&z->dev, "%s IDE controller\n", board_name[type]);
+
+ board = z->resource.start;
+
+ if (type != BOARD_XSURF) {
+ if (!devm_request_mem_region(&z->dev,
+ board + BUDDHA_BASE1,
+ 0x800, DRV_NAME))
+ continue;
+ } else {
+ if (!devm_request_mem_region(&z->dev,
+ board + XSURF_BASE1,
+ 0x1000, DRV_NAME))
+ continue;
+ if (!devm_request_mem_region(&z->dev,
+ board + XSURF_BASE2,
+ 0x1000, DRV_NAME))
+ continue;
+ }
+
+ /* allocate host */
+ host = ata_host_alloc(&z->dev, nr_ports);
+ if (!host)
+ continue;
+
+ buddha_board = ZTWO_VADDR(board);
+
+ /* enable the board IRQ on Buddha/Catweasel */
+ if (type != BOARD_XSURF)
+ z_writeb(0, buddha_board + BUDDHA_IRQ_MR);
+
+ for (i = 0; i < nr_ports; i++) {
+ struct ata_port *ap = host->ports[i];
+ void __iomem *base, *irqport;
+ unsigned long ctl = 0;
+
+ if (type != BOARD_XSURF) {
+ ap->ops = &pata_buddha_ops;
+ base = buddha_board + buddha_bases[i];
+ ctl = BUDDHA_CONTROL;
+ irqport = buddha_board + BUDDHA_IRQ + i * 0x40;
+ } else {
+ ap->ops = &pata_xsurf_ops;
+ base = buddha_board + xsurf_bases[i];
+ /* X-Surf has no CS1* (Control/AltStat) */
+ irqport = buddha_board + XSURF_IRQ;
+ }
+
+ ap->pio_mask = ATA_PIO4;
+ ap->flags |= ATA_FLAG_SLAVE_POSS | ATA_FLAG_NO_IORDY;
+
+ ap->ioaddr.data_addr = base;
+ ap->ioaddr.error_addr = base + 2 + 1 * 4;
+ ap->ioaddr.feature_addr = base + 2 + 1 * 4;
+ ap->ioaddr.nsect_addr = base + 2 + 2 * 4;
+ ap->ioaddr.lbal_addr = base + 2 + 3 * 4;
+ ap->ioaddr.lbam_addr = base + 2 + 4 * 4;
+ ap->ioaddr.lbah_addr = base + 2 + 5 * 4;
+ ap->ioaddr.device_addr = base + 2 + 6 * 4;
+ ap->ioaddr.status_addr = base + 2 + 7 * 4;
+ ap->ioaddr.command_addr = base + 2 + 7 * 4;
+
+ if (ctl) {
+ ap->ioaddr.altstatus_addr = base + ctl;
+ ap->ioaddr.ctl_addr = base + ctl;
+ }
+
+ ap->private_data = (void *)irqport;
+
+ ata_port_desc(ap, "cmd 0x%lx ctl 0x%lx", board,
+ ctl ? board + buddha_bases[i] + ctl : 0);
+ }
+
+ ata_host_activate(host, IRQ_AMIGA_PORTS, ata_sff_interrupt,
+ IRQF_SHARED, &pata_buddha_sht);
+
+ }
+
+ return 0;
+}
+
+module_init(pata_buddha_init_one);
+
+MODULE_AUTHOR("Bartlomiej Zolnierkiewicz");
+MODULE_DESCRIPTION("low-level driver for Buddha/Catweasel/X-Surf PATA");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION(DRV_VERSION);
diff --git a/drivers/ata/pata_macio.c b/drivers/ata/pata_macio.c
index 9e7fc302430f..456ae7184f92 100644
--- a/drivers/ata/pata_macio.c
+++ b/drivers/ata/pata_macio.c
@@ -954,7 +954,7 @@ static void pata_macio_invariants(struct pata_macio_priv *priv)
priv->kind = controller_k2_ata6;
priv->timings = pata_macio_kauai_timings;
} else if (of_device_is_compatible(priv->node, "keylargo-ata")) {
- if (strcmp(priv->node->name, "ata-4") == 0) {
+ if (of_node_name_eq(priv->node, "ata-4")) {
priv->kind = controller_kl_ata4;
priv->timings = pata_macio_kl66_timings;
} else {
diff --git a/drivers/ata/pata_of_platform.c b/drivers/ata/pata_of_platform.c
index 01161c1aef4d..7a0b1759e5f0 100644
--- a/drivers/ata/pata_of_platform.c
+++ b/drivers/ata/pata_of_platform.c
@@ -32,6 +32,7 @@ static int pata_of_platform_probe(struct platform_device *ofdev)
unsigned int reg_shift = 0;
int pio_mode = 0;
int pio_mask;
+ bool use16bit;
ret = of_address_to_resource(dn, 0, &io_res);
if (ret) {
@@ -60,11 +61,14 @@ static int pata_of_platform_probe(struct platform_device *ofdev)
dev_info(&ofdev->dev, "pio-mode unspecified, assuming PIO0\n");
}
+ use16bit = of_property_read_bool(dn, "ata-generic,use16bit");
+
pio_mask = 1 << pio_mode;
pio_mask |= (1 << pio_mode) - 1;
return __pata_platform_probe(&ofdev->dev, &io_res, &ctl_res, irq_res,
- reg_shift, pio_mask, &pata_platform_sht);
+ reg_shift, pio_mask, &pata_platform_sht,
+ use16bit);
}
static const struct of_device_id pata_of_platform_match[] = {
diff --git a/drivers/ata/pata_platform.c b/drivers/ata/pata_platform.c
index d6f8f5406442..5aba691f09af 100644
--- a/drivers/ata/pata_platform.c
+++ b/drivers/ata/pata_platform.c
@@ -47,13 +47,6 @@ static struct scsi_host_template pata_platform_sht = {
ATA_PIO_SHT(DRV_NAME),
};
-static struct ata_port_operations pata_platform_port_ops = {
- .inherits = &ata_sff_port_ops,
- .sff_data_xfer = ata_sff_data_xfer32,
- .cable_detect = ata_cable_unknown,
- .set_mode = pata_platform_set_mode,
-};
-
static void pata_platform_setup_port(struct ata_ioports *ioaddr,
unsigned int shift)
{
@@ -79,6 +72,7 @@ static void pata_platform_setup_port(struct ata_ioports *ioaddr,
* @ioport_shift: I/O port shift
* @__pio_mask: PIO mask
* @sht: scsi_host_template to use when registering
+ * @use16bit: Flag to indicate 16-bit IO instead of 32-bit
*
* Register a platform bus IDE interface. Such interfaces are PIO and we
* assume do not support IRQ sharing.
@@ -101,7 +95,7 @@ static void pata_platform_setup_port(struct ata_ioports *ioaddr,
int __pata_platform_probe(struct device *dev, struct resource *io_res,
struct resource *ctl_res, struct resource *irq_res,
unsigned int ioport_shift, int __pio_mask,
- struct scsi_host_template *sht)
+ struct scsi_host_template *sht, bool use16bit)
{
struct ata_host *host;
struct ata_port *ap;
@@ -120,7 +114,7 @@ int __pata_platform_probe(struct device *dev, struct resource *io_res,
*/
if (irq_res && irq_res->start > 0) {
irq = irq_res->start;
- irq_flags = irq_res->flags & IRQF_TRIGGER_MASK;
+ irq_flags = (irq_res->flags & IRQF_TRIGGER_MASK) | IRQF_SHARED;
}
/*
@@ -131,7 +125,15 @@ int __pata_platform_probe(struct device *dev, struct resource *io_res,
return -ENOMEM;
ap = host->ports[0];
- ap->ops = &pata_platform_port_ops;
+ ap->ops = devm_kzalloc(dev, sizeof(*ap->ops), GFP_KERNEL);
+ ap->ops->inherits = &ata_sff_port_ops;
+ ap->ops->cable_detect = ata_cable_unknown;
+ ap->ops->set_mode = pata_platform_set_mode;
+ if (use16bit)
+ ap->ops->sff_data_xfer = ata_sff_data_xfer;
+ else
+ ap->ops->sff_data_xfer = ata_sff_data_xfer32;
+
ap->pio_mask = __pio_mask;
ap->flags |= ATA_FLAG_SLAVE_POSS;
@@ -218,7 +220,7 @@ static int pata_platform_probe(struct platform_device *pdev)
return __pata_platform_probe(&pdev->dev, io_res, ctl_res, irq_res,
pp_info ? pp_info->ioport_shift : 0,
- pio_mask, &pata_platform_sht);
+ pio_mask, &pata_platform_sht, false);
}
static struct platform_driver pata_platform_driver = {
diff --git a/drivers/ata/pata_samsung_cf.c b/drivers/ata/pata_samsung_cf.c
index f5bd44b8bd63..1dc3361cb5a5 100644
--- a/drivers/ata/pata_samsung_cf.c
+++ b/drivers/ata/pata_samsung_cf.c
@@ -609,17 +609,15 @@ static int __exit pata_s3c_remove(struct platform_device *pdev)
#ifdef CONFIG_PM_SLEEP
static int pata_s3c_suspend(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct ata_host *host = platform_get_drvdata(pdev);
+ struct ata_host *host = dev_get_drvdata(dev);
return ata_host_suspend(host, PMSG_SUSPEND);
}
static int pata_s3c_resume(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct ata_host *host = platform_get_drvdata(pdev);
- struct s3c_ide_platdata *pdata = dev_get_platdata(&pdev->dev);
+ struct ata_host *host = dev_get_drvdata(dev);
+ struct s3c_ide_platdata *pdata = dev_get_platdata(dev);
struct s3c_ide_info *info = host->private_data;
pata_s3c_hwinit(info, pdata);
diff --git a/drivers/auxdisplay/Kconfig b/drivers/auxdisplay/Kconfig
index 57410f9c5d44..c52c738e554a 100644
--- a/drivers/auxdisplay/Kconfig
+++ b/drivers/auxdisplay/Kconfig
@@ -164,9 +164,7 @@ config ARM_CHARLCD
line and the Linux version on the second line, but that's
still useful.
-endif # AUXDISPLAY
-
-menuconfig PANEL
+menuconfig PARPORT_PANEL
tristate "Parallel port LCD/Keypad Panel support"
depends on PARPORT
select CHARLCD
@@ -178,7 +176,7 @@ menuconfig PANEL
compiled as a module, or linked into the kernel and started at boot.
If you don't understand what all this is about, say N.
-if PANEL
+if PARPORT_PANEL
config PANEL_PARPORT
int "Default parallel port number (0=LPT1)"
@@ -419,8 +417,11 @@ config PANEL_LCD_PIN_BL
Default for the 'BL' pin in custom profile is '0' (uncontrolled).
+endif # PARPORT_PANEL
+
config PANEL_CHANGE_MESSAGE
bool "Change LCD initialization message ?"
+ depends on CHARLCD
default "n"
---help---
This allows you to replace the boot message indicating the kernel version
@@ -444,7 +445,34 @@ config PANEL_BOOT_MESSAGE
An empty message will only clear the display at driver init time. Any other
printf()-formatted message is valid with newline and escape codes.
-endif # PANEL
+choice
+ prompt "Backlight initial state"
+ default CHARLCD_BL_FLASH
+
+ config CHARLCD_BL_OFF
+ bool "Off"
+ help
+ Backlight is initially turned off
+
+ config CHARLCD_BL_ON
+ bool "On"
+ help
+ Backlight is initially turned on
+
+ config CHARLCD_BL_FLASH
+ bool "Flash"
+ help
+ Backlight is flashed briefly on init
+
+endchoice
+
+endif # AUXDISPLAY
+
+config PANEL
+ tristate "Parallel port LCD/Keypad Panel support (OLD OPTION)"
+ depends on PARPORT
+ select AUXDISPLAY
+ select PARPORT_PANEL
config CHARLCD
tristate "Character LCD core support" if COMPILE_TEST
diff --git a/drivers/auxdisplay/Makefile b/drivers/auxdisplay/Makefile
index 7ac6776ca3f6..cf54b5efb07e 100644
--- a/drivers/auxdisplay/Makefile
+++ b/drivers/auxdisplay/Makefile
@@ -10,4 +10,4 @@ obj-$(CONFIG_CFAG12864B) += cfag12864b.o cfag12864bfb.o
obj-$(CONFIG_IMG_ASCII_LCD) += img-ascii-lcd.o
obj-$(CONFIG_HD44780) += hd44780.o
obj-$(CONFIG_HT16K33) += ht16k33.o
-obj-$(CONFIG_PANEL) += panel.o
+obj-$(CONFIG_PARPORT_PANEL) += panel.o
diff --git a/drivers/auxdisplay/charlcd.c b/drivers/auxdisplay/charlcd.c
index 60e0b772673f..92745efefb54 100644
--- a/drivers/auxdisplay/charlcd.c
+++ b/drivers/auxdisplay/charlcd.c
@@ -91,7 +91,7 @@ struct charlcd_priv {
unsigned long long drvdata[0];
};
-#define to_priv(p) container_of(p, struct charlcd_priv, lcd)
+#define charlcd_to_priv(p) container_of(p, struct charlcd_priv, lcd)
/* Device single-open policy control */
static atomic_t charlcd_available = ATOMIC_INIT(1);
@@ -105,7 +105,7 @@ static void long_sleep(int ms)
/* turn the backlight on or off */
static void charlcd_backlight(struct charlcd *lcd, int on)
{
- struct charlcd_priv *priv = to_priv(lcd);
+ struct charlcd_priv *priv = charlcd_to_priv(lcd);
if (!lcd->ops->backlight)
return;
@@ -134,7 +134,7 @@ static void charlcd_bl_off(struct work_struct *work)
/* turn the backlight on for a little while */
void charlcd_poke(struct charlcd *lcd)
{
- struct charlcd_priv *priv = to_priv(lcd);
+ struct charlcd_priv *priv = charlcd_to_priv(lcd);
if (!lcd->ops->backlight)
return;
@@ -152,7 +152,7 @@ EXPORT_SYMBOL_GPL(charlcd_poke);
static void charlcd_gotoxy(struct charlcd *lcd)
{
- struct charlcd_priv *priv = to_priv(lcd);
+ struct charlcd_priv *priv = charlcd_to_priv(lcd);
unsigned int addr;
/*
@@ -170,7 +170,7 @@ static void charlcd_gotoxy(struct charlcd *lcd)
static void charlcd_home(struct charlcd *lcd)
{
- struct charlcd_priv *priv = to_priv(lcd);
+ struct charlcd_priv *priv = charlcd_to_priv(lcd);
priv->addr.x = 0;
priv->addr.y = 0;
@@ -179,7 +179,7 @@ static void charlcd_home(struct charlcd *lcd)
static void charlcd_print(struct charlcd *lcd, char c)
{
- struct charlcd_priv *priv = to_priv(lcd);
+ struct charlcd_priv *priv = charlcd_to_priv(lcd);
if (priv->addr.x < lcd->bwidth) {
if (lcd->char_conv)
@@ -211,7 +211,7 @@ static void charlcd_clear_fast(struct charlcd *lcd)
/* clears the display and resets X/Y */
static void charlcd_clear_display(struct charlcd *lcd)
{
- struct charlcd_priv *priv = to_priv(lcd);
+ struct charlcd_priv *priv = charlcd_to_priv(lcd);
lcd->ops->write_cmd(lcd, LCD_CMD_DISPLAY_CLEAR);
priv->addr.x = 0;
@@ -223,7 +223,7 @@ static void charlcd_clear_display(struct charlcd *lcd)
static int charlcd_init_display(struct charlcd *lcd)
{
void (*write_cmd_raw)(struct charlcd *lcd, int cmd);
- struct charlcd_priv *priv = to_priv(lcd);
+ struct charlcd_priv *priv = charlcd_to_priv(lcd);
u8 init;
if (lcd->ifwidth != 4 && lcd->ifwidth != 8)
@@ -369,7 +369,7 @@ static bool parse_xy(const char *s, unsigned long *x, unsigned long *y)
static inline int handle_lcd_special_code(struct charlcd *lcd)
{
- struct charlcd_priv *priv = to_priv(lcd);
+ struct charlcd_priv *priv = charlcd_to_priv(lcd);
/* LCD special codes */
@@ -580,7 +580,7 @@ static inline int handle_lcd_special_code(struct charlcd *lcd)
static void charlcd_write_char(struct charlcd *lcd, char c)
{
- struct charlcd_priv *priv = to_priv(lcd);
+ struct charlcd_priv *priv = charlcd_to_priv(lcd);
/* first, we'll test if we're in escape mode */
if ((c != '\n') && priv->esc_seq.len >= 0) {
@@ -705,7 +705,7 @@ static ssize_t charlcd_write(struct file *file, const char __user *buf,
static int charlcd_open(struct inode *inode, struct file *file)
{
- struct charlcd_priv *priv = to_priv(the_charlcd);
+ struct charlcd_priv *priv = charlcd_to_priv(the_charlcd);
int ret;
ret = -EBUSY;
@@ -763,10 +763,24 @@ static void charlcd_puts(struct charlcd *lcd, const char *s)
}
}
+#ifdef CONFIG_PANEL_BOOT_MESSAGE
+#define LCD_INIT_TEXT CONFIG_PANEL_BOOT_MESSAGE
+#else
+#define LCD_INIT_TEXT "Linux-" UTS_RELEASE "\n"
+#endif
+
+#ifdef CONFIG_CHARLCD_BL_ON
+#define LCD_INIT_BL "\x1b[L+"
+#elif defined(CONFIG_CHARLCD_BL_FLASH)
+#define LCD_INIT_BL "\x1b[L*"
+#else
+#define LCD_INIT_BL "\x1b[L-"
+#endif
+
/* initialize the LCD driver */
static int charlcd_init(struct charlcd *lcd)
{
- struct charlcd_priv *priv = to_priv(lcd);
+ struct charlcd_priv *priv = charlcd_to_priv(lcd);
int ret;
if (lcd->ops->backlight) {
@@ -784,13 +798,8 @@ static int charlcd_init(struct charlcd *lcd)
return ret;
/* display a short message */
-#ifdef CONFIG_PANEL_CHANGE_MESSAGE
-#ifdef CONFIG_PANEL_BOOT_MESSAGE
- charlcd_puts(lcd, "\x1b[Lc\x1b[Lb\x1b[L*" CONFIG_PANEL_BOOT_MESSAGE);
-#endif
-#else
- charlcd_puts(lcd, "\x1b[Lc\x1b[Lb\x1b[L*Linux-" UTS_RELEASE "\n");
-#endif
+ charlcd_puts(lcd, "\x1b[Lc\x1b[Lb" LCD_INIT_BL LCD_INIT_TEXT);
+
/* clear the display on the next device opening */
priv->must_clear = true;
charlcd_home(lcd);
@@ -818,6 +827,12 @@ struct charlcd *charlcd_alloc(unsigned int drvdata_size)
}
EXPORT_SYMBOL_GPL(charlcd_alloc);
+void charlcd_free(struct charlcd *lcd)
+{
+ kfree(charlcd_to_priv(lcd));
+}
+EXPORT_SYMBOL_GPL(charlcd_free);
+
static int panel_notify_sys(struct notifier_block *this, unsigned long code,
void *unused)
{
@@ -866,7 +881,7 @@ EXPORT_SYMBOL_GPL(charlcd_register);
int charlcd_unregister(struct charlcd *lcd)
{
- struct charlcd_priv *priv = to_priv(lcd);
+ struct charlcd_priv *priv = charlcd_to_priv(lcd);
unregister_reboot_notifier(&panel_notifier);
charlcd_puts(lcd, "\x0cLCD driver unloaded.\x1b[Lc\x1b[Lb\x1b[L-");
diff --git a/drivers/auxdisplay/hd44780.c b/drivers/auxdisplay/hd44780.c
index 9ad93ea42fdc..ab15b64707ad 100644
--- a/drivers/auxdisplay/hd44780.c
+++ b/drivers/auxdisplay/hd44780.c
@@ -271,7 +271,7 @@ static int hd44780_probe(struct platform_device *pdev)
return 0;
fail:
- kfree(lcd);
+ charlcd_free(lcd);
return ret;
}
@@ -280,6 +280,8 @@ static int hd44780_remove(struct platform_device *pdev)
struct charlcd *lcd = platform_get_drvdata(pdev);
charlcd_unregister(lcd);
+
+ charlcd_free(lcd);
return 0;
}
diff --git a/drivers/auxdisplay/panel.c b/drivers/auxdisplay/panel.c
index 21b9b2f2470a..e06de63497cf 100644
--- a/drivers/auxdisplay/panel.c
+++ b/drivers/auxdisplay/panel.c
@@ -1620,7 +1620,7 @@ err_lcd_unreg:
if (lcd.enabled)
charlcd_unregister(lcd.charlcd);
err_unreg_device:
- kfree(lcd.charlcd);
+ charlcd_free(lcd.charlcd);
lcd.charlcd = NULL;
parport_unregister_device(pprt);
pprt = NULL;
@@ -1647,7 +1647,7 @@ static void panel_detach(struct parport *port)
if (lcd.enabled) {
charlcd_unregister(lcd.charlcd);
lcd.initialized = false;
- kfree(lcd.charlcd);
+ charlcd_free(lcd.charlcd);
lcd.charlcd = NULL;
}
diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig
index 3e63a900b330..059700ea3521 100644
--- a/drivers/base/Kconfig
+++ b/drivers/base/Kconfig
@@ -191,83 +191,6 @@ config DMA_FENCE_TRACE
lockup related problems for dma-buffers shared across multiple
devices.
-config DMA_CMA
- bool "DMA Contiguous Memory Allocator"
- depends on HAVE_DMA_CONTIGUOUS && CMA
- help
- This enables the Contiguous Memory Allocator which allows drivers
- to allocate big physically-contiguous blocks of memory for use with
- hardware components that do not support I/O map nor scatter-gather.
-
- You can disable CMA by specifying "cma=0" on the kernel's command
- line.
-
- For more information see <include/linux/dma-contiguous.h>.
- If unsure, say "n".
-
-if DMA_CMA
-comment "Default contiguous memory area size:"
-
-config CMA_SIZE_MBYTES
- int "Size in Mega Bytes"
- depends on !CMA_SIZE_SEL_PERCENTAGE
- default 0 if X86
- default 16
- help
- Defines the size (in MiB) of the default memory area for Contiguous
- Memory Allocator. If the size of 0 is selected, CMA is disabled by
- default, but it can be enabled by passing cma=size[MG] to the kernel.
-
-
-config CMA_SIZE_PERCENTAGE
- int "Percentage of total memory"
- depends on !CMA_SIZE_SEL_MBYTES
- default 0 if X86
- default 10
- help
- Defines the size of the default memory area for Contiguous Memory
- Allocator as a percentage of the total memory in the system.
- If 0 percent is selected, CMA is disabled by default, but it can be
- enabled by passing cma=size[MG] to the kernel.
-
-choice
- prompt "Selected region size"
- default CMA_SIZE_SEL_MBYTES
-
-config CMA_SIZE_SEL_MBYTES
- bool "Use mega bytes value only"
-
-config CMA_SIZE_SEL_PERCENTAGE
- bool "Use percentage value only"
-
-config CMA_SIZE_SEL_MIN
- bool "Use lower value (minimum)"
-
-config CMA_SIZE_SEL_MAX
- bool "Use higher value (maximum)"
-
-endchoice
-
-config CMA_ALIGNMENT
- int "Maximum PAGE_SIZE order of alignment for contiguous buffers"
- range 4 12
- default 8
- help
- DMA mapping framework by default aligns all buffers to the smallest
- PAGE_SIZE order which is greater than or equal to the requested buffer
- size. This works well for buffers up to a few hundreds kilobytes, but
- for larger buffers it just a memory waste. With this parameter you can
- specify the maximum PAGE_SIZE order for contiguous buffers. Larger
- buffers will be aligned only to this specified order. The order is
- expressed as a power of two multiplied by the PAGE_SIZE.
-
- For example, if your system defaults to 4KiB pages, the order value
- of 8 means that the buffers will be aligned up to 1MiB only.
-
- If unsure, leave the default value "8".
-
-endif
-
config GENERIC_ARCH_TOPOLOGY
bool
help
diff --git a/drivers/base/memory.c b/drivers/base/memory.c
index 048cbf7d5233..cb8347500ce2 100644
--- a/drivers/base/memory.c
+++ b/drivers/base/memory.c
@@ -88,6 +88,7 @@ unsigned long __weak memory_block_size_bytes(void)
{
return MIN_MEMORY_BLOCK_SIZE;
}
+EXPORT_SYMBOL_GPL(memory_block_size_bytes);
static unsigned long get_memory_block_size(void)
{
diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index 4e45ac21d672..dab0a5abc391 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -80,6 +80,26 @@ struct resource *platform_get_resource(struct platform_device *dev,
EXPORT_SYMBOL_GPL(platform_get_resource);
/**
+ * devm_platform_ioremap_resource - call devm_ioremap_resource() for a platform
+ * device
+ *
+ * @pdev: platform device to use both for memory resource lookup as well as
+ * resource managemend
+ * @index: resource index
+ */
+#ifdef CONFIG_HAS_IOMEM
+void __iomem *devm_platform_ioremap_resource(struct platform_device *pdev,
+ unsigned int index)
+{
+ struct resource *res;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, index);
+ return devm_ioremap_resource(&pdev->dev, res);
+}
+EXPORT_SYMBOL_GPL(devm_platform_ioremap_resource);
+#endif /* CONFIG_HAS_IOMEM */
+
+/**
* platform_get_irq - get an IRQ for a device
* @dev: platform device
* @num: IRQ number index
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index 2c334c01fc43..96a6dc9d305c 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -6,6 +6,8 @@
* This file is released under the GPLv2.
*/
+#define pr_fmt(fmt) "PM: " fmt
+
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/io.h>
@@ -457,19 +459,19 @@ static int _genpd_power_off(struct generic_pm_domain *genpd, bool timed)
time_start = ktime_get();
ret = genpd->power_off(genpd);
- if (ret == -EBUSY)
+ if (ret)
return ret;
elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
if (elapsed_ns <= genpd->states[state_idx].power_off_latency_ns)
- return ret;
+ return 0;
genpd->states[state_idx].power_off_latency_ns = elapsed_ns;
genpd->max_off_time_changed = true;
pr_debug("%s: Power-%s latency exceeded, new value %lld ns\n",
genpd->name, "off", elapsed_ns);
- return ret;
+ return 0;
}
/**
@@ -1467,12 +1469,12 @@ static int genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
if (IS_ERR(gpd_data))
return PTR_ERR(gpd_data);
- genpd_lock(genpd);
-
ret = genpd->attach_dev ? genpd->attach_dev(genpd, dev) : 0;
if (ret)
goto out;
+ genpd_lock(genpd);
+
dev_pm_domain_set(dev, &genpd->domain);
genpd->device_count++;
@@ -1480,9 +1482,8 @@ static int genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
list_add_tail(&gpd_data->base.list_node, &genpd->dev_list);
- out:
genpd_unlock(genpd);
-
+ out:
if (ret)
genpd_free_dev_data(dev, gpd_data);
else
@@ -1531,15 +1532,15 @@ static int genpd_remove_device(struct generic_pm_domain *genpd,
genpd->device_count--;
genpd->max_off_time_changed = true;
- if (genpd->detach_dev)
- genpd->detach_dev(genpd, dev);
-
dev_pm_domain_set(dev, NULL);
list_del_init(&pdd->list_node);
genpd_unlock(genpd);
+ if (genpd->detach_dev)
+ genpd->detach_dev(genpd, dev);
+
genpd_free_dev_data(dev, gpd_data);
return 0;
@@ -1657,8 +1658,8 @@ int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
genpd_lock_nested(genpd, SINGLE_DEPTH_NESTING);
if (!list_empty(&subdomain->master_links) || subdomain->device_count) {
- pr_warn("%s: unable to remove subdomain %s\n", genpd->name,
- subdomain->name);
+ pr_warn("%s: unable to remove subdomain %s\n",
+ genpd->name, subdomain->name);
ret = -EBUSY;
goto out;
}
@@ -1766,8 +1767,8 @@ int pm_genpd_init(struct generic_pm_domain *genpd,
ret = genpd_set_default_power_state(genpd);
if (ret)
return ret;
- } else if (!gov) {
- pr_warn("%s : no governor for states\n", genpd->name);
+ } else if (!gov && genpd->state_count > 1) {
+ pr_warn("%s: no governor for states\n", genpd->name);
}
device_initialize(&genpd->dev);
@@ -2514,7 +2515,7 @@ static int genpd_parse_state(struct genpd_power_state *genpd_state,
&entry_latency);
if (err) {
pr_debug(" * %pOF missing entry-latency-us property\n",
- state_node);
+ state_node);
return -EINVAL;
}
@@ -2522,7 +2523,7 @@ static int genpd_parse_state(struct genpd_power_state *genpd_state,
&exit_latency);
if (err) {
pr_debug(" * %pOF missing exit-latency-us property\n",
- state_node);
+ state_node);
return -EINVAL;
}
diff --git a/drivers/base/power/domain_governor.c b/drivers/base/power/domain_governor.c
index 99896fbf18e4..4d07e38a8247 100644
--- a/drivers/base/power/domain_governor.c
+++ b/drivers/base/power/domain_governor.c
@@ -128,7 +128,6 @@ static bool __default_power_down_ok(struct dev_pm_domain *pd,
off_on_time_ns = genpd->states[state].power_off_latency_ns +
genpd->states[state].power_on_latency_ns;
-
min_off_time_ns = -1;
/*
* Check if subdomains can be off for enough time.
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index 5a8149829ab3..f80d298de3fa 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -17,6 +17,8 @@
* subsystem list maintains.
*/
+#define pr_fmt(fmt) "PM: " fmt
+
#include <linux/device.h>
#include <linux/export.h>
#include <linux/mutex.h>
@@ -128,7 +130,7 @@ void device_pm_add(struct device *dev)
if (device_pm_not_required(dev))
return;
- pr_debug("PM: Adding info for %s:%s\n",
+ pr_debug("Adding info for %s:%s\n",
dev->bus ? dev->bus->name : "No Bus", dev_name(dev));
device_pm_check_callbacks(dev);
mutex_lock(&dpm_list_mtx);
@@ -149,7 +151,7 @@ void device_pm_remove(struct device *dev)
if (device_pm_not_required(dev))
return;
- pr_debug("PM: Removing info for %s:%s\n",
+ pr_debug("Removing info for %s:%s\n",
dev->bus ? dev->bus->name : "No Bus", dev_name(dev));
complete_all(&dev->power.completion);
mutex_lock(&dpm_list_mtx);
@@ -168,7 +170,7 @@ void device_pm_remove(struct device *dev)
*/
void device_pm_move_before(struct device *deva, struct device *devb)
{
- pr_debug("PM: Moving %s:%s before %s:%s\n",
+ pr_debug("Moving %s:%s before %s:%s\n",
deva->bus ? deva->bus->name : "No Bus", dev_name(deva),
devb->bus ? devb->bus->name : "No Bus", dev_name(devb));
/* Delete deva from dpm_list and reinsert before devb. */
@@ -182,7 +184,7 @@ void device_pm_move_before(struct device *deva, struct device *devb)
*/
void device_pm_move_after(struct device *deva, struct device *devb)
{
- pr_debug("PM: Moving %s:%s after %s:%s\n",
+ pr_debug("Moving %s:%s after %s:%s\n",
deva->bus ? deva->bus->name : "No Bus", dev_name(deva),
devb->bus ? devb->bus->name : "No Bus", dev_name(devb));
/* Delete deva from dpm_list and reinsert after devb. */
@@ -195,7 +197,7 @@ void device_pm_move_after(struct device *deva, struct device *devb)
*/
void device_pm_move_last(struct device *dev)
{
- pr_debug("PM: Moving %s:%s to end of list\n",
+ pr_debug("Moving %s:%s to end of list\n",
dev->bus ? dev->bus->name : "No Bus", dev_name(dev));
list_move_tail(&dev->power.entry, &dpm_list);
}
@@ -418,8 +420,8 @@ static void pm_dev_dbg(struct device *dev, pm_message_t state, const char *info)
static void pm_dev_err(struct device *dev, pm_message_t state, const char *info,
int error)
{
- printk(KERN_ERR "PM: Device %s failed to %s%s: error %d\n",
- dev_name(dev), pm_verb(state.event), info, error);
+ pr_err("Device %s failed to %s%s: error %d\n",
+ dev_name(dev), pm_verb(state.event), info, error);
}
static void dpm_show_time(ktime_t starttime, pm_message_t state, int error,
@@ -2022,8 +2024,7 @@ int dpm_prepare(pm_message_t state)
error = 0;
continue;
}
- printk(KERN_INFO "PM: Device %s not prepared "
- "for power transition: code %d\n",
+ pr_info("Device %s not prepared for power transition: code %d\n",
dev_name(dev), error);
put_device(dev);
break;
@@ -2062,7 +2063,7 @@ EXPORT_SYMBOL_GPL(dpm_suspend_start);
void __suspend_report_result(const char *function, void *fn, int ret)
{
if (ret)
- printk(KERN_ERR "%s(): %pF returns %d\n", function, fn, ret);
+ pr_err("%s(): %pF returns %d\n", function, fn, ret);
}
EXPORT_SYMBOL_GPL(__suspend_report_result);
diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h
index c511def48b48..ec33fbdb919b 100644
--- a/drivers/base/power/power.h
+++ b/drivers/base/power/power.h
@@ -21,6 +21,7 @@ static inline void pm_runtime_early_init(struct device *dev)
extern void pm_runtime_init(struct device *dev);
extern void pm_runtime_reinit(struct device *dev);
extern void pm_runtime_remove(struct device *dev);
+extern u64 pm_runtime_active_time(struct device *dev);
#define WAKE_IRQ_DEDICATED_ALLOCATED BIT(0)
#define WAKE_IRQ_DEDICATED_MANAGED BIT(1)
diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c
index 3382542b39b7..f80e402ef778 100644
--- a/drivers/base/power/qos.c
+++ b/drivers/base/power/qos.c
@@ -22,7 +22,7 @@
* per-device constraint data struct.
*
* Note about the per-device constraint data struct allocation:
- * . The per-device constraints data struct ptr is tored into the device
+ * . The per-device constraints data struct ptr is stored into the device
* dev_pm_info.
* . To minimize the data usage by the per-device constraints, the data struct
* is only allocated at the first call to dev_pm_qos_add_request.
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c
index a80dbf08a99c..977db40378b0 100644
--- a/drivers/base/power/runtime.c
+++ b/drivers/base/power/runtime.c
@@ -64,7 +64,7 @@ static int rpm_suspend(struct device *dev, int rpmflags);
* runtime_status field is updated, to account the time in the old state
* correctly.
*/
-void update_pm_runtime_accounting(struct device *dev)
+static void update_pm_runtime_accounting(struct device *dev)
{
u64 now, last, delta;
@@ -98,7 +98,7 @@ static void __update_runtime_status(struct device *dev, enum rpm_status status)
dev->power.runtime_status = status;
}
-u64 pm_runtime_suspended_time(struct device *dev)
+static u64 rpm_get_accounted_time(struct device *dev, bool suspended)
{
u64 time;
unsigned long flags;
@@ -106,12 +106,22 @@ u64 pm_runtime_suspended_time(struct device *dev)
spin_lock_irqsave(&dev->power.lock, flags);
update_pm_runtime_accounting(dev);
- time = dev->power.suspended_time;
+ time = suspended ? dev->power.suspended_time : dev->power.active_time;
spin_unlock_irqrestore(&dev->power.lock, flags);
return time;
}
+
+u64 pm_runtime_active_time(struct device *dev)
+{
+ return rpm_get_accounted_time(dev, false);
+}
+
+u64 pm_runtime_suspended_time(struct device *dev)
+{
+ return rpm_get_accounted_time(dev, true);
+}
EXPORT_SYMBOL_GPL(pm_runtime_suspended_time);
/**
diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c
index c6bf76124184..1226e441ddfe 100644
--- a/drivers/base/power/sysfs.c
+++ b/drivers/base/power/sysfs.c
@@ -125,13 +125,9 @@ static ssize_t runtime_active_time_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int ret;
- u64 tmp;
- spin_lock_irq(&dev->power.lock);
- update_pm_runtime_accounting(dev);
- tmp = dev->power.active_time;
+ u64 tmp = pm_runtime_active_time(dev);
do_div(tmp, NSEC_PER_MSEC);
ret = sprintf(buf, "%llu\n", tmp);
- spin_unlock_irq(&dev->power.lock);
return ret;
}
@@ -141,13 +137,9 @@ static ssize_t runtime_suspended_time_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int ret;
- u64 tmp;
- spin_lock_irq(&dev->power.lock);
- update_pm_runtime_accounting(dev);
- tmp = dev->power.suspended_time;
+ u64 tmp = pm_runtime_suspended_time(dev);
do_div(tmp, NSEC_PER_MSEC);
ret = sprintf(buf, "%llu\n", tmp);
- spin_unlock_irq(&dev->power.lock);
return ret;
}
diff --git a/drivers/base/power/trace.c b/drivers/base/power/trace.c
index b11f47a1e819..2bd9d2c744ca 100644
--- a/drivers/base/power/trace.c
+++ b/drivers/base/power/trace.c
@@ -7,6 +7,8 @@
* devices may be working.
*/
+#define pr_fmt(fmt) "PM: " fmt
+
#include <linux/pm-trace.h>
#include <linux/export.h>
#include <linux/rtc.h>
diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c
index f1fee72ed970..bb1ae175fae1 100644
--- a/drivers/base/power/wakeup.c
+++ b/drivers/base/power/wakeup.c
@@ -6,6 +6,8 @@
* This file is released under the GPLv2.
*/
+#define pr_fmt(fmt) "PM: " fmt
+
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/sched/signal.h>
@@ -106,23 +108,6 @@ struct wakeup_source *wakeup_source_create(const char *name)
}
EXPORT_SYMBOL_GPL(wakeup_source_create);
-/**
- * wakeup_source_drop - Prepare a struct wakeup_source object for destruction.
- * @ws: Wakeup source to prepare for destruction.
- *
- * Callers must ensure that __pm_stay_awake() or __pm_wakeup_event() will never
- * be run in parallel with this function for the same wakeup source object.
- */
-void wakeup_source_drop(struct wakeup_source *ws)
-{
- if (!ws)
- return;
-
- del_timer_sync(&ws->timer);
- __pm_relax(ws);
-}
-EXPORT_SYMBOL_GPL(wakeup_source_drop);
-
/*
* Record wakeup_source statistics being deleted into a dummy wakeup_source.
*/
@@ -162,7 +147,7 @@ void wakeup_source_destroy(struct wakeup_source *ws)
if (!ws)
return;
- wakeup_source_drop(ws);
+ __pm_relax(ws);
wakeup_source_record(ws);
kfree_const(ws->name);
kfree(ws);
@@ -205,6 +190,13 @@ void wakeup_source_remove(struct wakeup_source *ws)
list_del_rcu(&ws->entry);
raw_spin_unlock_irqrestore(&events_lock, flags);
synchronize_srcu(&wakeup_srcu);
+
+ del_timer_sync(&ws->timer);
+ /*
+ * Clear timer.function to make wakeup_source_not_registered() treat
+ * this wakeup source as not registered.
+ */
+ ws->timer.function = NULL;
}
EXPORT_SYMBOL_GPL(wakeup_source_remove);
@@ -853,7 +845,7 @@ bool pm_wakeup_pending(void)
raw_spin_unlock_irqrestore(&events_lock, flags);
if (ret) {
- pr_debug("PM: Wakeup pending, aborting suspend\n");
+ pr_debug("Wakeup pending, aborting suspend\n");
pm_print_active_wakeup_sources();
}
diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c
index 1fad9291f6aa..7fc5a18e02ad 100644
--- a/drivers/base/swnode.c
+++ b/drivers/base/swnode.c
@@ -472,7 +472,7 @@ static int software_node_read_string_array(const struct fwnode_handle *fwnode,
val, nval);
}
-struct fwnode_handle *
+static struct fwnode_handle *
software_node_get_parent(const struct fwnode_handle *fwnode)
{
struct software_node *swnode = to_software_node(fwnode);
@@ -481,7 +481,7 @@ software_node_get_parent(const struct fwnode_handle *fwnode)
NULL;
}
-struct fwnode_handle *
+static struct fwnode_handle *
software_node_get_next_child(const struct fwnode_handle *fwnode,
struct fwnode_handle *child)
{
diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c
index 55481b40df9a..95f608d1a098 100644
--- a/drivers/block/floppy.c
+++ b/drivers/block/floppy.c
@@ -2230,7 +2230,6 @@ static void floppy_end_request(struct request *req, blk_status_t error)
static void request_done(int uptodate)
{
struct request *req = current_req;
- struct request_queue *q;
int block;
char msg[sizeof("request done ") + sizeof(int) * 3];
@@ -2243,8 +2242,6 @@ static void request_done(int uptodate)
return;
}
- q = req->q;
-
if (uptodate) {
/* maintain values for invalidation on geometry
* change */
diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index cf5538942834..bf1c61cab8eb 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -511,21 +511,22 @@ static int lo_rw_aio(struct loop_device *lo, struct loop_cmd *cmd,
loff_t pos, bool rw)
{
struct iov_iter iter;
+ struct req_iterator rq_iter;
struct bio_vec *bvec;
struct request *rq = blk_mq_rq_from_pdu(cmd);
struct bio *bio = rq->bio;
struct file *file = lo->lo_backing_file;
+ struct bio_vec tmp;
unsigned int offset;
- int segments = 0;
+ int nr_bvec = 0;
int ret;
+ rq_for_each_bvec(tmp, rq, rq_iter)
+ nr_bvec++;
+
if (rq->bio != rq->biotail) {
- struct req_iterator iter;
- struct bio_vec tmp;
- __rq_for_each_bio(bio, rq)
- segments += bio_segments(bio);
- bvec = kmalloc_array(segments, sizeof(struct bio_vec),
+ bvec = kmalloc_array(nr_bvec, sizeof(struct bio_vec),
GFP_NOIO);
if (!bvec)
return -EIO;
@@ -534,10 +535,10 @@ static int lo_rw_aio(struct loop_device *lo, struct loop_cmd *cmd,
/*
* The bios of the request may be started from the middle of
* the 'bvec' because of bio splitting, so we can't directly
- * copy bio->bi_iov_vec to new bvec. The rq_for_each_segment
+ * copy bio->bi_iov_vec to new bvec. The rq_for_each_bvec
* API will take care of all details for us.
*/
- rq_for_each_segment(tmp, rq, iter) {
+ rq_for_each_bvec(tmp, rq, rq_iter) {
*bvec = tmp;
bvec++;
}
@@ -551,11 +552,10 @@ static int lo_rw_aio(struct loop_device *lo, struct loop_cmd *cmd,
*/
offset = bio->bi_iter.bi_bvec_done;
bvec = __bvec_iter_bvec(bio->bi_io_vec, bio->bi_iter);
- segments = bio_segments(bio);
}
atomic_set(&cmd->ref, 2);
- iov_iter_bvec(&iter, rw, bvec, segments, blk_rq_bytes(rq));
+ iov_iter_bvec(&iter, rw, bvec, nr_bvec, blk_rq_bytes(rq));
iter.iov_offset = offset;
cmd->iocb.ki_pos = pos;
@@ -656,7 +656,7 @@ static int loop_validate_file(struct file *file, struct block_device *bdev)
return -EBADF;
l = f->f_mapping->host->i_bdev->bd_disk->private_data;
- if (l->lo_state == Lo_unbound) {
+ if (l->lo_state != Lo_bound) {
return -EINVAL;
}
f = l->lo_backing_file;
@@ -1089,16 +1089,12 @@ static int __loop_clr_fd(struct loop_device *lo, bool release)
kobject_uevent(&disk_to_dev(bdev->bd_disk)->kobj, KOBJ_CHANGE);
}
mapping_set_gfp_mask(filp->f_mapping, gfp);
- lo->lo_state = Lo_unbound;
/* This is safe: open() is still holding a reference. */
module_put(THIS_MODULE);
blk_mq_unfreeze_queue(lo->lo_queue);
partscan = lo->lo_flags & LO_FLAGS_PARTSCAN && bdev;
lo_number = lo->lo_number;
- lo->lo_flags = 0;
- if (!part_shift)
- lo->lo_disk->flags |= GENHD_FL_NO_PART_SCAN;
loop_unprepare_queue(lo);
out_unlock:
mutex_unlock(&loop_ctl_mutex);
@@ -1115,11 +1111,29 @@ out_unlock:
err = __blkdev_reread_part(bdev);
else
err = blkdev_reread_part(bdev);
- pr_warn("%s: partition scan of loop%d failed (rc=%d)\n",
- __func__, lo_number, err);
+ if (err)
+ pr_warn("%s: partition scan of loop%d failed (rc=%d)\n",
+ __func__, lo_number, err);
/* Device is gone, no point in returning error */
err = 0;
}
+
+ /*
+ * lo->lo_state is set to Lo_unbound here after above partscan has
+ * finished.
+ *
+ * There cannot be anybody else entering __loop_clr_fd() as
+ * lo->lo_backing_file is already cleared and Lo_rundown state
+ * protects us from all the other places trying to change the 'lo'
+ * device.
+ */
+ mutex_lock(&loop_ctl_mutex);
+ lo->lo_flags = 0;
+ if (!part_shift)
+ lo->lo_disk->flags |= GENHD_FL_NO_PART_SCAN;
+ lo->lo_state = Lo_unbound;
+ mutex_unlock(&loop_ctl_mutex);
+
/*
* Need not hold loop_ctl_mutex to fput backing file.
* Calling fput holding loop_ctl_mutex triggers a circular
@@ -1937,7 +1951,7 @@ static int loop_add(struct loop_device **l, int i)
lo->tag_set.queue_depth = 128;
lo->tag_set.numa_node = NUMA_NO_NODE;
lo->tag_set.cmd_size = sizeof(struct loop_cmd);
- lo->tag_set.flags = BLK_MQ_F_SHOULD_MERGE | BLK_MQ_F_SG_MERGE;
+ lo->tag_set.flags = BLK_MQ_F_SHOULD_MERGE;
lo->tag_set.driver_data = lo;
err = blk_mq_alloc_tag_set(&lo->tag_set);
diff --git a/drivers/block/mtip32xx/mtip32xx.c b/drivers/block/mtip32xx/mtip32xx.c
index 2f3ee4d6af82..83302ecdc8db 100644
--- a/drivers/block/mtip32xx/mtip32xx.c
+++ b/drivers/block/mtip32xx/mtip32xx.c
@@ -1416,7 +1416,7 @@ static blk_status_t mtip_send_trim(struct driver_data *dd, unsigned int lba,
WARN_ON(sizeof(struct mtip_trim) > ATA_SECT_SIZE);
/* Allocate a DMA buffer for the trim structure */
- buf = dmam_alloc_coherent(&dd->pdev->dev, ATA_SECT_SIZE, &dma_addr,
+ buf = dma_alloc_coherent(&dd->pdev->dev, ATA_SECT_SIZE, &dma_addr,
GFP_KERNEL);
if (!buf)
return BLK_STS_RESOURCE;
@@ -1453,7 +1453,7 @@ static blk_status_t mtip_send_trim(struct driver_data *dd, unsigned int lba,
MTIP_TRIM_TIMEOUT_MS) < 0)
ret = BLK_STS_IOERR;
- dmam_free_coherent(&dd->pdev->dev, ATA_SECT_SIZE, buf, dma_addr);
+ dma_free_coherent(&dd->pdev->dev, ATA_SECT_SIZE, buf, dma_addr);
return ret;
}
@@ -1656,7 +1656,7 @@ static int exec_drive_command(struct mtip_port *port, u8 *command,
if (!user_buffer)
return -EFAULT;
- buf = dmam_alloc_coherent(&port->dd->pdev->dev,
+ buf = dma_alloc_coherent(&port->dd->pdev->dev,
ATA_SECT_SIZE * xfer_sz,
&dma_addr,
GFP_KERNEL);
@@ -1734,7 +1734,7 @@ static int exec_drive_command(struct mtip_port *port, u8 *command,
}
exit_drive_command:
if (buf)
- dmam_free_coherent(&port->dd->pdev->dev,
+ dma_free_coherent(&port->dd->pdev->dev,
ATA_SECT_SIZE * xfer_sz, buf, dma_addr);
return rv;
}
@@ -2838,11 +2838,11 @@ static void mtip_dma_free(struct driver_data *dd)
struct mtip_port *port = dd->port;
if (port->block1)
- dmam_free_coherent(&dd->pdev->dev, BLOCK_DMA_ALLOC_SZ,
+ dma_free_coherent(&dd->pdev->dev, BLOCK_DMA_ALLOC_SZ,
port->block1, port->block1_dma);
if (port->command_list) {
- dmam_free_coherent(&dd->pdev->dev, AHCI_CMD_TBL_SZ,
+ dma_free_coherent(&dd->pdev->dev, AHCI_CMD_TBL_SZ,
port->command_list, port->command_list_dma);
}
}
@@ -2861,7 +2861,7 @@ static int mtip_dma_alloc(struct driver_data *dd)
/* Allocate dma memory for RX Fis, Identify, and Sector Bufffer */
port->block1 =
- dmam_alloc_coherent(&dd->pdev->dev, BLOCK_DMA_ALLOC_SZ,
+ dma_alloc_coherent(&dd->pdev->dev, BLOCK_DMA_ALLOC_SZ,
&port->block1_dma, GFP_KERNEL);
if (!port->block1)
return -ENOMEM;
@@ -2869,10 +2869,10 @@ static int mtip_dma_alloc(struct driver_data *dd)
/* Allocate dma memory for command list */
port->command_list =
- dmam_alloc_coherent(&dd->pdev->dev, AHCI_CMD_TBL_SZ,
+ dma_alloc_coherent(&dd->pdev->dev, AHCI_CMD_TBL_SZ,
&port->command_list_dma, GFP_KERNEL);
if (!port->command_list) {
- dmam_free_coherent(&dd->pdev->dev, BLOCK_DMA_ALLOC_SZ,
+ dma_free_coherent(&dd->pdev->dev, BLOCK_DMA_ALLOC_SZ,
port->block1, port->block1_dma);
port->block1 = NULL;
port->block1_dma = 0;
@@ -3057,13 +3057,8 @@ static int mtip_hw_init(struct driver_data *dd)
mtip_start_port(dd->port);
/* Setup the ISR and enable interrupts. */
- rv = devm_request_irq(&dd->pdev->dev,
- dd->pdev->irq,
- mtip_irq_handler,
- IRQF_SHARED,
- dev_driver_string(&dd->pdev->dev),
- dd);
-
+ rv = request_irq(dd->pdev->irq, mtip_irq_handler, IRQF_SHARED,
+ dev_driver_string(&dd->pdev->dev), dd);
if (rv) {
dev_err(&dd->pdev->dev,
"Unable to allocate IRQ %d\n", dd->pdev->irq);
@@ -3091,7 +3086,7 @@ out3:
/* Release the IRQ. */
irq_set_affinity_hint(dd->pdev->irq, NULL);
- devm_free_irq(&dd->pdev->dev, dd->pdev->irq, dd);
+ free_irq(dd->pdev->irq, dd);
out2:
mtip_deinit_port(dd->port);
@@ -3146,7 +3141,7 @@ static int mtip_hw_exit(struct driver_data *dd)
/* Release the IRQ. */
irq_set_affinity_hint(dd->pdev->irq, NULL);
- devm_free_irq(&dd->pdev->dev, dd->pdev->irq, dd);
+ free_irq(dd->pdev->irq, dd);
msleep(1000);
/* Free dma regions */
@@ -3610,8 +3605,8 @@ static void mtip_free_cmd(struct blk_mq_tag_set *set, struct request *rq,
if (!cmd->command)
return;
- dmam_free_coherent(&dd->pdev->dev, CMD_DMA_ALLOC_SZ,
- cmd->command, cmd->command_dma);
+ dma_free_coherent(&dd->pdev->dev, CMD_DMA_ALLOC_SZ, cmd->command,
+ cmd->command_dma);
}
static int mtip_init_cmd(struct blk_mq_tag_set *set, struct request *rq,
@@ -3620,7 +3615,7 @@ static int mtip_init_cmd(struct blk_mq_tag_set *set, struct request *rq,
struct driver_data *dd = set->driver_data;
struct mtip_cmd *cmd = blk_mq_rq_to_pdu(rq);
- cmd->command = dmam_alloc_coherent(&dd->pdev->dev, CMD_DMA_ALLOC_SZ,
+ cmd->command = dma_alloc_coherent(&dd->pdev->dev, CMD_DMA_ALLOC_SZ,
&cmd->command_dma, GFP_KERNEL);
if (!cmd->command)
return -ENOMEM;
diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c
index 7c9a949e876b..90ba9f4c03f3 100644
--- a/drivers/block/nbd.c
+++ b/drivers/block/nbd.c
@@ -1571,7 +1571,7 @@ static int nbd_dev_add(int index)
nbd->tag_set.numa_node = NUMA_NO_NODE;
nbd->tag_set.cmd_size = sizeof(struct nbd_cmd);
nbd->tag_set.flags = BLK_MQ_F_SHOULD_MERGE |
- BLK_MQ_F_SG_MERGE | BLK_MQ_F_BLOCKING;
+ BLK_MQ_F_BLOCKING;
nbd->tag_set.driver_data = nbd;
err = blk_mq_alloc_tag_set(&nbd->tag_set);
@@ -2118,8 +2118,7 @@ static int nbd_genl_status(struct sk_buff *skb, struct genl_info *info)
}
nla_nest_end(reply, dev_list);
genlmsg_end(reply, reply_head);
- genlmsg_reply(reply, info);
- ret = 0;
+ ret = genlmsg_reply(reply, info);
out:
mutex_unlock(&nbd_index_mutex);
return ret;
diff --git a/drivers/block/null_blk_main.c b/drivers/block/null_blk_main.c
index 62c9654b9ce8..417a9f15c116 100644
--- a/drivers/block/null_blk_main.c
+++ b/drivers/block/null_blk_main.c
@@ -1104,7 +1104,7 @@ static int null_handle_bio(struct nullb_cmd *cmd)
len = bvec.bv_len;
err = null_transfer(nullb, bvec.bv_page, len, bvec.bv_offset,
op_is_write(bio_op(bio)), sector,
- bio_op(bio) & REQ_FUA);
+ bio->bi_opf & REQ_FUA);
if (err) {
spin_unlock_irq(&nullb->lock);
return err;
@@ -1678,7 +1678,6 @@ static int null_add_dev(struct nullb_device *dev)
if (dev->cache_size > 0) {
set_bit(NULLB_DEV_FL_CACHE, &nullb->dev->flags);
blk_queue_write_cache(nullb->q, true, true);
- blk_queue_flush_queueable(nullb->q, true);
}
if (dev->zoned) {
diff --git a/drivers/block/paride/pcd.c b/drivers/block/paride/pcd.c
index 96670eefaeb2..377a694dc228 100644
--- a/drivers/block/paride/pcd.c
+++ b/drivers/block/paride/pcd.c
@@ -749,8 +749,12 @@ static int pcd_detect(void)
return 0;
printk("%s: No CD-ROM drive found\n", name);
- for (unit = 0, cd = pcd; unit < PCD_UNITS; unit++, cd++)
+ for (unit = 0, cd = pcd; unit < PCD_UNITS; unit++, cd++) {
+ blk_cleanup_queue(cd->disk->queue);
+ cd->disk->queue = NULL;
+ blk_mq_free_tag_set(&cd->tag_set);
put_disk(cd->disk);
+ }
pi_unregister_driver(par_drv);
return -1;
}
diff --git a/drivers/block/paride/pf.c b/drivers/block/paride/pf.c
index e92e7a8eeeb2..103b617cdc31 100644
--- a/drivers/block/paride/pf.c
+++ b/drivers/block/paride/pf.c
@@ -761,8 +761,12 @@ static int pf_detect(void)
return 0;
printk("%s: No ATAPI disk detected\n", name);
- for (pf = units, unit = 0; unit < PF_UNITS; pf++, unit++)
+ for (pf = units, unit = 0; unit < PF_UNITS; pf++, unit++) {
+ blk_cleanup_queue(pf->disk->queue);
+ pf->disk->queue = NULL;
+ blk_mq_free_tag_set(&pf->tag_set);
put_disk(pf->disk);
+ }
pi_unregister_driver(par_drv);
return -1;
}
@@ -1047,13 +1051,15 @@ static void __exit pf_exit(void)
int unit;
unregister_blkdev(major, name);
for (pf = units, unit = 0; unit < PF_UNITS; pf++, unit++) {
- if (!pf->present)
- continue;
- del_gendisk(pf->disk);
+ if (pf->present)
+ del_gendisk(pf->disk);
+
blk_cleanup_queue(pf->disk->queue);
blk_mq_free_tag_set(&pf->tag_set);
put_disk(pf->disk);
- pi_release(pf->pi);
+
+ if (pf->present)
+ pi_release(pf->pi);
}
}
diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c
index 282e2e82d849..2210c1b9491b 100644
--- a/drivers/block/rbd.c
+++ b/drivers/block/rbd.c
@@ -115,12 +115,14 @@ static int atomic_dec_return_safe(atomic_t *v)
#define RBD_FEATURE_LAYERING (1ULL<<0)
#define RBD_FEATURE_STRIPINGV2 (1ULL<<1)
#define RBD_FEATURE_EXCLUSIVE_LOCK (1ULL<<2)
+#define RBD_FEATURE_DEEP_FLATTEN (1ULL<<5)
#define RBD_FEATURE_DATA_POOL (1ULL<<7)
#define RBD_FEATURE_OPERATIONS (1ULL<<8)
#define RBD_FEATURES_ALL (RBD_FEATURE_LAYERING | \
RBD_FEATURE_STRIPINGV2 | \
RBD_FEATURE_EXCLUSIVE_LOCK | \
+ RBD_FEATURE_DEEP_FLATTEN | \
RBD_FEATURE_DATA_POOL | \
RBD_FEATURE_OPERATIONS)
@@ -214,28 +216,40 @@ enum obj_operation_type {
OBJ_OP_READ = 1,
OBJ_OP_WRITE,
OBJ_OP_DISCARD,
+ OBJ_OP_ZEROOUT,
};
/*
* Writes go through the following state machine to deal with
* layering:
*
- * need copyup
- * RBD_OBJ_WRITE_GUARD ---------------> RBD_OBJ_WRITE_COPYUP
- * | ^ |
- * v \------------------------------/
- * done
- * ^
- * |
- * RBD_OBJ_WRITE_FLAT
+ * . . . . . RBD_OBJ_WRITE_GUARD. . . . . . . . . . . . . .
+ * . | .
+ * . v .
+ * . RBD_OBJ_WRITE_READ_FROM_PARENT. . . .
+ * . | . .
+ * . v v (deep-copyup .
+ * (image . RBD_OBJ_WRITE_COPYUP_EMPTY_SNAPC . not needed) .
+ * flattened) v | . .
+ * . v . .
+ * . . . .RBD_OBJ_WRITE_COPYUP_OPS. . . . . (copyup .
+ * | not needed) v
+ * v .
+ * done . . . . . . . . . . . . . . . . . .
+ * ^
+ * |
+ * RBD_OBJ_WRITE_FLAT
*
* Writes start in RBD_OBJ_WRITE_GUARD or _FLAT, depending on whether
- * there is a parent or not.
+ * assert_exists guard is needed or not (in some cases it's not needed
+ * even if there is a parent).
*/
enum rbd_obj_write_state {
RBD_OBJ_WRITE_FLAT = 1,
RBD_OBJ_WRITE_GUARD,
- RBD_OBJ_WRITE_COPYUP,
+ RBD_OBJ_WRITE_READ_FROM_PARENT,
+ RBD_OBJ_WRITE_COPYUP_EMPTY_SNAPC,
+ RBD_OBJ_WRITE_COPYUP_OPS,
};
struct rbd_obj_request {
@@ -291,7 +305,6 @@ struct rbd_img_request {
int result; /* first nonzero obj_request result */
struct list_head object_extents; /* obj_req.ex structs */
- u32 obj_request_count;
u32 pending_count;
struct kref kref;
@@ -421,6 +434,10 @@ static DEFINE_IDA(rbd_dev_id_ida);
static struct workqueue_struct *rbd_wq;
+static struct ceph_snap_context rbd_empty_snapc = {
+ .nref = REFCOUNT_INIT(1),
+};
+
/*
* single-major requires >= 0.75 version of userspace rbd utility.
*/
@@ -732,6 +749,7 @@ static struct rbd_client *rbd_client_find(struct ceph_options *ceph_opts)
*/
enum {
Opt_queue_depth,
+ Opt_alloc_size,
Opt_lock_timeout,
Opt_last_int,
/* int args above */
@@ -748,6 +766,7 @@ enum {
static match_table_t rbd_opts_tokens = {
{Opt_queue_depth, "queue_depth=%d"},
+ {Opt_alloc_size, "alloc_size=%d"},
{Opt_lock_timeout, "lock_timeout=%d"},
/* int args above */
{Opt_pool_ns, "_pool_ns=%s"},
@@ -764,6 +783,7 @@ static match_table_t rbd_opts_tokens = {
struct rbd_options {
int queue_depth;
+ int alloc_size;
unsigned long lock_timeout;
bool read_only;
bool lock_on_read;
@@ -772,6 +792,7 @@ struct rbd_options {
};
#define RBD_QUEUE_DEPTH_DEFAULT BLKDEV_MAX_RQ
+#define RBD_ALLOC_SIZE_DEFAULT (64 * 1024)
#define RBD_LOCK_TIMEOUT_DEFAULT 0 /* no timeout */
#define RBD_READ_ONLY_DEFAULT false
#define RBD_LOCK_ON_READ_DEFAULT false
@@ -811,6 +832,17 @@ static int parse_rbd_opts_token(char *c, void *private)
}
pctx->opts->queue_depth = intval;
break;
+ case Opt_alloc_size:
+ if (intval < SECTOR_SIZE) {
+ pr_err("alloc_size out of range\n");
+ return -EINVAL;
+ }
+ if (!is_power_of_2(intval)) {
+ pr_err("alloc_size must be a power of 2\n");
+ return -EINVAL;
+ }
+ pctx->opts->alloc_size = intval;
+ break;
case Opt_lock_timeout:
/* 0 is "wait forever" (i.e. infinite timeout) */
if (intval < 0 || intval > INT_MAX / 1000) {
@@ -857,6 +889,8 @@ static char* obj_op_name(enum obj_operation_type op_type)
return "write";
case OBJ_OP_DISCARD:
return "discard";
+ case OBJ_OP_ZEROOUT:
+ return "zeroout";
default:
return "???";
}
@@ -890,23 +924,6 @@ static void rbd_put_client(struct rbd_client *rbdc)
kref_put(&rbdc->kref, rbd_client_release);
}
-static int wait_for_latest_osdmap(struct ceph_client *client)
-{
- u64 newest_epoch;
- int ret;
-
- ret = ceph_monc_get_version(&client->monc, "osdmap", &newest_epoch);
- if (ret)
- return ret;
-
- if (client->osdc.osdmap->epoch >= newest_epoch)
- return 0;
-
- ceph_osdc_maybe_request_map(&client->osdc);
- return ceph_monc_wait_osdmap(&client->monc, newest_epoch,
- client->options->mount_timeout);
-}
-
/*
* Get a ceph client with specific addr and configuration, if one does
* not exist create it. Either way, ceph_opts is consumed by this
@@ -926,7 +943,8 @@ static struct rbd_client *rbd_get_client(struct ceph_options *ceph_opts)
* Using an existing client. Make sure ->pg_pools is up to
* date before we look up the pool id in do_rbd_add().
*/
- ret = wait_for_latest_osdmap(rbdc->client);
+ ret = ceph_wait_for_latest_osdmap(rbdc->client,
+ rbdc->client->options->mount_timeout);
if (ret) {
rbd_warn(NULL, "failed to get latest osdmap: %d", ret);
rbd_put_client(rbdc);
@@ -1344,7 +1362,6 @@ static inline void rbd_img_obj_request_add(struct rbd_img_request *img_request,
/* Image request now owns object's original reference */
obj_request->img_request = img_request;
- img_request->obj_request_count++;
img_request->pending_count++;
dout("%s: img %p obj %p\n", __func__, img_request, obj_request);
}
@@ -1354,8 +1371,6 @@ static inline void rbd_img_obj_request_del(struct rbd_img_request *img_request,
{
dout("%s: img %p obj %p\n", __func__, img_request, obj_request);
list_del(&obj_request->ex.oe_item);
- rbd_assert(img_request->obj_request_count > 0);
- img_request->obj_request_count--;
rbd_assert(obj_request->img_request == img_request);
rbd_obj_request_put(obj_request);
}
@@ -1409,6 +1424,19 @@ static bool rbd_obj_is_tail(struct rbd_obj_request *obj_req)
rbd_dev->layout.object_size;
}
+/*
+ * Must be called after rbd_obj_calc_img_extents().
+ */
+static bool rbd_obj_copyup_enabled(struct rbd_obj_request *obj_req)
+{
+ if (!obj_req->num_img_extents ||
+ (rbd_obj_is_entire(obj_req) &&
+ !obj_req->img_request->snapc->num_snaps))
+ return false;
+
+ return true;
+}
+
static u64 rbd_obj_img_extents_bytes(struct rbd_obj_request *obj_req)
{
return ceph_file_extents_bytes(obj_req->img_extents,
@@ -1422,6 +1450,7 @@ static bool rbd_img_is_write(struct rbd_img_request *img_req)
return false;
case OBJ_OP_WRITE:
case OBJ_OP_DISCARD:
+ case OBJ_OP_ZEROOUT:
return true;
default:
BUG();
@@ -1470,18 +1499,16 @@ static void rbd_osd_req_format_write(struct rbd_obj_request *obj_request)
}
static struct ceph_osd_request *
-rbd_osd_req_create(struct rbd_obj_request *obj_req, unsigned int num_ops)
+__rbd_osd_req_create(struct rbd_obj_request *obj_req,
+ struct ceph_snap_context *snapc, unsigned int num_ops)
{
- struct rbd_img_request *img_req = obj_req->img_request;
- struct rbd_device *rbd_dev = img_req->rbd_dev;
+ struct rbd_device *rbd_dev = obj_req->img_request->rbd_dev;
struct ceph_osd_client *osdc = &rbd_dev->rbd_client->client->osdc;
struct ceph_osd_request *req;
const char *name_format = rbd_dev->image_format == 1 ?
RBD_V1_DATA_FORMAT : RBD_V2_DATA_FORMAT;
- req = ceph_osdc_alloc_request(osdc,
- (rbd_img_is_write(img_req) ? img_req->snapc : NULL),
- num_ops, false, GFP_NOIO);
+ req = ceph_osdc_alloc_request(osdc, snapc, num_ops, false, GFP_NOIO);
if (!req)
return NULL;
@@ -1506,6 +1533,13 @@ err_req:
return NULL;
}
+static struct ceph_osd_request *
+rbd_osd_req_create(struct rbd_obj_request *obj_req, unsigned int num_ops)
+{
+ return __rbd_osd_req_create(obj_req, obj_req->img_request->snapc,
+ num_ops);
+}
+
static void rbd_osd_req_destroy(struct ceph_osd_request *osd_req)
{
ceph_osdc_put_request(osd_req);
@@ -1671,7 +1705,6 @@ static void rbd_img_request_destroy(struct kref *kref)
for_each_obj_request_safe(img_request, obj_request, next_obj_request)
rbd_img_obj_request_del(img_request, obj_request);
- rbd_assert(img_request->obj_request_count == 0);
if (img_request_layered_test(img_request)) {
img_request_layered_clear(img_request);
@@ -1754,7 +1787,7 @@ static void rbd_osd_req_setup_data(struct rbd_obj_request *obj_req, u32 which)
static int rbd_obj_setup_read(struct rbd_obj_request *obj_req)
{
- obj_req->osd_req = rbd_osd_req_create(obj_req, 1);
+ obj_req->osd_req = __rbd_osd_req_create(obj_req, NULL, 1);
if (!obj_req->osd_req)
return -ENOMEM;
@@ -1790,6 +1823,11 @@ static int __rbd_obj_setup_stat(struct rbd_obj_request *obj_req,
return 0;
}
+static int count_write_ops(struct rbd_obj_request *obj_req)
+{
+ return 2; /* setallochint + write/writefull */
+}
+
static void __rbd_obj_setup_write(struct rbd_obj_request *obj_req,
unsigned int which)
{
@@ -1816,6 +1854,7 @@ static void __rbd_obj_setup_write(struct rbd_obj_request *obj_req,
static int rbd_obj_setup_write(struct rbd_obj_request *obj_req)
{
unsigned int num_osd_ops, which = 0;
+ bool need_guard;
int ret;
/* reverse map the entire object onto the parent */
@@ -1823,47 +1862,112 @@ static int rbd_obj_setup_write(struct rbd_obj_request *obj_req)
if (ret)
return ret;
- if (obj_req->num_img_extents) {
- obj_req->write_state = RBD_OBJ_WRITE_GUARD;
- num_osd_ops = 3; /* stat + setallochint + write/writefull */
- } else {
- obj_req->write_state = RBD_OBJ_WRITE_FLAT;
- num_osd_ops = 2; /* setallochint + write/writefull */
- }
+ need_guard = rbd_obj_copyup_enabled(obj_req);
+ num_osd_ops = need_guard + count_write_ops(obj_req);
obj_req->osd_req = rbd_osd_req_create(obj_req, num_osd_ops);
if (!obj_req->osd_req)
return -ENOMEM;
- if (obj_req->num_img_extents) {
+ if (need_guard) {
ret = __rbd_obj_setup_stat(obj_req, which++);
if (ret)
return ret;
+
+ obj_req->write_state = RBD_OBJ_WRITE_GUARD;
+ } else {
+ obj_req->write_state = RBD_OBJ_WRITE_FLAT;
}
__rbd_obj_setup_write(obj_req, which);
return 0;
}
-static void __rbd_obj_setup_discard(struct rbd_obj_request *obj_req,
+static u16 truncate_or_zero_opcode(struct rbd_obj_request *obj_req)
+{
+ return rbd_obj_is_tail(obj_req) ? CEPH_OSD_OP_TRUNCATE :
+ CEPH_OSD_OP_ZERO;
+}
+
+static int rbd_obj_setup_discard(struct rbd_obj_request *obj_req)
+{
+ struct rbd_device *rbd_dev = obj_req->img_request->rbd_dev;
+ u64 off = obj_req->ex.oe_off;
+ u64 next_off = obj_req->ex.oe_off + obj_req->ex.oe_len;
+ int ret;
+
+ /*
+ * Align the range to alloc_size boundary and punt on discards
+ * that are too small to free up any space.
+ *
+ * alloc_size == object_size && is_tail() is a special case for
+ * filestore with filestore_punch_hole = false, needed to allow
+ * truncate (in addition to delete).
+ */
+ if (rbd_dev->opts->alloc_size != rbd_dev->layout.object_size ||
+ !rbd_obj_is_tail(obj_req)) {
+ off = round_up(off, rbd_dev->opts->alloc_size);
+ next_off = round_down(next_off, rbd_dev->opts->alloc_size);
+ if (off >= next_off)
+ return 1;
+ }
+
+ /* reverse map the entire object onto the parent */
+ ret = rbd_obj_calc_img_extents(obj_req, true);
+ if (ret)
+ return ret;
+
+ obj_req->osd_req = rbd_osd_req_create(obj_req, 1);
+ if (!obj_req->osd_req)
+ return -ENOMEM;
+
+ if (rbd_obj_is_entire(obj_req) && !obj_req->num_img_extents) {
+ osd_req_op_init(obj_req->osd_req, 0, CEPH_OSD_OP_DELETE, 0);
+ } else {
+ dout("%s %p %llu~%llu -> %llu~%llu\n", __func__,
+ obj_req, obj_req->ex.oe_off, obj_req->ex.oe_len,
+ off, next_off - off);
+ osd_req_op_extent_init(obj_req->osd_req, 0,
+ truncate_or_zero_opcode(obj_req),
+ off, next_off - off, 0, 0);
+ }
+
+ obj_req->write_state = RBD_OBJ_WRITE_FLAT;
+ rbd_osd_req_format_write(obj_req);
+ return 0;
+}
+
+static int count_zeroout_ops(struct rbd_obj_request *obj_req)
+{
+ int num_osd_ops;
+
+ if (rbd_obj_is_entire(obj_req) && obj_req->num_img_extents &&
+ !rbd_obj_copyup_enabled(obj_req))
+ num_osd_ops = 2; /* create + truncate */
+ else
+ num_osd_ops = 1; /* delete/truncate/zero */
+
+ return num_osd_ops;
+}
+
+static void __rbd_obj_setup_zeroout(struct rbd_obj_request *obj_req,
unsigned int which)
{
u16 opcode;
if (rbd_obj_is_entire(obj_req)) {
if (obj_req->num_img_extents) {
- osd_req_op_init(obj_req->osd_req, which++,
- CEPH_OSD_OP_CREATE, 0);
+ if (!rbd_obj_copyup_enabled(obj_req))
+ osd_req_op_init(obj_req->osd_req, which++,
+ CEPH_OSD_OP_CREATE, 0);
opcode = CEPH_OSD_OP_TRUNCATE;
} else {
osd_req_op_init(obj_req->osd_req, which++,
CEPH_OSD_OP_DELETE, 0);
opcode = 0;
}
- } else if (rbd_obj_is_tail(obj_req)) {
- opcode = CEPH_OSD_OP_TRUNCATE;
} else {
- opcode = CEPH_OSD_OP_ZERO;
+ opcode = truncate_or_zero_opcode(obj_req);
}
if (opcode)
@@ -1875,9 +1979,10 @@ static void __rbd_obj_setup_discard(struct rbd_obj_request *obj_req,
rbd_osd_req_format_write(obj_req);
}
-static int rbd_obj_setup_discard(struct rbd_obj_request *obj_req)
+static int rbd_obj_setup_zeroout(struct rbd_obj_request *obj_req)
{
unsigned int num_osd_ops, which = 0;
+ bool need_guard;
int ret;
/* reverse map the entire object onto the parent */
@@ -1885,33 +1990,24 @@ static int rbd_obj_setup_discard(struct rbd_obj_request *obj_req)
if (ret)
return ret;
- if (rbd_obj_is_entire(obj_req)) {
- obj_req->write_state = RBD_OBJ_WRITE_FLAT;
- if (obj_req->num_img_extents)
- num_osd_ops = 2; /* create + truncate */
- else
- num_osd_ops = 1; /* delete */
- } else {
- if (obj_req->num_img_extents) {
- obj_req->write_state = RBD_OBJ_WRITE_GUARD;
- num_osd_ops = 2; /* stat + truncate/zero */
- } else {
- obj_req->write_state = RBD_OBJ_WRITE_FLAT;
- num_osd_ops = 1; /* truncate/zero */
- }
- }
+ need_guard = rbd_obj_copyup_enabled(obj_req);
+ num_osd_ops = need_guard + count_zeroout_ops(obj_req);
obj_req->osd_req = rbd_osd_req_create(obj_req, num_osd_ops);
if (!obj_req->osd_req)
return -ENOMEM;
- if (!rbd_obj_is_entire(obj_req) && obj_req->num_img_extents) {
+ if (need_guard) {
ret = __rbd_obj_setup_stat(obj_req, which++);
if (ret)
return ret;
+
+ obj_req->write_state = RBD_OBJ_WRITE_GUARD;
+ } else {
+ obj_req->write_state = RBD_OBJ_WRITE_FLAT;
}
- __rbd_obj_setup_discard(obj_req, which);
+ __rbd_obj_setup_zeroout(obj_req, which);
return 0;
}
@@ -1922,10 +2018,10 @@ static int rbd_obj_setup_discard(struct rbd_obj_request *obj_req)
*/
static int __rbd_img_fill_request(struct rbd_img_request *img_req)
{
- struct rbd_obj_request *obj_req;
+ struct rbd_obj_request *obj_req, *next_obj_req;
int ret;
- for_each_obj_request(img_req, obj_req) {
+ for_each_obj_request_safe(img_req, obj_req, next_obj_req) {
switch (img_req->op_type) {
case OBJ_OP_READ:
ret = rbd_obj_setup_read(obj_req);
@@ -1936,11 +2032,20 @@ static int __rbd_img_fill_request(struct rbd_img_request *img_req)
case OBJ_OP_DISCARD:
ret = rbd_obj_setup_discard(obj_req);
break;
+ case OBJ_OP_ZEROOUT:
+ ret = rbd_obj_setup_zeroout(obj_req);
+ break;
default:
rbd_assert(0);
}
- if (ret)
+ if (ret < 0)
return ret;
+ if (ret > 0) {
+ img_req->xferred += obj_req->ex.oe_len;
+ img_req->pending_count--;
+ rbd_img_obj_request_del(img_req, obj_req);
+ continue;
+ }
ret = ceph_osdc_alloc_messages(obj_req->osd_req, GFP_NOIO);
if (ret)
@@ -2356,21 +2461,19 @@ static bool is_zero_bvecs(struct bio_vec *bvecs, u32 bytes)
return true;
}
-static int rbd_obj_issue_copyup(struct rbd_obj_request *obj_req, u32 bytes)
+#define MODS_ONLY U32_MAX
+
+static int rbd_obj_issue_copyup_empty_snapc(struct rbd_obj_request *obj_req,
+ u32 bytes)
{
- unsigned int num_osd_ops = obj_req->osd_req->r_num_ops;
int ret;
dout("%s obj_req %p bytes %u\n", __func__, obj_req, bytes);
rbd_assert(obj_req->osd_req->r_ops[0].op == CEPH_OSD_OP_STAT);
+ rbd_assert(bytes > 0 && bytes != MODS_ONLY);
rbd_osd_req_destroy(obj_req->osd_req);
- /*
- * Create a copyup request with the same number of OSD ops as
- * the original request. The original request was stat + op(s),
- * the new copyup request will be copyup + the same op(s).
- */
- obj_req->osd_req = rbd_osd_req_create(obj_req, num_osd_ops);
+ obj_req->osd_req = __rbd_osd_req_create(obj_req, &rbd_empty_snapc, 1);
if (!obj_req->osd_req)
return -ENOMEM;
@@ -2378,27 +2481,65 @@ static int rbd_obj_issue_copyup(struct rbd_obj_request *obj_req, u32 bytes)
if (ret)
return ret;
- /*
- * Only send non-zero copyup data to save some I/O and network
- * bandwidth -- zero copyup data is equivalent to the object not
- * existing.
- */
- if (is_zero_bvecs(obj_req->copyup_bvecs, bytes)) {
- dout("%s obj_req %p detected zeroes\n", __func__, obj_req);
- bytes = 0;
- }
osd_req_op_cls_request_data_bvecs(obj_req->osd_req, 0,
obj_req->copyup_bvecs,
obj_req->copyup_bvec_count,
bytes);
+ rbd_osd_req_format_write(obj_req);
- switch (obj_req->img_request->op_type) {
+ ret = ceph_osdc_alloc_messages(obj_req->osd_req, GFP_NOIO);
+ if (ret)
+ return ret;
+
+ rbd_obj_request_submit(obj_req);
+ return 0;
+}
+
+static int rbd_obj_issue_copyup_ops(struct rbd_obj_request *obj_req, u32 bytes)
+{
+ struct rbd_img_request *img_req = obj_req->img_request;
+ unsigned int num_osd_ops = (bytes != MODS_ONLY);
+ unsigned int which = 0;
+ int ret;
+
+ dout("%s obj_req %p bytes %u\n", __func__, obj_req, bytes);
+ rbd_assert(obj_req->osd_req->r_ops[0].op == CEPH_OSD_OP_STAT ||
+ obj_req->osd_req->r_ops[0].op == CEPH_OSD_OP_CALL);
+ rbd_osd_req_destroy(obj_req->osd_req);
+
+ switch (img_req->op_type) {
case OBJ_OP_WRITE:
- __rbd_obj_setup_write(obj_req, 1);
+ num_osd_ops += count_write_ops(obj_req);
break;
- case OBJ_OP_DISCARD:
- rbd_assert(!rbd_obj_is_entire(obj_req));
- __rbd_obj_setup_discard(obj_req, 1);
+ case OBJ_OP_ZEROOUT:
+ num_osd_ops += count_zeroout_ops(obj_req);
+ break;
+ default:
+ rbd_assert(0);
+ }
+
+ obj_req->osd_req = rbd_osd_req_create(obj_req, num_osd_ops);
+ if (!obj_req->osd_req)
+ return -ENOMEM;
+
+ if (bytes != MODS_ONLY) {
+ ret = osd_req_op_cls_init(obj_req->osd_req, which, "rbd",
+ "copyup");
+ if (ret)
+ return ret;
+
+ osd_req_op_cls_request_data_bvecs(obj_req->osd_req, which++,
+ obj_req->copyup_bvecs,
+ obj_req->copyup_bvec_count,
+ bytes);
+ }
+
+ switch (img_req->op_type) {
+ case OBJ_OP_WRITE:
+ __rbd_obj_setup_write(obj_req, which);
+ break;
+ case OBJ_OP_ZEROOUT:
+ __rbd_obj_setup_zeroout(obj_req, which);
break;
default:
rbd_assert(0);
@@ -2412,6 +2553,33 @@ static int rbd_obj_issue_copyup(struct rbd_obj_request *obj_req, u32 bytes)
return 0;
}
+static int rbd_obj_issue_copyup(struct rbd_obj_request *obj_req, u32 bytes)
+{
+ /*
+ * Only send non-zero copyup data to save some I/O and network
+ * bandwidth -- zero copyup data is equivalent to the object not
+ * existing.
+ */
+ if (is_zero_bvecs(obj_req->copyup_bvecs, bytes)) {
+ dout("%s obj_req %p detected zeroes\n", __func__, obj_req);
+ bytes = 0;
+ }
+
+ if (obj_req->img_request->snapc->num_snaps && bytes > 0) {
+ /*
+ * Send a copyup request with an empty snapshot context to
+ * deep-copyup the object through all existing snapshots.
+ * A second request with the current snapshot context will be
+ * sent for the actual modification.
+ */
+ obj_req->write_state = RBD_OBJ_WRITE_COPYUP_EMPTY_SNAPC;
+ return rbd_obj_issue_copyup_empty_snapc(obj_req, bytes);
+ }
+
+ obj_req->write_state = RBD_OBJ_WRITE_COPYUP_OPS;
+ return rbd_obj_issue_copyup_ops(obj_req, bytes);
+}
+
static int setup_copyup_bvecs(struct rbd_obj_request *obj_req, u64 obj_overlap)
{
u32 i;
@@ -2451,22 +2619,19 @@ static int rbd_obj_handle_write_guard(struct rbd_obj_request *obj_req)
if (!obj_req->num_img_extents) {
/*
* The overlap has become 0 (most likely because the
- * image has been flattened). Use rbd_obj_issue_copyup()
- * to re-submit the original write request -- the copyup
- * operation itself will be a no-op, since someone must
- * have populated the child object while we weren't
- * looking. Move to WRITE_FLAT state as we'll be done
- * with the operation once the null copyup completes.
+ * image has been flattened). Re-submit the original write
+ * request -- pass MODS_ONLY since the copyup isn't needed
+ * anymore.
*/
- obj_req->write_state = RBD_OBJ_WRITE_FLAT;
- return rbd_obj_issue_copyup(obj_req, 0);
+ obj_req->write_state = RBD_OBJ_WRITE_COPYUP_OPS;
+ return rbd_obj_issue_copyup_ops(obj_req, MODS_ONLY);
}
ret = setup_copyup_bvecs(obj_req, rbd_obj_img_extents_bytes(obj_req));
if (ret)
return ret;
- obj_req->write_state = RBD_OBJ_WRITE_COPYUP;
+ obj_req->write_state = RBD_OBJ_WRITE_READ_FROM_PARENT;
return rbd_obj_read_from_parent(obj_req);
}
@@ -2474,7 +2639,6 @@ static bool rbd_obj_handle_write(struct rbd_obj_request *obj_req)
{
int ret;
-again:
switch (obj_req->write_state) {
case RBD_OBJ_WRITE_GUARD:
rbd_assert(!obj_req->xferred);
@@ -2493,6 +2657,7 @@ again:
}
/* fall through */
case RBD_OBJ_WRITE_FLAT:
+ case RBD_OBJ_WRITE_COPYUP_OPS:
if (!obj_req->result)
/*
* There is no such thing as a successful short
@@ -2500,15 +2665,26 @@ again:
*/
obj_req->xferred = obj_req->ex.oe_len;
return true;
- case RBD_OBJ_WRITE_COPYUP:
- obj_req->write_state = RBD_OBJ_WRITE_GUARD;
+ case RBD_OBJ_WRITE_READ_FROM_PARENT:
if (obj_req->result)
- goto again;
+ return true;
rbd_assert(obj_req->xferred);
ret = rbd_obj_issue_copyup(obj_req, obj_req->xferred);
if (ret) {
obj_req->result = ret;
+ obj_req->xferred = 0;
+ return true;
+ }
+ return false;
+ case RBD_OBJ_WRITE_COPYUP_EMPTY_SNAPC:
+ if (obj_req->result)
+ return true;
+
+ obj_req->write_state = RBD_OBJ_WRITE_COPYUP_OPS;
+ ret = rbd_obj_issue_copyup_ops(obj_req, MODS_ONLY);
+ if (ret) {
+ obj_req->result = ret;
return true;
}
return false;
@@ -2528,6 +2704,7 @@ static bool __rbd_obj_handle_request(struct rbd_obj_request *obj_req)
case OBJ_OP_WRITE:
return rbd_obj_handle_write(obj_req);
case OBJ_OP_DISCARD:
+ case OBJ_OP_ZEROOUT:
if (rbd_obj_handle_write(obj_req)) {
/*
* Hide -ENOENT from delete/truncate/zero -- discarding
@@ -3640,9 +3817,11 @@ static void rbd_queue_workfn(struct work_struct *work)
switch (req_op(rq)) {
case REQ_OP_DISCARD:
- case REQ_OP_WRITE_ZEROES:
op_type = OBJ_OP_DISCARD;
break;
+ case REQ_OP_WRITE_ZEROES:
+ op_type = OBJ_OP_ZEROOUT;
+ break;
case REQ_OP_WRITE:
op_type = OBJ_OP_WRITE;
break;
@@ -3722,12 +3901,12 @@ static void rbd_queue_workfn(struct work_struct *work)
img_request->rq = rq;
snapc = NULL; /* img_request consumes a ref */
- if (op_type == OBJ_OP_DISCARD)
+ if (op_type == OBJ_OP_DISCARD || op_type == OBJ_OP_ZEROOUT)
result = rbd_img_fill_nodata(img_request, offset, length);
else
result = rbd_img_fill_from_bio(img_request, offset, length,
rq->bio);
- if (result)
+ if (result || !img_request->pending_count)
goto err_img_request;
rbd_img_request_submit(img_request);
@@ -3987,7 +4166,7 @@ static int rbd_init_disk(struct rbd_device *rbd_dev)
rbd_dev->tag_set.ops = &rbd_mq_ops;
rbd_dev->tag_set.queue_depth = rbd_dev->opts->queue_depth;
rbd_dev->tag_set.numa_node = NUMA_NO_NODE;
- rbd_dev->tag_set.flags = BLK_MQ_F_SHOULD_MERGE | BLK_MQ_F_SG_MERGE;
+ rbd_dev->tag_set.flags = BLK_MQ_F_SHOULD_MERGE;
rbd_dev->tag_set.nr_hw_queues = 1;
rbd_dev->tag_set.cmd_size = sizeof(struct work_struct);
@@ -4008,12 +4187,12 @@ static int rbd_init_disk(struct rbd_device *rbd_dev)
q->limits.max_sectors = queue_max_hw_sectors(q);
blk_queue_max_segments(q, USHRT_MAX);
blk_queue_max_segment_size(q, UINT_MAX);
- blk_queue_io_min(q, objset_bytes);
- blk_queue_io_opt(q, objset_bytes);
+ blk_queue_io_min(q, rbd_dev->opts->alloc_size);
+ blk_queue_io_opt(q, rbd_dev->opts->alloc_size);
if (rbd_dev->opts->trim) {
blk_queue_flag_set(QUEUE_FLAG_DISCARD, q);
- q->limits.discard_granularity = objset_bytes;
+ q->limits.discard_granularity = rbd_dev->opts->alloc_size;
blk_queue_max_discard_sectors(q, objset_bytes >> SECTOR_SHIFT);
blk_queue_max_write_zeroes_sectors(q, objset_bytes >> SECTOR_SHIFT);
}
@@ -5388,6 +5567,7 @@ static int rbd_add_parse_args(const char *buf,
pctx.opts->read_only = RBD_READ_ONLY_DEFAULT;
pctx.opts->queue_depth = RBD_QUEUE_DEPTH_DEFAULT;
+ pctx.opts->alloc_size = RBD_ALLOC_SIZE_DEFAULT;
pctx.opts->lock_timeout = RBD_LOCK_TIMEOUT_DEFAULT;
pctx.opts->lock_on_read = RBD_LOCK_ON_READ_DEFAULT;
pctx.opts->exclusive = RBD_EXCLUSIVE_DEFAULT;
@@ -5795,14 +5975,6 @@ static int rbd_dev_image_probe(struct rbd_device *rbd_dev, int depth)
ret = rbd_dev_v2_parent_info(rbd_dev);
if (ret)
goto err_out_probe;
-
- /*
- * Need to warn users if this image is the one being
- * mapped and has a parent.
- */
- if (!depth && rbd_dev->parent_spec)
- rbd_warn(rbd_dev,
- "WARNING: kernel layering is EXPERIMENTAL!");
}
ret = rbd_dev_probe_parent(rbd_dev, depth);
@@ -5885,6 +6057,12 @@ static ssize_t do_rbd_add(struct bus_type *bus,
if (rbd_dev->spec->snap_id != CEPH_NOSNAP)
rbd_dev->opts->read_only = true;
+ if (rbd_dev->opts->alloc_size > rbd_dev->layout.object_size) {
+ rbd_warn(rbd_dev, "alloc_size adjusted to %u",
+ rbd_dev->layout.object_size);
+ rbd_dev->opts->alloc_size = rbd_dev->layout.object_size;
+ }
+
rc = rbd_dev_device_setup(rbd_dev);
if (rc)
goto err_out_image_probe;
diff --git a/drivers/block/skd_main.c b/drivers/block/skd_main.c
index ab893a7571a2..7d3ad6c22ee5 100644
--- a/drivers/block/skd_main.c
+++ b/drivers/block/skd_main.c
@@ -2843,7 +2843,6 @@ static int skd_cons_disk(struct skd_device *skdev)
skdev->sgs_per_request * sizeof(struct scatterlist);
skdev->tag_set.numa_node = NUMA_NO_NODE;
skdev->tag_set.flags = BLK_MQ_F_SHOULD_MERGE |
- BLK_MQ_F_SG_MERGE |
BLK_ALLOC_POLICY_TO_MQ_FLAG(BLK_TAG_ALLOC_FIFO);
skdev->tag_set.driver_data = skdev;
rc = blk_mq_alloc_tag_set(&skdev->tag_set);
diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c
index b16a887bbd02..4bc083b7c9b5 100644
--- a/drivers/block/virtio_blk.c
+++ b/drivers/block/virtio_blk.c
@@ -723,7 +723,7 @@ static int virtblk_probe(struct virtio_device *vdev)
struct request_queue *q;
int err, index;
- u32 v, blk_size, sg_elems, opt_io_size;
+ u32 v, blk_size, max_size, sg_elems, opt_io_size;
u16 min_io_size;
u8 physical_block_exp, alignment_offset;
@@ -826,14 +826,16 @@ static int virtblk_probe(struct virtio_device *vdev)
/* No real sector limit. */
blk_queue_max_hw_sectors(q, -1U);
+ max_size = virtio_max_dma_size(vdev);
+
/* Host can optionally specify maximum segment size and number of
* segments. */
err = virtio_cread_feature(vdev, VIRTIO_BLK_F_SIZE_MAX,
struct virtio_blk_config, size_max, &v);
if (!err)
- blk_queue_max_segment_size(q, v);
- else
- blk_queue_max_segment_size(q, -1U);
+ max_size = min(max_size, v);
+
+ blk_queue_max_segment_size(q, max_size);
/* Host can optionally specify the block size of the device */
err = virtio_cread_feature(vdev, VIRTIO_BLK_F_BLK_SIZE,
diff --git a/drivers/block/xen-blkback/xenbus.c b/drivers/block/xen-blkback/xenbus.c
index a4bc74e72c39..24896ffb04ed 100644
--- a/drivers/block/xen-blkback/xenbus.c
+++ b/drivers/block/xen-blkback/xenbus.c
@@ -926,7 +926,7 @@ static int read_per_ring_refs(struct xen_blkif_ring *ring, const char *dir)
int err, i, j;
struct xen_blkif *blkif = ring->blkif;
struct xenbus_device *dev = blkif->be->dev;
- unsigned int ring_page_order, nr_grefs, evtchn;
+ unsigned int nr_grefs, evtchn;
err = xenbus_scanf(XBT_NIL, dir, "event-channel", "%u",
&evtchn);
@@ -936,43 +936,42 @@ static int read_per_ring_refs(struct xen_blkif_ring *ring, const char *dir)
return err;
}
- err = xenbus_scanf(XBT_NIL, dev->otherend, "ring-page-order", "%u",
- &ring_page_order);
- if (err != 1) {
- err = xenbus_scanf(XBT_NIL, dir, "ring-ref", "%u", &ring_ref[0]);
+ nr_grefs = blkif->nr_ring_pages;
+
+ if (unlikely(!nr_grefs)) {
+ WARN_ON(true);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < nr_grefs; i++) {
+ char ring_ref_name[RINGREF_NAME_LEN];
+
+ snprintf(ring_ref_name, RINGREF_NAME_LEN, "ring-ref%u", i);
+ err = xenbus_scanf(XBT_NIL, dir, ring_ref_name,
+ "%u", &ring_ref[i]);
+
if (err != 1) {
+ if (nr_grefs == 1)
+ break;
+
err = -EINVAL;
- xenbus_dev_fatal(dev, err, "reading %s/ring-ref", dir);
+ xenbus_dev_fatal(dev, err, "reading %s/%s",
+ dir, ring_ref_name);
return err;
}
- nr_grefs = 1;
- } else {
- unsigned int i;
+ }
- if (ring_page_order > xen_blkif_max_ring_order) {
+ if (err != 1) {
+ WARN_ON(nr_grefs != 1);
+
+ err = xenbus_scanf(XBT_NIL, dir, "ring-ref", "%u",
+ &ring_ref[0]);
+ if (err != 1) {
err = -EINVAL;
- xenbus_dev_fatal(dev, err, "%s/request %d ring page order exceed max:%d",
- dir, ring_page_order,
- xen_blkif_max_ring_order);
+ xenbus_dev_fatal(dev, err, "reading %s/ring-ref", dir);
return err;
}
-
- nr_grefs = 1 << ring_page_order;
- for (i = 0; i < nr_grefs; i++) {
- char ring_ref_name[RINGREF_NAME_LEN];
-
- snprintf(ring_ref_name, RINGREF_NAME_LEN, "ring-ref%u", i);
- err = xenbus_scanf(XBT_NIL, dir, ring_ref_name,
- "%u", &ring_ref[i]);
- if (err != 1) {
- err = -EINVAL;
- xenbus_dev_fatal(dev, err, "reading %s/%s",
- dir, ring_ref_name);
- return err;
- }
- }
}
- blkif->nr_ring_pages = nr_grefs;
for (i = 0; i < nr_grefs * XEN_BLKIF_REQS_PER_PAGE; i++) {
req = kzalloc(sizeof(*req), GFP_KERNEL);
@@ -1023,6 +1022,7 @@ fail:
static int connect_ring(struct backend_info *be)
{
struct xenbus_device *dev = be->dev;
+ struct xen_blkif *blkif = be->blkif;
unsigned int pers_grants;
char protocol[64] = "";
int err, i;
@@ -1030,28 +1030,29 @@ static int connect_ring(struct backend_info *be)
size_t xspathsize;
const size_t xenstore_path_ext_size = 11; /* sufficient for "/queue-NNN" */
unsigned int requested_num_queues = 0;
+ unsigned int ring_page_order;
pr_debug("%s %s\n", __func__, dev->otherend);
- be->blkif->blk_protocol = BLKIF_PROTOCOL_DEFAULT;
+ blkif->blk_protocol = BLKIF_PROTOCOL_DEFAULT;
err = xenbus_scanf(XBT_NIL, dev->otherend, "protocol",
"%63s", protocol);
if (err <= 0)
strcpy(protocol, "unspecified, assuming default");
else if (0 == strcmp(protocol, XEN_IO_PROTO_ABI_NATIVE))
- be->blkif->blk_protocol = BLKIF_PROTOCOL_NATIVE;
+ blkif->blk_protocol = BLKIF_PROTOCOL_NATIVE;
else if (0 == strcmp(protocol, XEN_IO_PROTO_ABI_X86_32))
- be->blkif->blk_protocol = BLKIF_PROTOCOL_X86_32;
+ blkif->blk_protocol = BLKIF_PROTOCOL_X86_32;
else if (0 == strcmp(protocol, XEN_IO_PROTO_ABI_X86_64))
- be->blkif->blk_protocol = BLKIF_PROTOCOL_X86_64;
+ blkif->blk_protocol = BLKIF_PROTOCOL_X86_64;
else {
xenbus_dev_fatal(dev, err, "unknown fe protocol %s", protocol);
return -ENOSYS;
}
pers_grants = xenbus_read_unsigned(dev->otherend, "feature-persistent",
0);
- be->blkif->vbd.feature_gnt_persistent = pers_grants;
- be->blkif->vbd.overflow_max_grants = 0;
+ blkif->vbd.feature_gnt_persistent = pers_grants;
+ blkif->vbd.overflow_max_grants = 0;
/*
* Read the number of hardware queues from frontend.
@@ -1067,16 +1068,30 @@ static int connect_ring(struct backend_info *be)
requested_num_queues, xenblk_max_queues);
return -ENOSYS;
}
- be->blkif->nr_rings = requested_num_queues;
- if (xen_blkif_alloc_rings(be->blkif))
+ blkif->nr_rings = requested_num_queues;
+ if (xen_blkif_alloc_rings(blkif))
return -ENOMEM;
pr_info("%s: using %d queues, protocol %d (%s) %s\n", dev->nodename,
- be->blkif->nr_rings, be->blkif->blk_protocol, protocol,
+ blkif->nr_rings, blkif->blk_protocol, protocol,
pers_grants ? "persistent grants" : "");
- if (be->blkif->nr_rings == 1)
- return read_per_ring_refs(&be->blkif->rings[0], dev->otherend);
+ ring_page_order = xenbus_read_unsigned(dev->otherend,
+ "ring-page-order", 0);
+
+ if (ring_page_order > xen_blkif_max_ring_order) {
+ err = -EINVAL;
+ xenbus_dev_fatal(dev, err,
+ "requested ring page order %d exceed max:%d",
+ ring_page_order,
+ xen_blkif_max_ring_order);
+ return err;
+ }
+
+ blkif->nr_ring_pages = 1 << ring_page_order;
+
+ if (blkif->nr_rings == 1)
+ return read_per_ring_refs(&blkif->rings[0], dev->otherend);
else {
xspathsize = strlen(dev->otherend) + xenstore_path_ext_size;
xspath = kmalloc(xspathsize, GFP_KERNEL);
@@ -1085,10 +1100,10 @@ static int connect_ring(struct backend_info *be)
return -ENOMEM;
}
- for (i = 0; i < be->blkif->nr_rings; i++) {
+ for (i = 0; i < blkif->nr_rings; i++) {
memset(xspath, 0, xspathsize);
snprintf(xspath, xspathsize, "%s/queue-%u", dev->otherend, i);
- err = read_per_ring_refs(&be->blkif->rings[i], xspath);
+ err = read_per_ring_refs(&blkif->rings[i], xspath);
if (err) {
kfree(xspath);
return err;
diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c
index 0ed4b200fa58..d43a5677ccbc 100644
--- a/drivers/block/xen-blkfront.c
+++ b/drivers/block/xen-blkfront.c
@@ -977,7 +977,7 @@ static int xlvbd_init_blk_queue(struct gendisk *gd, u16 sector_size,
} else
info->tag_set.queue_depth = BLK_RING_SIZE(info);
info->tag_set.numa_node = NUMA_NO_NODE;
- info->tag_set.flags = BLK_MQ_F_SHOULD_MERGE | BLK_MQ_F_SG_MERGE;
+ info->tag_set.flags = BLK_MQ_F_SHOULD_MERGE;
info->tag_set.cmd_size = sizeof(struct blkif_req);
info->tag_set.driver_data = info;
diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c
index 04ca65912638..399cad7daae7 100644
--- a/drivers/block/zram/zram_drv.c
+++ b/drivers/block/zram/zram_drv.c
@@ -41,7 +41,7 @@ static DEFINE_IDR(zram_index_idr);
static DEFINE_MUTEX(zram_index_mutex);
static int zram_major;
-static const char *default_compressor = "lzo";
+static const char *default_compressor = "lzo-rle";
/* Module params (documentation at end) */
static unsigned int num_devices = 1;
@@ -290,18 +290,8 @@ static ssize_t idle_store(struct device *dev,
struct zram *zram = dev_to_zram(dev);
unsigned long nr_pages = zram->disksize >> PAGE_SHIFT;
int index;
- char mode_buf[8];
- ssize_t sz;
- sz = strscpy(mode_buf, buf, sizeof(mode_buf));
- if (sz <= 0)
- return -EINVAL;
-
- /* ignore trailing new line */
- if (mode_buf[sz - 1] == '\n')
- mode_buf[sz - 1] = 0x00;
-
- if (strcmp(mode_buf, "all"))
+ if (!sysfs_streq(buf, "all"))
return -EINVAL;
down_read(&zram->init_lock);
@@ -635,25 +625,15 @@ static ssize_t writeback_store(struct device *dev,
struct bio bio;
struct bio_vec bio_vec;
struct page *page;
- ssize_t ret, sz;
- char mode_buf[8];
- int mode = -1;
+ ssize_t ret;
+ int mode;
unsigned long blk_idx = 0;
- sz = strscpy(mode_buf, buf, sizeof(mode_buf));
- if (sz <= 0)
- return -EINVAL;
-
- /* ignore trailing newline */
- if (mode_buf[sz - 1] == '\n')
- mode_buf[sz - 1] = 0x00;
-
- if (!strcmp(mode_buf, "idle"))
+ if (sysfs_streq(buf, "idle"))
mode = IDLE_WRITEBACK;
- else if (!strcmp(mode_buf, "huge"))
+ else if (sysfs_streq(buf, "huge"))
mode = HUGE_WRITEBACK;
-
- if (mode == -1)
+ else
return -EINVAL;
down_read(&zram->init_lock);
diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c
index 614ecdbb4ab7..933268b8d6a5 100644
--- a/drivers/cdrom/cdrom.c
+++ b/drivers/cdrom/cdrom.c
@@ -265,6 +265,7 @@
/* #define ERRLOGMASK (CD_WARNING|CD_OPEN|CD_COUNT_TRACKS|CD_CLOSE) */
/* #define ERRLOGMASK (CD_WARNING|CD_REG_UNREG|CD_DO_IOCTL|CD_OPEN|CD_CLOSE|CD_COUNT_TRACKS) */
+#include <linux/atomic.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/major.h>
@@ -3692,9 +3693,9 @@ static struct ctl_table_header *cdrom_sysctl_header;
static void cdrom_sysctl_register(void)
{
- static int initialized;
+ static atomic_t initialized = ATOMIC_INIT(0);
- if (initialized == 1)
+ if (!atomic_add_unless(&initialized, 1, 1))
return;
cdrom_sysctl_header = register_sysctl_table(cdrom_root_table);
@@ -3705,8 +3706,6 @@ static void cdrom_sysctl_register(void)
cdrom_sysctl_settings.debug = debug;
cdrom_sysctl_settings.lock = lockdoor;
cdrom_sysctl_settings.check = check_media_type;
-
- initialized = 1;
}
static void cdrom_sysctl_unregister(void)
diff --git a/drivers/char/ipmi/Kconfig b/drivers/char/ipmi/Kconfig
index c108441882cc..94719fc6ff9d 100644
--- a/drivers/char/ipmi/Kconfig
+++ b/drivers/char/ipmi/Kconfig
@@ -18,6 +18,10 @@ menuconfig IPMI_HANDLER
If unsure, say N.
config IPMI_DMI_DECODE
+ select IPMI_PLAT_DATA
+ bool
+
+config IPMI_PLAT_DATA
bool
if IPMI_HANDLER
@@ -56,6 +60,7 @@ config IPMI_DEVICE_INTERFACE
config IPMI_SI
tristate 'IPMI System Interface handler'
+ select IPMI_PLAT_DATA
help
Provides a driver for System Interfaces (KCS, SMIC, BT).
Currently, only KCS and SMIC are supported. If
diff --git a/drivers/char/ipmi/Makefile b/drivers/char/ipmi/Makefile
index 7a3baf301a8f..3f06b2062475 100644
--- a/drivers/char/ipmi/Makefile
+++ b/drivers/char/ipmi/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_IPMI_HANDLER) += ipmi_msghandler.o
obj-$(CONFIG_IPMI_DEVICE_INTERFACE) += ipmi_devintf.o
obj-$(CONFIG_IPMI_SI) += ipmi_si.o
obj-$(CONFIG_IPMI_DMI_DECODE) += ipmi_dmi.o
+obj-$(CONFIG_IPMI_PLAT_DATA) += ipmi_plat_data.o
obj-$(CONFIG_IPMI_SSIF) += ipmi_ssif.o
obj-$(CONFIG_IPMI_POWERNV) += ipmi_powernv.o
obj-$(CONFIG_IPMI_WATCHDOG) += ipmi_watchdog.o
diff --git a/drivers/char/ipmi/ipmi_devintf.c b/drivers/char/ipmi/ipmi_devintf.c
index effab11887ca..99c9f581a1f3 100644
--- a/drivers/char/ipmi/ipmi_devintf.c
+++ b/drivers/char/ipmi/ipmi_devintf.c
@@ -207,7 +207,7 @@ static int handle_recv(struct ipmi_file_private *priv,
struct list_head *entry;
struct ipmi_recv_msg *msg;
unsigned long flags;
- int rv = 0;
+ int rv = 0, rv2 = 0;
/* We claim a mutex because we don't want two
users getting something from the queue at a time.
@@ -250,7 +250,7 @@ static int handle_recv(struct ipmi_file_private *priv,
if (msg->msg.data_len > 0) {
if (rsp->msg.data_len < msg->msg.data_len) {
- rv = -EMSGSIZE;
+ rv2 = -EMSGSIZE;
if (trunc)
msg->msg.data_len = rsp->msg.data_len;
else
@@ -274,7 +274,7 @@ static int handle_recv(struct ipmi_file_private *priv,
mutex_unlock(&priv->recv_mutex);
ipmi_free_recv_msg(msg);
- return 0;
+ return rv2;
recv_putback_on_err:
/* If we got an error, put the message back onto
diff --git a/drivers/char/ipmi/ipmi_dmi.c b/drivers/char/ipmi/ipmi_dmi.c
index 249880457b17..ff0b199be472 100644
--- a/drivers/char/ipmi/ipmi_dmi.c
+++ b/drivers/char/ipmi/ipmi_dmi.c
@@ -14,6 +14,7 @@
#include <linux/property.h>
#include "ipmi_si_sm.h"
#include "ipmi_dmi.h"
+#include "ipmi_plat_data.h"
#define IPMI_DMI_TYPE_KCS 0x01
#define IPMI_DMI_TYPE_SMIC 0x02
@@ -22,7 +23,7 @@
struct ipmi_dmi_info {
enum si_type si_type;
- u32 flags;
+ unsigned int space; /* addr space for si, intf# for ssif */
unsigned long addr;
u8 slave_addr;
struct ipmi_dmi_info *next;
@@ -33,133 +34,60 @@ static struct ipmi_dmi_info *ipmi_dmi_infos;
static int ipmi_dmi_nr __initdata;
static void __init dmi_add_platform_ipmi(unsigned long base_addr,
- u32 flags,
+ unsigned int space,
u8 slave_addr,
int irq,
int offset,
int type)
{
- struct platform_device *pdev;
- struct resource r[4];
- unsigned int num_r = 1, size;
- struct property_entry p[5];
- unsigned int pidx = 0;
- char *name;
- int rv;
- enum si_type si_type;
+ const char *name;
struct ipmi_dmi_info *info;
+ struct ipmi_plat_data p;
- memset(p, 0, sizeof(p));
+ memset(&p, 0, sizeof(p));
name = "dmi-ipmi-si";
switch (type) {
case IPMI_DMI_TYPE_SSIF:
name = "dmi-ipmi-ssif";
- offset = 1;
- size = 1;
- si_type = SI_TYPE_INVALID;
+ p.type = SI_TYPE_INVALID;
break;
case IPMI_DMI_TYPE_BT:
- size = 3;
- si_type = SI_BT;
+ p.type = SI_BT;
break;
case IPMI_DMI_TYPE_KCS:
- size = 2;
- si_type = SI_KCS;
+ p.type = SI_KCS;
break;
case IPMI_DMI_TYPE_SMIC:
- size = 2;
- si_type = SI_SMIC;
+ p.type = SI_SMIC;
break;
default:
pr_err("Invalid IPMI type: %d\n", type);
return;
}
- if (si_type != SI_TYPE_INVALID)
- p[pidx++] = PROPERTY_ENTRY_U8("ipmi-type", si_type);
-
- p[pidx++] = PROPERTY_ENTRY_U8("slave-addr", slave_addr);
- p[pidx++] = PROPERTY_ENTRY_U8("addr-source", SI_SMBIOS);
+ memset(&p, 0, sizeof(p));
+ p.addr = base_addr;
+ p.space = space;
+ p.regspacing = offset;
+ p.irq = irq;
+ p.slave_addr = slave_addr;
+ p.addr_source = SI_SMBIOS;
info = kmalloc(sizeof(*info), GFP_KERNEL);
if (!info) {
pr_warn("Could not allocate dmi info\n");
} else {
- info->si_type = si_type;
- info->flags = flags;
+ info->si_type = p.type;
+ info->space = space;
info->addr = base_addr;
info->slave_addr = slave_addr;
info->next = ipmi_dmi_infos;
ipmi_dmi_infos = info;
}
- pdev = platform_device_alloc(name, ipmi_dmi_nr);
- if (!pdev) {
- pr_err("Error allocation IPMI platform device\n");
- return;
- }
-
- if (type == IPMI_DMI_TYPE_SSIF) {
- p[pidx++] = PROPERTY_ENTRY_U16("i2c-addr", base_addr);
- goto add_properties;
- }
-
- memset(r, 0, sizeof(r));
-
- r[0].start = base_addr;
- r[0].end = r[0].start + offset - 1;
- r[0].name = "IPMI Address 1";
- r[0].flags = flags;
-
- if (size > 1) {
- r[1].start = r[0].start + offset;
- r[1].end = r[1].start + offset - 1;
- r[1].name = "IPMI Address 2";
- r[1].flags = flags;
- num_r++;
- }
-
- if (size > 2) {
- r[2].start = r[1].start + offset;
- r[2].end = r[2].start + offset - 1;
- r[2].name = "IPMI Address 3";
- r[2].flags = flags;
- num_r++;
- }
-
- if (irq) {
- r[num_r].start = irq;
- r[num_r].end = irq;
- r[num_r].name = "IPMI IRQ";
- r[num_r].flags = IORESOURCE_IRQ;
- num_r++;
- }
-
- rv = platform_device_add_resources(pdev, r, num_r);
- if (rv) {
- dev_err(&pdev->dev, "Unable to add resources: %d\n", rv);
- goto err;
- }
-
-add_properties:
- rv = platform_device_add_properties(pdev, p);
- if (rv) {
- dev_err(&pdev->dev, "Unable to add properties: %d\n", rv);
- goto err;
- }
-
- rv = platform_device_add(pdev);
- if (rv) {
- dev_err(&pdev->dev, "Unable to add device: %d\n", rv);
- goto err;
- }
-
- ipmi_dmi_nr++;
- return;
-
-err:
- platform_device_put(pdev);
+ if (ipmi_platform_add(name, ipmi_dmi_nr, &p))
+ ipmi_dmi_nr++;
}
/*
@@ -169,14 +97,14 @@ err:
* This function allows an ACPI-specified IPMI device to look up the
* slave address from the DMI table.
*/
-int ipmi_dmi_get_slave_addr(enum si_type si_type, u32 flags,
+int ipmi_dmi_get_slave_addr(enum si_type si_type, unsigned int space,
unsigned long base_addr)
{
struct ipmi_dmi_info *info = ipmi_dmi_infos;
while (info) {
if (info->si_type == si_type &&
- info->flags == flags &&
+ info->space == space &&
info->addr == base_addr)
return info->slave_addr;
info = info->next;
@@ -197,13 +125,13 @@ EXPORT_SYMBOL(ipmi_dmi_get_slave_addr);
static void __init dmi_decode_ipmi(const struct dmi_header *dm)
{
- const u8 *data = (const u8 *) dm;
- u32 flags = IORESOURCE_IO;
- unsigned long base_addr;
- u8 len = dm->length;
- u8 slave_addr;
- int irq = 0, offset;
- int type;
+ const u8 *data = (const u8 *) dm;
+ int space = IPMI_IO_ADDR_SPACE;
+ unsigned long base_addr;
+ u8 len = dm->length;
+ u8 slave_addr;
+ int irq = 0, offset = 0;
+ int type;
if (len < DMI_IPMI_MIN_LENGTH)
return;
@@ -218,8 +146,7 @@ static void __init dmi_decode_ipmi(const struct dmi_header *dm)
}
if (len >= DMI_IPMI_VER2_LENGTH) {
if (type == IPMI_DMI_TYPE_SSIF) {
- offset = 0;
- flags = 0;
+ space = 0; /* Match I2C interface 0. */
base_addr = data[DMI_IPMI_ADDR] >> 1;
if (base_addr == 0) {
/*
@@ -236,7 +163,7 @@ static void __init dmi_decode_ipmi(const struct dmi_header *dm)
base_addr &= DMI_IPMI_IO_MASK;
} else {
/* Memory */
- flags = IORESOURCE_MEM;
+ space = IPMI_MEM_ADDR_SPACE;
}
/*
@@ -280,7 +207,7 @@ static void __init dmi_decode_ipmi(const struct dmi_header *dm)
offset = 1;
}
- dmi_add_platform_ipmi(base_addr, flags, slave_addr, irq,
+ dmi_add_platform_ipmi(base_addr, space, slave_addr, irq,
offset, type);
}
diff --git a/drivers/char/ipmi/ipmi_dmi.h b/drivers/char/ipmi/ipmi_dmi.h
index 8d2b094db8e6..2dbec0461d0c 100644
--- a/drivers/char/ipmi/ipmi_dmi.h
+++ b/drivers/char/ipmi/ipmi_dmi.h
@@ -4,6 +4,6 @@
*/
#ifdef CONFIG_IPMI_DMI_DECODE
-int ipmi_dmi_get_slave_addr(enum si_type si_type, u32 flags,
+int ipmi_dmi_get_slave_addr(enum si_type si_type, unsigned int space,
unsigned long base_addr);
#endif
diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c
index c518659b4d9f..e8ba67834746 100644
--- a/drivers/char/ipmi/ipmi_msghandler.c
+++ b/drivers/char/ipmi/ipmi_msghandler.c
@@ -529,9 +529,27 @@ struct ipmi_smi {
unsigned int waiting_events_count; /* How many events in queue? */
char delivering_events;
char event_msg_printed;
+
+ /* How many users are waiting for events? */
atomic_t event_waiters;
unsigned int ticks_to_req_ev;
- int last_needs_timer;
+
+ spinlock_t watch_lock; /* For dealing with watch stuff below. */
+
+ /* How many users are waiting for commands? */
+ unsigned int command_waiters;
+
+ /* How many users are waiting for watchdogs? */
+ unsigned int watchdog_waiters;
+
+ /* How many users are waiting for message responses? */
+ unsigned int response_waiters;
+
+ /*
+ * Tells what the lower layer has last been asked to watch for,
+ * messages and/or watchdogs. Protected by watch_lock.
+ */
+ unsigned int last_watch_mask;
/*
* The event receiver for my BMC, only really used at panic
@@ -925,6 +943,64 @@ static void deliver_err_response(struct ipmi_smi *intf,
deliver_local_response(intf, msg);
}
+static void smi_add_watch(struct ipmi_smi *intf, unsigned int flags)
+{
+ unsigned long iflags;
+
+ if (!intf->handlers->set_need_watch)
+ return;
+
+ spin_lock_irqsave(&intf->watch_lock, iflags);
+ if (flags & IPMI_WATCH_MASK_CHECK_MESSAGES)
+ intf->response_waiters++;
+
+ if (flags & IPMI_WATCH_MASK_CHECK_WATCHDOG)
+ intf->watchdog_waiters++;
+
+ if (flags & IPMI_WATCH_MASK_CHECK_COMMANDS)
+ intf->command_waiters++;
+
+ if ((intf->last_watch_mask & flags) != flags) {
+ intf->last_watch_mask |= flags;
+ intf->handlers->set_need_watch(intf->send_info,
+ intf->last_watch_mask);
+ }
+ spin_unlock_irqrestore(&intf->watch_lock, iflags);
+}
+
+static void smi_remove_watch(struct ipmi_smi *intf, unsigned int flags)
+{
+ unsigned long iflags;
+
+ if (!intf->handlers->set_need_watch)
+ return;
+
+ spin_lock_irqsave(&intf->watch_lock, iflags);
+ if (flags & IPMI_WATCH_MASK_CHECK_MESSAGES)
+ intf->response_waiters--;
+
+ if (flags & IPMI_WATCH_MASK_CHECK_WATCHDOG)
+ intf->watchdog_waiters--;
+
+ if (flags & IPMI_WATCH_MASK_CHECK_COMMANDS)
+ intf->command_waiters--;
+
+ flags = 0;
+ if (intf->response_waiters)
+ flags |= IPMI_WATCH_MASK_CHECK_MESSAGES;
+ if (intf->watchdog_waiters)
+ flags |= IPMI_WATCH_MASK_CHECK_WATCHDOG;
+ if (intf->command_waiters)
+ flags |= IPMI_WATCH_MASK_CHECK_COMMANDS;
+
+ if (intf->last_watch_mask != flags) {
+ intf->last_watch_mask = flags;
+ intf->handlers->set_need_watch(intf->send_info,
+ intf->last_watch_mask);
+ }
+ spin_unlock_irqrestore(&intf->watch_lock, iflags);
+}
+
/*
* Find the next sequence number not being used and add the given
* message with the given timeout to the sequence table. This must be
@@ -968,6 +1044,7 @@ static int intf_next_seq(struct ipmi_smi *intf,
*seq = i;
*seqid = intf->seq_table[i].seqid;
intf->curr_seq = (i+1)%IPMI_IPMB_NUM_SEQ;
+ smi_add_watch(intf, IPMI_WATCH_MASK_CHECK_MESSAGES);
need_waiter(intf);
} else {
rv = -EAGAIN;
@@ -1006,6 +1083,7 @@ static int intf_find_seq(struct ipmi_smi *intf,
&& (ipmi_addr_equal(addr, &msg->addr))) {
*recv_msg = msg;
intf->seq_table[seq].inuse = 0;
+ smi_remove_watch(intf, IPMI_WATCH_MASK_CHECK_MESSAGES);
rv = 0;
}
}
@@ -1067,6 +1145,7 @@ static int intf_err_seq(struct ipmi_smi *intf,
struct seq_table *ent = &intf->seq_table[seq];
ent->inuse = 0;
+ smi_remove_watch(intf, IPMI_WATCH_MASK_CHECK_MESSAGES);
msg = ent->recv_msg;
rv = 0;
}
@@ -1078,7 +1157,6 @@ static int intf_err_seq(struct ipmi_smi *intf,
return rv;
}
-
int ipmi_create_user(unsigned int if_num,
const struct ipmi_user_hndl *handler,
void *handler_data,
@@ -1139,11 +1217,9 @@ int ipmi_create_user(unsigned int if_num,
spin_lock_irqsave(&intf->seq_lock, flags);
list_add_rcu(&new_user->link, &intf->users);
spin_unlock_irqrestore(&intf->seq_lock, flags);
- if (handler->ipmi_watchdog_pretimeout) {
+ if (handler->ipmi_watchdog_pretimeout)
/* User wants pretimeouts, so make sure to watch for them. */
- if (atomic_inc_return(&intf->event_waiters) == 1)
- need_waiter(intf);
- }
+ smi_add_watch(intf, IPMI_WATCH_MASK_CHECK_WATCHDOG);
srcu_read_unlock(&ipmi_interfaces_srcu, index);
*user = new_user;
return 0;
@@ -1214,7 +1290,7 @@ static void _ipmi_destroy_user(struct ipmi_user *user)
user->handler->shutdown(user->handler_data);
if (user->handler->ipmi_watchdog_pretimeout)
- atomic_dec(&intf->event_waiters);
+ smi_remove_watch(intf, IPMI_WATCH_MASK_CHECK_WATCHDOG);
if (user->gets_events)
atomic_dec(&intf->event_waiters);
@@ -1227,6 +1303,7 @@ static void _ipmi_destroy_user(struct ipmi_user *user)
if (intf->seq_table[i].inuse
&& (intf->seq_table[i].recv_msg->user == user)) {
intf->seq_table[i].inuse = 0;
+ smi_remove_watch(intf, IPMI_WATCH_MASK_CHECK_MESSAGES);
ipmi_free_recv_msg(intf->seq_table[i].recv_msg);
}
}
@@ -1569,8 +1646,7 @@ int ipmi_register_for_cmd(struct ipmi_user *user,
goto out_unlock;
}
- if (atomic_inc_return(&intf->event_waiters) == 1)
- need_waiter(intf);
+ smi_add_watch(intf, IPMI_WATCH_MASK_CHECK_COMMANDS);
list_add_rcu(&rcvr->link, &intf->cmd_rcvrs);
@@ -1620,7 +1696,7 @@ int ipmi_unregister_for_cmd(struct ipmi_user *user,
synchronize_rcu();
release_ipmi_user(user, index);
while (rcvrs) {
- atomic_dec(&intf->event_waiters);
+ smi_remove_watch(intf, IPMI_WATCH_MASK_CHECK_COMMANDS);
rcvr = rcvrs;
rcvrs = rcvr->next;
kfree(rcvr);
@@ -1737,22 +1813,19 @@ static struct ipmi_smi_msg *smi_add_send_msg(struct ipmi_smi *intf,
return smi_msg;
}
-
static void smi_send(struct ipmi_smi *intf,
const struct ipmi_smi_handlers *handlers,
struct ipmi_smi_msg *smi_msg, int priority)
{
int run_to_completion = intf->run_to_completion;
+ unsigned long flags = 0;
- if (run_to_completion) {
- smi_msg = smi_add_send_msg(intf, smi_msg, priority);
- } else {
- unsigned long flags;
-
+ if (!run_to_completion)
spin_lock_irqsave(&intf->xmit_msgs_lock, flags);
- smi_msg = smi_add_send_msg(intf, smi_msg, priority);
+ smi_msg = smi_add_send_msg(intf, smi_msg, priority);
+
+ if (!run_to_completion)
spin_unlock_irqrestore(&intf->xmit_msgs_lock, flags);
- }
if (smi_msg)
handlers->sender(intf->send_info, smi_msg);
@@ -2676,7 +2749,7 @@ static ssize_t guid_show(struct device *dev, struct device_attribute *attr,
if (!guid_set)
return -ENOENT;
- return snprintf(buf, 38, "%pUl\n", guid.b);
+ return snprintf(buf, UUID_STRING_LEN + 1 + 1, "%pUl\n", &guid);
}
static DEVICE_ATTR_RO(guid);
@@ -3075,15 +3148,15 @@ static void guid_handler(struct ipmi_smi *intf, struct ipmi_recv_msg *msg)
goto out;
}
- if (msg->msg.data_len < 17) {
+ if (msg->msg.data_len < UUID_SIZE + 1) {
bmc->dyn_guid_set = 0;
dev_warn(intf->si_dev,
- "The GUID response from the BMC was too short, it was %d but should have been 17. Assuming GUID is not available.\n",
- msg->msg.data_len);
+ "The GUID response from the BMC was too short, it was %d but should have been %d. Assuming GUID is not available.\n",
+ msg->msg.data_len, UUID_SIZE + 1);
goto out;
}
- memcpy(bmc->fetch_guid.b, msg->msg.data + 1, 16);
+ guid_copy(&bmc->fetch_guid, (guid_t *)(msg->msg.data + 1));
/*
* Make sure the guid data is available before setting
* dyn_guid_set.
@@ -3350,6 +3423,7 @@ int ipmi_register_smi(const struct ipmi_smi_handlers *handlers,
INIT_LIST_HEAD(&intf->xmit_msgs);
INIT_LIST_HEAD(&intf->hp_xmit_msgs);
spin_lock_init(&intf->events_lock);
+ spin_lock_init(&intf->watch_lock);
atomic_set(&intf->event_waiters, 0);
intf->ticks_to_req_ev = IPMI_REQUEST_EV_TIME;
INIT_LIST_HEAD(&intf->waiting_events);
@@ -4365,6 +4439,7 @@ static void smi_recv_tasklet(unsigned long val)
intf->curr_msg = newmsg;
}
}
+
if (!run_to_completion)
spin_unlock_irqrestore(&intf->xmit_msgs_lock, flags);
if (newmsg)
@@ -4492,7 +4567,7 @@ static void check_msg_timeout(struct ipmi_smi *intf, struct seq_table *ent,
struct list_head *timeouts,
unsigned long timeout_period,
int slot, unsigned long *flags,
- unsigned int *waiting_msgs)
+ bool *need_timer)
{
struct ipmi_recv_msg *msg;
@@ -4504,13 +4579,14 @@ static void check_msg_timeout(struct ipmi_smi *intf, struct seq_table *ent,
if (timeout_period < ent->timeout) {
ent->timeout -= timeout_period;
- (*waiting_msgs)++;
+ *need_timer = true;
return;
}
if (ent->retries_left == 0) {
/* The message has used all its retries. */
ent->inuse = 0;
+ smi_remove_watch(intf, IPMI_WATCH_MASK_CHECK_MESSAGES);
msg = ent->recv_msg;
list_add_tail(&msg->link, timeouts);
if (ent->broadcast)
@@ -4523,7 +4599,7 @@ static void check_msg_timeout(struct ipmi_smi *intf, struct seq_table *ent,
struct ipmi_smi_msg *smi_msg;
/* More retries, send again. */
- (*waiting_msgs)++;
+ *need_timer = true;
/*
* Start with the max timer, set to normal timer after
@@ -4568,20 +4644,20 @@ static void check_msg_timeout(struct ipmi_smi *intf, struct seq_table *ent,
}
}
-static unsigned int ipmi_timeout_handler(struct ipmi_smi *intf,
- unsigned long timeout_period)
+static bool ipmi_timeout_handler(struct ipmi_smi *intf,
+ unsigned long timeout_period)
{
struct list_head timeouts;
struct ipmi_recv_msg *msg, *msg2;
unsigned long flags;
int i;
- unsigned int waiting_msgs = 0;
+ bool need_timer = false;
if (!intf->bmc_registered) {
kref_get(&intf->refcount);
if (!schedule_work(&intf->bmc_reg_work)) {
kref_put(&intf->refcount, intf_free);
- waiting_msgs++;
+ need_timer = true;
}
}
@@ -4601,7 +4677,7 @@ static unsigned int ipmi_timeout_handler(struct ipmi_smi *intf,
for (i = 0; i < IPMI_IPMB_NUM_SEQ; i++)
check_msg_timeout(intf, &intf->seq_table[i],
&timeouts, timeout_period, i,
- &flags, &waiting_msgs);
+ &flags, &need_timer);
spin_unlock_irqrestore(&intf->seq_lock, flags);
list_for_each_entry_safe(msg, msg2, &timeouts, link)
@@ -4632,7 +4708,7 @@ static unsigned int ipmi_timeout_handler(struct ipmi_smi *intf,
tasklet_schedule(&intf->recv_tasklet);
- return waiting_msgs;
+ return need_timer;
}
static void ipmi_request_event(struct ipmi_smi *intf)
@@ -4652,37 +4728,28 @@ static atomic_t stop_operation;
static void ipmi_timeout(struct timer_list *unused)
{
struct ipmi_smi *intf;
- int nt = 0, index;
+ bool need_timer = false;
+ int index;
if (atomic_read(&stop_operation))
return;
index = srcu_read_lock(&ipmi_interfaces_srcu);
list_for_each_entry_rcu(intf, &ipmi_interfaces, link) {
- int lnt = 0;
-
if (atomic_read(&intf->event_waiters)) {
intf->ticks_to_req_ev--;
if (intf->ticks_to_req_ev == 0) {
ipmi_request_event(intf);
intf->ticks_to_req_ev = IPMI_REQUEST_EV_TIME;
}
- lnt++;
+ need_timer = true;
}
- lnt += ipmi_timeout_handler(intf, IPMI_TIMEOUT_TIME);
-
- lnt = !!lnt;
- if (lnt != intf->last_needs_timer &&
- intf->handlers->set_need_watch)
- intf->handlers->set_need_watch(intf->send_info, lnt);
- intf->last_needs_timer = lnt;
-
- nt += lnt;
+ need_timer |= ipmi_timeout_handler(intf, IPMI_TIMEOUT_TIME);
}
srcu_read_unlock(&ipmi_interfaces_srcu, index);
- if (nt)
+ if (need_timer)
mod_timer(&ipmi_timer, jiffies + IPMI_TIMEOUT_JIFFIES);
}
diff --git a/drivers/char/ipmi/ipmi_plat_data.c b/drivers/char/ipmi/ipmi_plat_data.c
new file mode 100644
index 000000000000..8f0ca2a848eb
--- /dev/null
+++ b/drivers/char/ipmi/ipmi_plat_data.c
@@ -0,0 +1,121 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/*
+ * Add an IPMI platform device.
+ */
+
+#include <linux/platform_device.h>
+#include "ipmi_plat_data.h"
+#include "ipmi_si.h"
+
+struct platform_device *ipmi_platform_add(const char *name, unsigned int inst,
+ struct ipmi_plat_data *p)
+{
+ struct platform_device *pdev;
+ unsigned int num_r = 1, size, pidx = 0;
+ struct resource r[4];
+ struct property_entry pr[6];
+ u32 flags;
+ int rv;
+
+ memset(pr, 0, sizeof(pr));
+ memset(r, 0, sizeof(r));
+
+ if (p->type == SI_BT)
+ size = 3;
+ else if (p->type == SI_TYPE_INVALID)
+ size = 0;
+ else
+ size = 2;
+
+ if (p->regsize == 0)
+ p->regsize = DEFAULT_REGSIZE;
+ if (p->regspacing == 0)
+ p->regspacing = p->regsize;
+
+ pr[pidx++] = PROPERTY_ENTRY_U8("ipmi-type", p->type);
+ if (p->slave_addr)
+ pr[pidx++] = PROPERTY_ENTRY_U8("slave-addr", p->slave_addr);
+ pr[pidx++] = PROPERTY_ENTRY_U8("addr-source", p->addr_source);
+ if (p->regshift)
+ pr[pidx++] = PROPERTY_ENTRY_U8("reg-shift", p->regshift);
+ pr[pidx++] = PROPERTY_ENTRY_U8("reg-size", p->regsize);
+ /* Last entry must be left NULL to terminate it. */
+
+ pdev = platform_device_alloc(name, inst);
+ if (!pdev) {
+ pr_err("Error allocating IPMI platform device %s.%d\n",
+ name, inst);
+ return NULL;
+ }
+
+ if (size == 0)
+ /* An invalid or SSIF interface, no resources. */
+ goto add_properties;
+
+ /*
+ * Register spacing is derived from the resources in
+ * the IPMI platform code.
+ */
+
+ if (p->space == IPMI_IO_ADDR_SPACE)
+ flags = IORESOURCE_IO;
+ else
+ flags = IORESOURCE_MEM;
+
+ r[0].start = p->addr;
+ r[0].end = r[0].start + p->regsize - 1;
+ r[0].name = "IPMI Address 1";
+ r[0].flags = flags;
+
+ if (size > 1) {
+ r[1].start = r[0].start + p->regspacing;
+ r[1].end = r[1].start + p->regsize - 1;
+ r[1].name = "IPMI Address 2";
+ r[1].flags = flags;
+ num_r++;
+ }
+
+ if (size > 2) {
+ r[2].start = r[1].start + p->regspacing;
+ r[2].end = r[2].start + p->regsize - 1;
+ r[2].name = "IPMI Address 3";
+ r[2].flags = flags;
+ num_r++;
+ }
+
+ if (p->irq) {
+ r[num_r].start = p->irq;
+ r[num_r].end = p->irq;
+ r[num_r].name = "IPMI IRQ";
+ r[num_r].flags = IORESOURCE_IRQ;
+ num_r++;
+ }
+
+ rv = platform_device_add_resources(pdev, r, num_r);
+ if (rv) {
+ dev_err(&pdev->dev,
+ "Unable to add hard-code resources: %d\n", rv);
+ goto err;
+ }
+ add_properties:
+ rv = platform_device_add_properties(pdev, pr);
+ if (rv) {
+ dev_err(&pdev->dev,
+ "Unable to add hard-code properties: %d\n", rv);
+ goto err;
+ }
+
+ rv = platform_device_add(pdev);
+ if (rv) {
+ dev_err(&pdev->dev,
+ "Unable to add hard-code device: %d\n", rv);
+ goto err;
+ }
+ return pdev;
+
+err:
+ platform_device_put(pdev);
+ return NULL;
+}
+EXPORT_SYMBOL(ipmi_platform_add);
diff --git a/drivers/char/ipmi/ipmi_plat_data.h b/drivers/char/ipmi/ipmi_plat_data.h
new file mode 100644
index 000000000000..567cfcec8ada
--- /dev/null
+++ b/drivers/char/ipmi/ipmi_plat_data.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+
+/*
+ * Generic code to add IPMI platform devices.
+ */
+
+#include <linux/ipmi.h>
+
+struct ipmi_plat_data {
+ unsigned int type; /* si_type for si, SI_INVALID for others */
+ unsigned int space; /* addr_space for si, intf# for ssif. */
+ unsigned long addr;
+ unsigned int regspacing;
+ unsigned int regsize;
+ unsigned int regshift;
+ unsigned int irq;
+ unsigned int slave_addr;
+ enum ipmi_addr_src addr_source;
+};
+
+struct platform_device *ipmi_platform_add(const char *name, unsigned int inst,
+ struct ipmi_plat_data *p);
diff --git a/drivers/char/ipmi/ipmi_si.h b/drivers/char/ipmi/ipmi_si.h
index 52f6152d1fcb..357a229c9012 100644
--- a/drivers/char/ipmi/ipmi_si.h
+++ b/drivers/char/ipmi/ipmi_si.h
@@ -7,11 +7,9 @@
*/
#include <linux/interrupt.h>
+#include <linux/platform_device.h>
#include "ipmi_si_sm.h"
-#define IPMI_IO_ADDR_SPACE 0
-#define IPMI_MEM_ADDR_SPACE 1
-
#define DEFAULT_REGSPACING 1
#define DEFAULT_REGSIZE 1
@@ -23,11 +21,15 @@ void ipmi_irq_start_cleanup(struct si_sm_io *io);
int ipmi_std_irq_setup(struct si_sm_io *io);
void ipmi_irq_finish_setup(struct si_sm_io *io);
int ipmi_si_remove_by_dev(struct device *dev);
-void ipmi_si_remove_by_data(int addr_space, enum si_type si_type,
- unsigned long addr);
-int ipmi_si_hardcode_find_bmc(void);
+struct device *ipmi_si_remove_by_data(int addr_space, enum si_type si_type,
+ unsigned long addr);
+void ipmi_hardcode_init(void);
+void ipmi_si_hardcode_exit(void);
+void ipmi_si_hotmod_exit(void);
+int ipmi_si_hardcode_match(int addr_space, unsigned long addr);
void ipmi_si_platform_init(void);
void ipmi_si_platform_shutdown(void);
+void ipmi_remove_platform_device_by_name(char *name);
extern struct platform_driver ipmi_platform_driver;
diff --git a/drivers/char/ipmi/ipmi_si_hardcode.c b/drivers/char/ipmi/ipmi_si_hardcode.c
index 487642809c58..01946cad3d13 100644
--- a/drivers/char/ipmi/ipmi_si_hardcode.c
+++ b/drivers/char/ipmi/ipmi_si_hardcode.c
@@ -3,7 +3,9 @@
#define pr_fmt(fmt) "ipmi_hardcode: " fmt
#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
#include "ipmi_si.h"
+#include "ipmi_plat_data.h"
/*
* There can be 4 IO ports passed in (with or without IRQs), 4 addresses,
@@ -12,23 +14,22 @@
#define SI_MAX_PARMS 4
-static char *si_type[SI_MAX_PARMS];
#define MAX_SI_TYPE_STR 30
-static char si_type_str[MAX_SI_TYPE_STR];
+static char si_type_str[MAX_SI_TYPE_STR] __initdata;
static unsigned long addrs[SI_MAX_PARMS];
static unsigned int num_addrs;
static unsigned int ports[SI_MAX_PARMS];
static unsigned int num_ports;
-static int irqs[SI_MAX_PARMS];
-static unsigned int num_irqs;
-static int regspacings[SI_MAX_PARMS];
-static unsigned int num_regspacings;
-static int regsizes[SI_MAX_PARMS];
-static unsigned int num_regsizes;
-static int regshifts[SI_MAX_PARMS];
-static unsigned int num_regshifts;
-static int slave_addrs[SI_MAX_PARMS]; /* Leaving 0 chooses the default value */
-static unsigned int num_slave_addrs;
+static int irqs[SI_MAX_PARMS] __initdata;
+static unsigned int num_irqs __initdata;
+static int regspacings[SI_MAX_PARMS] __initdata;
+static unsigned int num_regspacings __initdata;
+static int regsizes[SI_MAX_PARMS] __initdata;
+static unsigned int num_regsizes __initdata;
+static int regshifts[SI_MAX_PARMS] __initdata;
+static unsigned int num_regshifts __initdata;
+static int slave_addrs[SI_MAX_PARMS] __initdata;
+static unsigned int num_slave_addrs __initdata;
module_param_string(type, si_type_str, MAX_SI_TYPE_STR, 0);
MODULE_PARM_DESC(type, "Defines the type of each interface, each"
@@ -73,12 +74,49 @@ MODULE_PARM_DESC(slave_addrs, "Set the default IPMB slave address for"
" overridden by this parm. This is an array indexed"
" by interface number.");
-int ipmi_si_hardcode_find_bmc(void)
+static void __init ipmi_hardcode_init_one(const char *si_type_str,
+ unsigned int i,
+ unsigned long addr,
+ enum ipmi_addr_space addr_space)
{
- int ret = -ENODEV;
- int i;
- struct si_sm_io io;
+ struct ipmi_plat_data p;
+
+ memset(&p, 0, sizeof(p));
+
+ if (!si_type_str || !*si_type_str || strcmp(si_type_str, "kcs") == 0) {
+ p.type = SI_KCS;
+ } else if (strcmp(si_type_str, "smic") == 0) {
+ p.type = SI_SMIC;
+ } else if (strcmp(si_type_str, "bt") == 0) {
+ p.type = SI_BT;
+ } else if (strcmp(si_type_str, "invalid") == 0) {
+ /*
+ * Allow a firmware-specified interface to be
+ * disabled.
+ */
+ p.type = SI_TYPE_INVALID;
+ } else {
+ pr_warn("Interface type specified for interface %d, was invalid: %s\n",
+ i, si_type_str);
+ return;
+ }
+
+ p.regsize = regsizes[i];
+ p.slave_addr = slave_addrs[i];
+ p.addr_source = SI_HARDCODED;
+ p.regshift = regshifts[i];
+ p.regsize = regsizes[i];
+ p.addr = addr;
+ p.space = addr_space;
+
+ ipmi_platform_add("hardcode-ipmi-si", i, &p);
+}
+
+void __init ipmi_hardcode_init(void)
+{
+ unsigned int i;
char *str;
+ char *si_type[SI_MAX_PARMS];
/* Parse out the si_type string into its components. */
str = si_type_str;
@@ -95,54 +133,41 @@ int ipmi_si_hardcode_find_bmc(void)
}
}
- memset(&io, 0, sizeof(io));
for (i = 0; i < SI_MAX_PARMS; i++) {
- if (!ports[i] && !addrs[i])
- continue;
-
- io.addr_source = SI_HARDCODED;
- pr_info("probing via hardcoded address\n");
-
- if (!si_type[i] || strcmp(si_type[i], "kcs") == 0) {
- io.si_type = SI_KCS;
- } else if (strcmp(si_type[i], "smic") == 0) {
- io.si_type = SI_SMIC;
- } else if (strcmp(si_type[i], "bt") == 0) {
- io.si_type = SI_BT;
- } else {
- pr_warn("Interface type specified for interface %d, was invalid: %s\n",
- i, si_type[i]);
- continue;
- }
+ if (i < num_ports && ports[i])
+ ipmi_hardcode_init_one(si_type[i], i, ports[i],
+ IPMI_IO_ADDR_SPACE);
+ if (i < num_addrs && addrs[i])
+ ipmi_hardcode_init_one(si_type[i], i, addrs[i],
+ IPMI_MEM_ADDR_SPACE);
+ }
+}
- if (ports[i]) {
- /* An I/O port */
- io.addr_data = ports[i];
- io.addr_type = IPMI_IO_ADDR_SPACE;
- } else if (addrs[i]) {
- /* A memory port */
- io.addr_data = addrs[i];
- io.addr_type = IPMI_MEM_ADDR_SPACE;
- } else {
- pr_warn("Interface type specified for interface %d, but port and address were not set or set to zero\n",
- i);
- continue;
- }
- io.addr = NULL;
- io.regspacing = regspacings[i];
- if (!io.regspacing)
- io.regspacing = DEFAULT_REGSPACING;
- io.regsize = regsizes[i];
- if (!io.regsize)
- io.regsize = DEFAULT_REGSIZE;
- io.regshift = regshifts[i];
- io.irq = irqs[i];
- if (io.irq)
- io.irq_setup = ipmi_std_irq_setup;
- io.slave_addr = slave_addrs[i];
-
- ret = ipmi_si_add_smi(&io);
+void ipmi_si_hardcode_exit(void)
+{
+ ipmi_remove_platform_device_by_name("hardcode-ipmi-si");
+}
+
+/*
+ * Returns true of the given address exists as a hardcoded address,
+ * false if not.
+ */
+int ipmi_si_hardcode_match(int addr_space, unsigned long addr)
+{
+ unsigned int i;
+
+ if (addr_space == IPMI_IO_ADDR_SPACE) {
+ for (i = 0; i < num_ports; i++) {
+ if (ports[i] == addr)
+ return 1;
+ }
+ } else {
+ for (i = 0; i < num_addrs; i++) {
+ if (addrs[i] == addr)
+ return 1;
+ }
}
- return ret;
+
+ return 0;
}
diff --git a/drivers/char/ipmi/ipmi_si_hotmod.c b/drivers/char/ipmi/ipmi_si_hotmod.c
index c0067fd0480d..03140f6cdf6f 100644
--- a/drivers/char/ipmi/ipmi_si_hotmod.c
+++ b/drivers/char/ipmi/ipmi_si_hotmod.c
@@ -10,7 +10,9 @@
#include <linux/moduleparam.h>
#include <linux/ipmi.h>
+#include <linux/atomic.h>
#include "ipmi_si.h"
+#include "ipmi_plat_data.h"
static int hotmod_handler(const char *val, const struct kernel_param *kp);
@@ -54,8 +56,8 @@ static const struct hotmod_vals hotmod_as[] = {
{ NULL }
};
-static int parse_str(const struct hotmod_vals *v, int *val, char *name,
- char **curr)
+static int parse_str(const struct hotmod_vals *v, unsigned int *val, char *name,
+ const char **curr)
{
char *s;
int i;
@@ -80,7 +82,7 @@ static int parse_str(const struct hotmod_vals *v, int *val, char *name,
}
static int check_hotmod_int_op(const char *curr, const char *option,
- const char *name, int *val)
+ const char *name, unsigned int *val)
{
char *n;
@@ -99,22 +101,94 @@ static int check_hotmod_int_op(const char *curr, const char *option,
return 0;
}
+static int parse_hotmod_str(const char *curr, enum hotmod_op *op,
+ struct ipmi_plat_data *h)
+{
+ char *s, *o;
+ int rv;
+ unsigned int ival;
+
+ rv = parse_str(hotmod_ops, &ival, "operation", &curr);
+ if (rv)
+ return rv;
+ *op = ival;
+
+ rv = parse_str(hotmod_si, &ival, "interface type", &curr);
+ if (rv)
+ return rv;
+ h->type = ival;
+
+ rv = parse_str(hotmod_as, &ival, "address space", &curr);
+ if (rv)
+ return rv;
+ h->space = ival;
+
+ s = strchr(curr, ',');
+ if (s) {
+ *s = '\0';
+ s++;
+ }
+ rv = kstrtoul(curr, 0, &h->addr);
+ if (rv) {
+ pr_warn("Invalid hotmod address '%s': %d\n", curr, rv);
+ return rv;
+ }
+
+ while (s) {
+ curr = s;
+ s = strchr(curr, ',');
+ if (s) {
+ *s = '\0';
+ s++;
+ }
+ o = strchr(curr, '=');
+ if (o) {
+ *o = '\0';
+ o++;
+ }
+ rv = check_hotmod_int_op(curr, o, "rsp", &h->regspacing);
+ if (rv < 0)
+ return rv;
+ else if (rv)
+ continue;
+ rv = check_hotmod_int_op(curr, o, "rsi", &h->regsize);
+ if (rv < 0)
+ return rv;
+ else if (rv)
+ continue;
+ rv = check_hotmod_int_op(curr, o, "rsh", &h->regshift);
+ if (rv < 0)
+ return rv;
+ else if (rv)
+ continue;
+ rv = check_hotmod_int_op(curr, o, "irq", &h->irq);
+ if (rv < 0)
+ return rv;
+ else if (rv)
+ continue;
+ rv = check_hotmod_int_op(curr, o, "ipmb", &h->slave_addr);
+ if (rv < 0)
+ return rv;
+ else if (rv)
+ continue;
+
+ pr_warn("Invalid hotmod option '%s'\n", curr);
+ return -EINVAL;
+ }
+
+ h->addr_source = SI_HOTMOD;
+ return 0;
+}
+
+static atomic_t hotmod_nr;
+
static int hotmod_handler(const char *val, const struct kernel_param *kp)
{
- char *str = kstrdup(val, GFP_KERNEL);
+ char *str = kstrdup(val, GFP_KERNEL), *curr, *next;
int rv;
- char *next, *curr, *s, *n, *o;
- enum hotmod_op op;
- enum si_type si_type;
- int addr_space;
- unsigned long addr;
- int regspacing;
- int regsize;
- int regshift;
- int irq;
- int ipmb;
+ struct ipmi_plat_data h;
+ unsigned int len;
int ival;
- int len;
if (!str)
return -ENOMEM;
@@ -128,11 +202,7 @@ static int hotmod_handler(const char *val, const struct kernel_param *kp)
}
for (curr = str; curr; curr = next) {
- regspacing = 1;
- regsize = 1;
- regshift = 0;
- irq = 0;
- ipmb = 0; /* Choose the default if not specified */
+ enum hotmod_op op;
next = strchr(curr, ':');
if (next) {
@@ -140,101 +210,28 @@ static int hotmod_handler(const char *val, const struct kernel_param *kp)
next++;
}
- rv = parse_str(hotmod_ops, &ival, "operation", &curr);
- if (rv)
- break;
- op = ival;
-
- rv = parse_str(hotmod_si, &ival, "interface type", &curr);
+ memset(&h, 0, sizeof(h));
+ rv = parse_hotmod_str(curr, &op, &h);
if (rv)
- break;
- si_type = ival;
-
- rv = parse_str(hotmod_as, &addr_space, "address space", &curr);
- if (rv)
- break;
-
- s = strchr(curr, ',');
- if (s) {
- *s = '\0';
- s++;
- }
- addr = simple_strtoul(curr, &n, 0);
- if ((*n != '\0') || (*curr == '\0')) {
- pr_warn("Invalid hotmod address '%s'\n", curr);
- break;
- }
-
- while (s) {
- curr = s;
- s = strchr(curr, ',');
- if (s) {
- *s = '\0';
- s++;
- }
- o = strchr(curr, '=');
- if (o) {
- *o = '\0';
- o++;
- }
- rv = check_hotmod_int_op(curr, o, "rsp", &regspacing);
- if (rv < 0)
- goto out;
- else if (rv)
- continue;
- rv = check_hotmod_int_op(curr, o, "rsi", &regsize);
- if (rv < 0)
- goto out;
- else if (rv)
- continue;
- rv = check_hotmod_int_op(curr, o, "rsh", &regshift);
- if (rv < 0)
- goto out;
- else if (rv)
- continue;
- rv = check_hotmod_int_op(curr, o, "irq", &irq);
- if (rv < 0)
- goto out;
- else if (rv)
- continue;
- rv = check_hotmod_int_op(curr, o, "ipmb", &ipmb);
- if (rv < 0)
- goto out;
- else if (rv)
- continue;
-
- rv = -EINVAL;
- pr_warn("Invalid hotmod option '%s'\n", curr);
goto out;
- }
if (op == HM_ADD) {
- struct si_sm_io io;
-
- memset(&io, 0, sizeof(io));
- io.addr_source = SI_HOTMOD;
- io.si_type = si_type;
- io.addr_data = addr;
- io.addr_type = addr_space;
-
- io.addr = NULL;
- io.regspacing = regspacing;
- if (!io.regspacing)
- io.regspacing = DEFAULT_REGSPACING;
- io.regsize = regsize;
- if (!io.regsize)
- io.regsize = DEFAULT_REGSIZE;
- io.regshift = regshift;
- io.irq = irq;
- if (io.irq)
- io.irq_setup = ipmi_std_irq_setup;
- io.slave_addr = ipmb;
-
- rv = ipmi_si_add_smi(&io);
- if (rv)
- goto out;
+ ipmi_platform_add("hotmod-ipmi-si",
+ atomic_inc_return(&hotmod_nr),
+ &h);
} else {
- ipmi_si_remove_by_data(addr_space, si_type, addr);
+ struct device *dev;
+
+ dev = ipmi_si_remove_by_data(h.space, h.type, h.addr);
+ if (dev && dev_is_platform(dev)) {
+ struct platform_device *pdev;
+
+ pdev = to_platform_device(dev);
+ if (strcmp(pdev->name, "hotmod-ipmi-si") == 0)
+ platform_device_unregister(pdev);
+ }
+ if (dev)
+ put_device(dev);
}
}
rv = len;
@@ -242,3 +239,8 @@ out:
kfree(str);
return rv;
}
+
+void ipmi_si_hotmod_exit(void)
+{
+ ipmi_remove_platform_device_by_name("hotmod-ipmi-si");
+}
diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c
index dc8603d34320..b1732882b97e 100644
--- a/drivers/char/ipmi/ipmi_si_intf.c
+++ b/drivers/char/ipmi/ipmi_si_intf.c
@@ -229,15 +229,9 @@ struct smi_info {
/* From the get device id response... */
struct ipmi_device_id device_id;
- /* Default driver model device. */
- struct platform_device *pdev;
-
/* Have we added the device group to the device? */
bool dev_group_added;
- /* Have we added the platform device? */
- bool pdev_registered;
-
/* Counters and things for the proc filesystem. */
atomic_t stats[SI_NUM_STATS];
@@ -1060,10 +1054,13 @@ static void request_events(void *send_info)
atomic_set(&smi_info->req_events, 1);
}
-static void set_need_watch(void *send_info, bool enable)
+static void set_need_watch(void *send_info, unsigned int watch_mask)
{
struct smi_info *smi_info = send_info;
unsigned long flags;
+ int enable;
+
+ enable = !!watch_mask;
atomic_set(&smi_info->need_watch, enable);
spin_lock_irqsave(&smi_info->si_lock, flags);
@@ -1642,7 +1639,7 @@ static ssize_t ipmi_params_show(struct device *dev,
return snprintf(buf, 200,
"%s,%s,0x%lx,rsp=%d,rsi=%d,rsh=%d,irq=%d,ipmb=%d\n",
si_to_str[smi_info->io.si_type],
- addr_space_to_str[smi_info->io.addr_type],
+ addr_space_to_str[smi_info->io.addr_space],
smi_info->io.addr_data,
smi_info->io.regspacing,
smi_info->io.regsize,
@@ -1840,7 +1837,7 @@ static struct smi_info *find_dup_si(struct smi_info *info)
struct smi_info *e;
list_for_each_entry(e, &smi_infos, link) {
- if (e->io.addr_type != info->io.addr_type)
+ if (e->io.addr_space != info->io.addr_space)
continue;
if (e->io.addr_data == info->io.addr_data) {
/*
@@ -1862,10 +1859,22 @@ int ipmi_si_add_smi(struct si_sm_io *io)
int rv = 0;
struct smi_info *new_smi, *dup;
+ /*
+ * If the user gave us a hard-coded device at the same
+ * address, they presumably want us to use it and not what is
+ * in the firmware.
+ */
+ if (io->addr_source != SI_HARDCODED && io->addr_source != SI_HOTMOD &&
+ ipmi_si_hardcode_match(io->addr_space, io->addr_data)) {
+ dev_info(io->dev,
+ "Hard-coded device at this address already exists");
+ return -ENODEV;
+ }
+
if (!io->io_setup) {
- if (io->addr_type == IPMI_IO_ADDR_SPACE) {
+ if (io->addr_space == IPMI_IO_ADDR_SPACE) {
io->io_setup = ipmi_si_port_setup;
- } else if (io->addr_type == IPMI_MEM_ADDR_SPACE) {
+ } else if (io->addr_space == IPMI_MEM_ADDR_SPACE) {
io->io_setup = ipmi_si_mem_setup;
} else {
return -EINVAL;
@@ -1927,7 +1936,7 @@ static int try_smi_init(struct smi_info *new_smi)
pr_info("Trying %s-specified %s state machine at %s address 0x%lx, slave address 0x%x, irq %d\n",
ipmi_addr_src_to_str(new_smi->io.addr_source),
si_to_str[new_smi->io.si_type],
- addr_space_to_str[new_smi->io.addr_type],
+ addr_space_to_str[new_smi->io.addr_space],
new_smi->io.addr_data,
new_smi->io.slave_addr, new_smi->io.irq);
@@ -1954,24 +1963,9 @@ static int try_smi_init(struct smi_info *new_smi)
/* Do this early so it's available for logs. */
if (!new_smi->io.dev) {
- init_name = kasprintf(GFP_KERNEL, "ipmi_si.%d",
- new_smi->si_num);
-
- /*
- * If we don't already have a device from something
- * else (like PCI), then register a new one.
- */
- new_smi->pdev = platform_device_alloc("ipmi_si",
- new_smi->si_num);
- if (!new_smi->pdev) {
- pr_err("Unable to allocate platform device\n");
- rv = -ENOMEM;
- goto out_err;
- }
- new_smi->io.dev = &new_smi->pdev->dev;
- new_smi->io.dev->driver = &ipmi_platform_driver.driver;
- /* Nulled by device_add() */
- new_smi->io.dev->init_name = init_name;
+ pr_err("IPMI interface added with no device\n");
+ rv = EIO;
+ goto out_err;
}
/* Allocate the state machine's data and initialize it. */
@@ -2044,17 +2038,6 @@ static int try_smi_init(struct smi_info *new_smi)
atomic_set(&new_smi->req_events, 1);
}
- if (new_smi->pdev && !new_smi->pdev_registered) {
- rv = platform_device_add(new_smi->pdev);
- if (rv) {
- dev_err(new_smi->io.dev,
- "Unable to register system interface device: %d\n",
- rv);
- goto out_err;
- }
- new_smi->pdev_registered = true;
- }
-
dev_set_drvdata(new_smi->io.dev, new_smi);
rv = device_add_group(new_smi->io.dev, &ipmi_si_dev_attr_group);
if (rv) {
@@ -2085,11 +2068,16 @@ static int try_smi_init(struct smi_info *new_smi)
WARN_ON(new_smi->io.dev->init_name != NULL);
out_err:
+ if (rv && new_smi->io.io_cleanup) {
+ new_smi->io.io_cleanup(&new_smi->io);
+ new_smi->io.io_cleanup = NULL;
+ }
+
kfree(init_name);
return rv;
}
-static int init_ipmi_si(void)
+static int __init init_ipmi_si(void)
{
struct smi_info *e;
enum ipmi_addr_src type = SI_INVALID;
@@ -2097,11 +2085,9 @@ static int init_ipmi_si(void)
if (initialized)
return 0;
- pr_info("IPMI System Interface driver\n");
+ ipmi_hardcode_init();
- /* If the user gave us a device, they presumably want us to use it */
- if (!ipmi_si_hardcode_find_bmc())
- goto do_scan;
+ pr_info("IPMI System Interface driver\n");
ipmi_si_platform_init();
@@ -2113,7 +2099,6 @@ static int init_ipmi_si(void)
with multiple BMCs we assume that there will be several instances
of a given type so if we succeed in registering a type then also
try to register everything else of the same type */
-do_scan:
mutex_lock(&smi_infos_lock);
list_for_each_entry(e, &smi_infos, link) {
/* Try to register a device if it has an IRQ and we either
@@ -2236,13 +2221,6 @@ static void cleanup_one_si(struct smi_info *smi_info)
if (smi_info->intf)
ipmi_unregister_smi(smi_info->intf);
- if (smi_info->pdev) {
- if (smi_info->pdev_registered)
- platform_device_unregister(smi_info->pdev);
- else
- platform_device_put(smi_info->pdev);
- }
-
kfree(smi_info);
}
@@ -2264,22 +2242,27 @@ int ipmi_si_remove_by_dev(struct device *dev)
return rv;
}
-void ipmi_si_remove_by_data(int addr_space, enum si_type si_type,
- unsigned long addr)
+struct device *ipmi_si_remove_by_data(int addr_space, enum si_type si_type,
+ unsigned long addr)
{
/* remove */
struct smi_info *e, *tmp_e;
+ struct device *dev = NULL;
mutex_lock(&smi_infos_lock);
list_for_each_entry_safe(e, tmp_e, &smi_infos, link) {
- if (e->io.addr_type != addr_space)
+ if (e->io.addr_space != addr_space)
continue;
if (e->io.si_type != si_type)
continue;
- if (e->io.addr_data == addr)
+ if (e->io.addr_data == addr) {
+ dev = get_device(e->io.dev);
cleanup_one_si(e);
+ }
}
mutex_unlock(&smi_infos_lock);
+
+ return dev;
}
static void cleanup_ipmi_si(void)
@@ -2299,6 +2282,9 @@ static void cleanup_ipmi_si(void)
list_for_each_entry_safe(e, tmp_e, &smi_infos, link)
cleanup_one_si(e);
mutex_unlock(&smi_infos_lock);
+
+ ipmi_si_hardcode_exit();
+ ipmi_si_hotmod_exit();
}
module_exit(cleanup_ipmi_si);
diff --git a/drivers/char/ipmi/ipmi_si_mem_io.c b/drivers/char/ipmi/ipmi_si_mem_io.c
index fd0ec8d6bf0e..75583612ab10 100644
--- a/drivers/char/ipmi/ipmi_si_mem_io.c
+++ b/drivers/char/ipmi/ipmi_si_mem_io.c
@@ -81,8 +81,6 @@ int ipmi_si_mem_setup(struct si_sm_io *io)
if (!addr)
return -ENODEV;
- io->io_cleanup = mem_cleanup;
-
/*
* Figure out the actual readb/readw/readl/etc routine to use based
* upon the register size.
@@ -141,5 +139,8 @@ int ipmi_si_mem_setup(struct si_sm_io *io)
mem_region_cleanup(io, io->io_size);
return -EIO;
}
+
+ io->io_cleanup = mem_cleanup;
+
return 0;
}
diff --git a/drivers/char/ipmi/ipmi_si_parisc.c b/drivers/char/ipmi/ipmi_si_parisc.c
index f3c99820f564..11c9160275df 100644
--- a/drivers/char/ipmi/ipmi_si_parisc.c
+++ b/drivers/char/ipmi/ipmi_si_parisc.c
@@ -15,7 +15,7 @@ static int __init ipmi_parisc_probe(struct parisc_device *dev)
io.si_type = SI_KCS;
io.addr_source = SI_DEVICETREE;
- io.addr_type = IPMI_MEM_ADDR_SPACE;
+ io.addr_space = IPMI_MEM_ADDR_SPACE;
io.addr_data = dev->hpa.start;
io.regsize = 1;
io.regspacing = 1;
diff --git a/drivers/char/ipmi/ipmi_si_pci.c b/drivers/char/ipmi/ipmi_si_pci.c
index ce00c0da5866..ce93fc7a1e36 100644
--- a/drivers/char/ipmi/ipmi_si_pci.c
+++ b/drivers/char/ipmi/ipmi_si_pci.c
@@ -107,10 +107,10 @@ static int ipmi_pci_probe(struct pci_dev *pdev,
io.addr_source_data = pdev;
if (pci_resource_flags(pdev, 0) & IORESOURCE_IO) {
- io.addr_type = IPMI_IO_ADDR_SPACE;
+ io.addr_space = IPMI_IO_ADDR_SPACE;
io.io_setup = ipmi_si_port_setup;
} else {
- io.addr_type = IPMI_MEM_ADDR_SPACE;
+ io.addr_space = IPMI_MEM_ADDR_SPACE;
io.io_setup = ipmi_si_mem_setup;
}
io.addr_data = pci_resource_start(pdev, 0);
diff --git a/drivers/char/ipmi/ipmi_si_platform.c b/drivers/char/ipmi/ipmi_si_platform.c
index 15cf819f884f..54c7ded2a1ff 100644
--- a/drivers/char/ipmi/ipmi_si_platform.c
+++ b/drivers/char/ipmi/ipmi_si_platform.c
@@ -107,11 +107,11 @@ ipmi_get_info_from_resources(struct platform_device *pdev,
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
if (res) {
- io->addr_type = IPMI_IO_ADDR_SPACE;
+ io->addr_space = IPMI_IO_ADDR_SPACE;
} else {
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res)
- io->addr_type = IPMI_MEM_ADDR_SPACE;
+ io->addr_space = IPMI_MEM_ADDR_SPACE;
}
if (!res) {
dev_err(&pdev->dev, "no I/O or memory address\n");
@@ -121,15 +121,13 @@ ipmi_get_info_from_resources(struct platform_device *pdev,
io->regspacing = DEFAULT_REGSPACING;
res_second = platform_get_resource(pdev,
- (io->addr_type == IPMI_IO_ADDR_SPACE) ?
+ (io->addr_space == IPMI_IO_ADDR_SPACE) ?
IORESOURCE_IO : IORESOURCE_MEM,
1);
if (res_second) {
if (res_second->start > io->addr_data)
io->regspacing = res_second->start - io->addr_data;
}
- io->regsize = DEFAULT_REGSIZE;
- io->regshift = 0;
return res;
}
@@ -137,7 +135,7 @@ ipmi_get_info_from_resources(struct platform_device *pdev,
static int platform_ipmi_probe(struct platform_device *pdev)
{
struct si_sm_io io;
- u8 type, slave_addr, addr_source;
+ u8 type, slave_addr, addr_source, regsize, regshift;
int rv;
rv = device_property_read_u8(&pdev->dev, "addr-source", &addr_source);
@@ -149,7 +147,7 @@ static int platform_ipmi_probe(struct platform_device *pdev)
if (addr_source == SI_SMBIOS) {
if (!si_trydmi)
return -ENODEV;
- } else {
+ } else if (addr_source != SI_HARDCODED) {
if (!si_tryplatform)
return -ENODEV;
}
@@ -169,11 +167,23 @@ static int platform_ipmi_probe(struct platform_device *pdev)
case SI_BT:
io.si_type = type;
break;
+ case SI_TYPE_INVALID: /* User disabled this in hardcode. */
+ return -ENODEV;
default:
dev_err(&pdev->dev, "ipmi-type property is invalid\n");
return -EINVAL;
}
+ io.regsize = DEFAULT_REGSIZE;
+ rv = device_property_read_u8(&pdev->dev, "reg-size", &regsize);
+ if (!rv)
+ io.regsize = regsize;
+
+ io.regshift = 0;
+ rv = device_property_read_u8(&pdev->dev, "reg-shift", &regshift);
+ if (!rv)
+ io.regshift = regshift;
+
if (!ipmi_get_info_from_resources(pdev, &io))
return -EINVAL;
@@ -193,8 +203,9 @@ static int platform_ipmi_probe(struct platform_device *pdev)
io.dev = &pdev->dev;
- pr_info("ipmi_si: SMBIOS: %s %#lx regsize %d spacing %d irq %d\n",
- (io.addr_type == IPMI_IO_ADDR_SPACE) ? "io" : "mem",
+ pr_info("ipmi_si: %s: %s %#lx regsize %d spacing %d irq %d\n",
+ ipmi_addr_src_to_str(addr_source),
+ (io.addr_space == IPMI_IO_ADDR_SPACE) ? "io" : "mem",
io.addr_data, io.regsize, io.regspacing, io.irq);
ipmi_si_add_smi(&io);
@@ -266,9 +277,9 @@ static int of_ipmi_probe(struct platform_device *pdev)
io.irq_setup = ipmi_std_irq_setup;
if (resource.flags & IORESOURCE_IO)
- io.addr_type = IPMI_IO_ADDR_SPACE;
+ io.addr_space = IPMI_IO_ADDR_SPACE;
else
- io.addr_type = IPMI_MEM_ADDR_SPACE;
+ io.addr_space = IPMI_MEM_ADDR_SPACE;
io.addr_data = resource.start;
@@ -296,15 +307,10 @@ static int of_ipmi_probe(struct platform_device *dev)
static int find_slave_address(struct si_sm_io *io, int slave_addr)
{
#ifdef CONFIG_IPMI_DMI_DECODE
- if (!slave_addr) {
- u32 flags = IORESOURCE_IO;
-
- if (io->addr_type == IPMI_MEM_ADDR_SPACE)
- flags = IORESOURCE_MEM;
-
- slave_addr = ipmi_dmi_get_slave_addr(io->si_type, flags,
+ if (!slave_addr)
+ slave_addr = ipmi_dmi_get_slave_addr(io->si_type,
+ io->addr_space,
io->addr_data);
- }
#endif
return slave_addr;
@@ -358,6 +364,9 @@ static int acpi_ipmi_probe(struct platform_device *pdev)
goto err_free;
}
+ io.regsize = DEFAULT_REGSIZE;
+ io.regshift = 0;
+
res = ipmi_get_info_from_resources(pdev, &io);
if (!res) {
rv = -EINVAL;
@@ -419,9 +428,31 @@ static int ipmi_remove(struct platform_device *pdev)
return ipmi_si_remove_by_dev(&pdev->dev);
}
+static int pdev_match_name(struct device *dev, void *data)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ const char *name = data;
+
+ return strcmp(pdev->name, name) == 0;
+}
+
+void ipmi_remove_platform_device_by_name(char *name)
+{
+ struct device *dev;
+
+ while ((dev = bus_find_device(&platform_bus_type, NULL, name,
+ pdev_match_name))) {
+ struct platform_device *pdev = to_platform_device(dev);
+
+ platform_device_unregister(pdev);
+ }
+}
+
static const struct platform_device_id si_plat_ids[] = {
- { "dmi-ipmi-si", 0 },
- { }
+ { "dmi-ipmi-si", 0 },
+ { "hardcode-ipmi-si", 0 },
+ { "hotmod-ipmi-si", 0 },
+ { }
};
struct platform_driver ipmi_platform_driver = {
diff --git a/drivers/char/ipmi/ipmi_si_port_io.c b/drivers/char/ipmi/ipmi_si_port_io.c
index ef6dffcea9fa..03924c32b6e9 100644
--- a/drivers/char/ipmi/ipmi_si_port_io.c
+++ b/drivers/char/ipmi/ipmi_si_port_io.c
@@ -68,8 +68,6 @@ int ipmi_si_port_setup(struct si_sm_io *io)
if (!addr)
return -ENODEV;
- io->io_cleanup = port_cleanup;
-
/*
* Figure out the actual inb/inw/inl/etc routine to use based
* upon the register size.
@@ -109,5 +107,8 @@ int ipmi_si_port_setup(struct si_sm_io *io)
return -EIO;
}
}
+
+ io->io_cleanup = port_cleanup;
+
return 0;
}
diff --git a/drivers/char/ipmi/ipmi_si_sm.h b/drivers/char/ipmi/ipmi_si_sm.h
index aaddf047d923..499db820fadb 100644
--- a/drivers/char/ipmi/ipmi_si_sm.h
+++ b/drivers/char/ipmi/ipmi_si_sm.h
@@ -26,6 +26,10 @@ enum si_type {
SI_TYPE_INVALID, SI_KCS, SI_SMIC, SI_BT
};
+enum ipmi_addr_space {
+ IPMI_IO_ADDR_SPACE, IPMI_MEM_ADDR_SPACE
+};
+
/*
* The structure for doing I/O in the state machine. The state
* machine doesn't have the actual I/O routines, they are done through
@@ -42,11 +46,11 @@ struct si_sm_io {
* state machine shouldn't touch these.
*/
void __iomem *addr;
- int regspacing;
- int regsize;
- int regshift;
- int addr_type;
- long addr_data;
+ unsigned int regspacing;
+ unsigned int regsize;
+ unsigned int regshift;
+ enum ipmi_addr_space addr_space;
+ unsigned long addr_data;
enum ipmi_addr_src addr_source; /* ACPI, PCI, SMBIOS, hardcode, etc. */
void (*addr_source_cleanup)(struct si_sm_io *io);
void *addr_source_data;
diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c
index b7a1ae2afaea..8b5aec5430f1 100644
--- a/drivers/char/ipmi/ipmi_ssif.c
+++ b/drivers/char/ipmi/ipmi_ssif.c
@@ -28,6 +28,7 @@
*/
#define pr_fmt(fmt) "ipmi_ssif: " fmt
+#define dev_fmt(fmt) "ipmi_ssif: " fmt
#if defined(MODVERSIONS)
#include <linux/modversions.h>
@@ -90,6 +91,12 @@
#define SSIF_MSG_JIFFIES ((SSIF_MSG_USEC * 1000) / TICK_NSEC)
#define SSIF_MSG_PART_JIFFIES ((SSIF_MSG_PART_USEC * 1000) / TICK_NSEC)
+/*
+ * Timeout for the watch, only used for get flag timer.
+ */
+#define SSIF_WATCH_MSG_TIMEOUT msecs_to_jiffies(10)
+#define SSIF_WATCH_WATCHDOG_TIMEOUT msecs_to_jiffies(250)
+
enum ssif_intf_state {
SSIF_NORMAL,
SSIF_GETTING_FLAGS,
@@ -270,6 +277,9 @@ struct ssif_info {
struct timer_list retry_timer;
int retries_left;
+ long watch_timeout; /* Timeout for flags check, 0 if off. */
+ struct timer_list watch_timer; /* Flag fetch timer. */
+
/* Info from SSIF cmd */
unsigned char max_xmit_msg_size;
unsigned char max_recv_msg_size;
@@ -319,7 +329,8 @@ static void deliver_recv_msg(struct ssif_info *ssif_info,
{
if (msg->rsp_size < 0) {
return_hosed_msg(ssif_info, msg);
- pr_err("%s: Malformed message: rsp_size = %d\n",
+ dev_err(&ssif_info->client->dev,
+ "%s: Malformed message: rsp_size = %d\n",
__func__, msg->rsp_size);
} else {
ipmi_smi_msg_received(ssif_info->intf, msg);
@@ -536,7 +547,8 @@ static void start_get(struct ssif_info *ssif_info)
if (rv < 0) {
/* request failed, just return the error. */
if (ssif_info->ssif_debug & SSIF_DEBUG_MSG)
- pr_info("Error from i2c_non_blocking_op(5)\n");
+ dev_dbg(&ssif_info->client->dev,
+ "Error from i2c_non_blocking_op(5)\n");
msg_done_handler(ssif_info, -EIO, NULL, 0);
}
@@ -560,6 +572,26 @@ static void retry_timeout(struct timer_list *t)
start_get(ssif_info);
}
+static void watch_timeout(struct timer_list *t)
+{
+ struct ssif_info *ssif_info = from_timer(ssif_info, t, watch_timer);
+ unsigned long oflags, *flags;
+
+ if (ssif_info->stopping)
+ return;
+
+ flags = ipmi_ssif_lock_cond(ssif_info, &oflags);
+ if (ssif_info->watch_timeout) {
+ mod_timer(&ssif_info->watch_timer,
+ jiffies + ssif_info->watch_timeout);
+ if (SSIF_IDLE(ssif_info)) {
+ start_flag_fetch(ssif_info, flags); /* Releases lock */
+ return;
+ }
+ ssif_info->req_flags = true;
+ }
+ ipmi_ssif_unlock_cond(ssif_info, flags);
+}
static void ssif_alert(struct i2c_client *client, enum i2c_alert_protocol type,
unsigned int data)
@@ -618,7 +650,8 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
ssif_inc_stat(ssif_info, receive_errors);
if (ssif_info->ssif_debug & SSIF_DEBUG_MSG)
- pr_info("Error in msg_done_handler: %d\n", result);
+ dev_dbg(&ssif_info->client->dev,
+ "%s: Error %d\n", __func__, result);
len = 0;
goto continue_op;
}
@@ -643,7 +676,8 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
ssif_info->recv, I2C_SMBUS_BLOCK_DATA);
if (rv < 0) {
if (ssif_info->ssif_debug & SSIF_DEBUG_MSG)
- pr_info("Error from i2c_non_blocking_op(1)\n");
+ dev_dbg(&ssif_info->client->dev,
+ "Error from i2c_non_blocking_op(1)\n");
result = -EIO;
} else
@@ -656,7 +690,8 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
if (len == 0) {
result = -EIO;
if (ssif_info->ssif_debug & SSIF_DEBUG_MSG)
- pr_info("Middle message with no data\n");
+ dev_dbg(&ssif_info->client->dev,
+ "Middle message with no data\n");
goto continue_op;
}
@@ -669,7 +704,8 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
/* All blocks but the last must have 31 data bytes. */
result = -EIO;
if (ssif_info->ssif_debug & SSIF_DEBUG_MSG)
- pr_info("Received middle message <31\n");
+ dev_dbg(&ssif_info->client->dev,
+ "Received middle message <31\n");
goto continue_op;
}
@@ -678,7 +714,8 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
/* Received message too big, abort the operation. */
result = -E2BIG;
if (ssif_info->ssif_debug & SSIF_DEBUG_MSG)
- pr_info("Received message too big\n");
+ dev_dbg(&ssif_info->client->dev,
+ "Received message too big\n");
goto continue_op;
}
@@ -709,7 +746,8 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
I2C_SMBUS_BLOCK_DATA);
if (rv < 0) {
if (ssif_info->ssif_debug & SSIF_DEBUG_MSG)
- pr_info("Error from ssif_i2c_send\n");
+ dev_dbg(&ssif_info->client->dev,
+ "Error from ssif_i2c_send\n");
result = -EIO;
} else
@@ -726,7 +764,8 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
}
if (ssif_info->ssif_debug & SSIF_DEBUG_STATE)
- pr_info("DONE 1: state = %d, result=%d\n",
+ dev_dbg(&ssif_info->client->dev,
+ "DONE 1: state = %d, result=%d\n",
ssif_info->ssif_state, result);
flags = ipmi_ssif_lock_cond(ssif_info, &oflags);
@@ -760,8 +799,9 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
*/
ssif_info->ssif_state = SSIF_NORMAL;
ipmi_ssif_unlock_cond(ssif_info, flags);
- pr_warn("Error getting flags: %d %d, %x\n",
- result, len, (len >= 3) ? data[2] : 0);
+ dev_warn(&ssif_info->client->dev,
+ "Error getting flags: %d %d, %x\n",
+ result, len, (len >= 3) ? data[2] : 0);
} else if (data[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2
|| data[1] != IPMI_GET_MSG_FLAGS_CMD) {
/*
@@ -769,8 +809,9 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
* response to a previous command.
*/
ipmi_ssif_unlock_cond(ssif_info, flags);
- pr_warn("Invalid response getting flags: %x %x\n",
- data[0], data[1]);
+ dev_warn(&ssif_info->client->dev,
+ "Invalid response getting flags: %x %x\n",
+ data[0], data[1]);
} else {
ssif_inc_stat(ssif_info, flag_fetches);
ssif_info->msg_flags = data[3];
@@ -782,12 +823,14 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
/* We cleared the flags. */
if ((result < 0) || (len < 3) || (data[2] != 0)) {
/* Error clearing flags */
- pr_warn("Error clearing flags: %d %d, %x\n",
- result, len, (len >= 3) ? data[2] : 0);
+ dev_warn(&ssif_info->client->dev,
+ "Error clearing flags: %d %d, %x\n",
+ result, len, (len >= 3) ? data[2] : 0);
} else if (data[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2
|| data[1] != IPMI_CLEAR_MSG_FLAGS_CMD) {
- pr_warn("Invalid response clearing flags: %x %x\n",
- data[0], data[1]);
+ dev_warn(&ssif_info->client->dev,
+ "Invalid response clearing flags: %x %x\n",
+ data[0], data[1]);
}
ssif_info->ssif_state = SSIF_NORMAL;
ipmi_ssif_unlock_cond(ssif_info, flags);
@@ -803,8 +846,9 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
handle_flags(ssif_info, flags);
} else if (msg->rsp[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2
|| msg->rsp[1] != IPMI_READ_EVENT_MSG_BUFFER_CMD) {
- pr_warn("Invalid response getting events: %x %x\n",
- msg->rsp[0], msg->rsp[1]);
+ dev_warn(&ssif_info->client->dev,
+ "Invalid response getting events: %x %x\n",
+ msg->rsp[0], msg->rsp[1]);
msg->done(msg);
/* Take off the event flag. */
ssif_info->msg_flags &= ~EVENT_MSG_BUFFER_FULL;
@@ -826,8 +870,9 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
handle_flags(ssif_info, flags);
} else if (msg->rsp[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2
|| msg->rsp[1] != IPMI_GET_MSG_CMD) {
- pr_warn("Invalid response clearing flags: %x %x\n",
- msg->rsp[0], msg->rsp[1]);
+ dev_warn(&ssif_info->client->dev,
+ "Invalid response clearing flags: %x %x\n",
+ msg->rsp[0], msg->rsp[1]);
msg->done(msg);
/* Take off the msg flag. */
@@ -853,7 +898,8 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
ipmi_ssif_unlock_cond(ssif_info, flags);
if (ssif_info->ssif_debug & SSIF_DEBUG_STATE)
- pr_info("DONE 2: state = %d.\n", ssif_info->ssif_state);
+ dev_dbg(&ssif_info->client->dev,
+ "DONE 2: state = %d.\n", ssif_info->ssif_state);
}
static void msg_written_handler(struct ssif_info *ssif_info, int result,
@@ -873,7 +919,8 @@ static void msg_written_handler(struct ssif_info *ssif_info, int result,
ssif_inc_stat(ssif_info, send_errors);
if (ssif_info->ssif_debug & SSIF_DEBUG_MSG)
- pr_info("%s: Out of retries\n", __func__);
+ dev_dbg(&ssif_info->client->dev,
+ "%s: Out of retries\n", __func__);
msg_done_handler(ssif_info, -EIO, NULL, 0);
return;
}
@@ -885,7 +932,8 @@ static void msg_written_handler(struct ssif_info *ssif_info, int result,
* handle it.
*/
if (ssif_info->ssif_debug & SSIF_DEBUG_MSG)
- pr_info("Error in msg_written_handler: %d\n", result);
+ dev_dbg(&ssif_info->client->dev,
+ "%s: Error %d\n", __func__, result);
msg_done_handler(ssif_info, result, NULL, 0);
return;
@@ -929,7 +977,8 @@ static void msg_written_handler(struct ssif_info *ssif_info, int result,
ssif_inc_stat(ssif_info, send_errors);
if (ssif_info->ssif_debug & SSIF_DEBUG_MSG)
- pr_info("Error from i2c_non_blocking_op(3)\n");
+ dev_dbg(&ssif_info->client->dev,
+ "Error from i2c_non_blocking_op(3)\n");
msg_done_handler(ssif_info, -EIO, NULL, 0);
}
} else {
@@ -985,7 +1034,8 @@ static int start_resend(struct ssif_info *ssif_info)
rv = ssif_i2c_send(ssif_info, msg_written_handler, I2C_SMBUS_WRITE,
command, ssif_info->data, I2C_SMBUS_BLOCK_DATA);
if (rv && (ssif_info->ssif_debug & SSIF_DEBUG_MSG))
- pr_info("Error from i2c_non_blocking_op(4)\n");
+ dev_dbg(&ssif_info->client->dev,
+ "Error from i2c_non_blocking_op(4)\n");
return rv;
}
@@ -1054,7 +1104,8 @@ static void sender(void *send_info,
struct timespec64 t;
ktime_get_real_ts64(&t);
- pr_info("**Enqueue %02x %02x: %lld.%6.6ld\n",
+ 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);
}
@@ -1073,8 +1124,7 @@ static int get_smi_info(void *send_info, struct ipmi_smi_info *data)
}
/*
- * Instead of having our own timer to periodically check the message
- * flags, we let the message handler drive us.
+ * Upper layer wants us to request events.
*/
static void request_events(void *send_info)
{
@@ -1085,18 +1135,33 @@ static void request_events(void *send_info)
return;
flags = ipmi_ssif_lock_cond(ssif_info, &oflags);
- /*
- * Request flags first, not events, because the lower layer
- * doesn't have a way to send an attention. But make sure
- * event checking still happens.
- */
ssif_info->req_events = true;
- if (SSIF_IDLE(ssif_info))
- start_flag_fetch(ssif_info, flags);
- else {
- ssif_info->req_flags = true;
- ipmi_ssif_unlock_cond(ssif_info, flags);
+ ipmi_ssif_unlock_cond(ssif_info, flags);
+}
+
+/*
+ * Upper layer is changing the flag saying whether we need to request
+ * flags periodically or not.
+ */
+static void ssif_set_need_watch(void *send_info, unsigned int watch_mask)
+{
+ struct ssif_info *ssif_info = (struct ssif_info *) send_info;
+ unsigned long oflags, *flags;
+ long timeout = 0;
+
+ if (watch_mask & IPMI_WATCH_MASK_CHECK_MESSAGES)
+ timeout = SSIF_WATCH_MSG_TIMEOUT;
+ else if (watch_mask)
+ timeout = SSIF_WATCH_WATCHDOG_TIMEOUT;
+
+ flags = ipmi_ssif_lock_cond(ssif_info, &oflags);
+ if (timeout != ssif_info->watch_timeout) {
+ ssif_info->watch_timeout = timeout;
+ if (ssif_info->watch_timeout)
+ mod_timer(&ssif_info->watch_timer,
+ jiffies + ssif_info->watch_timeout);
}
+ ipmi_ssif_unlock_cond(ssif_info, flags);
}
static int ssif_start_processing(void *send_info,
@@ -1223,6 +1288,7 @@ static void shutdown_ssif(void *send_info)
schedule_timeout(1);
ssif_info->stopping = true;
+ del_timer_sync(&ssif_info->watch_timer);
del_timer_sync(&ssif_info->retry_timer);
if (ssif_info->thread) {
complete(&ssif_info->wake_thread);
@@ -1570,7 +1636,8 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
slave_addr = find_slave_address(client, slave_addr);
- pr_info("Trying %s-specified SSIF interface at i2c address 0x%x, adapter %s, slave address 0x%x\n",
+ dev_info(&client->dev,
+ "Trying %s-specified SSIF interface at i2c address 0x%x, adapter %s, slave address 0x%x\n",
ipmi_addr_src_to_str(ssif_info->addr_source),
client->addr, client->adapter->name, slave_addr);
@@ -1585,7 +1652,8 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
if (!rv && (len >= 3) && (resp[2] == 0)) {
if (len < 7) {
if (ssif_dbg_probe)
- pr_info("SSIF info too short: %d\n", len);
+ dev_dbg(&ssif_info->client->dev,
+ "SSIF info too short: %d\n", len);
goto no_support;
}
@@ -1622,7 +1690,8 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
} else {
no_support:
/* Assume no multi-part or PEC support */
- pr_info("Error fetching SSIF: %d %d %2.2x, your system probably doesn't support this command so using defaults\n",
+ dev_info(&ssif_info->client->dev,
+ "Error fetching SSIF: %d %d %2.2x, your system probably doesn't support this command so using defaults\n",
rv, len, resp[2]);
ssif_info->max_xmit_msg_size = 32;
@@ -1639,16 +1708,18 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
msg[2] = WDT_PRE_TIMEOUT_INT;
rv = do_cmd(client, 3, msg, &len, resp);
if (rv || (len < 3) || (resp[2] != 0))
- pr_warn("Unable to clear message flags: %d %d %2.2x\n",
- rv, len, resp[2]);
+ dev_warn(&ssif_info->client->dev,
+ "Unable to clear message flags: %d %d %2.2x\n",
+ rv, len, resp[2]);
/* Attempt to enable the event buffer. */
msg[0] = IPMI_NETFN_APP_REQUEST << 2;
msg[1] = IPMI_GET_BMC_GLOBAL_ENABLES_CMD;
rv = do_cmd(client, 2, msg, &len, resp);
if (rv || (len < 4) || (resp[2] != 0)) {
- pr_warn("Error getting global enables: %d %d %2.2x\n",
- rv, len, resp[2]);
+ dev_warn(&ssif_info->client->dev,
+ "Error getting global enables: %d %d %2.2x\n",
+ rv, len, resp[2]);
rv = 0; /* Not fatal */
goto found;
}
@@ -1666,8 +1737,9 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
msg[2] = ssif_info->global_enables | IPMI_BMC_EVT_MSG_BUFF;
rv = do_cmd(client, 3, msg, &len, resp);
if (rv || (len < 2)) {
- pr_warn("Error setting global enables: %d %d %2.2x\n",
- rv, len, resp[2]);
+ dev_warn(&ssif_info->client->dev,
+ "Error setting global enables: %d %d %2.2x\n",
+ rv, len, resp[2]);
rv = 0; /* Not fatal */
goto found;
}
@@ -1687,8 +1759,9 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
msg[2] = ssif_info->global_enables | IPMI_BMC_RCV_MSG_INTR;
rv = do_cmd(client, 3, msg, &len, resp);
if (rv || (len < 2)) {
- pr_warn("Error setting global enables: %d %d %2.2x\n",
- rv, len, resp[2]);
+ dev_warn(&ssif_info->client->dev,
+ "Error setting global enables: %d %d %2.2x\n",
+ rv, len, resp[2]);
rv = 0; /* Not fatal */
goto found;
}
@@ -1701,13 +1774,15 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
found:
if (ssif_dbg_probe) {
- pr_info("ssif_probe: i2c_probe found device at i2c address %x\n",
- client->addr);
+ dev_dbg(&ssif_info->client->dev,
+ "%s: i2c_probe found device at i2c address %x\n",
+ __func__, client->addr);
}
spin_lock_init(&ssif_info->lock);
ssif_info->ssif_state = SSIF_NORMAL;
timer_setup(&ssif_info->retry_timer, retry_timeout, 0);
+ timer_setup(&ssif_info->watch_timer, watch_timeout, 0);
for (i = 0; i < SSIF_NUM_STATS; i++)
atomic_set(&ssif_info->stats[i], 0);
@@ -1721,6 +1796,7 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
ssif_info->handlers.get_smi_info = get_smi_info;
ssif_info->handlers.sender = sender;
ssif_info->handlers.request_events = request_events;
+ ssif_info->handlers.set_need_watch = ssif_set_need_watch;
{
unsigned int thread_num;
@@ -1754,8 +1830,9 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
ssif_info,
&ssif_info->client->dev,
slave_addr);
- if (rv) {
- pr_err("Unable to register device: error %d\n", rv);
+ if (rv) {
+ dev_err(&ssif_info->client->dev,
+ "Unable to register device: error %d\n", rv);
goto out_remove_attr;
}
@@ -1764,7 +1841,8 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
if (addr_info)
addr_info->client = NULL;
- dev_err(&client->dev, "Unable to start IPMI SSIF: %d\n", rv);
+ dev_err(&ssif_info->client->dev,
+ "Unable to start IPMI SSIF: %d\n", rv);
kfree(ssif_info);
}
kfree(resp);
diff --git a/drivers/char/ipmi/kcs_bmc.c b/drivers/char/ipmi/kcs_bmc.c
index e6124bd548df..ed4dc3b1843e 100644
--- a/drivers/char/ipmi/kcs_bmc.c
+++ b/drivers/char/ipmi/kcs_bmc.c
@@ -440,12 +440,13 @@ struct kcs_bmc *kcs_bmc_alloc(struct device *dev, int sizeof_priv, u32 channel)
kcs_bmc->data_in = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
kcs_bmc->data_out = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
kcs_bmc->kbuffer = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
- if (!kcs_bmc->data_in || !kcs_bmc->data_out || !kcs_bmc->kbuffer)
- return NULL;
kcs_bmc->miscdev.minor = MISC_DYNAMIC_MINOR;
kcs_bmc->miscdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s%u",
DEVICE_NAME, channel);
+ if (!kcs_bmc->data_in || !kcs_bmc->data_out || !kcs_bmc->kbuffer ||
+ !kcs_bmc->miscdev.name)
+ return NULL;
kcs_bmc->miscdev.fops = &kcs_bmc_fops;
return kcs_bmc;
diff --git a/drivers/char/tpm/eventlog/tpm1.c b/drivers/char/tpm/eventlog/tpm1.c
index 58c84784ba25..bfdff9271be0 100644
--- a/drivers/char/tpm/eventlog/tpm1.c
+++ b/drivers/char/tpm/eventlog/tpm1.c
@@ -74,7 +74,7 @@ static const char* tcpa_pc_event_id_strings[] = {
/* returns pointer to start of pos. entry of tcg log */
static void *tpm1_bios_measurements_start(struct seq_file *m, loff_t *pos)
{
- loff_t i;
+ loff_t i = 0;
struct tpm_chip *chip = m->private;
struct tpm_bios_log *log = &chip->log;
void *addr = log->bios_event_log;
@@ -83,38 +83,29 @@ static void *tpm1_bios_measurements_start(struct seq_file *m, loff_t *pos)
u32 converted_event_size;
u32 converted_event_type;
-
/* read over *pos measurements */
- for (i = 0; i < *pos; i++) {
+ do {
event = addr;
+ /* check if current entry is valid */
+ if (addr + sizeof(struct tcpa_event) > limit)
+ return NULL;
+
converted_event_size =
do_endian_conversion(event->event_size);
converted_event_type =
do_endian_conversion(event->event_type);
- if ((addr + sizeof(struct tcpa_event)) < limit) {
- if ((converted_event_type == 0) &&
- (converted_event_size == 0))
- return NULL;
- addr += (sizeof(struct tcpa_event) +
- converted_event_size);
- }
- }
-
- /* now check if current entry is valid */
- if ((addr + sizeof(struct tcpa_event)) >= limit)
- return NULL;
-
- event = addr;
+ if (((converted_event_type == 0) && (converted_event_size == 0))
+ || ((addr + sizeof(struct tcpa_event) + converted_event_size)
+ > limit))
+ return NULL;
- converted_event_size = do_endian_conversion(event->event_size);
- converted_event_type = do_endian_conversion(event->event_type);
+ if (i++ == *pos)
+ break;
- if (((converted_event_type == 0) && (converted_event_size == 0))
- || ((addr + sizeof(struct tcpa_event) + converted_event_size)
- >= limit))
- return NULL;
+ addr += (sizeof(struct tcpa_event) + converted_event_size);
+ } while (1);
return addr;
}
@@ -134,7 +125,7 @@ static void *tpm1_bios_measurements_next(struct seq_file *m, void *v,
v += sizeof(struct tcpa_event) + converted_event_size;
/* now check if current entry is valid */
- if ((v + sizeof(struct tcpa_event)) >= limit)
+ if ((v + sizeof(struct tcpa_event)) > limit)
return NULL;
event = v;
@@ -143,7 +134,7 @@ static void *tpm1_bios_measurements_next(struct seq_file *m, void *v,
converted_event_type = do_endian_conversion(event->event_type);
if (((converted_event_type == 0) && (converted_event_size == 0)) ||
- ((v + sizeof(struct tcpa_event) + converted_event_size) >= limit))
+ ((v + sizeof(struct tcpa_event) + converted_event_size) > limit))
return NULL;
(*pos)++;
diff --git a/drivers/char/tpm/eventlog/tpm2.c b/drivers/char/tpm/eventlog/tpm2.c
index 1b8fa9de2cac..d8b77133a83a 100644
--- a/drivers/char/tpm/eventlog/tpm2.c
+++ b/drivers/char/tpm/eventlog/tpm2.c
@@ -37,10 +37,10 @@
*
* Returns size of the event. If it is an invalid event, returns 0.
*/
-static int calc_tpm2_event_size(struct tcg_pcr_event2 *event,
+static int calc_tpm2_event_size(struct tcg_pcr_event2_head *event,
struct tcg_pcr_event *event_header)
{
- struct tcg_efi_specid_event *efispecid;
+ struct tcg_efi_specid_event_head *efispecid;
struct tcg_event_field *event_field;
void *marker;
void *marker_start;
@@ -55,7 +55,7 @@ static int calc_tpm2_event_size(struct tcg_pcr_event2 *event,
marker = marker + sizeof(event->pcr_idx) + sizeof(event->event_type)
+ sizeof(event->count);
- efispecid = (struct tcg_efi_specid_event *)event_header->event;
+ efispecid = (struct tcg_efi_specid_event_head *)event_header->event;
/* Check if event is malformed. */
if (event->count > efispecid->num_algs)
@@ -95,7 +95,7 @@ static void *tpm2_bios_measurements_start(struct seq_file *m, loff_t *pos)
void *addr = log->bios_event_log;
void *limit = log->bios_event_log_end;
struct tcg_pcr_event *event_header;
- struct tcg_pcr_event2 *event;
+ struct tcg_pcr_event2_head *event;
size_t size;
int i;
@@ -136,7 +136,7 @@ static void *tpm2_bios_measurements_next(struct seq_file *m, void *v,
loff_t *pos)
{
struct tcg_pcr_event *event_header;
- struct tcg_pcr_event2 *event;
+ struct tcg_pcr_event2_head *event;
struct tpm_chip *chip = m->private;
struct tpm_bios_log *log = &chip->log;
void *limit = log->bios_event_log_end;
@@ -180,7 +180,7 @@ static int tpm2_binary_bios_measurements_show(struct seq_file *m, void *v)
struct tpm_chip *chip = m->private;
struct tpm_bios_log *log = &chip->log;
struct tcg_pcr_event *event_header = log->bios_event_log;
- struct tcg_pcr_event2 *event = v;
+ struct tcg_pcr_event2_head *event = v;
void *temp_ptr;
size_t size;
diff --git a/drivers/char/tpm/st33zp24/i2c.c b/drivers/char/tpm/st33zp24/i2c.c
index be5d1abd3e8e..8390c5b54c3b 100644
--- a/drivers/char/tpm/st33zp24/i2c.c
+++ b/drivers/char/tpm/st33zp24/i2c.c
@@ -33,7 +33,7 @@
struct st33zp24_i2c_phy {
struct i2c_client *client;
- u8 buf[TPM_BUFSIZE + 1];
+ u8 buf[ST33ZP24_BUFSIZE + 1];
int io_lpcpd;
};
diff --git a/drivers/char/tpm/st33zp24/spi.c b/drivers/char/tpm/st33zp24/spi.c
index d7909ab287a8..ff019a1e3c68 100644
--- a/drivers/char/tpm/st33zp24/spi.c
+++ b/drivers/char/tpm/st33zp24/spi.c
@@ -63,7 +63,7 @@
* some latency byte before the answer is available (max 15).
* We have 2048 + 1024 + 15.
*/
-#define ST33ZP24_SPI_BUFFER_SIZE (TPM_BUFSIZE + (TPM_BUFSIZE / 2) +\
+#define ST33ZP24_SPI_BUFFER_SIZE (ST33ZP24_BUFSIZE + (ST33ZP24_BUFSIZE / 2) +\
MAX_SPI_LATENCY)
diff --git a/drivers/char/tpm/st33zp24/st33zp24.c b/drivers/char/tpm/st33zp24/st33zp24.c
index 64dc560859f2..13dc614b7ebc 100644
--- a/drivers/char/tpm/st33zp24/st33zp24.c
+++ b/drivers/char/tpm/st33zp24/st33zp24.c
@@ -436,7 +436,7 @@ static int st33zp24_send(struct tpm_chip *chip, unsigned char *buf,
goto out_err;
}
- return len;
+ return 0;
out_err:
st33zp24_cancel(chip);
release_locality(chip);
diff --git a/drivers/char/tpm/st33zp24/st33zp24.h b/drivers/char/tpm/st33zp24/st33zp24.h
index 6f4a4198af6a..20da0a84988d 100644
--- a/drivers/char/tpm/st33zp24/st33zp24.h
+++ b/drivers/char/tpm/st33zp24/st33zp24.h
@@ -18,8 +18,8 @@
#ifndef __LOCAL_ST33ZP24_H__
#define __LOCAL_ST33ZP24_H__
-#define TPM_WRITE_DIRECTION 0x80
-#define TPM_BUFSIZE 2048
+#define TPM_WRITE_DIRECTION 0x80
+#define ST33ZP24_BUFSIZE 2048
struct st33zp24_dev {
struct tpm_chip *chip;
diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c
index 32db84683c40..8804c9e916fd 100644
--- a/drivers/char/tpm/tpm-chip.c
+++ b/drivers/char/tpm/tpm-chip.c
@@ -37,6 +37,103 @@ struct class *tpm_class;
struct class *tpmrm_class;
dev_t tpm_devt;
+static int tpm_request_locality(struct tpm_chip *chip)
+{
+ int rc;
+
+ if (!chip->ops->request_locality)
+ return 0;
+
+ rc = chip->ops->request_locality(chip, 0);
+ if (rc < 0)
+ return rc;
+
+ chip->locality = rc;
+ return 0;
+}
+
+static void tpm_relinquish_locality(struct tpm_chip *chip)
+{
+ int rc;
+
+ if (!chip->ops->relinquish_locality)
+ return;
+
+ rc = chip->ops->relinquish_locality(chip, chip->locality);
+ if (rc)
+ dev_err(&chip->dev, "%s: : error %d\n", __func__, rc);
+
+ chip->locality = -1;
+}
+
+static int tpm_cmd_ready(struct tpm_chip *chip)
+{
+ if (!chip->ops->cmd_ready)
+ return 0;
+
+ return chip->ops->cmd_ready(chip);
+}
+
+static int tpm_go_idle(struct tpm_chip *chip)
+{
+ if (!chip->ops->go_idle)
+ return 0;
+
+ return chip->ops->go_idle(chip);
+}
+
+/**
+ * tpm_chip_start() - power on the TPM
+ * @chip: a TPM chip to use
+ *
+ * Return:
+ * * The response length - OK
+ * * -errno - A system error
+ */
+int tpm_chip_start(struct tpm_chip *chip)
+{
+ int ret;
+
+ if (chip->ops->clk_enable)
+ chip->ops->clk_enable(chip, true);
+
+ if (chip->locality == -1) {
+ ret = tpm_request_locality(chip);
+ if (ret) {
+ chip->ops->clk_enable(chip, false);
+ return ret;
+ }
+ }
+
+ ret = tpm_cmd_ready(chip);
+ if (ret) {
+ tpm_relinquish_locality(chip);
+ if (chip->ops->clk_enable)
+ chip->ops->clk_enable(chip, false);
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(tpm_chip_start);
+
+/**
+ * tpm_chip_stop() - power off the TPM
+ * @chip: a TPM chip to use
+ *
+ * Return:
+ * * The response length - OK
+ * * -errno - A system error
+ */
+void tpm_chip_stop(struct tpm_chip *chip)
+{
+ tpm_go_idle(chip);
+ tpm_relinquish_locality(chip);
+ if (chip->ops->clk_enable)
+ chip->ops->clk_enable(chip, false);
+}
+EXPORT_SYMBOL_GPL(tpm_chip_stop);
+
/**
* tpm_try_get_ops() - Get a ref to the tpm_chip
* @chip: Chip to ref
@@ -56,10 +153,17 @@ int tpm_try_get_ops(struct tpm_chip *chip)
down_read(&chip->ops_sem);
if (!chip->ops)
+ goto out_ops;
+
+ mutex_lock(&chip->tpm_mutex);
+ rc = tpm_chip_start(chip);
+ if (rc)
goto out_lock;
return 0;
out_lock:
+ mutex_unlock(&chip->tpm_mutex);
+out_ops:
up_read(&chip->ops_sem);
put_device(&chip->dev);
return rc;
@@ -75,6 +179,8 @@ EXPORT_SYMBOL_GPL(tpm_try_get_ops);
*/
void tpm_put_ops(struct tpm_chip *chip)
{
+ tpm_chip_stop(chip);
+ mutex_unlock(&chip->tpm_mutex);
up_read(&chip->ops_sem);
put_device(&chip->dev);
}
@@ -160,6 +266,7 @@ static void tpm_dev_release(struct device *dev)
kfree(chip->log.bios_event_log);
kfree(chip->work_space.context_buf);
kfree(chip->work_space.session_buf);
+ kfree(chip->allocated_banks);
kfree(chip);
}
@@ -189,7 +296,10 @@ static int tpm_class_shutdown(struct device *dev)
if (chip->flags & TPM_CHIP_FLAG_TPM2) {
down_write(&chip->ops_sem);
- tpm2_shutdown(chip, TPM2_SU_CLEAR);
+ if (!tpm_chip_start(chip)) {
+ tpm2_shutdown(chip, TPM2_SU_CLEAR);
+ tpm_chip_stop(chip);
+ }
chip->ops = NULL;
up_write(&chip->ops_sem);
}
@@ -368,8 +478,12 @@ static void tpm_del_char_device(struct tpm_chip *chip)
/* Make the driver uncallable. */
down_write(&chip->ops_sem);
- if (chip->flags & TPM_CHIP_FLAG_TPM2)
- tpm2_shutdown(chip, TPM2_SU_CLEAR);
+ if (chip->flags & TPM_CHIP_FLAG_TPM2) {
+ if (!tpm_chip_start(chip)) {
+ tpm2_shutdown(chip, TPM2_SU_CLEAR);
+ tpm_chip_stop(chip);
+ }
+ }
chip->ops = NULL;
up_write(&chip->ops_sem);
}
@@ -451,7 +565,11 @@ int tpm_chip_register(struct tpm_chip *chip)
{
int rc;
+ rc = tpm_chip_start(chip);
+ if (rc)
+ return rc;
rc = tpm_auto_startup(chip);
+ tpm_chip_stop(chip);
if (rc)
return rc;
diff --git a/drivers/char/tpm/tpm-dev-common.c b/drivers/char/tpm/tpm-dev-common.c
index 5eecad233ea1..8856cce5a23b 100644
--- a/drivers/char/tpm/tpm-dev-common.c
+++ b/drivers/char/tpm/tpm-dev-common.c
@@ -27,7 +27,38 @@
static struct workqueue_struct *tpm_dev_wq;
static DEFINE_MUTEX(tpm_dev_wq_lock);
-static void tpm_async_work(struct work_struct *work)
+static ssize_t tpm_dev_transmit(struct tpm_chip *chip, struct tpm_space *space,
+ u8 *buf, size_t bufsiz)
+{
+ struct tpm_header *header = (void *)buf;
+ ssize_t ret, len;
+
+ ret = tpm2_prepare_space(chip, space, buf, bufsiz);
+ /* If the command is not implemented by the TPM, synthesize a
+ * response with a TPM2_RC_COMMAND_CODE return for user-space.
+ */
+ if (ret == -EOPNOTSUPP) {
+ header->length = cpu_to_be32(sizeof(*header));
+ header->tag = cpu_to_be16(TPM2_ST_NO_SESSIONS);
+ header->return_code = cpu_to_be32(TPM2_RC_COMMAND_CODE |
+ TSS2_RESMGR_TPM_RC_LAYER);
+ ret = sizeof(*header);
+ }
+ if (ret)
+ goto out_rc;
+
+ len = tpm_transmit(chip, buf, bufsiz);
+ if (len < 0)
+ ret = len;
+
+ if (!ret)
+ ret = tpm2_commit_space(chip, space, buf, &len);
+
+out_rc:
+ return ret ? ret : len;
+}
+
+static void tpm_dev_async_work(struct work_struct *work)
{
struct file_priv *priv =
container_of(work, struct file_priv, async_work);
@@ -35,9 +66,8 @@ static void tpm_async_work(struct work_struct *work)
mutex_lock(&priv->buffer_mutex);
priv->command_enqueued = false;
- ret = tpm_transmit(priv->chip, priv->space, priv->data_buffer,
- sizeof(priv->data_buffer), 0);
-
+ ret = tpm_dev_transmit(priv->chip, priv->space, priv->data_buffer,
+ sizeof(priv->data_buffer));
tpm_put_ops(priv->chip);
if (ret > 0) {
priv->response_length = ret;
@@ -80,7 +110,7 @@ void tpm_common_open(struct file *file, struct tpm_chip *chip,
mutex_init(&priv->buffer_mutex);
timer_setup(&priv->user_read_timer, user_reader_timeout, 0);
INIT_WORK(&priv->timeout_work, tpm_timeout_work);
- INIT_WORK(&priv->async_work, tpm_async_work);
+ INIT_WORK(&priv->async_work, tpm_dev_async_work);
init_waitqueue_head(&priv->async_wait);
file->private_data = priv;
}
@@ -183,8 +213,8 @@ ssize_t tpm_common_write(struct file *file, const char __user *buf,
return size;
}
- ret = tpm_transmit(priv->chip, priv->space, priv->data_buffer,
- sizeof(priv->data_buffer), 0);
+ ret = tpm_dev_transmit(priv->chip, priv->space, priv->data_buffer,
+ sizeof(priv->data_buffer));
tpm_put_ops(priv->chip);
if (ret > 0) {
diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
index d9439f9abe78..83ece5639f86 100644
--- a/drivers/char/tpm/tpm-interface.c
+++ b/drivers/char/tpm/tpm-interface.c
@@ -62,137 +62,22 @@ unsigned long tpm_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal)
}
EXPORT_SYMBOL_GPL(tpm_calc_ordinal_duration);
-static int tpm_validate_command(struct tpm_chip *chip,
- struct tpm_space *space,
- const u8 *cmd,
- size_t len)
-{
- const struct tpm_input_header *header = (const void *)cmd;
- int i;
- u32 cc;
- u32 attrs;
- unsigned int nr_handles;
-
- if (len < TPM_HEADER_SIZE)
- return -EINVAL;
-
- if (!space)
- return 0;
-
- if (chip->flags & TPM_CHIP_FLAG_TPM2 && chip->nr_commands) {
- cc = be32_to_cpu(header->ordinal);
-
- i = tpm2_find_cc(chip, cc);
- if (i < 0) {
- dev_dbg(&chip->dev, "0x%04X is an invalid command\n",
- cc);
- return -EOPNOTSUPP;
- }
-
- attrs = chip->cc_attrs_tbl[i];
- nr_handles =
- 4 * ((attrs >> TPM2_CC_ATTR_CHANDLES) & GENMASK(2, 0));
- if (len < TPM_HEADER_SIZE + 4 * nr_handles)
- goto err_len;
- }
-
- return 0;
-err_len:
- dev_dbg(&chip->dev,
- "%s: insufficient command length %zu", __func__, len);
- return -EINVAL;
-}
-
-static int tpm_request_locality(struct tpm_chip *chip, unsigned int flags)
+static ssize_t tpm_try_transmit(struct tpm_chip *chip, void *buf, size_t bufsiz)
{
- int rc;
-
- if (flags & TPM_TRANSMIT_NESTED)
- return 0;
-
- if (!chip->ops->request_locality)
- return 0;
-
- rc = chip->ops->request_locality(chip, 0);
- if (rc < 0)
- return rc;
-
- chip->locality = rc;
-
- return 0;
-}
-
-static void tpm_relinquish_locality(struct tpm_chip *chip, unsigned int flags)
-{
- int rc;
-
- if (flags & TPM_TRANSMIT_NESTED)
- return;
-
- if (!chip->ops->relinquish_locality)
- return;
-
- rc = chip->ops->relinquish_locality(chip, chip->locality);
- if (rc)
- dev_err(&chip->dev, "%s: : error %d\n", __func__, rc);
-
- chip->locality = -1;
-}
-
-static int tpm_cmd_ready(struct tpm_chip *chip, unsigned int flags)
-{
- if (flags & TPM_TRANSMIT_NESTED)
- return 0;
-
- if (!chip->ops->cmd_ready)
- return 0;
-
- return chip->ops->cmd_ready(chip);
-}
-
-static int tpm_go_idle(struct tpm_chip *chip, unsigned int flags)
-{
- if (flags & TPM_TRANSMIT_NESTED)
- return 0;
-
- if (!chip->ops->go_idle)
- return 0;
-
- return chip->ops->go_idle(chip);
-}
-
-static ssize_t tpm_try_transmit(struct tpm_chip *chip,
- struct tpm_space *space,
- u8 *buf, size_t bufsiz,
- unsigned int flags)
-{
- struct tpm_output_header *header = (void *)buf;
+ struct tpm_header *header = buf;
int rc;
ssize_t len = 0;
u32 count, ordinal;
unsigned long stop;
- bool need_locality;
- rc = tpm_validate_command(chip, space, buf, bufsiz);
- if (rc == -EINVAL)
- return rc;
- /*
- * If the command is not implemented by the TPM, synthesize a
- * response with a TPM2_RC_COMMAND_CODE return for user-space.
- */
- if (rc == -EOPNOTSUPP) {
- header->length = cpu_to_be32(sizeof(*header));
- header->tag = cpu_to_be16(TPM2_ST_NO_SESSIONS);
- header->return_code = cpu_to_be32(TPM2_RC_COMMAND_CODE |
- TSS2_RESMGR_TPM_RC_LAYER);
- return sizeof(*header);
- }
+ if (bufsiz < TPM_HEADER_SIZE)
+ return -EINVAL;
if (bufsiz > TPM_BUFSIZE)
bufsiz = TPM_BUFSIZE;
- count = be32_to_cpu(*((__be32 *) (buf + 2)));
- ordinal = be32_to_cpu(*((__be32 *) (buf + 6)));
+ count = be32_to_cpu(header->length);
+ ordinal = be32_to_cpu(header->ordinal);
if (count == 0)
return -ENODATA;
if (count > bufsiz) {
@@ -201,37 +86,21 @@ static ssize_t tpm_try_transmit(struct tpm_chip *chip,
return -E2BIG;
}
- if (!(flags & TPM_TRANSMIT_UNLOCKED) && !(flags & TPM_TRANSMIT_NESTED))
- mutex_lock(&chip->tpm_mutex);
-
- if (chip->ops->clk_enable != NULL)
- chip->ops->clk_enable(chip, true);
-
- /* Store the decision as chip->locality will be changed. */
- need_locality = chip->locality == -1;
-
- if (need_locality) {
- rc = tpm_request_locality(chip, flags);
- if (rc < 0) {
- need_locality = false;
- goto out_locality;
- }
- }
-
- rc = tpm_cmd_ready(chip, flags);
- if (rc)
- goto out_locality;
-
- rc = tpm2_prepare_space(chip, space, ordinal, buf);
- if (rc)
- goto out;
-
rc = chip->ops->send(chip, buf, count);
if (rc < 0) {
if (rc != -EPIPE)
dev_err(&chip->dev,
- "%s: tpm_send: error %d\n", __func__, rc);
- goto out;
+ "%s: send(): error %d\n", __func__, rc);
+ return rc;
+ }
+
+ /* A sanity check. send() should just return zero on success e.g.
+ * not the command length.
+ */
+ if (rc > 0) {
+ dev_warn(&chip->dev,
+ "%s: send(): invalid value %d\n", __func__, rc);
+ rc = 0;
}
if (chip->flags & TPM_CHIP_FLAG_IRQ)
@@ -246,8 +115,7 @@ static ssize_t tpm_try_transmit(struct tpm_chip *chip,
if (chip->ops->req_canceled(chip, status)) {
dev_err(&chip->dev, "Operation Canceled\n");
- rc = -ECANCELED;
- goto out;
+ return -ECANCELED;
}
tpm_msleep(TPM_TIMEOUT_POLL);
@@ -256,77 +124,45 @@ static ssize_t tpm_try_transmit(struct tpm_chip *chip,
chip->ops->cancel(chip);
dev_err(&chip->dev, "Operation Timed out\n");
- rc = -ETIME;
- goto out;
+ return -ETIME;
out_recv:
len = chip->ops->recv(chip, buf, bufsiz);
if (len < 0) {
rc = len;
- dev_err(&chip->dev,
- "tpm_transmit: tpm_recv: error %d\n", rc);
- goto out;
- } else if (len < TPM_HEADER_SIZE) {
+ dev_err(&chip->dev, "tpm_transmit: tpm_recv: error %d\n", rc);
+ } else if (len < TPM_HEADER_SIZE || len != be32_to_cpu(header->length))
rc = -EFAULT;
- goto out;
- }
- if (len != be32_to_cpu(header->length)) {
- rc = -EFAULT;
- goto out;
- }
-
- rc = tpm2_commit_space(chip, space, ordinal, buf, &len);
- if (rc)
- dev_err(&chip->dev, "tpm2_commit_space: error %d\n", rc);
-
-out:
- /* may fail but do not override previous error value in rc */
- tpm_go_idle(chip, flags);
-
-out_locality:
- if (need_locality)
- tpm_relinquish_locality(chip, flags);
-
- if (chip->ops->clk_enable != NULL)
- chip->ops->clk_enable(chip, false);
-
- if (!(flags & TPM_TRANSMIT_UNLOCKED) && !(flags & TPM_TRANSMIT_NESTED))
- mutex_unlock(&chip->tpm_mutex);
return rc ? rc : len;
}
/**
* tpm_transmit - Internal kernel interface to transmit TPM commands.
+ * @chip: a TPM chip to use
+ * @buf: a TPM command buffer
+ * @bufsiz: length of the TPM command buffer
*
- * @chip: TPM chip to use
- * @space: tpm space
- * @buf: TPM command buffer
- * @bufsiz: length of the TPM command buffer
- * @flags: tpm transmit flags - bitmap
- *
- * A wrapper around tpm_try_transmit that handles TPM2_RC_RETRY
- * returns from the TPM and retransmits the command after a delay up
- * to a maximum wait of TPM2_DURATION_LONG.
+ * A wrapper around tpm_try_transmit() that handles TPM2_RC_RETRY returns from
+ * the TPM and retransmits the command after a delay up to a maximum wait of
+ * TPM2_DURATION_LONG.
*
- * Note: TPM1 never returns TPM2_RC_RETRY so the retry logic is TPM2
- * only
+ * Note that TPM 1.x never returns TPM2_RC_RETRY so the retry logic is TPM 2.0
+ * only.
*
* Return:
- * the length of the return when the operation is successful.
- * A negative number for system errors (errno).
+ * * The response length - OK
+ * * -errno - A system error
*/
-ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space,
- u8 *buf, size_t bufsiz, unsigned int flags)
+ssize_t tpm_transmit(struct tpm_chip *chip, u8 *buf, size_t bufsiz)
{
- struct tpm_output_header *header = (struct tpm_output_header *)buf;
+ struct tpm_header *header = (struct tpm_header *)buf;
/* space for header and handles */
u8 save[TPM_HEADER_SIZE + 3*sizeof(u32)];
unsigned int delay_msec = TPM2_DURATION_SHORT;
u32 rc = 0;
ssize_t ret;
- const size_t save_size = min(space ? sizeof(save) : TPM_HEADER_SIZE,
- bufsiz);
+ const size_t save_size = min(sizeof(save), bufsiz);
/* the command code is where the return code will be */
u32 cc = be32_to_cpu(header->return_code);
@@ -338,7 +174,7 @@ ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space,
memcpy(save, buf, save_size);
for (;;) {
- ret = tpm_try_transmit(chip, space, buf, bufsiz, flags);
+ ret = tpm_try_transmit(chip, buf, bufsiz);
if (ret < 0)
break;
rc = be32_to_cpu(header->return_code);
@@ -365,39 +201,33 @@ ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space,
}
return ret;
}
+
/**
* tpm_transmit_cmd - send a tpm command to the device
- * The function extracts tpm out header return code
- *
- * @chip: TPM chip to use
- * @space: tpm space
- * @buf: TPM command buffer
- * @bufsiz: length of the buffer
- * @min_rsp_body_length: minimum expected length of response body
- * @flags: tpm transmit flags - bitmap
- * @desc: command description used in the error message
+ * @chip: a TPM chip to use
+ * @buf: a TPM command buffer
+ * @min_rsp_body_length: minimum expected length of response body
+ * @desc: command description used in the error message
*
* Return:
- * 0 when the operation is successful.
- * A negative number for system errors (errno).
- * A positive number for a TPM error.
+ * * 0 - OK
+ * * -errno - A system error
+ * * TPM_RC - A TPM error
*/
-ssize_t tpm_transmit_cmd(struct tpm_chip *chip, struct tpm_space *space,
- void *buf, size_t bufsiz,
- size_t min_rsp_body_length, unsigned int flags,
- const char *desc)
+ssize_t tpm_transmit_cmd(struct tpm_chip *chip, struct tpm_buf *buf,
+ size_t min_rsp_body_length, const char *desc)
{
- const struct tpm_output_header *header = buf;
+ const struct tpm_header *header = (struct tpm_header *)buf->data;
int err;
ssize_t len;
- len = tpm_transmit(chip, space, buf, bufsiz, flags);
+ len = tpm_transmit(chip, buf->data, PAGE_SIZE);
if (len < 0)
return len;
err = be32_to_cpu(header->return_code);
if (err != 0 && err != TPM_ERR_DISABLED && err != TPM_ERR_DEACTIVATED
- && desc)
+ && err != TPM2_RC_TESTING && desc)
dev_err(&chip->dev, "A TPM error (%d) occurred %s\n", err,
desc);
if (err)
@@ -451,11 +281,12 @@ EXPORT_SYMBOL_GPL(tpm_is_tpm2);
* tpm_pcr_read - read a PCR value from SHA1 bank
* @chip: a &struct tpm_chip instance, %NULL for the default chip
* @pcr_idx: the PCR to be retrieved
- * @res_buf: the value of the PCR
+ * @digest: the PCR bank and buffer current PCR value is written to
*
* Return: same as with tpm_transmit_cmd()
*/
-int tpm_pcr_read(struct tpm_chip *chip, u32 pcr_idx, u8 *res_buf)
+int tpm_pcr_read(struct tpm_chip *chip, u32 pcr_idx,
+ struct tpm_digest *digest)
{
int rc;
@@ -464,9 +295,9 @@ int tpm_pcr_read(struct tpm_chip *chip, u32 pcr_idx, u8 *res_buf)
return -ENODEV;
if (chip->flags & TPM_CHIP_FLAG_TPM2)
- rc = tpm2_pcr_read(chip, pcr_idx, res_buf);
+ rc = tpm2_pcr_read(chip, pcr_idx, digest, NULL);
else
- rc = tpm1_pcr_read(chip, pcr_idx, res_buf);
+ rc = tpm1_pcr_read(chip, pcr_idx, digest->digest);
tpm_put_ops(chip);
return rc;
@@ -477,41 +308,34 @@ EXPORT_SYMBOL_GPL(tpm_pcr_read);
* tpm_pcr_extend - extend a PCR value in SHA1 bank.
* @chip: a &struct tpm_chip instance, %NULL for the default chip
* @pcr_idx: the PCR to be retrieved
- * @hash: the hash value used to extend the PCR value
+ * @digests: array of tpm_digest structures used to extend PCRs
*
- * Note: with TPM 2.0 extends also those banks with a known digest size to the
- * cryto subsystem in order to prevent malicious use of those PCR banks. In the
- * future we should dynamically determine digest sizes.
+ * Note: callers must pass a digest for every allocated PCR bank, in the same
+ * order of the banks in chip->allocated_banks.
*
* Return: same as with tpm_transmit_cmd()
*/
-int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, const u8 *hash)
+int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
+ struct tpm_digest *digests)
{
int rc;
- struct tpm2_digest digest_list[ARRAY_SIZE(chip->active_banks)];
- u32 count = 0;
int i;
chip = tpm_find_get_ops(chip);
if (!chip)
return -ENODEV;
- if (chip->flags & TPM_CHIP_FLAG_TPM2) {
- memset(digest_list, 0, sizeof(digest_list));
-
- for (i = 0; i < ARRAY_SIZE(chip->active_banks) &&
- chip->active_banks[i] != TPM2_ALG_ERROR; i++) {
- digest_list[i].alg_id = chip->active_banks[i];
- memcpy(digest_list[i].digest, hash, TPM_DIGEST_SIZE);
- count++;
- }
+ for (i = 0; i < chip->nr_allocated_banks; i++)
+ if (digests[i].alg_id != chip->allocated_banks[i].alg_id)
+ return -EINVAL;
- rc = tpm2_pcr_extend(chip, pcr_idx, count, digest_list);
+ if (chip->flags & TPM_CHIP_FLAG_TPM2) {
+ rc = tpm2_pcr_extend(chip, pcr_idx, digests);
tpm_put_ops(chip);
return rc;
}
- rc = tpm1_pcr_extend(chip, pcr_idx, hash,
+ rc = tpm1_pcr_extend(chip, pcr_idx, digests[0].digest,
"attempting extend a PCR value");
tpm_put_ops(chip);
return rc;
@@ -528,14 +352,21 @@ EXPORT_SYMBOL_GPL(tpm_pcr_extend);
*/
int tpm_send(struct tpm_chip *chip, void *cmd, size_t buflen)
{
+ struct tpm_buf buf;
int rc;
chip = tpm_find_get_ops(chip);
if (!chip)
return -ENODEV;
- rc = tpm_transmit_cmd(chip, NULL, cmd, buflen, 0, 0,
- "attempting to a send a command");
+ rc = tpm_buf_init(&buf, 0, 0);
+ if (rc)
+ goto out;
+
+ memcpy(buf.data, cmd, buflen);
+ rc = tpm_transmit_cmd(chip, &buf, 0, "attempting to a send a command");
+ tpm_buf_destroy(&buf);
+out:
tpm_put_ops(chip);
return rc;
}
@@ -571,10 +402,16 @@ int tpm_pm_suspend(struct device *dev)
if (chip->flags & TPM_CHIP_FLAG_ALWAYS_POWERED)
return 0;
- if (chip->flags & TPM_CHIP_FLAG_TPM2)
- tpm2_shutdown(chip, TPM2_SU_STATE);
- else
+ if (chip->flags & TPM_CHIP_FLAG_TPM2) {
+ mutex_lock(&chip->tpm_mutex);
+ if (!tpm_chip_start(chip)) {
+ tpm2_shutdown(chip, TPM2_SU_STATE);
+ tpm_chip_stop(chip);
+ }
+ mutex_unlock(&chip->tpm_mutex);
+ } else {
rc = tpm1_pm_suspend(chip, tpm_suspend_pcr);
+ }
return rc;
}
diff --git a/drivers/char/tpm/tpm-sysfs.c b/drivers/char/tpm/tpm-sysfs.c
index b88e08ec2c59..533a260d744e 100644
--- a/drivers/char/tpm/tpm-sysfs.c
+++ b/drivers/char/tpm/tpm-sysfs.c
@@ -39,7 +39,6 @@ static ssize_t pubek_show(struct device *dev, struct device_attribute *attr,
{
struct tpm_buf tpm_buf;
struct tpm_readpubek_out *out;
- ssize_t rc;
int i;
char *str = buf;
struct tpm_chip *chip = to_tpm_chip(dev);
@@ -47,19 +46,17 @@ static ssize_t pubek_show(struct device *dev, struct device_attribute *attr,
memset(&anti_replay, 0, sizeof(anti_replay));
- rc = tpm_buf_init(&tpm_buf, TPM_TAG_RQU_COMMAND, TPM_ORD_READPUBEK);
- if (rc)
- return rc;
+ if (tpm_try_get_ops(chip))
+ return 0;
+
+ if (tpm_buf_init(&tpm_buf, TPM_TAG_RQU_COMMAND, TPM_ORD_READPUBEK))
+ goto out_ops;
tpm_buf_append(&tpm_buf, anti_replay, sizeof(anti_replay));
- rc = tpm_transmit_cmd(chip, NULL, tpm_buf.data, PAGE_SIZE,
- READ_PUBEK_RESULT_MIN_BODY_SIZE, 0,
- "attempting to read the PUBEK");
- if (rc) {
- tpm_buf_destroy(&tpm_buf);
- return 0;
- }
+ if (tpm_transmit_cmd(chip, &tpm_buf, READ_PUBEK_RESULT_MIN_BODY_SIZE,
+ "attempting to read the PUBEK"))
+ goto out_buf;
out = (struct tpm_readpubek_out *)&tpm_buf.data[10];
str +=
@@ -90,9 +87,11 @@ static ssize_t pubek_show(struct device *dev, struct device_attribute *attr,
str += sprintf(str, "\n");
}
- rc = str - buf;
+out_buf:
tpm_buf_destroy(&tpm_buf);
- return rc;
+out_ops:
+ tpm_put_ops(chip);
+ return str - buf;
}
static DEVICE_ATTR_RO(pubek);
@@ -101,27 +100,32 @@ static ssize_t pcrs_show(struct device *dev, struct device_attribute *attr,
{
cap_t cap;
u8 digest[TPM_DIGEST_SIZE];
- ssize_t rc;
u32 i, j, num_pcrs;
char *str = buf;
struct tpm_chip *chip = to_tpm_chip(dev);
- rc = tpm1_getcap(chip, TPM_CAP_PROP_PCR, &cap,
- "attempting to determine the number of PCRS",
- sizeof(cap.num_pcrs));
- if (rc)
+ if (tpm_try_get_ops(chip))
return 0;
+ if (tpm1_getcap(chip, TPM_CAP_PROP_PCR, &cap,
+ "attempting to determine the number of PCRS",
+ sizeof(cap.num_pcrs))) {
+ tpm_put_ops(chip);
+ return 0;
+ }
+
num_pcrs = be32_to_cpu(cap.num_pcrs);
for (i = 0; i < num_pcrs; i++) {
- rc = tpm1_pcr_read(chip, i, digest);
- if (rc)
+ if (tpm1_pcr_read(chip, i, digest)) {
+ str = buf;
break;
+ }
str += sprintf(str, "PCR-%02d: ", i);
for (j = 0; j < TPM_DIGEST_SIZE; j++)
str += sprintf(str, "%02X ", digest[j]);
str += sprintf(str, "\n");
}
+ tpm_put_ops(chip);
return str - buf;
}
static DEVICE_ATTR_RO(pcrs);
@@ -129,16 +133,21 @@ static DEVICE_ATTR_RO(pcrs);
static ssize_t enabled_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
+ struct tpm_chip *chip = to_tpm_chip(dev);
+ ssize_t rc = 0;
cap_t cap;
- ssize_t rc;
- rc = tpm1_getcap(to_tpm_chip(dev), TPM_CAP_FLAG_PERM, &cap,
- "attempting to determine the permanent enabled state",
- sizeof(cap.perm_flags));
- if (rc)
+ if (tpm_try_get_ops(chip))
return 0;
+ if (tpm1_getcap(chip, TPM_CAP_FLAG_PERM, &cap,
+ "attempting to determine the permanent enabled state",
+ sizeof(cap.perm_flags)))
+ goto out_ops;
+
rc = sprintf(buf, "%d\n", !cap.perm_flags.disable);
+out_ops:
+ tpm_put_ops(chip);
return rc;
}
static DEVICE_ATTR_RO(enabled);
@@ -146,16 +155,21 @@ static DEVICE_ATTR_RO(enabled);
static ssize_t active_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
+ struct tpm_chip *chip = to_tpm_chip(dev);
+ ssize_t rc = 0;
cap_t cap;
- ssize_t rc;
- rc = tpm1_getcap(to_tpm_chip(dev), TPM_CAP_FLAG_PERM, &cap,
- "attempting to determine the permanent active state",
- sizeof(cap.perm_flags));
- if (rc)
+ if (tpm_try_get_ops(chip))
return 0;
+ if (tpm1_getcap(chip, TPM_CAP_FLAG_PERM, &cap,
+ "attempting to determine the permanent active state",
+ sizeof(cap.perm_flags)))
+ goto out_ops;
+
rc = sprintf(buf, "%d\n", !cap.perm_flags.deactivated);
+out_ops:
+ tpm_put_ops(chip);
return rc;
}
static DEVICE_ATTR_RO(active);
@@ -163,16 +177,21 @@ static DEVICE_ATTR_RO(active);
static ssize_t owned_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
+ struct tpm_chip *chip = to_tpm_chip(dev);
+ ssize_t rc = 0;
cap_t cap;
- ssize_t rc;
- rc = tpm1_getcap(to_tpm_chip(dev), TPM_CAP_PROP_OWNER, &cap,
- "attempting to determine the owner state",
- sizeof(cap.owned));
- if (rc)
+ if (tpm_try_get_ops(chip))
return 0;
+ if (tpm1_getcap(to_tpm_chip(dev), TPM_CAP_PROP_OWNER, &cap,
+ "attempting to determine the owner state",
+ sizeof(cap.owned)))
+ goto out_ops;
+
rc = sprintf(buf, "%d\n", cap.owned);
+out_ops:
+ tpm_put_ops(chip);
return rc;
}
static DEVICE_ATTR_RO(owned);
@@ -180,16 +199,21 @@ static DEVICE_ATTR_RO(owned);
static ssize_t temp_deactivated_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
+ struct tpm_chip *chip = to_tpm_chip(dev);
+ ssize_t rc = 0;
cap_t cap;
- ssize_t rc;
- rc = tpm1_getcap(to_tpm_chip(dev), TPM_CAP_FLAG_VOL, &cap,
- "attempting to determine the temporary state",
- sizeof(cap.stclear_flags));
- if (rc)
+ if (tpm_try_get_ops(chip))
return 0;
+ if (tpm1_getcap(to_tpm_chip(dev), TPM_CAP_FLAG_VOL, &cap,
+ "attempting to determine the temporary state",
+ sizeof(cap.stclear_flags)))
+ goto out_ops;
+
rc = sprintf(buf, "%d\n", cap.stclear_flags.deactivated);
+out_ops:
+ tpm_put_ops(chip);
return rc;
}
static DEVICE_ATTR_RO(temp_deactivated);
@@ -198,15 +222,18 @@ static ssize_t caps_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct tpm_chip *chip = to_tpm_chip(dev);
- cap_t cap;
- ssize_t rc;
+ ssize_t rc = 0;
char *str = buf;
+ cap_t cap;
- rc = tpm1_getcap(chip, TPM_CAP_PROP_MANUFACTURER, &cap,
- "attempting to determine the manufacturer",
- sizeof(cap.manufacturer_id));
- if (rc)
+ if (tpm_try_get_ops(chip))
return 0;
+
+ if (tpm1_getcap(chip, TPM_CAP_PROP_MANUFACTURER, &cap,
+ "attempting to determine the manufacturer",
+ sizeof(cap.manufacturer_id)))
+ goto out_ops;
+
str += sprintf(str, "Manufacturer: 0x%x\n",
be32_to_cpu(cap.manufacturer_id));
@@ -223,11 +250,10 @@ static ssize_t caps_show(struct device *dev, struct device_attribute *attr,
cap.tpm_version_1_2.revMinor);
} else {
/* Otherwise just use TPM_STRUCT_VER */
- rc = tpm1_getcap(chip, TPM_CAP_VERSION_1_1, &cap,
- "attempting to determine the 1.1 version",
- sizeof(cap.tpm_version));
- if (rc)
- return 0;
+ if (tpm1_getcap(chip, TPM_CAP_VERSION_1_1, &cap,
+ "attempting to determine the 1.1 version",
+ sizeof(cap.tpm_version)))
+ goto out_ops;
str += sprintf(str,
"TCG version: %d.%d\nFirmware version: %d.%d\n",
cap.tpm_version.Major,
@@ -235,8 +261,10 @@ static ssize_t caps_show(struct device *dev, struct device_attribute *attr,
cap.tpm_version.revMajor,
cap.tpm_version.revMinor);
}
-
- return str - buf;
+ rc = str - buf;
+out_ops:
+ tpm_put_ops(chip);
+ return rc;
}
static DEVICE_ATTR_RO(caps);
@@ -244,10 +272,12 @@ static ssize_t cancel_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct tpm_chip *chip = to_tpm_chip(dev);
- if (chip == NULL)
+
+ if (tpm_try_get_ops(chip))
return 0;
chip->ops->cancel(chip);
+ tpm_put_ops(chip);
return count;
}
static DEVICE_ATTR_WO(cancel);
diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
index f27d1f38a93d..2cce072f25b5 100644
--- a/drivers/char/tpm/tpm.h
+++ b/drivers/char/tpm/tpm.h
@@ -25,30 +25,22 @@
#include <linux/module.h>
#include <linux/delay.h>
-#include <linux/fs.h>
-#include <linux/hw_random.h>
#include <linux/mutex.h>
#include <linux/sched.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/tpm.h>
-#include <linux/acpi.h>
-#include <linux/cdev.h>
#include <linux/highmem.h>
#include <linux/tpm_eventlog.h>
-#include <crypto/hash_info.h>
#ifdef CONFIG_X86
#include <asm/intel-family.h>
#endif
-enum tpm_const {
- TPM_MINOR = 224, /* officially assigned */
- TPM_BUFSIZE = 4096,
- TPM_NUM_DEVICES = 65536,
- TPM_RETRY = 50, /* 5 seconds */
- TPM_NUM_EVENT_LOG_FILES = 3,
-};
+#define TPM_MINOR 224 /* officially assigned */
+#define TPM_BUFSIZE 4096
+#define TPM_NUM_DEVICES 65536
+#define TPM_RETRY 50
enum tpm_timeout {
TPM_TIMEOUT = 5, /* msecs */
@@ -65,16 +57,6 @@ enum tpm_addr {
TPM_ADDR = 0x4E,
};
-/* Indexes the duration array */
-enum tpm_duration {
- TPM_SHORT = 0,
- TPM_MEDIUM = 1,
- TPM_LONG = 2,
- TPM_LONG_LONG = 3,
- TPM_UNDEFINED,
- TPM_NUM_DURATIONS = TPM_UNDEFINED,
-};
-
#define TPM_WARN_RETRY 0x800
#define TPM_WARN_DOING_SELFTEST 0x802
#define TPM_ERR_DEACTIVATED 0x6
@@ -122,17 +104,6 @@ enum tpm2_return_codes {
TPM2_RC_RETRY = 0x0922,
};
-enum tpm2_algorithms {
- TPM2_ALG_ERROR = 0x0000,
- TPM2_ALG_SHA1 = 0x0004,
- TPM2_ALG_KEYEDHASH = 0x0008,
- TPM2_ALG_SHA256 = 0x000B,
- TPM2_ALG_SHA384 = 0x000C,
- TPM2_ALG_SHA512 = 0x000D,
- TPM2_ALG_NULL = 0x0010,
- TPM2_ALG_SM3_256 = 0x0012,
-};
-
enum tpm2_command_codes {
TPM2_CC_FIRST = 0x011F,
TPM2_CC_HIERARCHY_CONTROL = 0x0121,
@@ -190,15 +161,6 @@ enum tpm2_cc_attrs {
#define TPM_VID_WINBOND 0x1050
#define TPM_VID_STM 0x104A
-#define TPM_PPI_VERSION_LEN 3
-
-struct tpm_space {
- u32 context_tbl[3];
- u8 *context_buf;
- u32 session_tbl[3];
- u8 *session_buf;
-};
-
enum tpm_chip_flags {
TPM_CHIP_FLAG_TPM2 = BIT(1),
TPM_CHIP_FLAG_IRQ = BIT(2),
@@ -207,82 +169,15 @@ enum tpm_chip_flags {
TPM_CHIP_FLAG_ALWAYS_POWERED = BIT(5),
};
-struct tpm_bios_log {
- void *bios_event_log;
- void *bios_event_log_end;
-};
-
-struct tpm_chip_seqops {
- struct tpm_chip *chip;
- const struct seq_operations *seqops;
-};
-
-struct tpm_chip {
- struct device dev;
- struct device devs;
- struct cdev cdev;
- struct cdev cdevs;
-
- /* A driver callback under ops cannot be run unless ops_sem is held
- * (sometimes implicitly, eg for the sysfs code). ops becomes null
- * when the driver is unregistered, see tpm_try_get_ops.
- */
- struct rw_semaphore ops_sem;
- const struct tpm_class_ops *ops;
-
- struct tpm_bios_log log;
- struct tpm_chip_seqops bin_log_seqops;
- struct tpm_chip_seqops ascii_log_seqops;
-
- unsigned int flags;
-
- int dev_num; /* /dev/tpm# */
- unsigned long is_open; /* only one allowed */
-
- char hwrng_name[64];
- struct hwrng hwrng;
-
- struct mutex tpm_mutex; /* tpm is processing */
-
- unsigned long timeout_a; /* jiffies */
- unsigned long timeout_b; /* jiffies */
- unsigned long timeout_c; /* jiffies */
- unsigned long timeout_d; /* jiffies */
- bool timeout_adjusted;
- unsigned long duration[TPM_NUM_DURATIONS]; /* jiffies */
- bool duration_adjusted;
-
- struct dentry *bios_dir[TPM_NUM_EVENT_LOG_FILES];
-
- const struct attribute_group *groups[3];
- unsigned int groups_cnt;
-
- u16 active_banks[7];
-#ifdef CONFIG_ACPI
- acpi_handle acpi_dev_handle;
- char ppi_version[TPM_PPI_VERSION_LEN + 1];
-#endif /* CONFIG_ACPI */
-
- struct tpm_space work_space;
- u32 nr_commands;
- u32 *cc_attrs_tbl;
-
- /* active locality */
- int locality;
-};
-
#define to_tpm_chip(d) container_of(d, struct tpm_chip, dev)
-struct tpm_input_header {
- __be16 tag;
- __be32 length;
- __be32 ordinal;
-} __packed;
-
-struct tpm_output_header {
- __be16 tag;
- __be32 length;
- __be32 return_code;
+struct tpm_header {
+ __be16 tag;
+ __be32 length;
+ union {
+ __be32 ordinal;
+ __be32 return_code;
+ };
} __packed;
#define TPM_TAG_RQU_COMMAND 193
@@ -401,8 +296,8 @@ struct tpm_buf {
static inline void tpm_buf_reset(struct tpm_buf *buf, u16 tag, u32 ordinal)
{
- struct tpm_input_header *head;
- head = (struct tpm_input_header *)buf->data;
+ struct tpm_header *head = (struct tpm_header *)buf->data;
+
head->tag = cpu_to_be16(tag);
head->length = cpu_to_be32(sizeof(*head));
head->ordinal = cpu_to_be32(ordinal);
@@ -428,14 +323,14 @@ static inline void tpm_buf_destroy(struct tpm_buf *buf)
static inline u32 tpm_buf_length(struct tpm_buf *buf)
{
- struct tpm_input_header *head = (struct tpm_input_header *) buf->data;
+ struct tpm_header *head = (struct tpm_header *)buf->data;
return be32_to_cpu(head->length);
}
static inline u16 tpm_buf_tag(struct tpm_buf *buf)
{
- struct tpm_input_header *head = (struct tpm_input_header *) buf->data;
+ struct tpm_header *head = (struct tpm_header *)buf->data;
return be16_to_cpu(head->tag);
}
@@ -444,7 +339,7 @@ static inline void tpm_buf_append(struct tpm_buf *buf,
const unsigned char *new_data,
unsigned int new_len)
{
- struct tpm_input_header *head = (struct tpm_input_header *) buf->data;
+ struct tpm_header *head = (struct tpm_header *)buf->data;
u32 len = tpm_buf_length(buf);
/* Return silently if overflow has already happened. */
@@ -487,25 +382,9 @@ extern const struct file_operations tpm_fops;
extern const struct file_operations tpmrm_fops;
extern struct idr dev_nums_idr;
-/**
- * enum tpm_transmit_flags - flags for tpm_transmit()
- *
- * @TPM_TRANSMIT_UNLOCKED: do not lock the chip
- * @TPM_TRANSMIT_NESTED: discard setup steps (power management,
- * locality) including locking (i.e. implicit
- * UNLOCKED)
- */
-enum tpm_transmit_flags {
- TPM_TRANSMIT_UNLOCKED = BIT(0),
- TPM_TRANSMIT_NESTED = BIT(1),
-};
-
-ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space,
- u8 *buf, size_t bufsiz, unsigned int flags);
-ssize_t tpm_transmit_cmd(struct tpm_chip *chip, struct tpm_space *space,
- void *buf, size_t bufsiz,
- size_t min_rsp_body_length, unsigned int flags,
- const char *desc);
+ssize_t tpm_transmit(struct tpm_chip *chip, u8 *buf, size_t bufsiz);
+ssize_t tpm_transmit_cmd(struct tpm_chip *chip, struct tpm_buf *buf,
+ size_t min_rsp_body_length, const char *desc);
int tpm_get_timeouts(struct tpm_chip *);
int tpm_auto_startup(struct tpm_chip *chip);
@@ -530,6 +409,8 @@ static inline void tpm_msleep(unsigned int delay_msec)
delay_msec * 1000);
};
+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);
__must_check int tpm_try_get_ops(struct tpm_chip *chip);
void tpm_put_ops(struct tpm_chip *chip);
@@ -558,12 +439,12 @@ static inline u32 tpm2_rc_value(u32 rc)
}
int tpm2_get_timeouts(struct tpm_chip *chip);
-int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx, u8 *res_buf);
-int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, u32 count,
- struct tpm2_digest *digests);
+int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx,
+ struct tpm_digest *digest, u16 *digest_size_ptr);
+int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
+ struct tpm_digest *digests);
int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max);
-void tpm2_flush_context_cmd(struct tpm_chip *chip, u32 handle,
- unsigned int flags);
+void tpm2_flush_context(struct tpm_chip *chip, u32 handle);
int tpm2_seal_trusted(struct tpm_chip *chip,
struct trusted_key_payload *payload,
struct trusted_key_options *options);
@@ -580,10 +461,11 @@ int tpm2_probe(struct tpm_chip *chip);
int tpm2_find_cc(struct tpm_chip *chip, u32 cc);
int tpm2_init_space(struct tpm_space *space);
void tpm2_del_space(struct tpm_chip *chip, struct tpm_space *space);
-int tpm2_prepare_space(struct tpm_chip *chip, struct tpm_space *space, u32 cc,
- u8 *cmd);
-int tpm2_commit_space(struct tpm_chip *chip, struct tpm_space *space,
- u32 cc, u8 *buf, size_t *bufsiz);
+void tpm2_flush_space(struct tpm_chip *chip);
+int tpm2_prepare_space(struct tpm_chip *chip, struct tpm_space *space, u8 *cmd,
+ size_t cmdsiz);
+int tpm2_commit_space(struct tpm_chip *chip, struct tpm_space *space, void *buf,
+ size_t *bufsiz);
int tpm_bios_log_setup(struct tpm_chip *chip);
void tpm_bios_log_teardown(struct tpm_chip *chip);
diff --git a/drivers/char/tpm/tpm1-cmd.c b/drivers/char/tpm/tpm1-cmd.c
index 6f306338953b..85dcf2654d11 100644
--- a/drivers/char/tpm/tpm1-cmd.c
+++ b/drivers/char/tpm/tpm1-cmd.c
@@ -334,11 +334,8 @@ static int tpm1_startup(struct tpm_chip *chip)
tpm_buf_append_u16(&buf, TPM_ST_CLEAR);
- rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 0, 0,
- "attempting to start the TPM");
-
+ rc = tpm_transmit_cmd(chip, &buf, 0, "attempting to start the TPM");
tpm_buf_destroy(&buf);
-
return rc;
}
@@ -380,8 +377,7 @@ int tpm1_get_timeouts(struct tpm_chip *chip)
* of misreporting.
*/
if (chip->ops->update_timeouts)
- chip->timeout_adjusted =
- chip->ops->update_timeouts(chip, timeout_eff);
+ chip->ops->update_timeouts(chip, timeout_eff);
if (!chip->timeout_adjusted) {
/* Restore default if chip reported 0 */
@@ -462,9 +458,7 @@ int tpm1_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, const u8 *hash,
tpm_buf_append_u32(&buf, pcr_idx);
tpm_buf_append(&buf, hash, TPM_DIGEST_SIZE);
- rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE,
- TPM_DIGEST_SIZE, 0, log_msg);
-
+ rc = tpm_transmit_cmd(chip, &buf, TPM_DIGEST_SIZE, log_msg);
tpm_buf_destroy(&buf);
return rc;
}
@@ -494,11 +488,9 @@ ssize_t tpm1_getcap(struct tpm_chip *chip, u32 subcap_id, cap_t *cap,
tpm_buf_append_u32(&buf, 4);
tpm_buf_append_u32(&buf, subcap_id);
}
- rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE,
- min_cap_length, 0, desc);
+ rc = tpm_transmit_cmd(chip, &buf, min_cap_length, desc);
if (!rc)
*cap = *(cap_t *)&buf.data[TPM_HEADER_SIZE + 4];
-
tpm_buf_destroy(&buf);
return rc;
}
@@ -537,8 +529,7 @@ int tpm1_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
do {
tpm_buf_append_u32(&buf, num_bytes);
- rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE,
- sizeof(out->rng_data_len), 0,
+ rc = tpm_transmit_cmd(chip, &buf, sizeof(out->rng_data_len),
"attempting get random");
if (rc)
goto out;
@@ -583,8 +574,7 @@ int tpm1_pcr_read(struct tpm_chip *chip, u32 pcr_idx, u8 *res_buf)
tpm_buf_append_u32(&buf, pcr_idx);
- rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE,
- TPM_DIGEST_SIZE, 0,
+ rc = tpm_transmit_cmd(chip, &buf, TPM_DIGEST_SIZE,
"attempting to read a pcr value");
if (rc)
goto out;
@@ -618,11 +608,8 @@ static int tpm1_continue_selftest(struct tpm_chip *chip)
if (rc)
return rc;
- rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE,
- 0, 0, "continue selftest");
-
+ rc = tpm_transmit_cmd(chip, &buf, 0, "continue selftest");
tpm_buf_destroy(&buf);
-
return rc;
}
@@ -709,6 +696,18 @@ int tpm1_auto_startup(struct tpm_chip *chip)
goto out;
}
+ chip->allocated_banks = kcalloc(1, sizeof(*chip->allocated_banks),
+ GFP_KERNEL);
+ if (!chip->allocated_banks) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ 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;
+ chip->nr_allocated_banks = 1;
+
return rc;
out:
if (rc > 0)
@@ -747,9 +746,7 @@ int tpm1_pm_suspend(struct tpm_chip *chip, u32 tpm_suspend_pcr)
return rc;
/* now do the actual savestate */
for (try = 0; try < TPM_RETRY; try++) {
- rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE,
- 0, 0, NULL);
-
+ rc = tpm_transmit_cmd(chip, &buf, 0, NULL);
/*
* If the TPM indicates that it is too busy to respond to
* this command then retry before giving up. It can take
diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
index a6bec13afa69..e74c5b7b64bf 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -33,11 +33,11 @@ struct tpm2_hash {
};
static struct tpm2_hash tpm2_hash_map[] = {
- {HASH_ALGO_SHA1, TPM2_ALG_SHA1},
- {HASH_ALGO_SHA256, TPM2_ALG_SHA256},
- {HASH_ALGO_SHA384, TPM2_ALG_SHA384},
- {HASH_ALGO_SHA512, TPM2_ALG_SHA512},
- {HASH_ALGO_SM3_256, TPM2_ALG_SM3_256},
+ {HASH_ALGO_SHA1, TPM_ALG_SHA1},
+ {HASH_ALGO_SHA256, TPM_ALG_SHA256},
+ {HASH_ALGO_SHA384, TPM_ALG_SHA384},
+ {HASH_ALGO_SHA512, TPM_ALG_SHA512},
+ {HASH_ALGO_SM3_256, TPM_ALG_SM3_256},
};
int tpm2_get_timeouts(struct tpm_chip *chip)
@@ -171,20 +171,36 @@ struct tpm2_pcr_read_out {
* tpm2_pcr_read() - read a PCR value
* @chip: TPM chip to use.
* @pcr_idx: index of the PCR to read.
- * @res_buf: buffer to store the resulting hash.
+ * @digest: PCR bank and buffer current PCR value is written to.
+ * @digest_size_ptr: pointer to variable that stores the digest size.
*
* Return: Same as with tpm_transmit_cmd.
*/
-int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx, u8 *res_buf)
+int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx,
+ struct tpm_digest *digest, u16 *digest_size_ptr)
{
+ int i;
int rc;
struct tpm_buf buf;
struct tpm2_pcr_read_out *out;
u8 pcr_select[TPM2_PCR_SELECT_MIN] = {0};
+ u16 digest_size;
+ u16 expected_digest_size = 0;
if (pcr_idx >= TPM2_PLATFORM_PCR)
return -EINVAL;
+ if (!digest_size_ptr) {
+ for (i = 0; i < chip->nr_allocated_banks &&
+ chip->allocated_banks[i].alg_id != digest->alg_id; i++)
+ ;
+
+ if (i == chip->nr_allocated_banks)
+ return -EINVAL;
+
+ expected_digest_size = chip->allocated_banks[i].digest_size;
+ }
+
rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_PCR_READ);
if (rc)
return rc;
@@ -192,18 +208,28 @@ int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx, u8 *res_buf)
pcr_select[pcr_idx >> 3] = 1 << (pcr_idx & 0x7);
tpm_buf_append_u32(&buf, 1);
- tpm_buf_append_u16(&buf, TPM2_ALG_SHA1);
+ tpm_buf_append_u16(&buf, digest->alg_id);
tpm_buf_append_u8(&buf, TPM2_PCR_SELECT_MIN);
tpm_buf_append(&buf, (const unsigned char *)pcr_select,
sizeof(pcr_select));
- rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 0, 0,
- res_buf ? "attempting to read a pcr value" : NULL);
- if (rc == 0 && res_buf) {
- out = (struct tpm2_pcr_read_out *)&buf.data[TPM_HEADER_SIZE];
- memcpy(res_buf, out->digest, SHA1_DIGEST_SIZE);
+ rc = tpm_transmit_cmd(chip, &buf, 0, "attempting to read a pcr value");
+ if (rc)
+ goto out;
+
+ out = (struct tpm2_pcr_read_out *)&buf.data[TPM_HEADER_SIZE];
+ digest_size = be16_to_cpu(out->digest_size);
+ if (digest_size > sizeof(digest->digest) ||
+ (!digest_size_ptr && digest_size != expected_digest_size)) {
+ rc = -EINVAL;
+ goto out;
}
+ if (digest_size_ptr)
+ *digest_size_ptr = digest_size;
+
+ memcpy(digest->digest, out->digest, digest_size);
+out:
tpm_buf_destroy(&buf);
return rc;
}
@@ -220,22 +246,17 @@ struct tpm2_null_auth_area {
*
* @chip: TPM chip to use.
* @pcr_idx: index of the PCR.
- * @count: number of digests passed.
* @digests: list of pcr banks and corresponding digest values to extend.
*
* Return: Same as with tpm_transmit_cmd.
*/
-int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, u32 count,
- struct tpm2_digest *digests)
+int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
+ struct tpm_digest *digests)
{
struct tpm_buf buf;
struct tpm2_null_auth_area auth_area;
int rc;
int i;
- int j;
-
- if (count > ARRAY_SIZE(chip->active_banks))
- return -EINVAL;
rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_PCR_EXTEND);
if (rc)
@@ -251,21 +272,15 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, u32 count,
tpm_buf_append_u32(&buf, sizeof(struct tpm2_null_auth_area));
tpm_buf_append(&buf, (const unsigned char *)&auth_area,
sizeof(auth_area));
- tpm_buf_append_u32(&buf, count);
-
- for (i = 0; i < count; i++) {
- for (j = 0; j < ARRAY_SIZE(tpm2_hash_map); j++) {
- if (digests[i].alg_id != tpm2_hash_map[j].tpm_id)
- continue;
- tpm_buf_append_u16(&buf, digests[i].alg_id);
- tpm_buf_append(&buf, (const unsigned char
- *)&digests[i].digest,
- hash_digest_size[tpm2_hash_map[j].crypto_id]);
- }
+ tpm_buf_append_u32(&buf, chip->nr_allocated_banks);
+
+ for (i = 0; i < chip->nr_allocated_banks; i++) {
+ tpm_buf_append_u16(&buf, digests[i].alg_id);
+ tpm_buf_append(&buf, (const unsigned char *)&digests[i].digest,
+ chip->allocated_banks[i].digest_size);
}
- rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 0, 0,
- "attempting extend a PCR value");
+ rc = tpm_transmit_cmd(chip, &buf, 0, "attempting extend a PCR value");
tpm_buf_destroy(&buf);
@@ -309,10 +324,10 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
do {
tpm_buf_reset(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_RANDOM);
tpm_buf_append_u16(&buf, num_bytes);
- err = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE,
+ err = tpm_transmit_cmd(chip, &buf,
offsetof(struct tpm2_get_random_out,
buffer),
- 0, "attempting get random");
+ "attempting get random");
if (err)
goto out;
@@ -341,14 +356,11 @@ out:
}
/**
- * tpm2_flush_context_cmd() - execute a TPM2_FlushContext command
+ * tpm2_flush_context() - execute a TPM2_FlushContext command
* @chip: TPM chip to use
* @handle: context handle
- * @flags: tpm transmit flags - bitmap
- *
*/
-void tpm2_flush_context_cmd(struct tpm_chip *chip, u32 handle,
- unsigned int flags)
+void tpm2_flush_context(struct tpm_chip *chip, u32 handle)
{
struct tpm_buf buf;
int rc;
@@ -362,9 +374,7 @@ void tpm2_flush_context_cmd(struct tpm_chip *chip, u32 handle,
tpm_buf_append_u32(&buf, handle);
- (void) tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 0, flags,
- "flushing context");
-
+ tpm_transmit_cmd(chip, &buf, 0, "flushing context");
tpm_buf_destroy(&buf);
}
@@ -449,7 +459,7 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
/* public */
tpm_buf_append_u16(&buf, 14 + options->policydigest_len);
- tpm_buf_append_u16(&buf, TPM2_ALG_KEYEDHASH);
+ tpm_buf_append_u16(&buf, TPM_ALG_KEYEDHASH);
tpm_buf_append_u16(&buf, hash);
/* policy */
@@ -464,7 +474,7 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
}
/* public parameters */
- tpm_buf_append_u16(&buf, TPM2_ALG_NULL);
+ tpm_buf_append_u16(&buf, TPM_ALG_NULL);
tpm_buf_append_u16(&buf, 0);
/* outside info */
@@ -478,8 +488,7 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
goto out;
}
- rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 4, 0,
- "sealing data");
+ rc = tpm_transmit_cmd(chip, &buf, 4, "sealing data");
if (rc)
goto out;
@@ -516,7 +525,6 @@ out:
* @payload: the key data in clear and encrypted form
* @options: authentication values and other options
* @blob_handle: returned blob handle
- * @flags: tpm transmit flags
*
* Return: 0 on success.
* -E2BIG on wrong payload size.
@@ -526,7 +534,7 @@ out:
static int tpm2_load_cmd(struct tpm_chip *chip,
struct trusted_key_payload *payload,
struct trusted_key_options *options,
- u32 *blob_handle, unsigned int flags)
+ u32 *blob_handle)
{
struct tpm_buf buf;
unsigned int private_len;
@@ -561,8 +569,7 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
goto out;
}
- rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 4, flags,
- "loading blob");
+ rc = tpm_transmit_cmd(chip, &buf, 4, "loading blob");
if (!rc)
*blob_handle = be32_to_cpup(
(__be32 *) &buf.data[TPM_HEADER_SIZE]);
@@ -583,7 +590,6 @@ out:
* @payload: the key data in clear and encrypted form
* @options: authentication values and other options
* @blob_handle: blob handle
- * @flags: tpm_transmit_cmd flags
*
* Return: 0 on success
* -EPERM on tpm error status
@@ -592,7 +598,7 @@ out:
static int tpm2_unseal_cmd(struct tpm_chip *chip,
struct trusted_key_payload *payload,
struct trusted_key_options *options,
- u32 blob_handle, unsigned int flags)
+ u32 blob_handle)
{
struct tpm_buf buf;
u16 data_len;
@@ -612,8 +618,7 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
options->blobauth /* hmac */,
TPM_DIGEST_SIZE);
- rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 6, flags,
- "unsealing");
+ rc = tpm_transmit_cmd(chip, &buf, 6, "unsealing");
if (rc > 0)
rc = -EPERM;
@@ -657,17 +662,12 @@ int tpm2_unseal_trusted(struct tpm_chip *chip,
u32 blob_handle;
int rc;
- mutex_lock(&chip->tpm_mutex);
- rc = tpm2_load_cmd(chip, payload, options, &blob_handle,
- TPM_TRANSMIT_UNLOCKED);
+ rc = tpm2_load_cmd(chip, payload, options, &blob_handle);
if (rc)
- goto out;
+ return rc;
- rc = tpm2_unseal_cmd(chip, payload, options, blob_handle,
- TPM_TRANSMIT_UNLOCKED);
- tpm2_flush_context_cmd(chip, blob_handle, TPM_TRANSMIT_UNLOCKED);
-out:
- mutex_unlock(&chip->tpm_mutex);
+ rc = tpm2_unseal_cmd(chip, payload, options, blob_handle);
+ tpm2_flush_context(chip, blob_handle);
return rc;
}
@@ -703,7 +703,7 @@ ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id, u32 *value,
tpm_buf_append_u32(&buf, TPM2_CAP_TPM_PROPERTIES);
tpm_buf_append_u32(&buf, property_id);
tpm_buf_append_u32(&buf, 1);
- rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 0, 0, NULL);
+ rc = tpm_transmit_cmd(chip, &buf, 0, NULL);
if (!rc) {
out = (struct tpm2_get_cap_out *)
&buf.data[TPM_HEADER_SIZE];
@@ -733,8 +733,7 @@ void tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type)
if (rc)
return;
tpm_buf_append_u16(&buf, shutdown_type);
- tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 0, 0,
- "stopping the TPM");
+ tpm_transmit_cmd(chip, &buf, 0, "stopping the TPM");
tpm_buf_destroy(&buf);
}
@@ -763,7 +762,7 @@ static int tpm2_do_selftest(struct tpm_chip *chip)
return rc;
tpm_buf_append_u8(&buf, full);
- rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 0, 0,
+ rc = tpm_transmit_cmd(chip, &buf, 0,
"attempting the self test");
tpm_buf_destroy(&buf);
@@ -790,7 +789,7 @@ static int tpm2_do_selftest(struct tpm_chip *chip)
*/
int tpm2_probe(struct tpm_chip *chip)
{
- struct tpm_output_header *out;
+ struct tpm_header *out;
struct tpm_buf buf;
int rc;
@@ -800,10 +799,10 @@ int tpm2_probe(struct tpm_chip *chip)
tpm_buf_append_u32(&buf, TPM2_CAP_TPM_PROPERTIES);
tpm_buf_append_u32(&buf, TPM_PT_TOTAL_COMMANDS);
tpm_buf_append_u32(&buf, 1);
- rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 0, 0, NULL);
+ rc = tpm_transmit_cmd(chip, &buf, 0, NULL);
/* We ignore TPM return codes on purpose. */
if (rc >= 0) {
- out = (struct tpm_output_header *)buf.data;
+ out = (struct tpm_header *)buf.data;
if (be16_to_cpu(out->tag) == TPM2_ST_NO_SESSIONS)
chip->flags |= TPM_CHIP_FLAG_TPM2;
}
@@ -812,6 +811,30 @@ int tpm2_probe(struct tpm_chip *chip)
}
EXPORT_SYMBOL_GPL(tpm2_probe);
+static int tpm2_init_bank_info(struct tpm_chip *chip, u32 bank_index)
+{
+ struct tpm_bank_info *bank = chip->allocated_banks + bank_index;
+ struct tpm_digest digest = { .alg_id = bank->alg_id };
+ int i;
+
+ /*
+ * Avoid unnecessary PCR read operations to reduce overhead
+ * and obtain identifiers of the crypto subsystem.
+ */
+ for (i = 0; i < ARRAY_SIZE(tpm2_hash_map); i++) {
+ enum hash_algo crypto_algo = tpm2_hash_map[i].crypto_id;
+
+ if (bank->alg_id != tpm2_hash_map[i].tpm_id)
+ continue;
+
+ bank->digest_size = hash_digest_size[crypto_algo];
+ bank->crypto_id = crypto_algo;
+ return 0;
+ }
+
+ return tpm2_pcr_read(chip, 0, &digest, &bank->digest_size);
+}
+
struct tpm2_pcr_selection {
__be16 hash_alg;
u8 size_of_select;
@@ -825,8 +848,10 @@ static ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip)
void *marker;
void *end;
void *pcr_select_offset;
- unsigned int count;
u32 sizeof_pcr_selection;
+ u32 nr_possible_banks;
+ u32 nr_alloc_banks = 0;
+ u16 hash_alg;
u32 rsp_len;
int rc;
int i = 0;
@@ -839,16 +864,18 @@ static ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip)
tpm_buf_append_u32(&buf, 0);
tpm_buf_append_u32(&buf, 1);
- rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 9, 0,
- "get tpm pcr allocation");
+ rc = tpm_transmit_cmd(chip, &buf, 9, "get tpm pcr allocation");
if (rc)
goto out;
- count = be32_to_cpup(
+ nr_possible_banks = be32_to_cpup(
(__be32 *)&buf.data[TPM_HEADER_SIZE + 5]);
- if (count > ARRAY_SIZE(chip->active_banks)) {
- rc = -ENODEV;
+ chip->allocated_banks = kcalloc(nr_possible_banks,
+ sizeof(*chip->allocated_banks),
+ GFP_KERNEL);
+ if (!chip->allocated_banks) {
+ rc = -ENOMEM;
goto out;
}
@@ -857,7 +884,7 @@ static ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip)
rsp_len = be32_to_cpup((__be32 *)&buf.data[2]);
end = &buf.data[rsp_len];
- for (i = 0; i < count; i++) {
+ for (i = 0; i < nr_possible_banks; i++) {
pcr_select_offset = marker +
offsetof(struct tpm2_pcr_selection, size_of_select);
if (pcr_select_offset >= end) {
@@ -866,17 +893,28 @@ static ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip)
}
memcpy(&pcr_selection, marker, sizeof(pcr_selection));
- chip->active_banks[i] = be16_to_cpu(pcr_selection.hash_alg);
+ hash_alg = be16_to_cpu(pcr_selection.hash_alg);
+
+ pcr_select_offset = memchr_inv(pcr_selection.pcr_select, 0,
+ pcr_selection.size_of_select);
+ if (pcr_select_offset) {
+ chip->allocated_banks[nr_alloc_banks].alg_id = hash_alg;
+
+ rc = tpm2_init_bank_info(chip, nr_alloc_banks);
+ if (rc < 0)
+ break;
+
+ nr_alloc_banks++;
+ }
+
sizeof_pcr_selection = sizeof(pcr_selection.hash_alg) +
sizeof(pcr_selection.size_of_select) +
pcr_selection.size_of_select;
marker = marker + sizeof_pcr_selection;
}
+ chip->nr_allocated_banks = nr_alloc_banks;
out:
- if (i < ARRAY_SIZE(chip->active_banks))
- chip->active_banks[i] = TPM2_ALG_ERROR;
-
tpm_buf_destroy(&buf);
return rc;
@@ -911,8 +949,7 @@ static int tpm2_get_cc_attrs_tbl(struct tpm_chip *chip)
tpm_buf_append_u32(&buf, TPM2_CC_FIRST);
tpm_buf_append_u32(&buf, nr_commands);
- rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE,
- 9 + 4 * nr_commands, 0, NULL);
+ rc = tpm_transmit_cmd(chip, &buf, 9 + 4 * nr_commands, NULL);
if (rc) {
tpm_buf_destroy(&buf);
goto out;
@@ -969,8 +1006,7 @@ static int tpm2_startup(struct tpm_chip *chip)
return rc;
tpm_buf_append_u16(&buf, TPM2_SU_CLEAR);
- rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 0, 0,
- "attempting to start the TPM");
+ rc = tpm_transmit_cmd(chip, &buf, 0, "attempting to start the TPM");
tpm_buf_destroy(&buf);
return rc;
diff --git a/drivers/char/tpm/tpm2-space.c b/drivers/char/tpm/tpm2-space.c
index dcdfde3c253e..4a2773c3374f 100644
--- a/drivers/char/tpm/tpm2-space.c
+++ b/drivers/char/tpm/tpm2-space.c
@@ -38,8 +38,7 @@ static void tpm2_flush_sessions(struct tpm_chip *chip, struct tpm_space *space)
for (i = 0; i < ARRAY_SIZE(space->session_tbl); i++) {
if (space->session_tbl[i])
- tpm2_flush_context_cmd(chip, space->session_tbl[i],
- TPM_TRANSMIT_NESTED);
+ tpm2_flush_context(chip, space->session_tbl[i]);
}
}
@@ -61,7 +60,10 @@ int tpm2_init_space(struct tpm_space *space)
void tpm2_del_space(struct tpm_chip *chip, struct tpm_space *space)
{
mutex_lock(&chip->tpm_mutex);
- tpm2_flush_sessions(chip, space);
+ if (!tpm_chip_start(chip)) {
+ tpm2_flush_sessions(chip, space);
+ tpm_chip_stop(chip);
+ }
mutex_unlock(&chip->tpm_mutex);
kfree(space->context_buf);
kfree(space->session_buf);
@@ -83,8 +85,7 @@ static int tpm2_load_context(struct tpm_chip *chip, u8 *buf,
body_size = sizeof(*ctx) + be16_to_cpu(ctx->blob_size);
tpm_buf_append(&tbuf, &buf[*offset], body_size);
- rc = tpm_transmit_cmd(chip, NULL, tbuf.data, PAGE_SIZE, 4,
- TPM_TRANSMIT_NESTED, NULL);
+ rc = tpm_transmit_cmd(chip, &tbuf, 4, NULL);
if (rc < 0) {
dev_warn(&chip->dev, "%s: failed with a system error %d\n",
__func__, rc);
@@ -132,8 +133,7 @@ static int tpm2_save_context(struct tpm_chip *chip, u32 handle, u8 *buf,
tpm_buf_append_u32(&tbuf, handle);
- rc = tpm_transmit_cmd(chip, NULL, tbuf.data, PAGE_SIZE, 0,
- TPM_TRANSMIT_NESTED, NULL);
+ rc = tpm_transmit_cmd(chip, &tbuf, 0, NULL);
if (rc < 0) {
dev_warn(&chip->dev, "%s: failed with a system error %d\n",
__func__, rc);
@@ -162,15 +162,14 @@ static int tpm2_save_context(struct tpm_chip *chip, u32 handle, u8 *buf,
return 0;
}
-static void tpm2_flush_space(struct tpm_chip *chip)
+void tpm2_flush_space(struct tpm_chip *chip)
{
struct tpm_space *space = &chip->work_space;
int i;
for (i = 0; i < ARRAY_SIZE(space->context_tbl); i++)
if (space->context_tbl[i] && ~space->context_tbl[i])
- tpm2_flush_context_cmd(chip, space->context_tbl[i],
- TPM_TRANSMIT_NESTED);
+ tpm2_flush_context(chip, space->context_tbl[i]);
tpm2_flush_sessions(chip, space);
}
@@ -264,14 +263,54 @@ static int tpm2_map_command(struct tpm_chip *chip, u32 cc, u8 *cmd)
return 0;
}
-int tpm2_prepare_space(struct tpm_chip *chip, struct tpm_space *space, u32 cc,
- u8 *cmd)
+static int tpm_find_and_validate_cc(struct tpm_chip *chip,
+ struct tpm_space *space,
+ const void *cmd, size_t len)
+{
+ const struct tpm_header *header = (const void *)cmd;
+ int i;
+ u32 cc;
+ u32 attrs;
+ unsigned int nr_handles;
+
+ if (len < TPM_HEADER_SIZE || !chip->nr_commands)
+ return -EINVAL;
+
+ cc = be32_to_cpu(header->ordinal);
+
+ i = tpm2_find_cc(chip, cc);
+ if (i < 0) {
+ dev_dbg(&chip->dev, "0x%04X is an invalid command\n",
+ cc);
+ return -EOPNOTSUPP;
+ }
+
+ attrs = chip->cc_attrs_tbl[i];
+ nr_handles =
+ 4 * ((attrs >> TPM2_CC_ATTR_CHANDLES) & GENMASK(2, 0));
+ if (len < TPM_HEADER_SIZE + 4 * nr_handles)
+ goto err_len;
+
+ return cc;
+err_len:
+ dev_dbg(&chip->dev, "%s: insufficient command length %zu", __func__,
+ len);
+ return -EINVAL;
+}
+
+int tpm2_prepare_space(struct tpm_chip *chip, struct tpm_space *space, u8 *cmd,
+ size_t cmdsiz)
{
int rc;
+ int cc;
if (!space)
return 0;
+ cc = tpm_find_and_validate_cc(chip, space, cmd, cmdsiz);
+ if (cc < 0)
+ return cc;
+
memcpy(&chip->work_space.context_tbl, &space->context_tbl,
sizeof(space->context_tbl));
memcpy(&chip->work_space.session_tbl, &space->session_tbl,
@@ -291,6 +330,7 @@ int tpm2_prepare_space(struct tpm_chip *chip, struct tpm_space *space, u32 cc,
return rc;
}
+ chip->last_cc = cc;
return 0;
}
@@ -334,7 +374,7 @@ static int tpm2_map_response_header(struct tpm_chip *chip, u32 cc, u8 *rsp,
size_t len)
{
struct tpm_space *space = &chip->work_space;
- struct tpm_output_header *header = (void *)rsp;
+ struct tpm_header *header = (struct tpm_header *)rsp;
u32 phandle;
u32 phandle_type;
u32 vhandle;
@@ -377,7 +417,7 @@ static int tpm2_map_response_header(struct tpm_chip *chip, u32 cc, u8 *rsp,
return 0;
out_no_slots:
- tpm2_flush_context_cmd(chip, phandle, TPM_TRANSMIT_NESTED);
+ tpm2_flush_context(chip, phandle);
dev_warn(&chip->dev, "%s: out of slots for 0x%08X\n", __func__,
phandle);
return -ENOMEM;
@@ -394,7 +434,7 @@ static int tpm2_map_response_body(struct tpm_chip *chip, u32 cc, u8 *rsp,
size_t len)
{
struct tpm_space *space = &chip->work_space;
- struct tpm_output_header *header = (void *)rsp;
+ struct tpm_header *header = (struct tpm_header *)rsp;
struct tpm2_cap_handles *data;
u32 phandle;
u32 phandle_type;
@@ -464,8 +504,7 @@ static int tpm2_save_space(struct tpm_chip *chip)
} else if (rc)
return rc;
- tpm2_flush_context_cmd(chip, space->context_tbl[i],
- TPM_TRANSMIT_NESTED);
+ tpm2_flush_context(chip, space->context_tbl[i]);
space->context_tbl[i] = ~0;
}
@@ -490,30 +529,30 @@ static int tpm2_save_space(struct tpm_chip *chip)
}
int tpm2_commit_space(struct tpm_chip *chip, struct tpm_space *space,
- u32 cc, u8 *buf, size_t *bufsiz)
+ void *buf, size_t *bufsiz)
{
- struct tpm_output_header *header = (void *)buf;
+ struct tpm_header *header = buf;
int rc;
if (!space)
return 0;
- rc = tpm2_map_response_header(chip, cc, buf, *bufsiz);
+ rc = tpm2_map_response_header(chip, chip->last_cc, buf, *bufsiz);
if (rc) {
tpm2_flush_space(chip);
- return rc;
+ goto out;
}
- rc = tpm2_map_response_body(chip, cc, buf, *bufsiz);
+ rc = tpm2_map_response_body(chip, chip->last_cc, buf, *bufsiz);
if (rc) {
tpm2_flush_space(chip);
- return rc;
+ goto out;
}
rc = tpm2_save_space(chip);
if (rc) {
tpm2_flush_space(chip);
- return rc;
+ goto out;
}
*bufsiz = be32_to_cpu(header->length);
@@ -526,4 +565,7 @@ int tpm2_commit_space(struct tpm_chip *chip, struct tpm_space *space,
memcpy(space->session_buf, chip->work_space.session_buf, PAGE_SIZE);
return 0;
+out:
+ dev_err(&chip->dev, "%s: error %d\n", __func__, rc);
+ return rc;
}
diff --git a/drivers/char/tpm/tpm_atmel.c b/drivers/char/tpm/tpm_atmel.c
index 66a14526aaf4..a290b30a0c35 100644
--- a/drivers/char/tpm/tpm_atmel.c
+++ b/drivers/char/tpm/tpm_atmel.c
@@ -105,7 +105,7 @@ static int tpm_atml_send(struct tpm_chip *chip, u8 *buf, size_t count)
iowrite8(buf[i], priv->iobase);
}
- return count;
+ return 0;
}
static void tpm_atml_cancel(struct tpm_chip *chip)
diff --git a/drivers/char/tpm/tpm_crb.c b/drivers/char/tpm/tpm_crb.c
index 36952ef98f90..763fc7e6c005 100644
--- a/drivers/char/tpm/tpm_crb.c
+++ b/drivers/char/tpm/tpm_crb.c
@@ -287,19 +287,29 @@ static int crb_recv(struct tpm_chip *chip, u8 *buf, size_t count)
struct crb_priv *priv = dev_get_drvdata(&chip->dev);
unsigned int expected;
- /* sanity check */
- if (count < 6)
+ /* A sanity check that the upper layer wants to get at least the header
+ * as that is the minimum size for any TPM response.
+ */
+ if (count < TPM_HEADER_SIZE)
return -EIO;
+ /* If this bit is set, according to the spec, the TPM is in
+ * unrecoverable condition.
+ */
if (ioread32(&priv->regs_t->ctrl_sts) & CRB_CTRL_STS_ERROR)
return -EIO;
- memcpy_fromio(buf, priv->rsp, 6);
- expected = be32_to_cpup((__be32 *) &buf[2]);
- if (expected > count || expected < 6)
+ /* Read the first 8 bytes in order to get the length of the response.
+ * We read exactly a quad word in order to make sure that the remaining
+ * reads will be aligned.
+ */
+ memcpy_fromio(buf, priv->rsp, 8);
+
+ expected = be32_to_cpup((__be32 *)&buf[2]);
+ if (expected > count || expected < TPM_HEADER_SIZE)
return -EIO;
- memcpy_fromio(&buf[6], &priv->rsp[6], expected - 6);
+ memcpy_fromio(&buf[8], &priv->rsp[8], expected - 8);
return expected;
}
diff --git a/drivers/char/tpm/tpm_i2c_atmel.c b/drivers/char/tpm/tpm_i2c_atmel.c
index 95ce2e9ccdc6..8a7e80923091 100644
--- a/drivers/char/tpm/tpm_i2c_atmel.c
+++ b/drivers/char/tpm/tpm_i2c_atmel.c
@@ -46,7 +46,7 @@ struct priv_data {
/* This is the amount we read on the first try. 25 was chosen to fit a
* fair number of read responses in the buffer so a 2nd retry can be
* avoided in small message cases. */
- u8 buffer[sizeof(struct tpm_output_header) + 25];
+ u8 buffer[sizeof(struct tpm_header) + 25];
};
static int i2c_atmel_send(struct tpm_chip *chip, u8 *buf, size_t len)
@@ -65,15 +65,22 @@ static int i2c_atmel_send(struct tpm_chip *chip, u8 *buf, size_t len)
dev_dbg(&chip->dev,
"%s(buf=%*ph len=%0zx) -> sts=%d\n", __func__,
(int)min_t(size_t, 64, len), buf, len, status);
- return status;
+
+ if (status < 0)
+ return status;
+
+ /* The upper layer does not support incomplete sends. */
+ if (status != len)
+ return -E2BIG;
+
+ return 0;
}
static int i2c_atmel_recv(struct tpm_chip *chip, u8 *buf, size_t count)
{
struct priv_data *priv = dev_get_drvdata(&chip->dev);
struct i2c_client *client = to_i2c_client(chip->dev.parent);
- struct tpm_output_header *hdr =
- (struct tpm_output_header *)priv->buffer;
+ struct tpm_header *hdr = (struct tpm_header *)priv->buffer;
u32 expected_len;
int rc;
diff --git a/drivers/char/tpm/tpm_i2c_infineon.c b/drivers/char/tpm/tpm_i2c_infineon.c
index 9086edc9066b..3b4e9672ff6c 100644
--- a/drivers/char/tpm/tpm_i2c_infineon.c
+++ b/drivers/char/tpm/tpm_i2c_infineon.c
@@ -26,8 +26,7 @@
#include <linux/wait.h>
#include "tpm.h"
-/* max. buffer size supported by our TPM */
-#define TPM_BUFSIZE 1260
+#define TPM_I2C_INFINEON_BUFSIZE 1260
/* max. number of iterations after I2C NAK */
#define MAX_COUNT 3
@@ -63,11 +62,13 @@ enum i2c_chip_type {
UNKNOWN,
};
-/* Structure to store I2C TPM specific stuff */
struct tpm_inf_dev {
struct i2c_client *client;
int locality;
- u8 buf[TPM_BUFSIZE + sizeof(u8)]; /* max. buffer size + addr */
+ /* In addition to the data itself, the buffer must fit the 7-bit I2C
+ * address and the direction bit.
+ */
+ u8 buf[TPM_I2C_INFINEON_BUFSIZE + 1];
struct tpm_chip *chip;
enum i2c_chip_type chip_type;
unsigned int adapterlimit;
@@ -219,7 +220,7 @@ static int iic_tpm_write_generic(u8 addr, u8 *buffer, size_t len,
.buf = tpm_dev.buf
};
- if (len > TPM_BUFSIZE)
+ if (len > TPM_I2C_INFINEON_BUFSIZE)
return -EINVAL;
if (!tpm_dev.client->adapter->algo->master_xfer)
@@ -527,8 +528,8 @@ static int tpm_tis_i2c_send(struct tpm_chip *chip, u8 *buf, size_t len)
u8 retries = 0;
u8 sts = TPM_STS_GO;
- if (len > TPM_BUFSIZE)
- return -E2BIG; /* command is too long for our tpm, sorry */
+ if (len > TPM_I2C_INFINEON_BUFSIZE)
+ return -E2BIG;
if (request_locality(chip, 0) < 0)
return -EBUSY;
@@ -587,7 +588,7 @@ static int tpm_tis_i2c_send(struct tpm_chip *chip, u8 *buf, size_t len)
/* go and do it */
iic_tpm_write(TPM_STS(tpm_dev.locality), &sts, 1);
- return len;
+ return 0;
out_err:
tpm_tis_i2c_ready(chip);
/* The TPM needs some time to clean up here,
diff --git a/drivers/char/tpm/tpm_i2c_nuvoton.c b/drivers/char/tpm/tpm_i2c_nuvoton.c
index 217f7f1cbde8..315a3b4548f7 100644
--- a/drivers/char/tpm/tpm_i2c_nuvoton.c
+++ b/drivers/char/tpm/tpm_i2c_nuvoton.c
@@ -35,14 +35,12 @@
#include "tpm.h"
/* I2C interface offsets */
-#define TPM_STS 0x00
-#define TPM_BURST_COUNT 0x01
-#define TPM_DATA_FIFO_W 0x20
-#define TPM_DATA_FIFO_R 0x40
-#define TPM_VID_DID_RID 0x60
-/* TPM command header size */
-#define TPM_HEADER_SIZE 10
-#define TPM_RETRY 5
+#define TPM_STS 0x00
+#define TPM_BURST_COUNT 0x01
+#define TPM_DATA_FIFO_W 0x20
+#define TPM_DATA_FIFO_R 0x40
+#define TPM_VID_DID_RID 0x60
+#define TPM_I2C_RETRIES 5
/*
* I2C bus device maximum buffer size w/o counting I2C address or command
* i.e. max size required for I2C write is 34 = addr, command, 32 bytes data
@@ -292,7 +290,7 @@ static int i2c_nuvoton_recv(struct tpm_chip *chip, u8 *buf, size_t count)
dev_err(dev, "%s() count < header size\n", __func__);
return -EIO;
}
- for (retries = 0; retries < TPM_RETRY; retries++) {
+ for (retries = 0; retries < TPM_I2C_RETRIES; retries++) {
if (retries > 0) {
/* if this is not the first trial, set responseRetry */
i2c_nuvoton_write_status(client,
@@ -467,7 +465,7 @@ static int i2c_nuvoton_send(struct tpm_chip *chip, u8 *buf, size_t len)
}
dev_dbg(dev, "%s() -> %zd\n", __func__, len);
- return len;
+ return 0;
}
static bool i2c_nuvoton_req_canceled(struct tpm_chip *chip, u8 status)
diff --git a/drivers/char/tpm/tpm_ibmvtpm.c b/drivers/char/tpm/tpm_ibmvtpm.c
index 07b5a487d0c8..757ca45b39b8 100644
--- a/drivers/char/tpm/tpm_ibmvtpm.c
+++ b/drivers/char/tpm/tpm_ibmvtpm.c
@@ -139,14 +139,14 @@ static int tpm_ibmvtpm_recv(struct tpm_chip *chip, u8 *buf, size_t count)
}
/**
- * tpm_ibmvtpm_send - Send tpm request
- *
+ * tpm_ibmvtpm_send() - Send a TPM command
* @chip: tpm chip struct
* @buf: buffer contains data to send
* @count: size of buffer
*
* Return:
- * Number of bytes sent or < 0 on error.
+ * 0 on success,
+ * -errno on error
*/
static int tpm_ibmvtpm_send(struct tpm_chip *chip, u8 *buf, size_t count)
{
@@ -192,7 +192,7 @@ static int tpm_ibmvtpm_send(struct tpm_chip *chip, u8 *buf, size_t count)
rc = 0;
ibmvtpm->tpm_processing_cmd = false;
} else
- rc = count;
+ rc = 0;
spin_unlock(&ibmvtpm->rtce_lock);
return rc;
diff --git a/drivers/char/tpm/tpm_infineon.c b/drivers/char/tpm/tpm_infineon.c
index d8f10047fbba..97f6d4fe0aee 100644
--- a/drivers/char/tpm/tpm_infineon.c
+++ b/drivers/char/tpm/tpm_infineon.c
@@ -354,7 +354,7 @@ static int tpm_inf_send(struct tpm_chip *chip, u8 * buf, size_t count)
for (i = 0; i < count; i++) {
wait_and_send(chip, buf[i]);
}
- return count;
+ return 0;
}
static void tpm_inf_cancel(struct tpm_chip *chip)
diff --git a/drivers/char/tpm/tpm_nsc.c b/drivers/char/tpm/tpm_nsc.c
index 5d6cce74cd3f..9bee3c5eb4bf 100644
--- a/drivers/char/tpm/tpm_nsc.c
+++ b/drivers/char/tpm/tpm_nsc.c
@@ -226,7 +226,7 @@ static int tpm_nsc_send(struct tpm_chip *chip, u8 * buf, size_t count)
}
outb(NSC_COMMAND_EOC, priv->base + NSC_COMMAND);
- return count;
+ return 0;
}
static void tpm_nsc_cancel(struct tpm_chip *chip)
diff --git a/drivers/char/tpm/tpm_ppi.c b/drivers/char/tpm/tpm_ppi.c
index 86dd8521feef..75e7a856177c 100644
--- a/drivers/char/tpm/tpm_ppi.c
+++ b/drivers/char/tpm/tpm_ppi.c
@@ -20,7 +20,8 @@
#include <linux/acpi.h>
#include "tpm.h"
-#define TPM_PPI_REVISION_ID 1
+#define TPM_PPI_REVISION_ID_1 1
+#define TPM_PPI_REVISION_ID_2 2
#define TPM_PPI_FN_VERSION 1
#define TPM_PPI_FN_SUBREQ 2
#define TPM_PPI_FN_GETREQ 3
@@ -28,7 +29,7 @@
#define TPM_PPI_FN_GETRSP 5
#define TPM_PPI_FN_SUBREQ2 7
#define TPM_PPI_FN_GETOPR 8
-#define PPI_TPM_REQ_MAX 22
+#define PPI_TPM_REQ_MAX 101 /* PPI 1.3 for TPM 2 */
#define PPI_VS_REQ_START 128
#define PPI_VS_REQ_END 255
@@ -36,14 +37,18 @@ static const guid_t tpm_ppi_guid =
GUID_INIT(0x3DDDFAA6, 0x361B, 0x4EB4,
0xA4, 0x24, 0x8D, 0x10, 0x08, 0x9D, 0x16, 0x53);
+static bool tpm_ppi_req_has_parameter(u64 req)
+{
+ return req == 23;
+}
+
static inline union acpi_object *
tpm_eval_dsm(acpi_handle ppi_handle, int func, acpi_object_type type,
- union acpi_object *argv4)
+ union acpi_object *argv4, u64 rev)
{
BUG_ON(!ppi_handle);
return acpi_evaluate_dsm_typed(ppi_handle, &tpm_ppi_guid,
- TPM_PPI_REVISION_ID,
- func, argv4, type);
+ rev, func, argv4, type);
}
static ssize_t tpm_show_ppi_version(struct device *dev,
@@ -60,9 +65,14 @@ static ssize_t tpm_show_ppi_request(struct device *dev,
ssize_t size = -EINVAL;
union acpi_object *obj;
struct tpm_chip *chip = to_tpm_chip(dev);
+ u64 rev = TPM_PPI_REVISION_ID_2;
+ u64 req;
+
+ if (strcmp(chip->ppi_version, "1.2") < 0)
+ rev = TPM_PPI_REVISION_ID_1;
obj = tpm_eval_dsm(chip->acpi_dev_handle, TPM_PPI_FN_GETREQ,
- ACPI_TYPE_PACKAGE, NULL);
+ ACPI_TYPE_PACKAGE, NULL, rev);
if (!obj)
return -ENXIO;
@@ -72,7 +82,23 @@ static ssize_t tpm_show_ppi_request(struct device *dev,
* error. The second is pending TPM operation requested by the OS, 0
* means none and >0 means operation value.
*/
- if (obj->package.count == 2 &&
+ if (obj->package.count == 3 &&
+ obj->package.elements[0].type == ACPI_TYPE_INTEGER &&
+ obj->package.elements[1].type == ACPI_TYPE_INTEGER &&
+ obj->package.elements[2].type == ACPI_TYPE_INTEGER) {
+ if (obj->package.elements[0].integer.value)
+ size = -EFAULT;
+ else {
+ req = obj->package.elements[1].integer.value;
+ if (tpm_ppi_req_has_parameter(req))
+ size = scnprintf(buf, PAGE_SIZE,
+ "%llu %llu\n", req,
+ obj->package.elements[2].integer.value);
+ else
+ size = scnprintf(buf, PAGE_SIZE,
+ "%llu\n", req);
+ }
+ } else if (obj->package.count == 2 &&
obj->package.elements[0].type == ACPI_TYPE_INTEGER &&
obj->package.elements[1].type == ACPI_TYPE_INTEGER) {
if (obj->package.elements[0].integer.value)
@@ -94,9 +120,10 @@ static ssize_t tpm_store_ppi_request(struct device *dev,
u32 req;
u64 ret;
int func = TPM_PPI_FN_SUBREQ;
- union acpi_object *obj, tmp;
- union acpi_object argv4 = ACPI_INIT_DSM_ARGV4(1, &tmp);
+ union acpi_object *obj, tmp[2];
+ union acpi_object argv4 = ACPI_INIT_DSM_ARGV4(2, tmp);
struct tpm_chip *chip = to_tpm_chip(dev);
+ u64 rev = TPM_PPI_REVISION_ID_1;
/*
* the function to submit TPM operation request to pre-os environment
@@ -104,7 +131,7 @@ static ssize_t tpm_store_ppi_request(struct device *dev,
* version 1.1
*/
if (acpi_check_dsm(chip->acpi_dev_handle, &tpm_ppi_guid,
- TPM_PPI_REVISION_ID, 1 << TPM_PPI_FN_SUBREQ2))
+ TPM_PPI_REVISION_ID_1, 1 << TPM_PPI_FN_SUBREQ2))
func = TPM_PPI_FN_SUBREQ2;
/*
@@ -113,20 +140,29 @@ static ssize_t tpm_store_ppi_request(struct device *dev,
* string/package type. For PPI version 1.0 and 1.1, use buffer type
* for compatibility, and use package type since 1.2 according to spec.
*/
- if (strcmp(chip->ppi_version, "1.2") < 0) {
+ if (strcmp(chip->ppi_version, "1.3") == 0) {
+ if (sscanf(buf, "%llu %llu", &tmp[0].integer.value,
+ &tmp[1].integer.value) != 2)
+ goto ppi12;
+ rev = TPM_PPI_REVISION_ID_2;
+ tmp[0].type = ACPI_TYPE_INTEGER;
+ tmp[1].type = ACPI_TYPE_INTEGER;
+ } else if (strcmp(chip->ppi_version, "1.2") < 0) {
if (sscanf(buf, "%d", &req) != 1)
return -EINVAL;
argv4.type = ACPI_TYPE_BUFFER;
argv4.buffer.length = sizeof(req);
argv4.buffer.pointer = (u8 *)&req;
} else {
- tmp.type = ACPI_TYPE_INTEGER;
- if (sscanf(buf, "%llu", &tmp.integer.value) != 1)
+ppi12:
+ argv4.package.count = 1;
+ tmp[0].type = ACPI_TYPE_INTEGER;
+ if (sscanf(buf, "%llu", &tmp[0].integer.value) != 1)
return -EINVAL;
}
obj = tpm_eval_dsm(chip->acpi_dev_handle, func, ACPI_TYPE_INTEGER,
- &argv4);
+ &argv4, rev);
if (!obj) {
return -ENXIO;
} else {
@@ -170,7 +206,7 @@ static ssize_t tpm_show_ppi_transition_action(struct device *dev,
if (strcmp(chip->ppi_version, "1.2") < 0)
obj = &tmp;
obj = tpm_eval_dsm(chip->acpi_dev_handle, TPM_PPI_FN_GETACT,
- ACPI_TYPE_INTEGER, obj);
+ ACPI_TYPE_INTEGER, obj, TPM_PPI_REVISION_ID_1);
if (!obj) {
return -ENXIO;
} else {
@@ -196,7 +232,7 @@ static ssize_t tpm_show_ppi_response(struct device *dev,
struct tpm_chip *chip = to_tpm_chip(dev);
obj = tpm_eval_dsm(chip->acpi_dev_handle, TPM_PPI_FN_GETRSP,
- ACPI_TYPE_PACKAGE, NULL);
+ ACPI_TYPE_PACKAGE, NULL, TPM_PPI_REVISION_ID_1);
if (!obj)
return -ENXIO;
@@ -264,7 +300,7 @@ static ssize_t show_ppi_operations(acpi_handle dev_handle, char *buf, u32 start,
"User not required",
};
- if (!acpi_check_dsm(dev_handle, &tpm_ppi_guid, TPM_PPI_REVISION_ID,
+ if (!acpi_check_dsm(dev_handle, &tpm_ppi_guid, TPM_PPI_REVISION_ID_1,
1 << TPM_PPI_FN_GETOPR))
return -EPERM;
@@ -272,7 +308,8 @@ static ssize_t show_ppi_operations(acpi_handle dev_handle, char *buf, u32 start,
for (i = start; i <= end; i++) {
tmp.integer.value = i;
obj = tpm_eval_dsm(dev_handle, TPM_PPI_FN_GETOPR,
- ACPI_TYPE_INTEGER, &argv);
+ ACPI_TYPE_INTEGER, &argv,
+ TPM_PPI_REVISION_ID_1);
if (!obj) {
return -ENOMEM;
} else {
@@ -338,12 +375,13 @@ void tpm_add_ppi(struct tpm_chip *chip)
return;
if (!acpi_check_dsm(chip->acpi_dev_handle, &tpm_ppi_guid,
- TPM_PPI_REVISION_ID, 1 << TPM_PPI_FN_VERSION))
+ TPM_PPI_REVISION_ID_1, 1 << TPM_PPI_FN_VERSION))
return;
/* Cache PPI version string. */
obj = acpi_evaluate_dsm_typed(chip->acpi_dev_handle, &tpm_ppi_guid,
- TPM_PPI_REVISION_ID, TPM_PPI_FN_VERSION,
+ TPM_PPI_REVISION_ID_1,
+ TPM_PPI_FN_VERSION,
NULL, ACPI_TYPE_STRING);
if (obj) {
strlcpy(chip->ppi_version, obj->string.pointer,
diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c
index bf7e49cfa643..b9f64684c3fb 100644
--- a/drivers/char/tpm/tpm_tis_core.c
+++ b/drivers/char/tpm/tpm_tis_core.c
@@ -481,7 +481,7 @@ static int tpm_tis_send_main(struct tpm_chip *chip, const u8 *buf, size_t len)
goto out_err;
}
}
- return len;
+ return 0;
out_err:
tpm_tis_ready(chip);
return rc;
@@ -521,35 +521,38 @@ static const struct tis_vendor_timeout_override vendor_timeout_overrides[] = {
(TIS_SHORT_TIMEOUT*1000), (TIS_SHORT_TIMEOUT*1000) } },
};
-static bool tpm_tis_update_timeouts(struct tpm_chip *chip,
+static void tpm_tis_update_timeouts(struct tpm_chip *chip,
unsigned long *timeout_cap)
{
struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
int i, rc;
u32 did_vid;
+ chip->timeout_adjusted = false;
+
if (chip->ops->clk_enable != NULL)
chip->ops->clk_enable(chip, true);
rc = tpm_tis_read32(priv, TPM_DID_VID(0), &did_vid);
- if (rc < 0)
+ if (rc < 0) {
+ dev_warn(&chip->dev, "%s: failed to read did_vid: %d\n",
+ __func__, rc);
goto out;
+ }
for (i = 0; i != ARRAY_SIZE(vendor_timeout_overrides); i++) {
if (vendor_timeout_overrides[i].did_vid != did_vid)
continue;
memcpy(timeout_cap, vendor_timeout_overrides[i].timeout_us,
sizeof(vendor_timeout_overrides[i].timeout_us));
- rc = true;
+ chip->timeout_adjusted = true;
}
- rc = false;
-
out:
if (chip->ops->clk_enable != NULL)
chip->ops->clk_enable(chip, false);
- return rc;
+ return;
}
/*
@@ -913,7 +916,11 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq,
intmask &= ~TPM_GLOBAL_INT_ENABLE;
tpm_tis_write32(priv, TPM_INT_ENABLE(priv->locality), intmask);
+ rc = tpm_chip_start(chip);
+ if (rc)
+ goto out_err;
rc = tpm2_probe(chip);
+ tpm_chip_stop(chip);
if (rc)
goto out_err;
diff --git a/drivers/char/tpm/tpm_vtpm_proxy.c b/drivers/char/tpm/tpm_vtpm_proxy.c
index 87a0ce47f201..d74f3de74ae6 100644
--- a/drivers/char/tpm/tpm_vtpm_proxy.c
+++ b/drivers/char/tpm/tpm_vtpm_proxy.c
@@ -303,9 +303,9 @@ out:
static int vtpm_proxy_is_driver_command(struct tpm_chip *chip,
u8 *buf, size_t count)
{
- struct tpm_input_header *hdr = (struct tpm_input_header *)buf;
+ struct tpm_header *hdr = (struct tpm_header *)buf;
- if (count < sizeof(struct tpm_input_header))
+ if (count < sizeof(struct tpm_header))
return 0;
if (chip->flags & TPM_CHIP_FLAG_TPM2) {
@@ -335,7 +335,6 @@ static int vtpm_proxy_is_driver_command(struct tpm_chip *chip,
static int vtpm_proxy_tpm_op_send(struct tpm_chip *chip, u8 *buf, size_t count)
{
struct proxy_dev *proxy_dev = dev_get_drvdata(&chip->dev);
- int rc = 0;
if (count > sizeof(proxy_dev->buffer)) {
dev_err(&chip->dev,
@@ -366,7 +365,7 @@ static int vtpm_proxy_tpm_op_send(struct tpm_chip *chip, u8 *buf, size_t count)
wake_up_interruptible(&proxy_dev->wq);
- return rc;
+ return 0;
}
static void vtpm_proxy_tpm_op_cancel(struct tpm_chip *chip)
@@ -402,7 +401,7 @@ static int vtpm_proxy_request_locality(struct tpm_chip *chip, int locality)
{
struct tpm_buf buf;
int rc;
- const struct tpm_output_header *header;
+ const struct tpm_header *header;
struct proxy_dev *proxy_dev = dev_get_drvdata(&chip->dev);
if (chip->flags & TPM_CHIP_FLAG_TPM2)
@@ -417,9 +416,7 @@ static int vtpm_proxy_request_locality(struct tpm_chip *chip, int locality)
proxy_dev->state |= STATE_DRIVER_COMMAND;
- rc = tpm_transmit_cmd(chip, NULL, buf.data, tpm_buf_length(&buf), 0,
- TPM_TRANSMIT_NESTED,
- "attempting to set locality");
+ rc = tpm_transmit_cmd(chip, &buf, 0, "attempting to set locality");
proxy_dev->state &= ~STATE_DRIVER_COMMAND;
@@ -428,7 +425,7 @@ static int vtpm_proxy_request_locality(struct tpm_chip *chip, int locality)
goto out;
}
- header = (const struct tpm_output_header *)buf.data;
+ header = (const struct tpm_header *)buf.data;
rc = be32_to_cpu(header->return_code);
if (rc)
locality = -1;
diff --git a/drivers/char/tpm/xen-tpmfront.c b/drivers/char/tpm/xen-tpmfront.c
index b150f87f38f5..4e2d00cb0d81 100644
--- a/drivers/char/tpm/xen-tpmfront.c
+++ b/drivers/char/tpm/xen-tpmfront.c
@@ -163,7 +163,7 @@ static int vtpm_send(struct tpm_chip *chip, u8 *buf, size_t count)
wmb();
notify_remote_via_evtchn(priv->evtchn);
- ordinal = be32_to_cpu(((struct tpm_input_header*)buf)->ordinal);
+ ordinal = be32_to_cpu(((struct tpm_header *)buf)->ordinal);
duration = tpm_calc_ordinal_duration(chip, ordinal);
if (wait_for_tpm_stat(chip, VTPM_STATUS_IDLE, duration,
@@ -173,7 +173,7 @@ static int vtpm_send(struct tpm_chip *chip, u8 *buf, size_t count)
return -ETIME;
}
- return count;
+ return 0;
}
static int vtpm_recv(struct tpm_chip *chip, u8 *buf, size_t count)
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index d2f0bb5ba47e..e705aab9e38b 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -290,6 +290,12 @@ config COMMON_CLK_BD718XX
This driver supports ROHM BD71837 and ROHM BD71847
PMICs clock gates.
+config COMMON_CLK_FIXED_MMIO
+ bool "Clock driver for Memory Mapped Fixed values"
+ depends on COMMON_CLK && OF
+ help
+ Support for Memory Mapped IO Fixed clocks
+
source "drivers/clk/actions/Kconfig"
source "drivers/clk/bcm/Kconfig"
source "drivers/clk/hisilicon/Kconfig"
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 8a9440a97500..1db133652f0c 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -27,6 +27,7 @@ obj-$(CONFIG_COMMON_CLK_CDCE925) += clk-cdce925.o
obj-$(CONFIG_ARCH_CLPS711X) += clk-clps711x.o
obj-$(CONFIG_COMMON_CLK_CS2000_CP) += clk-cs2000-cp.o
obj-$(CONFIG_ARCH_EFM32) += clk-efm32gg.o
+obj-$(CONFIG_COMMON_CLK_FIXED_MMIO) += clk-fixed-mmio.o
obj-$(CONFIG_COMMON_CLK_GEMINI) += clk-gemini.o
obj-$(CONFIG_COMMON_CLK_ASPEED) += clk-aspeed.o
obj-$(CONFIG_ARCH_HIGHBANK) += clk-highbank.o
@@ -78,7 +79,7 @@ obj-$(CONFIG_ARCH_K3) += keystone/
obj-$(CONFIG_ARCH_KEYSTONE) += keystone/
obj-$(CONFIG_MACH_LOONGSON32) += loongson1/
obj-y += mediatek/
-obj-$(CONFIG_COMMON_CLK_AMLOGIC) += meson/
+obj-$(CONFIG_ARCH_MESON) += meson/
obj-$(CONFIG_MACH_PIC32) += microchip/
ifeq ($(CONFIG_COMMON_CLK), y)
obj-$(CONFIG_ARCH_MMP) += mmp/
diff --git a/drivers/clk/actions/Kconfig b/drivers/clk/actions/Kconfig
index 04f0a6355726..5b45ca35757e 100644
--- a/drivers/clk/actions/Kconfig
+++ b/drivers/clk/actions/Kconfig
@@ -9,6 +9,11 @@ if CLK_ACTIONS
# SoC Drivers
+config CLK_OWL_S500
+ bool "Support for the Actions Semi OWL S500 clocks"
+ depends on ARCH_ACTIONS || COMPILE_TEST
+ default ARCH_ACTIONS
+
config CLK_OWL_S700
bool "Support for the Actions Semi OWL S700 clocks"
depends on (ARM64 && ARCH_ACTIONS) || COMPILE_TEST
diff --git a/drivers/clk/actions/Makefile b/drivers/clk/actions/Makefile
index ccfdf9781cef..a2588e55c790 100644
--- a/drivers/clk/actions/Makefile
+++ b/drivers/clk/actions/Makefile
@@ -10,5 +10,6 @@ clk-owl-y += owl-pll.o
clk-owl-y += owl-reset.o
# SoC support
+obj-$(CONFIG_CLK_OWL_S500) += owl-s500.o
obj-$(CONFIG_CLK_OWL_S700) += owl-s700.o
obj-$(CONFIG_CLK_OWL_S900) += owl-s900.o
diff --git a/drivers/clk/actions/owl-pll.c b/drivers/clk/actions/owl-pll.c
index 058e06d7099f..02437bdedf4d 100644
--- a/drivers/clk/actions/owl-pll.c
+++ b/drivers/clk/actions/owl-pll.c
@@ -179,7 +179,7 @@ static int owl_pll_set_rate(struct clk_hw *hw, unsigned long rate,
regmap_write(common->regmap, pll_hw->reg, reg);
- udelay(PLL_STABILITY_WAIT_US);
+ udelay(pll_hw->delay);
return 0;
}
diff --git a/drivers/clk/actions/owl-pll.h b/drivers/clk/actions/owl-pll.h
index 0aae30abd5dc..6fb0d45bb088 100644
--- a/drivers/clk/actions/owl-pll.h
+++ b/drivers/clk/actions/owl-pll.h
@@ -13,6 +13,8 @@
#include "owl-common.h"
+#define OWL_PLL_DEF_DELAY 50
+
/* last entry should have rate = 0 */
struct clk_pll_table {
unsigned int val;
@@ -27,6 +29,7 @@ struct owl_pll_hw {
u8 width;
u8 min_mul;
u8 max_mul;
+ u8 delay;
const struct clk_pll_table *table;
};
@@ -36,7 +39,7 @@ struct owl_pll {
};
#define OWL_PLL_HW(_reg, _bfreq, _bit_idx, _shift, \
- _width, _min_mul, _max_mul, _table) \
+ _width, _min_mul, _max_mul, _delay, _table) \
{ \
.reg = _reg, \
.bfreq = _bfreq, \
@@ -45,6 +48,7 @@ struct owl_pll {
.width = _width, \
.min_mul = _min_mul, \
.max_mul = _max_mul, \
+ .delay = _delay, \
.table = _table, \
}
@@ -52,8 +56,8 @@ struct owl_pll {
_shift, _width, _min_mul, _max_mul, _table, _flags) \
struct owl_pll _struct = { \
.pll_hw = OWL_PLL_HW(_reg, _bfreq, _bit_idx, _shift, \
- _width, _min_mul, \
- _max_mul, _table), \
+ _width, _min_mul, _max_mul, \
+ OWL_PLL_DEF_DELAY, _table), \
.common = { \
.regmap = NULL, \
.hw.init = CLK_HW_INIT(_name, \
@@ -67,8 +71,23 @@ struct owl_pll {
_shift, _width, _min_mul, _max_mul, _table, _flags) \
struct owl_pll _struct = { \
.pll_hw = OWL_PLL_HW(_reg, _bfreq, _bit_idx, _shift, \
- _width, _min_mul, \
- _max_mul, _table), \
+ _width, _min_mul, _max_mul, \
+ OWL_PLL_DEF_DELAY, _table), \
+ .common = { \
+ .regmap = NULL, \
+ .hw.init = CLK_HW_INIT_NO_PARENT(_name, \
+ &owl_pll_ops, \
+ _flags), \
+ }, \
+ }
+
+#define OWL_PLL_NO_PARENT_DELAY(_struct, _name, _reg, _bfreq, _bit_idx, \
+ _shift, _width, _min_mul, _max_mul, _delay, _table, \
+ _flags) \
+ struct owl_pll _struct = { \
+ .pll_hw = OWL_PLL_HW(_reg, _bfreq, _bit_idx, _shift, \
+ _width, _min_mul, _max_mul, \
+ _delay, _table), \
.common = { \
.regmap = NULL, \
.hw.init = CLK_HW_INIT_NO_PARENT(_name, \
@@ -78,7 +97,6 @@ struct owl_pll {
}
#define mul_mask(m) ((1 << ((m)->width)) - 1)
-#define PLL_STABILITY_WAIT_US (50)
static inline struct owl_pll *hw_to_owl_pll(const struct clk_hw *hw)
{
diff --git a/drivers/clk/actions/owl-s500.c b/drivers/clk/actions/owl-s500.c
new file mode 100644
index 000000000000..e2007ac4d235
--- /dev/null
+++ b/drivers/clk/actions/owl-s500.c
@@ -0,0 +1,525 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Actions Semi Owl S500 SoC clock driver
+ *
+ * Copyright (c) 2014 Actions Semi Inc.
+ * Author: David Liu <liuwei@actions-semi.com>
+ *
+ * Copyright (c) 2018 Linaro Ltd.
+ * Author: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+ *
+ * Copyright (c) 2018 LSI-TEC - Caninos Loucos
+ * Author: Edgar Bernardi Righi <edgar.righi@lsitec.org.br>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/platform_device.h>
+
+#include "owl-common.h"
+#include "owl-composite.h"
+#include "owl-divider.h"
+#include "owl-factor.h"
+#include "owl-fixed-factor.h"
+#include "owl-gate.h"
+#include "owl-mux.h"
+#include "owl-pll.h"
+
+#include <dt-bindings/clock/actions,s500-cmu.h>
+
+#define CMU_COREPLL (0x0000)
+#define CMU_DEVPLL (0x0004)
+#define CMU_DDRPLL (0x0008)
+#define CMU_NANDPLL (0x000C)
+#define CMU_DISPLAYPLL (0x0010)
+#define CMU_AUDIOPLL (0x0014)
+#define CMU_TVOUTPLL (0x0018)
+#define CMU_BUSCLK (0x001C)
+#define CMU_SENSORCLK (0x0020)
+#define CMU_LCDCLK (0x0024)
+#define CMU_DSICLK (0x0028)
+#define CMU_CSICLK (0x002C)
+#define CMU_DECLK (0x0030)
+#define CMU_BISPCLK (0x0034)
+#define CMU_BUSCLK1 (0x0038)
+#define CMU_VDECLK (0x0040)
+#define CMU_VCECLK (0x0044)
+#define CMU_NANDCCLK (0x004C)
+#define CMU_SD0CLK (0x0050)
+#define CMU_SD1CLK (0x0054)
+#define CMU_SD2CLK (0x0058)
+#define CMU_UART0CLK (0x005C)
+#define CMU_UART1CLK (0x0060)
+#define CMU_UART2CLK (0x0064)
+#define CMU_PWM4CLK (0x0068)
+#define CMU_PWM5CLK (0x006C)
+#define CMU_PWM0CLK (0x0070)
+#define CMU_PWM1CLK (0x0074)
+#define CMU_PWM2CLK (0x0078)
+#define CMU_PWM3CLK (0x007C)
+#define CMU_USBPLL (0x0080)
+#define CMU_ETHERNETPLL (0x0084)
+#define CMU_CVBSPLL (0x0088)
+#define CMU_LENSCLK (0x008C)
+#define CMU_GPU3DCLK (0x0090)
+#define CMU_CORECTL (0x009C)
+#define CMU_DEVCLKEN0 (0x00A0)
+#define CMU_DEVCLKEN1 (0x00A4)
+#define CMU_DEVRST0 (0x00A8)
+#define CMU_DEVRST1 (0x00AC)
+#define CMU_UART3CLK (0x00B0)
+#define CMU_UART4CLK (0x00B4)
+#define CMU_UART5CLK (0x00B8)
+#define CMU_UART6CLK (0x00BC)
+#define CMU_SSCLK (0x00C0)
+#define CMU_DIGITALDEBUG (0x00D0)
+#define CMU_ANALOGDEBUG (0x00D4)
+#define CMU_COREPLLDEBUG (0x00D8)
+#define CMU_DEVPLLDEBUG (0x00DC)
+#define CMU_DDRPLLDEBUG (0x00E0)
+#define CMU_NANDPLLDEBUG (0x00E4)
+#define CMU_DISPLAYPLLDEBUG (0x00E8)
+#define CMU_TVOUTPLLDEBUG (0x00EC)
+#define CMU_DEEPCOLORPLLDEBUG (0x00F4)
+#define CMU_AUDIOPLL_ETHPLLDEBUG (0x00F8)
+#define CMU_CVBSPLLDEBUG (0x00FC)
+
+#define OWL_S500_COREPLL_DELAY (150)
+#define OWL_S500_DDRPLL_DELAY (63)
+#define OWL_S500_DEVPLL_DELAY (28)
+#define OWL_S500_NANDPLL_DELAY (44)
+#define OWL_S500_DISPLAYPLL_DELAY (57)
+#define OWL_S500_ETHERNETPLL_DELAY (25)
+#define OWL_S500_AUDIOPLL_DELAY (100)
+
+static const struct clk_pll_table clk_audio_pll_table[] = {
+ { 0, 45158400 }, { 1, 49152000 },
+ { 0, 0 },
+};
+
+/* pll clocks */
+static OWL_PLL_NO_PARENT_DELAY(ethernet_pll_clk, "ethernet_pll_clk", CMU_ETHERNETPLL, 500000000, 0, 0, 0, 0, 0, OWL_S500_ETHERNETPLL_DELAY, NULL, CLK_IGNORE_UNUSED);
+static OWL_PLL_NO_PARENT_DELAY(core_pll_clk, "core_pll_clk", CMU_COREPLL, 12000000, 9, 0, 8, 4, 134, OWL_S500_COREPLL_DELAY, NULL, CLK_IGNORE_UNUSED);
+static OWL_PLL_NO_PARENT_DELAY(ddr_pll_clk, "ddr_pll_clk", CMU_DDRPLL, 12000000, 8, 0, 8, 1, 67, OWL_S500_DDRPLL_DELAY, NULL, CLK_IGNORE_UNUSED);
+static OWL_PLL_NO_PARENT_DELAY(nand_pll_clk, "nand_pll_clk", CMU_NANDPLL, 6000000, 8, 0, 7, 2, 86, OWL_S500_NANDPLL_DELAY, NULL, CLK_IGNORE_UNUSED);
+static OWL_PLL_NO_PARENT_DELAY(display_pll_clk, "display_pll_clk", CMU_DISPLAYPLL, 6000000, 8, 0, 8, 2, 126, OWL_S500_DISPLAYPLL_DELAY, NULL, CLK_IGNORE_UNUSED);
+static OWL_PLL_NO_PARENT_DELAY(dev_pll_clk, "dev_pll_clk", CMU_DEVPLL, 6000000, 8, 0, 7, 8, 126, OWL_S500_DEVPLL_DELAY, NULL, CLK_IGNORE_UNUSED);
+static OWL_PLL_NO_PARENT_DELAY(audio_pll_clk, "audio_pll_clk", CMU_AUDIOPLL, 0, 4, 0, 1, 0, 0, OWL_S500_AUDIOPLL_DELAY, clk_audio_pll_table, CLK_IGNORE_UNUSED);
+
+static const char * const dev_clk_mux_p[] = { "hosc", "dev_pll_clk" };
+static const char * const bisp_clk_mux_p[] = { "display_pll_clk", "dev_clk" };
+static const char * const sensor_clk_mux_p[] = { "hosc", "bisp_clk" };
+static const char * const sd_clk_mux_p[] = { "dev_clk", "nand_pll_clk" };
+static const char * const pwm_clk_mux_p[] = { "losc", "hosc" };
+static const char * const ahbprediv_clk_mux_p[] = { "dev_clk", "display_pll_clk", "nand_pll_clk", "ddr_pll_clk" };
+static const char * const uart_clk_mux_p[] = { "hosc", "dev_pll_clk" };
+static const char * const de_clk_mux_p[] = { "display_pll_clk", "dev_clk" };
+static const char * const i2s_clk_mux_p[] = { "audio_pll_clk" };
+static const char * const hde_clk_mux_p[] = { "dev_clk", "display_pll_clk", "nand_pll_clk", "ddr_pll_clk" };
+static const char * const nand_clk_mux_p[] = { "nand_pll_clk", "display_pll_clk", "dev_clk", "ddr_pll_clk" };
+
+static struct clk_factor_table sd_factor_table[] = {
+ /* bit0 ~ 4 */
+ { 0, 1, 1 }, { 1, 1, 2 }, { 2, 1, 3 }, { 3, 1, 4 },
+ { 4, 1, 5 }, { 5, 1, 6 }, { 6, 1, 7 }, { 7, 1, 8 },
+ { 8, 1, 9 }, { 9, 1, 10 }, { 10, 1, 11 }, { 11, 1, 12 },
+ { 12, 1, 13 }, { 13, 1, 14 }, { 14, 1, 15 }, { 15, 1, 16 },
+ { 16, 1, 17 }, { 17, 1, 18 }, { 18, 1, 19 }, { 19, 1, 20 },
+ { 20, 1, 21 }, { 21, 1, 22 }, { 22, 1, 23 }, { 23, 1, 24 },
+ { 24, 1, 25 }, { 25, 1, 26 }, { 26, 1, 27 }, { 27, 1, 28 },
+ { 28, 1, 29 }, { 29, 1, 30 }, { 30, 1, 31 }, { 31, 1, 32 },
+
+ /* bit8: /128 */
+ { 256, 1, 1 * 128 }, { 257, 1, 2 * 128 }, { 258, 1, 3 * 128 }, { 259, 1, 4 * 128 },
+ { 260, 1, 5 * 128 }, { 261, 1, 6 * 128 }, { 262, 1, 7 * 128 }, { 263, 1, 8 * 128 },
+ { 264, 1, 9 * 128 }, { 265, 1, 10 * 128 }, { 266, 1, 11 * 128 }, { 267, 1, 12 * 128 },
+ { 268, 1, 13 * 128 }, { 269, 1, 14 * 128 }, { 270, 1, 15 * 128 }, { 271, 1, 16 * 128 },
+ { 272, 1, 17 * 128 }, { 273, 1, 18 * 128 }, { 274, 1, 19 * 128 }, { 275, 1, 20 * 128 },
+ { 276, 1, 21 * 128 }, { 277, 1, 22 * 128 }, { 278, 1, 23 * 128 }, { 279, 1, 24 * 128 },
+ { 280, 1, 25 * 128 }, { 281, 1, 26 * 128 }, { 282, 1, 27 * 128 }, { 283, 1, 28 * 128 },
+ { 284, 1, 29 * 128 }, { 285, 1, 30 * 128 }, { 286, 1, 31 * 128 }, { 287, 1, 32 * 128 },
+ { 0, 0, 0 },
+};
+
+static struct clk_factor_table bisp_factor_table[] = {
+ { 0, 1, 1 }, { 1, 1, 2 }, { 2, 1, 3 }, { 3, 1, 4 },
+ { 4, 1, 5 }, { 5, 1, 6 }, { 6, 1, 7 }, { 7, 1, 8 },
+ { 0, 0, 0 },
+};
+
+static struct clk_factor_table ahb_factor_table[] = {
+ { 1, 1, 2 }, { 2, 1, 3 },
+ { 0, 0, 0 },
+};
+
+static struct clk_div_table rmii_ref_div_table[] = {
+ { 0, 4 }, { 1, 10 },
+ { 0, 0 },
+};
+
+static struct clk_div_table i2s_div_table[] = {
+ { 0, 1 }, { 1, 2 }, { 2, 3 }, { 3, 4 },
+ { 4, 6 }, { 5, 8 }, { 6, 12 }, { 7, 16 },
+ { 8, 24 },
+ { 0, 0 },
+};
+
+static struct clk_div_table nand_div_table[] = {
+ { 0, 1 }, { 1, 2 }, { 2, 4 }, { 3, 6 },
+ { 4, 8 }, { 5, 10 }, { 6, 12 }, { 7, 14 },
+ { 8, 16 }, { 9, 18 }, { 10, 20 }, { 11, 22 },
+ { 0, 0 },
+};
+
+/* mux clock */
+static OWL_MUX(dev_clk, "dev_clk", dev_clk_mux_p, CMU_DEVPLL, 12, 1, CLK_SET_RATE_PARENT);
+static OWL_MUX(ahbprediv_clk, "ahbprediv_clk", ahbprediv_clk_mux_p, CMU_BUSCLK1, 8, 3, CLK_SET_RATE_PARENT);
+
+/* gate clocks */
+static OWL_GATE(spi0_clk, "spi0_clk", "ahb_clk", CMU_DEVCLKEN1, 10, 0, CLK_IGNORE_UNUSED);
+static OWL_GATE(spi1_clk, "spi1_clk", "ahb_clk", CMU_DEVCLKEN1, 11, 0, CLK_IGNORE_UNUSED);
+static OWL_GATE(spi2_clk, "spi2_clk", "ahb_clk", CMU_DEVCLKEN1, 12, 0, CLK_IGNORE_UNUSED);
+static OWL_GATE(spi3_clk, "spi3_clk", "ahb_clk", CMU_DEVCLKEN1, 13, 0, CLK_IGNORE_UNUSED);
+static OWL_GATE(timer_clk, "timer_clk", "hosc", CMU_DEVCLKEN1, 27, 0, 0);
+static OWL_GATE(hdmi_clk, "hdmi_clk", "hosc", CMU_DEVCLKEN1, 3, 0, 0);
+
+/* divider clocks */
+static OWL_DIVIDER(h_clk, "h_clk", "ahbprevdiv_clk", CMU_BUSCLK1, 12, 2, NULL, 0, 0);
+static OWL_DIVIDER(rmii_ref_clk, "rmii_ref_clk", "ethernet_pll_clk", CMU_ETHERNETPLL, 1, 1, rmii_ref_div_table, 0, 0);
+
+/* factor clocks */
+static OWL_FACTOR(ahb_clk, "ahb_clk", "h_clk", CMU_BUSCLK1, 2, 2, ahb_factor_table, 0, 0);
+static OWL_FACTOR(de1_clk, "de_clk1", "de_clk", CMU_DECLK, 0, 3, bisp_factor_table, 0, 0);
+static OWL_FACTOR(de2_clk, "de_clk2", "de_clk", CMU_DECLK, 4, 3, bisp_factor_table, 0, 0);
+
+/* composite clocks */
+static OWL_COMP_FACTOR(vce_clk, "vce_clk", hde_clk_mux_p,
+ OWL_MUX_HW(CMU_VCECLK, 4, 2),
+ OWL_GATE_HW(CMU_DEVCLKEN0, 26, 0),
+ OWL_FACTOR_HW(CMU_VCECLK, 0, 3, 0, bisp_factor_table),
+ 0);
+
+static OWL_COMP_FACTOR(vde_clk, "vde_clk", hde_clk_mux_p,
+ OWL_MUX_HW(CMU_VDECLK, 4, 2),
+ OWL_GATE_HW(CMU_DEVCLKEN0, 25, 0),
+ OWL_FACTOR_HW(CMU_VDECLK, 0, 3, 0, bisp_factor_table),
+ 0);
+
+static OWL_COMP_FACTOR(bisp_clk, "bisp_clk", bisp_clk_mux_p,
+ OWL_MUX_HW(CMU_BISPCLK, 4, 1),
+ OWL_GATE_HW(CMU_DEVCLKEN0, 14, 0),
+ OWL_FACTOR_HW(CMU_BISPCLK, 0, 3, 0, bisp_factor_table),
+ 0);
+
+static OWL_COMP_FACTOR(sensor0_clk, "sensor0_clk", sensor_clk_mux_p,
+ OWL_MUX_HW(CMU_SENSORCLK, 4, 1),
+ OWL_GATE_HW(CMU_DEVCLKEN0, 14, 0),
+ OWL_FACTOR_HW(CMU_SENSORCLK, 0, 3, 0, bisp_factor_table),
+ CLK_IGNORE_UNUSED);
+
+static OWL_COMP_FACTOR(sensor1_clk, "sensor1_clk", sensor_clk_mux_p,
+ OWL_MUX_HW(CMU_SENSORCLK, 4, 1),
+ OWL_GATE_HW(CMU_DEVCLKEN0, 14, 0),
+ OWL_FACTOR_HW(CMU_SENSORCLK, 8, 3, 0, bisp_factor_table),
+ CLK_IGNORE_UNUSED);
+
+static OWL_COMP_FACTOR(sd0_clk, "sd0_clk", sd_clk_mux_p,
+ OWL_MUX_HW(CMU_SD0CLK, 9, 1),
+ OWL_GATE_HW(CMU_DEVCLKEN0, 5, 0),
+ OWL_FACTOR_HW(CMU_SD0CLK, 0, 9, 0, sd_factor_table),
+ 0);
+
+static OWL_COMP_FACTOR(sd1_clk, "sd1_clk", sd_clk_mux_p,
+ OWL_MUX_HW(CMU_SD1CLK, 9, 1),
+ OWL_GATE_HW(CMU_DEVCLKEN0, 6, 0),
+ OWL_FACTOR_HW(CMU_SD1CLK, 0, 9, 0, sd_factor_table),
+ 0);
+
+static OWL_COMP_FACTOR(sd2_clk, "sd2_clk", sd_clk_mux_p,
+ OWL_MUX_HW(CMU_SD2CLK, 9, 1),
+ OWL_GATE_HW(CMU_DEVCLKEN0, 7, 0),
+ OWL_FACTOR_HW(CMU_SD2CLK, 0, 9, 0, sd_factor_table),
+ 0);
+
+static OWL_COMP_DIV(pwm0_clk, "pwm0_clk", pwm_clk_mux_p,
+ OWL_MUX_HW(CMU_PWM0CLK, 12, 1),
+ OWL_GATE_HW(CMU_DEVCLKEN1, 23, 0),
+ OWL_DIVIDER_HW(CMU_PWM0CLK, 0, 10, 0, NULL),
+ 0);
+
+static OWL_COMP_DIV(pwm1_clk, "pwm1_clk", pwm_clk_mux_p,
+ OWL_MUX_HW(CMU_PWM1CLK, 12, 1),
+ OWL_GATE_HW(CMU_DEVCLKEN1, 24, 0),
+ OWL_DIVIDER_HW(CMU_PWM1CLK, 0, 10, 0, NULL),
+ 0);
+
+static OWL_COMP_DIV(pwm2_clk, "pwm2_clk", pwm_clk_mux_p,
+ OWL_MUX_HW(CMU_PWM2CLK, 12, 1),
+ OWL_GATE_HW(CMU_DEVCLKEN1, 25, 0),
+ OWL_DIVIDER_HW(CMU_PWM2CLK, 0, 10, 0, NULL),
+ 0);
+
+static OWL_COMP_DIV(pwm3_clk, "pwm3_clk", pwm_clk_mux_p,
+ OWL_MUX_HW(CMU_PWM3CLK, 12, 1),
+ OWL_GATE_HW(CMU_DEVCLKEN1, 26, 0),
+ OWL_DIVIDER_HW(CMU_PWM3CLK, 0, 10, 0, NULL),
+ 0);
+
+static OWL_COMP_DIV(pwm4_clk, "pwm4_clk", pwm_clk_mux_p,
+ OWL_MUX_HW(CMU_PWM4CLK, 12, 1),
+ OWL_GATE_HW(CMU_DEVCLKEN0, 11, 0),
+ OWL_DIVIDER_HW(CMU_PWM4CLK, 0, 10, 0, NULL),
+ 0);
+
+static OWL_COMP_DIV(pwm5_clk, "pwm5_clk", pwm_clk_mux_p,
+ OWL_MUX_HW(CMU_PWM5CLK, 12, 1),
+ OWL_GATE_HW(CMU_DEVCLKEN0, 0, 0),
+ OWL_DIVIDER_HW(CMU_PWM5CLK, 0, 10, 0, NULL),
+ 0);
+
+static OWL_COMP_PASS(de_clk, "de_clk", de_clk_mux_p,
+ OWL_MUX_HW(CMU_DECLK, 12, 1),
+ OWL_GATE_HW(CMU_DEVCLKEN0, 8, 0),
+ 0);
+
+static OWL_COMP_FIXED_FACTOR(i2c0_clk, "i2c0_clk", "ethernet_pll_clk",
+ OWL_GATE_HW(CMU_DEVCLKEN1, 14, 0),
+ 1, 5, 0);
+
+static OWL_COMP_FIXED_FACTOR(i2c1_clk, "i2c1_clk", "ethernet_pll_clk",
+ OWL_GATE_HW(CMU_DEVCLKEN1, 15, 0),
+ 1, 5, 0);
+
+static OWL_COMP_FIXED_FACTOR(i2c2_clk, "i2c2_clk", "ethernet_pll_clk",
+ OWL_GATE_HW(CMU_DEVCLKEN1, 30, 0),
+ 1, 5, 0);
+
+static OWL_COMP_FIXED_FACTOR(i2c3_clk, "i2c3_clk", "ethernet_pll_clk",
+ OWL_GATE_HW(CMU_DEVCLKEN1, 31, 0),
+ 1, 5, 0);
+
+static OWL_COMP_DIV(uart0_clk, "uart0_clk", uart_clk_mux_p,
+ OWL_MUX_HW(CMU_UART0CLK, 16, 1),
+ OWL_GATE_HW(CMU_DEVCLKEN1, 6, 0),
+ OWL_DIVIDER_HW(CMU_UART1CLK, 0, 8, CLK_DIVIDER_ROUND_CLOSEST, NULL),
+ CLK_IGNORE_UNUSED);
+
+static OWL_COMP_DIV(uart1_clk, "uart1_clk", uart_clk_mux_p,
+ OWL_MUX_HW(CMU_UART1CLK, 16, 1),
+ OWL_GATE_HW(CMU_DEVCLKEN1, 7, 0),
+ OWL_DIVIDER_HW(CMU_UART1CLK, 0, 8, CLK_DIVIDER_ROUND_CLOSEST, NULL),
+ CLK_IGNORE_UNUSED);
+
+static OWL_COMP_DIV(uart2_clk, "uart2_clk", uart_clk_mux_p,
+ OWL_MUX_HW(CMU_UART2CLK, 16, 1),
+ OWL_GATE_HW(CMU_DEVCLKEN1, 8, 0),
+ OWL_DIVIDER_HW(CMU_UART1CLK, 0, 8, CLK_DIVIDER_ROUND_CLOSEST, NULL),
+ CLK_IGNORE_UNUSED);
+
+static OWL_COMP_DIV(uart3_clk, "uart3_clk", uart_clk_mux_p,
+ OWL_MUX_HW(CMU_UART3CLK, 16, 1),
+ OWL_GATE_HW(CMU_DEVCLKEN1, 19, 0),
+ OWL_DIVIDER_HW(CMU_UART1CLK, 0, 8, CLK_DIVIDER_ROUND_CLOSEST, NULL),
+ CLK_IGNORE_UNUSED);
+
+static OWL_COMP_DIV(uart4_clk, "uart4_clk", uart_clk_mux_p,
+ OWL_MUX_HW(CMU_UART4CLK, 16, 1),
+ OWL_GATE_HW(CMU_DEVCLKEN1, 20, 0),
+ OWL_DIVIDER_HW(CMU_UART1CLK, 0, 8, CLK_DIVIDER_ROUND_CLOSEST, NULL),
+ CLK_IGNORE_UNUSED);
+
+static OWL_COMP_DIV(uart5_clk, "uart5_clk", uart_clk_mux_p,
+ OWL_MUX_HW(CMU_UART5CLK, 16, 1),
+ OWL_GATE_HW(CMU_DEVCLKEN1, 21, 0),
+ OWL_DIVIDER_HW(CMU_UART1CLK, 0, 8, CLK_DIVIDER_ROUND_CLOSEST, NULL),
+ CLK_IGNORE_UNUSED);
+
+static OWL_COMP_DIV(uart6_clk, "uart6_clk", uart_clk_mux_p,
+ OWL_MUX_HW(CMU_UART6CLK, 16, 1),
+ OWL_GATE_HW(CMU_DEVCLKEN1, 18, 0),
+ OWL_DIVIDER_HW(CMU_UART1CLK, 0, 8, CLK_DIVIDER_ROUND_CLOSEST, NULL),
+ CLK_IGNORE_UNUSED);
+
+static OWL_COMP_DIV(i2srx_clk, "i2srx_clk", i2s_clk_mux_p,
+ OWL_MUX_HW(CMU_AUDIOPLL, 24, 1),
+ OWL_GATE_HW(CMU_DEVCLKEN0, 21, 0),
+ OWL_DIVIDER_HW(CMU_AUDIOPLL, 20, 4, 0, i2s_div_table),
+ 0);
+
+static OWL_COMP_DIV(i2stx_clk, "i2stx_clk", i2s_clk_mux_p,
+ OWL_MUX_HW(CMU_AUDIOPLL, 24, 1),
+ OWL_GATE_HW(CMU_DEVCLKEN0, 20, 0),
+ OWL_DIVIDER_HW(CMU_AUDIOPLL, 16, 4, 0, i2s_div_table),
+ 0);
+
+static OWL_COMP_DIV(hdmia_clk, "hdmia_clk", i2s_clk_mux_p,
+ OWL_MUX_HW(CMU_AUDIOPLL, 24, 1),
+ OWL_GATE_HW(CMU_DEVCLKEN0, 22, 0),
+ OWL_DIVIDER_HW(CMU_AUDIOPLL, 24, 4, 0, i2s_div_table),
+ 0);
+
+static OWL_COMP_DIV(spdif_clk, "spdif_clk", i2s_clk_mux_p,
+ OWL_MUX_HW(CMU_AUDIOPLL, 24, 1),
+ OWL_GATE_HW(CMU_DEVCLKEN0, 23, 0),
+ OWL_DIVIDER_HW(CMU_AUDIOPLL, 28, 4, 0, i2s_div_table),
+ 0);
+
+static OWL_COMP_DIV(nand_clk, "nand_clk", nand_clk_mux_p,
+ OWL_MUX_HW(CMU_NANDCCLK, 8, 2),
+ OWL_GATE_HW(CMU_DEVCLKEN0, 4, 0),
+ OWL_DIVIDER_HW(CMU_NANDCCLK, 0, 3, 0, nand_div_table),
+ CLK_SET_RATE_PARENT);
+
+static OWL_COMP_DIV(ecc_clk, "ecc_clk", nand_clk_mux_p,
+ OWL_MUX_HW(CMU_NANDCCLK, 8, 2),
+ OWL_GATE_HW(CMU_DEVCLKEN0, 4, 0),
+ OWL_DIVIDER_HW(CMU_NANDCCLK, 4, 3, 0, nand_div_table),
+ CLK_SET_RATE_PARENT);
+
+static struct owl_clk_common *s500_clks[] = {
+ &ethernet_pll_clk.common,
+ &core_pll_clk.common,
+ &ddr_pll_clk.common,
+ &dev_pll_clk.common,
+ &nand_pll_clk.common,
+ &audio_pll_clk.common,
+ &display_pll_clk.common,
+ &dev_clk.common,
+ &timer_clk.common,
+ &i2c0_clk.common,
+ &i2c1_clk.common,
+ &i2c2_clk.common,
+ &i2c3_clk.common,
+ &uart0_clk.common,
+ &uart1_clk.common,
+ &uart2_clk.common,
+ &uart3_clk.common,
+ &uart4_clk.common,
+ &uart5_clk.common,
+ &uart6_clk.common,
+ &pwm0_clk.common,
+ &pwm1_clk.common,
+ &pwm2_clk.common,
+ &pwm3_clk.common,
+ &pwm4_clk.common,
+ &pwm5_clk.common,
+ &sensor0_clk.common,
+ &sensor1_clk.common,
+ &sd0_clk.common,
+ &sd1_clk.common,
+ &sd2_clk.common,
+ &bisp_clk.common,
+ &ahb_clk.common,
+ &ahbprediv_clk.common,
+ &h_clk.common,
+ &spi0_clk.common,
+ &spi1_clk.common,
+ &spi2_clk.common,
+ &spi3_clk.common,
+ &rmii_ref_clk.common,
+ &de_clk.common,
+ &de1_clk.common,
+ &de2_clk.common,
+ &i2srx_clk.common,
+ &i2stx_clk.common,
+ &hdmia_clk.common,
+ &hdmi_clk.common,
+ &vce_clk.common,
+ &vde_clk.common,
+ &spdif_clk.common,
+ &nand_clk.common,
+ &ecc_clk.common,
+};
+
+static struct clk_hw_onecell_data s500_hw_clks = {
+ .hws = {
+ [CLK_ETHERNET_PLL] = &ethernet_pll_clk.common.hw,
+ [CLK_CORE_PLL] = &core_pll_clk.common.hw,
+ [CLK_DDR_PLL] = &ddr_pll_clk.common.hw,
+ [CLK_NAND_PLL] = &nand_pll_clk.common.hw,
+ [CLK_DISPLAY_PLL] = &display_pll_clk.common.hw,
+ [CLK_DEV_PLL] = &dev_pll_clk.common.hw,
+ [CLK_AUDIO_PLL] = &audio_pll_clk.common.hw,
+ [CLK_TIMER] = &timer_clk.common.hw,
+ [CLK_DEV] = &dev_clk.common.hw,
+ [CLK_DE] = &de_clk.common.hw,
+ [CLK_DE1] = &de1_clk.common.hw,
+ [CLK_DE2] = &de2_clk.common.hw,
+ [CLK_I2C0] = &i2c0_clk.common.hw,
+ [CLK_I2C1] = &i2c1_clk.common.hw,
+ [CLK_I2C2] = &i2c2_clk.common.hw,
+ [CLK_I2C3] = &i2c3_clk.common.hw,
+ [CLK_I2SRX] = &i2srx_clk.common.hw,
+ [CLK_I2STX] = &i2stx_clk.common.hw,
+ [CLK_UART0] = &uart0_clk.common.hw,
+ [CLK_UART1] = &uart1_clk.common.hw,
+ [CLK_UART2] = &uart2_clk.common.hw,
+ [CLK_UART3] = &uart3_clk.common.hw,
+ [CLK_UART4] = &uart4_clk.common.hw,
+ [CLK_UART5] = &uart5_clk.common.hw,
+ [CLK_UART6] = &uart6_clk.common.hw,
+ [CLK_PWM0] = &pwm0_clk.common.hw,
+ [CLK_PWM1] = &pwm1_clk.common.hw,
+ [CLK_PWM2] = &pwm2_clk.common.hw,
+ [CLK_PWM3] = &pwm3_clk.common.hw,
+ [CLK_PWM4] = &pwm4_clk.common.hw,
+ [CLK_PWM5] = &pwm5_clk.common.hw,
+ [CLK_SENSOR0] = &sensor0_clk.common.hw,
+ [CLK_SENSOR1] = &sensor1_clk.common.hw,
+ [CLK_SD0] = &sd0_clk.common.hw,
+ [CLK_SD1] = &sd1_clk.common.hw,
+ [CLK_SD2] = &sd2_clk.common.hw,
+ [CLK_BISP] = &bisp_clk.common.hw,
+ [CLK_SPI0] = &spi0_clk.common.hw,
+ [CLK_SPI1] = &spi1_clk.common.hw,
+ [CLK_SPI2] = &spi2_clk.common.hw,
+ [CLK_SPI3] = &spi3_clk.common.hw,
+ [CLK_AHB] = &ahb_clk.common.hw,
+ [CLK_H] = &h_clk.common.hw,
+ [CLK_AHBPREDIV] = &ahbprediv_clk.common.hw,
+ [CLK_RMII_REF] = &rmii_ref_clk.common.hw,
+ [CLK_HDMI_AUDIO] = &hdmia_clk.common.hw,
+ [CLK_HDMI] = &hdmi_clk.common.hw,
+ [CLK_VDE] = &vde_clk.common.hw,
+ [CLK_VCE] = &vce_clk.common.hw,
+ [CLK_SPDIF] = &spdif_clk.common.hw,
+ [CLK_NAND] = &nand_clk.common.hw,
+ [CLK_ECC] = &ecc_clk.common.hw,
+ },
+ .num = CLK_NR_CLKS,
+};
+
+static struct owl_clk_desc s500_clk_desc = {
+ .clks = s500_clks,
+ .num_clks = ARRAY_SIZE(s500_clks),
+
+ .hw_clks = &s500_hw_clks,
+};
+
+static int s500_clk_probe(struct platform_device *pdev)
+{
+ struct owl_clk_desc *desc;
+
+ desc = &s500_clk_desc;
+ owl_clk_regmap_init(pdev, desc);
+
+ return owl_clk_probe(&pdev->dev, desc->hw_clks);
+}
+
+static const struct of_device_id s500_clk_of_match[] = {
+ { .compatible = "actions,s500-cmu", },
+ { /* sentinel */ }
+};
+
+static struct platform_driver s500_clk_driver = {
+ .probe = s500_clk_probe,
+ .driver = {
+ .name = "s500-cmu",
+ .of_match_table = s500_clk_of_match,
+ },
+};
+
+static int __init s500_clk_init(void)
+{
+ return platform_driver_register(&s500_clk_driver);
+}
+core_initcall(s500_clk_init);
diff --git a/drivers/clk/at91/clk-audio-pll.c b/drivers/clk/at91/clk-audio-pll.c
index 36d77146a3bd..3cc4a82f4e9f 100644
--- a/drivers/clk/at91/clk-audio-pll.c
+++ b/drivers/clk/at91/clk-audio-pll.c
@@ -340,7 +340,12 @@ static long clk_audio_pll_pmc_round_rate(struct clk_hw *hw, unsigned long rate,
pr_debug("A PLL/PMC: %s, rate = %lu (parent_rate = %lu)\n", __func__,
rate, *parent_rate);
- for (div = 1; div <= AUDIO_PLL_QDPMC_MAX; div++) {
+ if (!rate)
+ return 0;
+
+ best_parent_rate = clk_round_rate(pclk->clk, 1);
+ div = max(best_parent_rate / rate, 1UL);
+ for (; div <= AUDIO_PLL_QDPMC_MAX; div++) {
best_parent_rate = clk_round_rate(pclk->clk, rate * div);
tmp_rate = best_parent_rate / div;
tmp_diff = abs(rate - tmp_rate);
@@ -350,6 +355,8 @@ static long clk_audio_pll_pmc_round_rate(struct clk_hw *hw, unsigned long rate,
best_rate = tmp_rate;
best_diff = tmp_diff;
tmp_qd = div;
+ if (!best_diff)
+ break; /* got exact match */
}
}
diff --git a/drivers/clk/at91/clk-programmable.c b/drivers/clk/at91/clk-programmable.c
index 5bc68b9c5498..89d6f3736dbf 100644
--- a/drivers/clk/at91/clk-programmable.c
+++ b/drivers/clk/at91/clk-programmable.c
@@ -132,11 +132,8 @@ static int clk_programmable_set_rate(struct clk_hw *hw, unsigned long rate,
struct clk_programmable *prog = to_clk_programmable(hw);
const struct clk_programmable_layout *layout = prog->layout;
unsigned long div = parent_rate / rate;
- unsigned int pckr;
int shift = 0;
- regmap_read(prog->regmap, AT91_PMC_PCKR(prog->id), &pckr);
-
if (!div)
return -EINVAL;
diff --git a/drivers/clk/at91/sama5d2.c b/drivers/clk/at91/sama5d2.c
index cd0ef7274fdb..1f70cb164b06 100644
--- a/drivers/clk/at91/sama5d2.c
+++ b/drivers/clk/at91/sama5d2.c
@@ -241,13 +241,14 @@ static void __init sama5d2_pmc_setup(struct device_node *np)
parent_names[2] = "plladivck";
parent_names[3] = "utmick";
parent_names[4] = "masterck";
+ parent_names[5] = "audiopll_pmcck";
for (i = 0; i < 3; i++) {
char name[6];
snprintf(name, sizeof(name), "prog%d", i);
hw = at91_clk_register_programmable(regmap, name,
- parent_names, 5, i,
+ parent_names, 6, i,
&at91sam9x5_programmable_layout);
if (IS_ERR(hw))
goto err_free;
diff --git a/drivers/clk/clk-clps711x.c b/drivers/clk/clk-clps711x.c
index 2c04396402ab..c36c47bdba02 100644
--- a/drivers/clk/clk-clps711x.c
+++ b/drivers/clk/clk-clps711x.c
@@ -44,21 +44,21 @@ struct clps711x_clk {
struct clk_hw_onecell_data clk_data;
};
-static struct clps711x_clk * __init _clps711x_clk_init(void __iomem *base,
- u32 fref)
+static void __init clps711x_clk_init_dt(struct device_node *np)
{
- u32 tmp, f_cpu, f_pll, f_bus, f_tim, f_pwm, f_spi;
+ u32 tmp, f_cpu, f_pll, f_bus, f_tim, f_pwm, f_spi, fref = 0;
struct clps711x_clk *clps711x_clk;
- unsigned i;
+ void __iomem *base;
+
+ WARN_ON(of_property_read_u32(np, "startup-frequency", &fref));
- if (!base)
- return ERR_PTR(-ENOMEM);
+ base = of_iomap(np, 0);
+ BUG_ON(!base);
clps711x_clk = kzalloc(struct_size(clps711x_clk, clk_data.hws,
CLPS711X_CLK_MAX),
GFP_KERNEL);
- if (!clps711x_clk)
- return ERR_PTR(-ENOMEM);
+ BUG_ON(!clps711x_clk);
spin_lock_init(&clps711x_clk->lock);
@@ -137,52 +137,13 @@ static struct clps711x_clk * __init _clps711x_clk_init(void __iomem *base,
clk_hw_register_fixed_factor(NULL, "uart", "bus", 0, 1, 10);
clps711x_clk->clk_data.hws[CLPS711X_CLK_TICK] =
clk_hw_register_fixed_rate(NULL, "tick", NULL, 0, 64);
- for (i = 0; i < CLPS711X_CLK_MAX; i++)
- if (IS_ERR(clps711x_clk->clk_data.hws[i]))
+ for (tmp = 0; tmp < CLPS711X_CLK_MAX; tmp++)
+ if (IS_ERR(clps711x_clk->clk_data.hws[tmp]))
pr_err("clk %i: register failed with %ld\n",
- i, PTR_ERR(clps711x_clk->clk_data.hws[i]));
-
- return clps711x_clk;
-}
-
-void __init clps711x_clk_init(void __iomem *base)
-{
- struct clps711x_clk *clps711x_clk;
-
- clps711x_clk = _clps711x_clk_init(base, 73728000);
-
- BUG_ON(IS_ERR(clps711x_clk));
-
- /* Clocksource */
- clk_hw_register_clkdev(clps711x_clk->clk_data.hws[CLPS711X_CLK_TIMER1],
- NULL, "clps711x-timer.0");
- clk_hw_register_clkdev(clps711x_clk->clk_data.hws[CLPS711X_CLK_TIMER2],
- NULL, "clps711x-timer.1");
-
- /* Drivers */
- clk_hw_register_clkdev(clps711x_clk->clk_data.hws[CLPS711X_CLK_PWM],
- NULL, "clps711x-pwm");
- clk_hw_register_clkdev(clps711x_clk->clk_data.hws[CLPS711X_CLK_UART],
- NULL, "clps711x-uart.0");
- clk_hw_register_clkdev(clps711x_clk->clk_data.hws[CLPS711X_CLK_UART],
- NULL, "clps711x-uart.1");
-}
-
-#ifdef CONFIG_OF
-static void __init clps711x_clk_init_dt(struct device_node *np)
-{
- void __iomem *base = of_iomap(np, 0);
- struct clps711x_clk *clps711x_clk;
- u32 fref = 0;
-
- WARN_ON(of_property_read_u32(np, "startup-frequency", &fref));
-
- clps711x_clk = _clps711x_clk_init(base, fref);
- BUG_ON(IS_ERR(clps711x_clk));
+ tmp, PTR_ERR(clps711x_clk->clk_data.hws[tmp]));
clps711x_clk->clk_data.num = CLPS711X_CLK_MAX;
of_clk_add_hw_provider(np, of_clk_hw_onecell_get,
&clps711x_clk->clk_data);
}
CLK_OF_DECLARE(clps711x, "cirrus,ep7209-clk", clps711x_clk_init_dt);
-#endif
diff --git a/drivers/clk/clk-devres.c b/drivers/clk/clk-devres.c
index c9a86156ced8..daa1fc8fba53 100644
--- a/drivers/clk/clk-devres.c
+++ b/drivers/clk/clk-devres.c
@@ -29,6 +29,17 @@ struct clk *devm_clk_get(struct device *dev, const char *id)
}
EXPORT_SYMBOL(devm_clk_get);
+struct clk *devm_clk_get_optional(struct device *dev, const char *id)
+{
+ struct clk *clk = devm_clk_get(dev, id);
+
+ if (clk == ERR_PTR(-ENOENT))
+ return NULL;
+
+ return clk;
+}
+EXPORT_SYMBOL(devm_clk_get_optional);
+
struct clk_bulk_devres {
struct clk_bulk_data *clks;
int num_clks;
diff --git a/drivers/clk/clk-fixed-mmio.c b/drivers/clk/clk-fixed-mmio.c
new file mode 100644
index 000000000000..d1a97d971183
--- /dev/null
+++ b/drivers/clk/clk-fixed-mmio.c
@@ -0,0 +1,101 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Memory Mapped IO Fixed clock driver
+ *
+ * Copyright (C) 2018 Cadence Design Systems, Inc.
+ *
+ * Authors:
+ * Jan Kotas <jank@cadence.com>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/of_address.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+static struct clk_hw *fixed_mmio_clk_setup(struct device_node *node)
+{
+ struct clk_hw *clk;
+ const char *clk_name = node->name;
+ void __iomem *base;
+ u32 freq;
+ int ret;
+
+ base = of_iomap(node, 0);
+ if (!base) {
+ pr_err("%pOFn: failed to map address\n", node);
+ return ERR_PTR(-EIO);
+ }
+
+ freq = readl(base);
+ iounmap(base);
+ of_property_read_string(node, "clock-output-names", &clk_name);
+
+ clk = clk_hw_register_fixed_rate(NULL, clk_name, NULL, 0, freq);
+ if (IS_ERR(clk)) {
+ pr_err("%pOFn: failed to register fixed rate clock\n", node);
+ return clk;
+ }
+
+ ret = of_clk_add_hw_provider(node, of_clk_hw_simple_get, clk);
+ if (ret) {
+ pr_err("%pOFn: failed to add clock provider\n", node);
+ clk_hw_unregister(clk);
+ clk = ERR_PTR(ret);
+ }
+
+ return clk;
+}
+
+static void __init of_fixed_mmio_clk_setup(struct device_node *node)
+{
+ fixed_mmio_clk_setup(node);
+}
+CLK_OF_DECLARE(fixed_mmio_clk, "fixed-mmio-clock", of_fixed_mmio_clk_setup);
+
+/**
+ * This is not executed when of_fixed_mmio_clk_setup succeeded.
+ */
+static int of_fixed_mmio_clk_probe(struct platform_device *pdev)
+{
+ struct clk_hw *clk;
+
+ clk = fixed_mmio_clk_setup(pdev->dev.of_node);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ platform_set_drvdata(pdev, clk);
+
+ return 0;
+}
+
+static int of_fixed_mmio_clk_remove(struct platform_device *pdev)
+{
+ struct clk_hw *clk = platform_get_drvdata(pdev);
+
+ of_clk_del_provider(pdev->dev.of_node);
+ clk_hw_unregister_fixed_rate(clk);
+
+ return 0;
+}
+
+static const struct of_device_id of_fixed_mmio_clk_ids[] = {
+ { .compatible = "fixed-mmio-clock" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, of_fixed_mmio_clk_ids);
+
+static struct platform_driver of_fixed_mmio_clk_driver = {
+ .driver = {
+ .name = "of_fixed_mmio_clk",
+ .of_match_table = of_fixed_mmio_clk_ids,
+ },
+ .probe = of_fixed_mmio_clk_probe,
+ .remove = of_fixed_mmio_clk_remove,
+};
+module_platform_driver(of_fixed_mmio_clk_driver);
+
+MODULE_AUTHOR("Jan Kotas <jank@cadence.com>");
+MODULE_DESCRIPTION("Memory Mapped IO Fixed clock driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/clk/clk-fractional-divider.c b/drivers/clk/clk-fractional-divider.c
index 545dceec0bbf..fdfe2e423d15 100644
--- a/drivers/clk/clk-fractional-divider.c
+++ b/drivers/clk/clk-fractional-divider.c
@@ -79,7 +79,7 @@ static long clk_fd_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long m, n;
u64 ret;
- if (!rate || rate >= *parent_rate)
+ if (!rate || (!clk_hw_can_set_rate_parent(hw) && rate >= *parent_rate))
return *parent_rate;
if (fd->approximation)
diff --git a/drivers/clk/clk-gpio.c b/drivers/clk/clk-gpio.c
index 25eed3e0251f..c2f07f0d077c 100644
--- a/drivers/clk/clk-gpio.c
+++ b/drivers/clk/clk-gpio.c
@@ -58,6 +58,35 @@ const struct clk_ops clk_gpio_gate_ops = {
};
EXPORT_SYMBOL_GPL(clk_gpio_gate_ops);
+static int clk_sleeping_gpio_gate_prepare(struct clk_hw *hw)
+{
+ struct clk_gpio *clk = to_clk_gpio(hw);
+
+ gpiod_set_value_cansleep(clk->gpiod, 1);
+
+ return 0;
+}
+
+static void clk_sleeping_gpio_gate_unprepare(struct clk_hw *hw)
+{
+ struct clk_gpio *clk = to_clk_gpio(hw);
+
+ gpiod_set_value_cansleep(clk->gpiod, 0);
+}
+
+static int clk_sleeping_gpio_gate_is_prepared(struct clk_hw *hw)
+{
+ struct clk_gpio *clk = to_clk_gpio(hw);
+
+ return gpiod_get_value_cansleep(clk->gpiod);
+}
+
+static const struct clk_ops clk_sleeping_gpio_gate_ops = {
+ .prepare = clk_sleeping_gpio_gate_prepare,
+ .unprepare = clk_sleeping_gpio_gate_unprepare,
+ .is_prepared = clk_sleeping_gpio_gate_is_prepared,
+};
+
/**
* DOC: basic clock multiplexer which can be controlled with a gpio output
* Traits of this clock:
@@ -144,10 +173,16 @@ struct clk_hw *clk_hw_register_gpio_gate(struct device *dev, const char *name,
const char *parent_name, struct gpio_desc *gpiod,
unsigned long flags)
{
+ const struct clk_ops *ops;
+
+ if (gpiod_cansleep(gpiod))
+ ops = &clk_sleeping_gpio_gate_ops;
+ else
+ ops = &clk_gpio_gate_ops;
+
return clk_register_gpio(dev, name,
(parent_name ? &parent_name : NULL),
- (parent_name ? 1 : 0), gpiod, flags,
- &clk_gpio_gate_ops);
+ (parent_name ? 1 : 0), gpiod, flags, ops);
}
EXPORT_SYMBOL_GPL(clk_hw_register_gpio_gate);
diff --git a/drivers/clk/clk-highbank.c b/drivers/clk/clk-highbank.c
index 727ed8e1bb72..8e4581004695 100644
--- a/drivers/clk/clk-highbank.c
+++ b/drivers/clk/clk-highbank.c
@@ -293,6 +293,7 @@ static __init struct clk *hb_clk_init(struct device_node *node, const struct clk
/* Map system registers */
srnp = of_find_compatible_node(NULL, NULL, "calxeda,hb-sregs");
hb_clk->reg = of_iomap(srnp, 0);
+ of_node_put(srnp);
BUG_ON(!hb_clk->reg);
hb_clk->reg += reg;
diff --git a/drivers/clk/clk-max77686.c b/drivers/clk/clk-max77686.c
index 22c937644c93..3727d5472450 100644
--- a/drivers/clk/clk-max77686.c
+++ b/drivers/clk/clk-max77686.c
@@ -235,8 +235,9 @@ static int max77686_clk_probe(struct platform_device *pdev)
return ret;
}
- ret = clk_hw_register_clkdev(&max_clk_data->hw,
- max_clk_data->clk_idata.name, NULL);
+ ret = devm_clk_hw_register_clkdev(dev, &max_clk_data->hw,
+ max_clk_data->clk_idata.name,
+ NULL);
if (ret < 0) {
dev_err(dev, "Failed to clkdev register: %d\n", ret);
return ret;
@@ -244,8 +245,8 @@ static int max77686_clk_probe(struct platform_device *pdev)
}
if (parent->of_node) {
- ret = of_clk_add_hw_provider(parent->of_node, of_clk_max77686_get,
- drv_data);
+ ret = devm_of_clk_add_hw_provider(dev, of_clk_max77686_get,
+ drv_data);
if (ret < 0) {
dev_err(dev, "Failed to register OF clock provider: %d\n",
@@ -261,27 +262,11 @@ static int max77686_clk_probe(struct platform_device *pdev)
1 << MAX77802_CLOCK_LOW_JITTER_SHIFT);
if (ret < 0) {
dev_err(dev, "Failed to config low-jitter: %d\n", ret);
- goto remove_of_clk_provider;
+ return ret;
}
}
return 0;
-
-remove_of_clk_provider:
- if (parent->of_node)
- of_clk_del_provider(parent->of_node);
-
- return ret;
-}
-
-static int max77686_clk_remove(struct platform_device *pdev)
-{
- struct device *parent = pdev->dev.parent;
-
- if (parent->of_node)
- of_clk_del_provider(parent->of_node);
-
- return 0;
}
static const struct platform_device_id max77686_clk_id[] = {
@@ -297,7 +282,6 @@ static struct platform_driver max77686_clk_driver = {
.name = "max77686-clk",
},
.probe = max77686_clk_probe,
- .remove = max77686_clk_remove,
.id_table = max77686_clk_id,
};
diff --git a/drivers/clk/clk-qoriq.c b/drivers/clk/clk-qoriq.c
index 5baa9e051110..1212a9be7e80 100644
--- a/drivers/clk/clk-qoriq.c
+++ b/drivers/clk/clk-qoriq.c
@@ -1148,8 +1148,8 @@ static void __init create_one_pll(struct clockgen *cg, int idx)
pll->div[i].clk = clk;
ret = clk_register_clkdev(clk, pll->div[i].name, NULL);
if (ret != 0)
- pr_err("%s: %s: register to lookup table failed %ld\n",
- __func__, pll->div[i].name, PTR_ERR(clk));
+ pr_err("%s: %s: register to lookup table failed %d\n",
+ __func__, pll->div[i].name, ret);
}
}
@@ -1389,6 +1389,7 @@ static void __init clockgen_init(struct device_node *np)
pr_err("%s: Couldn't map %pOF regs\n", __func__,
guts);
}
+ of_node_put(guts);
}
}
diff --git a/drivers/clk/clk-stm32mp1.c b/drivers/clk/clk-stm32mp1.c
index 6a31f7f434ce..a0ae8dc16909 100644
--- a/drivers/clk/clk-stm32mp1.c
+++ b/drivers/clk/clk-stm32mp1.c
@@ -121,7 +121,7 @@ static const char * const cpu_src[] = {
};
static const char * const axi_src[] = {
- "ck_hsi", "ck_hse", "pll2_p", "pll3_p"
+ "ck_hsi", "ck_hse", "pll2_p"
};
static const char * const per_src[] = {
@@ -225,19 +225,19 @@ static const char * const usart6_src[] = {
};
static const char * const fdcan_src[] = {
- "ck_hse", "pll3_q", "pll4_q"
+ "ck_hse", "pll3_q", "pll4_q", "pll4_r"
};
static const char * const sai_src[] = {
- "pll4_q", "pll3_q", "i2s_ckin", "ck_per"
+ "pll4_q", "pll3_q", "i2s_ckin", "ck_per", "pll3_r"
};
static const char * const sai2_src[] = {
- "pll4_q", "pll3_q", "i2s_ckin", "ck_per", "spdif_ck_symb"
+ "pll4_q", "pll3_q", "i2s_ckin", "ck_per", "spdif_ck_symb", "pll3_r"
};
static const char * const adc12_src[] = {
- "pll4_q", "ck_per"
+ "pll4_r", "ck_per", "pll3_q"
};
static const char * const dsi_src[] = {
@@ -269,7 +269,7 @@ static const struct clk_div_table axi_div_table[] = {
static const struct clk_div_table mcu_div_table[] = {
{ 0, 1 }, { 1, 2 }, { 2, 4 }, { 3, 8 },
{ 4, 16 }, { 5, 32 }, { 6, 64 }, { 7, 128 },
- { 8, 512 }, { 9, 512 }, { 10, 512}, { 11, 512 },
+ { 8, 256 }, { 9, 512 }, { 10, 512}, { 11, 512 },
{ 12, 512 }, { 13, 512 }, { 14, 512}, { 15, 512 },
{ 0 },
};
@@ -1286,10 +1286,11 @@ _clk_stm32_register_composite(struct device *dev,
MGATE_MP1(_id, _name, _parent, _flags, _mgate)
#define KCLK(_id, _name, _parents, _flags, _mgate, _mmux)\
- COMPOSITE(_id, _name, _parents, CLK_OPS_PARENT_ENABLE | _flags,\
- _MGATE_MP1(_mgate),\
- _MMUX(_mmux),\
- _NO_DIV)
+ COMPOSITE(_id, _name, _parents, CLK_OPS_PARENT_ENABLE |\
+ CLK_SET_RATE_NO_REPARENT | _flags,\
+ _MGATE_MP1(_mgate),\
+ _MMUX(_mmux),\
+ _NO_DIV)
enum {
G_SAI1,
@@ -1655,12 +1656,14 @@ static const struct stm32_mux_cfg ker_mux_cfg[M_LAST] = {
static const struct clock_config stm32mp1_clock_cfg[] = {
/* Oscillator divider */
- DIV(NO_ID, "clk-hsi-div", "clk-hsi", 0, RCC_HSICFGR, 0, 2,
- CLK_DIVIDER_READ_ONLY),
+ DIV(NO_ID, "clk-hsi-div", "clk-hsi", CLK_DIVIDER_POWER_OF_TWO,
+ RCC_HSICFGR, 0, 2, CLK_DIVIDER_READ_ONLY),
/* External / Internal Oscillators */
GATE_MP1(CK_HSE, "ck_hse", "clk-hse", 0, RCC_OCENSETR, 8, 0),
- GATE_MP1(CK_CSI, "ck_csi", "clk-csi", 0, RCC_OCENSETR, 4, 0),
+ /* ck_csi is used by IO compensation and should be critical */
+ GATE_MP1(CK_CSI, "ck_csi", "clk-csi", CLK_IS_CRITICAL,
+ RCC_OCENSETR, 4, 0),
GATE_MP1(CK_HSI, "ck_hsi", "clk-hsi-div", 0, RCC_OCENSETR, 0, 0),
GATE(CK_LSI, "ck_lsi", "clk-lsi", 0, RCC_RDLSICR, 0, 0),
GATE(CK_LSE, "ck_lse", "clk-lse", 0, RCC_BDCR, 0, 0),
@@ -1952,14 +1955,14 @@ static const struct clock_config stm32mp1_clock_cfg[] = {
MGATE_MP1(GPU_K, "gpu_k", "pll2_q", 0, G_GPU),
MGATE_MP1(DAC12_K, "dac12_k", "ck_lsi", 0, G_DAC12),
- COMPOSITE(ETHPTP_K, "ethptp_k", eth_src, CLK_OPS_PARENT_ENABLE,
+ COMPOSITE(ETHPTP_K, "ethptp_k", eth_src, CLK_OPS_PARENT_ENABLE |
+ CLK_SET_RATE_NO_REPARENT,
_NO_GATE,
_MMUX(M_ETHCK),
- _DIV(RCC_ETHCKSELR, 4, 4, CLK_DIVIDER_ALLOW_ZERO, NULL)),
+ _DIV(RCC_ETHCKSELR, 4, 4, 0, NULL)),
/* RTC clock */
- DIV(NO_ID, "ck_hse_rtc", "ck_hse", 0, RCC_RTCDIVR, 0, 7,
- CLK_DIVIDER_ALLOW_ZERO),
+ DIV(NO_ID, "ck_hse_rtc", "ck_hse", 0, RCC_RTCDIVR, 0, 6, 0),
COMPOSITE(RTC, "ck_rtc", rtc_src, CLK_OPS_PARENT_ENABLE |
CLK_SET_RATE_PARENT,
diff --git a/drivers/clk/clk-twl6040.c b/drivers/clk/clk-twl6040.c
index ea846f77750b..0cad5748bf0e 100644
--- a/drivers/clk/clk-twl6040.c
+++ b/drivers/clk/clk-twl6040.c
@@ -41,6 +41,43 @@ static int twl6040_pdmclk_is_prepared(struct clk_hw *hw)
return pdmclk->enabled;
}
+static int twl6040_pdmclk_reset_one_clock(struct twl6040_pdmclk *pdmclk,
+ unsigned int reg)
+{
+ const u8 reset_mask = TWL6040_HPLLRST; /* Same for HPPLL and LPPLL */
+ int ret;
+
+ ret = twl6040_set_bits(pdmclk->twl6040, reg, reset_mask);
+ if (ret < 0)
+ return ret;
+
+ ret = twl6040_clear_bits(pdmclk->twl6040, reg, reset_mask);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+/*
+ * TWL6040A2 Phoenix Audio IC erratum #6: "PDM Clock Generation Issue At
+ * Cold Temperature". This affects cold boot and deeper idle states it
+ * seems. The workaround consists of resetting HPPLL and LPPLL.
+ */
+static int twl6040_pdmclk_quirk_reset_clocks(struct twl6040_pdmclk *pdmclk)
+{
+ int ret;
+
+ ret = twl6040_pdmclk_reset_one_clock(pdmclk, TWL6040_REG_HPPLLCTL);
+ if (ret)
+ return ret;
+
+ ret = twl6040_pdmclk_reset_one_clock(pdmclk, TWL6040_REG_LPPLLCTL);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
static int twl6040_pdmclk_prepare(struct clk_hw *hw)
{
struct twl6040_pdmclk *pdmclk = container_of(hw, struct twl6040_pdmclk,
@@ -48,8 +85,20 @@ static int twl6040_pdmclk_prepare(struct clk_hw *hw)
int ret;
ret = twl6040_power(pdmclk->twl6040, 1);
- if (!ret)
- pdmclk->enabled = 1;
+ if (ret)
+ return ret;
+
+ ret = twl6040_pdmclk_quirk_reset_clocks(pdmclk);
+ if (ret)
+ goto out_err;
+
+ pdmclk->enabled = 1;
+
+ return 0;
+
+out_err:
+ dev_err(pdmclk->dev, "%s: error %i\n", __func__, ret);
+ twl6040_power(pdmclk->twl6040, 0);
return ret;
}
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index d2477a5058ac..96053a96fe2f 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -57,6 +57,7 @@ struct clk_core {
struct clk_core *new_child;
unsigned long flags;
bool orphan;
+ bool rpm_enabled;
unsigned int enable_count;
unsigned int prepare_count;
unsigned int protect_count;
@@ -81,6 +82,7 @@ struct clk_core {
struct clk {
struct clk_core *core;
+ struct device *dev;
const char *dev_id;
const char *con_id;
unsigned long min_rate;
@@ -92,9 +94,9 @@ struct clk {
/*** runtime pm ***/
static int clk_pm_runtime_get(struct clk_core *core)
{
- int ret = 0;
+ int ret;
- if (!core->dev)
+ if (!core->rpm_enabled)
return 0;
ret = pm_runtime_get_sync(core->dev);
@@ -103,7 +105,7 @@ static int clk_pm_runtime_get(struct clk_core *core)
static void clk_pm_runtime_put(struct clk_core *core)
{
- if (!core->dev)
+ if (!core->rpm_enabled)
return;
pm_runtime_put_sync(core->dev);
@@ -223,7 +225,7 @@ static bool clk_core_is_enabled(struct clk_core *core)
* taking enable spinlock, but the below check is needed if one tries
* to call it from other places.
*/
- if (core->dev) {
+ if (core->rpm_enabled) {
pm_runtime_get_noresume(core->dev);
if (!pm_runtime_active(core->dev)) {
ret = false;
@@ -233,7 +235,7 @@ static bool clk_core_is_enabled(struct clk_core *core)
ret = core->ops->is_enabled(core->hw);
done:
- if (core->dev)
+ if (core->rpm_enabled)
pm_runtime_put(core->dev);
return ret;
@@ -394,16 +396,19 @@ bool clk_hw_is_prepared(const struct clk_hw *hw)
{
return clk_core_is_prepared(hw->core);
}
+EXPORT_SYMBOL_GPL(clk_hw_is_prepared);
bool clk_hw_rate_is_protected(const struct clk_hw *hw)
{
return clk_core_rate_is_protected(hw->core);
}
+EXPORT_SYMBOL_GPL(clk_hw_rate_is_protected);
bool clk_hw_is_enabled(const struct clk_hw *hw)
{
return clk_core_is_enabled(hw->core);
}
+EXPORT_SYMBOL_GPL(clk_hw_is_enabled);
bool __clk_is_enabled(struct clk *clk)
{
@@ -3209,43 +3214,106 @@ unlock:
return ret;
}
-struct clk *__clk_create_clk(struct clk_hw *hw, const char *dev_id,
+/**
+ * clk_core_link_consumer - Add a clk consumer to the list of consumers in a clk_core
+ * @core: clk to add consumer to
+ * @clk: consumer to link to a clk
+ */
+static void clk_core_link_consumer(struct clk_core *core, struct clk *clk)
+{
+ clk_prepare_lock();
+ hlist_add_head(&clk->clks_node, &core->clks);
+ clk_prepare_unlock();
+}
+
+/**
+ * clk_core_unlink_consumer - Remove a clk consumer from the list of consumers in a clk_core
+ * @clk: consumer to unlink
+ */
+static void clk_core_unlink_consumer(struct clk *clk)
+{
+ lockdep_assert_held(&prepare_lock);
+ hlist_del(&clk->clks_node);
+}
+
+/**
+ * alloc_clk - Allocate a clk consumer, but leave it unlinked to the clk_core
+ * @core: clk to allocate a consumer for
+ * @dev_id: string describing device name
+ * @con_id: connection ID string on device
+ *
+ * Returns: clk consumer left unlinked from the consumer list
+ */
+static struct clk *alloc_clk(struct clk_core *core, const char *dev_id,
const char *con_id)
{
struct clk *clk;
- /* This is to allow this function to be chained to others */
- if (IS_ERR_OR_NULL(hw))
- return ERR_CAST(hw);
-
clk = kzalloc(sizeof(*clk), GFP_KERNEL);
if (!clk)
return ERR_PTR(-ENOMEM);
- clk->core = hw->core;
+ clk->core = core;
clk->dev_id = dev_id;
clk->con_id = kstrdup_const(con_id, GFP_KERNEL);
clk->max_rate = ULONG_MAX;
- clk_prepare_lock();
- hlist_add_head(&clk->clks_node, &hw->core->clks);
- clk_prepare_unlock();
-
return clk;
}
-/* keep in sync with __clk_put */
-void __clk_free_clk(struct clk *clk)
+/**
+ * free_clk - Free a clk consumer
+ * @clk: clk consumer to free
+ *
+ * Note, this assumes the clk has been unlinked from the clk_core consumer
+ * list.
+ */
+static void free_clk(struct clk *clk)
{
- clk_prepare_lock();
- hlist_del(&clk->clks_node);
- clk_prepare_unlock();
-
kfree_const(clk->con_id);
kfree(clk);
}
/**
+ * clk_hw_create_clk: Allocate and link a clk consumer to a clk_core given
+ * a clk_hw
+ * @dev: clk consumer device
+ * @hw: clk_hw associated with the clk being consumed
+ * @dev_id: string describing device name
+ * @con_id: connection ID string on device
+ *
+ * This is the main function used to create a clk pointer for use by clk
+ * consumers. It connects a consumer to the clk_core and clk_hw structures
+ * used by the framework and clk provider respectively.
+ */
+struct clk *clk_hw_create_clk(struct device *dev, struct clk_hw *hw,
+ const char *dev_id, const char *con_id)
+{
+ struct clk *clk;
+ struct clk_core *core;
+
+ /* This is to allow this function to be chained to others */
+ if (IS_ERR_OR_NULL(hw))
+ return ERR_CAST(hw);
+
+ core = hw->core;
+ clk = alloc_clk(core, dev_id, con_id);
+ if (IS_ERR(clk))
+ return clk;
+ clk->dev = dev;
+
+ if (!try_module_get(core->owner)) {
+ free_clk(clk);
+ return ERR_PTR(-ENOENT);
+ }
+
+ kref_get(&core->ref);
+ clk_core_link_consumer(core, clk);
+
+ return clk;
+}
+
+/**
* clk_register - allocate a new clock, register it and return an opaque cookie
* @dev: device that is registering this clock
* @hw: link to hardware-specific clock data
@@ -3280,7 +3348,8 @@ struct clk *clk_register(struct device *dev, struct clk_hw *hw)
core->ops = hw->init->ops;
if (dev && pm_runtime_enabled(dev))
- core->dev = dev;
+ core->rpm_enabled = true;
+ core->dev = dev;
if (dev && dev->driver)
core->owner = dev->driver->owner;
core->hw = hw;
@@ -3320,17 +3389,27 @@ struct clk *clk_register(struct device *dev, struct clk_hw *hw)
INIT_HLIST_HEAD(&core->clks);
- hw->clk = __clk_create_clk(hw, NULL, NULL);
+ /*
+ * Don't call clk_hw_create_clk() here because that would pin the
+ * provider module to itself and prevent it from ever being removed.
+ */
+ hw->clk = alloc_clk(core, NULL, NULL);
if (IS_ERR(hw->clk)) {
ret = PTR_ERR(hw->clk);
goto fail_parents;
}
+ clk_core_link_consumer(hw->core, hw->clk);
+
ret = __clk_core_init(core);
if (!ret)
return hw->clk;
- __clk_free_clk(hw->clk);
+ clk_prepare_lock();
+ clk_core_unlink_consumer(hw->clk);
+ clk_prepare_unlock();
+
+ free_clk(hw->clk);
hw->clk = NULL;
fail_parents:
@@ -3601,20 +3680,7 @@ EXPORT_SYMBOL_GPL(devm_clk_hw_unregister);
/*
* clkdev helpers
*/
-int __clk_get(struct clk *clk)
-{
- struct clk_core *core = !clk ? NULL : clk->core;
-
- if (core) {
- if (!try_module_get(core->owner))
- return 0;
- kref_get(&core->ref);
- }
- return 1;
-}
-
-/* keep in sync with __clk_free_clk */
void __clk_put(struct clk *clk)
{
struct module *owner;
@@ -3648,8 +3714,7 @@ void __clk_put(struct clk *clk)
module_put(owner);
- kfree_const(clk->con_id);
- kfree(clk);
+ free_clk(clk);
}
/*** clk rate change notifiers ***/
@@ -4006,6 +4071,49 @@ void devm_of_clk_del_provider(struct device *dev)
}
EXPORT_SYMBOL(devm_of_clk_del_provider);
+/*
+ * Beware the return values when np is valid, but no clock provider is found.
+ * If name == NULL, the function returns -ENOENT.
+ * If name != NULL, the function returns -EINVAL. This is because
+ * of_parse_phandle_with_args() is called even if of_property_match_string()
+ * returns an error.
+ */
+static int of_parse_clkspec(const struct device_node *np, int index,
+ const char *name, struct of_phandle_args *out_args)
+{
+ int ret = -ENOENT;
+
+ /* Walk up the tree of devices looking for a clock property that matches */
+ while (np) {
+ /*
+ * For named clocks, first look up the name in the
+ * "clock-names" property. If it cannot be found, then index
+ * will be an error code and of_parse_phandle_with_args() will
+ * return -EINVAL.
+ */
+ if (name)
+ index = of_property_match_string(np, "clock-names", name);
+ ret = of_parse_phandle_with_args(np, "clocks", "#clock-cells",
+ index, out_args);
+ if (!ret)
+ break;
+ if (name && index >= 0)
+ break;
+
+ /*
+ * No matching clock found on this node. If the parent node
+ * has a "clock-ranges" property, then we can try one of its
+ * clocks.
+ */
+ np = np->parent;
+ if (np && !of_get_property(np, "clock-ranges", NULL))
+ break;
+ index = 0;
+ }
+
+ return ret;
+}
+
static struct clk_hw *
__of_clk_get_hw_from_provider(struct of_clk_provider *provider,
struct of_phandle_args *clkspec)
@@ -4021,36 +4129,26 @@ __of_clk_get_hw_from_provider(struct of_clk_provider *provider,
return __clk_get_hw(clk);
}
-struct clk *__of_clk_get_from_provider(struct of_phandle_args *clkspec,
- const char *dev_id, const char *con_id)
+static struct clk_hw *
+of_clk_get_hw_from_clkspec(struct of_phandle_args *clkspec)
{
struct of_clk_provider *provider;
- struct clk *clk = ERR_PTR(-EPROBE_DEFER);
- struct clk_hw *hw;
+ struct clk_hw *hw = ERR_PTR(-EPROBE_DEFER);
if (!clkspec)
return ERR_PTR(-EINVAL);
- /* Check if we have such a provider in our array */
mutex_lock(&of_clk_mutex);
list_for_each_entry(provider, &of_clk_providers, link) {
if (provider->node == clkspec->np) {
hw = __of_clk_get_hw_from_provider(provider, clkspec);
- clk = __clk_create_clk(hw, dev_id, con_id);
- }
-
- if (!IS_ERR(clk)) {
- if (!__clk_get(clk)) {
- __clk_free_clk(clk);
- clk = ERR_PTR(-ENOENT);
- }
-
- break;
+ if (!IS_ERR(hw))
+ break;
}
}
mutex_unlock(&of_clk_mutex);
- return clk;
+ return hw;
}
/**
@@ -4063,10 +4161,62 @@ struct clk *__of_clk_get_from_provider(struct of_phandle_args *clkspec,
*/
struct clk *of_clk_get_from_provider(struct of_phandle_args *clkspec)
{
- return __of_clk_get_from_provider(clkspec, NULL, __func__);
+ struct clk_hw *hw = of_clk_get_hw_from_clkspec(clkspec);
+
+ return clk_hw_create_clk(NULL, hw, NULL, __func__);
}
EXPORT_SYMBOL_GPL(of_clk_get_from_provider);
+struct clk_hw *of_clk_get_hw(struct device_node *np, int index,
+ const char *con_id)
+{
+ int ret;
+ struct clk_hw *hw;
+ struct of_phandle_args clkspec;
+
+ ret = of_parse_clkspec(np, index, con_id, &clkspec);
+ if (ret)
+ return ERR_PTR(ret);
+
+ hw = of_clk_get_hw_from_clkspec(&clkspec);
+ of_node_put(clkspec.np);
+
+ return hw;
+}
+
+static struct clk *__of_clk_get(struct device_node *np,
+ int index, const char *dev_id,
+ const char *con_id)
+{
+ struct clk_hw *hw = of_clk_get_hw(np, index, con_id);
+
+ return clk_hw_create_clk(NULL, hw, dev_id, con_id);
+}
+
+struct clk *of_clk_get(struct device_node *np, int index)
+{
+ return __of_clk_get(np, index, np->full_name, NULL);
+}
+EXPORT_SYMBOL(of_clk_get);
+
+/**
+ * of_clk_get_by_name() - Parse and lookup a clock referenced by a device node
+ * @np: pointer to clock consumer node
+ * @name: name of consumer's clock input, or NULL for the first clock reference
+ *
+ * This function parses the clocks and clock-names properties,
+ * and uses them to look up the struct clk from the registered list of clock
+ * providers.
+ */
+struct clk *of_clk_get_by_name(struct device_node *np, const char *name)
+{
+ if (!np)
+ return ERR_PTR(-ENOENT);
+
+ return __of_clk_get(np, 0, np->full_name, name);
+}
+EXPORT_SYMBOL(of_clk_get_by_name);
+
/**
* of_clk_get_parent_count() - Count the number of clocks a device node has
* @np: device node to count
diff --git a/drivers/clk/clk.h b/drivers/clk/clk.h
index b02f5e604e69..553f531cc232 100644
--- a/drivers/clk/clk.h
+++ b/drivers/clk/clk.h
@@ -5,31 +5,36 @@
*/
struct clk_hw;
+struct device;
+struct of_phandle_args;
#if defined(CONFIG_OF) && defined(CONFIG_COMMON_CLK)
-struct clk *__of_clk_get_from_provider(struct of_phandle_args *clkspec,
- const char *dev_id, const char *con_id);
+struct clk_hw *of_clk_get_hw(struct device_node *np,
+ int index, const char *con_id);
+#else /* !CONFIG_COMMON_CLK || !CONFIG_OF */
+static inline struct clk_hw *of_clk_get_hw(struct device_node *np,
+ int index, const char *con_id)
+{
+ return ERR_PTR(-ENOENT);
+}
#endif
#ifdef CONFIG_COMMON_CLK
-struct clk *__clk_create_clk(struct clk_hw *hw, const char *dev_id,
- const char *con_id);
-void __clk_free_clk(struct clk *clk);
-int __clk_get(struct clk *clk);
+struct clk *clk_hw_create_clk(struct device *dev, struct clk_hw *hw,
+ const char *dev_id, const char *con_id);
void __clk_put(struct clk *clk);
#else
/* All these casts to avoid ifdefs in clkdev... */
static inline struct clk *
-__clk_create_clk(struct clk_hw *hw, const char *dev_id, const char *con_id)
+clk_hw_create_clk(struct device *dev, struct clk_hw *hw, const char *dev_id,
+ const char *con_id)
{
return (struct clk *)hw;
}
-static inline void __clk_free_clk(struct clk *clk) { }
static struct clk_hw *__clk_get_hw(struct clk *clk)
{
return (struct clk_hw *)clk;
}
-static inline int __clk_get(struct clk *clk) { return 1; }
static inline void __clk_put(struct clk *clk) { }
#endif
diff --git a/drivers/clk/clkdev.c b/drivers/clk/clkdev.c
index 9ab3db8b3988..8c4435c53f09 100644
--- a/drivers/clk/clkdev.c
+++ b/drivers/clk/clkdev.c
@@ -27,99 +27,6 @@
static LIST_HEAD(clocks);
static DEFINE_MUTEX(clocks_mutex);
-#if defined(CONFIG_OF) && defined(CONFIG_COMMON_CLK)
-static struct clk *__of_clk_get(struct device_node *np, int index,
- const char *dev_id, const char *con_id)
-{
- struct of_phandle_args clkspec;
- struct clk *clk;
- int rc;
-
- rc = of_parse_phandle_with_args(np, "clocks", "#clock-cells", index,
- &clkspec);
- if (rc)
- return ERR_PTR(rc);
-
- clk = __of_clk_get_from_provider(&clkspec, dev_id, con_id);
- of_node_put(clkspec.np);
-
- return clk;
-}
-
-struct clk *of_clk_get(struct device_node *np, int index)
-{
- return __of_clk_get(np, index, np->full_name, NULL);
-}
-EXPORT_SYMBOL(of_clk_get);
-
-static struct clk *__of_clk_get_by_name(struct device_node *np,
- const char *dev_id,
- const char *name)
-{
- struct clk *clk = ERR_PTR(-ENOENT);
-
- /* Walk up the tree of devices looking for a clock that matches */
- while (np) {
- int index = 0;
-
- /*
- * For named clocks, first look up the name in the
- * "clock-names" property. If it cannot be found, then
- * index will be an error code, and of_clk_get() will fail.
- */
- if (name)
- index = of_property_match_string(np, "clock-names", name);
- clk = __of_clk_get(np, index, dev_id, name);
- if (!IS_ERR(clk)) {
- break;
- } else if (name && index >= 0) {
- if (PTR_ERR(clk) != -EPROBE_DEFER)
- pr_err("ERROR: could not get clock %pOF:%s(%i)\n",
- np, name ? name : "", index);
- return clk;
- }
-
- /*
- * No matching clock found on this node. If the parent node
- * has a "clock-ranges" property, then we can try one of its
- * clocks.
- */
- np = np->parent;
- if (np && !of_get_property(np, "clock-ranges", NULL))
- break;
- }
-
- return clk;
-}
-
-/**
- * of_clk_get_by_name() - Parse and lookup a clock referenced by a device node
- * @np: pointer to clock consumer node
- * @name: name of consumer's clock input, or NULL for the first clock reference
- *
- * This function parses the clocks and clock-names properties,
- * and uses them to look up the struct clk from the registered list of clock
- * providers.
- */
-struct clk *of_clk_get_by_name(struct device_node *np, const char *name)
-{
- if (!np)
- return ERR_PTR(-ENOENT);
-
- return __of_clk_get_by_name(np, np->full_name, name);
-}
-EXPORT_SYMBOL(of_clk_get_by_name);
-
-#else /* defined(CONFIG_OF) && defined(CONFIG_COMMON_CLK) */
-
-static struct clk *__of_clk_get_by_name(struct device_node *np,
- const char *dev_id,
- const char *name)
-{
- return ERR_PTR(-ENOENT);
-}
-#endif
-
/*
* Find the correct struct clk for the device and connection ID.
* We do slightly fuzzy matching here:
@@ -163,7 +70,8 @@ static struct clk_lookup *clk_find(const char *dev_id, const char *con_id)
return cl;
}
-struct clk *clk_get_sys(const char *dev_id, const char *con_id)
+static struct clk *__clk_get_sys(struct device *dev, const char *dev_id,
+ const char *con_id)
{
struct clk_lookup *cl;
struct clk *clk = NULL;
@@ -174,35 +82,33 @@ struct clk *clk_get_sys(const char *dev_id, const char *con_id)
if (!cl)
goto out;
- clk = __clk_create_clk(cl->clk_hw, dev_id, con_id);
+ clk = clk_hw_create_clk(dev, cl->clk_hw, dev_id, con_id);
if (IS_ERR(clk))
- goto out;
-
- if (!__clk_get(clk)) {
- __clk_free_clk(clk);
cl = NULL;
- goto out;
- }
-
out:
mutex_unlock(&clocks_mutex);
return cl ? clk : ERR_PTR(-ENOENT);
}
+
+struct clk *clk_get_sys(const char *dev_id, const char *con_id)
+{
+ return __clk_get_sys(NULL, dev_id, con_id);
+}
EXPORT_SYMBOL(clk_get_sys);
struct clk *clk_get(struct device *dev, const char *con_id)
{
const char *dev_id = dev ? dev_name(dev) : NULL;
- struct clk *clk;
+ struct clk_hw *hw;
if (dev && dev->of_node) {
- clk = __of_clk_get_by_name(dev->of_node, dev_id, con_id);
- if (!IS_ERR(clk) || PTR_ERR(clk) == -EPROBE_DEFER)
- return clk;
+ hw = of_clk_get_hw(dev->of_node, 0, con_id);
+ if (!IS_ERR(hw) || PTR_ERR(hw) == -EPROBE_DEFER)
+ return clk_hw_create_clk(dev, hw, dev_id, con_id);
}
- return clk_get_sys(dev_id, con_id);
+ return __clk_get_sys(dev, dev_id, con_id);
}
EXPORT_SYMBOL(clk_get);
@@ -401,6 +307,23 @@ static struct clk_lookup *__clk_register_clkdev(struct clk_hw *hw,
return cl;
}
+static int do_clk_register_clkdev(struct clk_hw *hw,
+ struct clk_lookup **cl, const char *con_id, const char *dev_id)
+{
+ if (IS_ERR(hw))
+ return PTR_ERR(hw);
+ /*
+ * Since dev_id can be NULL, and NULL is handled specially, we must
+ * pass it as either a NULL format string, or with "%s".
+ */
+ if (dev_id)
+ *cl = __clk_register_clkdev(hw, con_id, "%s", dev_id);
+ else
+ *cl = __clk_register_clkdev(hw, con_id, NULL);
+
+ return *cl ? 0 : -ENOMEM;
+}
+
/**
* clk_register_clkdev - register one clock lookup for a struct clk
* @clk: struct clk to associate with all clk_lookups
@@ -423,17 +346,8 @@ int clk_register_clkdev(struct clk *clk, const char *con_id,
if (IS_ERR(clk))
return PTR_ERR(clk);
- /*
- * Since dev_id can be NULL, and NULL is handled specially, we must
- * pass it as either a NULL format string, or with "%s".
- */
- if (dev_id)
- cl = __clk_register_clkdev(__clk_get_hw(clk), con_id, "%s",
- dev_id);
- else
- cl = __clk_register_clkdev(__clk_get_hw(clk), con_id, NULL);
-
- return cl ? 0 : -ENOMEM;
+ return do_clk_register_clkdev(__clk_get_hw(clk), &cl, con_id,
+ dev_id);
}
EXPORT_SYMBOL(clk_register_clkdev);
@@ -456,18 +370,75 @@ int clk_hw_register_clkdev(struct clk_hw *hw, const char *con_id,
{
struct clk_lookup *cl;
- if (IS_ERR(hw))
- return PTR_ERR(hw);
+ return do_clk_register_clkdev(hw, &cl, con_id, dev_id);
+}
+EXPORT_SYMBOL(clk_hw_register_clkdev);
- /*
- * Since dev_id can be NULL, and NULL is handled specially, we must
- * pass it as either a NULL format string, or with "%s".
- */
- if (dev_id)
- cl = __clk_register_clkdev(hw, con_id, "%s", dev_id);
- else
- cl = __clk_register_clkdev(hw, con_id, NULL);
+static void devm_clkdev_release(struct device *dev, void *res)
+{
+ clkdev_drop(*(struct clk_lookup **)res);
+}
+
+static int devm_clk_match_clkdev(struct device *dev, void *res, void *data)
+{
+ struct clk_lookup **l = res;
- return cl ? 0 : -ENOMEM;
+ return *l == data;
}
-EXPORT_SYMBOL(clk_hw_register_clkdev);
+
+/**
+ * devm_clk_release_clkdev - Resource managed clkdev lookup release
+ * @dev: device this lookup is bound
+ * @con_id: connection ID string on device
+ * @dev_id: format string describing device name
+ *
+ * Drop the clkdev lookup created with devm_clk_hw_register_clkdev.
+ * Normally this function will not need to be called and the resource
+ * management code will ensure that the resource is freed.
+ */
+void devm_clk_release_clkdev(struct device *dev, const char *con_id,
+ const char *dev_id)
+{
+ struct clk_lookup *cl;
+ int rval;
+
+ cl = clk_find(dev_id, con_id);
+ WARN_ON(!cl);
+ rval = devres_release(dev, devm_clkdev_release,
+ devm_clk_match_clkdev, cl);
+ WARN_ON(rval);
+}
+EXPORT_SYMBOL(devm_clk_release_clkdev);
+
+/**
+ * devm_clk_hw_register_clkdev - managed clk lookup registration for clk_hw
+ * @dev: device this lookup is bound
+ * @hw: struct clk_hw to associate with all clk_lookups
+ * @con_id: connection ID string on device
+ * @dev_id: format string describing device name
+ *
+ * con_id or dev_id may be NULL as a wildcard, just as in the rest of
+ * clkdev.
+ *
+ * To make things easier for mass registration, we detect error clk_hws
+ * from a previous clk_hw_register_*() call, and return the error code for
+ * those. This is to permit this function to be called immediately
+ * after clk_hw_register_*().
+ */
+int devm_clk_hw_register_clkdev(struct device *dev, struct clk_hw *hw,
+ const char *con_id, const char *dev_id)
+{
+ int rval = -ENOMEM;
+ struct clk_lookup **cl;
+
+ cl = devres_alloc(devm_clkdev_release, sizeof(*cl), GFP_KERNEL);
+ if (cl) {
+ rval = do_clk_register_clkdev(hw, cl, con_id, dev_id);
+ if (!rval)
+ devres_add(dev, cl);
+ else
+ devres_free(cl);
+ }
+ return rval;
+}
+EXPORT_SYMBOL(devm_clk_hw_register_clkdev);
diff --git a/drivers/clk/imx/Kconfig b/drivers/clk/imx/Kconfig
index 4aae31a23449..0eaf41848280 100644
--- a/drivers/clk/imx/Kconfig
+++ b/drivers/clk/imx/Kconfig
@@ -8,6 +8,12 @@ config MXC_CLK_SCU
bool
depends on IMX_SCU
+config CLK_IMX8MM
+ bool "IMX8MM CCM Clock Driver"
+ depends on ARCH_MXC && ARM64
+ help
+ Build the driver for i.MX8MM CCM Clock Driver
+
config CLK_IMX8MQ
bool "IMX8MQ CCM Clock Driver"
depends on ARCH_MXC && ARM64
diff --git a/drivers/clk/imx/Makefile b/drivers/clk/imx/Makefile
index 73119fbfa547..0d5180fbe988 100644
--- a/drivers/clk/imx/Makefile
+++ b/drivers/clk/imx/Makefile
@@ -18,12 +18,14 @@ obj-$(CONFIG_MXC_CLK) += \
clk-pllv2.o \
clk-pllv3.o \
clk-pllv4.o \
- clk-sccg-pll.o
+ clk-sccg-pll.o \
+ clk-pll14xx.o
obj-$(CONFIG_MXC_CLK_SCU) += \
clk-scu.o \
clk-lpcg-scu.o
+obj-$(CONFIG_CLK_IMX8MM) += clk-imx8mm.o
obj-$(CONFIG_CLK_IMX8MQ) += clk-imx8mq.o
obj-$(CONFIG_CLK_IMX8QXP) += clk-imx8qxp.o clk-imx8qxp-lpcg.o
diff --git a/drivers/clk/imx/clk-composite-8m.c b/drivers/clk/imx/clk-composite-8m.c
index 527ade1d6933..574fac1a169f 100644
--- a/drivers/clk/imx/clk-composite-8m.c
+++ b/drivers/clk/imx/clk-composite-8m.c
@@ -123,7 +123,7 @@ static const struct clk_ops imx8m_clk_composite_divider_ops = {
};
struct clk *imx8m_clk_composite_flags(const char *name,
- const char **parent_names,
+ const char * const *parent_names,
int num_parents, void __iomem *reg,
unsigned long flags)
{
diff --git a/drivers/clk/imx/clk-imx51-imx53.c b/drivers/clk/imx/clk-imx51-imx53.c
index fc8e782d817b..e91c826bce70 100644
--- a/drivers/clk/imx/clk-imx51-imx53.c
+++ b/drivers/clk/imx/clk-imx51-imx53.c
@@ -428,6 +428,7 @@ static void __init mx51_clocks_init(struct device_node *np)
clk[IMX5_CLK_ESDHC4_PER_GATE] = imx_clk_gate2("esdhc4_per_gate", "esdhc_d_sel", MXC_CCM_CCGR3, 14);
clk[IMX5_CLK_USB_PHY_GATE] = imx_clk_gate2("usb_phy_gate", "usb_phy_sel", MXC_CCM_CCGR2, 0);
clk[IMX5_CLK_HSI2C_GATE] = imx_clk_gate2("hsi2c_gate", "ipg", MXC_CCM_CCGR1, 22);
+ clk[IMX5_CLK_SCC2_IPG_GATE] = imx_clk_gate2("scc2_gate", "ipg", MXC_CCM_CCGR1, 30);
clk[IMX5_CLK_MIPI_HSC1_GATE] = imx_clk_gate2_flags("mipi_hsc1_gate", "ipg", MXC_CCM_CCGR4, 6, CLK_IS_CRITICAL);
clk[IMX5_CLK_MIPI_HSC2_GATE] = imx_clk_gate2_flags("mipi_hsc2_gate", "ipg", MXC_CCM_CCGR4, 8, CLK_IS_CRITICAL);
clk[IMX5_CLK_MIPI_ESC_GATE] = imx_clk_gate2_flags("mipi_esc_gate", "ipg", MXC_CCM_CCGR4, 10, CLK_IS_CRITICAL);
diff --git a/drivers/clk/imx/clk-imx6q.c b/drivers/clk/imx/clk-imx6q.c
index 716eac3136b4..708e7c5590dd 100644
--- a/drivers/clk/imx/clk-imx6q.c
+++ b/drivers/clk/imx/clk-imx6q.c
@@ -471,6 +471,7 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node)
np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-anatop");
anatop_base = base = of_iomap(np, 0);
WARN_ON(!base);
+ of_node_put(np);
/* Audio/video PLL post dividers do not work on i.MX6q revision 1.0 */
if (clk_on_imx6q() && imx_get_soc_revision() == IMX_CHIP_REVISION_1_0) {
diff --git a/drivers/clk/imx/clk-imx6sx.c b/drivers/clk/imx/clk-imx6sx.c
index 18527a335ace..91558b09bf9e 100644
--- a/drivers/clk/imx/clk-imx6sx.c
+++ b/drivers/clk/imx/clk-imx6sx.c
@@ -151,6 +151,7 @@ static void __init imx6sx_clocks_init(struct device_node *ccm_node)
np = of_find_compatible_node(NULL, NULL, "fsl,imx6sx-anatop");
base = of_iomap(np, 0);
WARN_ON(!base);
+ of_node_put(np);
clks[IMX6SX_PLL1_BYPASS_SRC] = imx_clk_mux("pll1_bypass_src", base + 0x00, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels));
clks[IMX6SX_PLL2_BYPASS_SRC] = imx_clk_mux("pll2_bypass_src", base + 0x30, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels));
diff --git a/drivers/clk/imx/clk-imx7d.c b/drivers/clk/imx/clk-imx7d.c
index 06c105d580a4..cfbd8d4edb85 100644
--- a/drivers/clk/imx/clk-imx7d.c
+++ b/drivers/clk/imx/clk-imx7d.c
@@ -404,6 +404,7 @@ static void __init imx7d_clocks_init(struct device_node *ccm_node)
np = of_find_compatible_node(NULL, NULL, "fsl,imx7d-anatop");
base = of_iomap(np, 0);
WARN_ON(!base);
+ of_node_put(np);
clks[IMX7D_PLL_ARM_MAIN_SRC] = imx_clk_mux("pll_arm_main_src", base + 0x60, 14, 2, pll_bypass_src_sel, ARRAY_SIZE(pll_bypass_src_sel));
clks[IMX7D_PLL_DRAM_MAIN_SRC] = imx_clk_mux("pll_dram_main_src", base + 0x70, 14, 2, pll_bypass_src_sel, ARRAY_SIZE(pll_bypass_src_sel));
diff --git a/drivers/clk/imx/clk-imx7ulp.c b/drivers/clk/imx/clk-imx7ulp.c
index 4e18f629f823..ce306631e844 100644
--- a/drivers/clk/imx/clk-imx7ulp.c
+++ b/drivers/clk/imx/clk-imx7ulp.c
@@ -48,8 +48,8 @@ static void __init imx7ulp_clk_scg1_init(struct device_node *np)
struct clk_hw **clks;
void __iomem *base;
- clk_data = kzalloc(sizeof(*clk_data) + sizeof(*clk_data->hws) *
- IMX7ULP_CLK_SCG1_END, GFP_KERNEL);
+ clk_data = kzalloc(struct_size(clk_data, hws, IMX7ULP_CLK_SCG1_END),
+ GFP_KERNEL);
if (!clk_data)
return;
@@ -136,8 +136,8 @@ static void __init imx7ulp_clk_pcc2_init(struct device_node *np)
struct clk_hw **clks;
void __iomem *base;
- clk_data = kzalloc(sizeof(*clk_data) + sizeof(*clk_data->hws) *
- IMX7ULP_CLK_PCC2_END, GFP_KERNEL);
+ clk_data = kzalloc(struct_size(clk_data, hws, IMX7ULP_CLK_PCC2_END),
+ GFP_KERNEL);
if (!clk_data)
return;
@@ -183,8 +183,8 @@ static void __init imx7ulp_clk_pcc3_init(struct device_node *np)
struct clk_hw **clks;
void __iomem *base;
- clk_data = kzalloc(sizeof(*clk_data) + sizeof(*clk_data->hws) *
- IMX7ULP_CLK_PCC3_END, GFP_KERNEL);
+ clk_data = kzalloc(struct_size(clk_data, hws, IMX7ULP_CLK_PCC3_END),
+ GFP_KERNEL);
if (!clk_data)
return;
@@ -228,8 +228,8 @@ static void __init imx7ulp_clk_smc1_init(struct device_node *np)
struct clk_hw **clks;
void __iomem *base;
- clk_data = kzalloc(sizeof(*clk_data) + sizeof(*clk_data->hws) *
- IMX7ULP_CLK_SMC1_END, GFP_KERNEL);
+ clk_data = kzalloc(struct_size(clk_data, hws, IMX7ULP_CLK_SMC1_END),
+ GFP_KERNEL);
if (!clk_data)
return;
diff --git a/drivers/clk/imx/clk-imx8mm.c b/drivers/clk/imx/clk-imx8mm.c
new file mode 100644
index 000000000000..1ef8438e3d6d
--- /dev/null
+++ b/drivers/clk/imx/clk-imx8mm.c
@@ -0,0 +1,675 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2017-2018 NXP.
+ */
+
+#include <dt-bindings/clock/imx8mm-clock.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+
+#include "clk.h"
+
+static u32 share_count_sai1;
+static u32 share_count_sai2;
+static u32 share_count_sai3;
+static u32 share_count_sai4;
+static u32 share_count_sai5;
+static u32 share_count_sai6;
+static u32 share_count_dcss;
+static u32 share_count_pdm;
+static u32 share_count_nand;
+
+#define PLL_1416X_RATE(_rate, _m, _p, _s) \
+ { \
+ .rate = (_rate), \
+ .mdiv = (_m), \
+ .pdiv = (_p), \
+ .sdiv = (_s), \
+ }
+
+#define PLL_1443X_RATE(_rate, _m, _p, _s, _k) \
+ { \
+ .rate = (_rate), \
+ .mdiv = (_m), \
+ .pdiv = (_p), \
+ .sdiv = (_s), \
+ .kdiv = (_k), \
+ }
+
+static const struct imx_pll14xx_rate_table imx8mm_pll1416x_tbl[] = {
+ PLL_1416X_RATE(1800000000U, 225, 3, 0),
+ PLL_1416X_RATE(1600000000U, 200, 3, 0),
+ PLL_1416X_RATE(1200000000U, 300, 3, 1),
+ PLL_1416X_RATE(1000000000U, 250, 3, 1),
+ PLL_1416X_RATE(800000000U, 200, 3, 1),
+ PLL_1416X_RATE(750000000U, 250, 2, 2),
+ PLL_1416X_RATE(700000000U, 350, 3, 2),
+ PLL_1416X_RATE(600000000U, 300, 3, 2),
+};
+
+static const struct imx_pll14xx_rate_table imx8mm_audiopll_tbl[] = {
+ PLL_1443X_RATE(786432000U, 655, 5, 2, 23593),
+ PLL_1443X_RATE(722534400U, 301, 5, 1, 3670),
+};
+
+static const struct imx_pll14xx_rate_table imx8mm_videopll_tbl[] = {
+ PLL_1443X_RATE(650000000U, 325, 3, 2, 0),
+ PLL_1443X_RATE(594000000U, 198, 2, 2, 0),
+};
+
+static const struct imx_pll14xx_rate_table imx8mm_drampll_tbl[] = {
+ PLL_1443X_RATE(650000000U, 325, 3, 2, 0),
+};
+
+static struct imx_pll14xx_clk imx8mm_audio_pll __initdata = {
+ .type = PLL_1443X,
+ .rate_table = imx8mm_audiopll_tbl,
+ .rate_count = ARRAY_SIZE(imx8mm_audiopll_tbl),
+};
+
+static struct imx_pll14xx_clk imx8mm_video_pll __initdata = {
+ .type = PLL_1443X,
+ .rate_table = imx8mm_videopll_tbl,
+ .rate_count = ARRAY_SIZE(imx8mm_videopll_tbl),
+};
+
+static struct imx_pll14xx_clk imx8mm_dram_pll __initdata = {
+ .type = PLL_1443X,
+ .rate_table = imx8mm_drampll_tbl,
+ .rate_count = ARRAY_SIZE(imx8mm_drampll_tbl),
+};
+
+static struct imx_pll14xx_clk imx8mm_arm_pll __initdata = {
+ .type = PLL_1416X,
+ .rate_table = imx8mm_pll1416x_tbl,
+ .rate_count = ARRAY_SIZE(imx8mm_pll1416x_tbl),
+};
+
+static struct imx_pll14xx_clk imx8mm_gpu_pll __initdata = {
+ .type = PLL_1416X,
+ .rate_table = imx8mm_pll1416x_tbl,
+ .rate_count = ARRAY_SIZE(imx8mm_pll1416x_tbl),
+};
+
+static struct imx_pll14xx_clk imx8mm_vpu_pll __initdata = {
+ .type = PLL_1416X,
+ .rate_table = imx8mm_pll1416x_tbl,
+ .rate_count = ARRAY_SIZE(imx8mm_pll1416x_tbl),
+};
+
+static struct imx_pll14xx_clk imx8mm_sys_pll __initdata = {
+ .type = PLL_1416X,
+ .rate_table = imx8mm_pll1416x_tbl,
+ .rate_count = ARRAY_SIZE(imx8mm_pll1416x_tbl),
+};
+
+static const char *pll_ref_sels[] = { "osc_24m", "dummy", "dummy", "dummy", };
+static const char *audio_pll1_bypass_sels[] = {"audio_pll1", "audio_pll1_ref_sel", };
+static const char *audio_pll2_bypass_sels[] = {"audio_pll2", "audio_pll2_ref_sel", };
+static const char *video_pll1_bypass_sels[] = {"video_pll1", "video_pll1_ref_sel", };
+static const char *dram_pll_bypass_sels[] = {"dram_pll", "dram_pll_ref_sel", };
+static const char *gpu_pll_bypass_sels[] = {"gpu_pll", "gpu_pll_ref_sel", };
+static const char *vpu_pll_bypass_sels[] = {"vpu_pll", "vpu_pll_ref_sel", };
+static const char *arm_pll_bypass_sels[] = {"arm_pll", "arm_pll_ref_sel", };
+static const char *sys_pll1_bypass_sels[] = {"sys_pll1", "sys_pll1_ref_sel", };
+static const char *sys_pll2_bypass_sels[] = {"sys_pll2", "sys_pll2_ref_sel", };
+static const char *sys_pll3_bypass_sels[] = {"sys_pll3", "sys_pll3_ref_sel", };
+
+/* CCM ROOT */
+static const char *imx8mm_a53_sels[] = {"osc_24m", "arm_pll_out", "sys_pll2_500m", "sys_pll2_1000m",
+ "sys_pll1_800m", "sys_pll1_400m", "audio_pll1_out", "sys_pll3_out", };
+
+static const char *imx8mm_m4_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll2_250m", "sys_pll1_266m",
+ "sys_pll1_800m", "audio_pll1_out", "video_pll1_out", "sys_pll3_out", };
+
+static const char *imx8mm_vpu_sels[] = {"osc_24m", "arm_pll_out", "sys_pll2_500m", "sys_pll2_1000m",
+ "sys_pll1_800m", "sys_pll1_400m", "audio_pll1_out", "vpu_pll_out", };
+
+static const char *imx8mm_gpu3d_sels[] = {"osc_24m", "gpu_pll_out", "sys_pll1_800m", "sys_pll3_out",
+ "sys_pll2_1000m", "audio_pll1_out", "video_pll1_out", "audio_pll2_out", };
+
+static const char *imx8mm_gpu2d_sels[] = {"osc_24m", "gpu_pll_out", "sys_pll1_800m", "sys_pll3_out",
+ "sys_pll2_1000m", "audio_pll1_out", "video_pll1_out", "audio_pll2_out", };
+
+static const char *imx8mm_main_axi_sels[] = {"osc_24m", "sys_pll2_333m", "sys_pll1_800m", "sys_pll2_250m",
+ "sys_pll2_1000m", "audio_pll1_out", "video_pll1_out", "sys_pll1_100m",};
+
+static const char *imx8mm_enet_axi_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll1_800m", "sys_pll2_250m",
+ "sys_pll2_200m", "audio_pll1_out", "video_pll1_out", "sys_pll3_out", };
+
+static const char *imx8mm_nand_usdhc_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll1_800m", "sys_pll2_200m",
+ "sys_pll1_133m", "sys_pll3_out", "sys_pll2_250m", "audio_pll1_out", };
+
+static const char *imx8mm_vpu_bus_sels[] = {"osc_24m", "sys_pll1_800m", "vpu_pll_out", "audio_pll2_out",
+ "sys_pll3_out", "sys_pll2_1000m", "sys_pll2_200m", "sys_pll1_100m", };
+
+static const char *imx8mm_disp_axi_sels[] = {"osc_24m", "sys_pll2_1000m", "sys_pll1_800m", "sys_pll3_out",
+ "sys_pll1_40m", "audio_pll2_out", "clk_ext1", "clk_ext4", };
+
+static const char *imx8mm_disp_apb_sels[] = {"osc_24m", "sys_pll2_125m", "sys_pll1_800m", "sys_pll3_out",
+ "sys_pll1_40m", "audio_pll2_out", "clk_ext1", "clk_ext3", };
+
+static const char *imx8mm_disp_rtrm_sels[] = {"osc_24m", "sys_pll1_800m", "sys_pll2_200m", "sys_pll2_1000m",
+ "audio_pll1_out", "video_pll1_out", "clk_ext2", "clk_ext3", };
+
+static const char *imx8mm_usb_bus_sels[] = {"osc_24m", "sys_pll2_500m", "sys_pll1_800m", "sys_pll2_100m",
+ "sys_pll2_200m", "clk_ext2", "clk_ext4", "audio_pll2_out", };
+
+static const char *imx8mm_gpu_axi_sels[] = {"osc_24m", "sys_pll1_800m", "gpu_pll_out", "sys_pll3_out", "sys_pll2_1000m",
+ "audio_pll1_out", "video_pll1_out", "audio_pll2_out", };
+
+static const char *imx8mm_gpu_ahb_sels[] = {"osc_24m", "sys_pll1_800m", "gpu_pll_out", "sys_pll3_out", "sys_pll2_1000m",
+ "audio_pll1_out", "video_pll1_out", "audio_pll2_out", };
+
+static const char *imx8mm_noc_sels[] = {"osc_24m", "sys_pll1_800m", "sys_pll3_out", "sys_pll2_1000m", "sys_pll2_500m",
+ "audio_pll1_out", "video_pll1_out", "audio_pll2_out", };
+
+static const char *imx8mm_noc_apb_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll3_out", "sys_pll2_333m", "sys_pll2_200m",
+ "sys_pll1_800m", "audio_pll1_out", "video_pll1_out", };
+
+static const char *imx8mm_ahb_sels[] = {"osc_24m", "sys_pll1_133m", "sys_pll1_800m", "sys_pll1_400m",
+ "sys_pll2_125m", "sys_pll3_out", "audio_pll1_out", "video_pll1_out", };
+
+static const char *imx8mm_audio_ahb_sels[] = {"osc_24m", "sys_pll2_500m", "sys_pll1_800m", "sys_pll2_1000m",
+ "sys_pll2_166m", "sys_pll3_out", "audio_pll1_out", "video_pll1_out", };
+
+static const char *imx8mm_dram_alt_sels[] = {"osc_24m", "sys_pll1_800m", "sys_pll1_100m", "sys_pll2_500m",
+ "sys_pll2_1000m", "sys_pll3_out", "audio_pll1_out", "sys_pll1_266m", };
+
+static const char *imx8mm_dram_apb_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m", "sys_pll1_160m",
+ "sys_pll1_800m", "sys_pll3_out", "sys_pll2_250m", "audio_pll2_out", };
+
+static const char *imx8mm_vpu_g1_sels[] = {"osc_24m", "vpu_pll_out", "sys_pll1_800m", "sys_pll2_1000m",
+ "sys_pll1_100m", "sys_pll2_125m", "sys_pll3_out", "audio_pll1_out", };
+
+static const char *imx8mm_vpu_g2_sels[] = {"osc_24m", "vpu_pll_out", "sys_pll1_800m", "sys_pll2_1000m",
+ "sys_pll1_100m", "sys_pll2_125m", "sys_pll3_out", "audio_pll1_out", };
+
+static const char *imx8mm_disp_dtrc_sels[] = {"osc_24m", "video_pll2_out", "sys_pll1_800m", "sys_pll2_1000m",
+ "sys_pll1_160m", "video_pll1_out", "sys_pll3_out", "audio_pll2_out", };
+
+static const char *imx8mm_disp_dc8000_sels[] = {"osc_24m", "video_pll2_out", "sys_pll1_800m", "sys_pll2_1000m",
+ "sys_pll1_160m", "video_pll1_out", "sys_pll3_out", "audio_pll2_out", };
+
+static const char *imx8mm_pcie1_ctrl_sels[] = {"osc_24m", "sys_pll2_250m", "sys_pll2_200m", "sys_pll1_266m",
+ "sys_pll1_800m", "sys_pll2_500m", "sys_pll2_333m", "sys_pll3_out", };
+
+static const char *imx8mm_pcie1_phy_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll2_500m", "clk_ext1", "clk_ext2",
+ "clk_ext3", "clk_ext4", "sys_pll1_400m", };
+
+static const char *imx8mm_pcie1_aux_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll2_50m", "sys_pll3_out",
+ "sys_pll2_100m", "sys_pll1_80m", "sys_pll1_160m", "sys_pll1_200m", };
+
+static const char *imx8mm_dc_pixel_sels[] = {"osc_24m", "video_pll1_out", "audio_pll2_out", "audio_pll1_out",
+ "sys_pll1_800m", "sys_pll2_1000m", "sys_pll3_out", "clk_ext4", };
+
+static const char *imx8mm_lcdif_pixel_sels[] = {"osc_24m", "video_pll1_out", "audio_pll2_out", "audio_pll1_out",
+ "sys_pll1_800m", "sys_pll2_1000m", "sys_pll3_out", "clk_ext4", };
+
+static const char *imx8mm_sai1_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out",
+ "sys_pll1_133m", "osc_hdmi", "clk_ext1", "clk_ext2", };
+
+static const char *imx8mm_sai2_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out",
+ "sys_pll1_133m", "osc_hdmi", "clk_ext2", "clk_ext3", };
+
+static const char *imx8mm_sai3_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out",
+ "sys_pll1_133m", "osc_hdmi", "clk_ext3", "clk_ext4", };
+
+static const char *imx8mm_sai4_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out",
+ "sys_pll1_133m", "osc_hdmi", "clk_ext1", "clk_ext2", };
+
+static const char *imx8mm_sai5_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out",
+ "sys_pll1_133m", "osc_hdmi", "clk_ext2", "clk_ext3", };
+
+static const char *imx8mm_sai6_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out",
+ "sys_pll1_133m", "osc_hdmi", "clk_ext3", "clk_ext4", };
+
+static const char *imx8mm_spdif1_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out",
+ "sys_pll1_133m", "osc_hdmi", "clk_ext2", "clk_ext3", };
+
+static const char *imx8mm_spdif2_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out",
+ "sys_pll1_133m", "osc_hdmi", "clk_ext3", "clk_ext4", };
+
+static const char *imx8mm_enet_ref_sels[] = {"osc_24m", "sys_pll2_125m", "sys_pll2_50m", "sys_pll2_100m",
+ "sys_pll1_160m", "audio_pll1_out", "video_pll1_out", "clk_ext4", };
+
+static const char *imx8mm_enet_timer_sels[] = {"osc_24m", "sys_pll2_100m", "audio_pll1_out", "clk_ext1", "clk_ext2",
+ "clk_ext3", "clk_ext4", "video_pll1_out", };
+
+static const char *imx8mm_enet_phy_sels[] = {"osc_24m", "sys_pll2_50m", "sys_pll2_125m", "sys_pll2_200m",
+ "sys_pll2_500m", "video_pll1_out", "audio_pll2_out", };
+
+static const char *imx8mm_nand_sels[] = {"osc_24m", "sys_pll2_500m", "audio_pll1_out", "sys_pll1_400m",
+ "audio_pll2_out", "sys_pll3_out", "sys_pll2_250m", "video_pll1_out", };
+
+static const char *imx8mm_qspi_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll1_800m", "sys_pll2_500m",
+ "audio_pll2_out", "sys_pll1_266m", "sys_pll3_out", "sys_pll1_100m", };
+
+static const char *imx8mm_usdhc1_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll1_800m", "sys_pll2_500m",
+ "sys_pll3_out", "sys_pll1_266m", "audio_pll2_out", "sys_pll1_100m", };
+
+static const char *imx8mm_usdhc2_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll1_800m", "sys_pll2_500m",
+ "sys_pll3_out", "sys_pll1_266m", "audio_pll2_out", "sys_pll1_100m", };
+
+static const char *imx8mm_i2c1_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", "sys_pll3_out", "audio_pll1_out",
+ "video_pll1_out", "audio_pll2_out", "sys_pll1_133m", };
+
+static const char *imx8mm_i2c2_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", "sys_pll3_out", "audio_pll1_out",
+ "video_pll1_out", "audio_pll2_out", "sys_pll1_133m", };
+
+static const char *imx8mm_i2c3_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", "sys_pll3_out", "audio_pll1_out",
+ "video_pll1_out", "audio_pll2_out", "sys_pll1_133m", };
+
+static const char *imx8mm_i2c4_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", "sys_pll3_out", "audio_pll1_out",
+ "video_pll1_out", "audio_pll2_out", "sys_pll1_133m", };
+
+static const char *imx8mm_uart1_sels[] = {"osc_24m", "sys_pll1_80m", "sys_pll2_200m", "sys_pll2_100m",
+ "sys_pll3_out", "clk_ext2", "clk_ext4", "audio_pll2_out", };
+
+static const char *imx8mm_uart2_sels[] = {"osc_24m", "sys_pll1_80m", "sys_pll2_200m", "sys_pll2_100m",
+ "sys_pll3_out", "clk_ext2", "clk_ext3", "audio_pll2_out", };
+
+static const char *imx8mm_uart3_sels[] = {"osc_24m", "sys_pll1_80m", "sys_pll2_200m", "sys_pll2_100m",
+ "sys_pll3_out", "clk_ext2", "clk_ext4", "audio_pll2_out", };
+
+static const char *imx8mm_uart4_sels[] = {"osc_24m", "sys_pll1_80m", "sys_pll2_200m", "sys_pll2_100m",
+ "sys_pll3_out", "clk_ext2", "clk_ext3", "audio_pll2_out", };
+
+static const char *imx8mm_usb_core_sels[] = {"osc_24m", "sys_pll1_100m", "sys_pll1_40m", "sys_pll2_100m",
+ "sys_pll2_200m", "clk_ext2", "clk_ext3", "audio_pll2_out", };
+
+static const char *imx8mm_usb_phy_sels[] = {"osc_24m", "sys_pll1_100m", "sys_pll1_40m", "sys_pll2_100m",
+ "sys_pll2_200m", "clk_ext2", "clk_ext3", "audio_pll2_out", };
+
+static const char *imx8mm_ecspi1_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m", "sys_pll1_160m",
+ "sys_pll1_800m", "sys_pll3_out", "sys_pll2_250m", "audio_pll2_out", };
+
+static const char *imx8mm_ecspi2_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m", "sys_pll1_160m",
+ "sys_pll1_800m", "sys_pll3_out", "sys_pll2_250m", "audio_pll2_out", };
+
+static const char *imx8mm_pwm1_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m", "sys_pll1_40m",
+ "sys_pll3_out", "clk_ext1", "sys_pll1_80m", "video_pll1_out", };
+
+static const char *imx8mm_pwm2_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m", "sys_pll1_40m",
+ "sys_pll3_out", "clk_ext1", "sys_pll1_80m", "video_pll1_out", };
+
+static const char *imx8mm_pwm3_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m", "sys_pll1_40m",
+ "sys3_pll2_out", "clk_ext2", "sys_pll1_80m", "video_pll1_out", };
+
+static const char *imx8mm_pwm4_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m", "sys_pll1_40m",
+ "sys_pll3_out", "clk_ext2", "sys_pll1_80m", "video_pll1_out", };
+
+static const char *imx8mm_gpt1_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_400m", "sys_pll1_40m",
+ "video_pll1_out", "sys_pll1_800m", "audio_pll1_out", "clk_ext1" };
+
+static const char *imx8mm_wdog_sels[] = {"osc_24m", "sys_pll1_133m", "sys_pll1_160m", "vpu_pll_out",
+ "sys_pll2_125m", "sys_pll3_out", "sys_pll1_80m", "sys_pll2_166m", };
+
+static const char *imx8mm_wrclk_sels[] = {"osc_24m", "sys_pll1_40m", "vpu_pll_out", "sys_pll3_out", "sys_pll2_200m",
+ "sys_pll1_266m", "sys_pll2_500m", "sys_pll1_100m", };
+
+static const char *imx8mm_dsi_core_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll2_250m", "sys_pll1_800m",
+ "sys_pll2_1000m", "sys_pll3_out", "audio_pll2_out", "video_pll1_out", };
+
+static const char *imx8mm_dsi_phy_sels[] = {"osc_24m", "sys_pll2_125m", "sys_pll2_100m", "sys_pll1_800m",
+ "sys_pll2_1000m", "clk_ext2", "audio_pll2_out", "video_pll1_out", };
+
+static const char *imx8mm_dsi_dbi_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll2_100m", "sys_pll1_800m",
+ "sys_pll2_1000m", "sys_pll3_out", "audio_pll2_out", "video_pll1_out", };
+
+static const char *imx8mm_usdhc3_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll1_800m", "sys_pll2_500m",
+ "sys_pll3_out", "sys_pll1_266m", "audio_pll2_clk", "sys_pll1_100m", };
+
+static const char *imx8mm_csi1_core_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll2_250m", "sys_pll1_800m",
+ "sys_pll2_1000m", "sys_pll3_out", "audio_pll2_out", "video_pll1_out", };
+
+static const char *imx8mm_csi1_phy_sels[] = {"osc_24m", "sys_pll2_333m", "sys_pll2_100m", "sys_pll1_800m",
+ "sys_pll2_1000m", "clk_ext2", "audio_pll2_out", "video_pll1_out", };
+
+static const char *imx8mm_csi1_esc_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_80m", "sys_pll1_800m",
+ "sys_pll2_1000m", "sys_pll3_out", "clk_ext3", "audio_pll2_out", };
+
+static const char *imx8mm_csi2_core_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll2_250m", "sys_pll1_800m",
+ "sys_pll2_1000m", "sys_pll3_out", "audio_pll2_out", "video_pll1_out", };
+
+static const char *imx8mm_csi2_phy_sels[] = {"osc_24m", "sys_pll2_333m", "sys_pll2_100m", "sys_pll1_800m",
+ "sys_pll2_1000m", "clk_ext2", "audio_pll2_out", "video_pll1_out", };
+
+static const char *imx8mm_csi2_esc_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_80m", "sys_pll1_800m",
+ "sys_pll2_1000m", "sys_pll3_out", "clk_ext3", "audio_pll2_out", };
+
+static const char *imx8mm_pcie2_ctrl_sels[] = {"osc_24m", "sys_pll2_250m", "sys_pll2_200m", "sys_pll1_266m",
+ "sys_pll1_800m", "sys_pll2_500m", "sys_pll2_333m", "sys_pll3_out", };
+
+static const char *imx8mm_pcie2_phy_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll2_500m", "clk_ext1",
+ "clk_ext2", "clk_ext3", "clk_ext4", "sys_pll1_400m", };
+
+static const char *imx8mm_pcie2_aux_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll2_50m", "sys_pll3_out",
+ "sys_pll2_100m", "sys_pll1_80m", "sys_pll1_160m", "sys_pll1_200m", };
+
+static const char *imx8mm_ecspi3_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m", "sys_pll1_160m",
+ "sys_pll1_800m", "sys_pll3_out", "sys_pll2_250m", "audio_pll2_out", };
+
+static const char *imx8mm_pdm_sels[] = {"osc_24m", "sys_pll2_100m", "audio_pll1_out", "sys_pll1_800m",
+ "sys_pll2_1000m", "sys_pll3_out", "clk_ext3", "audio_pll2_out", };
+
+static const char *imx8mm_vpu_h1_sels[] = {"osc_24m", "vpu_pll_out", "sys_pll1_800m", "sys_pll2_1000m",
+ "audio_pll2_clk", "sys_pll2_125m", "sys_pll3_clk", "audio_pll1_out", };
+
+static const char *imx8mm_dram_core_sels[] = {"dram_pll_out", "dram_alt_root", };
+
+static const char *imx8mm_clko1_sels[] = {"osc_24m", "sys_pll1_800m", "osc_27m", "sys_pll1_200m", "audio_pll2_clk",
+ "vpu_pll", "sys_pll1_80m", };
+
+static struct clk *clks[IMX8MM_CLK_END];
+static struct clk_onecell_data clk_data;
+
+static struct clk ** const uart_clks[] __initconst = {
+ &clks[IMX8MM_CLK_UART1_ROOT],
+ &clks[IMX8MM_CLK_UART2_ROOT],
+ &clks[IMX8MM_CLK_UART3_ROOT],
+ &clks[IMX8MM_CLK_UART4_ROOT],
+ NULL
+};
+
+static int __init imx8mm_clocks_init(struct device_node *ccm_node)
+{
+ struct device_node *np;
+ void __iomem *base;
+ int ret;
+
+ clks[IMX8MM_CLK_DUMMY] = imx_clk_fixed("dummy", 0);
+ clks[IMX8MM_CLK_24M] = of_clk_get_by_name(ccm_node, "osc_24m");
+ clks[IMX8MM_CLK_32K] = of_clk_get_by_name(ccm_node, "osc_32k");
+ clks[IMX8MM_CLK_EXT1] = of_clk_get_by_name(ccm_node, "clk_ext1");
+ clks[IMX8MM_CLK_EXT2] = of_clk_get_by_name(ccm_node, "clk_ext2");
+ clks[IMX8MM_CLK_EXT3] = of_clk_get_by_name(ccm_node, "clk_ext3");
+ clks[IMX8MM_CLK_EXT4] = of_clk_get_by_name(ccm_node, "clk_ext4");
+
+ np = of_find_compatible_node(NULL, NULL, "fsl,imx8mm-anatop");
+ base = of_iomap(np, 0);
+ if (WARN_ON(!base))
+ return -ENOMEM;
+
+ clks[IMX8MM_AUDIO_PLL1_REF_SEL] = imx_clk_mux("audio_pll1_ref_sel", base + 0x0, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels));
+ clks[IMX8MM_AUDIO_PLL2_REF_SEL] = imx_clk_mux("audio_pll2_ref_sel", base + 0x14, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels));
+ clks[IMX8MM_VIDEO_PLL1_REF_SEL] = imx_clk_mux("video_pll1_ref_sel", base + 0x28, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels));
+ clks[IMX8MM_DRAM_PLL_REF_SEL] = imx_clk_mux("dram_pll_ref_sel", base + 0x50, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels));
+ clks[IMX8MM_GPU_PLL_REF_SEL] = imx_clk_mux("gpu_pll_ref_sel", base + 0x64, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels));
+ clks[IMX8MM_VPU_PLL_REF_SEL] = imx_clk_mux("vpu_pll_ref_sel", base + 0x74, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels));
+ clks[IMX8MM_ARM_PLL_REF_SEL] = imx_clk_mux("arm_pll_ref_sel", base + 0x84, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels));
+ clks[IMX8MM_SYS_PLL1_REF_SEL] = imx_clk_mux("sys_pll1_ref_sel", base + 0x94, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels));
+ clks[IMX8MM_SYS_PLL2_REF_SEL] = imx_clk_mux("sys_pll2_ref_sel", base + 0x104, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels));
+ clks[IMX8MM_SYS_PLL3_REF_SEL] = imx_clk_mux("sys_pll3_ref_sel", base + 0x114, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels));
+
+ clks[IMX8MM_AUDIO_PLL1] = imx_clk_pll14xx("audio_pll1", "audio_pll1_ref_sel", base, &imx8mm_audio_pll);
+ clks[IMX8MM_AUDIO_PLL2] = imx_clk_pll14xx("audio_pll2", "audio_pll2_ref_sel", base + 0x14, &imx8mm_audio_pll);
+ clks[IMX8MM_VIDEO_PLL1] = imx_clk_pll14xx("video_pll1", "video_pll1_ref_sel", base + 0x28, &imx8mm_video_pll);
+ clks[IMX8MM_DRAM_PLL] = imx_clk_pll14xx("dram_pll", "dram_pll_ref_sel", base + 0x50, &imx8mm_dram_pll);
+ clks[IMX8MM_GPU_PLL] = imx_clk_pll14xx("gpu_pll", "gpu_pll_ref_sel", base + 0x64, &imx8mm_gpu_pll);
+ clks[IMX8MM_VPU_PLL] = imx_clk_pll14xx("vpu_pll", "vpu_pll_ref_sel", base + 0x74, &imx8mm_vpu_pll);
+ clks[IMX8MM_ARM_PLL] = imx_clk_pll14xx("arm_pll", "arm_pll_ref_sel", base + 0x84, &imx8mm_arm_pll);
+ clks[IMX8MM_SYS_PLL1] = imx_clk_pll14xx("sys_pll1", "sys_pll1_ref_sel", base + 0x94, &imx8mm_sys_pll);
+ clks[IMX8MM_SYS_PLL2] = imx_clk_pll14xx("sys_pll2", "sys_pll2_ref_sel", base + 0x104, &imx8mm_sys_pll);
+ clks[IMX8MM_SYS_PLL3] = imx_clk_pll14xx("sys_pll3", "sys_pll3_ref_sel", base + 0x114, &imx8mm_sys_pll);
+
+ /* PLL bypass out */
+ clks[IMX8MM_AUDIO_PLL1_BYPASS] = imx_clk_mux_flags("audio_pll1_bypass", base, 4, 1, audio_pll1_bypass_sels, ARRAY_SIZE(audio_pll1_bypass_sels), CLK_SET_RATE_PARENT);
+ clks[IMX8MM_AUDIO_PLL2_BYPASS] = imx_clk_mux_flags("audio_pll2_bypass", base + 0x14, 4, 1, audio_pll2_bypass_sels, ARRAY_SIZE(audio_pll2_bypass_sels), CLK_SET_RATE_PARENT);
+ clks[IMX8MM_VIDEO_PLL1_BYPASS] = imx_clk_mux_flags("video_pll1_bypass", base + 0x28, 4, 1, video_pll1_bypass_sels, ARRAY_SIZE(video_pll1_bypass_sels), CLK_SET_RATE_PARENT);
+ clks[IMX8MM_DRAM_PLL_BYPASS] = imx_clk_mux_flags("dram_pll_bypass", base + 0x50, 4, 1, dram_pll_bypass_sels, ARRAY_SIZE(dram_pll_bypass_sels), CLK_SET_RATE_PARENT);
+ clks[IMX8MM_GPU_PLL_BYPASS] = imx_clk_mux_flags("gpu_pll_bypass", base + 0x64, 4, 1, gpu_pll_bypass_sels, ARRAY_SIZE(gpu_pll_bypass_sels), CLK_SET_RATE_PARENT);
+ clks[IMX8MM_VPU_PLL_BYPASS] = imx_clk_mux_flags("vpu_pll_bypass", base + 0x74, 4, 1, vpu_pll_bypass_sels, ARRAY_SIZE(vpu_pll_bypass_sels), CLK_SET_RATE_PARENT);
+ clks[IMX8MM_ARM_PLL_BYPASS] = imx_clk_mux_flags("arm_pll_bypass", base + 0x84, 4, 1, arm_pll_bypass_sels, ARRAY_SIZE(arm_pll_bypass_sels), CLK_SET_RATE_PARENT);
+ clks[IMX8MM_SYS_PLL1_BYPASS] = imx_clk_mux_flags("sys_pll1_bypass", base + 0x94, 4, 1, sys_pll1_bypass_sels, ARRAY_SIZE(sys_pll1_bypass_sels), CLK_SET_RATE_PARENT);
+ clks[IMX8MM_SYS_PLL2_BYPASS] = imx_clk_mux_flags("sys_pll2_bypass", base + 0x104, 4, 1, sys_pll2_bypass_sels, ARRAY_SIZE(sys_pll2_bypass_sels), CLK_SET_RATE_PARENT);
+ clks[IMX8MM_SYS_PLL3_BYPASS] = imx_clk_mux_flags("sys_pll3_bypass", base + 0x114, 4, 1, sys_pll3_bypass_sels, ARRAY_SIZE(sys_pll3_bypass_sels), CLK_SET_RATE_PARENT);
+
+ /* unbypass all the plls */
+ clk_set_parent(clks[IMX8MM_AUDIO_PLL1_BYPASS], clks[IMX8MM_AUDIO_PLL1]);
+ clk_set_parent(clks[IMX8MM_AUDIO_PLL2_BYPASS], clks[IMX8MM_AUDIO_PLL2]);
+ clk_set_parent(clks[IMX8MM_VIDEO_PLL1_BYPASS], clks[IMX8MM_VIDEO_PLL1]);
+ clk_set_parent(clks[IMX8MM_DRAM_PLL_BYPASS], clks[IMX8MM_DRAM_PLL]);
+ clk_set_parent(clks[IMX8MM_GPU_PLL_BYPASS], clks[IMX8MM_GPU_PLL]);
+ clk_set_parent(clks[IMX8MM_VPU_PLL_BYPASS], clks[IMX8MM_VPU_PLL]);
+ clk_set_parent(clks[IMX8MM_ARM_PLL_BYPASS], clks[IMX8MM_ARM_PLL]);
+ clk_set_parent(clks[IMX8MM_SYS_PLL1_BYPASS], clks[IMX8MM_SYS_PLL1]);
+ clk_set_parent(clks[IMX8MM_SYS_PLL2_BYPASS], clks[IMX8MM_SYS_PLL2]);
+ clk_set_parent(clks[IMX8MM_SYS_PLL3_BYPASS], clks[IMX8MM_SYS_PLL3]);
+
+ /* PLL out gate */
+ clks[IMX8MM_AUDIO_PLL1_OUT] = imx_clk_gate("audio_pll1_out", "audio_pll1_bypass", base, 13);
+ clks[IMX8MM_AUDIO_PLL2_OUT] = imx_clk_gate("audio_pll2_out", "audio_pll2_bypass", base + 0x14, 13);
+ clks[IMX8MM_VIDEO_PLL1_OUT] = imx_clk_gate("video_pll1_out", "video_pll1_bypass", base + 0x28, 13);
+ clks[IMX8MM_DRAM_PLL_OUT] = imx_clk_gate("dram_pll_out", "dram_pll_bypass", base + 0x50, 13);
+ clks[IMX8MM_GPU_PLL_OUT] = imx_clk_gate("gpu_pll_out", "gpu_pll_bypass", base + 0x64, 13);
+ clks[IMX8MM_VPU_PLL_OUT] = imx_clk_gate("vpu_pll_out", "vpu_pll_bypass", base + 0x74, 13);
+ clks[IMX8MM_ARM_PLL_OUT] = imx_clk_gate("arm_pll_out", "arm_pll_bypass", base + 0x84, 13);
+ clks[IMX8MM_SYS_PLL1_OUT] = imx_clk_gate("sys_pll1_out", "sys_pll1_bypass", base + 0x94, 13);
+ clks[IMX8MM_SYS_PLL2_OUT] = imx_clk_gate("sys_pll2_out", "sys_pll2_bypass", base + 0x104, 13);
+ clks[IMX8MM_SYS_PLL3_OUT] = imx_clk_gate("sys_pll3_out", "sys_pll3_bypass", base + 0x114, 13);
+
+ /* SYS PLL fixed output */
+ clks[IMX8MM_SYS_PLL1_40M] = imx_clk_fixed_factor("sys_pll1_40m", "sys_pll1_out", 1, 20);
+ clks[IMX8MM_SYS_PLL1_80M] = imx_clk_fixed_factor("sys_pll1_80m", "sys_pll1_out", 1, 10);
+ clks[IMX8MM_SYS_PLL1_100M] = imx_clk_fixed_factor("sys_pll1_100m", "sys_pll1_out", 1, 8);
+ clks[IMX8MM_SYS_PLL1_133M] = imx_clk_fixed_factor("sys_pll1_133m", "sys_pll1_out", 1, 6);
+ clks[IMX8MM_SYS_PLL1_160M] = imx_clk_fixed_factor("sys_pll1_160m", "sys_pll1_out", 1, 5);
+ clks[IMX8MM_SYS_PLL1_200M] = imx_clk_fixed_factor("sys_pll1_200m", "sys_pll1_out", 1, 4);
+ clks[IMX8MM_SYS_PLL1_266M] = imx_clk_fixed_factor("sys_pll1_266m", "sys_pll1_out", 1, 3);
+ clks[IMX8MM_SYS_PLL1_400M] = imx_clk_fixed_factor("sys_pll1_400m", "sys_pll1_out", 1, 2);
+ clks[IMX8MM_SYS_PLL1_800M] = imx_clk_fixed_factor("sys_pll1_800m", "sys_pll1_out", 1, 1);
+
+ clks[IMX8MM_SYS_PLL2_50M] = imx_clk_fixed_factor("sys_pll2_50m", "sys_pll2_out", 1, 20);
+ clks[IMX8MM_SYS_PLL2_100M] = imx_clk_fixed_factor("sys_pll2_100m", "sys_pll2_out", 1, 10);
+ clks[IMX8MM_SYS_PLL2_125M] = imx_clk_fixed_factor("sys_pll2_125m", "sys_pll2_out", 1, 8);
+ clks[IMX8MM_SYS_PLL2_166M] = imx_clk_fixed_factor("sys_pll2_166m", "sys_pll2_out", 1, 6);
+ clks[IMX8MM_SYS_PLL2_200M] = imx_clk_fixed_factor("sys_pll2_200m", "sys_pll2_out", 1, 5);
+ clks[IMX8MM_SYS_PLL2_250M] = imx_clk_fixed_factor("sys_pll2_250m", "sys_pll2_out", 1, 4);
+ clks[IMX8MM_SYS_PLL2_333M] = imx_clk_fixed_factor("sys_pll2_333m", "sys_pll2_out", 1, 3);
+ clks[IMX8MM_SYS_PLL2_500M] = imx_clk_fixed_factor("sys_pll2_500m", "sys_pll2_out", 1, 2);
+ clks[IMX8MM_SYS_PLL2_1000M] = imx_clk_fixed_factor("sys_pll2_1000m", "sys_pll2_out", 1, 1);
+
+ np = ccm_node;
+ base = of_iomap(np, 0);
+ if (WARN_ON(!base))
+ return -ENOMEM;
+
+ /* Core Slice */
+ clks[IMX8MM_CLK_A53_SRC] = imx_clk_mux2("arm_a53_src", base + 0x8000, 24, 3, imx8mm_a53_sels, ARRAY_SIZE(imx8mm_a53_sels));
+ clks[IMX8MM_CLK_M4_SRC] = imx_clk_mux2("arm_m4_src", base + 0x8080, 24, 3, imx8mm_m4_sels, ARRAY_SIZE(imx8mm_m4_sels));
+ clks[IMX8MM_CLK_VPU_SRC] = imx_clk_mux2("vpu_src", base + 0x8100, 24, 3, imx8mm_vpu_sels, ARRAY_SIZE(imx8mm_vpu_sels));
+ clks[IMX8MM_CLK_GPU3D_SRC] = imx_clk_mux2("gpu3d_src", base + 0x8180, 24, 3, imx8mm_gpu3d_sels, ARRAY_SIZE(imx8mm_gpu3d_sels));
+ clks[IMX8MM_CLK_GPU2D_SRC] = imx_clk_mux2("gpu2d_src", base + 0x8200, 24, 3, imx8mm_gpu2d_sels, ARRAY_SIZE(imx8mm_gpu2d_sels));
+ clks[IMX8MM_CLK_A53_CG] = imx_clk_gate3("arm_a53_cg", "arm_a53_src", base + 0x8000, 28);
+ clks[IMX8MM_CLK_M4_CG] = imx_clk_gate3("arm_m4_cg", "arm_m4_src", base + 0x8080, 28);
+ clks[IMX8MM_CLK_VPU_CG] = imx_clk_gate3("vpu_cg", "vpu_src", base + 0x8100, 28);
+ clks[IMX8MM_CLK_GPU3D_CG] = imx_clk_gate3("gpu3d_cg", "gpu3d_src", base + 0x8180, 28);
+ clks[IMX8MM_CLK_GPU2D_CG] = imx_clk_gate3("gpu2d_cg", "gpu2d_src", base + 0x8200, 28);
+ clks[IMX8MM_CLK_A53_DIV] = imx_clk_divider2("arm_a53_div", "arm_a53_cg", base + 0x8000, 0, 3);
+ clks[IMX8MM_CLK_M4_DIV] = imx_clk_divider2("arm_m4_div", "arm_m4_cg", base + 0x8080, 0, 3);
+ clks[IMX8MM_CLK_VPU_DIV] = imx_clk_divider2("vpu_div", "vpu_cg", base + 0x8100, 0, 3);
+ clks[IMX8MM_CLK_GPU3D_DIV] = imx_clk_divider2("gpu3d_div", "gpu3d_cg", base + 0x8180, 0, 3);
+ clks[IMX8MM_CLK_GPU2D_DIV] = imx_clk_divider2("gpu2d_div", "gpu2d_cg", base + 0x8200, 0, 3);
+
+ /* BUS */
+ clks[IMX8MM_CLK_MAIN_AXI] = imx8m_clk_composite_critical("main_axi", imx8mm_main_axi_sels, base + 0x8800);
+ clks[IMX8MM_CLK_ENET_AXI] = imx8m_clk_composite("enet_axi", imx8mm_enet_axi_sels, base + 0x8880);
+ clks[IMX8MM_CLK_NAND_USDHC_BUS] = imx8m_clk_composite_critical("nand_usdhc_bus", imx8mm_nand_usdhc_sels, base + 0x8900);
+ clks[IMX8MM_CLK_VPU_BUS] = imx8m_clk_composite("vpu_bus", imx8mm_vpu_bus_sels, base + 0x8980);
+ clks[IMX8MM_CLK_DISP_AXI] = imx8m_clk_composite("disp_axi", imx8mm_disp_axi_sels, base + 0x8a00);
+ clks[IMX8MM_CLK_DISP_APB] = imx8m_clk_composite("disp_apb", imx8mm_disp_apb_sels, base + 0x8a80);
+ clks[IMX8MM_CLK_DISP_RTRM] = imx8m_clk_composite("disp_rtrm", imx8mm_disp_rtrm_sels, base + 0x8b00);
+ clks[IMX8MM_CLK_USB_BUS] = imx8m_clk_composite("usb_bus", imx8mm_usb_bus_sels, base + 0x8b80);
+ clks[IMX8MM_CLK_GPU_AXI] = imx8m_clk_composite("gpu_axi", imx8mm_gpu_axi_sels, base + 0x8c00);
+ clks[IMX8MM_CLK_GPU_AHB] = imx8m_clk_composite("gpu_ahb", imx8mm_gpu_ahb_sels, base + 0x8c80);
+ clks[IMX8MM_CLK_NOC] = imx8m_clk_composite_critical("noc", imx8mm_noc_sels, base + 0x8d00);
+ clks[IMX8MM_CLK_NOC_APB] = imx8m_clk_composite_critical("noc_apb", imx8mm_noc_apb_sels, base + 0x8d80);
+
+ /* AHB */
+ clks[IMX8MM_CLK_AHB] = imx8m_clk_composite_critical("ahb", imx8mm_ahb_sels, base + 0x9000);
+ clks[IMX8MM_CLK_AUDIO_AHB] = imx8m_clk_composite("audio_ahb", imx8mm_audio_ahb_sels, base + 0x9100);
+
+ /* IPG */
+ clks[IMX8MM_CLK_IPG_ROOT] = imx_clk_divider2("ipg_root", "ahb", base + 0x9080, 0, 1);
+ clks[IMX8MM_CLK_IPG_AUDIO_ROOT] = imx_clk_divider2("ipg_audio_root", "audio_ahb", base + 0x9180, 0, 1);
+
+ /* IP */
+ clks[IMX8MM_CLK_DRAM_ALT] = imx8m_clk_composite("dram_alt", imx8mm_dram_alt_sels, base + 0xa000);
+ clks[IMX8MM_CLK_DRAM_APB] = imx8m_clk_composite("dram_apb", imx8mm_dram_apb_sels, base + 0xa080);
+ clks[IMX8MM_CLK_VPU_G1] = imx8m_clk_composite("vpu_g1", imx8mm_vpu_g1_sels, base + 0xa100);
+ clks[IMX8MM_CLK_VPU_G2] = imx8m_clk_composite("vpu_g2", imx8mm_vpu_g2_sels, base + 0xa180);
+ clks[IMX8MM_CLK_DISP_DTRC] = imx8m_clk_composite("disp_dtrc", imx8mm_disp_dtrc_sels, base + 0xa200);
+ clks[IMX8MM_CLK_DISP_DC8000] = imx8m_clk_composite("disp_dc8000", imx8mm_disp_dc8000_sels, base + 0xa280);
+ clks[IMX8MM_CLK_PCIE1_CTRL] = imx8m_clk_composite("pcie1_ctrl", imx8mm_pcie1_ctrl_sels, base + 0xa300);
+ clks[IMX8MM_CLK_PCIE1_PHY] = imx8m_clk_composite("pcie1_phy", imx8mm_pcie1_phy_sels, base + 0xa380);
+ clks[IMX8MM_CLK_PCIE1_AUX] = imx8m_clk_composite("pcie1_aux", imx8mm_pcie1_aux_sels, base + 0xa400);
+ clks[IMX8MM_CLK_DC_PIXEL] = imx8m_clk_composite("dc_pixel", imx8mm_dc_pixel_sels, base + 0xa480);
+ clks[IMX8MM_CLK_LCDIF_PIXEL] = imx8m_clk_composite("lcdif_pixel", imx8mm_lcdif_pixel_sels, base + 0xa500);
+ clks[IMX8MM_CLK_SAI1] = imx8m_clk_composite("sai1", imx8mm_sai1_sels, base + 0xa580);
+ clks[IMX8MM_CLK_SAI2] = imx8m_clk_composite("sai2", imx8mm_sai2_sels, base + 0xa600);
+ clks[IMX8MM_CLK_SAI3] = imx8m_clk_composite("sai3", imx8mm_sai3_sels, base + 0xa680);
+ clks[IMX8MM_CLK_SAI4] = imx8m_clk_composite("sai4", imx8mm_sai4_sels, base + 0xa700);
+ clks[IMX8MM_CLK_SAI5] = imx8m_clk_composite("sai5", imx8mm_sai5_sels, base + 0xa780);
+ clks[IMX8MM_CLK_SAI6] = imx8m_clk_composite("sai6", imx8mm_sai6_sels, base + 0xa800);
+ clks[IMX8MM_CLK_SPDIF1] = imx8m_clk_composite("spdif1", imx8mm_spdif1_sels, base + 0xa880);
+ clks[IMX8MM_CLK_SPDIF2] = imx8m_clk_composite("spdif2", imx8mm_spdif2_sels, base + 0xa900);
+ clks[IMX8MM_CLK_ENET_REF] = imx8m_clk_composite("enet_ref", imx8mm_enet_ref_sels, base + 0xa980);
+ clks[IMX8MM_CLK_ENET_TIMER] = imx8m_clk_composite("enet_timer", imx8mm_enet_timer_sels, base + 0xaa00);
+ clks[IMX8MM_CLK_ENET_PHY_REF] = imx8m_clk_composite("enet_phy", imx8mm_enet_phy_sels, base + 0xaa80);
+ clks[IMX8MM_CLK_NAND] = imx8m_clk_composite("nand", imx8mm_nand_sels, base + 0xab00);
+ clks[IMX8MM_CLK_QSPI] = imx8m_clk_composite("qspi", imx8mm_qspi_sels, base + 0xab80);
+ clks[IMX8MM_CLK_USDHC1] = imx8m_clk_composite("usdhc1", imx8mm_usdhc1_sels, base + 0xac00);
+ clks[IMX8MM_CLK_USDHC2] = imx8m_clk_composite("usdhc2", imx8mm_usdhc2_sels, base + 0xac80);
+ clks[IMX8MM_CLK_I2C1] = imx8m_clk_composite("i2c1", imx8mm_i2c1_sels, base + 0xad00);
+ clks[IMX8MM_CLK_I2C2] = imx8m_clk_composite("i2c2", imx8mm_i2c2_sels, base + 0xad80);
+ clks[IMX8MM_CLK_I2C3] = imx8m_clk_composite("i2c3", imx8mm_i2c3_sels, base + 0xae00);
+ clks[IMX8MM_CLK_I2C4] = imx8m_clk_composite("i2c4", imx8mm_i2c4_sels, base + 0xae80);
+ clks[IMX8MM_CLK_UART1] = imx8m_clk_composite("uart1", imx8mm_uart1_sels, base + 0xaf00);
+ clks[IMX8MM_CLK_UART2] = imx8m_clk_composite("uart2", imx8mm_uart2_sels, base + 0xaf80);
+ clks[IMX8MM_CLK_UART3] = imx8m_clk_composite("uart3", imx8mm_uart3_sels, base + 0xb000);
+ clks[IMX8MM_CLK_UART4] = imx8m_clk_composite("uart4", imx8mm_uart4_sels, base + 0xb080);
+ clks[IMX8MM_CLK_USB_CORE_REF] = imx8m_clk_composite("usb_core_ref", imx8mm_usb_core_sels, base + 0xb100);
+ clks[IMX8MM_CLK_USB_PHY_REF] = imx8m_clk_composite("usb_phy_ref", imx8mm_usb_phy_sels, base + 0xb180);
+ clks[IMX8MM_CLK_ECSPI1] = imx8m_clk_composite("ecspi1", imx8mm_ecspi1_sels, base + 0xb280);
+ clks[IMX8MM_CLK_ECSPI2] = imx8m_clk_composite("ecspi2", imx8mm_ecspi2_sels, base + 0xb300);
+ clks[IMX8MM_CLK_PWM1] = imx8m_clk_composite("pwm1", imx8mm_pwm1_sels, base + 0xb380);
+ clks[IMX8MM_CLK_PWM2] = imx8m_clk_composite("pwm2", imx8mm_pwm2_sels, base + 0xb400);
+ clks[IMX8MM_CLK_PWM3] = imx8m_clk_composite("pwm3", imx8mm_pwm3_sels, base + 0xb480);
+ clks[IMX8MM_CLK_PWM4] = imx8m_clk_composite("pwm4", imx8mm_pwm4_sels, base + 0xb500);
+ clks[IMX8MM_CLK_GPT1] = imx8m_clk_composite("gpt1", imx8mm_gpt1_sels, base + 0xb580);
+ clks[IMX8MM_CLK_WDOG] = imx8m_clk_composite("wdog", imx8mm_wdog_sels, base + 0xb900);
+ clks[IMX8MM_CLK_WRCLK] = imx8m_clk_composite("wrclk", imx8mm_wrclk_sels, base + 0xb980);
+ clks[IMX8MM_CLK_CLKO1] = imx8m_clk_composite("clko1", imx8mm_clko1_sels, base + 0xba00);
+ clks[IMX8MM_CLK_DSI_CORE] = imx8m_clk_composite("dsi_core", imx8mm_dsi_core_sels, base + 0xbb00);
+ clks[IMX8MM_CLK_DSI_PHY_REF] = imx8m_clk_composite("dsi_phy_ref", imx8mm_dsi_phy_sels, base + 0xbb80);
+ clks[IMX8MM_CLK_DSI_DBI] = imx8m_clk_composite("dsi_dbi", imx8mm_dsi_dbi_sels, base + 0xbc00);
+ clks[IMX8MM_CLK_USDHC3] = imx8m_clk_composite("usdhc3", imx8mm_usdhc3_sels, base + 0xbc80);
+ clks[IMX8MM_CLK_CSI1_CORE] = imx8m_clk_composite("csi1_core", imx8mm_csi1_core_sels, base + 0xbd00);
+ clks[IMX8MM_CLK_CSI1_PHY_REF] = imx8m_clk_composite("csi1_phy_ref", imx8mm_csi1_phy_sels, base + 0xbd80);
+ clks[IMX8MM_CLK_CSI1_ESC] = imx8m_clk_composite("csi1_esc", imx8mm_csi1_esc_sels, base + 0xbe00);
+ clks[IMX8MM_CLK_CSI2_CORE] = imx8m_clk_composite("csi2_core", imx8mm_csi2_core_sels, base + 0xbe80);
+ clks[IMX8MM_CLK_CSI2_PHY_REF] = imx8m_clk_composite("csi2_phy_ref", imx8mm_csi2_phy_sels, base + 0xbf00);
+ clks[IMX8MM_CLK_CSI2_ESC] = imx8m_clk_composite("csi2_esc", imx8mm_csi2_esc_sels, base + 0xbf80);
+ clks[IMX8MM_CLK_PCIE2_CTRL] = imx8m_clk_composite("pcie2_ctrl", imx8mm_pcie2_ctrl_sels, base + 0xc000);
+ clks[IMX8MM_CLK_PCIE2_PHY] = imx8m_clk_composite("pcie2_phy", imx8mm_pcie2_phy_sels, base + 0xc080);
+ clks[IMX8MM_CLK_PCIE2_AUX] = imx8m_clk_composite("pcie2_aux", imx8mm_pcie2_aux_sels, base + 0xc100);
+ clks[IMX8MM_CLK_ECSPI3] = imx8m_clk_composite("ecspi3", imx8mm_ecspi3_sels, base + 0xc180);
+ clks[IMX8MM_CLK_PDM] = imx8m_clk_composite("pdm", imx8mm_pdm_sels, base + 0xc200);
+ clks[IMX8MM_CLK_VPU_H1] = imx8m_clk_composite("vpu_h1", imx8mm_vpu_h1_sels, base + 0xc280);
+
+ /* CCGR */
+ clks[IMX8MM_CLK_ECSPI1_ROOT] = imx_clk_gate4("ecspi1_root_clk", "ecspi1", base + 0x4070, 0);
+ clks[IMX8MM_CLK_ECSPI2_ROOT] = imx_clk_gate4("ecspi2_root_clk", "ecspi2", base + 0x4080, 0);
+ clks[IMX8MM_CLK_ECSPI3_ROOT] = imx_clk_gate4("ecspi3_root_clk", "ecspi3", base + 0x4090, 0);
+ clks[IMX8MM_CLK_ENET1_ROOT] = imx_clk_gate4("enet1_root_clk", "enet_axi", base + 0x40a0, 0);
+ clks[IMX8MM_CLK_GPT1_ROOT] = imx_clk_gate4("gpt1_root_clk", "gpt1", base + 0x4100, 0);
+ clks[IMX8MM_CLK_I2C1_ROOT] = imx_clk_gate4("i2c1_root_clk", "i2c1", base + 0x4170, 0);
+ clks[IMX8MM_CLK_I2C2_ROOT] = imx_clk_gate4("i2c2_root_clk", "i2c2", base + 0x4180, 0);
+ clks[IMX8MM_CLK_I2C3_ROOT] = imx_clk_gate4("i2c3_root_clk", "i2c3", base + 0x4190, 0);
+ clks[IMX8MM_CLK_I2C4_ROOT] = imx_clk_gate4("i2c4_root_clk", "i2c4", base + 0x41a0, 0);
+ clks[IMX8MM_CLK_MU_ROOT] = imx_clk_gate4("mu_root_clk", "ipg_root", base + 0x4210, 0);
+ clks[IMX8MM_CLK_OCOTP_ROOT] = imx_clk_gate4("ocotp_root_clk", "ipg_root", base + 0x4220, 0);
+ clks[IMX8MM_CLK_PCIE1_ROOT] = imx_clk_gate4("pcie1_root_clk", "pcie1_ctrl", base + 0x4250, 0);
+ clks[IMX8MM_CLK_PWM1_ROOT] = imx_clk_gate4("pwm1_root_clk", "pwm1", base + 0x4280, 0);
+ clks[IMX8MM_CLK_PWM2_ROOT] = imx_clk_gate4("pwm2_root_clk", "pwm2", base + 0x4290, 0);
+ clks[IMX8MM_CLK_PWM3_ROOT] = imx_clk_gate4("pwm3_root_clk", "pwm3", base + 0x42a0, 0);
+ clks[IMX8MM_CLK_PWM4_ROOT] = imx_clk_gate4("pwm4_root_clk", "pwm4", base + 0x42b0, 0);
+ clks[IMX8MM_CLK_QSPI_ROOT] = imx_clk_gate4("qspi_root_clk", "qspi", base + 0x42f0, 0);
+ clks[IMX8MM_CLK_NAND_ROOT] = imx_clk_gate2_shared2("nand_root_clk", "nand", base + 0x4300, 0, &share_count_nand);
+ clks[IMX8MM_CLK_NAND_USDHC_BUS_RAWNAND_CLK] = imx_clk_gate2_shared2("nand_usdhc_rawnand_clk", "nand_usdhc_bus", base + 0x4300, 0, &share_count_nand);
+ clks[IMX8MM_CLK_SAI1_ROOT] = imx_clk_gate2_shared2("sai1_root_clk", "sai1", base + 0x4330, 0, &share_count_sai1);
+ clks[IMX8MM_CLK_SAI1_IPG] = imx_clk_gate2_shared2("sai1_ipg_clk", "ipg_audio_root", base + 0x4330, 0, &share_count_sai1);
+ clks[IMX8MM_CLK_SAI2_ROOT] = imx_clk_gate2_shared2("sai2_root_clk", "sai2", base + 0x4340, 0, &share_count_sai2);
+ clks[IMX8MM_CLK_SAI2_IPG] = imx_clk_gate2_shared2("sai2_ipg_clk", "ipg_audio_root", base + 0x4340, 0, &share_count_sai2);
+ clks[IMX8MM_CLK_SAI3_ROOT] = imx_clk_gate2_shared2("sai3_root_clk", "sai3", base + 0x4350, 0, &share_count_sai3);
+ clks[IMX8MM_CLK_SAI3_IPG] = imx_clk_gate2_shared2("sai3_ipg_clk", "ipg_audio_root", base + 0x4350, 0, &share_count_sai3);
+ clks[IMX8MM_CLK_SAI4_ROOT] = imx_clk_gate2_shared2("sai4_root_clk", "sai4", base + 0x4360, 0, &share_count_sai4);
+ clks[IMX8MM_CLK_SAI4_IPG] = imx_clk_gate2_shared2("sai4_ipg_clk", "ipg_audio_root", base + 0x4360, 0, &share_count_sai4);
+ clks[IMX8MM_CLK_SAI5_ROOT] = imx_clk_gate2_shared2("sai5_root_clk", "sai5", base + 0x4370, 0, &share_count_sai5);
+ clks[IMX8MM_CLK_SAI5_IPG] = imx_clk_gate2_shared2("sai5_ipg_clk", "ipg_audio_root", base + 0x4370, 0, &share_count_sai5);
+ clks[IMX8MM_CLK_SAI6_ROOT] = imx_clk_gate2_shared2("sai6_root_clk", "sai6", base + 0x4380, 0, &share_count_sai6);
+ clks[IMX8MM_CLK_SAI6_IPG] = imx_clk_gate2_shared2("sai6_ipg_clk", "ipg_audio_root", base + 0x4380, 0, &share_count_sai6);
+ clks[IMX8MM_CLK_UART1_ROOT] = imx_clk_gate4("uart1_root_clk", "uart1", base + 0x4490, 0);
+ clks[IMX8MM_CLK_UART2_ROOT] = imx_clk_gate4("uart2_root_clk", "uart2", base + 0x44a0, 0);
+ clks[IMX8MM_CLK_UART3_ROOT] = imx_clk_gate4("uart3_root_clk", "uart3", base + 0x44b0, 0);
+ clks[IMX8MM_CLK_UART4_ROOT] = imx_clk_gate4("uart4_root_clk", "uart4", base + 0x44c0, 0);
+ clks[IMX8MM_CLK_USB1_CTRL_ROOT] = imx_clk_gate4("usb1_ctrl_root_clk", "usb_core_ref", base + 0x44d0, 0);
+ clks[IMX8MM_CLK_GPU3D_ROOT] = imx_clk_gate4("gpu3d_root_clk", "gpu3d_div", base + 0x44f0, 0);
+ clks[IMX8MM_CLK_USDHC1_ROOT] = imx_clk_gate4("usdhc1_root_clk", "usdhc1", base + 0x4510, 0);
+ clks[IMX8MM_CLK_USDHC2_ROOT] = imx_clk_gate4("usdhc2_root_clk", "usdhc2", base + 0x4520, 0);
+ clks[IMX8MM_CLK_WDOG1_ROOT] = imx_clk_gate4("wdog1_root_clk", "wdog", base + 0x4530, 0);
+ clks[IMX8MM_CLK_WDOG2_ROOT] = imx_clk_gate4("wdog2_root_clk", "wdog", base + 0x4540, 0);
+ clks[IMX8MM_CLK_WDOG3_ROOT] = imx_clk_gate4("wdog3_root_clk", "wdog", base + 0x4550, 0);
+ clks[IMX8MM_CLK_VPU_G1_ROOT] = imx_clk_gate4("vpu_g1_root_clk", "vpu_g1", base + 0x4560, 0);
+ clks[IMX8MM_CLK_GPU_BUS_ROOT] = imx_clk_gate4("gpu_root_clk", "gpu_axi", base + 0x4570, 0);
+ clks[IMX8MM_CLK_VPU_H1_ROOT] = imx_clk_gate4("vpu_h1_root_clk", "vpu_h1", base + 0x4590, 0);
+ clks[IMX8MM_CLK_VPU_G2_ROOT] = imx_clk_gate4("vpu_g2_root_clk", "vpu_g2", base + 0x45a0, 0);
+ clks[IMX8MM_CLK_PDM_ROOT] = imx_clk_gate2_shared2("pdm_root_clk", "pdm", base + 0x45b0, 0, &share_count_pdm);
+ clks[IMX8MM_CLK_PDM_IPG] = imx_clk_gate2_shared2("pdm_ipg_clk", "ipg_audio_root", base + 0x45b0, 0, &share_count_pdm);
+ clks[IMX8MM_CLK_DISP_ROOT] = imx_clk_gate2_shared2("disp_root_clk", "disp_dc8000", base + 0x45d0, 0, &share_count_dcss);
+ clks[IMX8MM_CLK_DISP_AXI_ROOT] = imx_clk_gate2_shared2("disp_axi_root_clk", "disp_axi", base + 0x45d0, 0, &share_count_dcss);
+ clks[IMX8MM_CLK_DISP_APB_ROOT] = imx_clk_gate2_shared2("disp_apb_root_clk", "disp_apb", base + 0x45d0, 0, &share_count_dcss);
+ clks[IMX8MM_CLK_DISP_RTRM_ROOT] = imx_clk_gate2_shared2("disp_rtrm_root_clk", "disp_rtrm", base + 0x45d0, 0, &share_count_dcss);
+ clks[IMX8MM_CLK_USDHC3_ROOT] = imx_clk_gate4("usdhc3_root_clk", "usdhc3", base + 0x45e0, 0);
+ clks[IMX8MM_CLK_TMU_ROOT] = imx_clk_gate4("tmu_root_clk", "ipg_root", base + 0x4620, 0);
+ clks[IMX8MM_CLK_VPU_DEC_ROOT] = imx_clk_gate4("vpu_dec_root_clk", "vpu_bus", base + 0x4630, 0);
+ clks[IMX8MM_CLK_SDMA1_ROOT] = imx_clk_gate4("sdma1_clk", "ipg_root", base + 0x43a0, 0);
+ clks[IMX8MM_CLK_SDMA2_ROOT] = imx_clk_gate4("sdma2_clk", "ipg_audio_root", base + 0x43b0, 0);
+ clks[IMX8MM_CLK_SDMA3_ROOT] = imx_clk_gate4("sdma3_clk", "ipg_audio_root", base + 0x45f0, 0);
+ clks[IMX8MM_CLK_GPU2D_ROOT] = imx_clk_gate4("gpu2d_root_clk", "gpu2d_div", base + 0x4660, 0);
+ clks[IMX8MM_CLK_CSI1_ROOT] = imx_clk_gate4("csi1_root_clk", "csi1_core", base + 0x4650, 0);
+
+ clks[IMX8MM_CLK_GPT_3M] = imx_clk_fixed_factor("gpt_3m", "osc_24m", 1, 8);
+
+ clks[IMX8MM_CLK_DRAM_ALT_ROOT] = imx_clk_fixed_factor("dram_alt_root", "dram_alt", 1, 4);
+ clks[IMX8MM_CLK_DRAM_CORE] = imx_clk_mux2_flags("dram_core_clk", base + 0x9800, 24, 1, imx8mm_dram_core_sels, ARRAY_SIZE(imx8mm_dram_core_sels), CLK_IS_CRITICAL);
+
+ clks[IMX8MM_CLK_ARM] = imx_clk_cpu("arm", "arm_a53_div",
+ clks[IMX8MM_CLK_A53_DIV],
+ clks[IMX8MM_CLK_A53_SRC],
+ clks[IMX8MM_ARM_PLL_OUT],
+ clks[IMX8MM_CLK_24M]);
+
+ imx_check_clocks(clks, ARRAY_SIZE(clks));
+
+ clk_data.clks = clks;
+ clk_data.clk_num = ARRAY_SIZE(clks);
+ ret = of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
+ if (ret < 0) {
+ pr_err("failed to register clks for i.MX8MM\n");
+ return -EINVAL;
+ }
+
+ imx_register_uart_clocks(uart_clks);
+
+ return 0;
+}
+CLK_OF_DECLARE_DRIVER(imx8mm, "fsl,imx8mm-ccm", imx8mm_clocks_init);
diff --git a/drivers/clk/imx/clk-imx8mq.c b/drivers/clk/imx/clk-imx8mq.c
index 26b57f43ccc3..a9b3888aef0c 100644
--- a/drivers/clk/imx/clk-imx8mq.c
+++ b/drivers/clk/imx/clk-imx8mq.c
@@ -26,246 +26,246 @@ static u32 share_count_nand;
static struct clk *clks[IMX8MQ_CLK_END];
-static const char *pll_ref_sels[] = { "osc_25m", "osc_27m", "dummy", "dummy", };
-static const char *arm_pll_bypass_sels[] = {"arm_pll", "arm_pll_ref_sel", };
-static const char *gpu_pll_bypass_sels[] = {"gpu_pll", "gpu_pll_ref_sel", };
-static const char *vpu_pll_bypass_sels[] = {"vpu_pll", "vpu_pll_ref_sel", };
-static const char *audio_pll1_bypass_sels[] = {"audio_pll1", "audio_pll1_ref_sel", };
-static const char *audio_pll2_bypass_sels[] = {"audio_pll2", "audio_pll2_ref_sel", };
-static const char *video_pll1_bypass_sels[] = {"video_pll1", "video_pll1_ref_sel", };
-
-static const char *sys1_pll1_out_sels[] = {"sys1_pll1", "sys1_pll1_ref_sel", };
-static const char *sys2_pll1_out_sels[] = {"sys2_pll1", "sys1_pll1_ref_sel", };
-static const char *sys3_pll1_out_sels[] = {"sys3_pll1", "sys3_pll1_ref_sel", };
-static const char *dram_pll1_out_sels[] = {"dram_pll1", "dram_pll1_ref_sel", };
-
-static const char *sys1_pll2_out_sels[] = {"sys1_pll2_div", "sys1_pll1_ref_sel", };
-static const char *sys2_pll2_out_sels[] = {"sys2_pll2_div", "sys2_pll1_ref_sel", };
-static const char *sys3_pll2_out_sels[] = {"sys3_pll2_div", "sys2_pll1_ref_sel", };
-static const char *dram_pll2_out_sels[] = {"dram_pll2_div", "dram_pll1_ref_sel", };
+static const char * const pll_ref_sels[] = { "osc_25m", "osc_27m", "dummy", "dummy", };
+static const char * const arm_pll_bypass_sels[] = {"arm_pll", "arm_pll_ref_sel", };
+static const char * const gpu_pll_bypass_sels[] = {"gpu_pll", "gpu_pll_ref_sel", };
+static const char * const vpu_pll_bypass_sels[] = {"vpu_pll", "vpu_pll_ref_sel", };
+static const char * const audio_pll1_bypass_sels[] = {"audio_pll1", "audio_pll1_ref_sel", };
+static const char * const audio_pll2_bypass_sels[] = {"audio_pll2", "audio_pll2_ref_sel", };
+static const char * const video_pll1_bypass_sels[] = {"video_pll1", "video_pll1_ref_sel", };
+
+static const char * const sys1_pll_out_sels[] = {"sys1_pll1_ref_sel", };
+static const char * const sys2_pll_out_sels[] = {"sys1_pll1_ref_sel", "sys2_pll1_ref_sel", };
+static const char * const sys3_pll_out_sels[] = {"sys3_pll1_ref_sel", "sys2_pll1_ref_sel", };
+static const char * const dram_pll_out_sels[] = {"dram_pll1_ref_sel", };
/* CCM ROOT */
-static const char *imx8mq_a53_sels[] = {"osc_25m", "arm_pll_out", "sys2_pll_500m", "sys2_pll_1000m",
+static const char * const imx8mq_a53_sels[] = {"osc_25m", "arm_pll_out", "sys2_pll_500m", "sys2_pll_1000m",
"sys1_pll_800m", "sys1_pll_400m", "audio_pll1_out", "sys3_pll2_out", };
-static const char *imx8mq_vpu_sels[] = {"osc_25m", "arm_pll_out", "sys2_pll_500m", "sys2_pll_1000m",
+static const char * const imx8mq_arm_m4_sels[] = {"osc_25m", "sys2_pll_200m", "sys2_pll_250m", "sys1_pll_266m",
+ "sys1_pll_800m", "audio_pll1_out", "video_pll1_out", "sys3_pll2_out", };
+
+static const char * const imx8mq_vpu_sels[] = {"osc_25m", "arm_pll_out", "sys2_pll_500m", "sys2_pll_1000m",
"sys1_pll_800m", "sys1_pll_400m", "audio_pll1_out", "vpu_pll_out", };
-static const char *imx8mq_gpu_core_sels[] = {"osc_25m", "gpu_pll_out", "sys1_pll_800m", "sys3_pll2_out",
+static const char * const imx8mq_gpu_core_sels[] = {"osc_25m", "gpu_pll_out", "sys1_pll_800m", "sys3_pll2_out",
"sys2_pll_1000m", "audio_pll1_out", "video_pll1_out", "audio_pll2_out", };
-static const char *imx8mq_gpu_shader_sels[] = {"osc_25m", "gpu_pll_out", "sys1_pll_800m", "sys3_pll2_out",
+static const char * const imx8mq_gpu_shader_sels[] = {"osc_25m", "gpu_pll_out", "sys1_pll_800m", "sys3_pll2_out",
"sys2_pll_1000m", "audio_pll1_out", "video_pll1_out", "audio_pll2_out", };
-static const char *imx8mq_main_axi_sels[] = {"osc_25m", "sys2_pll_333m", "sys1_pll_800m", "sys2_pll_250m",
+static const char * const imx8mq_main_axi_sels[] = {"osc_25m", "sys2_pll_333m", "sys1_pll_800m", "sys2_pll_250m",
"sys2_pll_1000m", "audio_pll1_out", "video_pll1_out", "sys1_pll_100m",};
-static const char *imx8mq_enet_axi_sels[] = {"osc_25m", "sys1_pll_266m", "sys1_pll_800m", "sys2_pll_250m",
+static const char * const imx8mq_enet_axi_sels[] = {"osc_25m", "sys1_pll_266m", "sys1_pll_800m", "sys2_pll_250m",
"sys2_pll_200m", "audio_pll1_out", "video_pll1_out", "sys3_pll2_out", };
-static const char *imx8mq_nand_usdhc_sels[] = {"osc_25m", "sys1_pll_266m", "sys1_pll_800m", "sys2_pll_200m",
+static const char * const imx8mq_nand_usdhc_sels[] = {"osc_25m", "sys1_pll_266m", "sys1_pll_800m", "sys2_pll_200m",
"sys1_pll_133m", "sys3_pll2_out", "sys2_pll_250m", "audio_pll1_out", };
-static const char *imx8mq_vpu_bus_sels[] = {"osc_25m", "sys1_pll_800m", "vpu_pll_out", "audio_pll2_out", "sys3_pll2_out", "sys2_pll_1000m", "sys2_pll_200m", "sys1_pll_100m", };
+static const char * const imx8mq_vpu_bus_sels[] = {"osc_25m", "sys1_pll_800m", "vpu_pll_out", "audio_pll2_out", "sys3_pll2_out", "sys2_pll_1000m", "sys2_pll_200m", "sys1_pll_100m", };
-static const char *imx8mq_disp_axi_sels[] = {"osc_25m", "sys2_pll_125m", "sys1_pll_800m", "sys3_pll2_out", "sys1_pll_400m", "audio_pll2_out", "clk_ext1", "clk_ext4", };
+static const char * const imx8mq_disp_axi_sels[] = {"osc_25m", "sys2_pll_125m", "sys1_pll_800m", "sys3_pll2_out", "sys1_pll_400m", "audio_pll2_out", "clk_ext1", "clk_ext4", };
-static const char *imx8mq_disp_apb_sels[] = {"osc_25m", "sys2_pll_125m", "sys1_pll_800m", "sys3_pll2_out",
+static const char * const imx8mq_disp_apb_sels[] = {"osc_25m", "sys2_pll_125m", "sys1_pll_800m", "sys3_pll2_out",
"sys1_pll_40m", "audio_pll2_out", "clk_ext1", "clk_ext3", };
-static const char *imx8mq_disp_rtrm_sels[] = {"osc_25m", "sys1_pll_800m", "sys2_pll_200m", "sys1_pll_400m",
+static const char * const imx8mq_disp_rtrm_sels[] = {"osc_25m", "sys1_pll_800m", "sys2_pll_200m", "sys1_pll_400m",
"audio_pll1_out", "video_pll1_out", "clk_ext2", "clk_ext3", };
-static const char *imx8mq_usb_bus_sels[] = {"osc_25m", "sys2_pll_500m", "sys1_pll_800m", "sys2_pll_100m",
+static const char * const imx8mq_usb_bus_sels[] = {"osc_25m", "sys2_pll_500m", "sys1_pll_800m", "sys2_pll_100m",
"sys2_pll_200m", "clk_ext2", "clk_ext4", "audio_pll2_out", };
-static const char *imx8mq_gpu_axi_sels[] = {"osc_25m", "sys1_pll_800m", "gpu_pll_out", "sys3_pll2_out", "sys2_pll_1000m",
+static const char * const imx8mq_gpu_axi_sels[] = {"osc_25m", "sys1_pll_800m", "gpu_pll_out", "sys3_pll2_out", "sys2_pll_1000m",
"audio_pll1_out", "video_pll1_out", "audio_pll2_out", };
-static const char *imx8mq_gpu_ahb_sels[] = {"osc_25m", "sys1_pll_800m", "gpu_pll_out", "sys3_pll2_out", "sys2_pll_1000m",
+static const char * const imx8mq_gpu_ahb_sels[] = {"osc_25m", "sys1_pll_800m", "gpu_pll_out", "sys3_pll2_out", "sys2_pll_1000m",
"audio_pll1_out", "video_pll1_out", "audio_pll2_out", };
-static const char *imx8mq_noc_sels[] = {"osc_25m", "sys1_pll_800m", "sys3_pll2_out", "sys2_pll_1000m", "sys2_pll_500m",
+static const char * const imx8mq_noc_sels[] = {"osc_25m", "sys1_pll_800m", "sys3_pll2_out", "sys2_pll_1000m", "sys2_pll_500m",
"audio_pll1_out", "video_pll1_out", "audio_pll2_out", };
-static const char *imx8mq_noc_apb_sels[] = {"osc_25m", "sys1_pll_400m", "sys3_pll2_out", "sys2_pll_333m", "sys2_pll_200m",
+static const char * const imx8mq_noc_apb_sels[] = {"osc_25m", "sys1_pll_400m", "sys3_pll2_out", "sys2_pll_333m", "sys2_pll_200m",
"sys1_pll_800m", "audio_pll1_out", "video_pll1_out", };
-static const char *imx8mq_ahb_sels[] = {"osc_25m", "sys1_pll_133m", "sys1_pll_800m", "sys1_pll_400m",
+static const char * const imx8mq_ahb_sels[] = {"osc_25m", "sys1_pll_133m", "sys1_pll_800m", "sys1_pll_400m",
"sys2_pll_125m", "sys3_pll2_out", "audio_pll1_out", "video_pll1_out", };
-static const char *imx8mq_audio_ahb_sels[] = {"osc_25m", "sys2_pll_500m", "sys1_pll_800m", "sys2_pll_1000m",
+static const char * const imx8mq_audio_ahb_sels[] = {"osc_25m", "sys2_pll_500m", "sys1_pll_800m", "sys2_pll_1000m",
"sys2_pll_166m", "sys3_pll2_out", "audio_pll1_out", "video_pll1_out", };
-static const char *imx8mq_dsi_ahb_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_80m", "sys1_pll_800m",
+static const char * const imx8mq_dsi_ahb_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_80m", "sys1_pll_800m",
"sys2_pll_1000m", "sys3_pll2_out", "clk_ext3", "audio_pll2_out"};
-static const char *imx8mq_dram_alt_sels[] = {"osc_25m", "sys1_pll_800m", "sys1_pll_100m", "sys2_pll_500m",
+static const char * const imx8mq_dram_alt_sels[] = {"osc_25m", "sys1_pll_800m", "sys1_pll_100m", "sys2_pll_500m",
"sys2_pll_250m", "sys1_pll_400m", "audio_pll1_out", "sys1_pll_266m", };
-static const char *imx8mq_dram_apb_sels[] = {"osc_25m", "sys2_pll_200m", "sys1_pll_40m", "sys1_pll_160m",
+static const char * const imx8mq_dram_apb_sels[] = {"osc_25m", "sys2_pll_200m", "sys1_pll_40m", "sys1_pll_160m",
"sys1_pll_800m", "sys3_pll2_out", "sys2_pll_250m", "audio_pll2_out", };
-static const char *imx8mq_vpu_g1_sels[] = {"osc_25m", "vpu_pll_out", "sys1_pll_800m", "sys2_pll_1000m", "sys1_pll_100m", "sys2_pll_125m", "sys3_pll2_out", "audio_pll1_out", };
+static const char * const imx8mq_vpu_g1_sels[] = {"osc_25m", "vpu_pll_out", "sys1_pll_800m", "sys2_pll_1000m", "sys1_pll_100m", "sys2_pll_125m", "sys3_pll2_out", "audio_pll1_out", };
-static const char *imx8mq_vpu_g2_sels[] = {"osc_25m", "vpu_pll_out", "sys1_pll_800m", "sys2_pll_1000m", "sys1_pll_100m", "sys2_pll_125m", "sys3_pll2_out", "audio_pll1_out", };
+static const char * const imx8mq_vpu_g2_sels[] = {"osc_25m", "vpu_pll_out", "sys1_pll_800m", "sys2_pll_1000m", "sys1_pll_100m", "sys2_pll_125m", "sys3_pll2_out", "audio_pll1_out", };
-static const char *imx8mq_disp_dtrc_sels[] = {"osc_25m", "vpu_pll_out", "sys1_pll_800m", "sys2_pll_1000m", "sys1_pll_160m", "sys2_pll_100m", "sys3_pll2_out", "audio_pll2_out", };
+static const char * const imx8mq_disp_dtrc_sels[] = {"osc_25m", "vpu_pll_out", "sys1_pll_800m", "sys2_pll_1000m", "sys1_pll_160m", "sys2_pll_100m", "sys3_pll2_out", "audio_pll2_out", };
-static const char *imx8mq_disp_dc8000_sels[] = {"osc_25m", "vpu_pll_out", "sys1_pll_800m", "sys2_pll_1000m", "sys1_pll_160m", "sys2_pll_100m", "sys3_pll2_out", "audio_pll2_out", };
+static const char * const imx8mq_disp_dc8000_sels[] = {"osc_25m", "vpu_pll_out", "sys1_pll_800m", "sys2_pll_1000m", "sys1_pll_160m", "sys2_pll_100m", "sys3_pll2_out", "audio_pll2_out", };
-static const char *imx8mq_pcie1_ctrl_sels[] = {"osc_25m", "sys2_pll_250m", "sys2_pll_200m", "sys1_pll_266m",
+static const char * const imx8mq_pcie1_ctrl_sels[] = {"osc_25m", "sys2_pll_250m", "sys2_pll_200m", "sys1_pll_266m",
"sys1_pll_800m", "sys2_pll_500m", "sys2_pll_250m", "sys3_pll2_out", };
-static const char *imx8mq_pcie1_phy_sels[] = {"osc_25m", "sys2_pll_100m", "sys2_pll_500m", "clk_ext1", "clk_ext2",
+static const char * const imx8mq_pcie1_phy_sels[] = {"osc_25m", "sys2_pll_100m", "sys2_pll_500m", "clk_ext1", "clk_ext2",
"clk_ext3", "clk_ext4", };
-static const char *imx8mq_pcie1_aux_sels[] = {"osc_25m", "sys2_pll_200m", "sys2_pll_500m", "sys3_pll2_out",
+static const char * const imx8mq_pcie1_aux_sels[] = {"osc_25m", "sys2_pll_200m", "sys2_pll_500m", "sys3_pll2_out",
"sys2_pll_100m", "sys1_pll_80m", "sys1_pll_160m", "sys1_pll_200m", };
-static const char *imx8mq_dc_pixel_sels[] = {"osc_25m", "video_pll1_out", "audio_pll2_out", "audio_pll1_out", "sys1_pll_800m", "sys2_pll_1000m", "sys3_pll2_out", "clk_ext4", };
+static const char * const imx8mq_dc_pixel_sels[] = {"osc_25m", "video_pll1_out", "audio_pll2_out", "audio_pll1_out", "sys1_pll_800m", "sys2_pll_1000m", "sys3_pll2_out", "clk_ext4", };
-static const char *imx8mq_lcdif_pixel_sels[] = {"osc_25m", "video_pll1_out", "audio_pll2_out", "audio_pll1_out", "sys1_pll_800m", "sys2_pll_1000m", "sys3_pll2_out", "clk_ext4", };
+static const char * const imx8mq_lcdif_pixel_sels[] = {"osc_25m", "video_pll1_out", "audio_pll2_out", "audio_pll1_out", "sys1_pll_800m", "sys2_pll_1000m", "sys3_pll2_out", "clk_ext4", };
-static const char *imx8mq_sai1_sels[] = {"osc_25m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", "sys1_pll_133m", "osc_27m", "clk_ext1", "clk_ext2", };
+static const char * const imx8mq_sai1_sels[] = {"osc_25m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", "sys1_pll_133m", "osc_27m", "clk_ext1", "clk_ext2", };
-static const char *imx8mq_sai2_sels[] = {"osc_25m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", "sys1_pll_133m", "osc_27m", "clk_ext2", "clk_ext3", };
+static const char * const imx8mq_sai2_sels[] = {"osc_25m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", "sys1_pll_133m", "osc_27m", "clk_ext2", "clk_ext3", };
-static const char *imx8mq_sai3_sels[] = {"osc_25m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", "sys1_pll_133m", "osc_27m", "clk_ext3", "clk_ext4", };
+static const char * const imx8mq_sai3_sels[] = {"osc_25m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", "sys1_pll_133m", "osc_27m", "clk_ext3", "clk_ext4", };
-static const char *imx8mq_sai4_sels[] = {"osc_25m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", "sys1_pll_133m", "osc_27m", "clk_ext1", "clk_ext2", };
+static const char * const imx8mq_sai4_sels[] = {"osc_25m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", "sys1_pll_133m", "osc_27m", "clk_ext1", "clk_ext2", };
-static const char *imx8mq_sai5_sels[] = {"osc_25m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", "sys1_pll_133m", "osc_27m", "clk_ext2", "clk_ext3", };
+static const char * const imx8mq_sai5_sels[] = {"osc_25m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", "sys1_pll_133m", "osc_27m", "clk_ext2", "clk_ext3", };
-static const char *imx8mq_sai6_sels[] = {"osc_25m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", "sys1_pll_133m", "osc_27m", "clk_ext3", "clk_ext4", };
+static const char * const imx8mq_sai6_sels[] = {"osc_25m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", "sys1_pll_133m", "osc_27m", "clk_ext3", "clk_ext4", };
-static const char *imx8mq_spdif1_sels[] = {"osc_25m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", "sys1_pll_133m", "osc_27m", "clk_ext2", "clk_ext3", };
+static const char * const imx8mq_spdif1_sels[] = {"osc_25m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", "sys1_pll_133m", "osc_27m", "clk_ext2", "clk_ext3", };
-static const char *imx8mq_spdif2_sels[] = {"osc_25m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", "sys1_pll_133m", "osc_27m", "clk_ext3", "clk_ext4", };
+static const char * const imx8mq_spdif2_sels[] = {"osc_25m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", "sys1_pll_133m", "osc_27m", "clk_ext3", "clk_ext4", };
-static const char *imx8mq_enet_ref_sels[] = {"osc_25m", "sys2_pll_125m", "sys2_pll_500m", "sys2_pll_100m",
+static const char * const imx8mq_enet_ref_sels[] = {"osc_25m", "sys2_pll_125m", "sys2_pll_500m", "sys2_pll_100m",
"sys1_pll_160m", "audio_pll1_out", "video_pll1_out", "clk_ext4", };
-static const char *imx8mq_enet_timer_sels[] = {"osc_25m", "sys2_pll_100m", "audio_pll1_out", "clk_ext1", "clk_ext2",
+static const char * const imx8mq_enet_timer_sels[] = {"osc_25m", "sys2_pll_100m", "audio_pll1_out", "clk_ext1", "clk_ext2",
"clk_ext3", "clk_ext4", "video_pll1_out", };
-static const char *imx8mq_enet_phy_sels[] = {"osc_25m", "sys2_pll_50m", "sys2_pll_125m", "sys2_pll_500m",
+static const char * const imx8mq_enet_phy_sels[] = {"osc_25m", "sys2_pll_50m", "sys2_pll_125m", "sys2_pll_500m",
"audio_pll1_out", "video_pll1_out", "audio_pll2_out", };
-static const char *imx8mq_nand_sels[] = {"osc_25m", "sys2_pll_500m", "audio_pll1_out", "sys1_pll_400m",
+static const char * const imx8mq_nand_sels[] = {"osc_25m", "sys2_pll_500m", "audio_pll1_out", "sys1_pll_400m",
"audio_pll2_out", "sys3_pll2_out", "sys2_pll_250m", "video_pll1_out", };
-static const char *imx8mq_qspi_sels[] = {"osc_25m", "sys1_pll_400m", "sys1_pll_800m", "sys2_pll_500m",
+static const char * const imx8mq_qspi_sels[] = {"osc_25m", "sys1_pll_400m", "sys1_pll_800m", "sys2_pll_500m",
"audio_pll2_out", "sys1_pll_266m", "sys3_pll2_out", "sys1_pll_100m", };
-static const char *imx8mq_usdhc1_sels[] = {"osc_25m", "sys1_pll_400m", "sys1_pll_800m", "sys2_pll_500m",
+static const char * const imx8mq_usdhc1_sels[] = {"osc_25m", "sys1_pll_400m", "sys1_pll_800m", "sys2_pll_500m",
"audio_pll2_out", "sys1_pll_266m", "sys3_pll2_out", "sys1_pll_100m", };
-static const char *imx8mq_usdhc2_sels[] = {"osc_25m", "sys1_pll_400m", "sys1_pll_800m", "sys2_pll_500m",
+static const char * const imx8mq_usdhc2_sels[] = {"osc_25m", "sys1_pll_400m", "sys1_pll_800m", "sys2_pll_500m",
"audio_pll2_out", "sys1_pll_266m", "sys3_pll2_out", "sys1_pll_100m", };
-static const char *imx8mq_i2c1_sels[] = {"osc_25m", "sys1_pll_160m", "sys2_pll_50m", "sys3_pll2_out", "audio_pll1_out",
+static const char * const imx8mq_i2c1_sels[] = {"osc_25m", "sys1_pll_160m", "sys2_pll_50m", "sys3_pll2_out", "audio_pll1_out",
"video_pll1_out", "audio_pll2_out", "sys1_pll_133m", };
-static const char *imx8mq_i2c2_sels[] = {"osc_25m", "sys1_pll_160m", "sys2_pll_50m", "sys3_pll2_out", "audio_pll1_out",
+static const char * const imx8mq_i2c2_sels[] = {"osc_25m", "sys1_pll_160m", "sys2_pll_50m", "sys3_pll2_out", "audio_pll1_out",
"video_pll1_out", "audio_pll2_out", "sys1_pll_133m", };
-static const char *imx8mq_i2c3_sels[] = {"osc_25m", "sys1_pll_160m", "sys2_pll_50m", "sys3_pll2_out", "audio_pll1_out",
+static const char * const imx8mq_i2c3_sels[] = {"osc_25m", "sys1_pll_160m", "sys2_pll_50m", "sys3_pll2_out", "audio_pll1_out",
"video_pll1_out", "audio_pll2_out", "sys1_pll_133m", };
-static const char *imx8mq_i2c4_sels[] = {"osc_25m", "sys1_pll_160m", "sys2_pll_50m", "sys3_pll2_out", "audio_pll1_out",
+static const char * const imx8mq_i2c4_sels[] = {"osc_25m", "sys1_pll_160m", "sys2_pll_50m", "sys3_pll2_out", "audio_pll1_out",
"video_pll1_out", "audio_pll2_out", "sys1_pll_133m", };
-static const char *imx8mq_uart1_sels[] = {"osc_25m", "sys1_pll_80m", "sys2_pll_200m", "sys2_pll_100m",
+static const char * const imx8mq_uart1_sels[] = {"osc_25m", "sys1_pll_80m", "sys2_pll_200m", "sys2_pll_100m",
"sys3_pll2_out", "clk_ext2", "clk_ext4", "audio_pll2_out", };
-static const char *imx8mq_uart2_sels[] = {"osc_25m", "sys1_pll_80m", "sys2_pll_200m", "sys2_pll_100m",
+static const char * const imx8mq_uart2_sels[] = {"osc_25m", "sys1_pll_80m", "sys2_pll_200m", "sys2_pll_100m",
"sys3_pll2_out", "clk_ext2", "clk_ext3", "audio_pll2_out", };
-static const char *imx8mq_uart3_sels[] = {"osc_25m", "sys1_pll_80m", "sys2_pll_200m", "sys2_pll_100m",
+static const char * const imx8mq_uart3_sels[] = {"osc_25m", "sys1_pll_80m", "sys2_pll_200m", "sys2_pll_100m",
"sys3_pll2_out", "clk_ext2", "clk_ext4", "audio_pll2_out", };
-static const char *imx8mq_uart4_sels[] = {"osc_25m", "sys1_pll_80m", "sys2_pll_200m", "sys2_pll_100m",
+static const char * const imx8mq_uart4_sels[] = {"osc_25m", "sys1_pll_80m", "sys2_pll_200m", "sys2_pll_100m",
"sys3_pll2_out", "clk_ext2", "clk_ext3", "audio_pll2_out", };
-static const char *imx8mq_usb_core_sels[] = {"osc_25m", "sys1_pll_100m", "sys1_pll_40m", "sys2_pll_100m",
+static const char * const imx8mq_usb_core_sels[] = {"osc_25m", "sys1_pll_100m", "sys1_pll_40m", "sys2_pll_100m",
"sys2_pll_200m", "clk_ext2", "clk_ext3", "audio_pll2_out", };
-static const char *imx8mq_usb_phy_sels[] = {"osc_25m", "sys1_pll_100m", "sys1_pll_40m", "sys2_pll_100m",
+static const char * const imx8mq_usb_phy_sels[] = {"osc_25m", "sys1_pll_100m", "sys1_pll_40m", "sys2_pll_100m",
"sys2_pll_200m", "clk_ext2", "clk_ext3", "audio_pll2_out", };
-static const char *imx8mq_ecspi1_sels[] = {"osc_25m", "sys2_pll_200m", "sys1_pll_40m", "sys1_pll_160m",
+static const char * const imx8mq_ecspi1_sels[] = {"osc_25m", "sys2_pll_200m", "sys1_pll_40m", "sys1_pll_160m",
"sys1_pll_800m", "sys3_pll2_out", "sys2_pll_250m", "audio_pll2_out", };
-static const char *imx8mq_ecspi2_sels[] = {"osc_25m", "sys2_pll_200m", "sys1_pll_40m", "sys1_pll_160m",
+static const char * const imx8mq_ecspi2_sels[] = {"osc_25m", "sys2_pll_200m", "sys1_pll_40m", "sys1_pll_160m",
"sys1_pll_800m", "sys3_pll2_out", "sys2_pll_250m", "audio_pll2_out", };
-static const char *imx8mq_pwm1_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_160m", "sys1_pll_40m",
+static const char * const imx8mq_pwm1_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_160m", "sys1_pll_40m",
"sys3_pll2_out", "clk_ext1", "sys1_pll_80m", "video_pll1_out", };
-static const char *imx8mq_pwm2_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_160m", "sys1_pll_40m",
+static const char * const imx8mq_pwm2_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_160m", "sys1_pll_40m",
"sys3_pll2_out", "clk_ext1", "sys1_pll_80m", "video_pll1_out", };
-static const char *imx8mq_pwm3_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_160m", "sys1_pll_40m",
+static const char * const imx8mq_pwm3_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_160m", "sys1_pll_40m",
"sys3_pll2_out", "clk_ext2", "sys1_pll_80m", "video_pll1_out", };
-static const char *imx8mq_pwm4_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_160m", "sys1_pll_40m",
+static const char * const imx8mq_pwm4_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_160m", "sys1_pll_40m",
"sys3_pll2_out", "clk_ext2", "sys1_pll_80m", "video_pll1_out", };
-static const char *imx8mq_gpt1_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_400m", "sys1_pll_40m",
+static const char * const imx8mq_gpt1_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_400m", "sys1_pll_40m",
"sys1_pll_80m", "audio_pll1_out", "clk_ext1", };
-static const char *imx8mq_wdog_sels[] = {"osc_25m", "sys1_pll_133m", "sys1_pll_160m", "vpu_pll_out",
+static const char * const imx8mq_wdog_sels[] = {"osc_25m", "sys1_pll_133m", "sys1_pll_160m", "vpu_pll_out",
"sys2_pll_125m", "sys3_pll2_out", "sys1_pll_80m", "sys2_pll_166m", };
-static const char *imx8mq_wrclk_sels[] = {"osc_25m", "sys1_pll_40m", "vpu_pll_out", "sys3_pll2_out", "sys2_pll_200m",
+static const char * const imx8mq_wrclk_sels[] = {"osc_25m", "sys1_pll_40m", "vpu_pll_out", "sys3_pll2_out", "sys2_pll_200m",
"sys1_pll_266m", "sys2_pll_500m", "sys1_pll_100m", };
-static const char *imx8mq_dsi_core_sels[] = {"osc_25m", "sys1_pll_266m", "sys2_pll_250m", "sys1_pll_800m",
+static const char * const imx8mq_dsi_core_sels[] = {"osc_25m", "sys1_pll_266m", "sys2_pll_250m", "sys1_pll_800m",
"sys2_pll_1000m", "sys3_pll2_out", "audio_pll2_out", "video_pll1_out", };
-static const char *imx8mq_dsi_phy_sels[] = {"osc_25m", "sys2_pll_125m", "sys2_pll_100m", "sys1_pll_800m",
+static const char * const imx8mq_dsi_phy_sels[] = {"osc_25m", "sys2_pll_125m", "sys2_pll_100m", "sys1_pll_800m",
"sys2_pll_1000m", "clk_ext2", "audio_pll2_out", "video_pll1_out", };
-static const char *imx8mq_dsi_dbi_sels[] = {"osc_25m", "sys1_pll_266m", "sys2_pll_100m", "sys1_pll_800m",
+static const char * const imx8mq_dsi_dbi_sels[] = {"osc_25m", "sys1_pll_266m", "sys2_pll_100m", "sys1_pll_800m",
"sys2_pll_1000m", "sys3_pll2_out", "audio_pll2_out", "video_pll1_out", };
-static const char *imx8mq_dsi_esc_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_80m", "sys1_pll_800m",
+static const char * const imx8mq_dsi_esc_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_80m", "sys1_pll_800m",
"sys2_pll_1000m", "sys3_pll2_out", "clk_ext3", "audio_pll2_out", };
-static const char *imx8mq_csi1_core_sels[] = {"osc_25m", "sys1_pll_266m", "sys2_pll_250m", "sys1_pll_800m",
+static const char * const imx8mq_csi1_core_sels[] = {"osc_25m", "sys1_pll_266m", "sys2_pll_250m", "sys1_pll_800m",
"sys2_pll_1000m", "sys3_pll2_out", "audio_pll2_out", "video_pll1_out", };
-static const char *imx8mq_csi1_phy_sels[] = {"osc_25m", "sys2_pll_125m", "sys2_pll_100m", "sys1_pll_800m",
+static const char * const imx8mq_csi1_phy_sels[] = {"osc_25m", "sys2_pll_125m", "sys2_pll_100m", "sys1_pll_800m",
"sys2_pll_1000m", "clk_ext2", "audio_pll2_out", "video_pll1_out", };
-static const char *imx8mq_csi1_esc_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_80m", "sys1_pll_800m",
+static const char * const imx8mq_csi1_esc_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_80m", "sys1_pll_800m",
"sys2_pll_1000m", "sys3_pll2_out", "clk_ext3", "audio_pll2_out", };
-static const char *imx8mq_csi2_core_sels[] = {"osc_25m", "sys1_pll_266m", "sys2_pll_250m", "sys1_pll_800m",
+static const char * const imx8mq_csi2_core_sels[] = {"osc_25m", "sys1_pll_266m", "sys2_pll_250m", "sys1_pll_800m",
"sys2_pll_1000m", "sys3_pll2_out", "audio_pll2_out", "video_pll1_out", };
-static const char *imx8mq_csi2_phy_sels[] = {"osc_25m", "sys2_pll_125m", "sys2_pll_100m", "sys1_pll_800m",
+static const char * const imx8mq_csi2_phy_sels[] = {"osc_25m", "sys2_pll_125m", "sys2_pll_100m", "sys1_pll_800m",
"sys2_pll_1000m", "clk_ext2", "audio_pll2_out", "video_pll1_out", };
-static const char *imx8mq_csi2_esc_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_80m", "sys1_pll_800m",
+static const char * const imx8mq_csi2_esc_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_80m", "sys1_pll_800m",
"sys2_pll_1000m", "sys3_pll2_out", "clk_ext3", "audio_pll2_out", };
-static const char *imx8mq_pcie2_ctrl_sels[] = {"osc_25m", "sys2_pll_250m", "sys2_pll_200m", "sys1_pll_266m",
+static const char * const imx8mq_pcie2_ctrl_sels[] = {"osc_25m", "sys2_pll_250m", "sys2_pll_200m", "sys1_pll_266m",
"sys1_pll_800m", "sys2_pll_500m", "sys2_pll_333m", "sys3_pll2_out", };
-static const char *imx8mq_pcie2_phy_sels[] = {"osc_25m", "sys2_pll_100m", "sys2_pll_500m", "clk_ext1",
+static const char * const imx8mq_pcie2_phy_sels[] = {"osc_25m", "sys2_pll_100m", "sys2_pll_500m", "clk_ext1",
"clk_ext2", "clk_ext3", "clk_ext4", "sys1_pll_400m", };
-static const char *imx8mq_pcie2_aux_sels[] = {"osc_25m", "sys2_pll_200m", "sys2_pll_50m", "sys3_pll2_out",
+static const char * const imx8mq_pcie2_aux_sels[] = {"osc_25m", "sys2_pll_200m", "sys2_pll_50m", "sys3_pll2_out",
"sys2_pll_100m", "sys1_pll_80m", "sys1_pll_160m", "sys1_pll_200m", };
-static const char *imx8mq_ecspi3_sels[] = {"osc_25m", "sys2_pll_200m", "sys1_pll_40m", "sys1_pll_160m",
+static const char * const imx8mq_ecspi3_sels[] = {"osc_25m", "sys2_pll_200m", "sys1_pll_40m", "sys1_pll_160m",
"sys1_pll_800m", "sys3_pll2_out", "sys2_pll_250m", "audio_pll2_out", };
-static const char *imx8mq_dram_core_sels[] = {"dram_pll_out", "dram_alt_root", };
+static const char * const imx8mq_dram_core_sels[] = {"dram_pll_out", "dram_alt_root", };
-static const char *imx8mq_clko2_sels[] = {"osc_25m", "sys2_pll_200m", "sys1_pll_400m", "sys2_pll_166m", "audio_pll1_out",
- "video_pll1_out", "ckil", };
+static const char * const imx8mq_clko1_sels[] = {"osc_25m", "sys1_pll_800m", "osc_27m", "sys1_pll_200m",
+ "audio_pll2_out", "sys2_pll_500m", "vpu_pll_out", "sys1_pll_80m", };
+static const char * const imx8mq_clko2_sels[] = {"osc_25m", "sys2_pll_200m", "sys1_pll_400m", "sys2_pll_166m",
+ "sys3_pll2_out", "audio_pll1_out", "video_pll1_out", "ckil", };
static struct clk_onecell_data clk_data;
@@ -308,10 +308,6 @@ static int imx8mq_clocks_probe(struct platform_device *pdev)
clks[IMX8MQ_AUDIO_PLL1_REF_DIV] = imx_clk_divider("audio_pll1_ref_div", "audio_pll1_ref_sel", base + 0x0, 5, 6);
clks[IMX8MQ_AUDIO_PLL2_REF_DIV] = imx_clk_divider("audio_pll2_ref_div", "audio_pll2_ref_sel", base + 0x8, 5, 6);
clks[IMX8MQ_VIDEO_PLL1_REF_DIV] = imx_clk_divider("video_pll1_ref_div", "video_pll1_ref_sel", base + 0x10, 5, 6);
- clks[IMX8MQ_SYS1_PLL1_REF_DIV] = imx_clk_divider("sys1_pll1_ref_div", "sys1_pll1_ref_sel", base + 0x38, 25, 3);
- clks[IMX8MQ_SYS2_PLL1_REF_DIV] = imx_clk_divider("sys2_pll1_ref_div", "sys2_pll1_ref_sel", base + 0x44, 25, 3);
- clks[IMX8MQ_SYS3_PLL1_REF_DIV] = imx_clk_divider("sys3_pll1_ref_div", "sys3_pll1_ref_sel", base + 0x50, 25, 3);
- clks[IMX8MQ_DRAM_PLL1_REF_DIV] = imx_clk_divider("dram_pll1_ref_div", "dram_pll1_ref_sel", base + 0x68, 25, 3);
clks[IMX8MQ_ARM_PLL] = imx_clk_frac_pll("arm_pll", "arm_pll_ref_div", base + 0x28);
clks[IMX8MQ_GPU_PLL] = imx_clk_frac_pll("gpu_pll", "gpu_pll_ref_div", base + 0x18);
@@ -319,43 +315,15 @@ static int imx8mq_clocks_probe(struct platform_device *pdev)
clks[IMX8MQ_AUDIO_PLL1] = imx_clk_frac_pll("audio_pll1", "audio_pll1_ref_div", base + 0x0);
clks[IMX8MQ_AUDIO_PLL2] = imx_clk_frac_pll("audio_pll2", "audio_pll2_ref_div", base + 0x8);
clks[IMX8MQ_VIDEO_PLL1] = imx_clk_frac_pll("video_pll1", "video_pll1_ref_div", base + 0x10);
- clks[IMX8MQ_SYS1_PLL1] = imx_clk_sccg_pll("sys1_pll1", "sys1_pll1_ref_div", base + 0x30, SCCG_PLL1);
- clks[IMX8MQ_SYS2_PLL1] = imx_clk_sccg_pll("sys2_pll1", "sys2_pll1_ref_div", base + 0x3c, SCCG_PLL1);
- clks[IMX8MQ_SYS3_PLL1] = imx_clk_sccg_pll("sys3_pll1", "sys3_pll1_ref_div", base + 0x48, SCCG_PLL1);
- clks[IMX8MQ_DRAM_PLL1] = imx_clk_sccg_pll("dram_pll1", "dram_pll1_ref_div", base + 0x60, SCCG_PLL1);
-
- clks[IMX8MQ_SYS1_PLL2] = imx_clk_sccg_pll("sys1_pll2", "sys1_pll1_out_div", base + 0x30, SCCG_PLL2);
- clks[IMX8MQ_SYS2_PLL2] = imx_clk_sccg_pll("sys2_pll2", "sys2_pll1_out_div", base + 0x3c, SCCG_PLL2);
- clks[IMX8MQ_SYS3_PLL2] = imx_clk_sccg_pll("sys3_pll2", "sys3_pll1_out_div", base + 0x48, SCCG_PLL2);
- clks[IMX8MQ_DRAM_PLL2] = imx_clk_sccg_pll("dram_pll2", "dram_pll1_out_div", base + 0x60, SCCG_PLL2);
-
- /* PLL divs */
- clks[IMX8MQ_SYS1_PLL1_OUT_DIV] = imx_clk_divider("sys1_pll1_out_div", "sys1_pll1_out", base + 0x38, 19, 6);
- clks[IMX8MQ_SYS2_PLL1_OUT_DIV] = imx_clk_divider("sys2_pll1_out_div", "sys2_pll1_out", base + 0x44, 19, 6);
- clks[IMX8MQ_SYS3_PLL1_OUT_DIV] = imx_clk_divider("sys3_pll1_out_div", "sys3_pll1_out", base + 0x50, 19, 6);
- clks[IMX8MQ_DRAM_PLL1_OUT_DIV] = imx_clk_divider("dram_pll1_out_div", "dram_pll1_out", base + 0x68, 19, 6);
- clks[IMX8MQ_SYS1_PLL2_DIV] = imx_clk_divider("sys1_pll2_div", "sys1_pll2", base + 0x38, 1, 6);
- clks[IMX8MQ_SYS2_PLL2_DIV] = imx_clk_divider("sys2_pll2_div", "sys2_pll2", base + 0x44, 1, 6);
- clks[IMX8MQ_SYS3_PLL2_DIV] = imx_clk_divider("sys3_pll2_div", "sys3_pll2", base + 0x50, 1, 6);
- clks[IMX8MQ_DRAM_PLL2_DIV] = imx_clk_divider("dram_pll2_div", "dram_pll2", base + 0x68, 1, 6);
/* PLL bypass out */
- clks[IMX8MQ_ARM_PLL_BYPASS] = imx_clk_mux("arm_pll_bypass", base + 0x28, 14, 1, arm_pll_bypass_sels, ARRAY_SIZE(arm_pll_bypass_sels));
+ clks[IMX8MQ_ARM_PLL_BYPASS] = imx_clk_mux_flags("arm_pll_bypass", base + 0x28, 14, 1, arm_pll_bypass_sels, ARRAY_SIZE(arm_pll_bypass_sels), CLK_SET_RATE_PARENT);
clks[IMX8MQ_GPU_PLL_BYPASS] = imx_clk_mux("gpu_pll_bypass", base + 0x18, 14, 1, gpu_pll_bypass_sels, ARRAY_SIZE(gpu_pll_bypass_sels));
clks[IMX8MQ_VPU_PLL_BYPASS] = imx_clk_mux("vpu_pll_bypass", base + 0x20, 14, 1, vpu_pll_bypass_sels, ARRAY_SIZE(vpu_pll_bypass_sels));
clks[IMX8MQ_AUDIO_PLL1_BYPASS] = imx_clk_mux("audio_pll1_bypass", base + 0x0, 14, 1, audio_pll1_bypass_sels, ARRAY_SIZE(audio_pll1_bypass_sels));
clks[IMX8MQ_AUDIO_PLL2_BYPASS] = imx_clk_mux("audio_pll2_bypass", base + 0x8, 14, 1, audio_pll2_bypass_sels, ARRAY_SIZE(audio_pll2_bypass_sels));
clks[IMX8MQ_VIDEO_PLL1_BYPASS] = imx_clk_mux("video_pll1_bypass", base + 0x10, 14, 1, video_pll1_bypass_sels, ARRAY_SIZE(video_pll1_bypass_sels));
- clks[IMX8MQ_SYS1_PLL1_OUT] = imx_clk_mux("sys1_pll1_out", base + 0x30, 5, 1, sys1_pll1_out_sels, ARRAY_SIZE(sys1_pll1_out_sels));
- clks[IMX8MQ_SYS2_PLL1_OUT] = imx_clk_mux("sys2_pll1_out", base + 0x3c, 5, 1, sys2_pll1_out_sels, ARRAY_SIZE(sys2_pll1_out_sels));
- clks[IMX8MQ_SYS3_PLL1_OUT] = imx_clk_mux("sys3_pll1_out", base + 0x48, 5, 1, sys3_pll1_out_sels, ARRAY_SIZE(sys3_pll1_out_sels));
- clks[IMX8MQ_DRAM_PLL1_OUT] = imx_clk_mux("dram_pll1_out", base + 0x60, 5, 1, dram_pll1_out_sels, ARRAY_SIZE(dram_pll1_out_sels));
- clks[IMX8MQ_SYS1_PLL2_OUT] = imx_clk_mux("sys1_pll2_out", base + 0x30, 4, 1, sys1_pll2_out_sels, ARRAY_SIZE(sys1_pll2_out_sels));
- clks[IMX8MQ_SYS2_PLL2_OUT] = imx_clk_mux("sys2_pll2_out", base + 0x3c, 4, 1, sys2_pll2_out_sels, ARRAY_SIZE(sys2_pll2_out_sels));
- clks[IMX8MQ_SYS3_PLL2_OUT] = imx_clk_mux("sys3_pll2_out", base + 0x48, 4, 1, sys3_pll2_out_sels, ARRAY_SIZE(sys3_pll2_out_sels));
- clks[IMX8MQ_DRAM_PLL2_OUT] = imx_clk_mux("dram_pll2_out", base + 0x60, 4, 1, dram_pll2_out_sels, ARRAY_SIZE(dram_pll2_out_sels));
-
/* PLL OUT GATE */
clks[IMX8MQ_ARM_PLL_OUT] = imx_clk_gate("arm_pll_out", "arm_pll_bypass", base + 0x28, 21);
clks[IMX8MQ_GPU_PLL_OUT] = imx_clk_gate("gpu_pll_out", "gpu_pll_bypass", base + 0x18, 21);
@@ -363,11 +331,11 @@ static int imx8mq_clocks_probe(struct platform_device *pdev)
clks[IMX8MQ_AUDIO_PLL1_OUT] = imx_clk_gate("audio_pll1_out", "audio_pll1_bypass", base + 0x0, 21);
clks[IMX8MQ_AUDIO_PLL2_OUT] = imx_clk_gate("audio_pll2_out", "audio_pll2_bypass", base + 0x8, 21);
clks[IMX8MQ_VIDEO_PLL1_OUT] = imx_clk_gate("video_pll1_out", "video_pll1_bypass", base + 0x10, 21);
- clks[IMX8MQ_SYS1_PLL_OUT] = imx_clk_gate("sys1_pll_out", "sys1_pll2_out", base + 0x30, 9);
- clks[IMX8MQ_SYS2_PLL_OUT] = imx_clk_gate("sys2_pll_out", "sys2_pll2_out", base + 0x3c, 9);
- clks[IMX8MQ_SYS3_PLL_OUT] = imx_clk_gate("sys3_pll_out", "sys3_pll2_out", base + 0x48, 9);
- clks[IMX8MQ_DRAM_PLL_OUT] = imx_clk_gate("dram_pll_out", "dram_pll2_out", base + 0x60, 9);
+ clks[IMX8MQ_SYS1_PLL_OUT] = imx_clk_sccg_pll("sys1_pll_out", sys1_pll_out_sels, ARRAY_SIZE(sys1_pll_out_sels), 0, 0, 0, base + 0x30, CLK_IS_CRITICAL);
+ clks[IMX8MQ_SYS2_PLL_OUT] = imx_clk_sccg_pll("sys2_pll_out", sys2_pll_out_sels, ARRAY_SIZE(sys2_pll_out_sels), 0, 0, 1, base + 0x3c, CLK_IS_CRITICAL);
+ clks[IMX8MQ_SYS3_PLL_OUT] = imx_clk_sccg_pll("sys3_pll_out", sys3_pll_out_sels, ARRAY_SIZE(sys3_pll_out_sels), 0, 0, 1, base + 0x48, CLK_IS_CRITICAL);
+ clks[IMX8MQ_DRAM_PLL_OUT] = imx_clk_sccg_pll("dram_pll_out", dram_pll_out_sels, ARRAY_SIZE(dram_pll_out_sels), 0, 0, 0, base + 0x60, CLK_IS_CRITICAL);
/* SYS PLL fixed output */
clks[IMX8MQ_SYS1_PLL_40M] = imx_clk_fixed_factor("sys1_pll_40m", "sys1_pll_out", 1, 20);
clks[IMX8MQ_SYS1_PLL_80M] = imx_clk_fixed_factor("sys1_pll_80m", "sys1_pll_out", 1, 10);
@@ -396,15 +364,19 @@ static int imx8mq_clocks_probe(struct platform_device *pdev)
/* CORE */
clks[IMX8MQ_CLK_A53_SRC] = imx_clk_mux2("arm_a53_src", base + 0x8000, 24, 3, imx8mq_a53_sels, ARRAY_SIZE(imx8mq_a53_sels));
+ clks[IMX8MQ_CLK_M4_SRC] = imx_clk_mux2("arm_m4_src", base + 0x8080, 24, 3, imx8mq_arm_m4_sels, ARRAY_SIZE(imx8mq_arm_m4_sels));
clks[IMX8MQ_CLK_VPU_SRC] = imx_clk_mux2("vpu_src", base + 0x8100, 24, 3, imx8mq_vpu_sels, ARRAY_SIZE(imx8mq_vpu_sels));
clks[IMX8MQ_CLK_GPU_CORE_SRC] = imx_clk_mux2("gpu_core_src", base + 0x8180, 24, 3, imx8mq_gpu_core_sels, ARRAY_SIZE(imx8mq_gpu_core_sels));
clks[IMX8MQ_CLK_GPU_SHADER_SRC] = imx_clk_mux2("gpu_shader_src", base + 0x8200, 24, 3, imx8mq_gpu_shader_sels, ARRAY_SIZE(imx8mq_gpu_shader_sels));
+
clks[IMX8MQ_CLK_A53_CG] = imx_clk_gate3_flags("arm_a53_cg", "arm_a53_src", base + 0x8000, 28, CLK_IS_CRITICAL);
+ clks[IMX8MQ_CLK_M4_CG] = imx_clk_gate3("arm_m4_cg", "arm_m4_src", base + 0x8080, 28);
clks[IMX8MQ_CLK_VPU_CG] = imx_clk_gate3("vpu_cg", "vpu_src", base + 0x8100, 28);
clks[IMX8MQ_CLK_GPU_CORE_CG] = imx_clk_gate3("gpu_core_cg", "gpu_core_src", base + 0x8180, 28);
clks[IMX8MQ_CLK_GPU_SHADER_CG] = imx_clk_gate3("gpu_shader_cg", "gpu_shader_src", base + 0x8200, 28);
clks[IMX8MQ_CLK_A53_DIV] = imx_clk_divider2("arm_a53_div", "arm_a53_cg", base + 0x8000, 0, 3);
+ clks[IMX8MQ_CLK_M4_DIV] = imx_clk_divider2("arm_m4_div", "arm_m4_cg", base + 0x8080, 0, 3);
clks[IMX8MQ_CLK_VPU_DIV] = imx_clk_divider2("vpu_div", "vpu_cg", base + 0x8100, 0, 3);
clks[IMX8MQ_CLK_GPU_CORE_DIV] = imx_clk_divider2("gpu_core_div", "gpu_core_cg", base + 0x8180, 0, 3);
clks[IMX8MQ_CLK_GPU_SHADER_DIV] = imx_clk_divider2("gpu_shader_div", "gpu_shader_cg", base + 0x8200, 0, 3);
@@ -479,6 +451,7 @@ static int imx8mq_clocks_probe(struct platform_device *pdev)
clks[IMX8MQ_CLK_GPT1] = imx8m_clk_composite("gpt1", imx8mq_gpt1_sels, base + 0xb580);
clks[IMX8MQ_CLK_WDOG] = imx8m_clk_composite("wdog", imx8mq_wdog_sels, base + 0xb900);
clks[IMX8MQ_CLK_WRCLK] = imx8m_clk_composite("wrclk", imx8mq_wrclk_sels, base + 0xb980);
+ clks[IMX8MQ_CLK_CLKO1] = imx8m_clk_composite("clko1", imx8mq_clko1_sels, base + 0xba00);
clks[IMX8MQ_CLK_CLKO2] = imx8m_clk_composite("clko2", imx8mq_clko2_sels, base + 0xba80);
clks[IMX8MQ_CLK_DSI_CORE] = imx8m_clk_composite("dsi_core", imx8mq_dsi_core_sels, base + 0xbb00);
clks[IMX8MQ_CLK_DSI_PHY_REF] = imx8m_clk_composite("dsi_phy_ref", imx8mq_dsi_phy_sels, base + 0xbb80);
@@ -500,6 +473,11 @@ static int imx8mq_clocks_probe(struct platform_device *pdev)
clks[IMX8MQ_CLK_ECSPI2_ROOT] = imx_clk_gate4("ecspi2_root_clk", "ecspi2", base + 0x4080, 0);
clks[IMX8MQ_CLK_ECSPI3_ROOT] = imx_clk_gate4("ecspi3_root_clk", "ecspi3", base + 0x4090, 0);
clks[IMX8MQ_CLK_ENET1_ROOT] = imx_clk_gate4("enet1_root_clk", "enet_axi", base + 0x40a0, 0);
+ clks[IMX8MQ_CLK_GPIO1_ROOT] = imx_clk_gate4("gpio1_root_clk", "ipg_root", base + 0x40b0, 0);
+ clks[IMX8MQ_CLK_GPIO2_ROOT] = imx_clk_gate4("gpio2_root_clk", "ipg_root", base + 0x40c0, 0);
+ clks[IMX8MQ_CLK_GPIO3_ROOT] = imx_clk_gate4("gpio3_root_clk", "ipg_root", base + 0x40d0, 0);
+ clks[IMX8MQ_CLK_GPIO4_ROOT] = imx_clk_gate4("gpio4_root_clk", "ipg_root", base + 0x40e0, 0);
+ clks[IMX8MQ_CLK_GPIO5_ROOT] = imx_clk_gate4("gpio5_root_clk", "ipg_root", base + 0x40f0, 0);
clks[IMX8MQ_CLK_GPT1_ROOT] = imx_clk_gate4("gpt1_root_clk", "gpt1", base + 0x4100, 0);
clks[IMX8MQ_CLK_I2C1_ROOT] = imx_clk_gate4("i2c1_root_clk", "i2c1", base + 0x4170, 0);
clks[IMX8MQ_CLK_I2C2_ROOT] = imx_clk_gate4("i2c2_root_clk", "i2c2", base + 0x4180, 0);
@@ -558,6 +536,12 @@ static int imx8mq_clocks_probe(struct platform_device *pdev)
clks[IMX8MQ_GPT_3M_CLK] = imx_clk_fixed_factor("gpt_3m", "osc_25m", 1, 8);
clks[IMX8MQ_CLK_DRAM_ALT_ROOT] = imx_clk_fixed_factor("dram_alt_root", "dram_alt", 1, 4);
+ clks[IMX8MQ_CLK_ARM] = imx_clk_cpu("arm", "arm_a53_div",
+ clks[IMX8MQ_CLK_A53_DIV],
+ clks[IMX8MQ_CLK_A53_SRC],
+ clks[IMX8MQ_ARM_PLL_OUT],
+ clks[IMX8MQ_SYS1_PLL_800M]);
+
for (i = 0; i < IMX8MQ_CLK_END; i++)
if (IS_ERR(clks[i]))
pr_err("i.MX8mq clk %u register failed with %ld\n",
diff --git a/drivers/clk/imx/clk-imx8qxp.c b/drivers/clk/imx/clk-imx8qxp.c
index 83e2ef96d81d..5e2903efc488 100644
--- a/drivers/clk/imx/clk-imx8qxp.c
+++ b/drivers/clk/imx/clk-imx8qxp.c
@@ -138,6 +138,7 @@ static int imx8qxp_clk_probe(struct platform_device *pdev)
}
static const struct of_device_id imx8qxp_match[] = {
+ { .compatible = "fsl,scu-clk", },
{ .compatible = "fsl,imx8qxp-clk", },
{ /* sentinel */ }
};
diff --git a/drivers/clk/imx/clk-pll14xx.c b/drivers/clk/imx/clk-pll14xx.c
new file mode 100644
index 000000000000..1acfa3e3cfb4
--- /dev/null
+++ b/drivers/clk/imx/clk-pll14xx.c
@@ -0,0 +1,392 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2017-2018 NXP.
+ */
+
+#include <linux/bitops.h>
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+
+#include "clk.h"
+
+#define GNRL_CTL 0x0
+#define DIV_CTL 0x4
+#define LOCK_STATUS BIT(31)
+#define LOCK_SEL_MASK BIT(29)
+#define CLKE_MASK BIT(11)
+#define RST_MASK BIT(9)
+#define BYPASS_MASK BIT(4)
+#define MDIV_SHIFT 12
+#define MDIV_MASK GENMASK(21, 12)
+#define PDIV_SHIFT 4
+#define PDIV_MASK GENMASK(9, 4)
+#define SDIV_SHIFT 0
+#define SDIV_MASK GENMASK(2, 0)
+#define KDIV_SHIFT 0
+#define KDIV_MASK GENMASK(15, 0)
+
+#define LOCK_TIMEOUT_US 10000
+
+struct clk_pll14xx {
+ struct clk_hw hw;
+ void __iomem *base;
+ enum imx_pll14xx_type type;
+ const struct imx_pll14xx_rate_table *rate_table;
+ int rate_count;
+};
+
+#define to_clk_pll14xx(_hw) container_of(_hw, struct clk_pll14xx, hw)
+
+static const struct imx_pll14xx_rate_table *imx_get_pll_settings(
+ struct clk_pll14xx *pll, unsigned long rate)
+{
+ const struct imx_pll14xx_rate_table *rate_table = pll->rate_table;
+ int i;
+
+ for (i = 0; i < pll->rate_count; i++)
+ if (rate == rate_table[i].rate)
+ return &rate_table[i];
+
+ return NULL;
+}
+
+static long clk_pll14xx_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ struct clk_pll14xx *pll = to_clk_pll14xx(hw);
+ const struct imx_pll14xx_rate_table *rate_table = pll->rate_table;
+ int i;
+
+ /* Assumming rate_table is in descending order */
+ for (i = 0; i < pll->rate_count; i++)
+ if (rate >= rate_table[i].rate)
+ return rate_table[i].rate;
+
+ /* return minimum supported value */
+ return rate_table[i - 1].rate;
+}
+
+static unsigned long clk_pll1416x_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_pll14xx *pll = to_clk_pll14xx(hw);
+ u32 mdiv, pdiv, sdiv, pll_gnrl, pll_div;
+ u64 fvco = parent_rate;
+
+ pll_gnrl = readl_relaxed(pll->base);
+ pll_div = readl_relaxed(pll->base + 4);
+ mdiv = (pll_div & MDIV_MASK) >> MDIV_SHIFT;
+ pdiv = (pll_div & PDIV_MASK) >> PDIV_SHIFT;
+ sdiv = (pll_div & SDIV_MASK) >> SDIV_SHIFT;
+
+ fvco *= mdiv;
+ do_div(fvco, pdiv << sdiv);
+
+ return fvco;
+}
+
+static unsigned long clk_pll1443x_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_pll14xx *pll = to_clk_pll14xx(hw);
+ u32 mdiv, pdiv, sdiv, pll_gnrl, pll_div_ctl0, pll_div_ctl1;
+ short int kdiv;
+ u64 fvco = parent_rate;
+
+ pll_gnrl = readl_relaxed(pll->base);
+ pll_div_ctl0 = readl_relaxed(pll->base + 4);
+ pll_div_ctl1 = readl_relaxed(pll->base + 8);
+ mdiv = (pll_div_ctl0 & MDIV_MASK) >> MDIV_SHIFT;
+ pdiv = (pll_div_ctl0 & PDIV_MASK) >> PDIV_SHIFT;
+ sdiv = (pll_div_ctl0 & SDIV_MASK) >> SDIV_SHIFT;
+ kdiv = pll_div_ctl1 & KDIV_MASK;
+
+ /* fvco = (m * 65536 + k) * Fin / (p * 65536) */
+ fvco *= (mdiv * 65536 + kdiv);
+ pdiv *= 65536;
+
+ do_div(fvco, pdiv << sdiv);
+
+ return fvco;
+}
+
+static inline bool clk_pll1416x_mp_change(const struct imx_pll14xx_rate_table *rate,
+ u32 pll_div)
+{
+ u32 old_mdiv, old_pdiv;
+
+ old_mdiv = (pll_div >> MDIV_SHIFT) & MDIV_MASK;
+ old_pdiv = (pll_div >> PDIV_SHIFT) & PDIV_MASK;
+
+ return rate->mdiv != old_mdiv || rate->pdiv != old_pdiv;
+}
+
+static inline bool clk_pll1443x_mpk_change(const struct imx_pll14xx_rate_table *rate,
+ u32 pll_div_ctl0, u32 pll_div_ctl1)
+{
+ u32 old_mdiv, old_pdiv, old_kdiv;
+
+ old_mdiv = (pll_div_ctl0 >> MDIV_SHIFT) & MDIV_MASK;
+ old_pdiv = (pll_div_ctl0 >> PDIV_SHIFT) & PDIV_MASK;
+ old_kdiv = (pll_div_ctl1 >> KDIV_SHIFT) & KDIV_MASK;
+
+ return rate->mdiv != old_mdiv || rate->pdiv != old_pdiv ||
+ rate->kdiv != old_kdiv;
+}
+
+static inline bool clk_pll1443x_mp_change(const struct imx_pll14xx_rate_table *rate,
+ u32 pll_div_ctl0, u32 pll_div_ctl1)
+{
+ u32 old_mdiv, old_pdiv, old_kdiv;
+
+ old_mdiv = (pll_div_ctl0 >> MDIV_SHIFT) & MDIV_MASK;
+ old_pdiv = (pll_div_ctl0 >> PDIV_SHIFT) & PDIV_MASK;
+ old_kdiv = (pll_div_ctl1 >> KDIV_SHIFT) & KDIV_MASK;
+
+ return rate->mdiv != old_mdiv || rate->pdiv != old_pdiv ||
+ rate->kdiv != old_kdiv;
+}
+
+static int clk_pll14xx_wait_lock(struct clk_pll14xx *pll)
+{
+ u32 val;
+
+ return readl_poll_timeout(pll->base, val, val & LOCK_TIMEOUT_US, 0,
+ LOCK_TIMEOUT_US);
+}
+
+static int clk_pll1416x_set_rate(struct clk_hw *hw, unsigned long drate,
+ unsigned long prate)
+{
+ struct clk_pll14xx *pll = to_clk_pll14xx(hw);
+ const struct imx_pll14xx_rate_table *rate;
+ u32 tmp, div_val;
+ int ret;
+
+ rate = imx_get_pll_settings(pll, drate);
+ if (!rate) {
+ pr_err("%s: Invalid rate : %lu for pll clk %s\n", __func__,
+ drate, clk_hw_get_name(hw));
+ return -EINVAL;
+ }
+
+ tmp = readl_relaxed(pll->base + 4);
+
+ if (!clk_pll1416x_mp_change(rate, tmp)) {
+ tmp &= ~(SDIV_MASK) << SDIV_SHIFT;
+ tmp |= rate->sdiv << SDIV_SHIFT;
+ writel_relaxed(tmp, pll->base + 4);
+
+ return 0;
+ }
+
+ /* Bypass clock and set lock to pll output lock */
+ tmp = readl_relaxed(pll->base);
+ tmp |= LOCK_SEL_MASK;
+ writel_relaxed(tmp, pll->base);
+
+ /* Enable RST */
+ tmp &= ~RST_MASK;
+ writel_relaxed(tmp, pll->base);
+
+ div_val = (rate->mdiv << MDIV_SHIFT) | (rate->pdiv << PDIV_SHIFT) |
+ (rate->sdiv << SDIV_SHIFT);
+ writel_relaxed(div_val, pll->base + 0x4);
+
+ /*
+ * According to SPEC, t3 - t2 need to be greater than
+ * 1us and 1/FREF, respectively.
+ * FREF is FIN / Prediv, the prediv is [1, 63], so choose
+ * 3us.
+ */
+ udelay(3);
+
+ /* Disable RST */
+ tmp |= RST_MASK;
+ writel_relaxed(tmp, pll->base);
+
+ /* Wait Lock */
+ ret = clk_pll14xx_wait_lock(pll);
+ if (ret)
+ return ret;
+
+ /* Bypass */
+ tmp &= ~BYPASS_MASK;
+ writel_relaxed(tmp, pll->base);
+
+ return 0;
+}
+
+static int clk_pll1443x_set_rate(struct clk_hw *hw, unsigned long drate,
+ unsigned long prate)
+{
+ struct clk_pll14xx *pll = to_clk_pll14xx(hw);
+ const struct imx_pll14xx_rate_table *rate;
+ u32 tmp, div_val;
+ int ret;
+
+ rate = imx_get_pll_settings(pll, drate);
+ if (!rate) {
+ pr_err("%s: Invalid rate : %lu for pll clk %s\n", __func__,
+ drate, clk_hw_get_name(hw));
+ return -EINVAL;
+ }
+
+ tmp = readl_relaxed(pll->base + 4);
+ div_val = readl_relaxed(pll->base + 8);
+
+ if (!clk_pll1443x_mpk_change(rate, tmp, div_val)) {
+ tmp &= ~(SDIV_MASK) << SDIV_SHIFT;
+ tmp |= rate->sdiv << SDIV_SHIFT;
+ writel_relaxed(tmp, pll->base + 4);
+
+ return 0;
+ }
+
+ /* Enable RST */
+ tmp = readl_relaxed(pll->base);
+ tmp &= ~RST_MASK;
+ writel_relaxed(tmp, pll->base);
+
+ div_val = (rate->mdiv << MDIV_SHIFT) | (rate->pdiv << PDIV_SHIFT) |
+ (rate->sdiv << SDIV_SHIFT);
+ writel_relaxed(div_val, pll->base + 0x4);
+ writel_relaxed(rate->kdiv << KDIV_SHIFT, pll->base + 0x8);
+
+ /*
+ * According to SPEC, t3 - t2 need to be greater than
+ * 1us and 1/FREF, respectively.
+ * FREF is FIN / Prediv, the prediv is [1, 63], so choose
+ * 3us.
+ */
+ udelay(3);
+
+ /* Disable RST */
+ tmp |= RST_MASK;
+ writel_relaxed(tmp, pll->base);
+
+ /* Wait Lock*/
+ ret = clk_pll14xx_wait_lock(pll);
+ if (ret)
+ return ret;
+
+ /* Bypass */
+ tmp &= ~BYPASS_MASK;
+ writel_relaxed(tmp, pll->base);
+
+ return 0;
+}
+
+static int clk_pll14xx_prepare(struct clk_hw *hw)
+{
+ struct clk_pll14xx *pll = to_clk_pll14xx(hw);
+ u32 val;
+
+ /*
+ * RESETB = 1 from 0, PLL starts its normal
+ * operation after lock time
+ */
+ val = readl_relaxed(pll->base + GNRL_CTL);
+ val |= RST_MASK;
+ writel_relaxed(val, pll->base + GNRL_CTL);
+
+ return clk_pll14xx_wait_lock(pll);
+}
+
+static int clk_pll14xx_is_prepared(struct clk_hw *hw)
+{
+ struct clk_pll14xx *pll = to_clk_pll14xx(hw);
+ u32 val;
+
+ val = readl_relaxed(pll->base + GNRL_CTL);
+
+ return (val & RST_MASK) ? 1 : 0;
+}
+
+static void clk_pll14xx_unprepare(struct clk_hw *hw)
+{
+ struct clk_pll14xx *pll = to_clk_pll14xx(hw);
+ u32 val;
+
+ /*
+ * Set RST to 0, power down mode is enabled and
+ * every digital block is reset
+ */
+ val = readl_relaxed(pll->base + GNRL_CTL);
+ val &= ~RST_MASK;
+ writel_relaxed(val, pll->base + GNRL_CTL);
+}
+
+static const struct clk_ops clk_pll1416x_ops = {
+ .prepare = clk_pll14xx_prepare,
+ .unprepare = clk_pll14xx_unprepare,
+ .is_prepared = clk_pll14xx_is_prepared,
+ .recalc_rate = clk_pll1416x_recalc_rate,
+ .round_rate = clk_pll14xx_round_rate,
+ .set_rate = clk_pll1416x_set_rate,
+};
+
+static const struct clk_ops clk_pll1416x_min_ops = {
+ .recalc_rate = clk_pll1416x_recalc_rate,
+};
+
+static const struct clk_ops clk_pll1443x_ops = {
+ .prepare = clk_pll14xx_prepare,
+ .unprepare = clk_pll14xx_unprepare,
+ .is_prepared = clk_pll14xx_is_prepared,
+ .recalc_rate = clk_pll1443x_recalc_rate,
+ .round_rate = clk_pll14xx_round_rate,
+ .set_rate = clk_pll1443x_set_rate,
+};
+
+struct clk *imx_clk_pll14xx(const char *name, const char *parent_name,
+ void __iomem *base,
+ const struct imx_pll14xx_clk *pll_clk)
+{
+ struct clk_pll14xx *pll;
+ struct clk *clk;
+ struct clk_init_data init;
+
+ pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+ if (!pll)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.flags = pll_clk->flags;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+
+ switch (pll_clk->type) {
+ case PLL_1416X:
+ if (!pll->rate_table)
+ init.ops = &clk_pll1416x_min_ops;
+ else
+ init.ops = &clk_pll1416x_ops;
+ break;
+ case PLL_1443X:
+ init.ops = &clk_pll1443x_ops;
+ break;
+ default:
+ pr_err("%s: Unknown pll type for pll clk %s\n",
+ __func__, name);
+ };
+
+ pll->base = base;
+ pll->hw.init = &init;
+ pll->type = pll_clk->type;
+ pll->rate_table = pll_clk->rate_table;
+ pll->rate_count = pll_clk->rate_count;
+
+ clk = clk_register(NULL, &pll->hw);
+ if (IS_ERR(clk)) {
+ pr_err("%s: failed to register pll %s %lu\n",
+ __func__, name, PTR_ERR(clk));
+ kfree(pll);
+ }
+
+ return clk;
+}
diff --git a/drivers/clk/imx/clk-sccg-pll.c b/drivers/clk/imx/clk-sccg-pll.c
index ee7752bace89..9dfd03a95557 100644
--- a/drivers/clk/imx/clk-sccg-pll.c
+++ b/drivers/clk/imx/clk-sccg-pll.c
@@ -25,87 +25,292 @@
#define PLL_DIVF2_MASK GENMASK(12, 7)
#define PLL_DIVR1_MASK GENMASK(27, 25)
#define PLL_DIVR2_MASK GENMASK(24, 19)
+#define PLL_DIVQ_MASK GENMASK(6, 1)
#define PLL_REF_MASK GENMASK(2, 0)
#define PLL_LOCK_MASK BIT(31)
#define PLL_PD_MASK BIT(7)
-#define OSC_25M 25000000
-#define OSC_27M 27000000
+/* These are the specification limits for the SSCG PLL */
+#define PLL_REF_MIN_FREQ 25000000UL
+#define PLL_REF_MAX_FREQ 235000000UL
-#define PLL_SCCG_LOCK_TIMEOUT 70
+#define PLL_STAGE1_MIN_FREQ 1600000000UL
+#define PLL_STAGE1_MAX_FREQ 2400000000UL
+
+#define PLL_STAGE1_REF_MIN_FREQ 25000000UL
+#define PLL_STAGE1_REF_MAX_FREQ 54000000UL
+
+#define PLL_STAGE2_MIN_FREQ 1200000000UL
+#define PLL_STAGE2_MAX_FREQ 2400000000UL
+
+#define PLL_STAGE2_REF_MIN_FREQ 54000000UL
+#define PLL_STAGE2_REF_MAX_FREQ 75000000UL
+
+#define PLL_OUT_MIN_FREQ 20000000UL
+#define PLL_OUT_MAX_FREQ 1200000000UL
+
+#define PLL_DIVR1_MAX 7
+#define PLL_DIVR2_MAX 63
+#define PLL_DIVF1_MAX 63
+#define PLL_DIVF2_MAX 63
+#define PLL_DIVQ_MAX 63
+
+#define PLL_BYPASS_NONE 0x0
+#define PLL_BYPASS1 0x2
+#define PLL_BYPASS2 0x1
+
+#define SSCG_PLL_BYPASS1_MASK BIT(5)
+#define SSCG_PLL_BYPASS2_MASK BIT(4)
+#define SSCG_PLL_BYPASS_MASK GENMASK(5, 4)
+
+#define PLL_SCCG_LOCK_TIMEOUT 70
+
+struct clk_sccg_pll_setup {
+ int divr1, divf1;
+ int divr2, divf2;
+ int divq;
+ int bypass;
+
+ uint64_t vco1;
+ uint64_t vco2;
+ uint64_t fout;
+ uint64_t ref;
+ uint64_t ref_div1;
+ uint64_t ref_div2;
+ uint64_t fout_request;
+ int fout_error;
+};
struct clk_sccg_pll {
struct clk_hw hw;
- void __iomem *base;
+ const struct clk_ops ops;
+
+ void __iomem *base;
+
+ struct clk_sccg_pll_setup setup;
+
+ u8 parent;
+ u8 bypass1;
+ u8 bypass2;
};
#define to_clk_sccg_pll(_hw) container_of(_hw, struct clk_sccg_pll, hw)
-static int clk_pll_wait_lock(struct clk_sccg_pll *pll)
+static int clk_sccg_pll_wait_lock(struct clk_sccg_pll *pll)
{
u32 val;
- return readl_poll_timeout(pll->base, val, val & PLL_LOCK_MASK, 0,
- PLL_SCCG_LOCK_TIMEOUT);
+ val = readl_relaxed(pll->base + PLL_CFG0);
+
+ /* don't wait for lock if all plls are bypassed */
+ if (!(val & SSCG_PLL_BYPASS2_MASK))
+ return readl_poll_timeout(pll->base, val, val & PLL_LOCK_MASK,
+ 0, PLL_SCCG_LOCK_TIMEOUT);
+
+ return 0;
}
-static int clk_pll1_is_prepared(struct clk_hw *hw)
+static int clk_sccg_pll2_check_match(struct clk_sccg_pll_setup *setup,
+ struct clk_sccg_pll_setup *temp_setup)
{
- struct clk_sccg_pll *pll = to_clk_sccg_pll(hw);
- u32 val;
+ int new_diff = temp_setup->fout - temp_setup->fout_request;
+ int diff = temp_setup->fout_error;
- val = readl_relaxed(pll->base + PLL_CFG0);
- return (val & PLL_PD_MASK) ? 0 : 1;
+ if (abs(diff) > abs(new_diff)) {
+ temp_setup->fout_error = new_diff;
+ memcpy(setup, temp_setup, sizeof(struct clk_sccg_pll_setup));
+
+ if (temp_setup->fout_request == temp_setup->fout)
+ return 0;
+ }
+ return -1;
}
-static unsigned long clk_pll1_recalc_rate(struct clk_hw *hw,
- unsigned long parent_rate)
+static int clk_sccg_divq_lookup(struct clk_sccg_pll_setup *setup,
+ struct clk_sccg_pll_setup *temp_setup)
{
- struct clk_sccg_pll *pll = to_clk_sccg_pll(hw);
- u32 val, divf;
+ int ret = -EINVAL;
+
+ for (temp_setup->divq = 0; temp_setup->divq <= PLL_DIVQ_MAX;
+ temp_setup->divq++) {
+ temp_setup->vco2 = temp_setup->vco1;
+ do_div(temp_setup->vco2, temp_setup->divr2 + 1);
+ temp_setup->vco2 *= 2;
+ temp_setup->vco2 *= temp_setup->divf2 + 1;
+ if (temp_setup->vco2 >= PLL_STAGE2_MIN_FREQ &&
+ temp_setup->vco2 <= PLL_STAGE2_MAX_FREQ) {
+ temp_setup->fout = temp_setup->vco2;
+ do_div(temp_setup->fout, 2 * (temp_setup->divq + 1));
+
+ ret = clk_sccg_pll2_check_match(setup, temp_setup);
+ if (!ret) {
+ temp_setup->bypass = PLL_BYPASS1;
+ return ret;
+ }
+ }
+ }
- val = readl_relaxed(pll->base + PLL_CFG2);
- divf = FIELD_GET(PLL_DIVF1_MASK, val);
+ return ret;
+}
+
+static int clk_sccg_divf2_lookup(struct clk_sccg_pll_setup *setup,
+ struct clk_sccg_pll_setup *temp_setup)
+{
+ int ret = -EINVAL;
+
+ for (temp_setup->divf2 = 0; temp_setup->divf2 <= PLL_DIVF2_MAX;
+ temp_setup->divf2++) {
+ ret = clk_sccg_divq_lookup(setup, temp_setup);
+ if (!ret)
+ return ret;
+ }
- return parent_rate * 2 * (divf + 1);
+ return ret;
}
-static long clk_pll1_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
+static int clk_sccg_divr2_lookup(struct clk_sccg_pll_setup *setup,
+ struct clk_sccg_pll_setup *temp_setup)
{
- unsigned long parent_rate = *prate;
- u32 div;
+ int ret = -EINVAL;
+
+ for (temp_setup->divr2 = 0; temp_setup->divr2 <= PLL_DIVR2_MAX;
+ temp_setup->divr2++) {
+ temp_setup->ref_div2 = temp_setup->vco1;
+ do_div(temp_setup->ref_div2, temp_setup->divr2 + 1);
+ if (temp_setup->ref_div2 >= PLL_STAGE2_REF_MIN_FREQ &&
+ temp_setup->ref_div2 <= PLL_STAGE2_REF_MAX_FREQ) {
+ ret = clk_sccg_divf2_lookup(setup, temp_setup);
+ if (!ret)
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static int clk_sccg_pll2_find_setup(struct clk_sccg_pll_setup *setup,
+ struct clk_sccg_pll_setup *temp_setup,
+ uint64_t ref)
+{
+
+ int ret = -EINVAL;
- if (!parent_rate)
- return 0;
+ if (ref < PLL_STAGE1_MIN_FREQ || ref > PLL_STAGE1_MAX_FREQ)
+ return ret;
- div = rate / (parent_rate * 2);
+ temp_setup->vco1 = ref;
- return parent_rate * div * 2;
+ ret = clk_sccg_divr2_lookup(setup, temp_setup);
+ return ret;
}
-static int clk_pll1_set_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long parent_rate)
+static int clk_sccg_divf1_lookup(struct clk_sccg_pll_setup *setup,
+ struct clk_sccg_pll_setup *temp_setup)
{
- struct clk_sccg_pll *pll = to_clk_sccg_pll(hw);
- u32 val;
- u32 divf;
+ int ret = -EINVAL;
- if (!parent_rate)
- return -EINVAL;
+ for (temp_setup->divf1 = 0; temp_setup->divf1 <= PLL_DIVF1_MAX;
+ temp_setup->divf1++) {
+ uint64_t vco1 = temp_setup->ref;
- divf = rate / (parent_rate * 2);
+ do_div(vco1, temp_setup->divr1 + 1);
+ vco1 *= 2;
+ vco1 *= temp_setup->divf1 + 1;
- val = readl_relaxed(pll->base + PLL_CFG2);
- val &= ~PLL_DIVF1_MASK;
- val |= FIELD_PREP(PLL_DIVF1_MASK, divf - 1);
- writel_relaxed(val, pll->base + PLL_CFG2);
+ ret = clk_sccg_pll2_find_setup(setup, temp_setup, vco1);
+ if (!ret) {
+ temp_setup->bypass = PLL_BYPASS_NONE;
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static int clk_sccg_divr1_lookup(struct clk_sccg_pll_setup *setup,
+ struct clk_sccg_pll_setup *temp_setup)
+{
+ int ret = -EINVAL;
+
+ for (temp_setup->divr1 = 0; temp_setup->divr1 <= PLL_DIVR1_MAX;
+ temp_setup->divr1++) {
+ temp_setup->ref_div1 = temp_setup->ref;
+ do_div(temp_setup->ref_div1, temp_setup->divr1 + 1);
+ if (temp_setup->ref_div1 >= PLL_STAGE1_REF_MIN_FREQ &&
+ temp_setup->ref_div1 <= PLL_STAGE1_REF_MAX_FREQ) {
+ ret = clk_sccg_divf1_lookup(setup, temp_setup);
+ if (!ret)
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static int clk_sccg_pll1_find_setup(struct clk_sccg_pll_setup *setup,
+ struct clk_sccg_pll_setup *temp_setup,
+ uint64_t ref)
+{
+
+ int ret = -EINVAL;
+
+ if (ref < PLL_REF_MIN_FREQ || ref > PLL_REF_MAX_FREQ)
+ return ret;
+
+ temp_setup->ref = ref;
+
+ ret = clk_sccg_divr1_lookup(setup, temp_setup);
+
+ return ret;
+}
+
+static int clk_sccg_pll_find_setup(struct clk_sccg_pll_setup *setup,
+ uint64_t prate,
+ uint64_t rate, int try_bypass)
+{
+ struct clk_sccg_pll_setup temp_setup;
+ int ret = -EINVAL;
+
+ memset(&temp_setup, 0, sizeof(struct clk_sccg_pll_setup));
+ memset(setup, 0, sizeof(struct clk_sccg_pll_setup));
+
+ temp_setup.fout_error = PLL_OUT_MAX_FREQ;
+ temp_setup.fout_request = rate;
+
+ switch (try_bypass) {
- return clk_pll_wait_lock(pll);
+ case PLL_BYPASS2:
+ if (prate == rate) {
+ setup->bypass = PLL_BYPASS2;
+ setup->fout = rate;
+ ret = 0;
+ }
+ break;
+
+ case PLL_BYPASS1:
+ ret = clk_sccg_pll2_find_setup(setup, &temp_setup, prate);
+ break;
+
+ case PLL_BYPASS_NONE:
+ ret = clk_sccg_pll1_find_setup(setup, &temp_setup, prate);
+ break;
+ }
+
+ return ret;
+}
+
+
+static int clk_sccg_pll_is_prepared(struct clk_hw *hw)
+{
+ struct clk_sccg_pll *pll = to_clk_sccg_pll(hw);
+
+ u32 val = readl_relaxed(pll->base + PLL_CFG0);
+
+ return (val & PLL_PD_MASK) ? 0 : 1;
}
-static int clk_pll1_prepare(struct clk_hw *hw)
+static int clk_sccg_pll_prepare(struct clk_hw *hw)
{
struct clk_sccg_pll *pll = to_clk_sccg_pll(hw);
u32 val;
@@ -114,10 +319,10 @@ static int clk_pll1_prepare(struct clk_hw *hw)
val &= ~PLL_PD_MASK;
writel_relaxed(val, pll->base + PLL_CFG0);
- return clk_pll_wait_lock(pll);
+ return clk_sccg_pll_wait_lock(pll);
}
-static void clk_pll1_unprepare(struct clk_hw *hw)
+static void clk_sccg_pll_unprepare(struct clk_hw *hw)
{
struct clk_sccg_pll *pll = to_clk_sccg_pll(hw);
u32 val;
@@ -125,121 +330,208 @@ static void clk_pll1_unprepare(struct clk_hw *hw)
val = readl_relaxed(pll->base + PLL_CFG0);
val |= PLL_PD_MASK;
writel_relaxed(val, pll->base + PLL_CFG0);
-
}
-static unsigned long clk_pll2_recalc_rate(struct clk_hw *hw,
+static unsigned long clk_sccg_pll_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct clk_sccg_pll *pll = to_clk_sccg_pll(hw);
- u32 val, ref, divr1, divf1, divr2, divf2;
+ u32 val, divr1, divf1, divr2, divf2, divq;
u64 temp64;
- val = readl_relaxed(pll->base + PLL_CFG0);
- switch (FIELD_GET(PLL_REF_MASK, val)) {
- case 0:
- ref = OSC_25M;
- break;
- case 1:
- ref = OSC_27M;
- break;
- default:
- ref = OSC_25M;
- break;
- }
-
val = readl_relaxed(pll->base + PLL_CFG2);
divr1 = FIELD_GET(PLL_DIVR1_MASK, val);
divr2 = FIELD_GET(PLL_DIVR2_MASK, val);
divf1 = FIELD_GET(PLL_DIVF1_MASK, val);
divf2 = FIELD_GET(PLL_DIVF2_MASK, val);
-
- temp64 = ref * 2;
- temp64 *= (divf1 + 1) * (divf2 + 1);
-
- do_div(temp64, (divr1 + 1) * (divr2 + 1));
+ divq = FIELD_GET(PLL_DIVQ_MASK, val);
+
+ temp64 = parent_rate;
+
+ val = clk_readl(pll->base + PLL_CFG0);
+ if (val & SSCG_PLL_BYPASS2_MASK) {
+ temp64 = parent_rate;
+ } else if (val & SSCG_PLL_BYPASS1_MASK) {
+ temp64 *= divf2;
+ do_div(temp64, (divr2 + 1) * (divq + 1));
+ } else {
+ temp64 *= 2;
+ temp64 *= (divf1 + 1) * (divf2 + 1);
+ do_div(temp64, (divr1 + 1) * (divr2 + 1) * (divq + 1));
+ }
return temp64;
}
-static long clk_pll2_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
+static int clk_sccg_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
{
- u32 div;
- unsigned long parent_rate = *prate;
+ struct clk_sccg_pll *pll = to_clk_sccg_pll(hw);
+ struct clk_sccg_pll_setup *setup = &pll->setup;
+ u32 val;
- if (!parent_rate)
- return 0;
+ /* set bypass here too since the parent might be the same */
+ val = clk_readl(pll->base + PLL_CFG0);
+ val &= ~SSCG_PLL_BYPASS_MASK;
+ val |= FIELD_PREP(SSCG_PLL_BYPASS_MASK, setup->bypass);
+ clk_writel(val, pll->base + PLL_CFG0);
- div = rate / parent_rate;
+ val = readl_relaxed(pll->base + PLL_CFG2);
+ val &= ~(PLL_DIVF1_MASK | PLL_DIVF2_MASK);
+ val &= ~(PLL_DIVR1_MASK | PLL_DIVR2_MASK | PLL_DIVQ_MASK);
+ val |= FIELD_PREP(PLL_DIVF1_MASK, setup->divf1);
+ val |= FIELD_PREP(PLL_DIVF2_MASK, setup->divf2);
+ val |= FIELD_PREP(PLL_DIVR1_MASK, setup->divr1);
+ val |= FIELD_PREP(PLL_DIVR2_MASK, setup->divr2);
+ val |= FIELD_PREP(PLL_DIVQ_MASK, setup->divq);
+ writel_relaxed(val, pll->base + PLL_CFG2);
- return parent_rate * div;
+ return clk_sccg_pll_wait_lock(pll);
}
-static int clk_pll2_set_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long parent_rate)
+static u8 clk_sccg_pll_get_parent(struct clk_hw *hw)
{
+ struct clk_sccg_pll *pll = to_clk_sccg_pll(hw);
u32 val;
- u32 divf;
+ u8 ret = pll->parent;
+
+ val = clk_readl(pll->base + PLL_CFG0);
+ if (val & SSCG_PLL_BYPASS2_MASK)
+ ret = pll->bypass2;
+ else if (val & SSCG_PLL_BYPASS1_MASK)
+ ret = pll->bypass1;
+ return ret;
+}
+
+static int clk_sccg_pll_set_parent(struct clk_hw *hw, u8 index)
+{
struct clk_sccg_pll *pll = to_clk_sccg_pll(hw);
+ u32 val;
- if (!parent_rate)
- return -EINVAL;
+ val = clk_readl(pll->base + PLL_CFG0);
+ val &= ~SSCG_PLL_BYPASS_MASK;
+ val |= FIELD_PREP(SSCG_PLL_BYPASS_MASK, pll->setup.bypass);
+ clk_writel(val, pll->base + PLL_CFG0);
- divf = rate / parent_rate;
+ return clk_sccg_pll_wait_lock(pll);
+}
- val = readl_relaxed(pll->base + PLL_CFG2);
- val &= ~PLL_DIVF2_MASK;
- val |= FIELD_PREP(PLL_DIVF2_MASK, divf - 1);
- writel_relaxed(val, pll->base + PLL_CFG2);
+static int __clk_sccg_pll_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req,
+ uint64_t min,
+ uint64_t max,
+ uint64_t rate,
+ int bypass)
+{
+ struct clk_sccg_pll *pll = to_clk_sccg_pll(hw);
+ struct clk_sccg_pll_setup *setup = &pll->setup;
+ struct clk_hw *parent_hw = NULL;
+ int bypass_parent_index;
+ int ret = -EINVAL;
+
+ req->max_rate = max;
+ req->min_rate = min;
+
+ switch (bypass) {
+ case PLL_BYPASS2:
+ bypass_parent_index = pll->bypass2;
+ break;
+ case PLL_BYPASS1:
+ bypass_parent_index = pll->bypass1;
+ break;
+ default:
+ bypass_parent_index = pll->parent;
+ break;
+ }
+
+ parent_hw = clk_hw_get_parent_by_index(hw, bypass_parent_index);
+ ret = __clk_determine_rate(parent_hw, req);
+ if (!ret) {
+ ret = clk_sccg_pll_find_setup(setup, req->rate,
+ rate, bypass);
+ }
+
+ req->best_parent_hw = parent_hw;
+ req->best_parent_rate = req->rate;
+ req->rate = setup->fout;
- return clk_pll_wait_lock(pll);
+ return ret;
}
-static const struct clk_ops clk_sccg_pll1_ops = {
- .is_prepared = clk_pll1_is_prepared,
- .recalc_rate = clk_pll1_recalc_rate,
- .round_rate = clk_pll1_round_rate,
- .set_rate = clk_pll1_set_rate,
-};
+static int clk_sccg_pll_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct clk_sccg_pll *pll = to_clk_sccg_pll(hw);
+ struct clk_sccg_pll_setup *setup = &pll->setup;
+ uint64_t rate = req->rate;
+ uint64_t min = req->min_rate;
+ uint64_t max = req->max_rate;
+ int ret = -EINVAL;
+
+ if (rate < PLL_OUT_MIN_FREQ || rate > PLL_OUT_MAX_FREQ)
+ return ret;
+
+ ret = __clk_sccg_pll_determine_rate(hw, req, req->rate, req->rate,
+ rate, PLL_BYPASS2);
+ if (!ret)
+ return ret;
+
+ ret = __clk_sccg_pll_determine_rate(hw, req, PLL_STAGE1_REF_MIN_FREQ,
+ PLL_STAGE1_REF_MAX_FREQ, rate,
+ PLL_BYPASS1);
+ if (!ret)
+ return ret;
+
+ ret = __clk_sccg_pll_determine_rate(hw, req, PLL_REF_MIN_FREQ,
+ PLL_REF_MAX_FREQ, rate,
+ PLL_BYPASS_NONE);
+ if (!ret)
+ return ret;
+
+ if (setup->fout >= min && setup->fout <= max)
+ ret = 0;
+
+ return ret;
+}
-static const struct clk_ops clk_sccg_pll2_ops = {
- .prepare = clk_pll1_prepare,
- .unprepare = clk_pll1_unprepare,
- .recalc_rate = clk_pll2_recalc_rate,
- .round_rate = clk_pll2_round_rate,
- .set_rate = clk_pll2_set_rate,
+static const struct clk_ops clk_sccg_pll_ops = {
+ .prepare = clk_sccg_pll_prepare,
+ .unprepare = clk_sccg_pll_unprepare,
+ .is_prepared = clk_sccg_pll_is_prepared,
+ .recalc_rate = clk_sccg_pll_recalc_rate,
+ .set_rate = clk_sccg_pll_set_rate,
+ .set_parent = clk_sccg_pll_set_parent,
+ .get_parent = clk_sccg_pll_get_parent,
+ .determine_rate = clk_sccg_pll_determine_rate,
};
struct clk *imx_clk_sccg_pll(const char *name,
- const char *parent_name,
+ const char * const *parent_names,
+ u8 num_parents,
+ u8 parent, u8 bypass1, u8 bypass2,
void __iomem *base,
- enum imx_sccg_pll_type pll_type)
+ unsigned long flags)
{
struct clk_sccg_pll *pll;
struct clk_init_data init;
struct clk_hw *hw;
int ret;
- switch (pll_type) {
- case SCCG_PLL1:
- init.ops = &clk_sccg_pll1_ops;
- break;
- case SCCG_PLL2:
- init.ops = &clk_sccg_pll2_ops;
- break;
- default:
- return ERR_PTR(-EINVAL);
- }
-
pll = kzalloc(sizeof(*pll), GFP_KERNEL);
if (!pll)
return ERR_PTR(-ENOMEM);
+ pll->parent = parent;
+ pll->bypass1 = bypass1;
+ pll->bypass2 = bypass2;
+
+ pll->base = base;
init.name = name;
- init.flags = 0;
- init.parent_names = &parent_name;
- init.num_parents = 1;
+ init.ops = &clk_sccg_pll_ops;
+
+ init.flags = flags;
+ init.parent_names = parent_names;
+ init.num_parents = num_parents;
pll->base = base;
pll->hw.init = &init;
diff --git a/drivers/clk/imx/clk-scu.c b/drivers/clk/imx/clk-scu.c
index 7ccf7edfe11c..fbef740704d0 100644
--- a/drivers/clk/imx/clk-scu.c
+++ b/drivers/clk/imx/clk-scu.c
@@ -4,12 +4,17 @@
* Dong Aisheng <aisheng.dong@nxp.com>
*/
+#include <dt-bindings/firmware/imx/rsrc.h>
+#include <linux/arm-smccc.h>
#include <linux/clk-provider.h>
#include <linux/err.h>
#include <linux/slab.h>
#include "clk-scu.h"
+#define IMX_SIP_CPUFREQ 0xC2000001
+#define IMX_SIP_SET_CPUFREQ 0x00
+
static struct imx_sc_ipc *ccm_ipc_handle;
/*
@@ -66,6 +71,41 @@ struct imx_sc_msg_get_clock_rate {
};
/*
+ * struct imx_sc_msg_get_clock_parent - clock get parent protocol
+ * @hdr: SCU protocol header
+ * @req: get parent request protocol
+ * @resp: get parent response protocol
+ *
+ * This structure describes the SCU protocol of clock get parent
+ */
+struct imx_sc_msg_get_clock_parent {
+ struct imx_sc_rpc_msg hdr;
+ union {
+ struct req_get_clock_parent {
+ __le16 resource;
+ u8 clk;
+ } __packed req;
+ struct resp_get_clock_parent {
+ u8 parent;
+ } resp;
+ } data;
+};
+
+/*
+ * struct imx_sc_msg_set_clock_parent - clock set parent protocol
+ * @hdr: SCU protocol header
+ * @req: set parent request protocol
+ *
+ * This structure describes the SCU protocol of clock set parent
+ */
+struct imx_sc_msg_set_clock_parent {
+ struct imx_sc_rpc_msg hdr;
+ __le16 resource;
+ u8 clk;
+ u8 parent;
+} __packed;
+
+/*
* struct imx_sc_msg_req_clock_enable - clock gate protocol
* @hdr: SCU protocol header
* @resource: clock resource to gate
@@ -145,6 +185,25 @@ static long clk_scu_round_rate(struct clk_hw *hw, unsigned long rate,
return rate;
}
+static int clk_scu_atf_set_cpu_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_scu *clk = to_clk_scu(hw);
+ struct arm_smccc_res res;
+ unsigned long cluster_id;
+
+ if (clk->rsrc_id == IMX_SC_R_A35)
+ cluster_id = 0;
+ else
+ return -EINVAL;
+
+ /* CPU frequency scaling can ONLY be done by ARM-Trusted-Firmware */
+ arm_smccc_smc(IMX_SIP_CPUFREQ, IMX_SIP_SET_CPUFREQ,
+ cluster_id, rate, 0, 0, 0, 0, &res);
+
+ return 0;
+}
+
/*
* clk_scu_set_rate - Set rate for a SCU clock
* @hw: clock to change rate for
@@ -173,6 +232,49 @@ static int clk_scu_set_rate(struct clk_hw *hw, unsigned long rate,
return imx_scu_call_rpc(ccm_ipc_handle, &msg, true);
}
+static u8 clk_scu_get_parent(struct clk_hw *hw)
+{
+ struct clk_scu *clk = to_clk_scu(hw);
+ struct imx_sc_msg_get_clock_parent msg;
+ struct imx_sc_rpc_msg *hdr = &msg.hdr;
+ int ret;
+
+ hdr->ver = IMX_SC_RPC_VERSION;
+ hdr->svc = IMX_SC_RPC_SVC_PM;
+ hdr->func = IMX_SC_PM_FUNC_GET_CLOCK_PARENT;
+ hdr->size = 2;
+
+ msg.data.req.resource = cpu_to_le16(clk->rsrc_id);
+ msg.data.req.clk = clk->clk_type;
+
+ ret = imx_scu_call_rpc(ccm_ipc_handle, &msg, true);
+ if (ret) {
+ pr_err("%s: failed to get clock parent %d\n",
+ clk_hw_get_name(hw), ret);
+ return 0;
+ }
+
+ return msg.data.resp.parent;
+}
+
+static int clk_scu_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct clk_scu *clk = to_clk_scu(hw);
+ struct imx_sc_msg_set_clock_parent msg;
+ struct imx_sc_rpc_msg *hdr = &msg.hdr;
+
+ hdr->ver = IMX_SC_RPC_VERSION;
+ hdr->svc = IMX_SC_RPC_SVC_PM;
+ hdr->func = IMX_SC_PM_FUNC_SET_CLOCK_PARENT;
+ hdr->size = 2;
+
+ msg.resource = cpu_to_le16(clk->rsrc_id);
+ msg.clk = clk->clk_type;
+ msg.parent = index;
+
+ return imx_scu_call_rpc(ccm_ipc_handle, &msg, true);
+}
+
static int sc_pm_clock_enable(struct imx_sc_ipc *ipc, u16 resource,
u8 clk, bool enable, bool autog)
{
@@ -228,11 +330,22 @@ static const struct clk_ops clk_scu_ops = {
.recalc_rate = clk_scu_recalc_rate,
.round_rate = clk_scu_round_rate,
.set_rate = clk_scu_set_rate,
+ .get_parent = clk_scu_get_parent,
+ .set_parent = clk_scu_set_parent,
+ .prepare = clk_scu_prepare,
+ .unprepare = clk_scu_unprepare,
+};
+
+static const struct clk_ops clk_scu_cpu_ops = {
+ .recalc_rate = clk_scu_recalc_rate,
+ .round_rate = clk_scu_round_rate,
+ .set_rate = clk_scu_atf_set_cpu_rate,
.prepare = clk_scu_prepare,
.unprepare = clk_scu_unprepare,
};
-struct clk_hw *imx_clk_scu(const char *name, u32 rsrc_id, u8 clk_type)
+struct clk_hw *__imx_clk_scu(const char *name, const char * const *parents,
+ int num_parents, u32 rsrc_id, u8 clk_type)
{
struct clk_init_data init;
struct clk_scu *clk;
@@ -248,7 +361,13 @@ struct clk_hw *imx_clk_scu(const char *name, u32 rsrc_id, u8 clk_type)
init.name = name;
init.ops = &clk_scu_ops;
- init.num_parents = 0;
+ if (rsrc_id == IMX_SC_R_A35)
+ init.ops = &clk_scu_cpu_ops;
+ else
+ init.ops = &clk_scu_ops;
+ init.parent_names = parents;
+ init.num_parents = num_parents;
+
/*
* Note on MX8, the clocks are tightly coupled with power domain
* that once the power domain is off, the clock status may be
diff --git a/drivers/clk/imx/clk-scu.h b/drivers/clk/imx/clk-scu.h
index 52c1746ec988..2bcfaf06a458 100644
--- a/drivers/clk/imx/clk-scu.h
+++ b/drivers/clk/imx/clk-scu.h
@@ -10,7 +10,21 @@
#include <linux/firmware/imx/sci.h>
int imx_clk_scu_init(void);
-struct clk_hw *imx_clk_scu(const char *name, u32 rsrc_id, u8 clk_type);
+
+struct clk_hw *__imx_clk_scu(const char *name, const char * const *parents,
+ int num_parents, u32 rsrc_id, u8 clk_type);
+
+static inline struct clk_hw *imx_clk_scu(const char *name, u32 rsrc_id,
+ u8 clk_type)
+{
+ return __imx_clk_scu(name, NULL, 0, rsrc_id, clk_type);
+}
+
+static inline struct clk_hw *imx_clk_scu2(const char *name, const char * const *parents,
+ int num_parents, u32 rsrc_id, u8 clk_type)
+{
+ return __imx_clk_scu(name, parents, num_parents, rsrc_id, clk_type);
+}
struct clk_hw *imx_clk_lpcg_scu(const char *name, const char *parent_name,
unsigned long flags, void __iomem *reg,
diff --git a/drivers/clk/imx/clk-vf610.c b/drivers/clk/imx/clk-vf610.c
index 6dae54325a91..a334667c450a 100644
--- a/drivers/clk/imx/clk-vf610.c
+++ b/drivers/clk/imx/clk-vf610.c
@@ -203,6 +203,7 @@ static void __init vf610_clocks_init(struct device_node *ccm_node)
np = of_find_compatible_node(NULL, NULL, "fsl,vf610-anatop");
anatop_base = of_iomap(np, 0);
BUG_ON(!anatop_base);
+ of_node_put(np);
np = ccm_node;
ccm_base = of_iomap(np, 0);
diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h
index 028312de21b8..5748ec8673e4 100644
--- a/drivers/clk/imx/clk.h
+++ b/drivers/clk/imx/clk.h
@@ -27,6 +27,30 @@ enum imx_sccg_pll_type {
SCCG_PLL2,
};
+enum imx_pll14xx_type {
+ PLL_1416X,
+ PLL_1443X,
+};
+
+/* NOTE: Rate table should be kept sorted in descending order. */
+struct imx_pll14xx_rate_table {
+ unsigned int rate;
+ unsigned int pdiv;
+ unsigned int mdiv;
+ unsigned int sdiv;
+ unsigned int kdiv;
+};
+
+struct imx_pll14xx_clk {
+ enum imx_pll14xx_type type;
+ const struct imx_pll14xx_rate_table *rate_table;
+ int rate_count;
+ int flags;
+};
+
+struct clk *imx_clk_pll14xx(const char *name, const char *parent_name,
+ void __iomem *base, const struct imx_pll14xx_clk *pll_clk);
+
struct clk *imx_clk_pllv1(enum imx_pllv1_type type, const char *name,
const char *parent, void __iomem *base);
@@ -36,9 +60,12 @@ struct clk *imx_clk_pllv2(const char *name, const char *parent,
struct clk *imx_clk_frac_pll(const char *name, const char *parent_name,
void __iomem *base);
-struct clk *imx_clk_sccg_pll(const char *name, const char *parent_name,
- void __iomem *base,
- enum imx_sccg_pll_type pll_type);
+struct clk *imx_clk_sccg_pll(const char *name,
+ const char * const *parent_names,
+ u8 num_parents,
+ u8 parent, u8 bypass1, u8 bypass2,
+ void __iomem *base,
+ unsigned long flags);
enum imx_pllv3_type {
IMX_PLLV3_GENERIC,
@@ -329,7 +356,8 @@ static inline struct clk *imx_clk_mux_flags(const char *name,
}
static inline struct clk *imx_clk_mux2_flags(const char *name,
- void __iomem *reg, u8 shift, u8 width, const char **parents,
+ void __iomem *reg, u8 shift, u8 width,
+ const char * const *parents,
int num_parents, unsigned long flags)
{
return clk_register_mux(NULL, name, parents, num_parents,
@@ -354,7 +382,7 @@ struct clk *imx_clk_cpu(const char *name, const char *parent_name,
struct clk *step);
struct clk *imx8m_clk_composite_flags(const char *name,
- const char **parent_names,
+ const char * const *parent_names,
int num_parents, void __iomem *reg,
unsigned long flags);
diff --git a/drivers/clk/ingenic/cgu.c b/drivers/clk/ingenic/cgu.c
index 5ef7d9ba2195..510b685212d3 100644
--- a/drivers/clk/ingenic/cgu.c
+++ b/drivers/clk/ingenic/cgu.c
@@ -83,7 +83,7 @@ ingenic_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
const struct ingenic_cgu_clk_info *clk_info;
const struct ingenic_cgu_pll_info *pll_info;
unsigned m, n, od_enc, od;
- bool bypass, enable;
+ bool bypass;
unsigned long flags;
u32 ctl;
@@ -103,7 +103,6 @@ ingenic_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
od_enc &= GENMASK(pll_info->od_bits - 1, 0);
bypass = !pll_info->no_bypass_bit &&
!!(ctl & BIT(pll_info->bypass_bit));
- enable = !!(ctl & BIT(pll_info->enable_bit));
if (bypass)
return parent_rate;
@@ -426,16 +425,16 @@ ingenic_clk_round_rate(struct clk_hw *hw, unsigned long req_rate,
struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw);
struct ingenic_cgu *cgu = ingenic_clk->cgu;
const struct ingenic_cgu_clk_info *clk_info;
- long rate = *parent_rate;
+ unsigned int div = 1;
clk_info = &cgu->clock_info[ingenic_clk->idx];
if (clk_info->type & CGU_CLK_DIV)
- rate /= ingenic_clk_calc_div(clk_info, *parent_rate, req_rate);
+ div = ingenic_clk_calc_div(clk_info, *parent_rate, req_rate);
else if (clk_info->type & CGU_CLK_FIXDIV)
- rate /= clk_info->fixdiv.div;
+ div = clk_info->fixdiv.div;
- return rate;
+ return DIV_ROUND_UP(*parent_rate, div);
}
static int
@@ -455,7 +454,7 @@ ingenic_clk_set_rate(struct clk_hw *hw, unsigned long req_rate,
if (clk_info->type & CGU_CLK_DIV) {
div = ingenic_clk_calc_div(clk_info, parent_rate, req_rate);
- rate = parent_rate / div;
+ rate = DIV_ROUND_UP(parent_rate, div);
if (rate != req_rate)
return -EINVAL;
diff --git a/drivers/clk/ingenic/cgu.h b/drivers/clk/ingenic/cgu.h
index 502bcbb61b04..e12716d8ce3c 100644
--- a/drivers/clk/ingenic/cgu.h
+++ b/drivers/clk/ingenic/cgu.h
@@ -80,7 +80,7 @@ struct ingenic_cgu_mux_info {
* @reg: offset of the divider control register within the CGU
* @shift: number of bits to left shift the divide value by (ie. the index of
* the lowest bit of the divide value within its control register)
- * @div: number of bits to divide the divider value by (i.e. if the
+ * @div: number to divide the divider value by (i.e. if the
* effective divider value is the value written to the register
* multiplied by some constant)
* @bits: the size of the divide value in bits
diff --git a/drivers/clk/ingenic/jz4740-cgu.c b/drivers/clk/ingenic/jz4740-cgu.c
index 4479c102e899..b86edd328249 100644
--- a/drivers/clk/ingenic/jz4740-cgu.c
+++ b/drivers/clk/ingenic/jz4740-cgu.c
@@ -165,7 +165,7 @@ static const struct ingenic_cgu_clk_info jz4740_cgu_clocks[] = {
.parents = { JZ4740_CLK_EXT, JZ4740_CLK_PLL_HALF, -1, -1 },
.mux = { CGU_REG_CPCCR, 29, 1 },
.div = { CGU_REG_CPCCR, 23, 1, 6, -1, -1, -1 },
- .gate = { CGU_REG_SCR, 6 },
+ .gate = { CGU_REG_SCR, 6, true },
},
/* Gate-only clocks */
diff --git a/drivers/clk/mediatek/clk-gate.c b/drivers/clk/mediatek/clk-gate.c
index 934bf0e45e26..9628d4e7690b 100644
--- a/drivers/clk/mediatek/clk-gate.c
+++ b/drivers/clk/mediatek/clk-gate.c
@@ -157,7 +157,8 @@ struct clk *mtk_clk_register_gate(
int clr_ofs,
int sta_ofs,
u8 bit,
- const struct clk_ops *ops)
+ const struct clk_ops *ops,
+ unsigned long flags)
{
struct mtk_clk_gate *cg;
struct clk *clk;
@@ -172,6 +173,7 @@ struct clk *mtk_clk_register_gate(
init.parent_names = parent_name ? &parent_name : NULL;
init.num_parents = parent_name ? 1 : 0;
init.ops = ops;
+ init.flags = flags;
cg->regmap = regmap;
cg->set_ofs = set_ofs;
diff --git a/drivers/clk/mediatek/clk-gate.h b/drivers/clk/mediatek/clk-gate.h
index 72ef89b3ad7b..9f766dfe1d57 100644
--- a/drivers/clk/mediatek/clk-gate.h
+++ b/drivers/clk/mediatek/clk-gate.h
@@ -47,6 +47,7 @@ struct clk *mtk_clk_register_gate(
int clr_ofs,
int sta_ofs,
u8 bit,
- const struct clk_ops *ops);
+ const struct clk_ops *ops,
+ unsigned long flags);
#endif /* __DRV_CLK_GATE_H */
diff --git a/drivers/clk/mediatek/clk-mt2701.c b/drivers/clk/mediatek/clk-mt2701.c
index ab6ab07f53e6..905a2316f6a7 100644
--- a/drivers/clk/mediatek/clk-mt2701.c
+++ b/drivers/clk/mediatek/clk-mt2701.c
@@ -535,8 +535,8 @@ static const struct mtk_composite top_muxes[] = {
0x0080, 8, 2, 15),
MUX_GATE(CLK_TOP_DPI0_SEL, "dpi0_sel", dpi0_parents,
0x0080, 16, 3, 23),
- MUX_GATE(CLK_TOP_DPI1_SEL, "dpi1_sel", dpi1_parents,
- 0x0080, 24, 2, 31),
+ MUX_GATE_FLAGS_2(CLK_TOP_DPI1_SEL, "dpi1_sel", dpi1_parents,
+ 0x0080, 24, 2, 31, 0, CLK_MUX_ROUND_CLOSEST),
MUX_GATE(CLK_TOP_TVE_SEL, "tve_sel", tve_parents,
0x0090, 0, 3, 7),
diff --git a/drivers/clk/mediatek/clk-mt2712.c b/drivers/clk/mediatek/clk-mt2712.c
index 991d4093726e..b09cb3d99f66 100644
--- a/drivers/clk/mediatek/clk-mt2712.c
+++ b/drivers/clk/mediatek/clk-mt2712.c
@@ -223,6 +223,8 @@ static const struct mtk_fixed_factor top_divs[] = {
4),
FACTOR(CLK_TOP_APLL1_D3, "apll1_d3", "apll1_ck", 1,
3),
+ FACTOR(CLK_TOP_APLL2_D3, "apll2_d3", "apll2_ck", 1,
+ 3),
};
static const char * const axi_parents[] = {
@@ -594,7 +596,8 @@ static const char * const a1sys_hp_parents[] = {
"apll1_ck",
"apll1_d2",
"apll1_d4",
- "apll1_d8"
+ "apll1_d8",
+ "apll1_d3"
};
static const char * const a2sys_hp_parents[] = {
@@ -602,7 +605,8 @@ static const char * const a2sys_hp_parents[] = {
"apll2_ck",
"apll2_d2",
"apll2_d4",
- "apll2_d8"
+ "apll2_d8",
+ "apll2_d3"
};
static const char * const asm_l_parents[] = {
@@ -1463,7 +1467,6 @@ static struct platform_driver clk_mt2712_drv = {
.probe = clk_mt2712_probe,
.driver = {
.name = "clk-mt2712",
- .owner = THIS_MODULE,
.of_match_table = of_match_clk_mt2712,
},
};
diff --git a/drivers/clk/mediatek/clk-mt6797.c b/drivers/clk/mediatek/clk-mt6797.c
index 5702bc974ed9..c2b46b184b9a 100644
--- a/drivers/clk/mediatek/clk-mt6797.c
+++ b/drivers/clk/mediatek/clk-mt6797.c
@@ -324,6 +324,10 @@ static const char * const anc_md32_parents[] = {
"univpll_d5",
};
+/*
+ * Clock mux ddrphycfg is needed by the DRAM controller. We mark it as
+ * critical as otherwise the system will hang after boot.
+ */
static const struct mtk_composite top_muxes[] = {
MUX(CLK_TOP_MUX_ULPOSC_AXI_CK_MUX_PRE, "ulposc_axi_ck_mux_pre",
ulposc_axi_ck_mux_pre_parents, 0x0040, 3, 1),
@@ -331,8 +335,8 @@ static const struct mtk_composite top_muxes[] = {
ulposc_axi_ck_mux_parents, 0x0040, 2, 1),
MUX(CLK_TOP_MUX_AXI, "axi_sel", axi_parents,
0x0040, 0, 2),
- MUX(CLK_TOP_MUX_DDRPHYCFG, "ddrphycfg_sel", ddrphycfg_parents,
- 0x0040, 16, 2),
+ MUX_FLAGS(CLK_TOP_MUX_DDRPHYCFG, "ddrphycfg_sel", ddrphycfg_parents,
+ 0x0040, 16, 2, CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
MUX(CLK_TOP_MUX_MM, "mm_sel", mm_parents,
0x0040, 24, 2),
MUX_GATE(CLK_TOP_MUX_PWM, "pwm_sel", pwm_parents, 0x0050, 0, 3, 7),
@@ -424,33 +428,45 @@ static const struct mtk_gate_regs infra2_cg_regs = {
.sta_ofs = 0x00b0,
};
-#define GATE_ICG0(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &infra0_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr, \
+#define GATE_ICG0(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &infra0_cg_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_ops_setclr, \
}
-#define GATE_ICG1(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &infra1_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr, \
+#define GATE_ICG1(_id, _name, _parent, _shift) \
+ GATE_ICG1_FLAGS(_id, _name, _parent, _shift, 0)
+
+#define GATE_ICG1_FLAGS(_id, _name, _parent, _shift, _flags) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &infra1_cg_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_ops_setclr, \
+ .flags = _flags, \
}
-#define GATE_ICG2(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &infra2_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr, \
+#define GATE_ICG2(_id, _name, _parent, _shift) \
+ GATE_ICG2_FLAGS(_id, _name, _parent, _shift, 0)
+
+#define GATE_ICG2_FLAGS(_id, _name, _parent, _shift, _flags) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &infra2_cg_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_ops_setclr, \
+ .flags = _flags, \
}
+/*
+ * Clock gates dramc and dramc_b are needed by the DRAM controller.
+ * We mark them as critical as otherwise the system will hang after boot.
+ */
static const struct mtk_gate infra_clks[] = {
GATE_ICG0(CLK_INFRA_PMIC_TMR, "infra_pmic_tmr", "ulposc", 0),
GATE_ICG0(CLK_INFRA_PMIC_AP, "infra_pmic_ap", "pmicspi_sel", 1),
@@ -505,7 +521,8 @@ static const struct mtk_gate infra_clks[] = {
GATE_ICG1(CLK_INFRA_CCIF_AP, "infra_ccif_ap", "axi_sel", 23),
GATE_ICG1(CLK_INFRA_AUDIO, "infra_audio", "axi_sel", 25),
GATE_ICG1(CLK_INFRA_CCIF_MD, "infra_ccif_md", "axi_sel", 26),
- GATE_ICG1(CLK_INFRA_DRAMC_F26M, "infra_dramc_f26m", "clk26m", 31),
+ GATE_ICG1_FLAGS(CLK_INFRA_DRAMC_F26M, "infra_dramc_f26m",
+ "clk26m", 31, CLK_IS_CRITICAL),
GATE_ICG2(CLK_INFRA_I2C4, "infra_i2c4", "axi_sel", 0),
GATE_ICG2(CLK_INFRA_I2C_APPM, "infra_i2c_appm", "axi_sel", 1),
GATE_ICG2(CLK_INFRA_I2C_GPUPM, "infra_i2c_gpupm", "axi_sel", 2),
@@ -516,7 +533,8 @@ static const struct mtk_gate infra_clks[] = {
GATE_ICG2(CLK_INFRA_I2C5, "infra_i2c5", "axi_sel", 7),
GATE_ICG2(CLK_INFRA_SYS_CIRQ, "infra_sys_cirq", "axi_sel", 8),
GATE_ICG2(CLK_INFRA_SPI1, "infra_spi1", "spi_sel", 10),
- GATE_ICG2(CLK_INFRA_DRAMC_B_F26M, "infra_dramc_b_f26m", "clk26m", 11),
+ GATE_ICG2_FLAGS(CLK_INFRA_DRAMC_B_F26M, "infra_dramc_b_f26m",
+ "clk26m", 11, CLK_IS_CRITICAL),
GATE_ICG2(CLK_INFRA_ANC_MD32, "infra_anc_md32", "anc_md32_sel", 12),
GATE_ICG2(CLK_INFRA_ANC_MD32_32K, "infra_anc_md32_32k", "clk26m", 13),
GATE_ICG2(CLK_INFRA_DVFS_SPM1, "infra_dvfs_spm1", "axi_sel", 15),
diff --git a/drivers/clk/mediatek/clk-mt8173.c b/drivers/clk/mediatek/clk-mt8173.c
index 96c292c3e440..deedeb3ea33b 100644
--- a/drivers/clk/mediatek/clk-mt8173.c
+++ b/drivers/clk/mediatek/clk-mt8173.c
@@ -533,7 +533,7 @@ static const char * const ca53_parents[] __initconst = {
"univpll"
};
-static const char * const ca57_parents[] __initconst = {
+static const char * const ca72_parents[] __initconst = {
"clk26m",
"armca15pll",
"mainpll",
@@ -542,7 +542,7 @@ static const char * const ca57_parents[] __initconst = {
static const struct mtk_composite cpu_muxes[] __initconst = {
MUX(CLK_INFRA_CA53SEL, "infra_ca53_sel", ca53_parents, 0x0000, 0, 2),
- MUX(CLK_INFRA_CA57SEL, "infra_ca57_sel", ca57_parents, 0x0000, 2, 2),
+ MUX(CLK_INFRA_CA72SEL, "infra_ca72_sel", ca72_parents, 0x0000, 2, 2),
};
static const struct mtk_composite top_muxes[] __initconst = {
diff --git a/drivers/clk/mediatek/clk-mtk.c b/drivers/clk/mediatek/clk-mtk.c
index 9c0ae4278a94..5531dd2e496d 100644
--- a/drivers/clk/mediatek/clk-mtk.c
+++ b/drivers/clk/mediatek/clk-mtk.c
@@ -130,7 +130,7 @@ int mtk_clk_register_gates(struct device_node *node,
gate->regs->set_ofs,
gate->regs->clr_ofs,
gate->regs->sta_ofs,
- gate->shift, gate->ops);
+ gate->shift, gate->ops, gate->flags);
if (IS_ERR(clk)) {
pr_err("Failed to register clk %s: %ld\n",
@@ -167,7 +167,7 @@ struct clk *mtk_clk_register_composite(const struct mtk_composite *mc,
mux->mask = BIT(mc->mux_width) - 1;
mux->shift = mc->mux_shift;
mux->lock = lock;
-
+ mux->flags = mc->mux_flags;
mux_hw = &mux->hw;
mux_ops = &clk_mux_ops;
diff --git a/drivers/clk/mediatek/clk-mtk.h b/drivers/clk/mediatek/clk-mtk.h
index f83c2bbb677e..fb27b5bf30d9 100644
--- a/drivers/clk/mediatek/clk-mtk.h
+++ b/drivers/clk/mediatek/clk-mtk.h
@@ -81,15 +81,13 @@ struct mtk_composite {
signed char divider_shift;
signed char divider_width;
+ u8 mux_flags;
+
signed char num_parents;
};
-/*
- * In case the rate change propagation to parent clocks is undesirable,
- * this macro allows to specify the clock flags manually.
- */
-#define MUX_GATE_FLAGS(_id, _name, _parents, _reg, _shift, _width, \
- _gate, _flags) { \
+#define MUX_GATE_FLAGS_2(_id, _name, _parents, _reg, _shift, \
+ _width, _gate, _flags, _muxflags) { \
.id = _id, \
.name = _name, \
.mux_reg = _reg, \
@@ -101,9 +99,19 @@ struct mtk_composite {
.parent_names = _parents, \
.num_parents = ARRAY_SIZE(_parents), \
.flags = _flags, \
+ .mux_flags = _muxflags, \
}
/*
+ * In case the rate change propagation to parent clocks is undesirable,
+ * this macro allows to specify the clock flags manually.
+ */
+#define MUX_GATE_FLAGS(_id, _name, _parents, _reg, _shift, _width, \
+ _gate, _flags) \
+ MUX_GATE_FLAGS_2(_id, _name, _parents, _reg, \
+ _shift, _width, _gate, _flags, 0)
+
+/*
* Unless necessary, all MUX_GATE clocks propagate rate changes to their
* parent clock by default.
*/
@@ -111,7 +119,11 @@ struct mtk_composite {
MUX_GATE_FLAGS(_id, _name, _parents, _reg, _shift, _width, \
_gate, CLK_SET_RATE_PARENT)
-#define MUX(_id, _name, _parents, _reg, _shift, _width) { \
+#define MUX(_id, _name, _parents, _reg, _shift, _width) \
+ MUX_FLAGS(_id, _name, _parents, _reg, \
+ _shift, _width, CLK_SET_RATE_PARENT)
+
+#define MUX_FLAGS(_id, _name, _parents, _reg, _shift, _width, _flags) { \
.id = _id, \
.name = _name, \
.mux_reg = _reg, \
@@ -121,7 +133,7 @@ struct mtk_composite {
.divider_shift = -1, \
.parent_names = _parents, \
.num_parents = ARRAY_SIZE(_parents), \
- .flags = CLK_SET_RATE_PARENT, \
+ .flags = _flags, \
}
#define DIV_GATE(_id, _name, _parent, _gate_reg, _gate_shift, _div_reg, \
@@ -158,6 +170,7 @@ struct mtk_gate {
const struct mtk_gate_regs *regs;
int shift;
const struct clk_ops *ops;
+ unsigned long flags;
};
int mtk_clk_register_gates(struct device_node *node,
diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig
index efaa70f682b4..3858747f5438 100644
--- a/drivers/clk/meson/Kconfig
+++ b/drivers/clk/meson/Kconfig
@@ -1,27 +1,52 @@
-config COMMON_CLK_AMLOGIC
- bool
- depends on ARCH_MESON || COMPILE_TEST
- select COMMON_CLK_REGMAP_MESON
+config COMMON_CLK_MESON_INPUT
+ tristate
-config COMMON_CLK_AMLOGIC_AUDIO
- bool
- depends on ARCH_MESON || COMPILE_TEST
- select COMMON_CLK_AMLOGIC
+config COMMON_CLK_MESON_REGMAP
+ tristate
+ select REGMAP
-config COMMON_CLK_MESON_AO
- bool
- depends on OF
- depends on ARCH_MESON || COMPILE_TEST
- select COMMON_CLK_REGMAP_MESON
+config COMMON_CLK_MESON_DUALDIV
+ tristate
+ select COMMON_CLK_MESON_REGMAP
+
+config COMMON_CLK_MESON_MPLL
+ tristate
+ select COMMON_CLK_MESON_REGMAP
+
+config COMMON_CLK_MESON_PHASE
+ tristate
+ select COMMON_CLK_MESON_REGMAP
+
+config COMMON_CLK_MESON_PLL
+ tristate
+ select COMMON_CLK_MESON_REGMAP
+
+config COMMON_CLK_MESON_SCLK_DIV
+ tristate
+ select COMMON_CLK_MESON_REGMAP
+
+config COMMON_CLK_MESON_VID_PLL_DIV
+ tristate
+ select COMMON_CLK_MESON_REGMAP
+
+config COMMON_CLK_MESON_AO_CLKC
+ tristate
+ select COMMON_CLK_MESON_REGMAP
+ select COMMON_CLK_MESON_INPUT
select RESET_CONTROLLER
-config COMMON_CLK_REGMAP_MESON
- bool
- select REGMAP
+config COMMON_CLK_MESON_EE_CLKC
+ tristate
+ select COMMON_CLK_MESON_REGMAP
+ select COMMON_CLK_MESON_INPUT
config COMMON_CLK_MESON8B
bool
- select COMMON_CLK_AMLOGIC
+ depends on ARCH_MESON
+ select COMMON_CLK_MESON_REGMAP
+ select COMMON_CLK_MESON_MPLL
+ select COMMON_CLK_MESON_PLL
+ select MFD_SYSCON
select RESET_CONTROLLER
help
Support for the clock controller on AmLogic S802 (Meson8),
@@ -30,8 +55,14 @@ config COMMON_CLK_MESON8B
config COMMON_CLK_GXBB
bool
- select COMMON_CLK_AMLOGIC
- select COMMON_CLK_MESON_AO
+ depends on ARCH_MESON
+ select COMMON_CLK_MESON_REGMAP
+ select COMMON_CLK_MESON_DUALDIV
+ select COMMON_CLK_MESON_VID_PLL_DIV
+ select COMMON_CLK_MESON_MPLL
+ select COMMON_CLK_MESON_PLL
+ select COMMON_CLK_MESON_AO_CLKC
+ select COMMON_CLK_MESON_EE_CLKC
select MFD_SYSCON
help
Support for the clock controller on AmLogic S905 devices, aka gxbb.
@@ -39,8 +70,13 @@ config COMMON_CLK_GXBB
config COMMON_CLK_AXG
bool
- select COMMON_CLK_AMLOGIC
- select COMMON_CLK_MESON_AO
+ depends on ARCH_MESON
+ select COMMON_CLK_MESON_REGMAP
+ select COMMON_CLK_MESON_DUALDIV
+ select COMMON_CLK_MESON_MPLL
+ select COMMON_CLK_MESON_PLL
+ select COMMON_CLK_MESON_AO_CLKC
+ select COMMON_CLK_MESON_EE_CLKC
select MFD_SYSCON
help
Support for the clock controller on AmLogic A113D devices, aka axg.
@@ -48,9 +84,26 @@ config COMMON_CLK_AXG
config COMMON_CLK_AXG_AUDIO
tristate "Meson AXG Audio Clock Controller Driver"
- depends on COMMON_CLK_AXG
- select COMMON_CLK_AMLOGIC_AUDIO
- select MFD_SYSCON
+ depends on ARCH_MESON
+ select COMMON_CLK_MESON_INPUT
+ select COMMON_CLK_MESON_REGMAP
+ select COMMON_CLK_MESON_PHASE
+ select COMMON_CLK_MESON_SCLK_DIV
+ select REGMAP_MMIO
help
Support for the audio clock controller on AmLogic A113D devices,
aka axg, Say Y if you want audio subsystem to work.
+
+config COMMON_CLK_G12A
+ bool
+ depends on ARCH_MESON
+ select COMMON_CLK_MESON_REGMAP
+ select COMMON_CLK_MESON_DUALDIV
+ select COMMON_CLK_MESON_MPLL
+ select COMMON_CLK_MESON_PLL
+ select COMMON_CLK_MESON_AO_CLKC
+ select COMMON_CLK_MESON_EE_CLKC
+ select MFD_SYSCON
+ help
+ Support for the clock controller on Amlogic S905D2, S905X2 and S905Y2
+ devices, aka g12a. Say Y if you want peripherals to work.
diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile
index a849aa809825..021fc290e749 100644
--- a/drivers/clk/meson/Makefile
+++ b/drivers/clk/meson/Makefile
@@ -1,13 +1,20 @@
-#
-# Makefile for Meson specific clk
-#
+# Amlogic clock drivers
-obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-pll.o clk-mpll.o clk-phase.o vid-pll-div.o
-obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-input.o
-obj-$(CONFIG_COMMON_CLK_AMLOGIC_AUDIO) += clk-triphase.o sclk-div.o
-obj-$(CONFIG_COMMON_CLK_MESON_AO) += meson-aoclk.o
+obj-$(CONFIG_COMMON_CLK_MESON_AO_CLKC) += meson-aoclk.o
+obj-$(CONFIG_COMMON_CLK_MESON_DUALDIV) += clk-dualdiv.o
+obj-$(CONFIG_COMMON_CLK_MESON_EE_CLKC) += meson-eeclk.o
+obj-$(CONFIG_COMMON_CLK_MESON_INPUT) += clk-input.o
+obj-$(CONFIG_COMMON_CLK_MESON_MPLL) += clk-mpll.o
+obj-$(CONFIG_COMMON_CLK_MESON_PHASE) += clk-phase.o
+obj-$(CONFIG_COMMON_CLK_MESON_PLL) += clk-pll.o
+obj-$(CONFIG_COMMON_CLK_MESON_REGMAP) += clk-regmap.o
+obj-$(CONFIG_COMMON_CLK_MESON_SCLK_DIV) += sclk-div.o
+obj-$(CONFIG_COMMON_CLK_MESON_VID_PLL_DIV) += vid-pll-div.o
+
+# Amlogic Clock controllers
+
+obj-$(CONFIG_COMMON_CLK_AXG) += axg.o axg-aoclk.o
+obj-$(CONFIG_COMMON_CLK_AXG_AUDIO) += axg-audio.o
+obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o
+obj-$(CONFIG_COMMON_CLK_G12A) += g12a.o g12a-aoclk.o
obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o
-obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o gxbb-aoclk-32k.o
-obj-$(CONFIG_COMMON_CLK_AXG) += axg.o axg-aoclk.o
-obj-$(CONFIG_COMMON_CLK_AXG_AUDIO) += axg-audio.o
-obj-$(CONFIG_COMMON_CLK_REGMAP_MESON) += clk-regmap.o
diff --git a/drivers/clk/meson/axg-aoclk.c b/drivers/clk/meson/axg-aoclk.c
index 29e088542387..0086f31288eb 100644
--- a/drivers/clk/meson/axg-aoclk.c
+++ b/drivers/clk/meson/axg-aoclk.c
@@ -12,10 +12,27 @@
#include <linux/platform_device.h>
#include <linux/reset-controller.h>
#include <linux/mfd/syscon.h>
-#include "clk-regmap.h"
#include "meson-aoclk.h"
#include "axg-aoclk.h"
+#include "clk-regmap.h"
+#include "clk-dualdiv.h"
+
+#define IN_PREFIX "ao-in-"
+
+/*
+ * AO Configuration Clock registers offsets
+ * Register offsets from the data sheet must be multiplied by 4.
+ */
+#define AO_RTI_PWR_CNTL_REG1 0x0C
+#define AO_RTI_PWR_CNTL_REG0 0x10
+#define AO_RTI_GEN_CNTL_REG0 0x40
+#define AO_OSCIN_CNTL 0x58
+#define AO_CRT_CLK_CNTL1 0x68
+#define AO_SAR_CLK 0x90
+#define AO_RTC_ALT_CLK_CNTL0 0x94
+#define AO_RTC_ALT_CLK_CNTL1 0x98
+
#define AXG_AO_GATE(_name, _bit) \
static struct clk_regmap axg_aoclk_##_name = { \
.data = &(struct clk_regmap_gate_data) { \
@@ -25,7 +42,7 @@ static struct clk_regmap axg_aoclk_##_name = { \
.hw.init = &(struct clk_init_data) { \
.name = "axg_ao_" #_name, \
.ops = &clk_regmap_gate_ops, \
- .parent_names = (const char *[]){ "clk81" }, \
+ .parent_names = (const char *[]){ IN_PREFIX "mpeg-clk" }, \
.num_parents = 1, \
.flags = CLK_IGNORE_UNUSED, \
}, \
@@ -39,17 +56,141 @@ AXG_AO_GATE(uart2, 5);
AXG_AO_GATE(ir_blaster, 6);
AXG_AO_GATE(saradc, 7);
+static struct clk_regmap axg_aoclk_cts_oscin = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = AO_RTI_PWR_CNTL_REG0,
+ .bit_idx = 14,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "cts_oscin",
+ .ops = &clk_regmap_gate_ro_ops,
+ .parent_names = (const char *[]){ IN_PREFIX "xtal" },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap axg_aoclk_32k_pre = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = AO_RTC_ALT_CLK_CNTL0,
+ .bit_idx = 31,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "axg_ao_32k_pre",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "cts_oscin" },
+ .num_parents = 1,
+ },
+};
+
+static const struct meson_clk_dualdiv_param axg_32k_div_table[] = {
+ {
+ .dual = 1,
+ .n1 = 733,
+ .m1 = 8,
+ .n2 = 732,
+ .m2 = 11,
+ }, {}
+};
+
+static struct clk_regmap axg_aoclk_32k_div = {
+ .data = &(struct meson_clk_dualdiv_data){
+ .n1 = {
+ .reg_off = AO_RTC_ALT_CLK_CNTL0,
+ .shift = 0,
+ .width = 12,
+ },
+ .n2 = {
+ .reg_off = AO_RTC_ALT_CLK_CNTL0,
+ .shift = 12,
+ .width = 12,
+ },
+ .m1 = {
+ .reg_off = AO_RTC_ALT_CLK_CNTL1,
+ .shift = 0,
+ .width = 12,
+ },
+ .m2 = {
+ .reg_off = AO_RTC_ALT_CLK_CNTL1,
+ .shift = 12,
+ .width = 12,
+ },
+ .dual = {
+ .reg_off = AO_RTC_ALT_CLK_CNTL0,
+ .shift = 28,
+ .width = 1,
+ },
+ .table = axg_32k_div_table,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "axg_ao_32k_div",
+ .ops = &meson_clk_dualdiv_ops,
+ .parent_names = (const char *[]){ "axg_ao_32k_pre" },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap axg_aoclk_32k_sel = {
+ .data = &(struct clk_regmap_mux_data) {
+ .offset = AO_RTC_ALT_CLK_CNTL1,
+ .mask = 0x1,
+ .shift = 24,
+ .flags = CLK_MUX_ROUND_CLOSEST,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "axg_ao_32k_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_names = (const char *[]){ "axg_ao_32k_div",
+ "axg_ao_32k_pre" },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap axg_aoclk_32k = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = AO_RTC_ALT_CLK_CNTL0,
+ .bit_idx = 30,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "axg_ao_32k",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "axg_ao_32k_sel" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap axg_aoclk_cts_rtc_oscin = {
+ .data = &(struct clk_regmap_mux_data) {
+ .offset = AO_RTI_PWR_CNTL_REG0,
+ .mask = 0x1,
+ .shift = 10,
+ .flags = CLK_MUX_ROUND_CLOSEST,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "axg_ao_cts_rtc_oscin",
+ .ops = &clk_regmap_mux_ops,
+ .parent_names = (const char *[]){ "axg_ao_32k",
+ IN_PREFIX "ext_32k-0" },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
static struct clk_regmap axg_aoclk_clk81 = {
.data = &(struct clk_regmap_mux_data) {
.offset = AO_RTI_PWR_CNTL_REG0,
.mask = 0x1,
.shift = 8,
+ .flags = CLK_MUX_ROUND_CLOSEST,
},
.hw.init = &(struct clk_init_data){
.name = "axg_ao_clk81",
.ops = &clk_regmap_mux_ro_ops,
- .parent_names = (const char *[]){ "clk81", "ao_alt_xtal"},
+ .parent_names = (const char *[]){ IN_PREFIX "mpeg-clk",
+ "axg_ao_cts_rtc_oscin"},
.num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT,
},
};
@@ -62,7 +203,8 @@ static struct clk_regmap axg_aoclk_saradc_mux = {
.hw.init = &(struct clk_init_data){
.name = "axg_ao_saradc_mux",
.ops = &clk_regmap_mux_ops,
- .parent_names = (const char *[]){ "xtal", "axg_ao_clk81" },
+ .parent_names = (const char *[]){ IN_PREFIX "xtal",
+ "axg_ao_clk81" },
.num_parents = 2,
},
};
@@ -106,17 +248,23 @@ static const unsigned int axg_aoclk_reset[] = {
};
static struct clk_regmap *axg_aoclk_regmap[] = {
- [CLKID_AO_REMOTE] = &axg_aoclk_remote,
- [CLKID_AO_I2C_MASTER] = &axg_aoclk_i2c_master,
- [CLKID_AO_I2C_SLAVE] = &axg_aoclk_i2c_slave,
- [CLKID_AO_UART1] = &axg_aoclk_uart1,
- [CLKID_AO_UART2] = &axg_aoclk_uart2,
- [CLKID_AO_IR_BLASTER] = &axg_aoclk_ir_blaster,
- [CLKID_AO_SAR_ADC] = &axg_aoclk_saradc,
- [CLKID_AO_CLK81] = &axg_aoclk_clk81,
- [CLKID_AO_SAR_ADC_SEL] = &axg_aoclk_saradc_mux,
- [CLKID_AO_SAR_ADC_DIV] = &axg_aoclk_saradc_div,
- [CLKID_AO_SAR_ADC_CLK] = &axg_aoclk_saradc_gate,
+ &axg_aoclk_remote,
+ &axg_aoclk_i2c_master,
+ &axg_aoclk_i2c_slave,
+ &axg_aoclk_uart1,
+ &axg_aoclk_uart2,
+ &axg_aoclk_ir_blaster,
+ &axg_aoclk_saradc,
+ &axg_aoclk_cts_oscin,
+ &axg_aoclk_32k_pre,
+ &axg_aoclk_32k_div,
+ &axg_aoclk_32k_sel,
+ &axg_aoclk_32k,
+ &axg_aoclk_cts_rtc_oscin,
+ &axg_aoclk_clk81,
+ &axg_aoclk_saradc_mux,
+ &axg_aoclk_saradc_div,
+ &axg_aoclk_saradc_gate,
};
static const struct clk_hw_onecell_data axg_aoclk_onecell_data = {
@@ -132,10 +280,22 @@ static const struct clk_hw_onecell_data axg_aoclk_onecell_data = {
[CLKID_AO_SAR_ADC_SEL] = &axg_aoclk_saradc_mux.hw,
[CLKID_AO_SAR_ADC_DIV] = &axg_aoclk_saradc_div.hw,
[CLKID_AO_SAR_ADC_CLK] = &axg_aoclk_saradc_gate.hw,
+ [CLKID_AO_CTS_OSCIN] = &axg_aoclk_cts_oscin.hw,
+ [CLKID_AO_32K_PRE] = &axg_aoclk_32k_pre.hw,
+ [CLKID_AO_32K_DIV] = &axg_aoclk_32k_div.hw,
+ [CLKID_AO_32K_SEL] = &axg_aoclk_32k_sel.hw,
+ [CLKID_AO_32K] = &axg_aoclk_32k.hw,
+ [CLKID_AO_CTS_RTC_OSCIN] = &axg_aoclk_cts_rtc_oscin.hw,
},
.num = NR_CLKS,
};
+static const struct meson_aoclk_input axg_aoclk_inputs[] = {
+ { .name = "xtal", .required = true },
+ { .name = "mpeg-clk", .required = true },
+ { .name = "ext-32k-0", .required = false },
+};
+
static const struct meson_aoclk_data axg_aoclkc_data = {
.reset_reg = AO_RTI_GEN_CNTL_REG0,
.num_reset = ARRAY_SIZE(axg_aoclk_reset),
@@ -143,6 +303,9 @@ static const struct meson_aoclk_data axg_aoclkc_data = {
.num_clks = ARRAY_SIZE(axg_aoclk_regmap),
.clks = axg_aoclk_regmap,
.hw_data = &axg_aoclk_onecell_data,
+ .inputs = axg_aoclk_inputs,
+ .num_inputs = ARRAY_SIZE(axg_aoclk_inputs),
+ .input_prefix = IN_PREFIX,
};
static const struct of_device_id axg_aoclkc_match_table[] = {
diff --git a/drivers/clk/meson/axg-aoclk.h b/drivers/clk/meson/axg-aoclk.h
index 91384d8dd844..3cc27e85170f 100644
--- a/drivers/clk/meson/axg-aoclk.h
+++ b/drivers/clk/meson/axg-aoclk.h
@@ -10,18 +10,7 @@
#ifndef __AXG_AOCLKC_H
#define __AXG_AOCLKC_H
-#define NR_CLKS 11
-/* AO Configuration Clock registers offsets
- * Register offsets from the data sheet must be multiplied by 4.
- */
-#define AO_RTI_PWR_CNTL_REG1 0x0C
-#define AO_RTI_PWR_CNTL_REG0 0x10
-#define AO_RTI_GEN_CNTL_REG0 0x40
-#define AO_OSCIN_CNTL 0x58
-#define AO_CRT_CLK_CNTL1 0x68
-#define AO_SAR_CLK 0x90
-#define AO_RTC_ALT_CLK_CNTL0 0x94
-#define AO_RTC_ALT_CLK_CNTL1 0x98
+#define NR_CLKS 17
#include <dt-bindings/clock/axg-aoclkc.h>
#include <dt-bindings/reset/axg-aoclkc.h>
diff --git a/drivers/clk/meson/axg-audio.c b/drivers/clk/meson/axg-audio.c
index 8ac3a2295473..7ab200b6c3bf 100644
--- a/drivers/clk/meson/axg-audio.c
+++ b/drivers/clk/meson/axg-audio.c
@@ -14,8 +14,11 @@
#include <linux/reset.h>
#include <linux/slab.h>
-#include "clkc-audio.h"
#include "axg-audio.h"
+#include "clk-input.h"
+#include "clk-regmap.h"
+#include "clk-phase.h"
+#include "sclk-div.h"
#define AXG_MST_IN_COUNT 8
#define AXG_SLV_SCLK_COUNT 10
diff --git a/drivers/clk/meson/axg.c b/drivers/clk/meson/axg.c
index 792735d7e46e..7a8ef80e5f2c 100644
--- a/drivers/clk/meson/axg.c
+++ b/drivers/clk/meson/axg.c
@@ -9,16 +9,17 @@
* Author: Qiufang Dai <qiufang.dai@amlogic.com>
*/
-#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/init.h>
#include <linux/of_device.h>
-#include <linux/mfd/syscon.h>
#include <linux/platform_device.h>
-#include <linux/regmap.h>
-#include "clkc.h"
+#include "clk-input.h"
+#include "clk-regmap.h"
+#include "clk-pll.h"
+#include "clk-mpll.h"
#include "axg.h"
+#include "meson-eeclk.h"
static DEFINE_SPINLOCK(meson_clk_lock);
@@ -58,7 +59,7 @@ static struct clk_regmap axg_fixed_pll_dco = {
.hw.init = &(struct clk_init_data){
.name = "fixed_pll_dco",
.ops = &meson_clk_pll_ro_ops,
- .parent_names = (const char *[]){ "xtal" },
+ .parent_names = (const char *[]){ IN_PREFIX "xtal" },
.num_parents = 1,
},
};
@@ -113,7 +114,7 @@ static struct clk_regmap axg_sys_pll_dco = {
.hw.init = &(struct clk_init_data){
.name = "sys_pll_dco",
.ops = &meson_clk_pll_ro_ops,
- .parent_names = (const char *[]){ "xtal" },
+ .parent_names = (const char *[]){ IN_PREFIX "xtal" },
.num_parents = 1,
},
};
@@ -214,7 +215,7 @@ static struct clk_regmap axg_gp0_pll_dco = {
.hw.init = &(struct clk_init_data){
.name = "gp0_pll_dco",
.ops = &meson_clk_pll_ops,
- .parent_names = (const char *[]){ "xtal" },
+ .parent_names = (const char *[]){ IN_PREFIX "xtal" },
.num_parents = 1,
},
};
@@ -283,7 +284,7 @@ static struct clk_regmap axg_hifi_pll_dco = {
.hw.init = &(struct clk_init_data){
.name = "hifi_pll_dco",
.ops = &meson_clk_pll_ops,
- .parent_names = (const char *[]){ "xtal" },
+ .parent_names = (const char *[]){ IN_PREFIX "xtal" },
.num_parents = 1,
},
};
@@ -701,7 +702,7 @@ static struct clk_regmap axg_pcie_pll_dco = {
.hw.init = &(struct clk_init_data){
.name = "pcie_pll_dco",
.ops = &meson_clk_pll_ops,
- .parent_names = (const char *[]){ "xtal" },
+ .parent_names = (const char *[]){ IN_PREFIX "xtal" },
.num_parents = 1,
},
};
@@ -803,7 +804,7 @@ static struct clk_regmap axg_pcie_cml_en1 = {
static u32 mux_table_clk81[] = { 0, 2, 3, 4, 5, 6, 7 };
static const char * const clk81_parent_names[] = {
- "xtal", "fclk_div7", "mpll1", "mpll2", "fclk_div4",
+ IN_PREFIX "xtal", "fclk_div7", "mpll1", "mpll2", "fclk_div4",
"fclk_div3", "fclk_div5"
};
@@ -852,7 +853,7 @@ static struct clk_regmap axg_clk81 = {
};
static const char * const axg_sd_emmc_clk0_parent_names[] = {
- "xtal", "fclk_div2", "fclk_div3", "fclk_div5", "fclk_div7",
+ IN_PREFIX "xtal", "fclk_div2", "fclk_div3", "fclk_div5", "fclk_div7",
/*
* Following these parent clocks, we should also have had mpll2, mpll3
@@ -957,7 +958,7 @@ static struct clk_regmap axg_sd_emmc_c_clk0 = {
static u32 mux_table_gen_clk[] = { 0, 4, 5, 6, 7, 8,
9, 10, 11, 13, 14, };
static const char * const gen_clk_parent_names[] = {
- "xtal", "hifi_pll", "mpll0", "mpll1", "mpll2", "mpll3",
+ IN_PREFIX "xtal", "hifi_pll", "mpll0", "mpll1", "mpll2", "mpll3",
"fclk_div4", "fclk_div3", "fclk_div5", "fclk_div7", "gp0_pll",
};
@@ -1255,46 +1256,20 @@ static struct clk_regmap *const axg_clk_regmaps[] = {
&axg_pcie_pll_od,
};
+static const struct meson_eeclkc_data axg_clkc_data = {
+ .regmap_clks = axg_clk_regmaps,
+ .regmap_clk_num = ARRAY_SIZE(axg_clk_regmaps),
+ .hw_onecell_data = &axg_hw_onecell_data,
+};
+
+
static const struct of_device_id clkc_match_table[] = {
- { .compatible = "amlogic,axg-clkc" },
+ { .compatible = "amlogic,axg-clkc", .data = &axg_clkc_data },
{}
};
-static int axg_clkc_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct regmap *map;
- int ret, i;
-
- /* Get the hhi system controller node if available */
- map = syscon_node_to_regmap(of_get_parent(dev->of_node));
- if (IS_ERR(map)) {
- dev_err(dev, "failed to get HHI regmap\n");
- return PTR_ERR(map);
- }
-
- /* Populate regmap for the regmap backed clocks */
- for (i = 0; i < ARRAY_SIZE(axg_clk_regmaps); i++)
- axg_clk_regmaps[i]->map = map;
-
- for (i = 0; i < axg_hw_onecell_data.num; i++) {
- /* array might be sparse */
- if (!axg_hw_onecell_data.hws[i])
- continue;
-
- ret = devm_clk_hw_register(dev, axg_hw_onecell_data.hws[i]);
- if (ret) {
- dev_err(dev, "Clock registration failed\n");
- return ret;
- }
- }
-
- return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
- &axg_hw_onecell_data);
-}
-
static struct platform_driver axg_driver = {
- .probe = axg_clkc_probe,
+ .probe = meson_eeclkc_probe,
.driver = {
.name = "axg-clkc",
.of_match_table = clkc_match_table,
diff --git a/drivers/clk/meson/clk-dualdiv.c b/drivers/clk/meson/clk-dualdiv.c
new file mode 100644
index 000000000000..c5ca23a5e3e8
--- /dev/null
+++ b/drivers/clk/meson/clk-dualdiv.c
@@ -0,0 +1,138 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2017 BayLibre, SAS
+ * Author: Neil Armstrong <narmstrong@baylibre.com>
+ * Author: Jerome Brunet <jbrunet@baylibre.com>
+ */
+
+/*
+ * The AO Domain embeds a dual/divider to generate a more precise
+ * 32,768KHz clock for low-power suspend mode and CEC.
+ * ______ ______
+ * | | | |
+ * | Div1 |-| Cnt1 |
+ * /|______| |______|\
+ * -| ______ ______ X--> Out
+ * \| | | |/
+ * | Div2 |-| Cnt2 |
+ * |______| |______|
+ *
+ * The dividing can be switched to single or dual, with a counter
+ * for each divider to set when the switching is done.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+
+#include "clk-regmap.h"
+#include "clk-dualdiv.h"
+
+static inline struct meson_clk_dualdiv_data *
+meson_clk_dualdiv_data(struct clk_regmap *clk)
+{
+ return (struct meson_clk_dualdiv_data *)clk->data;
+}
+
+static unsigned long
+__dualdiv_param_to_rate(unsigned long parent_rate,
+ const struct meson_clk_dualdiv_param *p)
+{
+ if (!p->dual)
+ return DIV_ROUND_CLOSEST(parent_rate, p->n1);
+
+ return DIV_ROUND_CLOSEST(parent_rate * (p->m1 + p->m2),
+ p->n1 * p->m1 + p->n2 * p->m2);
+}
+
+static unsigned long meson_clk_dualdiv_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct meson_clk_dualdiv_data *dualdiv = meson_clk_dualdiv_data(clk);
+ struct meson_clk_dualdiv_param setting;
+
+ setting.dual = meson_parm_read(clk->map, &dualdiv->dual);
+ setting.n1 = meson_parm_read(clk->map, &dualdiv->n1) + 1;
+ setting.m1 = meson_parm_read(clk->map, &dualdiv->m1) + 1;
+ setting.n2 = meson_parm_read(clk->map, &dualdiv->n2) + 1;
+ setting.m2 = meson_parm_read(clk->map, &dualdiv->m2) + 1;
+
+ return __dualdiv_param_to_rate(parent_rate, &setting);
+}
+
+static const struct meson_clk_dualdiv_param *
+__dualdiv_get_setting(unsigned long rate, unsigned long parent_rate,
+ struct meson_clk_dualdiv_data *dualdiv)
+{
+ const struct meson_clk_dualdiv_param *table = dualdiv->table;
+ unsigned long best = 0, now = 0;
+ unsigned int i, best_i = 0;
+
+ if (!table)
+ return NULL;
+
+ for (i = 0; table[i].n1; i++) {
+ now = __dualdiv_param_to_rate(parent_rate, &table[i]);
+
+ /* If we get an exact match, don't bother any further */
+ if (now == rate) {
+ return &table[i];
+ } else if (abs(now - rate) < abs(best - rate)) {
+ best = now;
+ best_i = i;
+ }
+ }
+
+ return (struct meson_clk_dualdiv_param *)&table[best_i];
+}
+
+static long meson_clk_dualdiv_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct meson_clk_dualdiv_data *dualdiv = meson_clk_dualdiv_data(clk);
+ const struct meson_clk_dualdiv_param *setting =
+ __dualdiv_get_setting(rate, *parent_rate, dualdiv);
+
+ if (!setting)
+ return meson_clk_dualdiv_recalc_rate(hw, *parent_rate);
+
+ return __dualdiv_param_to_rate(*parent_rate, setting);
+}
+
+static int meson_clk_dualdiv_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct meson_clk_dualdiv_data *dualdiv = meson_clk_dualdiv_data(clk);
+ const struct meson_clk_dualdiv_param *setting =
+ __dualdiv_get_setting(rate, parent_rate, dualdiv);
+
+ if (!setting)
+ return -EINVAL;
+
+ meson_parm_write(clk->map, &dualdiv->dual, setting->dual);
+ meson_parm_write(clk->map, &dualdiv->n1, setting->n1 - 1);
+ meson_parm_write(clk->map, &dualdiv->m1, setting->m1 - 1);
+ meson_parm_write(clk->map, &dualdiv->n2, setting->n2 - 1);
+ meson_parm_write(clk->map, &dualdiv->m2, setting->m2 - 1);
+
+ return 0;
+}
+
+const struct clk_ops meson_clk_dualdiv_ops = {
+ .recalc_rate = meson_clk_dualdiv_recalc_rate,
+ .round_rate = meson_clk_dualdiv_round_rate,
+ .set_rate = meson_clk_dualdiv_set_rate,
+};
+EXPORT_SYMBOL_GPL(meson_clk_dualdiv_ops);
+
+const struct clk_ops meson_clk_dualdiv_ro_ops = {
+ .recalc_rate = meson_clk_dualdiv_recalc_rate,
+};
+EXPORT_SYMBOL_GPL(meson_clk_dualdiv_ro_ops);
+
+MODULE_DESCRIPTION("Amlogic dual divider driver");
+MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
+MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/clk/meson/clk-dualdiv.h b/drivers/clk/meson/clk-dualdiv.h
new file mode 100644
index 000000000000..4aa939018012
--- /dev/null
+++ b/drivers/clk/meson/clk-dualdiv.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 BayLibre, SAS.
+ * Author: Jerome Brunet <jbrunet@baylibre.com>
+ */
+
+#ifndef __MESON_CLK_DUALDIV_H
+#define __MESON_CLK_DUALDIV_H
+
+#include <linux/clk-provider.h>
+#include "parm.h"
+
+struct meson_clk_dualdiv_param {
+ unsigned int n1;
+ unsigned int n2;
+ unsigned int m1;
+ unsigned int m2;
+ unsigned int dual;
+};
+
+struct meson_clk_dualdiv_data {
+ struct parm n1;
+ struct parm n2;
+ struct parm m1;
+ struct parm m2;
+ struct parm dual;
+ const struct meson_clk_dualdiv_param *table;
+};
+
+extern const struct clk_ops meson_clk_dualdiv_ops;
+extern const struct clk_ops meson_clk_dualdiv_ro_ops;
+
+#endif /* __MESON_CLK_DUALDIV_H */
diff --git a/drivers/clk/meson/clk-input.c b/drivers/clk/meson/clk-input.c
index 06b3e3bb6a66..086226e9dba6 100644
--- a/drivers/clk/meson/clk-input.c
+++ b/drivers/clk/meson/clk-input.c
@@ -7,7 +7,8 @@
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/device.h>
-#include "clkc.h"
+#include <linux/module.h>
+#include "clk-input.h"
static const struct clk_ops meson_clk_no_ops = {};
@@ -42,3 +43,7 @@ struct clk_hw *meson_clk_hw_register_input(struct device *dev,
return ret ? ERR_PTR(ret) : hw;
}
EXPORT_SYMBOL_GPL(meson_clk_hw_register_input);
+
+MODULE_DESCRIPTION("Amlogic clock input helper");
+MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/clk/meson/clk-input.h b/drivers/clk/meson/clk-input.h
new file mode 100644
index 000000000000..4a541b9685a6
--- /dev/null
+++ b/drivers/clk/meson/clk-input.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 BayLibre, SAS.
+ * Author: Jerome Brunet <jbrunet@baylibre.com>
+ */
+
+#ifndef __MESON_CLK_INPUT_H
+#define __MESON_CLK_INPUT_H
+
+#include <linux/clk-provider.h>
+
+struct device;
+
+struct clk_hw *meson_clk_hw_register_input(struct device *dev,
+ const char *of_name,
+ const char *clk_name,
+ unsigned long flags);
+
+#endif /* __MESON_CLK_INPUT_H */
diff --git a/drivers/clk/meson/clk-mpll.c b/drivers/clk/meson/clk-mpll.c
index 650f75cc15a9..f76850d99e59 100644
--- a/drivers/clk/meson/clk-mpll.c
+++ b/drivers/clk/meson/clk-mpll.c
@@ -12,7 +12,11 @@
*/
#include <linux/clk-provider.h>
-#include "clkc.h"
+#include <linux/module.h>
+#include <linux/spinlock.h>
+
+#include "clk-regmap.h"
+#include "clk-mpll.h"
#define SDM_DEN 16384
#define N2_MIN 4
@@ -138,9 +142,15 @@ const struct clk_ops meson_clk_mpll_ro_ops = {
.recalc_rate = mpll_recalc_rate,
.round_rate = mpll_round_rate,
};
+EXPORT_SYMBOL_GPL(meson_clk_mpll_ro_ops);
const struct clk_ops meson_clk_mpll_ops = {
.recalc_rate = mpll_recalc_rate,
.round_rate = mpll_round_rate,
.set_rate = mpll_set_rate,
};
+EXPORT_SYMBOL_GPL(meson_clk_mpll_ops);
+
+MODULE_DESCRIPTION("Amlogic MPLL driver");
+MODULE_AUTHOR("Michael Turquette <mturquette@baylibre.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/clk/meson/clk-mpll.h b/drivers/clk/meson/clk-mpll.h
new file mode 100644
index 000000000000..cf79340006dd
--- /dev/null
+++ b/drivers/clk/meson/clk-mpll.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 BayLibre, SAS.
+ * Author: Jerome Brunet <jbrunet@baylibre.com>
+ */
+
+#ifndef __MESON_CLK_MPLL_H
+#define __MESON_CLK_MPLL_H
+
+#include <linux/clk-provider.h>
+#include <linux/spinlock.h>
+
+#include "parm.h"
+
+struct meson_clk_mpll_data {
+ struct parm sdm;
+ struct parm sdm_en;
+ struct parm n2;
+ struct parm ssen;
+ struct parm misc;
+ spinlock_t *lock;
+ u8 flags;
+};
+
+#define CLK_MESON_MPLL_ROUND_CLOSEST BIT(0)
+
+extern const struct clk_ops meson_clk_mpll_ro_ops;
+extern const struct clk_ops meson_clk_mpll_ops;
+
+#endif /* __MESON_CLK_MPLL_H */
diff --git a/drivers/clk/meson/clk-phase.c b/drivers/clk/meson/clk-phase.c
index cba43748ce3d..80c3ada193a4 100644
--- a/drivers/clk/meson/clk-phase.c
+++ b/drivers/clk/meson/clk-phase.c
@@ -5,7 +5,10 @@
*/
#include <linux/clk-provider.h>
-#include "clkc.h"
+#include <linux/module.h>
+
+#include "clk-regmap.h"
+#include "clk-phase.h"
#define phase_step(_width) (360 / (1 << (_width)))
@@ -15,13 +18,12 @@ meson_clk_phase_data(struct clk_regmap *clk)
return (struct meson_clk_phase_data *)clk->data;
}
-int meson_clk_degrees_from_val(unsigned int val, unsigned int width)
+static int meson_clk_degrees_from_val(unsigned int val, unsigned int width)
{
return phase_step(width) * val;
}
-EXPORT_SYMBOL_GPL(meson_clk_degrees_from_val);
-unsigned int meson_clk_degrees_to_val(int degrees, unsigned int width)
+static unsigned int meson_clk_degrees_to_val(int degrees, unsigned int width)
{
unsigned int val = DIV_ROUND_CLOSEST(degrees, phase_step(width));
@@ -31,7 +33,6 @@ unsigned int meson_clk_degrees_to_val(int degrees, unsigned int width)
*/
return val % (1 << width);
}
-EXPORT_SYMBOL_GPL(meson_clk_degrees_to_val);
static int meson_clk_phase_get_phase(struct clk_hw *hw)
{
@@ -61,3 +62,67 @@ const struct clk_ops meson_clk_phase_ops = {
.set_phase = meson_clk_phase_set_phase,
};
EXPORT_SYMBOL_GPL(meson_clk_phase_ops);
+
+/*
+ * This is a special clock for the audio controller.
+ * The phase of mst_sclk clock output can be controlled independently
+ * for the outside world (ph0), the tdmout (ph1) and tdmin (ph2).
+ * Controlling these 3 phases as just one makes things simpler and
+ * give the same clock view to all the element on the i2s bus.
+ * If necessary, we can still control the phase in the tdm block
+ * which makes these independent control redundant.
+ */
+static inline struct meson_clk_triphase_data *
+meson_clk_triphase_data(struct clk_regmap *clk)
+{
+ return (struct meson_clk_triphase_data *)clk->data;
+}
+
+static void meson_clk_triphase_sync(struct clk_hw *hw)
+{
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk);
+ unsigned int val;
+
+ /* Get phase 0 and sync it to phase 1 and 2 */
+ val = meson_parm_read(clk->map, &tph->ph0);
+ meson_parm_write(clk->map, &tph->ph1, val);
+ meson_parm_write(clk->map, &tph->ph2, val);
+}
+
+static int meson_clk_triphase_get_phase(struct clk_hw *hw)
+{
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk);
+ unsigned int val;
+
+ /* Phase are in sync, reading phase 0 is enough */
+ val = meson_parm_read(clk->map, &tph->ph0);
+
+ return meson_clk_degrees_from_val(val, tph->ph0.width);
+}
+
+static int meson_clk_triphase_set_phase(struct clk_hw *hw, int degrees)
+{
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk);
+ unsigned int val;
+
+ val = meson_clk_degrees_to_val(degrees, tph->ph0.width);
+ meson_parm_write(clk->map, &tph->ph0, val);
+ meson_parm_write(clk->map, &tph->ph1, val);
+ meson_parm_write(clk->map, &tph->ph2, val);
+
+ return 0;
+}
+
+const struct clk_ops meson_clk_triphase_ops = {
+ .init = meson_clk_triphase_sync,
+ .get_phase = meson_clk_triphase_get_phase,
+ .set_phase = meson_clk_triphase_set_phase,
+};
+EXPORT_SYMBOL_GPL(meson_clk_triphase_ops);
+
+MODULE_DESCRIPTION("Amlogic phase driver");
+MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/clk/meson/clk-phase.h b/drivers/clk/meson/clk-phase.h
new file mode 100644
index 000000000000..5579f9ced142
--- /dev/null
+++ b/drivers/clk/meson/clk-phase.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 BayLibre, SAS.
+ * Author: Jerome Brunet <jbrunet@baylibre.com>
+ */
+
+#ifndef __MESON_CLK_PHASE_H
+#define __MESON_CLK_PHASE_H
+
+#include <linux/clk-provider.h>
+#include "parm.h"
+
+struct meson_clk_phase_data {
+ struct parm ph;
+};
+
+struct meson_clk_triphase_data {
+ struct parm ph0;
+ struct parm ph1;
+ struct parm ph2;
+};
+
+extern const struct clk_ops meson_clk_phase_ops;
+extern const struct clk_ops meson_clk_triphase_ops;
+
+#endif /* __MESON_CLK_PHASE_H */
diff --git a/drivers/clk/meson/clk-pll.c b/drivers/clk/meson/clk-pll.c
index afffc1547e20..41e16dd7272a 100644
--- a/drivers/clk/meson/clk-pll.c
+++ b/drivers/clk/meson/clk-pll.c
@@ -32,11 +32,10 @@
#include <linux/io.h>
#include <linux/math64.h>
#include <linux/module.h>
-#include <linux/of_address.h>
-#include <linux/slab.h>
-#include <linux/string.h>
+#include <linux/rational.h>
-#include "clkc.h"
+#include "clk-regmap.h"
+#include "clk-pll.h"
static inline struct meson_clk_pll_data *
meson_clk_pll_data(struct clk_regmap *clk)
@@ -44,12 +43,21 @@ meson_clk_pll_data(struct clk_regmap *clk)
return (struct meson_clk_pll_data *)clk->data;
}
+static int __pll_round_closest_mult(struct meson_clk_pll_data *pll)
+{
+ if ((pll->flags & CLK_MESON_PLL_ROUND_CLOSEST) &&
+ !MESON_PARM_APPLICABLE(&pll->frac))
+ return 1;
+
+ return 0;
+}
+
static unsigned long __pll_params_to_rate(unsigned long parent_rate,
- const struct pll_params_table *pllt,
- u16 frac,
+ unsigned int m, unsigned int n,
+ unsigned int frac,
struct meson_clk_pll_data *pll)
{
- u64 rate = (u64)parent_rate * pllt->m;
+ u64 rate = (u64)parent_rate * m;
if (frac && MESON_PARM_APPLICABLE(&pll->frac)) {
u64 frac_rate = (u64)parent_rate * frac;
@@ -58,7 +66,7 @@ static unsigned long __pll_params_to_rate(unsigned long parent_rate,
(1 << pll->frac.width));
}
- return DIV_ROUND_UP_ULL(rate, pllt->n);
+ return DIV_ROUND_UP_ULL(rate, n);
}
static unsigned long meson_clk_pll_recalc_rate(struct clk_hw *hw,
@@ -66,35 +74,39 @@ static unsigned long meson_clk_pll_recalc_rate(struct clk_hw *hw,
{
struct clk_regmap *clk = to_clk_regmap(hw);
struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
- struct pll_params_table pllt;
- u16 frac;
+ unsigned int m, n, frac;
- pllt.n = meson_parm_read(clk->map, &pll->n);
- pllt.m = meson_parm_read(clk->map, &pll->m);
+ n = meson_parm_read(clk->map, &pll->n);
+ m = meson_parm_read(clk->map, &pll->m);
frac = MESON_PARM_APPLICABLE(&pll->frac) ?
meson_parm_read(clk->map, &pll->frac) :
0;
- return __pll_params_to_rate(parent_rate, &pllt, frac, pll);
+ return __pll_params_to_rate(parent_rate, m, n, frac, pll);
}
-static u16 __pll_params_with_frac(unsigned long rate,
- unsigned long parent_rate,
- const struct pll_params_table *pllt,
- struct meson_clk_pll_data *pll)
+static unsigned int __pll_params_with_frac(unsigned long rate,
+ unsigned long parent_rate,
+ unsigned int m,
+ unsigned int n,
+ struct meson_clk_pll_data *pll)
{
- u16 frac_max = (1 << pll->frac.width);
- u64 val = (u64)rate * pllt->n;
+ unsigned int frac_max = (1 << pll->frac.width);
+ u64 val = (u64)rate * n;
+
+ /* Bail out if we are already over the requested rate */
+ if (rate < parent_rate * m / n)
+ return 0;
if (pll->flags & CLK_MESON_PLL_ROUND_CLOSEST)
val = DIV_ROUND_CLOSEST_ULL(val * frac_max, parent_rate);
else
val = div_u64(val * frac_max, parent_rate);
- val -= pllt->m * frac_max;
+ val -= m * frac_max;
- return min((u16)val, (u16)(frac_max - 1));
+ return min((unsigned int)val, (frac_max - 1));
}
static bool meson_clk_pll_is_better(unsigned long rate,
@@ -102,45 +114,123 @@ static bool meson_clk_pll_is_better(unsigned long rate,
unsigned long now,
struct meson_clk_pll_data *pll)
{
- if (!(pll->flags & CLK_MESON_PLL_ROUND_CLOSEST) ||
- MESON_PARM_APPLICABLE(&pll->frac)) {
- /* Round down */
- if (now < rate && best < now)
- return true;
- } else {
+ if (__pll_round_closest_mult(pll)) {
/* Round Closest */
if (abs(now - rate) < abs(best - rate))
return true;
+ } else {
+ /* Round down */
+ if (now < rate && best < now)
+ return true;
}
return false;
}
-static const struct pll_params_table *
-meson_clk_get_pll_settings(unsigned long rate,
- unsigned long parent_rate,
- struct meson_clk_pll_data *pll)
+static int meson_clk_get_pll_table_index(unsigned int index,
+ unsigned int *m,
+ unsigned int *n,
+ struct meson_clk_pll_data *pll)
{
- const struct pll_params_table *table = pll->table;
- unsigned long best = 0, now = 0;
- unsigned int i, best_i = 0;
+ if (!pll->table[index].n)
+ return -EINVAL;
+
+ *m = pll->table[index].m;
+ *n = pll->table[index].n;
+
+ return 0;
+}
+
+static unsigned int meson_clk_get_pll_range_m(unsigned long rate,
+ unsigned long parent_rate,
+ unsigned int n,
+ struct meson_clk_pll_data *pll)
+{
+ u64 val = (u64)rate * n;
- if (!table)
- return NULL;
+ if (__pll_round_closest_mult(pll))
+ return DIV_ROUND_CLOSEST_ULL(val, parent_rate);
- for (i = 0; table[i].n; i++) {
- now = __pll_params_to_rate(parent_rate, &table[i], 0, pll);
+ return div_u64(val, parent_rate);
+}
- /* If we get an exact match, don't bother any further */
- if (now == rate) {
- return &table[i];
- } else if (meson_clk_pll_is_better(rate, best, now, pll)) {
+static int meson_clk_get_pll_range_index(unsigned long rate,
+ unsigned long parent_rate,
+ unsigned int index,
+ unsigned int *m,
+ unsigned int *n,
+ struct meson_clk_pll_data *pll)
+{
+ *n = index + 1;
+
+ /* Check the predivider range */
+ if (*n >= (1 << pll->n.width))
+ return -EINVAL;
+
+ if (*n == 1) {
+ /* Get the boundaries out the way */
+ if (rate <= pll->range->min * parent_rate) {
+ *m = pll->range->min;
+ return -ENODATA;
+ } else if (rate >= pll->range->max * parent_rate) {
+ *m = pll->range->max;
+ return -ENODATA;
+ }
+ }
+
+ *m = meson_clk_get_pll_range_m(rate, parent_rate, *n, pll);
+
+ /* the pre-divider gives a multiplier too big - stop */
+ if (*m >= (1 << pll->m.width))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int meson_clk_get_pll_get_index(unsigned long rate,
+ unsigned long parent_rate,
+ unsigned int index,
+ unsigned int *m,
+ unsigned int *n,
+ struct meson_clk_pll_data *pll)
+{
+ if (pll->range)
+ return meson_clk_get_pll_range_index(rate, parent_rate,
+ index, m, n, pll);
+ else if (pll->table)
+ return meson_clk_get_pll_table_index(index, m, n, pll);
+
+ return -EINVAL;
+}
+
+static int meson_clk_get_pll_settings(unsigned long rate,
+ unsigned long parent_rate,
+ unsigned int *best_m,
+ unsigned int *best_n,
+ struct meson_clk_pll_data *pll)
+{
+ unsigned long best = 0, now = 0;
+ unsigned int i, m, n;
+ int ret;
+
+ for (i = 0, ret = 0; !ret; i++) {
+ ret = meson_clk_get_pll_get_index(rate, parent_rate,
+ i, &m, &n, pll);
+ if (ret == -EINVAL)
+ break;
+
+ now = __pll_params_to_rate(parent_rate, m, n, 0, pll);
+ if (meson_clk_pll_is_better(rate, best, now, pll)) {
best = now;
- best_i = i;
+ *best_m = m;
+ *best_n = n;
+
+ if (now == rate)
+ break;
}
}
- return (struct pll_params_table *)&table[best_i];
+ return best ? 0 : -EINVAL;
}
static long meson_clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
@@ -148,15 +238,15 @@ static long meson_clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
{
struct clk_regmap *clk = to_clk_regmap(hw);
struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
- const struct pll_params_table *pllt =
- meson_clk_get_pll_settings(rate, *parent_rate, pll);
+ unsigned int m, n, frac;
unsigned long round;
- u16 frac;
+ int ret;
- if (!pllt)
+ ret = meson_clk_get_pll_settings(rate, *parent_rate, &m, &n, pll);
+ if (ret)
return meson_clk_pll_recalc_rate(hw, *parent_rate);
- round = __pll_params_to_rate(*parent_rate, pllt, 0, pll);
+ round = __pll_params_to_rate(*parent_rate, m, n, 0, pll);
if (!MESON_PARM_APPLICABLE(&pll->frac) || rate == round)
return round;
@@ -165,9 +255,9 @@ static long meson_clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
* The rate provided by the setting is not an exact match, let's
* try to improve the result using the fractional parameter
*/
- frac = __pll_params_with_frac(rate, *parent_rate, pllt, pll);
+ frac = __pll_params_with_frac(rate, *parent_rate, m, n, pll);
- return __pll_params_to_rate(*parent_rate, pllt, frac, pll);
+ return __pll_params_to_rate(*parent_rate, m, n, frac, pll);
}
static int meson_clk_pll_wait_lock(struct clk_hw *hw)
@@ -254,30 +344,27 @@ static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
{
struct clk_regmap *clk = to_clk_regmap(hw);
struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
- const struct pll_params_table *pllt;
- unsigned int enabled;
+ unsigned int enabled, m, n, frac = 0, ret;
unsigned long old_rate;
- u16 frac = 0;
if (parent_rate == 0 || rate == 0)
return -EINVAL;
old_rate = rate;
- pllt = meson_clk_get_pll_settings(rate, parent_rate, pll);
- if (!pllt)
- return -EINVAL;
+ ret = meson_clk_get_pll_settings(rate, parent_rate, &m, &n, pll);
+ if (ret)
+ return ret;
enabled = meson_parm_read(clk->map, &pll->en);
if (enabled)
meson_clk_pll_disable(hw);
- meson_parm_write(clk->map, &pll->n, pllt->n);
- meson_parm_write(clk->map, &pll->m, pllt->m);
-
+ meson_parm_write(clk->map, &pll->n, n);
+ meson_parm_write(clk->map, &pll->m, m);
if (MESON_PARM_APPLICABLE(&pll->frac)) {
- frac = __pll_params_with_frac(rate, parent_rate, pllt, pll);
+ frac = __pll_params_with_frac(rate, parent_rate, m, n, pll);
meson_parm_write(clk->map, &pll->frac, frac);
}
@@ -309,8 +396,15 @@ const struct clk_ops meson_clk_pll_ops = {
.enable = meson_clk_pll_enable,
.disable = meson_clk_pll_disable
};
+EXPORT_SYMBOL_GPL(meson_clk_pll_ops);
const struct clk_ops meson_clk_pll_ro_ops = {
.recalc_rate = meson_clk_pll_recalc_rate,
.is_enabled = meson_clk_pll_is_enabled,
};
+EXPORT_SYMBOL_GPL(meson_clk_pll_ro_ops);
+
+MODULE_DESCRIPTION("Amlogic PLL driver");
+MODULE_AUTHOR("Carlo Caione <carlo@endlessm.com>");
+MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/clk/meson/clk-pll.h b/drivers/clk/meson/clk-pll.h
new file mode 100644
index 000000000000..55af2e285b1b
--- /dev/null
+++ b/drivers/clk/meson/clk-pll.h
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 BayLibre, SAS.
+ * Author: Jerome Brunet <jbrunet@baylibre.com>
+ */
+
+#ifndef __MESON_CLK_PLL_H
+#define __MESON_CLK_PLL_H
+
+#include <linux/clk-provider.h>
+#include <linux/regmap.h>
+#include "parm.h"
+
+struct pll_params_table {
+ unsigned int m;
+ unsigned int n;
+};
+
+struct pll_mult_range {
+ unsigned int min;
+ unsigned int max;
+};
+
+#define PLL_PARAMS(_m, _n) \
+ { \
+ .m = (_m), \
+ .n = (_n), \
+ }
+
+#define CLK_MESON_PLL_ROUND_CLOSEST BIT(0)
+
+struct meson_clk_pll_data {
+ struct parm en;
+ struct parm m;
+ struct parm n;
+ struct parm frac;
+ struct parm l;
+ struct parm rst;
+ const struct reg_sequence *init_regs;
+ unsigned int init_count;
+ const struct pll_params_table *table;
+ const struct pll_mult_range *range;
+ u8 flags;
+};
+
+extern const struct clk_ops meson_clk_pll_ro_ops;
+extern const struct clk_ops meson_clk_pll_ops;
+
+#endif /* __MESON_CLK_PLL_H */
diff --git a/drivers/clk/meson/clk-regmap.c b/drivers/clk/meson/clk-regmap.c
index c515f67322a3..dcd1757cc5df 100644
--- a/drivers/clk/meson/clk-regmap.c
+++ b/drivers/clk/meson/clk-regmap.c
@@ -4,6 +4,7 @@
* Author: Jerome Brunet <jbrunet@baylibre.com>
*/
+#include <linux/module.h>
#include "clk-regmap.h"
static int clk_regmap_gate_endisable(struct clk_hw *hw, int enable)
@@ -180,3 +181,7 @@ const struct clk_ops clk_regmap_mux_ro_ops = {
.get_parent = clk_regmap_mux_get_parent,
};
EXPORT_SYMBOL_GPL(clk_regmap_mux_ro_ops);
+
+MODULE_DESCRIPTION("Amlogic regmap backed clock driver");
+MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/clk/meson/clk-regmap.h b/drivers/clk/meson/clk-regmap.h
index e9c5728d40eb..1dd0abe3ba91 100644
--- a/drivers/clk/meson/clk-regmap.h
+++ b/drivers/clk/meson/clk-regmap.h
@@ -111,4 +111,24 @@ clk_get_regmap_mux_data(struct clk_regmap *clk)
extern const struct clk_ops clk_regmap_mux_ops;
extern const struct clk_ops clk_regmap_mux_ro_ops;
+#define __MESON_GATE(_name, _reg, _bit, _ops) \
+struct clk_regmap _name = { \
+ .data = &(struct clk_regmap_gate_data){ \
+ .offset = (_reg), \
+ .bit_idx = (_bit), \
+ }, \
+ .hw.init = &(struct clk_init_data) { \
+ .name = #_name, \
+ .ops = _ops, \
+ .parent_names = (const char *[]){ "clk81" }, \
+ .num_parents = 1, \
+ .flags = (CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED), \
+ }, \
+}
+
+#define MESON_GATE(_name, _reg, _bit) \
+ __MESON_GATE(_name, _reg, _bit, &clk_regmap_gate_ops)
+
+#define MESON_GATE_RO(_name, _reg, _bit) \
+ __MESON_GATE(_name, _reg, _bit, &clk_regmap_gate_ro_ops)
#endif /* __CLK_REGMAP_H */
diff --git a/drivers/clk/meson/clk-triphase.c b/drivers/clk/meson/clk-triphase.c
deleted file mode 100644
index 4a59936251e5..000000000000
--- a/drivers/clk/meson/clk-triphase.c
+++ /dev/null
@@ -1,68 +0,0 @@
-// SPDX-License-Identifier: (GPL-2.0 OR MIT)
-/*
- * Copyright (c) 2018 BayLibre, SAS.
- * Author: Jerome Brunet <jbrunet@baylibre.com>
- */
-
-#include <linux/clk-provider.h>
-#include "clkc-audio.h"
-
-/*
- * This is a special clock for the audio controller.
- * The phase of mst_sclk clock output can be controlled independently
- * for the outside world (ph0), the tdmout (ph1) and tdmin (ph2).
- * Controlling these 3 phases as just one makes things simpler and
- * give the same clock view to all the element on the i2s bus.
- * If necessary, we can still control the phase in the tdm block
- * which makes these independent control redundant.
- */
-static inline struct meson_clk_triphase_data *
-meson_clk_triphase_data(struct clk_regmap *clk)
-{
- return (struct meson_clk_triphase_data *)clk->data;
-}
-
-static void meson_clk_triphase_sync(struct clk_hw *hw)
-{
- struct clk_regmap *clk = to_clk_regmap(hw);
- struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk);
- unsigned int val;
-
- /* Get phase 0 and sync it to phase 1 and 2 */
- val = meson_parm_read(clk->map, &tph->ph0);
- meson_parm_write(clk->map, &tph->ph1, val);
- meson_parm_write(clk->map, &tph->ph2, val);
-}
-
-static int meson_clk_triphase_get_phase(struct clk_hw *hw)
-{
- struct clk_regmap *clk = to_clk_regmap(hw);
- struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk);
- unsigned int val;
-
- /* Phase are in sync, reading phase 0 is enough */
- val = meson_parm_read(clk->map, &tph->ph0);
-
- return meson_clk_degrees_from_val(val, tph->ph0.width);
-}
-
-static int meson_clk_triphase_set_phase(struct clk_hw *hw, int degrees)
-{
- struct clk_regmap *clk = to_clk_regmap(hw);
- struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk);
- unsigned int val;
-
- val = meson_clk_degrees_to_val(degrees, tph->ph0.width);
- meson_parm_write(clk->map, &tph->ph0, val);
- meson_parm_write(clk->map, &tph->ph1, val);
- meson_parm_write(clk->map, &tph->ph2, val);
-
- return 0;
-}
-
-const struct clk_ops meson_clk_triphase_ops = {
- .init = meson_clk_triphase_sync,
- .get_phase = meson_clk_triphase_get_phase,
- .set_phase = meson_clk_triphase_set_phase,
-};
-EXPORT_SYMBOL_GPL(meson_clk_triphase_ops);
diff --git a/drivers/clk/meson/clkc.h b/drivers/clk/meson/clkc.h
deleted file mode 100644
index 6183b22c4bf2..000000000000
--- a/drivers/clk/meson/clkc.h
+++ /dev/null
@@ -1,127 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * Copyright (c) 2015 Endless Mobile, Inc.
- * Author: Carlo Caione <carlo@endlessm.com>
- */
-
-#ifndef __CLKC_H
-#define __CLKC_H
-
-#include <linux/clk-provider.h>
-#include "clk-regmap.h"
-
-#define PMASK(width) GENMASK(width - 1, 0)
-#define SETPMASK(width, shift) GENMASK(shift + width - 1, shift)
-#define CLRPMASK(width, shift) (~SETPMASK(width, shift))
-
-#define PARM_GET(width, shift, reg) \
- (((reg) & SETPMASK(width, shift)) >> (shift))
-#define PARM_SET(width, shift, reg, val) \
- (((reg) & CLRPMASK(width, shift)) | ((val) << (shift)))
-
-#define MESON_PARM_APPLICABLE(p) (!!((p)->width))
-
-struct parm {
- u16 reg_off;
- u8 shift;
- u8 width;
-};
-
-static inline unsigned int meson_parm_read(struct regmap *map, struct parm *p)
-{
- unsigned int val;
-
- regmap_read(map, p->reg_off, &val);
- return PARM_GET(p->width, p->shift, val);
-}
-
-static inline void meson_parm_write(struct regmap *map, struct parm *p,
- unsigned int val)
-{
- regmap_update_bits(map, p->reg_off, SETPMASK(p->width, p->shift),
- val << p->shift);
-}
-
-
-struct pll_params_table {
- u16 m;
- u16 n;
-};
-
-#define PLL_PARAMS(_m, _n) \
- { \
- .m = (_m), \
- .n = (_n), \
- }
-
-#define CLK_MESON_PLL_ROUND_CLOSEST BIT(0)
-
-struct meson_clk_pll_data {
- struct parm en;
- struct parm m;
- struct parm n;
- struct parm frac;
- struct parm l;
- struct parm rst;
- const struct reg_sequence *init_regs;
- unsigned int init_count;
- const struct pll_params_table *table;
- u8 flags;
-};
-
-#define to_meson_clk_pll(_hw) container_of(_hw, struct meson_clk_pll, hw)
-
-struct meson_clk_mpll_data {
- struct parm sdm;
- struct parm sdm_en;
- struct parm n2;
- struct parm ssen;
- struct parm misc;
- spinlock_t *lock;
- u8 flags;
-};
-
-#define CLK_MESON_MPLL_ROUND_CLOSEST BIT(0)
-
-struct meson_clk_phase_data {
- struct parm ph;
-};
-
-int meson_clk_degrees_from_val(unsigned int val, unsigned int width);
-unsigned int meson_clk_degrees_to_val(int degrees, unsigned int width);
-
-struct meson_vid_pll_div_data {
- struct parm val;
- struct parm sel;
-};
-
-#define MESON_GATE(_name, _reg, _bit) \
-struct clk_regmap _name = { \
- .data = &(struct clk_regmap_gate_data){ \
- .offset = (_reg), \
- .bit_idx = (_bit), \
- }, \
- .hw.init = &(struct clk_init_data) { \
- .name = #_name, \
- .ops = &clk_regmap_gate_ops, \
- .parent_names = (const char *[]){ "clk81" }, \
- .num_parents = 1, \
- .flags = (CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED), \
- }, \
-};
-
-/* clk_ops */
-extern const struct clk_ops meson_clk_pll_ro_ops;
-extern const struct clk_ops meson_clk_pll_ops;
-extern const struct clk_ops meson_clk_cpu_ops;
-extern const struct clk_ops meson_clk_mpll_ro_ops;
-extern const struct clk_ops meson_clk_mpll_ops;
-extern const struct clk_ops meson_clk_phase_ops;
-extern const struct clk_ops meson_vid_pll_div_ro_ops;
-
-struct clk_hw *meson_clk_hw_register_input(struct device *dev,
- const char *of_name,
- const char *clk_name,
- unsigned long flags);
-
-#endif /* __CLKC_H */
diff --git a/drivers/clk/meson/g12a-aoclk.c b/drivers/clk/meson/g12a-aoclk.c
new file mode 100644
index 000000000000..1994e735396b
--- /dev/null
+++ b/drivers/clk/meson/g12a-aoclk.c
@@ -0,0 +1,454 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Amlogic Meson-AXG Clock Controller Driver
+ *
+ * Copyright (c) 2016 Baylibre SAS.
+ * Author: Michael Turquette <mturquette@baylibre.com>
+ *
+ * Copyright (c) 2019 Baylibre SAS.
+ * Author: Neil Armstrong <narmstrong@baylibre.com>
+ */
+#include <linux/clk-provider.h>
+#include <linux/platform_device.h>
+#include <linux/reset-controller.h>
+#include <linux/mfd/syscon.h>
+#include "meson-aoclk.h"
+#include "g12a-aoclk.h"
+
+#include "clk-regmap.h"
+#include "clk-dualdiv.h"
+
+#define IN_PREFIX "ao-in-"
+
+/*
+ * AO Configuration Clock registers offsets
+ * Register offsets from the data sheet must be multiplied by 4.
+ */
+#define AO_RTI_STATUS_REG3 0x0C
+#define AO_RTI_PWR_CNTL_REG0 0x10
+#define AO_RTI_GEN_CNTL_REG0 0x40
+#define AO_CLK_GATE0 0x4c
+#define AO_CLK_GATE0_SP 0x50
+#define AO_OSCIN_CNTL 0x58
+#define AO_CEC_CLK_CNTL_REG0 0x74
+#define AO_CEC_CLK_CNTL_REG1 0x78
+#define AO_SAR_CLK 0x90
+#define AO_RTC_ALT_CLK_CNTL0 0x94
+#define AO_RTC_ALT_CLK_CNTL1 0x98
+
+/*
+ * Like every other peripheral clock gate in Amlogic Clock drivers,
+ * we are using CLK_IGNORE_UNUSED here, so we keep the state of the
+ * bootloader. The goal is to remove this flag at some point.
+ * Actually removing it will require some extensive test to be done safely.
+ */
+#define AXG_AO_GATE(_name, _reg, _bit) \
+static struct clk_regmap g12a_aoclk_##_name = { \
+ .data = &(struct clk_regmap_gate_data) { \
+ .offset = (_reg), \
+ .bit_idx = (_bit), \
+ }, \
+ .hw.init = &(struct clk_init_data) { \
+ .name = "g12a_ao_" #_name, \
+ .ops = &clk_regmap_gate_ops, \
+ .parent_names = (const char *[]){ IN_PREFIX "mpeg-clk" }, \
+ .num_parents = 1, \
+ .flags = CLK_IGNORE_UNUSED, \
+ }, \
+}
+
+AXG_AO_GATE(ahb, AO_CLK_GATE0, 0);
+AXG_AO_GATE(ir_in, AO_CLK_GATE0, 1);
+AXG_AO_GATE(i2c_m0, AO_CLK_GATE0, 2);
+AXG_AO_GATE(i2c_s0, AO_CLK_GATE0, 3);
+AXG_AO_GATE(uart, AO_CLK_GATE0, 4);
+AXG_AO_GATE(prod_i2c, AO_CLK_GATE0, 5);
+AXG_AO_GATE(uart2, AO_CLK_GATE0, 6);
+AXG_AO_GATE(ir_out, AO_CLK_GATE0, 7);
+AXG_AO_GATE(saradc, AO_CLK_GATE0, 8);
+AXG_AO_GATE(mailbox, AO_CLK_GATE0_SP, 0);
+AXG_AO_GATE(m3, AO_CLK_GATE0_SP, 1);
+AXG_AO_GATE(ahb_sram, AO_CLK_GATE0_SP, 2);
+AXG_AO_GATE(rti, AO_CLK_GATE0_SP, 3);
+AXG_AO_GATE(m4_fclk, AO_CLK_GATE0_SP, 4);
+AXG_AO_GATE(m4_hclk, AO_CLK_GATE0_SP, 5);
+
+static struct clk_regmap g12a_aoclk_cts_oscin = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = AO_RTI_PWR_CNTL_REG0,
+ .bit_idx = 14,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "cts_oscin",
+ .ops = &clk_regmap_gate_ro_ops,
+ .parent_names = (const char *[]){ IN_PREFIX "xtal" },
+ .num_parents = 1,
+ },
+};
+
+static const struct meson_clk_dualdiv_param g12a_32k_div_table[] = {
+ {
+ .dual = 1,
+ .n1 = 733,
+ .m1 = 8,
+ .n2 = 732,
+ .m2 = 11,
+ }, {}
+};
+
+/* 32k_by_oscin clock */
+
+static struct clk_regmap g12a_aoclk_32k_by_oscin_pre = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = AO_RTC_ALT_CLK_CNTL0,
+ .bit_idx = 31,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "g12a_ao_32k_by_oscin_pre",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "cts_oscin" },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap g12a_aoclk_32k_by_oscin_div = {
+ .data = &(struct meson_clk_dualdiv_data){
+ .n1 = {
+ .reg_off = AO_RTC_ALT_CLK_CNTL0,
+ .shift = 0,
+ .width = 12,
+ },
+ .n2 = {
+ .reg_off = AO_RTC_ALT_CLK_CNTL0,
+ .shift = 12,
+ .width = 12,
+ },
+ .m1 = {
+ .reg_off = AO_RTC_ALT_CLK_CNTL1,
+ .shift = 0,
+ .width = 12,
+ },
+ .m2 = {
+ .reg_off = AO_RTC_ALT_CLK_CNTL1,
+ .shift = 12,
+ .width = 12,
+ },
+ .dual = {
+ .reg_off = AO_RTC_ALT_CLK_CNTL0,
+ .shift = 28,
+ .width = 1,
+ },
+ .table = g12a_32k_div_table,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "g12a_ao_32k_by_oscin_div",
+ .ops = &meson_clk_dualdiv_ops,
+ .parent_names = (const char *[]){ "g12a_ao_32k_by_oscin_pre" },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap g12a_aoclk_32k_by_oscin_sel = {
+ .data = &(struct clk_regmap_mux_data) {
+ .offset = AO_RTC_ALT_CLK_CNTL1,
+ .mask = 0x1,
+ .shift = 24,
+ .flags = CLK_MUX_ROUND_CLOSEST,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "g12a_ao_32k_by_oscin_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_names = (const char *[]){ "g12a_ao_32k_by_oscin_div",
+ "g12a_ao_32k_by_oscin_pre" },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap g12a_aoclk_32k_by_oscin = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = AO_RTC_ALT_CLK_CNTL0,
+ .bit_idx = 30,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "g12a_ao_32k_by_oscin",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "g12a_ao_32k_by_oscin_sel" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+/* cec clock */
+
+static struct clk_regmap g12a_aoclk_cec_pre = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = AO_CEC_CLK_CNTL_REG0,
+ .bit_idx = 31,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "g12a_ao_cec_pre",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "cts_oscin" },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap g12a_aoclk_cec_div = {
+ .data = &(struct meson_clk_dualdiv_data){
+ .n1 = {
+ .reg_off = AO_CEC_CLK_CNTL_REG0,
+ .shift = 0,
+ .width = 12,
+ },
+ .n2 = {
+ .reg_off = AO_CEC_CLK_CNTL_REG0,
+ .shift = 12,
+ .width = 12,
+ },
+ .m1 = {
+ .reg_off = AO_CEC_CLK_CNTL_REG1,
+ .shift = 0,
+ .width = 12,
+ },
+ .m2 = {
+ .reg_off = AO_CEC_CLK_CNTL_REG1,
+ .shift = 12,
+ .width = 12,
+ },
+ .dual = {
+ .reg_off = AO_CEC_CLK_CNTL_REG0,
+ .shift = 28,
+ .width = 1,
+ },
+ .table = g12a_32k_div_table,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "g12a_ao_cec_div",
+ .ops = &meson_clk_dualdiv_ops,
+ .parent_names = (const char *[]){ "g12a_ao_cec_pre" },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap g12a_aoclk_cec_sel = {
+ .data = &(struct clk_regmap_mux_data) {
+ .offset = AO_CEC_CLK_CNTL_REG1,
+ .mask = 0x1,
+ .shift = 24,
+ .flags = CLK_MUX_ROUND_CLOSEST,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "g12a_ao_cec_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_names = (const char *[]){ "g12a_ao_cec_div",
+ "g12a_ao_cec_pre" },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap g12a_aoclk_cec = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = AO_CEC_CLK_CNTL_REG0,
+ .bit_idx = 30,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "g12a_ao_cec",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "g12a_ao_cec_sel" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap g12a_aoclk_cts_rtc_oscin = {
+ .data = &(struct clk_regmap_mux_data) {
+ .offset = AO_RTI_PWR_CNTL_REG0,
+ .mask = 0x1,
+ .shift = 10,
+ .flags = CLK_MUX_ROUND_CLOSEST,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "g12a_ao_cts_rtc_oscin",
+ .ops = &clk_regmap_mux_ops,
+ .parent_names = (const char *[]){ "g12a_ao_32k_by_oscin",
+ IN_PREFIX "ext_32k-0" },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap g12a_aoclk_clk81 = {
+ .data = &(struct clk_regmap_mux_data) {
+ .offset = AO_RTI_PWR_CNTL_REG0,
+ .mask = 0x1,
+ .shift = 8,
+ .flags = CLK_MUX_ROUND_CLOSEST,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "g12a_ao_clk81",
+ .ops = &clk_regmap_mux_ro_ops,
+ .parent_names = (const char *[]){ IN_PREFIX "mpeg-clk",
+ "g12a_ao_cts_rtc_oscin"},
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap g12a_aoclk_saradc_mux = {
+ .data = &(struct clk_regmap_mux_data) {
+ .offset = AO_SAR_CLK,
+ .mask = 0x3,
+ .shift = 9,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "g12a_ao_saradc_mux",
+ .ops = &clk_regmap_mux_ops,
+ .parent_names = (const char *[]){ IN_PREFIX "xtal",
+ "g12a_ao_clk81" },
+ .num_parents = 2,
+ },
+};
+
+static struct clk_regmap g12a_aoclk_saradc_div = {
+ .data = &(struct clk_regmap_div_data) {
+ .offset = AO_SAR_CLK,
+ .shift = 0,
+ .width = 8,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "g12a_ao_saradc_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_names = (const char *[]){ "g12a_ao_saradc_mux" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap g12a_aoclk_saradc_gate = {
+ .data = &(struct clk_regmap_gate_data) {
+ .offset = AO_SAR_CLK,
+ .bit_idx = 8,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "g12a_ao_saradc_gate",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "g12a_ao_saradc_div" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static const unsigned int g12a_aoclk_reset[] = {
+ [RESET_AO_IR_IN] = 16,
+ [RESET_AO_UART] = 17,
+ [RESET_AO_I2C_M] = 18,
+ [RESET_AO_I2C_S] = 19,
+ [RESET_AO_SAR_ADC] = 20,
+ [RESET_AO_UART2] = 22,
+ [RESET_AO_IR_OUT] = 23,
+};
+
+static struct clk_regmap *g12a_aoclk_regmap[] = {
+ &g12a_aoclk_ahb,
+ &g12a_aoclk_ir_in,
+ &g12a_aoclk_i2c_m0,
+ &g12a_aoclk_i2c_s0,
+ &g12a_aoclk_uart,
+ &g12a_aoclk_prod_i2c,
+ &g12a_aoclk_uart2,
+ &g12a_aoclk_ir_out,
+ &g12a_aoclk_saradc,
+ &g12a_aoclk_mailbox,
+ &g12a_aoclk_m3,
+ &g12a_aoclk_ahb_sram,
+ &g12a_aoclk_rti,
+ &g12a_aoclk_m4_fclk,
+ &g12a_aoclk_m4_hclk,
+ &g12a_aoclk_cts_oscin,
+ &g12a_aoclk_32k_by_oscin_pre,
+ &g12a_aoclk_32k_by_oscin_div,
+ &g12a_aoclk_32k_by_oscin_sel,
+ &g12a_aoclk_32k_by_oscin,
+ &g12a_aoclk_cec_pre,
+ &g12a_aoclk_cec_div,
+ &g12a_aoclk_cec_sel,
+ &g12a_aoclk_cec,
+ &g12a_aoclk_cts_rtc_oscin,
+ &g12a_aoclk_clk81,
+ &g12a_aoclk_saradc_mux,
+ &g12a_aoclk_saradc_div,
+ &g12a_aoclk_saradc_gate,
+};
+
+static const struct clk_hw_onecell_data g12a_aoclk_onecell_data = {
+ .hws = {
+ [CLKID_AO_AHB] = &g12a_aoclk_ahb.hw,
+ [CLKID_AO_IR_IN] = &g12a_aoclk_ir_in.hw,
+ [CLKID_AO_I2C_M0] = &g12a_aoclk_i2c_m0.hw,
+ [CLKID_AO_I2C_S0] = &g12a_aoclk_i2c_s0.hw,
+ [CLKID_AO_UART] = &g12a_aoclk_uart.hw,
+ [CLKID_AO_PROD_I2C] = &g12a_aoclk_prod_i2c.hw,
+ [CLKID_AO_UART2] = &g12a_aoclk_uart2.hw,
+ [CLKID_AO_IR_OUT] = &g12a_aoclk_ir_out.hw,
+ [CLKID_AO_SAR_ADC] = &g12a_aoclk_saradc.hw,
+ [CLKID_AO_MAILBOX] = &g12a_aoclk_mailbox.hw,
+ [CLKID_AO_M3] = &g12a_aoclk_m3.hw,
+ [CLKID_AO_AHB_SRAM] = &g12a_aoclk_ahb_sram.hw,
+ [CLKID_AO_RTI] = &g12a_aoclk_rti.hw,
+ [CLKID_AO_M4_FCLK] = &g12a_aoclk_m4_fclk.hw,
+ [CLKID_AO_M4_HCLK] = &g12a_aoclk_m4_hclk.hw,
+ [CLKID_AO_CLK81] = &g12a_aoclk_clk81.hw,
+ [CLKID_AO_SAR_ADC_SEL] = &g12a_aoclk_saradc_mux.hw,
+ [CLKID_AO_SAR_ADC_DIV] = &g12a_aoclk_saradc_div.hw,
+ [CLKID_AO_SAR_ADC_CLK] = &g12a_aoclk_saradc_gate.hw,
+ [CLKID_AO_CTS_OSCIN] = &g12a_aoclk_cts_oscin.hw,
+ [CLKID_AO_32K_PRE] = &g12a_aoclk_32k_by_oscin_pre.hw,
+ [CLKID_AO_32K_DIV] = &g12a_aoclk_32k_by_oscin_div.hw,
+ [CLKID_AO_32K_SEL] = &g12a_aoclk_32k_by_oscin_sel.hw,
+ [CLKID_AO_32K] = &g12a_aoclk_32k_by_oscin.hw,
+ [CLKID_AO_CEC_PRE] = &g12a_aoclk_cec_pre.hw,
+ [CLKID_AO_CEC_DIV] = &g12a_aoclk_cec_div.hw,
+ [CLKID_AO_CEC_SEL] = &g12a_aoclk_cec_sel.hw,
+ [CLKID_AO_CEC] = &g12a_aoclk_cec.hw,
+ [CLKID_AO_CTS_RTC_OSCIN] = &g12a_aoclk_cts_rtc_oscin.hw,
+ },
+ .num = NR_CLKS,
+};
+
+static const struct meson_aoclk_input g12a_aoclk_inputs[] = {
+ { .name = "xtal", .required = true },
+ { .name = "mpeg-clk", .required = true },
+ { .name = "ext-32k-0", .required = false },
+};
+
+static const struct meson_aoclk_data g12a_aoclkc_data = {
+ .reset_reg = AO_RTI_GEN_CNTL_REG0,
+ .num_reset = ARRAY_SIZE(g12a_aoclk_reset),
+ .reset = g12a_aoclk_reset,
+ .num_clks = ARRAY_SIZE(g12a_aoclk_regmap),
+ .clks = g12a_aoclk_regmap,
+ .hw_data = &g12a_aoclk_onecell_data,
+ .inputs = g12a_aoclk_inputs,
+ .num_inputs = ARRAY_SIZE(g12a_aoclk_inputs),
+ .input_prefix = IN_PREFIX,
+};
+
+static const struct of_device_id g12a_aoclkc_match_table[] = {
+ {
+ .compatible = "amlogic,meson-g12a-aoclkc",
+ .data = &g12a_aoclkc_data,
+ },
+ { }
+};
+
+static struct platform_driver g12a_aoclkc_driver = {
+ .probe = meson_aoclkc_probe,
+ .driver = {
+ .name = "g12a-aoclkc",
+ .of_match_table = g12a_aoclkc_match_table,
+ },
+};
+
+builtin_platform_driver(g12a_aoclkc_driver);
diff --git a/drivers/clk/meson/g12a-aoclk.h b/drivers/clk/meson/g12a-aoclk.h
new file mode 100644
index 000000000000..04b0d5506641
--- /dev/null
+++ b/drivers/clk/meson/g12a-aoclk.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */
+/*
+ * Copyright (c) 2019 BayLibre, SAS
+ * Author: Neil Armstrong <narmstrong@baylibre.com>
+ */
+
+#ifndef __G12A_AOCLKC_H
+#define __G12A_AOCLKC_H
+
+/*
+ * CLKID index values
+ *
+ * These indices are entirely contrived and do not map onto the hardware.
+ * It has now been decided to expose everything by default in the DT header:
+ * include/dt-bindings/clock/g12a-aoclkc.h. Only the clocks ids we don't want
+ * to expose, such as the internal muxes and dividers of composite clocks,
+ * will remain defined here.
+ */
+#define CLKID_AO_SAR_ADC_SEL 16
+#define CLKID_AO_SAR_ADC_DIV 17
+#define CLKID_AO_CTS_OSCIN 19
+#define CLKID_AO_32K_PRE 20
+#define CLKID_AO_32K_DIV 21
+#define CLKID_AO_32K_SEL 22
+#define CLKID_AO_CEC_PRE 24
+#define CLKID_AO_CEC_DIV 25
+#define CLKID_AO_CEC_SEL 26
+
+#define NR_CLKS 29
+
+#include <dt-bindings/clock/g12a-aoclkc.h>
+#include <dt-bindings/reset/g12a-aoclkc.h>
+
+#endif /* __G12A_AOCLKC_H */
diff --git a/drivers/clk/meson/g12a.c b/drivers/clk/meson/g12a.c
new file mode 100644
index 000000000000..0e1ce8c03259
--- /dev/null
+++ b/drivers/clk/meson/g12a.c
@@ -0,0 +1,2359 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Amlogic Meson-G12A Clock Controller Driver
+ *
+ * Copyright (c) 2016 Baylibre SAS.
+ * Author: Michael Turquette <mturquette@baylibre.com>
+ *
+ * Copyright (c) 2018 Amlogic, inc.
+ * Author: Qiufang Dai <qiufang.dai@amlogic.com>
+ * Author: Jian Hu <jian.hu@amlogic.com>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/init.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+
+#include "clk-input.h"
+#include "clk-mpll.h"
+#include "clk-pll.h"
+#include "clk-regmap.h"
+#include "vid-pll-div.h"
+#include "meson-eeclk.h"
+#include "g12a.h"
+
+static DEFINE_SPINLOCK(meson_clk_lock);
+
+static struct clk_regmap g12a_fixed_pll_dco = {
+ .data = &(struct meson_clk_pll_data){
+ .en = {
+ .reg_off = HHI_FIX_PLL_CNTL0,
+ .shift = 28,
+ .width = 1,
+ },
+ .m = {
+ .reg_off = HHI_FIX_PLL_CNTL0,
+ .shift = 0,
+ .width = 8,
+ },
+ .n = {
+ .reg_off = HHI_FIX_PLL_CNTL0,
+ .shift = 10,
+ .width = 5,
+ },
+ .frac = {
+ .reg_off = HHI_FIX_PLL_CNTL1,
+ .shift = 0,
+ .width = 17,
+ },
+ .l = {
+ .reg_off = HHI_FIX_PLL_CNTL0,
+ .shift = 31,
+ .width = 1,
+ },
+ .rst = {
+ .reg_off = HHI_FIX_PLL_CNTL0,
+ .shift = 29,
+ .width = 1,
+ },
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "fixed_pll_dco",
+ .ops = &meson_clk_pll_ro_ops,
+ .parent_names = (const char *[]){ IN_PREFIX "xtal" },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap g12a_fixed_pll = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = HHI_FIX_PLL_CNTL0,
+ .shift = 16,
+ .width = 2,
+ .flags = CLK_DIVIDER_POWER_OF_TWO,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "fixed_pll",
+ .ops = &clk_regmap_divider_ro_ops,
+ .parent_names = (const char *[]){ "fixed_pll_dco" },
+ .num_parents = 1,
+ /*
+ * This clock won't ever change at runtime so
+ * CLK_SET_RATE_PARENT is not required
+ */
+ },
+};
+
+/*
+ * Internal sys pll emulation configuration parameters
+ */
+static const struct reg_sequence g12a_sys_init_regs[] = {
+ { .reg = HHI_SYS_PLL_CNTL1, .def = 0x00000000 },
+ { .reg = HHI_SYS_PLL_CNTL2, .def = 0x00000000 },
+ { .reg = HHI_SYS_PLL_CNTL3, .def = 0x48681c00 },
+ { .reg = HHI_SYS_PLL_CNTL4, .def = 0x88770290 },
+ { .reg = HHI_SYS_PLL_CNTL5, .def = 0x39272000 },
+ { .reg = HHI_SYS_PLL_CNTL6, .def = 0x56540000 },
+};
+
+static struct clk_regmap g12a_sys_pll_dco = {
+ .data = &(struct meson_clk_pll_data){
+ .en = {
+ .reg_off = HHI_SYS_PLL_CNTL0,
+ .shift = 28,
+ .width = 1,
+ },
+ .m = {
+ .reg_off = HHI_SYS_PLL_CNTL0,
+ .shift = 0,
+ .width = 8,
+ },
+ .n = {
+ .reg_off = HHI_SYS_PLL_CNTL0,
+ .shift = 10,
+ .width = 5,
+ },
+ .l = {
+ .reg_off = HHI_SYS_PLL_CNTL0,
+ .shift = 31,
+ .width = 1,
+ },
+ .rst = {
+ .reg_off = HHI_SYS_PLL_CNTL0,
+ .shift = 29,
+ .width = 1,
+ },
+ .init_regs = g12a_sys_init_regs,
+ .init_count = ARRAY_SIZE(g12a_sys_init_regs),
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "sys_pll_dco",
+ .ops = &meson_clk_pll_ro_ops,
+ .parent_names = (const char *[]){ IN_PREFIX "xtal" },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap g12a_sys_pll = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = HHI_SYS_PLL_CNTL0,
+ .shift = 16,
+ .width = 3,
+ .flags = CLK_DIVIDER_POWER_OF_TWO,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "sys_pll",
+ .ops = &clk_regmap_divider_ro_ops,
+ .parent_names = (const char *[]){ "sys_pll_dco" },
+ .num_parents = 1,
+ },
+};
+
+static const struct pll_mult_range g12a_gp0_pll_mult_range = {
+ .min = 55,
+ .max = 255,
+};
+
+/*
+ * Internal gp0 pll emulation configuration parameters
+ */
+static const struct reg_sequence g12a_gp0_init_regs[] = {
+ { .reg = HHI_GP0_PLL_CNTL1, .def = 0x00000000 },
+ { .reg = HHI_GP0_PLL_CNTL2, .def = 0x00000000 },
+ { .reg = HHI_GP0_PLL_CNTL3, .def = 0x48681c00 },
+ { .reg = HHI_GP0_PLL_CNTL4, .def = 0x33771290 },
+ { .reg = HHI_GP0_PLL_CNTL5, .def = 0x39272000 },
+ { .reg = HHI_GP0_PLL_CNTL6, .def = 0x56540000 },
+};
+
+static struct clk_regmap g12a_gp0_pll_dco = {
+ .data = &(struct meson_clk_pll_data){
+ .en = {
+ .reg_off = HHI_GP0_PLL_CNTL0,
+ .shift = 28,
+ .width = 1,
+ },
+ .m = {
+ .reg_off = HHI_GP0_PLL_CNTL0,
+ .shift = 0,
+ .width = 8,
+ },
+ .n = {
+ .reg_off = HHI_GP0_PLL_CNTL0,
+ .shift = 10,
+ .width = 5,
+ },
+ .frac = {
+ .reg_off = HHI_GP0_PLL_CNTL1,
+ .shift = 0,
+ .width = 17,
+ },
+ .l = {
+ .reg_off = HHI_GP0_PLL_CNTL0,
+ .shift = 31,
+ .width = 1,
+ },
+ .rst = {
+ .reg_off = HHI_GP0_PLL_CNTL0,
+ .shift = 29,
+ .width = 1,
+ },
+ .range = &g12a_gp0_pll_mult_range,
+ .init_regs = g12a_gp0_init_regs,
+ .init_count = ARRAY_SIZE(g12a_gp0_init_regs),
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "gp0_pll_dco",
+ .ops = &meson_clk_pll_ops,
+ .parent_names = (const char *[]){ IN_PREFIX "xtal" },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap g12a_gp0_pll = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = HHI_GP0_PLL_CNTL0,
+ .shift = 16,
+ .width = 3,
+ .flags = (CLK_DIVIDER_POWER_OF_TWO |
+ CLK_DIVIDER_ROUND_CLOSEST),
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "gp0_pll",
+ .ops = &clk_regmap_divider_ops,
+ .parent_names = (const char *[]){ "gp0_pll_dco" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+/*
+ * Internal hifi pll emulation configuration parameters
+ */
+static const struct reg_sequence g12a_hifi_init_regs[] = {
+ { .reg = HHI_HIFI_PLL_CNTL1, .def = 0x00000000 },
+ { .reg = HHI_HIFI_PLL_CNTL2, .def = 0x00000000 },
+ { .reg = HHI_HIFI_PLL_CNTL3, .def = 0x6a285c00 },
+ { .reg = HHI_HIFI_PLL_CNTL4, .def = 0x65771290 },
+ { .reg = HHI_HIFI_PLL_CNTL5, .def = 0x39272000 },
+ { .reg = HHI_HIFI_PLL_CNTL6, .def = 0x56540000 },
+};
+
+static struct clk_regmap g12a_hifi_pll_dco = {
+ .data = &(struct meson_clk_pll_data){
+ .en = {
+ .reg_off = HHI_HIFI_PLL_CNTL0,
+ .shift = 28,
+ .width = 1,
+ },
+ .m = {
+ .reg_off = HHI_HIFI_PLL_CNTL0,
+ .shift = 0,
+ .width = 8,
+ },
+ .n = {
+ .reg_off = HHI_HIFI_PLL_CNTL0,
+ .shift = 10,
+ .width = 5,
+ },
+ .frac = {
+ .reg_off = HHI_HIFI_PLL_CNTL1,
+ .shift = 0,
+ .width = 17,
+ },
+ .l = {
+ .reg_off = HHI_HIFI_PLL_CNTL0,
+ .shift = 31,
+ .width = 1,
+ },
+ .rst = {
+ .reg_off = HHI_HIFI_PLL_CNTL0,
+ .shift = 29,
+ .width = 1,
+ },
+ .range = &g12a_gp0_pll_mult_range,
+ .init_regs = g12a_hifi_init_regs,
+ .init_count = ARRAY_SIZE(g12a_hifi_init_regs),
+ .flags = CLK_MESON_PLL_ROUND_CLOSEST,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "hifi_pll_dco",
+ .ops = &meson_clk_pll_ops,
+ .parent_names = (const char *[]){ IN_PREFIX "xtal" },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap g12a_hifi_pll = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = HHI_HIFI_PLL_CNTL0,
+ .shift = 16,
+ .width = 2,
+ .flags = (CLK_DIVIDER_POWER_OF_TWO |
+ CLK_DIVIDER_ROUND_CLOSEST),
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "hifi_pll",
+ .ops = &clk_regmap_divider_ops,
+ .parent_names = (const char *[]){ "hifi_pll_dco" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap g12a_hdmi_pll_dco = {
+ .data = &(struct meson_clk_pll_data){
+ .en = {
+ .reg_off = HHI_HDMI_PLL_CNTL0,
+ .shift = 28,
+ .width = 1,
+ },
+ .m = {
+ .reg_off = HHI_HDMI_PLL_CNTL0,
+ .shift = 0,
+ .width = 8,
+ },
+ .n = {
+ .reg_off = HHI_HDMI_PLL_CNTL0,
+ .shift = 10,
+ .width = 5,
+ },
+ .frac = {
+ .reg_off = HHI_HDMI_PLL_CNTL1,
+ .shift = 0,
+ .width = 16,
+ },
+ .l = {
+ .reg_off = HHI_HDMI_PLL_CNTL0,
+ .shift = 30,
+ .width = 1,
+ },
+ .rst = {
+ .reg_off = HHI_HDMI_PLL_CNTL0,
+ .shift = 29,
+ .width = 1,
+ },
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "hdmi_pll_dco",
+ .ops = &meson_clk_pll_ro_ops,
+ .parent_names = (const char *[]){ IN_PREFIX "xtal" },
+ .num_parents = 1,
+ /*
+ * Display directly handle hdmi pll registers ATM, we need
+ * NOCACHE to keep our view of the clock as accurate as possible
+ */
+ .flags = CLK_GET_RATE_NOCACHE,
+ },
+};
+
+static struct clk_regmap g12a_hdmi_pll_od = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = HHI_HDMI_PLL_CNTL0,
+ .shift = 16,
+ .width = 2,
+ .flags = CLK_DIVIDER_POWER_OF_TWO,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "hdmi_pll_od",
+ .ops = &clk_regmap_divider_ro_ops,
+ .parent_names = (const char *[]){ "hdmi_pll_dco" },
+ .num_parents = 1,
+ .flags = CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap g12a_hdmi_pll_od2 = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = HHI_HDMI_PLL_CNTL0,
+ .shift = 18,
+ .width = 2,
+ .flags = CLK_DIVIDER_POWER_OF_TWO,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "hdmi_pll_od2",
+ .ops = &clk_regmap_divider_ro_ops,
+ .parent_names = (const char *[]){ "hdmi_pll_od" },
+ .num_parents = 1,
+ .flags = CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap g12a_hdmi_pll = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = HHI_HDMI_PLL_CNTL0,
+ .shift = 20,
+ .width = 2,
+ .flags = CLK_DIVIDER_POWER_OF_TWO,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "hdmi_pll",
+ .ops = &clk_regmap_divider_ro_ops,
+ .parent_names = (const char *[]){ "hdmi_pll_od2" },
+ .num_parents = 1,
+ .flags = CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_fixed_factor g12a_fclk_div2_div = {
+ .mult = 1,
+ .div = 2,
+ .hw.init = &(struct clk_init_data){
+ .name = "fclk_div2_div",
+ .ops = &clk_fixed_factor_ops,
+ .parent_names = (const char *[]){ "fixed_pll" },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap g12a_fclk_div2 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_FIX_PLL_CNTL1,
+ .bit_idx = 24,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "fclk_div2",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "fclk_div2_div" },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_fixed_factor g12a_fclk_div3_div = {
+ .mult = 1,
+ .div = 3,
+ .hw.init = &(struct clk_init_data){
+ .name = "fclk_div3_div",
+ .ops = &clk_fixed_factor_ops,
+ .parent_names = (const char *[]){ "fixed_pll" },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap g12a_fclk_div3 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_FIX_PLL_CNTL1,
+ .bit_idx = 20,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "fclk_div3",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "fclk_div3_div" },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_fixed_factor g12a_fclk_div4_div = {
+ .mult = 1,
+ .div = 4,
+ .hw.init = &(struct clk_init_data){
+ .name = "fclk_div4_div",
+ .ops = &clk_fixed_factor_ops,
+ .parent_names = (const char *[]){ "fixed_pll" },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap g12a_fclk_div4 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_FIX_PLL_CNTL1,
+ .bit_idx = 21,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "fclk_div4",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "fclk_div4_div" },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_fixed_factor g12a_fclk_div5_div = {
+ .mult = 1,
+ .div = 5,
+ .hw.init = &(struct clk_init_data){
+ .name = "fclk_div5_div",
+ .ops = &clk_fixed_factor_ops,
+ .parent_names = (const char *[]){ "fixed_pll" },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap g12a_fclk_div5 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_FIX_PLL_CNTL1,
+ .bit_idx = 22,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "fclk_div5",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "fclk_div5_div" },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_fixed_factor g12a_fclk_div7_div = {
+ .mult = 1,
+ .div = 7,
+ .hw.init = &(struct clk_init_data){
+ .name = "fclk_div7_div",
+ .ops = &clk_fixed_factor_ops,
+ .parent_names = (const char *[]){ "fixed_pll" },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap g12a_fclk_div7 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_FIX_PLL_CNTL1,
+ .bit_idx = 23,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "fclk_div7",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "fclk_div7_div" },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_fixed_factor g12a_fclk_div2p5_div = {
+ .mult = 1,
+ .div = 5,
+ .hw.init = &(struct clk_init_data){
+ .name = "fclk_div2p5_div",
+ .ops = &clk_fixed_factor_ops,
+ .parent_names = (const char *[]){ "fixed_pll_dco" },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap g12a_fclk_div2p5 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_FIX_PLL_CNTL1,
+ .bit_idx = 25,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "fclk_div2p5",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "fclk_div2p5_div" },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_fixed_factor g12a_mpll_50m_div = {
+ .mult = 1,
+ .div = 80,
+ .hw.init = &(struct clk_init_data){
+ .name = "mpll_50m_div",
+ .ops = &clk_fixed_factor_ops,
+ .parent_names = (const char *[]){ "fixed_pll_dco" },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap g12a_mpll_50m = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HHI_FIX_PLL_CNTL3,
+ .mask = 0x1,
+ .shift = 5,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "mpll_50m",
+ .ops = &clk_regmap_mux_ro_ops,
+ .parent_names = (const char *[]){ IN_PREFIX "xtal",
+ "mpll_50m_div" },
+ .num_parents = 2,
+ },
+};
+
+static struct clk_fixed_factor g12a_mpll_prediv = {
+ .mult = 1,
+ .div = 2,
+ .hw.init = &(struct clk_init_data){
+ .name = "mpll_prediv",
+ .ops = &clk_fixed_factor_ops,
+ .parent_names = (const char *[]){ "fixed_pll_dco" },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap g12a_mpll0_div = {
+ .data = &(struct meson_clk_mpll_data){
+ .sdm = {
+ .reg_off = HHI_MPLL_CNTL1,
+ .shift = 0,
+ .width = 14,
+ },
+ .sdm_en = {
+ .reg_off = HHI_MPLL_CNTL1,
+ .shift = 30,
+ .width = 1,
+ },
+ .n2 = {
+ .reg_off = HHI_MPLL_CNTL1,
+ .shift = 20,
+ .width = 9,
+ },
+ .ssen = {
+ .reg_off = HHI_MPLL_CNTL1,
+ .shift = 29,
+ .width = 1,
+ },
+ .lock = &meson_clk_lock,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "mpll0_div",
+ .ops = &meson_clk_mpll_ops,
+ .parent_names = (const char *[]){ "mpll_prediv" },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap g12a_mpll0 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_MPLL_CNTL1,
+ .bit_idx = 31,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "mpll0",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "mpll0_div" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap g12a_mpll1_div = {
+ .data = &(struct meson_clk_mpll_data){
+ .sdm = {
+ .reg_off = HHI_MPLL_CNTL3,
+ .shift = 0,
+ .width = 14,
+ },
+ .sdm_en = {
+ .reg_off = HHI_MPLL_CNTL3,
+ .shift = 30,
+ .width = 1,
+ },
+ .n2 = {
+ .reg_off = HHI_MPLL_CNTL3,
+ .shift = 20,
+ .width = 9,
+ },
+ .ssen = {
+ .reg_off = HHI_MPLL_CNTL3,
+ .shift = 29,
+ .width = 1,
+ },
+ .lock = &meson_clk_lock,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "mpll1_div",
+ .ops = &meson_clk_mpll_ops,
+ .parent_names = (const char *[]){ "mpll_prediv" },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap g12a_mpll1 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_MPLL_CNTL3,
+ .bit_idx = 31,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "mpll1",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "mpll1_div" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap g12a_mpll2_div = {
+ .data = &(struct meson_clk_mpll_data){
+ .sdm = {
+ .reg_off = HHI_MPLL_CNTL5,
+ .shift = 0,
+ .width = 14,
+ },
+ .sdm_en = {
+ .reg_off = HHI_MPLL_CNTL5,
+ .shift = 30,
+ .width = 1,
+ },
+ .n2 = {
+ .reg_off = HHI_MPLL_CNTL5,
+ .shift = 20,
+ .width = 9,
+ },
+ .ssen = {
+ .reg_off = HHI_MPLL_CNTL5,
+ .shift = 29,
+ .width = 1,
+ },
+ .lock = &meson_clk_lock,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "mpll2_div",
+ .ops = &meson_clk_mpll_ops,
+ .parent_names = (const char *[]){ "mpll_prediv" },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap g12a_mpll2 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_MPLL_CNTL5,
+ .bit_idx = 31,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "mpll2",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "mpll2_div" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap g12a_mpll3_div = {
+ .data = &(struct meson_clk_mpll_data){
+ .sdm = {
+ .reg_off = HHI_MPLL_CNTL7,
+ .shift = 0,
+ .width = 14,
+ },
+ .sdm_en = {
+ .reg_off = HHI_MPLL_CNTL7,
+ .shift = 30,
+ .width = 1,
+ },
+ .n2 = {
+ .reg_off = HHI_MPLL_CNTL7,
+ .shift = 20,
+ .width = 9,
+ },
+ .ssen = {
+ .reg_off = HHI_MPLL_CNTL7,
+ .shift = 29,
+ .width = 1,
+ },
+ .lock = &meson_clk_lock,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "mpll3_div",
+ .ops = &meson_clk_mpll_ops,
+ .parent_names = (const char *[]){ "mpll_prediv" },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap g12a_mpll3 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_MPLL_CNTL7,
+ .bit_idx = 31,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "mpll3",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "mpll3_div" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static u32 mux_table_clk81[] = { 0, 2, 3, 4, 5, 6, 7 };
+static const char * const clk81_parent_names[] = {
+ IN_PREFIX "xtal", "fclk_div7", "mpll1", "mpll2", "fclk_div4",
+ "fclk_div3", "fclk_div5"
+};
+
+static struct clk_regmap g12a_mpeg_clk_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HHI_MPEG_CLK_CNTL,
+ .mask = 0x7,
+ .shift = 12,
+ .table = mux_table_clk81,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "mpeg_clk_sel",
+ .ops = &clk_regmap_mux_ro_ops,
+ .parent_names = clk81_parent_names,
+ .num_parents = ARRAY_SIZE(clk81_parent_names),
+ },
+};
+
+static struct clk_regmap g12a_mpeg_clk_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = HHI_MPEG_CLK_CNTL,
+ .shift = 0,
+ .width = 7,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "mpeg_clk_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_names = (const char *[]){ "mpeg_clk_sel" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap g12a_clk81 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_MPEG_CLK_CNTL,
+ .bit_idx = 7,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "clk81",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "mpeg_clk_div" },
+ .num_parents = 1,
+ .flags = (CLK_SET_RATE_PARENT | CLK_IS_CRITICAL),
+ },
+};
+
+static const char * const g12a_sd_emmc_clk0_parent_names[] = {
+ IN_PREFIX "xtal", "fclk_div2", "fclk_div3", "fclk_div5", "fclk_div7",
+
+ /*
+ * Following these parent clocks, we should also have had mpll2, mpll3
+ * and gp0_pll but these clocks are too precious to be used here. All
+ * the necessary rates for MMC and NAND operation can be acheived using
+ * g12a_ee_core or fclk_div clocks
+ */
+};
+
+/* SDIO clock */
+static struct clk_regmap g12a_sd_emmc_a_clk0_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HHI_SD_EMMC_CLK_CNTL,
+ .mask = 0x7,
+ .shift = 9,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "sd_emmc_a_clk0_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_names = g12a_sd_emmc_clk0_parent_names,
+ .num_parents = ARRAY_SIZE(g12a_sd_emmc_clk0_parent_names),
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap g12a_sd_emmc_a_clk0_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = HHI_SD_EMMC_CLK_CNTL,
+ .shift = 0,
+ .width = 7,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "sd_emmc_a_clk0_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_names = (const char *[]){ "sd_emmc_a_clk0_sel" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap g12a_sd_emmc_a_clk0 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_SD_EMMC_CLK_CNTL,
+ .bit_idx = 7,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "sd_emmc_a_clk0",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "sd_emmc_a_clk0_div" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+/* SDcard clock */
+static struct clk_regmap g12a_sd_emmc_b_clk0_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HHI_SD_EMMC_CLK_CNTL,
+ .mask = 0x7,
+ .shift = 25,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "sd_emmc_b_clk0_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_names = g12a_sd_emmc_clk0_parent_names,
+ .num_parents = ARRAY_SIZE(g12a_sd_emmc_clk0_parent_names),
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap g12a_sd_emmc_b_clk0_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = HHI_SD_EMMC_CLK_CNTL,
+ .shift = 16,
+ .width = 7,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "sd_emmc_b_clk0_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_names = (const char *[]){ "sd_emmc_b_clk0_sel" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap g12a_sd_emmc_b_clk0 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_SD_EMMC_CLK_CNTL,
+ .bit_idx = 23,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "sd_emmc_b_clk0",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "sd_emmc_b_clk0_div" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+/* EMMC/NAND clock */
+static struct clk_regmap g12a_sd_emmc_c_clk0_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HHI_NAND_CLK_CNTL,
+ .mask = 0x7,
+ .shift = 9,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "sd_emmc_c_clk0_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_names = g12a_sd_emmc_clk0_parent_names,
+ .num_parents = ARRAY_SIZE(g12a_sd_emmc_clk0_parent_names),
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap g12a_sd_emmc_c_clk0_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = HHI_NAND_CLK_CNTL,
+ .shift = 0,
+ .width = 7,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "sd_emmc_c_clk0_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_names = (const char *[]){ "sd_emmc_c_clk0_sel" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap g12a_sd_emmc_c_clk0 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_NAND_CLK_CNTL,
+ .bit_idx = 7,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "sd_emmc_c_clk0",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "sd_emmc_c_clk0_div" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+/* VPU Clock */
+
+static const char * const g12a_vpu_parent_names[] = {
+ "fclk_div4", "fclk_div3", "fclk_div5", "fclk_div7",
+ "mpll1", "vid_pll", "hifi_pll", "gp0_pll",
+};
+
+static struct clk_regmap g12a_vpu_0_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HHI_VPU_CLK_CNTL,
+ .mask = 0x3,
+ .shift = 9,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "vpu_0_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_names = g12a_vpu_parent_names,
+ .num_parents = ARRAY_SIZE(g12a_vpu_parent_names),
+ .flags = CLK_SET_RATE_NO_REPARENT,
+ },
+};
+
+static struct clk_regmap g12a_vpu_0_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = HHI_VPU_CLK_CNTL,
+ .shift = 0,
+ .width = 7,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "vpu_0_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_names = (const char *[]){ "vpu_0_sel" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap g12a_vpu_0 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_VPU_CLK_CNTL,
+ .bit_idx = 8,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vpu_0",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "vpu_0_div" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
+ },
+};
+
+static struct clk_regmap g12a_vpu_1_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HHI_VPU_CLK_CNTL,
+ .mask = 0x3,
+ .shift = 25,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "vpu_1_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_names = g12a_vpu_parent_names,
+ .num_parents = ARRAY_SIZE(g12a_vpu_parent_names),
+ .flags = CLK_SET_RATE_NO_REPARENT,
+ },
+};
+
+static struct clk_regmap g12a_vpu_1_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = HHI_VPU_CLK_CNTL,
+ .shift = 16,
+ .width = 7,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "vpu_1_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_names = (const char *[]){ "vpu_1_sel" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap g12a_vpu_1 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_VPU_CLK_CNTL,
+ .bit_idx = 24,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vpu_1",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "vpu_1_div" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
+ },
+};
+
+static struct clk_regmap g12a_vpu = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HHI_VPU_CLK_CNTL,
+ .mask = 1,
+ .shift = 31,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "vpu",
+ .ops = &clk_regmap_mux_ops,
+ /*
+ * bit 31 selects from 2 possible parents:
+ * vpu_0 or vpu_1
+ */
+ .parent_names = (const char *[]){ "vpu_0", "vpu_1" },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_NO_REPARENT,
+ },
+};
+
+/* VAPB Clock */
+
+static const char * const g12a_vapb_parent_names[] = {
+ "fclk_div4", "fclk_div3", "fclk_div5", "fclk_div7",
+ "mpll1", "vid_pll", "mpll2", "fclk_div2p5",
+};
+
+static struct clk_regmap g12a_vapb_0_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HHI_VAPBCLK_CNTL,
+ .mask = 0x3,
+ .shift = 9,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "vapb_0_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_names = g12a_vapb_parent_names,
+ .num_parents = ARRAY_SIZE(g12a_vapb_parent_names),
+ .flags = CLK_SET_RATE_NO_REPARENT,
+ },
+};
+
+static struct clk_regmap g12a_vapb_0_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = HHI_VAPBCLK_CNTL,
+ .shift = 0,
+ .width = 7,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "vapb_0_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_names = (const char *[]){ "vapb_0_sel" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap g12a_vapb_0 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_VAPBCLK_CNTL,
+ .bit_idx = 8,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vapb_0",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "vapb_0_div" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
+ },
+};
+
+static struct clk_regmap g12a_vapb_1_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HHI_VAPBCLK_CNTL,
+ .mask = 0x3,
+ .shift = 25,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "vapb_1_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_names = g12a_vapb_parent_names,
+ .num_parents = ARRAY_SIZE(g12a_vapb_parent_names),
+ .flags = CLK_SET_RATE_NO_REPARENT,
+ },
+};
+
+static struct clk_regmap g12a_vapb_1_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = HHI_VAPBCLK_CNTL,
+ .shift = 16,
+ .width = 7,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "vapb_1_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_names = (const char *[]){ "vapb_1_sel" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap g12a_vapb_1 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_VAPBCLK_CNTL,
+ .bit_idx = 24,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vapb_1",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "vapb_1_div" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
+ },
+};
+
+static struct clk_regmap g12a_vapb_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HHI_VAPBCLK_CNTL,
+ .mask = 1,
+ .shift = 31,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "vapb_sel",
+ .ops = &clk_regmap_mux_ops,
+ /*
+ * bit 31 selects from 2 possible parents:
+ * vapb_0 or vapb_1
+ */
+ .parent_names = (const char *[]){ "vapb_0", "vapb_1" },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_NO_REPARENT,
+ },
+};
+
+static struct clk_regmap g12a_vapb = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_VAPBCLK_CNTL,
+ .bit_idx = 30,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vapb",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "vapb_sel" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
+ },
+};
+
+/* Video Clocks */
+
+static struct clk_regmap g12a_vid_pll_div = {
+ .data = &(struct meson_vid_pll_div_data){
+ .val = {
+ .reg_off = HHI_VID_PLL_CLK_DIV,
+ .shift = 0,
+ .width = 15,
+ },
+ .sel = {
+ .reg_off = HHI_VID_PLL_CLK_DIV,
+ .shift = 16,
+ .width = 2,
+ },
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vid_pll_div",
+ .ops = &meson_vid_pll_div_ro_ops,
+ .parent_names = (const char *[]){ "hdmi_pll" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE,
+ },
+};
+
+static const char * const g12a_vid_pll_parent_names[] = { "vid_pll_div",
+ "hdmi_pll" };
+
+static struct clk_regmap g12a_vid_pll_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HHI_VID_PLL_CLK_DIV,
+ .mask = 0x1,
+ .shift = 18,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "vid_pll_sel",
+ .ops = &clk_regmap_mux_ops,
+ /*
+ * bit 18 selects from 2 possible parents:
+ * vid_pll_div or hdmi_pll
+ */
+ .parent_names = g12a_vid_pll_parent_names,
+ .num_parents = ARRAY_SIZE(g12a_vid_pll_parent_names),
+ .flags = CLK_SET_RATE_NO_REPARENT | CLK_GET_RATE_NOCACHE,
+ },
+};
+
+static struct clk_regmap g12a_vid_pll = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_VID_PLL_CLK_DIV,
+ .bit_idx = 19,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vid_pll",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "vid_pll_sel" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
+ },
+};
+
+static const char * const g12a_vclk_parent_names[] = {
+ "vid_pll", "gp0_pll", "hifi_pll", "mpll1", "fclk_div3", "fclk_div4",
+ "fclk_div5", "fclk_div7"
+};
+
+static struct clk_regmap g12a_vclk_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HHI_VID_CLK_CNTL,
+ .mask = 0x7,
+ .shift = 16,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "vclk_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_names = g12a_vclk_parent_names,
+ .num_parents = ARRAY_SIZE(g12a_vclk_parent_names),
+ .flags = CLK_SET_RATE_NO_REPARENT | CLK_GET_RATE_NOCACHE,
+ },
+};
+
+static struct clk_regmap g12a_vclk2_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HHI_VIID_CLK_CNTL,
+ .mask = 0x7,
+ .shift = 16,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "vclk2_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_names = g12a_vclk_parent_names,
+ .num_parents = ARRAY_SIZE(g12a_vclk_parent_names),
+ .flags = CLK_SET_RATE_NO_REPARENT | CLK_GET_RATE_NOCACHE,
+ },
+};
+
+static struct clk_regmap g12a_vclk_input = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_VID_CLK_DIV,
+ .bit_idx = 16,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vclk_input",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "vclk_sel" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
+ },
+};
+
+static struct clk_regmap g12a_vclk2_input = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_VIID_CLK_DIV,
+ .bit_idx = 16,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vclk2_input",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "vclk2_sel" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
+ },
+};
+
+static struct clk_regmap g12a_vclk_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = HHI_VID_CLK_DIV,
+ .shift = 0,
+ .width = 8,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "vclk_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_names = (const char *[]){ "vclk_input" },
+ .num_parents = 1,
+ .flags = CLK_GET_RATE_NOCACHE,
+ },
+};
+
+static struct clk_regmap g12a_vclk2_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = HHI_VIID_CLK_DIV,
+ .shift = 0,
+ .width = 8,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "vclk2_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_names = (const char *[]){ "vclk2_input" },
+ .num_parents = 1,
+ .flags = CLK_GET_RATE_NOCACHE,
+ },
+};
+
+static struct clk_regmap g12a_vclk = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_VID_CLK_CNTL,
+ .bit_idx = 19,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vclk",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "vclk_div" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
+ },
+};
+
+static struct clk_regmap g12a_vclk2 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_VIID_CLK_CNTL,
+ .bit_idx = 19,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vclk2",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "vclk2_div" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
+ },
+};
+
+static struct clk_regmap g12a_vclk_div1 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_VID_CLK_CNTL,
+ .bit_idx = 0,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vclk_div1",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "vclk" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
+ },
+};
+
+static struct clk_regmap g12a_vclk_div2_en = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_VID_CLK_CNTL,
+ .bit_idx = 1,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vclk_div2_en",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "vclk" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
+ },
+};
+
+static struct clk_regmap g12a_vclk_div4_en = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_VID_CLK_CNTL,
+ .bit_idx = 2,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vclk_div4_en",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "vclk" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
+ },
+};
+
+static struct clk_regmap g12a_vclk_div6_en = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_VID_CLK_CNTL,
+ .bit_idx = 3,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vclk_div6_en",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "vclk" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
+ },
+};
+
+static struct clk_regmap g12a_vclk_div12_en = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_VID_CLK_CNTL,
+ .bit_idx = 4,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vclk_div12_en",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "vclk" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
+ },
+};
+
+static struct clk_regmap g12a_vclk2_div1 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_VIID_CLK_CNTL,
+ .bit_idx = 0,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vclk2_div1",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "vclk2" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
+ },
+};
+
+static struct clk_regmap g12a_vclk2_div2_en = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_VIID_CLK_CNTL,
+ .bit_idx = 1,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vclk2_div2_en",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "vclk2" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
+ },
+};
+
+static struct clk_regmap g12a_vclk2_div4_en = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_VIID_CLK_CNTL,
+ .bit_idx = 2,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vclk2_div4_en",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "vclk2" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
+ },
+};
+
+static struct clk_regmap g12a_vclk2_div6_en = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_VIID_CLK_CNTL,
+ .bit_idx = 3,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vclk2_div6_en",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "vclk2" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
+ },
+};
+
+static struct clk_regmap g12a_vclk2_div12_en = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_VIID_CLK_CNTL,
+ .bit_idx = 4,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vclk2_div12_en",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "vclk2" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
+ },
+};
+
+static struct clk_fixed_factor g12a_vclk_div2 = {
+ .mult = 1,
+ .div = 2,
+ .hw.init = &(struct clk_init_data){
+ .name = "vclk_div2",
+ .ops = &clk_fixed_factor_ops,
+ .parent_names = (const char *[]){ "vclk_div2_en" },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_fixed_factor g12a_vclk_div4 = {
+ .mult = 1,
+ .div = 4,
+ .hw.init = &(struct clk_init_data){
+ .name = "vclk_div4",
+ .ops = &clk_fixed_factor_ops,
+ .parent_names = (const char *[]){ "vclk_div4_en" },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_fixed_factor g12a_vclk_div6 = {
+ .mult = 1,
+ .div = 6,
+ .hw.init = &(struct clk_init_data){
+ .name = "vclk_div6",
+ .ops = &clk_fixed_factor_ops,
+ .parent_names = (const char *[]){ "vclk_div6_en" },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_fixed_factor g12a_vclk_div12 = {
+ .mult = 1,
+ .div = 12,
+ .hw.init = &(struct clk_init_data){
+ .name = "vclk_div12",
+ .ops = &clk_fixed_factor_ops,
+ .parent_names = (const char *[]){ "vclk_div12_en" },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_fixed_factor g12a_vclk2_div2 = {
+ .mult = 1,
+ .div = 2,
+ .hw.init = &(struct clk_init_data){
+ .name = "vclk2_div2",
+ .ops = &clk_fixed_factor_ops,
+ .parent_names = (const char *[]){ "vclk2_div2_en" },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_fixed_factor g12a_vclk2_div4 = {
+ .mult = 1,
+ .div = 4,
+ .hw.init = &(struct clk_init_data){
+ .name = "vclk2_div4",
+ .ops = &clk_fixed_factor_ops,
+ .parent_names = (const char *[]){ "vclk2_div4_en" },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_fixed_factor g12a_vclk2_div6 = {
+ .mult = 1,
+ .div = 6,
+ .hw.init = &(struct clk_init_data){
+ .name = "vclk2_div6",
+ .ops = &clk_fixed_factor_ops,
+ .parent_names = (const char *[]){ "vclk2_div6_en" },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_fixed_factor g12a_vclk2_div12 = {
+ .mult = 1,
+ .div = 12,
+ .hw.init = &(struct clk_init_data){
+ .name = "vclk2_div12",
+ .ops = &clk_fixed_factor_ops,
+ .parent_names = (const char *[]){ "vclk2_div12_en" },
+ .num_parents = 1,
+ },
+};
+
+static u32 mux_table_cts_sel[] = { 0, 1, 2, 3, 4, 8, 9, 10, 11, 12 };
+static const char * const g12a_cts_parent_names[] = {
+ "vclk_div1", "vclk_div2", "vclk_div4", "vclk_div6",
+ "vclk_div12", "vclk2_div1", "vclk2_div2", "vclk2_div4",
+ "vclk2_div6", "vclk2_div12"
+};
+
+static struct clk_regmap g12a_cts_enci_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HHI_VID_CLK_DIV,
+ .mask = 0xf,
+ .shift = 28,
+ .table = mux_table_cts_sel,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "cts_enci_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_names = g12a_cts_parent_names,
+ .num_parents = ARRAY_SIZE(g12a_cts_parent_names),
+ .flags = CLK_SET_RATE_NO_REPARENT | CLK_GET_RATE_NOCACHE,
+ },
+};
+
+static struct clk_regmap g12a_cts_encp_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HHI_VID_CLK_DIV,
+ .mask = 0xf,
+ .shift = 20,
+ .table = mux_table_cts_sel,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "cts_encp_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_names = g12a_cts_parent_names,
+ .num_parents = ARRAY_SIZE(g12a_cts_parent_names),
+ .flags = CLK_SET_RATE_NO_REPARENT | CLK_GET_RATE_NOCACHE,
+ },
+};
+
+static struct clk_regmap g12a_cts_vdac_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HHI_VIID_CLK_DIV,
+ .mask = 0xf,
+ .shift = 28,
+ .table = mux_table_cts_sel,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "cts_vdac_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_names = g12a_cts_parent_names,
+ .num_parents = ARRAY_SIZE(g12a_cts_parent_names),
+ .flags = CLK_SET_RATE_NO_REPARENT | CLK_GET_RATE_NOCACHE,
+ },
+};
+
+/* TOFIX: add support for cts_tcon */
+static u32 mux_table_hdmi_tx_sel[] = { 0, 1, 2, 3, 4, 8, 9, 10, 11, 12 };
+static const char * const g12a_cts_hdmi_tx_parent_names[] = {
+ "vclk_div1", "vclk_div2", "vclk_div4", "vclk_div6",
+ "vclk_div12", "vclk2_div1", "vclk2_div2", "vclk2_div4",
+ "vclk2_div6", "vclk2_div12"
+};
+
+static struct clk_regmap g12a_hdmi_tx_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HHI_HDMI_CLK_CNTL,
+ .mask = 0xf,
+ .shift = 16,
+ .table = mux_table_hdmi_tx_sel,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "hdmi_tx_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_names = g12a_cts_hdmi_tx_parent_names,
+ .num_parents = ARRAY_SIZE(g12a_cts_hdmi_tx_parent_names),
+ .flags = CLK_SET_RATE_NO_REPARENT | CLK_GET_RATE_NOCACHE,
+ },
+};
+
+static struct clk_regmap g12a_cts_enci = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_VID_CLK_CNTL2,
+ .bit_idx = 0,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "cts_enci",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "cts_enci_sel" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
+ },
+};
+
+static struct clk_regmap g12a_cts_encp = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_VID_CLK_CNTL2,
+ .bit_idx = 2,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "cts_encp",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "cts_encp_sel" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
+ },
+};
+
+static struct clk_regmap g12a_cts_vdac = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_VID_CLK_CNTL2,
+ .bit_idx = 4,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "cts_vdac",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "cts_vdac_sel" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
+ },
+};
+
+static struct clk_regmap g12a_hdmi_tx = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_VID_CLK_CNTL2,
+ .bit_idx = 5,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "hdmi_tx",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "hdmi_tx_sel" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
+ },
+};
+
+/* HDMI Clocks */
+
+static const char * const g12a_hdmi_parent_names[] = {
+ IN_PREFIX "xtal", "fclk_div4", "fclk_div3", "fclk_div5"
+};
+
+static struct clk_regmap g12a_hdmi_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HHI_HDMI_CLK_CNTL,
+ .mask = 0x3,
+ .shift = 9,
+ .flags = CLK_MUX_ROUND_CLOSEST,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "hdmi_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_names = g12a_hdmi_parent_names,
+ .num_parents = ARRAY_SIZE(g12a_hdmi_parent_names),
+ .flags = CLK_SET_RATE_NO_REPARENT | CLK_GET_RATE_NOCACHE,
+ },
+};
+
+static struct clk_regmap g12a_hdmi_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = HHI_HDMI_CLK_CNTL,
+ .shift = 0,
+ .width = 7,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "hdmi_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_names = (const char *[]){ "hdmi_sel" },
+ .num_parents = 1,
+ .flags = CLK_GET_RATE_NOCACHE,
+ },
+};
+
+static struct clk_regmap g12a_hdmi = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_HDMI_CLK_CNTL,
+ .bit_idx = 8,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "hdmi",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "hdmi_div" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
+ },
+};
+
+/*
+ * The MALI IP is clocked by two identical clocks (mali_0 and mali_1)
+ * muxed by a glitch-free switch.
+ */
+
+static const char * const g12a_mali_0_1_parent_names[] = {
+ IN_PREFIX "xtal", "gp0_pll", "hihi_pll", "fclk_div2p5",
+ "fclk_div3", "fclk_div4", "fclk_div5", "fclk_div7"
+};
+
+static struct clk_regmap g12a_mali_0_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HHI_MALI_CLK_CNTL,
+ .mask = 0x7,
+ .shift = 9,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "mali_0_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_names = g12a_mali_0_1_parent_names,
+ .num_parents = 8,
+ .flags = CLK_SET_RATE_NO_REPARENT,
+ },
+};
+
+static struct clk_regmap g12a_mali_0_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = HHI_MALI_CLK_CNTL,
+ .shift = 0,
+ .width = 7,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "mali_0_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_names = (const char *[]){ "mali_0_sel" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_NO_REPARENT,
+ },
+};
+
+static struct clk_regmap g12a_mali_0 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_MALI_CLK_CNTL,
+ .bit_idx = 8,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "mali_0",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "mali_0_div" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap g12a_mali_1_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HHI_MALI_CLK_CNTL,
+ .mask = 0x7,
+ .shift = 25,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "mali_1_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_names = g12a_mali_0_1_parent_names,
+ .num_parents = 8,
+ .flags = CLK_SET_RATE_NO_REPARENT,
+ },
+};
+
+static struct clk_regmap g12a_mali_1_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = HHI_MALI_CLK_CNTL,
+ .shift = 16,
+ .width = 7,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "mali_1_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_names = (const char *[]){ "mali_1_sel" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_NO_REPARENT,
+ },
+};
+
+static struct clk_regmap g12a_mali_1 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_MALI_CLK_CNTL,
+ .bit_idx = 24,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "mali_1",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "mali_1_div" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static const char * const g12a_mali_parent_names[] = {
+ "mali_0", "mali_1"
+};
+
+static struct clk_regmap g12a_mali = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HHI_MALI_CLK_CNTL,
+ .mask = 1,
+ .shift = 31,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "mali",
+ .ops = &clk_regmap_mux_ops,
+ .parent_names = g12a_mali_parent_names,
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_NO_REPARENT,
+ },
+};
+
+/* Everything Else (EE) domain gates */
+static MESON_GATE(g12a_ddr, HHI_GCLK_MPEG0, 0);
+static MESON_GATE(g12a_dos, HHI_GCLK_MPEG0, 1);
+static MESON_GATE(g12a_audio_locker, HHI_GCLK_MPEG0, 2);
+static MESON_GATE(g12a_mipi_dsi_host, HHI_GCLK_MPEG0, 3);
+static MESON_GATE(g12a_eth_phy, HHI_GCLK_MPEG0, 4);
+static MESON_GATE(g12a_isa, HHI_GCLK_MPEG0, 5);
+static MESON_GATE(g12a_pl301, HHI_GCLK_MPEG0, 6);
+static MESON_GATE(g12a_periphs, HHI_GCLK_MPEG0, 7);
+static MESON_GATE(g12a_spicc_0, HHI_GCLK_MPEG0, 8);
+static MESON_GATE(g12a_i2c, HHI_GCLK_MPEG0, 9);
+static MESON_GATE(g12a_sana, HHI_GCLK_MPEG0, 10);
+static MESON_GATE(g12a_sd, HHI_GCLK_MPEG0, 11);
+static MESON_GATE(g12a_rng0, HHI_GCLK_MPEG0, 12);
+static MESON_GATE(g12a_uart0, HHI_GCLK_MPEG0, 13);
+static MESON_GATE(g12a_spicc_1, HHI_GCLK_MPEG0, 14);
+static MESON_GATE(g12a_hiu_reg, HHI_GCLK_MPEG0, 19);
+static MESON_GATE(g12a_mipi_dsi_phy, HHI_GCLK_MPEG0, 20);
+static MESON_GATE(g12a_assist_misc, HHI_GCLK_MPEG0, 23);
+static MESON_GATE(g12a_emmc_a, HHI_GCLK_MPEG0, 4);
+static MESON_GATE(g12a_emmc_b, HHI_GCLK_MPEG0, 25);
+static MESON_GATE(g12a_emmc_c, HHI_GCLK_MPEG0, 26);
+static MESON_GATE(g12a_audio_codec, HHI_GCLK_MPEG0, 28);
+
+static MESON_GATE(g12a_audio, HHI_GCLK_MPEG1, 0);
+static MESON_GATE(g12a_eth_core, HHI_GCLK_MPEG1, 3);
+static MESON_GATE(g12a_demux, HHI_GCLK_MPEG1, 4);
+static MESON_GATE(g12a_audio_ififo, HHI_GCLK_MPEG1, 11);
+static MESON_GATE(g12a_adc, HHI_GCLK_MPEG1, 13);
+static MESON_GATE(g12a_uart1, HHI_GCLK_MPEG1, 16);
+static MESON_GATE(g12a_g2d, HHI_GCLK_MPEG1, 20);
+static MESON_GATE(g12a_reset, HHI_GCLK_MPEG1, 23);
+static MESON_GATE(g12a_pcie_comb, HHI_GCLK_MPEG1, 24);
+static MESON_GATE(g12a_parser, HHI_GCLK_MPEG1, 25);
+static MESON_GATE(g12a_usb_general, HHI_GCLK_MPEG1, 26);
+static MESON_GATE(g12a_pcie_phy, HHI_GCLK_MPEG1, 27);
+static MESON_GATE(g12a_ahb_arb0, HHI_GCLK_MPEG1, 29);
+
+static MESON_GATE(g12a_ahb_data_bus, HHI_GCLK_MPEG2, 1);
+static MESON_GATE(g12a_ahb_ctrl_bus, HHI_GCLK_MPEG2, 2);
+static MESON_GATE(g12a_htx_hdcp22, HHI_GCLK_MPEG2, 3);
+static MESON_GATE(g12a_htx_pclk, HHI_GCLK_MPEG2, 4);
+static MESON_GATE(g12a_bt656, HHI_GCLK_MPEG2, 6);
+static MESON_GATE(g12a_usb1_to_ddr, HHI_GCLK_MPEG2, 8);
+static MESON_GATE(g12a_mmc_pclk, HHI_GCLK_MPEG2, 11);
+static MESON_GATE(g12a_uart2, HHI_GCLK_MPEG2, 15);
+static MESON_GATE(g12a_vpu_intr, HHI_GCLK_MPEG2, 25);
+static MESON_GATE(g12a_gic, HHI_GCLK_MPEG2, 30);
+
+static MESON_GATE(g12a_vclk2_venci0, HHI_GCLK_OTHER, 1);
+static MESON_GATE(g12a_vclk2_venci1, HHI_GCLK_OTHER, 2);
+static MESON_GATE(g12a_vclk2_vencp0, HHI_GCLK_OTHER, 3);
+static MESON_GATE(g12a_vclk2_vencp1, HHI_GCLK_OTHER, 4);
+static MESON_GATE(g12a_vclk2_venct0, HHI_GCLK_OTHER, 5);
+static MESON_GATE(g12a_vclk2_venct1, HHI_GCLK_OTHER, 6);
+static MESON_GATE(g12a_vclk2_other, HHI_GCLK_OTHER, 7);
+static MESON_GATE(g12a_vclk2_enci, HHI_GCLK_OTHER, 8);
+static MESON_GATE(g12a_vclk2_encp, HHI_GCLK_OTHER, 9);
+static MESON_GATE(g12a_dac_clk, HHI_GCLK_OTHER, 10);
+static MESON_GATE(g12a_aoclk_gate, HHI_GCLK_OTHER, 14);
+static MESON_GATE(g12a_iec958_gate, HHI_GCLK_OTHER, 16);
+static MESON_GATE(g12a_enc480p, HHI_GCLK_OTHER, 20);
+static MESON_GATE(g12a_rng1, HHI_GCLK_OTHER, 21);
+static MESON_GATE(g12a_vclk2_enct, HHI_GCLK_OTHER, 22);
+static MESON_GATE(g12a_vclk2_encl, HHI_GCLK_OTHER, 23);
+static MESON_GATE(g12a_vclk2_venclmmc, HHI_GCLK_OTHER, 24);
+static MESON_GATE(g12a_vclk2_vencl, HHI_GCLK_OTHER, 25);
+static MESON_GATE(g12a_vclk2_other1, HHI_GCLK_OTHER, 26);
+
+static MESON_GATE_RO(g12a_dma, HHI_GCLK_OTHER2, 0);
+static MESON_GATE_RO(g12a_efuse, HHI_GCLK_OTHER2, 1);
+static MESON_GATE_RO(g12a_rom_boot, HHI_GCLK_OTHER2, 2);
+static MESON_GATE_RO(g12a_reset_sec, HHI_GCLK_OTHER2, 3);
+static MESON_GATE_RO(g12a_sec_ahb_apb3, HHI_GCLK_OTHER2, 4);
+
+/* Array of all clocks provided by this provider */
+static struct clk_hw_onecell_data g12a_hw_onecell_data = {
+ .hws = {
+ [CLKID_SYS_PLL] = &g12a_sys_pll.hw,
+ [CLKID_FIXED_PLL] = &g12a_fixed_pll.hw,
+ [CLKID_FCLK_DIV2] = &g12a_fclk_div2.hw,
+ [CLKID_FCLK_DIV3] = &g12a_fclk_div3.hw,
+ [CLKID_FCLK_DIV4] = &g12a_fclk_div4.hw,
+ [CLKID_FCLK_DIV5] = &g12a_fclk_div5.hw,
+ [CLKID_FCLK_DIV7] = &g12a_fclk_div7.hw,
+ [CLKID_FCLK_DIV2P5] = &g12a_fclk_div2p5.hw,
+ [CLKID_GP0_PLL] = &g12a_gp0_pll.hw,
+ [CLKID_MPEG_SEL] = &g12a_mpeg_clk_sel.hw,
+ [CLKID_MPEG_DIV] = &g12a_mpeg_clk_div.hw,
+ [CLKID_CLK81] = &g12a_clk81.hw,
+ [CLKID_MPLL0] = &g12a_mpll0.hw,
+ [CLKID_MPLL1] = &g12a_mpll1.hw,
+ [CLKID_MPLL2] = &g12a_mpll2.hw,
+ [CLKID_MPLL3] = &g12a_mpll3.hw,
+ [CLKID_DDR] = &g12a_ddr.hw,
+ [CLKID_DOS] = &g12a_dos.hw,
+ [CLKID_AUDIO_LOCKER] = &g12a_audio_locker.hw,
+ [CLKID_MIPI_DSI_HOST] = &g12a_mipi_dsi_host.hw,
+ [CLKID_ETH_PHY] = &g12a_eth_phy.hw,
+ [CLKID_ISA] = &g12a_isa.hw,
+ [CLKID_PL301] = &g12a_pl301.hw,
+ [CLKID_PERIPHS] = &g12a_periphs.hw,
+ [CLKID_SPICC0] = &g12a_spicc_0.hw,
+ [CLKID_I2C] = &g12a_i2c.hw,
+ [CLKID_SANA] = &g12a_sana.hw,
+ [CLKID_SD] = &g12a_sd.hw,
+ [CLKID_RNG0] = &g12a_rng0.hw,
+ [CLKID_UART0] = &g12a_uart0.hw,
+ [CLKID_SPICC1] = &g12a_spicc_1.hw,
+ [CLKID_HIU_IFACE] = &g12a_hiu_reg.hw,
+ [CLKID_MIPI_DSI_PHY] = &g12a_mipi_dsi_phy.hw,
+ [CLKID_ASSIST_MISC] = &g12a_assist_misc.hw,
+ [CLKID_SD_EMMC_A] = &g12a_emmc_a.hw,
+ [CLKID_SD_EMMC_B] = &g12a_emmc_b.hw,
+ [CLKID_SD_EMMC_C] = &g12a_emmc_c.hw,
+ [CLKID_AUDIO_CODEC] = &g12a_audio_codec.hw,
+ [CLKID_AUDIO] = &g12a_audio.hw,
+ [CLKID_ETH] = &g12a_eth_core.hw,
+ [CLKID_DEMUX] = &g12a_demux.hw,
+ [CLKID_AUDIO_IFIFO] = &g12a_audio_ififo.hw,
+ [CLKID_ADC] = &g12a_adc.hw,
+ [CLKID_UART1] = &g12a_uart1.hw,
+ [CLKID_G2D] = &g12a_g2d.hw,
+ [CLKID_RESET] = &g12a_reset.hw,
+ [CLKID_PCIE_COMB] = &g12a_pcie_comb.hw,
+ [CLKID_PARSER] = &g12a_parser.hw,
+ [CLKID_USB] = &g12a_usb_general.hw,
+ [CLKID_PCIE_PHY] = &g12a_pcie_phy.hw,
+ [CLKID_AHB_ARB0] = &g12a_ahb_arb0.hw,
+ [CLKID_AHB_DATA_BUS] = &g12a_ahb_data_bus.hw,
+ [CLKID_AHB_CTRL_BUS] = &g12a_ahb_ctrl_bus.hw,
+ [CLKID_HTX_HDCP22] = &g12a_htx_hdcp22.hw,
+ [CLKID_HTX_PCLK] = &g12a_htx_pclk.hw,
+ [CLKID_BT656] = &g12a_bt656.hw,
+ [CLKID_USB1_DDR_BRIDGE] = &g12a_usb1_to_ddr.hw,
+ [CLKID_MMC_PCLK] = &g12a_mmc_pclk.hw,
+ [CLKID_UART2] = &g12a_uart2.hw,
+ [CLKID_VPU_INTR] = &g12a_vpu_intr.hw,
+ [CLKID_GIC] = &g12a_gic.hw,
+ [CLKID_SD_EMMC_A_CLK0_SEL] = &g12a_sd_emmc_a_clk0_sel.hw,
+ [CLKID_SD_EMMC_A_CLK0_DIV] = &g12a_sd_emmc_a_clk0_div.hw,
+ [CLKID_SD_EMMC_A_CLK0] = &g12a_sd_emmc_a_clk0.hw,
+ [CLKID_SD_EMMC_B_CLK0_SEL] = &g12a_sd_emmc_b_clk0_sel.hw,
+ [CLKID_SD_EMMC_B_CLK0_DIV] = &g12a_sd_emmc_b_clk0_div.hw,
+ [CLKID_SD_EMMC_B_CLK0] = &g12a_sd_emmc_b_clk0.hw,
+ [CLKID_SD_EMMC_C_CLK0_SEL] = &g12a_sd_emmc_c_clk0_sel.hw,
+ [CLKID_SD_EMMC_C_CLK0_DIV] = &g12a_sd_emmc_c_clk0_div.hw,
+ [CLKID_SD_EMMC_C_CLK0] = &g12a_sd_emmc_c_clk0.hw,
+ [CLKID_MPLL0_DIV] = &g12a_mpll0_div.hw,
+ [CLKID_MPLL1_DIV] = &g12a_mpll1_div.hw,
+ [CLKID_MPLL2_DIV] = &g12a_mpll2_div.hw,
+ [CLKID_MPLL3_DIV] = &g12a_mpll3_div.hw,
+ [CLKID_FCLK_DIV2_DIV] = &g12a_fclk_div2_div.hw,
+ [CLKID_FCLK_DIV3_DIV] = &g12a_fclk_div3_div.hw,
+ [CLKID_FCLK_DIV4_DIV] = &g12a_fclk_div4_div.hw,
+ [CLKID_FCLK_DIV5_DIV] = &g12a_fclk_div5_div.hw,
+ [CLKID_FCLK_DIV7_DIV] = &g12a_fclk_div7_div.hw,
+ [CLKID_FCLK_DIV2P5_DIV] = &g12a_fclk_div2p5_div.hw,
+ [CLKID_HIFI_PLL] = &g12a_hifi_pll.hw,
+ [CLKID_VCLK2_VENCI0] = &g12a_vclk2_venci0.hw,
+ [CLKID_VCLK2_VENCI1] = &g12a_vclk2_venci1.hw,
+ [CLKID_VCLK2_VENCP0] = &g12a_vclk2_vencp0.hw,
+ [CLKID_VCLK2_VENCP1] = &g12a_vclk2_vencp1.hw,
+ [CLKID_VCLK2_VENCT0] = &g12a_vclk2_venct0.hw,
+ [CLKID_VCLK2_VENCT1] = &g12a_vclk2_venct1.hw,
+ [CLKID_VCLK2_OTHER] = &g12a_vclk2_other.hw,
+ [CLKID_VCLK2_ENCI] = &g12a_vclk2_enci.hw,
+ [CLKID_VCLK2_ENCP] = &g12a_vclk2_encp.hw,
+ [CLKID_DAC_CLK] = &g12a_dac_clk.hw,
+ [CLKID_AOCLK] = &g12a_aoclk_gate.hw,
+ [CLKID_IEC958] = &g12a_iec958_gate.hw,
+ [CLKID_ENC480P] = &g12a_enc480p.hw,
+ [CLKID_RNG1] = &g12a_rng1.hw,
+ [CLKID_VCLK2_ENCT] = &g12a_vclk2_enct.hw,
+ [CLKID_VCLK2_ENCL] = &g12a_vclk2_encl.hw,
+ [CLKID_VCLK2_VENCLMMC] = &g12a_vclk2_venclmmc.hw,
+ [CLKID_VCLK2_VENCL] = &g12a_vclk2_vencl.hw,
+ [CLKID_VCLK2_OTHER1] = &g12a_vclk2_other1.hw,
+ [CLKID_FIXED_PLL_DCO] = &g12a_fixed_pll_dco.hw,
+ [CLKID_SYS_PLL_DCO] = &g12a_sys_pll_dco.hw,
+ [CLKID_GP0_PLL_DCO] = &g12a_gp0_pll_dco.hw,
+ [CLKID_HIFI_PLL_DCO] = &g12a_hifi_pll_dco.hw,
+ [CLKID_DMA] = &g12a_dma.hw,
+ [CLKID_EFUSE] = &g12a_efuse.hw,
+ [CLKID_ROM_BOOT] = &g12a_rom_boot.hw,
+ [CLKID_RESET_SEC] = &g12a_reset_sec.hw,
+ [CLKID_SEC_AHB_APB3] = &g12a_sec_ahb_apb3.hw,
+ [CLKID_MPLL_PREDIV] = &g12a_mpll_prediv.hw,
+ [CLKID_VPU_0_SEL] = &g12a_vpu_0_sel.hw,
+ [CLKID_VPU_0_DIV] = &g12a_vpu_0_div.hw,
+ [CLKID_VPU_0] = &g12a_vpu_0.hw,
+ [CLKID_VPU_1_SEL] = &g12a_vpu_1_sel.hw,
+ [CLKID_VPU_1_DIV] = &g12a_vpu_1_div.hw,
+ [CLKID_VPU_1] = &g12a_vpu_1.hw,
+ [CLKID_VPU] = &g12a_vpu.hw,
+ [CLKID_VAPB_0_SEL] = &g12a_vapb_0_sel.hw,
+ [CLKID_VAPB_0_DIV] = &g12a_vapb_0_div.hw,
+ [CLKID_VAPB_0] = &g12a_vapb_0.hw,
+ [CLKID_VAPB_1_SEL] = &g12a_vapb_1_sel.hw,
+ [CLKID_VAPB_1_DIV] = &g12a_vapb_1_div.hw,
+ [CLKID_VAPB_1] = &g12a_vapb_1.hw,
+ [CLKID_VAPB_SEL] = &g12a_vapb_sel.hw,
+ [CLKID_VAPB] = &g12a_vapb.hw,
+ [CLKID_HDMI_PLL_DCO] = &g12a_hdmi_pll_dco.hw,
+ [CLKID_HDMI_PLL_OD] = &g12a_hdmi_pll_od.hw,
+ [CLKID_HDMI_PLL_OD2] = &g12a_hdmi_pll_od2.hw,
+ [CLKID_HDMI_PLL] = &g12a_hdmi_pll.hw,
+ [CLKID_VID_PLL] = &g12a_vid_pll_div.hw,
+ [CLKID_VID_PLL_SEL] = &g12a_vid_pll_sel.hw,
+ [CLKID_VID_PLL_DIV] = &g12a_vid_pll.hw,
+ [CLKID_VCLK_SEL] = &g12a_vclk_sel.hw,
+ [CLKID_VCLK2_SEL] = &g12a_vclk2_sel.hw,
+ [CLKID_VCLK_INPUT] = &g12a_vclk_input.hw,
+ [CLKID_VCLK2_INPUT] = &g12a_vclk2_input.hw,
+ [CLKID_VCLK_DIV] = &g12a_vclk_div.hw,
+ [CLKID_VCLK2_DIV] = &g12a_vclk2_div.hw,
+ [CLKID_VCLK] = &g12a_vclk.hw,
+ [CLKID_VCLK2] = &g12a_vclk2.hw,
+ [CLKID_VCLK_DIV1] = &g12a_vclk_div1.hw,
+ [CLKID_VCLK_DIV2_EN] = &g12a_vclk_div2_en.hw,
+ [CLKID_VCLK_DIV4_EN] = &g12a_vclk_div4_en.hw,
+ [CLKID_VCLK_DIV6_EN] = &g12a_vclk_div6_en.hw,
+ [CLKID_VCLK_DIV12_EN] = &g12a_vclk_div12_en.hw,
+ [CLKID_VCLK2_DIV1] = &g12a_vclk2_div1.hw,
+ [CLKID_VCLK2_DIV2_EN] = &g12a_vclk2_div2_en.hw,
+ [CLKID_VCLK2_DIV4_EN] = &g12a_vclk2_div4_en.hw,
+ [CLKID_VCLK2_DIV6_EN] = &g12a_vclk2_div6_en.hw,
+ [CLKID_VCLK2_DIV12_EN] = &g12a_vclk2_div12_en.hw,
+ [CLKID_VCLK_DIV2] = &g12a_vclk_div2.hw,
+ [CLKID_VCLK_DIV4] = &g12a_vclk_div4.hw,
+ [CLKID_VCLK_DIV6] = &g12a_vclk_div6.hw,
+ [CLKID_VCLK_DIV12] = &g12a_vclk_div12.hw,
+ [CLKID_VCLK2_DIV2] = &g12a_vclk2_div2.hw,
+ [CLKID_VCLK2_DIV4] = &g12a_vclk2_div4.hw,
+ [CLKID_VCLK2_DIV6] = &g12a_vclk2_div6.hw,
+ [CLKID_VCLK2_DIV12] = &g12a_vclk2_div12.hw,
+ [CLKID_CTS_ENCI_SEL] = &g12a_cts_enci_sel.hw,
+ [CLKID_CTS_ENCP_SEL] = &g12a_cts_encp_sel.hw,
+ [CLKID_CTS_VDAC_SEL] = &g12a_cts_vdac_sel.hw,
+ [CLKID_HDMI_TX_SEL] = &g12a_hdmi_tx_sel.hw,
+ [CLKID_CTS_ENCI] = &g12a_cts_enci.hw,
+ [CLKID_CTS_ENCP] = &g12a_cts_encp.hw,
+ [CLKID_CTS_VDAC] = &g12a_cts_vdac.hw,
+ [CLKID_HDMI_TX] = &g12a_hdmi_tx.hw,
+ [CLKID_HDMI_SEL] = &g12a_hdmi_sel.hw,
+ [CLKID_HDMI_DIV] = &g12a_hdmi_div.hw,
+ [CLKID_HDMI] = &g12a_hdmi.hw,
+ [CLKID_MALI_0_SEL] = &g12a_mali_0_sel.hw,
+ [CLKID_MALI_0_DIV] = &g12a_mali_0_div.hw,
+ [CLKID_MALI_0] = &g12a_mali_0.hw,
+ [CLKID_MALI_1_SEL] = &g12a_mali_1_sel.hw,
+ [CLKID_MALI_1_DIV] = &g12a_mali_1_div.hw,
+ [CLKID_MALI_1] = &g12a_mali_1.hw,
+ [CLKID_MALI] = &g12a_mali.hw,
+ [CLKID_MPLL_5OM_DIV] = &g12a_mpll_50m_div.hw,
+ [CLKID_MPLL_5OM] = &g12a_mpll_50m.hw,
+ [NR_CLKS] = NULL,
+ },
+ .num = NR_CLKS,
+};
+
+/* Convenience table to populate regmap in .probe */
+static struct clk_regmap *const g12a_clk_regmaps[] = {
+ &g12a_clk81,
+ &g12a_dos,
+ &g12a_ddr,
+ &g12a_audio_locker,
+ &g12a_mipi_dsi_host,
+ &g12a_eth_phy,
+ &g12a_isa,
+ &g12a_pl301,
+ &g12a_periphs,
+ &g12a_spicc_0,
+ &g12a_i2c,
+ &g12a_sana,
+ &g12a_sd,
+ &g12a_rng0,
+ &g12a_uart0,
+ &g12a_spicc_1,
+ &g12a_hiu_reg,
+ &g12a_mipi_dsi_phy,
+ &g12a_assist_misc,
+ &g12a_emmc_a,
+ &g12a_emmc_b,
+ &g12a_emmc_c,
+ &g12a_audio_codec,
+ &g12a_audio,
+ &g12a_eth_core,
+ &g12a_demux,
+ &g12a_audio_ififo,
+ &g12a_adc,
+ &g12a_uart1,
+ &g12a_g2d,
+ &g12a_reset,
+ &g12a_pcie_comb,
+ &g12a_parser,
+ &g12a_usb_general,
+ &g12a_pcie_phy,
+ &g12a_ahb_arb0,
+ &g12a_ahb_data_bus,
+ &g12a_ahb_ctrl_bus,
+ &g12a_htx_hdcp22,
+ &g12a_htx_pclk,
+ &g12a_bt656,
+ &g12a_usb1_to_ddr,
+ &g12a_mmc_pclk,
+ &g12a_vpu_intr,
+ &g12a_gic,
+ &g12a_sd_emmc_a_clk0,
+ &g12a_sd_emmc_b_clk0,
+ &g12a_sd_emmc_c_clk0,
+ &g12a_mpeg_clk_div,
+ &g12a_sd_emmc_a_clk0_div,
+ &g12a_sd_emmc_b_clk0_div,
+ &g12a_sd_emmc_c_clk0_div,
+ &g12a_mpeg_clk_sel,
+ &g12a_sd_emmc_a_clk0_sel,
+ &g12a_sd_emmc_b_clk0_sel,
+ &g12a_sd_emmc_c_clk0_sel,
+ &g12a_mpll0,
+ &g12a_mpll1,
+ &g12a_mpll2,
+ &g12a_mpll3,
+ &g12a_mpll0_div,
+ &g12a_mpll1_div,
+ &g12a_mpll2_div,
+ &g12a_mpll3_div,
+ &g12a_fixed_pll,
+ &g12a_sys_pll,
+ &g12a_gp0_pll,
+ &g12a_hifi_pll,
+ &g12a_vclk2_venci0,
+ &g12a_vclk2_venci1,
+ &g12a_vclk2_vencp0,
+ &g12a_vclk2_vencp1,
+ &g12a_vclk2_venct0,
+ &g12a_vclk2_venct1,
+ &g12a_vclk2_other,
+ &g12a_vclk2_enci,
+ &g12a_vclk2_encp,
+ &g12a_dac_clk,
+ &g12a_aoclk_gate,
+ &g12a_iec958_gate,
+ &g12a_enc480p,
+ &g12a_rng1,
+ &g12a_vclk2_enct,
+ &g12a_vclk2_encl,
+ &g12a_vclk2_venclmmc,
+ &g12a_vclk2_vencl,
+ &g12a_vclk2_other1,
+ &g12a_fixed_pll_dco,
+ &g12a_sys_pll_dco,
+ &g12a_gp0_pll_dco,
+ &g12a_hifi_pll_dco,
+ &g12a_fclk_div2,
+ &g12a_fclk_div3,
+ &g12a_fclk_div4,
+ &g12a_fclk_div5,
+ &g12a_fclk_div7,
+ &g12a_fclk_div2p5,
+ &g12a_dma,
+ &g12a_efuse,
+ &g12a_rom_boot,
+ &g12a_reset_sec,
+ &g12a_sec_ahb_apb3,
+ &g12a_vpu_0_sel,
+ &g12a_vpu_0_div,
+ &g12a_vpu_0,
+ &g12a_vpu_1_sel,
+ &g12a_vpu_1_div,
+ &g12a_vpu_1,
+ &g12a_vpu,
+ &g12a_vapb_0_sel,
+ &g12a_vapb_0_div,
+ &g12a_vapb_0,
+ &g12a_vapb_1_sel,
+ &g12a_vapb_1_div,
+ &g12a_vapb_1,
+ &g12a_vapb_sel,
+ &g12a_vapb,
+ &g12a_hdmi_pll_dco,
+ &g12a_hdmi_pll_od,
+ &g12a_hdmi_pll_od2,
+ &g12a_hdmi_pll,
+ &g12a_vid_pll_div,
+ &g12a_vid_pll_sel,
+ &g12a_vid_pll,
+ &g12a_vclk_sel,
+ &g12a_vclk2_sel,
+ &g12a_vclk_input,
+ &g12a_vclk2_input,
+ &g12a_vclk_div,
+ &g12a_vclk2_div,
+ &g12a_vclk,
+ &g12a_vclk2,
+ &g12a_vclk_div1,
+ &g12a_vclk_div2_en,
+ &g12a_vclk_div4_en,
+ &g12a_vclk_div6_en,
+ &g12a_vclk_div12_en,
+ &g12a_vclk2_div1,
+ &g12a_vclk2_div2_en,
+ &g12a_vclk2_div4_en,
+ &g12a_vclk2_div6_en,
+ &g12a_vclk2_div12_en,
+ &g12a_cts_enci_sel,
+ &g12a_cts_encp_sel,
+ &g12a_cts_vdac_sel,
+ &g12a_hdmi_tx_sel,
+ &g12a_cts_enci,
+ &g12a_cts_encp,
+ &g12a_cts_vdac,
+ &g12a_hdmi_tx,
+ &g12a_hdmi_sel,
+ &g12a_hdmi_div,
+ &g12a_hdmi,
+ &g12a_mali_0_sel,
+ &g12a_mali_0_div,
+ &g12a_mali_0,
+ &g12a_mali_1_sel,
+ &g12a_mali_1_div,
+ &g12a_mali_1,
+ &g12a_mali,
+ &g12a_mpll_50m,
+};
+
+static const struct meson_eeclkc_data g12a_clkc_data = {
+ .regmap_clks = g12a_clk_regmaps,
+ .regmap_clk_num = ARRAY_SIZE(g12a_clk_regmaps),
+ .hw_onecell_data = &g12a_hw_onecell_data
+};
+
+static const struct of_device_id clkc_match_table[] = {
+ { .compatible = "amlogic,g12a-clkc", .data = &g12a_clkc_data },
+ {}
+};
+
+static struct platform_driver g12a_driver = {
+ .probe = meson_eeclkc_probe,
+ .driver = {
+ .name = "g12a-clkc",
+ .of_match_table = clkc_match_table,
+ },
+};
+
+builtin_platform_driver(g12a_driver);
diff --git a/drivers/clk/meson/g12a.h b/drivers/clk/meson/g12a.h
new file mode 100644
index 000000000000..f399dfe1401c
--- /dev/null
+++ b/drivers/clk/meson/g12a.h
@@ -0,0 +1,175 @@
+/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */
+/*
+ * Copyright (c) 2016 Amlogic, Inc.
+ * Author: Michael Turquette <mturquette@baylibre.com>
+ *
+ * Copyright (c) 2018 Amlogic, inc.
+ * Author: Qiufang Dai <qiufang.dai@amlogic.com>
+ * Author: Jian Hu <jian.hu@amlogic.com>
+ *
+ */
+#ifndef __G12A_H
+#define __G12A_H
+
+/*
+ * Clock controller register offsets
+ *
+ * Register offsets from the data sheet must be multiplied by 4 before
+ * adding them to the base address to get the right value.
+ */
+#define HHI_MIPI_CNTL0 0x000
+#define HHI_MIPI_CNTL1 0x004
+#define HHI_MIPI_CNTL2 0x008
+#define HHI_MIPI_STS 0x00C
+#define HHI_GP0_PLL_CNTL0 0x040
+#define HHI_GP0_PLL_CNTL1 0x044
+#define HHI_GP0_PLL_CNTL2 0x048
+#define HHI_GP0_PLL_CNTL3 0x04C
+#define HHI_GP0_PLL_CNTL4 0x050
+#define HHI_GP0_PLL_CNTL5 0x054
+#define HHI_GP0_PLL_CNTL6 0x058
+#define HHI_GP0_PLL_STS 0x05C
+#define HHI_PCIE_PLL_CNTL0 0x098
+#define HHI_PCIE_PLL_CNTL1 0x09C
+#define HHI_PCIE_PLL_CNTL2 0x0A0
+#define HHI_PCIE_PLL_CNTL3 0x0A4
+#define HHI_PCIE_PLL_CNTL4 0x0A8
+#define HHI_PCIE_PLL_CNTL5 0x0AC
+#define HHI_PCIE_PLL_STS 0x0B8
+#define HHI_HIFI_PLL_CNTL0 0x0D8
+#define HHI_HIFI_PLL_CNTL1 0x0DC
+#define HHI_HIFI_PLL_CNTL2 0x0E0
+#define HHI_HIFI_PLL_CNTL3 0x0E4
+#define HHI_HIFI_PLL_CNTL4 0x0E8
+#define HHI_HIFI_PLL_CNTL5 0x0EC
+#define HHI_HIFI_PLL_CNTL6 0x0F0
+#define HHI_VIID_CLK_DIV 0x128
+#define HHI_VIID_CLK_CNTL 0x12C
+#define HHI_GCLK_MPEG0 0x140
+#define HHI_GCLK_MPEG1 0x144
+#define HHI_GCLK_MPEG2 0x148
+#define HHI_GCLK_OTHER 0x150
+#define HHI_GCLK_OTHER2 0x154
+#define HHI_VID_CLK_DIV 0x164
+#define HHI_MPEG_CLK_CNTL 0x174
+#define HHI_AUD_CLK_CNTL 0x178
+#define HHI_VID_CLK_CNTL 0x17c
+#define HHI_TS_CLK_CNTL 0x190
+#define HHI_VID_CLK_CNTL2 0x194
+#define HHI_SYS_CPU_CLK_CNTL0 0x19c
+#define HHI_VID_PLL_CLK_DIV 0x1A0
+#define HHI_MALI_CLK_CNTL 0x1b0
+#define HHI_VPU_CLKC_CNTL 0x1b4
+#define HHI_VPU_CLK_CNTL 0x1bC
+#define HHI_HDMI_CLK_CNTL 0x1CC
+#define HHI_VDEC_CLK_CNTL 0x1E0
+#define HHI_VDEC2_CLK_CNTL 0x1E4
+#define HHI_VDEC3_CLK_CNTL 0x1E8
+#define HHI_VDEC4_CLK_CNTL 0x1EC
+#define HHI_HDCP22_CLK_CNTL 0x1F0
+#define HHI_VAPBCLK_CNTL 0x1F4
+#define HHI_VPU_CLKB_CNTL 0x20C
+#define HHI_GEN_CLK_CNTL 0x228
+#define HHI_VDIN_MEAS_CLK_CNTL 0x250
+#define HHI_MIPIDSI_PHY_CLK_CNTL 0x254
+#define HHI_NAND_CLK_CNTL 0x25C
+#define HHI_SD_EMMC_CLK_CNTL 0x264
+#define HHI_MPLL_CNTL0 0x278
+#define HHI_MPLL_CNTL1 0x27C
+#define HHI_MPLL_CNTL2 0x280
+#define HHI_MPLL_CNTL3 0x284
+#define HHI_MPLL_CNTL4 0x288
+#define HHI_MPLL_CNTL5 0x28c
+#define HHI_MPLL_CNTL6 0x290
+#define HHI_MPLL_CNTL7 0x294
+#define HHI_MPLL_CNTL8 0x298
+#define HHI_FIX_PLL_CNTL0 0x2A0
+#define HHI_FIX_PLL_CNTL1 0x2A4
+#define HHI_FIX_PLL_CNTL3 0x2AC
+#define HHI_SYS_PLL_CNTL0 0x2f4
+#define HHI_SYS_PLL_CNTL1 0x2f8
+#define HHI_SYS_PLL_CNTL2 0x2fc
+#define HHI_SYS_PLL_CNTL3 0x300
+#define HHI_SYS_PLL_CNTL4 0x304
+#define HHI_SYS_PLL_CNTL5 0x308
+#define HHI_SYS_PLL_CNTL6 0x30c
+#define HHI_HDMI_PLL_CNTL0 0x320
+#define HHI_HDMI_PLL_CNTL1 0x324
+#define HHI_HDMI_PLL_CNTL2 0x328
+#define HHI_HDMI_PLL_CNTL3 0x32c
+#define HHI_HDMI_PLL_CNTL4 0x330
+#define HHI_HDMI_PLL_CNTL5 0x334
+#define HHI_HDMI_PLL_CNTL6 0x338
+#define HHI_SPICC_CLK_CNTL 0x3dc
+
+/*
+ * CLKID index values
+ *
+ * These indices are entirely contrived and do not map onto the hardware.
+ * It has now been decided to expose everything by default in the DT header:
+ * include/dt-bindings/clock/g12a-clkc.h. Only the clocks ids we don't want
+ * to expose, such as the internal muxes and dividers of composite clocks,
+ * will remain defined here.
+ */
+#define CLKID_MPEG_SEL 8
+#define CLKID_MPEG_DIV 9
+#define CLKID_SD_EMMC_A_CLK0_SEL 63
+#define CLKID_SD_EMMC_A_CLK0_DIV 64
+#define CLKID_SD_EMMC_B_CLK0_SEL 65
+#define CLKID_SD_EMMC_B_CLK0_DIV 66
+#define CLKID_SD_EMMC_C_CLK0_SEL 67
+#define CLKID_SD_EMMC_C_CLK0_DIV 68
+#define CLKID_MPLL0_DIV 69
+#define CLKID_MPLL1_DIV 70
+#define CLKID_MPLL2_DIV 71
+#define CLKID_MPLL3_DIV 72
+#define CLKID_MPLL_PREDIV 73
+#define CLKID_FCLK_DIV2_DIV 75
+#define CLKID_FCLK_DIV3_DIV 76
+#define CLKID_FCLK_DIV4_DIV 77
+#define CLKID_FCLK_DIV5_DIV 78
+#define CLKID_FCLK_DIV7_DIV 79
+#define CLKID_FCLK_DIV2P5_DIV 100
+#define CLKID_FIXED_PLL_DCO 101
+#define CLKID_SYS_PLL_DCO 102
+#define CLKID_GP0_PLL_DCO 103
+#define CLKID_HIFI_PLL_DCO 104
+#define CLKID_VPU_0_DIV 111
+#define CLKID_VPU_1_DIV 114
+#define CLKID_VAPB_0_DIV 118
+#define CLKID_VAPB_1_DIV 121
+#define CLKID_HDMI_PLL_DCO 125
+#define CLKID_HDMI_PLL_OD 126
+#define CLKID_HDMI_PLL_OD2 127
+#define CLKID_VID_PLL_SEL 130
+#define CLKID_VID_PLL_DIV 131
+#define CLKID_VCLK_SEL 132
+#define CLKID_VCLK2_SEL 133
+#define CLKID_VCLK_INPUT 134
+#define CLKID_VCLK2_INPUT 135
+#define CLKID_VCLK_DIV 136
+#define CLKID_VCLK2_DIV 137
+#define CLKID_VCLK_DIV2_EN 140
+#define CLKID_VCLK_DIV4_EN 141
+#define CLKID_VCLK_DIV6_EN 142
+#define CLKID_VCLK_DIV12_EN 143
+#define CLKID_VCLK2_DIV2_EN 144
+#define CLKID_VCLK2_DIV4_EN 145
+#define CLKID_VCLK2_DIV6_EN 146
+#define CLKID_VCLK2_DIV12_EN 147
+#define CLKID_CTS_ENCI_SEL 158
+#define CLKID_CTS_ENCP_SEL 159
+#define CLKID_CTS_VDAC_SEL 160
+#define CLKID_HDMI_TX_SEL 161
+#define CLKID_HDMI_SEL 166
+#define CLKID_HDMI_DIV 167
+#define CLKID_MALI_0_DIV 170
+#define CLKID_MALI_1_DIV 173
+#define CLKID_MPLL_5OM_DIV 176
+
+#define NR_CLKS 178
+
+/* include the CLKIDs that have been made part of the DT binding */
+#include <dt-bindings/clock/g12a-clkc.h>
+
+#endif /* __G12A_H */
diff --git a/drivers/clk/meson/gxbb-aoclk-32k.c b/drivers/clk/meson/gxbb-aoclk-32k.c
deleted file mode 100644
index 680467141a1d..000000000000
--- a/drivers/clk/meson/gxbb-aoclk-32k.c
+++ /dev/null
@@ -1,193 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Copyright (c) 2017 BayLibre, SAS.
- * Author: Neil Armstrong <narmstrong@baylibre.com>
- */
-
-#include <linux/clk-provider.h>
-#include <linux/bitfield.h>
-#include <linux/regmap.h>
-#include "gxbb-aoclk.h"
-
-/*
- * The AO Domain embeds a dual/divider to generate a more precise
- * 32,768KHz clock for low-power suspend mode and CEC.
- * ______ ______
- * | | | |
- * ______ | Div1 |-| Cnt1 | ______
- * | | /|______| |______|\ | |
- * Xtal-->| Gate |---| ______ ______ X-X--| Gate |-->
- * |______| | \| | | |/ | |______|
- * | | Div2 |-| Cnt2 | |
- * | |______| |______| |
- * |_______________________|
- *
- * The dividing can be switched to single or dual, with a counter
- * for each divider to set when the switching is done.
- * The entire dividing mechanism can be also bypassed.
- */
-
-#define CLK_CNTL0_N1_MASK GENMASK(11, 0)
-#define CLK_CNTL0_N2_MASK GENMASK(23, 12)
-#define CLK_CNTL0_DUALDIV_EN BIT(28)
-#define CLK_CNTL0_OUT_GATE_EN BIT(30)
-#define CLK_CNTL0_IN_GATE_EN BIT(31)
-
-#define CLK_CNTL1_M1_MASK GENMASK(11, 0)
-#define CLK_CNTL1_M2_MASK GENMASK(23, 12)
-#define CLK_CNTL1_BYPASS_EN BIT(24)
-#define CLK_CNTL1_SELECT_OSC BIT(27)
-
-#define PWR_CNTL_ALT_32K_SEL GENMASK(13, 10)
-
-struct cec_32k_freq_table {
- unsigned long parent_rate;
- unsigned long target_rate;
- bool dualdiv;
- unsigned int n1;
- unsigned int n2;
- unsigned int m1;
- unsigned int m2;
-};
-
-static const struct cec_32k_freq_table aoclk_cec_32k_table[] = {
- [0] = {
- .parent_rate = 24000000,
- .target_rate = 32768,
- .dualdiv = true,
- .n1 = 733,
- .n2 = 732,
- .m1 = 8,
- .m2 = 11,
- },
-};
-
-/*
- * If CLK_CNTL0_DUALDIV_EN == 0
- * - will use N1 divider only
- * If CLK_CNTL0_DUALDIV_EN == 1
- * - hold M1 cycles of N1 divider then changes to N2
- * - hold M2 cycles of N2 divider then changes to N1
- * Then we can get more accurate division.
- */
-static unsigned long aoclk_cec_32k_recalc_rate(struct clk_hw *hw,
- unsigned long parent_rate)
-{
- struct aoclk_cec_32k *cec_32k = to_aoclk_cec_32k(hw);
- unsigned long n1;
- u32 reg0, reg1;
-
- regmap_read(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL0, &reg0);
- regmap_read(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL1, &reg1);
-
- if (reg1 & CLK_CNTL1_BYPASS_EN)
- return parent_rate;
-
- if (reg0 & CLK_CNTL0_DUALDIV_EN) {
- unsigned long n2, m1, m2, f1, f2, p1, p2;
-
- n1 = FIELD_GET(CLK_CNTL0_N1_MASK, reg0) + 1;
- n2 = FIELD_GET(CLK_CNTL0_N2_MASK, reg0) + 1;
-
- m1 = FIELD_GET(CLK_CNTL1_M1_MASK, reg1) + 1;
- m2 = FIELD_GET(CLK_CNTL1_M2_MASK, reg1) + 1;
-
- f1 = DIV_ROUND_CLOSEST(parent_rate, n1);
- f2 = DIV_ROUND_CLOSEST(parent_rate, n2);
-
- p1 = DIV_ROUND_CLOSEST(100000000 * m1, f1 * (m1 + m2));
- p2 = DIV_ROUND_CLOSEST(100000000 * m2, f2 * (m1 + m2));
-
- return DIV_ROUND_UP(100000000, p1 + p2);
- }
-
- n1 = FIELD_GET(CLK_CNTL0_N1_MASK, reg0) + 1;
-
- return DIV_ROUND_CLOSEST(parent_rate, n1);
-}
-
-static const struct cec_32k_freq_table *find_cec_32k_freq(unsigned long rate,
- unsigned long prate)
-{
- int i;
-
- for (i = 0 ; i < ARRAY_SIZE(aoclk_cec_32k_table) ; ++i)
- if (aoclk_cec_32k_table[i].parent_rate == prate &&
- aoclk_cec_32k_table[i].target_rate == rate)
- return &aoclk_cec_32k_table[i];
-
- return NULL;
-}
-
-static long aoclk_cec_32k_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
-{
- const struct cec_32k_freq_table *freq = find_cec_32k_freq(rate,
- *prate);
-
- /* If invalid return first one */
- if (!freq)
- return aoclk_cec_32k_table[0].target_rate;
-
- return freq->target_rate;
-}
-
-/*
- * From the Amlogic init procedure, the IN and OUT gates needs to be handled
- * in the init procedure to avoid any glitches.
- */
-
-static int aoclk_cec_32k_set_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long parent_rate)
-{
- const struct cec_32k_freq_table *freq = find_cec_32k_freq(rate,
- parent_rate);
- struct aoclk_cec_32k *cec_32k = to_aoclk_cec_32k(hw);
- u32 reg = 0;
-
- if (!freq)
- return -EINVAL;
-
- /* Disable clock */
- regmap_update_bits(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL0,
- CLK_CNTL0_IN_GATE_EN | CLK_CNTL0_OUT_GATE_EN, 0);
-
- reg = FIELD_PREP(CLK_CNTL0_N1_MASK, freq->n1 - 1);
- if (freq->dualdiv)
- reg |= CLK_CNTL0_DUALDIV_EN |
- FIELD_PREP(CLK_CNTL0_N2_MASK, freq->n2 - 1);
-
- regmap_write(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL0, reg);
-
- reg = FIELD_PREP(CLK_CNTL1_M1_MASK, freq->m1 - 1);
- if (freq->dualdiv)
- reg |= FIELD_PREP(CLK_CNTL1_M2_MASK, freq->m2 - 1);
-
- regmap_write(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL1, reg);
-
- /* Enable clock */
- regmap_update_bits(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL0,
- CLK_CNTL0_IN_GATE_EN, CLK_CNTL0_IN_GATE_EN);
-
- udelay(200);
-
- regmap_update_bits(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL0,
- CLK_CNTL0_OUT_GATE_EN, CLK_CNTL0_OUT_GATE_EN);
-
- regmap_update_bits(cec_32k->regmap, AO_CRT_CLK_CNTL1,
- CLK_CNTL1_SELECT_OSC, CLK_CNTL1_SELECT_OSC);
-
- /* Select 32k from XTAL */
- regmap_update_bits(cec_32k->regmap,
- AO_RTI_PWR_CNTL_REG0,
- PWR_CNTL_ALT_32K_SEL,
- FIELD_PREP(PWR_CNTL_ALT_32K_SEL, 4));
-
- return 0;
-}
-
-const struct clk_ops meson_aoclk_cec_32k_ops = {
- .recalc_rate = aoclk_cec_32k_recalc_rate,
- .round_rate = aoclk_cec_32k_round_rate,
- .set_rate = aoclk_cec_32k_set_rate,
-};
diff --git a/drivers/clk/meson/gxbb-aoclk.c b/drivers/clk/meson/gxbb-aoclk.c
index 42ed61d3c3fb..449f6ac189d8 100644
--- a/drivers/clk/meson/gxbb-aoclk.c
+++ b/drivers/clk/meson/gxbb-aoclk.c
@@ -5,10 +5,23 @@
*/
#include <linux/platform_device.h>
#include <linux/mfd/syscon.h>
-#include "clk-regmap.h"
#include "meson-aoclk.h"
#include "gxbb-aoclk.h"
+#include "clk-regmap.h"
+#include "clk-dualdiv.h"
+
+#define IN_PREFIX "ao-in-"
+
+/* AO Configuration Clock registers offsets */
+#define AO_RTI_PWR_CNTL_REG1 0x0c
+#define AO_RTI_PWR_CNTL_REG0 0x10
+#define AO_RTI_GEN_CNTL_REG0 0x40
+#define AO_OSCIN_CNTL 0x58
+#define AO_CRT_CLK_CNTL1 0x68
+#define AO_RTC_ALT_CLK_CNTL0 0x94
+#define AO_RTC_ALT_CLK_CNTL1 0x98
+
#define GXBB_AO_GATE(_name, _bit) \
static struct clk_regmap _name##_ao = { \
.data = &(struct clk_regmap_gate_data) { \
@@ -18,7 +31,7 @@ static struct clk_regmap _name##_ao = { \
.hw.init = &(struct clk_init_data) { \
.name = #_name "_ao", \
.ops = &clk_regmap_gate_ops, \
- .parent_names = (const char *[]){ "clk81" }, \
+ .parent_names = (const char *[]){ IN_PREFIX "mpeg-clk" }, \
.num_parents = 1, \
.flags = CLK_IGNORE_UNUSED, \
}, \
@@ -31,13 +44,174 @@ GXBB_AO_GATE(uart1, 3);
GXBB_AO_GATE(uart2, 5);
GXBB_AO_GATE(ir_blaster, 6);
-static struct aoclk_cec_32k cec_32k_ao = {
- .hw.init = &(struct clk_init_data) {
- .name = "cec_32k_ao",
- .ops = &meson_aoclk_cec_32k_ops,
- .parent_names = (const char *[]){ "xtal" },
+static struct clk_regmap ao_cts_oscin = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = AO_RTI_PWR_CNTL_REG0,
+ .bit_idx = 6,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "ao_cts_oscin",
+ .ops = &clk_regmap_gate_ro_ops,
+ .parent_names = (const char *[]){ IN_PREFIX "xtal" },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap ao_32k_pre = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = AO_RTC_ALT_CLK_CNTL0,
+ .bit_idx = 31,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "ao_32k_pre",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "ao_cts_oscin" },
+ .num_parents = 1,
+ },
+};
+
+static const struct meson_clk_dualdiv_param gxbb_32k_div_table[] = {
+ {
+ .dual = 1,
+ .n1 = 733,
+ .m1 = 8,
+ .n2 = 732,
+ .m2 = 11,
+ }, {}
+};
+
+static struct clk_regmap ao_32k_div = {
+ .data = &(struct meson_clk_dualdiv_data){
+ .n1 = {
+ .reg_off = AO_RTC_ALT_CLK_CNTL0,
+ .shift = 0,
+ .width = 12,
+ },
+ .n2 = {
+ .reg_off = AO_RTC_ALT_CLK_CNTL0,
+ .shift = 12,
+ .width = 12,
+ },
+ .m1 = {
+ .reg_off = AO_RTC_ALT_CLK_CNTL1,
+ .shift = 0,
+ .width = 12,
+ },
+ .m2 = {
+ .reg_off = AO_RTC_ALT_CLK_CNTL1,
+ .shift = 12,
+ .width = 12,
+ },
+ .dual = {
+ .reg_off = AO_RTC_ALT_CLK_CNTL0,
+ .shift = 28,
+ .width = 1,
+ },
+ .table = gxbb_32k_div_table,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "ao_32k_div",
+ .ops = &meson_clk_dualdiv_ops,
+ .parent_names = (const char *[]){ "ao_32k_pre" },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap ao_32k_sel = {
+ .data = &(struct clk_regmap_mux_data) {
+ .offset = AO_RTC_ALT_CLK_CNTL1,
+ .mask = 0x1,
+ .shift = 24,
+ .flags = CLK_MUX_ROUND_CLOSEST,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "ao_32k_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_names = (const char *[]){ "ao_32k_div",
+ "ao_32k_pre" },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap ao_32k = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = AO_RTC_ALT_CLK_CNTL0,
+ .bit_idx = 30,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "ao_32k",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "ao_32k_sel" },
.num_parents = 1,
- .flags = CLK_IGNORE_UNUSED,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap ao_cts_rtc_oscin = {
+ .data = &(struct clk_regmap_mux_data) {
+ .offset = AO_RTI_PWR_CNTL_REG0,
+ .mask = 0x7,
+ .shift = 10,
+ .table = (u32[]){ 1, 2, 3, 4 },
+ .flags = CLK_MUX_ROUND_CLOSEST,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "ao_cts_rtc_oscin",
+ .ops = &clk_regmap_mux_ops,
+ .parent_names = (const char *[]){ IN_PREFIX "ext-32k-0",
+ IN_PREFIX "ext-32k-1",
+ IN_PREFIX "ext-32k-2",
+ "ao_32k" },
+ .num_parents = 4,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap ao_clk81 = {
+ .data = &(struct clk_regmap_mux_data) {
+ .offset = AO_RTI_PWR_CNTL_REG0,
+ .mask = 0x1,
+ .shift = 0,
+ .flags = CLK_MUX_ROUND_CLOSEST,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "ao_clk81",
+ .ops = &clk_regmap_mux_ro_ops,
+ .parent_names = (const char *[]){ IN_PREFIX "mpeg-clk",
+ "ao_cts_rtc_oscin" },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap ao_cts_cec = {
+ .data = &(struct clk_regmap_mux_data) {
+ .offset = AO_CRT_CLK_CNTL1,
+ .mask = 0x1,
+ .shift = 27,
+ .flags = CLK_MUX_ROUND_CLOSEST,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "ao_cts_cec",
+ .ops = &clk_regmap_mux_ops,
+ /*
+ * FIXME: The 'fixme' parent obviously does not exist.
+ *
+ * ATM, CCF won't call get_parent() if num_parents is 1. It
+ * does not allow NULL as a parent name either.
+ *
+ * On this particular mux, we only know the input #1 parent
+ * but, on boot, unknown input #0 is set, so it is critical
+ * to call .get_parent() on it
+ *
+ * Until CCF gets fixed, adding this fake parent that won't
+ * ever be registered should work around the problem
+ */
+ .parent_names = (const char *[]){ "fixme",
+ "ao_cts_rtc_oscin" },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT,
},
};
@@ -50,13 +224,21 @@ static const unsigned int gxbb_aoclk_reset[] = {
[RESET_AO_IR_BLASTER] = 23,
};
-static struct clk_regmap *gxbb_aoclk_gate[] = {
- [CLKID_AO_REMOTE] = &remote_ao,
- [CLKID_AO_I2C_MASTER] = &i2c_master_ao,
- [CLKID_AO_I2C_SLAVE] = &i2c_slave_ao,
- [CLKID_AO_UART1] = &uart1_ao,
- [CLKID_AO_UART2] = &uart2_ao,
- [CLKID_AO_IR_BLASTER] = &ir_blaster_ao,
+static struct clk_regmap *gxbb_aoclk[] = {
+ &remote_ao,
+ &i2c_master_ao,
+ &i2c_slave_ao,
+ &uart1_ao,
+ &uart2_ao,
+ &ir_blaster_ao,
+ &ao_cts_oscin,
+ &ao_32k_pre,
+ &ao_32k_div,
+ &ao_32k_sel,
+ &ao_32k,
+ &ao_cts_rtc_oscin,
+ &ao_clk81,
+ &ao_cts_cec,
};
static const struct clk_hw_onecell_data gxbb_aoclk_onecell_data = {
@@ -67,52 +249,38 @@ static const struct clk_hw_onecell_data gxbb_aoclk_onecell_data = {
[CLKID_AO_UART1] = &uart1_ao.hw,
[CLKID_AO_UART2] = &uart2_ao.hw,
[CLKID_AO_IR_BLASTER] = &ir_blaster_ao.hw,
- [CLKID_AO_CEC_32K] = &cec_32k_ao.hw,
+ [CLKID_AO_CEC_32K] = &ao_cts_cec.hw,
+ [CLKID_AO_CTS_OSCIN] = &ao_cts_oscin.hw,
+ [CLKID_AO_32K_PRE] = &ao_32k_pre.hw,
+ [CLKID_AO_32K_DIV] = &ao_32k_div.hw,
+ [CLKID_AO_32K_SEL] = &ao_32k_sel.hw,
+ [CLKID_AO_32K] = &ao_32k.hw,
+ [CLKID_AO_CTS_RTC_OSCIN] = &ao_cts_rtc_oscin.hw,
+ [CLKID_AO_CLK81] = &ao_clk81.hw,
},
.num = NR_CLKS,
};
-static int gxbb_register_cec_ao_32k(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct regmap *regmap;
- int ret;
-
- regmap = syscon_node_to_regmap(of_get_parent(dev->of_node));
- if (IS_ERR(regmap)) {
- dev_err(dev, "failed to get regmap\n");
- return PTR_ERR(regmap);
- }
-
- /* Specific clocks */
- cec_32k_ao.regmap = regmap;
- ret = devm_clk_hw_register(dev, &cec_32k_ao.hw);
- if (ret) {
- dev_err(&pdev->dev, "clk cec_32k_ao register failed.\n");
- return ret;
- }
-
- return 0;
-}
+static const struct meson_aoclk_input gxbb_aoclk_inputs[] = {
+ { .name = "xtal", .required = true, },
+ { .name = "mpeg-clk", .required = true, },
+ {. name = "ext-32k-0", .required = false, },
+ {. name = "ext-32k-1", .required = false, },
+ {. name = "ext-32k-2", .required = false, },
+};
static const struct meson_aoclk_data gxbb_aoclkc_data = {
.reset_reg = AO_RTI_GEN_CNTL_REG0,
.num_reset = ARRAY_SIZE(gxbb_aoclk_reset),
.reset = gxbb_aoclk_reset,
- .num_clks = ARRAY_SIZE(gxbb_aoclk_gate),
- .clks = gxbb_aoclk_gate,
+ .num_clks = ARRAY_SIZE(gxbb_aoclk),
+ .clks = gxbb_aoclk,
.hw_data = &gxbb_aoclk_onecell_data,
+ .inputs = gxbb_aoclk_inputs,
+ .num_inputs = ARRAY_SIZE(gxbb_aoclk_inputs),
+ .input_prefix = IN_PREFIX,
};
-static int gxbb_aoclkc_probe(struct platform_device *pdev)
-{
- int ret = gxbb_register_cec_ao_32k(pdev);
- if (ret)
- return ret;
-
- return meson_aoclkc_probe(pdev);
-}
-
static const struct of_device_id gxbb_aoclkc_match_table[] = {
{
.compatible = "amlogic,meson-gx-aoclkc",
@@ -122,7 +290,7 @@ static const struct of_device_id gxbb_aoclkc_match_table[] = {
};
static struct platform_driver gxbb_aoclkc_driver = {
- .probe = gxbb_aoclkc_probe,
+ .probe = meson_aoclkc_probe,
.driver = {
.name = "gxbb-aoclkc",
.of_match_table = gxbb_aoclkc_match_table,
diff --git a/drivers/clk/meson/gxbb-aoclk.h b/drivers/clk/meson/gxbb-aoclk.h
index c514493d989a..1db16f9b37d4 100644
--- a/drivers/clk/meson/gxbb-aoclk.h
+++ b/drivers/clk/meson/gxbb-aoclk.h
@@ -7,25 +7,7 @@
#ifndef __GXBB_AOCLKC_H
#define __GXBB_AOCLKC_H
-#define NR_CLKS 7
-
-/* AO Configuration Clock registers offsets */
-#define AO_RTI_PWR_CNTL_REG1 0x0c
-#define AO_RTI_PWR_CNTL_REG0 0x10
-#define AO_RTI_GEN_CNTL_REG0 0x40
-#define AO_OSCIN_CNTL 0x58
-#define AO_CRT_CLK_CNTL1 0x68
-#define AO_RTC_ALT_CLK_CNTL0 0x94
-#define AO_RTC_ALT_CLK_CNTL1 0x98
-
-struct aoclk_cec_32k {
- struct clk_hw hw;
- struct regmap *regmap;
-};
-
-#define to_aoclk_cec_32k(_hw) container_of(_hw, struct aoclk_cec_32k, hw)
-
-extern const struct clk_ops meson_aoclk_cec_32k_ops;
+#define NR_CLKS 14
#include <dt-bindings/clock/gxbb-aoclkc.h>
#include <dt-bindings/reset/gxbb-aoclkc.h>
diff --git a/drivers/clk/meson/gxbb.c b/drivers/clk/meson/gxbb.c
index 65f2599e5243..04df2e208ed6 100644
--- a/drivers/clk/meson/gxbb.c
+++ b/drivers/clk/meson/gxbb.c
@@ -4,17 +4,20 @@
* Michael Turquette <mturquette@baylibre.com>
*/
-#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/init.h>
#include <linux/of_device.h>
-#include <linux/mfd/syscon.h>
#include <linux/platform_device.h>
-#include <linux/regmap.h>
-#include "clkc.h"
#include "gxbb.h"
+#include "clk-input.h"
#include "clk-regmap.h"
+#include "clk-pll.h"
+#include "clk-mpll.h"
+#include "meson-eeclk.h"
+#include "vid-pll-div.h"
+
+#define IN_PREFIX "ee-in-"
static DEFINE_SPINLOCK(meson_clk_lock);
@@ -118,7 +121,7 @@ static struct clk_regmap gxbb_fixed_pll_dco = {
.hw.init = &(struct clk_init_data){
.name = "fixed_pll_dco",
.ops = &meson_clk_pll_ro_ops,
- .parent_names = (const char *[]){ "xtal" },
+ .parent_names = (const char *[]){ IN_PREFIX "xtal" },
.num_parents = 1,
},
};
@@ -148,7 +151,7 @@ static struct clk_fixed_factor gxbb_hdmi_pll_pre_mult = {
.hw.init = &(struct clk_init_data){
.name = "hdmi_pll_pre_mult",
.ops = &clk_fixed_factor_ops,
- .parent_names = (const char *[]){ "xtal" },
+ .parent_names = (const char *[]){ IN_PREFIX "xtal" },
.num_parents = 1,
},
};
@@ -241,7 +244,7 @@ static struct clk_regmap gxl_hdmi_pll_dco = {
.hw.init = &(struct clk_init_data){
.name = "hdmi_pll_dco",
.ops = &meson_clk_pll_ro_ops,
- .parent_names = (const char *[]){ "xtal" },
+ .parent_names = (const char *[]){ IN_PREFIX "xtal" },
.num_parents = 1,
/*
* Display directly handle hdmi pll registers ATM, we need
@@ -378,7 +381,7 @@ static struct clk_regmap gxbb_sys_pll_dco = {
.hw.init = &(struct clk_init_data){
.name = "sys_pll_dco",
.ops = &meson_clk_pll_ro_ops,
- .parent_names = (const char *[]){ "xtal" },
+ .parent_names = (const char *[]){ IN_PREFIX "xtal" },
.num_parents = 1,
},
};
@@ -439,7 +442,7 @@ static struct clk_regmap gxbb_gp0_pll_dco = {
.hw.init = &(struct clk_init_data){
.name = "gp0_pll_dco",
.ops = &meson_clk_pll_ops,
- .parent_names = (const char *[]){ "xtal" },
+ .parent_names = (const char *[]){ IN_PREFIX "xtal" },
.num_parents = 1,
},
};
@@ -491,7 +494,7 @@ static struct clk_regmap gxl_gp0_pll_dco = {
.hw.init = &(struct clk_init_data){
.name = "gp0_pll_dco",
.ops = &meson_clk_pll_ops,
- .parent_names = (const char *[]){ "xtal" },
+ .parent_names = (const char *[]){ IN_PREFIX "xtal" },
.num_parents = 1,
},
};
@@ -789,7 +792,7 @@ static struct clk_regmap gxbb_mpll2 = {
static u32 mux_table_clk81[] = { 0, 2, 3, 4, 5, 6, 7 };
static const char * const clk81_parent_names[] = {
- "xtal", "fclk_div7", "mpll1", "mpll2", "fclk_div4",
+ IN_PREFIX "xtal", "fclk_div7", "mpll1", "mpll2", "fclk_div4",
"fclk_div3", "fclk_div5"
};
@@ -852,7 +855,7 @@ static struct clk_regmap gxbb_sar_adc_clk_sel = {
.name = "sar_adc_clk_sel",
.ops = &clk_regmap_mux_ops,
/* NOTE: The datasheet doesn't list the parents for bit 10 */
- .parent_names = (const char *[]){ "xtal", "clk81", },
+ .parent_names = (const char *[]){ IN_PREFIX "xtal", "clk81", },
.num_parents = 2,
},
};
@@ -891,7 +894,7 @@ static struct clk_regmap gxbb_sar_adc_clk = {
*/
static const char * const gxbb_mali_0_1_parent_names[] = {
- "xtal", "gp0_pll", "mpll2", "mpll1", "fclk_div7",
+ IN_PREFIX "xtal", "gp0_pll", "mpll2", "mpll1", "fclk_div7",
"fclk_div4", "fclk_div3", "fclk_div5"
};
@@ -1153,7 +1156,7 @@ static struct clk_regmap gxbb_32k_clk = {
};
static const char * const gxbb_32k_clk_parent_names[] = {
- "xtal", "cts_slow_oscin", "fclk_div3", "fclk_div5"
+ IN_PREFIX "xtal", "cts_slow_oscin", "fclk_div3", "fclk_div5"
};
static struct clk_regmap gxbb_32k_clk_sel = {
@@ -1172,7 +1175,7 @@ static struct clk_regmap gxbb_32k_clk_sel = {
};
static const char * const gxbb_sd_emmc_clk0_parent_names[] = {
- "xtal", "fclk_div2", "fclk_div3", "fclk_div5", "fclk_div7",
+ IN_PREFIX "xtal", "fclk_div2", "fclk_div3", "fclk_div5", "fclk_div7",
/*
* Following these parent clocks, we should also have had mpll2, mpll3
@@ -2138,7 +2141,7 @@ static struct clk_regmap gxbb_hdmi_tx = {
/* HDMI Clocks */
static const char * const gxbb_hdmi_parent_names[] = {
- "xtal", "fclk_div4", "fclk_div3", "fclk_div5"
+ IN_PREFIX "xtal", "fclk_div4", "fclk_div3", "fclk_div5"
};
static struct clk_regmap gxbb_hdmi_sel = {
@@ -2285,7 +2288,7 @@ static struct clk_regmap gxbb_vdec_hevc = {
static u32 mux_table_gen_clk[] = { 0, 4, 5, 6, 7, 8,
9, 10, 11, 13, 14, };
static const char * const gen_clk_parent_names[] = {
- "xtal", "vdec_1", "vdec_hevc", "mpll0", "mpll1", "mpll2",
+ IN_PREFIX "xtal", "vdec_1", "vdec_hevc", "mpll0", "mpll1", "mpll2",
"fclk_div4", "fclk_div3", "fclk_div5", "fclk_div7", "gp0_pll",
};
@@ -2854,6 +2857,192 @@ static struct clk_hw_onecell_data gxl_hw_onecell_data = {
};
static struct clk_regmap *const gxbb_clk_regmaps[] = {
+ &gxbb_clk81,
+ &gxbb_ddr,
+ &gxbb_dos,
+ &gxbb_isa,
+ &gxbb_pl301,
+ &gxbb_periphs,
+ &gxbb_spicc,
+ &gxbb_i2c,
+ &gxbb_sar_adc,
+ &gxbb_smart_card,
+ &gxbb_rng0,
+ &gxbb_uart0,
+ &gxbb_sdhc,
+ &gxbb_stream,
+ &gxbb_async_fifo,
+ &gxbb_sdio,
+ &gxbb_abuf,
+ &gxbb_hiu_iface,
+ &gxbb_assist_misc,
+ &gxbb_spi,
+ &gxbb_i2s_spdif,
+ &gxbb_eth,
+ &gxbb_demux,
+ &gxbb_aiu_glue,
+ &gxbb_iec958,
+ &gxbb_i2s_out,
+ &gxbb_amclk,
+ &gxbb_aififo2,
+ &gxbb_mixer,
+ &gxbb_mixer_iface,
+ &gxbb_adc,
+ &gxbb_blkmv,
+ &gxbb_aiu,
+ &gxbb_uart1,
+ &gxbb_g2d,
+ &gxbb_usb0,
+ &gxbb_usb1,
+ &gxbb_reset,
+ &gxbb_nand,
+ &gxbb_dos_parser,
+ &gxbb_usb,
+ &gxbb_vdin1,
+ &gxbb_ahb_arb0,
+ &gxbb_efuse,
+ &gxbb_boot_rom,
+ &gxbb_ahb_data_bus,
+ &gxbb_ahb_ctrl_bus,
+ &gxbb_hdmi_intr_sync,
+ &gxbb_hdmi_pclk,
+ &gxbb_usb1_ddr_bridge,
+ &gxbb_usb0_ddr_bridge,
+ &gxbb_mmc_pclk,
+ &gxbb_dvin,
+ &gxbb_uart2,
+ &gxbb_sana,
+ &gxbb_vpu_intr,
+ &gxbb_sec_ahb_ahb3_bridge,
+ &gxbb_clk81_a53,
+ &gxbb_vclk2_venci0,
+ &gxbb_vclk2_venci1,
+ &gxbb_vclk2_vencp0,
+ &gxbb_vclk2_vencp1,
+ &gxbb_gclk_venci_int0,
+ &gxbb_gclk_vencp_int,
+ &gxbb_dac_clk,
+ &gxbb_aoclk_gate,
+ &gxbb_iec958_gate,
+ &gxbb_enc480p,
+ &gxbb_rng1,
+ &gxbb_gclk_venci_int1,
+ &gxbb_vclk2_venclmcc,
+ &gxbb_vclk2_vencl,
+ &gxbb_vclk_other,
+ &gxbb_edp,
+ &gxbb_ao_media_cpu,
+ &gxbb_ao_ahb_sram,
+ &gxbb_ao_ahb_bus,
+ &gxbb_ao_iface,
+ &gxbb_ao_i2c,
+ &gxbb_emmc_a,
+ &gxbb_emmc_b,
+ &gxbb_emmc_c,
+ &gxbb_sar_adc_clk,
+ &gxbb_mali_0,
+ &gxbb_mali_1,
+ &gxbb_cts_amclk,
+ &gxbb_cts_mclk_i958,
+ &gxbb_32k_clk,
+ &gxbb_sd_emmc_a_clk0,
+ &gxbb_sd_emmc_b_clk0,
+ &gxbb_sd_emmc_c_clk0,
+ &gxbb_vpu_0,
+ &gxbb_vpu_1,
+ &gxbb_vapb_0,
+ &gxbb_vapb_1,
+ &gxbb_vapb,
+ &gxbb_mpeg_clk_div,
+ &gxbb_sar_adc_clk_div,
+ &gxbb_mali_0_div,
+ &gxbb_mali_1_div,
+ &gxbb_cts_mclk_i958_div,
+ &gxbb_32k_clk_div,
+ &gxbb_sd_emmc_a_clk0_div,
+ &gxbb_sd_emmc_b_clk0_div,
+ &gxbb_sd_emmc_c_clk0_div,
+ &gxbb_vpu_0_div,
+ &gxbb_vpu_1_div,
+ &gxbb_vapb_0_div,
+ &gxbb_vapb_1_div,
+ &gxbb_mpeg_clk_sel,
+ &gxbb_sar_adc_clk_sel,
+ &gxbb_mali_0_sel,
+ &gxbb_mali_1_sel,
+ &gxbb_mali,
+ &gxbb_cts_amclk_sel,
+ &gxbb_cts_mclk_i958_sel,
+ &gxbb_cts_i958,
+ &gxbb_32k_clk_sel,
+ &gxbb_sd_emmc_a_clk0_sel,
+ &gxbb_sd_emmc_b_clk0_sel,
+ &gxbb_sd_emmc_c_clk0_sel,
+ &gxbb_vpu_0_sel,
+ &gxbb_vpu_1_sel,
+ &gxbb_vpu,
+ &gxbb_vapb_0_sel,
+ &gxbb_vapb_1_sel,
+ &gxbb_vapb_sel,
+ &gxbb_mpll0,
+ &gxbb_mpll1,
+ &gxbb_mpll2,
+ &gxbb_mpll0_div,
+ &gxbb_mpll1_div,
+ &gxbb_mpll2_div,
+ &gxbb_cts_amclk_div,
+ &gxbb_fixed_pll,
+ &gxbb_sys_pll,
+ &gxbb_mpll_prediv,
+ &gxbb_fclk_div2,
+ &gxbb_fclk_div3,
+ &gxbb_fclk_div4,
+ &gxbb_fclk_div5,
+ &gxbb_fclk_div7,
+ &gxbb_vdec_1_sel,
+ &gxbb_vdec_1_div,
+ &gxbb_vdec_1,
+ &gxbb_vdec_hevc_sel,
+ &gxbb_vdec_hevc_div,
+ &gxbb_vdec_hevc,
+ &gxbb_gen_clk_sel,
+ &gxbb_gen_clk_div,
+ &gxbb_gen_clk,
+ &gxbb_fixed_pll_dco,
+ &gxbb_sys_pll_dco,
+ &gxbb_gp0_pll,
+ &gxbb_vid_pll,
+ &gxbb_vid_pll_sel,
+ &gxbb_vid_pll_div,
+ &gxbb_vclk,
+ &gxbb_vclk_sel,
+ &gxbb_vclk_div,
+ &gxbb_vclk_input,
+ &gxbb_vclk_div1,
+ &gxbb_vclk_div2_en,
+ &gxbb_vclk_div4_en,
+ &gxbb_vclk_div6_en,
+ &gxbb_vclk_div12_en,
+ &gxbb_vclk2,
+ &gxbb_vclk2_sel,
+ &gxbb_vclk2_div,
+ &gxbb_vclk2_input,
+ &gxbb_vclk2_div1,
+ &gxbb_vclk2_div2_en,
+ &gxbb_vclk2_div4_en,
+ &gxbb_vclk2_div6_en,
+ &gxbb_vclk2_div12_en,
+ &gxbb_cts_enci,
+ &gxbb_cts_enci_sel,
+ &gxbb_cts_encp,
+ &gxbb_cts_encp_sel,
+ &gxbb_cts_vdac,
+ &gxbb_cts_vdac_sel,
+ &gxbb_hdmi_tx,
+ &gxbb_hdmi_tx_sel,
+ &gxbb_hdmi_sel,
+ &gxbb_hdmi_div,
+ &gxbb_hdmi,
&gxbb_gp0_pll_dco,
&gxbb_hdmi_pll,
&gxbb_hdmi_pll_od,
@@ -2862,14 +3051,6 @@ static struct clk_regmap *const gxbb_clk_regmaps[] = {
};
static struct clk_regmap *const gxl_clk_regmaps[] = {
- &gxl_gp0_pll_dco,
- &gxl_hdmi_pll,
- &gxl_hdmi_pll_od,
- &gxl_hdmi_pll_od2,
- &gxl_hdmi_pll_dco,
-};
-
-static struct clk_regmap *const gx_clk_regmaps[] = {
&gxbb_clk81,
&gxbb_ddr,
&gxbb_dos,
@@ -3056,23 +3237,22 @@ static struct clk_regmap *const gx_clk_regmaps[] = {
&gxbb_hdmi_sel,
&gxbb_hdmi_div,
&gxbb_hdmi,
+ &gxl_gp0_pll_dco,
+ &gxl_hdmi_pll,
+ &gxl_hdmi_pll_od,
+ &gxl_hdmi_pll_od2,
+ &gxl_hdmi_pll_dco,
};
-struct clkc_data {
- struct clk_regmap *const *regmap_clks;
- unsigned int regmap_clks_count;
- struct clk_hw_onecell_data *hw_onecell_data;
-};
-
-static const struct clkc_data gxbb_clkc_data = {
+static const struct meson_eeclkc_data gxbb_clkc_data = {
.regmap_clks = gxbb_clk_regmaps,
- .regmap_clks_count = ARRAY_SIZE(gxbb_clk_regmaps),
+ .regmap_clk_num = ARRAY_SIZE(gxbb_clk_regmaps),
.hw_onecell_data = &gxbb_hw_onecell_data,
};
-static const struct clkc_data gxl_clkc_data = {
+static const struct meson_eeclkc_data gxl_clkc_data = {
.regmap_clks = gxl_clk_regmaps,
- .regmap_clks_count = ARRAY_SIZE(gxl_clk_regmaps),
+ .regmap_clk_num = ARRAY_SIZE(gxl_clk_regmaps),
.hw_onecell_data = &gxl_hw_onecell_data,
};
@@ -3082,52 +3262,8 @@ static const struct of_device_id clkc_match_table[] = {
{},
};
-static int gxbb_clkc_probe(struct platform_device *pdev)
-{
- const struct clkc_data *clkc_data;
- struct regmap *map;
- int ret, i;
- struct device *dev = &pdev->dev;
-
- clkc_data = of_device_get_match_data(dev);
- if (!clkc_data)
- return -EINVAL;
-
- /* Get the hhi system controller node if available */
- map = syscon_node_to_regmap(of_get_parent(dev->of_node));
- if (IS_ERR(map)) {
- dev_err(dev, "failed to get HHI regmap\n");
- return PTR_ERR(map);
- }
-
- /* Populate regmap for the common regmap backed clocks */
- for (i = 0; i < ARRAY_SIZE(gx_clk_regmaps); i++)
- gx_clk_regmaps[i]->map = map;
-
- /* Populate regmap for soc specific clocks */
- for (i = 0; i < clkc_data->regmap_clks_count; i++)
- clkc_data->regmap_clks[i]->map = map;
-
- /* Register all clks */
- for (i = 0; i < clkc_data->hw_onecell_data->num; i++) {
- /* array might be sparse */
- if (!clkc_data->hw_onecell_data->hws[i])
- continue;
-
- ret = devm_clk_hw_register(dev,
- clkc_data->hw_onecell_data->hws[i]);
- if (ret) {
- dev_err(dev, "Clock registration failed\n");
- return ret;
- }
- }
-
- return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
- clkc_data->hw_onecell_data);
-}
-
static struct platform_driver gxbb_driver = {
- .probe = gxbb_clkc_probe,
+ .probe = meson_eeclkc_probe,
.driver = {
.name = "gxbb-clkc",
.of_match_table = clkc_match_table,
diff --git a/drivers/clk/meson/meson-aoclk.c b/drivers/clk/meson/meson-aoclk.c
index f965845917e3..b67951909e04 100644
--- a/drivers/clk/meson/meson-aoclk.c
+++ b/drivers/clk/meson/meson-aoclk.c
@@ -14,9 +14,11 @@
#include <linux/reset-controller.h>
#include <linux/mfd/syscon.h>
#include <linux/of_device.h>
-#include "clk-regmap.h"
+#include <linux/slab.h>
#include "meson-aoclk.h"
+#include "clk-input.h"
+
static int meson_aoclk_do_reset(struct reset_controller_dev *rcdev,
unsigned long id)
{
@@ -31,6 +33,37 @@ static const struct reset_control_ops meson_aoclk_reset_ops = {
.reset = meson_aoclk_do_reset,
};
+static int meson_aoclkc_register_inputs(struct device *dev,
+ struct meson_aoclk_data *data)
+{
+ struct clk_hw *hw;
+ char *str;
+ int i;
+
+ for (i = 0; i < data->num_inputs; i++) {
+ const struct meson_aoclk_input *in = &data->inputs[i];
+
+ str = kasprintf(GFP_KERNEL, "%s%s", data->input_prefix,
+ in->name);
+ if (!str)
+ return -ENOMEM;
+
+ hw = meson_clk_hw_register_input(dev, in->name, str, 0);
+ kfree(str);
+
+ if (IS_ERR(hw)) {
+ if (!in->required && PTR_ERR(hw) == -ENOENT)
+ continue;
+ else if (PTR_ERR(hw) != -EPROBE_DEFER)
+ dev_err(dev, "failed to register input %s\n",
+ in->name);
+ return PTR_ERR(hw);
+ }
+ }
+
+ return 0;
+}
+
int meson_aoclkc_probe(struct platform_device *pdev)
{
struct meson_aoclk_reset_controller *rstc;
@@ -53,6 +86,10 @@ int meson_aoclkc_probe(struct platform_device *pdev)
return PTR_ERR(regmap);
}
+ ret = meson_aoclkc_register_inputs(dev, data);
+ if (ret)
+ return ret;
+
/* Reset Controller */
rstc->data = data;
rstc->regmap = regmap;
@@ -65,15 +102,20 @@ int meson_aoclkc_probe(struct platform_device *pdev)
return ret;
}
- /*
- * Populate regmap and register all clks
- */
- for (clkid = 0; clkid < data->num_clks; clkid++) {
+ /* Populate regmap */
+ for (clkid = 0; clkid < data->num_clks; clkid++)
data->clks[clkid]->map = regmap;
+ /* Register all clks */
+ for (clkid = 0; clkid < data->hw_data->num; clkid++) {
+ if (!data->hw_data->hws[clkid])
+ continue;
+
ret = devm_clk_hw_register(dev, data->hw_data->hws[clkid]);
- if (ret)
+ if (ret) {
+ dev_err(dev, "Clock registration failed\n");
return ret;
+ }
}
return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
diff --git a/drivers/clk/meson/meson-aoclk.h b/drivers/clk/meson/meson-aoclk.h
index ab2819e88922..999cde3868f7 100644
--- a/drivers/clk/meson/meson-aoclk.h
+++ b/drivers/clk/meson/meson-aoclk.h
@@ -11,16 +11,27 @@
#ifndef __MESON_AOCLK_H__
#define __MESON_AOCLK_H__
+#include <linux/clk-provider.h>
#include <linux/platform_device.h>
+#include <linux/regmap.h>
#include <linux/reset-controller.h>
+
#include "clk-regmap.h"
+struct meson_aoclk_input {
+ const char *name;
+ bool required;
+};
+
struct meson_aoclk_data {
const unsigned int reset_reg;
const int num_reset;
const unsigned int *reset;
- int num_clks;
+ const int num_clks;
struct clk_regmap **clks;
+ const int num_inputs;
+ const struct meson_aoclk_input *inputs;
+ const char *input_prefix;
const struct clk_hw_onecell_data *hw_data;
};
diff --git a/drivers/clk/meson/meson-eeclk.c b/drivers/clk/meson/meson-eeclk.c
new file mode 100644
index 000000000000..37a34c9c3885
--- /dev/null
+++ b/drivers/clk/meson/meson-eeclk.c
@@ -0,0 +1,63 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019 BayLibre, SAS.
+ * Author: Jerome Brunet <jbrunet@baylibre.com>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+
+#include "clk-input.h"
+#include "clk-regmap.h"
+#include "meson-eeclk.h"
+
+int meson_eeclkc_probe(struct platform_device *pdev)
+{
+ const struct meson_eeclkc_data *data;
+ struct device *dev = &pdev->dev;
+ struct clk_hw *input;
+ struct regmap *map;
+ int ret, i;
+
+ data = of_device_get_match_data(dev);
+ if (!data)
+ return -EINVAL;
+
+ /* Get the hhi system controller node */
+ map = syscon_node_to_regmap(of_get_parent(dev->of_node));
+ if (IS_ERR(map)) {
+ dev_err(dev,
+ "failed to get HHI regmap\n");
+ return PTR_ERR(map);
+ }
+
+ input = meson_clk_hw_register_input(dev, "xtal", IN_PREFIX "xtal", 0);
+ if (IS_ERR(input)) {
+ ret = PTR_ERR(input);
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "failed to get input clock");
+ return ret;
+ }
+
+ /* Populate regmap for the regmap backed clocks */
+ for (i = 0; i < data->regmap_clk_num; i++)
+ data->regmap_clks[i]->map = map;
+
+ for (i = 0; i < data->hw_onecell_data->num; i++) {
+ /* array might be sparse */
+ if (!data->hw_onecell_data->hws[i])
+ continue;
+
+ ret = devm_clk_hw_register(dev, data->hw_onecell_data->hws[i]);
+ if (ret) {
+ dev_err(dev, "Clock registration failed\n");
+ return ret;
+ }
+ }
+
+ return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
+ data->hw_onecell_data);
+}
diff --git a/drivers/clk/meson/meson-eeclk.h b/drivers/clk/meson/meson-eeclk.h
new file mode 100644
index 000000000000..1b809b1419fe
--- /dev/null
+++ b/drivers/clk/meson/meson-eeclk.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 BayLibre, SAS.
+ * Author: Jerome Brunet <jbrunet@baylibre.com>
+ */
+
+#ifndef __MESON_CLKC_H
+#define __MESON_CLKC_H
+
+#include <linux/clk-provider.h>
+#include "clk-regmap.h"
+
+#define IN_PREFIX "ee-in-"
+
+struct platform_device;
+
+struct meson_eeclkc_data {
+ struct clk_regmap *const *regmap_clks;
+ unsigned int regmap_clk_num;
+ struct clk_hw_onecell_data *hw_onecell_data;
+};
+
+int meson_eeclkc_probe(struct platform_device *pdev);
+
+#endif /* __MESON_CLKC_H */
diff --git a/drivers/clk/meson/meson8b.c b/drivers/clk/meson/meson8b.c
index 950d0e548c75..576ad42252d0 100644
--- a/drivers/clk/meson/meson8b.c
+++ b/drivers/clk/meson/meson8b.c
@@ -16,9 +16,10 @@
#include <linux/slab.h>
#include <linux/regmap.h>
-#include "clkc.h"
#include "meson8b.h"
#include "clk-regmap.h"
+#include "clk-pll.h"
+#include "clk-mpll.h"
static DEFINE_SPINLOCK(meson_clk_lock);
@@ -803,16 +804,16 @@ static struct clk_fixed_factor meson8b_cpu_clk_div8 = {
},
};
-static u32 mux_table_abp[] = { 1, 2, 3, 4, 5, 6, 7 };
-static struct clk_regmap meson8b_abp_clk_sel = {
+static u32 mux_table_apb[] = { 1, 2, 3, 4, 5, 6, 7 };
+static struct clk_regmap meson8b_apb_clk_sel = {
.data = &(struct clk_regmap_mux_data){
.offset = HHI_SYS_CPU_CLK_CNTL1,
.mask = 0x7,
.shift = 3,
- .table = mux_table_abp,
+ .table = mux_table_apb,
},
.hw.init = &(struct clk_init_data){
- .name = "abp_clk_sel",
+ .name = "apb_clk_sel",
.ops = &clk_regmap_mux_ops,
.parent_names = (const char *[]){ "cpu_clk_div2",
"cpu_clk_div3",
@@ -825,16 +826,16 @@ static struct clk_regmap meson8b_abp_clk_sel = {
},
};
-static struct clk_regmap meson8b_abp_clk_gate = {
+static struct clk_regmap meson8b_apb_clk_gate = {
.data = &(struct clk_regmap_gate_data){
.offset = HHI_SYS_CPU_CLK_CNTL1,
.bit_idx = 16,
.flags = CLK_GATE_SET_TO_DISABLE,
},
.hw.init = &(struct clk_init_data){
- .name = "abp_clk_dis",
+ .name = "apb_clk_dis",
.ops = &clk_regmap_gate_ro_ops,
- .parent_names = (const char *[]){ "abp_clk_sel" },
+ .parent_names = (const char *[]){ "apb_clk_sel" },
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
},
@@ -1573,6 +1574,135 @@ static struct clk_regmap meson8b_hdmi_sys = {
},
};
+/*
+ * The MALI IP is clocked by two identical clocks (mali_0 and mali_1)
+ * muxed by a glitch-free switch on Meson8b and Meson8m2. Meson8 only
+ * has mali_0 and no glitch-free mux.
+ */
+static const char * const meson8b_mali_0_1_parent_names[] = {
+ "xtal", "mpll2", "mpll1", "fclk_div7", "fclk_div4", "fclk_div3",
+ "fclk_div5"
+};
+
+static u32 meson8b_mali_0_1_mux_table[] = { 0, 2, 3, 4, 5, 6, 7 };
+
+static struct clk_regmap meson8b_mali_0_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HHI_MALI_CLK_CNTL,
+ .mask = 0x7,
+ .shift = 9,
+ .table = meson8b_mali_0_1_mux_table,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "mali_0_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_names = meson8b_mali_0_1_parent_names,
+ .num_parents = ARRAY_SIZE(meson8b_mali_0_1_parent_names),
+ /*
+ * Don't propagate rate changes up because the only changeable
+ * parents are mpll1 and mpll2 but we need those for audio and
+ * RGMII (Ethernet). We don't want to change the audio or
+ * Ethernet clocks when setting the GPU frequency.
+ */
+ .flags = 0,
+ },
+};
+
+static struct clk_regmap meson8b_mali_0_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = HHI_MALI_CLK_CNTL,
+ .shift = 0,
+ .width = 7,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "mali_0_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_names = (const char *[]){ "mali_0_sel" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap meson8b_mali_0 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_MALI_CLK_CNTL,
+ .bit_idx = 8,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "mali_0",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "mali_0_div" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap meson8b_mali_1_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HHI_MALI_CLK_CNTL,
+ .mask = 0x7,
+ .shift = 25,
+ .table = meson8b_mali_0_1_mux_table,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "mali_1_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_names = meson8b_mali_0_1_parent_names,
+ .num_parents = ARRAY_SIZE(meson8b_mali_0_1_parent_names),
+ /*
+ * Don't propagate rate changes up because the only changeable
+ * parents are mpll1 and mpll2 but we need those for audio and
+ * RGMII (Ethernet). We don't want to change the audio or
+ * Ethernet clocks when setting the GPU frequency.
+ */
+ .flags = 0,
+ },
+};
+
+static struct clk_regmap meson8b_mali_1_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = HHI_MALI_CLK_CNTL,
+ .shift = 16,
+ .width = 7,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "mali_1_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_names = (const char *[]){ "mali_1_sel" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap meson8b_mali_1 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_MALI_CLK_CNTL,
+ .bit_idx = 24,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "mali_1",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "mali_1_div" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap meson8b_mali = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HHI_MALI_CLK_CNTL,
+ .mask = 1,
+ .shift = 31,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "mali",
+ .ops = &clk_regmap_mux_ops,
+ .parent_names = (const char *[]){ "mali_0", "mali_1" },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
/* Everything Else (EE) domain gates */
static MESON_GATE(meson8b_ddr, HHI_GCLK_MPEG0, 0);
@@ -1659,6 +1789,188 @@ static MESON_GATE(meson8b_ao_ahb_sram, HHI_GCLK_AO, 1);
static MESON_GATE(meson8b_ao_ahb_bus, HHI_GCLK_AO, 2);
static MESON_GATE(meson8b_ao_iface, HHI_GCLK_AO, 3);
+static struct clk_hw_onecell_data meson8_hw_onecell_data = {
+ .hws = {
+ [CLKID_XTAL] = &meson8b_xtal.hw,
+ [CLKID_PLL_FIXED] = &meson8b_fixed_pll.hw,
+ [CLKID_PLL_VID] = &meson8b_vid_pll.hw,
+ [CLKID_PLL_SYS] = &meson8b_sys_pll.hw,
+ [CLKID_FCLK_DIV2] = &meson8b_fclk_div2.hw,
+ [CLKID_FCLK_DIV3] = &meson8b_fclk_div3.hw,
+ [CLKID_FCLK_DIV4] = &meson8b_fclk_div4.hw,
+ [CLKID_FCLK_DIV5] = &meson8b_fclk_div5.hw,
+ [CLKID_FCLK_DIV7] = &meson8b_fclk_div7.hw,
+ [CLKID_CPUCLK] = &meson8b_cpu_clk.hw,
+ [CLKID_MPEG_SEL] = &meson8b_mpeg_clk_sel.hw,
+ [CLKID_MPEG_DIV] = &meson8b_mpeg_clk_div.hw,
+ [CLKID_CLK81] = &meson8b_clk81.hw,
+ [CLKID_DDR] = &meson8b_ddr.hw,
+ [CLKID_DOS] = &meson8b_dos.hw,
+ [CLKID_ISA] = &meson8b_isa.hw,
+ [CLKID_PL301] = &meson8b_pl301.hw,
+ [CLKID_PERIPHS] = &meson8b_periphs.hw,
+ [CLKID_SPICC] = &meson8b_spicc.hw,
+ [CLKID_I2C] = &meson8b_i2c.hw,
+ [CLKID_SAR_ADC] = &meson8b_sar_adc.hw,
+ [CLKID_SMART_CARD] = &meson8b_smart_card.hw,
+ [CLKID_RNG0] = &meson8b_rng0.hw,
+ [CLKID_UART0] = &meson8b_uart0.hw,
+ [CLKID_SDHC] = &meson8b_sdhc.hw,
+ [CLKID_STREAM] = &meson8b_stream.hw,
+ [CLKID_ASYNC_FIFO] = &meson8b_async_fifo.hw,
+ [CLKID_SDIO] = &meson8b_sdio.hw,
+ [CLKID_ABUF] = &meson8b_abuf.hw,
+ [CLKID_HIU_IFACE] = &meson8b_hiu_iface.hw,
+ [CLKID_ASSIST_MISC] = &meson8b_assist_misc.hw,
+ [CLKID_SPI] = &meson8b_spi.hw,
+ [CLKID_I2S_SPDIF] = &meson8b_i2s_spdif.hw,
+ [CLKID_ETH] = &meson8b_eth.hw,
+ [CLKID_DEMUX] = &meson8b_demux.hw,
+ [CLKID_AIU_GLUE] = &meson8b_aiu_glue.hw,
+ [CLKID_IEC958] = &meson8b_iec958.hw,
+ [CLKID_I2S_OUT] = &meson8b_i2s_out.hw,
+ [CLKID_AMCLK] = &meson8b_amclk.hw,
+ [CLKID_AIFIFO2] = &meson8b_aififo2.hw,
+ [CLKID_MIXER] = &meson8b_mixer.hw,
+ [CLKID_MIXER_IFACE] = &meson8b_mixer_iface.hw,
+ [CLKID_ADC] = &meson8b_adc.hw,
+ [CLKID_BLKMV] = &meson8b_blkmv.hw,
+ [CLKID_AIU] = &meson8b_aiu.hw,
+ [CLKID_UART1] = &meson8b_uart1.hw,
+ [CLKID_G2D] = &meson8b_g2d.hw,
+ [CLKID_USB0] = &meson8b_usb0.hw,
+ [CLKID_USB1] = &meson8b_usb1.hw,
+ [CLKID_RESET] = &meson8b_reset.hw,
+ [CLKID_NAND] = &meson8b_nand.hw,
+ [CLKID_DOS_PARSER] = &meson8b_dos_parser.hw,
+ [CLKID_USB] = &meson8b_usb.hw,
+ [CLKID_VDIN1] = &meson8b_vdin1.hw,
+ [CLKID_AHB_ARB0] = &meson8b_ahb_arb0.hw,
+ [CLKID_EFUSE] = &meson8b_efuse.hw,
+ [CLKID_BOOT_ROM] = &meson8b_boot_rom.hw,
+ [CLKID_AHB_DATA_BUS] = &meson8b_ahb_data_bus.hw,
+ [CLKID_AHB_CTRL_BUS] = &meson8b_ahb_ctrl_bus.hw,
+ [CLKID_HDMI_INTR_SYNC] = &meson8b_hdmi_intr_sync.hw,
+ [CLKID_HDMI_PCLK] = &meson8b_hdmi_pclk.hw,
+ [CLKID_USB1_DDR_BRIDGE] = &meson8b_usb1_ddr_bridge.hw,
+ [CLKID_USB0_DDR_BRIDGE] = &meson8b_usb0_ddr_bridge.hw,
+ [CLKID_MMC_PCLK] = &meson8b_mmc_pclk.hw,
+ [CLKID_DVIN] = &meson8b_dvin.hw,
+ [CLKID_UART2] = &meson8b_uart2.hw,
+ [CLKID_SANA] = &meson8b_sana.hw,
+ [CLKID_VPU_INTR] = &meson8b_vpu_intr.hw,
+ [CLKID_SEC_AHB_AHB3_BRIDGE] = &meson8b_sec_ahb_ahb3_bridge.hw,
+ [CLKID_CLK81_A9] = &meson8b_clk81_a9.hw,
+ [CLKID_VCLK2_VENCI0] = &meson8b_vclk2_venci0.hw,
+ [CLKID_VCLK2_VENCI1] = &meson8b_vclk2_venci1.hw,
+ [CLKID_VCLK2_VENCP0] = &meson8b_vclk2_vencp0.hw,
+ [CLKID_VCLK2_VENCP1] = &meson8b_vclk2_vencp1.hw,
+ [CLKID_GCLK_VENCI_INT] = &meson8b_gclk_venci_int.hw,
+ [CLKID_GCLK_VENCP_INT] = &meson8b_gclk_vencp_int.hw,
+ [CLKID_DAC_CLK] = &meson8b_dac_clk.hw,
+ [CLKID_AOCLK_GATE] = &meson8b_aoclk_gate.hw,
+ [CLKID_IEC958_GATE] = &meson8b_iec958_gate.hw,
+ [CLKID_ENC480P] = &meson8b_enc480p.hw,
+ [CLKID_RNG1] = &meson8b_rng1.hw,
+ [CLKID_GCLK_VENCL_INT] = &meson8b_gclk_vencl_int.hw,
+ [CLKID_VCLK2_VENCLMCC] = &meson8b_vclk2_venclmcc.hw,
+ [CLKID_VCLK2_VENCL] = &meson8b_vclk2_vencl.hw,
+ [CLKID_VCLK2_OTHER] = &meson8b_vclk2_other.hw,
+ [CLKID_EDP] = &meson8b_edp.hw,
+ [CLKID_AO_MEDIA_CPU] = &meson8b_ao_media_cpu.hw,
+ [CLKID_AO_AHB_SRAM] = &meson8b_ao_ahb_sram.hw,
+ [CLKID_AO_AHB_BUS] = &meson8b_ao_ahb_bus.hw,
+ [CLKID_AO_IFACE] = &meson8b_ao_iface.hw,
+ [CLKID_MPLL0] = &meson8b_mpll0.hw,
+ [CLKID_MPLL1] = &meson8b_mpll1.hw,
+ [CLKID_MPLL2] = &meson8b_mpll2.hw,
+ [CLKID_MPLL0_DIV] = &meson8b_mpll0_div.hw,
+ [CLKID_MPLL1_DIV] = &meson8b_mpll1_div.hw,
+ [CLKID_MPLL2_DIV] = &meson8b_mpll2_div.hw,
+ [CLKID_CPU_IN_SEL] = &meson8b_cpu_in_sel.hw,
+ [CLKID_CPU_IN_DIV2] = &meson8b_cpu_in_div2.hw,
+ [CLKID_CPU_IN_DIV3] = &meson8b_cpu_in_div3.hw,
+ [CLKID_CPU_SCALE_DIV] = &meson8b_cpu_scale_div.hw,
+ [CLKID_CPU_SCALE_OUT_SEL] = &meson8b_cpu_scale_out_sel.hw,
+ [CLKID_MPLL_PREDIV] = &meson8b_mpll_prediv.hw,
+ [CLKID_FCLK_DIV2_DIV] = &meson8b_fclk_div2_div.hw,
+ [CLKID_FCLK_DIV3_DIV] = &meson8b_fclk_div3_div.hw,
+ [CLKID_FCLK_DIV4_DIV] = &meson8b_fclk_div4_div.hw,
+ [CLKID_FCLK_DIV5_DIV] = &meson8b_fclk_div5_div.hw,
+ [CLKID_FCLK_DIV7_DIV] = &meson8b_fclk_div7_div.hw,
+ [CLKID_NAND_SEL] = &meson8b_nand_clk_sel.hw,
+ [CLKID_NAND_DIV] = &meson8b_nand_clk_div.hw,
+ [CLKID_NAND_CLK] = &meson8b_nand_clk_gate.hw,
+ [CLKID_PLL_FIXED_DCO] = &meson8b_fixed_pll_dco.hw,
+ [CLKID_HDMI_PLL_DCO] = &meson8b_hdmi_pll_dco.hw,
+ [CLKID_PLL_SYS_DCO] = &meson8b_sys_pll_dco.hw,
+ [CLKID_CPU_CLK_DIV2] = &meson8b_cpu_clk_div2.hw,
+ [CLKID_CPU_CLK_DIV3] = &meson8b_cpu_clk_div3.hw,
+ [CLKID_CPU_CLK_DIV4] = &meson8b_cpu_clk_div4.hw,
+ [CLKID_CPU_CLK_DIV5] = &meson8b_cpu_clk_div5.hw,
+ [CLKID_CPU_CLK_DIV6] = &meson8b_cpu_clk_div6.hw,
+ [CLKID_CPU_CLK_DIV7] = &meson8b_cpu_clk_div7.hw,
+ [CLKID_CPU_CLK_DIV8] = &meson8b_cpu_clk_div8.hw,
+ [CLKID_APB_SEL] = &meson8b_apb_clk_sel.hw,
+ [CLKID_APB] = &meson8b_apb_clk_gate.hw,
+ [CLKID_PERIPH_SEL] = &meson8b_periph_clk_sel.hw,
+ [CLKID_PERIPH] = &meson8b_periph_clk_gate.hw,
+ [CLKID_AXI_SEL] = &meson8b_axi_clk_sel.hw,
+ [CLKID_AXI] = &meson8b_axi_clk_gate.hw,
+ [CLKID_L2_DRAM_SEL] = &meson8b_l2_dram_clk_sel.hw,
+ [CLKID_L2_DRAM] = &meson8b_l2_dram_clk_gate.hw,
+ [CLKID_HDMI_PLL_LVDS_OUT] = &meson8b_hdmi_pll_lvds_out.hw,
+ [CLKID_HDMI_PLL_HDMI_OUT] = &meson8b_hdmi_pll_hdmi_out.hw,
+ [CLKID_VID_PLL_IN_SEL] = &meson8b_vid_pll_in_sel.hw,
+ [CLKID_VID_PLL_IN_EN] = &meson8b_vid_pll_in_en.hw,
+ [CLKID_VID_PLL_PRE_DIV] = &meson8b_vid_pll_pre_div.hw,
+ [CLKID_VID_PLL_POST_DIV] = &meson8b_vid_pll_post_div.hw,
+ [CLKID_VID_PLL_FINAL_DIV] = &meson8b_vid_pll_final_div.hw,
+ [CLKID_VCLK_IN_SEL] = &meson8b_vclk_in_sel.hw,
+ [CLKID_VCLK_IN_EN] = &meson8b_vclk_in_en.hw,
+ [CLKID_VCLK_DIV1] = &meson8b_vclk_div1_gate.hw,
+ [CLKID_VCLK_DIV2_DIV] = &meson8b_vclk_div2_div.hw,
+ [CLKID_VCLK_DIV2] = &meson8b_vclk_div2_div_gate.hw,
+ [CLKID_VCLK_DIV4_DIV] = &meson8b_vclk_div4_div.hw,
+ [CLKID_VCLK_DIV4] = &meson8b_vclk_div4_div_gate.hw,
+ [CLKID_VCLK_DIV6_DIV] = &meson8b_vclk_div6_div.hw,
+ [CLKID_VCLK_DIV6] = &meson8b_vclk_div6_div_gate.hw,
+ [CLKID_VCLK_DIV12_DIV] = &meson8b_vclk_div12_div.hw,
+ [CLKID_VCLK_DIV12] = &meson8b_vclk_div12_div_gate.hw,
+ [CLKID_VCLK2_IN_SEL] = &meson8b_vclk2_in_sel.hw,
+ [CLKID_VCLK2_IN_EN] = &meson8b_vclk2_clk_in_en.hw,
+ [CLKID_VCLK2_DIV1] = &meson8b_vclk2_div1_gate.hw,
+ [CLKID_VCLK2_DIV2_DIV] = &meson8b_vclk2_div2_div.hw,
+ [CLKID_VCLK2_DIV2] = &meson8b_vclk2_div2_div_gate.hw,
+ [CLKID_VCLK2_DIV4_DIV] = &meson8b_vclk2_div4_div.hw,
+ [CLKID_VCLK2_DIV4] = &meson8b_vclk2_div4_div_gate.hw,
+ [CLKID_VCLK2_DIV6_DIV] = &meson8b_vclk2_div6_div.hw,
+ [CLKID_VCLK2_DIV6] = &meson8b_vclk2_div6_div_gate.hw,
+ [CLKID_VCLK2_DIV12_DIV] = &meson8b_vclk2_div12_div.hw,
+ [CLKID_VCLK2_DIV12] = &meson8b_vclk2_div12_div_gate.hw,
+ [CLKID_CTS_ENCT_SEL] = &meson8b_cts_enct_sel.hw,
+ [CLKID_CTS_ENCT] = &meson8b_cts_enct.hw,
+ [CLKID_CTS_ENCP_SEL] = &meson8b_cts_encp_sel.hw,
+ [CLKID_CTS_ENCP] = &meson8b_cts_encp.hw,
+ [CLKID_CTS_ENCI_SEL] = &meson8b_cts_enci_sel.hw,
+ [CLKID_CTS_ENCI] = &meson8b_cts_enci.hw,
+ [CLKID_HDMI_TX_PIXEL_SEL] = &meson8b_hdmi_tx_pixel_sel.hw,
+ [CLKID_HDMI_TX_PIXEL] = &meson8b_hdmi_tx_pixel.hw,
+ [CLKID_CTS_ENCL_SEL] = &meson8b_cts_encl_sel.hw,
+ [CLKID_CTS_ENCL] = &meson8b_cts_encl.hw,
+ [CLKID_CTS_VDAC0_SEL] = &meson8b_cts_vdac0_sel.hw,
+ [CLKID_CTS_VDAC0] = &meson8b_cts_vdac0.hw,
+ [CLKID_HDMI_SYS_SEL] = &meson8b_hdmi_sys_sel.hw,
+ [CLKID_HDMI_SYS_DIV] = &meson8b_hdmi_sys_div.hw,
+ [CLKID_HDMI_SYS] = &meson8b_hdmi_sys.hw,
+ [CLKID_MALI_0_SEL] = &meson8b_mali_0_sel.hw,
+ [CLKID_MALI_0_DIV] = &meson8b_mali_0_div.hw,
+ [CLKID_MALI] = &meson8b_mali_0.hw,
+ [CLK_NR_CLKS] = NULL,
+ },
+ .num = CLK_NR_CLKS,
+};
+
static struct clk_hw_onecell_data meson8b_hw_onecell_data = {
.hws = {
[CLKID_XTAL] = &meson8b_xtal.hw,
@@ -1781,8 +2093,8 @@ static struct clk_hw_onecell_data meson8b_hw_onecell_data = {
[CLKID_CPU_CLK_DIV6] = &meson8b_cpu_clk_div6.hw,
[CLKID_CPU_CLK_DIV7] = &meson8b_cpu_clk_div7.hw,
[CLKID_CPU_CLK_DIV8] = &meson8b_cpu_clk_div8.hw,
- [CLKID_ABP_SEL] = &meson8b_abp_clk_sel.hw,
- [CLKID_ABP] = &meson8b_abp_clk_gate.hw,
+ [CLKID_APB_SEL] = &meson8b_apb_clk_sel.hw,
+ [CLKID_APB] = &meson8b_apb_clk_gate.hw,
[CLKID_PERIPH_SEL] = &meson8b_periph_clk_sel.hw,
[CLKID_PERIPH] = &meson8b_periph_clk_gate.hw,
[CLKID_AXI_SEL] = &meson8b_axi_clk_sel.hw,
@@ -1833,6 +2145,13 @@ static struct clk_hw_onecell_data meson8b_hw_onecell_data = {
[CLKID_HDMI_SYS_SEL] = &meson8b_hdmi_sys_sel.hw,
[CLKID_HDMI_SYS_DIV] = &meson8b_hdmi_sys_div.hw,
[CLKID_HDMI_SYS] = &meson8b_hdmi_sys.hw,
+ [CLKID_MALI_0_SEL] = &meson8b_mali_0_sel.hw,
+ [CLKID_MALI_0_DIV] = &meson8b_mali_0_div.hw,
+ [CLKID_MALI_0] = &meson8b_mali_0.hw,
+ [CLKID_MALI_1_SEL] = &meson8b_mali_1_sel.hw,
+ [CLKID_MALI_1_DIV] = &meson8b_mali_1_div.hw,
+ [CLKID_MALI_1] = &meson8b_mali_1.hw,
+ [CLKID_MALI] = &meson8b_mali.hw,
[CLK_NR_CLKS] = NULL,
},
.num = CLK_NR_CLKS,
@@ -1943,8 +2262,8 @@ static struct clk_regmap *const meson8b_clk_regmaps[] = {
&meson8b_fixed_pll_dco,
&meson8b_hdmi_pll_dco,
&meson8b_sys_pll_dco,
- &meson8b_abp_clk_sel,
- &meson8b_abp_clk_gate,
+ &meson8b_apb_clk_sel,
+ &meson8b_apb_clk_gate,
&meson8b_periph_clk_sel,
&meson8b_periph_clk_gate,
&meson8b_axi_clk_sel,
@@ -1988,6 +2307,13 @@ static struct clk_regmap *const meson8b_clk_regmaps[] = {
&meson8b_hdmi_sys_sel,
&meson8b_hdmi_sys_div,
&meson8b_hdmi_sys,
+ &meson8b_mali_0_sel,
+ &meson8b_mali_0_div,
+ &meson8b_mali_0,
+ &meson8b_mali_1_sel,
+ &meson8b_mali_1_div,
+ &meson8b_mali_1,
+ &meson8b_mali,
};
static const struct meson8b_clk_reset_line {
@@ -2132,7 +2458,6 @@ static int meson8b_cpu_clk_notifier_cb(struct notifier_block *nb,
static struct meson8b_nb_data meson8b_cpu_nb_data = {
.nb.notifier_call = meson8b_cpu_clk_notifier_cb,
- .onecell_data = &meson8b_hw_onecell_data,
};
static const struct regmap_config clkc_regmap_config = {
@@ -2141,7 +2466,8 @@ static const struct regmap_config clkc_regmap_config = {
.reg_stride = 4,
};
-static void __init meson8b_clkc_init(struct device_node *np)
+static void __init meson8b_clkc_init_common(struct device_node *np,
+ struct clk_hw_onecell_data *clk_hw_onecell_data)
{
struct meson8b_clk_reset *rstc;
const char *notifier_clk_name;
@@ -2192,14 +2518,16 @@ static void __init meson8b_clkc_init(struct device_node *np)
*/
for (i = CLKID_XTAL; i < CLK_NR_CLKS; i++) {
/* array might be sparse */
- if (!meson8b_hw_onecell_data.hws[i])
+ if (!clk_hw_onecell_data->hws[i])
continue;
- ret = clk_hw_register(NULL, meson8b_hw_onecell_data.hws[i]);
+ ret = clk_hw_register(NULL, clk_hw_onecell_data->hws[i]);
if (ret)
return;
}
+ meson8b_cpu_nb_data.onecell_data = clk_hw_onecell_data;
+
/*
* FIXME we shouldn't program the muxes in notifier handlers. The
* tricky programming sequence will be handled by the forthcoming
@@ -2215,13 +2543,23 @@ static void __init meson8b_clkc_init(struct device_node *np)
}
ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get,
- &meson8b_hw_onecell_data);
+ clk_hw_onecell_data);
if (ret)
pr_err("%s: failed to register clock provider\n", __func__);
}
+static void __init meson8_clkc_init(struct device_node *np)
+{
+ return meson8b_clkc_init_common(np, &meson8_hw_onecell_data);
+}
+
+static void __init meson8b_clkc_init(struct device_node *np)
+{
+ return meson8b_clkc_init_common(np, &meson8b_hw_onecell_data);
+}
+
CLK_OF_DECLARE_DRIVER(meson8_clkc, "amlogic,meson8-clkc",
- meson8b_clkc_init);
+ meson8_clkc_init);
CLK_OF_DECLARE_DRIVER(meson8b_clkc, "amlogic,meson8b-clkc",
meson8b_clkc_init);
CLK_OF_DECLARE_DRIVER(meson8m2_clkc, "amlogic,meson8m2-clkc",
diff --git a/drivers/clk/meson/meson8b.h b/drivers/clk/meson/meson8b.h
index 87fba739af81..b8c58faeae52 100644
--- a/drivers/clk/meson/meson8b.h
+++ b/drivers/clk/meson/meson8b.h
@@ -33,6 +33,7 @@
#define HHI_VID_CLK_CNTL2 0x194 /* 0x65 offset in data sheet */
#define HHI_VID_DIVIDER_CNTL 0x198 /* 0x66 offset in data sheet */
#define HHI_SYS_CPU_CLK_CNTL0 0x19c /* 0x67 offset in data sheet */
+#define HHI_MALI_CLK_CNTL 0x1b0 /* 0x6c offset in data sheet */
#define HHI_HDMI_CLK_CNTL 0x1cc /* 0x73 offset in data sheet */
#define HHI_NAND_CLK_CNTL 0x25c /* 0x97 offset in data sheet */
#define HHI_MPLL_CNTL 0x280 /* 0xa0 offset in data sheet */
@@ -91,7 +92,7 @@
#define CLKID_CPU_CLK_DIV6 120
#define CLKID_CPU_CLK_DIV7 121
#define CLKID_CPU_CLK_DIV8 122
-#define CLKID_ABP_SEL 123
+#define CLKID_APB_SEL 123
#define CLKID_PERIPH_SEL 125
#define CLKID_AXI_SEL 127
#define CLKID_L2_DRAM_SEL 129
@@ -139,8 +140,14 @@
#define CLKID_HDMI_SYS_SEL 172
#define CLKID_HDMI_SYS_DIV 173
#define CLKID_HDMI_SYS 174
+#define CLKID_MALI_0_SEL 175
+#define CLKID_MALI_0_DIV 176
+#define CLKID_MALI_0 177
+#define CLKID_MALI_1_SEL 178
+#define CLKID_MALI_1_DIV 179
+#define CLKID_MALI_1 180
-#define CLK_NR_CLKS 175
+#define CLK_NR_CLKS 181
/*
* include the CLKID and RESETID that have
diff --git a/drivers/clk/meson/parm.h b/drivers/clk/meson/parm.h
new file mode 100644
index 000000000000..3c9ef1b505ce
--- /dev/null
+++ b/drivers/clk/meson/parm.h
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2015 Endless Mobile, Inc.
+ * Author: Carlo Caione <carlo@endlessm.com>
+ */
+
+#ifndef __MESON_PARM_H
+#define __MESON_PARM_H
+
+#include <linux/bits.h>
+#include <linux/regmap.h>
+
+#define PMASK(width) GENMASK(width - 1, 0)
+#define SETPMASK(width, shift) GENMASK(shift + width - 1, shift)
+#define CLRPMASK(width, shift) (~SETPMASK(width, shift))
+
+#define PARM_GET(width, shift, reg) \
+ (((reg) & SETPMASK(width, shift)) >> (shift))
+#define PARM_SET(width, shift, reg, val) \
+ (((reg) & CLRPMASK(width, shift)) | ((val) << (shift)))
+
+#define MESON_PARM_APPLICABLE(p) (!!((p)->width))
+
+struct parm {
+ u16 reg_off;
+ u8 shift;
+ u8 width;
+};
+
+static inline unsigned int meson_parm_read(struct regmap *map, struct parm *p)
+{
+ unsigned int val;
+
+ regmap_read(map, p->reg_off, &val);
+ return PARM_GET(p->width, p->shift, val);
+}
+
+static inline void meson_parm_write(struct regmap *map, struct parm *p,
+ unsigned int val)
+{
+ regmap_update_bits(map, p->reg_off, SETPMASK(p->width, p->shift),
+ val << p->shift);
+}
+
+#endif /* __MESON_PARM_H */
+
diff --git a/drivers/clk/meson/sclk-div.c b/drivers/clk/meson/sclk-div.c
index bc64019b8eeb..3acf03780221 100644
--- a/drivers/clk/meson/sclk-div.c
+++ b/drivers/clk/meson/sclk-div.c
@@ -16,7 +16,11 @@
* duty_cycle = (1 + hi) / (1 + val)
*/
-#include "clkc-audio.h"
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+
+#include "clk-regmap.h"
+#include "sclk-div.h"
static inline struct meson_sclk_div_data *
meson_sclk_div_data(struct clk_regmap *clk)
@@ -241,3 +245,7 @@ const struct clk_ops meson_sclk_div_ops = {
.init = sclk_div_init,
};
EXPORT_SYMBOL_GPL(meson_sclk_div_ops);
+
+MODULE_DESCRIPTION("Amlogic Sample divider driver");
+MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/clk/meson/clkc-audio.h b/drivers/clk/meson/sclk-div.h
index 0a7c157ebf81..b64b2a32005f 100644
--- a/drivers/clk/meson/clkc-audio.h
+++ b/drivers/clk/meson/sclk-div.h
@@ -4,16 +4,11 @@
* Author: Jerome Brunet <jbrunet@baylibre.com>
*/
-#ifndef __MESON_CLKC_AUDIO_H
-#define __MESON_CLKC_AUDIO_H
+#ifndef __MESON_SCLK_DIV_H
+#define __MESON_SCLK_DIV_H
-#include "clkc.h"
-
-struct meson_clk_triphase_data {
- struct parm ph0;
- struct parm ph1;
- struct parm ph2;
-};
+#include <linux/clk-provider.h>
+#include "parm.h"
struct meson_sclk_div_data {
struct parm div;
@@ -22,7 +17,6 @@ struct meson_sclk_div_data {
struct clk_duty cached_duty;
};
-extern const struct clk_ops meson_clk_triphase_ops;
extern const struct clk_ops meson_sclk_div_ops;
-#endif /* __MESON_CLKC_AUDIO_H */
+#endif /* __MESON_SCLK_DIV_H */
diff --git a/drivers/clk/meson/vid-pll-div.c b/drivers/clk/meson/vid-pll-div.c
index 88af0e282ea0..08bcc01c0923 100644
--- a/drivers/clk/meson/vid-pll-div.c
+++ b/drivers/clk/meson/vid-pll-div.c
@@ -5,7 +5,10 @@
*/
#include <linux/clk-provider.h>
-#include "clkc.h"
+#include <linux/module.h>
+
+#include "clk-regmap.h"
+#include "vid-pll-div.h"
static inline struct meson_vid_pll_div_data *
meson_vid_pll_div_data(struct clk_regmap *clk)
@@ -89,3 +92,8 @@ static unsigned long meson_vid_pll_div_recalc_rate(struct clk_hw *hw,
const struct clk_ops meson_vid_pll_div_ro_ops = {
.recalc_rate = meson_vid_pll_div_recalc_rate,
};
+EXPORT_SYMBOL_GPL(meson_vid_pll_div_ro_ops);
+
+MODULE_DESCRIPTION("Amlogic video pll divider driver");
+MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/clk/meson/vid-pll-div.h b/drivers/clk/meson/vid-pll-div.h
new file mode 100644
index 000000000000..c0128e33ccf9
--- /dev/null
+++ b/drivers/clk/meson/vid-pll-div.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 BayLibre, SAS.
+ * Author: Jerome Brunet <jbrunet@baylibre.com>
+ */
+
+#ifndef __MESON_VID_PLL_DIV_H
+#define __MESON_VID_PLL_DIV_H
+
+#include <linux/clk-provider.h>
+#include "parm.h"
+
+struct meson_vid_pll_div_data {
+ struct parm val;
+ struct parm sel;
+};
+
+extern const struct clk_ops meson_vid_pll_div_ro_ops;
+
+#endif /* __MESON_VID_PLL_DIV_H */
diff --git a/drivers/clk/mmp/clk-of-mmp2.c b/drivers/clk/mmp/clk-of-mmp2.c
index d083b860f083..a60a1be937ad 100644
--- a/drivers/clk/mmp/clk-of-mmp2.c
+++ b/drivers/clk/mmp/clk-of-mmp2.c
@@ -229,9 +229,10 @@ static struct mmp_param_gate_clk apmu_gate_clks[] = {
{MMP2_CLK_SDH1, "sdh1_clk", "sdh_mix_clk", CLK_SET_RATE_PARENT, APMU_SDH1, 0x1b, 0x1b, 0x0, 0, &sdh_lock},
{MMP2_CLK_SDH2, "sdh2_clk", "sdh_mix_clk", CLK_SET_RATE_PARENT, APMU_SDH2, 0x1b, 0x1b, 0x0, 0, &sdh_lock},
{MMP2_CLK_SDH3, "sdh3_clk", "sdh_mix_clk", CLK_SET_RATE_PARENT, APMU_SDH3, 0x1b, 0x1b, 0x0, 0, &sdh_lock},
- {MMP2_CLK_DISP0, "disp0_clk", "disp0_div", CLK_SET_RATE_PARENT, APMU_DISP0, 0x1b, 0x1b, 0x0, 0, &disp0_lock},
+ {MMP2_CLK_DISP0, "disp0_clk", "disp0_div", CLK_SET_RATE_PARENT, APMU_DISP0, 0x09, 0x09, 0x0, 0, &disp0_lock},
+ {MMP2_CLK_DISP0_LCDC, "disp0_lcdc_clk", "disp0_mux", CLK_SET_RATE_PARENT, APMU_DISP0, 0x12, 0x12, 0x0, 0, &disp0_lock},
{MMP2_CLK_DISP0_SPHY, "disp0_sphy_clk", "disp0_sphy_div", CLK_SET_RATE_PARENT, APMU_DISP0, 0x1024, 0x1024, 0x0, 0, &disp0_lock},
- {MMP2_CLK_DISP1, "disp1_clk", "disp1_div", CLK_SET_RATE_PARENT, APMU_DISP1, 0x1b, 0x1b, 0x0, 0, &disp1_lock},
+ {MMP2_CLK_DISP1, "disp1_clk", "disp1_div", CLK_SET_RATE_PARENT, APMU_DISP1, 0x09, 0x09, 0x0, 0, &disp1_lock},
{MMP2_CLK_CCIC_ARBITER, "ccic_arbiter", "vctcxo", CLK_SET_RATE_PARENT, APMU_CCIC0, 0x1800, 0x1800, 0x0, 0, &ccic0_lock},
{MMP2_CLK_CCIC0, "ccic0_clk", "ccic0_mix_clk", CLK_SET_RATE_PARENT, APMU_CCIC0, 0x1b, 0x1b, 0x0, 0, &ccic0_lock},
{MMP2_CLK_CCIC0_PHY, "ccic0_phy_clk", "ccic0_mix_clk", CLK_SET_RATE_PARENT, APMU_CCIC0, 0x24, 0x24, 0x0, 0, &ccic0_lock},
diff --git a/drivers/clk/mvebu/armada-370.c b/drivers/clk/mvebu/armada-370.c
index 7dedfaa6e152..5c6bbee396b3 100644
--- a/drivers/clk/mvebu/armada-370.c
+++ b/drivers/clk/mvebu/armada-370.c
@@ -175,8 +175,10 @@ static void __init a370_clk_init(struct device_node *np)
mvebu_coreclk_setup(np, &a370_coreclks);
- if (cgnp)
+ if (cgnp) {
mvebu_clk_gating_setup(cgnp, a370_gating_desc);
+ of_node_put(cgnp);
+ }
}
CLK_OF_DECLARE(a370_clk, "marvell,armada-370-core-clock", a370_clk_init);
diff --git a/drivers/clk/mvebu/armada-xp.c b/drivers/clk/mvebu/armada-xp.c
index e8f03293ec83..fa1568279c23 100644
--- a/drivers/clk/mvebu/armada-xp.c
+++ b/drivers/clk/mvebu/armada-xp.c
@@ -226,7 +226,9 @@ static void __init axp_clk_init(struct device_node *np)
mvebu_coreclk_setup(np, &axp_coreclks);
- if (cgnp)
+ if (cgnp) {
mvebu_clk_gating_setup(cgnp, axp_gating_desc);
+ of_node_put(cgnp);
+ }
}
CLK_OF_DECLARE(axp_clk, "marvell,armada-xp-core-clock", axp_clk_init);
diff --git a/drivers/clk/mvebu/dove.c b/drivers/clk/mvebu/dove.c
index e0dd99f36bf4..0bd09d33f9cf 100644
--- a/drivers/clk/mvebu/dove.c
+++ b/drivers/clk/mvebu/dove.c
@@ -188,10 +188,14 @@ static void __init dove_clk_init(struct device_node *np)
mvebu_coreclk_setup(np, &dove_coreclks);
- if (ddnp)
+ if (ddnp) {
dove_divider_clk_init(ddnp);
+ of_node_put(ddnp);
+ }
- if (cgnp)
+ if (cgnp) {
mvebu_clk_gating_setup(cgnp, dove_gating_desc);
+ of_node_put(cgnp);
+ }
}
CLK_OF_DECLARE(dove_clk, "marvell,dove-core-clock", dove_clk_init);
diff --git a/drivers/clk/mvebu/kirkwood.c b/drivers/clk/mvebu/kirkwood.c
index 6f784167bda4..35af3aa18f1c 100644
--- a/drivers/clk/mvebu/kirkwood.c
+++ b/drivers/clk/mvebu/kirkwood.c
@@ -331,6 +331,8 @@ static void __init kirkwood_clk_init(struct device_node *np)
if (cgnp) {
mvebu_clk_gating_setup(cgnp, kirkwood_gating_desc);
kirkwood_clk_muxing_setup(cgnp, kirkwood_mux_desc);
+
+ of_node_put(cgnp);
}
}
CLK_OF_DECLARE(kirkwood_clk, "marvell,kirkwood-core-clock",
diff --git a/drivers/clk/mvebu/mv98dx3236.c b/drivers/clk/mvebu/mv98dx3236.c
index 0a74cf7a7725..1c8ab4f834ba 100644
--- a/drivers/clk/mvebu/mv98dx3236.c
+++ b/drivers/clk/mvebu/mv98dx3236.c
@@ -172,7 +172,9 @@ static void __init mv98dx3236_clk_init(struct device_node *np)
mvebu_coreclk_setup(np, &mv98dx3236_core_clocks);
- if (cgnp)
+ if (cgnp) {
mvebu_clk_gating_setup(cgnp, mv98dx3236_gating_desc);
+ of_node_put(cgnp);
+ }
}
CLK_OF_DECLARE(mv98dx3236_clk, "marvell,mv98dx3236-core-clock", mv98dx3236_clk_init);
diff --git a/drivers/clk/qcom/clk-rcg.h b/drivers/clk/qcom/clk-rcg.h
index e5eca8a1abe4..c25b57c3cbc8 100644
--- a/drivers/clk/qcom/clk-rcg.h
+++ b/drivers/clk/qcom/clk-rcg.h
@@ -71,7 +71,6 @@ struct src_sel {
* @freq_tbl: frequency table
* @clkr: regmap clock handle
* @lock: register lock
- *
*/
struct clk_rcg {
u32 ns_reg;
@@ -107,7 +106,6 @@ extern const struct clk_ops clk_rcg_lcc_ops;
* @freq_tbl: frequency table
* @clkr: regmap clock handle
* @lock: register lock
- *
*/
struct clk_dyn_rcg {
u32 ns_reg[2];
@@ -140,7 +138,7 @@ extern const struct clk_ops clk_dyn_rcg_ops;
* @parent_map: map from software's parent index to hardware's src_sel field
* @freq_tbl: frequency table
* @clkr: regmap clock handle
- *
+ * @cfg_off: defines the cfg register offset from the CMD_RCGR + CFG_REG
*/
struct clk_rcg2 {
u32 cmd_rcgr;
@@ -150,6 +148,7 @@ struct clk_rcg2 {
const struct parent_map *parent_map;
const struct freq_tbl *freq_tbl;
struct clk_regmap clkr;
+ u8 cfg_off;
};
#define to_clk_rcg2(_hw) container_of(to_clk_regmap(_hw), struct clk_rcg2, clkr)
diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c
index 6e3bd195d012..8c02bffe50df 100644
--- a/drivers/clk/qcom/clk-rcg2.c
+++ b/drivers/clk/qcom/clk-rcg2.c
@@ -41,6 +41,11 @@
#define N_REG 0xc
#define D_REG 0x10
+#define RCG_CFG_OFFSET(rcg) ((rcg)->cmd_rcgr + (rcg)->cfg_off + CFG_REG)
+#define RCG_M_OFFSET(rcg) ((rcg)->cmd_rcgr + (rcg)->cfg_off + M_REG)
+#define RCG_N_OFFSET(rcg) ((rcg)->cmd_rcgr + (rcg)->cfg_off + N_REG)
+#define RCG_D_OFFSET(rcg) ((rcg)->cmd_rcgr + (rcg)->cfg_off + D_REG)
+
/* Dynamic Frequency Scaling */
#define MAX_PERF_LEVEL 8
#define SE_CMD_DFSR_OFFSET 0x14
@@ -74,7 +79,7 @@ static u8 clk_rcg2_get_parent(struct clk_hw *hw)
u32 cfg;
int i, ret;
- ret = regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, &cfg);
+ ret = regmap_read(rcg->clkr.regmap, RCG_CFG_OFFSET(rcg), &cfg);
if (ret)
goto err;
@@ -123,7 +128,7 @@ static int clk_rcg2_set_parent(struct clk_hw *hw, u8 index)
int ret;
u32 cfg = rcg->parent_map[index].cfg << CFG_SRC_SEL_SHIFT;
- ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG,
+ ret = regmap_update_bits(rcg->clkr.regmap, RCG_CFG_OFFSET(rcg),
CFG_SRC_SEL_MASK, cfg);
if (ret)
return ret;
@@ -162,13 +167,13 @@ clk_rcg2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
u32 cfg, hid_div, m = 0, n = 0, mode = 0, mask;
- regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, &cfg);
+ regmap_read(rcg->clkr.regmap, RCG_CFG_OFFSET(rcg), &cfg);
if (rcg->mnd_width) {
mask = BIT(rcg->mnd_width) - 1;
- regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + M_REG, &m);
+ regmap_read(rcg->clkr.regmap, RCG_M_OFFSET(rcg), &m);
m &= mask;
- regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + N_REG, &n);
+ regmap_read(rcg->clkr.regmap, RCG_N_OFFSET(rcg), &n);
n = ~n;
n &= mask;
n += m;
@@ -263,17 +268,17 @@ static int __clk_rcg2_configure(struct clk_rcg2 *rcg, const struct freq_tbl *f)
if (rcg->mnd_width && f->n) {
mask = BIT(rcg->mnd_width) - 1;
ret = regmap_update_bits(rcg->clkr.regmap,
- rcg->cmd_rcgr + M_REG, mask, f->m);
+ RCG_M_OFFSET(rcg), mask, f->m);
if (ret)
return ret;
ret = regmap_update_bits(rcg->clkr.regmap,
- rcg->cmd_rcgr + N_REG, mask, ~(f->n - f->m));
+ RCG_N_OFFSET(rcg), mask, ~(f->n - f->m));
if (ret)
return ret;
ret = regmap_update_bits(rcg->clkr.regmap,
- rcg->cmd_rcgr + D_REG, mask, ~f->n);
+ RCG_D_OFFSET(rcg), mask, ~f->n);
if (ret)
return ret;
}
@@ -284,8 +289,7 @@ static int __clk_rcg2_configure(struct clk_rcg2 *rcg, const struct freq_tbl *f)
cfg |= rcg->parent_map[index].cfg << CFG_SRC_SEL_SHIFT;
if (rcg->mnd_width && f->n && (f->m != f->n))
cfg |= CFG_MODE_DUAL_EDGE;
-
- return regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG,
+ return regmap_update_bits(rcg->clkr.regmap, RCG_CFG_OFFSET(rcg),
mask, cfg);
}
diff --git a/drivers/clk/qcom/clk-rpmh.c b/drivers/clk/qcom/clk-rpmh.c
index 9f4fc7773fb2..c3fd632af119 100644
--- a/drivers/clk/qcom/clk-rpmh.c
+++ b/drivers/clk/qcom/clk-rpmh.c
@@ -18,6 +18,31 @@
#define CLK_RPMH_ARC_EN_OFFSET 0
#define CLK_RPMH_VRM_EN_OFFSET 4
+#define BCM_TCS_CMD_COMMIT_MASK 0x40000000
+#define BCM_TCS_CMD_VALID_SHIFT 29
+#define BCM_TCS_CMD_VOTE_MASK 0x3fff
+#define BCM_TCS_CMD_VOTE_SHIFT 0
+
+#define BCM_TCS_CMD(valid, vote) \
+ (BCM_TCS_CMD_COMMIT_MASK | \
+ ((valid) << BCM_TCS_CMD_VALID_SHIFT) | \
+ ((vote & BCM_TCS_CMD_VOTE_MASK) \
+ << BCM_TCS_CMD_VOTE_SHIFT))
+
+/**
+ * struct bcm_db - Auxiliary data pertaining to each Bus Clock Manager(BCM)
+ * @unit: divisor used to convert Hz value to an RPMh msg
+ * @width: multiplier used to convert Hz value to an RPMh msg
+ * @vcd: virtual clock domain that this bcm belongs to
+ * @reserved: reserved to pad the struct
+ */
+struct bcm_db {
+ __le32 unit;
+ __le16 width;
+ u8 vcd;
+ u8 reserved;
+};
+
/**
* struct clk_rpmh - individual rpmh clock data structure
* @hw: handle between common and hardware-specific interfaces
@@ -29,6 +54,7 @@
* @aggr_state: rpmh clock aggregated state
* @last_sent_aggr_state: rpmh clock last aggr state sent to RPMh
* @valid_state_mask: mask to determine the state of the rpmh clock
+ * @unit: divisor to convert rate to rpmh msg in magnitudes of Khz
* @dev: device to which it is attached
* @peer: pointer to the clock rpmh sibling
*/
@@ -42,6 +68,7 @@ struct clk_rpmh {
u32 aggr_state;
u32 last_sent_aggr_state;
u32 valid_state_mask;
+ u32 unit;
struct device *dev;
struct clk_rpmh *peer;
};
@@ -98,6 +125,17 @@ static DEFINE_MUTEX(rpmh_clk_lock);
__DEFINE_CLK_RPMH(_platform, _name, _name_active, _res_name, \
CLK_RPMH_VRM_EN_OFFSET, 1, _div)
+#define DEFINE_CLK_RPMH_BCM(_platform, _name, _res_name) \
+ static struct clk_rpmh _platform##_##_name = { \
+ .res_name = _res_name, \
+ .valid_state_mask = BIT(RPMH_ACTIVE_ONLY_STATE), \
+ .div = 1, \
+ .hw.init = &(struct clk_init_data){ \
+ .ops = &clk_rpmh_bcm_ops, \
+ .name = #_name, \
+ }, \
+ }
+
static inline struct clk_rpmh *to_clk_rpmh(struct clk_hw *_hw)
{
return container_of(_hw, struct clk_rpmh, hw);
@@ -210,6 +248,96 @@ static const struct clk_ops clk_rpmh_ops = {
.recalc_rate = clk_rpmh_recalc_rate,
};
+static int clk_rpmh_bcm_send_cmd(struct clk_rpmh *c, bool enable)
+{
+ struct tcs_cmd cmd = { 0 };
+ u32 cmd_state;
+ int ret;
+
+ mutex_lock(&rpmh_clk_lock);
+
+ cmd_state = 0;
+ if (enable) {
+ cmd_state = 1;
+ if (c->aggr_state)
+ cmd_state = c->aggr_state;
+ }
+
+ if (c->last_sent_aggr_state == cmd_state) {
+ mutex_unlock(&rpmh_clk_lock);
+ return 0;
+ }
+
+ cmd.addr = c->res_addr;
+ cmd.data = BCM_TCS_CMD(enable, cmd_state);
+
+ ret = rpmh_write_async(c->dev, RPMH_ACTIVE_ONLY_STATE, &cmd, 1);
+ if (ret) {
+ dev_err(c->dev, "set active state of %s failed: (%d)\n",
+ c->res_name, ret);
+ mutex_unlock(&rpmh_clk_lock);
+ return ret;
+ }
+
+ c->last_sent_aggr_state = cmd_state;
+
+ mutex_unlock(&rpmh_clk_lock);
+
+ return 0;
+}
+
+static int clk_rpmh_bcm_prepare(struct clk_hw *hw)
+{
+ struct clk_rpmh *c = to_clk_rpmh(hw);
+
+ return clk_rpmh_bcm_send_cmd(c, true);
+};
+
+static void clk_rpmh_bcm_unprepare(struct clk_hw *hw)
+{
+ struct clk_rpmh *c = to_clk_rpmh(hw);
+
+ clk_rpmh_bcm_send_cmd(c, false);
+};
+
+static int clk_rpmh_bcm_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_rpmh *c = to_clk_rpmh(hw);
+
+ c->aggr_state = rate / c->unit;
+ /*
+ * Since any non-zero value sent to hw would result in enabling the
+ * clock, only send the value if the clock has already been prepared.
+ */
+ if (clk_hw_is_prepared(hw))
+ clk_rpmh_bcm_send_cmd(c, true);
+
+ return 0;
+};
+
+static long clk_rpmh_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ return rate;
+}
+
+static unsigned long clk_rpmh_bcm_recalc_rate(struct clk_hw *hw,
+ unsigned long prate)
+{
+ struct clk_rpmh *c = to_clk_rpmh(hw);
+
+ return c->aggr_state * c->unit;
+}
+
+static const struct clk_ops clk_rpmh_bcm_ops = {
+ .prepare = clk_rpmh_bcm_prepare,
+ .unprepare = clk_rpmh_bcm_unprepare,
+ .set_rate = clk_rpmh_bcm_set_rate,
+ .round_rate = clk_rpmh_round_rate,
+ .recalc_rate = clk_rpmh_bcm_recalc_rate,
+};
+
/* Resource name must match resource id present in cmd-db. */
DEFINE_CLK_RPMH_ARC(sdm845, bi_tcxo, bi_tcxo_ao, "xo.lvl", 0x3, 2);
DEFINE_CLK_RPMH_VRM(sdm845, ln_bb_clk2, ln_bb_clk2_ao, "lnbclka2", 2);
@@ -217,6 +345,7 @@ DEFINE_CLK_RPMH_VRM(sdm845, ln_bb_clk3, ln_bb_clk3_ao, "lnbclka3", 2);
DEFINE_CLK_RPMH_VRM(sdm845, rf_clk1, rf_clk1_ao, "rfclka1", 1);
DEFINE_CLK_RPMH_VRM(sdm845, rf_clk2, rf_clk2_ao, "rfclka2", 1);
DEFINE_CLK_RPMH_VRM(sdm845, rf_clk3, rf_clk3_ao, "rfclka3", 1);
+DEFINE_CLK_RPMH_BCM(sdm845, ipa, "IP0");
static struct clk_hw *sdm845_rpmh_clocks[] = {
[RPMH_CXO_CLK] = &sdm845_bi_tcxo.hw,
@@ -231,6 +360,7 @@ static struct clk_hw *sdm845_rpmh_clocks[] = {
[RPMH_RF_CLK2_A] = &sdm845_rf_clk2_ao.hw,
[RPMH_RF_CLK3] = &sdm845_rf_clk3.hw,
[RPMH_RF_CLK3_A] = &sdm845_rf_clk3_ao.hw,
+ [RPMH_IPA_CLK] = &sdm845_ipa.hw,
};
static const struct clk_rpmh_desc clk_rpmh_sdm845 = {
@@ -267,6 +397,8 @@ static int clk_rpmh_probe(struct platform_device *pdev)
for (i = 0; i < desc->num_clks; i++) {
u32 res_addr;
+ size_t aux_data_len;
+ const struct bcm_db *data;
rpmh_clk = to_clk_rpmh(hw_clks[i]);
res_addr = cmd_db_read_addr(rpmh_clk->res_name);
@@ -275,6 +407,20 @@ static int clk_rpmh_probe(struct platform_device *pdev)
rpmh_clk->res_name);
return -ENODEV;
}
+
+ data = cmd_db_read_aux_data(rpmh_clk->res_name, &aux_data_len);
+ if (IS_ERR(data)) {
+ ret = PTR_ERR(data);
+ dev_err(&pdev->dev,
+ "error reading RPMh aux data for %s (%d)\n",
+ rpmh_clk->res_name, ret);
+ return ret;
+ }
+
+ /* Convert unit from Khz to Hz */
+ if (aux_data_len == sizeof(*data))
+ rpmh_clk->unit = le32_to_cpu(data->unit) * 1000ULL;
+
rpmh_clk->res_addr += res_addr;
rpmh_clk->dev = &pdev->dev;
diff --git a/drivers/clk/qcom/clk-smd-rpm.c b/drivers/clk/qcom/clk-smd-rpm.c
index d3aadaeb2903..22dd42ad9223 100644
--- a/drivers/clk/qcom/clk-smd-rpm.c
+++ b/drivers/clk/qcom/clk-smd-rpm.c
@@ -655,10 +655,73 @@ static const struct rpm_smd_clk_desc rpm_clk_qcs404 = {
.num_clks = ARRAY_SIZE(qcs404_clks),
};
+/* msm8998 */
+DEFINE_CLK_SMD_RPM(msm8998, snoc_clk, snoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 1);
+DEFINE_CLK_SMD_RPM(msm8998, cnoc_clk, cnoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 2);
+DEFINE_CLK_SMD_RPM(msm8998, ce1_clk, ce1_a_clk, QCOM_SMD_RPM_CE_CLK, 0);
+DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8998, div_clk1, div_clk1_a, 0xb);
+DEFINE_CLK_SMD_RPM(msm8998, ipa_clk, ipa_a_clk, QCOM_SMD_RPM_IPA_CLK, 0);
+DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8998, ln_bb_clk1, ln_bb_clk1_a, 1);
+DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8998, ln_bb_clk2, ln_bb_clk2_a, 2);
+DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8998, ln_bb_clk3_pin, ln_bb_clk3_a_pin,
+ 3);
+DEFINE_CLK_SMD_RPM(msm8998, mmssnoc_axi_rpm_clk, mmssnoc_axi_rpm_a_clk,
+ QCOM_SMD_RPM_MMAXI_CLK, 0);
+DEFINE_CLK_SMD_RPM(msm8998, aggre1_noc_clk, aggre1_noc_a_clk,
+ QCOM_SMD_RPM_AGGR_CLK, 1);
+DEFINE_CLK_SMD_RPM(msm8998, aggre2_noc_clk, aggre2_noc_a_clk,
+ QCOM_SMD_RPM_AGGR_CLK, 2);
+DEFINE_CLK_SMD_RPM_QDSS(msm8998, qdss_clk, qdss_a_clk,
+ QCOM_SMD_RPM_MISC_CLK, 1);
+DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8998, rf_clk1, rf_clk1_a, 4);
+DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8998, rf_clk2_pin, rf_clk2_a_pin, 5);
+DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8998, rf_clk3, rf_clk3_a, 6);
+DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8998, rf_clk3_pin, rf_clk3_a_pin, 6);
+static struct clk_smd_rpm *msm8998_clks[] = {
+ [RPM_SMD_SNOC_CLK] = &msm8998_snoc_clk,
+ [RPM_SMD_SNOC_A_CLK] = &msm8998_snoc_a_clk,
+ [RPM_SMD_CNOC_CLK] = &msm8998_cnoc_clk,
+ [RPM_SMD_CNOC_A_CLK] = &msm8998_cnoc_a_clk,
+ [RPM_SMD_CE1_CLK] = &msm8998_ce1_clk,
+ [RPM_SMD_CE1_A_CLK] = &msm8998_ce1_a_clk,
+ [RPM_SMD_DIV_CLK1] = &msm8998_div_clk1,
+ [RPM_SMD_DIV_A_CLK1] = &msm8998_div_clk1_a,
+ [RPM_SMD_IPA_CLK] = &msm8998_ipa_clk,
+ [RPM_SMD_IPA_A_CLK] = &msm8998_ipa_a_clk,
+ [RPM_SMD_LN_BB_CLK1] = &msm8998_ln_bb_clk1,
+ [RPM_SMD_LN_BB_CLK1_A] = &msm8998_ln_bb_clk1_a,
+ [RPM_SMD_LN_BB_CLK2] = &msm8998_ln_bb_clk2,
+ [RPM_SMD_LN_BB_CLK2_A] = &msm8998_ln_bb_clk2_a,
+ [RPM_SMD_LN_BB_CLK3_PIN] = &msm8998_ln_bb_clk3_pin,
+ [RPM_SMD_LN_BB_CLK3_A_PIN] = &msm8998_ln_bb_clk3_a_pin,
+ [RPM_SMD_MMAXI_CLK] = &msm8998_mmssnoc_axi_rpm_clk,
+ [RPM_SMD_MMAXI_A_CLK] = &msm8998_mmssnoc_axi_rpm_a_clk,
+ [RPM_SMD_AGGR1_NOC_CLK] = &msm8998_aggre1_noc_clk,
+ [RPM_SMD_AGGR1_NOC_A_CLK] = &msm8998_aggre1_noc_a_clk,
+ [RPM_SMD_AGGR2_NOC_CLK] = &msm8998_aggre2_noc_clk,
+ [RPM_SMD_AGGR2_NOC_A_CLK] = &msm8998_aggre2_noc_a_clk,
+ [RPM_SMD_QDSS_CLK] = &msm8998_qdss_clk,
+ [RPM_SMD_QDSS_A_CLK] = &msm8998_qdss_a_clk,
+ [RPM_SMD_RF_CLK1] = &msm8998_rf_clk1,
+ [RPM_SMD_RF_CLK1_A] = &msm8998_rf_clk1_a,
+ [RPM_SMD_RF_CLK2_PIN] = &msm8998_rf_clk2_pin,
+ [RPM_SMD_RF_CLK2_A_PIN] = &msm8998_rf_clk2_a_pin,
+ [RPM_SMD_RF_CLK3] = &msm8998_rf_clk3,
+ [RPM_SMD_RF_CLK3_A] = &msm8998_rf_clk3_a,
+ [RPM_SMD_RF_CLK3_PIN] = &msm8998_rf_clk3_pin,
+ [RPM_SMD_RF_CLK3_A_PIN] = &msm8998_rf_clk3_a_pin,
+};
+
+static const struct rpm_smd_clk_desc rpm_clk_msm8998 = {
+ .clks = msm8998_clks,
+ .num_clks = ARRAY_SIZE(msm8998_clks),
+};
+
static const struct of_device_id rpm_smd_clk_match_table[] = {
{ .compatible = "qcom,rpmcc-msm8916", .data = &rpm_clk_msm8916 },
{ .compatible = "qcom,rpmcc-msm8974", .data = &rpm_clk_msm8974 },
{ .compatible = "qcom,rpmcc-msm8996", .data = &rpm_clk_msm8996 },
+ { .compatible = "qcom,rpmcc-msm8998", .data = &rpm_clk_msm8998 },
{ .compatible = "qcom,rpmcc-qcs404", .data = &rpm_clk_qcs404 },
{ }
};
diff --git a/drivers/clk/qcom/common.c b/drivers/clk/qcom/common.c
index 0a48ed56833b..a6b2f86112d8 100644
--- a/drivers/clk/qcom/common.c
+++ b/drivers/clk/qcom/common.c
@@ -231,6 +231,8 @@ int qcom_cc_really_probe(struct platform_device *pdev,
struct gdsc_desc *scd;
size_t num_clks = desc->num_clks;
struct clk_regmap **rclks = desc->clks;
+ size_t num_clk_hws = desc->num_clk_hws;
+ struct clk_hw **clk_hws = desc->clk_hws;
cc = devm_kzalloc(dev, sizeof(*cc), GFP_KERNEL);
if (!cc)
@@ -269,6 +271,12 @@ int qcom_cc_really_probe(struct platform_device *pdev,
qcom_cc_drop_protected(dev, cc);
+ for (i = 0; i < num_clk_hws; i++) {
+ ret = devm_clk_hw_register(dev, clk_hws[i]);
+ if (ret)
+ return ret;
+ }
+
for (i = 0; i < num_clks; i++) {
if (!rclks[i])
continue;
diff --git a/drivers/clk/qcom/common.h b/drivers/clk/qcom/common.h
index 4aa33ee70bae..1e2a8bdac55a 100644
--- a/drivers/clk/qcom/common.h
+++ b/drivers/clk/qcom/common.h
@@ -27,6 +27,8 @@ struct qcom_cc_desc {
size_t num_resets;
struct gdsc **gdscs;
size_t num_gdscs;
+ struct clk_hw **clk_hws;
+ size_t num_clk_hws;
};
/**
diff --git a/drivers/clk/qcom/gcc-ipq8074.c b/drivers/clk/qcom/gcc-ipq8074.c
index 505c6263141d..0e32892b438c 100644
--- a/drivers/clk/qcom/gcc-ipq8074.c
+++ b/drivers/clk/qcom/gcc-ipq8074.c
@@ -4715,18 +4715,12 @@ static const struct qcom_cc_desc gcc_ipq8074_desc = {
.num_clks = ARRAY_SIZE(gcc_ipq8074_clks),
.resets = gcc_ipq8074_resets,
.num_resets = ARRAY_SIZE(gcc_ipq8074_resets),
+ .clk_hws = gcc_ipq8074_hws,
+ .num_clk_hws = ARRAY_SIZE(gcc_ipq8074_hws),
};
static int gcc_ipq8074_probe(struct platform_device *pdev)
{
- int ret, i;
-
- for (i = 0; i < ARRAY_SIZE(gcc_ipq8074_hws); i++) {
- ret = devm_clk_hw_register(&pdev->dev, gcc_ipq8074_hws[i]);
- if (ret)
- return ret;
- }
-
return qcom_cc_probe(pdev, &gcc_ipq8074_desc);
}
diff --git a/drivers/clk/qcom/gcc-mdm9615.c b/drivers/clk/qcom/gcc-mdm9615.c
index 849046fbed6d..8c6d93144b9c 100644
--- a/drivers/clk/qcom/gcc-mdm9615.c
+++ b/drivers/clk/qcom/gcc-mdm9615.c
@@ -1702,6 +1702,8 @@ static const struct qcom_cc_desc gcc_mdm9615_desc = {
.num_clks = ARRAY_SIZE(gcc_mdm9615_clks),
.resets = gcc_mdm9615_resets,
.num_resets = ARRAY_SIZE(gcc_mdm9615_resets),
+ .clk_hws = gcc_mdm9615_hws,
+ .num_clk_hws = ARRAY_SIZE(gcc_mdm9615_hws),
};
static const struct of_device_id gcc_mdm9615_match_table[] = {
@@ -1712,21 +1714,12 @@ MODULE_DEVICE_TABLE(of, gcc_mdm9615_match_table);
static int gcc_mdm9615_probe(struct platform_device *pdev)
{
- struct device *dev = &pdev->dev;
struct regmap *regmap;
- int ret;
- int i;
regmap = qcom_cc_map(pdev, &gcc_mdm9615_desc);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
- for (i = 0; i < ARRAY_SIZE(gcc_mdm9615_hws); i++) {
- ret = devm_clk_hw_register(dev, gcc_mdm9615_hws[i]);
- if (ret)
- return ret;
- }
-
return qcom_cc_really_probe(pdev, &gcc_mdm9615_desc, regmap);
}
diff --git a/drivers/clk/qcom/gcc-msm8996.c b/drivers/clk/qcom/gcc-msm8996.c
index 9d136172c27c..4632b9272b7f 100644
--- a/drivers/clk/qcom/gcc-msm8996.c
+++ b/drivers/clk/qcom/gcc-msm8996.c
@@ -3656,6 +3656,8 @@ static const struct qcom_cc_desc gcc_msm8996_desc = {
.num_resets = ARRAY_SIZE(gcc_msm8996_resets),
.gdscs = gcc_msm8996_gdscs,
.num_gdscs = ARRAY_SIZE(gcc_msm8996_gdscs),
+ .clk_hws = gcc_msm8996_hws,
+ .num_clk_hws = ARRAY_SIZE(gcc_msm8996_hws),
};
static const struct of_device_id gcc_msm8996_match_table[] = {
@@ -3666,8 +3668,6 @@ MODULE_DEVICE_TABLE(of, gcc_msm8996_match_table);
static int gcc_msm8996_probe(struct platform_device *pdev)
{
- struct device *dev = &pdev->dev;
- int i, ret;
struct regmap *regmap;
regmap = qcom_cc_map(pdev, &gcc_msm8996_desc);
@@ -3680,12 +3680,6 @@ static int gcc_msm8996_probe(struct platform_device *pdev)
*/
regmap_update_bits(regmap, 0x52008, BIT(21), BIT(21));
- for (i = 0; i < ARRAY_SIZE(gcc_msm8996_hws); i++) {
- ret = devm_clk_hw_register(dev, gcc_msm8996_hws[i]);
- if (ret)
- return ret;
- }
-
return qcom_cc_really_probe(pdev, &gcc_msm8996_desc, regmap);
}
diff --git a/drivers/clk/qcom/gcc-msm8998.c b/drivers/clk/qcom/gcc-msm8998.c
index 1b779396e04f..c240fba794c7 100644
--- a/drivers/clk/qcom/gcc-msm8998.c
+++ b/drivers/clk/qcom/gcc-msm8998.c
@@ -1112,6 +1112,7 @@ static struct clk_rcg2 ufs_axi_clk_src = {
static const struct freq_tbl ftbl_usb30_master_clk_src[] = {
F(19200000, P_XO, 1, 0, 0),
+ F(60000000, P_GPLL0_OUT_MAIN, 10, 0, 0),
F(120000000, P_GPLL0_OUT_MAIN, 5, 0, 0),
F(150000000, P_GPLL0_OUT_MAIN, 4, 0, 0),
{ }
@@ -1189,6 +1190,7 @@ static struct clk_branch gcc_aggre1_ufs_axi_clk = {
"ufs_axi_clk_src",
},
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1206,6 +1208,7 @@ static struct clk_branch gcc_aggre1_usb3_axi_clk = {
"usb30_master_clk_src",
},
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1288,6 +1291,7 @@ static struct clk_branch gcc_blsp1_qup1_i2c_apps_clk = {
"blsp1_qup1_i2c_apps_clk_src",
},
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1305,6 +1309,7 @@ static struct clk_branch gcc_blsp1_qup1_spi_apps_clk = {
"blsp1_qup1_spi_apps_clk_src",
},
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1322,6 +1327,7 @@ static struct clk_branch gcc_blsp1_qup2_i2c_apps_clk = {
"blsp1_qup2_i2c_apps_clk_src",
},
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1339,6 +1345,7 @@ static struct clk_branch gcc_blsp1_qup2_spi_apps_clk = {
"blsp1_qup2_spi_apps_clk_src",
},
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1356,6 +1363,7 @@ static struct clk_branch gcc_blsp1_qup3_i2c_apps_clk = {
"blsp1_qup3_i2c_apps_clk_src",
},
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1373,6 +1381,7 @@ static struct clk_branch gcc_blsp1_qup3_spi_apps_clk = {
"blsp1_qup3_spi_apps_clk_src",
},
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1390,6 +1399,7 @@ static struct clk_branch gcc_blsp1_qup4_i2c_apps_clk = {
"blsp1_qup4_i2c_apps_clk_src",
},
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1407,6 +1417,7 @@ static struct clk_branch gcc_blsp1_qup4_spi_apps_clk = {
"blsp1_qup4_spi_apps_clk_src",
},
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1424,6 +1435,7 @@ static struct clk_branch gcc_blsp1_qup5_i2c_apps_clk = {
"blsp1_qup5_i2c_apps_clk_src",
},
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1441,6 +1453,7 @@ static struct clk_branch gcc_blsp1_qup5_spi_apps_clk = {
"blsp1_qup5_spi_apps_clk_src",
},
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1458,6 +1471,7 @@ static struct clk_branch gcc_blsp1_qup6_i2c_apps_clk = {
"blsp1_qup6_i2c_apps_clk_src",
},
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1475,6 +1489,7 @@ static struct clk_branch gcc_blsp1_qup6_spi_apps_clk = {
"blsp1_qup6_spi_apps_clk_src",
},
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1505,6 +1520,7 @@ static struct clk_branch gcc_blsp1_uart1_apps_clk = {
"blsp1_uart1_apps_clk_src",
},
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1522,6 +1538,7 @@ static struct clk_branch gcc_blsp1_uart2_apps_clk = {
"blsp1_uart2_apps_clk_src",
},
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1539,6 +1556,7 @@ static struct clk_branch gcc_blsp1_uart3_apps_clk = {
"blsp1_uart3_apps_clk_src",
},
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1569,6 +1587,7 @@ static struct clk_branch gcc_blsp2_qup1_i2c_apps_clk = {
"blsp2_qup1_i2c_apps_clk_src",
},
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1586,6 +1605,7 @@ static struct clk_branch gcc_blsp2_qup1_spi_apps_clk = {
"blsp2_qup1_spi_apps_clk_src",
},
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1603,6 +1623,7 @@ static struct clk_branch gcc_blsp2_qup2_i2c_apps_clk = {
"blsp2_qup2_i2c_apps_clk_src",
},
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1620,6 +1641,7 @@ static struct clk_branch gcc_blsp2_qup2_spi_apps_clk = {
"blsp2_qup2_spi_apps_clk_src",
},
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1637,6 +1659,7 @@ static struct clk_branch gcc_blsp2_qup3_i2c_apps_clk = {
"blsp2_qup3_i2c_apps_clk_src",
},
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1654,6 +1677,7 @@ static struct clk_branch gcc_blsp2_qup3_spi_apps_clk = {
"blsp2_qup3_spi_apps_clk_src",
},
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1671,6 +1695,7 @@ static struct clk_branch gcc_blsp2_qup4_i2c_apps_clk = {
"blsp2_qup4_i2c_apps_clk_src",
},
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1688,6 +1713,7 @@ static struct clk_branch gcc_blsp2_qup4_spi_apps_clk = {
"blsp2_qup4_spi_apps_clk_src",
},
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1705,6 +1731,7 @@ static struct clk_branch gcc_blsp2_qup5_i2c_apps_clk = {
"blsp2_qup5_i2c_apps_clk_src",
},
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1722,6 +1749,7 @@ static struct clk_branch gcc_blsp2_qup5_spi_apps_clk = {
"blsp2_qup5_spi_apps_clk_src",
},
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1739,6 +1767,7 @@ static struct clk_branch gcc_blsp2_qup6_i2c_apps_clk = {
"blsp2_qup6_i2c_apps_clk_src",
},
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1756,6 +1785,7 @@ static struct clk_branch gcc_blsp2_qup6_spi_apps_clk = {
"blsp2_qup6_spi_apps_clk_src",
},
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1786,6 +1816,7 @@ static struct clk_branch gcc_blsp2_uart1_apps_clk = {
"blsp2_uart1_apps_clk_src",
},
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1803,6 +1834,7 @@ static struct clk_branch gcc_blsp2_uart2_apps_clk = {
"blsp2_uart2_apps_clk_src",
},
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1820,6 +1852,7 @@ static struct clk_branch gcc_blsp2_uart3_apps_clk = {
"blsp2_uart3_apps_clk_src",
},
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1837,6 +1870,7 @@ static struct clk_branch gcc_cfg_noc_usb3_axi_clk = {
"usb30_master_clk_src",
},
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1854,6 +1888,7 @@ static struct clk_branch gcc_gp1_clk = {
"gp1_clk_src",
},
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1871,6 +1906,7 @@ static struct clk_branch gcc_gp2_clk = {
"gp2_clk_src",
},
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1888,6 +1924,7 @@ static struct clk_branch gcc_gp3_clk = {
"gp3_clk_src",
},
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1957,6 +1994,7 @@ static struct clk_branch gcc_hmss_ahb_clk = {
"hmss_ahb_clk_src",
},
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1987,6 +2025,7 @@ static struct clk_branch gcc_hmss_rbcpr_clk = {
"hmss_rbcpr_clk_src",
},
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -2088,6 +2127,7 @@ static struct clk_branch gcc_pcie_0_aux_clk = {
"pcie_aux_clk_src",
},
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -2157,6 +2197,7 @@ static struct clk_branch gcc_pcie_phy_aux_clk = {
"pcie_aux_clk_src",
},
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -2174,6 +2215,7 @@ static struct clk_branch gcc_pdm2_clk = {
"pdm2_clk_src",
},
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -2243,6 +2285,7 @@ static struct clk_branch gcc_sdcc2_apps_clk = {
"sdcc2_apps_clk_src",
},
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -2273,6 +2316,7 @@ static struct clk_branch gcc_sdcc4_apps_clk = {
"sdcc4_apps_clk_src",
},
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -2316,6 +2360,7 @@ static struct clk_branch gcc_tsif_ref_clk = {
"tsif_ref_clk_src",
},
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -2346,6 +2391,7 @@ static struct clk_branch gcc_ufs_axi_clk = {
"ufs_axi_clk_src",
},
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -2441,6 +2487,7 @@ static struct clk_branch gcc_usb30_master_clk = {
"usb30_master_clk_src",
},
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -2458,6 +2505,7 @@ static struct clk_branch gcc_usb30_mock_utmi_clk = {
"usb30_mock_utmi_clk_src",
},
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -2488,6 +2536,7 @@ static struct clk_branch gcc_usb3_phy_aux_clk = {
"usb3_phy_aux_clk_src",
},
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -2495,7 +2544,7 @@ static struct clk_branch gcc_usb3_phy_aux_clk = {
static struct clk_branch gcc_usb3_phy_pipe_clk = {
.halt_reg = 0x50004,
- .halt_check = BRANCH_HALT,
+ .halt_check = BRANCH_HALT_SKIP,
.clkr = {
.enable_reg = 0x50004,
.enable_mask = BIT(0),
@@ -2910,6 +2959,10 @@ static const struct regmap_config gcc_msm8998_regmap_config = {
.fast_io = true,
};
+static struct clk_hw *gcc_msm8998_hws[] = {
+ &xo.hw,
+};
+
static const struct qcom_cc_desc gcc_msm8998_desc = {
.config = &gcc_msm8998_regmap_config,
.clks = gcc_msm8998_clocks,
@@ -2918,6 +2971,8 @@ static const struct qcom_cc_desc gcc_msm8998_desc = {
.num_resets = ARRAY_SIZE(gcc_msm8998_resets),
.gdscs = gcc_msm8998_gdscs,
.num_gdscs = ARRAY_SIZE(gcc_msm8998_gdscs),
+ .clk_hws = gcc_msm8998_hws,
+ .num_clk_hws = ARRAY_SIZE(gcc_msm8998_hws),
};
static int gcc_msm8998_probe(struct platform_device *pdev)
@@ -2937,10 +2992,6 @@ static int gcc_msm8998_probe(struct platform_device *pdev)
if (ret)
return ret;
- ret = devm_clk_hw_register(&pdev->dev, &xo.hw);
- if (ret)
- return ret;
-
return qcom_cc_really_probe(pdev, &gcc_msm8998_desc, regmap);
}
diff --git a/drivers/clk/qcom/gcc-qcs404.c b/drivers/clk/qcom/gcc-qcs404.c
index 64da032bb9ed..5a62f64ada93 100644
--- a/drivers/clk/qcom/gcc-qcs404.c
+++ b/drivers/clk/qcom/gcc-qcs404.c
@@ -678,6 +678,7 @@ static struct clk_rcg2 blsp1_uart3_apps_clk_src = {
.cmd_rcgr = 0x4014,
.mnd_width = 16,
.hid_width = 5,
+ .cfg_off = 0x20,
.parent_map = gcc_parent_map_0,
.freq_tbl = ftbl_blsp1_uart0_apps_clk_src,
.clkr.hw.init = &(struct clk_init_data){
@@ -2692,6 +2693,8 @@ static const struct qcom_cc_desc gcc_qcs404_desc = {
.num_clks = ARRAY_SIZE(gcc_qcs404_clocks),
.resets = gcc_qcs404_resets,
.num_resets = ARRAY_SIZE(gcc_qcs404_resets),
+ .clk_hws = gcc_qcs404_hws,
+ .num_clk_hws = ARRAY_SIZE(gcc_qcs404_hws),
};
static const struct of_device_id gcc_qcs404_match_table[] = {
@@ -2703,7 +2706,6 @@ MODULE_DEVICE_TABLE(of, gcc_qcs404_match_table);
static int gcc_qcs404_probe(struct platform_device *pdev)
{
struct regmap *regmap;
- int ret, i;
regmap = qcom_cc_map(pdev, &gcc_qcs404_desc);
if (IS_ERR(regmap))
@@ -2711,12 +2713,6 @@ static int gcc_qcs404_probe(struct platform_device *pdev)
clk_alpha_pll_configure(&gpll3_out_main, regmap, &gpll3_config);
- for (i = 0; i < ARRAY_SIZE(gcc_qcs404_hws); i++) {
- ret = devm_clk_hw_register(&pdev->dev, gcc_qcs404_hws[i]);
- if (ret)
- return ret;
- }
-
return qcom_cc_really_probe(pdev, &gcc_qcs404_desc, regmap);
}
diff --git a/drivers/clk/qcom/gcc-sdm660.c b/drivers/clk/qcom/gcc-sdm660.c
index ba239ea4c842..8827db23066f 100644
--- a/drivers/clk/qcom/gcc-sdm660.c
+++ b/drivers/clk/qcom/gcc-sdm660.c
@@ -2420,6 +2420,8 @@ static const struct qcom_cc_desc gcc_sdm660_desc = {
.num_resets = ARRAY_SIZE(gcc_sdm660_resets),
.gdscs = gcc_sdm660_gdscs,
.num_gdscs = ARRAY_SIZE(gcc_sdm660_gdscs),
+ .clk_hws = gcc_sdm660_hws,
+ .num_clk_hws = ARRAY_SIZE(gcc_sdm660_hws),
};
static const struct of_device_id gcc_sdm660_match_table[] = {
@@ -2431,7 +2433,7 @@ MODULE_DEVICE_TABLE(of, gcc_sdm660_match_table);
static int gcc_sdm660_probe(struct platform_device *pdev)
{
- int i, ret;
+ int ret;
struct regmap *regmap;
regmap = qcom_cc_map(pdev, &gcc_sdm660_desc);
@@ -2446,13 +2448,6 @@ static int gcc_sdm660_probe(struct platform_device *pdev)
if (ret)
return ret;
- /* Register the hws */
- for (i = 0; i < ARRAY_SIZE(gcc_sdm660_hws); i++) {
- ret = devm_clk_hw_register(&pdev->dev, gcc_sdm660_hws[i]);
- if (ret)
- return ret;
- }
-
return qcom_cc_really_probe(pdev, &gcc_sdm660_desc, regmap);
}
diff --git a/drivers/clk/qcom/gcc-sdm845.c b/drivers/clk/qcom/gcc-sdm845.c
index 58fa5c247af1..7131dcf9b060 100644
--- a/drivers/clk/qcom/gcc-sdm845.c
+++ b/drivers/clk/qcom/gcc-sdm845.c
@@ -1703,6 +1703,9 @@ static struct clk_branch gcc_pcie_0_pipe_clk = {
.enable_mask = BIT(4),
.hw.init = &(struct clk_init_data){
.name = "gcc_pcie_0_pipe_clk",
+ .parent_names = (const char *[]){ "pcie_0_pipe_clk" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1802,6 +1805,8 @@ static struct clk_branch gcc_pcie_1_pipe_clk = {
.enable_mask = BIT(30),
.hw.init = &(struct clk_init_data){
.name = "gcc_pcie_1_pipe_clk",
+ .parent_names = (const char *[]){ "pcie_1_pipe_clk" },
+ .num_parents = 1,
.ops = &clk_branch2_ops,
},
},
diff --git a/drivers/clk/qcom/mmcc-msm8996.c b/drivers/clk/qcom/mmcc-msm8996.c
index 7d4ee109435c..7235510eac94 100644
--- a/drivers/clk/qcom/mmcc-msm8996.c
+++ b/drivers/clk/qcom/mmcc-msm8996.c
@@ -3347,6 +3347,8 @@ static const struct qcom_cc_desc mmcc_msm8996_desc = {
.num_resets = ARRAY_SIZE(mmcc_msm8996_resets),
.gdscs = mmcc_msm8996_gdscs,
.num_gdscs = ARRAY_SIZE(mmcc_msm8996_gdscs),
+ .clk_hws = mmcc_msm8996_hws,
+ .num_clk_hws = ARRAY_SIZE(mmcc_msm8996_hws),
};
static const struct of_device_id mmcc_msm8996_match_table[] = {
@@ -3357,8 +3359,6 @@ MODULE_DEVICE_TABLE(of, mmcc_msm8996_match_table);
static int mmcc_msm8996_probe(struct platform_device *pdev)
{
- struct device *dev = &pdev->dev;
- int i, ret;
struct regmap *regmap;
regmap = qcom_cc_map(pdev, &mmcc_msm8996_desc);
@@ -3370,12 +3370,6 @@ static int mmcc_msm8996_probe(struct platform_device *pdev)
/* Disable the NoC FSM for mmss_mmagic_cfg_ahb_clk */
regmap_update_bits(regmap, 0x5054, BIT(15), 0);
- for (i = 0; i < ARRAY_SIZE(mmcc_msm8996_hws); i++) {
- ret = devm_clk_hw_register(dev, mmcc_msm8996_hws[i]);
- if (ret)
- return ret;
- }
-
return qcom_cc_really_probe(pdev, &mmcc_msm8996_desc, regmap);
}
diff --git a/drivers/clk/renesas/r8a774a1-cpg-mssr.c b/drivers/clk/renesas/r8a774a1-cpg-mssr.c
index 10e852518870..4d92b27a6153 100644
--- a/drivers/clk/renesas/r8a774a1-cpg-mssr.c
+++ b/drivers/clk/renesas/r8a774a1-cpg-mssr.c
@@ -21,7 +21,7 @@
enum clk_ids {
/* Core Clock Outputs exported to DT */
- LAST_DT_CORE_CLK = R8A774A1_CLK_OSC,
+ LAST_DT_CORE_CLK = R8A774A1_CLK_CANFD,
/* External Input Clocks */
CLK_EXTAL,
@@ -102,6 +102,7 @@ static const struct cpg_core_clk r8a774a1_core_clks[] __initconst = {
DEF_FIXED("cp", R8A774A1_CLK_CP, CLK_EXTAL, 2, 1),
DEF_FIXED("cpex", R8A774A1_CLK_CPEX, CLK_EXTAL, 2, 1),
+ DEF_DIV6P1("canfd", R8A774A1_CLK_CANFD, CLK_PLL1_DIV4, 0x244),
DEF_DIV6P1("csi0", R8A774A1_CLK_CSI0, CLK_PLL1_DIV4, 0x00c),
DEF_DIV6P1("mso", R8A774A1_CLK_MSO, CLK_PLL1_DIV4, 0x014),
DEF_DIV6P1("hdmi", R8A774A1_CLK_HDMI, CLK_PLL1_DIV4, 0x250),
@@ -191,6 +192,7 @@ static const struct mssr_mod_clk r8a774a1_mod_clks[] __initconst = {
DEF_MOD("gpio2", 910, R8A774A1_CLK_S3D4),
DEF_MOD("gpio1", 911, R8A774A1_CLK_S3D4),
DEF_MOD("gpio0", 912, R8A774A1_CLK_S3D4),
+ DEF_MOD("can-fd", 914, R8A774A1_CLK_S3D2),
DEF_MOD("can-if1", 915, R8A774A1_CLK_S3D4),
DEF_MOD("can-if0", 916, R8A774A1_CLK_S3D4),
DEF_MOD("i2c6", 918, R8A774A1_CLK_S0D6),
diff --git a/drivers/clk/renesas/r8a774c0-cpg-mssr.c b/drivers/clk/renesas/r8a774c0-cpg-mssr.c
index 10b96895d452..34e274f2a273 100644
--- a/drivers/clk/renesas/r8a774c0-cpg-mssr.c
+++ b/drivers/clk/renesas/r8a774c0-cpg-mssr.c
@@ -22,7 +22,7 @@
enum clk_ids {
/* Core Clock Outputs exported to DT */
- LAST_DT_CORE_CLK = R8A774C0_CLK_CPEX,
+ LAST_DT_CORE_CLK = R8A774C0_CLK_CANFD,
/* External Input Clocks */
CLK_EXTAL,
@@ -33,6 +33,7 @@ enum clk_ids {
CLK_PLL1,
CLK_PLL3,
CLK_PLL0D4,
+ CLK_PLL0D6,
CLK_PLL0D8,
CLK_PLL0D20,
CLK_PLL0D24,
@@ -61,6 +62,7 @@ static const struct cpg_core_clk r8a774c0_core_clks[] __initconst = {
DEF_FIXED(".pll0", CLK_PLL0, CLK_MAIN, 1, 100),
DEF_FIXED(".pll0d4", CLK_PLL0D4, CLK_PLL0, 4, 1),
+ DEF_FIXED(".pll0d6", CLK_PLL0D6, CLK_PLL0, 6, 1),
DEF_FIXED(".pll0d8", CLK_PLL0D8, CLK_PLL0, 8, 1),
DEF_FIXED(".pll0d20", CLK_PLL0D20, CLK_PLL0, 20, 1),
DEF_FIXED(".pll0d24", CLK_PLL0D24, CLK_PLL0, 24, 1),
@@ -112,6 +114,7 @@ static const struct cpg_core_clk r8a774c0_core_clks[] __initconst = {
DEF_GEN3_PE("s3d2c", R8A774C0_CLK_S3D2C, CLK_S3, 2, CLK_PE, 2),
DEF_GEN3_PE("s3d4c", R8A774C0_CLK_S3D4C, CLK_S3, 4, CLK_PE, 4),
+ DEF_DIV6P1("canfd", R8A774C0_CLK_CANFD, CLK_PLL0D6, 0x244),
DEF_DIV6P1("csi0", R8A774C0_CLK_CSI0, CLK_PLL1D2, 0x00c),
DEF_DIV6P1("mso", R8A774C0_CLK_MSO, CLK_PLL1D2, 0x014),
@@ -119,6 +122,11 @@ static const struct cpg_core_clk r8a774c0_core_clks[] __initconst = {
};
static const struct mssr_mod_clk r8a774c0_mod_clks[] __initconst = {
+ DEF_MOD("tmu4", 121, R8A774C0_CLK_S0D6C),
+ DEF_MOD("tmu3", 122, R8A774C0_CLK_S3D2C),
+ DEF_MOD("tmu2", 123, R8A774C0_CLK_S3D2C),
+ DEF_MOD("tmu1", 124, R8A774C0_CLK_S3D2C),
+ DEF_MOD("tmu0", 125, R8A774C0_CLK_CP),
DEF_MOD("scif5", 202, R8A774C0_CLK_S3D4C),
DEF_MOD("scif4", 203, R8A774C0_CLK_S3D4C),
DEF_MOD("scif3", 204, R8A774C0_CLK_S3D4C),
@@ -172,8 +180,8 @@ static const struct mssr_mod_clk r8a774c0_mod_clks[] __initconst = {
DEF_MOD("ehci0", 703, R8A774C0_CLK_S3D4),
DEF_MOD("hsusb", 704, R8A774C0_CLK_S3D4),
DEF_MOD("csi40", 716, R8A774C0_CLK_CSI0),
- DEF_MOD("du1", 723, R8A774C0_CLK_S2D1),
- DEF_MOD("du0", 724, R8A774C0_CLK_S2D1),
+ DEF_MOD("du1", 723, R8A774C0_CLK_S1D1),
+ DEF_MOD("du0", 724, R8A774C0_CLK_S1D1),
DEF_MOD("lvds", 727, R8A774C0_CLK_S2D1),
DEF_MOD("vin5", 806, R8A774C0_CLK_S1D2),
@@ -187,6 +195,7 @@ static const struct mssr_mod_clk r8a774c0_mod_clks[] __initconst = {
DEF_MOD("gpio2", 910, R8A774C0_CLK_S3D4),
DEF_MOD("gpio1", 911, R8A774C0_CLK_S3D4),
DEF_MOD("gpio0", 912, R8A774C0_CLK_S3D4),
+ DEF_MOD("can-fd", 914, R8A774C0_CLK_S3D2),
DEF_MOD("can-if1", 915, R8A774C0_CLK_S3D4),
DEF_MOD("can-if0", 916, R8A774C0_CLK_S3D4),
DEF_MOD("i2c6", 918, R8A774C0_CLK_S3D2),
diff --git a/drivers/clk/renesas/r8a77980-cpg-mssr.c b/drivers/clk/renesas/r8a77980-cpg-mssr.c
index 25a3083b6764..f9e07fcc0d96 100644
--- a/drivers/clk/renesas/r8a77980-cpg-mssr.c
+++ b/drivers/clk/renesas/r8a77980-cpg-mssr.c
@@ -41,6 +41,7 @@ enum clk_ids {
CLK_S2,
CLK_S3,
CLK_SDSRC,
+ CLK_RPCSRC,
CLK_OCO,
/* Module Clocks */
@@ -65,8 +66,14 @@ static const struct cpg_core_clk r8a77980_core_clks[] __initconst = {
DEF_FIXED(".s2", CLK_S2, CLK_PLL1_DIV2, 4, 1),
DEF_FIXED(".s3", CLK_S3, CLK_PLL1_DIV2, 6, 1),
DEF_FIXED(".sdsrc", CLK_SDSRC, CLK_PLL1_DIV2, 2, 1),
+ DEF_BASE(".rpcsrc", CLK_RPCSRC, CLK_TYPE_GEN3_RPCSRC, CLK_PLL1),
DEF_RATE(".oco", CLK_OCO, 32768),
+ DEF_BASE("rpc", R8A77980_CLK_RPC, CLK_TYPE_GEN3_RPC,
+ CLK_RPCSRC),
+ DEF_BASE("rpcd2", R8A77980_CLK_RPCD2, CLK_TYPE_GEN3_RPCD2,
+ R8A77980_CLK_RPC),
+
/* Core Clock Outputs */
DEF_FIXED("ztr", R8A77980_CLK_ZTR, CLK_PLL1_DIV2, 6, 1),
DEF_FIXED("ztrd2", R8A77980_CLK_ZTRD2, CLK_PLL1_DIV2, 12, 1),
@@ -164,6 +171,7 @@ static const struct mssr_mod_clk r8a77980_mod_clks[] __initconst = {
DEF_MOD("gpio1", 911, R8A77980_CLK_CP),
DEF_MOD("gpio0", 912, R8A77980_CLK_CP),
DEF_MOD("can-fd", 914, R8A77980_CLK_S3D2),
+ DEF_MOD("rpc-if", 917, R8A77980_CLK_RPC),
DEF_MOD("i2c4", 927, R8A77980_CLK_S0D6),
DEF_MOD("i2c3", 928, R8A77980_CLK_S0D6),
DEF_MOD("i2c2", 929, R8A77980_CLK_S3D2),
diff --git a/drivers/clk/renesas/rcar-gen3-cpg.c b/drivers/clk/renesas/rcar-gen3-cpg.c
index be2ccbd6d623..9a8071a8114d 100644
--- a/drivers/clk/renesas/rcar-gen3-cpg.c
+++ b/drivers/clk/renesas/rcar-gen3-cpg.c
@@ -30,6 +30,21 @@
#define CPG_RCKCR_CKSEL BIT(15) /* RCLK Clock Source Select */
+static spinlock_t cpg_lock;
+
+static void cpg_reg_modify(void __iomem *reg, u32 clear, u32 set)
+{
+ unsigned long flags;
+ u32 val;
+
+ spin_lock_irqsave(&cpg_lock, flags);
+ val = readl(reg);
+ val &= ~clear;
+ val |= set;
+ writel(val, reg);
+ spin_unlock_irqrestore(&cpg_lock, flags);
+};
+
struct cpg_simple_notifier {
struct notifier_block nb;
void __iomem *reg;
@@ -118,7 +133,6 @@ static int cpg_z_clk_set_rate(struct clk_hw *hw, unsigned long rate,
struct cpg_z_clk *zclk = to_z_clk(hw);
unsigned int mult;
unsigned int i;
- u32 val, kick;
/* Factor of 2 is for fixed divider */
mult = DIV_ROUND_CLOSEST_ULL(rate * 32ULL * 2, parent_rate);
@@ -127,17 +141,14 @@ static int cpg_z_clk_set_rate(struct clk_hw *hw, unsigned long rate,
if (readl(zclk->kick_reg) & CPG_FRQCRB_KICK)
return -EBUSY;
- val = readl(zclk->reg) & ~zclk->mask;
- val |= ((32 - mult) << __ffs(zclk->mask)) & zclk->mask;
- writel(val, zclk->reg);
+ cpg_reg_modify(zclk->reg, zclk->mask,
+ ((32 - mult) << __ffs(zclk->mask)) & zclk->mask);
/*
* Set KICK bit in FRQCRB to update hardware setting and wait for
* clock change completion.
*/
- kick = readl(zclk->kick_reg);
- kick |= CPG_FRQCRB_KICK;
- writel(kick, zclk->kick_reg);
+ cpg_reg_modify(zclk->kick_reg, 0, CPG_FRQCRB_KICK);
/*
* Note: There is no HW information about the worst case latency.
@@ -266,12 +277,10 @@ static const struct sd_div_table cpg_sd_div_table[] = {
static int cpg_sd_clock_enable(struct clk_hw *hw)
{
struct sd_clock *clock = to_sd_clock(hw);
- u32 val = readl(clock->csn.reg);
-
- val &= ~(CPG_SD_STP_MASK);
- val |= clock->div_table[clock->cur_div_idx].val & CPG_SD_STP_MASK;
- writel(val, clock->csn.reg);
+ cpg_reg_modify(clock->csn.reg, CPG_SD_STP_MASK,
+ clock->div_table[clock->cur_div_idx].val &
+ CPG_SD_STP_MASK);
return 0;
}
@@ -280,7 +289,7 @@ static void cpg_sd_clock_disable(struct clk_hw *hw)
{
struct sd_clock *clock = to_sd_clock(hw);
- writel(readl(clock->csn.reg) | CPG_SD_STP_MASK, clock->csn.reg);
+ cpg_reg_modify(clock->csn.reg, 0, CPG_SD_STP_MASK);
}
static int cpg_sd_clock_is_enabled(struct clk_hw *hw)
@@ -327,7 +336,6 @@ static int cpg_sd_clock_set_rate(struct clk_hw *hw, unsigned long rate,
{
struct sd_clock *clock = to_sd_clock(hw);
unsigned int div = cpg_sd_clock_calc_div(clock, rate, parent_rate);
- u32 val;
unsigned int i;
for (i = 0; i < clock->div_num; i++)
@@ -339,10 +347,9 @@ static int cpg_sd_clock_set_rate(struct clk_hw *hw, unsigned long rate,
clock->cur_div_idx = i;
- val = readl(clock->csn.reg);
- val &= ~(CPG_SD_STP_MASK | CPG_SD_FC_MASK);
- val |= clock->div_table[i].val & (CPG_SD_STP_MASK | CPG_SD_FC_MASK);
- writel(val, clock->csn.reg);
+ cpg_reg_modify(clock->csn.reg, CPG_SD_STP_MASK | CPG_SD_FC_MASK,
+ clock->div_table[i].val &
+ (CPG_SD_STP_MASK | CPG_SD_FC_MASK));
return 0;
}
@@ -415,6 +422,92 @@ free_clock:
return clk;
}
+struct rpc_clock {
+ struct clk_divider div;
+ struct clk_gate gate;
+ /*
+ * One notifier covers both RPC and RPCD2 clocks as they are both
+ * controlled by the same RPCCKCR register...
+ */
+ struct cpg_simple_notifier csn;
+};
+
+static const struct clk_div_table cpg_rpcsrc_div_table[] = {
+ { 2, 5 }, { 3, 6 }, { 0, 0 },
+};
+
+static const struct clk_div_table cpg_rpc_div_table[] = {
+ { 1, 2 }, { 3, 4 }, { 5, 6 }, { 7, 8 }, { 0, 0 },
+};
+
+static struct clk * __init cpg_rpc_clk_register(const char *name,
+ void __iomem *base, const char *parent_name,
+ struct raw_notifier_head *notifiers)
+{
+ struct rpc_clock *rpc;
+ struct clk *clk;
+
+ rpc = kzalloc(sizeof(*rpc), GFP_KERNEL);
+ if (!rpc)
+ return ERR_PTR(-ENOMEM);
+
+ rpc->div.reg = base + CPG_RPCCKCR;
+ rpc->div.width = 3;
+ rpc->div.table = cpg_rpc_div_table;
+ rpc->div.lock = &cpg_lock;
+
+ rpc->gate.reg = base + CPG_RPCCKCR;
+ rpc->gate.bit_idx = 8;
+ rpc->gate.flags = CLK_GATE_SET_TO_DISABLE;
+ rpc->gate.lock = &cpg_lock;
+
+ rpc->csn.reg = base + CPG_RPCCKCR;
+
+ clk = clk_register_composite(NULL, name, &parent_name, 1, NULL, NULL,
+ &rpc->div.hw, &clk_divider_ops,
+ &rpc->gate.hw, &clk_gate_ops, 0);
+ if (IS_ERR(clk)) {
+ kfree(rpc);
+ return clk;
+ }
+
+ cpg_simple_notifier_register(notifiers, &rpc->csn);
+ return clk;
+}
+
+struct rpcd2_clock {
+ struct clk_fixed_factor fixed;
+ struct clk_gate gate;
+};
+
+static struct clk * __init cpg_rpcd2_clk_register(const char *name,
+ void __iomem *base,
+ const char *parent_name)
+{
+ struct rpcd2_clock *rpcd2;
+ struct clk *clk;
+
+ rpcd2 = kzalloc(sizeof(*rpcd2), GFP_KERNEL);
+ if (!rpcd2)
+ return ERR_PTR(-ENOMEM);
+
+ rpcd2->fixed.mult = 1;
+ rpcd2->fixed.div = 2;
+
+ rpcd2->gate.reg = base + CPG_RPCCKCR;
+ rpcd2->gate.bit_idx = 9;
+ rpcd2->gate.flags = CLK_GATE_SET_TO_DISABLE;
+ rpcd2->gate.lock = &cpg_lock;
+
+ clk = clk_register_composite(NULL, name, &parent_name, 1, NULL, NULL,
+ &rpcd2->fixed.hw, &clk_fixed_factor_ops,
+ &rpcd2->gate.hw, &clk_gate_ops, 0);
+ if (IS_ERR(clk))
+ kfree(rpcd2);
+
+ return clk;
+}
+
static const struct rcar_gen3_cpg_pll_config *cpg_pll_config __initdata;
static unsigned int cpg_clk_extalr __initdata;
@@ -593,6 +686,21 @@ struct clk * __init rcar_gen3_cpg_clk_register(struct device *dev,
}
break;
+ case CLK_TYPE_GEN3_RPCSRC:
+ return clk_register_divider_table(NULL, core->name,
+ __clk_get_name(parent), 0,
+ base + CPG_RPCCKCR, 3, 2, 0,
+ cpg_rpcsrc_div_table,
+ &cpg_lock);
+
+ case CLK_TYPE_GEN3_RPC:
+ return cpg_rpc_clk_register(core->name, base,
+ __clk_get_name(parent), notifiers);
+
+ case CLK_TYPE_GEN3_RPCD2:
+ return cpg_rpcd2_clk_register(core->name, base,
+ __clk_get_name(parent));
+
default:
return ERR_PTR(-EINVAL);
}
@@ -613,5 +721,8 @@ int __init rcar_gen3_cpg_init(const struct rcar_gen3_cpg_pll_config *config,
if (attr)
cpg_quirks = (uintptr_t)attr->data;
pr_debug("%s: mode = 0x%x quirks = 0x%x\n", __func__, mode, cpg_quirks);
+
+ spin_lock_init(&cpg_lock);
+
return 0;
}
diff --git a/drivers/clk/renesas/rcar-gen3-cpg.h b/drivers/clk/renesas/rcar-gen3-cpg.h
index f4fb6cf16688..eac1b057455a 100644
--- a/drivers/clk/renesas/rcar-gen3-cpg.h
+++ b/drivers/clk/renesas/rcar-gen3-cpg.h
@@ -23,6 +23,9 @@ enum rcar_gen3_clk_types {
CLK_TYPE_GEN3_Z2,
CLK_TYPE_GEN3_OSC, /* OSC EXTAL predivider and fixed divider */
CLK_TYPE_GEN3_RCKSEL, /* Select parent/divider using RCKCR.CKSEL */
+ CLK_TYPE_GEN3_RPCSRC,
+ CLK_TYPE_GEN3_RPC,
+ CLK_TYPE_GEN3_RPCD2,
/* SoC specific definitions start here */
CLK_TYPE_GEN3_SOC_BASE,
@@ -57,6 +60,7 @@ struct rcar_gen3_cpg_pll_config {
u8 osc_prediv;
};
+#define CPG_RPCCKCR 0x238
#define CPG_RCKCR 0x240
struct clk *rcar_gen3_cpg_clk_register(struct device *dev,
diff --git a/drivers/clk/rockchip/clk-rk3188.c b/drivers/clk/rockchip/clk-rk3188.c
index 7ea20341e870..5ecf28854876 100644
--- a/drivers/clk/rockchip/clk-rk3188.c
+++ b/drivers/clk/rockchip/clk-rk3188.c
@@ -586,12 +586,12 @@ static struct rockchip_clk_branch rk3066a_clk_branches[] __initdata = {
COMPOSITE(0, "dclk_lcdc0_src", mux_pll_src_cpll_gpll_p, 0,
RK2928_CLKSEL_CON(27), 0, 1, MFLAGS, 8, 8, DFLAGS,
RK2928_CLKGATE_CON(3), 1, GFLAGS),
- MUX(DCLK_LCDC0, "dclk_lcdc0", mux_rk3066_lcdc0_p, 0,
+ MUX(DCLK_LCDC0, "dclk_lcdc0", mux_rk3066_lcdc0_p, CLK_SET_RATE_PARENT,
RK2928_CLKSEL_CON(27), 4, 1, MFLAGS),
COMPOSITE(0, "dclk_lcdc1_src", mux_pll_src_cpll_gpll_p, 0,
RK2928_CLKSEL_CON(28), 0, 1, MFLAGS, 8, 8, DFLAGS,
RK2928_CLKGATE_CON(3), 2, GFLAGS),
- MUX(DCLK_LCDC1, "dclk_lcdc1", mux_rk3066_lcdc1_p, 0,
+ MUX(DCLK_LCDC1, "dclk_lcdc1", mux_rk3066_lcdc1_p, CLK_SET_RATE_PARENT,
RK2928_CLKSEL_CON(28), 4, 1, MFLAGS),
COMPOSITE_NOMUX(0, "cif1_pre", "cif_src", 0,
diff --git a/drivers/clk/rockchip/clk-rk3328.c b/drivers/clk/rockchip/clk-rk3328.c
index faa94adb2a37..65ab5c2f48b0 100644
--- a/drivers/clk/rockchip/clk-rk3328.c
+++ b/drivers/clk/rockchip/clk-rk3328.c
@@ -78,17 +78,17 @@ static struct rockchip_pll_rate_table rk3328_pll_rates[] = {
static struct rockchip_pll_rate_table rk3328_pll_frac_rates[] = {
/* _mhz, _refdiv, _fbdiv, _postdiv1, _postdiv2, _dsmpd, _frac */
- RK3036_PLL_RATE(1016064000, 3, 127, 1, 1, 0, 134217),
+ RK3036_PLL_RATE(1016064000, 3, 127, 1, 1, 0, 134218),
/* vco = 1016064000 */
- RK3036_PLL_RATE(983040000, 24, 983, 1, 1, 0, 671088),
+ RK3036_PLL_RATE(983040000, 24, 983, 1, 1, 0, 671089),
/* vco = 983040000 */
- RK3036_PLL_RATE(491520000, 24, 983, 2, 1, 0, 671088),
+ RK3036_PLL_RATE(491520000, 24, 983, 2, 1, 0, 671089),
/* vco = 983040000 */
- RK3036_PLL_RATE(61440000, 6, 215, 7, 2, 0, 671088),
+ RK3036_PLL_RATE(61440000, 6, 215, 7, 2, 0, 671089),
/* vco = 860156000 */
- RK3036_PLL_RATE(56448000, 12, 451, 4, 4, 0, 9797894),
+ RK3036_PLL_RATE(56448000, 12, 451, 4, 4, 0, 9797895),
/* vco = 903168000 */
- RK3036_PLL_RATE(40960000, 12, 409, 4, 5, 0, 10066329),
+ RK3036_PLL_RATE(40960000, 12, 409, 4, 5, 0, 10066330),
/* vco = 819200000 */
{ /* sentinel */ },
};
diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c
index 59d4d46667ce..54066e6508d3 100644
--- a/drivers/clk/samsung/clk-exynos4.c
+++ b/drivers/clk/samsung/clk-exynos4.c
@@ -1028,6 +1028,7 @@ static unsigned long __init exynos4_get_xom(void)
xom = readl(chipid_base + 8);
iounmap(chipid_base);
+ of_node_put(np);
}
return xom;
diff --git a/drivers/clk/samsung/clk-exynos5-subcmu.c b/drivers/clk/samsung/clk-exynos5-subcmu.c
index 93306283d764..8ae44b5db4c2 100644
--- a/drivers/clk/samsung/clk-exynos5-subcmu.c
+++ b/drivers/clk/samsung/clk-exynos5-subcmu.c
@@ -136,15 +136,20 @@ static int __init exynos5_clk_register_subcmu(struct device *parent,
{
struct of_phandle_args genpdspec = { .np = pd_node };
struct platform_device *pdev;
+ int ret;
+
+ pdev = platform_device_alloc("exynos5-subcmu", PLATFORM_DEVID_AUTO);
+ if (!pdev)
+ return -ENOMEM;
- pdev = platform_device_alloc(info->pd_name, -1);
pdev->dev.parent = parent;
- pdev->driver_override = "exynos5-subcmu";
platform_set_drvdata(pdev, (void *)info);
of_genpd_add_device(&genpdspec, &pdev->dev);
- platform_device_add(pdev);
+ ret = platform_device_add(pdev);
+ if (ret)
+ platform_device_put(pdev);
- return 0;
+ return ret;
}
static int __init exynos5_clk_probe(struct platform_device *pdev)
diff --git a/drivers/clk/samsung/clk-exynos5433.c b/drivers/clk/samsung/clk-exynos5433.c
index 751e2c4fb65b..dae1c96de933 100644
--- a/drivers/clk/samsung/clk-exynos5433.c
+++ b/drivers/clk/samsung/clk-exynos5433.c
@@ -559,7 +559,7 @@ static const struct samsung_gate_clock top_gate_clks[] __initconst = {
/* ENABLE_ACLK_TOP */
GATE(CLK_ACLK_G3D_400, "aclk_g3d_400", "div_aclk_g3d_400",
ENABLE_ACLK_TOP, 30, CLK_IS_CRITICAL, 0),
- GATE(CLK_ACLK_IMEM_SSX_266, "aclk_imem_ssx_266",
+ GATE(CLK_ACLK_IMEM_SSSX_266, "aclk_imem_sssx_266",
"div_aclk_imem_sssx_266", ENABLE_ACLK_TOP,
29, CLK_IGNORE_UNUSED, 0),
GATE(CLK_ACLK_BUS0_400, "aclk_bus0_400", "div_aclk_bus0_400",
@@ -568,10 +568,10 @@ static const struct samsung_gate_clock top_gate_clks[] __initconst = {
GATE(CLK_ACLK_BUS1_400, "aclk_bus1_400", "div_aclk_bus1_400",
ENABLE_ACLK_TOP, 25,
CLK_IS_CRITICAL | CLK_SET_RATE_PARENT, 0),
- GATE(CLK_ACLK_IMEM_200, "aclk_imem_200", "div_aclk_imem_266",
+ GATE(CLK_ACLK_IMEM_200, "aclk_imem_200", "div_aclk_imem_200",
ENABLE_ACLK_TOP, 24,
CLK_IS_CRITICAL | CLK_SET_RATE_PARENT, 0),
- GATE(CLK_ACLK_IMEM_266, "aclk_imem_266", "div_aclk_imem_200",
+ GATE(CLK_ACLK_IMEM_266, "aclk_imem_266", "div_aclk_imem_266",
ENABLE_ACLK_TOP, 23,
CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0),
GATE(CLK_ACLK_PERIC_66, "aclk_peric_66", "div_aclk_peric_66_b",
@@ -5467,6 +5467,35 @@ static const struct samsung_cmu_info cam1_cmu_info __initconst = {
.clk_name = "aclk_cam1_400",
};
+/*
+ * Register offset definitions for CMU_IMEM
+ */
+#define ENABLE_ACLK_IMEM_SLIMSSS 0x080c
+#define ENABLE_PCLK_IMEM_SLIMSSS 0x0908
+
+static const unsigned long imem_clk_regs[] __initconst = {
+ ENABLE_ACLK_IMEM_SLIMSSS,
+ ENABLE_PCLK_IMEM_SLIMSSS,
+};
+
+static const struct samsung_gate_clock imem_gate_clks[] __initconst = {
+ /* ENABLE_ACLK_IMEM_SLIMSSS */
+ GATE(CLK_ACLK_SLIMSSS, "aclk_slimsss", "aclk_imem_sssx_266",
+ ENABLE_ACLK_IMEM_SLIMSSS, 0, CLK_IGNORE_UNUSED, 0),
+
+ /* ENABLE_PCLK_IMEM_SLIMSSS */
+ GATE(CLK_PCLK_SLIMSSS, "pclk_slimsss", "aclk_imem_200",
+ ENABLE_PCLK_IMEM_SLIMSSS, 0, CLK_IGNORE_UNUSED, 0),
+};
+
+static const struct samsung_cmu_info imem_cmu_info __initconst = {
+ .gate_clks = imem_gate_clks,
+ .nr_gate_clks = ARRAY_SIZE(imem_gate_clks),
+ .nr_clk_ids = IMEM_NR_CLK,
+ .clk_regs = imem_clk_regs,
+ .nr_clk_regs = ARRAY_SIZE(imem_clk_regs),
+ .clk_name = "aclk_imem_200",
+};
struct exynos5433_cmu_data {
struct samsung_clk_reg_dump *clk_save;
@@ -5655,6 +5684,9 @@ static const struct of_device_id exynos5433_cmu_of_match[] = {
.compatible = "samsung,exynos5433-cmu-mscl",
.data = &mscl_cmu_info,
}, {
+ .compatible = "samsung,exynos5433-cmu-imem",
+ .data = &imem_cmu_info,
+ }, {
},
};
diff --git a/drivers/clk/samsung/clk-s3c2443.c b/drivers/clk/samsung/clk-s3c2443.c
index 884067e4f1a1..f38f0e24e3b6 100644
--- a/drivers/clk/samsung/clk-s3c2443.c
+++ b/drivers/clk/samsung/clk-s3c2443.c
@@ -389,7 +389,7 @@ void __init s3c2443_common_clk_init(struct device_node *np, unsigned long xti_f,
ARRAY_SIZE(s3c2450_gates));
samsung_clk_register_alias(ctx, s3c2450_aliases,
ARRAY_SIZE(s3c2450_aliases));
- /* fall through, as s3c2450 extends the s3c2416 clocks */
+ /* fall through - as s3c2450 extends the s3c2416 clocks */
case S3C2416:
samsung_clk_register_div(ctx, s3c2416_dividers,
ARRAY_SIZE(s3c2416_dividers));
diff --git a/drivers/clk/samsung/clk.h b/drivers/clk/samsung/clk.h
index c3f309d7100d..9cfaca5fbcdb 100644
--- a/drivers/clk/samsung/clk.h
+++ b/drivers/clk/samsung/clk.h
@@ -26,7 +26,7 @@ struct samsung_clk_provider {
void __iomem *reg_base;
struct device *dev;
spinlock_t lock;
- /* clk_data must be the last entry due to variable lenght 'hws' array */
+ /* clk_data must be the last entry due to variable length 'hws' array */
struct clk_hw_onecell_data clk_data;
};
diff --git a/drivers/clk/socfpga/clk-gate.c b/drivers/clk/socfpga/clk-gate.c
index aa7a6e6a15b6..73e03328d5c5 100644
--- a/drivers/clk/socfpga/clk-gate.c
+++ b/drivers/clk/socfpga/clk-gate.c
@@ -176,8 +176,7 @@ static struct clk_ops gateclk_ops = {
.set_parent = socfpga_clk_set_parent,
};
-static void __init __socfpga_gate_init(struct device_node *node,
- const struct clk_ops *ops)
+void __init socfpga_gate_init(struct device_node *node)
{
u32 clk_gate[2];
u32 div_reg[3];
@@ -188,12 +187,17 @@ static void __init __socfpga_gate_init(struct device_node *node,
const char *clk_name = node->name;
const char *parent_name[SOCFPGA_MAX_PARENTS];
struct clk_init_data init;
+ struct clk_ops *ops;
int rc;
socfpga_clk = kzalloc(sizeof(*socfpga_clk), GFP_KERNEL);
if (WARN_ON(!socfpga_clk))
return;
+ ops = kmemdup(&gateclk_ops, sizeof(gateclk_ops), GFP_KERNEL);
+ if (WARN_ON(!ops))
+ return;
+
rc = of_property_read_u32_array(node, "clk-gate", clk_gate, 2);
if (rc)
clk_gate[0] = 0;
@@ -202,8 +206,8 @@ static void __init __socfpga_gate_init(struct device_node *node,
socfpga_clk->hw.reg = clk_mgr_base_addr + clk_gate[0];
socfpga_clk->hw.bit_idx = clk_gate[1];
- gateclk_ops.enable = clk_gate_ops.enable;
- gateclk_ops.disable = clk_gate_ops.disable;
+ ops->enable = clk_gate_ops.enable;
+ ops->disable = clk_gate_ops.disable;
}
rc = of_property_read_u32(node, "fixed-divider", &fixed_div);
@@ -234,6 +238,11 @@ static void __init __socfpga_gate_init(struct device_node *node,
init.flags = 0;
init.num_parents = of_clk_parent_fill(node, parent_name, SOCFPGA_MAX_PARENTS);
+ if (init.num_parents < 2) {
+ ops->get_parent = NULL;
+ ops->set_parent = NULL;
+ }
+
init.parent_names = parent_name;
socfpga_clk->hw.hw.init = &init;
@@ -246,8 +255,3 @@ static void __init __socfpga_gate_init(struct device_node *node,
if (WARN_ON(rc))
return;
}
-
-void __init socfpga_gate_init(struct device_node *node)
-{
- __socfpga_gate_init(node, &gateclk_ops);
-}
diff --git a/drivers/clk/socfpga/clk-pll-a10.c b/drivers/clk/socfpga/clk-pll-a10.c
index 35fabe1a32c3..269467e8e07e 100644
--- a/drivers/clk/socfpga/clk-pll-a10.c
+++ b/drivers/clk/socfpga/clk-pll-a10.c
@@ -95,6 +95,7 @@ static struct clk * __init __socfpga_pll_init(struct device_node *node,
clkmgr_np = of_find_compatible_node(NULL, NULL, "altr,clk-mgr");
clk_mgr_a10_base_addr = of_iomap(clkmgr_np, 0);
+ of_node_put(clkmgr_np);
BUG_ON(!clk_mgr_a10_base_addr);
pll_clk->hw.reg = clk_mgr_a10_base_addr + reg;
diff --git a/drivers/clk/socfpga/clk-pll.c b/drivers/clk/socfpga/clk-pll.c
index c7f463172e4b..b4b44e9b5901 100644
--- a/drivers/clk/socfpga/clk-pll.c
+++ b/drivers/clk/socfpga/clk-pll.c
@@ -100,6 +100,7 @@ static __init struct clk *__socfpga_pll_init(struct device_node *node,
clkmgr_np = of_find_compatible_node(NULL, NULL, "altr,clk-mgr");
clk_mgr_base_addr = of_iomap(clkmgr_np, 0);
+ of_node_put(clkmgr_np);
BUG_ON(!clk_mgr_base_addr);
pll_clk->hw.reg = clk_mgr_base_addr + reg;
diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-a23.c b/drivers/clk/sunxi-ng/ccu-sun8i-a23.c
index a4fa2945f230..4b5f8f4e4ab8 100644
--- a/drivers/clk/sunxi-ng/ccu-sun8i-a23.c
+++ b/drivers/clk/sunxi-ng/ccu-sun8i-a23.c
@@ -144,7 +144,7 @@ static SUNXI_CCU_NKM_WITH_GATE_LOCK(pll_mipi_clk, "pll-mipi",
8, 4, /* N */
4, 2, /* K */
0, 4, /* M */
- BIT(31), /* gate */
+ BIT(31) | BIT(23) | BIT(22), /* gate */
BIT(28), /* lock */
CLK_SET_RATE_UNGATE);
diff --git a/drivers/clk/tegra/clk-dfll.c b/drivers/clk/tegra/clk-dfll.c
index 0400e5b1d627..1fc71baae13b 100644
--- a/drivers/clk/tegra/clk-dfll.c
+++ b/drivers/clk/tegra/clk-dfll.c
@@ -1293,8 +1293,8 @@ static int attr_enable_set(void *data, u64 val)
return val ? dfll_enable(td) : dfll_disable(td);
}
-DEFINE_SIMPLE_ATTRIBUTE(enable_fops, attr_enable_get, attr_enable_set,
- "%llu\n");
+DEFINE_DEBUGFS_ATTRIBUTE(enable_fops, attr_enable_get, attr_enable_set,
+ "%llu\n");
static int attr_lock_get(void *data, u64 *val)
{
@@ -1310,8 +1310,7 @@ static int attr_lock_set(void *data, u64 val)
return val ? dfll_lock(td) : dfll_unlock(td);
}
-DEFINE_SIMPLE_ATTRIBUTE(lock_fops, attr_lock_get, attr_lock_set,
- "%llu\n");
+DEFINE_DEBUGFS_ATTRIBUTE(lock_fops, attr_lock_get, attr_lock_set, "%llu\n");
static int attr_rate_get(void *data, u64 *val)
{
@@ -1328,7 +1327,7 @@ static int attr_rate_set(void *data, u64 val)
return dfll_request_rate(td, val);
}
-DEFINE_SIMPLE_ATTRIBUTE(rate_fops, attr_rate_get, attr_rate_set, "%llu\n");
+DEFINE_DEBUGFS_ATTRIBUTE(rate_fops, attr_rate_get, attr_rate_set, "%llu\n");
static int attr_registers_show(struct seq_file *s, void *data)
{
@@ -1379,10 +1378,11 @@ static void dfll_debug_init(struct tegra_dfll *td)
root = debugfs_create_dir("tegra_dfll_fcpu", NULL);
td->debugfs_dir = root;
- debugfs_create_file("enable", S_IRUGO | S_IWUSR, root, td, &enable_fops);
- debugfs_create_file("lock", S_IRUGO, root, td, &lock_fops);
- debugfs_create_file("rate", S_IRUGO, root, td, &rate_fops);
- debugfs_create_file("registers", S_IRUGO, root, td, &attr_registers_fops);
+ debugfs_create_file_unsafe("enable", 0644, root, td,
+ &enable_fops);
+ debugfs_create_file_unsafe("lock", 0444, root, td, &lock_fops);
+ debugfs_create_file_unsafe("rate", 0444, root, td, &rate_fops);
+ debugfs_create_file("registers", 0444, root, td, &attr_registers_fops);
}
#else
diff --git a/drivers/clk/ti/adpll.c b/drivers/clk/ti/adpll.c
index 688e403333b9..0c210984765a 100644
--- a/drivers/clk/ti/adpll.c
+++ b/drivers/clk/ti/adpll.c
@@ -614,7 +614,7 @@ static int ti_adpll_init_clkout(struct ti_adpll_data *d,
init.name = child_name;
init.ops = ops;
- init.flags = CLK_IS_BASIC;
+ init.flags = 0;
co->hw.init = &init;
parent_names[0] = __clk_get_name(clk0);
parent_names[1] = __clk_get_name(clk1);
diff --git a/drivers/clk/ti/apll.c b/drivers/clk/ti/apll.c
index 222f68bc3f2a..015a657d3382 100644
--- a/drivers/clk/ti/apll.c
+++ b/drivers/clk/ti/apll.c
@@ -165,7 +165,7 @@ static void __init omap_clk_register_apll(void *user,
ad->clk_bypass = __clk_get_hw(clk);
- clk = ti_clk_register(NULL, &clk_hw->hw, node->name);
+ clk = ti_clk_register_omap_hw(NULL, &clk_hw->hw, node->name);
if (!IS_ERR(clk)) {
of_clk_add_provider(node, of_clk_src_simple_get, clk);
kfree(clk_hw->hw.init->parent_names);
@@ -402,7 +402,7 @@ static void __init of_omap2_apll_setup(struct device_node *node)
if (ret)
goto cleanup;
- clk = clk_register(NULL, &clk_hw->hw);
+ clk = ti_clk_register_omap_hw(NULL, &clk_hw->hw, node->name);
if (!IS_ERR(clk)) {
of_clk_add_provider(node, of_clk_src_simple_get, clk);
kfree(init);
diff --git a/drivers/clk/ti/autoidle.c b/drivers/clk/ti/autoidle.c
index 7bb9afbe4058..1cae226759dd 100644
--- a/drivers/clk/ti/autoidle.c
+++ b/drivers/clk/ti/autoidle.c
@@ -35,7 +35,44 @@ struct clk_ti_autoidle {
#define AUTOIDLE_LOW 0x1
static LIST_HEAD(autoidle_clks);
-static LIST_HEAD(clk_hw_omap_clocks);
+
+/*
+ * we have some non-atomic read/write
+ * operations behind it, so lets
+ * take one lock for handling autoidle
+ * of all clocks
+ */
+static DEFINE_SPINLOCK(autoidle_spinlock);
+
+static int _omap2_clk_deny_idle(struct clk_hw_omap *clk)
+{
+ if (clk->ops && clk->ops->deny_idle) {
+ unsigned long irqflags;
+
+ spin_lock_irqsave(&autoidle_spinlock, irqflags);
+ clk->autoidle_count++;
+ if (clk->autoidle_count == 1)
+ clk->ops->deny_idle(clk);
+
+ spin_unlock_irqrestore(&autoidle_spinlock, irqflags);
+ }
+ return 0;
+}
+
+static int _omap2_clk_allow_idle(struct clk_hw_omap *clk)
+{
+ if (clk->ops && clk->ops->allow_idle) {
+ unsigned long irqflags;
+
+ spin_lock_irqsave(&autoidle_spinlock, irqflags);
+ clk->autoidle_count--;
+ if (clk->autoidle_count == 0)
+ clk->ops->allow_idle(clk);
+
+ spin_unlock_irqrestore(&autoidle_spinlock, irqflags);
+ }
+ return 0;
+}
/**
* omap2_clk_deny_idle - disable autoidle on an OMAP clock
@@ -45,12 +82,15 @@ static LIST_HEAD(clk_hw_omap_clocks);
*/
int omap2_clk_deny_idle(struct clk *clk)
{
- struct clk_hw_omap *c;
+ struct clk_hw *hw = __clk_get_hw(clk);
- c = to_clk_hw_omap(__clk_get_hw(clk));
- if (c->ops && c->ops->deny_idle)
- c->ops->deny_idle(c);
- return 0;
+ if (omap2_clk_is_hw_omap(hw)) {
+ struct clk_hw_omap *c = to_clk_hw_omap(hw);
+
+ return _omap2_clk_deny_idle(c);
+ }
+
+ return -EINVAL;
}
/**
@@ -61,12 +101,15 @@ int omap2_clk_deny_idle(struct clk *clk)
*/
int omap2_clk_allow_idle(struct clk *clk)
{
- struct clk_hw_omap *c;
+ struct clk_hw *hw = __clk_get_hw(clk);
- c = to_clk_hw_omap(__clk_get_hw(clk));
- if (c->ops && c->ops->allow_idle)
- c->ops->allow_idle(c);
- return 0;
+ if (omap2_clk_is_hw_omap(hw)) {
+ struct clk_hw_omap *c = to_clk_hw_omap(hw);
+
+ return _omap2_clk_allow_idle(c);
+ }
+
+ return -EINVAL;
}
static void _allow_autoidle(struct clk_ti_autoidle *clk)
@@ -168,26 +211,6 @@ int __init of_ti_clk_autoidle_setup(struct device_node *node)
}
/**
- * omap2_init_clk_hw_omap_clocks - initialize an OMAP clock
- * @hw: struct clk_hw * to initialize
- *
- * Add an OMAP clock @clk to the internal list of OMAP clocks. Used
- * temporarily for autoidle handling, until this support can be
- * integrated into the common clock framework code in some way. No
- * return value.
- */
-void omap2_init_clk_hw_omap_clocks(struct clk_hw *hw)
-{
- struct clk_hw_omap *c;
-
- if (clk_hw_get_flags(hw) & CLK_IS_BASIC)
- return;
-
- c = to_clk_hw_omap(hw);
- list_add(&c->node, &clk_hw_omap_clocks);
-}
-
-/**
* omap2_clk_enable_autoidle_all - enable autoidle on all OMAP clocks that
* support it
*
@@ -198,11 +221,11 @@ void omap2_init_clk_hw_omap_clocks(struct clk_hw *hw)
*/
int omap2_clk_enable_autoidle_all(void)
{
- struct clk_hw_omap *c;
+ int ret;
- list_for_each_entry(c, &clk_hw_omap_clocks, node)
- if (c->ops && c->ops->allow_idle)
- c->ops->allow_idle(c);
+ ret = omap2_clk_for_each(_omap2_clk_allow_idle);
+ if (ret)
+ return ret;
_clk_generic_allow_autoidle_all();
@@ -220,11 +243,11 @@ int omap2_clk_enable_autoidle_all(void)
*/
int omap2_clk_disable_autoidle_all(void)
{
- struct clk_hw_omap *c;
+ int ret;
- list_for_each_entry(c, &clk_hw_omap_clocks, node)
- if (c->ops && c->ops->deny_idle)
- c->ops->deny_idle(c);
+ ret = omap2_clk_for_each(_omap2_clk_deny_idle);
+ if (ret)
+ return ret;
_clk_generic_deny_autoidle_all();
diff --git a/drivers/clk/ti/clk.c b/drivers/clk/ti/clk.c
index d0cd58534781..ba17cc5bd04b 100644
--- a/drivers/clk/ti/clk.c
+++ b/drivers/clk/ti/clk.c
@@ -31,6 +31,7 @@
#undef pr_fmt
#define pr_fmt(fmt) "%s: " fmt, __func__
+static LIST_HEAD(clk_hw_omap_clocks);
struct ti_clk_ll_ops *ti_clk_ll_ops;
static struct device_node *clocks_node_ptr[CLK_MAX_MEMMAPS];
@@ -191,9 +192,13 @@ void __init ti_dt_clocks_register(struct ti_dt_clk oclks[])
clkdev_add(&c->lk);
} else {
if (num_args && !has_clkctrl_data) {
- if (of_find_compatible_node(NULL, NULL,
- "ti,clkctrl")) {
+ struct device_node *np;
+
+ np = of_find_compatible_node(NULL, NULL,
+ "ti,clkctrl");
+ if (np) {
has_clkctrl_data = true;
+ of_node_put(np);
} else {
clkctrl_nodes_missing = true;
@@ -351,6 +356,9 @@ void __init omap2_clk_legacy_provider_init(int index, void __iomem *mem)
struct clk_iomap *io;
io = memblock_alloc(sizeof(*io), SMP_CACHE_BYTES);
+ if (!io)
+ panic("%s: Failed to allocate %zu bytes\n", __func__,
+ sizeof(*io));
io->mem = mem;
@@ -517,3 +525,74 @@ struct clk *ti_clk_register(struct device *dev, struct clk_hw *hw,
return clk;
}
+
+/**
+ * ti_clk_register_omap_hw - register a clk_hw_omap to the clock framework
+ * @dev: device for this clock
+ * @hw: hardware clock handle
+ * @con: connection ID for this clock
+ *
+ * Registers a clk_hw_omap clock to the clock framewor, adds a clock alias
+ * for it, and adds the list to the available clk_hw_omap type clocks.
+ * Returns a handle to the registered clock if successful, ERR_PTR value
+ * in failure.
+ */
+struct clk *ti_clk_register_omap_hw(struct device *dev, struct clk_hw *hw,
+ const char *con)
+{
+ struct clk *clk;
+ struct clk_hw_omap *oclk;
+
+ clk = ti_clk_register(dev, hw, con);
+ if (IS_ERR(clk))
+ return clk;
+
+ oclk = to_clk_hw_omap(hw);
+
+ list_add(&oclk->node, &clk_hw_omap_clocks);
+
+ return clk;
+}
+
+/**
+ * omap2_clk_for_each - call function for each registered clk_hw_omap
+ * @fn: pointer to a callback function
+ *
+ * Call @fn for each registered clk_hw_omap, passing @hw to each
+ * function. @fn must return 0 for success or any other value for
+ * failure. If @fn returns non-zero, the iteration across clocks
+ * will stop and the non-zero return value will be passed to the
+ * caller of omap2_clk_for_each().
+ */
+int omap2_clk_for_each(int (*fn)(struct clk_hw_omap *hw))
+{
+ int ret;
+ struct clk_hw_omap *hw;
+
+ list_for_each_entry(hw, &clk_hw_omap_clocks, node) {
+ ret = (*fn)(hw);
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
+/**
+ * omap2_clk_is_hw_omap - check if the provided clk_hw is OMAP clock
+ * @hw: clk_hw to check if it is an omap clock or not
+ *
+ * Checks if the provided clk_hw is OMAP clock or not. Returns true if
+ * it is, false otherwise.
+ */
+bool omap2_clk_is_hw_omap(struct clk_hw *hw)
+{
+ struct clk_hw_omap *oclk;
+
+ list_for_each_entry(oclk, &clk_hw_omap_clocks, node) {
+ if (&oclk->hw == hw)
+ return true;
+ }
+
+ return false;
+}
diff --git a/drivers/clk/ti/clkctrl.c b/drivers/clk/ti/clkctrl.c
index 40630eb950fc..639f515e08f0 100644
--- a/drivers/clk/ti/clkctrl.c
+++ b/drivers/clk/ti/clkctrl.c
@@ -276,7 +276,7 @@ _ti_clkctrl_clk_register(struct omap_clkctrl_provider *provider,
init.parent_names = parents;
init.num_parents = num_parents;
init.ops = ops;
- init.flags = CLK_IS_BASIC;
+ init.flags = 0;
clk = ti_clk_register(NULL, clk_hw, init.name);
if (IS_ERR_OR_NULL(clk)) {
@@ -530,7 +530,7 @@ static void __init _ti_omap4_clkctrl_setup(struct device_node *node)
* Create default clkdm name, replace _cm from end of parent
* node name with _clkdm
*/
- provider->clkdm_name[strlen(provider->clkdm_name) - 5] = 0;
+ provider->clkdm_name[strlen(provider->clkdm_name) - 2] = 0;
} else {
provider->clkdm_name = kasprintf(GFP_KERNEL, "%pOFn", node);
if (!provider->clkdm_name) {
diff --git a/drivers/clk/ti/clock.h b/drivers/clk/ti/clock.h
index 9f312a219510..1c0fac59d809 100644
--- a/drivers/clk/ti/clock.h
+++ b/drivers/clk/ti/clock.h
@@ -203,6 +203,8 @@ typedef void (*ti_of_clk_init_cb_t)(void *, struct device_node *);
struct clk *ti_clk_register(struct device *dev, struct clk_hw *hw,
const char *con);
+struct clk *ti_clk_register_omap_hw(struct device *dev, struct clk_hw *hw,
+ const char *con);
int ti_clk_add_alias(struct device *dev, struct clk *clk, const char *con);
void ti_clk_add_aliases(void);
@@ -221,7 +223,6 @@ int ti_clk_retry_init(struct device_node *node, void *user,
ti_of_clk_init_cb_t func);
int ti_clk_add_component(struct device_node *node, struct clk_hw *hw, int type);
-void omap2_init_clk_hw_omap_clocks(struct clk_hw *hw);
int of_ti_clk_autoidle_setup(struct device_node *node);
void omap2_clk_enable_init_clocks(const char **clk_names, u8 num_clocks);
@@ -301,6 +302,8 @@ long omap4_dpll_regm4xen_round_rate(struct clk_hw *hw,
unsigned long *parent_rate);
int omap4_dpll_regm4xen_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req);
+int omap2_clk_for_each(int (*fn)(struct clk_hw_omap *hw));
+bool omap2_clk_is_hw_omap(struct clk_hw *hw);
extern struct ti_clk_ll_ops *ti_clk_ll_ops;
diff --git a/drivers/clk/ti/clockdomain.c b/drivers/clk/ti/clockdomain.c
index 07a805125e98..423a99b9f10c 100644
--- a/drivers/clk/ti/clockdomain.c
+++ b/drivers/clk/ti/clockdomain.c
@@ -143,7 +143,7 @@ static void __init of_ti_clockdomain_setup(struct device_node *node)
continue;
}
clk_hw = __clk_get_hw(clk);
- if (clk_hw_get_flags(clk_hw) & CLK_IS_BASIC) {
+ if (!omap2_clk_is_hw_omap(clk_hw)) {
pr_warn("can't setup clkdm for basic clk %s\n",
__clk_get_name(clk));
continue;
diff --git a/drivers/clk/ti/divider.c b/drivers/clk/ti/divider.c
index 0241450f3eb3..4786e0ebc2e8 100644
--- a/drivers/clk/ti/divider.c
+++ b/drivers/clk/ti/divider.c
@@ -336,7 +336,7 @@ static struct clk *_register_divider(struct device *dev, const char *name,
init.name = name;
init.ops = &ti_clk_divider_ops;
- init.flags = flags | CLK_IS_BASIC;
+ init.flags = flags;
init.parent_names = (parent_name ? &parent_name : NULL);
init.num_parents = (parent_name ? 1 : 0);
diff --git a/drivers/clk/ti/dpll.c b/drivers/clk/ti/dpll.c
index 6c3329bc116f..659dadb23279 100644
--- a/drivers/clk/ti/dpll.c
+++ b/drivers/clk/ti/dpll.c
@@ -192,10 +192,9 @@ static void __init _register_dpll(void *user,
dd->clk_bypass = __clk_get_hw(clk);
/* register the clock */
- clk = ti_clk_register(NULL, &clk_hw->hw, node->name);
+ clk = ti_clk_register_omap_hw(NULL, &clk_hw->hw, node->name);
if (!IS_ERR(clk)) {
- omap2_init_clk_hw_omap_clocks(&clk_hw->hw);
of_clk_add_provider(node, of_clk_src_simple_get, clk);
kfree(clk_hw->hw.init->parent_names);
kfree(clk_hw->hw.init);
@@ -265,14 +264,12 @@ static void _register_dpll_x2(struct device_node *node,
#endif
/* register the clock */
- clk = ti_clk_register(NULL, &clk_hw->hw, name);
+ clk = ti_clk_register_omap_hw(NULL, &clk_hw->hw, name);
- if (IS_ERR(clk)) {
+ if (IS_ERR(clk))
kfree(clk_hw);
- } else {
- omap2_init_clk_hw_omap_clocks(&clk_hw->hw);
+ else
of_clk_add_provider(node, of_clk_src_simple_get, clk);
- }
}
#endif
diff --git a/drivers/clk/ti/dpll3xxx.c b/drivers/clk/ti/dpll3xxx.c
index 44b6b6403753..3dde6c8c3354 100644
--- a/drivers/clk/ti/dpll3xxx.c
+++ b/drivers/clk/ti/dpll3xxx.c
@@ -731,7 +731,7 @@ static struct clk_hw_omap *omap3_find_clkoutx2_dpll(struct clk_hw *hw)
do {
do {
hw = clk_hw_get_parent(hw);
- } while (hw && (clk_hw_get_flags(hw) & CLK_IS_BASIC));
+ } while (hw && (!omap2_clk_is_hw_omap(hw)));
if (!hw)
break;
pclk = to_clk_hw_omap(hw);
diff --git a/drivers/clk/ti/gate.c b/drivers/clk/ti/gate.c
index 1c78fff5513c..504c0e91cdc7 100644
--- a/drivers/clk/ti/gate.c
+++ b/drivers/clk/ti/gate.c
@@ -123,7 +123,7 @@ static struct clk *_register_gate(struct device *dev, const char *name,
init.flags = flags;
- clk = ti_clk_register(NULL, &clk_hw->hw, name);
+ clk = ti_clk_register_omap_hw(NULL, &clk_hw->hw, name);
if (IS_ERR(clk))
kfree(clk_hw);
diff --git a/drivers/clk/ti/interface.c b/drivers/clk/ti/interface.c
index 87e00c2ee957..83e34429d3b1 100644
--- a/drivers/clk/ti/interface.c
+++ b/drivers/clk/ti/interface.c
@@ -57,12 +57,10 @@ static struct clk *_register_interface(struct device *dev, const char *name,
init.num_parents = 1;
init.parent_names = &parent_name;
- clk = ti_clk_register(NULL, &clk_hw->hw, name);
+ clk = ti_clk_register_omap_hw(NULL, &clk_hw->hw, name);
if (IS_ERR(clk))
kfree(clk_hw);
- else
- omap2_init_clk_hw_omap_clocks(&clk_hw->hw);
return clk;
}
diff --git a/drivers/clk/ti/mux.c b/drivers/clk/ti/mux.c
index 883bdde94d04..b7f9a4f068bf 100644
--- a/drivers/clk/ti/mux.c
+++ b/drivers/clk/ti/mux.c
@@ -143,7 +143,7 @@ static struct clk *_register_mux(struct device *dev, const char *name,
init.name = name;
init.ops = &ti_clk_mux_ops;
- init.flags = flags | CLK_IS_BASIC;
+ init.flags = flags;
init.parent_names = parent_names;
init.num_parents = num_parents;
diff --git a/drivers/clk/uniphier/clk-uniphier-cpugear.c b/drivers/clk/uniphier/clk-uniphier-cpugear.c
index ec11f55594ad..5d2d42b7e182 100644
--- a/drivers/clk/uniphier/clk-uniphier-cpugear.c
+++ b/drivers/clk/uniphier/clk-uniphier-cpugear.c
@@ -47,7 +47,7 @@ static int uniphier_clk_cpugear_set_parent(struct clk_hw *hw, u8 index)
return ret;
ret = regmap_write_bits(gear->regmap,
- gear->regbase + UNIPHIER_CLK_CPUGEAR_SET,
+ gear->regbase + UNIPHIER_CLK_CPUGEAR_UPD,
UNIPHIER_CLK_CPUGEAR_UPD_BIT,
UNIPHIER_CLK_CPUGEAR_UPD_BIT);
if (ret)
diff --git a/drivers/clk/x86/clk-lpt.c b/drivers/clk/x86/clk-lpt.c
index 6b40eb89ae19..68bd3abaef2c 100644
--- a/drivers/clk/x86/clk-lpt.c
+++ b/drivers/clk/x86/clk-lpt.c
@@ -13,7 +13,7 @@
#include <linux/clk-provider.h>
#include <linux/err.h>
#include <linux/module.h>
-#include <linux/platform_data/clk-lpss.h>
+#include <linux/platform_data/x86/clk-lpss.h>
#include <linux/platform_device.h>
static int lpt_clk_probe(struct platform_device *pdev)
diff --git a/drivers/clk/x86/clk-st.c b/drivers/clk/x86/clk-st.c
index 3a0996f2d556..25d4b97aff9b 100644
--- a/drivers/clk/x86/clk-st.c
+++ b/drivers/clk/x86/clk-st.c
@@ -52,7 +52,8 @@ static int st_clk_probe(struct platform_device *pdev)
0, st_data->base + MISCCLKCNTL1, OSCCLKENB,
CLK_GATE_SET_TO_DISABLE, NULL);
- clk_hw_register_clkdev(hws[ST_CLK_GATE], "oscout1", NULL);
+ devm_clk_hw_register_clkdev(&pdev->dev, hws[ST_CLK_GATE], "oscout1",
+ NULL);
return 0;
}
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
index a8b20b65bd4b..aa4ec53281ce 100644
--- a/drivers/clocksource/arm_arch_timer.c
+++ b/drivers/clocksource/arm_arch_timer.c
@@ -1261,6 +1261,13 @@ static enum arch_timer_ppi_nr __init arch_timer_select_ppi(void)
return ARCH_TIMER_PHYS_SECURE_PPI;
}
+static void __init arch_timer_populate_kvm_info(void)
+{
+ arch_timer_kvm_info.virtual_irq = arch_timer_ppi[ARCH_TIMER_VIRT_PPI];
+ if (is_kernel_in_hyp_mode())
+ arch_timer_kvm_info.physical_irq = arch_timer_ppi[ARCH_TIMER_PHYS_NONSECURE_PPI];
+}
+
static int __init arch_timer_of_init(struct device_node *np)
{
int i, ret;
@@ -1275,7 +1282,7 @@ static int __init arch_timer_of_init(struct device_node *np)
for (i = ARCH_TIMER_PHYS_SECURE_PPI; i < ARCH_TIMER_MAX_TIMER_PPI; i++)
arch_timer_ppi[i] = irq_of_parse_and_map(np, i);
- arch_timer_kvm_info.virtual_irq = arch_timer_ppi[ARCH_TIMER_VIRT_PPI];
+ arch_timer_populate_kvm_info();
rate = arch_timer_get_cntfrq();
arch_timer_of_configure_rate(rate, np);
@@ -1605,7 +1612,7 @@ static int __init arch_timer_acpi_init(struct acpi_table_header *table)
arch_timer_ppi[ARCH_TIMER_HYP_PPI] =
acpi_gtdt_map_ppi(ARCH_TIMER_HYP_PPI);
- arch_timer_kvm_info.virtual_irq = arch_timer_ppi[ARCH_TIMER_VIRT_PPI];
+ arch_timer_populate_kvm_info();
/*
* When probing via ACPI, we have no mechanism to override the sysreg
diff --git a/drivers/clocksource/clps711x-timer.c b/drivers/clocksource/clps711x-timer.c
index a8dd80576c95..857f8c086274 100644
--- a/drivers/clocksource/clps711x-timer.c
+++ b/drivers/clocksource/clps711x-timer.c
@@ -31,16 +31,9 @@ static u64 notrace clps711x_sched_clock_read(void)
return ~readw(tcd);
}
-static int __init _clps711x_clksrc_init(struct clk *clock, void __iomem *base)
+static void __init clps711x_clksrc_init(struct clk *clock, void __iomem *base)
{
- unsigned long rate;
-
- if (!base)
- return -ENOMEM;
- if (IS_ERR(clock))
- return PTR_ERR(clock);
-
- rate = clk_get_rate(clock);
+ unsigned long rate = clk_get_rate(clock);
tcd = base;
@@ -48,8 +41,6 @@ static int __init _clps711x_clksrc_init(struct clk *clock, void __iomem *base)
clocksource_mmio_readw_down);
sched_clock_register(clps711x_sched_clock_read, 16, rate);
-
- return 0;
}
static irqreturn_t clps711x_timer_interrupt(int irq, void *dev_id)
@@ -67,13 +58,6 @@ static int __init _clps711x_clkevt_init(struct clk *clock, void __iomem *base,
struct clock_event_device *clkevt;
unsigned long rate;
- if (!irq)
- return -EINVAL;
- if (!base)
- return -ENOMEM;
- if (IS_ERR(clock))
- return PTR_ERR(clock);
-
clkevt = kzalloc(sizeof(*clkevt), GFP_KERNEL);
if (!clkevt)
return -ENOMEM;
@@ -93,31 +77,29 @@ static int __init _clps711x_clkevt_init(struct clk *clock, void __iomem *base,
"clps711x-timer", clkevt);
}
-void __init clps711x_clksrc_init(void __iomem *tc1_base, void __iomem *tc2_base,
- unsigned int irq)
-{
- struct clk *tc1 = clk_get_sys("clps711x-timer.0", NULL);
- struct clk *tc2 = clk_get_sys("clps711x-timer.1", NULL);
-
- BUG_ON(_clps711x_clksrc_init(tc1, tc1_base));
- BUG_ON(_clps711x_clkevt_init(tc2, tc2_base, irq));
-}
-
-#ifdef CONFIG_TIMER_OF
static int __init clps711x_timer_init(struct device_node *np)
{
unsigned int irq = irq_of_parse_and_map(np, 0);
struct clk *clock = of_clk_get(np, 0);
void __iomem *base = of_iomap(np, 0);
+ if (!base)
+ return -ENOMEM;
+ if (!irq)
+ return -EINVAL;
+ if (IS_ERR(clock))
+ return PTR_ERR(clock);
+
switch (of_alias_get_id(np, "timer")) {
case CLPS711X_CLKSRC_CLOCKSOURCE:
- return _clps711x_clksrc_init(clock, base);
+ clps711x_clksrc_init(clock, base);
+ break;
case CLPS711X_CLKSRC_CLOCKEVENT:
return _clps711x_clkevt_init(clock, base, irq);
default:
return -EINVAL;
}
+
+ return 0;
}
TIMER_OF_DECLARE(clps711x, "cirrus,ep7209-timer", clps711x_timer_init);
-#endif
diff --git a/drivers/clocksource/mips-gic-timer.c b/drivers/clocksource/mips-gic-timer.c
index 54f8a331b53a..37671a5d4ed9 100644
--- a/drivers/clocksource/mips-gic-timer.c
+++ b/drivers/clocksource/mips-gic-timer.c
@@ -67,7 +67,7 @@ static irqreturn_t gic_compare_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}
-struct irqaction gic_compare_irqaction = {
+static struct irqaction gic_compare_irqaction = {
.handler = gic_compare_interrupt,
.percpu_dev_id = &gic_clockevent_device,
.flags = IRQF_PERCPU | IRQF_TIMER,
diff --git a/drivers/clocksource/tcb_clksrc.c b/drivers/clocksource/tcb_clksrc.c
index 43f4d5c4d6fa..f987027ca566 100644
--- a/drivers/clocksource/tcb_clksrc.c
+++ b/drivers/clocksource/tcb_clksrc.c
@@ -71,7 +71,7 @@ static u64 tc_get_cycles32(struct clocksource *cs)
return readl_relaxed(tcaddr + ATMEL_TC_REG(0, CV));
}
-void tc_clksrc_suspend(struct clocksource *cs)
+static void tc_clksrc_suspend(struct clocksource *cs)
{
int i;
@@ -86,7 +86,7 @@ void tc_clksrc_suspend(struct clocksource *cs)
bmr_cache = readl(tcaddr + ATMEL_TC_BMR);
}
-void tc_clksrc_resume(struct clocksource *cs)
+static void tc_clksrc_resume(struct clocksource *cs)
{
int i;
diff --git a/drivers/clocksource/timer-riscv.c b/drivers/clocksource/timer-riscv.c
index e8163693e936..5e6038fbf115 100644
--- a/drivers/clocksource/timer-riscv.c
+++ b/drivers/clocksource/timer-riscv.c
@@ -58,7 +58,7 @@ static u64 riscv_sched_clock(void)
static DEFINE_PER_CPU(struct clocksource, riscv_clocksource) = {
.name = "riscv_clocksource",
.rating = 300,
- .mask = CLOCKSOURCE_MASK(BITS_PER_LONG),
+ .mask = CLOCKSOURCE_MASK(64),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
.read = riscv_clocksource_rdtime,
};
@@ -120,8 +120,7 @@ static int __init riscv_timer_init_dt(struct device_node *n)
return error;
}
- sched_clock_register(riscv_sched_clock,
- BITS_PER_LONG, riscv_timebase);
+ sched_clock_register(riscv_sched_clock, 64, riscv_timebase);
error = cpuhp_setup_state(CPUHP_AP_RISCV_TIMER_STARTING,
"clockevents/riscv/timer:starting",
diff --git a/drivers/clocksource/timer-ti-dm.c b/drivers/clocksource/timer-ti-dm.c
index c364027638e1..3352da6ed61f 100644
--- a/drivers/clocksource/timer-ti-dm.c
+++ b/drivers/clocksource/timer-ti-dm.c
@@ -586,8 +586,8 @@ static int omap_dm_timer_set_load(struct omap_dm_timer *timer, int autoreload,
}
/* Optimized set_load which removes costly spin wait in timer_start */
-int omap_dm_timer_set_load_start(struct omap_dm_timer *timer, int autoreload,
- unsigned int load)
+static int omap_dm_timer_set_load_start(struct omap_dm_timer *timer,
+ int autoreload, unsigned int load)
{
u32 l;
diff --git a/drivers/connector/cn_proc.c b/drivers/connector/cn_proc.c
index ed5e42461094..ad48fd52cb53 100644
--- a/drivers/connector/cn_proc.c
+++ b/drivers/connector/cn_proc.c
@@ -250,6 +250,7 @@ void proc_coredump_connector(struct task_struct *task)
{
struct cn_msg *msg;
struct proc_event *ev;
+ struct task_struct *parent;
__u8 buffer[CN_PROC_MSG_SIZE] __aligned(8);
if (atomic_read(&proc_event_num_listeners) < 1)
@@ -262,8 +263,14 @@ void proc_coredump_connector(struct task_struct *task)
ev->what = PROC_EVENT_COREDUMP;
ev->event_data.coredump.process_pid = task->pid;
ev->event_data.coredump.process_tgid = task->tgid;
- ev->event_data.coredump.parent_pid = task->real_parent->pid;
- ev->event_data.coredump.parent_tgid = task->real_parent->tgid;
+
+ rcu_read_lock();
+ if (pid_alive(task)) {
+ parent = rcu_dereference(task->real_parent);
+ ev->event_data.coredump.parent_pid = parent->pid;
+ ev->event_data.coredump.parent_tgid = parent->tgid;
+ }
+ rcu_read_unlock();
memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id));
msg->ack = 0; /* not used */
@@ -276,6 +283,7 @@ void proc_exit_connector(struct task_struct *task)
{
struct cn_msg *msg;
struct proc_event *ev;
+ struct task_struct *parent;
__u8 buffer[CN_PROC_MSG_SIZE] __aligned(8);
if (atomic_read(&proc_event_num_listeners) < 1)
@@ -290,8 +298,14 @@ void proc_exit_connector(struct task_struct *task)
ev->event_data.exit.process_tgid = task->tgid;
ev->event_data.exit.exit_code = task->exit_code;
ev->event_data.exit.exit_signal = task->exit_signal;
- ev->event_data.exit.parent_pid = task->real_parent->pid;
- ev->event_data.exit.parent_tgid = task->real_parent->tgid;
+
+ rcu_read_lock();
+ if (pid_alive(task)) {
+ parent = rcu_dereference(task->real_parent);
+ ev->event_data.exit.parent_pid = parent->pid;
+ ev->event_data.exit.parent_tgid = parent->tgid;
+ }
+ rcu_read_unlock();
memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id));
msg->ack = 0; /* not used */
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index 0e626b00053b..e10922709d13 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -206,17 +206,15 @@ unsigned int cpufreq_generic_get(unsigned int cpu)
EXPORT_SYMBOL_GPL(cpufreq_generic_get);
/**
- * cpufreq_cpu_get: returns policy for a cpu and marks it busy.
+ * cpufreq_cpu_get - Return policy for a CPU and mark it as busy.
+ * @cpu: CPU to find the policy for.
*
- * @cpu: cpu to find policy for.
+ * Call cpufreq_cpu_get_raw() to obtain a cpufreq policy for @cpu and increment
+ * the kobject reference counter of that policy. Return a valid policy on
+ * success or NULL on failure.
*
- * This returns policy for 'cpu', returns NULL if it doesn't exist.
- * It also increments the kobject reference count to mark it busy and so would
- * require a corresponding call to cpufreq_cpu_put() to decrement it back.
- * If corresponding call cpufreq_cpu_put() isn't made, the policy wouldn't be
- * freed as that depends on the kobj count.
- *
- * Return: A valid policy on success, otherwise NULL on failure.
+ * The policy returned by this function has to be released with the help of
+ * cpufreq_cpu_put() to balance its kobject reference counter properly.
*/
struct cpufreq_policy *cpufreq_cpu_get(unsigned int cpu)
{
@@ -243,12 +241,8 @@ struct cpufreq_policy *cpufreq_cpu_get(unsigned int cpu)
EXPORT_SYMBOL_GPL(cpufreq_cpu_get);
/**
- * cpufreq_cpu_put: Decrements the usage count of a policy
- *
- * @policy: policy earlier returned by cpufreq_cpu_get().
- *
- * This decrements the kobject reference count incremented earlier by calling
- * cpufreq_cpu_get().
+ * cpufreq_cpu_put - Decrement kobject usage counter for cpufreq policy.
+ * @policy: cpufreq policy returned by cpufreq_cpu_get().
*/
void cpufreq_cpu_put(struct cpufreq_policy *policy)
{
diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c
index 002f5169d4eb..b599c7318aab 100644
--- a/drivers/cpufreq/intel_pstate.c
+++ b/drivers/cpufreq/intel_pstate.c
@@ -385,7 +385,10 @@ static int intel_pstate_get_cppc_guranteed(int cpu)
if (ret)
return ret;
- return cppc_perf.guaranteed_perf;
+ if (cppc_perf.guaranteed_perf)
+ return cppc_perf.guaranteed_perf;
+
+ return cppc_perf.nominal_perf;
}
#else /* CONFIG_ACPI_CPPC_LIB */
@@ -1762,7 +1765,7 @@ static void intel_pstate_update_util(struct update_util_data *data, u64 time,
/* Start over if the CPU may have been idle. */
if (delta_ns > TICK_NSEC) {
cpu->iowait_boost = ONE_EIGHTH_FP;
- } else if (cpu->iowait_boost) {
+ } else if (cpu->iowait_boost >= ONE_EIGHTH_FP) {
cpu->iowait_boost <<= 1;
if (cpu->iowait_boost > int_tofp(1))
cpu->iowait_boost = int_tofp(1);
diff --git a/drivers/cpufreq/pxa2xx-cpufreq.c b/drivers/cpufreq/pxa2xx-cpufreq.c
index 46254e583982..74e0e0c20c46 100644
--- a/drivers/cpufreq/pxa2xx-cpufreq.c
+++ b/drivers/cpufreq/pxa2xx-cpufreq.c
@@ -143,7 +143,7 @@ static int pxa_cpufreq_change_voltage(const struct pxa_freqs *pxa_freq)
return ret;
}
-static void __init pxa_cpufreq_init_voltages(void)
+static void pxa_cpufreq_init_voltages(void)
{
vcc_core = regulator_get(NULL, "vcc_core");
if (IS_ERR(vcc_core)) {
@@ -159,7 +159,7 @@ static int pxa_cpufreq_change_voltage(const struct pxa_freqs *pxa_freq)
return 0;
}
-static void __init pxa_cpufreq_init_voltages(void) { }
+static void pxa_cpufreq_init_voltages(void) { }
#endif
static void find_freq_tables(struct cpufreq_frequency_table **freq_table,
diff --git a/drivers/cpufreq/scpi-cpufreq.c b/drivers/cpufreq/scpi-cpufreq.c
index 3f49427766b8..2b51e0718c9f 100644
--- a/drivers/cpufreq/scpi-cpufreq.c
+++ b/drivers/cpufreq/scpi-cpufreq.c
@@ -189,8 +189,8 @@ static int scpi_cpufreq_exit(struct cpufreq_policy *policy)
clk_put(priv->clk);
dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &policy->freq_table);
- kfree(priv);
dev_pm_opp_remove_all_dynamic(priv->cpu_dev);
+ kfree(priv);
return 0;
}
diff --git a/drivers/cpuidle/governor.c b/drivers/cpuidle/governor.c
index bb93e5cf6a4a..9fddf828a76f 100644
--- a/drivers/cpuidle/governor.c
+++ b/drivers/cpuidle/governor.c
@@ -89,6 +89,7 @@ int cpuidle_register_governor(struct cpuidle_governor *gov)
mutex_lock(&cpuidle_lock);
if (__cpuidle_find_governor(gov->name) == NULL) {
ret = 0;
+ list_add_tail(&gov->governor_list, &cpuidle_governors);
if (!cpuidle_curr_governor ||
!strncasecmp(param_governor, gov->name, CPUIDLE_NAME_LEN) ||
(cpuidle_curr_governor->rating < gov->rating &&
diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c
index 61316fc51548..5951604e7d5c 100644
--- a/drivers/cpuidle/governors/menu.c
+++ b/drivers/cpuidle/governors/menu.c
@@ -186,7 +186,7 @@ static unsigned int get_typical_interval(struct menu_device *data,
unsigned int min, max, thresh, avg;
uint64_t sum, variance;
- thresh = UINT_MAX; /* Discard outliers above this value */
+ thresh = INT_MAX; /* Discard outliers above this value */
again:
diff --git a/drivers/crypto/caam/caamalg.c b/drivers/crypto/caam/caamalg.c
index 9eac5099098e..579578498deb 100644
--- a/drivers/crypto/caam/caamalg.c
+++ b/drivers/crypto/caam/caamalg.c
@@ -3455,7 +3455,6 @@ static int __init caam_algapi_init(void)
{
struct device_node *dev_node;
struct platform_device *pdev;
- struct device *ctrldev;
struct caam_drv_private *priv;
int i = 0, err = 0;
u32 aes_vid, aes_inst, des_inst, md_vid, md_inst, ccha_inst, ptha_inst;
@@ -3476,16 +3475,17 @@ static int __init caam_algapi_init(void)
return -ENODEV;
}
- ctrldev = &pdev->dev;
- priv = dev_get_drvdata(ctrldev);
+ priv = dev_get_drvdata(&pdev->dev);
of_node_put(dev_node);
/*
* If priv is NULL, it's probably because the caam driver wasn't
* properly initialized (e.g. RNG4 init failed). Thus, bail out here.
*/
- if (!priv)
- return -ENODEV;
+ if (!priv) {
+ err = -ENODEV;
+ goto out_put_dev;
+ }
/*
@@ -3626,6 +3626,8 @@ static int __init caam_algapi_init(void)
if (registered)
pr_info("caam algorithms registered in /proc/crypto\n");
+out_put_dev:
+ put_device(&pdev->dev);
return err;
}
diff --git a/drivers/crypto/caam/caamalg_qi.c b/drivers/crypto/caam/caamalg_qi.c
index a15ce9213310..c61921d32489 100644
--- a/drivers/crypto/caam/caamalg_qi.c
+++ b/drivers/crypto/caam/caamalg_qi.c
@@ -2492,12 +2492,15 @@ static int __init caam_qi_algapi_init(void)
* If priv is NULL, it's probably because the caam driver wasn't
* properly initialized (e.g. RNG4 init failed). Thus, bail out here.
*/
- if (!priv || !priv->qi_present)
- return -ENODEV;
+ if (!priv || !priv->qi_present) {
+ err = -ENODEV;
+ goto out_put_dev;
+ }
if (caam_dpaa2) {
dev_info(ctrldev, "caam/qi frontend driver not suitable for DPAA 2.x, aborting...\n");
- return -ENODEV;
+ err = -ENODEV;
+ goto out_put_dev;
}
/*
@@ -2610,6 +2613,8 @@ static int __init caam_qi_algapi_init(void)
if (registered)
dev_info(priv->qidev, "algorithms registered in /proc/crypto\n");
+out_put_dev:
+ put_device(ctrldev);
return err;
}
diff --git a/drivers/crypto/caam/caamhash.c b/drivers/crypto/caam/caamhash.c
index d7483e4d0ce2..b1eadc6652b5 100644
--- a/drivers/crypto/caam/caamhash.c
+++ b/drivers/crypto/caam/caamhash.c
@@ -1993,7 +1993,6 @@ static int __init caam_algapi_hash_init(void)
{
struct device_node *dev_node;
struct platform_device *pdev;
- struct device *ctrldev;
int i = 0, err = 0;
struct caam_drv_private *priv;
unsigned int md_limit = SHA512_DIGEST_SIZE;
@@ -2012,16 +2011,17 @@ static int __init caam_algapi_hash_init(void)
return -ENODEV;
}
- ctrldev = &pdev->dev;
- priv = dev_get_drvdata(ctrldev);
+ priv = dev_get_drvdata(&pdev->dev);
of_node_put(dev_node);
/*
* If priv is NULL, it's probably because the caam driver wasn't
* properly initialized (e.g. RNG4 init failed). Thus, bail out here.
*/
- if (!priv)
- return -ENODEV;
+ if (!priv) {
+ err = -ENODEV;
+ goto out_put_dev;
+ }
/*
* Register crypto algorithms the device supports. First, identify
@@ -2043,8 +2043,10 @@ static int __init caam_algapi_hash_init(void)
* Skip registration of any hashing algorithms if MD block
* is not present.
*/
- if (!md_inst)
- return -ENODEV;
+ if (!md_inst) {
+ err = -ENODEV;
+ goto out_put_dev;
+ }
/* Limit digest size based on LP256 */
if (md_vid == CHA_VER_VID_MD_LP256)
@@ -2101,6 +2103,8 @@ static int __init caam_algapi_hash_init(void)
list_add_tail(&t_alg->entry, &hash_list);
}
+out_put_dev:
+ put_device(&pdev->dev);
return err;
}
diff --git a/drivers/crypto/caam/caampkc.c b/drivers/crypto/caam/caampkc.c
index 77ab28a2811a..58285642306e 100644
--- a/drivers/crypto/caam/caampkc.c
+++ b/drivers/crypto/caam/caampkc.c
@@ -1042,8 +1042,10 @@ static int __init caam_pkc_init(void)
* If priv is NULL, it's probably because the caam driver wasn't
* properly initialized (e.g. RNG4 init failed). Thus, bail out here.
*/
- if (!priv)
- return -ENODEV;
+ if (!priv) {
+ err = -ENODEV;
+ goto out_put_dev;
+ }
/* Determine public key hardware accelerator presence. */
if (priv->era < 10)
@@ -1053,8 +1055,10 @@ static int __init caam_pkc_init(void)
pk_inst = rd_reg32(&priv->ctrl->vreg.pkha) & CHA_VER_NUM_MASK;
/* Do not register algorithms if PKHA is not present. */
- if (!pk_inst)
- return -ENODEV;
+ if (!pk_inst) {
+ err = -ENODEV;
+ goto out_put_dev;
+ }
err = crypto_register_akcipher(&caam_rsa);
if (err)
@@ -1063,6 +1067,8 @@ static int __init caam_pkc_init(void)
else
dev_info(ctrldev, "caam pkc algorithms registered in /proc/crypto\n");
+out_put_dev:
+ put_device(ctrldev);
return err;
}
diff --git a/drivers/crypto/caam/caamrng.c b/drivers/crypto/caam/caamrng.c
index a387c8d49a62..95eb5402c59f 100644
--- a/drivers/crypto/caam/caamrng.c
+++ b/drivers/crypto/caam/caamrng.c
@@ -308,7 +308,6 @@ static int __init caam_rng_init(void)
struct device *dev;
struct device_node *dev_node;
struct platform_device *pdev;
- struct device *ctrldev;
struct caam_drv_private *priv;
u32 rng_inst;
int err;
@@ -326,16 +325,17 @@ static int __init caam_rng_init(void)
return -ENODEV;
}
- ctrldev = &pdev->dev;
- priv = dev_get_drvdata(ctrldev);
+ priv = dev_get_drvdata(&pdev->dev);
of_node_put(dev_node);
/*
* If priv is NULL, it's probably because the caam driver wasn't
* properly initialized (e.g. RNG4 init failed). Thus, bail out here.
*/
- if (!priv)
- return -ENODEV;
+ if (!priv) {
+ err = -ENODEV;
+ goto out_put_dev;
+ }
/* Check for an instantiated RNG before registration */
if (priv->era < 10)
@@ -344,13 +344,16 @@ static int __init caam_rng_init(void)
else
rng_inst = rd_reg32(&priv->ctrl->vreg.rng) & CHA_VER_NUM_MASK;
- if (!rng_inst)
- return -ENODEV;
+ if (!rng_inst) {
+ err = -ENODEV;
+ goto out_put_dev;
+ }
dev = caam_jr_alloc();
if (IS_ERR(dev)) {
pr_err("Job Ring Device allocation for transform failed\n");
- return PTR_ERR(dev);
+ err = PTR_ERR(dev);
+ goto out_put_dev;
}
rng_ctx = kmalloc(sizeof(*rng_ctx), GFP_DMA | GFP_KERNEL);
if (!rng_ctx) {
@@ -361,6 +364,7 @@ static int __init caam_rng_init(void)
if (err)
goto free_rng_ctx;
+ put_device(&pdev->dev);
dev_info(dev, "registering rng-caam\n");
return hwrng_register(&caam_rng);
@@ -368,6 +372,8 @@ free_rng_ctx:
kfree(rng_ctx);
free_caam_alloc:
caam_jr_free(dev);
+out_put_dev:
+ put_device(&pdev->dev);
return err;
}
diff --git a/drivers/crypto/s5p-sss.c b/drivers/crypto/s5p-sss.c
index f4e625cf53ca..1afdcb81d8ed 100644
--- a/drivers/crypto/s5p-sss.c
+++ b/drivers/crypto/s5p-sss.c
@@ -241,7 +241,7 @@
struct samsung_aes_variant {
unsigned int aes_offset;
unsigned int hash_offset;
- const char *clk_names[];
+ const char *clk_names[2];
};
struct s5p_aes_reqctx {
diff --git a/drivers/dax/Kconfig b/drivers/dax/Kconfig
index e0700bf4893a..5ef624fe3934 100644
--- a/drivers/dax/Kconfig
+++ b/drivers/dax/Kconfig
@@ -23,12 +23,38 @@ config DEV_DAX
config DEV_DAX_PMEM
tristate "PMEM DAX: direct access to persistent memory"
depends on LIBNVDIMM && NVDIMM_DAX && DEV_DAX
+ depends on m # until we can kill DEV_DAX_PMEM_COMPAT
default DEV_DAX
help
Support raw access to persistent memory. Note that this
driver consumes memory ranges allocated and exported by the
libnvdimm sub-system.
- Say Y if unsure
+ Say M if unsure
+
+config DEV_DAX_KMEM
+ tristate "KMEM DAX: volatile-use of persistent memory"
+ default DEV_DAX
+ depends on DEV_DAX
+ depends on MEMORY_HOTPLUG # for add_memory() and friends
+ help
+ Support access to persistent memory as if it were RAM. This
+ allows easier use of persistent memory by unmodified
+ applications.
+
+ To use this feature, a DAX device must be unbound from the
+ device_dax driver (PMEM DAX) and bound to this kmem driver
+ on each boot.
+
+ Say N if unsure.
+
+config DEV_DAX_PMEM_COMPAT
+ tristate "PMEM DAX: support the deprecated /sys/class/dax interface"
+ depends on DEV_DAX_PMEM
+ default DEV_DAX_PMEM
+ help
+ Older versions of the libdaxctl library expect to find all
+ device-dax instances under /sys/class/dax. If libdaxctl in
+ your distribution is older than v58 say M, otherwise say N.
endif
diff --git a/drivers/dax/Makefile b/drivers/dax/Makefile
index 574286fac87c..81f7d54dadfb 100644
--- a/drivers/dax/Makefile
+++ b/drivers/dax/Makefile
@@ -1,8 +1,10 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_DAX) += dax.o
obj-$(CONFIG_DEV_DAX) += device_dax.o
-obj-$(CONFIG_DEV_DAX_PMEM) += dax_pmem.o
+obj-$(CONFIG_DEV_DAX_KMEM) += kmem.o
dax-y := super.o
-dax_pmem-y := pmem.o
+dax-y += bus.o
device_dax-y := device.o
+
+obj-y += pmem/
diff --git a/drivers/dax/bus.c b/drivers/dax/bus.c
new file mode 100644
index 000000000000..2109cfe80219
--- /dev/null
+++ b/drivers/dax/bus.c
@@ -0,0 +1,503 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright(c) 2017-2018 Intel Corporation. All rights reserved. */
+#include <linux/memremap.h>
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/dax.h>
+#include "dax-private.h"
+#include "bus.h"
+
+static struct class *dax_class;
+
+static DEFINE_MUTEX(dax_bus_lock);
+
+#define DAX_NAME_LEN 30
+struct dax_id {
+ struct list_head list;
+ char dev_name[DAX_NAME_LEN];
+};
+
+static int dax_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ /*
+ * We only ever expect to handle device-dax instances, i.e. the
+ * @type argument to MODULE_ALIAS_DAX_DEVICE() is always zero
+ */
+ return add_uevent_var(env, "MODALIAS=" DAX_DEVICE_MODALIAS_FMT, 0);
+}
+
+static struct dax_device_driver *to_dax_drv(struct device_driver *drv)
+{
+ return container_of(drv, struct dax_device_driver, drv);
+}
+
+static struct dax_id *__dax_match_id(struct dax_device_driver *dax_drv,
+ const char *dev_name)
+{
+ struct dax_id *dax_id;
+
+ lockdep_assert_held(&dax_bus_lock);
+
+ list_for_each_entry(dax_id, &dax_drv->ids, list)
+ if (sysfs_streq(dax_id->dev_name, dev_name))
+ return dax_id;
+ return NULL;
+}
+
+static int dax_match_id(struct dax_device_driver *dax_drv, struct device *dev)
+{
+ int match;
+
+ mutex_lock(&dax_bus_lock);
+ match = !!__dax_match_id(dax_drv, dev_name(dev));
+ mutex_unlock(&dax_bus_lock);
+
+ return match;
+}
+
+enum id_action {
+ ID_REMOVE,
+ ID_ADD,
+};
+
+static ssize_t do_id_store(struct device_driver *drv, const char *buf,
+ size_t count, enum id_action action)
+{
+ struct dax_device_driver *dax_drv = to_dax_drv(drv);
+ unsigned int region_id, id;
+ char devname[DAX_NAME_LEN];
+ struct dax_id *dax_id;
+ ssize_t rc = count;
+ int fields;
+
+ fields = sscanf(buf, "dax%d.%d", &region_id, &id);
+ if (fields != 2)
+ return -EINVAL;
+ sprintf(devname, "dax%d.%d", region_id, id);
+ if (!sysfs_streq(buf, devname))
+ return -EINVAL;
+
+ mutex_lock(&dax_bus_lock);
+ dax_id = __dax_match_id(dax_drv, buf);
+ if (!dax_id) {
+ if (action == ID_ADD) {
+ dax_id = kzalloc(sizeof(*dax_id), GFP_KERNEL);
+ if (dax_id) {
+ strncpy(dax_id->dev_name, buf, DAX_NAME_LEN);
+ list_add(&dax_id->list, &dax_drv->ids);
+ } else
+ rc = -ENOMEM;
+ } else
+ /* nothing to remove */;
+ } else if (action == ID_REMOVE) {
+ list_del(&dax_id->list);
+ kfree(dax_id);
+ } else
+ /* dax_id already added */;
+ mutex_unlock(&dax_bus_lock);
+
+ if (rc < 0)
+ return rc;
+ if (action == ID_ADD)
+ rc = driver_attach(drv);
+ if (rc)
+ return rc;
+ return count;
+}
+
+static ssize_t new_id_store(struct device_driver *drv, const char *buf,
+ size_t count)
+{
+ return do_id_store(drv, buf, count, ID_ADD);
+}
+static DRIVER_ATTR_WO(new_id);
+
+static ssize_t remove_id_store(struct device_driver *drv, const char *buf,
+ size_t count)
+{
+ return do_id_store(drv, buf, count, ID_REMOVE);
+}
+static DRIVER_ATTR_WO(remove_id);
+
+static struct attribute *dax_drv_attrs[] = {
+ &driver_attr_new_id.attr,
+ &driver_attr_remove_id.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(dax_drv);
+
+static int dax_bus_match(struct device *dev, struct device_driver *drv);
+
+static struct bus_type dax_bus_type = {
+ .name = "dax",
+ .uevent = dax_bus_uevent,
+ .match = dax_bus_match,
+ .drv_groups = dax_drv_groups,
+};
+
+static int dax_bus_match(struct device *dev, struct device_driver *drv)
+{
+ struct dax_device_driver *dax_drv = to_dax_drv(drv);
+
+ /*
+ * All but the 'device-dax' driver, which has 'match_always'
+ * set, requires an exact id match.
+ */
+ if (dax_drv->match_always)
+ return 1;
+
+ return dax_match_id(dax_drv, dev);
+}
+
+/*
+ * Rely on the fact that drvdata is set before the attributes are
+ * registered, and that the attributes are unregistered before drvdata
+ * is cleared to assume that drvdata is always valid.
+ */
+static ssize_t id_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct dax_region *dax_region = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", dax_region->id);
+}
+static DEVICE_ATTR_RO(id);
+
+static ssize_t region_size_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct dax_region *dax_region = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%llu\n", (unsigned long long)
+ resource_size(&dax_region->res));
+}
+static struct device_attribute dev_attr_region_size = __ATTR(size, 0444,
+ region_size_show, NULL);
+
+static ssize_t align_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct dax_region *dax_region = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%u\n", dax_region->align);
+}
+static DEVICE_ATTR_RO(align);
+
+static struct attribute *dax_region_attributes[] = {
+ &dev_attr_region_size.attr,
+ &dev_attr_align.attr,
+ &dev_attr_id.attr,
+ NULL,
+};
+
+static const struct attribute_group dax_region_attribute_group = {
+ .name = "dax_region",
+ .attrs = dax_region_attributes,
+};
+
+static const struct attribute_group *dax_region_attribute_groups[] = {
+ &dax_region_attribute_group,
+ NULL,
+};
+
+static void dax_region_free(struct kref *kref)
+{
+ struct dax_region *dax_region;
+
+ dax_region = container_of(kref, struct dax_region, kref);
+ kfree(dax_region);
+}
+
+void dax_region_put(struct dax_region *dax_region)
+{
+ kref_put(&dax_region->kref, dax_region_free);
+}
+EXPORT_SYMBOL_GPL(dax_region_put);
+
+static void dax_region_unregister(void *region)
+{
+ struct dax_region *dax_region = region;
+
+ sysfs_remove_groups(&dax_region->dev->kobj,
+ dax_region_attribute_groups);
+ dax_region_put(dax_region);
+}
+
+struct dax_region *alloc_dax_region(struct device *parent, int region_id,
+ struct resource *res, int target_node, unsigned int align,
+ unsigned long pfn_flags)
+{
+ struct dax_region *dax_region;
+
+ /*
+ * The DAX core assumes that it can store its private data in
+ * parent->driver_data. This WARN is a reminder / safeguard for
+ * developers of device-dax drivers.
+ */
+ if (dev_get_drvdata(parent)) {
+ dev_WARN(parent, "dax core failed to setup private data\n");
+ return NULL;
+ }
+
+ if (!IS_ALIGNED(res->start, align)
+ || !IS_ALIGNED(resource_size(res), align))
+ return NULL;
+
+ dax_region = kzalloc(sizeof(*dax_region), GFP_KERNEL);
+ if (!dax_region)
+ return NULL;
+
+ dev_set_drvdata(parent, dax_region);
+ memcpy(&dax_region->res, res, sizeof(*res));
+ dax_region->pfn_flags = pfn_flags;
+ kref_init(&dax_region->kref);
+ dax_region->id = region_id;
+ dax_region->align = align;
+ dax_region->dev = parent;
+ dax_region->target_node = target_node;
+ if (sysfs_create_groups(&parent->kobj, dax_region_attribute_groups)) {
+ kfree(dax_region);
+ return NULL;
+ }
+
+ kref_get(&dax_region->kref);
+ if (devm_add_action_or_reset(parent, dax_region_unregister, dax_region))
+ return NULL;
+ return dax_region;
+}
+EXPORT_SYMBOL_GPL(alloc_dax_region);
+
+static ssize_t size_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct dev_dax *dev_dax = to_dev_dax(dev);
+ unsigned long long size = resource_size(&dev_dax->region->res);
+
+ return sprintf(buf, "%llu\n", size);
+}
+static DEVICE_ATTR_RO(size);
+
+static int dev_dax_target_node(struct dev_dax *dev_dax)
+{
+ struct dax_region *dax_region = dev_dax->region;
+
+ return dax_region->target_node;
+}
+
+static ssize_t target_node_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct dev_dax *dev_dax = to_dev_dax(dev);
+
+ return sprintf(buf, "%d\n", dev_dax_target_node(dev_dax));
+}
+static DEVICE_ATTR_RO(target_node);
+
+static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ /*
+ * We only ever expect to handle device-dax instances, i.e. the
+ * @type argument to MODULE_ALIAS_DAX_DEVICE() is always zero
+ */
+ return sprintf(buf, DAX_DEVICE_MODALIAS_FMT "\n", 0);
+}
+static DEVICE_ATTR_RO(modalias);
+
+static umode_t dev_dax_visible(struct kobject *kobj, struct attribute *a, int n)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct dev_dax *dev_dax = to_dev_dax(dev);
+
+ if (a == &dev_attr_target_node.attr && dev_dax_target_node(dev_dax) < 0)
+ return 0;
+ return a->mode;
+}
+
+static struct attribute *dev_dax_attributes[] = {
+ &dev_attr_modalias.attr,
+ &dev_attr_size.attr,
+ &dev_attr_target_node.attr,
+ NULL,
+};
+
+static const struct attribute_group dev_dax_attribute_group = {
+ .attrs = dev_dax_attributes,
+ .is_visible = dev_dax_visible,
+};
+
+static const struct attribute_group *dax_attribute_groups[] = {
+ &dev_dax_attribute_group,
+ NULL,
+};
+
+void kill_dev_dax(struct dev_dax *dev_dax)
+{
+ struct dax_device *dax_dev = dev_dax->dax_dev;
+ struct inode *inode = dax_inode(dax_dev);
+
+ kill_dax(dax_dev);
+ unmap_mapping_range(inode->i_mapping, 0, 0, 1);
+}
+EXPORT_SYMBOL_GPL(kill_dev_dax);
+
+static void dev_dax_release(struct device *dev)
+{
+ struct dev_dax *dev_dax = to_dev_dax(dev);
+ struct dax_region *dax_region = dev_dax->region;
+ struct dax_device *dax_dev = dev_dax->dax_dev;
+
+ dax_region_put(dax_region);
+ put_dax(dax_dev);
+ kfree(dev_dax);
+}
+
+static void unregister_dev_dax(void *dev)
+{
+ struct dev_dax *dev_dax = to_dev_dax(dev);
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ kill_dev_dax(dev_dax);
+ device_del(dev);
+ put_device(dev);
+}
+
+struct dev_dax *__devm_create_dev_dax(struct dax_region *dax_region, int id,
+ struct dev_pagemap *pgmap, enum dev_dax_subsys subsys)
+{
+ struct device *parent = dax_region->dev;
+ struct dax_device *dax_dev;
+ struct dev_dax *dev_dax;
+ struct inode *inode;
+ struct device *dev;
+ int rc = -ENOMEM;
+
+ if (id < 0)
+ return ERR_PTR(-EINVAL);
+
+ dev_dax = kzalloc(sizeof(*dev_dax), GFP_KERNEL);
+ if (!dev_dax)
+ return ERR_PTR(-ENOMEM);
+
+ memcpy(&dev_dax->pgmap, pgmap, sizeof(*pgmap));
+
+ /*
+ * No 'host' or dax_operations since there is no access to this
+ * device outside of mmap of the resulting character device.
+ */
+ dax_dev = alloc_dax(dev_dax, NULL, NULL);
+ if (!dax_dev)
+ goto err;
+
+ /* a device_dax instance is dead while the driver is not attached */
+ kill_dax(dax_dev);
+
+ /* from here on we're committed to teardown via dax_dev_release() */
+ dev = &dev_dax->dev;
+ device_initialize(dev);
+
+ dev_dax->dax_dev = dax_dev;
+ dev_dax->region = dax_region;
+ dev_dax->target_node = dax_region->target_node;
+ kref_get(&dax_region->kref);
+
+ inode = dax_inode(dax_dev);
+ dev->devt = inode->i_rdev;
+ if (subsys == DEV_DAX_BUS)
+ dev->bus = &dax_bus_type;
+ else
+ dev->class = dax_class;
+ dev->parent = parent;
+ dev->groups = dax_attribute_groups;
+ dev->release = dev_dax_release;
+ dev_set_name(dev, "dax%d.%d", dax_region->id, id);
+
+ rc = device_add(dev);
+ if (rc) {
+ kill_dev_dax(dev_dax);
+ put_device(dev);
+ return ERR_PTR(rc);
+ }
+
+ rc = devm_add_action_or_reset(dax_region->dev, unregister_dev_dax, dev);
+ if (rc)
+ return ERR_PTR(rc);
+
+ return dev_dax;
+
+ err:
+ kfree(dev_dax);
+
+ return ERR_PTR(rc);
+}
+EXPORT_SYMBOL_GPL(__devm_create_dev_dax);
+
+static int match_always_count;
+
+int __dax_driver_register(struct dax_device_driver *dax_drv,
+ struct module *module, const char *mod_name)
+{
+ struct device_driver *drv = &dax_drv->drv;
+ int rc = 0;
+
+ INIT_LIST_HEAD(&dax_drv->ids);
+ drv->owner = module;
+ drv->name = mod_name;
+ drv->mod_name = mod_name;
+ drv->bus = &dax_bus_type;
+
+ /* there can only be one default driver */
+ mutex_lock(&dax_bus_lock);
+ match_always_count += dax_drv->match_always;
+ if (match_always_count > 1) {
+ match_always_count--;
+ WARN_ON(1);
+ rc = -EINVAL;
+ }
+ mutex_unlock(&dax_bus_lock);
+ if (rc)
+ return rc;
+ return driver_register(drv);
+}
+EXPORT_SYMBOL_GPL(__dax_driver_register);
+
+void dax_driver_unregister(struct dax_device_driver *dax_drv)
+{
+ struct device_driver *drv = &dax_drv->drv;
+ struct dax_id *dax_id, *_id;
+
+ mutex_lock(&dax_bus_lock);
+ match_always_count -= dax_drv->match_always;
+ list_for_each_entry_safe(dax_id, _id, &dax_drv->ids, list) {
+ list_del(&dax_id->list);
+ kfree(dax_id);
+ }
+ mutex_unlock(&dax_bus_lock);
+ driver_unregister(drv);
+}
+EXPORT_SYMBOL_GPL(dax_driver_unregister);
+
+int __init dax_bus_init(void)
+{
+ int rc;
+
+ if (IS_ENABLED(CONFIG_DEV_DAX_PMEM_COMPAT)) {
+ dax_class = class_create(THIS_MODULE, "dax");
+ if (IS_ERR(dax_class))
+ return PTR_ERR(dax_class);
+ }
+
+ rc = bus_register(&dax_bus_type);
+ if (rc)
+ class_destroy(dax_class);
+ return rc;
+}
+
+void __exit dax_bus_exit(void)
+{
+ bus_unregister(&dax_bus_type);
+ class_destroy(dax_class);
+}
diff --git a/drivers/dax/bus.h b/drivers/dax/bus.h
new file mode 100644
index 000000000000..8619e3299943
--- /dev/null
+++ b/drivers/dax/bus.h
@@ -0,0 +1,61 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright(c) 2016 - 2018 Intel Corporation. All rights reserved. */
+#ifndef __DAX_BUS_H__
+#define __DAX_BUS_H__
+#include <linux/device.h>
+
+struct dev_dax;
+struct resource;
+struct dax_device;
+struct dax_region;
+void dax_region_put(struct dax_region *dax_region);
+struct dax_region *alloc_dax_region(struct device *parent, int region_id,
+ struct resource *res, int target_node, unsigned int align,
+ unsigned long flags);
+
+enum dev_dax_subsys {
+ DEV_DAX_BUS,
+ DEV_DAX_CLASS,
+};
+
+struct dev_dax *__devm_create_dev_dax(struct dax_region *dax_region, int id,
+ struct dev_pagemap *pgmap, enum dev_dax_subsys subsys);
+
+static inline struct dev_dax *devm_create_dev_dax(struct dax_region *dax_region,
+ int id, struct dev_pagemap *pgmap)
+{
+ return __devm_create_dev_dax(dax_region, id, pgmap, DEV_DAX_BUS);
+}
+
+/* to be deleted when DEV_DAX_CLASS is removed */
+struct dev_dax *__dax_pmem_probe(struct device *dev, enum dev_dax_subsys subsys);
+
+struct dax_device_driver {
+ struct device_driver drv;
+ struct list_head ids;
+ int match_always;
+};
+
+int __dax_driver_register(struct dax_device_driver *dax_drv,
+ struct module *module, const char *mod_name);
+#define dax_driver_register(driver) \
+ __dax_driver_register(driver, THIS_MODULE, KBUILD_MODNAME)
+void dax_driver_unregister(struct dax_device_driver *dax_drv);
+void kill_dev_dax(struct dev_dax *dev_dax);
+
+#if IS_ENABLED(CONFIG_DEV_DAX_PMEM_COMPAT)
+int dev_dax_probe(struct device *dev);
+#endif
+
+/*
+ * While run_dax() is potentially a generic operation that could be
+ * defined in include/linux/dax.h we don't want to grow any users
+ * outside of drivers/dax/
+ */
+void run_dax(struct dax_device *dax_dev);
+
+#define MODULE_ALIAS_DAX_DEVICE(type) \
+ MODULE_ALIAS("dax:t" __stringify(type) "*")
+#define DAX_DEVICE_MODALIAS_FMT "dax:t%d"
+
+#endif /* __DAX_BUS_H__ */
diff --git a/drivers/dax/dax-private.h b/drivers/dax/dax-private.h
index b6fc4f04636d..a45612148ca0 100644
--- a/drivers/dax/dax-private.h
+++ b/drivers/dax/dax-private.h
@@ -16,10 +16,17 @@
#include <linux/device.h>
#include <linux/cdev.h>
+/* private routines between core files */
+struct dax_device;
+struct dax_device *inode_dax(struct inode *inode);
+struct inode *dax_inode(struct dax_device *dax_dev);
+int dax_bus_init(void);
+void dax_bus_exit(void);
+
/**
* struct dax_region - mapping infrastructure for dax devices
* @id: kernel-wide unique region for a memory range
- * @base: linear address corresponding to @res
+ * @target_node: effective numa node if this memory range is onlined
* @kref: to pin while other agents have a need to do lookups
* @dev: parent device backing this region
* @align: allocation and mapping alignment for child dax devices
@@ -28,8 +35,7 @@
*/
struct dax_region {
int id;
- struct ida ida;
- void *base;
+ int target_node;
struct kref kref;
struct device *dev;
unsigned int align;
@@ -38,20 +44,28 @@ struct dax_region {
};
/**
- * struct dev_dax - instance data for a subdivision of a dax region
+ * struct dev_dax - instance data for a subdivision of a dax region, and
+ * data while the device is activated in the driver.
* @region - parent region
* @dax_dev - core dax functionality
+ * @target_node: effective numa node if dev_dax memory range is onlined
* @dev - device core
- * @id - child id in the region
- * @num_resources - number of physical address extents in this device
- * @res - array of physical address ranges
+ * @pgmap - pgmap for memmap setup / lifetime (driver owned)
+ * @ref: pgmap reference count (driver owned)
+ * @cmp: @ref final put completion (driver owned)
*/
struct dev_dax {
struct dax_region *region;
struct dax_device *dax_dev;
+ int target_node;
struct device dev;
- int id;
- int num_resources;
- struct resource res[0];
+ struct dev_pagemap pgmap;
+ struct percpu_ref ref;
+ struct completion cmp;
};
+
+static inline struct dev_dax *to_dev_dax(struct device *dev)
+{
+ return container_of(dev, struct dev_dax, dev);
+}
#endif
diff --git a/drivers/dax/dax.h b/drivers/dax/dax.h
deleted file mode 100644
index f9e5feea742c..000000000000
--- a/drivers/dax/dax.h
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * Copyright(c) 2016 - 2017 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- */
-#ifndef __DAX_H__
-#define __DAX_H__
-struct dax_device;
-struct dax_device *inode_dax(struct inode *inode);
-struct inode *dax_inode(struct dax_device *dax_dev);
-#endif /* __DAX_H__ */
diff --git a/drivers/dax/device-dax.h b/drivers/dax/device-dax.h
deleted file mode 100644
index 688b051750bd..000000000000
--- a/drivers/dax/device-dax.h
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright(c) 2016 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- */
-#ifndef __DEVICE_DAX_H__
-#define __DEVICE_DAX_H__
-struct device;
-struct dev_dax;
-struct resource;
-struct dax_region;
-void dax_region_put(struct dax_region *dax_region);
-struct dax_region *alloc_dax_region(struct device *parent,
- int region_id, struct resource *res, unsigned int align,
- void *addr, unsigned long flags);
-struct dev_dax *devm_create_dev_dax(struct dax_region *dax_region,
- int id, struct resource *res, int count);
-#endif /* __DEVICE_DAX_H__ */
diff --git a/drivers/dax/device.c b/drivers/dax/device.c
index 948806e57cee..e428468ab661 100644
--- a/drivers/dax/device.c
+++ b/drivers/dax/device.c
@@ -1,15 +1,6 @@
-/*
- * Copyright(c) 2016 - 2017 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- */
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright(c) 2016-2018 Intel Corporation. All rights reserved. */
+#include <linux/memremap.h>
#include <linux/pagemap.h>
#include <linux/module.h>
#include <linux/device.h>
@@ -21,161 +12,39 @@
#include <linux/mm.h>
#include <linux/mman.h>
#include "dax-private.h"
-#include "dax.h"
+#include "bus.h"
-static struct class *dax_class;
-
-/*
- * Rely on the fact that drvdata is set before the attributes are
- * registered, and that the attributes are unregistered before drvdata
- * is cleared to assume that drvdata is always valid.
- */
-static ssize_t id_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct dax_region *dax_region = dev_get_drvdata(dev);
-
- return sprintf(buf, "%d\n", dax_region->id);
-}
-static DEVICE_ATTR_RO(id);
-
-static ssize_t region_size_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct dax_region *dax_region = dev_get_drvdata(dev);
-
- return sprintf(buf, "%llu\n", (unsigned long long)
- resource_size(&dax_region->res));
-}
-static struct device_attribute dev_attr_region_size = __ATTR(size, 0444,
- region_size_show, NULL);
-
-static ssize_t align_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct dax_region *dax_region = dev_get_drvdata(dev);
-
- return sprintf(buf, "%u\n", dax_region->align);
-}
-static DEVICE_ATTR_RO(align);
-
-static struct attribute *dax_region_attributes[] = {
- &dev_attr_region_size.attr,
- &dev_attr_align.attr,
- &dev_attr_id.attr,
- NULL,
-};
-
-static const struct attribute_group dax_region_attribute_group = {
- .name = "dax_region",
- .attrs = dax_region_attributes,
-};
-
-static const struct attribute_group *dax_region_attribute_groups[] = {
- &dax_region_attribute_group,
- NULL,
-};
-
-static void dax_region_free(struct kref *kref)
-{
- struct dax_region *dax_region;
-
- dax_region = container_of(kref, struct dax_region, kref);
- kfree(dax_region);
-}
-
-void dax_region_put(struct dax_region *dax_region)
+static struct dev_dax *ref_to_dev_dax(struct percpu_ref *ref)
{
- kref_put(&dax_region->kref, dax_region_free);
+ return container_of(ref, struct dev_dax, ref);
}
-EXPORT_SYMBOL_GPL(dax_region_put);
-static void dax_region_unregister(void *region)
+static void dev_dax_percpu_release(struct percpu_ref *ref)
{
- struct dax_region *dax_region = region;
+ struct dev_dax *dev_dax = ref_to_dev_dax(ref);
- sysfs_remove_groups(&dax_region->dev->kobj,
- dax_region_attribute_groups);
- dax_region_put(dax_region);
+ dev_dbg(&dev_dax->dev, "%s\n", __func__);
+ complete(&dev_dax->cmp);
}
-struct dax_region *alloc_dax_region(struct device *parent, int region_id,
- struct resource *res, unsigned int align, void *addr,
- unsigned long pfn_flags)
+static void dev_dax_percpu_exit(void *data)
{
- struct dax_region *dax_region;
-
- /*
- * The DAX core assumes that it can store its private data in
- * parent->driver_data. This WARN is a reminder / safeguard for
- * developers of device-dax drivers.
- */
- if (dev_get_drvdata(parent)) {
- dev_WARN(parent, "dax core failed to setup private data\n");
- return NULL;
- }
-
- if (!IS_ALIGNED(res->start, align)
- || !IS_ALIGNED(resource_size(res), align))
- return NULL;
-
- dax_region = kzalloc(sizeof(*dax_region), GFP_KERNEL);
- if (!dax_region)
- return NULL;
-
- dev_set_drvdata(parent, dax_region);
- memcpy(&dax_region->res, res, sizeof(*res));
- dax_region->pfn_flags = pfn_flags;
- kref_init(&dax_region->kref);
- dax_region->id = region_id;
- ida_init(&dax_region->ida);
- dax_region->align = align;
- dax_region->dev = parent;
- dax_region->base = addr;
- if (sysfs_create_groups(&parent->kobj, dax_region_attribute_groups)) {
- kfree(dax_region);
- return NULL;
- }
+ struct percpu_ref *ref = data;
+ struct dev_dax *dev_dax = ref_to_dev_dax(ref);
- kref_get(&dax_region->kref);
- if (devm_add_action_or_reset(parent, dax_region_unregister, dax_region))
- return NULL;
- return dax_region;
+ dev_dbg(&dev_dax->dev, "%s\n", __func__);
+ wait_for_completion(&dev_dax->cmp);
+ percpu_ref_exit(ref);
}
-EXPORT_SYMBOL_GPL(alloc_dax_region);
-static struct dev_dax *to_dev_dax(struct device *dev)
+static void dev_dax_percpu_kill(struct percpu_ref *data)
{
- return container_of(dev, struct dev_dax, dev);
-}
-
-static ssize_t size_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct dev_dax *dev_dax = to_dev_dax(dev);
- unsigned long long size = 0;
- int i;
+ struct percpu_ref *ref = data;
+ struct dev_dax *dev_dax = ref_to_dev_dax(ref);
- for (i = 0; i < dev_dax->num_resources; i++)
- size += resource_size(&dev_dax->res[i]);
-
- return sprintf(buf, "%llu\n", size);
+ dev_dbg(&dev_dax->dev, "%s\n", __func__);
+ percpu_ref_kill(ref);
}
-static DEVICE_ATTR_RO(size);
-
-static struct attribute *dev_dax_attributes[] = {
- &dev_attr_size.attr,
- NULL,
-};
-
-static const struct attribute_group dev_dax_attribute_group = {
- .attrs = dev_dax_attributes,
-};
-
-static const struct attribute_group *dax_attribute_groups[] = {
- &dev_dax_attribute_group,
- NULL,
-};
static int check_vma(struct dev_dax *dev_dax, struct vm_area_struct *vma,
const char *func)
@@ -226,21 +95,11 @@ static int check_vma(struct dev_dax *dev_dax, struct vm_area_struct *vma,
__weak phys_addr_t dax_pgoff_to_phys(struct dev_dax *dev_dax, pgoff_t pgoff,
unsigned long size)
{
- struct resource *res;
- /* gcc-4.6.3-nolibc for i386 complains that this is uninitialized */
- phys_addr_t uninitialized_var(phys);
- int i;
-
- for (i = 0; i < dev_dax->num_resources; i++) {
- res = &dev_dax->res[i];
- phys = pgoff * PAGE_SIZE + res->start;
- if (phys >= res->start && phys <= res->end)
- break;
- pgoff -= PHYS_PFN(resource_size(res));
- }
+ struct resource *res = &dev_dax->region->res;
+ phys_addr_t phys;
- if (i < dev_dax->num_resources) {
- res = &dev_dax->res[i];
+ phys = pgoff * PAGE_SIZE + res->start;
+ if (phys >= res->start && phys <= res->end) {
if (phys + size - 1 <= res->end)
return phys;
}
@@ -576,152 +435,100 @@ static const struct file_operations dax_fops = {
.mmap_supported_flags = MAP_SYNC,
};
-static void dev_dax_release(struct device *dev)
+static void dev_dax_cdev_del(void *cdev)
{
- struct dev_dax *dev_dax = to_dev_dax(dev);
- struct dax_region *dax_region = dev_dax->region;
- struct dax_device *dax_dev = dev_dax->dax_dev;
-
- if (dev_dax->id >= 0)
- ida_simple_remove(&dax_region->ida, dev_dax->id);
- dax_region_put(dax_region);
- put_dax(dax_dev);
- kfree(dev_dax);
+ cdev_del(cdev);
}
-static void kill_dev_dax(struct dev_dax *dev_dax)
+static void dev_dax_kill(void *dev_dax)
{
- struct dax_device *dax_dev = dev_dax->dax_dev;
- struct inode *inode = dax_inode(dax_dev);
-
- kill_dax(dax_dev);
- unmap_mapping_range(inode->i_mapping, 0, 0, 1);
+ kill_dev_dax(dev_dax);
}
-static void unregister_dev_dax(void *dev)
+int dev_dax_probe(struct device *dev)
{
struct dev_dax *dev_dax = to_dev_dax(dev);
struct dax_device *dax_dev = dev_dax->dax_dev;
- struct inode *inode = dax_inode(dax_dev);
- struct cdev *cdev = inode->i_cdev;
-
- dev_dbg(dev, "trace\n");
-
- kill_dev_dax(dev_dax);
- cdev_device_del(cdev, dev);
- put_device(dev);
-}
-
-struct dev_dax *devm_create_dev_dax(struct dax_region *dax_region,
- int id, struct resource *res, int count)
-{
- struct device *parent = dax_region->dev;
- struct dax_device *dax_dev;
- struct dev_dax *dev_dax;
+ struct resource *res = &dev_dax->region->res;
struct inode *inode;
- struct device *dev;
struct cdev *cdev;
- int rc, i;
-
- if (!count)
- return ERR_PTR(-EINVAL);
-
- dev_dax = kzalloc(struct_size(dev_dax, res, count), GFP_KERNEL);
- if (!dev_dax)
- return ERR_PTR(-ENOMEM);
-
- for (i = 0; i < count; i++) {
- if (!IS_ALIGNED(res[i].start, dax_region->align)
- || !IS_ALIGNED(resource_size(&res[i]),
- dax_region->align)) {
- rc = -EINVAL;
- break;
- }
- dev_dax->res[i].start = res[i].start;
- dev_dax->res[i].end = res[i].end;
+ void *addr;
+ int rc;
+
+ /* 1:1 map region resource range to device-dax instance range */
+ if (!devm_request_mem_region(dev, res->start, resource_size(res),
+ dev_name(dev))) {
+ dev_warn(dev, "could not reserve region %pR\n", res);
+ return -EBUSY;
}
- if (i < count)
- goto err_id;
+ init_completion(&dev_dax->cmp);
+ rc = percpu_ref_init(&dev_dax->ref, dev_dax_percpu_release, 0,
+ GFP_KERNEL);
+ if (rc)
+ return rc;
- if (id < 0) {
- id = ida_simple_get(&dax_region->ida, 0, 0, GFP_KERNEL);
- dev_dax->id = id;
- if (id < 0) {
- rc = id;
- goto err_id;
- }
- } else {
- /* region provider owns @id lifetime */
- dev_dax->id = -1;
- }
+ rc = devm_add_action_or_reset(dev, dev_dax_percpu_exit, &dev_dax->ref);
+ if (rc)
+ return rc;
- /*
- * No 'host' or dax_operations since there is no access to this
- * device outside of mmap of the resulting character device.
- */
- dax_dev = alloc_dax(dev_dax, NULL, NULL);
- if (!dax_dev) {
- rc = -ENOMEM;
- goto err_dax;
+ dev_dax->pgmap.ref = &dev_dax->ref;
+ dev_dax->pgmap.kill = dev_dax_percpu_kill;
+ addr = devm_memremap_pages(dev, &dev_dax->pgmap);
+ if (IS_ERR(addr)) {
+ devm_remove_action(dev, dev_dax_percpu_exit, &dev_dax->ref);
+ percpu_ref_exit(&dev_dax->ref);
+ return PTR_ERR(addr);
}
- /* from here on we're committed to teardown via dax_dev_release() */
- dev = &dev_dax->dev;
- device_initialize(dev);
-
inode = dax_inode(dax_dev);
cdev = inode->i_cdev;
cdev_init(cdev, &dax_fops);
- cdev->owner = parent->driver->owner;
-
- dev_dax->num_resources = count;
- dev_dax->dax_dev = dax_dev;
- dev_dax->region = dax_region;
- kref_get(&dax_region->kref);
-
- dev->devt = inode->i_rdev;
- dev->class = dax_class;
- dev->parent = parent;
- dev->groups = dax_attribute_groups;
- dev->release = dev_dax_release;
- dev_set_name(dev, "dax%d.%d", dax_region->id, id);
-
- rc = cdev_device_add(cdev, dev);
- if (rc) {
- kill_dev_dax(dev_dax);
- put_device(dev);
- return ERR_PTR(rc);
- }
-
- rc = devm_add_action_or_reset(dax_region->dev, unregister_dev_dax, dev);
+ if (dev->class) {
+ /* for the CONFIG_DEV_DAX_PMEM_COMPAT case */
+ cdev->owner = dev->parent->driver->owner;
+ } else
+ cdev->owner = dev->driver->owner;
+ cdev_set_parent(cdev, &dev->kobj);
+ rc = cdev_add(cdev, dev->devt, 1);
if (rc)
- return ERR_PTR(rc);
+ return rc;
- return dev_dax;
+ rc = devm_add_action_or_reset(dev, dev_dax_cdev_del, cdev);
+ if (rc)
+ return rc;
- err_dax:
- if (dev_dax->id >= 0)
- ida_simple_remove(&dax_region->ida, dev_dax->id);
- err_id:
- kfree(dev_dax);
+ run_dax(dax_dev);
+ return devm_add_action_or_reset(dev, dev_dax_kill, dev_dax);
+}
+EXPORT_SYMBOL_GPL(dev_dax_probe);
- return ERR_PTR(rc);
+static int dev_dax_remove(struct device *dev)
+{
+ /* all probe actions are unwound by devm */
+ return 0;
}
-EXPORT_SYMBOL_GPL(devm_create_dev_dax);
+
+static struct dax_device_driver device_dax_driver = {
+ .drv = {
+ .probe = dev_dax_probe,
+ .remove = dev_dax_remove,
+ },
+ .match_always = 1,
+};
static int __init dax_init(void)
{
- dax_class = class_create(THIS_MODULE, "dax");
- return PTR_ERR_OR_ZERO(dax_class);
+ return dax_driver_register(&device_dax_driver);
}
static void __exit dax_exit(void)
{
- class_destroy(dax_class);
+ dax_driver_unregister(&device_dax_driver);
}
MODULE_AUTHOR("Intel Corporation");
MODULE_LICENSE("GPL v2");
-subsys_initcall(dax_init);
+module_init(dax_init);
module_exit(dax_exit);
+MODULE_ALIAS_DAX_DEVICE(0);
diff --git a/drivers/dax/kmem.c b/drivers/dax/kmem.c
new file mode 100644
index 000000000000..a02318c6d28a
--- /dev/null
+++ b/drivers/dax/kmem.c
@@ -0,0 +1,108 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright(c) 2016-2019 Intel Corporation. All rights reserved. */
+#include <linux/memremap.h>
+#include <linux/pagemap.h>
+#include <linux/memory.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/pfn_t.h>
+#include <linux/slab.h>
+#include <linux/dax.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include "dax-private.h"
+#include "bus.h"
+
+int dev_dax_kmem_probe(struct device *dev)
+{
+ struct dev_dax *dev_dax = to_dev_dax(dev);
+ struct resource *res = &dev_dax->region->res;
+ resource_size_t kmem_start;
+ resource_size_t kmem_size;
+ resource_size_t kmem_end;
+ struct resource *new_res;
+ int numa_node;
+ int rc;
+
+ /*
+ * Ensure good NUMA information for the persistent memory.
+ * Without this check, there is a risk that slow memory
+ * could be mixed in a node with faster memory, causing
+ * unavoidable performance issues.
+ */
+ numa_node = dev_dax->target_node;
+ if (numa_node < 0) {
+ dev_warn(dev, "rejecting DAX region %pR with invalid node: %d\n",
+ res, numa_node);
+ return -EINVAL;
+ }
+
+ /* Hotplug starting at the beginning of the next block: */
+ kmem_start = ALIGN(res->start, memory_block_size_bytes());
+
+ kmem_size = resource_size(res);
+ /* Adjust the size down to compensate for moving up kmem_start: */
+ kmem_size -= kmem_start - res->start;
+ /* Align the size down to cover only complete blocks: */
+ kmem_size &= ~(memory_block_size_bytes() - 1);
+ kmem_end = kmem_start + kmem_size;
+
+ /* Region is permanently reserved. Hot-remove not yet implemented. */
+ new_res = request_mem_region(kmem_start, kmem_size, dev_name(dev));
+ if (!new_res) {
+ dev_warn(dev, "could not reserve region [%pa-%pa]\n",
+ &kmem_start, &kmem_end);
+ return -EBUSY;
+ }
+
+ /*
+ * Set flags appropriate for System RAM. Leave ..._BUSY clear
+ * so that add_memory() can add a child resource. Do not
+ * inherit flags from the parent since it may set new flags
+ * unknown to us that will break add_memory() below.
+ */
+ new_res->flags = IORESOURCE_SYSTEM_RAM;
+ new_res->name = dev_name(dev);
+
+ rc = add_memory(numa_node, new_res->start, resource_size(new_res));
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+static int dev_dax_kmem_remove(struct device *dev)
+{
+ /*
+ * Purposely leak the request_mem_region() for the device-dax
+ * range and return '0' to ->remove() attempts. The removal of
+ * the device from the driver always succeeds, but the region
+ * is permanently pinned as reserved by the unreleased
+ * request_mem_region().
+ */
+ return 0;
+}
+
+static struct dax_device_driver device_dax_kmem_driver = {
+ .drv = {
+ .probe = dev_dax_kmem_probe,
+ .remove = dev_dax_kmem_remove,
+ },
+};
+
+static int __init dax_kmem_init(void)
+{
+ return dax_driver_register(&device_dax_kmem_driver);
+}
+
+static void __exit dax_kmem_exit(void)
+{
+ dax_driver_unregister(&device_dax_kmem_driver);
+}
+
+MODULE_AUTHOR("Intel Corporation");
+MODULE_LICENSE("GPL v2");
+module_init(dax_kmem_init);
+module_exit(dax_kmem_exit);
+MODULE_ALIAS_DAX_DEVICE(0);
diff --git a/drivers/dax/pmem.c b/drivers/dax/pmem.c
deleted file mode 100644
index 2c1f459c0c63..000000000000
--- a/drivers/dax/pmem.c
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * Copyright(c) 2016 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- */
-#include <linux/percpu-refcount.h>
-#include <linux/memremap.h>
-#include <linux/module.h>
-#include <linux/pfn_t.h>
-#include "../nvdimm/pfn.h"
-#include "../nvdimm/nd.h"
-#include "device-dax.h"
-
-struct dax_pmem {
- struct device *dev;
- struct percpu_ref ref;
- struct dev_pagemap pgmap;
- struct completion cmp;
-};
-
-static struct dax_pmem *to_dax_pmem(struct percpu_ref *ref)
-{
- return container_of(ref, struct dax_pmem, ref);
-}
-
-static void dax_pmem_percpu_release(struct percpu_ref *ref)
-{
- struct dax_pmem *dax_pmem = to_dax_pmem(ref);
-
- dev_dbg(dax_pmem->dev, "trace\n");
- complete(&dax_pmem->cmp);
-}
-
-static void dax_pmem_percpu_exit(void *data)
-{
- struct percpu_ref *ref = data;
- struct dax_pmem *dax_pmem = to_dax_pmem(ref);
-
- dev_dbg(dax_pmem->dev, "trace\n");
- wait_for_completion(&dax_pmem->cmp);
- percpu_ref_exit(ref);
-}
-
-static void dax_pmem_percpu_kill(struct percpu_ref *ref)
-{
- struct dax_pmem *dax_pmem = to_dax_pmem(ref);
-
- dev_dbg(dax_pmem->dev, "trace\n");
- percpu_ref_kill(ref);
-}
-
-static int dax_pmem_probe(struct device *dev)
-{
- void *addr;
- struct resource res;
- int rc, id, region_id;
- struct nd_pfn_sb *pfn_sb;
- struct dev_dax *dev_dax;
- struct dax_pmem *dax_pmem;
- struct nd_namespace_io *nsio;
- struct dax_region *dax_region;
- struct nd_namespace_common *ndns;
- struct nd_dax *nd_dax = to_nd_dax(dev);
- struct nd_pfn *nd_pfn = &nd_dax->nd_pfn;
-
- ndns = nvdimm_namespace_common_probe(dev);
- if (IS_ERR(ndns))
- return PTR_ERR(ndns);
- nsio = to_nd_namespace_io(&ndns->dev);
-
- dax_pmem = devm_kzalloc(dev, sizeof(*dax_pmem), GFP_KERNEL);
- if (!dax_pmem)
- return -ENOMEM;
-
- /* parse the 'pfn' info block via ->rw_bytes */
- rc = devm_nsio_enable(dev, nsio);
- if (rc)
- return rc;
- rc = nvdimm_setup_pfn(nd_pfn, &dax_pmem->pgmap);
- if (rc)
- return rc;
- devm_nsio_disable(dev, nsio);
-
- pfn_sb = nd_pfn->pfn_sb;
-
- if (!devm_request_mem_region(dev, nsio->res.start,
- resource_size(&nsio->res),
- dev_name(&ndns->dev))) {
- dev_warn(dev, "could not reserve region %pR\n", &nsio->res);
- return -EBUSY;
- }
-
- dax_pmem->dev = dev;
- init_completion(&dax_pmem->cmp);
- rc = percpu_ref_init(&dax_pmem->ref, dax_pmem_percpu_release, 0,
- GFP_KERNEL);
- if (rc)
- return rc;
-
- rc = devm_add_action(dev, dax_pmem_percpu_exit, &dax_pmem->ref);
- if (rc) {
- percpu_ref_exit(&dax_pmem->ref);
- return rc;
- }
-
- dax_pmem->pgmap.ref = &dax_pmem->ref;
- dax_pmem->pgmap.kill = dax_pmem_percpu_kill;
- addr = devm_memremap_pages(dev, &dax_pmem->pgmap);
- if (IS_ERR(addr))
- return PTR_ERR(addr);
-
- /* adjust the dax_region resource to the start of data */
- memcpy(&res, &dax_pmem->pgmap.res, sizeof(res));
- res.start += le64_to_cpu(pfn_sb->dataoff);
-
- rc = sscanf(dev_name(&ndns->dev), "namespace%d.%d", &region_id, &id);
- if (rc != 2)
- return -EINVAL;
-
- dax_region = alloc_dax_region(dev, region_id, &res,
- le32_to_cpu(pfn_sb->align), addr, PFN_DEV|PFN_MAP);
- if (!dax_region)
- return -ENOMEM;
-
- /* TODO: support for subdividing a dax region... */
- dev_dax = devm_create_dev_dax(dax_region, id, &res, 1);
-
- /* child dev_dax instances now own the lifetime of the dax_region */
- dax_region_put(dax_region);
-
- return PTR_ERR_OR_ZERO(dev_dax);
-}
-
-static struct nd_device_driver dax_pmem_driver = {
- .probe = dax_pmem_probe,
- .drv = {
- .name = "dax_pmem",
- },
- .type = ND_DRIVER_DAX_PMEM,
-};
-
-module_nd_driver(dax_pmem_driver);
-
-MODULE_LICENSE("GPL v2");
-MODULE_AUTHOR("Intel Corporation");
-MODULE_ALIAS_ND_DEVICE(ND_DEVICE_DAX_PMEM);
diff --git a/drivers/dax/pmem/Makefile b/drivers/dax/pmem/Makefile
new file mode 100644
index 000000000000..e2e79bd3fdcf
--- /dev/null
+++ b/drivers/dax/pmem/Makefile
@@ -0,0 +1,7 @@
+obj-$(CONFIG_DEV_DAX_PMEM) += dax_pmem.o
+obj-$(CONFIG_DEV_DAX_PMEM) += dax_pmem_core.o
+obj-$(CONFIG_DEV_DAX_PMEM_COMPAT) += dax_pmem_compat.o
+
+dax_pmem-y := pmem.o
+dax_pmem_core-y := core.o
+dax_pmem_compat-y := compat.o
diff --git a/drivers/dax/pmem/compat.c b/drivers/dax/pmem/compat.c
new file mode 100644
index 000000000000..d7b15e6f30c5
--- /dev/null
+++ b/drivers/dax/pmem/compat.c
@@ -0,0 +1,73 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright(c) 2016 - 2018 Intel Corporation. All rights reserved. */
+#include <linux/percpu-refcount.h>
+#include <linux/memremap.h>
+#include <linux/module.h>
+#include <linux/pfn_t.h>
+#include <linux/nd.h>
+#include "../bus.h"
+
+/* we need the private definitions to implement compat suport */
+#include "../dax-private.h"
+
+static int dax_pmem_compat_probe(struct device *dev)
+{
+ struct dev_dax *dev_dax = __dax_pmem_probe(dev, DEV_DAX_CLASS);
+ int rc;
+
+ if (IS_ERR(dev_dax))
+ return PTR_ERR(dev_dax);
+
+ if (!devres_open_group(&dev_dax->dev, dev_dax, GFP_KERNEL))
+ return -ENOMEM;
+
+ device_lock(&dev_dax->dev);
+ rc = dev_dax_probe(&dev_dax->dev);
+ device_unlock(&dev_dax->dev);
+
+ devres_close_group(&dev_dax->dev, dev_dax);
+ if (rc)
+ devres_release_group(&dev_dax->dev, dev_dax);
+
+ return rc;
+}
+
+static int dax_pmem_compat_release(struct device *dev, void *data)
+{
+ device_lock(dev);
+ devres_release_group(dev, to_dev_dax(dev));
+ device_unlock(dev);
+
+ return 0;
+}
+
+static int dax_pmem_compat_remove(struct device *dev)
+{
+ device_for_each_child(dev, NULL, dax_pmem_compat_release);
+ return 0;
+}
+
+static struct nd_device_driver dax_pmem_compat_driver = {
+ .probe = dax_pmem_compat_probe,
+ .remove = dax_pmem_compat_remove,
+ .drv = {
+ .name = "dax_pmem_compat",
+ },
+ .type = ND_DRIVER_DAX_PMEM,
+};
+
+static int __init dax_pmem_compat_init(void)
+{
+ return nd_driver_register(&dax_pmem_compat_driver);
+}
+module_init(dax_pmem_compat_init);
+
+static void __exit dax_pmem_compat_exit(void)
+{
+ driver_unregister(&dax_pmem_compat_driver.drv);
+}
+module_exit(dax_pmem_compat_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Intel Corporation");
+MODULE_ALIAS_ND_DEVICE(ND_DEVICE_DAX_PMEM);
diff --git a/drivers/dax/pmem/core.c b/drivers/dax/pmem/core.c
new file mode 100644
index 000000000000..f71019ce0647
--- /dev/null
+++ b/drivers/dax/pmem/core.c
@@ -0,0 +1,71 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright(c) 2016 - 2018 Intel Corporation. All rights reserved. */
+#include <linux/memremap.h>
+#include <linux/module.h>
+#include <linux/pfn_t.h>
+#include "../../nvdimm/pfn.h"
+#include "../../nvdimm/nd.h"
+#include "../bus.h"
+
+struct dev_dax *__dax_pmem_probe(struct device *dev, enum dev_dax_subsys subsys)
+{
+ struct resource res;
+ int rc, id, region_id;
+ resource_size_t offset;
+ struct nd_pfn_sb *pfn_sb;
+ struct dev_dax *dev_dax;
+ struct nd_namespace_io *nsio;
+ struct dax_region *dax_region;
+ struct dev_pagemap pgmap = { 0 };
+ struct nd_namespace_common *ndns;
+ struct nd_dax *nd_dax = to_nd_dax(dev);
+ struct nd_pfn *nd_pfn = &nd_dax->nd_pfn;
+ struct nd_region *nd_region = to_nd_region(dev->parent);
+
+ ndns = nvdimm_namespace_common_probe(dev);
+ if (IS_ERR(ndns))
+ return ERR_CAST(ndns);
+ nsio = to_nd_namespace_io(&ndns->dev);
+
+ /* parse the 'pfn' info block via ->rw_bytes */
+ rc = devm_nsio_enable(dev, nsio);
+ if (rc)
+ return ERR_PTR(rc);
+ rc = nvdimm_setup_pfn(nd_pfn, &pgmap);
+ if (rc)
+ return ERR_PTR(rc);
+ devm_nsio_disable(dev, nsio);
+
+ /* reserve the metadata area, device-dax will reserve the data */
+ pfn_sb = nd_pfn->pfn_sb;
+ offset = le64_to_cpu(pfn_sb->dataoff);
+ if (!devm_request_mem_region(dev, nsio->res.start, offset,
+ dev_name(&ndns->dev))) {
+ dev_warn(dev, "could not reserve metadata\n");
+ return ERR_PTR(-EBUSY);
+ }
+
+ rc = sscanf(dev_name(&ndns->dev), "namespace%d.%d", &region_id, &id);
+ if (rc != 2)
+ return ERR_PTR(-EINVAL);
+
+ /* adjust the dax_region resource to the start of data */
+ memcpy(&res, &pgmap.res, sizeof(res));
+ res.start += offset;
+ dax_region = alloc_dax_region(dev, region_id, &res,
+ nd_region->target_node, le32_to_cpu(pfn_sb->align),
+ PFN_DEV|PFN_MAP);
+ if (!dax_region)
+ return ERR_PTR(-ENOMEM);
+
+ dev_dax = __devm_create_dev_dax(dax_region, id, &pgmap, subsys);
+
+ /* child dev_dax instances now own the lifetime of the dax_region */
+ dax_region_put(dax_region);
+
+ return dev_dax;
+}
+EXPORT_SYMBOL_GPL(__dax_pmem_probe);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Intel Corporation");
diff --git a/drivers/dax/pmem/pmem.c b/drivers/dax/pmem/pmem.c
new file mode 100644
index 000000000000..0ae4238a0ef8
--- /dev/null
+++ b/drivers/dax/pmem/pmem.c
@@ -0,0 +1,40 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright(c) 2016 - 2018 Intel Corporation. All rights reserved. */
+#include <linux/percpu-refcount.h>
+#include <linux/memremap.h>
+#include <linux/module.h>
+#include <linux/pfn_t.h>
+#include <linux/nd.h>
+#include "../bus.h"
+
+static int dax_pmem_probe(struct device *dev)
+{
+ return PTR_ERR_OR_ZERO(__dax_pmem_probe(dev, DEV_DAX_BUS));
+}
+
+static struct nd_device_driver dax_pmem_driver = {
+ .probe = dax_pmem_probe,
+ .drv = {
+ .name = "dax_pmem",
+ },
+ .type = ND_DRIVER_DAX_PMEM,
+};
+
+static int __init dax_pmem_init(void)
+{
+ return nd_driver_register(&dax_pmem_driver);
+}
+module_init(dax_pmem_init);
+
+static void __exit dax_pmem_exit(void)
+{
+ driver_unregister(&dax_pmem_driver.drv);
+}
+module_exit(dax_pmem_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Intel Corporation");
+#if !IS_ENABLED(CONFIG_DEV_DAX_PMEM_COMPAT)
+/* For compat builds, don't load this module by default */
+MODULE_ALIAS_ND_DEVICE(ND_DEVICE_DAX_PMEM);
+#endif
diff --git a/drivers/dax/super.c b/drivers/dax/super.c
index 6e928f37d084..0a339b85133e 100644
--- a/drivers/dax/super.c
+++ b/drivers/dax/super.c
@@ -22,6 +22,7 @@
#include <linux/uio.h>
#include <linux/dax.h>
#include <linux/fs.h>
+#include "dax-private.h"
static dev_t dax_devt;
DEFINE_STATIC_SRCU(dax_srcu);
@@ -86,12 +87,14 @@ bool __bdev_dax_supported(struct block_device *bdev, int blocksize)
{
struct dax_device *dax_dev;
bool dax_enabled = false;
+ pgoff_t pgoff, pgoff_end;
struct request_queue *q;
- pgoff_t pgoff;
- int err, id;
- pfn_t pfn;
- long len;
char buf[BDEVNAME_SIZE];
+ void *kaddr, *end_kaddr;
+ pfn_t pfn, end_pfn;
+ sector_t last_page;
+ long len, len2;
+ int err, id;
if (blocksize != PAGE_SIZE) {
pr_debug("%s: error: unsupported blocksize for dax\n",
@@ -113,6 +116,14 @@ bool __bdev_dax_supported(struct block_device *bdev, int blocksize)
return false;
}
+ last_page = PFN_DOWN(i_size_read(bdev->bd_inode) - 1) * 8;
+ err = bdev_dax_pgoff(bdev, last_page, PAGE_SIZE, &pgoff_end);
+ if (err) {
+ pr_debug("%s: error: unaligned partition for dax\n",
+ bdevname(bdev, buf));
+ return false;
+ }
+
dax_dev = dax_get_by_host(bdev->bd_disk->disk_name);
if (!dax_dev) {
pr_debug("%s: error: device does not support dax\n",
@@ -121,14 +132,15 @@ bool __bdev_dax_supported(struct block_device *bdev, int blocksize)
}
id = dax_read_lock();
- len = dax_direct_access(dax_dev, pgoff, 1, NULL, &pfn);
+ len = dax_direct_access(dax_dev, pgoff, 1, &kaddr, &pfn);
+ len2 = dax_direct_access(dax_dev, pgoff_end, 1, &end_kaddr, &end_pfn);
dax_read_unlock(id);
put_dax(dax_dev);
- if (len < 1) {
+ if (len < 1 || len2 < 1) {
pr_debug("%s: error: dax access failed (%ld)\n",
- bdevname(bdev, buf), len);
+ bdevname(bdev, buf), len < 1 ? len : len2);
return false;
}
@@ -143,13 +155,20 @@ bool __bdev_dax_supported(struct block_device *bdev, int blocksize)
*/
WARN_ON(IS_ENABLED(CONFIG_ARCH_HAS_PMEM_API));
dax_enabled = true;
- } else if (pfn_t_devmap(pfn)) {
- struct dev_pagemap *pgmap;
+ } else if (pfn_t_devmap(pfn) && pfn_t_devmap(end_pfn)) {
+ struct dev_pagemap *pgmap, *end_pgmap;
pgmap = get_dev_pagemap(pfn_t_to_pfn(pfn), NULL);
- if (pgmap && pgmap->type == MEMORY_DEVICE_FS_DAX)
+ end_pgmap = get_dev_pagemap(pfn_t_to_pfn(end_pfn), NULL);
+ if (pgmap && pgmap == end_pgmap && pgmap->type == MEMORY_DEVICE_FS_DAX
+ && pfn_t_to_page(pfn)->pgmap == pgmap
+ && pfn_t_to_page(end_pfn)->pgmap == pgmap
+ && pfn_t_to_pfn(pfn) == PHYS_PFN(__pa(kaddr))
+ && pfn_t_to_pfn(end_pfn) == PHYS_PFN(__pa(end_kaddr)))
dax_enabled = true;
put_dev_pagemap(pgmap);
+ put_dev_pagemap(end_pgmap);
+
}
if (!dax_enabled) {
@@ -365,11 +384,15 @@ void kill_dax(struct dax_device *dax_dev)
spin_lock(&dax_host_lock);
hlist_del_init(&dax_dev->list);
spin_unlock(&dax_host_lock);
-
- dax_dev->private = NULL;
}
EXPORT_SYMBOL_GPL(kill_dax);
+void run_dax(struct dax_device *dax_dev)
+{
+ set_bit(DAXDEV_ALIVE, &dax_dev->flags);
+}
+EXPORT_SYMBOL_GPL(run_dax);
+
static struct inode *dax_alloc_inode(struct super_block *sb)
{
struct dax_device *dax_dev;
@@ -584,6 +607,8 @@ EXPORT_SYMBOL_GPL(dax_inode);
void *dax_get_private(struct dax_device *dax_dev)
{
+ if (!test_bit(DAXDEV_ALIVE, &dax_dev->flags))
+ return NULL;
return dax_dev->private;
}
EXPORT_SYMBOL_GPL(dax_get_private);
@@ -597,7 +622,7 @@ static void init_once(void *_dax_dev)
inode_init_once(inode);
}
-static int __dax_fs_init(void)
+static int dax_fs_init(void)
{
int rc;
@@ -629,35 +654,45 @@ static int __dax_fs_init(void)
return rc;
}
-static void __dax_fs_exit(void)
+static void dax_fs_exit(void)
{
kern_unmount(dax_mnt);
unregister_filesystem(&dax_fs_type);
kmem_cache_destroy(dax_cache);
}
-static int __init dax_fs_init(void)
+static int __init dax_core_init(void)
{
int rc;
- rc = __dax_fs_init();
+ rc = dax_fs_init();
if (rc)
return rc;
rc = alloc_chrdev_region(&dax_devt, 0, MINORMASK+1, "dax");
if (rc)
- __dax_fs_exit();
- return rc;
+ goto err_chrdev;
+
+ rc = dax_bus_init();
+ if (rc)
+ goto err_bus;
+ return 0;
+
+err_bus:
+ unregister_chrdev_region(dax_devt, MINORMASK+1);
+err_chrdev:
+ dax_fs_exit();
+ return 0;
}
-static void __exit dax_fs_exit(void)
+static void __exit dax_core_exit(void)
{
unregister_chrdev_region(dax_devt, MINORMASK+1);
ida_destroy(&dax_minor_ida);
- __dax_fs_exit();
+ dax_fs_exit();
}
MODULE_AUTHOR("Intel Corporation");
MODULE_LICENSE("GPL v2");
-subsys_initcall(dax_fs_init);
-module_exit(dax_fs_exit);
+subsys_initcall(dax_core_init);
+module_exit(dax_core_exit);
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index d2286c7f7222..0b1dfb5bf2d9 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -218,6 +218,20 @@ config FSL_EDMA
multiplexing capability for DMA request sources(slot).
This module can be found on Freescale Vybrid and LS-1 SoCs.
+config FSL_QDMA
+ tristate "NXP Layerscape qDMA engine support"
+ depends on ARM || ARM64
+ select DMA_ENGINE
+ select DMA_VIRTUAL_CHANNELS
+ select DMA_ENGINE_RAID
+ select ASYNC_TX_ENABLE_CHANNEL_SWITCH
+ help
+ Support the NXP Layerscape qDMA engine with command queue and legacy mode.
+ Channel virtualization is supported through enqueuing of DMA jobs to,
+ or dequeuing DMA jobs from, different work queues.
+ This module can be found on NXP Layerscape SoCs.
+ The qdma driver only work on SoCs with a DPAA hardware block.
+
config FSL_RAID
tristate "Freescale RAID engine Support"
depends on FSL_SOC && !ASYNC_TX_ENABLE_CHANNEL_SWITCH
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index 09571a81353d..6126e1c3a875 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -33,6 +33,7 @@ obj-$(CONFIG_EP93XX_DMA) += ep93xx_dma.o
obj-$(CONFIG_FSL_DMA) += fsldma.o
obj-$(CONFIG_FSL_EDMA) += fsl-edma.o fsl-edma-common.o
obj-$(CONFIG_MCF_EDMA) += mcf-edma.o fsl-edma-common.o
+obj-$(CONFIG_FSL_QDMA) += fsl-qdma.o
obj-$(CONFIG_FSL_RAID) += fsl_raid.o
obj-$(CONFIG_HSU_DMA) += hsu/
obj-$(CONFIG_IMG_MDC_DMA) += img-mdc-dma.o
diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c
index 01d936c9fe89..a0a9cd76c1d4 100644
--- a/drivers/dma/at_hdmac.c
+++ b/drivers/dma/at_hdmac.c
@@ -134,7 +134,6 @@ static struct at_desc *atc_desc_get(struct at_dma_chan *atchan)
struct at_desc *ret = NULL;
unsigned long flags;
unsigned int i = 0;
- LIST_HEAD(tmp_list);
spin_lock_irqsave(&atchan->lock, flags);
list_for_each_entry_safe(desc, _desc, &atchan->free_list, desc_node) {
@@ -1387,8 +1386,6 @@ static int atc_pause(struct dma_chan *chan)
int chan_id = atchan->chan_common.chan_id;
unsigned long flags;
- LIST_HEAD(list);
-
dev_vdbg(chan2dev(chan), "%s\n", __func__);
spin_lock_irqsave(&atchan->lock, flags);
@@ -1408,8 +1405,6 @@ static int atc_resume(struct dma_chan *chan)
int chan_id = atchan->chan_common.chan_id;
unsigned long flags;
- LIST_HEAD(list);
-
dev_vdbg(chan2dev(chan), "%s\n", __func__);
if (!atc_chan_is_paused(atchan))
diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c
index ae10f5614f95..ec8a291d62ba 100644
--- a/drivers/dma/bcm2835-dma.c
+++ b/drivers/dma/bcm2835-dma.c
@@ -2,9 +2,6 @@
/*
* BCM2835 DMA engine support
*
- * This driver only supports cyclic DMA transfers
- * as needed for the I2S module.
- *
* Author: Florian Meier <florian.meier@koalo.de>
* Copyright 2013
*
@@ -42,7 +39,6 @@
struct bcm2835_dmadev {
struct dma_device ddev;
- spinlock_t lock;
void __iomem *base;
struct device_dma_parameters dma_parms;
};
@@ -64,7 +60,6 @@ struct bcm2835_cb_entry {
struct bcm2835_chan {
struct virt_dma_chan vc;
- struct list_head node;
struct dma_slave_config cfg;
unsigned int dreq;
@@ -312,8 +307,7 @@ static struct bcm2835_desc *bcm2835_dma_create_cb_chain(
return NULL;
/* allocate and setup the descriptor. */
- d = kzalloc(sizeof(*d) + frames * sizeof(struct bcm2835_cb_entry),
- gfp);
+ d = kzalloc(struct_size(d, cb_list, frames), gfp);
if (!d)
return NULL;
@@ -406,7 +400,7 @@ static void bcm2835_dma_fill_cb_chain_with_sg(
}
}
-static int bcm2835_dma_abort(struct bcm2835_chan *c)
+static void bcm2835_dma_abort(struct bcm2835_chan *c)
{
void __iomem *chan_base = c->chan_base;
long int timeout = 10000;
@@ -416,7 +410,7 @@ static int bcm2835_dma_abort(struct bcm2835_chan *c)
* (The ACTIVE flag in the CS register is not a reliable indicator.)
*/
if (!readl(chan_base + BCM2835_DMA_ADDR))
- return 0;
+ return;
/* Write 0 to the active bit - Pause the DMA */
writel(0, chan_base + BCM2835_DMA_CS);
@@ -432,7 +426,6 @@ static int bcm2835_dma_abort(struct bcm2835_chan *c)
"failed to complete outstanding writes\n");
writel(BCM2835_DMA_RESET, chan_base + BCM2835_DMA_CS);
- return 0;
}
static void bcm2835_dma_start_desc(struct bcm2835_chan *c)
@@ -504,8 +497,12 @@ static int bcm2835_dma_alloc_chan_resources(struct dma_chan *chan)
dev_dbg(dev, "Allocating DMA channel %d\n", c->ch);
+ /*
+ * Control blocks are 256 bit in length and must start at a 256 bit
+ * (32 byte) aligned address (BCM2835 ARM Peripherals, sec. 4.2.1.1).
+ */
c->cb_pool = dma_pool_create(dev_name(dev), dev,
- sizeof(struct bcm2835_dma_cb), 0, 0);
+ sizeof(struct bcm2835_dma_cb), 32, 0);
if (!c->cb_pool) {
dev_err(dev, "unable to allocate descriptor pool\n");
return -ENOMEM;
@@ -774,17 +771,11 @@ static int bcm2835_dma_slave_config(struct dma_chan *chan,
static int bcm2835_dma_terminate_all(struct dma_chan *chan)
{
struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
- struct bcm2835_dmadev *d = to_bcm2835_dma_dev(c->vc.chan.device);
unsigned long flags;
LIST_HEAD(head);
spin_lock_irqsave(&c->vc.lock, flags);
- /* Prevent this channel being scheduled */
- spin_lock(&d->lock);
- list_del_init(&c->node);
- spin_unlock(&d->lock);
-
/* stop DMA activity */
if (c->desc) {
vchan_terminate_vdesc(&c->desc->vd);
@@ -817,7 +808,6 @@ static int bcm2835_dma_chan_init(struct bcm2835_dmadev *d, int chan_id,
c->vc.desc_free = bcm2835_dma_desc_free;
vchan_init(&c->vc, &d->ddev);
- INIT_LIST_HEAD(&c->node);
c->chan_base = BCM2835_DMA_CHANIO(d->base, chan_id);
c->ch = chan_id;
@@ -920,7 +910,6 @@ static int bcm2835_dma_probe(struct platform_device *pdev)
od->ddev.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST;
od->ddev.dev = &pdev->dev;
INIT_LIST_HEAD(&od->ddev.channels);
- spin_lock_init(&od->lock);
platform_set_drvdata(pdev, od);
diff --git a/drivers/dma/dma-axi-dmac.c b/drivers/dma/dma-axi-dmac.c
index 15b2453d2647..ffc0adc2f6ce 100644
--- a/drivers/dma/dma-axi-dmac.c
+++ b/drivers/dma/dma-axi-dmac.c
@@ -367,8 +367,7 @@ static struct axi_dmac_desc *axi_dmac_alloc_desc(unsigned int num_sgs)
struct axi_dmac_desc *desc;
unsigned int i;
- desc = kzalloc(sizeof(struct axi_dmac_desc) +
- sizeof(struct axi_dmac_sg) * num_sgs, GFP_NOWAIT);
+ desc = kzalloc(struct_size(desc, sg, num_sgs), GFP_NOWAIT);
if (!desc)
return NULL;
diff --git a/drivers/dma/dma-jz4780.c b/drivers/dma/dma-jz4780.c
index a8b6225faa12..9ce0a386225b 100644
--- a/drivers/dma/dma-jz4780.c
+++ b/drivers/dma/dma-jz4780.c
@@ -838,9 +838,8 @@ static int jz4780_dma_probe(struct platform_device *pdev)
if (!soc_data)
return -EINVAL;
- jzdma = devm_kzalloc(dev, sizeof(*jzdma)
- + sizeof(*jzdma->chan) * soc_data->nb_channels,
- GFP_KERNEL);
+ jzdma = devm_kzalloc(dev, struct_size(jzdma, chan,
+ soc_data->nb_channels), GFP_KERNEL);
if (!jzdma)
return -ENOMEM;
diff --git a/drivers/dma/dmatest.c b/drivers/dma/dmatest.c
index 6511928b4cdf..b96814a7dceb 100644
--- a/drivers/dma/dmatest.c
+++ b/drivers/dma/dmatest.c
@@ -200,15 +200,20 @@ struct dmatest_done {
wait_queue_head_t *wait;
};
+struct dmatest_data {
+ u8 **raw;
+ u8 **aligned;
+ unsigned int cnt;
+ unsigned int off;
+};
+
struct dmatest_thread {
struct list_head node;
struct dmatest_info *info;
struct task_struct *task;
struct dma_chan *chan;
- u8 **srcs;
- u8 **usrcs;
- u8 **dsts;
- u8 **udsts;
+ struct dmatest_data src;
+ struct dmatest_data dst;
enum dma_transaction_type type;
wait_queue_head_t done_wait;
struct dmatest_done test_done;
@@ -481,6 +486,53 @@ static unsigned long long dmatest_KBs(s64 runtime, unsigned long long len)
return FIXPT_TO_INT(dmatest_persec(runtime, len >> 10));
}
+static void __dmatest_free_test_data(struct dmatest_data *d, unsigned int cnt)
+{
+ unsigned int i;
+
+ for (i = 0; i < cnt; i++)
+ kfree(d->raw[i]);
+
+ kfree(d->aligned);
+ kfree(d->raw);
+}
+
+static void dmatest_free_test_data(struct dmatest_data *d)
+{
+ __dmatest_free_test_data(d, d->cnt);
+}
+
+static int dmatest_alloc_test_data(struct dmatest_data *d,
+ unsigned int buf_size, u8 align)
+{
+ unsigned int i = 0;
+
+ d->raw = kcalloc(d->cnt + 1, sizeof(u8 *), GFP_KERNEL);
+ if (!d->raw)
+ return -ENOMEM;
+
+ d->aligned = kcalloc(d->cnt + 1, sizeof(u8 *), GFP_KERNEL);
+ if (!d->aligned)
+ goto err;
+
+ for (i = 0; i < d->cnt; i++) {
+ d->raw[i] = kmalloc(buf_size + align, GFP_KERNEL);
+ if (!d->raw[i])
+ goto err;
+
+ /* align to alignment restriction */
+ if (align)
+ d->aligned[i] = PTR_ALIGN(d->raw[i], align);
+ else
+ d->aligned[i] = d->raw[i];
+ }
+
+ return 0;
+err:
+ __dmatest_free_test_data(d, i);
+ return -ENOMEM;
+}
+
/*
* This function repeatedly tests DMA transfers of various lengths and
* offsets for a given operation type until it is told to exit by
@@ -511,8 +563,9 @@ static int dmatest_func(void *data)
enum dma_ctrl_flags flags;
u8 *pq_coefs = NULL;
int ret;
- int src_cnt;
- int dst_cnt;
+ unsigned int buf_size;
+ struct dmatest_data *src;
+ struct dmatest_data *dst;
int i;
ktime_t ktime, start, diff;
ktime_t filltime = 0;
@@ -535,25 +588,27 @@ static int dmatest_func(void *data)
params = &info->params;
chan = thread->chan;
dev = chan->device;
+ src = &thread->src;
+ dst = &thread->dst;
if (thread->type == DMA_MEMCPY) {
align = params->alignment < 0 ? dev->copy_align :
params->alignment;
- src_cnt = dst_cnt = 1;
+ src->cnt = dst->cnt = 1;
} else if (thread->type == DMA_MEMSET) {
align = params->alignment < 0 ? dev->fill_align :
params->alignment;
- src_cnt = dst_cnt = 1;
+ src->cnt = dst->cnt = 1;
is_memset = true;
} else if (thread->type == DMA_XOR) {
/* force odd to ensure dst = src */
- src_cnt = min_odd(params->xor_sources | 1, dev->max_xor);
- dst_cnt = 1;
+ src->cnt = min_odd(params->xor_sources | 1, dev->max_xor);
+ dst->cnt = 1;
align = params->alignment < 0 ? dev->xor_align :
params->alignment;
} else if (thread->type == DMA_PQ) {
/* force odd to ensure dst = src */
- src_cnt = min_odd(params->pq_sources | 1, dma_maxpq(dev, 0));
- dst_cnt = 2;
+ src->cnt = min_odd(params->pq_sources | 1, dma_maxpq(dev, 0));
+ dst->cnt = 2;
align = params->alignment < 0 ? dev->pq_align :
params->alignment;
@@ -561,75 +616,38 @@ static int dmatest_func(void *data)
if (!pq_coefs)
goto err_thread_type;
- for (i = 0; i < src_cnt; i++)
+ for (i = 0; i < src->cnt; i++)
pq_coefs[i] = 1;
} else
goto err_thread_type;
/* Check if buffer count fits into map count variable (u8) */
- if ((src_cnt + dst_cnt) >= 255) {
+ if ((src->cnt + dst->cnt) >= 255) {
pr_err("too many buffers (%d of 255 supported)\n",
- src_cnt + dst_cnt);
+ src->cnt + dst->cnt);
goto err_free_coefs;
}
- if (1 << align > params->buf_size) {
+ buf_size = params->buf_size;
+ if (1 << align > buf_size) {
pr_err("%u-byte buffer too small for %d-byte alignment\n",
- params->buf_size, 1 << align);
+ buf_size, 1 << align);
goto err_free_coefs;
}
- thread->srcs = kcalloc(src_cnt + 1, sizeof(u8 *), GFP_KERNEL);
- if (!thread->srcs)
+ if (dmatest_alloc_test_data(src, buf_size, align) < 0)
goto err_free_coefs;
- thread->usrcs = kcalloc(src_cnt + 1, sizeof(u8 *), GFP_KERNEL);
- if (!thread->usrcs)
- goto err_usrcs;
-
- for (i = 0; i < src_cnt; i++) {
- thread->usrcs[i] = kmalloc(params->buf_size + align,
- GFP_KERNEL);
- if (!thread->usrcs[i])
- goto err_srcbuf;
-
- /* align srcs to alignment restriction */
- if (align)
- thread->srcs[i] = PTR_ALIGN(thread->usrcs[i], align);
- else
- thread->srcs[i] = thread->usrcs[i];
- }
- thread->srcs[i] = NULL;
-
- thread->dsts = kcalloc(dst_cnt + 1, sizeof(u8 *), GFP_KERNEL);
- if (!thread->dsts)
- goto err_dsts;
-
- thread->udsts = kcalloc(dst_cnt + 1, sizeof(u8 *), GFP_KERNEL);
- if (!thread->udsts)
- goto err_udsts;
-
- for (i = 0; i < dst_cnt; i++) {
- thread->udsts[i] = kmalloc(params->buf_size + align,
- GFP_KERNEL);
- if (!thread->udsts[i])
- goto err_dstbuf;
-
- /* align dsts to alignment restriction */
- if (align)
- thread->dsts[i] = PTR_ALIGN(thread->udsts[i], align);
- else
- thread->dsts[i] = thread->udsts[i];
- }
- thread->dsts[i] = NULL;
+ if (dmatest_alloc_test_data(dst, buf_size, align) < 0)
+ goto err_src;
set_user_nice(current, 10);
- srcs = kcalloc(src_cnt, sizeof(dma_addr_t), GFP_KERNEL);
+ srcs = kcalloc(src->cnt, sizeof(dma_addr_t), GFP_KERNEL);
if (!srcs)
- goto err_dstbuf;
+ goto err_dst;
- dma_pq = kcalloc(dst_cnt, sizeof(dma_addr_t), GFP_KERNEL);
+ dma_pq = kcalloc(dst->cnt, sizeof(dma_addr_t), GFP_KERNEL);
if (!dma_pq)
goto err_srcs_array;
@@ -644,21 +662,21 @@ static int dmatest_func(void *data)
struct dma_async_tx_descriptor *tx = NULL;
struct dmaengine_unmap_data *um;
dma_addr_t *dsts;
- unsigned int src_off, dst_off, len;
+ unsigned int len;
total_tests++;
if (params->transfer_size) {
- if (params->transfer_size >= params->buf_size) {
+ if (params->transfer_size >= buf_size) {
pr_err("%u-byte transfer size must be lower than %u-buffer size\n",
- params->transfer_size, params->buf_size);
+ params->transfer_size, buf_size);
break;
}
len = params->transfer_size;
} else if (params->norandom) {
- len = params->buf_size;
+ len = buf_size;
} else {
- len = dmatest_random() % params->buf_size + 1;
+ len = dmatest_random() % buf_size + 1;
}
/* Do not alter transfer size explicitly defined by user */
@@ -670,57 +688,57 @@ static int dmatest_func(void *data)
total_len += len;
if (params->norandom) {
- src_off = 0;
- dst_off = 0;
+ src->off = 0;
+ dst->off = 0;
} else {
- src_off = dmatest_random() % (params->buf_size - len + 1);
- dst_off = dmatest_random() % (params->buf_size - len + 1);
+ src->off = dmatest_random() % (buf_size - len + 1);
+ dst->off = dmatest_random() % (buf_size - len + 1);
- src_off = (src_off >> align) << align;
- dst_off = (dst_off >> align) << align;
+ src->off = (src->off >> align) << align;
+ dst->off = (dst->off >> align) << align;
}
if (!params->noverify) {
start = ktime_get();
- dmatest_init_srcs(thread->srcs, src_off, len,
- params->buf_size, is_memset);
- dmatest_init_dsts(thread->dsts, dst_off, len,
- params->buf_size, is_memset);
+ dmatest_init_srcs(src->aligned, src->off, len,
+ buf_size, is_memset);
+ dmatest_init_dsts(dst->aligned, dst->off, len,
+ buf_size, is_memset);
diff = ktime_sub(ktime_get(), start);
filltime = ktime_add(filltime, diff);
}
- um = dmaengine_get_unmap_data(dev->dev, src_cnt + dst_cnt,
+ um = dmaengine_get_unmap_data(dev->dev, src->cnt + dst->cnt,
GFP_KERNEL);
if (!um) {
failed_tests++;
result("unmap data NULL", total_tests,
- src_off, dst_off, len, ret);
+ src->off, dst->off, len, ret);
continue;
}
- um->len = params->buf_size;
- for (i = 0; i < src_cnt; i++) {
- void *buf = thread->srcs[i];
+ um->len = buf_size;
+ for (i = 0; i < src->cnt; i++) {
+ void *buf = src->aligned[i];
struct page *pg = virt_to_page(buf);
unsigned long pg_off = offset_in_page(buf);
um->addr[i] = dma_map_page(dev->dev, pg, pg_off,
um->len, DMA_TO_DEVICE);
- srcs[i] = um->addr[i] + src_off;
+ srcs[i] = um->addr[i] + src->off;
ret = dma_mapping_error(dev->dev, um->addr[i]);
if (ret) {
result("src mapping error", total_tests,
- src_off, dst_off, len, ret);
+ src->off, dst->off, len, ret);
goto error_unmap_continue;
}
um->to_cnt++;
}
/* map with DMA_BIDIRECTIONAL to force writeback/invalidate */
- dsts = &um->addr[src_cnt];
- for (i = 0; i < dst_cnt; i++) {
- void *buf = thread->dsts[i];
+ dsts = &um->addr[src->cnt];
+ for (i = 0; i < dst->cnt; i++) {
+ void *buf = dst->aligned[i];
struct page *pg = virt_to_page(buf);
unsigned long pg_off = offset_in_page(buf);
@@ -729,7 +747,7 @@ static int dmatest_func(void *data)
ret = dma_mapping_error(dev->dev, dsts[i]);
if (ret) {
result("dst mapping error", total_tests,
- src_off, dst_off, len, ret);
+ src->off, dst->off, len, ret);
goto error_unmap_continue;
}
um->bidi_cnt++;
@@ -737,29 +755,29 @@ static int dmatest_func(void *data)
if (thread->type == DMA_MEMCPY)
tx = dev->device_prep_dma_memcpy(chan,
- dsts[0] + dst_off,
+ dsts[0] + dst->off,
srcs[0], len, flags);
else if (thread->type == DMA_MEMSET)
tx = dev->device_prep_dma_memset(chan,
- dsts[0] + dst_off,
- *(thread->srcs[0] + src_off),
+ dsts[0] + dst->off,
+ *(src->aligned[0] + src->off),
len, flags);
else if (thread->type == DMA_XOR)
tx = dev->device_prep_dma_xor(chan,
- dsts[0] + dst_off,
- srcs, src_cnt,
+ dsts[0] + dst->off,
+ srcs, src->cnt,
len, flags);
else if (thread->type == DMA_PQ) {
- for (i = 0; i < dst_cnt; i++)
- dma_pq[i] = dsts[i] + dst_off;
+ for (i = 0; i < dst->cnt; i++)
+ dma_pq[i] = dsts[i] + dst->off;
tx = dev->device_prep_dma_pq(chan, dma_pq, srcs,
- src_cnt, pq_coefs,
+ src->cnt, pq_coefs,
len, flags);
}
if (!tx) {
- result("prep error", total_tests, src_off,
- dst_off, len, ret);
+ result("prep error", total_tests, src->off,
+ dst->off, len, ret);
msleep(100);
goto error_unmap_continue;
}
@@ -770,8 +788,8 @@ static int dmatest_func(void *data)
cookie = tx->tx_submit(tx);
if (dma_submit_error(cookie)) {
- result("submit error", total_tests, src_off,
- dst_off, len, ret);
+ result("submit error", total_tests, src->off,
+ dst->off, len, ret);
msleep(100);
goto error_unmap_continue;
}
@@ -783,58 +801,58 @@ static int dmatest_func(void *data)
status = dma_async_is_tx_complete(chan, cookie, NULL, NULL);
if (!done->done) {
- result("test timed out", total_tests, src_off, dst_off,
+ result("test timed out", total_tests, src->off, dst->off,
len, 0);
goto error_unmap_continue;
} else if (status != DMA_COMPLETE) {
result(status == DMA_ERROR ?
"completion error status" :
- "completion busy status", total_tests, src_off,
- dst_off, len, ret);
+ "completion busy status", total_tests, src->off,
+ dst->off, len, ret);
goto error_unmap_continue;
}
dmaengine_unmap_put(um);
if (params->noverify) {
- verbose_result("test passed", total_tests, src_off,
- dst_off, len, 0);
+ verbose_result("test passed", total_tests, src->off,
+ dst->off, len, 0);
continue;
}
start = ktime_get();
pr_debug("%s: verifying source buffer...\n", current->comm);
- error_count = dmatest_verify(thread->srcs, 0, src_off,
+ error_count = dmatest_verify(src->aligned, 0, src->off,
0, PATTERN_SRC, true, is_memset);
- error_count += dmatest_verify(thread->srcs, src_off,
- src_off + len, src_off,
+ error_count += dmatest_verify(src->aligned, src->off,
+ src->off + len, src->off,
PATTERN_SRC | PATTERN_COPY, true, is_memset);
- error_count += dmatest_verify(thread->srcs, src_off + len,
- params->buf_size, src_off + len,
+ error_count += dmatest_verify(src->aligned, src->off + len,
+ buf_size, src->off + len,
PATTERN_SRC, true, is_memset);
pr_debug("%s: verifying dest buffer...\n", current->comm);
- error_count += dmatest_verify(thread->dsts, 0, dst_off,
+ error_count += dmatest_verify(dst->aligned, 0, dst->off,
0, PATTERN_DST, false, is_memset);
- error_count += dmatest_verify(thread->dsts, dst_off,
- dst_off + len, src_off,
+ error_count += dmatest_verify(dst->aligned, dst->off,
+ dst->off + len, src->off,
PATTERN_SRC | PATTERN_COPY, false, is_memset);
- error_count += dmatest_verify(thread->dsts, dst_off + len,
- params->buf_size, dst_off + len,
+ error_count += dmatest_verify(dst->aligned, dst->off + len,
+ buf_size, dst->off + len,
PATTERN_DST, false, is_memset);
diff = ktime_sub(ktime_get(), start);
comparetime = ktime_add(comparetime, diff);
if (error_count) {
- result("data error", total_tests, src_off, dst_off,
+ result("data error", total_tests, src->off, dst->off,
len, error_count);
failed_tests++;
} else {
- verbose_result("test passed", total_tests, src_off,
- dst_off, len, 0);
+ verbose_result("test passed", total_tests, src->off,
+ dst->off, len, 0);
}
continue;
@@ -852,19 +870,10 @@ error_unmap_continue:
kfree(dma_pq);
err_srcs_array:
kfree(srcs);
-err_dstbuf:
- for (i = 0; thread->udsts[i]; i++)
- kfree(thread->udsts[i]);
- kfree(thread->udsts);
-err_udsts:
- kfree(thread->dsts);
-err_dsts:
-err_srcbuf:
- for (i = 0; thread->usrcs[i]; i++)
- kfree(thread->usrcs[i]);
- kfree(thread->usrcs);
-err_usrcs:
- kfree(thread->srcs);
+err_dst:
+ dmatest_free_test_data(dst);
+err_src:
+ dmatest_free_test_data(src);
err_free_coefs:
kfree(pq_coefs);
err_thread_type:
diff --git a/drivers/dma/dw-axi-dmac/dw-axi-dmac.h b/drivers/dma/dw-axi-dmac/dw-axi-dmac.h
index f8888dc0b8dc..18b6014cf9b4 100644
--- a/drivers/dma/dw-axi-dmac/dw-axi-dmac.h
+++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac.h
@@ -75,7 +75,7 @@ struct __packed axi_dma_lli {
__le32 sstat;
__le32 dstat;
__le32 status_lo;
- __le32 ststus_hi;
+ __le32 status_hi;
__le32 reserved_lo;
__le32 reserved_hi;
};
diff --git a/drivers/dma/dw/Kconfig b/drivers/dma/dw/Kconfig
index 04b9728c1d26..e5162690de8f 100644
--- a/drivers/dma/dw/Kconfig
+++ b/drivers/dma/dw/Kconfig
@@ -1,3 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+
#
# DMA engine configuration for dw
#
diff --git a/drivers/dma/dw/Makefile b/drivers/dma/dw/Makefile
index 2b949c2e4504..63ed895c09aa 100644
--- a/drivers/dma/dw/Makefile
+++ b/drivers/dma/dw/Makefile
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_DW_DMAC_CORE) += dw_dmac_core.o
-dw_dmac_core-objs := core.o
+dw_dmac_core-objs := core.o dw.o idma32.o
obj-$(CONFIG_DW_DMAC) += dw_dmac.o
dw_dmac-objs := platform.o
diff --git a/drivers/dma/dw/core.c b/drivers/dma/dw/core.c
index dc053e62f894..21cb2a58dbd2 100644
--- a/drivers/dma/dw/core.c
+++ b/drivers/dma/dw/core.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Core driver for the Synopsys DesignWare DMA Controller
*
* Copyright (C) 2007-2008 Atmel Corporation
* Copyright (C) 2010-2011 ST Microelectronics
* Copyright (C) 2013 Intel Corporation
- *
- * 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 <linux/bitops.h>
@@ -37,27 +34,6 @@
* support descriptor writeback.
*/
-#define DWC_DEFAULT_CTLLO(_chan) ({ \
- struct dw_dma_chan *_dwc = to_dw_dma_chan(_chan); \
- struct dma_slave_config *_sconfig = &_dwc->dma_sconfig; \
- bool _is_slave = is_slave_direction(_dwc->direction); \
- u8 _smsize = _is_slave ? _sconfig->src_maxburst : \
- DW_DMA_MSIZE_16; \
- u8 _dmsize = _is_slave ? _sconfig->dst_maxburst : \
- DW_DMA_MSIZE_16; \
- u8 _dms = (_dwc->direction == DMA_MEM_TO_DEV) ? \
- _dwc->dws.p_master : _dwc->dws.m_master; \
- u8 _sms = (_dwc->direction == DMA_DEV_TO_MEM) ? \
- _dwc->dws.p_master : _dwc->dws.m_master; \
- \
- (DWC_CTLL_DST_MSIZE(_dmsize) \
- | DWC_CTLL_SRC_MSIZE(_smsize) \
- | DWC_CTLL_LLP_D_EN \
- | DWC_CTLL_LLP_S_EN \
- | DWC_CTLL_DMS(_dms) \
- | DWC_CTLL_SMS(_sms)); \
- })
-
/* The set of bus widths supported by the DMA controller */
#define DW_DMA_BUSWIDTHS \
BIT(DMA_SLAVE_BUSWIDTH_UNDEFINED) | \
@@ -138,44 +114,6 @@ static void dwc_desc_put(struct dw_dma_chan *dwc, struct dw_desc *desc)
dwc->descs_allocated--;
}
-static void dwc_initialize_chan_idma32(struct dw_dma_chan *dwc)
-{
- u32 cfghi = 0;
- u32 cfglo = 0;
-
- /* Set default burst alignment */
- cfglo |= IDMA32C_CFGL_DST_BURST_ALIGN | IDMA32C_CFGL_SRC_BURST_ALIGN;
-
- /* Low 4 bits of the request lines */
- cfghi |= IDMA32C_CFGH_DST_PER(dwc->dws.dst_id & 0xf);
- cfghi |= IDMA32C_CFGH_SRC_PER(dwc->dws.src_id & 0xf);
-
- /* Request line extension (2 bits) */
- cfghi |= IDMA32C_CFGH_DST_PER_EXT(dwc->dws.dst_id >> 4 & 0x3);
- cfghi |= IDMA32C_CFGH_SRC_PER_EXT(dwc->dws.src_id >> 4 & 0x3);
-
- channel_writel(dwc, CFG_LO, cfglo);
- channel_writel(dwc, CFG_HI, cfghi);
-}
-
-static void dwc_initialize_chan_dw(struct dw_dma_chan *dwc)
-{
- struct dw_dma *dw = to_dw_dma(dwc->chan.device);
- u32 cfghi = DWC_CFGH_FIFO_MODE;
- u32 cfglo = DWC_CFGL_CH_PRIOR(dwc->priority);
- bool hs_polarity = dwc->dws.hs_polarity;
-
- cfghi |= DWC_CFGH_DST_PER(dwc->dws.dst_id);
- cfghi |= DWC_CFGH_SRC_PER(dwc->dws.src_id);
- cfghi |= DWC_CFGH_PROTCTL(dw->pdata->protctl);
-
- /* Set polarity of handshake interface */
- cfglo |= hs_polarity ? DWC_CFGL_HS_DST_POL | DWC_CFGL_HS_SRC_POL : 0;
-
- channel_writel(dwc, CFG_LO, cfglo);
- channel_writel(dwc, CFG_HI, cfghi);
-}
-
static void dwc_initialize(struct dw_dma_chan *dwc)
{
struct dw_dma *dw = to_dw_dma(dwc->chan.device);
@@ -183,10 +121,7 @@ static void dwc_initialize(struct dw_dma_chan *dwc)
if (test_bit(DW_DMA_IS_INITIALIZED, &dwc->flags))
return;
- if (dw->pdata->is_idma32)
- dwc_initialize_chan_idma32(dwc);
- else
- dwc_initialize_chan_dw(dwc);
+ dw->initialize_chan(dwc);
/* Enable interrupts */
channel_set_bit(dw, MASK.XFER, dwc->mask);
@@ -215,37 +150,6 @@ static inline void dwc_chan_disable(struct dw_dma *dw, struct dw_dma_chan *dwc)
cpu_relax();
}
-static u32 bytes2block(struct dw_dma_chan *dwc, size_t bytes,
- unsigned int width, size_t *len)
-{
- struct dw_dma *dw = to_dw_dma(dwc->chan.device);
- u32 block;
-
- /* Always in bytes for iDMA 32-bit */
- if (dw->pdata->is_idma32)
- width = 0;
-
- if ((bytes >> width) > dwc->block_size) {
- block = dwc->block_size;
- *len = block << width;
- } else {
- block = bytes >> width;
- *len = bytes;
- }
-
- return block;
-}
-
-static size_t block2bytes(struct dw_dma_chan *dwc, u32 block, u32 width)
-{
- struct dw_dma *dw = to_dw_dma(dwc->chan.device);
-
- if (dw->pdata->is_idma32)
- return IDMA32C_CTLH_BLOCK_TS(block);
-
- return DWC_CTLH_BLOCK_TS(block) << width;
-}
-
/*----------------------------------------------------------------------*/
/* Perform single block transfer */
@@ -391,10 +295,11 @@ static void dwc_complete_all(struct dw_dma *dw, struct dw_dma_chan *dwc)
/* Returns how many bytes were already received from source */
static inline u32 dwc_get_sent(struct dw_dma_chan *dwc)
{
+ struct dw_dma *dw = to_dw_dma(dwc->chan.device);
u32 ctlhi = channel_readl(dwc, CTL_HI);
u32 ctllo = channel_readl(dwc, CTL_LO);
- return block2bytes(dwc, ctlhi, ctllo >> 4 & 7);
+ return dw->block2bytes(dwc, ctlhi, ctllo >> 4 & 7);
}
static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc)
@@ -651,7 +556,7 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
unsigned int src_width;
unsigned int dst_width;
unsigned int data_width = dw->pdata->data_width[m_master];
- u32 ctllo;
+ u32 ctllo, ctlhi;
u8 lms = DWC_LLP_LMS(m_master);
dev_vdbg(chan2dev(chan),
@@ -667,7 +572,7 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
src_width = dst_width = __ffs(data_width | src | dest | len);
- ctllo = DWC_DEFAULT_CTLLO(chan)
+ ctllo = dw->prepare_ctllo(dwc)
| DWC_CTLL_DST_WIDTH(dst_width)
| DWC_CTLL_SRC_WIDTH(src_width)
| DWC_CTLL_DST_INC
@@ -680,10 +585,12 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
if (!desc)
goto err_desc_get;
+ ctlhi = dw->bytes2block(dwc, len - offset, src_width, &xfer_count);
+
lli_write(desc, sar, src + offset);
lli_write(desc, dar, dest + offset);
lli_write(desc, ctllo, ctllo);
- lli_write(desc, ctlhi, bytes2block(dwc, len - offset, src_width, &xfer_count));
+ lli_write(desc, ctlhi, ctlhi);
desc->len = xfer_count;
if (!first) {
@@ -721,7 +628,7 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
struct dma_slave_config *sconfig = &dwc->dma_sconfig;
struct dw_desc *prev;
struct dw_desc *first;
- u32 ctllo;
+ u32 ctllo, ctlhi;
u8 m_master = dwc->dws.m_master;
u8 lms = DWC_LLP_LMS(m_master);
dma_addr_t reg;
@@ -745,10 +652,10 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
case DMA_MEM_TO_DEV:
reg_width = __ffs(sconfig->dst_addr_width);
reg = sconfig->dst_addr;
- ctllo = (DWC_DEFAULT_CTLLO(chan)
+ ctllo = dw->prepare_ctllo(dwc)
| DWC_CTLL_DST_WIDTH(reg_width)
| DWC_CTLL_DST_FIX
- | DWC_CTLL_SRC_INC);
+ | DWC_CTLL_SRC_INC;
ctllo |= sconfig->device_fc ? DWC_CTLL_FC(DW_DMA_FC_P_M2P) :
DWC_CTLL_FC(DW_DMA_FC_D_M2P);
@@ -768,9 +675,11 @@ slave_sg_todev_fill_desc:
if (!desc)
goto err_desc_get;
+ ctlhi = dw->bytes2block(dwc, len, mem_width, &dlen);
+
lli_write(desc, sar, mem);
lli_write(desc, dar, reg);
- lli_write(desc, ctlhi, bytes2block(dwc, len, mem_width, &dlen));
+ lli_write(desc, ctlhi, ctlhi);
lli_write(desc, ctllo, ctllo | DWC_CTLL_SRC_WIDTH(mem_width));
desc->len = dlen;
@@ -793,10 +702,10 @@ slave_sg_todev_fill_desc:
case DMA_DEV_TO_MEM:
reg_width = __ffs(sconfig->src_addr_width);
reg = sconfig->src_addr;
- ctllo = (DWC_DEFAULT_CTLLO(chan)
+ ctllo = dw->prepare_ctllo(dwc)
| DWC_CTLL_SRC_WIDTH(reg_width)
| DWC_CTLL_DST_INC
- | DWC_CTLL_SRC_FIX);
+ | DWC_CTLL_SRC_FIX;
ctllo |= sconfig->device_fc ? DWC_CTLL_FC(DW_DMA_FC_P_P2M) :
DWC_CTLL_FC(DW_DMA_FC_D_P2M);
@@ -814,9 +723,11 @@ slave_sg_fromdev_fill_desc:
if (!desc)
goto err_desc_get;
+ ctlhi = dw->bytes2block(dwc, len, reg_width, &dlen);
+
lli_write(desc, sar, reg);
lli_write(desc, dar, mem);
- lli_write(desc, ctlhi, bytes2block(dwc, len, reg_width, &dlen));
+ lli_write(desc, ctlhi, ctlhi);
mem_width = __ffs(data_width | mem | dlen);
lli_write(desc, ctllo, ctllo | DWC_CTLL_DST_WIDTH(mem_width));
desc->len = dlen;
@@ -876,22 +787,12 @@ EXPORT_SYMBOL_GPL(dw_dma_filter);
static int dwc_config(struct dma_chan *chan, struct dma_slave_config *sconfig)
{
struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
- struct dma_slave_config *sc = &dwc->dma_sconfig;
struct dw_dma *dw = to_dw_dma(chan->device);
- /*
- * Fix sconfig's burst size according to dw_dmac. We need to convert
- * them as:
- * 1 -> 0, 4 -> 1, 8 -> 2, 16 -> 3.
- *
- * NOTE: burst size 2 is not supported by DesignWare controller.
- * iDMA 32-bit supports it.
- */
- u32 s = dw->pdata->is_idma32 ? 1 : 2;
memcpy(&dwc->dma_sconfig, sconfig, sizeof(*sconfig));
- sc->src_maxburst = sc->src_maxburst > 1 ? fls(sc->src_maxburst) - s : 0;
- sc->dst_maxburst = sc->dst_maxburst > 1 ? fls(sc->dst_maxburst) - s : 0;
+ dw->encode_maxburst(dwc, &dwc->dma_sconfig.src_maxburst);
+ dw->encode_maxburst(dwc, &dwc->dma_sconfig.dst_maxburst);
return 0;
}
@@ -900,16 +801,9 @@ static void dwc_chan_pause(struct dw_dma_chan *dwc, bool drain)
{
struct dw_dma *dw = to_dw_dma(dwc->chan.device);
unsigned int count = 20; /* timeout iterations */
- u32 cfglo;
- cfglo = channel_readl(dwc, CFG_LO);
- if (dw->pdata->is_idma32) {
- if (drain)
- cfglo |= IDMA32C_CFGL_CH_DRAIN;
- else
- cfglo &= ~IDMA32C_CFGL_CH_DRAIN;
- }
- channel_writel(dwc, CFG_LO, cfglo | DWC_CFGL_CH_SUSP);
+ dw->suspend_chan(dwc, drain);
+
while (!(channel_readl(dwc, CFG_LO) & DWC_CFGL_FIFO_EMPTY) && count--)
udelay(2);
@@ -928,11 +822,11 @@ static int dwc_pause(struct dma_chan *chan)
return 0;
}
-static inline void dwc_chan_resume(struct dw_dma_chan *dwc)
+static inline void dwc_chan_resume(struct dw_dma_chan *dwc, bool drain)
{
- u32 cfglo = channel_readl(dwc, CFG_LO);
+ struct dw_dma *dw = to_dw_dma(dwc->chan.device);
- channel_writel(dwc, CFG_LO, cfglo & ~DWC_CFGL_CH_SUSP);
+ dw->resume_chan(dwc, drain);
clear_bit(DW_DMA_IS_PAUSED, &dwc->flags);
}
@@ -945,7 +839,7 @@ static int dwc_resume(struct dma_chan *chan)
spin_lock_irqsave(&dwc->lock, flags);
if (test_bit(DW_DMA_IS_PAUSED, &dwc->flags))
- dwc_chan_resume(dwc);
+ dwc_chan_resume(dwc, false);
spin_unlock_irqrestore(&dwc->lock, flags);
@@ -968,7 +862,7 @@ static int dwc_terminate_all(struct dma_chan *chan)
dwc_chan_disable(dw, dwc);
- dwc_chan_resume(dwc);
+ dwc_chan_resume(dwc, true);
/* active_list entries will end up before queued entries */
list_splice_init(&dwc->queue, &list);
@@ -1058,33 +952,7 @@ static void dwc_issue_pending(struct dma_chan *chan)
/*----------------------------------------------------------------------*/
-/*
- * Program FIFO size of channels.
- *
- * By default full FIFO (512 bytes) is assigned to channel 0. Here we
- * slice FIFO on equal parts between channels.
- */
-static void idma32_fifo_partition(struct dw_dma *dw)
-{
- u64 value = IDMA32C_FP_PSIZE_CH0(64) | IDMA32C_FP_PSIZE_CH1(64) |
- IDMA32C_FP_UPDATE;
- u64 fifo_partition = 0;
-
- if (!dw->pdata->is_idma32)
- return;
-
- /* Fill FIFO_PARTITION low bits (Channels 0..1, 4..5) */
- fifo_partition |= value << 0;
-
- /* Fill FIFO_PARTITION high bits (Channels 2..3, 6..7) */
- fifo_partition |= value << 32;
-
- /* Program FIFO Partition registers - 64 bytes per channel */
- idma32_writeq(dw, FIFO_PARTITION1, fifo_partition);
- idma32_writeq(dw, FIFO_PARTITION0, fifo_partition);
-}
-
-static void dw_dma_off(struct dw_dma *dw)
+void do_dw_dma_off(struct dw_dma *dw)
{
unsigned int i;
@@ -1103,7 +971,7 @@ static void dw_dma_off(struct dw_dma *dw)
clear_bit(DW_DMA_IS_INITIALIZED, &dw->chan[i].flags);
}
-static void dw_dma_on(struct dw_dma *dw)
+void do_dw_dma_on(struct dw_dma *dw)
{
dma_writel(dw, CFG, DW_CFG_DMA_EN);
}
@@ -1139,7 +1007,7 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan)
/* Enable controller here if needed */
if (!dw->in_use)
- dw_dma_on(dw);
+ do_dw_dma_on(dw);
dw->in_use |= dwc->mask;
return 0;
@@ -1150,7 +1018,6 @@ static void dwc_free_chan_resources(struct dma_chan *chan)
struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
struct dw_dma *dw = to_dw_dma(chan->device);
unsigned long flags;
- LIST_HEAD(list);
dev_dbg(chan2dev(chan), "%s: descs allocated=%u\n", __func__,
dwc->descs_allocated);
@@ -1177,30 +1044,25 @@ static void dwc_free_chan_resources(struct dma_chan *chan)
/* Disable controller in case it was a last user */
dw->in_use &= ~dwc->mask;
if (!dw->in_use)
- dw_dma_off(dw);
+ do_dw_dma_off(dw);
dev_vdbg(chan2dev(chan), "%s: done\n", __func__);
}
-int dw_dma_probe(struct dw_dma_chip *chip)
+int do_dma_probe(struct dw_dma_chip *chip)
{
+ struct dw_dma *dw = chip->dw;
struct dw_dma_platform_data *pdata;
- struct dw_dma *dw;
bool autocfg = false;
unsigned int dw_params;
unsigned int i;
int err;
- dw = devm_kzalloc(chip->dev, sizeof(*dw), GFP_KERNEL);
- if (!dw)
- return -ENOMEM;
-
dw->pdata = devm_kzalloc(chip->dev, sizeof(*dw->pdata), GFP_KERNEL);
if (!dw->pdata)
return -ENOMEM;
dw->regs = chip->regs;
- chip->dw = dw;
pm_runtime_get_sync(chip->dev);
@@ -1227,8 +1089,6 @@ int dw_dma_probe(struct dw_dma_chip *chip)
pdata->block_size = dma_readl(dw, MAX_BLK_SIZE);
/* Fill platform data with the default values */
- pdata->is_private = true;
- pdata->is_memcpy = true;
pdata->chan_allocation_order = CHAN_ALLOCATION_ASCENDING;
pdata->chan_priority = CHAN_PRIORITY_ASCENDING;
} else if (chip->pdata->nr_channels > DW_DMA_MAX_NR_CHANNELS) {
@@ -1252,15 +1112,10 @@ int dw_dma_probe(struct dw_dma_chip *chip)
dw->all_chan_mask = (1 << pdata->nr_channels) - 1;
/* Force dma off, just in case */
- dw_dma_off(dw);
-
- idma32_fifo_partition(dw);
+ dw->disable(dw);
/* Device and instance ID for IRQ and DMA pool */
- if (pdata->is_idma32)
- snprintf(dw->name, sizeof(dw->name), "idma32:dmac%d", chip->id);
- else
- snprintf(dw->name, sizeof(dw->name), "dw:dmac%d", chip->id);
+ dw->set_device_name(dw, chip->id);
/* Create a pool of consistent memory blocks for hardware descriptors */
dw->desc_pool = dmam_pool_create(dw->name, chip->dev,
@@ -1340,10 +1195,8 @@ int dw_dma_probe(struct dw_dma_chip *chip)
/* Set capabilities */
dma_cap_set(DMA_SLAVE, dw->dma.cap_mask);
- if (pdata->is_private)
- dma_cap_set(DMA_PRIVATE, dw->dma.cap_mask);
- if (pdata->is_memcpy)
- dma_cap_set(DMA_MEMCPY, dw->dma.cap_mask);
+ dma_cap_set(DMA_PRIVATE, dw->dma.cap_mask);
+ dma_cap_set(DMA_MEMCPY, dw->dma.cap_mask);
dw->dma.dev = chip->dev;
dw->dma.device_alloc_chan_resources = dwc_alloc_chan_resources;
@@ -1384,16 +1237,15 @@ err_pdata:
pm_runtime_put_sync_suspend(chip->dev);
return err;
}
-EXPORT_SYMBOL_GPL(dw_dma_probe);
-int dw_dma_remove(struct dw_dma_chip *chip)
+int do_dma_remove(struct dw_dma_chip *chip)
{
struct dw_dma *dw = chip->dw;
struct dw_dma_chan *dwc, *_dwc;
pm_runtime_get_sync(chip->dev);
- dw_dma_off(dw);
+ do_dw_dma_off(dw);
dma_async_device_unregister(&dw->dma);
free_irq(chip->irq, dw);
@@ -1408,27 +1260,24 @@ int dw_dma_remove(struct dw_dma_chip *chip)
pm_runtime_put_sync_suspend(chip->dev);
return 0;
}
-EXPORT_SYMBOL_GPL(dw_dma_remove);
-int dw_dma_disable(struct dw_dma_chip *chip)
+int do_dw_dma_disable(struct dw_dma_chip *chip)
{
struct dw_dma *dw = chip->dw;
- dw_dma_off(dw);
+ dw->disable(dw);
return 0;
}
-EXPORT_SYMBOL_GPL(dw_dma_disable);
+EXPORT_SYMBOL_GPL(do_dw_dma_disable);
-int dw_dma_enable(struct dw_dma_chip *chip)
+int do_dw_dma_enable(struct dw_dma_chip *chip)
{
struct dw_dma *dw = chip->dw;
- idma32_fifo_partition(dw);
-
- dw_dma_on(dw);
+ dw->enable(dw);
return 0;
}
-EXPORT_SYMBOL_GPL(dw_dma_enable);
+EXPORT_SYMBOL_GPL(do_dw_dma_enable);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Synopsys DesignWare DMA Controller core driver");
diff --git a/drivers/dma/dw/dw.c b/drivers/dma/dw/dw.c
new file mode 100644
index 000000000000..7a085b3c1854
--- /dev/null
+++ b/drivers/dma/dw/dw.c
@@ -0,0 +1,138 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2007-2008 Atmel Corporation
+// Copyright (C) 2010-2011 ST Microelectronics
+// Copyright (C) 2013,2018 Intel Corporation
+
+#include <linux/bitops.h>
+#include <linux/dmaengine.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include "internal.h"
+
+static void dw_dma_initialize_chan(struct dw_dma_chan *dwc)
+{
+ struct dw_dma *dw = to_dw_dma(dwc->chan.device);
+ u32 cfghi = DWC_CFGH_FIFO_MODE;
+ u32 cfglo = DWC_CFGL_CH_PRIOR(dwc->priority);
+ bool hs_polarity = dwc->dws.hs_polarity;
+
+ cfghi |= DWC_CFGH_DST_PER(dwc->dws.dst_id);
+ cfghi |= DWC_CFGH_SRC_PER(dwc->dws.src_id);
+ cfghi |= DWC_CFGH_PROTCTL(dw->pdata->protctl);
+
+ /* Set polarity of handshake interface */
+ cfglo |= hs_polarity ? DWC_CFGL_HS_DST_POL | DWC_CFGL_HS_SRC_POL : 0;
+
+ channel_writel(dwc, CFG_LO, cfglo);
+ channel_writel(dwc, CFG_HI, cfghi);
+}
+
+static void dw_dma_suspend_chan(struct dw_dma_chan *dwc, bool drain)
+{
+ u32 cfglo = channel_readl(dwc, CFG_LO);
+
+ channel_writel(dwc, CFG_LO, cfglo | DWC_CFGL_CH_SUSP);
+}
+
+static void dw_dma_resume_chan(struct dw_dma_chan *dwc, bool drain)
+{
+ u32 cfglo = channel_readl(dwc, CFG_LO);
+
+ channel_writel(dwc, CFG_LO, cfglo & ~DWC_CFGL_CH_SUSP);
+}
+
+static u32 dw_dma_bytes2block(struct dw_dma_chan *dwc,
+ size_t bytes, unsigned int width, size_t *len)
+{
+ u32 block;
+
+ if ((bytes >> width) > dwc->block_size) {
+ block = dwc->block_size;
+ *len = dwc->block_size << width;
+ } else {
+ block = bytes >> width;
+ *len = bytes;
+ }
+
+ return block;
+}
+
+static size_t dw_dma_block2bytes(struct dw_dma_chan *dwc, u32 block, u32 width)
+{
+ return DWC_CTLH_BLOCK_TS(block) << width;
+}
+
+static u32 dw_dma_prepare_ctllo(struct dw_dma_chan *dwc)
+{
+ struct dma_slave_config *sconfig = &dwc->dma_sconfig;
+ bool is_slave = is_slave_direction(dwc->direction);
+ u8 smsize = is_slave ? sconfig->src_maxburst : DW_DMA_MSIZE_16;
+ u8 dmsize = is_slave ? sconfig->dst_maxburst : DW_DMA_MSIZE_16;
+ u8 p_master = dwc->dws.p_master;
+ u8 m_master = dwc->dws.m_master;
+ u8 dms = (dwc->direction == DMA_MEM_TO_DEV) ? p_master : m_master;
+ u8 sms = (dwc->direction == DMA_DEV_TO_MEM) ? p_master : m_master;
+
+ return DWC_CTLL_LLP_D_EN | DWC_CTLL_LLP_S_EN |
+ DWC_CTLL_DST_MSIZE(dmsize) | DWC_CTLL_SRC_MSIZE(smsize) |
+ DWC_CTLL_DMS(dms) | DWC_CTLL_SMS(sms);
+}
+
+static void dw_dma_encode_maxburst(struct dw_dma_chan *dwc, u32 *maxburst)
+{
+ /*
+ * Fix burst size according to dw_dmac. We need to convert them as:
+ * 1 -> 0, 4 -> 1, 8 -> 2, 16 -> 3.
+ */
+ *maxburst = *maxburst > 1 ? fls(*maxburst) - 2 : 0;
+}
+
+static void dw_dma_set_device_name(struct dw_dma *dw, int id)
+{
+ snprintf(dw->name, sizeof(dw->name), "dw:dmac%d", id);
+}
+
+static void dw_dma_disable(struct dw_dma *dw)
+{
+ do_dw_dma_off(dw);
+}
+
+static void dw_dma_enable(struct dw_dma *dw)
+{
+ do_dw_dma_on(dw);
+}
+
+int dw_dma_probe(struct dw_dma_chip *chip)
+{
+ struct dw_dma *dw;
+
+ dw = devm_kzalloc(chip->dev, sizeof(*dw), GFP_KERNEL);
+ if (!dw)
+ return -ENOMEM;
+
+ /* Channel operations */
+ dw->initialize_chan = dw_dma_initialize_chan;
+ dw->suspend_chan = dw_dma_suspend_chan;
+ dw->resume_chan = dw_dma_resume_chan;
+ dw->prepare_ctllo = dw_dma_prepare_ctllo;
+ dw->encode_maxburst = dw_dma_encode_maxburst;
+ dw->bytes2block = dw_dma_bytes2block;
+ dw->block2bytes = dw_dma_block2bytes;
+
+ /* Device operations */
+ dw->set_device_name = dw_dma_set_device_name;
+ dw->disable = dw_dma_disable;
+ dw->enable = dw_dma_enable;
+
+ chip->dw = dw;
+ return do_dma_probe(chip);
+}
+EXPORT_SYMBOL_GPL(dw_dma_probe);
+
+int dw_dma_remove(struct dw_dma_chip *chip)
+{
+ return do_dma_remove(chip);
+}
+EXPORT_SYMBOL_GPL(dw_dma_remove);
diff --git a/drivers/dma/dw/idma32.c b/drivers/dma/dw/idma32.c
new file mode 100644
index 000000000000..f00657308811
--- /dev/null
+++ b/drivers/dma/dw/idma32.c
@@ -0,0 +1,160 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2013,2018 Intel Corporation
+
+#include <linux/bitops.h>
+#include <linux/dmaengine.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include "internal.h"
+
+static void idma32_initialize_chan(struct dw_dma_chan *dwc)
+{
+ u32 cfghi = 0;
+ u32 cfglo = 0;
+
+ /* Set default burst alignment */
+ cfglo |= IDMA32C_CFGL_DST_BURST_ALIGN | IDMA32C_CFGL_SRC_BURST_ALIGN;
+
+ /* Low 4 bits of the request lines */
+ cfghi |= IDMA32C_CFGH_DST_PER(dwc->dws.dst_id & 0xf);
+ cfghi |= IDMA32C_CFGH_SRC_PER(dwc->dws.src_id & 0xf);
+
+ /* Request line extension (2 bits) */
+ cfghi |= IDMA32C_CFGH_DST_PER_EXT(dwc->dws.dst_id >> 4 & 0x3);
+ cfghi |= IDMA32C_CFGH_SRC_PER_EXT(dwc->dws.src_id >> 4 & 0x3);
+
+ channel_writel(dwc, CFG_LO, cfglo);
+ channel_writel(dwc, CFG_HI, cfghi);
+}
+
+static void idma32_suspend_chan(struct dw_dma_chan *dwc, bool drain)
+{
+ u32 cfglo = channel_readl(dwc, CFG_LO);
+
+ if (drain)
+ cfglo |= IDMA32C_CFGL_CH_DRAIN;
+
+ channel_writel(dwc, CFG_LO, cfglo | DWC_CFGL_CH_SUSP);
+}
+
+static void idma32_resume_chan(struct dw_dma_chan *dwc, bool drain)
+{
+ u32 cfglo = channel_readl(dwc, CFG_LO);
+
+ if (drain)
+ cfglo &= ~IDMA32C_CFGL_CH_DRAIN;
+
+ channel_writel(dwc, CFG_LO, cfglo & ~DWC_CFGL_CH_SUSP);
+}
+
+static u32 idma32_bytes2block(struct dw_dma_chan *dwc,
+ size_t bytes, unsigned int width, size_t *len)
+{
+ u32 block;
+
+ if (bytes > dwc->block_size) {
+ block = dwc->block_size;
+ *len = dwc->block_size;
+ } else {
+ block = bytes;
+ *len = bytes;
+ }
+
+ return block;
+}
+
+static size_t idma32_block2bytes(struct dw_dma_chan *dwc, u32 block, u32 width)
+{
+ return IDMA32C_CTLH_BLOCK_TS(block);
+}
+
+static u32 idma32_prepare_ctllo(struct dw_dma_chan *dwc)
+{
+ struct dma_slave_config *sconfig = &dwc->dma_sconfig;
+ bool is_slave = is_slave_direction(dwc->direction);
+ u8 smsize = is_slave ? sconfig->src_maxburst : IDMA32_MSIZE_8;
+ u8 dmsize = is_slave ? sconfig->dst_maxburst : IDMA32_MSIZE_8;
+
+ return DWC_CTLL_LLP_D_EN | DWC_CTLL_LLP_S_EN |
+ DWC_CTLL_DST_MSIZE(dmsize) | DWC_CTLL_SRC_MSIZE(smsize);
+}
+
+static void idma32_encode_maxburst(struct dw_dma_chan *dwc, u32 *maxburst)
+{
+ *maxburst = *maxburst > 1 ? fls(*maxburst) - 1 : 0;
+}
+
+static void idma32_set_device_name(struct dw_dma *dw, int id)
+{
+ snprintf(dw->name, sizeof(dw->name), "idma32:dmac%d", id);
+}
+
+/*
+ * Program FIFO size of channels.
+ *
+ * By default full FIFO (512 bytes) is assigned to channel 0. Here we
+ * slice FIFO on equal parts between channels.
+ */
+static void idma32_fifo_partition(struct dw_dma *dw)
+{
+ u64 value = IDMA32C_FP_PSIZE_CH0(64) | IDMA32C_FP_PSIZE_CH1(64) |
+ IDMA32C_FP_UPDATE;
+ u64 fifo_partition = 0;
+
+ /* Fill FIFO_PARTITION low bits (Channels 0..1, 4..5) */
+ fifo_partition |= value << 0;
+
+ /* Fill FIFO_PARTITION high bits (Channels 2..3, 6..7) */
+ fifo_partition |= value << 32;
+
+ /* Program FIFO Partition registers - 64 bytes per channel */
+ idma32_writeq(dw, FIFO_PARTITION1, fifo_partition);
+ idma32_writeq(dw, FIFO_PARTITION0, fifo_partition);
+}
+
+static void idma32_disable(struct dw_dma *dw)
+{
+ do_dw_dma_off(dw);
+ idma32_fifo_partition(dw);
+}
+
+static void idma32_enable(struct dw_dma *dw)
+{
+ idma32_fifo_partition(dw);
+ do_dw_dma_on(dw);
+}
+
+int idma32_dma_probe(struct dw_dma_chip *chip)
+{
+ struct dw_dma *dw;
+
+ dw = devm_kzalloc(chip->dev, sizeof(*dw), GFP_KERNEL);
+ if (!dw)
+ return -ENOMEM;
+
+ /* Channel operations */
+ dw->initialize_chan = idma32_initialize_chan;
+ dw->suspend_chan = idma32_suspend_chan;
+ dw->resume_chan = idma32_resume_chan;
+ dw->prepare_ctllo = idma32_prepare_ctllo;
+ dw->encode_maxburst = idma32_encode_maxburst;
+ dw->bytes2block = idma32_bytes2block;
+ dw->block2bytes = idma32_block2bytes;
+
+ /* Device operations */
+ dw->set_device_name = idma32_set_device_name;
+ dw->disable = idma32_disable;
+ dw->enable = idma32_enable;
+
+ chip->dw = dw;
+ return do_dma_probe(chip);
+}
+EXPORT_SYMBOL_GPL(idma32_dma_probe);
+
+int idma32_dma_remove(struct dw_dma_chip *chip)
+{
+ return do_dma_remove(chip);
+}
+EXPORT_SYMBOL_GPL(idma32_dma_remove);
diff --git a/drivers/dma/dw/internal.h b/drivers/dma/dw/internal.h
index 41439732ff6b..1dd7a4e6dd23 100644
--- a/drivers/dma/dw/internal.h
+++ b/drivers/dma/dw/internal.h
@@ -1,11 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Driver for the Synopsys DesignWare DMA Controller
*
* Copyright (C) 2013 Intel Corporation
- *
- * 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.
*/
#ifndef _DMA_DW_INTERNAL_H
@@ -15,8 +12,14 @@
#include "regs.h"
-int dw_dma_disable(struct dw_dma_chip *chip);
-int dw_dma_enable(struct dw_dma_chip *chip);
+int do_dma_probe(struct dw_dma_chip *chip);
+int do_dma_remove(struct dw_dma_chip *chip);
+
+void do_dw_dma_on(struct dw_dma *dw);
+void do_dw_dma_off(struct dw_dma *dw);
+
+int do_dw_dma_disable(struct dw_dma_chip *chip);
+int do_dw_dma_enable(struct dw_dma_chip *chip);
extern bool dw_dma_filter(struct dma_chan *chan, void *param);
diff --git a/drivers/dma/dw/pci.c b/drivers/dma/dw/pci.c
index 7778ed705a1a..e79a75db0852 100644
--- a/drivers/dma/dw/pci.c
+++ b/drivers/dma/dw/pci.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* PCI driver for the Synopsys DesignWare DMA Controller
*
* Copyright (C) 2013 Intel Corporation
* Author: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
- *
- * 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 <linux/module.h>
@@ -15,21 +12,33 @@
#include "internal.h"
-static struct dw_dma_platform_data mrfld_pdata = {
+struct dw_dma_pci_data {
+ const struct dw_dma_platform_data *pdata;
+ int (*probe)(struct dw_dma_chip *chip);
+};
+
+static const struct dw_dma_pci_data dw_pci_data = {
+ .probe = dw_dma_probe,
+};
+
+static const struct dw_dma_platform_data idma32_pdata = {
.nr_channels = 8,
- .is_private = true,
- .is_memcpy = true,
- .is_idma32 = true,
.chan_allocation_order = CHAN_ALLOCATION_ASCENDING,
.chan_priority = CHAN_PRIORITY_ASCENDING,
.block_size = 131071,
.nr_masters = 1,
.data_width = {4},
+ .multi_block = {1, 1, 1, 1, 1, 1, 1, 1},
+};
+
+static const struct dw_dma_pci_data idma32_pci_data = {
+ .pdata = &idma32_pdata,
+ .probe = idma32_dma_probe,
};
static int dw_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pid)
{
- const struct dw_dma_platform_data *pdata = (void *)pid->driver_data;
+ const struct dw_dma_pci_data *data = (void *)pid->driver_data;
struct dw_dma_chip *chip;
int ret;
@@ -62,9 +71,9 @@ static int dw_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pid)
chip->id = pdev->devfn;
chip->regs = pcim_iomap_table(pdev)[0];
chip->irq = pdev->irq;
- chip->pdata = pdata;
+ chip->pdata = data->pdata;
- ret = dw_dma_probe(chip);
+ ret = data->probe(chip);
if (ret)
return ret;
@@ -90,7 +99,7 @@ static int dw_pci_suspend_late(struct device *dev)
struct pci_dev *pci = to_pci_dev(dev);
struct dw_dma_chip *chip = pci_get_drvdata(pci);
- return dw_dma_disable(chip);
+ return do_dw_dma_disable(chip);
};
static int dw_pci_resume_early(struct device *dev)
@@ -98,7 +107,7 @@ static int dw_pci_resume_early(struct device *dev)
struct pci_dev *pci = to_pci_dev(dev);
struct dw_dma_chip *chip = pci_get_drvdata(pci);
- return dw_dma_enable(chip);
+ return do_dw_dma_enable(chip);
};
#endif /* CONFIG_PM_SLEEP */
@@ -109,24 +118,24 @@ static const struct dev_pm_ops dw_pci_dev_pm_ops = {
static const struct pci_device_id dw_pci_id_table[] = {
/* Medfield (GPDMA) */
- { PCI_VDEVICE(INTEL, 0x0827) },
+ { PCI_VDEVICE(INTEL, 0x0827), (kernel_ulong_t)&dw_pci_data },
/* BayTrail */
- { PCI_VDEVICE(INTEL, 0x0f06) },
- { PCI_VDEVICE(INTEL, 0x0f40) },
+ { PCI_VDEVICE(INTEL, 0x0f06), (kernel_ulong_t)&dw_pci_data },
+ { PCI_VDEVICE(INTEL, 0x0f40), (kernel_ulong_t)&dw_pci_data },
- /* Merrifield iDMA 32-bit (GPDMA) */
- { PCI_VDEVICE(INTEL, 0x11a2), (kernel_ulong_t)&mrfld_pdata },
+ /* Merrifield */
+ { PCI_VDEVICE(INTEL, 0x11a2), (kernel_ulong_t)&idma32_pci_data },
/* Braswell */
- { PCI_VDEVICE(INTEL, 0x2286) },
- { PCI_VDEVICE(INTEL, 0x22c0) },
+ { PCI_VDEVICE(INTEL, 0x2286), (kernel_ulong_t)&dw_pci_data },
+ { PCI_VDEVICE(INTEL, 0x22c0), (kernel_ulong_t)&dw_pci_data },
/* Haswell */
- { PCI_VDEVICE(INTEL, 0x9c60) },
+ { PCI_VDEVICE(INTEL, 0x9c60), (kernel_ulong_t)&dw_pci_data },
/* Broadwell */
- { PCI_VDEVICE(INTEL, 0x9ce0) },
+ { PCI_VDEVICE(INTEL, 0x9ce0), (kernel_ulong_t)&dw_pci_data },
{ }
};
diff --git a/drivers/dma/dw/platform.c b/drivers/dma/dw/platform.c
index 31ff8113c3de..382dfd9e9600 100644
--- a/drivers/dma/dw/platform.c
+++ b/drivers/dma/dw/platform.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Platform driver for the Synopsys DesignWare DMA Controller
*
@@ -6,10 +7,6 @@
* Copyright (C) 2013 Intel Corporation
*
* Some parts of this driver are derived from the original dw_dmac.
- *
- * 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 <linux/module.h>
@@ -128,15 +125,6 @@ dw_dma_parse_dt(struct platform_device *pdev)
pdata->nr_masters = nr_masters;
pdata->nr_channels = nr_channels;
- if (of_property_read_bool(np, "is_private"))
- pdata->is_private = true;
-
- /*
- * All known devices, which use DT for configuration, support
- * memory-to-memory transfers. So enable it by default.
- */
- pdata->is_memcpy = true;
-
if (!of_property_read_u32(np, "chan_allocation_order", &tmp))
pdata->chan_allocation_order = (unsigned char)tmp;
@@ -264,7 +252,7 @@ static void dw_shutdown(struct platform_device *pdev)
struct dw_dma_chip *chip = platform_get_drvdata(pdev);
/*
- * We have to call dw_dma_disable() to stop any ongoing transfer. On
+ * We have to call do_dw_dma_disable() to stop any ongoing transfer. On
* some platforms we can't do that since DMA device is powered off.
* Moreover we have no possibility to check if the platform is affected
* or not. That's why we call pm_runtime_get_sync() / pm_runtime_put()
@@ -273,7 +261,7 @@ static void dw_shutdown(struct platform_device *pdev)
* used by the driver.
*/
pm_runtime_get_sync(chip->dev);
- dw_dma_disable(chip);
+ do_dw_dma_disable(chip);
pm_runtime_put_sync_suspend(chip->dev);
clk_disable_unprepare(chip->clk);
@@ -303,7 +291,7 @@ static int dw_suspend_late(struct device *dev)
{
struct dw_dma_chip *chip = dev_get_drvdata(dev);
- dw_dma_disable(chip);
+ do_dw_dma_disable(chip);
clk_disable_unprepare(chip->clk);
return 0;
@@ -318,7 +306,7 @@ static int dw_resume_early(struct device *dev)
if (ret)
return ret;
- return dw_dma_enable(chip);
+ return do_dw_dma_enable(chip);
}
#endif /* CONFIG_PM_SLEEP */
diff --git a/drivers/dma/dw/regs.h b/drivers/dma/dw/regs.h
index 646c9c960c07..3fce66ecee7a 100644
--- a/drivers/dma/dw/regs.h
+++ b/drivers/dma/dw/regs.h
@@ -1,13 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Driver for the Synopsys DesignWare AHB DMA Controller
*
* Copyright (C) 2005-2007 Atmel Corporation
* Copyright (C) 2010-2011 ST Microelectronics
* Copyright (C) 2016 Intel Corporation
- *
- * 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 <linux/bitops.h>
@@ -222,6 +219,16 @@ enum dw_dma_msize {
/* iDMA 32-bit support */
+/* bursts size */
+enum idma32_msize {
+ IDMA32_MSIZE_1,
+ IDMA32_MSIZE_2,
+ IDMA32_MSIZE_4,
+ IDMA32_MSIZE_8,
+ IDMA32_MSIZE_16,
+ IDMA32_MSIZE_32,
+};
+
/* Bitfields in CTL_HI */
#define IDMA32C_CTLH_BLOCK_TS_MASK GENMASK(16, 0)
#define IDMA32C_CTLH_BLOCK_TS(x) ((x) & IDMA32C_CTLH_BLOCK_TS_MASK)
@@ -312,6 +319,21 @@ struct dw_dma {
u8 all_chan_mask;
u8 in_use;
+ /* Channel operations */
+ void (*initialize_chan)(struct dw_dma_chan *dwc);
+ void (*suspend_chan)(struct dw_dma_chan *dwc, bool drain);
+ void (*resume_chan)(struct dw_dma_chan *dwc, bool drain);
+ u32 (*prepare_ctllo)(struct dw_dma_chan *dwc);
+ void (*encode_maxburst)(struct dw_dma_chan *dwc, u32 *maxburst);
+ u32 (*bytes2block)(struct dw_dma_chan *dwc, size_t bytes,
+ unsigned int width, size_t *len);
+ size_t (*block2bytes)(struct dw_dma_chan *dwc, u32 block, u32 width);
+
+ /* Device operations */
+ void (*set_device_name)(struct dw_dma *dw, int id);
+ void (*disable)(struct dw_dma *dw);
+ void (*enable)(struct dw_dma *dw);
+
/* platform data */
struct dw_dma_platform_data *pdata;
};
diff --git a/drivers/dma/fsl-edma-common.c b/drivers/dma/fsl-edma-common.c
index 8876c4c1bb2c..680b2a00a953 100644
--- a/drivers/dma/fsl-edma-common.c
+++ b/drivers/dma/fsl-edma-common.c
@@ -6,6 +6,7 @@
#include <linux/dmapool.h>
#include <linux/module.h>
#include <linux/slab.h>
+#include <linux/dma-mapping.h>
#include "fsl-edma-common.h"
@@ -173,12 +174,62 @@ int fsl_edma_resume(struct dma_chan *chan)
}
EXPORT_SYMBOL_GPL(fsl_edma_resume);
+static void fsl_edma_unprep_slave_dma(struct fsl_edma_chan *fsl_chan)
+{
+ if (fsl_chan->dma_dir != DMA_NONE)
+ dma_unmap_resource(fsl_chan->vchan.chan.device->dev,
+ fsl_chan->dma_dev_addr,
+ fsl_chan->dma_dev_size,
+ fsl_chan->dma_dir, 0);
+ fsl_chan->dma_dir = DMA_NONE;
+}
+
+static bool fsl_edma_prep_slave_dma(struct fsl_edma_chan *fsl_chan,
+ enum dma_transfer_direction dir)
+{
+ struct device *dev = fsl_chan->vchan.chan.device->dev;
+ enum dma_data_direction dma_dir;
+ phys_addr_t addr = 0;
+ u32 size = 0;
+
+ switch (dir) {
+ case DMA_MEM_TO_DEV:
+ dma_dir = DMA_FROM_DEVICE;
+ addr = fsl_chan->cfg.dst_addr;
+ size = fsl_chan->cfg.dst_maxburst;
+ break;
+ case DMA_DEV_TO_MEM:
+ dma_dir = DMA_TO_DEVICE;
+ addr = fsl_chan->cfg.src_addr;
+ size = fsl_chan->cfg.src_maxburst;
+ break;
+ default:
+ dma_dir = DMA_NONE;
+ break;
+ }
+
+ /* Already mapped for this config? */
+ if (fsl_chan->dma_dir == dma_dir)
+ return true;
+
+ fsl_edma_unprep_slave_dma(fsl_chan);
+
+ fsl_chan->dma_dev_addr = dma_map_resource(dev, addr, size, dma_dir, 0);
+ if (dma_mapping_error(dev, fsl_chan->dma_dev_addr))
+ return false;
+ fsl_chan->dma_dev_size = size;
+ fsl_chan->dma_dir = dma_dir;
+
+ return true;
+}
+
int fsl_edma_slave_config(struct dma_chan *chan,
struct dma_slave_config *cfg)
{
struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
memcpy(&fsl_chan->cfg, cfg, sizeof(*cfg));
+ fsl_edma_unprep_slave_dma(fsl_chan);
return 0;
}
@@ -339,9 +390,7 @@ static struct fsl_edma_desc *fsl_edma_alloc_desc(struct fsl_edma_chan *fsl_chan,
struct fsl_edma_desc *fsl_desc;
int i;
- fsl_desc = kzalloc(sizeof(*fsl_desc) +
- sizeof(struct fsl_edma_sw_tcd) *
- sg_len, GFP_NOWAIT);
+ fsl_desc = kzalloc(struct_size(fsl_desc, tcd, sg_len), GFP_NOWAIT);
if (!fsl_desc)
return NULL;
@@ -378,6 +427,9 @@ struct dma_async_tx_descriptor *fsl_edma_prep_dma_cyclic(
if (!is_slave_direction(direction))
return NULL;
+ if (!fsl_edma_prep_slave_dma(fsl_chan, direction))
+ return NULL;
+
sg_len = buf_len / period_len;
fsl_desc = fsl_edma_alloc_desc(fsl_chan, sg_len);
if (!fsl_desc)
@@ -409,11 +461,11 @@ struct dma_async_tx_descriptor *fsl_edma_prep_dma_cyclic(
if (direction == DMA_MEM_TO_DEV) {
src_addr = dma_buf_next;
- dst_addr = fsl_chan->cfg.dst_addr;
+ dst_addr = fsl_chan->dma_dev_addr;
soff = fsl_chan->cfg.dst_addr_width;
doff = 0;
} else {
- src_addr = fsl_chan->cfg.src_addr;
+ src_addr = fsl_chan->dma_dev_addr;
dst_addr = dma_buf_next;
soff = 0;
doff = fsl_chan->cfg.src_addr_width;
@@ -444,6 +496,9 @@ struct dma_async_tx_descriptor *fsl_edma_prep_slave_sg(
if (!is_slave_direction(direction))
return NULL;
+ if (!fsl_edma_prep_slave_dma(fsl_chan, direction))
+ return NULL;
+
fsl_desc = fsl_edma_alloc_desc(fsl_chan, sg_len);
if (!fsl_desc)
return NULL;
@@ -468,11 +523,11 @@ struct dma_async_tx_descriptor *fsl_edma_prep_slave_sg(
if (direction == DMA_MEM_TO_DEV) {
src_addr = sg_dma_address(sg);
- dst_addr = fsl_chan->cfg.dst_addr;
+ dst_addr = fsl_chan->dma_dev_addr;
soff = fsl_chan->cfg.dst_addr_width;
doff = 0;
} else {
- src_addr = fsl_chan->cfg.src_addr;
+ src_addr = fsl_chan->dma_dev_addr;
dst_addr = sg_dma_address(sg);
soff = 0;
doff = fsl_chan->cfg.src_addr_width;
@@ -555,6 +610,7 @@ void fsl_edma_free_chan_resources(struct dma_chan *chan)
fsl_edma_chan_mux(fsl_chan, 0, false);
fsl_chan->edesc = NULL;
vchan_get_all_descriptors(&fsl_chan->vchan, &head);
+ fsl_edma_unprep_slave_dma(fsl_chan);
spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags);
vchan_dma_desc_free_list(&fsl_chan->vchan, &head);
diff --git a/drivers/dma/fsl-edma-common.h b/drivers/dma/fsl-edma-common.h
index 8917e8865959..b435d8e1e3a1 100644
--- a/drivers/dma/fsl-edma-common.h
+++ b/drivers/dma/fsl-edma-common.h
@@ -6,6 +6,7 @@
#ifndef _FSL_EDMA_COMMON_H_
#define _FSL_EDMA_COMMON_H_
+#include <linux/dma-direction.h>
#include "virt-dma.h"
#define EDMA_CR_EDBG BIT(1)
@@ -120,6 +121,9 @@ struct fsl_edma_chan {
struct dma_slave_config cfg;
u32 attr;
struct dma_pool *tcd_pool;
+ dma_addr_t dma_dev_addr;
+ u32 dma_dev_size;
+ enum dma_data_direction dma_dir;
};
struct fsl_edma_desc {
diff --git a/drivers/dma/fsl-edma.c b/drivers/dma/fsl-edma.c
index 34d70112fcc9..75e8a7ba3a22 100644
--- a/drivers/dma/fsl-edma.c
+++ b/drivers/dma/fsl-edma.c
@@ -254,6 +254,7 @@ static int fsl_edma_probe(struct platform_device *pdev)
fsl_chan->pm_state = RUNNING;
fsl_chan->slave_id = 0;
fsl_chan->idle = true;
+ fsl_chan->dma_dir = DMA_NONE;
fsl_chan->vchan.desc_free = fsl_edma_free_desc;
vchan_init(&fsl_chan->vchan, &fsl_edma->dma_dev);
diff --git a/drivers/dma/fsl-qdma.c b/drivers/dma/fsl-qdma.c
new file mode 100644
index 000000000000..aa1d0ae3d207
--- /dev/null
+++ b/drivers/dma/fsl-qdma.c
@@ -0,0 +1,1259 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright 2014-2015 Freescale
+// Copyright 2018 NXP
+
+/*
+ * Driver for NXP Layerscape Queue Direct Memory Access Controller
+ *
+ * Author:
+ * Wen He <wen.he_1@nxp.com>
+ * Jiaheng Fan <jiaheng.fan@nxp.com>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/of_dma.h>
+#include <linux/dma-mapping.h>
+
+#include "virt-dma.h"
+#include "fsldma.h"
+
+/* Register related definition */
+#define FSL_QDMA_DMR 0x0
+#define FSL_QDMA_DSR 0x4
+#define FSL_QDMA_DEIER 0xe00
+#define FSL_QDMA_DEDR 0xe04
+#define FSL_QDMA_DECFDW0R 0xe10
+#define FSL_QDMA_DECFDW1R 0xe14
+#define FSL_QDMA_DECFDW2R 0xe18
+#define FSL_QDMA_DECFDW3R 0xe1c
+#define FSL_QDMA_DECFQIDR 0xe30
+#define FSL_QDMA_DECBR 0xe34
+
+#define FSL_QDMA_BCQMR(x) (0xc0 + 0x100 * (x))
+#define FSL_QDMA_BCQSR(x) (0xc4 + 0x100 * (x))
+#define FSL_QDMA_BCQEDPA_SADDR(x) (0xc8 + 0x100 * (x))
+#define FSL_QDMA_BCQDPA_SADDR(x) (0xcc + 0x100 * (x))
+#define FSL_QDMA_BCQEEPA_SADDR(x) (0xd0 + 0x100 * (x))
+#define FSL_QDMA_BCQEPA_SADDR(x) (0xd4 + 0x100 * (x))
+#define FSL_QDMA_BCQIER(x) (0xe0 + 0x100 * (x))
+#define FSL_QDMA_BCQIDR(x) (0xe4 + 0x100 * (x))
+
+#define FSL_QDMA_SQDPAR 0x80c
+#define FSL_QDMA_SQEPAR 0x814
+#define FSL_QDMA_BSQMR 0x800
+#define FSL_QDMA_BSQSR 0x804
+#define FSL_QDMA_BSQICR 0x828
+#define FSL_QDMA_CQMR 0xa00
+#define FSL_QDMA_CQDSCR1 0xa08
+#define FSL_QDMA_CQDSCR2 0xa0c
+#define FSL_QDMA_CQIER 0xa10
+#define FSL_QDMA_CQEDR 0xa14
+#define FSL_QDMA_SQCCMR 0xa20
+
+/* Registers for bit and genmask */
+#define FSL_QDMA_CQIDR_SQT BIT(15)
+#define QDMA_CCDF_FOTMAT BIT(29)
+#define QDMA_CCDF_SER BIT(30)
+#define QDMA_SG_FIN BIT(30)
+#define QDMA_SG_LEN_MASK GENMASK(29, 0)
+#define QDMA_CCDF_MASK GENMASK(28, 20)
+
+#define FSL_QDMA_DEDR_CLEAR GENMASK(31, 0)
+#define FSL_QDMA_BCQIDR_CLEAR GENMASK(31, 0)
+#define FSL_QDMA_DEIER_CLEAR GENMASK(31, 0)
+
+#define FSL_QDMA_BCQIER_CQTIE BIT(15)
+#define FSL_QDMA_BCQIER_CQPEIE BIT(23)
+#define FSL_QDMA_BSQICR_ICEN BIT(31)
+
+#define FSL_QDMA_BSQICR_ICST(x) ((x) << 16)
+#define FSL_QDMA_CQIER_MEIE BIT(31)
+#define FSL_QDMA_CQIER_TEIE BIT(0)
+#define FSL_QDMA_SQCCMR_ENTER_WM BIT(21)
+
+#define FSL_QDMA_BCQMR_EN BIT(31)
+#define FSL_QDMA_BCQMR_EI BIT(30)
+#define FSL_QDMA_BCQMR_CD_THLD(x) ((x) << 20)
+#define FSL_QDMA_BCQMR_CQ_SIZE(x) ((x) << 16)
+
+#define FSL_QDMA_BCQSR_QF BIT(16)
+#define FSL_QDMA_BCQSR_XOFF BIT(0)
+
+#define FSL_QDMA_BSQMR_EN BIT(31)
+#define FSL_QDMA_BSQMR_DI BIT(30)
+#define FSL_QDMA_BSQMR_CQ_SIZE(x) ((x) << 16)
+
+#define FSL_QDMA_BSQSR_QE BIT(17)
+
+#define FSL_QDMA_DMR_DQD BIT(30)
+#define FSL_QDMA_DSR_DB BIT(31)
+
+/* Size related definition */
+#define FSL_QDMA_QUEUE_MAX 8
+#define FSL_QDMA_COMMAND_BUFFER_SIZE 64
+#define FSL_QDMA_DESCRIPTOR_BUFFER_SIZE 32
+#define FSL_QDMA_CIRCULAR_DESC_SIZE_MIN 64
+#define FSL_QDMA_CIRCULAR_DESC_SIZE_MAX 16384
+#define FSL_QDMA_QUEUE_NUM_MAX 8
+
+/* Field definition for CMD */
+#define FSL_QDMA_CMD_RWTTYPE 0x4
+#define FSL_QDMA_CMD_LWC 0x2
+#define FSL_QDMA_CMD_RWTTYPE_OFFSET 28
+#define FSL_QDMA_CMD_NS_OFFSET 27
+#define FSL_QDMA_CMD_DQOS_OFFSET 24
+#define FSL_QDMA_CMD_WTHROTL_OFFSET 20
+#define FSL_QDMA_CMD_DSEN_OFFSET 19
+#define FSL_QDMA_CMD_LWC_OFFSET 16
+
+/* Field definition for Descriptor offset */
+#define QDMA_CCDF_STATUS 20
+#define QDMA_CCDF_OFFSET 20
+
+/* Field definition for safe loop count*/
+#define FSL_QDMA_HALT_COUNT 1500
+#define FSL_QDMA_MAX_SIZE 16385
+#define FSL_QDMA_COMP_TIMEOUT 1000
+#define FSL_COMMAND_QUEUE_OVERFLLOW 10
+
+#define FSL_QDMA_BLOCK_BASE_OFFSET(fsl_qdma_engine, x) \
+ (((fsl_qdma_engine)->block_offset) * (x))
+
+/**
+ * struct fsl_qdma_format - This is the struct holding describing compound
+ * descriptor format with qDMA.
+ * @status: Command status and enqueue status notification.
+ * @cfg: Frame offset and frame format.
+ * @addr_lo: Holding the compound descriptor of the lower
+ * 32-bits address in memory 40-bit address.
+ * @addr_hi: Same as above member, but point high 8-bits in
+ * memory 40-bit address.
+ * @__reserved1: Reserved field.
+ * @cfg8b_w1: Compound descriptor command queue origin produced
+ * by qDMA and dynamic debug field.
+ * @data Pointer to the memory 40-bit address, describes DMA
+ * source information and DMA destination information.
+ */
+struct fsl_qdma_format {
+ __le32 status;
+ __le32 cfg;
+ union {
+ struct {
+ __le32 addr_lo;
+ u8 addr_hi;
+ u8 __reserved1[2];
+ u8 cfg8b_w1;
+ } __packed;
+ __le64 data;
+ };
+} __packed;
+
+/* qDMA status notification pre information */
+struct fsl_pre_status {
+ u64 addr;
+ u8 queue;
+};
+
+static DEFINE_PER_CPU(struct fsl_pre_status, pre);
+
+struct fsl_qdma_chan {
+ struct virt_dma_chan vchan;
+ struct virt_dma_desc vdesc;
+ enum dma_status status;
+ struct fsl_qdma_engine *qdma;
+ struct fsl_qdma_queue *queue;
+};
+
+struct fsl_qdma_queue {
+ struct fsl_qdma_format *virt_head;
+ struct fsl_qdma_format *virt_tail;
+ struct list_head comp_used;
+ struct list_head comp_free;
+ struct dma_pool *comp_pool;
+ struct dma_pool *desc_pool;
+ spinlock_t queue_lock;
+ dma_addr_t bus_addr;
+ u32 n_cq;
+ u32 id;
+ struct fsl_qdma_format *cq;
+ void __iomem *block_base;
+};
+
+struct fsl_qdma_comp {
+ dma_addr_t bus_addr;
+ dma_addr_t desc_bus_addr;
+ struct fsl_qdma_format *virt_addr;
+ struct fsl_qdma_format *desc_virt_addr;
+ struct fsl_qdma_chan *qchan;
+ struct virt_dma_desc vdesc;
+ struct list_head list;
+};
+
+struct fsl_qdma_engine {
+ struct dma_device dma_dev;
+ void __iomem *ctrl_base;
+ void __iomem *status_base;
+ void __iomem *block_base;
+ u32 n_chans;
+ u32 n_queues;
+ struct mutex fsl_qdma_mutex;
+ int error_irq;
+ int *queue_irq;
+ u32 feature;
+ struct fsl_qdma_queue *queue;
+ struct fsl_qdma_queue **status;
+ struct fsl_qdma_chan *chans;
+ int block_number;
+ int block_offset;
+ int irq_base;
+ int desc_allocated;
+
+};
+
+static inline u64
+qdma_ccdf_addr_get64(const struct fsl_qdma_format *ccdf)
+{
+ return le64_to_cpu(ccdf->data) & (U64_MAX >> 24);
+}
+
+static inline void
+qdma_desc_addr_set64(struct fsl_qdma_format *ccdf, u64 addr)
+{
+ ccdf->addr_hi = upper_32_bits(addr);
+ ccdf->addr_lo = cpu_to_le32(lower_32_bits(addr));
+}
+
+static inline u8
+qdma_ccdf_get_queue(const struct fsl_qdma_format *ccdf)
+{
+ return ccdf->cfg8b_w1 & U8_MAX;
+}
+
+static inline int
+qdma_ccdf_get_offset(const struct fsl_qdma_format *ccdf)
+{
+ return (le32_to_cpu(ccdf->cfg) & QDMA_CCDF_MASK) >> QDMA_CCDF_OFFSET;
+}
+
+static inline void
+qdma_ccdf_set_format(struct fsl_qdma_format *ccdf, int offset)
+{
+ ccdf->cfg = cpu_to_le32(QDMA_CCDF_FOTMAT | offset);
+}
+
+static inline int
+qdma_ccdf_get_status(const struct fsl_qdma_format *ccdf)
+{
+ return (le32_to_cpu(ccdf->status) & QDMA_CCDF_MASK) >> QDMA_CCDF_STATUS;
+}
+
+static inline void
+qdma_ccdf_set_ser(struct fsl_qdma_format *ccdf, int status)
+{
+ ccdf->status = cpu_to_le32(QDMA_CCDF_SER | status);
+}
+
+static inline void qdma_csgf_set_len(struct fsl_qdma_format *csgf, int len)
+{
+ csgf->cfg = cpu_to_le32(len & QDMA_SG_LEN_MASK);
+}
+
+static inline void qdma_csgf_set_f(struct fsl_qdma_format *csgf, int len)
+{
+ csgf->cfg = cpu_to_le32(QDMA_SG_FIN | (len & QDMA_SG_LEN_MASK));
+}
+
+static u32 qdma_readl(struct fsl_qdma_engine *qdma, void __iomem *addr)
+{
+ return FSL_DMA_IN(qdma, addr, 32);
+}
+
+static void qdma_writel(struct fsl_qdma_engine *qdma, u32 val,
+ void __iomem *addr)
+{
+ FSL_DMA_OUT(qdma, addr, val, 32);
+}
+
+static struct fsl_qdma_chan *to_fsl_qdma_chan(struct dma_chan *chan)
+{
+ return container_of(chan, struct fsl_qdma_chan, vchan.chan);
+}
+
+static struct fsl_qdma_comp *to_fsl_qdma_comp(struct virt_dma_desc *vd)
+{
+ return container_of(vd, struct fsl_qdma_comp, vdesc);
+}
+
+static void fsl_qdma_free_chan_resources(struct dma_chan *chan)
+{
+ struct fsl_qdma_chan *fsl_chan = to_fsl_qdma_chan(chan);
+ struct fsl_qdma_queue *fsl_queue = fsl_chan->queue;
+ struct fsl_qdma_engine *fsl_qdma = fsl_chan->qdma;
+ struct fsl_qdma_comp *comp_temp, *_comp_temp;
+ unsigned long flags;
+ LIST_HEAD(head);
+
+ spin_lock_irqsave(&fsl_chan->vchan.lock, flags);
+ vchan_get_all_descriptors(&fsl_chan->vchan, &head);
+ spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags);
+
+ vchan_dma_desc_free_list(&fsl_chan->vchan, &head);
+
+ if (!fsl_queue->comp_pool && !fsl_queue->comp_pool)
+ return;
+
+ list_for_each_entry_safe(comp_temp, _comp_temp,
+ &fsl_queue->comp_used, list) {
+ dma_pool_free(fsl_queue->comp_pool,
+ comp_temp->virt_addr,
+ comp_temp->bus_addr);
+ dma_pool_free(fsl_queue->desc_pool,
+ comp_temp->desc_virt_addr,
+ comp_temp->desc_bus_addr);
+ list_del(&comp_temp->list);
+ kfree(comp_temp);
+ }
+
+ list_for_each_entry_safe(comp_temp, _comp_temp,
+ &fsl_queue->comp_free, list) {
+ dma_pool_free(fsl_queue->comp_pool,
+ comp_temp->virt_addr,
+ comp_temp->bus_addr);
+ dma_pool_free(fsl_queue->desc_pool,
+ comp_temp->desc_virt_addr,
+ comp_temp->desc_bus_addr);
+ list_del(&comp_temp->list);
+ kfree(comp_temp);
+ }
+
+ dma_pool_destroy(fsl_queue->comp_pool);
+ dma_pool_destroy(fsl_queue->desc_pool);
+
+ fsl_qdma->desc_allocated--;
+ fsl_queue->comp_pool = NULL;
+ fsl_queue->desc_pool = NULL;
+}
+
+static void fsl_qdma_comp_fill_memcpy(struct fsl_qdma_comp *fsl_comp,
+ dma_addr_t dst, dma_addr_t src, u32 len)
+{
+ struct fsl_qdma_format *sdf, *ddf;
+ struct fsl_qdma_format *ccdf, *csgf_desc, *csgf_src, *csgf_dest;
+
+ ccdf = fsl_comp->virt_addr;
+ csgf_desc = fsl_comp->virt_addr + 1;
+ csgf_src = fsl_comp->virt_addr + 2;
+ csgf_dest = fsl_comp->virt_addr + 3;
+ sdf = fsl_comp->desc_virt_addr;
+ ddf = fsl_comp->desc_virt_addr + 1;
+
+ memset(fsl_comp->virt_addr, 0, FSL_QDMA_COMMAND_BUFFER_SIZE);
+ memset(fsl_comp->desc_virt_addr, 0, FSL_QDMA_DESCRIPTOR_BUFFER_SIZE);
+ /* Head Command Descriptor(Frame Descriptor) */
+ qdma_desc_addr_set64(ccdf, fsl_comp->bus_addr + 16);
+ qdma_ccdf_set_format(ccdf, qdma_ccdf_get_offset(ccdf));
+ qdma_ccdf_set_ser(ccdf, qdma_ccdf_get_status(ccdf));
+ /* Status notification is enqueued to status queue. */
+ /* Compound Command Descriptor(Frame List Table) */
+ qdma_desc_addr_set64(csgf_desc, fsl_comp->desc_bus_addr);
+ /* It must be 32 as Compound S/G Descriptor */
+ qdma_csgf_set_len(csgf_desc, 32);
+ qdma_desc_addr_set64(csgf_src, src);
+ qdma_csgf_set_len(csgf_src, len);
+ qdma_desc_addr_set64(csgf_dest, dst);
+ qdma_csgf_set_len(csgf_dest, len);
+ /* This entry is the last entry. */
+ qdma_csgf_set_f(csgf_dest, len);
+ /* Descriptor Buffer */
+ sdf->data =
+ cpu_to_le64(FSL_QDMA_CMD_RWTTYPE <<
+ FSL_QDMA_CMD_RWTTYPE_OFFSET);
+ ddf->data =
+ cpu_to_le64(FSL_QDMA_CMD_RWTTYPE <<
+ FSL_QDMA_CMD_RWTTYPE_OFFSET);
+ ddf->data |=
+ cpu_to_le64(FSL_QDMA_CMD_LWC << FSL_QDMA_CMD_LWC_OFFSET);
+}
+
+/*
+ * Pre-request full command descriptor for enqueue.
+ */
+static int fsl_qdma_pre_request_enqueue_desc(struct fsl_qdma_queue *queue)
+{
+ int i;
+ struct fsl_qdma_comp *comp_temp, *_comp_temp;
+
+ for (i = 0; i < queue->n_cq + FSL_COMMAND_QUEUE_OVERFLLOW; i++) {
+ comp_temp = kzalloc(sizeof(*comp_temp), GFP_KERNEL);
+ if (!comp_temp)
+ goto err_alloc;
+ comp_temp->virt_addr =
+ dma_pool_alloc(queue->comp_pool, GFP_KERNEL,
+ &comp_temp->bus_addr);
+ if (!comp_temp->virt_addr)
+ goto err_dma_alloc;
+
+ comp_temp->desc_virt_addr =
+ dma_pool_alloc(queue->desc_pool, GFP_KERNEL,
+ &comp_temp->desc_bus_addr);
+ if (!comp_temp->desc_virt_addr)
+ goto err_desc_dma_alloc;
+
+ list_add_tail(&comp_temp->list, &queue->comp_free);
+ }
+
+ return 0;
+
+err_desc_dma_alloc:
+ dma_pool_free(queue->comp_pool, comp_temp->virt_addr,
+ comp_temp->bus_addr);
+
+err_dma_alloc:
+ kfree(comp_temp);
+
+err_alloc:
+ list_for_each_entry_safe(comp_temp, _comp_temp,
+ &queue->comp_free, list) {
+ if (comp_temp->virt_addr)
+ dma_pool_free(queue->comp_pool,
+ comp_temp->virt_addr,
+ comp_temp->bus_addr);
+ if (comp_temp->desc_virt_addr)
+ dma_pool_free(queue->desc_pool,
+ comp_temp->desc_virt_addr,
+ comp_temp->desc_bus_addr);
+
+ list_del(&comp_temp->list);
+ kfree(comp_temp);
+ }
+
+ return -ENOMEM;
+}
+
+/*
+ * Request a command descriptor for enqueue.
+ */
+static struct fsl_qdma_comp
+*fsl_qdma_request_enqueue_desc(struct fsl_qdma_chan *fsl_chan)
+{
+ unsigned long flags;
+ struct fsl_qdma_comp *comp_temp;
+ int timeout = FSL_QDMA_COMP_TIMEOUT;
+ struct fsl_qdma_queue *queue = fsl_chan->queue;
+
+ while (timeout--) {
+ spin_lock_irqsave(&queue->queue_lock, flags);
+ if (!list_empty(&queue->comp_free)) {
+ comp_temp = list_first_entry(&queue->comp_free,
+ struct fsl_qdma_comp,
+ list);
+ list_del(&comp_temp->list);
+
+ spin_unlock_irqrestore(&queue->queue_lock, flags);
+ comp_temp->qchan = fsl_chan;
+ return comp_temp;
+ }
+ spin_unlock_irqrestore(&queue->queue_lock, flags);
+ udelay(1);
+ }
+
+ return NULL;
+}
+
+static struct fsl_qdma_queue
+*fsl_qdma_alloc_queue_resources(struct platform_device *pdev,
+ struct fsl_qdma_engine *fsl_qdma)
+{
+ int ret, len, i, j;
+ int queue_num, block_number;
+ unsigned int queue_size[FSL_QDMA_QUEUE_MAX];
+ struct fsl_qdma_queue *queue_head, *queue_temp;
+
+ queue_num = fsl_qdma->n_queues;
+ block_number = fsl_qdma->block_number;
+
+ if (queue_num > FSL_QDMA_QUEUE_MAX)
+ queue_num = FSL_QDMA_QUEUE_MAX;
+ len = sizeof(*queue_head) * queue_num * block_number;
+ queue_head = devm_kzalloc(&pdev->dev, len, GFP_KERNEL);
+ if (!queue_head)
+ return NULL;
+
+ ret = device_property_read_u32_array(&pdev->dev, "queue-sizes",
+ queue_size, queue_num);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't get queue-sizes.\n");
+ return NULL;
+ }
+ for (j = 0; j < block_number; j++) {
+ for (i = 0; i < queue_num; i++) {
+ if (queue_size[i] > FSL_QDMA_CIRCULAR_DESC_SIZE_MAX ||
+ queue_size[i] < FSL_QDMA_CIRCULAR_DESC_SIZE_MIN) {
+ dev_err(&pdev->dev,
+ "Get wrong queue-sizes.\n");
+ return NULL;
+ }
+ queue_temp = queue_head + i + (j * queue_num);
+
+ queue_temp->cq =
+ dma_alloc_coherent(&pdev->dev,
+ sizeof(struct fsl_qdma_format) *
+ queue_size[i],
+ &queue_temp->bus_addr,
+ GFP_KERNEL);
+ if (!queue_temp->cq)
+ return NULL;
+ queue_temp->block_base = fsl_qdma->block_base +
+ FSL_QDMA_BLOCK_BASE_OFFSET(fsl_qdma, j);
+ queue_temp->n_cq = queue_size[i];
+ queue_temp->id = i;
+ queue_temp->virt_head = queue_temp->cq;
+ queue_temp->virt_tail = queue_temp->cq;
+ /*
+ * List for queue command buffer
+ */
+ INIT_LIST_HEAD(&queue_temp->comp_used);
+ spin_lock_init(&queue_temp->queue_lock);
+ }
+ }
+ return queue_head;
+}
+
+static struct fsl_qdma_queue
+*fsl_qdma_prep_status_queue(struct platform_device *pdev)
+{
+ int ret;
+ unsigned int status_size;
+ struct fsl_qdma_queue *status_head;
+ struct device_node *np = pdev->dev.of_node;
+
+ ret = of_property_read_u32(np, "status-sizes", &status_size);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't get status-sizes.\n");
+ return NULL;
+ }
+ if (status_size > FSL_QDMA_CIRCULAR_DESC_SIZE_MAX ||
+ status_size < FSL_QDMA_CIRCULAR_DESC_SIZE_MIN) {
+ dev_err(&pdev->dev, "Get wrong status_size.\n");
+ return NULL;
+ }
+ status_head = devm_kzalloc(&pdev->dev,
+ sizeof(*status_head), GFP_KERNEL);
+ if (!status_head)
+ return NULL;
+
+ /*
+ * Buffer for queue command
+ */
+ status_head->cq = dma_alloc_coherent(&pdev->dev,
+ sizeof(struct fsl_qdma_format) *
+ status_size,
+ &status_head->bus_addr,
+ GFP_KERNEL);
+ if (!status_head->cq) {
+ devm_kfree(&pdev->dev, status_head);
+ return NULL;
+ }
+ status_head->n_cq = status_size;
+ status_head->virt_head = status_head->cq;
+ status_head->virt_tail = status_head->cq;
+ status_head->comp_pool = NULL;
+
+ return status_head;
+}
+
+static int fsl_qdma_halt(struct fsl_qdma_engine *fsl_qdma)
+{
+ u32 reg;
+ int i, j, count = FSL_QDMA_HALT_COUNT;
+ void __iomem *block, *ctrl = fsl_qdma->ctrl_base;
+
+ /* Disable the command queue and wait for idle state. */
+ reg = qdma_readl(fsl_qdma, ctrl + FSL_QDMA_DMR);
+ reg |= FSL_QDMA_DMR_DQD;
+ qdma_writel(fsl_qdma, reg, ctrl + FSL_QDMA_DMR);
+ for (j = 0; j < fsl_qdma->block_number; j++) {
+ block = fsl_qdma->block_base +
+ FSL_QDMA_BLOCK_BASE_OFFSET(fsl_qdma, j);
+ for (i = 0; i < FSL_QDMA_QUEUE_NUM_MAX; i++)
+ qdma_writel(fsl_qdma, 0, block + FSL_QDMA_BCQMR(i));
+ }
+ while (1) {
+ reg = qdma_readl(fsl_qdma, ctrl + FSL_QDMA_DSR);
+ if (!(reg & FSL_QDMA_DSR_DB))
+ break;
+ if (count-- < 0)
+ return -EBUSY;
+ udelay(100);
+ }
+
+ for (j = 0; j < fsl_qdma->block_number; j++) {
+ block = fsl_qdma->block_base +
+ FSL_QDMA_BLOCK_BASE_OFFSET(fsl_qdma, j);
+
+ /* Disable status queue. */
+ qdma_writel(fsl_qdma, 0, block + FSL_QDMA_BSQMR);
+
+ /*
+ * clear the command queue interrupt detect register for
+ * all queues.
+ */
+ qdma_writel(fsl_qdma, FSL_QDMA_BCQIDR_CLEAR,
+ block + FSL_QDMA_BCQIDR(0));
+ }
+
+ return 0;
+}
+
+static int
+fsl_qdma_queue_transfer_complete(struct fsl_qdma_engine *fsl_qdma,
+ void *block,
+ int id)
+{
+ bool duplicate;
+ u32 reg, i, count;
+ struct fsl_qdma_queue *temp_queue;
+ struct fsl_qdma_format *status_addr;
+ struct fsl_qdma_comp *fsl_comp = NULL;
+ struct fsl_qdma_queue *fsl_queue = fsl_qdma->queue;
+ struct fsl_qdma_queue *fsl_status = fsl_qdma->status[id];
+
+ count = FSL_QDMA_MAX_SIZE;
+
+ while (count--) {
+ duplicate = 0;
+ reg = qdma_readl(fsl_qdma, block + FSL_QDMA_BSQSR);
+ if (reg & FSL_QDMA_BSQSR_QE)
+ return 0;
+
+ status_addr = fsl_status->virt_head;
+
+ if (qdma_ccdf_get_queue(status_addr) ==
+ __this_cpu_read(pre.queue) &&
+ qdma_ccdf_addr_get64(status_addr) ==
+ __this_cpu_read(pre.addr))
+ duplicate = 1;
+ i = qdma_ccdf_get_queue(status_addr) +
+ id * fsl_qdma->n_queues;
+ __this_cpu_write(pre.addr, qdma_ccdf_addr_get64(status_addr));
+ __this_cpu_write(pre.queue, qdma_ccdf_get_queue(status_addr));
+ temp_queue = fsl_queue + i;
+
+ spin_lock(&temp_queue->queue_lock);
+ if (list_empty(&temp_queue->comp_used)) {
+ if (!duplicate) {
+ spin_unlock(&temp_queue->queue_lock);
+ return -EAGAIN;
+ }
+ } else {
+ fsl_comp = list_first_entry(&temp_queue->comp_used,
+ struct fsl_qdma_comp, list);
+ if (fsl_comp->bus_addr + 16 !=
+ __this_cpu_read(pre.addr)) {
+ if (!duplicate) {
+ spin_unlock(&temp_queue->queue_lock);
+ return -EAGAIN;
+ }
+ }
+ }
+
+ if (duplicate) {
+ reg = qdma_readl(fsl_qdma, block + FSL_QDMA_BSQMR);
+ reg |= FSL_QDMA_BSQMR_DI;
+ qdma_desc_addr_set64(status_addr, 0x0);
+ fsl_status->virt_head++;
+ if (fsl_status->virt_head == fsl_status->cq
+ + fsl_status->n_cq)
+ fsl_status->virt_head = fsl_status->cq;
+ qdma_writel(fsl_qdma, reg, block + FSL_QDMA_BSQMR);
+ spin_unlock(&temp_queue->queue_lock);
+ continue;
+ }
+ list_del(&fsl_comp->list);
+
+ reg = qdma_readl(fsl_qdma, block + FSL_QDMA_BSQMR);
+ reg |= FSL_QDMA_BSQMR_DI;
+ qdma_desc_addr_set64(status_addr, 0x0);
+ fsl_status->virt_head++;
+ if (fsl_status->virt_head == fsl_status->cq + fsl_status->n_cq)
+ fsl_status->virt_head = fsl_status->cq;
+ qdma_writel(fsl_qdma, reg, block + FSL_QDMA_BSQMR);
+ spin_unlock(&temp_queue->queue_lock);
+
+ spin_lock(&fsl_comp->qchan->vchan.lock);
+ vchan_cookie_complete(&fsl_comp->vdesc);
+ fsl_comp->qchan->status = DMA_COMPLETE;
+ spin_unlock(&fsl_comp->qchan->vchan.lock);
+ }
+
+ return 0;
+}
+
+static irqreturn_t fsl_qdma_error_handler(int irq, void *dev_id)
+{
+ unsigned int intr;
+ struct fsl_qdma_engine *fsl_qdma = dev_id;
+ void __iomem *status = fsl_qdma->status_base;
+
+ intr = qdma_readl(fsl_qdma, status + FSL_QDMA_DEDR);
+
+ if (intr) {
+ dev_err(fsl_qdma->dma_dev.dev, "DMA transaction error!\n");
+ return IRQ_NONE;
+ }
+
+ qdma_writel(fsl_qdma, FSL_QDMA_DEDR_CLEAR, status + FSL_QDMA_DEDR);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t fsl_qdma_queue_handler(int irq, void *dev_id)
+{
+ int id;
+ unsigned int intr, reg;
+ struct fsl_qdma_engine *fsl_qdma = dev_id;
+ void __iomem *block, *ctrl = fsl_qdma->ctrl_base;
+
+ id = irq - fsl_qdma->irq_base;
+ if (id < 0 && id > fsl_qdma->block_number) {
+ dev_err(fsl_qdma->dma_dev.dev,
+ "irq %d is wrong irq_base is %d\n",
+ irq, fsl_qdma->irq_base);
+ }
+
+ block = fsl_qdma->block_base +
+ FSL_QDMA_BLOCK_BASE_OFFSET(fsl_qdma, id);
+
+ intr = qdma_readl(fsl_qdma, block + FSL_QDMA_BCQIDR(0));
+
+ if ((intr & FSL_QDMA_CQIDR_SQT) != 0)
+ intr = fsl_qdma_queue_transfer_complete(fsl_qdma, block, id);
+
+ if (intr != 0) {
+ reg = qdma_readl(fsl_qdma, ctrl + FSL_QDMA_DMR);
+ reg |= FSL_QDMA_DMR_DQD;
+ qdma_writel(fsl_qdma, reg, ctrl + FSL_QDMA_DMR);
+ qdma_writel(fsl_qdma, 0, block + FSL_QDMA_BCQIER(0));
+ dev_err(fsl_qdma->dma_dev.dev, "QDMA: status err!\n");
+ }
+
+ /* Clear all detected events and interrupts. */
+ qdma_writel(fsl_qdma, FSL_QDMA_BCQIDR_CLEAR,
+ block + FSL_QDMA_BCQIDR(0));
+
+ return IRQ_HANDLED;
+}
+
+static int
+fsl_qdma_irq_init(struct platform_device *pdev,
+ struct fsl_qdma_engine *fsl_qdma)
+{
+ int i;
+ int cpu;
+ int ret;
+ char irq_name[20];
+
+ fsl_qdma->error_irq =
+ platform_get_irq_byname(pdev, "qdma-error");
+ if (fsl_qdma->error_irq < 0) {
+ dev_err(&pdev->dev, "Can't get qdma controller irq.\n");
+ return fsl_qdma->error_irq;
+ }
+
+ ret = devm_request_irq(&pdev->dev, fsl_qdma->error_irq,
+ fsl_qdma_error_handler, 0,
+ "qDMA error", fsl_qdma);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't register qDMA controller IRQ.\n");
+ return ret;
+ }
+
+ for (i = 0; i < fsl_qdma->block_number; i++) {
+ sprintf(irq_name, "qdma-queue%d", i);
+ fsl_qdma->queue_irq[i] =
+ platform_get_irq_byname(pdev, irq_name);
+
+ if (fsl_qdma->queue_irq[i] < 0) {
+ dev_err(&pdev->dev,
+ "Can't get qdma queue %d irq.\n", i);
+ return fsl_qdma->queue_irq[i];
+ }
+
+ ret = devm_request_irq(&pdev->dev,
+ fsl_qdma->queue_irq[i],
+ fsl_qdma_queue_handler,
+ 0,
+ "qDMA queue",
+ fsl_qdma);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Can't register qDMA queue IRQ.\n");
+ return ret;
+ }
+
+ cpu = i % num_online_cpus();
+ ret = irq_set_affinity_hint(fsl_qdma->queue_irq[i],
+ get_cpu_mask(cpu));
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Can't set cpu %d affinity to IRQ %d.\n",
+ cpu,
+ fsl_qdma->queue_irq[i]);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void fsl_qdma_irq_exit(struct platform_device *pdev,
+ struct fsl_qdma_engine *fsl_qdma)
+{
+ int i;
+
+ devm_free_irq(&pdev->dev, fsl_qdma->error_irq, fsl_qdma);
+ for (i = 0; i < fsl_qdma->block_number; i++)
+ devm_free_irq(&pdev->dev, fsl_qdma->queue_irq[i], fsl_qdma);
+}
+
+static int fsl_qdma_reg_init(struct fsl_qdma_engine *fsl_qdma)
+{
+ u32 reg;
+ int i, j, ret;
+ struct fsl_qdma_queue *temp;
+ void __iomem *status = fsl_qdma->status_base;
+ void __iomem *block, *ctrl = fsl_qdma->ctrl_base;
+ struct fsl_qdma_queue *fsl_queue = fsl_qdma->queue;
+
+ /* Try to halt the qDMA engine first. */
+ ret = fsl_qdma_halt(fsl_qdma);
+ if (ret) {
+ dev_err(fsl_qdma->dma_dev.dev, "DMA halt failed!");
+ return ret;
+ }
+
+ for (i = 0; i < fsl_qdma->block_number; i++) {
+ /*
+ * Clear the command queue interrupt detect register for
+ * all queues.
+ */
+
+ block = fsl_qdma->block_base +
+ FSL_QDMA_BLOCK_BASE_OFFSET(fsl_qdma, i);
+ qdma_writel(fsl_qdma, FSL_QDMA_BCQIDR_CLEAR,
+ block + FSL_QDMA_BCQIDR(0));
+ }
+
+ for (j = 0; j < fsl_qdma->block_number; j++) {
+ block = fsl_qdma->block_base +
+ FSL_QDMA_BLOCK_BASE_OFFSET(fsl_qdma, j);
+ for (i = 0; i < fsl_qdma->n_queues; i++) {
+ temp = fsl_queue + i + (j * fsl_qdma->n_queues);
+ /*
+ * Initialize Command Queue registers to
+ * point to the first
+ * command descriptor in memory.
+ * Dequeue Pointer Address Registers
+ * Enqueue Pointer Address Registers
+ */
+
+ qdma_writel(fsl_qdma, temp->bus_addr,
+ block + FSL_QDMA_BCQDPA_SADDR(i));
+ qdma_writel(fsl_qdma, temp->bus_addr,
+ block + FSL_QDMA_BCQEPA_SADDR(i));
+
+ /* Initialize the queue mode. */
+ reg = FSL_QDMA_BCQMR_EN;
+ reg |= FSL_QDMA_BCQMR_CD_THLD(ilog2(temp->n_cq) - 4);
+ reg |= FSL_QDMA_BCQMR_CQ_SIZE(ilog2(temp->n_cq) - 6);
+ qdma_writel(fsl_qdma, reg, block + FSL_QDMA_BCQMR(i));
+ }
+
+ /*
+ * Workaround for erratum: ERR010812.
+ * We must enable XOFF to avoid the enqueue rejection occurs.
+ * Setting SQCCMR ENTER_WM to 0x20.
+ */
+
+ qdma_writel(fsl_qdma, FSL_QDMA_SQCCMR_ENTER_WM,
+ block + FSL_QDMA_SQCCMR);
+
+ /*
+ * Initialize status queue registers to point to the first
+ * command descriptor in memory.
+ * Dequeue Pointer Address Registers
+ * Enqueue Pointer Address Registers
+ */
+
+ qdma_writel(fsl_qdma, fsl_qdma->status[j]->bus_addr,
+ block + FSL_QDMA_SQEPAR);
+ qdma_writel(fsl_qdma, fsl_qdma->status[j]->bus_addr,
+ block + FSL_QDMA_SQDPAR);
+ /* Initialize status queue interrupt. */
+ qdma_writel(fsl_qdma, FSL_QDMA_BCQIER_CQTIE,
+ block + FSL_QDMA_BCQIER(0));
+ qdma_writel(fsl_qdma, FSL_QDMA_BSQICR_ICEN |
+ FSL_QDMA_BSQICR_ICST(5) | 0x8000,
+ block + FSL_QDMA_BSQICR);
+ qdma_writel(fsl_qdma, FSL_QDMA_CQIER_MEIE |
+ FSL_QDMA_CQIER_TEIE,
+ block + FSL_QDMA_CQIER);
+
+ /* Initialize the status queue mode. */
+ reg = FSL_QDMA_BSQMR_EN;
+ reg |= FSL_QDMA_BSQMR_CQ_SIZE(ilog2
+ (fsl_qdma->status[j]->n_cq) - 6);
+
+ qdma_writel(fsl_qdma, reg, block + FSL_QDMA_BSQMR);
+ reg = qdma_readl(fsl_qdma, block + FSL_QDMA_BSQMR);
+ }
+
+ /* Initialize controller interrupt register. */
+ qdma_writel(fsl_qdma, FSL_QDMA_DEDR_CLEAR, status + FSL_QDMA_DEDR);
+ qdma_writel(fsl_qdma, FSL_QDMA_DEIER_CLEAR, status + FSL_QDMA_DEIER);
+
+ reg = qdma_readl(fsl_qdma, ctrl + FSL_QDMA_DMR);
+ reg &= ~FSL_QDMA_DMR_DQD;
+ qdma_writel(fsl_qdma, reg, ctrl + FSL_QDMA_DMR);
+
+ return 0;
+}
+
+static struct dma_async_tx_descriptor *
+fsl_qdma_prep_memcpy(struct dma_chan *chan, dma_addr_t dst,
+ dma_addr_t src, size_t len, unsigned long flags)
+{
+ struct fsl_qdma_comp *fsl_comp;
+ struct fsl_qdma_chan *fsl_chan = to_fsl_qdma_chan(chan);
+
+ fsl_comp = fsl_qdma_request_enqueue_desc(fsl_chan);
+
+ if (!fsl_comp)
+ return NULL;
+
+ fsl_qdma_comp_fill_memcpy(fsl_comp, dst, src, len);
+
+ return vchan_tx_prep(&fsl_chan->vchan, &fsl_comp->vdesc, flags);
+}
+
+static void fsl_qdma_enqueue_desc(struct fsl_qdma_chan *fsl_chan)
+{
+ u32 reg;
+ struct virt_dma_desc *vdesc;
+ struct fsl_qdma_comp *fsl_comp;
+ struct fsl_qdma_queue *fsl_queue = fsl_chan->queue;
+ void __iomem *block = fsl_queue->block_base;
+
+ reg = qdma_readl(fsl_chan->qdma, block + FSL_QDMA_BCQSR(fsl_queue->id));
+ if (reg & (FSL_QDMA_BCQSR_QF | FSL_QDMA_BCQSR_XOFF))
+ return;
+ vdesc = vchan_next_desc(&fsl_chan->vchan);
+ if (!vdesc)
+ return;
+ list_del(&vdesc->node);
+ fsl_comp = to_fsl_qdma_comp(vdesc);
+
+ memcpy(fsl_queue->virt_head++,
+ fsl_comp->virt_addr, sizeof(struct fsl_qdma_format));
+ if (fsl_queue->virt_head == fsl_queue->cq + fsl_queue->n_cq)
+ fsl_queue->virt_head = fsl_queue->cq;
+
+ list_add_tail(&fsl_comp->list, &fsl_queue->comp_used);
+ barrier();
+ reg = qdma_readl(fsl_chan->qdma, block + FSL_QDMA_BCQMR(fsl_queue->id));
+ reg |= FSL_QDMA_BCQMR_EI;
+ qdma_writel(fsl_chan->qdma, reg, block + FSL_QDMA_BCQMR(fsl_queue->id));
+ fsl_chan->status = DMA_IN_PROGRESS;
+}
+
+static void fsl_qdma_free_desc(struct virt_dma_desc *vdesc)
+{
+ unsigned long flags;
+ struct fsl_qdma_comp *fsl_comp;
+ struct fsl_qdma_queue *fsl_queue;
+
+ fsl_comp = to_fsl_qdma_comp(vdesc);
+ fsl_queue = fsl_comp->qchan->queue;
+
+ spin_lock_irqsave(&fsl_queue->queue_lock, flags);
+ list_add_tail(&fsl_comp->list, &fsl_queue->comp_free);
+ spin_unlock_irqrestore(&fsl_queue->queue_lock, flags);
+}
+
+static void fsl_qdma_issue_pending(struct dma_chan *chan)
+{
+ unsigned long flags;
+ struct fsl_qdma_chan *fsl_chan = to_fsl_qdma_chan(chan);
+ struct fsl_qdma_queue *fsl_queue = fsl_chan->queue;
+
+ spin_lock_irqsave(&fsl_queue->queue_lock, flags);
+ spin_lock(&fsl_chan->vchan.lock);
+ if (vchan_issue_pending(&fsl_chan->vchan))
+ fsl_qdma_enqueue_desc(fsl_chan);
+ spin_unlock(&fsl_chan->vchan.lock);
+ spin_unlock_irqrestore(&fsl_queue->queue_lock, flags);
+}
+
+static void fsl_qdma_synchronize(struct dma_chan *chan)
+{
+ struct fsl_qdma_chan *fsl_chan = to_fsl_qdma_chan(chan);
+
+ vchan_synchronize(&fsl_chan->vchan);
+}
+
+static int fsl_qdma_terminate_all(struct dma_chan *chan)
+{
+ LIST_HEAD(head);
+ unsigned long flags;
+ struct fsl_qdma_chan *fsl_chan = to_fsl_qdma_chan(chan);
+
+ spin_lock_irqsave(&fsl_chan->vchan.lock, flags);
+ vchan_get_all_descriptors(&fsl_chan->vchan, &head);
+ spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags);
+ vchan_dma_desc_free_list(&fsl_chan->vchan, &head);
+ return 0;
+}
+
+static int fsl_qdma_alloc_chan_resources(struct dma_chan *chan)
+{
+ int ret;
+ struct fsl_qdma_chan *fsl_chan = to_fsl_qdma_chan(chan);
+ struct fsl_qdma_engine *fsl_qdma = fsl_chan->qdma;
+ struct fsl_qdma_queue *fsl_queue = fsl_chan->queue;
+
+ if (fsl_queue->comp_pool && fsl_queue->desc_pool)
+ return fsl_qdma->desc_allocated;
+
+ INIT_LIST_HEAD(&fsl_queue->comp_free);
+
+ /*
+ * The dma pool for queue command buffer
+ */
+ fsl_queue->comp_pool =
+ dma_pool_create("comp_pool",
+ chan->device->dev,
+ FSL_QDMA_COMMAND_BUFFER_SIZE,
+ 64, 0);
+ if (!fsl_queue->comp_pool)
+ return -ENOMEM;
+
+ /*
+ * The dma pool for Descriptor(SD/DD) buffer
+ */
+ fsl_queue->desc_pool =
+ dma_pool_create("desc_pool",
+ chan->device->dev,
+ FSL_QDMA_DESCRIPTOR_BUFFER_SIZE,
+ 32, 0);
+ if (!fsl_queue->desc_pool)
+ goto err_desc_pool;
+
+ ret = fsl_qdma_pre_request_enqueue_desc(fsl_queue);
+ if (ret) {
+ dev_err(chan->device->dev,
+ "failed to alloc dma buffer for S/G descriptor\n");
+ goto err_mem;
+ }
+
+ fsl_qdma->desc_allocated++;
+ return fsl_qdma->desc_allocated;
+
+err_mem:
+ dma_pool_destroy(fsl_queue->desc_pool);
+err_desc_pool:
+ dma_pool_destroy(fsl_queue->comp_pool);
+ return -ENOMEM;
+}
+
+static int fsl_qdma_probe(struct platform_device *pdev)
+{
+ int ret, i;
+ int blk_num, blk_off;
+ u32 len, chans, queues;
+ struct resource *res;
+ struct fsl_qdma_chan *fsl_chan;
+ struct fsl_qdma_engine *fsl_qdma;
+ struct device_node *np = pdev->dev.of_node;
+
+ ret = of_property_read_u32(np, "dma-channels", &chans);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't get dma-channels.\n");
+ return ret;
+ }
+
+ ret = of_property_read_u32(np, "block-offset", &blk_off);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't get block-offset.\n");
+ return ret;
+ }
+
+ ret = of_property_read_u32(np, "block-number", &blk_num);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't get block-number.\n");
+ return ret;
+ }
+
+ blk_num = min_t(int, blk_num, num_online_cpus());
+
+ len = sizeof(*fsl_qdma);
+ fsl_qdma = devm_kzalloc(&pdev->dev, len, GFP_KERNEL);
+ if (!fsl_qdma)
+ return -ENOMEM;
+
+ len = sizeof(*fsl_chan) * chans;
+ fsl_qdma->chans = devm_kzalloc(&pdev->dev, len, GFP_KERNEL);
+ if (!fsl_qdma->chans)
+ return -ENOMEM;
+
+ len = sizeof(struct fsl_qdma_queue *) * blk_num;
+ fsl_qdma->status = devm_kzalloc(&pdev->dev, len, GFP_KERNEL);
+ if (!fsl_qdma->status)
+ return -ENOMEM;
+
+ len = sizeof(int) * blk_num;
+ fsl_qdma->queue_irq = devm_kzalloc(&pdev->dev, len, GFP_KERNEL);
+ if (!fsl_qdma->queue_irq)
+ return -ENOMEM;
+
+ ret = of_property_read_u32(np, "fsl,dma-queues", &queues);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't get queues.\n");
+ return ret;
+ }
+
+ fsl_qdma->desc_allocated = 0;
+ fsl_qdma->n_chans = chans;
+ fsl_qdma->n_queues = queues;
+ fsl_qdma->block_number = blk_num;
+ fsl_qdma->block_offset = blk_off;
+
+ mutex_init(&fsl_qdma->fsl_qdma_mutex);
+
+ for (i = 0; i < fsl_qdma->block_number; i++) {
+ fsl_qdma->status[i] = fsl_qdma_prep_status_queue(pdev);
+ if (!fsl_qdma->status[i])
+ return -ENOMEM;
+ }
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ fsl_qdma->ctrl_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(fsl_qdma->ctrl_base))
+ return PTR_ERR(fsl_qdma->ctrl_base);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ fsl_qdma->status_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(fsl_qdma->status_base))
+ return PTR_ERR(fsl_qdma->status_base);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+ fsl_qdma->block_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(fsl_qdma->block_base))
+ return PTR_ERR(fsl_qdma->block_base);
+ fsl_qdma->queue = fsl_qdma_alloc_queue_resources(pdev, fsl_qdma);
+ if (!fsl_qdma->queue)
+ return -ENOMEM;
+
+ ret = fsl_qdma_irq_init(pdev, fsl_qdma);
+ if (ret)
+ return ret;
+
+ fsl_qdma->irq_base = platform_get_irq_byname(pdev, "qdma-queue0");
+ fsl_qdma->feature = of_property_read_bool(np, "big-endian");
+ INIT_LIST_HEAD(&fsl_qdma->dma_dev.channels);
+
+ for (i = 0; i < fsl_qdma->n_chans; i++) {
+ struct fsl_qdma_chan *fsl_chan = &fsl_qdma->chans[i];
+
+ fsl_chan->qdma = fsl_qdma;
+ fsl_chan->queue = fsl_qdma->queue + i % (fsl_qdma->n_queues *
+ fsl_qdma->block_number);
+ fsl_chan->vchan.desc_free = fsl_qdma_free_desc;
+ vchan_init(&fsl_chan->vchan, &fsl_qdma->dma_dev);
+ }
+
+ dma_cap_set(DMA_MEMCPY, fsl_qdma->dma_dev.cap_mask);
+
+ fsl_qdma->dma_dev.dev = &pdev->dev;
+ fsl_qdma->dma_dev.device_free_chan_resources =
+ fsl_qdma_free_chan_resources;
+ fsl_qdma->dma_dev.device_alloc_chan_resources =
+ fsl_qdma_alloc_chan_resources;
+ fsl_qdma->dma_dev.device_tx_status = dma_cookie_status;
+ fsl_qdma->dma_dev.device_prep_dma_memcpy = fsl_qdma_prep_memcpy;
+ fsl_qdma->dma_dev.device_issue_pending = fsl_qdma_issue_pending;
+ fsl_qdma->dma_dev.device_synchronize = fsl_qdma_synchronize;
+ fsl_qdma->dma_dev.device_terminate_all = fsl_qdma_terminate_all;
+
+ dma_set_mask(&pdev->dev, DMA_BIT_MASK(40));
+
+ platform_set_drvdata(pdev, fsl_qdma);
+
+ ret = dma_async_device_register(&fsl_qdma->dma_dev);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Can't register NXP Layerscape qDMA engine.\n");
+ return ret;
+ }
+
+ ret = fsl_qdma_reg_init(fsl_qdma);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't Initialize the qDMA engine.\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static void fsl_qdma_cleanup_vchan(struct dma_device *dmadev)
+{
+ struct fsl_qdma_chan *chan, *_chan;
+
+ list_for_each_entry_safe(chan, _chan,
+ &dmadev->channels, vchan.chan.device_node) {
+ list_del(&chan->vchan.chan.device_node);
+ tasklet_kill(&chan->vchan.task);
+ }
+}
+
+static int fsl_qdma_remove(struct platform_device *pdev)
+{
+ int i;
+ struct fsl_qdma_queue *status;
+ struct device_node *np = pdev->dev.of_node;
+ struct fsl_qdma_engine *fsl_qdma = platform_get_drvdata(pdev);
+
+ fsl_qdma_irq_exit(pdev, fsl_qdma);
+ fsl_qdma_cleanup_vchan(&fsl_qdma->dma_dev);
+ of_dma_controller_free(np);
+ dma_async_device_unregister(&fsl_qdma->dma_dev);
+
+ for (i = 0; i < fsl_qdma->block_number; i++) {
+ status = fsl_qdma->status[i];
+ dma_free_coherent(&pdev->dev, sizeof(struct fsl_qdma_format) *
+ status->n_cq, status->cq, status->bus_addr);
+ }
+ return 0;
+}
+
+static const struct of_device_id fsl_qdma_dt_ids[] = {
+ { .compatible = "fsl,ls1021a-qdma", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, fsl_qdma_dt_ids);
+
+static struct platform_driver fsl_qdma_driver = {
+ .driver = {
+ .name = "fsl-qdma",
+ .of_match_table = fsl_qdma_dt_ids,
+ },
+ .probe = fsl_qdma_probe,
+ .remove = fsl_qdma_remove,
+};
+
+module_platform_driver(fsl_qdma_driver);
+
+MODULE_ALIAS("platform:fsl-qdma");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("NXP Layerscape qDMA engine driver");
diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c
index 9d360a3fbae3..1e38e6b94006 100644
--- a/drivers/dma/fsldma.c
+++ b/drivers/dma/fsldma.c
@@ -53,42 +53,42 @@ static const char msg_ld_oom[] = "No free memory for link descriptor";
static void set_sr(struct fsldma_chan *chan, u32 val)
{
- DMA_OUT(chan, &chan->regs->sr, val, 32);
+ FSL_DMA_OUT(chan, &chan->regs->sr, val, 32);
}
static u32 get_sr(struct fsldma_chan *chan)
{
- return DMA_IN(chan, &chan->regs->sr, 32);
+ return FSL_DMA_IN(chan, &chan->regs->sr, 32);
}
static void set_mr(struct fsldma_chan *chan, u32 val)
{
- DMA_OUT(chan, &chan->regs->mr, val, 32);
+ FSL_DMA_OUT(chan, &chan->regs->mr, val, 32);
}
static u32 get_mr(struct fsldma_chan *chan)
{
- return DMA_IN(chan, &chan->regs->mr, 32);
+ return FSL_DMA_IN(chan, &chan->regs->mr, 32);
}
static void set_cdar(struct fsldma_chan *chan, dma_addr_t addr)
{
- DMA_OUT(chan, &chan->regs->cdar, addr | FSL_DMA_SNEN, 64);
+ FSL_DMA_OUT(chan, &chan->regs->cdar, addr | FSL_DMA_SNEN, 64);
}
static dma_addr_t get_cdar(struct fsldma_chan *chan)
{
- return DMA_IN(chan, &chan->regs->cdar, 64) & ~FSL_DMA_SNEN;
+ return FSL_DMA_IN(chan, &chan->regs->cdar, 64) & ~FSL_DMA_SNEN;
}
static void set_bcr(struct fsldma_chan *chan, u32 val)
{
- DMA_OUT(chan, &chan->regs->bcr, val, 32);
+ FSL_DMA_OUT(chan, &chan->regs->bcr, val, 32);
}
static u32 get_bcr(struct fsldma_chan *chan)
{
- return DMA_IN(chan, &chan->regs->bcr, 32);
+ return FSL_DMA_IN(chan, &chan->regs->bcr, 32);
}
/*
diff --git a/drivers/dma/fsldma.h b/drivers/dma/fsldma.h
index 4787d485dd76..a9b12f82b5c3 100644
--- a/drivers/dma/fsldma.h
+++ b/drivers/dma/fsldma.h
@@ -196,39 +196,67 @@ struct fsldma_chan {
#define to_fsl_desc(lh) container_of(lh, struct fsl_desc_sw, node)
#define tx_to_fsl_desc(tx) container_of(tx, struct fsl_desc_sw, async_tx)
-#ifndef __powerpc64__
-static u64 in_be64(const u64 __iomem *addr)
+#ifdef CONFIG_PPC
+#define fsl_ioread32(p) in_le32(p)
+#define fsl_ioread32be(p) in_be32(p)
+#define fsl_iowrite32(v, p) out_le32(p, v)
+#define fsl_iowrite32be(v, p) out_be32(p, v)
+
+#ifdef __powerpc64__
+#define fsl_ioread64(p) in_le64(p)
+#define fsl_ioread64be(p) in_be64(p)
+#define fsl_iowrite64(v, p) out_le64(p, v)
+#define fsl_iowrite64be(v, p) out_be64(p, v)
+#else
+static u64 fsl_ioread64(const u64 __iomem *addr)
{
- return ((u64)in_be32((u32 __iomem *)addr) << 32) |
- (in_be32((u32 __iomem *)addr + 1));
+ u32 fsl_addr = lower_32_bits(addr);
+ u64 fsl_addr_hi = (u64)in_le32((u32 *)(fsl_addr + 1)) << 32;
+
+ return fsl_addr_hi | in_le32((u32 *)fsl_addr);
}
-static void out_be64(u64 __iomem *addr, u64 val)
+static void fsl_iowrite64(u64 val, u64 __iomem *addr)
{
- out_be32((u32 __iomem *)addr, val >> 32);
- out_be32((u32 __iomem *)addr + 1, (u32)val);
+ out_le32((u32 __iomem *)addr + 1, val >> 32);
+ out_le32((u32 __iomem *)addr, (u32)val);
}
-/* There is no asm instructions for 64 bits reverse loads and stores */
-static u64 in_le64(const u64 __iomem *addr)
+static u64 fsl_ioread64be(const u64 __iomem *addr)
{
- return ((u64)in_le32((u32 __iomem *)addr + 1) << 32) |
- (in_le32((u32 __iomem *)addr));
+ u32 fsl_addr = lower_32_bits(addr);
+ u64 fsl_addr_hi = (u64)in_be32((u32 *)fsl_addr) << 32;
+
+ return fsl_addr_hi | in_be32((u32 *)(fsl_addr + 1));
}
-static void out_le64(u64 __iomem *addr, u64 val)
+static void fsl_iowrite64be(u64 val, u64 __iomem *addr)
{
- out_le32((u32 __iomem *)addr + 1, val >> 32);
- out_le32((u32 __iomem *)addr, (u32)val);
+ out_be32((u32 __iomem *)addr, val >> 32);
+ out_be32((u32 __iomem *)addr + 1, (u32)val);
}
#endif
+#endif
-#define DMA_IN(fsl_chan, addr, width) \
- (((fsl_chan)->feature & FSL_DMA_BIG_ENDIAN) ? \
- in_be##width(addr) : in_le##width(addr))
-#define DMA_OUT(fsl_chan, addr, val, width) \
- (((fsl_chan)->feature & FSL_DMA_BIG_ENDIAN) ? \
- out_be##width(addr, val) : out_le##width(addr, val))
+#if defined(CONFIG_ARM64) || defined(CONFIG_ARM)
+#define fsl_ioread32(p) ioread32(p)
+#define fsl_ioread32be(p) ioread32be(p)
+#define fsl_iowrite32(v, p) iowrite32(v, p)
+#define fsl_iowrite32be(v, p) iowrite32be(v, p)
+#define fsl_ioread64(p) ioread64(p)
+#define fsl_ioread64be(p) ioread64be(p)
+#define fsl_iowrite64(v, p) iowrite64(v, p)
+#define fsl_iowrite64be(v, p) iowrite64be(v, p)
+#endif
+
+#define FSL_DMA_IN(fsl_dma, addr, width) \
+ (((fsl_dma)->feature & FSL_DMA_BIG_ENDIAN) ? \
+ fsl_ioread##width##be(addr) : fsl_ioread##width(addr))
+
+#define FSL_DMA_OUT(fsl_dma, addr, val, width) \
+ (((fsl_dma)->feature & FSL_DMA_BIG_ENDIAN) ? \
+ fsl_iowrite##width##be(val, addr) : fsl_iowrite \
+ ##width(val, addr))
#define DMA_TO_CPU(fsl_chan, d, width) \
(((fsl_chan)->feature & FSL_DMA_BIG_ENDIAN) ? \
diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c
index 4a09af3cd546..00a089e24150 100644
--- a/drivers/dma/imx-dma.c
+++ b/drivers/dma/imx-dma.c
@@ -278,14 +278,14 @@ static int imxdma_hw_chain(struct imxdma_channel *imxdmac)
/*
* imxdma_sg_next - prepare next chunk for scatter-gather DMA emulation
*/
-static inline int imxdma_sg_next(struct imxdma_desc *d)
+static inline void imxdma_sg_next(struct imxdma_desc *d)
{
struct imxdma_channel *imxdmac = to_imxdma_chan(d->desc.chan);
struct imxdma_engine *imxdma = imxdmac->imxdma;
struct scatterlist *sg = d->sg;
- unsigned long now;
+ size_t now;
- now = min(d->len, sg_dma_len(sg));
+ now = min_t(size_t, d->len, sg_dma_len(sg));
if (d->len != IMX_DMA_LENGTH_LOOP)
d->len -= now;
@@ -303,8 +303,6 @@ static inline int imxdma_sg_next(struct imxdma_desc *d)
imx_dmav1_readl(imxdma, DMA_DAR(imxdmac->channel)),
imx_dmav1_readl(imxdma, DMA_SAR(imxdmac->channel)),
imx_dmav1_readl(imxdma, DMA_CNTR(imxdmac->channel)));
-
- return now;
}
static void imxdma_enable_hw(struct imxdma_desc *d)
diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c
index 86708fb9bda1..5f3c1378b90e 100644
--- a/drivers/dma/imx-sdma.c
+++ b/drivers/dma/imx-sdma.c
@@ -377,6 +377,7 @@ struct sdma_channel {
unsigned long watermark_level;
u32 shp_addr, per_addr;
enum dma_status status;
+ bool context_loaded;
struct imx_dma_data data;
struct work_struct terminate_worker;
};
@@ -440,6 +441,8 @@ struct sdma_engine {
unsigned int irq;
dma_addr_t bd0_phys;
struct sdma_buffer_descriptor *bd0;
+ /* clock ratio for AHB:SDMA core. 1:1 is 1, 2:1 is 0*/
+ bool clk_ratio;
};
static int sdma_config_write(struct dma_chan *chan,
@@ -662,8 +665,11 @@ static int sdma_run_channel0(struct sdma_engine *sdma)
dev_err(sdma->dev, "Timeout waiting for CH0 ready\n");
/* Set bits of CONFIG register with dynamic context switching */
- if (readl(sdma->regs + SDMA_H_CONFIG) == 0)
- writel_relaxed(SDMA_H_CONFIG_CSM, sdma->regs + SDMA_H_CONFIG);
+ reg = readl(sdma->regs + SDMA_H_CONFIG);
+ if ((reg & SDMA_H_CONFIG_CSM) == 0) {
+ reg |= SDMA_H_CONFIG_CSM;
+ writel_relaxed(reg, sdma->regs + SDMA_H_CONFIG);
+ }
return ret;
}
@@ -677,7 +683,7 @@ static int sdma_load_script(struct sdma_engine *sdma, void *buf, int size,
int ret;
unsigned long flags;
- buf_virt = dma_alloc_coherent(NULL, size, &buf_phys, GFP_KERNEL);
+ buf_virt = dma_alloc_coherent(sdma->dev, size, &buf_phys, GFP_KERNEL);
if (!buf_virt) {
return -ENOMEM;
}
@@ -696,7 +702,7 @@ static int sdma_load_script(struct sdma_engine *sdma, void *buf, int size,
spin_unlock_irqrestore(&sdma->channel_0_lock, flags);
- dma_free_coherent(NULL, size, buf_virt, buf_phys);
+ dma_free_coherent(sdma->dev, size, buf_virt, buf_phys);
return ret;
}
@@ -970,6 +976,9 @@ static int sdma_load_context(struct sdma_channel *sdmac)
int ret;
unsigned long flags;
+ if (sdmac->context_loaded)
+ return 0;
+
if (sdmac->direction == DMA_DEV_TO_MEM)
load_address = sdmac->pc_from_device;
else if (sdmac->direction == DMA_DEV_TO_DEV)
@@ -1012,6 +1021,8 @@ static int sdma_load_context(struct sdma_channel *sdmac)
spin_unlock_irqrestore(&sdma->channel_0_lock, flags);
+ sdmac->context_loaded = true;
+
return ret;
}
@@ -1051,6 +1062,7 @@ static void sdma_channel_terminate_work(struct work_struct *work)
sdmac->desc = NULL;
spin_unlock_irqrestore(&sdmac->vc.lock, flags);
vchan_dma_desc_free_list(&sdmac->vc, &head);
+ sdmac->context_loaded = false;
}
static int sdma_disable_channel_async(struct dma_chan *chan)
@@ -1182,8 +1194,8 @@ static int sdma_request_channel0(struct sdma_engine *sdma)
{
int ret = -EBUSY;
- sdma->bd0 = dma_alloc_coherent(NULL, PAGE_SIZE, &sdma->bd0_phys,
- GFP_NOWAIT);
+ sdma->bd0 = dma_alloc_coherent(sdma->dev, PAGE_SIZE, &sdma->bd0_phys,
+ GFP_NOWAIT);
if (!sdma->bd0) {
ret = -ENOMEM;
goto out;
@@ -1205,8 +1217,8 @@ static int sdma_alloc_bd(struct sdma_desc *desc)
u32 bd_size = desc->num_bd * sizeof(struct sdma_buffer_descriptor);
int ret = 0;
- desc->bd = dma_alloc_coherent(NULL, bd_size, &desc->bd_phys,
- GFP_NOWAIT);
+ desc->bd = dma_alloc_coherent(desc->sdmac->sdma->dev, bd_size,
+ &desc->bd_phys, GFP_NOWAIT);
if (!desc->bd) {
ret = -ENOMEM;
goto out;
@@ -1219,7 +1231,8 @@ static void sdma_free_bd(struct sdma_desc *desc)
{
u32 bd_size = desc->num_bd * sizeof(struct sdma_buffer_descriptor);
- dma_free_coherent(NULL, bd_size, desc->bd, desc->bd_phys);
+ dma_free_coherent(desc->sdmac->sdma->dev, bd_size, desc->bd,
+ desc->bd_phys);
}
static void sdma_desc_free(struct virt_dma_desc *vd)
@@ -1839,10 +1852,13 @@ static int sdma_init(struct sdma_engine *sdma)
if (ret)
goto disable_clk_ipg;
+ if (clk_get_rate(sdma->clk_ahb) == clk_get_rate(sdma->clk_ipg))
+ sdma->clk_ratio = 1;
+
/* Be sure SDMA has not started yet */
writel_relaxed(0, sdma->regs + SDMA_H_C0PTR);
- sdma->channel_control = dma_alloc_coherent(NULL,
+ sdma->channel_control = dma_alloc_coherent(sdma->dev,
MAX_DMA_CHANNELS * sizeof (struct sdma_channel_control) +
sizeof(struct sdma_context_data),
&ccb_phys, GFP_KERNEL);
@@ -1879,8 +1895,10 @@ static int sdma_init(struct sdma_engine *sdma)
writel_relaxed(0x4050, sdma->regs + SDMA_CHN0ADDR);
/* Set bits of CONFIG register but with static context switching */
- /* FIXME: Check whether to set ACR bit depending on clock ratios */
- writel_relaxed(0, sdma->regs + SDMA_H_CONFIG);
+ if (sdma->clk_ratio)
+ writel_relaxed(SDMA_H_CONFIG_ACR, sdma->regs + SDMA_H_CONFIG);
+ else
+ writel_relaxed(0, sdma->regs + SDMA_H_CONFIG);
writel_relaxed(ccb_phys, sdma->regs + SDMA_H_C0PTR);
@@ -1903,11 +1921,16 @@ disable_clk_ipg:
static bool sdma_filter_fn(struct dma_chan *chan, void *fn_param)
{
struct sdma_channel *sdmac = to_sdma_chan(chan);
+ struct sdma_engine *sdma = sdmac->sdma;
struct imx_dma_data *data = fn_param;
if (!imx_dma_is_general_purpose(chan))
return false;
+ /* return false if it's not the right device */
+ if (sdma->dev->of_node != data->of_node)
+ return false;
+
sdmac->data = *data;
chan->private = &sdmac->data;
@@ -1935,6 +1958,7 @@ static struct dma_chan *sdma_xlate(struct of_phandle_args *dma_spec,
* be set to sdmac->event_id1.
*/
data.dma_request2 = 0;
+ data.of_node = ofdma->of_node;
return dma_request_channel(mask, sdma_filter_fn, &data);
}
@@ -2097,6 +2121,7 @@ static int sdma_probe(struct platform_device *pdev)
sdma->dma_device.device_prep_dma_memcpy = sdma_prep_memcpy;
sdma->dma_device.device_issue_pending = sdma_issue_pending;
sdma->dma_device.dev->dma_parms = &sdma->dma_parms;
+ sdma->dma_device.copy_align = 2;
dma_set_max_seg_size(sdma->dma_device.dev, SDMA_BD_MAX_CNT);
platform_set_drvdata(pdev, sdma);
diff --git a/drivers/dma/ioat/dma.c b/drivers/dma/ioat/dma.c
index 23fb2fa04000..f373a139e0c3 100644
--- a/drivers/dma/ioat/dma.c
+++ b/drivers/dma/ioat/dma.c
@@ -372,6 +372,7 @@ struct ioat_ring_ent **
ioat_alloc_ring(struct dma_chan *c, int order, gfp_t flags)
{
struct ioatdma_chan *ioat_chan = to_ioat_chan(c);
+ struct ioatdma_device *ioat_dma = ioat_chan->ioat_dma;
struct ioat_ring_ent **ring;
int total_descs = 1 << order;
int i, chunks;
@@ -437,6 +438,17 @@ ioat_alloc_ring(struct dma_chan *c, int order, gfp_t flags)
}
ring[i]->hw->next = ring[0]->txd.phys;
+ /* setup descriptor pre-fetching for v3.4 */
+ if (ioat_dma->cap & IOAT_CAP_DPS) {
+ u16 drsctl = IOAT_CHAN_DRSZ_2MB | IOAT_CHAN_DRS_EN;
+
+ if (chunks == 1)
+ drsctl |= IOAT_CHAN_DRS_AUTOWRAP;
+
+ writew(drsctl, ioat_chan->reg_base + IOAT_CHAN_DRSCTL_OFFSET);
+
+ }
+
return ring;
}
diff --git a/drivers/dma/ioat/dma.h b/drivers/dma/ioat/dma.h
index 1ab42ec2b7ff..aaafd0e882b5 100644
--- a/drivers/dma/ioat/dma.h
+++ b/drivers/dma/ioat/dma.h
@@ -27,7 +27,7 @@
#include "registers.h"
#include "hw.h"
-#define IOAT_DMA_VERSION "4.00"
+#define IOAT_DMA_VERSION "5.00"
#define IOAT_DMA_DCA_ANY_CPU ~0
diff --git a/drivers/dma/ioat/hw.h b/drivers/dma/ioat/hw.h
index abcc51b343ce..781c94de8e81 100644
--- a/drivers/dma/ioat/hw.h
+++ b/drivers/dma/ioat/hw.h
@@ -66,11 +66,14 @@
#define PCI_DEVICE_ID_INTEL_IOAT_SKX 0x2021
+#define PCI_DEVICE_ID_INTEL_IOAT_ICX 0x0b00
+
#define IOAT_VER_1_2 0x12 /* Version 1.2 */
#define IOAT_VER_2_0 0x20 /* Version 2.0 */
#define IOAT_VER_3_0 0x30 /* Version 3.0 */
#define IOAT_VER_3_2 0x32 /* Version 3.2 */
#define IOAT_VER_3_3 0x33 /* Version 3.3 */
+#define IOAT_VER_3_4 0x34 /* Version 3.4 */
int system_has_dca_enabled(struct pci_dev *pdev);
diff --git a/drivers/dma/ioat/init.c b/drivers/dma/ioat/init.c
index 2d810dfcdc48..d41dc9a9ff68 100644
--- a/drivers/dma/ioat/init.c
+++ b/drivers/dma/ioat/init.c
@@ -119,6 +119,9 @@ static const struct pci_device_id ioat_pci_tbl[] = {
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_BDXDE2) },
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_BDXDE3) },
+ /* I/OAT v3.4 platforms */
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_ICX) },
+
{ 0, }
};
MODULE_DEVICE_TABLE(pci, ioat_pci_tbl);
@@ -135,10 +138,10 @@ static int ioat3_dma_self_test(struct ioatdma_device *ioat_dma);
static int ioat_dca_enabled = 1;
module_param(ioat_dca_enabled, int, 0644);
MODULE_PARM_DESC(ioat_dca_enabled, "control support of dca service (default: 1)");
-int ioat_pending_level = 4;
+int ioat_pending_level = 7;
module_param(ioat_pending_level, int, 0644);
MODULE_PARM_DESC(ioat_pending_level,
- "high-water mark for pushing ioat descriptors (default: 4)");
+ "high-water mark for pushing ioat descriptors (default: 7)");
static char ioat_interrupt_style[32] = "msix";
module_param_string(ioat_interrupt_style, ioat_interrupt_style,
sizeof(ioat_interrupt_style), 0644);
@@ -635,6 +638,11 @@ static void ioat_free_chan_resources(struct dma_chan *c)
ioat_stop(ioat_chan);
ioat_reset_hw(ioat_chan);
+ /* Put LTR to idle */
+ if (ioat_dma->version >= IOAT_VER_3_4)
+ writeb(IOAT_CHAN_LTR_SWSEL_IDLE,
+ ioat_chan->reg_base + IOAT_CHAN_LTR_SWSEL_OFFSET);
+
spin_lock_bh(&ioat_chan->cleanup_lock);
spin_lock_bh(&ioat_chan->prep_lock);
descs = ioat_ring_space(ioat_chan);
@@ -724,6 +732,28 @@ static int ioat_alloc_chan_resources(struct dma_chan *c)
spin_unlock_bh(&ioat_chan->prep_lock);
spin_unlock_bh(&ioat_chan->cleanup_lock);
+ /* Setting up LTR values for 3.4 or later */
+ if (ioat_chan->ioat_dma->version >= IOAT_VER_3_4) {
+ u32 lat_val;
+
+ lat_val = IOAT_CHAN_LTR_ACTIVE_SNVAL |
+ IOAT_CHAN_LTR_ACTIVE_SNLATSCALE |
+ IOAT_CHAN_LTR_ACTIVE_SNREQMNT;
+ writel(lat_val, ioat_chan->reg_base +
+ IOAT_CHAN_LTR_ACTIVE_OFFSET);
+
+ lat_val = IOAT_CHAN_LTR_IDLE_SNVAL |
+ IOAT_CHAN_LTR_IDLE_SNLATSCALE |
+ IOAT_CHAN_LTR_IDLE_SNREQMNT;
+ writel(lat_val, ioat_chan->reg_base +
+ IOAT_CHAN_LTR_IDLE_OFFSET);
+
+ /* Select to active */
+ writeb(IOAT_CHAN_LTR_SWSEL_ACTIVE,
+ ioat_chan->reg_base +
+ IOAT_CHAN_LTR_SWSEL_OFFSET);
+ }
+
ioat_start_null_desc(ioat_chan);
/* check that we got off the ground */
@@ -1185,6 +1215,10 @@ static int ioat3_dma_probe(struct ioatdma_device *ioat_dma, int dca)
if (err)
return err;
+ if (ioat_dma->cap & IOAT_CAP_DPS)
+ writeb(ioat_pending_level + 1,
+ ioat_dma->reg_base + IOAT_PREFETCH_LIMIT_OFFSET);
+
return 0;
}
@@ -1350,6 +1384,8 @@ static int ioat_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
pci_set_drvdata(pdev, device);
device->version = readb(device->reg_base + IOAT_VER_OFFSET);
+ if (device->version >= IOAT_VER_3_4)
+ ioat_dca_enabled = 0;
if (device->version >= IOAT_VER_3_0) {
if (is_skx_ioat(pdev))
device->version = IOAT_VER_3_2;
diff --git a/drivers/dma/ioat/registers.h b/drivers/dma/ioat/registers.h
index 2f3bbc88ff2a..99c1c24d465d 100644
--- a/drivers/dma/ioat/registers.h
+++ b/drivers/dma/ioat/registers.h
@@ -84,6 +84,9 @@
#define IOAT_CAP_PQ 0x00000200
#define IOAT_CAP_DWBES 0x00002000
#define IOAT_CAP_RAID16SS 0x00020000
+#define IOAT_CAP_DPS 0x00800000
+
+#define IOAT_PREFETCH_LIMIT_OFFSET 0x4C /* CHWPREFLMT */
#define IOAT_CHANNEL_MMIO_SIZE 0x80 /* Each Channel MMIO space is this size */
@@ -243,4 +246,25 @@
#define IOAT_CHANERR_MASK_OFFSET 0x2C /* 32-bit Channel Error Register */
+#define IOAT_CHAN_DRSCTL_OFFSET 0xB6
+#define IOAT_CHAN_DRSZ_4KB 0x0000
+#define IOAT_CHAN_DRSZ_8KB 0x0001
+#define IOAT_CHAN_DRSZ_2MB 0x0009
+#define IOAT_CHAN_DRS_EN 0x0100
+#define IOAT_CHAN_DRS_AUTOWRAP 0x0200
+
+#define IOAT_CHAN_LTR_SWSEL_OFFSET 0xBC
+#define IOAT_CHAN_LTR_SWSEL_ACTIVE 0x0
+#define IOAT_CHAN_LTR_SWSEL_IDLE 0x1
+
+#define IOAT_CHAN_LTR_ACTIVE_OFFSET 0xC0
+#define IOAT_CHAN_LTR_ACTIVE_SNVAL 0x0000 /* 0 us */
+#define IOAT_CHAN_LTR_ACTIVE_SNLATSCALE 0x0800 /* 1us scale */
+#define IOAT_CHAN_LTR_ACTIVE_SNREQMNT 0x8000 /* snoop req enable */
+
+#define IOAT_CHAN_LTR_IDLE_OFFSET 0xC4
+#define IOAT_CHAN_LTR_IDLE_SNVAL 0x0258 /* 600 us */
+#define IOAT_CHAN_LTR_IDLE_SNLATSCALE 0x0800 /* 1us scale */
+#define IOAT_CHAN_LTR_IDLE_SNREQMNT 0x8000 /* snoop req enable */
+
#endif /* _IOAT_REGISTERS_H_ */
diff --git a/drivers/dma/k3dma.c b/drivers/dma/k3dma.c
index fdec2b6cfbb0..5737d92eaeeb 100644
--- a/drivers/dma/k3dma.c
+++ b/drivers/dma/k3dma.c
@@ -52,8 +52,6 @@
#define CX_SRC 0x814
#define CX_DST 0x818
#define CX_CFG 0x81c
-#define AXI_CFG 0x820
-#define AXI_CFG_DEFAULT 0x201201
#define CX_LLI_CHAIN_EN 0x2
#define CX_CFG_EN 0x1
@@ -113,9 +111,18 @@ struct k3_dma_dev {
struct dma_pool *pool;
u32 dma_channels;
u32 dma_requests;
+ u32 dma_channel_mask;
unsigned int irq;
};
+
+#define K3_FLAG_NOCLK BIT(1)
+
+struct k3dma_soc_data {
+ unsigned long flags;
+};
+
+
#define to_k3_dma(dmadev) container_of(dmadev, struct k3_dma_dev, slave)
static int k3_dma_config_write(struct dma_chan *chan,
@@ -161,7 +168,6 @@ static void k3_dma_set_desc(struct k3_dma_phy *phy, struct k3_desc_hw *hw)
writel_relaxed(hw->count, phy->base + CX_CNT0);
writel_relaxed(hw->saddr, phy->base + CX_SRC);
writel_relaxed(hw->daddr, phy->base + CX_DST);
- writel_relaxed(AXI_CFG_DEFAULT, phy->base + AXI_CFG);
writel_relaxed(hw->config, phy->base + CX_CFG);
}
@@ -314,6 +320,9 @@ static void k3_dma_tasklet(unsigned long arg)
/* check new channel request in d->chan_pending */
spin_lock_irq(&d->lock);
for (pch = 0; pch < d->dma_channels; pch++) {
+ if (!(d->dma_channel_mask & (1 << pch)))
+ continue;
+
p = &d->phy[pch];
if (p->vchan == NULL && !list_empty(&d->chan_pending)) {
@@ -331,6 +340,9 @@ static void k3_dma_tasklet(unsigned long arg)
spin_unlock_irq(&d->lock);
for (pch = 0; pch < d->dma_channels; pch++) {
+ if (!(d->dma_channel_mask & (1 << pch)))
+ continue;
+
if (pch_alloc & (1 << pch)) {
p = &d->phy[pch];
c = p->vchan;
@@ -790,8 +802,21 @@ static int k3_dma_transfer_resume(struct dma_chan *chan)
return 0;
}
+static const struct k3dma_soc_data k3_v1_dma_data = {
+ .flags = 0,
+};
+
+static const struct k3dma_soc_data asp_v1_dma_data = {
+ .flags = K3_FLAG_NOCLK,
+};
+
static const struct of_device_id k3_pdma_dt_ids[] = {
- { .compatible = "hisilicon,k3-dma-1.0", },
+ { .compatible = "hisilicon,k3-dma-1.0",
+ .data = &k3_v1_dma_data
+ },
+ { .compatible = "hisilicon,hisi-pcm-asp-dma-1.0",
+ .data = &asp_v1_dma_data
+ },
{}
};
MODULE_DEVICE_TABLE(of, k3_pdma_dt_ids);
@@ -810,6 +835,7 @@ static struct dma_chan *k3_of_dma_simple_xlate(struct of_phandle_args *dma_spec,
static int k3_dma_probe(struct platform_device *op)
{
+ const struct k3dma_soc_data *soc_data;
struct k3_dma_dev *d;
const struct of_device_id *of_id;
struct resource *iores;
@@ -823,6 +849,10 @@ static int k3_dma_probe(struct platform_device *op)
if (!d)
return -ENOMEM;
+ soc_data = device_get_match_data(&op->dev);
+ if (!soc_data)
+ return -EINVAL;
+
d->base = devm_ioremap_resource(&op->dev, iores);
if (IS_ERR(d->base))
return PTR_ERR(d->base);
@@ -833,12 +863,21 @@ static int k3_dma_probe(struct platform_device *op)
"dma-channels", &d->dma_channels);
of_property_read_u32((&op->dev)->of_node,
"dma-requests", &d->dma_requests);
+ ret = of_property_read_u32((&op->dev)->of_node,
+ "dma-channel-mask", &d->dma_channel_mask);
+ if (ret) {
+ dev_warn(&op->dev,
+ "dma-channel-mask doesn't exist, considering all as available.\n");
+ d->dma_channel_mask = (u32)~0UL;
+ }
}
- d->clk = devm_clk_get(&op->dev, NULL);
- if (IS_ERR(d->clk)) {
- dev_err(&op->dev, "no dma clk\n");
- return PTR_ERR(d->clk);
+ if (!(soc_data->flags & K3_FLAG_NOCLK)) {
+ d->clk = devm_clk_get(&op->dev, NULL);
+ if (IS_ERR(d->clk)) {
+ dev_err(&op->dev, "no dma clk\n");
+ return PTR_ERR(d->clk);
+ }
}
irq = platform_get_irq(op, 0);
@@ -862,8 +901,12 @@ static int k3_dma_probe(struct platform_device *op)
return -ENOMEM;
for (i = 0; i < d->dma_channels; i++) {
- struct k3_dma_phy *p = &d->phy[i];
+ struct k3_dma_phy *p;
+
+ if (!(d->dma_channel_mask & BIT(i)))
+ continue;
+ p = &d->phy[i];
p->idx = i;
p->base = d->base + i * 0x40;
}
diff --git a/drivers/dma/mcf-edma.c b/drivers/dma/mcf-edma.c
index 5de1b07eddff..7de54b2fafdb 100644
--- a/drivers/dma/mcf-edma.c
+++ b/drivers/dma/mcf-edma.c
@@ -214,6 +214,7 @@ static int mcf_edma_probe(struct platform_device *pdev)
mcf_chan->edma = mcf_edma;
mcf_chan->slave_id = i;
mcf_chan->idle = true;
+ mcf_chan->dma_dir = DMA_NONE;
mcf_chan->vchan.desc_free = fsl_edma_free_desc;
vchan_init(&mcf_chan->vchan, &mcf_edma->dma_dev);
iowrite32(0x0, &regs->tcd[i].csr);
diff --git a/drivers/dma/mv_xor.c b/drivers/dma/mv_xor.c
index 7f595355fb79..65af2e7fcb2c 100644
--- a/drivers/dma/mv_xor.c
+++ b/drivers/dma/mv_xor.c
@@ -1059,6 +1059,7 @@ mv_xor_channel_add(struct mv_xor_device *xordev,
mv_chan->op_in_desc = XOR_MODE_IN_DESC;
dma_dev = &mv_chan->dmadev;
+ dma_dev->dev = &pdev->dev;
mv_chan->xordev = xordev;
/*
@@ -1091,7 +1092,6 @@ mv_xor_channel_add(struct mv_xor_device *xordev,
dma_dev->device_free_chan_resources = mv_xor_free_chan_resources;
dma_dev->device_tx_status = mv_xor_status;
dma_dev->device_issue_pending = mv_xor_issue_pending;
- dma_dev->dev = &pdev->dev;
/* set prep routines based on capability */
if (dma_has_cap(DMA_INTERRUPT, dma_dev->cap_mask))
@@ -1153,7 +1153,10 @@ mv_xor_channel_add(struct mv_xor_device *xordev,
dma_has_cap(DMA_MEMCPY, dma_dev->cap_mask) ? "cpy " : "",
dma_has_cap(DMA_INTERRUPT, dma_dev->cap_mask) ? "intr " : "");
- dma_async_device_register(dma_dev);
+ ret = dma_async_device_register(dma_dev);
+ if (ret)
+ goto err_free_irq;
+
return mv_chan;
err_free_irq:
diff --git a/drivers/dma/pch_dma.c b/drivers/dma/pch_dma.c
index afd8f27bda96..538b6e0e17bb 100644
--- a/drivers/dma/pch_dma.c
+++ b/drivers/dma/pch_dma.c
@@ -972,7 +972,6 @@ static void pch_dma_remove(struct pci_dev *pdev)
}
/* PCI Device ID of DMA device */
-#define PCI_VENDOR_ID_ROHM 0x10DB
#define PCI_DEVICE_ID_EG20T_PCH_DMA_8CH 0x8810
#define PCI_DEVICE_ID_EG20T_PCH_DMA_4CH 0x8815
#define PCI_DEVICE_ID_ML7213_DMA1_8CH 0x8026
diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
index cff1b143fff5..eec79fdf27a5 100644
--- a/drivers/dma/pl330.c
+++ b/drivers/dma/pl330.c
@@ -2267,7 +2267,6 @@ static int pl330_terminate_all(struct dma_chan *chan)
struct dma_pl330_desc *desc;
unsigned long flags;
struct pl330_dmac *pl330 = pch->dmac;
- LIST_HEAD(list);
bool power_down = false;
pm_runtime_get_sync(pl330->ddma.dev);
diff --git a/drivers/dma/qcom/bam_dma.c b/drivers/dma/qcom/bam_dma.c
index 1617715aa6e0..cb860cb53c27 100644
--- a/drivers/dma/qcom/bam_dma.c
+++ b/drivers/dma/qcom/bam_dma.c
@@ -636,8 +636,8 @@ static struct dma_async_tx_descriptor *bam_prep_slave_sg(struct dma_chan *chan,
num_alloc += DIV_ROUND_UP(sg_dma_len(sg), BAM_FIFO_SIZE);
/* allocate enough room to accomodate the number of entries */
- async_desc = kzalloc(sizeof(*async_desc) +
- (num_alloc * sizeof(struct bam_desc_hw)), GFP_NOWAIT);
+ async_desc = kzalloc(struct_size(async_desc, desc, num_alloc),
+ GFP_NOWAIT);
if (!async_desc)
goto err_out;
diff --git a/drivers/dma/qcom/hidma.c b/drivers/dma/qcom/hidma.c
index 43d4b00b8138..411f91fde734 100644
--- a/drivers/dma/qcom/hidma.c
+++ b/drivers/dma/qcom/hidma.c
@@ -138,24 +138,25 @@ static void hidma_process_completed(struct hidma_chan *mchan)
desc = &mdesc->desc;
last_cookie = desc->cookie;
+ llstat = hidma_ll_status(mdma->lldev, mdesc->tre_ch);
+
spin_lock_irqsave(&mchan->lock, irqflags);
+ if (llstat == DMA_COMPLETE) {
+ mchan->last_success = last_cookie;
+ result.result = DMA_TRANS_NOERROR;
+ } else {
+ result.result = DMA_TRANS_ABORTED;
+ }
+
dma_cookie_complete(desc);
spin_unlock_irqrestore(&mchan->lock, irqflags);
- llstat = hidma_ll_status(mdma->lldev, mdesc->tre_ch);
dmaengine_desc_get_callback(desc, &cb);
dma_run_dependencies(desc);
spin_lock_irqsave(&mchan->lock, irqflags);
list_move(&mdesc->node, &mchan->free);
-
- if (llstat == DMA_COMPLETE) {
- mchan->last_success = last_cookie;
- result.result = DMA_TRANS_NOERROR;
- } else
- result.result = DMA_TRANS_ABORTED;
-
spin_unlock_irqrestore(&mchan->lock, irqflags);
dmaengine_desc_callback_invoke(&cb, &result);
@@ -415,6 +416,7 @@ hidma_prep_dma_memcpy(struct dma_chan *dmach, dma_addr_t dest, dma_addr_t src,
if (!mdesc)
return NULL;
+ mdesc->desc.flags = flags;
hidma_ll_set_transfer_params(mdma->lldev, mdesc->tre_ch,
src, dest, len, flags,
HIDMA_TRE_MEMCPY);
@@ -447,6 +449,7 @@ hidma_prep_dma_memset(struct dma_chan *dmach, dma_addr_t dest, int value,
if (!mdesc)
return NULL;
+ mdesc->desc.flags = flags;
hidma_ll_set_transfer_params(mdma->lldev, mdesc->tre_ch,
value, dest, len, flags,
HIDMA_TRE_MEMSET);
diff --git a/drivers/dma/qcom/hidma_mgmt.c b/drivers/dma/qcom/hidma_mgmt.c
index d64edeb6771a..681de12f4c67 100644
--- a/drivers/dma/qcom/hidma_mgmt.c
+++ b/drivers/dma/qcom/hidma_mgmt.c
@@ -423,9 +423,8 @@ static int __init hidma_mgmt_init(void)
hidma_mgmt_of_populate_channels(child);
}
#endif
- platform_driver_register(&hidma_mgmt_driver);
+ return platform_driver_register(&hidma_mgmt_driver);
- return 0;
}
module_init(hidma_mgmt_init);
MODULE_LICENSE("GPL v2");
diff --git a/drivers/dma/sa11x0-dma.c b/drivers/dma/sa11x0-dma.c
index 784d5f1a473b..3fae23768b47 100644
--- a/drivers/dma/sa11x0-dma.c
+++ b/drivers/dma/sa11x0-dma.c
@@ -705,7 +705,6 @@ static int sa11x0_dma_device_pause(struct dma_chan *chan)
struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan);
struct sa11x0_dma_dev *d = to_sa11x0_dma(chan->device);
struct sa11x0_dma_phy *p;
- LIST_HEAD(head);
unsigned long flags;
dev_dbg(d->slave.dev, "vchan %p: pause\n", &c->vc);
@@ -732,7 +731,6 @@ static int sa11x0_dma_device_resume(struct dma_chan *chan)
struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan);
struct sa11x0_dma_dev *d = to_sa11x0_dma(chan->device);
struct sa11x0_dma_phy *p;
- LIST_HEAD(head);
unsigned long flags;
dev_dbg(d->slave.dev, "vchan %p: resume\n", &c->vc);
diff --git a/drivers/dma/sh/usb-dmac.c b/drivers/dma/sh/usb-dmac.c
index 7f7184c3cf95..59403f6d008a 100644
--- a/drivers/dma/sh/usb-dmac.c
+++ b/drivers/dma/sh/usb-dmac.c
@@ -694,6 +694,8 @@ static int usb_dmac_runtime_resume(struct device *dev)
#endif /* CONFIG_PM */
static const struct dev_pm_ops usb_dmac_pm = {
+ SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
SET_RUNTIME_PM_OPS(usb_dmac_runtime_suspend, usb_dmac_runtime_resume,
NULL)
};
diff --git a/drivers/dma/sprd-dma.c b/drivers/dma/sprd-dma.c
index e2f016700fcc..48431e2da987 100644
--- a/drivers/dma/sprd-dma.c
+++ b/drivers/dma/sprd-dma.c
@@ -580,15 +580,7 @@ static irqreturn_t dma_irq_handle(int irq, void *dev_id)
static int sprd_dma_alloc_chan_resources(struct dma_chan *chan)
{
- struct sprd_dma_chn *schan = to_sprd_dma_chan(chan);
- int ret;
-
- ret = pm_runtime_get_sync(chan->device->dev);
- if (ret < 0)
- return ret;
-
- schan->dev_id = SPRD_DMA_SOFTWARE_UID;
- return 0;
+ return pm_runtime_get_sync(chan->device->dev);
}
static void sprd_dma_free_chan_resources(struct dma_chan *chan)
@@ -1021,13 +1013,10 @@ static void sprd_dma_free_desc(struct virt_dma_desc *vd)
static bool sprd_dma_filter_fn(struct dma_chan *chan, void *param)
{
struct sprd_dma_chn *schan = to_sprd_dma_chan(chan);
- struct sprd_dma_dev *sdev = to_sprd_dma_dev(&schan->vc.chan);
- u32 req = *(u32 *)param;
+ u32 slave_id = *(u32 *)param;
- if (req < sdev->total_chns)
- return req == schan->chn_num + 1;
- else
- return false;
+ schan->dev_id = slave_id;
+ return true;
}
static int sprd_dma_probe(struct platform_device *pdev)
diff --git a/drivers/dma/st_fdma.c b/drivers/dma/st_fdma.c
index 07c20aa2e955..bc7a1de3f29b 100644
--- a/drivers/dma/st_fdma.c
+++ b/drivers/dma/st_fdma.c
@@ -243,8 +243,7 @@ static struct st_fdma_desc *st_fdma_alloc_desc(struct st_fdma_chan *fchan,
struct st_fdma_desc *fdesc;
int i;
- fdesc = kzalloc(sizeof(*fdesc) +
- sizeof(struct st_fdma_sw_node) * sg_len, GFP_NOWAIT);
+ fdesc = kzalloc(struct_size(fdesc, node, sg_len), GFP_NOWAIT);
if (!fdesc)
return NULL;
@@ -294,8 +293,6 @@ static void st_fdma_free_chan_res(struct dma_chan *chan)
struct rproc *rproc = fchan->fdev->slim_rproc->rproc;
unsigned long flags;
- LIST_HEAD(head);
-
dev_dbg(fchan->fdev->dev, "%s: freeing chan:%d\n",
__func__, fchan->vchan.chan.chan_id);
@@ -626,7 +623,6 @@ static void st_fdma_issue_pending(struct dma_chan *chan)
static int st_fdma_pause(struct dma_chan *chan)
{
unsigned long flags;
- LIST_HEAD(head);
struct st_fdma_chan *fchan = to_st_fdma_chan(chan);
int ch_id = fchan->vchan.chan.chan_id;
unsigned long cmd = FDMA_CMD_PAUSE(ch_id);
diff --git a/drivers/dma/stm32-dma.c b/drivers/dma/stm32-dma.c
index 4903a408fc14..ba239b529fa9 100644
--- a/drivers/dma/stm32-dma.c
+++ b/drivers/dma/stm32-dma.c
@@ -23,6 +23,7 @@
#include <linux/of_device.h>
#include <linux/of_dma.h>
#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <linux/reset.h>
#include <linux/sched.h>
#include <linux/slab.h>
@@ -641,12 +642,13 @@ static irqreturn_t stm32_dma_chan_irq(int irq, void *devid)
{
struct stm32_dma_chan *chan = devid;
struct stm32_dma_device *dmadev = stm32_dma_get_dev(chan);
- u32 status, scr;
+ u32 status, scr, sfcr;
spin_lock(&chan->vchan.lock);
status = stm32_dma_irq_status(chan);
scr = stm32_dma_read(dmadev, STM32_DMA_SCR(chan->id));
+ sfcr = stm32_dma_read(dmadev, STM32_DMA_SFCR(chan->id));
if (status & STM32_DMA_TCI) {
stm32_dma_irq_clear(chan, STM32_DMA_TCI);
@@ -661,10 +663,12 @@ static irqreturn_t stm32_dma_chan_irq(int irq, void *devid)
if (status & STM32_DMA_FEI) {
stm32_dma_irq_clear(chan, STM32_DMA_FEI);
status &= ~STM32_DMA_FEI;
- if (!(scr & STM32_DMA_SCR_EN))
- dev_err(chan2dev(chan), "FIFO Error\n");
- else
- dev_dbg(chan2dev(chan), "FIFO over/underrun\n");
+ if (sfcr & STM32_DMA_SFCR_FEIE) {
+ if (!(scr & STM32_DMA_SCR_EN))
+ dev_err(chan2dev(chan), "FIFO Error\n");
+ else
+ dev_dbg(chan2dev(chan), "FIFO over/underrun\n");
+ }
}
if (status) {
stm32_dma_irq_clear(chan, status);
@@ -1112,15 +1116,14 @@ static int stm32_dma_alloc_chan_resources(struct dma_chan *c)
int ret;
chan->config_init = false;
- ret = clk_prepare_enable(dmadev->clk);
- if (ret < 0) {
- dev_err(chan2dev(chan), "clk_prepare_enable failed: %d\n", ret);
+
+ ret = pm_runtime_get_sync(dmadev->ddev.dev);
+ if (ret < 0)
return ret;
- }
ret = stm32_dma_disable_chan(chan);
if (ret < 0)
- clk_disable_unprepare(dmadev->clk);
+ pm_runtime_put(dmadev->ddev.dev);
return ret;
}
@@ -1140,7 +1143,7 @@ static void stm32_dma_free_chan_resources(struct dma_chan *c)
spin_unlock_irqrestore(&chan->vchan.lock, flags);
}
- clk_disable_unprepare(dmadev->clk);
+ pm_runtime_put(dmadev->ddev.dev);
vchan_free_chan_resources(to_virt_chan(c));
}
@@ -1240,6 +1243,12 @@ static int stm32_dma_probe(struct platform_device *pdev)
return PTR_ERR(dmadev->clk);
}
+ ret = clk_prepare_enable(dmadev->clk);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "clk_prep_enable error: %d\n", ret);
+ return ret;
+ }
+
dmadev->mem2mem = of_property_read_bool(pdev->dev.of_node,
"st,mem2mem");
@@ -1289,7 +1298,7 @@ static int stm32_dma_probe(struct platform_device *pdev)
ret = dma_async_device_register(dd);
if (ret)
- return ret;
+ goto clk_free;
for (i = 0; i < STM32_DMA_MAX_CHANNELS; i++) {
chan = &dmadev->chan[i];
@@ -1321,20 +1330,58 @@ static int stm32_dma_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, dmadev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_get_noresume(&pdev->dev);
+ pm_runtime_put(&pdev->dev);
+
dev_info(&pdev->dev, "STM32 DMA driver registered\n");
return 0;
err_unregister:
dma_async_device_unregister(dd);
+clk_free:
+ clk_disable_unprepare(dmadev->clk);
return ret;
}
+#ifdef CONFIG_PM
+static int stm32_dma_runtime_suspend(struct device *dev)
+{
+ struct stm32_dma_device *dmadev = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(dmadev->clk);
+
+ return 0;
+}
+
+static int stm32_dma_runtime_resume(struct device *dev)
+{
+ struct stm32_dma_device *dmadev = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(dmadev->clk);
+ if (ret) {
+ dev_err(dev, "failed to prepare_enable clock\n");
+ return ret;
+ }
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops stm32_dma_pm_ops = {
+ SET_RUNTIME_PM_OPS(stm32_dma_runtime_suspend,
+ stm32_dma_runtime_resume, NULL)
+};
+
static struct platform_driver stm32_dma_driver = {
.driver = {
.name = "stm32-dma",
.of_match_table = stm32_dma_of_match,
+ .pm = &stm32_dma_pm_ops,
},
};
diff --git a/drivers/dma/stm32-dmamux.c b/drivers/dma/stm32-dmamux.c
index b922db90939a..a67119199c45 100644
--- a/drivers/dma/stm32-dmamux.c
+++ b/drivers/dma/stm32-dmamux.c
@@ -28,6 +28,7 @@
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/of_dma.h>
+#include <linux/pm_runtime.h>
#include <linux/reset.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
@@ -79,8 +80,7 @@ static void stm32_dmamux_free(struct device *dev, void *route_data)
stm32_dmamux_write(dmamux->iomem, STM32_DMAMUX_CCR(mux->chan_id), 0);
clear_bit(mux->chan_id, dmamux->dma_inuse);
- if (!IS_ERR(dmamux->clk))
- clk_disable(dmamux->clk);
+ pm_runtime_put_sync(dev);
spin_unlock_irqrestore(&dmamux->lock, flags);
@@ -146,13 +146,10 @@ static void *stm32_dmamux_route_allocate(struct of_phandle_args *dma_spec,
/* Set dma request */
spin_lock_irqsave(&dmamux->lock, flags);
- if (!IS_ERR(dmamux->clk)) {
- ret = clk_enable(dmamux->clk);
- if (ret < 0) {
- spin_unlock_irqrestore(&dmamux->lock, flags);
- dev_err(&pdev->dev, "clk_prep_enable issue: %d\n", ret);
- goto error;
- }
+ ret = pm_runtime_get_sync(&pdev->dev);
+ if (ret < 0) {
+ spin_unlock_irqrestore(&dmamux->lock, flags);
+ goto error;
}
spin_unlock_irqrestore(&dmamux->lock, flags);
@@ -254,6 +251,7 @@ static int stm32_dmamux_probe(struct platform_device *pdev)
dev_warn(&pdev->dev, "DMAMUX defaulting on %u requests\n",
stm32_dmamux->dmamux_requests);
}
+ pm_runtime_get_noresume(&pdev->dev);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
iomem = devm_ioremap_resource(&pdev->dev, res);
@@ -282,6 +280,8 @@ static int stm32_dmamux_probe(struct platform_device *pdev)
stm32_dmamux->dmarouter.route_free = stm32_dmamux_free;
platform_set_drvdata(pdev, stm32_dmamux);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
if (!IS_ERR(stm32_dmamux->clk)) {
ret = clk_prepare_enable(stm32_dmamux->clk);
@@ -291,17 +291,52 @@ static int stm32_dmamux_probe(struct platform_device *pdev)
}
}
+ pm_runtime_get_noresume(&pdev->dev);
+
/* Reset the dmamux */
for (i = 0; i < stm32_dmamux->dma_requests; i++)
stm32_dmamux_write(stm32_dmamux->iomem, STM32_DMAMUX_CCR(i), 0);
- if (!IS_ERR(stm32_dmamux->clk))
- clk_disable(stm32_dmamux->clk);
+ pm_runtime_put(&pdev->dev);
return of_dma_router_register(node, stm32_dmamux_route_allocate,
&stm32_dmamux->dmarouter);
}
+#ifdef CONFIG_PM
+static int stm32_dmamux_runtime_suspend(struct device *dev)
+{
+ struct platform_device *pdev =
+ container_of(dev, struct platform_device, dev);
+ struct stm32_dmamux_data *stm32_dmamux = platform_get_drvdata(pdev);
+
+ clk_disable_unprepare(stm32_dmamux->clk);
+
+ return 0;
+}
+
+static int stm32_dmamux_runtime_resume(struct device *dev)
+{
+ struct platform_device *pdev =
+ container_of(dev, struct platform_device, dev);
+ struct stm32_dmamux_data *stm32_dmamux = platform_get_drvdata(pdev);
+ int ret;
+
+ ret = clk_prepare_enable(stm32_dmamux->clk);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to prepare_enable clock\n");
+ return ret;
+ }
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops stm32_dmamux_pm_ops = {
+ SET_RUNTIME_PM_OPS(stm32_dmamux_runtime_suspend,
+ stm32_dmamux_runtime_resume, NULL)
+};
+
static const struct of_device_id stm32_dmamux_match[] = {
{ .compatible = "st,stm32h7-dmamux" },
{},
@@ -312,6 +347,7 @@ static struct platform_driver stm32_dmamux_driver = {
.driver = {
.name = "stm32-dmamux",
.of_match_table = stm32_dmamux_match,
+ .pm = &stm32_dmamux_pm_ops,
},
};
diff --git a/drivers/dma/stm32-mdma.c b/drivers/dma/stm32-mdma.c
index 390e4cae0e1a..ac0301b69593 100644
--- a/drivers/dma/stm32-mdma.c
+++ b/drivers/dma/stm32-mdma.c
@@ -37,6 +37,7 @@
#include <linux/of_device.h>
#include <linux/of_dma.h>
#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <linux/reset.h>
#include <linux/slab.h>
@@ -1456,15 +1457,13 @@ static int stm32_mdma_alloc_chan_resources(struct dma_chan *c)
return -ENOMEM;
}
- ret = clk_prepare_enable(dmadev->clk);
- if (ret < 0) {
- dev_err(chan2dev(chan), "clk_prepare_enable failed: %d\n", ret);
+ ret = pm_runtime_get_sync(dmadev->ddev.dev);
+ if (ret < 0)
return ret;
- }
ret = stm32_mdma_disable_chan(chan);
if (ret < 0)
- clk_disable_unprepare(dmadev->clk);
+ pm_runtime_put(dmadev->ddev.dev);
return ret;
}
@@ -1484,7 +1483,7 @@ static void stm32_mdma_free_chan_resources(struct dma_chan *c)
spin_unlock_irqrestore(&chan->vchan.lock, flags);
}
- clk_disable_unprepare(dmadev->clk);
+ pm_runtime_put(dmadev->ddev.dev);
vchan_free_chan_resources(to_virt_chan(c));
dmam_pool_destroy(chan->desc_pool);
chan->desc_pool = NULL;
@@ -1597,6 +1596,12 @@ static int stm32_mdma_probe(struct platform_device *pdev)
return ret;
}
+ ret = clk_prepare_enable(dmadev->clk);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "clk_prep_enable error: %d\n", ret);
+ return ret;
+ }
+
dmadev->rst = devm_reset_control_get(&pdev->dev, NULL);
if (!IS_ERR(dmadev->rst)) {
reset_control_assert(dmadev->rst);
@@ -1668,6 +1673,10 @@ static int stm32_mdma_probe(struct platform_device *pdev)
}
platform_set_drvdata(pdev, dmadev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_get_noresume(&pdev->dev);
+ pm_runtime_put(&pdev->dev);
dev_info(&pdev->dev, "STM32 MDMA driver registered\n");
@@ -1677,11 +1686,42 @@ err_unregister:
return ret;
}
+#ifdef CONFIG_PM
+static int stm32_mdma_runtime_suspend(struct device *dev)
+{
+ struct stm32_mdma_device *dmadev = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(dmadev->clk);
+
+ return 0;
+}
+
+static int stm32_mdma_runtime_resume(struct device *dev)
+{
+ struct stm32_mdma_device *dmadev = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(dmadev->clk);
+ if (ret) {
+ dev_err(dev, "failed to prepare_enable clock\n");
+ return ret;
+ }
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops stm32_mdma_pm_ops = {
+ SET_RUNTIME_PM_OPS(stm32_mdma_runtime_suspend,
+ stm32_mdma_runtime_resume, NULL)
+};
+
static struct platform_driver stm32_mdma_driver = {
.probe = stm32_mdma_probe,
.driver = {
.name = "stm32-mdma",
.of_match_table = stm32_mdma_of_match,
+ .pm = &stm32_mdma_pm_ops,
},
};
diff --git a/drivers/dma/tegra20-apb-dma.c b/drivers/dma/tegra20-apb-dma.c
index 9a558e30c461..cf462b1abc0b 100644
--- a/drivers/dma/tegra20-apb-dma.c
+++ b/drivers/dma/tegra20-apb-dma.c
@@ -38,6 +38,9 @@
#include "dmaengine.h"
+#define CREATE_TRACE_POINTS
+#include <trace/events/tegra_apb_dma.h>
+
#define TEGRA_APBDMA_GENERAL 0x0
#define TEGRA_APBDMA_GENERAL_ENABLE BIT(31)
@@ -146,7 +149,7 @@ struct tegra_dma_channel_regs {
};
/*
- * tegra_dma_sg_req: Dma request details to configure hardware. This
+ * tegra_dma_sg_req: DMA request details to configure hardware. This
* contains the details for one transfer to configure DMA hw.
* The client's request for data transfer can be broken into multiple
* sub-transfer as per requester details and hw support.
@@ -155,7 +158,7 @@ struct tegra_dma_channel_regs {
*/
struct tegra_dma_sg_req {
struct tegra_dma_channel_regs ch_regs;
- int req_len;
+ unsigned int req_len;
bool configured;
bool last_sg;
struct list_head node;
@@ -169,8 +172,8 @@ struct tegra_dma_sg_req {
*/
struct tegra_dma_desc {
struct dma_async_tx_descriptor txd;
- int bytes_requested;
- int bytes_transferred;
+ unsigned int bytes_requested;
+ unsigned int bytes_transferred;
enum dma_status dma_status;
struct list_head node;
struct list_head tx_list;
@@ -186,7 +189,7 @@ typedef void (*dma_isr_handler)(struct tegra_dma_channel *tdc,
/* tegra_dma_channel: Channel specific information */
struct tegra_dma_channel {
struct dma_chan dma_chan;
- char name[30];
+ char name[12];
bool config_init;
int id;
int irq;
@@ -574,7 +577,7 @@ static bool handle_continuous_head_request(struct tegra_dma_channel *tdc,
struct tegra_dma_sg_req *hsgreq = NULL;
if (list_empty(&tdc->pending_sg_req)) {
- dev_err(tdc2dev(tdc), "Dma is running without req\n");
+ dev_err(tdc2dev(tdc), "DMA is running without req\n");
tegra_dma_stop(tdc);
return false;
}
@@ -587,7 +590,7 @@ static bool handle_continuous_head_request(struct tegra_dma_channel *tdc,
hsgreq = list_first_entry(&tdc->pending_sg_req, typeof(*hsgreq), node);
if (!hsgreq->configured) {
tegra_dma_stop(tdc);
- dev_err(tdc2dev(tdc), "Error in dma transfer, aborting dma\n");
+ dev_err(tdc2dev(tdc), "Error in DMA transfer, aborting DMA\n");
tegra_dma_abort_all(tdc);
return false;
}
@@ -636,7 +639,10 @@ static void handle_cont_sngl_cycle_dma_done(struct tegra_dma_channel *tdc,
sgreq = list_first_entry(&tdc->pending_sg_req, typeof(*sgreq), node);
dma_desc = sgreq->dma_desc;
- dma_desc->bytes_transferred += sgreq->req_len;
+ /* if we dma for long enough the transfer count will wrap */
+ dma_desc->bytes_transferred =
+ (dma_desc->bytes_transferred + sgreq->req_len) %
+ dma_desc->bytes_requested;
/* Callback need to be call */
if (!dma_desc->cb_count)
@@ -669,6 +675,8 @@ static void tegra_dma_tasklet(unsigned long data)
dmaengine_desc_get_callback(&dma_desc->txd, &cb);
cb_count = dma_desc->cb_count;
dma_desc->cb_count = 0;
+ trace_tegra_dma_complete_cb(&tdc->dma_chan, cb_count,
+ cb.callback);
spin_unlock_irqrestore(&tdc->lock, flags);
while (cb_count--)
dmaengine_desc_callback_invoke(&cb, NULL);
@@ -685,6 +693,7 @@ static irqreturn_t tegra_dma_isr(int irq, void *dev_id)
spin_lock_irqsave(&tdc->lock, flags);
+ trace_tegra_dma_isr(&tdc->dma_chan, irq);
status = tdc_read(tdc, TEGRA_APBDMA_CHAN_STATUS);
if (status & TEGRA_APBDMA_STATUS_ISE_EOC) {
tdc_write(tdc, TEGRA_APBDMA_CHAN_STATUS, status);
@@ -843,6 +852,7 @@ found:
dma_set_residue(txstate, residual);
}
+ trace_tegra_dma_tx_status(&tdc->dma_chan, cookie, txstate);
spin_unlock_irqrestore(&tdc->lock, flags);
return ret;
}
@@ -919,7 +929,7 @@ static int get_transfer_param(struct tegra_dma_channel *tdc,
return 0;
default:
- dev_err(tdc2dev(tdc), "Dma direction is not supported\n");
+ dev_err(tdc2dev(tdc), "DMA direction is not supported\n");
return -EINVAL;
}
return -EINVAL;
@@ -952,7 +962,7 @@ static struct dma_async_tx_descriptor *tegra_dma_prep_slave_sg(
enum dma_slave_buswidth slave_bw;
if (!tdc->config_init) {
- dev_err(tdc2dev(tdc), "dma channel is not configured\n");
+ dev_err(tdc2dev(tdc), "DMA channel is not configured\n");
return NULL;
}
if (sg_len < 1) {
@@ -985,7 +995,7 @@ static struct dma_async_tx_descriptor *tegra_dma_prep_slave_sg(
dma_desc = tegra_dma_desc_get(tdc);
if (!dma_desc) {
- dev_err(tdc2dev(tdc), "Dma descriptors not available\n");
+ dev_err(tdc2dev(tdc), "DMA descriptors not available\n");
return NULL;
}
INIT_LIST_HEAD(&dma_desc->tx_list);
@@ -1005,14 +1015,14 @@ static struct dma_async_tx_descriptor *tegra_dma_prep_slave_sg(
if ((len & 3) || (mem & 3) ||
(len > tdc->tdma->chip_data->max_dma_count)) {
dev_err(tdc2dev(tdc),
- "Dma length/memory address is not supported\n");
+ "DMA length/memory address is not supported\n");
tegra_dma_desc_put(tdc, dma_desc);
return NULL;
}
sg_req = tegra_dma_sg_req_get(tdc);
if (!sg_req) {
- dev_err(tdc2dev(tdc), "Dma sg-req not available\n");
+ dev_err(tdc2dev(tdc), "DMA sg-req not available\n");
tegra_dma_desc_put(tdc, dma_desc);
return NULL;
}
@@ -1087,7 +1097,7 @@ static struct dma_async_tx_descriptor *tegra_dma_prep_dma_cyclic(
* terminating the DMA.
*/
if (tdc->busy) {
- dev_err(tdc2dev(tdc), "Request not allowed when dma running\n");
+ dev_err(tdc2dev(tdc), "Request not allowed when DMA running\n");
return NULL;
}
@@ -1144,7 +1154,7 @@ static struct dma_async_tx_descriptor *tegra_dma_prep_dma_cyclic(
while (remain_len) {
sg_req = tegra_dma_sg_req_get(tdc);
if (!sg_req) {
- dev_err(tdc2dev(tdc), "Dma sg-req not available\n");
+ dev_err(tdc2dev(tdc), "DMA sg-req not available\n");
tegra_dma_desc_put(tdc, dma_desc);
return NULL;
}
@@ -1319,8 +1329,9 @@ static int tegra_dma_probe(struct platform_device *pdev)
return -ENODEV;
}
- tdma = devm_kzalloc(&pdev->dev, sizeof(*tdma) + cdata->nr_channels *
- sizeof(struct tegra_dma_channel), GFP_KERNEL);
+ tdma = devm_kzalloc(&pdev->dev,
+ struct_size(tdma, channels, cdata->nr_channels),
+ GFP_KERNEL);
if (!tdma)
return -ENOMEM;
diff --git a/drivers/dma/tegra210-adma.c b/drivers/dma/tegra210-adma.c
index b26256f23d67..5ec0dd97b397 100644
--- a/drivers/dma/tegra210-adma.c
+++ b/drivers/dma/tegra210-adma.c
@@ -678,8 +678,9 @@ static int tegra_adma_probe(struct platform_device *pdev)
return -ENODEV;
}
- tdma = devm_kzalloc(&pdev->dev, sizeof(*tdma) + cdata->nr_channels *
- sizeof(struct tegra_adma_chan), GFP_KERNEL);
+ tdma = devm_kzalloc(&pdev->dev,
+ struct_size(tdma, channels, cdata->nr_channels),
+ GFP_KERNEL);
if (!tdma)
return -ENOMEM;
diff --git a/drivers/dma/timb_dma.c b/drivers/dma/timb_dma.c
index fc0f9c8766a8..afbb1c95b721 100644
--- a/drivers/dma/timb_dma.c
+++ b/drivers/dma/timb_dma.c
@@ -643,8 +643,8 @@ static int td_probe(struct platform_device *pdev)
DRIVER_NAME))
return -EBUSY;
- td = kzalloc(sizeof(struct timb_dma) +
- sizeof(struct timb_dma_chan) * pdata->nr_channels, GFP_KERNEL);
+ td = kzalloc(struct_size(td, channels, pdata->nr_channels),
+ GFP_KERNEL);
if (!td) {
err = -ENOMEM;
goto err_release_region;
diff --git a/drivers/dma/xilinx/xilinx_dma.c b/drivers/dma/xilinx/xilinx_dma.c
index cb20b411493e..c43c1a154604 100644
--- a/drivers/dma/xilinx/xilinx_dma.c
+++ b/drivers/dma/xilinx/xilinx_dma.c
@@ -86,6 +86,7 @@
#define XILINX_DMA_DMASR_DMA_DEC_ERR BIT(6)
#define XILINX_DMA_DMASR_DMA_SLAVE_ERR BIT(5)
#define XILINX_DMA_DMASR_DMA_INT_ERR BIT(4)
+#define XILINX_DMA_DMASR_SG_MASK BIT(3)
#define XILINX_DMA_DMASR_IDLE BIT(1)
#define XILINX_DMA_DMASR_HALTED BIT(0)
#define XILINX_DMA_DMASR_DELAY_MASK GENMASK(31, 24)
@@ -161,7 +162,9 @@
#define XILINX_DMA_REG_BTT 0x28
/* AXI DMA Specific Masks/Bit fields */
-#define XILINX_DMA_MAX_TRANS_LEN GENMASK(22, 0)
+#define XILINX_DMA_MAX_TRANS_LEN_MIN 8
+#define XILINX_DMA_MAX_TRANS_LEN_MAX 23
+#define XILINX_DMA_V2_MAX_TRANS_LEN_MAX 26
#define XILINX_DMA_CR_COALESCE_MAX GENMASK(23, 16)
#define XILINX_DMA_CR_CYCLIC_BD_EN_MASK BIT(4)
#define XILINX_DMA_CR_COALESCE_SHIFT 16
@@ -412,7 +415,6 @@ struct xilinx_dma_config {
* @dev: Device Structure
* @common: DMA device structure
* @chan: Driver specific DMA channel
- * @has_sg: Specifies whether Scatter-Gather is present or not
* @mcdma: Specifies whether Multi-Channel is present or not
* @flush_on_fsync: Flush on frame sync
* @ext_addr: Indicates 64 bit addressing is supported by dma device
@@ -425,13 +427,13 @@ struct xilinx_dma_config {
* @rxs_clk: DMA s2mm stream clock
* @nr_channels: Number of channels DMA device supports
* @chan_id: DMA channel identifier
+ * @max_buffer_len: Max buffer length
*/
struct xilinx_dma_device {
void __iomem *regs;
struct device *dev;
struct dma_device common;
struct xilinx_dma_chan *chan[XILINX_DMA_MAX_CHANS_PER_DEVICE];
- bool has_sg;
bool mcdma;
u32 flush_on_fsync;
bool ext_addr;
@@ -444,6 +446,7 @@ struct xilinx_dma_device {
struct clk *rxs_clk;
u32 nr_channels;
u32 chan_id;
+ u32 max_buffer_len;
};
/* Macros */
@@ -960,6 +963,34 @@ static int xilinx_dma_alloc_chan_resources(struct dma_chan *dchan)
}
/**
+ * xilinx_dma_calc_copysize - Calculate the amount of data to copy
+ * @chan: Driver specific DMA channel
+ * @size: Total data that needs to be copied
+ * @done: Amount of data that has been already copied
+ *
+ * Return: Amount of data that has to be copied
+ */
+static int xilinx_dma_calc_copysize(struct xilinx_dma_chan *chan,
+ int size, int done)
+{
+ size_t copy;
+
+ copy = min_t(size_t, size - done,
+ chan->xdev->max_buffer_len);
+
+ if ((copy + done < size) &&
+ chan->xdev->common.copy_align) {
+ /*
+ * If this is not the last descriptor, make sure
+ * the next one will be properly aligned
+ */
+ copy = rounddown(copy,
+ (1 << chan->xdev->common.copy_align));
+ }
+ return copy;
+}
+
+/**
* xilinx_dma_tx_status - Get DMA transaction status
* @dchan: DMA channel
* @cookie: Transaction identifier
@@ -992,7 +1023,7 @@ static enum dma_status xilinx_dma_tx_status(struct dma_chan *dchan,
list_for_each_entry(segment, &desc->segments, node) {
hw = &segment->hw;
residue += (hw->control - hw->status) &
- XILINX_DMA_MAX_TRANS_LEN;
+ chan->xdev->max_buffer_len;
}
}
spin_unlock_irqrestore(&chan->lock, flags);
@@ -1070,7 +1101,8 @@ static void xilinx_vdma_start_transfer(struct xilinx_dma_chan *chan)
struct xilinx_vdma_config *config = &chan->config;
struct xilinx_dma_tx_descriptor *desc, *tail_desc;
u32 reg, j;
- struct xilinx_vdma_tx_segment *tail_segment;
+ struct xilinx_vdma_tx_segment *segment, *last = NULL;
+ int i = 0;
/* This function was invoked with lock held */
if (chan->err)
@@ -1087,17 +1119,6 @@ static void xilinx_vdma_start_transfer(struct xilinx_dma_chan *chan)
tail_desc = list_last_entry(&chan->pending_list,
struct xilinx_dma_tx_descriptor, node);
- tail_segment = list_last_entry(&tail_desc->segments,
- struct xilinx_vdma_tx_segment, node);
-
- /*
- * If hardware is idle, then all descriptors on the running lists are
- * done, start new transfers
- */
- if (chan->has_sg)
- dma_ctrl_write(chan, XILINX_DMA_REG_CURDESC,
- desc->async_tx.phys);
-
/* Configure the hardware using info in the config structure */
if (chan->has_vflip) {
reg = dma_read(chan, XILINX_VDMA_REG_ENABLE_VERTICAL_FLIP);
@@ -1114,15 +1135,11 @@ static void xilinx_vdma_start_transfer(struct xilinx_dma_chan *chan)
else
reg &= ~XILINX_DMA_DMACR_FRAMECNT_EN;
- /*
- * With SG, start with circular mode, so that BDs can be fetched.
- * In direct register mode, if not parking, enable circular mode
- */
- if (chan->has_sg || !config->park)
- reg |= XILINX_DMA_DMACR_CIRC_EN;
-
+ /* If not parking, enable circular mode */
if (config->park)
reg &= ~XILINX_DMA_DMACR_CIRC_EN;
+ else
+ reg |= XILINX_DMA_DMACR_CIRC_EN;
dma_ctrl_write(chan, XILINX_DMA_REG_DMACR, reg);
@@ -1144,48 +1161,38 @@ static void xilinx_vdma_start_transfer(struct xilinx_dma_chan *chan)
return;
/* Start the transfer */
- if (chan->has_sg) {
- dma_ctrl_write(chan, XILINX_DMA_REG_TAILDESC,
- tail_segment->phys);
- list_splice_tail_init(&chan->pending_list, &chan->active_list);
- chan->desc_pendingcount = 0;
- } else {
- struct xilinx_vdma_tx_segment *segment, *last = NULL;
- int i = 0;
-
- if (chan->desc_submitcount < chan->num_frms)
- i = chan->desc_submitcount;
-
- list_for_each_entry(segment, &desc->segments, node) {
- if (chan->ext_addr)
- vdma_desc_write_64(chan,
- XILINX_VDMA_REG_START_ADDRESS_64(i++),
- segment->hw.buf_addr,
- segment->hw.buf_addr_msb);
- else
- vdma_desc_write(chan,
+ if (chan->desc_submitcount < chan->num_frms)
+ i = chan->desc_submitcount;
+
+ list_for_each_entry(segment, &desc->segments, node) {
+ if (chan->ext_addr)
+ vdma_desc_write_64(chan,
+ XILINX_VDMA_REG_START_ADDRESS_64(i++),
+ segment->hw.buf_addr,
+ segment->hw.buf_addr_msb);
+ else
+ vdma_desc_write(chan,
XILINX_VDMA_REG_START_ADDRESS(i++),
segment->hw.buf_addr);
- last = segment;
- }
+ last = segment;
+ }
- if (!last)
- return;
+ if (!last)
+ return;
- /* HW expects these parameters to be same for one transaction */
- vdma_desc_write(chan, XILINX_DMA_REG_HSIZE, last->hw.hsize);
- vdma_desc_write(chan, XILINX_DMA_REG_FRMDLY_STRIDE,
- last->hw.stride);
- vdma_desc_write(chan, XILINX_DMA_REG_VSIZE, last->hw.vsize);
+ /* HW expects these parameters to be same for one transaction */
+ vdma_desc_write(chan, XILINX_DMA_REG_HSIZE, last->hw.hsize);
+ vdma_desc_write(chan, XILINX_DMA_REG_FRMDLY_STRIDE,
+ last->hw.stride);
+ vdma_desc_write(chan, XILINX_DMA_REG_VSIZE, last->hw.vsize);
- chan->desc_submitcount++;
- chan->desc_pendingcount--;
- list_del(&desc->node);
- list_add_tail(&desc->node, &chan->active_list);
- if (chan->desc_submitcount == chan->num_frms)
- chan->desc_submitcount = 0;
- }
+ chan->desc_submitcount++;
+ chan->desc_pendingcount--;
+ list_del(&desc->node);
+ list_add_tail(&desc->node, &chan->active_list);
+ if (chan->desc_submitcount == chan->num_frms)
+ chan->desc_submitcount = 0;
chan->idle = false;
}
@@ -1254,7 +1261,7 @@ static void xilinx_cdma_start_transfer(struct xilinx_dma_chan *chan)
/* Start the transfer */
dma_ctrl_write(chan, XILINX_DMA_REG_BTT,
- hw->control & XILINX_DMA_MAX_TRANS_LEN);
+ hw->control & chan->xdev->max_buffer_len);
}
list_splice_tail_init(&chan->pending_list, &chan->active_list);
@@ -1357,7 +1364,7 @@ static void xilinx_dma_start_transfer(struct xilinx_dma_chan *chan)
/* Start the transfer */
dma_ctrl_write(chan, XILINX_DMA_REG_BTT,
- hw->control & XILINX_DMA_MAX_TRANS_LEN);
+ hw->control & chan->xdev->max_buffer_len);
}
list_splice_tail_init(&chan->pending_list, &chan->active_list);
@@ -1718,7 +1725,7 @@ xilinx_cdma_prep_memcpy(struct dma_chan *dchan, dma_addr_t dma_dst,
struct xilinx_cdma_tx_segment *segment;
struct xilinx_cdma_desc_hw *hw;
- if (!len || len > XILINX_DMA_MAX_TRANS_LEN)
+ if (!len || len > chan->xdev->max_buffer_len)
return NULL;
desc = xilinx_dma_alloc_tx_descriptor(chan);
@@ -1808,8 +1815,8 @@ static struct dma_async_tx_descriptor *xilinx_dma_prep_slave_sg(
* Calculate the maximum number of bytes to transfer,
* making sure it is less than the hw limit
*/
- copy = min_t(size_t, sg_dma_len(sg) - sg_used,
- XILINX_DMA_MAX_TRANS_LEN);
+ copy = xilinx_dma_calc_copysize(chan, sg_dma_len(sg),
+ sg_used);
hw = &segment->hw;
/* Fill in the descriptor */
@@ -1913,8 +1920,8 @@ static struct dma_async_tx_descriptor *xilinx_dma_prep_dma_cyclic(
* Calculate the maximum number of bytes to transfer,
* making sure it is less than the hw limit
*/
- copy = min_t(size_t, period_len - sg_used,
- XILINX_DMA_MAX_TRANS_LEN);
+ copy = xilinx_dma_calc_copysize(chan, period_len,
+ sg_used);
hw = &segment->hw;
xilinx_axidma_buf(chan, hw, buf_addr, sg_used,
period_len * i);
@@ -2389,7 +2396,6 @@ static int xilinx_dma_chan_probe(struct xilinx_dma_device *xdev,
chan->dev = xdev->dev;
chan->xdev = xdev;
- chan->has_sg = xdev->has_sg;
chan->desc_pendingcount = 0x0;
chan->ext_addr = xdev->ext_addr;
/* This variable ensures that descriptors are not
@@ -2489,6 +2495,15 @@ static int xilinx_dma_chan_probe(struct xilinx_dma_device *xdev,
chan->stop_transfer = xilinx_dma_stop_transfer;
}
+ /* check if SG is enabled (only for AXIDMA and CDMA) */
+ if (xdev->dma_config->dmatype != XDMA_TYPE_VDMA) {
+ if (dma_ctrl_read(chan, XILINX_DMA_REG_DMASR) &
+ XILINX_DMA_DMASR_SG_MASK)
+ chan->has_sg = true;
+ dev_dbg(chan->dev, "ch %d: SG %s\n", chan->id,
+ chan->has_sg ? "enabled" : "disabled");
+ }
+
/* Initialize the tasklet */
tasklet_init(&chan->tasklet, xilinx_dma_do_tasklet,
(unsigned long)chan);
@@ -2596,7 +2611,7 @@ static int xilinx_dma_probe(struct platform_device *pdev)
struct xilinx_dma_device *xdev;
struct device_node *child, *np = pdev->dev.of_node;
struct resource *io;
- u32 num_frames, addr_width;
+ u32 num_frames, addr_width, len_width;
int i, err;
/* Allocate and initialize the DMA engine structure */
@@ -2627,9 +2642,24 @@ static int xilinx_dma_probe(struct platform_device *pdev)
return PTR_ERR(xdev->regs);
/* Retrieve the DMA engine properties from the device tree */
- xdev->has_sg = of_property_read_bool(node, "xlnx,include-sg");
- if (xdev->dma_config->dmatype == XDMA_TYPE_AXIDMA)
+ xdev->max_buffer_len = GENMASK(XILINX_DMA_MAX_TRANS_LEN_MAX - 1, 0);
+
+ if (xdev->dma_config->dmatype == XDMA_TYPE_AXIDMA) {
xdev->mcdma = of_property_read_bool(node, "xlnx,mcdma");
+ if (!of_property_read_u32(node, "xlnx,sg-length-width",
+ &len_width)) {
+ if (len_width < XILINX_DMA_MAX_TRANS_LEN_MIN ||
+ len_width > XILINX_DMA_V2_MAX_TRANS_LEN_MAX) {
+ dev_warn(xdev->dev,
+ "invalid xlnx,sg-length-width property value. Using default width\n");
+ } else {
+ if (len_width > XILINX_DMA_MAX_TRANS_LEN_MAX)
+ dev_warn(xdev->dev, "Please ensure that IP supports buffer length > 23 bits\n");
+ xdev->max_buffer_len =
+ GENMASK(len_width - 1, 0);
+ }
+ }
+ }
if (xdev->dma_config->dmatype == XDMA_TYPE_VDMA) {
err = of_property_read_u32(node, "xlnx,num-fstores",
diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig
index e286b5b99003..47eb4d13ed5f 100644
--- a/drivers/edac/Kconfig
+++ b/drivers/edac/Kconfig
@@ -241,6 +241,18 @@ config EDAC_SKX
system has non-volatile DIMMs you should also manually
select CONFIG_ACPI_NFIT.
+config EDAC_I10NM
+ tristate "Intel 10nm server Integrated MC"
+ depends on PCI && X86_64 && X86_MCE_INTEL && PCI_MMCONFIG && ACPI
+ depends on ACPI_NFIT || !ACPI_NFIT # if ACPI_NFIT=m, EDAC_I10NM can't be y
+ select DMI
+ select ACPI_ADXL
+ help
+ Support for error detection and correction the Intel
+ 10nm server Integrated Memory Controllers. If your
+ system has non-volatile DIMMs you should also manually
+ select CONFIG_ACPI_NFIT.
+
config EDAC_PND2
tristate "Intel Pondicherry2"
depends on PCI && X86_64 && X86_MCE_INTEL
@@ -379,9 +391,17 @@ config EDAC_ALTERA
depends on EDAC=y && (ARCH_SOCFPGA || ARCH_STRATIX10)
help
Support for error detection and correction on the
- Altera SOCs. This must be selected for SDRAM ECC.
- Note that the preloader must initialize the SDRAM
- before loading the kernel.
+ Altera SOCs. This is the global enable for the
+ various Altera peripherals.
+
+config EDAC_ALTERA_SDRAM
+ bool "Altera SDRAM ECC"
+ depends on EDAC_ALTERA=y
+ help
+ Support for error detection and correction on the
+ Altera SDRAM Memory for Altera SoCs. Note that the
+ preloader must initialize the SDRAM before loading
+ the kernel.
config EDAC_ALTERA_L2C
bool "Altera L2 Cache ECC"
@@ -475,4 +495,13 @@ config EDAC_QCOM
For debugging issues having to do with stability and overall system
health, you should probably say 'Y' here.
+config EDAC_ASPEED
+ tristate "Aspeed AST 2500 SoC"
+ depends on MACH_ASPEED_G5
+ help
+ Support for error detection and correction on the Aspeed AST 2500 SoC.
+
+ First, ECC must be configured in the bootloader. Then, this driver
+ will expose error counters via the EDAC kernel framework.
+
endif # EDAC
diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile
index 716096d08ea0..89ad4a84a0f6 100644
--- a/drivers/edac/Makefile
+++ b/drivers/edac/Makefile
@@ -30,7 +30,6 @@ obj-$(CONFIG_EDAC_I5400) += i5400_edac.o
obj-$(CONFIG_EDAC_I7300) += i7300_edac.o
obj-$(CONFIG_EDAC_I7CORE) += i7core_edac.o
obj-$(CONFIG_EDAC_SBRIDGE) += sb_edac.o
-obj-$(CONFIG_EDAC_SKX) += skx_edac.o
obj-$(CONFIG_EDAC_PND2) += pnd2_edac.o
obj-$(CONFIG_EDAC_E7XXX) += e7xxx_edac.o
obj-$(CONFIG_EDAC_E752X) += e752x_edac.o
@@ -58,6 +57,12 @@ obj-$(CONFIG_EDAC_MPC85XX) += mpc85xx_edac_mod.o
layerscape_edac_mod-y := fsl_ddr_edac.o layerscape_edac.o
obj-$(CONFIG_EDAC_LAYERSCAPE) += layerscape_edac_mod.o
+skx_edac-y := skx_common.o skx_base.o
+obj-$(CONFIG_EDAC_SKX) += skx_edac.o
+
+i10nm_edac-y := skx_common.o i10nm_base.o
+obj-$(CONFIG_EDAC_I10NM) += i10nm_edac.o
+
obj-$(CONFIG_EDAC_MV64X60) += mv64x60_edac.o
obj-$(CONFIG_EDAC_CELL) += cell_edac.o
obj-$(CONFIG_EDAC_PPC4XX) += ppc4xx_edac.o
@@ -78,3 +83,4 @@ obj-$(CONFIG_EDAC_SYNOPSYS) += synopsys_edac.o
obj-$(CONFIG_EDAC_XGENE) += xgene_edac.o
obj-$(CONFIG_EDAC_TI) += ti_edac.o
obj-$(CONFIG_EDAC_QCOM) += qcom_edac.o
+obj-$(CONFIG_EDAC_ASPEED) += aspeed_edac.o
diff --git a/drivers/edac/altera_edac.c b/drivers/edac/altera_edac.c
index c89d82aa2776..1bcf9aea0cdf 100644
--- a/drivers/edac/altera_edac.c
+++ b/drivers/edac/altera_edac.c
@@ -29,6 +29,7 @@
#define EDAC_MOD_STR "altera_edac"
#define EDAC_DEVICE "Altera"
+#ifdef CONFIG_EDAC_ALTERA_SDRAM
static const struct altr_sdram_prv_data c5_data = {
.ecc_ctrl_offset = CV_CTLCFG_OFST,
.ecc_ctl_en_mask = CV_CTLCFG_ECC_AUTO_EN,
@@ -468,6 +469,39 @@ static int altr_sdram_remove(struct platform_device *pdev)
return 0;
}
+/*
+ * If you want to suspend, need to disable EDAC by removing it
+ * from the device tree or defconfig.
+ */
+#ifdef CONFIG_PM
+static int altr_sdram_prepare(struct device *dev)
+{
+ pr_err("Suspend not allowed when EDAC is enabled.\n");
+
+ return -EPERM;
+}
+
+static const struct dev_pm_ops altr_sdram_pm_ops = {
+ .prepare = altr_sdram_prepare,
+};
+#endif
+
+static struct platform_driver altr_sdram_edac_driver = {
+ .probe = altr_sdram_probe,
+ .remove = altr_sdram_remove,
+ .driver = {
+ .name = "altr_sdram_edac",
+#ifdef CONFIG_PM
+ .pm = &altr_sdram_pm_ops,
+#endif
+ .of_match_table = altr_sdram_ctrl_of_match,
+ },
+};
+
+module_platform_driver(altr_sdram_edac_driver);
+
+#endif /* CONFIG_EDAC_ALTERA_SDRAM */
+
/**************** Stratix 10 EDAC Memory Controller Functions ************/
/**
@@ -530,37 +564,6 @@ static const struct regmap_config s10_sdram_regmap_cfg = {
/************** </Stratix10 EDAC Memory Controller Functions> ***********/
-/*
- * If you want to suspend, need to disable EDAC by removing it
- * from the device tree or defconfig.
- */
-#ifdef CONFIG_PM
-static int altr_sdram_prepare(struct device *dev)
-{
- pr_err("Suspend not allowed when EDAC is enabled.\n");
-
- return -EPERM;
-}
-
-static const struct dev_pm_ops altr_sdram_pm_ops = {
- .prepare = altr_sdram_prepare,
-};
-#endif
-
-static struct platform_driver altr_sdram_edac_driver = {
- .probe = altr_sdram_probe,
- .remove = altr_sdram_remove,
- .driver = {
- .name = "altr_sdram_edac",
-#ifdef CONFIG_PM
- .pm = &altr_sdram_pm_ops,
-#endif
- .of_match_table = altr_sdram_ctrl_of_match,
- },
-};
-
-module_platform_driver(altr_sdram_edac_driver);
-
/************************* EDAC Parent Probe *************************/
static const struct of_device_id altr_edac_device_of_match[];
@@ -1046,14 +1049,17 @@ altr_init_a10_ecc_block(struct device_node *np, u32 irq_mask,
return -ENODEV;
}
- if (of_address_to_resource(sysmgr_np, 0, &res))
+ if (of_address_to_resource(sysmgr_np, 0, &res)) {
+ of_node_put(sysmgr_np);
return -ENOMEM;
+ }
/* Need physical address for SMCC call */
base = res.start;
ecc_mgr_map = regmap_init(NULL, NULL, (void *)base,
&s10_sdram_regmap_cfg);
+ of_node_put(sysmgr_np);
}
of_node_put(np_eccmgr);
if (IS_ERR(ecc_mgr_map)) {
@@ -2140,11 +2146,13 @@ static int altr_edac_a10_probe(struct platform_device *pdev)
altr_edac_a10_device_add(edac, child);
+#ifdef CONFIG_EDAC_ALTERA_SDRAM
else if ((of_device_is_compatible(child, "altr,sdram-edac-a10")) ||
(of_device_is_compatible(child, "altr,sdram-edac-s10")))
of_platform_populate(pdev->dev.of_node,
altr_sdram_ctrl_of_match,
NULL, &pdev->dev);
+#endif
}
return 0;
diff --git a/drivers/edac/aspeed_edac.c b/drivers/edac/aspeed_edac.c
new file mode 100644
index 000000000000..11833c0a5d07
--- /dev/null
+++ b/drivers/edac/aspeed_edac.c
@@ -0,0 +1,421 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2018, 2019 Cisco Systems
+ */
+
+#include <linux/edac.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/stop_machine.h>
+#include <linux/io.h>
+#include <linux/of_address.h>
+#include <linux/regmap.h>
+#include "edac_module.h"
+
+
+#define DRV_NAME "aspeed-edac"
+
+
+#define ASPEED_MCR_PROT 0x00 /* protection key register */
+#define ASPEED_MCR_CONF 0x04 /* configuration register */
+#define ASPEED_MCR_INTR_CTRL 0x50 /* interrupt control/status register */
+#define ASPEED_MCR_ADDR_UNREC 0x58 /* address of first un-recoverable error */
+#define ASPEED_MCR_ADDR_REC 0x5c /* address of last recoverable error */
+#define ASPEED_MCR_LAST ASPEED_MCR_ADDR_REC
+
+
+#define ASPEED_MCR_PROT_PASSWD 0xfc600309
+#define ASPEED_MCR_CONF_DRAM_TYPE BIT(4)
+#define ASPEED_MCR_CONF_ECC BIT(7)
+#define ASPEED_MCR_INTR_CTRL_CLEAR BIT(31)
+#define ASPEED_MCR_INTR_CTRL_CNT_REC GENMASK(23, 16)
+#define ASPEED_MCR_INTR_CTRL_CNT_UNREC GENMASK(15, 12)
+#define ASPEED_MCR_INTR_CTRL_ENABLE (BIT(0) | BIT(1))
+
+
+static struct regmap *aspeed_regmap;
+
+
+static int regmap_reg_write(void *context, unsigned int reg, unsigned int val)
+{
+ void __iomem *regs = (void __iomem *)context;
+
+ /* enable write to MCR register set */
+ writel(ASPEED_MCR_PROT_PASSWD, regs + ASPEED_MCR_PROT);
+
+ writel(val, regs + reg);
+
+ /* disable write to MCR register set */
+ writel(~ASPEED_MCR_PROT_PASSWD, regs + ASPEED_MCR_PROT);
+
+ return 0;
+}
+
+
+static int regmap_reg_read(void *context, unsigned int reg, unsigned int *val)
+{
+ void __iomem *regs = (void __iomem *)context;
+
+ *val = readl(regs + reg);
+
+ return 0;
+}
+
+static bool regmap_is_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case ASPEED_MCR_PROT:
+ case ASPEED_MCR_INTR_CTRL:
+ case ASPEED_MCR_ADDR_UNREC:
+ case ASPEED_MCR_ADDR_REC:
+ return true;
+ default:
+ return false;
+ }
+}
+
+
+static const struct regmap_config aspeed_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = ASPEED_MCR_LAST,
+ .reg_write = regmap_reg_write,
+ .reg_read = regmap_reg_read,
+ .volatile_reg = regmap_is_volatile,
+ .fast_io = true,
+};
+
+
+static void count_rec(struct mem_ctl_info *mci, u8 rec_cnt, u32 rec_addr)
+{
+ struct csrow_info *csrow = mci->csrows[0];
+ u32 page, offset, syndrome;
+
+ if (!rec_cnt)
+ return;
+
+ /* report first few errors (if there are) */
+ /* note: no addresses are recorded */
+ if (rec_cnt > 1) {
+ /* page, offset and syndrome are not available */
+ page = 0;
+ offset = 0;
+ syndrome = 0;
+ edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, rec_cnt-1,
+ page, offset, syndrome, 0, 0, -1,
+ "address(es) not available", "");
+ }
+
+ /* report last error */
+ /* note: rec_addr is the last recoverable error addr */
+ page = rec_addr >> PAGE_SHIFT;
+ offset = rec_addr & ~PAGE_MASK;
+ /* syndrome is not available */
+ syndrome = 0;
+ edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
+ csrow->first_page + page, offset, syndrome,
+ 0, 0, -1, "", "");
+}
+
+
+static void count_un_rec(struct mem_ctl_info *mci, u8 un_rec_cnt,
+ u32 un_rec_addr)
+{
+ struct csrow_info *csrow = mci->csrows[0];
+ u32 page, offset, syndrome;
+
+ if (!un_rec_cnt)
+ return;
+
+ /* report 1. error */
+ /* note: un_rec_addr is the first unrecoverable error addr */
+ page = un_rec_addr >> PAGE_SHIFT;
+ offset = un_rec_addr & ~PAGE_MASK;
+ /* syndrome is not available */
+ syndrome = 0;
+ edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
+ csrow->first_page + page, offset, syndrome,
+ 0, 0, -1, "", "");
+
+ /* report further errors (if there are) */
+ /* note: no addresses are recorded */
+ if (un_rec_cnt > 1) {
+ /* page, offset and syndrome are not available */
+ page = 0;
+ offset = 0;
+ syndrome = 0;
+ edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, un_rec_cnt-1,
+ page, offset, syndrome, 0, 0, -1,
+ "address(es) not available", "");
+ }
+}
+
+
+static irqreturn_t mcr_isr(int irq, void *arg)
+{
+ struct mem_ctl_info *mci = arg;
+ u32 rec_addr, un_rec_addr;
+ u32 reg50, reg5c, reg58;
+ u8 rec_cnt, un_rec_cnt;
+
+ regmap_read(aspeed_regmap, ASPEED_MCR_INTR_CTRL, &reg50);
+ dev_dbg(mci->pdev, "received edac interrupt w/ mcr register 50: 0x%x\n",
+ reg50);
+
+ /* collect data about recoverable and unrecoverable errors */
+ rec_cnt = (reg50 & ASPEED_MCR_INTR_CTRL_CNT_REC) >> 16;
+ un_rec_cnt = (reg50 & ASPEED_MCR_INTR_CTRL_CNT_UNREC) >> 12;
+
+ dev_dbg(mci->pdev, "%d recoverable interrupts and %d unrecoverable interrupts\n",
+ rec_cnt, un_rec_cnt);
+
+ regmap_read(aspeed_regmap, ASPEED_MCR_ADDR_UNREC, &reg58);
+ un_rec_addr = reg58;
+
+ regmap_read(aspeed_regmap, ASPEED_MCR_ADDR_REC, &reg5c);
+ rec_addr = reg5c;
+
+ /* clear interrupt flags and error counters: */
+ regmap_update_bits(aspeed_regmap, ASPEED_MCR_INTR_CTRL,
+ ASPEED_MCR_INTR_CTRL_CLEAR,
+ ASPEED_MCR_INTR_CTRL_CLEAR);
+
+ regmap_update_bits(aspeed_regmap, ASPEED_MCR_INTR_CTRL,
+ ASPEED_MCR_INTR_CTRL_CLEAR, 0);
+
+ /* process recoverable and unrecoverable errors */
+ count_rec(mci, rec_cnt, rec_addr);
+ count_un_rec(mci, un_rec_cnt, un_rec_addr);
+
+ if (!rec_cnt && !un_rec_cnt)
+ dev_dbg(mci->pdev, "received edac interrupt, but did not find any ECC counters\n");
+
+ regmap_read(aspeed_regmap, ASPEED_MCR_INTR_CTRL, &reg50);
+ dev_dbg(mci->pdev, "edac interrupt handled. mcr reg 50 is now: 0x%x\n",
+ reg50);
+
+ return IRQ_HANDLED;
+}
+
+
+static int config_irq(void *ctx, struct platform_device *pdev)
+{
+ int irq;
+ int rc;
+
+ /* register interrupt handler */
+ irq = platform_get_irq(pdev, 0);
+ dev_dbg(&pdev->dev, "got irq %d\n", irq);
+ if (!irq)
+ return -ENODEV;
+
+ rc = devm_request_irq(&pdev->dev, irq, mcr_isr, IRQF_TRIGGER_HIGH,
+ DRV_NAME, ctx);
+ if (rc) {
+ dev_err(&pdev->dev, "unable to request irq %d\n", irq);
+ return rc;
+ }
+
+ /* enable interrupts */
+ regmap_update_bits(aspeed_regmap, ASPEED_MCR_INTR_CTRL,
+ ASPEED_MCR_INTR_CTRL_ENABLE,
+ ASPEED_MCR_INTR_CTRL_ENABLE);
+
+ return 0;
+}
+
+
+static int init_csrows(struct mem_ctl_info *mci)
+{
+ struct csrow_info *csrow = mci->csrows[0];
+ u32 nr_pages, dram_type;
+ struct dimm_info *dimm;
+ struct device_node *np;
+ struct resource r;
+ u32 reg04;
+ int rc;
+
+ /* retrieve info about physical memory from device tree */
+ np = of_find_node_by_path("/memory");
+ if (!np) {
+ dev_err(mci->pdev, "dt: missing /memory node\n");
+ return -ENODEV;
+ };
+
+ rc = of_address_to_resource(np, 0, &r);
+
+ of_node_put(np);
+
+ if (rc) {
+ dev_err(mci->pdev, "dt: failed requesting resource for /memory node\n");
+ return rc;
+ };
+
+ dev_dbg(mci->pdev, "dt: /memory node resources: first page r.start=0x%x, resource_size=0x%x, PAGE_SHIFT macro=0x%x\n",
+ r.start, resource_size(&r), PAGE_SHIFT);
+
+ csrow->first_page = r.start >> PAGE_SHIFT;
+ nr_pages = resource_size(&r) >> PAGE_SHIFT;
+ csrow->last_page = csrow->first_page + nr_pages - 1;
+
+ regmap_read(aspeed_regmap, ASPEED_MCR_CONF, &reg04);
+ dram_type = (reg04 & ASPEED_MCR_CONF_DRAM_TYPE) ? MEM_DDR4 : MEM_DDR3;
+
+ dimm = csrow->channels[0]->dimm;
+ dimm->mtype = dram_type;
+ dimm->edac_mode = EDAC_SECDED;
+ dimm->nr_pages = nr_pages / csrow->nr_channels;
+
+ dev_dbg(mci->pdev, "initialized dimm with first_page=0x%lx and nr_pages=0x%x\n",
+ csrow->first_page, nr_pages);
+
+ return 0;
+}
+
+
+static int aspeed_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct edac_mc_layer layers[2];
+ struct mem_ctl_info *mci;
+ struct device_node *np;
+ struct resource *res;
+ void __iomem *regs;
+ u32 reg04;
+ int rc;
+
+ /* setup regmap */
+ np = dev->of_node;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENOENT;
+
+ regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ aspeed_regmap = devm_regmap_init(dev, NULL, (__force void *)regs,
+ &aspeed_regmap_config);
+ if (IS_ERR(aspeed_regmap))
+ return PTR_ERR(aspeed_regmap);
+
+ /* bail out if ECC mode is not configured */
+ regmap_read(aspeed_regmap, ASPEED_MCR_CONF, &reg04);
+ if (!(reg04 & ASPEED_MCR_CONF_ECC)) {
+ dev_err(&pdev->dev, "ECC mode is not configured in u-boot\n");
+ return -EPERM;
+ }
+
+ edac_op_state = EDAC_OPSTATE_INT;
+
+ /* allocate & init EDAC MC data structure */
+ layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
+ layers[0].size = 1;
+ layers[0].is_virt_csrow = true;
+ layers[1].type = EDAC_MC_LAYER_CHANNEL;
+ layers[1].size = 1;
+ layers[1].is_virt_csrow = false;
+
+ mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, 0);
+ if (!mci)
+ return -ENOMEM;
+
+ mci->pdev = &pdev->dev;
+ mci->mtype_cap = MEM_FLAG_DDR3 | MEM_FLAG_DDR4;
+ mci->edac_ctl_cap = EDAC_FLAG_SECDED;
+ mci->edac_cap = EDAC_FLAG_SECDED;
+ mci->scrub_cap = SCRUB_FLAG_HW_SRC;
+ mci->scrub_mode = SCRUB_HW_SRC;
+ mci->mod_name = DRV_NAME;
+ mci->ctl_name = "MIC";
+ mci->dev_name = dev_name(&pdev->dev);
+
+ rc = init_csrows(mci);
+ if (rc) {
+ dev_err(&pdev->dev, "failed to init csrows\n");
+ goto probe_exit02;
+ }
+
+ platform_set_drvdata(pdev, mci);
+
+ /* register with edac core */
+ rc = edac_mc_add_mc(mci);
+ if (rc) {
+ dev_err(&pdev->dev, "failed to register with EDAC core\n");
+ goto probe_exit02;
+ }
+
+ /* register interrupt handler and enable interrupts */
+ rc = config_irq(mci, pdev);
+ if (rc) {
+ dev_err(&pdev->dev, "failed setting up irq\n");
+ goto probe_exit01;
+ }
+
+ return 0;
+
+probe_exit01:
+ edac_mc_del_mc(&pdev->dev);
+probe_exit02:
+ edac_mc_free(mci);
+ return rc;
+}
+
+
+static int aspeed_remove(struct platform_device *pdev)
+{
+ struct mem_ctl_info *mci;
+
+ /* disable interrupts */
+ regmap_update_bits(aspeed_regmap, ASPEED_MCR_INTR_CTRL,
+ ASPEED_MCR_INTR_CTRL_ENABLE, 0);
+
+ /* free resources */
+ mci = edac_mc_del_mc(&pdev->dev);
+ if (mci)
+ edac_mc_free(mci);
+
+ return 0;
+}
+
+
+static const struct of_device_id aspeed_of_match[] = {
+ { .compatible = "aspeed,ast2500-sdram-edac" },
+ {},
+};
+
+
+static struct platform_driver aspeed_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .of_match_table = aspeed_of_match
+ },
+ .probe = aspeed_probe,
+ .remove = aspeed_remove
+};
+
+
+static int __init aspeed_init(void)
+{
+ return platform_driver_register(&aspeed_driver);
+}
+
+
+static void __exit aspeed_exit(void)
+{
+ platform_driver_unregister(&aspeed_driver);
+}
+
+
+module_init(aspeed_init);
+module_exit(aspeed_exit);
+
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Stefan Schaeckeler <sschaeck@cisco.com>");
+MODULE_DESCRIPTION("Aspeed AST2500 EDAC driver");
+MODULE_VERSION("1.0");
diff --git a/drivers/edac/debugfs.c b/drivers/edac/debugfs.c
index 92dbb7e2320c..0a9277228c50 100644
--- a/drivers/edac/debugfs.c
+++ b/drivers/edac/debugfs.c
@@ -41,14 +41,9 @@ static const struct file_operations debug_fake_inject_fops = {
.llseek = generic_file_llseek,
};
-int __init edac_debugfs_init(void)
+void __init edac_debugfs_init(void)
{
edac_debugfs = debugfs_create_dir("edac", NULL);
- if (IS_ERR(edac_debugfs)) {
- edac_debugfs = NULL;
- return -ENOMEM;
- }
- return 0;
}
void edac_debugfs_exit(void)
@@ -56,50 +51,31 @@ void edac_debugfs_exit(void)
debugfs_remove_recursive(edac_debugfs);
}
-int edac_create_debugfs_nodes(struct mem_ctl_info *mci)
+void edac_create_debugfs_nodes(struct mem_ctl_info *mci)
{
- struct dentry *d, *parent;
+ struct dentry *parent;
char name[80];
int i;
- if (!edac_debugfs)
- return -ENODEV;
-
- d = debugfs_create_dir(mci->dev.kobj.name, edac_debugfs);
- if (!d)
- return -ENOMEM;
- parent = d;
+ parent = debugfs_create_dir(mci->dev.kobj.name, edac_debugfs);
for (i = 0; i < mci->n_layers; i++) {
sprintf(name, "fake_inject_%s",
edac_layer_name[mci->layers[i].type]);
- d = debugfs_create_u8(name, S_IRUGO | S_IWUSR, parent,
- &mci->fake_inject_layer[i]);
- if (!d)
- goto nomem;
+ debugfs_create_u8(name, S_IRUGO | S_IWUSR, parent,
+ &mci->fake_inject_layer[i]);
}
- d = debugfs_create_bool("fake_inject_ue", S_IRUGO | S_IWUSR, parent,
- &mci->fake_inject_ue);
- if (!d)
- goto nomem;
+ debugfs_create_bool("fake_inject_ue", S_IRUGO | S_IWUSR, parent,
+ &mci->fake_inject_ue);
- d = debugfs_create_u16("fake_inject_count", S_IRUGO | S_IWUSR, parent,
- &mci->fake_inject_count);
- if (!d)
- goto nomem;
+ debugfs_create_u16("fake_inject_count", S_IRUGO | S_IWUSR, parent,
+ &mci->fake_inject_count);
- d = debugfs_create_file("fake_inject", S_IWUSR, parent,
- &mci->dev,
- &debug_fake_inject_fops);
- if (!d)
- goto nomem;
+ debugfs_create_file("fake_inject", S_IWUSR, parent, &mci->dev,
+ &debug_fake_inject_fops);
mci->debugfs = parent;
- return 0;
-nomem:
- edac_debugfs_remove_recursive(mci->debugfs);
- return -ENOMEM;
}
/* Create a toplevel dir under EDAC's debugfs hierarchy */
diff --git a/drivers/edac/edac_module.h b/drivers/edac/edac_module.h
index dec88dcea036..dd7d0b509aa3 100644
--- a/drivers/edac/edac_module.h
+++ b/drivers/edac/edac_module.h
@@ -69,9 +69,9 @@ extern void *edac_align_ptr(void **p, unsigned size, int n_elems);
#define edac_debugfs_remove_recursive debugfs_remove_recursive
#define edac_debugfs_remove debugfs_remove
#ifdef CONFIG_EDAC_DEBUG
-int edac_debugfs_init(void);
+void edac_debugfs_init(void);
void edac_debugfs_exit(void);
-int edac_create_debugfs_nodes(struct mem_ctl_info *mci);
+void edac_create_debugfs_nodes(struct mem_ctl_info *mci);
struct dentry *edac_debugfs_create_dir(const char *dirname);
struct dentry *
edac_debugfs_create_dir_at(const char *dirname, struct dentry *parent);
@@ -83,9 +83,9 @@ edac_debugfs_create_x8(const char *name, umode_t mode, struct dentry *parent, u8
struct dentry *
edac_debugfs_create_x16(const char *name, umode_t mode, struct dentry *parent, u16 *value);
#else
-static inline int edac_debugfs_init(void) { return -ENODEV; }
+static inline void edac_debugfs_init(void) { }
static inline void edac_debugfs_exit(void) { }
-static inline int edac_create_debugfs_nodes(struct mem_ctl_info *mci) { return 0; }
+static inline void edac_create_debugfs_nodes(struct mem_ctl_info *mci) { }
static inline struct dentry *edac_debugfs_create_dir(const char *dirname) { return NULL; }
static inline struct dentry *
edac_debugfs_create_dir_at(const char *dirname, struct dentry *parent) { return NULL; }
diff --git a/drivers/edac/i10nm_base.c b/drivers/edac/i10nm_base.c
new file mode 100644
index 000000000000..c334fb7c63df
--- /dev/null
+++ b/drivers/edac/i10nm_base.c
@@ -0,0 +1,275 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for Intel(R) 10nm server memory controller.
+ * Copyright (c) 2019, Intel Corporation.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <asm/cpu_device_id.h>
+#include <asm/intel-family.h>
+#include <asm/mce.h>
+#include "edac_module.h"
+#include "skx_common.h"
+
+#define I10NM_REVISION "v0.0.3"
+#define EDAC_MOD_STR "i10nm_edac"
+
+/* Debug macros */
+#define i10nm_printk(level, fmt, arg...) \
+ edac_printk(level, "i10nm", fmt, ##arg)
+
+#define I10NM_GET_SCK_BAR(d, reg) \
+ pci_read_config_dword((d)->uracu, 0xd0, &(reg))
+#define I10NM_GET_IMC_BAR(d, i, reg) \
+ pci_read_config_dword((d)->uracu, 0xd8 + (i) * 4, &(reg))
+#define I10NM_GET_DIMMMTR(m, i, j) \
+ (*(u32 *)((m)->mbase + 0x2080c + (i) * 0x4000 + (j) * 4))
+#define I10NM_GET_MCDDRTCFG(m, i, j) \
+ (*(u32 *)((m)->mbase + 0x20970 + (i) * 0x4000 + (j) * 4))
+
+#define I10NM_GET_SCK_MMIO_BASE(reg) (GET_BITFIELD(reg, 0, 28) << 23)
+#define I10NM_GET_IMC_MMIO_OFFSET(reg) (GET_BITFIELD(reg, 0, 10) << 12)
+#define I10NM_GET_IMC_MMIO_SIZE(reg) ((GET_BITFIELD(reg, 13, 23) - \
+ GET_BITFIELD(reg, 0, 10) + 1) << 12)
+
+static struct list_head *i10nm_edac_list;
+
+static struct pci_dev *pci_get_dev_wrapper(int dom, unsigned int bus,
+ unsigned int dev, unsigned int fun)
+{
+ struct pci_dev *pdev;
+
+ pdev = pci_get_domain_bus_and_slot(dom, bus, PCI_DEVFN(dev, fun));
+ if (!pdev) {
+ edac_dbg(2, "No device %02x:%02x.%x\n",
+ bus, dev, fun);
+ return NULL;
+ }
+
+ if (unlikely(pci_enable_device(pdev) < 0)) {
+ edac_dbg(2, "Failed to enable device %02x:%02x.%x\n",
+ bus, dev, fun);
+ return NULL;
+ }
+
+ pci_dev_get(pdev);
+
+ return pdev;
+}
+
+static int i10nm_get_all_munits(void)
+{
+ struct pci_dev *mdev;
+ void __iomem *mbase;
+ unsigned long size;
+ struct skx_dev *d;
+ int i, j = 0;
+ u32 reg, off;
+ u64 base;
+
+ list_for_each_entry(d, i10nm_edac_list, list) {
+ d->util_all = pci_get_dev_wrapper(d->seg, d->bus[1], 29, 1);
+ if (!d->util_all)
+ return -ENODEV;
+
+ d->uracu = pci_get_dev_wrapper(d->seg, d->bus[0], 0, 1);
+ if (!d->uracu)
+ return -ENODEV;
+
+ if (I10NM_GET_SCK_BAR(d, reg)) {
+ i10nm_printk(KERN_ERR, "Failed to socket bar\n");
+ return -ENODEV;
+ }
+
+ base = I10NM_GET_SCK_MMIO_BASE(reg);
+ edac_dbg(2, "socket%d mmio base 0x%llx (reg 0x%x)\n",
+ j++, base, reg);
+
+ for (i = 0; i < I10NM_NUM_IMC; i++) {
+ mdev = pci_get_dev_wrapper(d->seg, d->bus[0],
+ 12 + i, 0);
+ if (i == 0 && !mdev) {
+ i10nm_printk(KERN_ERR, "No IMC found\n");
+ return -ENODEV;
+ }
+ if (!mdev)
+ continue;
+
+ d->imc[i].mdev = mdev;
+
+ if (I10NM_GET_IMC_BAR(d, i, reg)) {
+ i10nm_printk(KERN_ERR, "Failed to get mc bar\n");
+ return -ENODEV;
+ }
+
+ off = I10NM_GET_IMC_MMIO_OFFSET(reg);
+ size = I10NM_GET_IMC_MMIO_SIZE(reg);
+ edac_dbg(2, "mc%d mmio base 0x%llx size 0x%lx (reg 0x%x)\n",
+ i, base + off, size, reg);
+
+ mbase = ioremap(base + off, size);
+ if (!mbase) {
+ i10nm_printk(KERN_ERR, "Failed to ioremap 0x%llx\n",
+ base + off);
+ return -ENODEV;
+ }
+
+ d->imc[i].mbase = mbase;
+ }
+ }
+
+ return 0;
+}
+
+static const struct x86_cpu_id i10nm_cpuids[] = {
+ { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_TREMONT_X, 0, 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(x86cpu, i10nm_cpuids);
+
+static bool i10nm_check_ecc(struct skx_imc *imc, int chan)
+{
+ u32 mcmtr;
+
+ mcmtr = *(u32 *)(imc->mbase + 0x20ef8 + chan * 0x4000);
+ edac_dbg(1, "ch%d mcmtr reg %x\n", chan, mcmtr);
+
+ return !!GET_BITFIELD(mcmtr, 2, 2);
+}
+
+static int i10nm_get_dimm_config(struct mem_ctl_info *mci)
+{
+ struct skx_pvt *pvt = mci->pvt_info;
+ struct skx_imc *imc = pvt->imc;
+ struct dimm_info *dimm;
+ u32 mtr, mcddrtcfg;
+ int i, j, ndimms;
+
+ for (i = 0; i < I10NM_NUM_CHANNELS; i++) {
+ if (!imc->mbase)
+ continue;
+
+ ndimms = 0;
+ for (j = 0; j < I10NM_NUM_DIMMS; j++) {
+ dimm = EDAC_DIMM_PTR(mci->layers, mci->dimms,
+ mci->n_layers, i, j, 0);
+ mtr = I10NM_GET_DIMMMTR(imc, i, j);
+ mcddrtcfg = I10NM_GET_MCDDRTCFG(imc, i, j);
+ edac_dbg(1, "dimmmtr 0x%x mcddrtcfg 0x%x (mc%d ch%d dimm%d)\n",
+ mtr, mcddrtcfg, imc->mc, i, j);
+
+ if (IS_DIMM_PRESENT(mtr))
+ ndimms += skx_get_dimm_info(mtr, 0, dimm,
+ imc, i, j);
+ else if (IS_NVDIMM_PRESENT(mcddrtcfg, j))
+ ndimms += skx_get_nvdimm_info(dimm, imc, i, j,
+ EDAC_MOD_STR);
+ }
+ if (ndimms && !i10nm_check_ecc(imc, 0)) {
+ i10nm_printk(KERN_ERR, "ECC is disabled on imc %d\n",
+ imc->mc);
+ return -ENODEV;
+ }
+ }
+
+ return 0;
+}
+
+static struct notifier_block i10nm_mce_dec = {
+ .notifier_call = skx_mce_check_error,
+ .priority = MCE_PRIO_EDAC,
+};
+
+static int __init i10nm_init(void)
+{
+ u8 mc = 0, src_id = 0, node_id = 0;
+ const struct x86_cpu_id *id;
+ const char *owner;
+ struct skx_dev *d;
+ int rc, i, off[3] = {0xd0, 0xc8, 0xcc};
+ u64 tolm, tohm;
+
+ edac_dbg(2, "\n");
+
+ owner = edac_get_owner();
+ if (owner && strncmp(owner, EDAC_MOD_STR, sizeof(EDAC_MOD_STR)))
+ return -EBUSY;
+
+ id = x86_match_cpu(i10nm_cpuids);
+ if (!id)
+ return -ENODEV;
+
+ rc = skx_get_hi_lo(0x09a2, off, &tolm, &tohm);
+ if (rc)
+ return rc;
+
+ rc = skx_get_all_bus_mappings(0x3452, 0xcc, I10NM, &i10nm_edac_list);
+ if (rc < 0)
+ goto fail;
+ if (rc == 0) {
+ i10nm_printk(KERN_ERR, "No memory controllers found\n");
+ return -ENODEV;
+ }
+
+ rc = i10nm_get_all_munits();
+ if (rc < 0)
+ goto fail;
+
+ list_for_each_entry(d, i10nm_edac_list, list) {
+ rc = skx_get_src_id(d, &src_id);
+ if (rc < 0)
+ goto fail;
+
+ rc = skx_get_node_id(d, &node_id);
+ if (rc < 0)
+ goto fail;
+
+ edac_dbg(2, "src_id = %d node_id = %d\n", src_id, node_id);
+ for (i = 0; i < I10NM_NUM_IMC; i++) {
+ if (!d->imc[i].mdev)
+ continue;
+
+ d->imc[i].mc = mc++;
+ d->imc[i].lmc = i;
+ d->imc[i].src_id = src_id;
+ d->imc[i].node_id = node_id;
+
+ rc = skx_register_mci(&d->imc[i], d->imc[i].mdev,
+ "Intel_10nm Socket", EDAC_MOD_STR,
+ i10nm_get_dimm_config);
+ if (rc < 0)
+ goto fail;
+ }
+ }
+
+ rc = skx_adxl_get();
+ if (rc)
+ goto fail;
+
+ opstate_init();
+ mce_register_decode_chain(&i10nm_mce_dec);
+ setup_skx_debug("i10nm_test");
+
+ i10nm_printk(KERN_INFO, "%s\n", I10NM_REVISION);
+
+ return 0;
+fail:
+ skx_remove();
+ return rc;
+}
+
+static void __exit i10nm_exit(void)
+{
+ edac_dbg(2, "\n");
+ teardown_skx_debug();
+ mce_unregister_decode_chain(&i10nm_mce_dec);
+ skx_adxl_put();
+ skx_remove();
+}
+
+module_init(i10nm_init);
+module_exit(i10nm_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MC Driver for Intel 10nm server processors");
diff --git a/drivers/edac/mce_amd.c b/drivers/edac/mce_amd.c
index c605089d899f..0a1814dad6cf 100644
--- a/drivers/edac/mce_amd.c
+++ b/drivers/edac/mce_amd.c
@@ -151,138 +151,223 @@ static const char * const mc6_mce_desc[] = {
/* Scalable MCA error strings */
static const char * const smca_ls_mce_desc[] = {
- "Load queue parity",
- "Store queue parity",
- "Miss address buffer payload parity",
- "L1 TLB parity",
- "Reserved",
- "DC tag error type 6",
- "DC tag error type 1",
+ "Load queue parity error",
+ "Store queue parity error",
+ "Miss address buffer payload parity error",
+ "Level 1 TLB parity error",
+ "DC Tag error type 5",
+ "DC Tag error type 6",
+ "DC Tag error type 1",
"Internal error type 1",
"Internal error type 2",
- "Sys Read data error thread 0",
- "Sys read data error thread 1",
- "DC tag error type 2",
- "DC data error type 1 (poison consumption)",
- "DC data error type 2",
- "DC data error type 3",
- "DC tag error type 4",
- "L2 TLB parity",
+ "System Read Data Error Thread 0",
+ "System Read Data Error Thread 1",
+ "DC Tag error type 2",
+ "DC Data error type 1 and poison consumption",
+ "DC Data error type 2",
+ "DC Data error type 3",
+ "DC Tag error type 4",
+ "Level 2 TLB parity error",
"PDC parity error",
- "DC tag error type 3",
- "DC tag error type 5",
- "L2 fill data error",
+ "DC Tag error type 3",
+ "DC Tag error type 5",
+ "L2 Fill Data error",
};
static const char * const smca_if_mce_desc[] = {
- "microtag probe port parity error",
- "IC microtag or full tag multi-hit error",
- "IC full tag parity",
- "IC data array parity",
- "Decoupling queue phys addr parity error",
- "L0 ITLB parity error",
- "L1 ITLB parity error",
- "L2 ITLB parity error",
- "BPQ snoop parity on Thread 0",
- "BPQ snoop parity on Thread 1",
- "L1 BTB multi-match error",
- "L2 BTB multi-match error",
- "L2 Cache Response Poison error",
- "System Read Data error",
+ "Op Cache Microtag Probe Port Parity Error",
+ "IC Microtag or Full Tag Multi-hit Error",
+ "IC Full Tag Parity Error",
+ "IC Data Array Parity Error",
+ "Decoupling Queue PhysAddr Parity Error",
+ "L0 ITLB Parity Error",
+ "L1 ITLB Parity Error",
+ "L2 ITLB Parity Error",
+ "BPQ Thread 0 Snoop Parity Error",
+ "BPQ Thread 1 Snoop Parity Error",
+ "L1 BTB Multi-Match Error",
+ "L2 BTB Multi-Match Error",
+ "L2 Cache Response Poison Error",
+ "System Read Data Error",
};
static const char * const smca_l2_mce_desc[] = {
- "L2M tag multi-way-hit error",
- "L2M tag ECC error",
- "L2M data ECC error",
- "HW assert",
+ "L2M Tag Multiple-Way-Hit error",
+ "L2M Tag or State Array ECC Error",
+ "L2M Data Array ECC Error",
+ "Hardware Assert Error",
};
static const char * const smca_de_mce_desc[] = {
- "uop cache tag parity error",
- "uop cache data parity error",
- "Insn buffer parity error",
- "uop queue parity error",
- "Insn dispatch queue parity error",
- "Fetch address FIFO parity",
- "Patch RAM data parity",
- "Patch RAM sequencer parity",
- "uop buffer parity"
+ "Micro-op cache tag parity error",
+ "Micro-op cache data parity error",
+ "Instruction buffer parity error",
+ "Micro-op queue parity error",
+ "Instruction dispatch queue parity error",
+ "Fetch address FIFO parity error",
+ "Patch RAM data parity error",
+ "Patch RAM sequencer parity error",
+ "Micro-op buffer parity error"
};
static const char * const smca_ex_mce_desc[] = {
- "Watchdog timeout error",
- "Phy register file parity",
- "Flag register file parity",
- "Immediate displacement register file parity",
- "Address generator payload parity",
- "EX payload parity",
- "Checkpoint queue parity",
- "Retire dispatch queue parity",
+ "Watchdog Timeout error",
+ "Physical register file parity error",
+ "Flag register file parity error",
+ "Immediate displacement register file parity error",
+ "Address generator payload parity error",
+ "EX payload parity error",
+ "Checkpoint queue parity error",
+ "Retire dispatch queue parity error",
"Retire status queue parity error",
"Scheduling queue parity error",
"Branch buffer queue parity error",
+ "Hardware Assertion error",
};
static const char * const smca_fp_mce_desc[] = {
- "Physical register file parity",
- "Freelist parity error",
- "Schedule queue parity",
+ "Physical register file (PRF) parity error",
+ "Freelist (FL) parity error",
+ "Schedule queue parity error",
"NSQ parity error",
- "Retire queue parity",
- "Status register file parity",
+ "Retire queue (RQ) parity error",
+ "Status register file (SRF) parity error",
"Hardware assertion",
};
static const char * const smca_l3_mce_desc[] = {
- "Shadow tag macro ECC error",
- "Shadow tag macro multi-way-hit error",
- "L3M tag ECC error",
- "L3M tag multi-way-hit error",
- "L3M data ECC error",
- "XI parity, L3 fill done channel error",
- "L3 victim queue parity",
- "L3 HW assert",
+ "Shadow Tag Macro ECC Error",
+ "Shadow Tag Macro Multi-way-hit Error",
+ "L3M Tag ECC Error",
+ "L3M Tag Multi-way-hit Error",
+ "L3M Data ECC Error",
+ "SDP Parity Error or SystemReadDataError from XI",
+ "L3 Victim Queue Parity Error",
+ "L3 Hardware Assertion",
};
static const char * const smca_cs_mce_desc[] = {
- "Illegal request from transport layer",
- "Address violation",
- "Security violation",
- "Illegal response from transport layer",
- "Unexpected response",
- "Parity error on incoming request or probe response data",
- "Parity error on incoming read response data",
- "Atomic request parity",
- "ECC error on probe filter access",
+ "Illegal Request",
+ "Address Violation",
+ "Security Violation",
+ "Illegal Response",
+ "Unexpected Response",
+ "Request or Probe Parity Error",
+ "Read Response Parity Error",
+ "Atomic Request Parity Error",
+ "Probe Filter ECC Error",
+};
+
+static const char * const smca_cs2_mce_desc[] = {
+ "Illegal Request",
+ "Address Violation",
+ "Security Violation",
+ "Illegal Response",
+ "Unexpected Response",
+ "Request or Probe Parity Error",
+ "Read Response Parity Error",
+ "Atomic Request Parity Error",
+ "SDP read response had no match in the CS queue",
+ "Probe Filter Protocol Error",
+ "Probe Filter ECC Error",
+ "SDP read response had an unexpected RETRY error",
+ "Counter overflow error",
+ "Counter underflow error",
};
static const char * const smca_pie_mce_desc[] = {
- "HW assert",
- "Internal PIE register security violation",
- "Error on GMI link",
- "Poison data written to internal PIE register",
+ "Hardware Assert",
+ "Register security violation",
+ "Link Error",
+ "Poison data consumption",
+ "A deferred error was detected in the DF"
};
static const char * const smca_umc_mce_desc[] = {
"DRAM ECC error",
- "Data poison error on DRAM",
+ "Data poison error",
"SDP parity error",
"Advanced peripheral bus error",
- "Command/address parity error",
+ "Address/Command parity error",
"Write data CRC error",
+ "DCQ SRAM ECC error",
+ "AES SRAM ECC error",
};
static const char * const smca_pb_mce_desc[] = {
- "Parameter Block RAM ECC error",
+ "An ECC error in the Parameter Block RAM array",
};
static const char * const smca_psp_mce_desc[] = {
- "PSP RAM ECC or parity error",
+ "An ECC or parity error in a PSP RAM instance",
+};
+
+static const char * const smca_psp2_mce_desc[] = {
+ "High SRAM ECC or parity error",
+ "Low SRAM ECC or parity error",
+ "Instruction Cache Bank 0 ECC or parity error",
+ "Instruction Cache Bank 1 ECC or parity error",
+ "Instruction Tag Ram 0 parity error",
+ "Instruction Tag Ram 1 parity error",
+ "Data Cache Bank 0 ECC or parity error",
+ "Data Cache Bank 1 ECC or parity error",
+ "Data Cache Bank 2 ECC or parity error",
+ "Data Cache Bank 3 ECC or parity error",
+ "Data Tag Bank 0 parity error",
+ "Data Tag Bank 1 parity error",
+ "Data Tag Bank 2 parity error",
+ "Data Tag Bank 3 parity error",
+ "Dirty Data Ram parity error",
+ "TLB Bank 0 parity error",
+ "TLB Bank 1 parity error",
+ "System Hub Read Buffer ECC or parity error",
};
static const char * const smca_smu_mce_desc[] = {
- "SMU RAM ECC or parity error",
+ "An ECC or parity error in an SMU RAM instance",
+};
+
+static const char * const smca_smu2_mce_desc[] = {
+ "High SRAM ECC or parity error",
+ "Low SRAM ECC or parity error",
+ "Data Cache Bank A ECC or parity error",
+ "Data Cache Bank B ECC or parity error",
+ "Data Tag Cache Bank A ECC or parity error",
+ "Data Tag Cache Bank B ECC or parity error",
+ "Instruction Cache Bank A ECC or parity error",
+ "Instruction Cache Bank B ECC or parity error",
+ "Instruction Tag Cache Bank A ECC or parity error",
+ "Instruction Tag Cache Bank B ECC or parity error",
+ "System Hub Read Buffer ECC or parity error",
+};
+
+static const char * const smca_mp5_mce_desc[] = {
+ "High SRAM ECC or parity error",
+ "Low SRAM ECC or parity error",
+ "Data Cache Bank A ECC or parity error",
+ "Data Cache Bank B ECC or parity error",
+ "Data Tag Cache Bank A ECC or parity error",
+ "Data Tag Cache Bank B ECC or parity error",
+ "Instruction Cache Bank A ECC or parity error",
+ "Instruction Cache Bank B ECC or parity error",
+ "Instruction Tag Cache Bank A ECC or parity error",
+ "Instruction Tag Cache Bank B ECC or parity error",
+};
+
+static const char * const smca_nbio_mce_desc[] = {
+ "ECC or Parity error",
+ "PCIE error",
+ "SDP ErrEvent error",
+ "SDP Egress Poison Error",
+ "IOHC Internal Poison Error",
+};
+
+static const char * const smca_pcie_mce_desc[] = {
+ "CCIX PER Message logging",
+ "CCIX Read Response with Status: Non-Data Error",
+ "CCIX Write Response with Status: Non-Data Error",
+ "CCIX Read Response with Status: Data Error",
+ "CCIX Non-okay write response with data error",
};
struct smca_mce_desc {
@@ -299,11 +384,17 @@ static struct smca_mce_desc smca_mce_descs[] = {
[SMCA_FP] = { smca_fp_mce_desc, ARRAY_SIZE(smca_fp_mce_desc) },
[SMCA_L3_CACHE] = { smca_l3_mce_desc, ARRAY_SIZE(smca_l3_mce_desc) },
[SMCA_CS] = { smca_cs_mce_desc, ARRAY_SIZE(smca_cs_mce_desc) },
+ [SMCA_CS_V2] = { smca_cs2_mce_desc, ARRAY_SIZE(smca_cs2_mce_desc) },
[SMCA_PIE] = { smca_pie_mce_desc, ARRAY_SIZE(smca_pie_mce_desc) },
[SMCA_UMC] = { smca_umc_mce_desc, ARRAY_SIZE(smca_umc_mce_desc) },
[SMCA_PB] = { smca_pb_mce_desc, ARRAY_SIZE(smca_pb_mce_desc) },
[SMCA_PSP] = { smca_psp_mce_desc, ARRAY_SIZE(smca_psp_mce_desc) },
+ [SMCA_PSP_V2] = { smca_psp2_mce_desc, ARRAY_SIZE(smca_psp2_mce_desc) },
[SMCA_SMU] = { smca_smu_mce_desc, ARRAY_SIZE(smca_smu_mce_desc) },
+ [SMCA_SMU_V2] = { smca_smu2_mce_desc, ARRAY_SIZE(smca_smu2_mce_desc) },
+ [SMCA_MP5] = { smca_mp5_mce_desc, ARRAY_SIZE(smca_mp5_mce_desc) },
+ [SMCA_NBIO] = { smca_nbio_mce_desc, ARRAY_SIZE(smca_nbio_mce_desc) },
+ [SMCA_PCIE] = { smca_pcie_mce_desc, ARRAY_SIZE(smca_pcie_mce_desc) },
};
static bool f12h_mc0_mce(u16 ec, u8 xec)
@@ -874,13 +965,12 @@ static void decode_smca_error(struct mce *m)
ip_name = smca_get_long_name(bank_type);
- pr_emerg(HW_ERR "%s Extended Error Code: %d\n", ip_name, xec);
+ pr_emerg(HW_ERR "%s Ext. Error Code: %d", ip_name, xec);
/* Only print the decode of valid error codes */
if (xec < smca_mce_descs[bank_type].num_descs &&
(hwid->xec_bitmap & BIT_ULL(xec))) {
- pr_emerg(HW_ERR "%s Error: ", ip_name);
- pr_cont("%s.\n", smca_mce_descs[bank_type].descs[xec]);
+ pr_cont(", %s.\n", smca_mce_descs[bank_type].descs[xec]);
}
if (bank_type == SMCA_UMC && xec == 0 && decode_dram_ecc)
@@ -961,26 +1051,18 @@ amd_decode_mce(struct notifier_block *nb, unsigned long val, void *data)
((m->status & MCI_STATUS_UC) ? "UE" :
(m->status & MCI_STATUS_DEFERRED) ? "-" : "CE"),
((m->status & MCI_STATUS_MISCV) ? "MiscV" : "-"),
- ((m->status & MCI_STATUS_PCC) ? "PCC" : "-"),
- ((m->status & MCI_STATUS_ADDRV) ? "AddrV" : "-"));
-
- if (fam >= 0x15) {
- pr_cont("|%s", (m->status & MCI_STATUS_DEFERRED ? "Deferred" : "-"));
-
- /* F15h, bank4, bit 43 is part of McaStatSubCache. */
- if (fam != 0x15 || m->bank != 4)
- pr_cont("|%s", (m->status & MCI_STATUS_POISON ? "Poison" : "-"));
- }
+ ((m->status & MCI_STATUS_ADDRV) ? "AddrV" : "-"),
+ ((m->status & MCI_STATUS_PCC) ? "PCC" : "-"));
if (boot_cpu_has(X86_FEATURE_SMCA)) {
u32 low, high;
u32 addr = MSR_AMD64_SMCA_MCx_CONFIG(m->bank);
- pr_cont("|%s", ((m->status & MCI_STATUS_SYNDV) ? "SyndV" : "-"));
-
if (!rdmsr_safe(addr, &low, &high) &&
(low & MCI_CONFIG_MCAX))
pr_cont("|%s", ((m->status & MCI_STATUS_TCC) ? "TCC" : "-"));
+
+ pr_cont("|%s", ((m->status & MCI_STATUS_SYNDV) ? "SyndV" : "-"));
}
/* do the two bits[14:13] together */
@@ -988,6 +1070,17 @@ amd_decode_mce(struct notifier_block *nb, unsigned long val, void *data)
if (ecc)
pr_cont("|%sECC", ((ecc == 2) ? "C" : "U"));
+ if (fam >= 0x15) {
+ pr_cont("|%s", (m->status & MCI_STATUS_DEFERRED ? "Deferred" : "-"));
+
+ /* F15h, bank4, bit 43 is part of McaStatSubCache. */
+ if (fam != 0x15 || m->bank != 4)
+ pr_cont("|%s", (m->status & MCI_STATUS_POISON ? "Poison" : "-"));
+ }
+
+ if (fam >= 0x17)
+ pr_cont("|%s", (m->status & MCI_STATUS_SCRUB ? "Scrub" : "-"));
+
pr_cont("]: 0x%016llx\n", m->status);
if (m->status & MCI_STATUS_ADDRV)
diff --git a/drivers/edac/skx_base.c b/drivers/edac/skx_base.c
new file mode 100644
index 000000000000..adae4c848ca1
--- /dev/null
+++ b/drivers/edac/skx_base.c
@@ -0,0 +1,650 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * EDAC driver for Intel(R) Xeon(R) Skylake processors
+ * Copyright (c) 2016, Intel Corporation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/processor.h>
+#include <asm/cpu_device_id.h>
+#include <asm/intel-family.h>
+#include <asm/mce.h>
+
+#include "edac_module.h"
+#include "skx_common.h"
+
+#define EDAC_MOD_STR "skx_edac"
+
+/*
+ * Debug macros
+ */
+#define skx_printk(level, fmt, arg...) \
+ edac_printk(level, "skx", fmt, ##arg)
+
+#define skx_mc_printk(mci, level, fmt, arg...) \
+ edac_mc_chipset_printk(mci, level, "skx", fmt, ##arg)
+
+static struct list_head *skx_edac_list;
+
+static u64 skx_tolm, skx_tohm;
+static int skx_num_sockets;
+static unsigned int nvdimm_count;
+
+#define MASK26 0x3FFFFFF /* Mask for 2^26 */
+#define MASK29 0x1FFFFFFF /* Mask for 2^29 */
+
+static struct skx_dev *get_skx_dev(struct pci_bus *bus, u8 idx)
+{
+ struct skx_dev *d;
+
+ list_for_each_entry(d, skx_edac_list, list) {
+ if (d->seg == pci_domain_nr(bus) && d->bus[idx] == bus->number)
+ return d;
+ }
+
+ return NULL;
+}
+
+enum munittype {
+ CHAN0, CHAN1, CHAN2, SAD_ALL, UTIL_ALL, SAD
+};
+
+struct munit {
+ u16 did;
+ u16 devfn[SKX_NUM_IMC];
+ u8 busidx;
+ u8 per_socket;
+ enum munittype mtype;
+};
+
+/*
+ * List of PCI device ids that we need together with some device
+ * number and function numbers to tell which memory controller the
+ * device belongs to.
+ */
+static const struct munit skx_all_munits[] = {
+ { 0x2054, { }, 1, 1, SAD_ALL },
+ { 0x2055, { }, 1, 1, UTIL_ALL },
+ { 0x2040, { PCI_DEVFN(10, 0), PCI_DEVFN(12, 0) }, 2, 2, CHAN0 },
+ { 0x2044, { PCI_DEVFN(10, 4), PCI_DEVFN(12, 4) }, 2, 2, CHAN1 },
+ { 0x2048, { PCI_DEVFN(11, 0), PCI_DEVFN(13, 0) }, 2, 2, CHAN2 },
+ { 0x208e, { }, 1, 0, SAD },
+ { }
+};
+
+static int get_all_munits(const struct munit *m)
+{
+ struct pci_dev *pdev, *prev;
+ struct skx_dev *d;
+ u32 reg;
+ int i = 0, ndev = 0;
+
+ prev = NULL;
+ for (;;) {
+ pdev = pci_get_device(PCI_VENDOR_ID_INTEL, m->did, prev);
+ if (!pdev)
+ break;
+ ndev++;
+ if (m->per_socket == SKX_NUM_IMC) {
+ for (i = 0; i < SKX_NUM_IMC; i++)
+ if (m->devfn[i] == pdev->devfn)
+ break;
+ if (i == SKX_NUM_IMC)
+ goto fail;
+ }
+ d = get_skx_dev(pdev->bus, m->busidx);
+ if (!d)
+ goto fail;
+
+ /* Be sure that the device is enabled */
+ if (unlikely(pci_enable_device(pdev) < 0)) {
+ skx_printk(KERN_ERR, "Couldn't enable device %04x:%04x\n",
+ PCI_VENDOR_ID_INTEL, m->did);
+ goto fail;
+ }
+
+ switch (m->mtype) {
+ case CHAN0: case CHAN1: case CHAN2:
+ pci_dev_get(pdev);
+ d->imc[i].chan[m->mtype].cdev = pdev;
+ break;
+ case SAD_ALL:
+ pci_dev_get(pdev);
+ d->sad_all = pdev;
+ break;
+ case UTIL_ALL:
+ pci_dev_get(pdev);
+ d->util_all = pdev;
+ break;
+ case SAD:
+ /*
+ * one of these devices per core, including cores
+ * that don't exist on this SKU. Ignore any that
+ * read a route table of zero, make sure all the
+ * non-zero values match.
+ */
+ pci_read_config_dword(pdev, 0xB4, &reg);
+ if (reg != 0) {
+ if (d->mcroute == 0) {
+ d->mcroute = reg;
+ } else if (d->mcroute != reg) {
+ skx_printk(KERN_ERR, "mcroute mismatch\n");
+ goto fail;
+ }
+ }
+ ndev--;
+ break;
+ }
+
+ prev = pdev;
+ }
+
+ return ndev;
+fail:
+ pci_dev_put(pdev);
+ return -ENODEV;
+}
+
+static const struct x86_cpu_id skx_cpuids[] = {
+ { X86_VENDOR_INTEL, 6, INTEL_FAM6_SKYLAKE_X, 0, 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(x86cpu, skx_cpuids);
+
+#define SKX_GET_MTMTR(dev, reg) \
+ pci_read_config_dword((dev), 0x87c, &(reg))
+
+static bool skx_check_ecc(struct pci_dev *pdev)
+{
+ u32 mtmtr;
+
+ SKX_GET_MTMTR(pdev, mtmtr);
+
+ return !!GET_BITFIELD(mtmtr, 2, 2);
+}
+
+static int skx_get_dimm_config(struct mem_ctl_info *mci)
+{
+ struct skx_pvt *pvt = mci->pvt_info;
+ struct skx_imc *imc = pvt->imc;
+ u32 mtr, amap, mcddrtcfg;
+ struct dimm_info *dimm;
+ int i, j;
+ int ndimms;
+
+ for (i = 0; i < SKX_NUM_CHANNELS; i++) {
+ ndimms = 0;
+ pci_read_config_dword(imc->chan[i].cdev, 0x8C, &amap);
+ pci_read_config_dword(imc->chan[i].cdev, 0x400, &mcddrtcfg);
+ for (j = 0; j < SKX_NUM_DIMMS; j++) {
+ dimm = EDAC_DIMM_PTR(mci->layers, mci->dimms,
+ mci->n_layers, i, j, 0);
+ pci_read_config_dword(imc->chan[i].cdev,
+ 0x80 + 4 * j, &mtr);
+ if (IS_DIMM_PRESENT(mtr)) {
+ ndimms += skx_get_dimm_info(mtr, amap, dimm, imc, i, j);
+ } else if (IS_NVDIMM_PRESENT(mcddrtcfg, j)) {
+ ndimms += skx_get_nvdimm_info(dimm, imc, i, j,
+ EDAC_MOD_STR);
+ nvdimm_count++;
+ }
+ }
+ if (ndimms && !skx_check_ecc(imc->chan[0].cdev)) {
+ skx_printk(KERN_ERR, "ECC is disabled on imc %d\n", imc->mc);
+ return -ENODEV;
+ }
+ }
+
+ return 0;
+}
+
+#define SKX_MAX_SAD 24
+
+#define SKX_GET_SAD(d, i, reg) \
+ pci_read_config_dword((d)->sad_all, 0x60 + 8 * (i), &(reg))
+#define SKX_GET_ILV(d, i, reg) \
+ pci_read_config_dword((d)->sad_all, 0x64 + 8 * (i), &(reg))
+
+#define SKX_SAD_MOD3MODE(sad) GET_BITFIELD((sad), 30, 31)
+#define SKX_SAD_MOD3(sad) GET_BITFIELD((sad), 27, 27)
+#define SKX_SAD_LIMIT(sad) (((u64)GET_BITFIELD((sad), 7, 26) << 26) | MASK26)
+#define SKX_SAD_MOD3ASMOD2(sad) GET_BITFIELD((sad), 5, 6)
+#define SKX_SAD_ATTR(sad) GET_BITFIELD((sad), 3, 4)
+#define SKX_SAD_INTERLEAVE(sad) GET_BITFIELD((sad), 1, 2)
+#define SKX_SAD_ENABLE(sad) GET_BITFIELD((sad), 0, 0)
+
+#define SKX_ILV_REMOTE(tgt) (((tgt) & 8) == 0)
+#define SKX_ILV_TARGET(tgt) ((tgt) & 7)
+
+static bool skx_sad_decode(struct decoded_addr *res)
+{
+ struct skx_dev *d = list_first_entry(skx_edac_list, typeof(*d), list);
+ u64 addr = res->addr;
+ int i, idx, tgt, lchan, shift;
+ u32 sad, ilv;
+ u64 limit, prev_limit;
+ int remote = 0;
+
+ /* Simple sanity check for I/O space or out of range */
+ if (addr >= skx_tohm || (addr >= skx_tolm && addr < BIT_ULL(32))) {
+ edac_dbg(0, "Address 0x%llx out of range\n", addr);
+ return false;
+ }
+
+restart:
+ prev_limit = 0;
+ for (i = 0; i < SKX_MAX_SAD; i++) {
+ SKX_GET_SAD(d, i, sad);
+ limit = SKX_SAD_LIMIT(sad);
+ if (SKX_SAD_ENABLE(sad)) {
+ if (addr >= prev_limit && addr <= limit)
+ goto sad_found;
+ }
+ prev_limit = limit + 1;
+ }
+ edac_dbg(0, "No SAD entry for 0x%llx\n", addr);
+ return false;
+
+sad_found:
+ SKX_GET_ILV(d, i, ilv);
+
+ switch (SKX_SAD_INTERLEAVE(sad)) {
+ case 0:
+ idx = GET_BITFIELD(addr, 6, 8);
+ break;
+ case 1:
+ idx = GET_BITFIELD(addr, 8, 10);
+ break;
+ case 2:
+ idx = GET_BITFIELD(addr, 12, 14);
+ break;
+ case 3:
+ idx = GET_BITFIELD(addr, 30, 32);
+ break;
+ }
+
+ tgt = GET_BITFIELD(ilv, 4 * idx, 4 * idx + 3);
+
+ /* If point to another node, find it and start over */
+ if (SKX_ILV_REMOTE(tgt)) {
+ if (remote) {
+ edac_dbg(0, "Double remote!\n");
+ return false;
+ }
+ remote = 1;
+ list_for_each_entry(d, skx_edac_list, list) {
+ if (d->imc[0].src_id == SKX_ILV_TARGET(tgt))
+ goto restart;
+ }
+ edac_dbg(0, "Can't find node %d\n", SKX_ILV_TARGET(tgt));
+ return false;
+ }
+
+ if (SKX_SAD_MOD3(sad) == 0) {
+ lchan = SKX_ILV_TARGET(tgt);
+ } else {
+ switch (SKX_SAD_MOD3MODE(sad)) {
+ case 0:
+ shift = 6;
+ break;
+ case 1:
+ shift = 8;
+ break;
+ case 2:
+ shift = 12;
+ break;
+ default:
+ edac_dbg(0, "illegal mod3mode\n");
+ return false;
+ }
+ switch (SKX_SAD_MOD3ASMOD2(sad)) {
+ case 0:
+ lchan = (addr >> shift) % 3;
+ break;
+ case 1:
+ lchan = (addr >> shift) % 2;
+ break;
+ case 2:
+ lchan = (addr >> shift) % 2;
+ lchan = (lchan << 1) | !lchan;
+ break;
+ case 3:
+ lchan = ((addr >> shift) % 2) << 1;
+ break;
+ }
+ lchan = (lchan << 1) | (SKX_ILV_TARGET(tgt) & 1);
+ }
+
+ res->dev = d;
+ res->socket = d->imc[0].src_id;
+ res->imc = GET_BITFIELD(d->mcroute, lchan * 3, lchan * 3 + 2);
+ res->channel = GET_BITFIELD(d->mcroute, lchan * 2 + 18, lchan * 2 + 19);
+
+ edac_dbg(2, "0x%llx: socket=%d imc=%d channel=%d\n",
+ res->addr, res->socket, res->imc, res->channel);
+ return true;
+}
+
+#define SKX_MAX_TAD 8
+
+#define SKX_GET_TADBASE(d, mc, i, reg) \
+ pci_read_config_dword((d)->imc[mc].chan[0].cdev, 0x850 + 4 * (i), &(reg))
+#define SKX_GET_TADWAYNESS(d, mc, i, reg) \
+ pci_read_config_dword((d)->imc[mc].chan[0].cdev, 0x880 + 4 * (i), &(reg))
+#define SKX_GET_TADCHNILVOFFSET(d, mc, ch, i, reg) \
+ pci_read_config_dword((d)->imc[mc].chan[ch].cdev, 0x90 + 4 * (i), &(reg))
+
+#define SKX_TAD_BASE(b) ((u64)GET_BITFIELD((b), 12, 31) << 26)
+#define SKX_TAD_SKT_GRAN(b) GET_BITFIELD((b), 4, 5)
+#define SKX_TAD_CHN_GRAN(b) GET_BITFIELD((b), 6, 7)
+#define SKX_TAD_LIMIT(b) (((u64)GET_BITFIELD((b), 12, 31) << 26) | MASK26)
+#define SKX_TAD_OFFSET(b) ((u64)GET_BITFIELD((b), 4, 23) << 26)
+#define SKX_TAD_SKTWAYS(b) (1 << GET_BITFIELD((b), 10, 11))
+#define SKX_TAD_CHNWAYS(b) (GET_BITFIELD((b), 8, 9) + 1)
+
+/* which bit used for both socket and channel interleave */
+static int skx_granularity[] = { 6, 8, 12, 30 };
+
+static u64 skx_do_interleave(u64 addr, int shift, int ways, u64 lowbits)
+{
+ addr >>= shift;
+ addr /= ways;
+ addr <<= shift;
+
+ return addr | (lowbits & ((1ull << shift) - 1));
+}
+
+static bool skx_tad_decode(struct decoded_addr *res)
+{
+ int i;
+ u32 base, wayness, chnilvoffset;
+ int skt_interleave_bit, chn_interleave_bit;
+ u64 channel_addr;
+
+ for (i = 0; i < SKX_MAX_TAD; i++) {
+ SKX_GET_TADBASE(res->dev, res->imc, i, base);
+ SKX_GET_TADWAYNESS(res->dev, res->imc, i, wayness);
+ if (SKX_TAD_BASE(base) <= res->addr && res->addr <= SKX_TAD_LIMIT(wayness))
+ goto tad_found;
+ }
+ edac_dbg(0, "No TAD entry for 0x%llx\n", res->addr);
+ return false;
+
+tad_found:
+ res->sktways = SKX_TAD_SKTWAYS(wayness);
+ res->chanways = SKX_TAD_CHNWAYS(wayness);
+ skt_interleave_bit = skx_granularity[SKX_TAD_SKT_GRAN(base)];
+ chn_interleave_bit = skx_granularity[SKX_TAD_CHN_GRAN(base)];
+
+ SKX_GET_TADCHNILVOFFSET(res->dev, res->imc, res->channel, i, chnilvoffset);
+ channel_addr = res->addr - SKX_TAD_OFFSET(chnilvoffset);
+
+ if (res->chanways == 3 && skt_interleave_bit > chn_interleave_bit) {
+ /* Must handle channel first, then socket */
+ channel_addr = skx_do_interleave(channel_addr, chn_interleave_bit,
+ res->chanways, channel_addr);
+ channel_addr = skx_do_interleave(channel_addr, skt_interleave_bit,
+ res->sktways, channel_addr);
+ } else {
+ /* Handle socket then channel. Preserve low bits from original address */
+ channel_addr = skx_do_interleave(channel_addr, skt_interleave_bit,
+ res->sktways, res->addr);
+ channel_addr = skx_do_interleave(channel_addr, chn_interleave_bit,
+ res->chanways, res->addr);
+ }
+
+ res->chan_addr = channel_addr;
+
+ edac_dbg(2, "0x%llx: chan_addr=0x%llx sktways=%d chanways=%d\n",
+ res->addr, res->chan_addr, res->sktways, res->chanways);
+ return true;
+}
+
+#define SKX_MAX_RIR 4
+
+#define SKX_GET_RIRWAYNESS(d, mc, ch, i, reg) \
+ pci_read_config_dword((d)->imc[mc].chan[ch].cdev, \
+ 0x108 + 4 * (i), &(reg))
+#define SKX_GET_RIRILV(d, mc, ch, idx, i, reg) \
+ pci_read_config_dword((d)->imc[mc].chan[ch].cdev, \
+ 0x120 + 16 * (idx) + 4 * (i), &(reg))
+
+#define SKX_RIR_VALID(b) GET_BITFIELD((b), 31, 31)
+#define SKX_RIR_LIMIT(b) (((u64)GET_BITFIELD((b), 1, 11) << 29) | MASK29)
+#define SKX_RIR_WAYS(b) (1 << GET_BITFIELD((b), 28, 29))
+#define SKX_RIR_CHAN_RANK(b) GET_BITFIELD((b), 16, 19)
+#define SKX_RIR_OFFSET(b) ((u64)(GET_BITFIELD((b), 2, 15) << 26))
+
+static bool skx_rir_decode(struct decoded_addr *res)
+{
+ int i, idx, chan_rank;
+ int shift;
+ u32 rirway, rirlv;
+ u64 rank_addr, prev_limit = 0, limit;
+
+ if (res->dev->imc[res->imc].chan[res->channel].dimms[0].close_pg)
+ shift = 6;
+ else
+ shift = 13;
+
+ for (i = 0; i < SKX_MAX_RIR; i++) {
+ SKX_GET_RIRWAYNESS(res->dev, res->imc, res->channel, i, rirway);
+ limit = SKX_RIR_LIMIT(rirway);
+ if (SKX_RIR_VALID(rirway)) {
+ if (prev_limit <= res->chan_addr &&
+ res->chan_addr <= limit)
+ goto rir_found;
+ }
+ prev_limit = limit;
+ }
+ edac_dbg(0, "No RIR entry for 0x%llx\n", res->addr);
+ return false;
+
+rir_found:
+ rank_addr = res->chan_addr >> shift;
+ rank_addr /= SKX_RIR_WAYS(rirway);
+ rank_addr <<= shift;
+ rank_addr |= res->chan_addr & GENMASK_ULL(shift - 1, 0);
+
+ res->rank_address = rank_addr;
+ idx = (res->chan_addr >> shift) % SKX_RIR_WAYS(rirway);
+
+ SKX_GET_RIRILV(res->dev, res->imc, res->channel, idx, i, rirlv);
+ res->rank_address = rank_addr - SKX_RIR_OFFSET(rirlv);
+ chan_rank = SKX_RIR_CHAN_RANK(rirlv);
+ res->channel_rank = chan_rank;
+ res->dimm = chan_rank / 4;
+ res->rank = chan_rank % 4;
+
+ edac_dbg(2, "0x%llx: dimm=%d rank=%d chan_rank=%d rank_addr=0x%llx\n",
+ res->addr, res->dimm, res->rank,
+ res->channel_rank, res->rank_address);
+ return true;
+}
+
+static u8 skx_close_row[] = {
+ 15, 16, 17, 18, 20, 21, 22, 28, 10, 11, 12, 13, 29, 30, 31, 32, 33
+};
+
+static u8 skx_close_column[] = {
+ 3, 4, 5, 14, 19, 23, 24, 25, 26, 27
+};
+
+static u8 skx_open_row[] = {
+ 14, 15, 16, 20, 28, 21, 22, 23, 24, 25, 26, 27, 29, 30, 31, 32, 33
+};
+
+static u8 skx_open_column[] = {
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
+};
+
+static u8 skx_open_fine_column[] = {
+ 3, 4, 5, 7, 8, 9, 10, 11, 12, 13
+};
+
+static int skx_bits(u64 addr, int nbits, u8 *bits)
+{
+ int i, res = 0;
+
+ for (i = 0; i < nbits; i++)
+ res |= ((addr >> bits[i]) & 1) << i;
+ return res;
+}
+
+static int skx_bank_bits(u64 addr, int b0, int b1, int do_xor, int x0, int x1)
+{
+ int ret = GET_BITFIELD(addr, b0, b0) | (GET_BITFIELD(addr, b1, b1) << 1);
+
+ if (do_xor)
+ ret ^= GET_BITFIELD(addr, x0, x0) | (GET_BITFIELD(addr, x1, x1) << 1);
+
+ return ret;
+}
+
+static bool skx_mad_decode(struct decoded_addr *r)
+{
+ struct skx_dimm *dimm = &r->dev->imc[r->imc].chan[r->channel].dimms[r->dimm];
+ int bg0 = dimm->fine_grain_bank ? 6 : 13;
+
+ if (dimm->close_pg) {
+ r->row = skx_bits(r->rank_address, dimm->rowbits, skx_close_row);
+ r->column = skx_bits(r->rank_address, dimm->colbits, skx_close_column);
+ r->column |= 0x400; /* C10 is autoprecharge, always set */
+ r->bank_address = skx_bank_bits(r->rank_address, 8, 9, dimm->bank_xor_enable, 22, 28);
+ r->bank_group = skx_bank_bits(r->rank_address, 6, 7, dimm->bank_xor_enable, 20, 21);
+ } else {
+ r->row = skx_bits(r->rank_address, dimm->rowbits, skx_open_row);
+ if (dimm->fine_grain_bank)
+ r->column = skx_bits(r->rank_address, dimm->colbits, skx_open_fine_column);
+ else
+ r->column = skx_bits(r->rank_address, dimm->colbits, skx_open_column);
+ r->bank_address = skx_bank_bits(r->rank_address, 18, 19, dimm->bank_xor_enable, 22, 23);
+ r->bank_group = skx_bank_bits(r->rank_address, bg0, 17, dimm->bank_xor_enable, 20, 21);
+ }
+ r->row &= (1u << dimm->rowbits) - 1;
+
+ edac_dbg(2, "0x%llx: row=0x%x col=0x%x bank_addr=%d bank_group=%d\n",
+ r->addr, r->row, r->column, r->bank_address,
+ r->bank_group);
+ return true;
+}
+
+static bool skx_decode(struct decoded_addr *res)
+{
+ return skx_sad_decode(res) && skx_tad_decode(res) &&
+ skx_rir_decode(res) && skx_mad_decode(res);
+}
+
+static struct notifier_block skx_mce_dec = {
+ .notifier_call = skx_mce_check_error,
+ .priority = MCE_PRIO_EDAC,
+};
+
+/*
+ * skx_init:
+ * make sure we are running on the correct cpu model
+ * search for all the devices we need
+ * check which DIMMs are present.
+ */
+static int __init skx_init(void)
+{
+ const struct x86_cpu_id *id;
+ const struct munit *m;
+ const char *owner;
+ int rc = 0, i, off[3] = {0xd0, 0xd4, 0xd8};
+ u8 mc = 0, src_id, node_id;
+ struct skx_dev *d;
+
+ edac_dbg(2, "\n");
+
+ owner = edac_get_owner();
+ if (owner && strncmp(owner, EDAC_MOD_STR, sizeof(EDAC_MOD_STR)))
+ return -EBUSY;
+
+ id = x86_match_cpu(skx_cpuids);
+ if (!id)
+ return -ENODEV;
+
+ rc = skx_get_hi_lo(0x2034, off, &skx_tolm, &skx_tohm);
+ if (rc)
+ return rc;
+
+ rc = skx_get_all_bus_mappings(0x2016, 0xcc, SKX, &skx_edac_list);
+ if (rc < 0)
+ goto fail;
+ if (rc == 0) {
+ edac_dbg(2, "No memory controllers found\n");
+ return -ENODEV;
+ }
+ skx_num_sockets = rc;
+
+ for (m = skx_all_munits; m->did; m++) {
+ rc = get_all_munits(m);
+ if (rc < 0)
+ goto fail;
+ if (rc != m->per_socket * skx_num_sockets) {
+ edac_dbg(2, "Expected %d, got %d of 0x%x\n",
+ m->per_socket * skx_num_sockets, rc, m->did);
+ rc = -ENODEV;
+ goto fail;
+ }
+ }
+
+ list_for_each_entry(d, skx_edac_list, list) {
+ rc = skx_get_src_id(d, &src_id);
+ if (rc < 0)
+ goto fail;
+ rc = skx_get_node_id(d, &node_id);
+ if (rc < 0)
+ goto fail;
+ edac_dbg(2, "src_id=%d node_id=%d\n", src_id, node_id);
+ for (i = 0; i < SKX_NUM_IMC; i++) {
+ d->imc[i].mc = mc++;
+ d->imc[i].lmc = i;
+ d->imc[i].src_id = src_id;
+ d->imc[i].node_id = node_id;
+ rc = skx_register_mci(&d->imc[i], d->imc[i].chan[0].cdev,
+ "Skylake Socket", EDAC_MOD_STR,
+ skx_get_dimm_config);
+ if (rc < 0)
+ goto fail;
+ }
+ }
+
+ skx_set_decode(skx_decode);
+
+ if (nvdimm_count && skx_adxl_get() == -ENODEV)
+ skx_printk(KERN_NOTICE, "Only decoding DDR4 address!\n");
+
+ /* Ensure that the OPSTATE is set correctly for POLL or NMI */
+ opstate_init();
+
+ setup_skx_debug("skx_test");
+
+ mce_register_decode_chain(&skx_mce_dec);
+
+ return 0;
+fail:
+ skx_remove();
+ return rc;
+}
+
+static void __exit skx_exit(void)
+{
+ edac_dbg(2, "\n");
+ mce_unregister_decode_chain(&skx_mce_dec);
+ teardown_skx_debug();
+ if (nvdimm_count)
+ skx_adxl_put();
+ skx_remove();
+}
+
+module_init(skx_init);
+module_exit(skx_exit);
+
+module_param(edac_op_state, int, 0444);
+MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Tony Luck");
+MODULE_DESCRIPTION("MC Driver for Intel Skylake server processors");
diff --git a/drivers/edac/skx_common.c b/drivers/edac/skx_common.c
new file mode 100644
index 000000000000..0e96e7b5b0a7
--- /dev/null
+++ b/drivers/edac/skx_common.c
@@ -0,0 +1,691 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Common codes for both the skx_edac driver and Intel 10nm server EDAC driver.
+ * Originally split out from the skx_edac driver.
+ *
+ * Copyright (c) 2018, Intel Corporation.
+ */
+
+#include <linux/acpi.h>
+#include <linux/dmi.h>
+#include <linux/adxl.h>
+#include <acpi/nfit.h>
+#include <asm/mce.h>
+#include "edac_module.h"
+#include "skx_common.h"
+
+static const char * const component_names[] = {
+ [INDEX_SOCKET] = "ProcessorSocketId",
+ [INDEX_MEMCTRL] = "MemoryControllerId",
+ [INDEX_CHANNEL] = "ChannelId",
+ [INDEX_DIMM] = "DimmSlotId",
+};
+
+static int component_indices[ARRAY_SIZE(component_names)];
+static int adxl_component_count;
+static const char * const *adxl_component_names;
+static u64 *adxl_values;
+static char *adxl_msg;
+
+static char skx_msg[MSG_SIZE];
+static skx_decode_f skx_decode;
+static u64 skx_tolm, skx_tohm;
+static LIST_HEAD(dev_edac_list);
+
+int __init skx_adxl_get(void)
+{
+ const char * const *names;
+ int i, j;
+
+ names = adxl_get_component_names();
+ if (!names) {
+ skx_printk(KERN_NOTICE, "No firmware support for address translation.\n");
+ return -ENODEV;
+ }
+
+ for (i = 0; i < INDEX_MAX; i++) {
+ for (j = 0; names[j]; j++) {
+ if (!strcmp(component_names[i], names[j])) {
+ component_indices[i] = j;
+ break;
+ }
+ }
+
+ if (!names[j])
+ goto err;
+ }
+
+ adxl_component_names = names;
+ while (*names++)
+ adxl_component_count++;
+
+ adxl_values = kcalloc(adxl_component_count, sizeof(*adxl_values),
+ GFP_KERNEL);
+ if (!adxl_values) {
+ adxl_component_count = 0;
+ return -ENOMEM;
+ }
+
+ adxl_msg = kzalloc(MSG_SIZE, GFP_KERNEL);
+ if (!adxl_msg) {
+ adxl_component_count = 0;
+ kfree(adxl_values);
+ return -ENOMEM;
+ }
+
+ return 0;
+err:
+ skx_printk(KERN_ERR, "'%s' is not matched from DSM parameters: ",
+ component_names[i]);
+ for (j = 0; names[j]; j++)
+ skx_printk(KERN_CONT, "%s ", names[j]);
+ skx_printk(KERN_CONT, "\n");
+
+ return -ENODEV;
+}
+
+void __exit skx_adxl_put(void)
+{
+ kfree(adxl_values);
+ kfree(adxl_msg);
+}
+
+static bool skx_adxl_decode(struct decoded_addr *res)
+{
+ int i, len = 0;
+
+ if (res->addr >= skx_tohm || (res->addr >= skx_tolm &&
+ res->addr < BIT_ULL(32))) {
+ edac_dbg(0, "Address 0x%llx out of range\n", res->addr);
+ return false;
+ }
+
+ if (adxl_decode(res->addr, adxl_values)) {
+ edac_dbg(0, "Failed to decode 0x%llx\n", res->addr);
+ return false;
+ }
+
+ res->socket = (int)adxl_values[component_indices[INDEX_SOCKET]];
+ res->imc = (int)adxl_values[component_indices[INDEX_MEMCTRL]];
+ res->channel = (int)adxl_values[component_indices[INDEX_CHANNEL]];
+ res->dimm = (int)adxl_values[component_indices[INDEX_DIMM]];
+
+ for (i = 0; i < adxl_component_count; i++) {
+ if (adxl_values[i] == ~0x0ull)
+ continue;
+
+ len += snprintf(adxl_msg + len, MSG_SIZE - len, " %s:0x%llx",
+ adxl_component_names[i], adxl_values[i]);
+ if (MSG_SIZE - len <= 0)
+ break;
+ }
+
+ return true;
+}
+
+void skx_set_decode(skx_decode_f decode)
+{
+ skx_decode = decode;
+}
+
+int skx_get_src_id(struct skx_dev *d, u8 *id)
+{
+ u32 reg;
+
+ if (pci_read_config_dword(d->util_all, 0xf0, &reg)) {
+ skx_printk(KERN_ERR, "Failed to read src id\n");
+ return -ENODEV;
+ }
+
+ *id = GET_BITFIELD(reg, 12, 14);
+ return 0;
+}
+
+int skx_get_node_id(struct skx_dev *d, u8 *id)
+{
+ u32 reg;
+
+ if (pci_read_config_dword(d->util_all, 0xf4, &reg)) {
+ skx_printk(KERN_ERR, "Failed to read node id\n");
+ return -ENODEV;
+ }
+
+ *id = GET_BITFIELD(reg, 0, 2);
+ return 0;
+}
+
+static int get_width(u32 mtr)
+{
+ switch (GET_BITFIELD(mtr, 8, 9)) {
+ case 0:
+ return DEV_X4;
+ case 1:
+ return DEV_X8;
+ case 2:
+ return DEV_X16;
+ }
+ return DEV_UNKNOWN;
+}
+
+/*
+ * We use the per-socket device @did to count how many sockets are present,
+ * and to detemine which PCI buses are associated with each socket. Allocate
+ * and build the full list of all the skx_dev structures that we need here.
+ */
+int skx_get_all_bus_mappings(unsigned int did, int off, enum type type,
+ struct list_head **list)
+{
+ struct pci_dev *pdev, *prev;
+ struct skx_dev *d;
+ u32 reg;
+ int ndev = 0;
+
+ prev = NULL;
+ for (;;) {
+ pdev = pci_get_device(PCI_VENDOR_ID_INTEL, did, prev);
+ if (!pdev)
+ break;
+ ndev++;
+ d = kzalloc(sizeof(*d), GFP_KERNEL);
+ if (!d) {
+ pci_dev_put(pdev);
+ return -ENOMEM;
+ }
+
+ if (pci_read_config_dword(pdev, off, &reg)) {
+ kfree(d);
+ pci_dev_put(pdev);
+ skx_printk(KERN_ERR, "Failed to read bus idx\n");
+ return -ENODEV;
+ }
+
+ d->bus[0] = GET_BITFIELD(reg, 0, 7);
+ d->bus[1] = GET_BITFIELD(reg, 8, 15);
+ if (type == SKX) {
+ d->seg = pci_domain_nr(pdev->bus);
+ d->bus[2] = GET_BITFIELD(reg, 16, 23);
+ d->bus[3] = GET_BITFIELD(reg, 24, 31);
+ } else {
+ d->seg = GET_BITFIELD(reg, 16, 23);
+ }
+
+ edac_dbg(2, "busses: 0x%x, 0x%x, 0x%x, 0x%x\n",
+ d->bus[0], d->bus[1], d->bus[2], d->bus[3]);
+ list_add_tail(&d->list, &dev_edac_list);
+ prev = pdev;
+ }
+
+ if (list)
+ *list = &dev_edac_list;
+ return ndev;
+}
+
+int skx_get_hi_lo(unsigned int did, int off[], u64 *tolm, u64 *tohm)
+{
+ struct pci_dev *pdev;
+ u32 reg;
+
+ pdev = pci_get_device(PCI_VENDOR_ID_INTEL, did, NULL);
+ if (!pdev) {
+ skx_printk(KERN_ERR, "Can't get tolm/tohm\n");
+ return -ENODEV;
+ }
+
+ if (pci_read_config_dword(pdev, off[0], &reg)) {
+ skx_printk(KERN_ERR, "Failed to read tolm\n");
+ goto fail;
+ }
+ skx_tolm = reg;
+
+ if (pci_read_config_dword(pdev, off[1], &reg)) {
+ skx_printk(KERN_ERR, "Failed to read lower tohm\n");
+ goto fail;
+ }
+ skx_tohm = reg;
+
+ if (pci_read_config_dword(pdev, off[2], &reg)) {
+ skx_printk(KERN_ERR, "Failed to read upper tohm\n");
+ goto fail;
+ }
+ skx_tohm |= (u64)reg << 32;
+
+ pci_dev_put(pdev);
+ *tolm = skx_tolm;
+ *tohm = skx_tohm;
+ edac_dbg(2, "tolm = 0x%llx tohm = 0x%llx\n", skx_tolm, skx_tohm);
+ return 0;
+fail:
+ pci_dev_put(pdev);
+ return -ENODEV;
+}
+
+static int skx_get_dimm_attr(u32 reg, int lobit, int hibit, int add,
+ int minval, int maxval, const char *name)
+{
+ u32 val = GET_BITFIELD(reg, lobit, hibit);
+
+ if (val < minval || val > maxval) {
+ edac_dbg(2, "bad %s = %d (raw=0x%x)\n", name, val, reg);
+ return -EINVAL;
+ }
+ return val + 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 numcol(reg) skx_get_dimm_attr(reg, 0, 1, 10, 0, 2, "cols")
+
+int skx_get_dimm_info(u32 mtr, u32 amap, struct dimm_info *dimm,
+ struct skx_imc *imc, int chan, int dimmno)
+{
+ int banks = 16, ranks, rows, cols, npages;
+ u64 size;
+
+ ranks = numrank(mtr);
+ rows = numrow(mtr);
+ cols = numcol(mtr);
+
+ /*
+ * Compute size in 8-byte (2^3) words, then shift to MiB (2^20)
+ */
+ size = ((1ull << (rows + cols + ranks)) * banks) >> (20 - 3);
+ npages = MiB_TO_PAGES(size);
+
+ edac_dbg(0, "mc#%d: channel %d, dimm %d, %lld MiB (%d pages) bank: %d, rank: %d, row: 0x%x, col: 0x%x\n",
+ imc->mc, chan, dimmno, size, npages,
+ banks, 1 << ranks, rows, cols);
+
+ imc->chan[chan].dimms[dimmno].close_pg = GET_BITFIELD(mtr, 0, 0);
+ imc->chan[chan].dimms[dimmno].bank_xor_enable = GET_BITFIELD(mtr, 9, 9);
+ imc->chan[chan].dimms[dimmno].fine_grain_bank = GET_BITFIELD(amap, 0, 0);
+ imc->chan[chan].dimms[dimmno].rowbits = rows;
+ imc->chan[chan].dimms[dimmno].colbits = cols;
+
+ dimm->nr_pages = npages;
+ dimm->grain = 32;
+ dimm->dtype = get_width(mtr);
+ dimm->mtype = MEM_DDR4;
+ dimm->edac_mode = EDAC_SECDED; /* likely better than this */
+ snprintf(dimm->label, sizeof(dimm->label), "CPU_SrcID#%u_MC#%u_Chan#%u_DIMM#%u",
+ imc->src_id, imc->lmc, chan, dimmno);
+
+ return 1;
+}
+
+int skx_get_nvdimm_info(struct dimm_info *dimm, struct skx_imc *imc,
+ int chan, int dimmno, const char *mod_str)
+{
+ int smbios_handle;
+ u32 dev_handle;
+ u16 flags;
+ u64 size = 0;
+
+ dev_handle = ACPI_NFIT_BUILD_DEVICE_HANDLE(dimmno, chan, imc->lmc,
+ imc->src_id, 0);
+
+ smbios_handle = nfit_get_smbios_id(dev_handle, &flags);
+ if (smbios_handle == -EOPNOTSUPP) {
+ pr_warn_once("%s: Can't find size of NVDIMM. Try enabling CONFIG_ACPI_NFIT\n", mod_str);
+ goto unknown_size;
+ }
+
+ if (smbios_handle < 0) {
+ skx_printk(KERN_ERR, "Can't find handle for NVDIMM ADR=0x%x\n", dev_handle);
+ goto unknown_size;
+ }
+
+ if (flags & ACPI_NFIT_MEM_MAP_FAILED) {
+ skx_printk(KERN_ERR, "NVDIMM ADR=0x%x is not mapped\n", dev_handle);
+ goto unknown_size;
+ }
+
+ size = dmi_memdev_size(smbios_handle);
+ if (size == ~0ull)
+ skx_printk(KERN_ERR, "Can't find size for NVDIMM ADR=0x%x/SMBIOS=0x%x\n",
+ dev_handle, smbios_handle);
+
+unknown_size:
+ dimm->nr_pages = size >> PAGE_SHIFT;
+ dimm->grain = 32;
+ dimm->dtype = DEV_UNKNOWN;
+ dimm->mtype = MEM_NVDIMM;
+ dimm->edac_mode = EDAC_SECDED; /* likely better than this */
+
+ edac_dbg(0, "mc#%d: channel %d, dimm %d, %llu MiB (%u pages)\n",
+ imc->mc, chan, dimmno, size >> 20, dimm->nr_pages);
+
+ snprintf(dimm->label, sizeof(dimm->label), "CPU_SrcID#%u_MC#%u_Chan#%u_DIMM#%u",
+ imc->src_id, imc->lmc, chan, dimmno);
+
+ return (size == 0 || size == ~0ull) ? 0 : 1;
+}
+
+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)
+{
+ struct mem_ctl_info *mci;
+ struct edac_mc_layer layers[2];
+ struct skx_pvt *pvt;
+ int rc;
+
+ /* Allocate a new MC control structure */
+ layers[0].type = EDAC_MC_LAYER_CHANNEL;
+ layers[0].size = NUM_CHANNELS;
+ layers[0].is_virt_csrow = false;
+ layers[1].type = EDAC_MC_LAYER_SLOT;
+ layers[1].size = NUM_DIMMS;
+ layers[1].is_virt_csrow = true;
+ mci = edac_mc_alloc(imc->mc, ARRAY_SIZE(layers), layers,
+ sizeof(struct skx_pvt));
+
+ if (unlikely(!mci))
+ return -ENOMEM;
+
+ edac_dbg(0, "MC#%d: mci = %p\n", imc->mc, mci);
+
+ /* Associate skx_dev and mci for future usage */
+ imc->mci = mci;
+ pvt = mci->pvt_info;
+ pvt->imc = imc;
+
+ mci->ctl_name = kasprintf(GFP_KERNEL, "%s#%d IMC#%d", ctl_name,
+ imc->node_id, imc->lmc);
+ if (!mci->ctl_name) {
+ rc = -ENOMEM;
+ goto fail0;
+ }
+
+ mci->mtype_cap = MEM_FLAG_DDR4 | MEM_FLAG_NVDIMM;
+ 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->ctl_page_to_phys = NULL;
+
+ rc = get_dimm_config(mci);
+ if (rc < 0)
+ goto fail;
+
+ /* Record ptr to the generic device */
+ mci->pdev = &pdev->dev;
+
+ /* Add this new MC control structure to EDAC's list of MCs */
+ if (unlikely(edac_mc_add_mc(mci))) {
+ edac_dbg(0, "MC: failed edac_mc_add_mc()\n");
+ rc = -EINVAL;
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ kfree(mci->ctl_name);
+fail0:
+ edac_mc_free(mci);
+ imc->mci = NULL;
+ return rc;
+}
+
+static void skx_unregister_mci(struct skx_imc *imc)
+{
+ struct mem_ctl_info *mci = imc->mci;
+
+ if (!mci)
+ return;
+
+ edac_dbg(0, "MC%d: mci = %p\n", imc->mc, mci);
+
+ /* Remove MC sysfs nodes */
+ edac_mc_del_mc(mci->pdev);
+
+ edac_dbg(1, "%s: free mci struct\n", mci->ctl_name);
+ kfree(mci->ctl_name);
+ edac_mc_free(mci);
+}
+
+static struct mem_ctl_info *get_mci(int src_id, int lmc)
+{
+ struct skx_dev *d;
+
+ if (lmc > NUM_IMC - 1) {
+ skx_printk(KERN_ERR, "Bad lmc %d\n", lmc);
+ return NULL;
+ }
+
+ list_for_each_entry(d, &dev_edac_list, list) {
+ if (d->imc[0].src_id == src_id)
+ return d->imc[lmc].mci;
+ }
+
+ skx_printk(KERN_ERR, "No mci for src_id %d lmc %d\n", src_id, lmc);
+ return NULL;
+}
+
+static void skx_mce_output_error(struct mem_ctl_info *mci,
+ const struct mce *m,
+ struct decoded_addr *res)
+{
+ enum hw_event_mc_err_type tp_event;
+ char *type, *optype;
+ bool ripv = GET_BITFIELD(m->mcgstatus, 0, 0);
+ bool overflow = GET_BITFIELD(m->status, 62, 62);
+ bool uncorrected_error = GET_BITFIELD(m->status, 61, 61);
+ bool recoverable;
+ u32 core_err_cnt = GET_BITFIELD(m->status, 38, 52);
+ u32 mscod = GET_BITFIELD(m->status, 16, 31);
+ u32 errcode = GET_BITFIELD(m->status, 0, 15);
+ u32 optypenum = GET_BITFIELD(m->status, 4, 6);
+
+ recoverable = GET_BITFIELD(m->status, 56, 56);
+
+ if (uncorrected_error) {
+ core_err_cnt = 1;
+ if (ripv) {
+ type = "FATAL";
+ tp_event = HW_EVENT_ERR_FATAL;
+ } else {
+ type = "NON_FATAL";
+ tp_event = HW_EVENT_ERR_UNCORRECTED;
+ }
+ } else {
+ type = "CORRECTED";
+ tp_event = HW_EVENT_ERR_CORRECTED;
+ }
+
+ /*
+ * According to Intel Architecture spec vol 3B,
+ * Table 15-10 "IA32_MCi_Status [15:0] Compound Error Code Encoding"
+ * memory errors should fit one of these masks:
+ * 000f 0000 1mmm cccc (binary)
+ * 000f 0010 1mmm cccc (binary) [RAM used as cache]
+ * where:
+ * f = Correction Report Filtering Bit. If 1, subsequent errors
+ * won't be shown
+ * mmm = error type
+ * cccc = channel
+ * If the mask doesn't match, report an error to the parsing logic
+ */
+ if (!((errcode & 0xef80) == 0x80 || (errcode & 0xef80) == 0x280)) {
+ optype = "Can't parse: it is not a mem";
+ } else {
+ switch (optypenum) {
+ case 0:
+ optype = "generic undef request error";
+ break;
+ case 1:
+ optype = "memory read error";
+ break;
+ case 2:
+ optype = "memory write error";
+ break;
+ case 3:
+ optype = "addr/cmd error";
+ break;
+ case 4:
+ optype = "memory scrubbing error";
+ break;
+ default:
+ optype = "reserved";
+ break;
+ }
+ }
+ if (adxl_component_count) {
+ snprintf(skx_msg, MSG_SIZE, "%s%s err_code:0x%04x:0x%04x %s",
+ overflow ? " OVERFLOW" : "",
+ (uncorrected_error && recoverable) ? " recoverable" : "",
+ mscod, errcode, adxl_msg);
+ } else {
+ snprintf(skx_msg, MSG_SIZE,
+ "%s%s err_code:0x%04x:0x%04x socket:%d imc:%d rank:%d bg:%d ba:%d row:0x%x col:0x%x",
+ overflow ? " OVERFLOW" : "",
+ (uncorrected_error && recoverable) ? " recoverable" : "",
+ mscod, errcode,
+ res->socket, res->imc, res->rank,
+ res->bank_group, res->bank_address, res->row, res->column);
+ }
+
+ edac_dbg(0, "%s\n", skx_msg);
+
+ /* Call the helper to output message */
+ edac_mc_handle_error(tp_event, mci, core_err_cnt,
+ m->addr >> PAGE_SHIFT, m->addr & ~PAGE_MASK, 0,
+ res->channel, res->dimm, -1,
+ optype, skx_msg);
+}
+
+int skx_mce_check_error(struct notifier_block *nb, unsigned long val,
+ void *data)
+{
+ struct mce *mce = (struct mce *)data;
+ struct decoded_addr res;
+ struct mem_ctl_info *mci;
+ char *type;
+
+ if (edac_get_report_status() == EDAC_REPORTING_DISABLED)
+ return NOTIFY_DONE;
+
+ /* ignore unless this is memory related with an address */
+ if ((mce->status & 0xefff) >> 7 != 1 || !(mce->status & MCI_STATUS_ADDRV))
+ return NOTIFY_DONE;
+
+ memset(&res, 0, sizeof(res));
+ res.addr = mce->addr;
+
+ if (adxl_component_count) {
+ if (!skx_adxl_decode(&res))
+ return NOTIFY_DONE;
+
+ mci = get_mci(res.socket, res.imc);
+ } else {
+ if (!skx_decode || !skx_decode(&res))
+ return NOTIFY_DONE;
+
+ mci = res.dev->imc[res.imc].mci;
+ }
+
+ if (!mci)
+ return NOTIFY_DONE;
+
+ if (mce->mcgstatus & MCG_STATUS_MCIP)
+ type = "Exception";
+ else
+ type = "Event";
+
+ skx_mc_printk(mci, KERN_DEBUG, "HANDLING MCE MEMORY ERROR\n");
+
+ skx_mc_printk(mci, KERN_DEBUG, "CPU %d: Machine Check %s: 0x%llx "
+ "Bank %d: 0x%llx\n", mce->extcpu, type,
+ mce->mcgstatus, mce->bank, mce->status);
+ skx_mc_printk(mci, KERN_DEBUG, "TSC 0x%llx ", mce->tsc);
+ skx_mc_printk(mci, KERN_DEBUG, "ADDR 0x%llx ", mce->addr);
+ skx_mc_printk(mci, KERN_DEBUG, "MISC 0x%llx ", mce->misc);
+
+ skx_mc_printk(mci, KERN_DEBUG, "PROCESSOR %u:0x%x TIME %llu SOCKET "
+ "%u APIC 0x%x\n", mce->cpuvendor, mce->cpuid,
+ mce->time, mce->socketid, mce->apicid);
+
+ skx_mce_output_error(mci, mce, &res);
+
+ return NOTIFY_DONE;
+}
+
+void skx_remove(void)
+{
+ int i, j;
+ struct skx_dev *d, *tmp;
+
+ edac_dbg(0, "\n");
+
+ list_for_each_entry_safe(d, tmp, &dev_edac_list, list) {
+ list_del(&d->list);
+ for (i = 0; i < NUM_IMC; i++) {
+ if (d->imc[i].mci)
+ skx_unregister_mci(&d->imc[i]);
+
+ if (d->imc[i].mdev)
+ pci_dev_put(d->imc[i].mdev);
+
+ if (d->imc[i].mbase)
+ iounmap(d->imc[i].mbase);
+
+ for (j = 0; j < NUM_CHANNELS; j++) {
+ if (d->imc[i].chan[j].cdev)
+ pci_dev_put(d->imc[i].chan[j].cdev);
+ }
+ }
+ if (d->util_all)
+ pci_dev_put(d->util_all);
+ if (d->sad_all)
+ pci_dev_put(d->sad_all);
+ if (d->uracu)
+ pci_dev_put(d->uracu);
+
+ kfree(d);
+ }
+}
+
+#ifdef CONFIG_EDAC_DEBUG
+/*
+ * Debug feature.
+ * Exercise the address decode logic by writing an address to
+ * /sys/kernel/debug/edac/dirname/addr.
+ */
+static struct dentry *skx_test;
+
+static int debugfs_u64_set(void *data, u64 val)
+{
+ struct mce m;
+
+ pr_warn_once("Fake error to 0x%llx injected via debugfs\n", val);
+
+ memset(&m, 0, sizeof(m));
+ /* ADDRV + MemRd + Unknown channel */
+ m.status = MCI_STATUS_ADDRV + 0x90;
+ /* One corrected error */
+ m.status |= BIT_ULL(MCI_STATUS_CEC_SHIFT);
+ m.addr = val;
+ skx_mce_check_error(NULL, 0, &m);
+
+ return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(fops_u64_wo, NULL, debugfs_u64_set, "%llu\n");
+
+void setup_skx_debug(const char *dirname)
+{
+ skx_test = edac_debugfs_create_dir(dirname);
+ if (!skx_test)
+ return;
+
+ if (!edac_debugfs_create_file("addr", 0200, skx_test,
+ NULL, &fops_u64_wo)) {
+ debugfs_remove(skx_test);
+ skx_test = NULL;
+ }
+}
+
+void teardown_skx_debug(void)
+{
+ debugfs_remove_recursive(skx_test);
+}
+#endif /*CONFIG_EDAC_DEBUG*/
diff --git a/drivers/edac/skx_common.h b/drivers/edac/skx_common.h
new file mode 100644
index 000000000000..d25374e34d4f
--- /dev/null
+++ b/drivers/edac/skx_common.h
@@ -0,0 +1,152 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Common codes for both the skx_edac driver and Intel 10nm server EDAC driver.
+ * Originally split out from the skx_edac driver.
+ *
+ * Copyright (c) 2018, Intel Corporation.
+ */
+
+#ifndef _SKX_COMM_EDAC_H
+#define _SKX_COMM_EDAC_H
+
+#define MSG_SIZE 1024
+
+/*
+ * Debug macros
+ */
+#define skx_printk(level, fmt, arg...) \
+ edac_printk(level, "skx", fmt, ##arg)
+
+#define skx_mc_printk(mci, level, fmt, arg...) \
+ edac_mc_chipset_printk(mci, level, "skx", fmt, ##arg)
+
+/*
+ * Get a bit field at register value <v>, from bit <lo> to bit <hi>
+ */
+#define GET_BITFIELD(v, lo, hi) \
+ (((v) & GENMASK_ULL((hi), (lo))) >> (lo))
+
+#define SKX_NUM_IMC 2 /* Memory controllers per socket */
+#define SKX_NUM_CHANNELS 3 /* Channels per memory controller */
+#define SKX_NUM_DIMMS 2 /* Max DIMMS per channel */
+
+#define I10NM_NUM_IMC 4
+#define I10NM_NUM_CHANNELS 2
+#define I10NM_NUM_DIMMS 2
+
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#define NUM_IMC MAX(SKX_NUM_IMC, I10NM_NUM_IMC)
+#define NUM_CHANNELS MAX(SKX_NUM_CHANNELS, I10NM_NUM_CHANNELS)
+#define NUM_DIMMS MAX(SKX_NUM_DIMMS, I10NM_NUM_DIMMS)
+
+#define IS_DIMM_PRESENT(r) GET_BITFIELD(r, 15, 15)
+#define IS_NVDIMM_PRESENT(r, i) GET_BITFIELD(r, i, i)
+
+/*
+ * Each cpu socket contains some pci devices that provide global
+ * information, and also some that are local to each of the two
+ * memory controllers on the die.
+ */
+struct skx_dev {
+ struct list_head list;
+ u8 bus[4];
+ int seg;
+ struct pci_dev *sad_all;
+ struct pci_dev *util_all;
+ struct pci_dev *uracu; /* for i10nm CPU */
+ u32 mcroute;
+ struct skx_imc {
+ struct mem_ctl_info *mci;
+ struct pci_dev *mdev; /* for i10nm CPU */
+ void __iomem *mbase; /* for i10nm CPU */
+ u8 mc; /* system wide mc# */
+ u8 lmc; /* socket relative mc# */
+ u8 src_id, node_id;
+ struct skx_channel {
+ struct pci_dev *cdev;
+ struct skx_dimm {
+ u8 close_pg;
+ u8 bank_xor_enable;
+ u8 fine_grain_bank;
+ u8 rowbits;
+ u8 colbits;
+ } dimms[NUM_DIMMS];
+ } chan[NUM_CHANNELS];
+ } imc[NUM_IMC];
+};
+
+struct skx_pvt {
+ struct skx_imc *imc;
+};
+
+enum type {
+ SKX,
+ I10NM
+};
+
+enum {
+ INDEX_SOCKET,
+ INDEX_MEMCTRL,
+ INDEX_CHANNEL,
+ INDEX_DIMM,
+ INDEX_MAX
+};
+
+struct decoded_addr {
+ struct skx_dev *dev;
+ u64 addr;
+ int socket;
+ int imc;
+ int channel;
+ u64 chan_addr;
+ int sktways;
+ int chanways;
+ int dimm;
+ int rank;
+ int channel_rank;
+ u64 rank_address;
+ int row;
+ int column;
+ int bank_address;
+ int bank_group;
+};
+
+typedef int (*get_dimm_config_f)(struct mem_ctl_info *mci);
+typedef bool (*skx_decode_f)(struct decoded_addr *res);
+
+int __init skx_adxl_get(void);
+void __exit skx_adxl_put(void);
+void skx_set_decode(skx_decode_f decode);
+
+int skx_get_src_id(struct skx_dev *d, u8 *id);
+int skx_get_node_id(struct skx_dev *d, u8 *id);
+
+int skx_get_all_bus_mappings(unsigned int did, int off, enum type,
+ struct list_head **list);
+
+int skx_get_hi_lo(unsigned int did, int off[], u64 *tolm, u64 *tohm);
+
+int skx_get_dimm_info(u32 mtr, u32 amap, struct dimm_info *dimm,
+ struct skx_imc *imc, int chan, int dimmno);
+
+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,
+ const char *ctl_name, const char *mod_str,
+ get_dimm_config_f get_dimm_config);
+
+int skx_mce_check_error(struct notifier_block *nb, unsigned long val,
+ void *data);
+
+void skx_remove(void);
+
+#ifdef CONFIG_EDAC_DEBUG
+void setup_skx_debug(const char *dirname);
+void teardown_skx_debug(void);
+#else
+static inline void setup_skx_debug(const char *dirname) {}
+static inline void teardown_skx_debug(void) {}
+#endif /*CONFIG_EDAC_DEBUG*/
+
+#endif /* _SKX_COMM_EDAC_H */
diff --git a/drivers/edac/skx_edac.c b/drivers/edac/skx_edac.c
deleted file mode 100644
index 93ef161bb5e1..000000000000
--- a/drivers/edac/skx_edac.c
+++ /dev/null
@@ -1,1358 +0,0 @@
-/*
- * EDAC driver for Intel(R) Xeon(R) Skylake processors
- * Copyright (c) 2016, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/acpi.h>
-#include <linux/dmi.h>
-#include <linux/pci.h>
-#include <linux/pci_ids.h>
-#include <linux/slab.h>
-#include <linux/delay.h>
-#include <linux/edac.h>
-#include <linux/mmzone.h>
-#include <linux/smp.h>
-#include <linux/bitmap.h>
-#include <linux/math64.h>
-#include <linux/mod_devicetable.h>
-#include <linux/adxl.h>
-#include <acpi/nfit.h>
-#include <asm/cpu_device_id.h>
-#include <asm/intel-family.h>
-#include <asm/processor.h>
-#include <asm/mce.h>
-
-#include "edac_module.h"
-
-#define EDAC_MOD_STR "skx_edac"
-#define MSG_SIZE 1024
-
-/*
- * Debug macros
- */
-#define skx_printk(level, fmt, arg...) \
- edac_printk(level, "skx", fmt, ##arg)
-
-#define skx_mc_printk(mci, level, fmt, arg...) \
- edac_mc_chipset_printk(mci, level, "skx", fmt, ##arg)
-
-/*
- * Get a bit field at register value <v>, from bit <lo> to bit <hi>
- */
-#define GET_BITFIELD(v, lo, hi) \
- (((v) & GENMASK_ULL((hi), (lo))) >> (lo))
-
-static LIST_HEAD(skx_edac_list);
-
-static u64 skx_tolm, skx_tohm;
-static char *skx_msg;
-static unsigned int nvdimm_count;
-
-enum {
- INDEX_SOCKET,
- INDEX_MEMCTRL,
- INDEX_CHANNEL,
- INDEX_DIMM,
- INDEX_MAX
-};
-
-static const char * const component_names[] = {
- [INDEX_SOCKET] = "ProcessorSocketId",
- [INDEX_MEMCTRL] = "MemoryControllerId",
- [INDEX_CHANNEL] = "ChannelId",
- [INDEX_DIMM] = "DimmSlotId",
-};
-
-static int component_indices[ARRAY_SIZE(component_names)];
-static int adxl_component_count;
-static const char * const *adxl_component_names;
-static u64 *adxl_values;
-static char *adxl_msg;
-
-#define NUM_IMC 2 /* memory controllers per socket */
-#define NUM_CHANNELS 3 /* channels per memory controller */
-#define NUM_DIMMS 2 /* Max DIMMS per channel */
-
-#define MASK26 0x3FFFFFF /* Mask for 2^26 */
-#define MASK29 0x1FFFFFFF /* Mask for 2^29 */
-
-/*
- * Each cpu socket contains some pci devices that provide global
- * information, and also some that are local to each of the two
- * memory controllers on the die.
- */
-struct skx_dev {
- struct list_head list;
- u8 bus[4];
- int seg;
- struct pci_dev *sad_all;
- struct pci_dev *util_all;
- u32 mcroute;
- struct skx_imc {
- struct mem_ctl_info *mci;
- u8 mc; /* system wide mc# */
- u8 lmc; /* socket relative mc# */
- u8 src_id, node_id;
- struct skx_channel {
- struct pci_dev *cdev;
- struct skx_dimm {
- u8 close_pg;
- u8 bank_xor_enable;
- u8 fine_grain_bank;
- u8 rowbits;
- u8 colbits;
- } dimms[NUM_DIMMS];
- } chan[NUM_CHANNELS];
- } imc[NUM_IMC];
-};
-static int skx_num_sockets;
-
-struct skx_pvt {
- struct skx_imc *imc;
-};
-
-struct decoded_addr {
- struct skx_dev *dev;
- u64 addr;
- int socket;
- int imc;
- int channel;
- u64 chan_addr;
- int sktways;
- int chanways;
- int dimm;
- int rank;
- int channel_rank;
- u64 rank_address;
- int row;
- int column;
- int bank_address;
- int bank_group;
-};
-
-static struct skx_dev *get_skx_dev(struct pci_bus *bus, u8 idx)
-{
- struct skx_dev *d;
-
- list_for_each_entry(d, &skx_edac_list, list) {
- if (d->seg == pci_domain_nr(bus) && d->bus[idx] == bus->number)
- return d;
- }
-
- return NULL;
-}
-
-enum munittype {
- CHAN0, CHAN1, CHAN2, SAD_ALL, UTIL_ALL, SAD
-};
-
-struct munit {
- u16 did;
- u16 devfn[NUM_IMC];
- u8 busidx;
- u8 per_socket;
- enum munittype mtype;
-};
-
-/*
- * List of PCI device ids that we need together with some device
- * number and function numbers to tell which memory controller the
- * device belongs to.
- */
-static const struct munit skx_all_munits[] = {
- { 0x2054, { }, 1, 1, SAD_ALL },
- { 0x2055, { }, 1, 1, UTIL_ALL },
- { 0x2040, { PCI_DEVFN(10, 0), PCI_DEVFN(12, 0) }, 2, 2, CHAN0 },
- { 0x2044, { PCI_DEVFN(10, 4), PCI_DEVFN(12, 4) }, 2, 2, CHAN1 },
- { 0x2048, { PCI_DEVFN(11, 0), PCI_DEVFN(13, 0) }, 2, 2, CHAN2 },
- { 0x208e, { }, 1, 0, SAD },
- { }
-};
-
-/*
- * We use the per-socket device 0x2016 to count how many sockets are present,
- * and to detemine which PCI buses are associated with each socket. Allocate
- * and build the full list of all the skx_dev structures that we need here.
- */
-static int get_all_bus_mappings(void)
-{
- struct pci_dev *pdev, *prev;
- struct skx_dev *d;
- u32 reg;
- int ndev = 0;
-
- prev = NULL;
- for (;;) {
- pdev = pci_get_device(PCI_VENDOR_ID_INTEL, 0x2016, prev);
- if (!pdev)
- break;
- ndev++;
- d = kzalloc(sizeof(*d), GFP_KERNEL);
- if (!d) {
- pci_dev_put(pdev);
- return -ENOMEM;
- }
- d->seg = pci_domain_nr(pdev->bus);
- pci_read_config_dword(pdev, 0xCC, &reg);
- d->bus[0] = GET_BITFIELD(reg, 0, 7);
- d->bus[1] = GET_BITFIELD(reg, 8, 15);
- d->bus[2] = GET_BITFIELD(reg, 16, 23);
- d->bus[3] = GET_BITFIELD(reg, 24, 31);
- edac_dbg(2, "busses: 0x%x, 0x%x, 0x%x, 0x%x\n",
- d->bus[0], d->bus[1], d->bus[2], d->bus[3]);
- list_add_tail(&d->list, &skx_edac_list);
- skx_num_sockets++;
- prev = pdev;
- }
-
- return ndev;
-}
-
-static int get_all_munits(const struct munit *m)
-{
- struct pci_dev *pdev, *prev;
- struct skx_dev *d;
- u32 reg;
- int i = 0, ndev = 0;
-
- prev = NULL;
- for (;;) {
- pdev = pci_get_device(PCI_VENDOR_ID_INTEL, m->did, prev);
- if (!pdev)
- break;
- ndev++;
- if (m->per_socket == NUM_IMC) {
- for (i = 0; i < NUM_IMC; i++)
- if (m->devfn[i] == pdev->devfn)
- break;
- if (i == NUM_IMC)
- goto fail;
- }
- d = get_skx_dev(pdev->bus, m->busidx);
- if (!d)
- goto fail;
-
- /* Be sure that the device is enabled */
- if (unlikely(pci_enable_device(pdev) < 0)) {
- skx_printk(KERN_ERR, "Couldn't enable device %04x:%04x\n",
- PCI_VENDOR_ID_INTEL, m->did);
- goto fail;
- }
-
- switch (m->mtype) {
- case CHAN0: case CHAN1: case CHAN2:
- pci_dev_get(pdev);
- d->imc[i].chan[m->mtype].cdev = pdev;
- break;
- case SAD_ALL:
- pci_dev_get(pdev);
- d->sad_all = pdev;
- break;
- case UTIL_ALL:
- pci_dev_get(pdev);
- d->util_all = pdev;
- break;
- case SAD:
- /*
- * one of these devices per core, including cores
- * that don't exist on this SKU. Ignore any that
- * read a route table of zero, make sure all the
- * non-zero values match.
- */
- pci_read_config_dword(pdev, 0xB4, &reg);
- if (reg != 0) {
- if (d->mcroute == 0)
- d->mcroute = reg;
- else if (d->mcroute != reg) {
- skx_printk(KERN_ERR,
- "mcroute mismatch\n");
- goto fail;
- }
- }
- ndev--;
- break;
- }
-
- prev = pdev;
- }
-
- return ndev;
-fail:
- pci_dev_put(pdev);
- return -ENODEV;
-}
-
-static const struct x86_cpu_id skx_cpuids[] = {
- { X86_VENDOR_INTEL, 6, INTEL_FAM6_SKYLAKE_X, 0, 0 },
- { }
-};
-MODULE_DEVICE_TABLE(x86cpu, skx_cpuids);
-
-static u8 get_src_id(struct skx_dev *d)
-{
- u32 reg;
-
- pci_read_config_dword(d->util_all, 0xF0, &reg);
-
- return GET_BITFIELD(reg, 12, 14);
-}
-
-static u8 skx_get_node_id(struct skx_dev *d)
-{
- u32 reg;
-
- pci_read_config_dword(d->util_all, 0xF4, &reg);
-
- return GET_BITFIELD(reg, 0, 2);
-}
-
-static int get_dimm_attr(u32 reg, int lobit, int hibit, int add, int minval,
- int maxval, char *name)
-{
- u32 val = GET_BITFIELD(reg, lobit, hibit);
-
- if (val < minval || val > maxval) {
- edac_dbg(2, "bad %s = %d (raw=0x%x)\n", name, val, reg);
- return -EINVAL;
- }
- return val + add;
-}
-
-#define IS_DIMM_PRESENT(mtr) GET_BITFIELD((mtr), 15, 15)
-#define IS_NVDIMM_PRESENT(mcddrtcfg, i) GET_BITFIELD((mcddrtcfg), (i), (i))
-
-#define numrank(reg) get_dimm_attr((reg), 12, 13, 0, 0, 2, "ranks")
-#define numrow(reg) get_dimm_attr((reg), 2, 4, 12, 1, 6, "rows")
-#define numcol(reg) get_dimm_attr((reg), 0, 1, 10, 0, 2, "cols")
-
-static int get_width(u32 mtr)
-{
- switch (GET_BITFIELD(mtr, 8, 9)) {
- case 0:
- return DEV_X4;
- case 1:
- return DEV_X8;
- case 2:
- return DEV_X16;
- }
- return DEV_UNKNOWN;
-}
-
-static int skx_get_hi_lo(void)
-{
- struct pci_dev *pdev;
- u32 reg;
-
- pdev = pci_get_device(PCI_VENDOR_ID_INTEL, 0x2034, NULL);
- if (!pdev) {
- edac_dbg(0, "Can't get tolm/tohm\n");
- return -ENODEV;
- }
-
- pci_read_config_dword(pdev, 0xD0, &reg);
- skx_tolm = reg;
- pci_read_config_dword(pdev, 0xD4, &reg);
- skx_tohm = reg;
- pci_read_config_dword(pdev, 0xD8, &reg);
- skx_tohm |= (u64)reg << 32;
-
- pci_dev_put(pdev);
- edac_dbg(2, "tolm=0x%llx tohm=0x%llx\n", skx_tolm, skx_tohm);
-
- return 0;
-}
-
-static int get_dimm_info(u32 mtr, u32 amap, struct dimm_info *dimm,
- struct skx_imc *imc, int chan, int dimmno)
-{
- int banks = 16, ranks, rows, cols, npages;
- u64 size;
-
- ranks = numrank(mtr);
- rows = numrow(mtr);
- cols = numcol(mtr);
-
- /*
- * Compute size in 8-byte (2^3) words, then shift to MiB (2^20)
- */
- size = ((1ull << (rows + cols + ranks)) * banks) >> (20 - 3);
- npages = MiB_TO_PAGES(size);
-
- edac_dbg(0, "mc#%d: channel %d, dimm %d, %lld MiB (%d pages) bank: %d, rank: %d, row: 0x%#x, col: 0x%#x\n",
- imc->mc, chan, dimmno, size, npages,
- banks, 1 << ranks, rows, cols);
-
- imc->chan[chan].dimms[dimmno].close_pg = GET_BITFIELD(mtr, 0, 0);
- imc->chan[chan].dimms[dimmno].bank_xor_enable = GET_BITFIELD(mtr, 9, 9);
- imc->chan[chan].dimms[dimmno].fine_grain_bank = GET_BITFIELD(amap, 0, 0);
- imc->chan[chan].dimms[dimmno].rowbits = rows;
- imc->chan[chan].dimms[dimmno].colbits = cols;
-
- dimm->nr_pages = npages;
- dimm->grain = 32;
- dimm->dtype = get_width(mtr);
- dimm->mtype = MEM_DDR4;
- dimm->edac_mode = EDAC_SECDED; /* likely better than this */
- snprintf(dimm->label, sizeof(dimm->label), "CPU_SrcID#%u_MC#%u_Chan#%u_DIMM#%u",
- imc->src_id, imc->lmc, chan, dimmno);
-
- return 1;
-}
-
-static int get_nvdimm_info(struct dimm_info *dimm, struct skx_imc *imc,
- int chan, int dimmno)
-{
- int smbios_handle;
- u32 dev_handle;
- u16 flags;
- u64 size = 0;
-
- nvdimm_count++;
-
- dev_handle = ACPI_NFIT_BUILD_DEVICE_HANDLE(dimmno, chan, imc->lmc,
- imc->src_id, 0);
-
- smbios_handle = nfit_get_smbios_id(dev_handle, &flags);
- if (smbios_handle == -EOPNOTSUPP) {
- pr_warn_once(EDAC_MOD_STR ": Can't find size of NVDIMM. Try enabling CONFIG_ACPI_NFIT\n");
- goto unknown_size;
- }
-
- if (smbios_handle < 0) {
- skx_printk(KERN_ERR, "Can't find handle for NVDIMM ADR=0x%x\n", dev_handle);
- goto unknown_size;
- }
-
- if (flags & ACPI_NFIT_MEM_MAP_FAILED) {
- skx_printk(KERN_ERR, "NVDIMM ADR=0x%x is not mapped\n", dev_handle);
- goto unknown_size;
- }
-
- size = dmi_memdev_size(smbios_handle);
- if (size == ~0ull)
- skx_printk(KERN_ERR, "Can't find size for NVDIMM ADR=0x%x/SMBIOS=0x%x\n",
- dev_handle, smbios_handle);
-
-unknown_size:
- dimm->nr_pages = size >> PAGE_SHIFT;
- dimm->grain = 32;
- dimm->dtype = DEV_UNKNOWN;
- dimm->mtype = MEM_NVDIMM;
- dimm->edac_mode = EDAC_SECDED; /* likely better than this */
-
- edac_dbg(0, "mc#%d: channel %d, dimm %d, %llu MiB (%u pages)\n",
- imc->mc, chan, dimmno, size >> 20, dimm->nr_pages);
-
- snprintf(dimm->label, sizeof(dimm->label), "CPU_SrcID#%u_MC#%u_Chan#%u_DIMM#%u",
- imc->src_id, imc->lmc, chan, dimmno);
-
- return (size == 0 || size == ~0ull) ? 0 : 1;
-}
-
-#define SKX_GET_MTMTR(dev, reg) \
- pci_read_config_dword((dev), 0x87c, &reg)
-
-static bool skx_check_ecc(struct pci_dev *pdev)
-{
- u32 mtmtr;
-
- SKX_GET_MTMTR(pdev, mtmtr);
-
- return !!GET_BITFIELD(mtmtr, 2, 2);
-}
-
-static int skx_get_dimm_config(struct mem_ctl_info *mci)
-{
- struct skx_pvt *pvt = mci->pvt_info;
- struct skx_imc *imc = pvt->imc;
- u32 mtr, amap, mcddrtcfg;
- struct dimm_info *dimm;
- int i, j;
- int ndimms;
-
- for (i = 0; i < NUM_CHANNELS; i++) {
- ndimms = 0;
- pci_read_config_dword(imc->chan[i].cdev, 0x8C, &amap);
- pci_read_config_dword(imc->chan[i].cdev, 0x400, &mcddrtcfg);
- for (j = 0; j < NUM_DIMMS; j++) {
- dimm = EDAC_DIMM_PTR(mci->layers, mci->dimms,
- mci->n_layers, i, j, 0);
- pci_read_config_dword(imc->chan[i].cdev,
- 0x80 + 4*j, &mtr);
- if (IS_DIMM_PRESENT(mtr))
- ndimms += get_dimm_info(mtr, amap, dimm, imc, i, j);
- else if (IS_NVDIMM_PRESENT(mcddrtcfg, j))
- ndimms += get_nvdimm_info(dimm, imc, i, j);
- }
- if (ndimms && !skx_check_ecc(imc->chan[0].cdev)) {
- skx_printk(KERN_ERR, "ECC is disabled on imc %d\n", imc->mc);
- return -ENODEV;
- }
- }
-
- return 0;
-}
-
-static void skx_unregister_mci(struct skx_imc *imc)
-{
- struct mem_ctl_info *mci = imc->mci;
-
- if (!mci)
- return;
-
- edac_dbg(0, "MC%d: mci = %p\n", imc->mc, mci);
-
- /* Remove MC sysfs nodes */
- edac_mc_del_mc(mci->pdev);
-
- edac_dbg(1, "%s: free mci struct\n", mci->ctl_name);
- kfree(mci->ctl_name);
- edac_mc_free(mci);
-}
-
-static int skx_register_mci(struct skx_imc *imc)
-{
- struct mem_ctl_info *mci;
- struct edac_mc_layer layers[2];
- struct pci_dev *pdev = imc->chan[0].cdev;
- struct skx_pvt *pvt;
- int rc;
-
- /* allocate a new MC control structure */
- layers[0].type = EDAC_MC_LAYER_CHANNEL;
- layers[0].size = NUM_CHANNELS;
- layers[0].is_virt_csrow = false;
- layers[1].type = EDAC_MC_LAYER_SLOT;
- layers[1].size = NUM_DIMMS;
- layers[1].is_virt_csrow = true;
- mci = edac_mc_alloc(imc->mc, ARRAY_SIZE(layers), layers,
- sizeof(struct skx_pvt));
-
- if (unlikely(!mci))
- return -ENOMEM;
-
- edac_dbg(0, "MC#%d: mci = %p\n", imc->mc, mci);
-
- /* Associate skx_dev and mci for future usage */
- imc->mci = mci;
- pvt = mci->pvt_info;
- pvt->imc = imc;
-
- mci->ctl_name = kasprintf(GFP_KERNEL, "Skylake Socket#%d IMC#%d",
- imc->node_id, imc->lmc);
- if (!mci->ctl_name) {
- rc = -ENOMEM;
- goto fail0;
- }
-
- mci->mtype_cap = MEM_FLAG_DDR4 | MEM_FLAG_NVDIMM;
- mci->edac_ctl_cap = EDAC_FLAG_NONE;
- mci->edac_cap = EDAC_FLAG_NONE;
- mci->mod_name = EDAC_MOD_STR;
- mci->dev_name = pci_name(imc->chan[0].cdev);
- mci->ctl_page_to_phys = NULL;
-
- rc = skx_get_dimm_config(mci);
- if (rc < 0)
- goto fail;
-
- /* record ptr to the generic device */
- mci->pdev = &pdev->dev;
-
- /* add this new MC control structure to EDAC's list of MCs */
- if (unlikely(edac_mc_add_mc(mci))) {
- edac_dbg(0, "MC: failed edac_mc_add_mc()\n");
- rc = -EINVAL;
- goto fail;
- }
-
- return 0;
-
-fail:
- kfree(mci->ctl_name);
-fail0:
- edac_mc_free(mci);
- imc->mci = NULL;
- return rc;
-}
-
-#define SKX_MAX_SAD 24
-
-#define SKX_GET_SAD(d, i, reg) \
- pci_read_config_dword((d)->sad_all, 0x60 + 8 * (i), &reg)
-#define SKX_GET_ILV(d, i, reg) \
- pci_read_config_dword((d)->sad_all, 0x64 + 8 * (i), &reg)
-
-#define SKX_SAD_MOD3MODE(sad) GET_BITFIELD((sad), 30, 31)
-#define SKX_SAD_MOD3(sad) GET_BITFIELD((sad), 27, 27)
-#define SKX_SAD_LIMIT(sad) (((u64)GET_BITFIELD((sad), 7, 26) << 26) | MASK26)
-#define SKX_SAD_MOD3ASMOD2(sad) GET_BITFIELD((sad), 5, 6)
-#define SKX_SAD_ATTR(sad) GET_BITFIELD((sad), 3, 4)
-#define SKX_SAD_INTERLEAVE(sad) GET_BITFIELD((sad), 1, 2)
-#define SKX_SAD_ENABLE(sad) GET_BITFIELD((sad), 0, 0)
-
-#define SKX_ILV_REMOTE(tgt) (((tgt) & 8) == 0)
-#define SKX_ILV_TARGET(tgt) ((tgt) & 7)
-
-static bool skx_sad_decode(struct decoded_addr *res)
-{
- struct skx_dev *d = list_first_entry(&skx_edac_list, typeof(*d), list);
- u64 addr = res->addr;
- int i, idx, tgt, lchan, shift;
- u32 sad, ilv;
- u64 limit, prev_limit;
- int remote = 0;
-
- /* Simple sanity check for I/O space or out of range */
- if (addr >= skx_tohm || (addr >= skx_tolm && addr < BIT_ULL(32))) {
- edac_dbg(0, "Address 0x%llx out of range\n", addr);
- return false;
- }
-
-restart:
- prev_limit = 0;
- for (i = 0; i < SKX_MAX_SAD; i++) {
- SKX_GET_SAD(d, i, sad);
- limit = SKX_SAD_LIMIT(sad);
- if (SKX_SAD_ENABLE(sad)) {
- if (addr >= prev_limit && addr <= limit)
- goto sad_found;
- }
- prev_limit = limit + 1;
- }
- edac_dbg(0, "No SAD entry for 0x%llx\n", addr);
- return false;
-
-sad_found:
- SKX_GET_ILV(d, i, ilv);
-
- switch (SKX_SAD_INTERLEAVE(sad)) {
- case 0:
- idx = GET_BITFIELD(addr, 6, 8);
- break;
- case 1:
- idx = GET_BITFIELD(addr, 8, 10);
- break;
- case 2:
- idx = GET_BITFIELD(addr, 12, 14);
- break;
- case 3:
- idx = GET_BITFIELD(addr, 30, 32);
- break;
- }
-
- tgt = GET_BITFIELD(ilv, 4 * idx, 4 * idx + 3);
-
- /* If point to another node, find it and start over */
- if (SKX_ILV_REMOTE(tgt)) {
- if (remote) {
- edac_dbg(0, "Double remote!\n");
- return false;
- }
- remote = 1;
- list_for_each_entry(d, &skx_edac_list, list) {
- if (d->imc[0].src_id == SKX_ILV_TARGET(tgt))
- goto restart;
- }
- edac_dbg(0, "Can't find node %d\n", SKX_ILV_TARGET(tgt));
- return false;
- }
-
- if (SKX_SAD_MOD3(sad) == 0)
- lchan = SKX_ILV_TARGET(tgt);
- else {
- switch (SKX_SAD_MOD3MODE(sad)) {
- case 0:
- shift = 6;
- break;
- case 1:
- shift = 8;
- break;
- case 2:
- shift = 12;
- break;
- default:
- edac_dbg(0, "illegal mod3mode\n");
- return false;
- }
- switch (SKX_SAD_MOD3ASMOD2(sad)) {
- case 0:
- lchan = (addr >> shift) % 3;
- break;
- case 1:
- lchan = (addr >> shift) % 2;
- break;
- case 2:
- lchan = (addr >> shift) % 2;
- lchan = (lchan << 1) | !lchan;
- break;
- case 3:
- lchan = ((addr >> shift) % 2) << 1;
- break;
- }
- lchan = (lchan << 1) | (SKX_ILV_TARGET(tgt) & 1);
- }
-
- res->dev = d;
- res->socket = d->imc[0].src_id;
- res->imc = GET_BITFIELD(d->mcroute, lchan * 3, lchan * 3 + 2);
- res->channel = GET_BITFIELD(d->mcroute, lchan * 2 + 18, lchan * 2 + 19);
-
- edac_dbg(2, "0x%llx: socket=%d imc=%d channel=%d\n",
- res->addr, res->socket, res->imc, res->channel);
- return true;
-}
-
-#define SKX_MAX_TAD 8
-
-#define SKX_GET_TADBASE(d, mc, i, reg) \
- pci_read_config_dword((d)->imc[mc].chan[0].cdev, 0x850 + 4 * (i), &reg)
-#define SKX_GET_TADWAYNESS(d, mc, i, reg) \
- pci_read_config_dword((d)->imc[mc].chan[0].cdev, 0x880 + 4 * (i), &reg)
-#define SKX_GET_TADCHNILVOFFSET(d, mc, ch, i, reg) \
- pci_read_config_dword((d)->imc[mc].chan[ch].cdev, 0x90 + 4 * (i), &reg)
-
-#define SKX_TAD_BASE(b) ((u64)GET_BITFIELD((b), 12, 31) << 26)
-#define SKX_TAD_SKT_GRAN(b) GET_BITFIELD((b), 4, 5)
-#define SKX_TAD_CHN_GRAN(b) GET_BITFIELD((b), 6, 7)
-#define SKX_TAD_LIMIT(b) (((u64)GET_BITFIELD((b), 12, 31) << 26) | MASK26)
-#define SKX_TAD_OFFSET(b) ((u64)GET_BITFIELD((b), 4, 23) << 26)
-#define SKX_TAD_SKTWAYS(b) (1 << GET_BITFIELD((b), 10, 11))
-#define SKX_TAD_CHNWAYS(b) (GET_BITFIELD((b), 8, 9) + 1)
-
-/* which bit used for both socket and channel interleave */
-static int skx_granularity[] = { 6, 8, 12, 30 };
-
-static u64 skx_do_interleave(u64 addr, int shift, int ways, u64 lowbits)
-{
- addr >>= shift;
- addr /= ways;
- addr <<= shift;
-
- return addr | (lowbits & ((1ull << shift) - 1));
-}
-
-static bool skx_tad_decode(struct decoded_addr *res)
-{
- int i;
- u32 base, wayness, chnilvoffset;
- int skt_interleave_bit, chn_interleave_bit;
- u64 channel_addr;
-
- for (i = 0; i < SKX_MAX_TAD; i++) {
- SKX_GET_TADBASE(res->dev, res->imc, i, base);
- SKX_GET_TADWAYNESS(res->dev, res->imc, i, wayness);
- if (SKX_TAD_BASE(base) <= res->addr && res->addr <= SKX_TAD_LIMIT(wayness))
- goto tad_found;
- }
- edac_dbg(0, "No TAD entry for 0x%llx\n", res->addr);
- return false;
-
-tad_found:
- res->sktways = SKX_TAD_SKTWAYS(wayness);
- res->chanways = SKX_TAD_CHNWAYS(wayness);
- skt_interleave_bit = skx_granularity[SKX_TAD_SKT_GRAN(base)];
- chn_interleave_bit = skx_granularity[SKX_TAD_CHN_GRAN(base)];
-
- SKX_GET_TADCHNILVOFFSET(res->dev, res->imc, res->channel, i, chnilvoffset);
- channel_addr = res->addr - SKX_TAD_OFFSET(chnilvoffset);
-
- if (res->chanways == 3 && skt_interleave_bit > chn_interleave_bit) {
- /* Must handle channel first, then socket */
- channel_addr = skx_do_interleave(channel_addr, chn_interleave_bit,
- res->chanways, channel_addr);
- channel_addr = skx_do_interleave(channel_addr, skt_interleave_bit,
- res->sktways, channel_addr);
- } else {
- /* Handle socket then channel. Preserve low bits from original address */
- channel_addr = skx_do_interleave(channel_addr, skt_interleave_bit,
- res->sktways, res->addr);
- channel_addr = skx_do_interleave(channel_addr, chn_interleave_bit,
- res->chanways, res->addr);
- }
-
- res->chan_addr = channel_addr;
-
- edac_dbg(2, "0x%llx: chan_addr=0x%llx sktways=%d chanways=%d\n",
- res->addr, res->chan_addr, res->sktways, res->chanways);
- return true;
-}
-
-#define SKX_MAX_RIR 4
-
-#define SKX_GET_RIRWAYNESS(d, mc, ch, i, reg) \
- pci_read_config_dword((d)->imc[mc].chan[ch].cdev, \
- 0x108 + 4 * (i), &reg)
-#define SKX_GET_RIRILV(d, mc, ch, idx, i, reg) \
- pci_read_config_dword((d)->imc[mc].chan[ch].cdev, \
- 0x120 + 16 * idx + 4 * (i), &reg)
-
-#define SKX_RIR_VALID(b) GET_BITFIELD((b), 31, 31)
-#define SKX_RIR_LIMIT(b) (((u64)GET_BITFIELD((b), 1, 11) << 29) | MASK29)
-#define SKX_RIR_WAYS(b) (1 << GET_BITFIELD((b), 28, 29))
-#define SKX_RIR_CHAN_RANK(b) GET_BITFIELD((b), 16, 19)
-#define SKX_RIR_OFFSET(b) ((u64)(GET_BITFIELD((b), 2, 15) << 26))
-
-static bool skx_rir_decode(struct decoded_addr *res)
-{
- int i, idx, chan_rank;
- int shift;
- u32 rirway, rirlv;
- u64 rank_addr, prev_limit = 0, limit;
-
- if (res->dev->imc[res->imc].chan[res->channel].dimms[0].close_pg)
- shift = 6;
- else
- shift = 13;
-
- for (i = 0; i < SKX_MAX_RIR; i++) {
- SKX_GET_RIRWAYNESS(res->dev, res->imc, res->channel, i, rirway);
- limit = SKX_RIR_LIMIT(rirway);
- if (SKX_RIR_VALID(rirway)) {
- if (prev_limit <= res->chan_addr &&
- res->chan_addr <= limit)
- goto rir_found;
- }
- prev_limit = limit;
- }
- edac_dbg(0, "No RIR entry for 0x%llx\n", res->addr);
- return false;
-
-rir_found:
- rank_addr = res->chan_addr >> shift;
- rank_addr /= SKX_RIR_WAYS(rirway);
- rank_addr <<= shift;
- rank_addr |= res->chan_addr & GENMASK_ULL(shift - 1, 0);
-
- res->rank_address = rank_addr;
- idx = (res->chan_addr >> shift) % SKX_RIR_WAYS(rirway);
-
- SKX_GET_RIRILV(res->dev, res->imc, res->channel, idx, i, rirlv);
- res->rank_address = rank_addr - SKX_RIR_OFFSET(rirlv);
- chan_rank = SKX_RIR_CHAN_RANK(rirlv);
- res->channel_rank = chan_rank;
- res->dimm = chan_rank / 4;
- res->rank = chan_rank % 4;
-
- edac_dbg(2, "0x%llx: dimm=%d rank=%d chan_rank=%d rank_addr=0x%llx\n",
- res->addr, res->dimm, res->rank,
- res->channel_rank, res->rank_address);
- return true;
-}
-
-static u8 skx_close_row[] = {
- 15, 16, 17, 18, 20, 21, 22, 28, 10, 11, 12, 13, 29, 30, 31, 32, 33
-};
-static u8 skx_close_column[] = {
- 3, 4, 5, 14, 19, 23, 24, 25, 26, 27
-};
-static u8 skx_open_row[] = {
- 14, 15, 16, 20, 28, 21, 22, 23, 24, 25, 26, 27, 29, 30, 31, 32, 33
-};
-static u8 skx_open_column[] = {
- 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
-};
-static u8 skx_open_fine_column[] = {
- 3, 4, 5, 7, 8, 9, 10, 11, 12, 13
-};
-
-static int skx_bits(u64 addr, int nbits, u8 *bits)
-{
- int i, res = 0;
-
- for (i = 0; i < nbits; i++)
- res |= ((addr >> bits[i]) & 1) << i;
- return res;
-}
-
-static int skx_bank_bits(u64 addr, int b0, int b1, int do_xor, int x0, int x1)
-{
- int ret = GET_BITFIELD(addr, b0, b0) | (GET_BITFIELD(addr, b1, b1) << 1);
-
- if (do_xor)
- ret ^= GET_BITFIELD(addr, x0, x0) | (GET_BITFIELD(addr, x1, x1) << 1);
-
- return ret;
-}
-
-static bool skx_mad_decode(struct decoded_addr *r)
-{
- struct skx_dimm *dimm = &r->dev->imc[r->imc].chan[r->channel].dimms[r->dimm];
- int bg0 = dimm->fine_grain_bank ? 6 : 13;
-
- if (dimm->close_pg) {
- r->row = skx_bits(r->rank_address, dimm->rowbits, skx_close_row);
- r->column = skx_bits(r->rank_address, dimm->colbits, skx_close_column);
- r->column |= 0x400; /* C10 is autoprecharge, always set */
- r->bank_address = skx_bank_bits(r->rank_address, 8, 9, dimm->bank_xor_enable, 22, 28);
- r->bank_group = skx_bank_bits(r->rank_address, 6, 7, dimm->bank_xor_enable, 20, 21);
- } else {
- r->row = skx_bits(r->rank_address, dimm->rowbits, skx_open_row);
- if (dimm->fine_grain_bank)
- r->column = skx_bits(r->rank_address, dimm->colbits, skx_open_fine_column);
- else
- r->column = skx_bits(r->rank_address, dimm->colbits, skx_open_column);
- r->bank_address = skx_bank_bits(r->rank_address, 18, 19, dimm->bank_xor_enable, 22, 23);
- r->bank_group = skx_bank_bits(r->rank_address, bg0, 17, dimm->bank_xor_enable, 20, 21);
- }
- r->row &= (1u << dimm->rowbits) - 1;
-
- edac_dbg(2, "0x%llx: row=0x%x col=0x%x bank_addr=%d bank_group=%d\n",
- r->addr, r->row, r->column, r->bank_address,
- r->bank_group);
- return true;
-}
-
-static bool skx_decode(struct decoded_addr *res)
-{
-
- return skx_sad_decode(res) && skx_tad_decode(res) &&
- skx_rir_decode(res) && skx_mad_decode(res);
-}
-
-static bool skx_adxl_decode(struct decoded_addr *res)
-
-{
- int i, len = 0;
-
- if (res->addr >= skx_tohm || (res->addr >= skx_tolm &&
- res->addr < BIT_ULL(32))) {
- edac_dbg(0, "Address 0x%llx out of range\n", res->addr);
- return false;
- }
-
- if (adxl_decode(res->addr, adxl_values)) {
- edac_dbg(0, "Failed to decode 0x%llx\n", res->addr);
- return false;
- }
-
- res->socket = (int)adxl_values[component_indices[INDEX_SOCKET]];
- res->imc = (int)adxl_values[component_indices[INDEX_MEMCTRL]];
- res->channel = (int)adxl_values[component_indices[INDEX_CHANNEL]];
- res->dimm = (int)adxl_values[component_indices[INDEX_DIMM]];
-
- for (i = 0; i < adxl_component_count; i++) {
- if (adxl_values[i] == ~0x0ull)
- continue;
-
- len += snprintf(adxl_msg + len, MSG_SIZE - len, " %s:0x%llx",
- adxl_component_names[i], adxl_values[i]);
- if (MSG_SIZE - len <= 0)
- break;
- }
-
- return true;
-}
-
-static void skx_mce_output_error(struct mem_ctl_info *mci,
- const struct mce *m,
- struct decoded_addr *res)
-{
- enum hw_event_mc_err_type tp_event;
- char *type, *optype;
- bool ripv = GET_BITFIELD(m->mcgstatus, 0, 0);
- bool overflow = GET_BITFIELD(m->status, 62, 62);
- bool uncorrected_error = GET_BITFIELD(m->status, 61, 61);
- bool recoverable;
- u32 core_err_cnt = GET_BITFIELD(m->status, 38, 52);
- u32 mscod = GET_BITFIELD(m->status, 16, 31);
- u32 errcode = GET_BITFIELD(m->status, 0, 15);
- u32 optypenum = GET_BITFIELD(m->status, 4, 6);
-
- recoverable = GET_BITFIELD(m->status, 56, 56);
-
- if (uncorrected_error) {
- core_err_cnt = 1;
- if (ripv) {
- type = "FATAL";
- tp_event = HW_EVENT_ERR_FATAL;
- } else {
- type = "NON_FATAL";
- tp_event = HW_EVENT_ERR_UNCORRECTED;
- }
- } else {
- type = "CORRECTED";
- tp_event = HW_EVENT_ERR_CORRECTED;
- }
-
- /*
- * According with Table 15-9 of the Intel Architecture spec vol 3A,
- * memory errors should fit in this mask:
- * 000f 0000 1mmm cccc (binary)
- * where:
- * f = Correction Report Filtering Bit. If 1, subsequent errors
- * won't be shown
- * mmm = error type
- * cccc = channel
- * If the mask doesn't match, report an error to the parsing logic
- */
- if (!((errcode & 0xef80) == 0x80)) {
- optype = "Can't parse: it is not a mem";
- } else {
- switch (optypenum) {
- case 0:
- optype = "generic undef request error";
- break;
- case 1:
- optype = "memory read error";
- break;
- case 2:
- optype = "memory write error";
- break;
- case 3:
- optype = "addr/cmd error";
- break;
- case 4:
- optype = "memory scrubbing error";
- break;
- default:
- optype = "reserved";
- break;
- }
- }
- if (adxl_component_count) {
- snprintf(skx_msg, MSG_SIZE, "%s%s err_code:0x%04x:0x%04x %s",
- overflow ? " OVERFLOW" : "",
- (uncorrected_error && recoverable) ? " recoverable" : "",
- mscod, errcode, adxl_msg);
- } else {
- snprintf(skx_msg, MSG_SIZE,
- "%s%s err_code:0x%04x:0x%04x socket:%d imc:%d rank:%d bg:%d ba:%d row:0x%x col:0x%x",
- overflow ? " OVERFLOW" : "",
- (uncorrected_error && recoverable) ? " recoverable" : "",
- mscod, errcode,
- res->socket, res->imc, res->rank,
- res->bank_group, res->bank_address, res->row, res->column);
- }
-
- edac_dbg(0, "%s\n", skx_msg);
-
- /* Call the helper to output message */
- edac_mc_handle_error(tp_event, mci, core_err_cnt,
- m->addr >> PAGE_SHIFT, m->addr & ~PAGE_MASK, 0,
- res->channel, res->dimm, -1,
- optype, skx_msg);
-}
-
-static struct mem_ctl_info *get_mci(int src_id, int lmc)
-{
- struct skx_dev *d;
-
- if (lmc > NUM_IMC - 1) {
- skx_printk(KERN_ERR, "Bad lmc %d\n", lmc);
- return NULL;
- }
-
- list_for_each_entry(d, &skx_edac_list, list) {
- if (d->imc[0].src_id == src_id)
- return d->imc[lmc].mci;
- }
-
- skx_printk(KERN_ERR, "No mci for src_id %d lmc %d\n", src_id, lmc);
-
- return NULL;
-}
-
-static int skx_mce_check_error(struct notifier_block *nb, unsigned long val,
- void *data)
-{
- struct mce *mce = (struct mce *)data;
- struct decoded_addr res;
- struct mem_ctl_info *mci;
- char *type;
-
- if (edac_get_report_status() == EDAC_REPORTING_DISABLED)
- return NOTIFY_DONE;
-
- /* ignore unless this is memory related with an address */
- if ((mce->status & 0xefff) >> 7 != 1 || !(mce->status & MCI_STATUS_ADDRV))
- return NOTIFY_DONE;
-
- memset(&res, 0, sizeof(res));
- res.addr = mce->addr;
-
- if (adxl_component_count) {
- if (!skx_adxl_decode(&res))
- return NOTIFY_DONE;
-
- mci = get_mci(res.socket, res.imc);
- } else {
- if (!skx_decode(&res))
- return NOTIFY_DONE;
-
- mci = res.dev->imc[res.imc].mci;
- }
-
- if (!mci)
- return NOTIFY_DONE;
-
- if (mce->mcgstatus & MCG_STATUS_MCIP)
- type = "Exception";
- else
- type = "Event";
-
- skx_mc_printk(mci, KERN_DEBUG, "HANDLING MCE MEMORY ERROR\n");
-
- skx_mc_printk(mci, KERN_DEBUG, "CPU %d: Machine Check %s: 0x%llx "
- "Bank %d: %016Lx\n", mce->extcpu, type,
- mce->mcgstatus, mce->bank, mce->status);
- skx_mc_printk(mci, KERN_DEBUG, "TSC 0x%llx ", mce->tsc);
- skx_mc_printk(mci, KERN_DEBUG, "ADDR 0x%llx ", mce->addr);
- skx_mc_printk(mci, KERN_DEBUG, "MISC 0x%llx ", mce->misc);
-
- skx_mc_printk(mci, KERN_DEBUG, "PROCESSOR %u:0x%x TIME %llu SOCKET "
- "%u APIC 0x%x\n", mce->cpuvendor, mce->cpuid,
- mce->time, mce->socketid, mce->apicid);
-
- skx_mce_output_error(mci, mce, &res);
-
- return NOTIFY_DONE;
-}
-
-static struct notifier_block skx_mce_dec = {
- .notifier_call = skx_mce_check_error,
- .priority = MCE_PRIO_EDAC,
-};
-
-#ifdef CONFIG_EDAC_DEBUG
-/*
- * Debug feature.
- * Exercise the address decode logic by writing an address to
- * /sys/kernel/debug/edac/skx_test/addr.
- */
-static struct dentry *skx_test;
-
-static int debugfs_u64_set(void *data, u64 val)
-{
- struct mce m;
-
- pr_warn_once("Fake error to 0x%llx injected via debugfs\n", val);
-
- memset(&m, 0, sizeof(m));
- /* ADDRV + MemRd + Unknown channel */
- m.status = MCI_STATUS_ADDRV + 0x90;
- /* One corrected error */
- m.status |= BIT_ULL(MCI_STATUS_CEC_SHIFT);
- m.addr = val;
- skx_mce_check_error(NULL, 0, &m);
-
- return 0;
-}
-DEFINE_SIMPLE_ATTRIBUTE(fops_u64_wo, NULL, debugfs_u64_set, "%llu\n");
-
-static void setup_skx_debug(void)
-{
- skx_test = edac_debugfs_create_dir("skx_test");
- if (!skx_test)
- return;
-
- if (!edac_debugfs_create_file("addr", 0200, skx_test,
- NULL, &fops_u64_wo)) {
- debugfs_remove(skx_test);
- skx_test = NULL;
- }
-}
-
-static void teardown_skx_debug(void)
-{
- debugfs_remove_recursive(skx_test);
-}
-#else
-static void setup_skx_debug(void) {}
-static void teardown_skx_debug(void) {}
-#endif /*CONFIG_EDAC_DEBUG*/
-
-static void skx_remove(void)
-{
- int i, j;
- struct skx_dev *d, *tmp;
-
- edac_dbg(0, "\n");
-
- list_for_each_entry_safe(d, tmp, &skx_edac_list, list) {
- list_del(&d->list);
- for (i = 0; i < NUM_IMC; i++) {
- skx_unregister_mci(&d->imc[i]);
- for (j = 0; j < NUM_CHANNELS; j++)
- pci_dev_put(d->imc[i].chan[j].cdev);
- }
- pci_dev_put(d->util_all);
- pci_dev_put(d->sad_all);
-
- kfree(d);
- }
-}
-
-static void __init skx_adxl_get(void)
-{
- const char * const *names;
- int i, j;
-
- names = adxl_get_component_names();
- if (!names) {
- skx_printk(KERN_NOTICE, "No firmware support for address translation.");
- skx_printk(KERN_CONT, " Only decoding DDR4 address!\n");
- return;
- }
-
- for (i = 0; i < INDEX_MAX; i++) {
- for (j = 0; names[j]; j++) {
- if (!strcmp(component_names[i], names[j])) {
- component_indices[i] = j;
- break;
- }
- }
-
- if (!names[j])
- goto err;
- }
-
- adxl_component_names = names;
- while (*names++)
- adxl_component_count++;
-
- adxl_values = kcalloc(adxl_component_count, sizeof(*adxl_values),
- GFP_KERNEL);
- if (!adxl_values) {
- adxl_component_count = 0;
- return;
- }
-
- adxl_msg = kzalloc(MSG_SIZE, GFP_KERNEL);
- if (!adxl_msg) {
- adxl_component_count = 0;
- kfree(adxl_values);
- }
-
- return;
-err:
- skx_printk(KERN_ERR, "'%s' is not matched from DSM parameters: ",
- component_names[i]);
- for (j = 0; names[j]; j++)
- skx_printk(KERN_CONT, "%s ", names[j]);
- skx_printk(KERN_CONT, "\n");
-}
-
-static void __exit skx_adxl_put(void)
-{
- kfree(adxl_values);
- kfree(adxl_msg);
-}
-
-/*
- * skx_init:
- * make sure we are running on the correct cpu model
- * search for all the devices we need
- * check which DIMMs are present.
- */
-static int __init skx_init(void)
-{
- const struct x86_cpu_id *id;
- const struct munit *m;
- const char *owner;
- int rc = 0, i;
- u8 mc = 0, src_id, node_id;
- struct skx_dev *d;
-
- edac_dbg(2, "\n");
-
- owner = edac_get_owner();
- if (owner && strncmp(owner, EDAC_MOD_STR, sizeof(EDAC_MOD_STR)))
- return -EBUSY;
-
- id = x86_match_cpu(skx_cpuids);
- if (!id)
- return -ENODEV;
-
- rc = skx_get_hi_lo();
- if (rc)
- return rc;
-
- rc = get_all_bus_mappings();
- if (rc < 0)
- goto fail;
- if (rc == 0) {
- edac_dbg(2, "No memory controllers found\n");
- return -ENODEV;
- }
-
- for (m = skx_all_munits; m->did; m++) {
- rc = get_all_munits(m);
- if (rc < 0)
- goto fail;
- if (rc != m->per_socket * skx_num_sockets) {
- edac_dbg(2, "Expected %d, got %d of 0x%x\n",
- m->per_socket * skx_num_sockets, rc, m->did);
- rc = -ENODEV;
- goto fail;
- }
- }
-
- list_for_each_entry(d, &skx_edac_list, list) {
- src_id = get_src_id(d);
- node_id = skx_get_node_id(d);
- edac_dbg(2, "src_id=%d node_id=%d\n", src_id, node_id);
- for (i = 0; i < NUM_IMC; i++) {
- d->imc[i].mc = mc++;
- d->imc[i].lmc = i;
- d->imc[i].src_id = src_id;
- d->imc[i].node_id = node_id;
- rc = skx_register_mci(&d->imc[i]);
- if (rc < 0)
- goto fail;
- }
- }
-
- skx_msg = kzalloc(MSG_SIZE, GFP_KERNEL);
- if (!skx_msg) {
- rc = -ENOMEM;
- goto fail;
- }
-
- if (nvdimm_count)
- skx_adxl_get();
-
- /* Ensure that the OPSTATE is set correctly for POLL or NMI */
- opstate_init();
-
- setup_skx_debug();
-
- mce_register_decode_chain(&skx_mce_dec);
-
- return 0;
-fail:
- skx_remove();
- return rc;
-}
-
-static void __exit skx_exit(void)
-{
- edac_dbg(2, "\n");
- mce_unregister_decode_chain(&skx_mce_dec);
- teardown_skx_debug();
- if (nvdimm_count)
- skx_adxl_put();
- kfree(skx_msg);
- skx_remove();
-}
-
-module_init(skx_init);
-module_exit(skx_exit);
-
-module_param(edac_op_state, int, 0444);
-MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");
-
-MODULE_LICENSE("GPL v2");
-MODULE_AUTHOR("Tony Luck");
-MODULE_DESCRIPTION("MC Driver for Intel Skylake server processors");
diff --git a/drivers/firmware/efi/arm-runtime.c b/drivers/firmware/efi/arm-runtime.c
index f99995666f86..0c1af675c338 100644
--- a/drivers/firmware/efi/arm-runtime.c
+++ b/drivers/firmware/efi/arm-runtime.c
@@ -42,10 +42,10 @@ static struct ptdump_info efi_ptdump_info = {
static int __init ptdump_init(void)
{
- if (!efi_enabled(EFI_RUNTIME_SERVICES))
- return 0;
+ if (efi_enabled(EFI_RUNTIME_SERVICES))
+ ptdump_debugfs_register(&efi_ptdump_info, "efi_page_tables");
- return ptdump_debugfs_register(&efi_ptdump_info, "efi_page_tables");
+ return 0;
}
device_initcall(ptdump_init);
diff --git a/drivers/firmware/efi/runtime-wrappers.c b/drivers/firmware/efi/runtime-wrappers.c
index 698745c249e8..6fa2df383f22 100644
--- a/drivers/firmware/efi/runtime-wrappers.c
+++ b/drivers/firmware/efi/runtime-wrappers.c
@@ -89,11 +89,24 @@ exit: \
efi_rts_work.status; \
})
+#ifndef arch_efi_save_flags
+#define arch_efi_save_flags(state_flags) local_save_flags(state_flags)
+#define arch_efi_restore_flags(state_flags) local_irq_restore(state_flags)
+#endif
+
+unsigned long efi_call_virt_save_flags(void)
+{
+ unsigned long flags;
+
+ arch_efi_save_flags(flags);
+ return flags;
+}
+
void efi_call_virt_check_flags(unsigned long flags, const char *call)
{
unsigned long cur_flags, mismatch;
- local_save_flags(cur_flags);
+ cur_flags = efi_call_virt_save_flags();
mismatch = flags ^ cur_flags;
if (!WARN_ON_ONCE(mismatch & ARCH_EFI_IRQ_FLAGS_MASK))
@@ -102,7 +115,7 @@ void efi_call_virt_check_flags(unsigned long flags, const char *call)
add_taint(TAINT_FIRMWARE_WORKAROUND, LOCKDEP_NOW_UNRELIABLE);
pr_err_ratelimited(FW_BUG "IRQ flags corrupted (0x%08lx=>0x%08lx) by EFI %s\n",
flags, cur_flags, call);
- local_irq_restore(flags);
+ arch_efi_restore_flags(flags);
}
/*
diff --git a/drivers/firmware/iscsi_ibft.c b/drivers/firmware/iscsi_ibft.c
index 6bc8e6640d71..c51462f5aa1e 100644
--- a/drivers/firmware/iscsi_ibft.c
+++ b/drivers/firmware/iscsi_ibft.c
@@ -542,6 +542,7 @@ static umode_t __init ibft_check_tgt_for(void *data, int type)
case ISCSI_BOOT_TGT_NIC_ASSOC:
case ISCSI_BOOT_TGT_CHAP_TYPE:
rc = S_IRUGO;
+ break;
case ISCSI_BOOT_TGT_NAME:
if (tgt->tgt_name_len)
rc = S_IRUGO;
diff --git a/drivers/firmware/iscsi_ibft_find.c b/drivers/firmware/iscsi_ibft_find.c
index 72d9ea18270b..85c656d04bb0 100644
--- a/drivers/firmware/iscsi_ibft_find.c
+++ b/drivers/firmware/iscsi_ibft_find.c
@@ -104,7 +104,7 @@ unsigned long __init find_ibft_region(unsigned long *sizep)
if (ibft_addr) {
*sizep = PAGE_ALIGN(ibft_addr->header.length);
- return (u64)isa_virt_to_bus(ibft_addr);
+ return (u64)virt_to_phys(ibft_addr);
}
*sizep = 0;
diff --git a/drivers/firmware/memmap.c b/drivers/firmware/memmap.c
index ec4fd253a4e9..d168c87c7d30 100644
--- a/drivers/firmware/memmap.c
+++ b/drivers/firmware/memmap.c
@@ -333,7 +333,7 @@ int __init firmware_map_add_early(u64 start, u64 end, const char *type)
{
struct firmware_map_entry *entry;
- entry = memblock_alloc_nopanic(sizeof(struct firmware_map_entry),
+ entry = memblock_alloc(sizeof(struct firmware_map_entry),
SMP_CACHE_BYTES);
if (WARN_ON(!entry))
return -ENOMEM;
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index b5a2845347ec..3f50526a771f 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -258,6 +258,7 @@ config GPIO_HLWD
tristate "Nintendo Wii (Hollywood) GPIO"
depends on OF_GPIO
select GPIO_GENERIC
+ select GPIOLIB_IRQCHIP
help
Select this to support the GPIO controller of the Nintendo Wii.
@@ -654,6 +655,15 @@ config GPIO_LOONGSON1
help
Say Y or M here to support GPIO on Loongson1 SoCs.
+config GPIO_AMD_FCH
+ tristate "GPIO support for AMD Fusion Controller Hub (G-series SOCs)"
+ help
+ This option enables driver for GPIO on AMDs Fusion Controller Hub,
+ as found on G-series SOCs (eg. GX-412TC)
+
+ Note: This driver doesn't registers itself automatically, as it
+ needs to be provided with platform specific configuration.
+ (See eg. CONFIG_PCENGINES_APU2.)
endmenu
menu "Port-mapped I/O GPIO drivers"
@@ -830,6 +840,13 @@ config GPIO_ADNP
enough to represent all pins, but the driver will assume a
register layout for 64 pins (8 registers).
+config GPIO_GW_PLD
+ tristate "Gateworks PLD GPIO Expander"
+ depends on OF_GPIO
+ help
+ Say yes here to provide access to the Gateworks I2C PLD GPIO
+ Expander. This is used at least on the Cambria GW2358-4.
+
config GPIO_MAX7300
tristate "Maxim MAX7300 GPIO expander"
select GPIO_MAX730X
@@ -1190,6 +1207,13 @@ config GPIO_TPS68470
of the TPS68470 must be available before dependent
drivers are loaded.
+config GPIO_TQMX86
+ tristate "TQ-Systems QTMX86 GPIO"
+ depends on MFD_TQMX86 || COMPILE_TEST
+ select GPIOLIB_IRQCHIP
+ help
+ This driver supports GPIO on the TQMX86 IO controller.
+
config GPIO_TWL4030
tristate "TWL4030, TWL5030, and TPS659x0 GPIOs"
depends on TWL4030_CORE
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 37628f8dbf70..54d55274b93a 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -27,6 +27,7 @@ obj-$(CONFIG_GPIO_ADP5520) += gpio-adp5520.o
obj-$(CONFIG_GPIO_ADP5588) += gpio-adp5588.o
obj-$(CONFIG_GPIO_ALTERA) += gpio-altera.o
obj-$(CONFIG_GPIO_ALTERA_A10SR) += gpio-altera-a10sr.o
+obj-$(CONFIG_GPIO_AMD_FCH) += gpio-amd-fch.o
obj-$(CONFIG_GPIO_AMD8111) += gpio-amd8111.o
obj-$(CONFIG_GPIO_AMDPT) += gpio-amdpt.o
obj-$(CONFIG_GPIO_ARIZONA) += gpio-arizona.o
@@ -55,6 +56,7 @@ obj-$(CONFIG_GPIO_FTGPIO010) += gpio-ftgpio010.o
obj-$(CONFIG_GPIO_GE_FPGA) += gpio-ge.o
obj-$(CONFIG_GPIO_GPIO_MM) += gpio-gpio-mm.o
obj-$(CONFIG_GPIO_GRGPIO) += gpio-grgpio.o
+obj-$(CONFIG_GPIO_GW_PLD) += gpio-gw-pld.o
obj-$(CONFIG_GPIO_HLWD) += gpio-hlwd.o
obj-$(CONFIG_HTC_EGPIO) += gpio-htc-egpio.o
obj-$(CONFIG_GPIO_ICH) += gpio-ich.o
@@ -135,6 +137,7 @@ obj-$(CONFIG_GPIO_TPS6586X) += gpio-tps6586x.o
obj-$(CONFIG_GPIO_TPS65910) += gpio-tps65910.o
obj-$(CONFIG_GPIO_TPS65912) += gpio-tps65912.o
obj-$(CONFIG_GPIO_TPS68470) += gpio-tps68470.o
+obj-$(CONFIG_GPIO_TQMX86) += gpio-tqmx86.o
obj-$(CONFIG_GPIO_TS4800) += gpio-ts4800.o
obj-$(CONFIG_GPIO_TS4900) += gpio-ts4900.o
obj-$(CONFIG_GPIO_TS5500) += gpio-ts5500.o
diff --git a/drivers/gpio/gpio-adnp.c b/drivers/gpio/gpio-adnp.c
index 91b90c0cea73..12acdac85820 100644
--- a/drivers/gpio/gpio-adnp.c
+++ b/drivers/gpio/gpio-adnp.c
@@ -132,8 +132,10 @@ static int adnp_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
if (err < 0)
goto out;
- if (err & BIT(pos))
- err = -EACCES;
+ if (value & BIT(pos)) {
+ err = -EPERM;
+ goto out;
+ }
err = 0;
diff --git a/drivers/gpio/gpio-adp5588.c b/drivers/gpio/gpio-adp5588.c
index cc33d8986ad3..c4a5b499f53e 100644
--- a/drivers/gpio/gpio-adp5588.c
+++ b/drivers/gpio/gpio-adp5588.c
@@ -15,6 +15,7 @@
#include <linux/gpio/driver.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
+#include <linux/of_device.h>
#include <linux/platform_data/adp5588.h>
@@ -33,16 +34,13 @@ struct adp5588_gpio {
struct mutex lock; /* protect cached dir, dat_out */
/* protect serialized access to the interrupt controller bus */
struct mutex irq_lock;
- unsigned gpio_start;
- unsigned irq_base;
uint8_t dat_out[3];
uint8_t dir[3];
- uint8_t int_lvl[3];
+ uint8_t int_lvl_low[3];
+ uint8_t int_lvl_high[3];
uint8_t int_en[3];
uint8_t irq_mask[3];
- uint8_t irq_stat[3];
uint8_t int_input_en[3];
- uint8_t int_lvl_cached[3];
};
static int adp5588_gpio_read(struct i2c_client *client, u8 reg)
@@ -148,16 +146,11 @@ static int adp5588_gpio_direction_output(struct gpio_chip *chip,
}
#ifdef CONFIG_GPIO_ADP5588_IRQ
-static int adp5588_gpio_to_irq(struct gpio_chip *chip, unsigned off)
-{
- struct adp5588_gpio *dev = gpiochip_get_data(chip);
-
- return dev->irq_base + off;
-}
static void adp5588_irq_bus_lock(struct irq_data *d)
{
- struct adp5588_gpio *dev = irq_data_get_irq_chip_data(d);
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct adp5588_gpio *dev = gpiochip_get_data(gc);
mutex_lock(&dev->irq_lock);
}
@@ -172,7 +165,8 @@ static void adp5588_irq_bus_lock(struct irq_data *d)
static void adp5588_irq_bus_sync_unlock(struct irq_data *d)
{
- struct adp5588_gpio *dev = irq_data_get_irq_chip_data(d);
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct adp5588_gpio *dev = gpiochip_get_data(gc);
int i;
for (i = 0; i <= ADP5588_BANK(ADP5588_MAXGPIO); i++) {
@@ -185,15 +179,9 @@ static void adp5588_irq_bus_sync_unlock(struct irq_data *d)
mutex_unlock(&dev->lock);
}
- if (dev->int_lvl_cached[i] != dev->int_lvl[i]) {
- dev->int_lvl_cached[i] = dev->int_lvl[i];
- adp5588_gpio_write(dev->client, GPIO_INT_LVL1 + i,
- dev->int_lvl[i]);
- }
-
if (dev->int_en[i] ^ dev->irq_mask[i]) {
dev->int_en[i] = dev->irq_mask[i];
- adp5588_gpio_write(dev->client, GPIO_INT_EN1 + i,
+ adp5588_gpio_write(dev->client, GPI_EM1 + i,
dev->int_en[i]);
}
}
@@ -203,41 +191,38 @@ static void adp5588_irq_bus_sync_unlock(struct irq_data *d)
static void adp5588_irq_mask(struct irq_data *d)
{
- struct adp5588_gpio *dev = irq_data_get_irq_chip_data(d);
- unsigned gpio = d->irq - dev->irq_base;
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct adp5588_gpio *dev = gpiochip_get_data(gc);
- dev->irq_mask[ADP5588_BANK(gpio)] &= ~ADP5588_BIT(gpio);
+ dev->irq_mask[ADP5588_BANK(d->hwirq)] &= ~ADP5588_BIT(d->hwirq);
}
static void adp5588_irq_unmask(struct irq_data *d)
{
- struct adp5588_gpio *dev = irq_data_get_irq_chip_data(d);
- unsigned gpio = d->irq - dev->irq_base;
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct adp5588_gpio *dev = gpiochip_get_data(gc);
- dev->irq_mask[ADP5588_BANK(gpio)] |= ADP5588_BIT(gpio);
+ dev->irq_mask[ADP5588_BANK(d->hwirq)] |= ADP5588_BIT(d->hwirq);
}
static int adp5588_irq_set_type(struct irq_data *d, unsigned int type)
{
- struct adp5588_gpio *dev = irq_data_get_irq_chip_data(d);
- uint16_t gpio = d->irq - dev->irq_base;
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct adp5588_gpio *dev = gpiochip_get_data(gc);
+ uint16_t gpio = d->hwirq;
unsigned bank, bit;
- if ((type & IRQ_TYPE_EDGE_BOTH)) {
- dev_err(&dev->client->dev, "irq %d: unsupported type %d\n",
- d->irq, type);
- return -EINVAL;
- }
-
bank = ADP5588_BANK(gpio);
bit = ADP5588_BIT(gpio);
- if (type & IRQ_TYPE_LEVEL_HIGH)
- dev->int_lvl[bank] |= bit;
- else if (type & IRQ_TYPE_LEVEL_LOW)
- dev->int_lvl[bank] &= ~bit;
- else
- return -EINVAL;
+ dev->int_lvl_low[bank] &= ~bit;
+ dev->int_lvl_high[bank] &= ~bit;
+
+ if (type & IRQ_TYPE_EDGE_BOTH || type & IRQ_TYPE_LEVEL_HIGH)
+ dev->int_lvl_high[bank] |= bit;
+
+ if (type & IRQ_TYPE_EDGE_BOTH || type & IRQ_TYPE_LEVEL_LOW)
+ dev->int_lvl_low[bank] |= bit;
dev->int_input_en[bank] |= bit;
@@ -253,40 +238,32 @@ static struct irq_chip adp5588_irq_chip = {
.irq_set_type = adp5588_irq_set_type,
};
-static int adp5588_gpio_read_intstat(struct i2c_client *client, u8 *buf)
-{
- int ret = i2c_smbus_read_i2c_block_data(client, GPIO_INT_STAT1, 3, buf);
-
- if (ret < 0)
- dev_err(&client->dev, "Read INT_STAT Error\n");
-
- return ret;
-}
-
static irqreturn_t adp5588_irq_handler(int irq, void *devid)
{
struct adp5588_gpio *dev = devid;
- unsigned status, bank, bit, pending;
- int ret;
- status = adp5588_gpio_read(dev->client, INT_STAT);
-
- if (status & ADP5588_GPI_INT) {
- ret = adp5588_gpio_read_intstat(dev->client, dev->irq_stat);
- if (ret < 0)
- memset(dev->irq_stat, 0, ARRAY_SIZE(dev->irq_stat));
-
- for (bank = 0, bit = 0; bank <= ADP5588_BANK(ADP5588_MAXGPIO);
- bank++, bit = 0) {
- pending = dev->irq_stat[bank] & dev->irq_mask[bank];
-
- while (pending) {
- if (pending & (1 << bit)) {
- handle_nested_irq(dev->irq_base +
- (bank << 3) + bit);
- pending &= ~(1 << bit);
-
- }
- bit++;
+ int status = adp5588_gpio_read(dev->client, INT_STAT);
+
+ if (status & ADP5588_KE_INT) {
+ int ev_cnt = adp5588_gpio_read(dev->client, KEY_LCK_EC_STAT);
+
+ if (ev_cnt > 0) {
+ int i;
+
+ for (i = 0; i < (ev_cnt & ADP5588_KEC); i++) {
+ int key = adp5588_gpio_read(dev->client,
+ Key_EVENTA + i);
+ /* GPIN events begin at 97,
+ * bit 7 indicates logic level
+ */
+ int gpio = (key & 0x7f) - 97;
+ int lvl = key & (1 << 7);
+ int bank = ADP5588_BANK(gpio);
+ int bit = ADP5588_BIT(gpio);
+
+ if ((lvl && dev->int_lvl_high[bank] & bit) ||
+ (!lvl && dev->int_lvl_low[bank] & bit))
+ handle_nested_irq(irq_find_mapping(
+ dev->gpio_chip.irq.domain, gpio));
}
}
}
@@ -299,53 +276,42 @@ static irqreturn_t adp5588_irq_handler(int irq, void *devid)
static int adp5588_irq_setup(struct adp5588_gpio *dev)
{
struct i2c_client *client = dev->client;
+ int ret;
struct adp5588_gpio_platform_data *pdata =
dev_get_platdata(&client->dev);
- unsigned gpio;
- int ret;
+ int irq_base = pdata ? pdata->irq_base : 0;
adp5588_gpio_write(client, CFG, ADP5588_AUTO_INC);
adp5588_gpio_write(client, INT_STAT, -1); /* status is W1C */
- adp5588_gpio_read_intstat(client, dev->irq_stat); /* read to clear */
- dev->irq_base = pdata->irq_base;
mutex_init(&dev->irq_lock);
- for (gpio = 0; gpio < dev->gpio_chip.ngpio; gpio++) {
- int irq = gpio + dev->irq_base;
- irq_set_chip_data(irq, dev);
- irq_set_chip_and_handler(irq, &adp5588_irq_chip,
- handle_level_irq);
- irq_set_nested_thread(irq, 1);
- irq_modify_status(irq, IRQ_NOREQUEST, IRQ_NOPROBE);
- }
-
- ret = request_threaded_irq(client->irq,
- NULL,
- adp5588_irq_handler,
- IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
- dev_name(&client->dev), dev);
+ ret = devm_request_threaded_irq(&client->dev, client->irq,
+ NULL, adp5588_irq_handler, IRQF_ONESHOT
+ | IRQF_TRIGGER_FALLING | IRQF_SHARED,
+ dev_name(&client->dev), dev);
if (ret) {
dev_err(&client->dev, "failed to request irq %d\n",
client->irq);
- goto out;
+ return ret;
}
+ ret = gpiochip_irqchip_add_nested(&dev->gpio_chip,
+ &adp5588_irq_chip, irq_base,
+ handle_simple_irq,
+ IRQ_TYPE_NONE);
+ if (ret) {
+ dev_err(&client->dev,
+ "could not connect irqchip to gpiochip\n");
+ return ret;
+ }
+ gpiochip_set_nested_irqchip(&dev->gpio_chip,
+ &adp5588_irq_chip,
+ client->irq);
- dev->gpio_chip.to_irq = adp5588_gpio_to_irq;
adp5588_gpio_write(client, CFG,
- ADP5588_AUTO_INC | ADP5588_INT_CFG | ADP5588_GPI_INT);
+ ADP5588_AUTO_INC | ADP5588_INT_CFG | ADP5588_KE_IEN);
return 0;
-
-out:
- dev->irq_base = 0;
- return ret;
-}
-
-static void adp5588_irq_teardown(struct adp5588_gpio *dev)
-{
- if (dev->irq_base)
- free_irq(dev->client->irq, dev);
}
#else
@@ -357,24 +323,16 @@ static int adp5588_irq_setup(struct adp5588_gpio *dev)
return 0;
}
-static void adp5588_irq_teardown(struct adp5588_gpio *dev)
-{
-}
#endif /* CONFIG_GPIO_ADP5588_IRQ */
-static int adp5588_gpio_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int adp5588_gpio_probe(struct i2c_client *client)
{
struct adp5588_gpio_platform_data *pdata =
dev_get_platdata(&client->dev);
struct adp5588_gpio *dev;
struct gpio_chip *gc;
int ret, i, revid;
-
- if (!pdata) {
- dev_err(&client->dev, "missing platform data\n");
- return -ENODEV;
- }
+ unsigned int pullup_dis_mask = 0;
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_BYTE_DATA)) {
@@ -394,18 +352,24 @@ static int adp5588_gpio_probe(struct i2c_client *client,
gc->get = adp5588_gpio_get_value;
gc->set = adp5588_gpio_set_value;
gc->can_sleep = true;
+ gc->base = -1;
+ gc->parent = &client->dev;
+
+ if (pdata) {
+ gc->base = pdata->gpio_start;
+ gc->names = pdata->names;
+ pullup_dis_mask = pdata->pullup_dis_mask;
+ }
- gc->base = pdata->gpio_start;
gc->ngpio = ADP5588_MAXGPIO;
gc->label = client->name;
gc->owner = THIS_MODULE;
- gc->names = pdata->names;
mutex_init(&dev->lock);
ret = adp5588_gpio_read(dev->client, DEV_ID);
if (ret < 0)
- goto err;
+ return ret;
revid = ret & ADP5588_DEVICE_ID_MASK;
@@ -414,30 +378,27 @@ static int adp5588_gpio_probe(struct i2c_client *client,
dev->dir[i] = adp5588_gpio_read(client, GPIO_DIR1 + i);
ret |= adp5588_gpio_write(client, KP_GPIO1 + i, 0);
ret |= adp5588_gpio_write(client, GPIO_PULL1 + i,
- (pdata->pullup_dis_mask >> (8 * i)) & 0xFF);
+ (pullup_dis_mask >> (8 * i)) & 0xFF);
ret |= adp5588_gpio_write(client, GPIO_INT_EN1 + i, 0);
if (ret)
- goto err;
+ return ret;
}
- if (pdata->irq_base) {
+ if (client->irq) {
if (WA_DELAYED_READOUT_REVID(revid)) {
dev_warn(&client->dev, "GPIO int not supported\n");
} else {
ret = adp5588_irq_setup(dev);
if (ret)
- goto err;
+ return ret;
}
}
ret = devm_gpiochip_add_data(&client->dev, &dev->gpio_chip, dev);
if (ret)
- goto err_irq;
+ return ret;
- dev_info(&client->dev, "IRQ Base: %d Rev.: %d\n",
- pdata->irq_base, revid);
-
- if (pdata->setup) {
+ if (pdata && pdata->setup) {
ret = pdata->setup(client, gc->base, gc->ngpio, pdata->context);
if (ret < 0)
dev_warn(&client->dev, "setup failed, %d\n", ret);
@@ -446,11 +407,6 @@ static int adp5588_gpio_probe(struct i2c_client *client,
i2c_set_clientdata(client, dev);
return 0;
-
-err_irq:
- adp5588_irq_teardown(dev);
-err:
- return ret;
}
static int adp5588_gpio_remove(struct i2c_client *client)
@@ -460,7 +416,7 @@ static int adp5588_gpio_remove(struct i2c_client *client)
struct adp5588_gpio *dev = i2c_get_clientdata(client);
int ret;
- if (pdata->teardown) {
+ if (pdata && pdata->teardown) {
ret = pdata->teardown(client,
dev->gpio_chip.base, dev->gpio_chip.ngpio,
pdata->context);
@@ -470,7 +426,7 @@ static int adp5588_gpio_remove(struct i2c_client *client)
}
}
- if (dev->irq_base)
+ if (dev->client->irq)
free_irq(dev->client->irq, dev);
return 0;
@@ -480,14 +436,22 @@ static const struct i2c_device_id adp5588_gpio_id[] = {
{DRV_NAME, 0},
{}
};
-
MODULE_DEVICE_TABLE(i2c, adp5588_gpio_id);
+#ifdef CONFIG_OF
+static const struct of_device_id adp5588_gpio_of_id[] = {
+ { .compatible = "adi," DRV_NAME, },
+ {},
+};
+MODULE_DEVICE_TABLE(of, adp5588_gpio_of_id);
+#endif
+
static struct i2c_driver adp5588_gpio_driver = {
.driver = {
- .name = DRV_NAME,
- },
- .probe = adp5588_gpio_probe,
+ .name = DRV_NAME,
+ .of_match_table = of_match_ptr(adp5588_gpio_of_id),
+ },
+ .probe_new = adp5588_gpio_probe,
.remove = adp5588_gpio_remove,
.id_table = adp5588_gpio_id,
};
diff --git a/drivers/gpio/gpio-altera-a10sr.c b/drivers/gpio/gpio-altera-a10sr.c
index 7f9e0304b510..1cea4efccf7c 100644
--- a/drivers/gpio/gpio-altera-a10sr.c
+++ b/drivers/gpio/gpio-altera-a10sr.c
@@ -58,19 +58,20 @@ static void altr_a10sr_gpio_set(struct gpio_chip *chip, unsigned int offset,
static int altr_a10sr_gpio_direction_input(struct gpio_chip *gc,
unsigned int nr)
{
- if (nr >= (ALTR_A10SR_IN_VALID_RANGE_LO - ALTR_A10SR_LED_VALID_SHIFT))
- return 0;
- return -EINVAL;
+ if (nr < (ALTR_A10SR_IN_VALID_RANGE_LO - ALTR_A10SR_LED_VALID_SHIFT))
+ return -EINVAL;
+
+ return 0;
}
static int altr_a10sr_gpio_direction_output(struct gpio_chip *gc,
unsigned int nr, int value)
{
- if (nr <= (ALTR_A10SR_OUT_VALID_RANGE_HI - ALTR_A10SR_LED_VALID_SHIFT)) {
- altr_a10sr_gpio_set(gc, nr, value);
- return 0;
- }
- return -EINVAL;
+ if (nr > (ALTR_A10SR_OUT_VALID_RANGE_HI - ALTR_A10SR_LED_VALID_SHIFT))
+ return -EINVAL;
+
+ altr_a10sr_gpio_set(gc, nr, value);
+ return 0;
}
static const struct gpio_chip altr_a10sr_gc = {
diff --git a/drivers/gpio/gpio-altera.c b/drivers/gpio/gpio-altera.c
index 8c3ff6e2366f..748fdd4e9a53 100644
--- a/drivers/gpio/gpio-altera.c
+++ b/drivers/gpio/gpio-altera.c
@@ -32,9 +32,9 @@
* struct altera_gpio_chip
* @mmchip : memory mapped chip structure.
* @gpio_lock : synchronization lock so that new irq/set/get requests
- will be blocked until the current one completes.
+* will be blocked until the current one completes.
* @interrupt_trigger : specifies the hardware configured IRQ trigger type
- (rising, falling, both, high)
+* (rising, falling, both, high)
* @mapped_irq : kernel mapped irq number.
*/
struct altera_gpio_chip {
diff --git a/drivers/gpio/gpio-amd-fch.c b/drivers/gpio/gpio-amd-fch.c
new file mode 100644
index 000000000000..38c3f4a3d4aa
--- /dev/null
+++ b/drivers/gpio/gpio-amd-fch.c
@@ -0,0 +1,194 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/*
+ * GPIO driver for the AMD G series FCH (eg. GX-412TC)
+ *
+ * Copyright (C) 2018 metux IT consult
+ * Author: Enrico Weigelt, metux IT consult <info@metux.net>
+ *
+ */
+
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/gpio/driver.h>
+#include <linux/platform_data/gpio/gpio-amd-fch.h>
+#include <linux/spinlock.h>
+
+#define AMD_FCH_MMIO_BASE 0xFED80000
+#define AMD_FCH_GPIO_BANK0_BASE 0x1500
+#define AMD_FCH_GPIO_SIZE 0x0300
+
+#define AMD_FCH_GPIO_FLAG_DIRECTION BIT(23)
+#define AMD_FCH_GPIO_FLAG_WRITE BIT(22)
+#define AMD_FCH_GPIO_FLAG_READ BIT(16)
+
+static struct resource amd_fch_gpio_iores =
+ DEFINE_RES_MEM_NAMED(
+ AMD_FCH_MMIO_BASE + AMD_FCH_GPIO_BANK0_BASE,
+ AMD_FCH_GPIO_SIZE,
+ "amd-fch-gpio-iomem");
+
+struct amd_fch_gpio_priv {
+ struct platform_device *pdev;
+ struct gpio_chip gc;
+ void __iomem *base;
+ struct amd_fch_gpio_pdata *pdata;
+ spinlock_t lock;
+};
+
+static void __iomem *amd_fch_gpio_addr(struct amd_fch_gpio_priv *priv,
+ unsigned int gpio)
+{
+ return priv->base + priv->pdata->gpio_reg[gpio]*sizeof(u32);
+}
+
+static int amd_fch_gpio_direction_input(struct gpio_chip *gc,
+ unsigned int offset)
+{
+ unsigned long flags;
+ struct amd_fch_gpio_priv *priv = gpiochip_get_data(gc);
+ void __iomem *ptr = amd_fch_gpio_addr(priv, offset);
+
+ spin_lock_irqsave(&priv->lock, flags);
+ writel_relaxed(readl_relaxed(ptr) & ~AMD_FCH_GPIO_FLAG_DIRECTION, ptr);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return 0;
+}
+
+static int amd_fch_gpio_direction_output(struct gpio_chip *gc,
+ unsigned int gpio, int value)
+{
+ unsigned long flags;
+ struct amd_fch_gpio_priv *priv = gpiochip_get_data(gc);
+ void __iomem *ptr = amd_fch_gpio_addr(priv, gpio);
+ u32 val;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ val = readl_relaxed(ptr);
+ if (value)
+ val |= AMD_FCH_GPIO_FLAG_WRITE;
+ else
+ val &= ~AMD_FCH_GPIO_FLAG_WRITE;
+
+ writel_relaxed(val | AMD_FCH_GPIO_FLAG_DIRECTION, ptr);
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return 0;
+}
+
+static int amd_fch_gpio_get_direction(struct gpio_chip *gc, unsigned int gpio)
+{
+ int ret;
+ unsigned long flags;
+ struct amd_fch_gpio_priv *priv = gpiochip_get_data(gc);
+ void __iomem *ptr = amd_fch_gpio_addr(priv, gpio);
+
+ spin_lock_irqsave(&priv->lock, flags);
+ ret = (readl_relaxed(ptr) & AMD_FCH_GPIO_FLAG_DIRECTION);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return ret;
+}
+
+static void amd_fch_gpio_set(struct gpio_chip *gc,
+ unsigned int gpio, int value)
+{
+ unsigned long flags;
+ struct amd_fch_gpio_priv *priv = gpiochip_get_data(gc);
+ void __iomem *ptr = amd_fch_gpio_addr(priv, gpio);
+ u32 mask;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ mask = readl_relaxed(ptr);
+ if (value)
+ mask |= AMD_FCH_GPIO_FLAG_WRITE;
+ else
+ mask &= ~AMD_FCH_GPIO_FLAG_WRITE;
+ writel_relaxed(mask, ptr);
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static int amd_fch_gpio_get(struct gpio_chip *gc,
+ unsigned int offset)
+{
+ unsigned long flags;
+ int ret;
+ struct amd_fch_gpio_priv *priv = gpiochip_get_data(gc);
+ void __iomem *ptr = amd_fch_gpio_addr(priv, offset);
+
+ spin_lock_irqsave(&priv->lock, flags);
+ ret = (readl_relaxed(ptr) & AMD_FCH_GPIO_FLAG_READ);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return ret;
+}
+
+static int amd_fch_gpio_request(struct gpio_chip *chip,
+ unsigned int gpio_pin)
+{
+ return 0;
+}
+
+static int amd_fch_gpio_probe(struct platform_device *pdev)
+{
+ struct amd_fch_gpio_priv *priv;
+ struct amd_fch_gpio_pdata *pdata;
+
+ pdata = dev_get_platdata(&pdev->dev);
+ if (!pdata) {
+ dev_err(&pdev->dev, "no platform_data\n");
+ return -ENOENT;
+ }
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->pdata = pdata;
+ priv->pdev = pdev;
+
+ priv->gc.owner = THIS_MODULE;
+ priv->gc.parent = &pdev->dev;
+ priv->gc.label = dev_name(&pdev->dev);
+ priv->gc.ngpio = priv->pdata->gpio_num;
+ priv->gc.names = priv->pdata->gpio_names;
+ priv->gc.base = -1;
+ priv->gc.request = amd_fch_gpio_request;
+ priv->gc.direction_input = amd_fch_gpio_direction_input;
+ priv->gc.direction_output = amd_fch_gpio_direction_output;
+ priv->gc.get_direction = amd_fch_gpio_get_direction;
+ priv->gc.get = amd_fch_gpio_get;
+ priv->gc.set = amd_fch_gpio_set;
+
+ spin_lock_init(&priv->lock);
+
+ priv->base = devm_ioremap_resource(&pdev->dev, &amd_fch_gpio_iores);
+ if (IS_ERR(priv->base))
+ return PTR_ERR(priv->base);
+
+ platform_set_drvdata(pdev, priv);
+
+ return devm_gpiochip_add_data(&pdev->dev, &priv->gc, priv);
+}
+
+static struct platform_driver amd_fch_gpio_driver = {
+ .driver = {
+ .name = AMD_FCH_GPIO_DRIVER_NAME,
+ },
+ .probe = amd_fch_gpio_probe,
+};
+
+module_platform_driver(amd_fch_gpio_driver);
+
+MODULE_AUTHOR("Enrico Weigelt, metux IT consult <info@metux.net>");
+MODULE_DESCRIPTION("AMD G-series FCH GPIO driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" AMD_FCH_GPIO_DRIVER_NAME);
diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c
index 854bce4fb9e7..217507002dbc 100644
--- a/drivers/gpio/gpio-aspeed.c
+++ b/drivers/gpio/gpio-aspeed.c
@@ -1224,6 +1224,8 @@ static int __init aspeed_gpio_probe(struct platform_device *pdev)
gpio->offset_timer =
devm_kzalloc(&pdev->dev, gpio->chip.ngpio, GFP_KERNEL);
+ if (!gpio->offset_timer)
+ return -ENOMEM;
return aspeed_gpio_setup_irqs(gpio, pdev);
}
diff --git a/drivers/gpio/gpio-crystalcove.c b/drivers/gpio/gpio-crystalcove.c
index 58531d8b8c6e..14d1f4c933b6 100644
--- a/drivers/gpio/gpio-crystalcove.c
+++ b/drivers/gpio/gpio-crystalcove.c
@@ -1,28 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0
/*
- * gpio-crystalcove.c - Intel Crystal Cove GPIO Driver
+ * Intel Crystal Cove GPIO Driver
*
* Copyright (C) 2012, 2014 Intel Corporation. All rights reserved.
*
- * 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.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
* Author: Yang, Bin <bin.yang@intel.com>
*/
+#include <linux/bitops.h>
+#include <linux/gpio/driver.h>
#include <linux/interrupt.h>
+#include <linux/mfd/intel_soc_pmic.h>
#include <linux/module.h>
#include <linux/platform_device.h>
-#include <linux/gpio/driver.h>
-#include <linux/seq_file.h>
-#include <linux/bitops.h>
#include <linux/regmap.h>
-#include <linux/mfd/intel_soc_pmic.h>
+#include <linux/seq_file.h>
#define CRYSTALCOVE_GPIO_NUM 16
#define CRYSTALCOVE_VGPIO_NUM 95
@@ -279,8 +271,8 @@ static struct irq_chip crystalcove_irqchip = {
static irqreturn_t crystalcove_gpio_irq_handler(int irq, void *data)
{
struct crystalcove_gpio *cg = data;
+ unsigned long pending;
unsigned int p0, p1;
- int pending;
int gpio;
unsigned int virq;
@@ -293,11 +285,9 @@ static irqreturn_t crystalcove_gpio_irq_handler(int irq, void *data)
pending = p0 | p1 << 8;
- for (gpio = 0; gpio < CRYSTALCOVE_GPIO_NUM; gpio++) {
- if (pending & BIT(gpio)) {
- virq = irq_find_mapping(cg->chip.irq.domain, gpio);
- handle_nested_irq(virq);
- }
+ for_each_set_bit(gpio, &pending, CRYSTALCOVE_GPIO_NUM) {
+ virq = irq_find_mapping(cg->chip.irq.domain, gpio);
+ handle_nested_irq(virq);
}
return IRQ_HANDLED;
diff --git a/drivers/gpio/gpio-davinci.c b/drivers/gpio/gpio-davinci.c
index bdb29e51b417..188b8e5c8e67 100644
--- a/drivers/gpio/gpio-davinci.c
+++ b/drivers/gpio/gpio-davinci.c
@@ -198,7 +198,6 @@ static int davinci_gpio_probe(struct platform_device *pdev)
struct davinci_gpio_controller *chips;
struct davinci_gpio_platform_data *pdata;
struct device *dev = &pdev->dev;
- struct resource *res;
pdata = davinci_gpio_get_pdata(pdev);
if (!pdata) {
@@ -236,8 +235,7 @@ static int davinci_gpio_probe(struct platform_device *pdev)
if (!chips)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- gpio_base = devm_ioremap_resource(dev, res);
+ gpio_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(gpio_base))
return PTR_ERR(gpio_base);
diff --git a/drivers/gpio/gpio-eic-sprd.c b/drivers/gpio/gpio-eic-sprd.c
index e41223c05f6e..f0223cee9774 100644
--- a/drivers/gpio/gpio-eic-sprd.c
+++ b/drivers/gpio/gpio-eic-sprd.c
@@ -432,6 +432,7 @@ static int sprd_eic_irq_set_type(struct irq_data *data, unsigned int flow_type)
default:
return -ENOTSUPP;
}
+ break;
default:
dev_err(chip->parent, "Unsupported EIC type.\n");
return -ENOTSUPP;
diff --git a/drivers/gpio/gpio-exar.c b/drivers/gpio/gpio-exar.c
index 0ecd2369c2ca..a09d2f9ebacc 100644
--- a/drivers/gpio/gpio-exar.c
+++ b/drivers/gpio/gpio-exar.c
@@ -148,6 +148,8 @@ static int gpio_exar_probe(struct platform_device *pdev)
mutex_init(&exar_gpio->lock);
index = ida_simple_get(&ida_index, 0, 0, GFP_KERNEL);
+ if (index < 0)
+ goto err_destroy;
sprintf(exar_gpio->name, "exar_gpio%d", index);
exar_gpio->gpio_chip.label = exar_gpio->name;
diff --git a/drivers/gpio/gpio-f7188x.c b/drivers/gpio/gpio-f7188x.c
index 13350c9d7f5e..0896c825b312 100644
--- a/drivers/gpio/gpio-f7188x.c
+++ b/drivers/gpio/gpio-f7188x.c
@@ -39,8 +39,10 @@
#define SIO_F71889_ID 0x0909 /* F71889 chipset ID */
#define SIO_F71889A_ID 0x1005 /* F71889A chipset ID */
#define SIO_F81866_ID 0x1010 /* F81866 chipset ID */
+#define SIO_F81804_ID 0x1502 /* F81804 chipset ID, same for f81966 */
-enum chips { f71869, f71869a, f71882fg, f71889a, f71889f, f81866 };
+
+enum chips { f71869, f71869a, f71882fg, f71889a, f71889f, f81866, f81804 };
static const char * const f7188x_names[] = {
"f71869",
@@ -49,6 +51,7 @@ static const char * const f7188x_names[] = {
"f71889a",
"f71889f",
"f81866",
+ "f81804",
};
struct f7188x_sio {
@@ -223,6 +226,18 @@ static struct f7188x_gpio_bank f81866_gpio_bank[] = {
F7188X_GPIO_BANK(80, 8, 0x88),
};
+
+static struct f7188x_gpio_bank f81804_gpio_bank[] = {
+ F7188X_GPIO_BANK(0, 8, 0xF0),
+ F7188X_GPIO_BANK(10, 8, 0xE0),
+ F7188X_GPIO_BANK(20, 8, 0xD0),
+ F7188X_GPIO_BANK(50, 8, 0xA0),
+ F7188X_GPIO_BANK(60, 8, 0x90),
+ F7188X_GPIO_BANK(70, 8, 0x80),
+ F7188X_GPIO_BANK(90, 8, 0x98),
+};
+
+
static int f7188x_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
{
int err;
@@ -407,6 +422,10 @@ static int f7188x_gpio_probe(struct platform_device *pdev)
data->nr_bank = ARRAY_SIZE(f81866_gpio_bank);
data->bank = f81866_gpio_bank;
break;
+ case f81804:
+ data->nr_bank = ARRAY_SIZE(f81804_gpio_bank);
+ data->bank = f81804_gpio_bank;
+ break;
default:
return -ENODEV;
}
@@ -469,6 +488,9 @@ static int __init f7188x_find(int addr, struct f7188x_sio *sio)
case SIO_F81866_ID:
sio->type = f81866;
break;
+ case SIO_F81804_ID:
+ sio->type = f81804;
+ break;
default:
pr_info(DRVNAME ": Unsupported Fintek device 0x%04x\n", devid);
goto err;
diff --git a/drivers/gpio/gpio-ftgpio010.c b/drivers/gpio/gpio-ftgpio010.c
index 95f578804b0e..45fe125823a8 100644
--- a/drivers/gpio/gpio-ftgpio010.c
+++ b/drivers/gpio/gpio-ftgpio010.c
@@ -41,12 +41,14 @@
* struct ftgpio_gpio - Gemini GPIO state container
* @dev: containing device for this instance
* @gc: gpiochip for this instance
+ * @irq: irqchip for this instance
* @base: remapped I/O-memory base
* @clk: silicon clock
*/
struct ftgpio_gpio {
struct device *dev;
struct gpio_chip gc;
+ struct irq_chip irq;
void __iomem *base;
struct clk *clk;
};
@@ -134,14 +136,6 @@ static int ftgpio_gpio_set_irq_type(struct irq_data *d, unsigned int type)
return 0;
}
-static struct irq_chip ftgpio_gpio_irqchip = {
- .name = "FTGPIO010",
- .irq_ack = ftgpio_gpio_ack_irq,
- .irq_mask = ftgpio_gpio_mask_irq,
- .irq_unmask = ftgpio_gpio_unmask_irq,
- .irq_set_type = ftgpio_gpio_set_irq_type,
-};
-
static void ftgpio_gpio_irq_handler(struct irq_desc *desc)
{
struct gpio_chip *gc = irq_desc_get_handler_data(desc);
@@ -297,14 +291,20 @@ static int ftgpio_gpio_probe(struct platform_device *pdev)
/* Clear any use of debounce */
writel(0x0, g->base + GPIO_DEBOUNCE_EN);
- ret = gpiochip_irqchip_add(&g->gc, &ftgpio_gpio_irqchip,
+ g->irq.name = "FTGPIO010";
+ g->irq.irq_ack = ftgpio_gpio_ack_irq;
+ g->irq.irq_mask = ftgpio_gpio_mask_irq;
+ g->irq.irq_unmask = ftgpio_gpio_unmask_irq;
+ g->irq.irq_set_type = ftgpio_gpio_set_irq_type;
+
+ ret = gpiochip_irqchip_add(&g->gc, &g->irq,
0, handle_bad_irq,
IRQ_TYPE_NONE);
if (ret) {
dev_info(dev, "could not add irqchip\n");
goto dis_clk;
}
- gpiochip_set_chained_irqchip(&g->gc, &ftgpio_gpio_irqchip,
+ gpiochip_set_chained_irqchip(&g->gc, &g->irq,
irq, ftgpio_gpio_irq_handler);
platform_set_drvdata(pdev, g);
diff --git a/drivers/gpio/gpio-gw-pld.c b/drivers/gpio/gpio-gw-pld.c
new file mode 100644
index 000000000000..242112ff60ee
--- /dev/null
+++ b/drivers/gpio/gpio-gw-pld.c
@@ -0,0 +1,137 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// Gateworks I2C PLD GPIO expander
+//
+// Copyright (C) 2019 Linus Walleij <linus.walleij@linaro.org>
+//
+// Based on code and know-how from the OpenWrt driver:
+// Copyright (C) 2009 Gateworks Corporation
+// Authors: Chris Lang, Imre Kaloz
+
+#include <linux/bits.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/gpio/driver.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+
+/**
+ * struct gw_pld - State container for Gateworks PLD
+ * @chip: GPIO chip instance
+ * @client: I2C client
+ * @out: shadow register for the output bute
+ */
+struct gw_pld {
+ struct gpio_chip chip;
+ struct i2c_client *client;
+ u8 out;
+};
+
+/*
+ * The Gateworks I2C PLD chip only expose one read and one write register.
+ * Writing a "one" bit (to match the reset state) lets that pin be used as an
+ * input. It is an open-drain model.
+ */
+static int gw_pld_input8(struct gpio_chip *gc, unsigned offset)
+{
+ struct gw_pld *gw = gpiochip_get_data(gc);
+
+ gw->out |= BIT(offset);
+ return i2c_smbus_write_byte(gw->client, gw->out);
+}
+
+static int gw_pld_get8(struct gpio_chip *gc, unsigned offset)
+{
+ struct gw_pld *gw = gpiochip_get_data(gc);
+ s32 val;
+
+ val = i2c_smbus_read_byte(gw->client);
+
+ return (val < 0) ? 0 : !!(val & BIT(offset));
+}
+
+static int gw_pld_output8(struct gpio_chip *gc, unsigned offset, int value)
+{
+ struct gw_pld *gw = gpiochip_get_data(gc);
+
+ if (value)
+ gw->out |= BIT(offset);
+ else
+ gw->out &= ~BIT(offset);
+
+ return i2c_smbus_write_byte(gw->client, gw->out);
+}
+
+static void gw_pld_set8(struct gpio_chip *gc, unsigned offset, int value)
+{
+ gw_pld_output8(gc, offset, value);
+}
+
+static int gw_pld_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &client->dev;
+ struct device_node *np = dev->of_node;
+ struct gw_pld *gw;
+ int ret;
+
+ gw = devm_kzalloc(dev, sizeof(*gw), GFP_KERNEL);
+ if (!gw)
+ return -ENOMEM;
+
+ gw->chip.base = -1;
+ gw->chip.can_sleep = true;
+ gw->chip.parent = dev;
+ gw->chip.of_node = np;
+ gw->chip.owner = THIS_MODULE;
+ gw->chip.label = dev_name(dev);
+ gw->chip.ngpio = 8;
+ gw->chip.direction_input = gw_pld_input8;
+ gw->chip.get = gw_pld_get8;
+ gw->chip.direction_output = gw_pld_output8;
+ gw->chip.set = gw_pld_set8;
+ gw->client = client;
+
+ /*
+ * The Gateworks I2C PLD chip does not properly send the acknowledge
+ * bit at all times, but we can still use the standard i2c_smbus
+ * functions by simply ignoring this bit.
+ */
+ client->flags |= I2C_M_IGNORE_NAK;
+ gw->out = 0xFF;
+
+ i2c_set_clientdata(client, gw);
+
+ ret = devm_gpiochip_add_data(dev, &gw->chip, gw);
+ if (ret)
+ return ret;
+
+ dev_info(dev, "registered Gateworks PLD GPIO device\n");
+
+ return 0;
+}
+
+static const struct i2c_device_id gw_pld_id[] = {
+ { "gw-pld", },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, gw_pld_id);
+
+static const struct of_device_id gw_pld_dt_ids[] = {
+ { .compatible = "gateworks,pld-gpio", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, gw_pld_dt_ids);
+
+static struct i2c_driver gw_pld_driver = {
+ .driver = {
+ .name = "gw_pld",
+ .of_match_table = gw_pld_dt_ids,
+ },
+ .probe = gw_pld_probe,
+ .id_table = gw_pld_id,
+};
+module_i2c_driver(gw_pld_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
diff --git a/drivers/gpio/gpio-hlwd.c b/drivers/gpio/gpio-hlwd.c
index a63136a68ba3..a7b17897356e 100644
--- a/drivers/gpio/gpio-hlwd.c
+++ b/drivers/gpio/gpio-hlwd.c
@@ -48,9 +48,163 @@
struct hlwd_gpio {
struct gpio_chip gpioc;
+ struct irq_chip irqc;
void __iomem *regs;
+ int irq;
+ u32 edge_emulation;
+ u32 rising_edge, falling_edge;
};
+static void hlwd_gpio_irqhandler(struct irq_desc *desc)
+{
+ struct hlwd_gpio *hlwd =
+ gpiochip_get_data(irq_desc_get_handler_data(desc));
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ unsigned long flags;
+ unsigned long pending;
+ int hwirq;
+ u32 emulated_pending;
+
+ spin_lock_irqsave(&hlwd->gpioc.bgpio_lock, flags);
+ pending = ioread32be(hlwd->regs + HW_GPIOB_INTFLAG);
+ pending &= ioread32be(hlwd->regs + HW_GPIOB_INTMASK);
+
+ /* Treat interrupts due to edge trigger emulation separately */
+ emulated_pending = hlwd->edge_emulation & pending;
+ pending &= ~emulated_pending;
+ if (emulated_pending) {
+ u32 level, rising, falling;
+
+ level = ioread32be(hlwd->regs + HW_GPIOB_INTLVL);
+ rising = level & emulated_pending;
+ falling = ~level & emulated_pending;
+
+ /* Invert the levels */
+ iowrite32be(level ^ emulated_pending,
+ hlwd->regs + HW_GPIOB_INTLVL);
+
+ /* Ack all emulated-edge interrupts */
+ iowrite32be(emulated_pending, hlwd->regs + HW_GPIOB_INTFLAG);
+
+ /* Signal interrupts only on the correct edge */
+ rising &= hlwd->rising_edge;
+ falling &= hlwd->falling_edge;
+
+ /* Mark emulated interrupts as pending */
+ pending |= rising | falling;
+ }
+ spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags);
+
+ chained_irq_enter(chip, desc);
+
+ for_each_set_bit(hwirq, &pending, 32) {
+ int irq = irq_find_mapping(hlwd->gpioc.irq.domain, hwirq);
+
+ generic_handle_irq(irq);
+ }
+
+ chained_irq_exit(chip, desc);
+}
+
+static void hlwd_gpio_irq_ack(struct irq_data *data)
+{
+ struct hlwd_gpio *hlwd =
+ gpiochip_get_data(irq_data_get_irq_chip_data(data));
+
+ iowrite32be(BIT(data->hwirq), hlwd->regs + HW_GPIOB_INTFLAG);
+}
+
+static void hlwd_gpio_irq_mask(struct irq_data *data)
+{
+ struct hlwd_gpio *hlwd =
+ gpiochip_get_data(irq_data_get_irq_chip_data(data));
+ unsigned long flags;
+ u32 mask;
+
+ spin_lock_irqsave(&hlwd->gpioc.bgpio_lock, flags);
+ mask = ioread32be(hlwd->regs + HW_GPIOB_INTMASK);
+ mask &= ~BIT(data->hwirq);
+ iowrite32be(mask, hlwd->regs + HW_GPIOB_INTMASK);
+ spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags);
+}
+
+static void hlwd_gpio_irq_unmask(struct irq_data *data)
+{
+ struct hlwd_gpio *hlwd =
+ gpiochip_get_data(irq_data_get_irq_chip_data(data));
+ unsigned long flags;
+ u32 mask;
+
+ spin_lock_irqsave(&hlwd->gpioc.bgpio_lock, flags);
+ mask = ioread32be(hlwd->regs + HW_GPIOB_INTMASK);
+ mask |= BIT(data->hwirq);
+ iowrite32be(mask, hlwd->regs + HW_GPIOB_INTMASK);
+ spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags);
+}
+
+static void hlwd_gpio_irq_enable(struct irq_data *data)
+{
+ hlwd_gpio_irq_ack(data);
+ hlwd_gpio_irq_unmask(data);
+}
+
+static void hlwd_gpio_irq_setup_emulation(struct hlwd_gpio *hlwd, int hwirq,
+ unsigned int flow_type)
+{
+ u32 level, state;
+
+ /* Set the trigger level to the inactive level */
+ level = ioread32be(hlwd->regs + HW_GPIOB_INTLVL);
+ state = ioread32be(hlwd->regs + HW_GPIOB_IN) & BIT(hwirq);
+ level &= ~BIT(hwirq);
+ level |= state ^ BIT(hwirq);
+ iowrite32be(level, hlwd->regs + HW_GPIOB_INTLVL);
+
+ hlwd->edge_emulation |= BIT(hwirq);
+ hlwd->rising_edge &= ~BIT(hwirq);
+ hlwd->falling_edge &= ~BIT(hwirq);
+ if (flow_type & IRQ_TYPE_EDGE_RISING)
+ hlwd->rising_edge |= BIT(hwirq);
+ if (flow_type & IRQ_TYPE_EDGE_FALLING)
+ hlwd->falling_edge |= BIT(hwirq);
+}
+
+static int hlwd_gpio_irq_set_type(struct irq_data *data, unsigned int flow_type)
+{
+ struct hlwd_gpio *hlwd =
+ gpiochip_get_data(irq_data_get_irq_chip_data(data));
+ unsigned long flags;
+ u32 level;
+
+ spin_lock_irqsave(&hlwd->gpioc.bgpio_lock, flags);
+
+ hlwd->edge_emulation &= ~BIT(data->hwirq);
+
+ switch (flow_type) {
+ case IRQ_TYPE_LEVEL_HIGH:
+ level = ioread32be(hlwd->regs + HW_GPIOB_INTLVL);
+ level |= BIT(data->hwirq);
+ iowrite32be(level, hlwd->regs + HW_GPIOB_INTLVL);
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ level = ioread32be(hlwd->regs + HW_GPIOB_INTLVL);
+ level &= ~BIT(data->hwirq);
+ iowrite32be(level, hlwd->regs + HW_GPIOB_INTLVL);
+ break;
+ case IRQ_TYPE_EDGE_RISING:
+ case IRQ_TYPE_EDGE_FALLING:
+ case IRQ_TYPE_EDGE_BOTH:
+ hlwd_gpio_irq_setup_emulation(hlwd, data->hwirq, flow_type);
+ break;
+ default:
+ spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags);
+ return -EINVAL;
+ }
+
+ spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags);
+ return 0;
+}
+
static int hlwd_gpio_probe(struct platform_device *pdev)
{
struct hlwd_gpio *hlwd;
@@ -92,7 +246,43 @@ static int hlwd_gpio_probe(struct platform_device *pdev)
ngpios = 32;
hlwd->gpioc.ngpio = ngpios;
- return devm_gpiochip_add_data(&pdev->dev, &hlwd->gpioc, hlwd);
+ res = devm_gpiochip_add_data(&pdev->dev, &hlwd->gpioc, hlwd);
+ if (res)
+ return res;
+
+ /* Mask and ack all interrupts */
+ iowrite32be(0, hlwd->regs + HW_GPIOB_INTMASK);
+ iowrite32be(0xffffffff, hlwd->regs + HW_GPIOB_INTFLAG);
+
+ /*
+ * If this GPIO controller is not marked as an interrupt controller in
+ * the DT, return.
+ */
+ if (!of_property_read_bool(pdev->dev.of_node, "interrupt-controller"))
+ return 0;
+
+ hlwd->irq = platform_get_irq(pdev, 0);
+ if (hlwd->irq < 0) {
+ dev_info(&pdev->dev, "platform_get_irq returned %d\n",
+ hlwd->irq);
+ return hlwd->irq;
+ }
+
+ hlwd->irqc.name = dev_name(&pdev->dev);
+ hlwd->irqc.irq_mask = hlwd_gpio_irq_mask;
+ hlwd->irqc.irq_unmask = hlwd_gpio_irq_unmask;
+ hlwd->irqc.irq_enable = hlwd_gpio_irq_enable;
+ hlwd->irqc.irq_set_type = hlwd_gpio_irq_set_type;
+
+ res = gpiochip_irqchip_add(&hlwd->gpioc, &hlwd->irqc, 0,
+ handle_level_irq, IRQ_TYPE_NONE);
+ if (res)
+ return res;
+
+ gpiochip_set_chained_irqchip(&hlwd->gpioc, &hlwd->irqc,
+ hlwd->irq, hlwd_gpio_irqhandler);
+
+ return 0;
}
static const struct of_device_id hlwd_gpio_match[] = {
diff --git a/drivers/gpio/gpio-madera.c b/drivers/gpio/gpio-madera.c
index 7ba68d1a0932..c9dad0543672 100644
--- a/drivers/gpio/gpio-madera.c
+++ b/drivers/gpio/gpio-madera.c
@@ -107,7 +107,7 @@ static void madera_gpio_set(struct gpio_chip *chip, unsigned int offset,
MADERA_GPIO1_CTRL_1 + reg_offset, ret);
}
-static struct gpio_chip madera_gpio_chip = {
+static const struct gpio_chip madera_gpio_chip = {
.label = "madera",
.owner = THIS_MODULE,
.request = gpiochip_generic_request,
diff --git a/drivers/gpio/gpio-ml-ioh.c b/drivers/gpio/gpio-ml-ioh.c
index 51c7d1b84c2e..0c076dce9e17 100644
--- a/drivers/gpio/gpio-ml-ioh.c
+++ b/drivers/gpio/gpio-ml-ioh.c
@@ -31,8 +31,6 @@
#define IOH_IRQ_BASE 0
-#define PCI_VENDOR_ID_ROHM 0x10DB
-
struct ioh_reg_comn {
u32 ien;
u32 istatus;
diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c
index 6a50f9f59c90..b6a4efce7c92 100644
--- a/drivers/gpio/gpio-mockup.c
+++ b/drivers/gpio/gpio-mockup.c
@@ -47,6 +47,7 @@ enum {
struct gpio_mockup_line_status {
int dir;
int value;
+ int pull;
};
struct gpio_mockup_chip {
@@ -54,12 +55,13 @@ struct gpio_mockup_chip {
struct gpio_mockup_line_status *lines;
struct irq_sim irqsim;
struct dentry *dbg_dir;
+ struct mutex lock;
};
struct gpio_mockup_dbgfs_private {
struct gpio_mockup_chip *chip;
struct gpio_desc *desc;
- int offset;
+ unsigned int offset;
};
static int gpio_mockup_ranges[GPIO_MOCKUP_MAX_RANGES];
@@ -82,29 +84,66 @@ static int gpio_mockup_range_ngpio(unsigned int index)
return gpio_mockup_ranges[index * 2 + 1];
}
+static int __gpio_mockup_get(struct gpio_mockup_chip *chip,
+ unsigned int offset)
+{
+ return chip->lines[offset].value;
+}
+
static int gpio_mockup_get(struct gpio_chip *gc, unsigned int offset)
{
struct gpio_mockup_chip *chip = gpiochip_get_data(gc);
+ int val;
- return chip->lines[offset].value;
+ mutex_lock(&chip->lock);
+ val = __gpio_mockup_get(chip, offset);
+ mutex_unlock(&chip->lock);
+
+ return val;
}
-static void gpio_mockup_set(struct gpio_chip *gc,
- unsigned int offset, int value)
+static int gpio_mockup_get_multiple(struct gpio_chip *gc,
+ unsigned long *mask, unsigned long *bits)
{
struct gpio_mockup_chip *chip = gpiochip_get_data(gc);
+ unsigned int bit, val;
+ mutex_lock(&chip->lock);
+ for_each_set_bit(bit, mask, gc->ngpio) {
+ val = __gpio_mockup_get(chip, bit);
+ __assign_bit(bit, bits, val);
+ }
+ mutex_unlock(&chip->lock);
+
+ return 0;
+}
+
+static void __gpio_mockup_set(struct gpio_mockup_chip *chip,
+ unsigned int offset, int value)
+{
chip->lines[offset].value = !!value;
}
+static void gpio_mockup_set(struct gpio_chip *gc,
+ unsigned int offset, int value)
+{
+ struct gpio_mockup_chip *chip = gpiochip_get_data(gc);
+
+ mutex_lock(&chip->lock);
+ __gpio_mockup_set(chip, offset, value);
+ mutex_unlock(&chip->lock);
+}
+
static void gpio_mockup_set_multiple(struct gpio_chip *gc,
unsigned long *mask, unsigned long *bits)
{
+ struct gpio_mockup_chip *chip = gpiochip_get_data(gc);
unsigned int bit;
+ mutex_lock(&chip->lock);
for_each_set_bit(bit, mask, gc->ngpio)
- gpio_mockup_set(gc, bit, test_bit(bit, bits));
-
+ __gpio_mockup_set(chip, bit, test_bit(bit, bits));
+ mutex_unlock(&chip->lock);
}
static int gpio_mockup_dirout(struct gpio_chip *gc,
@@ -112,8 +151,10 @@ static int gpio_mockup_dirout(struct gpio_chip *gc,
{
struct gpio_mockup_chip *chip = gpiochip_get_data(gc);
- gpio_mockup_set(gc, offset, value);
+ mutex_lock(&chip->lock);
chip->lines[offset].dir = GPIO_MOCKUP_DIR_OUT;
+ __gpio_mockup_set(chip, offset, value);
+ mutex_unlock(&chip->lock);
return 0;
}
@@ -122,7 +163,9 @@ static int gpio_mockup_dirin(struct gpio_chip *gc, unsigned int offset)
{
struct gpio_mockup_chip *chip = gpiochip_get_data(gc);
+ mutex_lock(&chip->lock);
chip->lines[offset].dir = GPIO_MOCKUP_DIR_IN;
+ mutex_unlock(&chip->lock);
return 0;
}
@@ -130,8 +173,13 @@ static int gpio_mockup_dirin(struct gpio_chip *gc, unsigned int offset)
static int gpio_mockup_get_direction(struct gpio_chip *gc, unsigned int offset)
{
struct gpio_mockup_chip *chip = gpiochip_get_data(gc);
+ int direction;
+
+ mutex_lock(&chip->lock);
+ direction = !chip->lines[offset].dir;
+ mutex_unlock(&chip->lock);
- return !chip->lines[offset].dir;
+ return direction;
}
static int gpio_mockup_to_irq(struct gpio_chip *gc, unsigned int offset)
@@ -141,15 +189,52 @@ static int gpio_mockup_to_irq(struct gpio_chip *gc, unsigned int offset)
return irq_sim_irqnum(&chip->irqsim, offset);
}
-static ssize_t gpio_mockup_event_write(struct file *file,
- const char __user *usr_buf,
- size_t size, loff_t *ppos)
+static void gpio_mockup_free(struct gpio_chip *gc, unsigned int offset)
+{
+ struct gpio_mockup_chip *chip = gpiochip_get_data(gc);
+
+ __gpio_mockup_set(chip, offset, chip->lines[offset].pull);
+}
+
+static ssize_t gpio_mockup_debugfs_read(struct file *file,
+ char __user *usr_buf,
+ size_t size, loff_t *ppos)
+{
+ struct gpio_mockup_dbgfs_private *priv;
+ struct gpio_mockup_chip *chip;
+ struct seq_file *sfile;
+ struct gpio_chip *gc;
+ int val, cnt;
+ char buf[3];
+
+ if (*ppos != 0)
+ return 0;
+
+ sfile = file->private_data;
+ priv = sfile->private;
+ chip = priv->chip;
+ gc = &chip->gc;
+
+ val = gpio_mockup_get(gc, priv->offset);
+ cnt = snprintf(buf, sizeof(buf), "%d\n", val);
+
+ return simple_read_from_buffer(usr_buf, size, ppos, buf, cnt);
+}
+
+static ssize_t gpio_mockup_debugfs_write(struct file *file,
+ const char __user *usr_buf,
+ size_t size, loff_t *ppos)
{
struct gpio_mockup_dbgfs_private *priv;
+ int rv, val, curr, irq, irq_type;
struct gpio_mockup_chip *chip;
struct seq_file *sfile;
struct gpio_desc *desc;
- int rv, val;
+ struct gpio_chip *gc;
+ struct irq_sim *sim;
+
+ if (*ppos != 0)
+ return -EINVAL;
rv = kstrtoint_from_user(usr_buf, size, 0, &val);
if (rv)
@@ -159,24 +244,70 @@ static ssize_t gpio_mockup_event_write(struct file *file,
sfile = file->private_data;
priv = sfile->private;
- desc = priv->desc;
chip = priv->chip;
+ gc = &chip->gc;
+ desc = &gc->gpiodev->descs[priv->offset];
+ sim = &chip->irqsim;
+
+ mutex_lock(&chip->lock);
+
+ if (test_bit(FLAG_REQUESTED, &desc->flags) &&
+ !test_bit(FLAG_IS_OUT, &desc->flags)) {
+ curr = __gpio_mockup_get(chip, priv->offset);
+ if (curr == val)
+ goto out;
+
+ irq = irq_sim_irqnum(sim, priv->offset);
+ irq_type = irq_get_trigger_type(irq);
+
+ if ((val == 1 && (irq_type & IRQ_TYPE_EDGE_RISING)) ||
+ (val == 0 && (irq_type & IRQ_TYPE_EDGE_FALLING)))
+ irq_sim_fire(sim, priv->offset);
+ }
+
+ /* Change the value unless we're actively driving the line. */
+ if (!test_bit(FLAG_REQUESTED, &desc->flags) ||
+ !test_bit(FLAG_IS_OUT, &desc->flags))
+ __gpio_mockup_set(chip, priv->offset, val);
- gpiod_set_value_cansleep(desc, val);
- irq_sim_fire(&chip->irqsim, priv->offset);
+out:
+ chip->lines[priv->offset].pull = val;
+ mutex_unlock(&chip->lock);
return size;
}
-static int gpio_mockup_event_open(struct inode *inode, struct file *file)
+static int gpio_mockup_debugfs_open(struct inode *inode, struct file *file)
{
return single_open(file, NULL, inode->i_private);
}
-static const struct file_operations gpio_mockup_event_ops = {
+/*
+ * Each mockup chip is represented by a directory named after the chip's device
+ * name under /sys/kernel/debug/gpio-mockup/. Each line is represented by
+ * a file using the line's offset as the name under the chip's directory.
+ *
+ * Reading from the line's file yields the current *value*, writing to the
+ * line's file changes the current *pull*. Default pull for mockup lines is
+ * down.
+ *
+ * Examples:
+ * - when a line pulled down is requested in output mode and driven high, its
+ * value will return to 0 once it's released
+ * - when the line is requested in output mode and driven high, writing 0 to
+ * the corresponding debugfs file will change the pull to down but the
+ * reported value will still be 1 until the line is released
+ * - line requested in input mode always reports the same value as its pull
+ * configuration
+ * - when the line is requested in input mode and monitored for events, writing
+ * the same value to the debugfs file will be a noop, while writing the
+ * opposite value will generate a dummy interrupt with an appropriate edge
+ */
+static const struct file_operations gpio_mockup_debugfs_ops = {
.owner = THIS_MODULE,
- .open = gpio_mockup_event_open,
- .write = gpio_mockup_event_write,
+ .open = gpio_mockup_debugfs_open,
+ .read = gpio_mockup_debugfs_read,
+ .write = gpio_mockup_debugfs_write,
.llseek = no_llseek,
};
@@ -184,7 +315,7 @@ static void gpio_mockup_debugfs_setup(struct device *dev,
struct gpio_mockup_chip *chip)
{
struct gpio_mockup_dbgfs_private *priv;
- struct dentry *evfile, *link;
+ struct dentry *evfile;
struct gpio_chip *gc;
const char *devname;
char *name;
@@ -197,10 +328,6 @@ static void gpio_mockup_debugfs_setup(struct device *dev,
if (IS_ERR_OR_NULL(chip->dbg_dir))
goto err;
- link = debugfs_create_symlink(gc->label, gpio_mockup_dbg_dir, devname);
- if (IS_ERR_OR_NULL(link))
- goto err;
-
for (i = 0; i < gc->ngpio; i++) {
name = devm_kasprintf(dev, GFP_KERNEL, "%d", i);
if (!name)
@@ -215,7 +342,7 @@ static void gpio_mockup_debugfs_setup(struct device *dev,
priv->desc = &gc->gpiodev->descs[i];
evfile = debugfs_create_file(name, 0200, chip->dbg_dir, priv,
- &gpio_mockup_event_ops);
+ &gpio_mockup_debugfs_ops);
if (IS_ERR_OR_NULL(evfile))
goto err;
}
@@ -223,7 +350,7 @@ static void gpio_mockup_debugfs_setup(struct device *dev,
return;
err:
- dev_err(dev, "error creating debugfs event files\n");
+ dev_err(dev, "error creating debugfs files\n");
}
static int gpio_mockup_name_lines(struct device *dev,
@@ -283,6 +410,8 @@ static int gpio_mockup_probe(struct platform_device *pdev)
return -ENOMEM;
}
+ mutex_init(&chip->lock);
+
gc = &chip->gc;
gc->base = base;
gc->ngpio = ngpio;
@@ -291,11 +420,13 @@ static int gpio_mockup_probe(struct platform_device *pdev)
gc->parent = dev;
gc->get = gpio_mockup_get;
gc->set = gpio_mockup_set;
+ gc->get_multiple = gpio_mockup_get_multiple;
gc->set_multiple = gpio_mockup_set_multiple;
gc->direction_output = gpio_mockup_dirout;
gc->direction_input = gpio_mockup_dirin;
gc->get_direction = gpio_mockup_get_direction;
gc->to_irq = gpio_mockup_to_irq;
+ gc->free = gpio_mockup_free;
chip->lines = devm_kcalloc(dev, gc->ngpio,
sizeof(*chip->lines), GFP_KERNEL);
@@ -369,7 +500,7 @@ static int __init gpio_mockup_init(void)
return -EINVAL;
}
- gpio_mockup_dbg_dir = debugfs_create_dir("gpio-mockup-event", NULL);
+ gpio_mockup_dbg_dir = debugfs_create_dir("gpio-mockup", NULL);
if (IS_ERR_OR_NULL(gpio_mockup_dbg_dir))
gpio_mockup_err("error creating debugfs directory\n");
diff --git a/drivers/gpio/gpio-msic.c b/drivers/gpio/gpio-msic.c
index 3b34dbecef99..7e3c96e4ab2c 100644
--- a/drivers/gpio/gpio-msic.c
+++ b/drivers/gpio/gpio-msic.c
@@ -1,32 +1,19 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Intel Medfield MSIC GPIO driver>
* Copyright (c) 2011, Intel Corporation.
*
* Author: Mathias Nyman <mathias.nyman@linux.intel.com>
* Based on intel_pmic_gpio.c
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/init.h>
#include <linux/gpio/driver.h>
-#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
#include <linux/mfd/intel_msic.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
/* the offset for the mapping of global gpio pin to irq */
#define MSIC_GPIO_IRQ_OFFSET 0x100
@@ -237,20 +224,17 @@ static void msic_gpio_irq_handler(struct irq_desc *desc)
struct msic_gpio *mg = irq_data_get_irq_handler_data(data);
struct irq_chip *chip = irq_data_get_irq_chip(data);
struct intel_msic *msic = pdev_to_intel_msic(mg->pdev);
+ unsigned long pending;
int i;
int bitnr;
u8 pin;
- unsigned long pending = 0;
for (i = 0; i < (mg->chip.ngpio / BITS_PER_BYTE); i++) {
intel_msic_irq_read(msic, INTEL_MSIC_GPIO0LVIRQ + i, &pin);
pending = pin;
- if (pending) {
- for_each_set_bit(bitnr, &pending, BITS_PER_BYTE)
- generic_handle_irq(mg->irq_base +
- (i * BITS_PER_BYTE) + bitnr);
- }
+ for_each_set_bit(bitnr, &pending, BITS_PER_BYTE)
+ generic_handle_irq(mg->irq_base + i * BITS_PER_BYTE + bitnr);
}
chip->irq_eoi(data);
}
diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c
index 7d5c55494ccd..f97ed32b8beb 100644
--- a/drivers/gpio/gpio-mvebu.c
+++ b/drivers/gpio/gpio-mvebu.c
@@ -376,6 +376,16 @@ static int mvebu_gpio_direction_output(struct gpio_chip *chip, unsigned int pin,
return 0;
}
+static int mvebu_gpio_get_direction(struct gpio_chip *chip, unsigned int pin)
+{
+ struct mvebu_gpio_chip *mvchip = gpiochip_get_data(chip);
+ u32 u;
+
+ regmap_read(mvchip->regs, GPIO_IO_CONF_OFF + mvchip->offset, &u);
+
+ return !!(u & BIT(pin));
+}
+
static int mvebu_gpio_to_irq(struct gpio_chip *chip, unsigned int pin)
{
struct mvebu_gpio_chip *mvchip = gpiochip_get_data(chip);
@@ -1130,6 +1140,7 @@ static int mvebu_gpio_probe(struct platform_device *pdev)
mvchip->chip.parent = &pdev->dev;
mvchip->chip.request = gpiochip_generic_request;
mvchip->chip.free = gpiochip_generic_free;
+ mvchip->chip.get_direction = mvebu_gpio_get_direction;
mvchip->chip.direction_input = mvebu_gpio_direction_input;
mvchip->chip.get = mvebu_gpio_get;
mvchip->chip.direction_output = mvebu_gpio_direction_output;
diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c
index 2d1dfa1e0745..e86e61dda4b7 100644
--- a/drivers/gpio/gpio-mxc.c
+++ b/drivers/gpio/gpio-mxc.c
@@ -438,8 +438,11 @@ static int mxc_gpio_probe(struct platform_device *pdev)
/* the controller clock is optional */
port->clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(port->clk))
+ if (IS_ERR(port->clk)) {
+ if (PTR_ERR(port->clk) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
port->clk = NULL;
+ }
err = clk_prepare_enable(port->clk);
if (err) {
diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c
index f4e9921fa966..7f33024b6d83 100644
--- a/drivers/gpio/gpio-omap.c
+++ b/drivers/gpio/gpio-omap.c
@@ -883,14 +883,16 @@ static void omap_gpio_unmask_irq(struct irq_data *d)
if (trigger)
omap_set_gpio_triggering(bank, offset, trigger);
- /* For level-triggered GPIOs, the clearing must be done after
- * the HW source is cleared, thus after the handler has run */
- if (bank->level_mask & BIT(offset)) {
- omap_set_gpio_irqenable(bank, offset, 0);
+ omap_set_gpio_irqenable(bank, offset, 1);
+
+ /*
+ * For level-triggered GPIOs, clearing must be done after the source
+ * is cleared, thus after the handler has run. OMAP4 needs this done
+ * after enabing the interrupt to clear the wakeup status.
+ */
+ if (bank->level_mask & BIT(offset))
omap_clear_gpio_irqstatus(bank, offset);
- }
- omap_set_gpio_irqenable(bank, offset, 1);
raw_spin_unlock_irqrestore(&bank->lock, flags);
}
diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c
index 0dc96419efe3..7e76830b3368 100644
--- a/drivers/gpio/gpio-pca953x.c
+++ b/drivers/gpio/gpio-pca953x.c
@@ -65,7 +65,7 @@
#define PCA_INT 0x0100
#define PCA_PCAL 0x0200
-#define PCA_LATCH_INT (PCA_PCAL | PCA_INT)
+#define PCA_LATCH_INT (PCA_PCAL | PCA_INT)
#define PCA953X_TYPE 0x1000
#define PCA957X_TYPE 0x2000
#define PCA_TYPE_MASK 0xF000
@@ -88,8 +88,9 @@ static const struct i2c_device_id pca953x_id[] = {
{ "pca9575", 16 | PCA957X_TYPE | PCA_INT, },
{ "pca9698", 40 | PCA953X_TYPE, },
- { "pcal6524", 24 | PCA953X_TYPE | PCA_INT | PCA_PCAL, },
- { "pcal9555a", 16 | PCA953X_TYPE | PCA_INT | PCA_PCAL, },
+ { "pcal6416", 16 | PCA953X_TYPE | PCA_LATCH_INT, },
+ { "pcal6524", 24 | PCA953X_TYPE | PCA_LATCH_INT, },
+ { "pcal9555a", 16 | PCA953X_TYPE | PCA_LATCH_INT, },
{ "max7310", 8 | PCA953X_TYPE, },
{ "max7312", 16 | PCA953X_TYPE | PCA_INT, },
@@ -108,7 +109,7 @@ static const struct i2c_device_id pca953x_id[] = {
MODULE_DEVICE_TABLE(i2c, pca953x_id);
static const struct acpi_device_id pca953x_acpi_ids[] = {
- { "INT3491", 16 | PCA953X_TYPE | PCA_INT | PCA_PCAL, },
+ { "INT3491", 16 | PCA953X_TYPE | PCA_LATCH_INT, },
{ }
};
MODULE_DEVICE_TABLE(acpi, pca953x_acpi_ids);
@@ -150,6 +151,7 @@ struct pca953x_chip {
u8 irq_stat[MAX_BANK];
u8 irq_trig_raise[MAX_BANK];
u8 irq_trig_fall[MAX_BANK];
+ struct irq_chip irq_chip;
#endif
struct i2c_client *client;
@@ -178,6 +180,8 @@ static int pca953x_bank_shift(struct pca953x_chip *chip)
#define PCA957x_BANK_OUTPUT BIT(5)
#define PCAL9xxx_BANK_IN_LATCH BIT(8 + 2)
+#define PCAL9xxx_BANK_PULL_EN BIT(8 + 3)
+#define PCAL9xxx_BANK_PULL_SEL BIT(8 + 4)
#define PCAL9xxx_BANK_IRQ_MASK BIT(8 + 5)
#define PCAL9xxx_BANK_IRQ_STAT BIT(8 + 6)
@@ -199,6 +203,8 @@ static int pca953x_bank_shift(struct pca953x_chip *chip)
* - Extended set, above 0x40, often chip specific.
* - PCAL6524/PCAL9555A with custom PCAL IRQ handling:
* Input latch register 0x40 + 2 * bank_size RW
+ * Pull-up/pull-down enable reg 0x40 + 3 * bank_size RW
+ * Pull-up/pull-down select reg 0x40 + 4 * bank_size RW
* Interrupt mask register 0x40 + 5 * bank_size RW
* Interrupt status register 0x40 + 6 * bank_size R
*
@@ -247,7 +253,8 @@ static bool pca953x_readable_register(struct device *dev, unsigned int reg)
}
if (chip->driver_data & PCA_PCAL) {
- bank |= PCAL9xxx_BANK_IN_LATCH | PCAL9xxx_BANK_IRQ_MASK |
+ bank |= PCAL9xxx_BANK_IN_LATCH | PCAL9xxx_BANK_PULL_EN |
+ PCAL9xxx_BANK_PULL_SEL | PCAL9xxx_BANK_IRQ_MASK |
PCAL9xxx_BANK_IRQ_STAT;
}
@@ -268,7 +275,8 @@ static bool pca953x_writeable_register(struct device *dev, unsigned int reg)
}
if (chip->driver_data & PCA_PCAL)
- bank |= PCAL9xxx_BANK_IN_LATCH | PCAL9xxx_BANK_IRQ_MASK;
+ bank |= PCAL9xxx_BANK_IN_LATCH | PCAL9xxx_BANK_PULL_EN |
+ PCAL9xxx_BANK_PULL_SEL | PCAL9xxx_BANK_IRQ_MASK;
return pca953x_check_register(chip, reg, bank);
}
@@ -473,6 +481,61 @@ exit:
mutex_unlock(&chip->i2c_lock);
}
+static int pca953x_gpio_set_pull_up_down(struct pca953x_chip *chip,
+ unsigned int offset,
+ unsigned long config)
+{
+ u8 pull_en_reg = pca953x_recalc_addr(chip, PCAL953X_PULL_EN, offset,
+ true, false);
+ u8 pull_sel_reg = pca953x_recalc_addr(chip, PCAL953X_PULL_SEL, offset,
+ true, false);
+ u8 bit = BIT(offset % BANK_SZ);
+ int ret;
+
+ /*
+ * pull-up/pull-down configuration requires PCAL extended
+ * registers
+ */
+ if (!(chip->driver_data & PCA_PCAL))
+ return -ENOTSUPP;
+
+ mutex_lock(&chip->i2c_lock);
+
+ /* Disable pull-up/pull-down */
+ ret = regmap_write_bits(chip->regmap, pull_en_reg, bit, 0);
+ if (ret)
+ goto exit;
+
+ /* Configure pull-up/pull-down */
+ if (config == PIN_CONFIG_BIAS_PULL_UP)
+ ret = regmap_write_bits(chip->regmap, pull_sel_reg, bit, bit);
+ else if (config == PIN_CONFIG_BIAS_PULL_DOWN)
+ ret = regmap_write_bits(chip->regmap, pull_sel_reg, bit, 0);
+ if (ret)
+ goto exit;
+
+ /* Enable pull-up/pull-down */
+ ret = regmap_write_bits(chip->regmap, pull_en_reg, bit, bit);
+
+exit:
+ mutex_unlock(&chip->i2c_lock);
+ return ret;
+}
+
+static int pca953x_gpio_set_config(struct gpio_chip *gc, unsigned int offset,
+ unsigned long config)
+{
+ struct pca953x_chip *chip = gpiochip_get_data(gc);
+
+ switch (config) {
+ case PIN_CONFIG_BIAS_PULL_UP:
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+ return pca953x_gpio_set_pull_up_down(chip, offset, config);
+ default:
+ return -ENOTSUPP;
+ }
+}
+
static void pca953x_setup_gpio(struct pca953x_chip *chip, int gpios)
{
struct gpio_chip *gc;
@@ -485,6 +548,7 @@ static void pca953x_setup_gpio(struct pca953x_chip *chip, int gpios)
gc->set = pca953x_gpio_set_value;
gc->get_direction = pca953x_gpio_get_direction;
gc->set_multiple = pca953x_gpio_set_multiple;
+ gc->set_config = pca953x_gpio_set_config;
gc->can_sleep = true;
gc->base = chip->gpio_start;
@@ -512,6 +576,14 @@ static void pca953x_irq_unmask(struct irq_data *d)
chip->irq_mask[d->hwirq / BANK_SZ] |= 1 << (d->hwirq % BANK_SZ);
}
+static int pca953x_irq_set_wake(struct irq_data *d, unsigned int on)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct pca953x_chip *chip = gpiochip_get_data(gc);
+
+ return irq_set_irq_wake(chip->client->irq, on);
+}
+
static void pca953x_irq_bus_lock(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
@@ -587,23 +659,14 @@ static int pca953x_irq_set_type(struct irq_data *d, unsigned int type)
static void pca953x_irq_shutdown(struct irq_data *d)
{
- struct pca953x_chip *chip = irq_data_get_irq_chip_data(d);
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct pca953x_chip *chip = gpiochip_get_data(gc);
u8 mask = 1 << (d->hwirq % BANK_SZ);
chip->irq_trig_raise[d->hwirq / BANK_SZ] &= ~mask;
chip->irq_trig_fall[d->hwirq / BANK_SZ] &= ~mask;
}
-static struct irq_chip pca953x_irq_chip = {
- .name = "pca953x",
- .irq_mask = pca953x_irq_mask,
- .irq_unmask = pca953x_irq_unmask,
- .irq_bus_lock = pca953x_irq_bus_lock,
- .irq_bus_sync_unlock = pca953x_irq_bus_sync_unlock,
- .irq_set_type = pca953x_irq_set_type,
- .irq_shutdown = pca953x_irq_shutdown,
-};
-
static bool pca953x_irq_pending(struct pca953x_chip *chip, u8 *pending)
{
u8 cur_stat[MAX_BANK];
@@ -699,56 +762,65 @@ static int pca953x_irq_setup(struct pca953x_chip *chip,
int irq_base)
{
struct i2c_client *client = chip->client;
+ struct irq_chip *irq_chip = &chip->irq_chip;
int reg_direction[MAX_BANK];
int ret, i;
- if (client->irq && irq_base != -1
- && (chip->driver_data & PCA_INT)) {
- ret = pca953x_read_regs(chip,
- chip->regs->input, chip->irq_stat);
- if (ret)
- return ret;
+ if (!client->irq)
+ return 0;
- /*
- * There is no way to know which GPIO line generated the
- * interrupt. We have to rely on the previous read for
- * this purpose.
- */
- regmap_bulk_read(chip->regmap, chip->regs->direction,
- reg_direction, NBANK(chip));
- for (i = 0; i < NBANK(chip); i++)
- chip->irq_stat[i] &= reg_direction[i];
- mutex_init(&chip->irq_lock);
-
- ret = devm_request_threaded_irq(&client->dev,
- client->irq,
- NULL,
- pca953x_irq_handler,
- IRQF_TRIGGER_LOW | IRQF_ONESHOT |
- IRQF_SHARED,
- dev_name(&client->dev), chip);
- if (ret) {
- dev_err(&client->dev, "failed to request irq %d\n",
- client->irq);
- return ret;
- }
+ if (irq_base == -1)
+ return 0;
- ret = gpiochip_irqchip_add_nested(&chip->gpio_chip,
- &pca953x_irq_chip,
- irq_base,
- handle_simple_irq,
- IRQ_TYPE_NONE);
- if (ret) {
- dev_err(&client->dev,
- "could not connect irqchip to gpiochip\n");
- return ret;
- }
+ if (!(chip->driver_data & PCA_INT))
+ return 0;
- gpiochip_set_nested_irqchip(&chip->gpio_chip,
- &pca953x_irq_chip,
- client->irq);
+ ret = pca953x_read_regs(chip, chip->regs->input, chip->irq_stat);
+ if (ret)
+ return ret;
+
+ /*
+ * There is no way to know which GPIO line generated the
+ * interrupt. We have to rely on the previous read for
+ * this purpose.
+ */
+ regmap_bulk_read(chip->regmap, chip->regs->direction, reg_direction,
+ NBANK(chip));
+ for (i = 0; i < NBANK(chip); i++)
+ chip->irq_stat[i] &= reg_direction[i];
+ mutex_init(&chip->irq_lock);
+
+ ret = devm_request_threaded_irq(&client->dev, client->irq,
+ NULL, pca953x_irq_handler,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT |
+ IRQF_SHARED,
+ dev_name(&client->dev), chip);
+ if (ret) {
+ dev_err(&client->dev, "failed to request irq %d\n",
+ client->irq);
+ return ret;
}
+ irq_chip->name = dev_name(&chip->client->dev);
+ irq_chip->irq_mask = pca953x_irq_mask;
+ irq_chip->irq_unmask = pca953x_irq_unmask;
+ irq_chip->irq_set_wake = pca953x_irq_set_wake;
+ irq_chip->irq_bus_lock = pca953x_irq_bus_lock;
+ irq_chip->irq_bus_sync_unlock = pca953x_irq_bus_sync_unlock;
+ irq_chip->irq_set_type = pca953x_irq_set_type;
+ irq_chip->irq_shutdown = pca953x_irq_shutdown;
+
+ ret = gpiochip_irqchip_add_nested(&chip->gpio_chip, irq_chip,
+ irq_base, handle_simple_irq,
+ IRQ_TYPE_NONE);
+ if (ret) {
+ dev_err(&client->dev,
+ "could not connect irqchip to gpiochip\n");
+ return ret;
+ }
+
+ gpiochip_set_nested_irqchip(&chip->gpio_chip, irq_chip, client->irq);
+
return 0;
}
diff --git a/drivers/gpio/gpio-pcf857x.c b/drivers/gpio/gpio-pcf857x.c
index 68a35b65925a..c9b650f617fa 100644
--- a/drivers/gpio/gpio-pcf857x.c
+++ b/drivers/gpio/gpio-pcf857x.c
@@ -89,7 +89,6 @@ struct pcf857x {
struct mutex lock; /* protect 'out' */
unsigned out; /* software latch */
unsigned status; /* current status */
- unsigned int irq_parent;
unsigned irq_enabled; /* enabled irqs */
int (*write)(struct i2c_client *client, unsigned data);
@@ -211,18 +210,7 @@ static int pcf857x_irq_set_wake(struct irq_data *data, unsigned int on)
{
struct pcf857x *gpio = irq_data_get_irq_chip_data(data);
- int error = 0;
-
- if (gpio->irq_parent) {
- error = irq_set_irq_wake(gpio->irq_parent, on);
- if (error) {
- dev_dbg(&gpio->client->dev,
- "irq %u doesn't support irq_set_wake\n",
- gpio->irq_parent);
- gpio->irq_parent = 0;
- }
- }
- return error;
+ return irq_set_irq_wake(gpio->client->irq, on);
}
static void pcf857x_irq_enable(struct irq_data *data)
@@ -392,7 +380,6 @@ static int pcf857x_probe(struct i2c_client *client,
gpiochip_set_nested_irqchip(&gpio->chip, &gpio->irqchip,
client->irq);
- gpio->irq_parent = client->irq;
}
/* Let platform code set up the GPIOs and their users.
diff --git a/drivers/gpio/gpio-pch.c b/drivers/gpio/gpio-pch.c
index ee79e5f88b5a..1d99293096f2 100644
--- a/drivers/gpio/gpio-pch.c
+++ b/drivers/gpio/gpio-pch.c
@@ -437,7 +437,6 @@ static int __maybe_unused pch_gpio_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(pch_gpio_pm_ops, pch_gpio_suspend, pch_gpio_resume);
-#define PCI_VENDOR_ID_ROHM 0x10DB
static const struct pci_device_id pch_gpio_pcidev_id[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x8803) },
{ PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x8014) },
diff --git a/drivers/gpio/gpio-pmic-eic-sprd.c b/drivers/gpio/gpio-pmic-eic-sprd.c
index 29e044ff4b17..24228cf79afc 100644
--- a/drivers/gpio/gpio-pmic-eic-sprd.c
+++ b/drivers/gpio/gpio-pmic-eic-sprd.c
@@ -322,7 +322,6 @@ static int sprd_pmic_eic_probe(struct platform_device *pdev)
ret = devm_request_threaded_irq(&pdev->dev, pmic_eic->irq, NULL,
sprd_pmic_eic_irq_handler,
- IRQF_TRIGGER_LOW |
IRQF_ONESHOT | IRQF_NO_SUSPEND,
dev_name(&pdev->dev), pmic_eic);
if (ret) {
@@ -365,7 +364,7 @@ static int sprd_pmic_eic_probe(struct platform_device *pdev)
}
static const struct of_device_id sprd_pmic_eic_of_match[] = {
- { .compatible = "sprd,sc27xx-eic", },
+ { .compatible = "sprd,sc2731-eic", },
{ /* end of list */ }
};
MODULE_DEVICE_TABLE(of, sprd_pmic_eic_of_match);
diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c
index 068ce25ffd28..500a3596aaf4 100644
--- a/drivers/gpio/gpio-rcar.c
+++ b/drivers/gpio/gpio-rcar.c
@@ -40,6 +40,7 @@ struct gpio_rcar_priv {
struct irq_chip irq_chip;
unsigned int irq_parent;
atomic_t wakeup_path;
+ bool has_outdtsel;
bool has_both_edge_trigger;
struct gpio_rcar_bank_info bank_info;
};
@@ -55,6 +56,7 @@ struct gpio_rcar_priv {
#define POSNEG 0x20 /* Positive/Negative Logic Select Register */
#define EDGLEVEL 0x24 /* Edge/level Select Register */
#define FILONOFF 0x28 /* Chattering Prevention On/Off Register */
+#define OUTDTSEL 0x40 /* Output Data Select Register */
#define BOTHEDGE 0x4c /* One Edge/Both Edge Select Register */
#define RCAR_MAX_GPIO_PER_BANK 32
@@ -235,6 +237,10 @@ static void gpio_rcar_config_general_input_output_mode(struct gpio_chip *chip,
/* Select Input Mode or Output Mode in INOUTSEL */
gpio_rcar_modify_bit(p, INOUTSEL, gpio, output);
+ /* Select General Output Register to output data in OUTDTSEL */
+ if (p->has_outdtsel && output)
+ gpio_rcar_modify_bit(p, OUTDTSEL, gpio, false);
+
spin_unlock_irqrestore(&p->lock, flags);
}
@@ -336,14 +342,17 @@ static int gpio_rcar_direction_output(struct gpio_chip *chip, unsigned offset,
}
struct gpio_rcar_info {
+ bool has_outdtsel;
bool has_both_edge_trigger;
};
static const struct gpio_rcar_info gpio_rcar_info_gen1 = {
+ .has_outdtsel = false,
.has_both_edge_trigger = false,
};
static const struct gpio_rcar_info gpio_rcar_info_gen2 = {
+ .has_outdtsel = true,
.has_both_edge_trigger = true,
};
@@ -403,10 +412,11 @@ static int gpio_rcar_parse_dt(struct gpio_rcar_priv *p, unsigned int *npins)
int ret;
info = of_device_get_match_data(p->dev);
+ p->has_outdtsel = info->has_outdtsel;
+ p->has_both_edge_trigger = info->has_both_edge_trigger;
ret = of_parse_phandle_with_fixed_args(np, "gpio-ranges", 3, 0, &args);
*npins = ret == 0 ? args.args[2] : RCAR_MAX_GPIO_PER_BANK;
- p->has_both_edge_trigger = info->has_both_edge_trigger;
if (*npins == 0 || *npins > RCAR_MAX_GPIO_PER_BANK) {
dev_warn(p->dev, "Invalid number of gpio lines %u, using %u\n",
diff --git a/drivers/gpio/gpio-sama5d2-piobu.c b/drivers/gpio/gpio-sama5d2-piobu.c
index 03a000659fa1..7d718557092e 100644
--- a/drivers/gpio/gpio-sama5d2-piobu.c
+++ b/drivers/gpio/gpio-sama5d2-piobu.c
@@ -109,16 +109,6 @@ static int sama5d2_piobu_read_value(struct gpio_chip *chip, unsigned int pin,
}
/**
- * sama5d2_piobu_set_direction() - mark pin as input or output
- */
-static int sama5d2_piobu_set_direction(struct gpio_chip *chip,
- unsigned int direction,
- unsigned int pin)
-{
- return sama5d2_piobu_write_value(chip, pin, PIOBU_DIRECTION, direction);
-}
-
-/**
* sama5d2_piobu_get_direction() - gpiochip get_direction
*/
static int sama5d2_piobu_get_direction(struct gpio_chip *chip,
@@ -138,7 +128,7 @@ static int sama5d2_piobu_get_direction(struct gpio_chip *chip,
static int sama5d2_piobu_direction_input(struct gpio_chip *chip,
unsigned int pin)
{
- return sama5d2_piobu_set_direction(chip, PIOBU_IN, pin);
+ return sama5d2_piobu_write_value(chip, pin, PIOBU_DIRECTION, PIOBU_IN);
}
/**
@@ -147,7 +137,13 @@ static int sama5d2_piobu_direction_input(struct gpio_chip *chip,
static int sama5d2_piobu_direction_output(struct gpio_chip *chip,
unsigned int pin, int value)
{
- return sama5d2_piobu_set_direction(chip, PIOBU_OUT, pin);
+ unsigned int val = PIOBU_OUT;
+
+ if (value)
+ val |= PIOBU_HIGH;
+
+ return sama5d2_piobu_write_value(chip, pin, PIOBU_DIRECTION | PIOBU_SOD,
+ val);
}
/**
diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c
index 02f6db925fd5..1ececf2c3282 100644
--- a/drivers/gpio/gpio-tegra.c
+++ b/drivers/gpio/gpio-tegra.c
@@ -2,6 +2,7 @@
* arch/arm/mach-tegra/gpio.c
*
* Copyright (c) 2010 Google, Inc
+ * Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
*
* Author:
* Erik Gilling <konkers@google.com>
@@ -141,14 +142,14 @@ static void tegra_gpio_disable(struct tegra_gpio_info *tgi, unsigned int gpio)
static int tegra_gpio_request(struct gpio_chip *chip, unsigned int offset)
{
- return pinctrl_gpio_request(offset);
+ return pinctrl_gpio_request(chip->base + offset);
}
static void tegra_gpio_free(struct gpio_chip *chip, unsigned int offset)
{
struct tegra_gpio_info *tgi = gpiochip_get_data(chip);
- pinctrl_gpio_free(offset);
+ pinctrl_gpio_free(chip->base + offset);
tegra_gpio_disable(tgi, offset);
}
@@ -176,10 +177,18 @@ static int tegra_gpio_direction_input(struct gpio_chip *chip,
unsigned int offset)
{
struct tegra_gpio_info *tgi = gpiochip_get_data(chip);
+ int ret;
tegra_gpio_mask_write(tgi, GPIO_MSK_OE(tgi, offset), offset, 0);
tegra_gpio_enable(tgi, offset);
- return 0;
+
+ ret = pinctrl_gpio_direction_input(chip->base + offset);
+ if (ret < 0)
+ dev_err(tgi->dev,
+ "Failed to set pinctrl input direction of GPIO %d: %d",
+ chip->base + offset, ret);
+
+ return ret;
}
static int tegra_gpio_direction_output(struct gpio_chip *chip,
@@ -187,11 +196,19 @@ static int tegra_gpio_direction_output(struct gpio_chip *chip,
int value)
{
struct tegra_gpio_info *tgi = gpiochip_get_data(chip);
+ int ret;
tegra_gpio_set(chip, offset, value);
tegra_gpio_mask_write(tgi, GPIO_MSK_OE(tgi, offset), offset, 1);
tegra_gpio_enable(tgi, offset);
- return 0;
+
+ ret = pinctrl_gpio_direction_output(chip->base + offset);
+ if (ret < 0)
+ dev_err(tgi->dev,
+ "Failed to set pinctrl output direction of GPIO %d: %d",
+ chip->base + offset, ret);
+
+ return ret;
}
static int tegra_gpio_get_direction(struct gpio_chip *chip,
diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c
index 66ec38bb7954..7d42e3d7572c 100644
--- a/drivers/gpio/gpio-tegra186.c
+++ b/drivers/gpio/gpio-tegra186.c
@@ -529,8 +529,8 @@ static int tegra186_gpio_remove(struct platform_device *pdev)
return 0;
}
-#define TEGRA_MAIN_GPIO_PORT(port, base, count, controller) \
- [TEGRA_MAIN_GPIO_PORT_##port] = { \
+#define TEGRA186_MAIN_GPIO_PORT(port, base, count, controller) \
+ [TEGRA186_MAIN_GPIO_PORT_##port] = { \
.name = #port, \
.offset = base, \
.pins = count, \
@@ -538,29 +538,29 @@ static int tegra186_gpio_remove(struct platform_device *pdev)
}
static const struct tegra_gpio_port tegra186_main_ports[] = {
- TEGRA_MAIN_GPIO_PORT( A, 0x2000, 7, 2),
- TEGRA_MAIN_GPIO_PORT( B, 0x3000, 7, 3),
- TEGRA_MAIN_GPIO_PORT( C, 0x3200, 7, 3),
- TEGRA_MAIN_GPIO_PORT( D, 0x3400, 6, 3),
- TEGRA_MAIN_GPIO_PORT( E, 0x2200, 8, 2),
- TEGRA_MAIN_GPIO_PORT( F, 0x2400, 6, 2),
- TEGRA_MAIN_GPIO_PORT( G, 0x4200, 6, 4),
- TEGRA_MAIN_GPIO_PORT( H, 0x1000, 7, 1),
- TEGRA_MAIN_GPIO_PORT( I, 0x0800, 8, 0),
- TEGRA_MAIN_GPIO_PORT( J, 0x5000, 8, 5),
- TEGRA_MAIN_GPIO_PORT( K, 0x5200, 1, 5),
- TEGRA_MAIN_GPIO_PORT( L, 0x1200, 8, 1),
- TEGRA_MAIN_GPIO_PORT( M, 0x5600, 6, 5),
- TEGRA_MAIN_GPIO_PORT( N, 0x0000, 7, 0),
- TEGRA_MAIN_GPIO_PORT( O, 0x0200, 4, 0),
- TEGRA_MAIN_GPIO_PORT( P, 0x4000, 7, 4),
- TEGRA_MAIN_GPIO_PORT( Q, 0x0400, 6, 0),
- TEGRA_MAIN_GPIO_PORT( R, 0x0a00, 6, 0),
- TEGRA_MAIN_GPIO_PORT( T, 0x0600, 4, 0),
- TEGRA_MAIN_GPIO_PORT( X, 0x1400, 8, 1),
- TEGRA_MAIN_GPIO_PORT( Y, 0x1600, 7, 1),
- TEGRA_MAIN_GPIO_PORT(BB, 0x2600, 2, 2),
- TEGRA_MAIN_GPIO_PORT(CC, 0x5400, 4, 5),
+ TEGRA186_MAIN_GPIO_PORT( A, 0x2000, 7, 2),
+ TEGRA186_MAIN_GPIO_PORT( B, 0x3000, 7, 3),
+ TEGRA186_MAIN_GPIO_PORT( C, 0x3200, 7, 3),
+ TEGRA186_MAIN_GPIO_PORT( D, 0x3400, 6, 3),
+ TEGRA186_MAIN_GPIO_PORT( E, 0x2200, 8, 2),
+ TEGRA186_MAIN_GPIO_PORT( F, 0x2400, 6, 2),
+ TEGRA186_MAIN_GPIO_PORT( G, 0x4200, 6, 4),
+ TEGRA186_MAIN_GPIO_PORT( H, 0x1000, 7, 1),
+ TEGRA186_MAIN_GPIO_PORT( I, 0x0800, 8, 0),
+ TEGRA186_MAIN_GPIO_PORT( J, 0x5000, 8, 5),
+ TEGRA186_MAIN_GPIO_PORT( K, 0x5200, 1, 5),
+ TEGRA186_MAIN_GPIO_PORT( L, 0x1200, 8, 1),
+ TEGRA186_MAIN_GPIO_PORT( M, 0x5600, 6, 5),
+ TEGRA186_MAIN_GPIO_PORT( N, 0x0000, 7, 0),
+ TEGRA186_MAIN_GPIO_PORT( O, 0x0200, 4, 0),
+ TEGRA186_MAIN_GPIO_PORT( P, 0x4000, 7, 4),
+ TEGRA186_MAIN_GPIO_PORT( Q, 0x0400, 6, 0),
+ TEGRA186_MAIN_GPIO_PORT( R, 0x0a00, 6, 0),
+ TEGRA186_MAIN_GPIO_PORT( T, 0x0600, 4, 0),
+ TEGRA186_MAIN_GPIO_PORT( X, 0x1400, 8, 1),
+ TEGRA186_MAIN_GPIO_PORT( Y, 0x1600, 7, 1),
+ TEGRA186_MAIN_GPIO_PORT(BB, 0x2600, 2, 2),
+ TEGRA186_MAIN_GPIO_PORT(CC, 0x5400, 4, 5),
};
static const struct tegra_gpio_soc tegra186_main_soc = {
@@ -569,8 +569,8 @@ static const struct tegra_gpio_soc tegra186_main_soc = {
.name = "tegra186-gpio",
};
-#define TEGRA_AON_GPIO_PORT(port, base, count, controller) \
- [TEGRA_AON_GPIO_PORT_##port] = { \
+#define TEGRA186_AON_GPIO_PORT(port, base, count, controller) \
+ [TEGRA186_AON_GPIO_PORT_##port] = { \
.name = #port, \
.offset = base, \
.pins = count, \
@@ -578,14 +578,14 @@ static const struct tegra_gpio_soc tegra186_main_soc = {
}
static const struct tegra_gpio_port tegra186_aon_ports[] = {
- TEGRA_AON_GPIO_PORT( S, 0x0200, 5, 0),
- TEGRA_AON_GPIO_PORT( U, 0x0400, 6, 0),
- TEGRA_AON_GPIO_PORT( V, 0x0800, 8, 0),
- TEGRA_AON_GPIO_PORT( W, 0x0a00, 8, 0),
- TEGRA_AON_GPIO_PORT( Z, 0x0e00, 4, 0),
- TEGRA_AON_GPIO_PORT(AA, 0x0c00, 8, 0),
- TEGRA_AON_GPIO_PORT(EE, 0x0600, 3, 0),
- TEGRA_AON_GPIO_PORT(FF, 0x0000, 5, 0),
+ TEGRA186_AON_GPIO_PORT( S, 0x0200, 5, 0),
+ TEGRA186_AON_GPIO_PORT( U, 0x0400, 6, 0),
+ TEGRA186_AON_GPIO_PORT( V, 0x0800, 8, 0),
+ TEGRA186_AON_GPIO_PORT( W, 0x0a00, 8, 0),
+ TEGRA186_AON_GPIO_PORT( Z, 0x0e00, 4, 0),
+ TEGRA186_AON_GPIO_PORT(AA, 0x0c00, 8, 0),
+ TEGRA186_AON_GPIO_PORT(EE, 0x0600, 3, 0),
+ TEGRA186_AON_GPIO_PORT(FF, 0x0000, 5, 0),
};
static const struct tegra_gpio_soc tegra186_aon_soc = {
diff --git a/drivers/gpio/gpio-tqmx86.c b/drivers/gpio/gpio-tqmx86.c
new file mode 100644
index 000000000000..d5880db7f9d4
--- /dev/null
+++ b/drivers/gpio/gpio-tqmx86.c
@@ -0,0 +1,332 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * TQ-Systems TQMx86 PLD GPIO driver
+ *
+ * Based on vendor driver by:
+ * Vadim V.Vlasov <vvlasov@dev.rtsoft.ru>
+ */
+
+#include <linux/bitops.h>
+#include <linux/errno.h>
+#include <linux/gpio/driver.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+
+#define TQMX86_NGPIO 8
+#define TQMX86_NGPO 4 /* 0-3 - output */
+#define TQMX86_NGPI 4 /* 4-7 - input */
+#define TQMX86_DIR_INPUT_MASK 0xf0 /* 0-3 - output, 4-7 - input */
+
+#define TQMX86_GPIODD 0 /* GPIO Data Direction Register */
+#define TQMX86_GPIOD 1 /* GPIO Data Register */
+#define TQMX86_GPIIC 3 /* GPI Interrupt Configuration Register */
+#define TQMX86_GPIIS 4 /* GPI Interrupt Status Register */
+
+#define TQMX86_GPII_FALLING BIT(0)
+#define TQMX86_GPII_RISING BIT(1)
+#define TQMX86_GPII_MASK (BIT(0) | BIT(1))
+#define TQMX86_GPII_BITS 2
+
+struct tqmx86_gpio_data {
+ struct gpio_chip chip;
+ struct irq_chip irq_chip;
+ void __iomem *io_base;
+ int irq;
+ raw_spinlock_t spinlock;
+ u8 irq_type[TQMX86_NGPI];
+};
+
+static u8 tqmx86_gpio_read(struct tqmx86_gpio_data *gd, unsigned int reg)
+{
+ return ioread8(gd->io_base + reg);
+}
+
+static void tqmx86_gpio_write(struct tqmx86_gpio_data *gd, u8 val,
+ unsigned int reg)
+{
+ iowrite8(val, gd->io_base + reg);
+}
+
+static int tqmx86_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+ struct tqmx86_gpio_data *gpio = gpiochip_get_data(chip);
+
+ return !!(tqmx86_gpio_read(gpio, TQMX86_GPIOD) & BIT(offset));
+}
+
+static void tqmx86_gpio_set(struct gpio_chip *chip, unsigned int offset,
+ int value)
+{
+ struct tqmx86_gpio_data *gpio = gpiochip_get_data(chip);
+ unsigned long flags;
+ u8 val;
+
+ raw_spin_lock_irqsave(&gpio->spinlock, flags);
+ val = tqmx86_gpio_read(gpio, TQMX86_GPIOD);
+ if (value)
+ val |= BIT(offset);
+ else
+ val &= ~BIT(offset);
+ tqmx86_gpio_write(gpio, val, TQMX86_GPIOD);
+ raw_spin_unlock_irqrestore(&gpio->spinlock, flags);
+}
+
+static int tqmx86_gpio_direction_input(struct gpio_chip *chip,
+ unsigned int offset)
+{
+ /* Direction cannot be changed. Validate is an input. */
+ if (BIT(offset) & TQMX86_DIR_INPUT_MASK)
+ return 0;
+ else
+ return -EINVAL;
+}
+
+static int tqmx86_gpio_direction_output(struct gpio_chip *chip,
+ unsigned int offset,
+ int value)
+{
+ /* Direction cannot be changed, validate is an output */
+ if (BIT(offset) & TQMX86_DIR_INPUT_MASK)
+ return -EINVAL;
+
+ tqmx86_gpio_set(chip, offset, value);
+ return 0;
+}
+
+static int tqmx86_gpio_get_direction(struct gpio_chip *chip,
+ unsigned int offset)
+{
+ return !!(TQMX86_DIR_INPUT_MASK & BIT(offset));
+}
+
+static void tqmx86_gpio_irq_mask(struct irq_data *data)
+{
+ unsigned int offset = (data->hwirq - TQMX86_NGPO);
+ struct tqmx86_gpio_data *gpio = gpiochip_get_data(
+ irq_data_get_irq_chip_data(data));
+ unsigned long flags;
+ u8 gpiic, mask;
+
+ mask = TQMX86_GPII_MASK << (offset * TQMX86_GPII_BITS);
+
+ raw_spin_lock_irqsave(&gpio->spinlock, flags);
+ gpiic = tqmx86_gpio_read(gpio, TQMX86_GPIIC);
+ gpiic &= ~mask;
+ tqmx86_gpio_write(gpio, gpiic, TQMX86_GPIIC);
+ raw_spin_unlock_irqrestore(&gpio->spinlock, flags);
+}
+
+static void tqmx86_gpio_irq_unmask(struct irq_data *data)
+{
+ unsigned int offset = (data->hwirq - TQMX86_NGPO);
+ struct tqmx86_gpio_data *gpio = gpiochip_get_data(
+ irq_data_get_irq_chip_data(data));
+ unsigned long flags;
+ u8 gpiic, mask;
+
+ mask = TQMX86_GPII_MASK << (offset * TQMX86_GPII_BITS);
+
+ raw_spin_lock_irqsave(&gpio->spinlock, flags);
+ gpiic = tqmx86_gpio_read(gpio, TQMX86_GPIIC);
+ gpiic &= ~mask;
+ gpiic |= gpio->irq_type[offset] << (offset * TQMX86_GPII_BITS);
+ tqmx86_gpio_write(gpio, gpiic, TQMX86_GPIIC);
+ raw_spin_unlock_irqrestore(&gpio->spinlock, flags);
+}
+
+static int tqmx86_gpio_irq_set_type(struct irq_data *data, unsigned int type)
+{
+ struct tqmx86_gpio_data *gpio = gpiochip_get_data(
+ irq_data_get_irq_chip_data(data));
+ unsigned int offset = (data->hwirq - TQMX86_NGPO);
+ unsigned int edge_type = type & IRQF_TRIGGER_MASK;
+ unsigned long flags;
+ u8 new_type, gpiic;
+
+ switch (edge_type) {
+ case IRQ_TYPE_EDGE_RISING:
+ new_type = TQMX86_GPII_RISING;
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ new_type = TQMX86_GPII_FALLING;
+ break;
+ case IRQ_TYPE_EDGE_BOTH:
+ new_type = TQMX86_GPII_FALLING | TQMX86_GPII_RISING;
+ break;
+ default:
+ return -EINVAL; /* not supported */
+ }
+
+ gpio->irq_type[offset] = new_type;
+
+ raw_spin_lock_irqsave(&gpio->spinlock, flags);
+ gpiic = tqmx86_gpio_read(gpio, TQMX86_GPIIC);
+ gpiic &= ~((TQMX86_GPII_MASK) << (offset * TQMX86_GPII_BITS));
+ gpiic |= new_type << (offset * TQMX86_GPII_BITS);
+ tqmx86_gpio_write(gpio, gpiic, TQMX86_GPIIC);
+ raw_spin_unlock_irqrestore(&gpio->spinlock, flags);
+
+ return 0;
+}
+
+static void tqmx86_gpio_irq_handler(struct irq_desc *desc)
+{
+ struct gpio_chip *chip = irq_desc_get_handler_data(desc);
+ struct tqmx86_gpio_data *gpio = gpiochip_get_data(chip);
+ struct irq_chip *irq_chip = irq_desc_get_chip(desc);
+ unsigned long irq_bits;
+ int i = 0, child_irq;
+ u8 irq_status;
+
+ chained_irq_enter(irq_chip, desc);
+
+ irq_status = tqmx86_gpio_read(gpio, TQMX86_GPIIS);
+ tqmx86_gpio_write(gpio, irq_status, TQMX86_GPIIS);
+
+ irq_bits = irq_status;
+ for_each_set_bit(i, &irq_bits, TQMX86_NGPI) {
+ child_irq = irq_find_mapping(gpio->chip.irq.domain,
+ i + TQMX86_NGPO);
+ generic_handle_irq(child_irq);
+ }
+
+ chained_irq_exit(irq_chip, desc);
+}
+
+/* Minimal runtime PM is needed by the IRQ subsystem */
+static int __maybe_unused tqmx86_gpio_runtime_suspend(struct device *dev)
+{
+ return 0;
+}
+
+static int __maybe_unused 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)
+};
+
+static int tqmx86_gpio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct tqmx86_gpio_data *gpio;
+ struct gpio_chip *chip;
+ void __iomem *io_base;
+ struct resource *res;
+ int ret, irq;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "Cannot get I/O\n");
+ return -ENODEV;
+ }
+
+ io_base = devm_ioport_map(&pdev->dev, res->start, resource_size(res));
+ if (!io_base)
+ return -ENOMEM;
+
+ gpio = devm_kzalloc(dev, sizeof(*gpio), GFP_KERNEL);
+ if (!gpio)
+ return -ENOMEM;
+
+ raw_spin_lock_init(&gpio->spinlock);
+ gpio->io_base = io_base;
+
+ tqmx86_gpio_write(gpio, (u8)~TQMX86_DIR_INPUT_MASK, TQMX86_GPIODD);
+
+ platform_set_drvdata(pdev, gpio);
+
+ chip = &gpio->chip;
+ chip->label = "gpio-tqmx86";
+ chip->owner = THIS_MODULE;
+ chip->can_sleep = false;
+ chip->base = -1;
+ chip->direction_input = tqmx86_gpio_direction_input;
+ chip->direction_output = tqmx86_gpio_direction_output;
+ chip->get_direction = tqmx86_gpio_get_direction;
+ chip->get = tqmx86_gpio_get;
+ chip->set = tqmx86_gpio_set;
+ chip->ngpio = TQMX86_NGPIO;
+ chip->irq.need_valid_mask = true;
+ chip->parent = pdev->dev.parent;
+
+ pm_runtime_enable(&pdev->dev);
+
+ ret = devm_gpiochip_add_data(dev, chip, gpio);
+ if (ret) {
+ dev_err(dev, "Could not register GPIO chip\n");
+ goto out_pm_dis;
+ }
+
+ if (irq) {
+ struct irq_chip *irq_chip = &gpio->irq_chip;
+ u8 irq_status;
+
+ irq_chip->name = chip->label;
+ irq_chip->parent_device = &pdev->dev;
+ irq_chip->irq_mask = tqmx86_gpio_irq_mask;
+ irq_chip->irq_unmask = tqmx86_gpio_irq_unmask;
+ irq_chip->irq_set_type = tqmx86_gpio_irq_set_type;
+
+ /* Mask all interrupts */
+ tqmx86_gpio_write(gpio, 0, TQMX86_GPIIC);
+
+ /* Clear all pending interrupts */
+ irq_status = tqmx86_gpio_read(gpio, TQMX86_GPIIS);
+ tqmx86_gpio_write(gpio, irq_status, TQMX86_GPIIS);
+
+ ret = gpiochip_irqchip_add(chip, irq_chip,
+ 0, handle_simple_irq,
+ IRQ_TYPE_EDGE_BOTH);
+ if (ret) {
+ dev_err(dev, "Could not add irq chip\n");
+ goto out_pm_dis;
+ }
+
+ gpiochip_set_chained_irqchip(chip, irq_chip,
+ irq, tqmx86_gpio_irq_handler);
+ }
+
+ /* Only GPIOs 4-7 are valid for interrupts. Clear the others */
+ clear_bit(0, chip->irq.valid_mask);
+ clear_bit(1, chip->irq.valid_mask);
+ clear_bit(2, chip->irq.valid_mask);
+ clear_bit(3, chip->irq.valid_mask);
+
+ dev_info(dev, "GPIO functionality initialized with %d pins\n",
+ chip->ngpio);
+
+ return 0;
+
+out_pm_dis:
+ pm_runtime_disable(&pdev->dev);
+
+ return ret;
+}
+
+static struct platform_driver tqmx86_gpio_driver = {
+ .driver = {
+ .name = "tqmx86-gpio",
+ .pm = &tqmx86_gpio_dev_pm_ops,
+ },
+ .probe = tqmx86_gpio_probe,
+};
+
+module_platform_driver(tqmx86_gpio_driver);
+
+MODULE_DESCRIPTION("TQMx86 PLD GPIO Driver");
+MODULE_AUTHOR("Andrew Lunn <andrew@lunn.ch>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:tqmx86-gpio");
diff --git a/drivers/gpio/gpio-wcove.c b/drivers/gpio/gpio-wcove.c
index dde7c6aecbb5..444fe9e7f04a 100644
--- a/drivers/gpio/gpio-wcove.c
+++ b/drivers/gpio/gpio-wcove.c
@@ -1,34 +1,26 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Intel Whiskey Cove PMIC GPIO Driver
*
* This driver is written based on gpio-crystalcove.c
*
* Copyright (C) 2016 Intel Corporation. All rights reserved.
- *
- * 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.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/bitops.h>
-#include <linux/module.h>
-#include <linux/interrupt.h>
#include <linux/gpio/driver.h>
+#include <linux/interrupt.h>
#include <linux/mfd/intel_soc_pmic.h>
+#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/seq_file.h>
/*
* Whiskey Cove PMIC has 13 physical GPIO pins divided into 3 banks:
- * Bank 0: Pin 0 - 6
- * Bank 1: Pin 7 - 10
- * Bank 2: Pin 11 -12
+ * Bank 0: Pin 0 - 6
+ * Bank 1: Pin 7 - 10
+ * Bank 2: Pin 11 - 12
* Each pin has one output control register and one input control register.
*/
#define BANK0_NR_PINS 7
@@ -75,8 +67,8 @@
#define CTLO_RVAL_50KDOWN (2 << 1)
#define CTLO_RVAL_50KUP (3 << 1)
-#define CTLO_INPUT_SET (CTLO_DRV_CMOS | CTLO_DRV_REN | CTLO_RVAL_2KUP)
-#define CTLO_OUTPUT_SET (CTLO_DIR_OUT | CTLO_INPUT_SET)
+#define CTLO_INPUT_SET (CTLO_DRV_CMOS | CTLO_DRV_REN | CTLO_RVAL_2KUP)
+#define CTLO_OUTPUT_SET (CTLO_DIR_OUT | CTLO_INPUT_SET)
enum ctrl_register {
CTRL_IN,
@@ -105,7 +97,7 @@ struct wcove_gpio {
bool set_irq_mask;
};
-static inline unsigned int to_reg(int gpio, enum ctrl_register reg_type)
+static inline int to_reg(int gpio, enum ctrl_register reg_type)
{
unsigned int reg;
@@ -203,8 +195,7 @@ static int wcove_gpio_get(struct gpio_chip *chip, unsigned int gpio)
return val & 0x1;
}
-static void wcove_gpio_set(struct gpio_chip *chip,
- unsigned int gpio, int value)
+static void wcove_gpio_set(struct gpio_chip *chip, unsigned int gpio, int value)
{
struct wcove_gpio *wg = gpiochip_get_data(chip);
int reg = to_reg(gpio, CTRL_OUT);
diff --git a/drivers/gpio/gpio-zynq.c b/drivers/gpio/gpio-zynq.c
index b3b4edcdffe0..00ff7b1fa8a1 100644
--- a/drivers/gpio/gpio-zynq.c
+++ b/drivers/gpio/gpio-zynq.c
@@ -555,6 +555,26 @@ static int zynq_gpio_set_wake(struct irq_data *data, unsigned int on)
return 0;
}
+static int zynq_gpio_irq_reqres(struct irq_data *d)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
+ int ret;
+
+ ret = pm_runtime_get_sync(chip->parent);
+ if (ret < 0)
+ return ret;
+
+ return gpiochip_reqres_irq(chip, d->hwirq);
+}
+
+static void zynq_gpio_irq_relres(struct irq_data *d)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
+
+ gpiochip_relres_irq(chip, d->hwirq);
+ pm_runtime_put(chip->parent);
+}
+
/* irq chip descriptor */
static struct irq_chip zynq_gpio_level_irqchip = {
.name = DRIVER_NAME,
@@ -564,6 +584,8 @@ static struct irq_chip zynq_gpio_level_irqchip = {
.irq_unmask = zynq_gpio_irq_unmask,
.irq_set_type = zynq_gpio_set_irq_type,
.irq_set_wake = zynq_gpio_set_wake,
+ .irq_request_resources = zynq_gpio_irq_reqres,
+ .irq_release_resources = zynq_gpio_irq_relres,
.flags = IRQCHIP_EOI_THREADED | IRQCHIP_EOI_IF_HANDLED |
IRQCHIP_MASK_ON_SUSPEND,
};
@@ -576,6 +598,8 @@ static struct irq_chip zynq_gpio_edge_irqchip = {
.irq_unmask = zynq_gpio_irq_unmask,
.irq_set_type = zynq_gpio_set_irq_type,
.irq_set_wake = zynq_gpio_set_wake,
+ .irq_request_resources = zynq_gpio_irq_reqres,
+ .irq_release_resources = zynq_gpio_irq_relres,
.flags = IRQCHIP_MASK_ON_SUSPEND,
};
diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c
index 3e8e2d3ce7a8..30d0baf7ddae 100644
--- a/drivers/gpio/gpiolib-acpi.c
+++ b/drivers/gpio/gpiolib-acpi.c
@@ -29,7 +29,7 @@
* @irq: Linux IRQ number for the event, for request_ / free_irq
* @irqflags: flags to pass to request_irq when requesting the IRQ
* @irq_is_wake: If the ACPI flags indicate the IRQ is a wakeup source
- * @is_requested: True if request_irq has been done
+ * @irq_requested:True if request_irq has been done
* @desc: gpio_desc for the GPIO pin for this event
*/
struct acpi_gpio_event {
@@ -469,6 +469,9 @@ acpi_gpio_to_gpiod_flags(const struct acpi_resource_gpio *agpio)
static int
__acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, enum gpiod_flags update)
{
+ const enum gpiod_flags mask =
+ GPIOD_FLAGS_BIT_DIR_SET | GPIOD_FLAGS_BIT_DIR_OUT |
+ GPIOD_FLAGS_BIT_DIR_VAL;
int ret = 0;
/*
@@ -489,7 +492,7 @@ __acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, enum gpiod_flags update)
if (((*flags & GPIOD_FLAGS_BIT_DIR_SET) && (diff & GPIOD_FLAGS_BIT_DIR_OUT)) ||
((*flags & GPIOD_FLAGS_BIT_DIR_OUT) && (diff & GPIOD_FLAGS_BIT_DIR_VAL)))
ret = -EINVAL;
- *flags = update;
+ *flags = (*flags & ~mask) | (update & mask);
}
return ret;
}
diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c
index c34eb9d9c59a..6a3ec575a404 100644
--- a/drivers/gpio/gpiolib-of.c
+++ b/drivers/gpio/gpiolib-of.c
@@ -86,7 +86,9 @@ static void of_gpio_flags_quirks(struct device_node *np,
if (IS_ENABLED(CONFIG_REGULATOR) &&
(of_device_is_compatible(np, "regulator-fixed") ||
of_device_is_compatible(np, "reg-fixed-voltage") ||
- of_device_is_compatible(np, "regulator-gpio"))) {
+ (of_device_is_compatible(np, "regulator-gpio") &&
+ !(strcmp(propname, "enable-gpio") &&
+ strcmp(propname, "enable-gpios"))))) {
/*
* The regulator GPIO handles are specified such that the
* presence or absence of "enable-active-high" solely controls
@@ -118,7 +120,8 @@ static void of_gpio_flags_quirks(struct device_node *np,
* to determine if the flags should have inverted semantics.
*/
if (IS_ENABLED(CONFIG_SPI_MASTER) &&
- of_property_read_bool(np, "cs-gpios")) {
+ of_property_read_bool(np, "cs-gpios") &&
+ !strcmp(propname, "cs-gpios")) {
struct device_node *child;
u32 cs;
int ret;
@@ -140,16 +143,16 @@ static void of_gpio_flags_quirks(struct device_node *np,
* conflict and the "spi-cs-high" flag will
* take precedence.
*/
- if (of_property_read_bool(np, "spi-cs-high")) {
+ if (of_property_read_bool(child, "spi-cs-high")) {
if (*flags & OF_GPIO_ACTIVE_LOW) {
pr_warn("%s GPIO handle specifies active low - ignored\n",
- of_node_full_name(np));
+ of_node_full_name(child));
*flags &= ~OF_GPIO_ACTIVE_LOW;
}
} else {
if (!(*flags & OF_GPIO_ACTIVE_LOW))
pr_info("%s enforce active low on chipselect handle\n",
- of_node_full_name(np));
+ of_node_full_name(child));
*flags |= OF_GPIO_ACTIVE_LOW;
}
break;
@@ -345,6 +348,11 @@ struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id,
if (of_flags & OF_GPIO_TRANSITORY)
*flags |= GPIO_TRANSITORY;
+ if (of_flags & OF_GPIO_PULL_UP)
+ *flags |= GPIO_PULL_UP;
+ if (of_flags & OF_GPIO_PULL_DOWN)
+ *flags |= GPIO_PULL_DOWN;
+
return desc;
}
@@ -710,7 +718,13 @@ int of_gpiochip_add(struct gpio_chip *chip)
of_node_get(chip->of_node);
- return of_gpiochip_scan_gpios(chip);
+ status = of_gpiochip_scan_gpios(chip);
+ if (status) {
+ of_node_put(chip->of_node);
+ gpiochip_remove_pin_ranges(chip);
+ }
+
+ return status;
}
void of_gpiochip_remove(struct gpio_chip *chip)
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index d1adfdf50fb3..0495bf1d480a 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -1782,6 +1782,43 @@ static const struct irq_domain_ops gpiochip_domain_ops = {
.xlate = irq_domain_xlate_twocell,
};
+/**
+ * gpiochip_irq_domain_activate() - Lock a GPIO to be used as an IRQ
+ * @domain: The IRQ domain used by this IRQ chip
+ * @data: Outermost irq_data associated with the IRQ
+ * @reserve: If set, only reserve an interrupt vector instead of assigning one
+ *
+ * This function is a wrapper that calls gpiochip_lock_as_irq() and is to be
+ * used as the activate function for the &struct irq_domain_ops. The host_data
+ * for the IRQ domain must be the &struct gpio_chip.
+ */
+int gpiochip_irq_domain_activate(struct irq_domain *domain,
+ struct irq_data *data, bool reserve)
+{
+ struct gpio_chip *chip = domain->host_data;
+
+ return gpiochip_lock_as_irq(chip, data->hwirq);
+}
+EXPORT_SYMBOL_GPL(gpiochip_irq_domain_activate);
+
+/**
+ * gpiochip_irq_domain_deactivate() - Unlock a GPIO used as an IRQ
+ * @domain: The IRQ domain used by this IRQ chip
+ * @data: Outermost irq_data associated with the IRQ
+ *
+ * This function is a wrapper that will call gpiochip_unlock_as_irq() and is to
+ * be used as the deactivate function for the &struct irq_domain_ops. The
+ * host_data for the IRQ domain must be the &struct gpio_chip.
+ */
+void gpiochip_irq_domain_deactivate(struct irq_domain *domain,
+ struct irq_data *data)
+{
+ struct gpio_chip *chip = domain->host_data;
+
+ return gpiochip_unlock_as_irq(chip, data->hwirq);
+}
+EXPORT_SYMBOL_GPL(gpiochip_irq_domain_deactivate);
+
static int gpiochip_to_irq(struct gpio_chip *chip, unsigned offset)
{
if (!gpiochip_irqchip_irq_valid(chip, offset))
@@ -2525,6 +2562,14 @@ EXPORT_SYMBOL_GPL(gpiochip_free_own_desc);
* rely on gpio_request() having been called beforehand.
*/
+static int gpio_set_config(struct gpio_chip *gc, unsigned offset,
+ enum pin_config_param mode)
+{
+ unsigned long config = { PIN_CONF_PACKED(mode, 0) };
+
+ return gc->set_config ? gc->set_config(gc, offset, config) : -ENOTSUPP;
+}
+
/**
* gpiod_direction_input - set the GPIO direction to input
* @desc: GPIO to set to input
@@ -2572,20 +2617,19 @@ int gpiod_direction_input(struct gpio_desc *desc)
if (status == 0)
clear_bit(FLAG_IS_OUT, &desc->flags);
+ if (test_bit(FLAG_PULL_UP, &desc->flags))
+ gpio_set_config(chip, gpio_chip_hwgpio(desc),
+ PIN_CONFIG_BIAS_PULL_UP);
+ else if (test_bit(FLAG_PULL_DOWN, &desc->flags))
+ gpio_set_config(chip, gpio_chip_hwgpio(desc),
+ PIN_CONFIG_BIAS_PULL_DOWN);
+
trace_gpio_direction(desc_to_gpio(desc), 1, status);
return status;
}
EXPORT_SYMBOL_GPL(gpiod_direction_input);
-static int gpio_set_drive_single_ended(struct gpio_chip *gc, unsigned offset,
- enum pin_config_param mode)
-{
- unsigned long config = { PIN_CONF_PACKED(mode, 0) };
-
- return gc->set_config ? gc->set_config(gc, offset, config) : -ENOTSUPP;
-}
-
static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value)
{
struct gpio_chip *gc = desc->gdev->chip;
@@ -2682,8 +2726,8 @@ int gpiod_direction_output(struct gpio_desc *desc, int value)
gc = desc->gdev->chip;
if (test_bit(FLAG_OPEN_DRAIN, &desc->flags)) {
/* First see if we can enable open drain in hardware */
- ret = gpio_set_drive_single_ended(gc, gpio_chip_hwgpio(desc),
- PIN_CONFIG_DRIVE_OPEN_DRAIN);
+ ret = gpio_set_config(gc, gpio_chip_hwgpio(desc),
+ PIN_CONFIG_DRIVE_OPEN_DRAIN);
if (!ret)
goto set_output_value;
/* Emulate open drain by not actively driving the line high */
@@ -2691,16 +2735,16 @@ int gpiod_direction_output(struct gpio_desc *desc, int value)
return gpiod_direction_input(desc);
}
else if (test_bit(FLAG_OPEN_SOURCE, &desc->flags)) {
- ret = gpio_set_drive_single_ended(gc, gpio_chip_hwgpio(desc),
- PIN_CONFIG_DRIVE_OPEN_SOURCE);
+ ret = gpio_set_config(gc, gpio_chip_hwgpio(desc),
+ PIN_CONFIG_DRIVE_OPEN_SOURCE);
if (!ret)
goto set_output_value;
/* Emulate open source by not actively driving the line low */
if (!value)
return gpiod_direction_input(desc);
} else {
- gpio_set_drive_single_ended(gc, gpio_chip_hwgpio(desc),
- PIN_CONFIG_DRIVE_PUSH_PULL);
+ gpio_set_config(gc, gpio_chip_hwgpio(desc),
+ PIN_CONFIG_DRIVE_PUSH_PULL);
}
set_output_value:
@@ -4057,6 +4101,17 @@ int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id,
if (lflags & GPIO_OPEN_SOURCE)
set_bit(FLAG_OPEN_SOURCE, &desc->flags);
+ if ((lflags & GPIO_PULL_UP) && (lflags & GPIO_PULL_DOWN)) {
+ gpiod_err(desc,
+ "both pull-up and pull-down enabled, invalid configuration\n");
+ return -EINVAL;
+ }
+
+ if (lflags & GPIO_PULL_UP)
+ set_bit(FLAG_PULL_UP, &desc->flags);
+ else if (lflags & GPIO_PULL_DOWN)
+ set_bit(FLAG_PULL_DOWN, &desc->flags);
+
status = gpiod_set_transitory(desc, (lflags & GPIO_TRANSITORY));
if (status < 0)
return status;
diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h
index bc57f0dc5953..078ab17b96bf 100644
--- a/drivers/gpio/gpiolib.h
+++ b/drivers/gpio/gpiolib.h
@@ -219,6 +219,8 @@ struct gpio_desc {
#define FLAG_IRQ_IS_ENABLED 10 /* GPIO is connected to an enabled IRQ */
#define FLAG_IS_HOGGED 11 /* GPIO is hogged */
#define FLAG_TRANSITORY 12 /* GPIO may lose value in sleep or reset */
+#define FLAG_PULL_UP 13 /* GPIO has pull up enabled */
+#define FLAG_PULL_DOWN 14 /* GPIO has pull down enabled */
/* Connection label */
const char *label;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
index ead851413c0a..16fcb56c232b 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
@@ -700,6 +700,8 @@ int amdgpu_vm_validate_pt_bos(struct amdgpu_device *adev, struct amdgpu_vm *vm,
struct amdgpu_vm_bo_base *bo_base, *tmp;
int r = 0;
+ vm->bulk_moveable &= list_empty(&vm->evicted);
+
list_for_each_entry_safe(bo_base, tmp, &vm->evicted, vm_status) {
struct amdgpu_bo *bo = bo_base->bo;
@@ -947,10 +949,6 @@ int amdgpu_vm_alloc_pts(struct amdgpu_device *adev,
if (r)
return r;
- r = amdgpu_vm_clear_bo(adev, vm, pt, cursor.level, ats);
- if (r)
- goto error_free_pt;
-
if (vm->use_cpu_for_update) {
r = amdgpu_bo_kmap(pt, NULL);
if (r)
@@ -963,6 +961,10 @@ int amdgpu_vm_alloc_pts(struct amdgpu_device *adev,
pt->parent = amdgpu_bo_ref(cursor.parent->base.bo);
amdgpu_vm_bo_base_init(&entry->base, vm, pt);
+
+ r = amdgpu_vm_clear_bo(adev, vm, pt, cursor.level, ats);
+ if (r)
+ goto error_free_pt;
}
return 0;
@@ -3033,13 +3035,14 @@ int amdgpu_vm_init(struct amdgpu_device *adev, struct amdgpu_vm *vm,
if (r)
goto error_unreserve;
+ amdgpu_vm_bo_base_init(&vm->root.base, vm, root);
+
r = amdgpu_vm_clear_bo(adev, vm, root,
adev->vm_manager.root_level,
vm->pte_support_ats);
if (r)
goto error_unreserve;
- amdgpu_vm_bo_base_init(&vm->root.base, vm, root);
amdgpu_bo_unreserve(vm->root.base.bo);
if (pasid) {
diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c
index 5533f6e4f4a4..d0309e8c9d12 100644
--- a/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c
@@ -220,6 +220,7 @@ static const struct soc15_reg_golden golden_settings_gc_9_1_rv2[] =
static const struct soc15_reg_golden golden_settings_gc_9_x_common[] =
{
+ SOC15_REG_GOLDEN_VALUE(GC, 0, mmCP_SD_CNTL, 0xffffffff, 0x000001ff),
SOC15_REG_GOLDEN_VALUE(GC, 0, mmGRBM_CAM_INDEX, 0xffffffff, 0x00000000),
SOC15_REG_GOLDEN_VALUE(GC, 0, mmGRBM_CAM_DATA, 0xffffffff, 0x2544c382)
};
diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c
index 600259b4e291..2fe8397241ea 100644
--- a/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c
@@ -742,7 +742,7 @@ static int gmc_v9_0_allocate_vm_inv_eng(struct amdgpu_device *adev)
}
ring->vm_inv_eng = inv_eng - 1;
- change_bit(inv_eng - 1, (unsigned long *)(&vm_inv_engs[vmhub]));
+ vm_inv_engs[vmhub] &= ~(1 << ring->vm_inv_eng);
dev_info(adev->dev, "ring %s uses VM inv eng %u on hub %u\n",
ring->name, ring->vm_inv_eng, ring->funcs->vmhub);
diff --git a/drivers/gpu/drm/amd/amdgpu/psp_v3_1.c b/drivers/gpu/drm/amd/amdgpu/psp_v3_1.c
index c63de945c021..0487e3a4e9e7 100644
--- a/drivers/gpu/drm/amd/amdgpu/psp_v3_1.c
+++ b/drivers/gpu/drm/amd/amdgpu/psp_v3_1.c
@@ -500,9 +500,7 @@ static bool psp_v3_1_smu_reload_quirk(struct psp_context *psp)
struct amdgpu_device *adev = psp->adev;
uint32_t reg;
- reg = smnMP1_FIRMWARE_FLAGS | 0x03b00000;
- WREG32_SOC15(NBIO, 0, mmPCIE_INDEX2, reg);
- reg = RREG32_SOC15(NBIO, 0, mmPCIE_DATA2);
+ reg = RREG32_PCIE(smnMP1_FIRMWARE_FLAGS | 0x03b00000);
return (reg & MP1_FIRMWARE_FLAGS__INTERRUPTS_ENABLED_MASK) ? true : false;
}
diff --git a/drivers/gpu/drm/amd/amdgpu/soc15.c b/drivers/gpu/drm/amd/amdgpu/soc15.c
index 99ebcf29dcb0..ed89a101f73f 100644
--- a/drivers/gpu/drm/amd/amdgpu/soc15.c
+++ b/drivers/gpu/drm/amd/amdgpu/soc15.c
@@ -461,7 +461,6 @@ static int soc15_asic_reset(struct amdgpu_device *adev)
switch (adev->asic_type) {
case CHIP_VEGA10:
- case CHIP_VEGA20:
soc15_asic_get_baco_capability(adev, &baco_reset);
break;
default:
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_cik.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_cik.c
index 47243165a082..ae90a99909ef 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_cik.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_cik.c
@@ -323,57 +323,7 @@ static int init_mqd_hiq(struct mqd_manager *mm, void **mqd,
struct kfd_mem_obj **mqd_mem_obj, uint64_t *gart_addr,
struct queue_properties *q)
{
- uint64_t addr;
- struct cik_mqd *m;
- int retval;
-
- retval = kfd_gtt_sa_allocate(mm->dev, sizeof(struct cik_mqd),
- mqd_mem_obj);
-
- if (retval != 0)
- return -ENOMEM;
-
- m = (struct cik_mqd *) (*mqd_mem_obj)->cpu_ptr;
- addr = (*mqd_mem_obj)->gpu_addr;
-
- memset(m, 0, ALIGN(sizeof(struct cik_mqd), 256));
-
- m->header = 0xC0310800;
- m->compute_pipelinestat_enable = 1;
- m->compute_static_thread_mgmt_se0 = 0xFFFFFFFF;
- m->compute_static_thread_mgmt_se1 = 0xFFFFFFFF;
- m->compute_static_thread_mgmt_se2 = 0xFFFFFFFF;
- m->compute_static_thread_mgmt_se3 = 0xFFFFFFFF;
-
- m->cp_hqd_persistent_state = DEFAULT_CP_HQD_PERSISTENT_STATE |
- PRELOAD_REQ;
- m->cp_hqd_quantum = QUANTUM_EN | QUANTUM_SCALE_1MS |
- QUANTUM_DURATION(10);
-
- m->cp_mqd_control = MQD_CONTROL_PRIV_STATE_EN;
- m->cp_mqd_base_addr_lo = lower_32_bits(addr);
- m->cp_mqd_base_addr_hi = upper_32_bits(addr);
-
- m->cp_hqd_ib_control = DEFAULT_MIN_IB_AVAIL_SIZE;
-
- /*
- * Pipe Priority
- * Identifies the pipe relative priority when this queue is connected
- * to the pipeline. The pipe priority is against the GFX pipe and HP3D.
- * In KFD we are using a fixed pipe priority set to CS_MEDIUM.
- * 0 = CS_LOW (typically below GFX)
- * 1 = CS_MEDIUM (typically between HP3D and GFX
- * 2 = CS_HIGH (typically above HP3D)
- */
- m->cp_hqd_pipe_priority = 1;
- m->cp_hqd_queue_priority = 15;
-
- *mqd = m;
- if (gart_addr)
- *gart_addr = addr;
- retval = mm->update_mqd(mm, m, q);
-
- return retval;
+ return init_mqd(mm, mqd, mqd_mem_obj, gart_addr, q);
}
static int update_mqd_hiq(struct mqd_manager *mm, void *mqd,
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 2f26581b93ff..81127f7d6ed1 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -886,6 +886,7 @@ static void emulated_link_detect(struct dc_link *link)
return;
}
+ /* dc_sink_create returns a new reference */
link->local_sink = sink;
edid_status = dm_helpers_read_local_edid(
@@ -952,6 +953,8 @@ static int dm_resume(void *handle)
if (aconnector->fake_enable && aconnector->dc_link->local_sink)
aconnector->fake_enable = false;
+ if (aconnector->dc_sink)
+ dc_sink_release(aconnector->dc_sink);
aconnector->dc_sink = NULL;
amdgpu_dm_update_connector_after_detect(aconnector);
mutex_unlock(&aconnector->hpd_lock);
@@ -1061,6 +1064,8 @@ amdgpu_dm_update_connector_after_detect(struct amdgpu_dm_connector *aconnector)
sink = aconnector->dc_link->local_sink;
+ if (sink)
+ dc_sink_retain(sink);
/*
* Edid mgmt connector gets first update only in mode_valid hook and then
@@ -1085,21 +1090,24 @@ amdgpu_dm_update_connector_after_detect(struct amdgpu_dm_connector *aconnector)
* to it anymore after disconnect, so on next crtc to connector
* reshuffle by UMD we will get into unwanted dc_sink release
*/
- if (aconnector->dc_sink != aconnector->dc_em_sink)
- dc_sink_release(aconnector->dc_sink);
+ dc_sink_release(aconnector->dc_sink);
}
aconnector->dc_sink = sink;
+ dc_sink_retain(aconnector->dc_sink);
amdgpu_dm_update_freesync_caps(connector,
aconnector->edid);
} else {
amdgpu_dm_update_freesync_caps(connector, NULL);
- if (!aconnector->dc_sink)
+ if (!aconnector->dc_sink) {
aconnector->dc_sink = aconnector->dc_em_sink;
- else if (aconnector->dc_sink != aconnector->dc_em_sink)
dc_sink_retain(aconnector->dc_sink);
+ }
}
mutex_unlock(&dev->mode_config.mutex);
+
+ if (sink)
+ dc_sink_release(sink);
return;
}
@@ -1107,8 +1115,10 @@ amdgpu_dm_update_connector_after_detect(struct amdgpu_dm_connector *aconnector)
* TODO: temporary guard to look for proper fix
* if this sink is MST sink, we should not do anything
*/
- if (sink && sink->sink_signal == SIGNAL_TYPE_DISPLAY_PORT_MST)
+ if (sink && sink->sink_signal == SIGNAL_TYPE_DISPLAY_PORT_MST) {
+ dc_sink_release(sink);
return;
+ }
if (aconnector->dc_sink == sink) {
/*
@@ -1117,6 +1127,8 @@ amdgpu_dm_update_connector_after_detect(struct amdgpu_dm_connector *aconnector)
*/
DRM_DEBUG_DRIVER("DCHPD: connector_id=%d: dc_sink didn't change.\n",
aconnector->connector_id);
+ if (sink)
+ dc_sink_release(sink);
return;
}
@@ -1138,6 +1150,7 @@ amdgpu_dm_update_connector_after_detect(struct amdgpu_dm_connector *aconnector)
amdgpu_dm_update_freesync_caps(connector, NULL);
aconnector->dc_sink = sink;
+ dc_sink_retain(aconnector->dc_sink);
if (sink->dc_edid.length == 0) {
aconnector->edid = NULL;
drm_dp_cec_unset_edid(&aconnector->dm_dp_aux.aux);
@@ -1158,11 +1171,15 @@ amdgpu_dm_update_connector_after_detect(struct amdgpu_dm_connector *aconnector)
amdgpu_dm_update_freesync_caps(connector, NULL);
drm_connector_update_edid_property(connector, NULL);
aconnector->num_modes = 0;
+ dc_sink_release(aconnector->dc_sink);
aconnector->dc_sink = NULL;
aconnector->edid = NULL;
}
mutex_unlock(&dev->mode_config.mutex);
+
+ if (sink)
+ dc_sink_release(sink);
}
static void handle_hpd_irq(void *param)
@@ -2977,6 +2994,7 @@ create_stream_for_sink(struct amdgpu_dm_connector *aconnector,
return stream;
} else {
sink = aconnector->dc_sink;
+ dc_sink_retain(sink);
}
stream = dc_create_stream_for_sink(sink);
@@ -3042,8 +3060,7 @@ create_stream_for_sink(struct amdgpu_dm_connector *aconnector,
update_stream_signal(stream, sink);
finish:
- if (sink && sink->sink_signal == SIGNAL_TYPE_VIRTUAL && aconnector->base.force != DRM_FORCE_ON)
- dc_sink_release(sink);
+ dc_sink_release(sink);
return stream;
}
@@ -3301,6 +3318,14 @@ static void amdgpu_dm_connector_destroy(struct drm_connector *connector)
dm->backlight_dev = NULL;
}
#endif
+
+ if (aconnector->dc_em_sink)
+ dc_sink_release(aconnector->dc_em_sink);
+ aconnector->dc_em_sink = NULL;
+ if (aconnector->dc_sink)
+ dc_sink_release(aconnector->dc_sink);
+ aconnector->dc_sink = NULL;
+
drm_dp_cec_unregister_connector(&aconnector->dm_dp_aux.aux);
drm_connector_unregister(connector);
drm_connector_cleanup(connector);
@@ -3398,10 +3423,12 @@ static void create_eml_sink(struct amdgpu_dm_connector *aconnector)
(edid->extensions + 1) * EDID_LENGTH,
&init_params);
- if (aconnector->base.force == DRM_FORCE_ON)
+ if (aconnector->base.force == DRM_FORCE_ON) {
aconnector->dc_sink = aconnector->dc_link->local_sink ?
aconnector->dc_link->local_sink :
aconnector->dc_em_sink;
+ dc_sink_retain(aconnector->dc_sink);
+ }
}
static void handle_edid_mgmt(struct amdgpu_dm_connector *aconnector)
@@ -5402,9 +5429,11 @@ static void get_freesync_config_for_crtc(
struct amdgpu_dm_connector *aconnector =
to_amdgpu_dm_connector(new_con_state->base.connector);
struct drm_display_mode *mode = &new_crtc_state->base.mode;
+ int vrefresh = drm_mode_vrefresh(mode);
new_crtc_state->vrr_supported = new_con_state->freesync_capable &&
- aconnector->min_vfreq <= drm_mode_vrefresh(mode);
+ vrefresh >= aconnector->min_vfreq &&
+ vrefresh <= aconnector->max_vfreq;
if (new_crtc_state->vrr_supported) {
new_crtc_state->stream->ignore_msa_timing_param = true;
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
index f51d52eb52e6..c4ea3a91f17a 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
@@ -191,6 +191,7 @@ static int dm_dp_mst_get_modes(struct drm_connector *connector)
&init_params);
dc_sink->priv = aconnector;
+ /* dc_link_add_remote_sink returns a new reference */
aconnector->dc_sink = dc_sink;
if (aconnector->dc_sink)
diff --git a/drivers/gpu/drm/amd/display/dc/calcs/dcn_calcs.c b/drivers/gpu/drm/amd/display/dc/calcs/dcn_calcs.c
index 12d1842079ae..eb62d10bb65c 100644
--- a/drivers/gpu/drm/amd/display/dc/calcs/dcn_calcs.c
+++ b/drivers/gpu/drm/amd/display/dc/calcs/dcn_calcs.c
@@ -1348,12 +1348,12 @@ void dcn_bw_update_from_pplib(struct dc *dc)
struct dm_pp_clock_levels_with_voltage fclks = {0}, dcfclks = {0};
bool res;
- kernel_fpu_begin();
-
/* TODO: This is not the proper way to obtain fabric_and_dram_bandwidth, should be min(fclk, memclk) */
res = dm_pp_get_clock_levels_by_type_with_voltage(
ctx, DM_PP_CLOCK_TYPE_FCLK, &fclks);
+ kernel_fpu_begin();
+
if (res)
res = verify_clock_values(&fclks);
@@ -1372,9 +1372,13 @@ void dcn_bw_update_from_pplib(struct dc *dc)
} else
BREAK_TO_DEBUGGER();
+ kernel_fpu_end();
+
res = dm_pp_get_clock_levels_by_type_with_voltage(
ctx, DM_PP_CLOCK_TYPE_DCFCLK, &dcfclks);
+ kernel_fpu_begin();
+
if (res)
res = verify_clock_values(&dcfclks);
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_link.c b/drivers/gpu/drm/amd/display/dc/core/dc_link.c
index 7f5a947ad31d..4eba3c4800b6 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc_link.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc_link.c
@@ -794,6 +794,7 @@ bool dc_link_detect(struct dc_link *link, enum dc_detect_reason reason)
sink->link->dongle_max_pix_clk = sink_caps.max_hdmi_pixel_clock;
sink->converter_disable_audio = converter_disable_audio;
+ /* dc_sink_create returns a new reference */
link->local_sink = sink;
edid_status = dm_helpers_read_local_edid(
@@ -2037,6 +2038,9 @@ static enum dc_status enable_link(
break;
}
+ if (status == DC_OK)
+ pipe_ctx->stream->link->link_status.link_active = true;
+
return status;
}
@@ -2060,6 +2064,14 @@ static void disable_link(struct dc_link *link, enum signal_type signal)
dp_disable_link_phy_mst(link, signal);
} else
link->link_enc->funcs->disable_output(link->link_enc, signal);
+
+ if (signal == SIGNAL_TYPE_DISPLAY_PORT_MST) {
+ /* MST disable link only when no stream use the link */
+ if (link->mst_stream_alloc_table.stream_count <= 0)
+ link->link_status.link_active = false;
+ } else {
+ link->link_status.link_active = false;
+ }
}
static bool dp_active_dongle_validate_timing(
@@ -2623,8 +2635,6 @@ void core_link_enable_stream(
}
}
- stream->link->link_status.link_active = true;
-
core_dc->hwss.enable_audio_stream(pipe_ctx);
/* turn off otg test pattern if enable */
@@ -2659,8 +2669,6 @@ void core_link_disable_stream(struct pipe_ctx *pipe_ctx, int option)
core_dc->hwss.disable_stream(pipe_ctx, option);
disable_link(pipe_ctx->stream->link, pipe_ctx->stream->signal);
-
- pipe_ctx->stream->link->link_status.link_active = false;
}
void core_link_set_avmute(struct pipe_ctx *pipe_ctx, bool enable)
diff --git a/drivers/gpu/drm/amd/display/modules/freesync/freesync.c b/drivers/gpu/drm/amd/display/modules/freesync/freesync.c
index 94a84bc57c7a..bfd27f10879e 100644
--- a/drivers/gpu/drm/amd/display/modules/freesync/freesync.c
+++ b/drivers/gpu/drm/amd/display/modules/freesync/freesync.c
@@ -724,7 +724,7 @@ static void build_vrr_infopacket_v1(enum signal_type signal,
static void build_vrr_infopacket_v2(enum signal_type signal,
const struct mod_vrr_params *vrr,
- const enum color_transfer_func *app_tf,
+ enum color_transfer_func app_tf,
struct dc_info_packet *infopacket)
{
unsigned int payload_size = 0;
@@ -732,8 +732,7 @@ static void build_vrr_infopacket_v2(enum signal_type signal,
build_vrr_infopacket_header_v2(signal, infopacket, &payload_size);
build_vrr_infopacket_data(vrr, infopacket);
- if (app_tf != NULL)
- build_vrr_infopacket_fs2_data(*app_tf, infopacket);
+ build_vrr_infopacket_fs2_data(app_tf, infopacket);
build_vrr_infopacket_checksum(&payload_size, infopacket);
@@ -757,7 +756,7 @@ void mod_freesync_build_vrr_infopacket(struct mod_freesync *mod_freesync,
const struct dc_stream_state *stream,
const struct mod_vrr_params *vrr,
enum vrr_packet_type packet_type,
- const enum color_transfer_func *app_tf,
+ enum color_transfer_func app_tf,
struct dc_info_packet *infopacket)
{
/* SPD info packet for FreeSync
diff --git a/drivers/gpu/drm/amd/display/modules/inc/mod_freesync.h b/drivers/gpu/drm/amd/display/modules/inc/mod_freesync.h
index 4222e403b151..dcef85994c45 100644
--- a/drivers/gpu/drm/amd/display/modules/inc/mod_freesync.h
+++ b/drivers/gpu/drm/amd/display/modules/inc/mod_freesync.h
@@ -145,7 +145,7 @@ void mod_freesync_build_vrr_infopacket(struct mod_freesync *mod_freesync,
const struct dc_stream_state *stream,
const struct mod_vrr_params *vrr,
enum vrr_packet_type packet_type,
- const enum color_transfer_func *app_tf,
+ enum color_transfer_func app_tf,
struct dc_info_packet *infopacket);
void mod_freesync_build_vrr_params(struct mod_freesync *mod_freesync,
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/pp_psm.c b/drivers/gpu/drm/amd/powerplay/hwmgr/pp_psm.c
index ce177d7f04cb..6bf48934fdc4 100644
--- a/drivers/gpu/drm/amd/powerplay/hwmgr/pp_psm.c
+++ b/drivers/gpu/drm/amd/powerplay/hwmgr/pp_psm.c
@@ -277,8 +277,7 @@ int psm_adjust_power_state_dynamic(struct pp_hwmgr *hwmgr, bool skip_display_set
if (!skip_display_settings)
phm_notify_smc_display_config_after_ps_adjustment(hwmgr);
- if ((hwmgr->request_dpm_level != hwmgr->dpm_level) &&
- !phm_force_dpm_levels(hwmgr, hwmgr->request_dpm_level))
+ if (!phm_force_dpm_levels(hwmgr, hwmgr->request_dpm_level))
hwmgr->dpm_level = hwmgr->request_dpm_level;
if (hwmgr->dpm_level != AMD_DPM_FORCED_LEVEL_MANUAL) {
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/ppatomfwctrl.c b/drivers/gpu/drm/amd/powerplay/hwmgr/ppatomfwctrl.c
index 4588bddf8b33..615cf2c09e54 100644
--- a/drivers/gpu/drm/amd/powerplay/hwmgr/ppatomfwctrl.c
+++ b/drivers/gpu/drm/amd/powerplay/hwmgr/ppatomfwctrl.c
@@ -489,15 +489,16 @@ int pp_atomfwctrl_get_gpio_information(struct pp_hwmgr *hwmgr,
}
int pp_atomfwctrl_get_clk_information_by_clkid(struct pp_hwmgr *hwmgr,
- uint8_t id, uint32_t *frequency)
+ uint8_t clk_id, uint8_t syspll_id,
+ uint32_t *frequency)
{
struct amdgpu_device *adev = hwmgr->adev;
struct atom_get_smu_clock_info_parameters_v3_1 parameters;
struct atom_get_smu_clock_info_output_parameters_v3_1 *output;
uint32_t ix;
- parameters.clk_id = id;
- parameters.syspll_id = 0;
+ parameters.clk_id = clk_id;
+ parameters.syspll_id = syspll_id;
parameters.command = GET_SMU_CLOCK_INFO_V3_1_GET_CLOCK_FREQ;
parameters.dfsdid = 0;
@@ -530,20 +531,23 @@ static void pp_atomfwctrl_copy_vbios_bootup_values_3_2(struct pp_hwmgr *hwmgr,
boot_values->ulSocClk = 0;
boot_values->ulDCEFClk = 0;
- if (!pp_atomfwctrl_get_clk_information_by_clkid(hwmgr, SMU11_SYSPLL0_SOCCLK_ID, &frequency))
+ if (!pp_atomfwctrl_get_clk_information_by_clkid(hwmgr, SMU11_SYSPLL0_SOCCLK_ID, SMU11_SYSPLL0_ID, &frequency))
boot_values->ulSocClk = frequency;
- if (!pp_atomfwctrl_get_clk_information_by_clkid(hwmgr, SMU11_SYSPLL0_DCEFCLK_ID, &frequency))
+ if (!pp_atomfwctrl_get_clk_information_by_clkid(hwmgr, SMU11_SYSPLL0_DCEFCLK_ID, SMU11_SYSPLL0_ID, &frequency))
boot_values->ulDCEFClk = frequency;
- if (!pp_atomfwctrl_get_clk_information_by_clkid(hwmgr, SMU11_SYSPLL0_ECLK_ID, &frequency))
+ if (!pp_atomfwctrl_get_clk_information_by_clkid(hwmgr, SMU11_SYSPLL0_ECLK_ID, SMU11_SYSPLL0_ID, &frequency))
boot_values->ulEClk = frequency;
- if (!pp_atomfwctrl_get_clk_information_by_clkid(hwmgr, SMU11_SYSPLL0_VCLK_ID, &frequency))
+ if (!pp_atomfwctrl_get_clk_information_by_clkid(hwmgr, SMU11_SYSPLL0_VCLK_ID, SMU11_SYSPLL0_ID, &frequency))
boot_values->ulVClk = frequency;
- if (!pp_atomfwctrl_get_clk_information_by_clkid(hwmgr, SMU11_SYSPLL0_DCLK_ID, &frequency))
+ if (!pp_atomfwctrl_get_clk_information_by_clkid(hwmgr, SMU11_SYSPLL0_DCLK_ID, SMU11_SYSPLL0_ID, &frequency))
boot_values->ulDClk = frequency;
+
+ if (!pp_atomfwctrl_get_clk_information_by_clkid(hwmgr, SMU11_SYSPLL1_0_FCLK_ID, SMU11_SYSPLL1_2_ID, &frequency))
+ boot_values->ulFClk = frequency;
}
static void pp_atomfwctrl_copy_vbios_bootup_values_3_1(struct pp_hwmgr *hwmgr,
@@ -563,19 +567,19 @@ static void pp_atomfwctrl_copy_vbios_bootup_values_3_1(struct pp_hwmgr *hwmgr,
boot_values->ulSocClk = 0;
boot_values->ulDCEFClk = 0;
- if (!pp_atomfwctrl_get_clk_information_by_clkid(hwmgr, SMU9_SYSPLL0_SOCCLK_ID, &frequency))
+ if (!pp_atomfwctrl_get_clk_information_by_clkid(hwmgr, SMU9_SYSPLL0_SOCCLK_ID, 0, &frequency))
boot_values->ulSocClk = frequency;
- if (!pp_atomfwctrl_get_clk_information_by_clkid(hwmgr, SMU9_SYSPLL0_DCEFCLK_ID, &frequency))
+ if (!pp_atomfwctrl_get_clk_information_by_clkid(hwmgr, SMU9_SYSPLL0_DCEFCLK_ID, 0, &frequency))
boot_values->ulDCEFClk = frequency;
- if (!pp_atomfwctrl_get_clk_information_by_clkid(hwmgr, SMU9_SYSPLL0_ECLK_ID, &frequency))
+ if (!pp_atomfwctrl_get_clk_information_by_clkid(hwmgr, SMU9_SYSPLL0_ECLK_ID, 0, &frequency))
boot_values->ulEClk = frequency;
- if (!pp_atomfwctrl_get_clk_information_by_clkid(hwmgr, SMU9_SYSPLL0_VCLK_ID, &frequency))
+ if (!pp_atomfwctrl_get_clk_information_by_clkid(hwmgr, SMU9_SYSPLL0_VCLK_ID, 0, &frequency))
boot_values->ulVClk = frequency;
- if (!pp_atomfwctrl_get_clk_information_by_clkid(hwmgr, SMU9_SYSPLL0_DCLK_ID, &frequency))
+ if (!pp_atomfwctrl_get_clk_information_by_clkid(hwmgr, SMU9_SYSPLL0_DCLK_ID, 0, &frequency))
boot_values->ulDClk = frequency;
}
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/ppatomfwctrl.h b/drivers/gpu/drm/amd/powerplay/hwmgr/ppatomfwctrl.h
index fe9e8ceef50e..b7e2651b570b 100644
--- a/drivers/gpu/drm/amd/powerplay/hwmgr/ppatomfwctrl.h
+++ b/drivers/gpu/drm/amd/powerplay/hwmgr/ppatomfwctrl.h
@@ -139,6 +139,7 @@ struct pp_atomfwctrl_bios_boot_up_values {
uint32_t ulEClk;
uint32_t ulVClk;
uint32_t ulDClk;
+ uint32_t ulFClk;
uint16_t usVddc;
uint16_t usVddci;
uint16_t usMvddc;
@@ -236,7 +237,8 @@ int pp_atomfwctrl_get_vbios_bootup_values(struct pp_hwmgr *hwmgr,
int pp_atomfwctrl_get_smc_dpm_information(struct pp_hwmgr *hwmgr,
struct pp_atomfwctrl_smc_dpm_parameters *param);
int pp_atomfwctrl_get_clk_information_by_clkid(struct pp_hwmgr *hwmgr,
- uint8_t id, uint32_t *frequency);
+ uint8_t clk_id, uint8_t syspll_id,
+ uint32_t *frequency);
#endif
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.c b/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.c
index 48187acac59e..83d3d935f3ac 100644
--- a/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.c
+++ b/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.c
@@ -3491,14 +3491,14 @@ static int smu7_get_gpu_power(struct pp_hwmgr *hwmgr, u32 *query)
smum_send_msg_to_smc(hwmgr, PPSMC_MSG_PmStatusLogStart);
cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
- ixSMU_PM_STATUS_94, 0);
+ ixSMU_PM_STATUS_95, 0);
for (i = 0; i < 10; i++) {
- mdelay(1);
+ mdelay(500);
smum_send_msg_to_smc(hwmgr, PPSMC_MSG_PmStatusLogSample);
tmp = cgs_read_ind_register(hwmgr->device,
CGS_IND_REG__SMC,
- ixSMU_PM_STATUS_94);
+ ixSMU_PM_STATUS_95);
if (tmp != 0)
break;
}
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/vega10_hwmgr.c b/drivers/gpu/drm/amd/powerplay/hwmgr/vega10_hwmgr.c
index 5479125ff4f6..5c4f701939ea 100644
--- a/drivers/gpu/drm/amd/powerplay/hwmgr/vega10_hwmgr.c
+++ b/drivers/gpu/drm/amd/powerplay/hwmgr/vega10_hwmgr.c
@@ -2575,10 +2575,10 @@ static int vega10_init_smc_table(struct pp_hwmgr *hwmgr)
data->vbios_boot_state.gfx_clock = boot_up_values.ulGfxClk;
data->vbios_boot_state.mem_clock = boot_up_values.ulUClk;
pp_atomfwctrl_get_clk_information_by_clkid(hwmgr,
- SMU9_SYSPLL0_SOCCLK_ID, &boot_up_values.ulSocClk);
+ SMU9_SYSPLL0_SOCCLK_ID, 0, &boot_up_values.ulSocClk);
pp_atomfwctrl_get_clk_information_by_clkid(hwmgr,
- SMU9_SYSPLL0_DCEFCLK_ID, &boot_up_values.ulDCEFClk);
+ SMU9_SYSPLL0_DCEFCLK_ID, 0, &boot_up_values.ulDCEFClk);
data->vbios_boot_state.soc_clock = boot_up_values.ulSocClk;
data->vbios_boot_state.dcef_clock = boot_up_values.ulDCEFClk;
@@ -4407,9 +4407,9 @@ static int vega10_set_ppfeature_status(struct pp_hwmgr *hwmgr, uint64_t new_ppfe
return ret;
features_to_disable =
- (features_enabled ^ new_ppfeature_masks) & features_enabled;
+ features_enabled & ~new_ppfeature_masks;
features_to_enable =
- (features_enabled ^ new_ppfeature_masks) ^ features_to_disable;
+ ~features_enabled & new_ppfeature_masks;
pr_debug("features_to_disable 0x%llx\n", features_to_disable);
pr_debug("features_to_enable 0x%llx\n", features_to_enable);
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/vega12_hwmgr.c b/drivers/gpu/drm/amd/powerplay/hwmgr/vega12_hwmgr.c
index 6c8e78611c03..bdb48e94eff6 100644
--- a/drivers/gpu/drm/amd/powerplay/hwmgr/vega12_hwmgr.c
+++ b/drivers/gpu/drm/amd/powerplay/hwmgr/vega12_hwmgr.c
@@ -2009,9 +2009,9 @@ static int vega12_set_ppfeature_status(struct pp_hwmgr *hwmgr, uint64_t new_ppfe
return ret;
features_to_disable =
- (features_enabled ^ new_ppfeature_masks) & features_enabled;
+ features_enabled & ~new_ppfeature_masks;
features_to_enable =
- (features_enabled ^ new_ppfeature_masks) ^ features_to_disable;
+ ~features_enabled & new_ppfeature_masks;
pr_debug("features_to_disable 0x%llx\n", features_to_disable);
pr_debug("features_to_enable 0x%llx\n", features_to_enable);
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_hwmgr.c b/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_hwmgr.c
index aad79affb081..9aa7bec1b5fe 100644
--- a/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_hwmgr.c
+++ b/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_hwmgr.c
@@ -463,9 +463,9 @@ static int vega20_setup_asic_task(struct pp_hwmgr *hwmgr)
static void vega20_init_dpm_state(struct vega20_dpm_state *dpm_state)
{
dpm_state->soft_min_level = 0x0;
- dpm_state->soft_max_level = 0xffff;
+ dpm_state->soft_max_level = VG20_CLOCK_MAX_DEFAULT;
dpm_state->hard_min_level = 0x0;
- dpm_state->hard_max_level = 0xffff;
+ dpm_state->hard_max_level = VG20_CLOCK_MAX_DEFAULT;
}
static int vega20_get_number_of_dpm_level(struct pp_hwmgr *hwmgr,
@@ -711,8 +711,10 @@ static int vega20_setup_default_dpm_tables(struct pp_hwmgr *hwmgr)
PP_ASSERT_WITH_CODE(!ret,
"[SetupDefaultDpmTable] failed to get fclk dpm levels!",
return ret);
- } else
- dpm_table->count = 0;
+ } else {
+ dpm_table->count = 1;
+ dpm_table->dpm_levels[0].value = data->vbios_boot_state.fclock / 100;
+ }
vega20_init_dpm_state(&(dpm_table->dpm_state));
/* save a copy of the default DPM table */
@@ -754,6 +756,7 @@ static int vega20_init_smc_table(struct pp_hwmgr *hwmgr)
data->vbios_boot_state.eclock = boot_up_values.ulEClk;
data->vbios_boot_state.vclock = boot_up_values.ulVClk;
data->vbios_boot_state.dclock = boot_up_values.ulDClk;
+ data->vbios_boot_state.fclock = boot_up_values.ulFClk;
data->vbios_boot_state.uc_cooling_id = boot_up_values.ucCoolingID;
smum_send_msg_to_smc_with_parameter(hwmgr,
@@ -780,6 +783,8 @@ static int vega20_init_smc_table(struct pp_hwmgr *hwmgr)
static int vega20_override_pcie_parameters(struct pp_hwmgr *hwmgr)
{
struct amdgpu_device *adev = (struct amdgpu_device *)(hwmgr->adev);
+ struct vega20_hwmgr *data =
+ (struct vega20_hwmgr *)(hwmgr->backend);
uint32_t pcie_gen = 0, pcie_width = 0, smu_pcie_arg;
int ret;
@@ -816,6 +821,10 @@ static int vega20_override_pcie_parameters(struct pp_hwmgr *hwmgr)
"[OverridePcieParameters] Attempt to override pcie params failed!",
return ret);
+ data->pcie_parameters_override = 1;
+ data->pcie_gen_level1 = pcie_gen;
+ data->pcie_width_level1 = pcie_width;
+
return 0;
}
@@ -979,6 +988,8 @@ static int vega20_od8_set_feature_capabilities(
}
if (data->smu_features[GNLD_DPM_UCLK].enabled) {
+ pptable_information->od_settings_min[OD8_SETTING_UCLK_FMAX] =
+ data->dpm_table.mem_table.dpm_levels[data->dpm_table.mem_table.count - 2].value;
if (pptable_information->od_feature_capabilities[ATOM_VEGA20_ODFEATURE_UCLK_MAX] &&
pptable_information->od_settings_min[OD8_SETTING_UCLK_FMAX] > 0 &&
pptable_information->od_settings_max[OD8_SETTING_UCLK_FMAX] > 0 &&
@@ -2314,32 +2325,8 @@ static int vega20_force_dpm_lowest(struct pp_hwmgr *hwmgr)
static int vega20_unforce_dpm_levels(struct pp_hwmgr *hwmgr)
{
- struct vega20_hwmgr *data =
- (struct vega20_hwmgr *)(hwmgr->backend);
- uint32_t soft_min_level, soft_max_level;
int ret = 0;
- soft_min_level = vega20_find_lowest_dpm_level(&(data->dpm_table.gfx_table));
- soft_max_level = vega20_find_highest_dpm_level(&(data->dpm_table.gfx_table));
- data->dpm_table.gfx_table.dpm_state.soft_min_level =
- data->dpm_table.gfx_table.dpm_levels[soft_min_level].value;
- data->dpm_table.gfx_table.dpm_state.soft_max_level =
- data->dpm_table.gfx_table.dpm_levels[soft_max_level].value;
-
- soft_min_level = vega20_find_lowest_dpm_level(&(data->dpm_table.mem_table));
- soft_max_level = vega20_find_highest_dpm_level(&(data->dpm_table.mem_table));
- data->dpm_table.mem_table.dpm_state.soft_min_level =
- data->dpm_table.mem_table.dpm_levels[soft_min_level].value;
- data->dpm_table.mem_table.dpm_state.soft_max_level =
- data->dpm_table.mem_table.dpm_levels[soft_max_level].value;
-
- soft_min_level = vega20_find_lowest_dpm_level(&(data->dpm_table.soc_table));
- soft_max_level = vega20_find_highest_dpm_level(&(data->dpm_table.soc_table));
- data->dpm_table.soc_table.dpm_state.soft_min_level =
- data->dpm_table.soc_table.dpm_levels[soft_min_level].value;
- data->dpm_table.soc_table.dpm_state.soft_max_level =
- data->dpm_table.soc_table.dpm_levels[soft_max_level].value;
-
ret = vega20_upload_dpm_min_level(hwmgr, 0xFFFFFFFF);
PP_ASSERT_WITH_CODE(!ret,
"Failed to upload DPM Bootup Levels!",
@@ -2641,9 +2628,8 @@ static int vega20_get_sclks(struct pp_hwmgr *hwmgr,
struct vega20_single_dpm_table *dpm_table = &(data->dpm_table.gfx_table);
int i, count;
- PP_ASSERT_WITH_CODE(data->smu_features[GNLD_DPM_GFXCLK].enabled,
- "[GetSclks]: gfxclk dpm not enabled!\n",
- return -EPERM);
+ if (!data->smu_features[GNLD_DPM_GFXCLK].enabled)
+ return -1;
count = (dpm_table->count > MAX_NUM_CLOCKS) ? MAX_NUM_CLOCKS : dpm_table->count;
clocks->num_levels = count;
@@ -2670,9 +2656,8 @@ static int vega20_get_memclocks(struct pp_hwmgr *hwmgr,
struct vega20_single_dpm_table *dpm_table = &(data->dpm_table.mem_table);
int i, count;
- PP_ASSERT_WITH_CODE(data->smu_features[GNLD_DPM_UCLK].enabled,
- "[GetMclks]: uclk dpm not enabled!\n",
- return -EPERM);
+ if (!data->smu_features[GNLD_DPM_UCLK].enabled)
+ return -1;
count = (dpm_table->count > MAX_NUM_CLOCKS) ? MAX_NUM_CLOCKS : dpm_table->count;
clocks->num_levels = data->mclk_latency_table.count = count;
@@ -2696,9 +2681,8 @@ static int vega20_get_dcefclocks(struct pp_hwmgr *hwmgr,
struct vega20_single_dpm_table *dpm_table = &(data->dpm_table.dcef_table);
int i, count;
- PP_ASSERT_WITH_CODE(data->smu_features[GNLD_DPM_DCEFCLK].enabled,
- "[GetDcfclocks]: dcefclk dpm not enabled!\n",
- return -EPERM);
+ if (!data->smu_features[GNLD_DPM_DCEFCLK].enabled)
+ return -1;
count = (dpm_table->count > MAX_NUM_CLOCKS) ? MAX_NUM_CLOCKS : dpm_table->count;
clocks->num_levels = count;
@@ -2719,9 +2703,8 @@ static int vega20_get_socclocks(struct pp_hwmgr *hwmgr,
struct vega20_single_dpm_table *dpm_table = &(data->dpm_table.soc_table);
int i, count;
- PP_ASSERT_WITH_CODE(data->smu_features[GNLD_DPM_SOCCLK].enabled,
- "[GetSocclks]: socclk dpm not enabled!\n",
- return -EPERM);
+ if (!data->smu_features[GNLD_DPM_SOCCLK].enabled)
+ return -1;
count = (dpm_table->count > MAX_NUM_CLOCKS) ? MAX_NUM_CLOCKS : dpm_table->count;
clocks->num_levels = count;
@@ -2799,7 +2782,6 @@ static int vega20_odn_edit_dpm_table(struct pp_hwmgr *hwmgr,
data->od8_settings.od8_settings_array;
OverDriveTable_t *od_table =
&(data->smc_state_table.overdrive_table);
- struct pp_clock_levels_with_latency clocks;
int32_t input_index, input_clk, input_vol, i;
int od8_id;
int ret;
@@ -2858,11 +2840,6 @@ static int vega20_odn_edit_dpm_table(struct pp_hwmgr *hwmgr,
return -EOPNOTSUPP;
}
- ret = vega20_get_memclocks(hwmgr, &clocks);
- PP_ASSERT_WITH_CODE(!ret,
- "Attempt to get memory clk levels failed!",
- return ret);
-
for (i = 0; i < size; i += 2) {
if (i + 2 > size) {
pr_info("invalid number of input parameters %d\n",
@@ -2879,11 +2856,11 @@ static int vega20_odn_edit_dpm_table(struct pp_hwmgr *hwmgr,
return -EINVAL;
}
- if (input_clk < clocks.data[0].clocks_in_khz / 1000 ||
+ if (input_clk < od8_settings[OD8_SETTING_UCLK_FMAX].min_value ||
input_clk > od8_settings[OD8_SETTING_UCLK_FMAX].max_value) {
pr_info("clock freq %d is not within allowed range [%d - %d]\n",
input_clk,
- clocks.data[0].clocks_in_khz / 1000,
+ od8_settings[OD8_SETTING_UCLK_FMAX].min_value,
od8_settings[OD8_SETTING_UCLK_FMAX].max_value);
return -EINVAL;
}
@@ -3088,9 +3065,9 @@ static int vega20_set_ppfeature_status(struct pp_hwmgr *hwmgr, uint64_t new_ppfe
return ret;
features_to_disable =
- (features_enabled ^ new_ppfeature_masks) & features_enabled;
+ features_enabled & ~new_ppfeature_masks;
features_to_enable =
- (features_enabled ^ new_ppfeature_masks) ^ features_to_disable;
+ ~features_enabled & new_ppfeature_masks;
pr_debug("features_to_disable 0x%llx\n", features_to_disable);
pr_debug("features_to_enable 0x%llx\n", features_to_enable);
@@ -3128,7 +3105,7 @@ static int vega20_print_clock_levels(struct pp_hwmgr *hwmgr,
&(data->dpm_table.fclk_table);
int i, now, size = 0;
int ret = 0;
- uint32_t gen_speed, lane_width;
+ uint32_t gen_speed, lane_width, current_gen_speed, current_lane_width;
switch (type) {
case PP_SCLK:
@@ -3137,10 +3114,11 @@ static int vega20_print_clock_levels(struct pp_hwmgr *hwmgr,
"Attempt to get current gfx clk Failed!",
return ret);
- ret = vega20_get_sclks(hwmgr, &clocks);
- PP_ASSERT_WITH_CODE(!ret,
- "Attempt to get gfx clk levels Failed!",
- return ret);
+ if (vega20_get_sclks(hwmgr, &clocks)) {
+ size += sprintf(buf + size, "0: %uMhz * (DPM disabled)\n",
+ now / 100);
+ break;
+ }
for (i = 0; i < clocks.num_levels; i++)
size += sprintf(buf + size, "%d: %uMhz %s\n",
@@ -3154,10 +3132,11 @@ static int vega20_print_clock_levels(struct pp_hwmgr *hwmgr,
"Attempt to get current mclk freq Failed!",
return ret);
- ret = vega20_get_memclocks(hwmgr, &clocks);
- PP_ASSERT_WITH_CODE(!ret,
- "Attempt to get memory clk levels Failed!",
- return ret);
+ if (vega20_get_memclocks(hwmgr, &clocks)) {
+ size += sprintf(buf + size, "0: %uMhz * (DPM disabled)\n",
+ now / 100);
+ break;
+ }
for (i = 0; i < clocks.num_levels; i++)
size += sprintf(buf + size, "%d: %uMhz %s\n",
@@ -3171,10 +3150,11 @@ static int vega20_print_clock_levels(struct pp_hwmgr *hwmgr,
"Attempt to get current socclk freq Failed!",
return ret);
- ret = vega20_get_socclocks(hwmgr, &clocks);
- PP_ASSERT_WITH_CODE(!ret,
- "Attempt to get soc clk levels Failed!",
- return ret);
+ if (vega20_get_socclocks(hwmgr, &clocks)) {
+ size += sprintf(buf + size, "0: %uMhz * (DPM disabled)\n",
+ now / 100);
+ break;
+ }
for (i = 0; i < clocks.num_levels; i++)
size += sprintf(buf + size, "%d: %uMhz %s\n",
@@ -3200,10 +3180,11 @@ static int vega20_print_clock_levels(struct pp_hwmgr *hwmgr,
"Attempt to get current dcefclk freq Failed!",
return ret);
- ret = vega20_get_dcefclocks(hwmgr, &clocks);
- PP_ASSERT_WITH_CODE(!ret,
- "Attempt to get dcefclk levels Failed!",
- return ret);
+ if (vega20_get_dcefclocks(hwmgr, &clocks)) {
+ size += sprintf(buf + size, "0: %uMhz * (DPM disabled)\n",
+ now / 100);
+ break;
+ }
for (i = 0; i < clocks.num_levels; i++)
size += sprintf(buf + size, "%d: %uMhz %s\n",
@@ -3212,28 +3193,36 @@ static int vega20_print_clock_levels(struct pp_hwmgr *hwmgr,
break;
case PP_PCIE:
- gen_speed = (RREG32_PCIE(smnPCIE_LC_SPEED_CNTL) &
+ current_gen_speed = (RREG32_PCIE(smnPCIE_LC_SPEED_CNTL) &
PSWUSP0_PCIE_LC_SPEED_CNTL__LC_CURRENT_DATA_RATE_MASK)
>> PSWUSP0_PCIE_LC_SPEED_CNTL__LC_CURRENT_DATA_RATE__SHIFT;
- lane_width = (RREG32_PCIE(smnPCIE_LC_LINK_WIDTH_CNTL) &
+ current_lane_width = (RREG32_PCIE(smnPCIE_LC_LINK_WIDTH_CNTL) &
PCIE_LC_LINK_WIDTH_CNTL__LC_LINK_WIDTH_RD_MASK)
>> PCIE_LC_LINK_WIDTH_CNTL__LC_LINK_WIDTH_RD__SHIFT;
- for (i = 0; i < NUM_LINK_LEVELS; i++)
+ for (i = 0; i < NUM_LINK_LEVELS; i++) {
+ if (i == 1 && data->pcie_parameters_override) {
+ gen_speed = data->pcie_gen_level1;
+ lane_width = data->pcie_width_level1;
+ } else {
+ gen_speed = pptable->PcieGenSpeed[i];
+ lane_width = pptable->PcieLaneCount[i];
+ }
size += sprintf(buf + size, "%d: %s %s %dMhz %s\n", i,
- (pptable->PcieGenSpeed[i] == 0) ? "2.5GT/s," :
- (pptable->PcieGenSpeed[i] == 1) ? "5.0GT/s," :
- (pptable->PcieGenSpeed[i] == 2) ? "8.0GT/s," :
- (pptable->PcieGenSpeed[i] == 3) ? "16.0GT/s," : "",
- (pptable->PcieLaneCount[i] == 1) ? "x1" :
- (pptable->PcieLaneCount[i] == 2) ? "x2" :
- (pptable->PcieLaneCount[i] == 3) ? "x4" :
- (pptable->PcieLaneCount[i] == 4) ? "x8" :
- (pptable->PcieLaneCount[i] == 5) ? "x12" :
- (pptable->PcieLaneCount[i] == 6) ? "x16" : "",
+ (gen_speed == 0) ? "2.5GT/s," :
+ (gen_speed == 1) ? "5.0GT/s," :
+ (gen_speed == 2) ? "8.0GT/s," :
+ (gen_speed == 3) ? "16.0GT/s," : "",
+ (lane_width == 1) ? "x1" :
+ (lane_width == 2) ? "x2" :
+ (lane_width == 3) ? "x4" :
+ (lane_width == 4) ? "x8" :
+ (lane_width == 5) ? "x12" :
+ (lane_width == 6) ? "x16" : "",
pptable->LclkFreq[i],
- (gen_speed == pptable->PcieGenSpeed[i]) &&
- (lane_width == pptable->PcieLaneCount[i]) ?
+ (current_gen_speed == gen_speed) &&
+ (current_lane_width == lane_width) ?
"*" : "");
+ }
break;
case OD_SCLK:
@@ -3288,13 +3277,8 @@ static int vega20_print_clock_levels(struct pp_hwmgr *hwmgr,
}
if (od8_settings[OD8_SETTING_UCLK_FMAX].feature_id) {
- ret = vega20_get_memclocks(hwmgr, &clocks);
- PP_ASSERT_WITH_CODE(!ret,
- "Fail to get memory clk levels!",
- return ret);
-
size += sprintf(buf + size, "MCLK: %7uMhz %10uMhz\n",
- clocks.data[0].clocks_in_khz / 1000,
+ od8_settings[OD8_SETTING_UCLK_FMAX].min_value,
od8_settings[OD8_SETTING_UCLK_FMAX].max_value);
}
@@ -3356,6 +3340,31 @@ static int vega20_set_uclk_to_highest_dpm_level(struct pp_hwmgr *hwmgr,
return ret;
}
+static int vega20_set_fclk_to_highest_dpm_level(struct pp_hwmgr *hwmgr)
+{
+ struct vega20_hwmgr *data = (struct vega20_hwmgr *)(hwmgr->backend);
+ struct vega20_single_dpm_table *dpm_table = &(data->dpm_table.fclk_table);
+ int ret = 0;
+
+ if (data->smu_features[GNLD_DPM_FCLK].enabled) {
+ PP_ASSERT_WITH_CODE(dpm_table->count > 0,
+ "[SetFclkToHightestDpmLevel] Dpm table has no entry!",
+ return -EINVAL);
+ PP_ASSERT_WITH_CODE(dpm_table->count <= NUM_FCLK_DPM_LEVELS,
+ "[SetFclkToHightestDpmLevel] Dpm table has too many entries!",
+ return -EINVAL);
+
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ PP_ASSERT_WITH_CODE(!(ret = smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_SetSoftMinByFreq,
+ (PPCLK_FCLK << 16 ) | dpm_table->dpm_state.soft_min_level)),
+ "[SetFclkToHightestDpmLevel] Set soft min fclk failed!",
+ return ret);
+ }
+
+ return ret;
+}
+
static int vega20_pre_display_configuration_changed_task(struct pp_hwmgr *hwmgr)
{
struct vega20_hwmgr *data = (struct vega20_hwmgr *)(hwmgr->backend);
@@ -3366,8 +3375,10 @@ static int vega20_pre_display_configuration_changed_task(struct pp_hwmgr *hwmgr)
ret = vega20_set_uclk_to_highest_dpm_level(hwmgr,
&data->dpm_table.mem_table);
+ if (ret)
+ return ret;
- return ret;
+ return vega20_set_fclk_to_highest_dpm_level(hwmgr);
}
static int vega20_display_configuration_changed_task(struct pp_hwmgr *hwmgr)
@@ -3461,9 +3472,9 @@ static int vega20_apply_clocks_adjust_rules(struct pp_hwmgr *hwmgr)
/* gfxclk */
dpm_table = &(data->dpm_table.gfx_table);
dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[0].value;
- dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ dpm_table->dpm_state.soft_max_level = VG20_CLOCK_MAX_DEFAULT;
dpm_table->dpm_state.hard_min_level = dpm_table->dpm_levels[0].value;
- dpm_table->dpm_state.hard_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ dpm_table->dpm_state.hard_max_level = VG20_CLOCK_MAX_DEFAULT;
if (PP_CAP(PHM_PlatformCaps_UMDPState)) {
if (VEGA20_UMD_PSTATE_GFXCLK_LEVEL < dpm_table->count) {
@@ -3485,9 +3496,9 @@ static int vega20_apply_clocks_adjust_rules(struct pp_hwmgr *hwmgr)
/* memclk */
dpm_table = &(data->dpm_table.mem_table);
dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[0].value;
- dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ dpm_table->dpm_state.soft_max_level = VG20_CLOCK_MAX_DEFAULT;
dpm_table->dpm_state.hard_min_level = dpm_table->dpm_levels[0].value;
- dpm_table->dpm_state.hard_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ dpm_table->dpm_state.hard_max_level = VG20_CLOCK_MAX_DEFAULT;
if (PP_CAP(PHM_PlatformCaps_UMDPState)) {
if (VEGA20_UMD_PSTATE_MCLK_LEVEL < dpm_table->count) {
@@ -3526,12 +3537,21 @@ static int vega20_apply_clocks_adjust_rules(struct pp_hwmgr *hwmgr)
if (hwmgr->display_config->nb_pstate_switch_disable)
dpm_table->dpm_state.hard_min_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ /* fclk */
+ dpm_table = &(data->dpm_table.fclk_table);
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[0].value;
+ dpm_table->dpm_state.soft_max_level = VG20_CLOCK_MAX_DEFAULT;
+ dpm_table->dpm_state.hard_min_level = dpm_table->dpm_levels[0].value;
+ dpm_table->dpm_state.hard_max_level = VG20_CLOCK_MAX_DEFAULT;
+ if (hwmgr->display_config->nb_pstate_switch_disable)
+ dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+
/* vclk */
dpm_table = &(data->dpm_table.vclk_table);
dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[0].value;
- dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ dpm_table->dpm_state.soft_max_level = VG20_CLOCK_MAX_DEFAULT;
dpm_table->dpm_state.hard_min_level = dpm_table->dpm_levels[0].value;
- dpm_table->dpm_state.hard_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ dpm_table->dpm_state.hard_max_level = VG20_CLOCK_MAX_DEFAULT;
if (PP_CAP(PHM_PlatformCaps_UMDPState)) {
if (VEGA20_UMD_PSTATE_UVDCLK_LEVEL < dpm_table->count) {
@@ -3548,9 +3568,9 @@ static int vega20_apply_clocks_adjust_rules(struct pp_hwmgr *hwmgr)
/* dclk */
dpm_table = &(data->dpm_table.dclk_table);
dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[0].value;
- dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ dpm_table->dpm_state.soft_max_level = VG20_CLOCK_MAX_DEFAULT;
dpm_table->dpm_state.hard_min_level = dpm_table->dpm_levels[0].value;
- dpm_table->dpm_state.hard_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ dpm_table->dpm_state.hard_max_level = VG20_CLOCK_MAX_DEFAULT;
if (PP_CAP(PHM_PlatformCaps_UMDPState)) {
if (VEGA20_UMD_PSTATE_UVDCLK_LEVEL < dpm_table->count) {
@@ -3567,9 +3587,9 @@ static int vega20_apply_clocks_adjust_rules(struct pp_hwmgr *hwmgr)
/* socclk */
dpm_table = &(data->dpm_table.soc_table);
dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[0].value;
- dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ dpm_table->dpm_state.soft_max_level = VG20_CLOCK_MAX_DEFAULT;
dpm_table->dpm_state.hard_min_level = dpm_table->dpm_levels[0].value;
- dpm_table->dpm_state.hard_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ dpm_table->dpm_state.hard_max_level = VG20_CLOCK_MAX_DEFAULT;
if (PP_CAP(PHM_PlatformCaps_UMDPState)) {
if (VEGA20_UMD_PSTATE_SOCCLK_LEVEL < dpm_table->count) {
@@ -3586,9 +3606,9 @@ static int vega20_apply_clocks_adjust_rules(struct pp_hwmgr *hwmgr)
/* eclk */
dpm_table = &(data->dpm_table.eclk_table);
dpm_table->dpm_state.soft_min_level = dpm_table->dpm_levels[0].value;
- dpm_table->dpm_state.soft_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ dpm_table->dpm_state.soft_max_level = VG20_CLOCK_MAX_DEFAULT;
dpm_table->dpm_state.hard_min_level = dpm_table->dpm_levels[0].value;
- dpm_table->dpm_state.hard_max_level = dpm_table->dpm_levels[dpm_table->count - 1].value;
+ dpm_table->dpm_state.hard_max_level = VG20_CLOCK_MAX_DEFAULT;
if (PP_CAP(PHM_PlatformCaps_UMDPState)) {
if (VEGA20_UMD_PSTATE_VCEMCLK_LEVEL < dpm_table->count) {
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_hwmgr.h b/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_hwmgr.h
index 37f5f5e657da..a5bc758ae097 100644
--- a/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_hwmgr.h
+++ b/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_hwmgr.h
@@ -42,6 +42,8 @@
#define AVFS_CURVE 0
#define OD8_HOTCURVE_TEMPERATURE 85
+#define VG20_CLOCK_MAX_DEFAULT 0xFFFF
+
typedef uint32_t PP_Clock;
enum {
@@ -219,6 +221,7 @@ struct vega20_vbios_boot_state {
uint32_t eclock;
uint32_t dclock;
uint32_t vclock;
+ uint32_t fclock;
};
#define DPMTABLE_OD_UPDATE_SCLK 0x00000001
@@ -523,6 +526,10 @@ struct vega20_hwmgr {
unsigned long metrics_time;
SmuMetrics_t metrics_table;
+
+ bool pcie_parameters_override;
+ uint32_t pcie_gen_level1;
+ uint32_t pcie_width_level1;
};
#define VEGA20_DPM2_NEAR_TDP_DEC 10
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_processpptables.c b/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_processpptables.c
index 97f8a1a970c3..7a7f15d0c53a 100644
--- a/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_processpptables.c
+++ b/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_processpptables.c
@@ -32,6 +32,8 @@
#include "cgs_common.h"
#include "vega20_pptable.h"
+#define VEGA20_FAN_TARGET_TEMPERATURE_OVERRIDE 105
+
static void set_hw_cap(struct pp_hwmgr *hwmgr, bool enable,
enum phm_platform_caps cap)
{
@@ -798,6 +800,17 @@ static int append_vbios_pptable(struct pp_hwmgr *hwmgr, PPTable_t *ppsmc_pptable
return 0;
}
+static int override_powerplay_table_fantargettemperature(struct pp_hwmgr *hwmgr)
+{
+ struct phm_ppt_v3_information *pptable_information =
+ (struct phm_ppt_v3_information *)hwmgr->pptable;
+ PPTable_t *ppsmc_pptable = (PPTable_t *)(pptable_information->smc_pptable);
+
+ ppsmc_pptable->FanTargetTemperature = VEGA20_FAN_TARGET_TEMPERATURE_OVERRIDE;
+
+ return 0;
+}
+
#define VEGA20_ENGINECLOCK_HARDMAX 198000
static int init_powerplay_table_information(
struct pp_hwmgr *hwmgr,
@@ -887,6 +900,10 @@ static int init_powerplay_table_information(
result = append_vbios_pptable(hwmgr, (pptable_information->smc_pptable));
+ if (result)
+ return result;
+
+ result = override_powerplay_table_fantargettemperature(hwmgr);
return result;
}
diff --git a/drivers/gpu/drm/amd/powerplay/smumgr/polaris10_smumgr.c b/drivers/gpu/drm/amd/powerplay/smumgr/polaris10_smumgr.c
index 52abca065764..2d4cfe14f72e 100644
--- a/drivers/gpu/drm/amd/powerplay/smumgr/polaris10_smumgr.c
+++ b/drivers/gpu/drm/amd/powerplay/smumgr/polaris10_smumgr.c
@@ -2330,6 +2330,7 @@ static uint32_t polaris10_get_offsetof(uint32_t type, uint32_t member)
case DRAM_LOG_BUFF_SIZE:
return offsetof(SMU74_SoftRegisters, DRAM_LOG_BUFF_SIZE);
}
+ break;
case SMU_Discrete_DpmTable:
switch (member) {
case UvdBootLevel:
@@ -2339,6 +2340,7 @@ static uint32_t polaris10_get_offsetof(uint32_t type, uint32_t member)
case LowSclkInterruptThreshold:
return offsetof(SMU74_Discrete_DpmTable, LowSclkInterruptThreshold);
}
+ break;
}
pr_warn("can't get the offset of type %x member %x\n", type, member);
return 0;
diff --git a/drivers/gpu/drm/amd/powerplay/smumgr/smu9_smumgr.c b/drivers/gpu/drm/amd/powerplay/smumgr/smu9_smumgr.c
index 079fc8e8f709..742b3dc1f6cb 100644
--- a/drivers/gpu/drm/amd/powerplay/smumgr/smu9_smumgr.c
+++ b/drivers/gpu/drm/amd/powerplay/smumgr/smu9_smumgr.c
@@ -40,10 +40,8 @@ bool smu9_is_smc_ram_running(struct pp_hwmgr *hwmgr)
struct amdgpu_device *adev = hwmgr->adev;
uint32_t mp1_fw_flags;
- WREG32_SOC15(NBIF, 0, mmPCIE_INDEX2,
- (MP1_Public | (smnMP1_FIRMWARE_FLAGS & 0xffffffff)));
-
- mp1_fw_flags = RREG32_SOC15(NBIF, 0, mmPCIE_DATA2);
+ mp1_fw_flags = RREG32_PCIE(MP1_Public |
+ (smnMP1_FIRMWARE_FLAGS & 0xffffffff));
if (mp1_fw_flags & MP1_FIRMWARE_FLAGS__INTERRUPTS_ENABLED_MASK)
return true;
diff --git a/drivers/gpu/drm/amd/powerplay/smumgr/vega20_smumgr.c b/drivers/gpu/drm/amd/powerplay/smumgr/vega20_smumgr.c
index b7ff7d4d6f44..ba00744c3413 100644
--- a/drivers/gpu/drm/amd/powerplay/smumgr/vega20_smumgr.c
+++ b/drivers/gpu/drm/amd/powerplay/smumgr/vega20_smumgr.c
@@ -49,10 +49,8 @@ static bool vega20_is_smc_ram_running(struct pp_hwmgr *hwmgr)
struct amdgpu_device *adev = hwmgr->adev;
uint32_t mp1_fw_flags;
- WREG32_SOC15(NBIF, 0, mmPCIE_INDEX2,
- (MP1_Public | (smnMP1_FIRMWARE_FLAGS & 0xffffffff)));
-
- mp1_fw_flags = RREG32_SOC15(NBIF, 0, mmPCIE_DATA2);
+ mp1_fw_flags = RREG32_PCIE(MP1_Public |
+ (smnMP1_FIRMWARE_FLAGS & 0xffffffff));
if ((mp1_fw_flags & MP1_FIRMWARE_FLAGS__INTERRUPTS_ENABLED_MASK) >>
MP1_FIRMWARE_FLAGS__INTERRUPTS_ENABLED__SHIFT)
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index 540a77a2ade9..40ac19848034 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -3039,9 +3039,31 @@ commit:
return 0;
}
-static int __drm_atomic_helper_disable_all(struct drm_device *dev,
- struct drm_modeset_acquire_ctx *ctx,
- bool clean_old_fbs)
+/**
+ * drm_atomic_helper_disable_all - disable all currently active outputs
+ * @dev: DRM device
+ * @ctx: lock acquisition context
+ *
+ * Loops through all connectors, finding those that aren't turned off and then
+ * turns them off by setting their DPMS mode to OFF and deactivating the CRTC
+ * that they are connected to.
+ *
+ * This is used for example in suspend/resume to disable all currently active
+ * functions when suspending. If you just want to shut down everything at e.g.
+ * driver unload, look at drm_atomic_helper_shutdown().
+ *
+ * Note that if callers haven't already acquired all modeset locks this might
+ * return -EDEADLK, which must be handled by calling drm_modeset_backoff().
+ *
+ * Returns:
+ * 0 on success or a negative error code on failure.
+ *
+ * See also:
+ * drm_atomic_helper_suspend(), drm_atomic_helper_resume() and
+ * drm_atomic_helper_shutdown().
+ */
+int drm_atomic_helper_disable_all(struct drm_device *dev,
+ struct drm_modeset_acquire_ctx *ctx)
{
struct drm_atomic_state *state;
struct drm_connector_state *conn_state;
@@ -3099,35 +3121,6 @@ free:
drm_atomic_state_put(state);
return ret;
}
-
-/**
- * drm_atomic_helper_disable_all - disable all currently active outputs
- * @dev: DRM device
- * @ctx: lock acquisition context
- *
- * Loops through all connectors, finding those that aren't turned off and then
- * turns them off by setting their DPMS mode to OFF and deactivating the CRTC
- * that they are connected to.
- *
- * This is used for example in suspend/resume to disable all currently active
- * functions when suspending. If you just want to shut down everything at e.g.
- * driver unload, look at drm_atomic_helper_shutdown().
- *
- * Note that if callers haven't already acquired all modeset locks this might
- * return -EDEADLK, which must be handled by calling drm_modeset_backoff().
- *
- * Returns:
- * 0 on success or a negative error code on failure.
- *
- * See also:
- * drm_atomic_helper_suspend(), drm_atomic_helper_resume() and
- * drm_atomic_helper_shutdown().
- */
-int drm_atomic_helper_disable_all(struct drm_device *dev,
- struct drm_modeset_acquire_ctx *ctx)
-{
- return __drm_atomic_helper_disable_all(dev, ctx, false);
-}
EXPORT_SYMBOL(drm_atomic_helper_disable_all);
/**
@@ -3148,7 +3141,7 @@ void drm_atomic_helper_shutdown(struct drm_device *dev)
DRM_MODESET_LOCK_ALL_BEGIN(dev, ctx, 0, ret);
- ret = __drm_atomic_helper_disable_all(dev, &ctx, true);
+ ret = drm_atomic_helper_disable_all(dev, &ctx);
if (ret)
DRM_ERROR("Disabling all crtc's during unload failed with %i\n", ret);
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index 381581b01d48..05bbc2b622fc 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -376,11 +376,7 @@ void drm_dev_unplug(struct drm_device *dev)
synchronize_srcu(&drm_unplug_srcu);
drm_dev_unregister(dev);
-
- mutex_lock(&drm_global_mutex);
- if (dev->open_count == 0)
- drm_dev_put(dev);
- mutex_unlock(&drm_global_mutex);
+ drm_dev_put(dev);
}
EXPORT_SYMBOL(drm_dev_unplug);
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 0e9349ff2d16..af2ab640cadb 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -1963,7 +1963,7 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
best_depth = fmt->depth;
}
}
- if (sizes.surface_depth != best_depth) {
+ if (sizes.surface_depth != best_depth && best_depth) {
DRM_INFO("requested bpp %d, scaled depth down to %d",
sizes.surface_bpp, best_depth);
sizes.surface_depth = best_depth;
diff --git a/drivers/gpu/drm/drm_file.c b/drivers/gpu/drm/drm_file.c
index 83a5bbca6e7e..7caa3c7ed978 100644
--- a/drivers/gpu/drm/drm_file.c
+++ b/drivers/gpu/drm/drm_file.c
@@ -489,11 +489,9 @@ int drm_release(struct inode *inode, struct file *filp)
drm_close_helper(filp);
- if (!--dev->open_count) {
+ if (!--dev->open_count)
drm_lastclose(dev);
- if (drm_dev_is_unplugged(dev))
- drm_put_dev(dev);
- }
+
mutex_unlock(&drm_global_mutex);
drm_minor_release(minor);
diff --git a/drivers/gpu/drm/drm_ioc32.c b/drivers/gpu/drm/drm_ioc32.c
index 67b1fca39aa6..0e3043e08c69 100644
--- a/drivers/gpu/drm/drm_ioc32.c
+++ b/drivers/gpu/drm/drm_ioc32.c
@@ -185,7 +185,7 @@ static int compat_drm_getmap(struct file *file, unsigned int cmd,
m32.size = map.size;
m32.type = map.type;
m32.flags = map.flags;
- m32.handle = ptr_to_compat(map.handle);
+ m32.handle = ptr_to_compat((void __user *)map.handle);
m32.mtrr = map.mtrr;
if (copy_to_user(argp, &m32, sizeof(m32)))
return -EFAULT;
@@ -216,7 +216,7 @@ static int compat_drm_addmap(struct file *file, unsigned int cmd,
m32.offset = map.offset;
m32.mtrr = map.mtrr;
- m32.handle = ptr_to_compat(map.handle);
+ m32.handle = ptr_to_compat((void __user *)map.handle);
if (map.handle != compat_ptr(m32.handle))
pr_err_ratelimited("compat_drm_addmap truncated handle %p for type %d offset %x\n",
map.handle, m32.type, m32.offset);
@@ -526,7 +526,7 @@ static int compat_drm_getsareactx(struct file *file, unsigned int cmd,
if (err)
return err;
- req32.handle = ptr_to_compat(req.handle);
+ req32.handle = ptr_to_compat((void __user *)req.handle);
if (copy_to_user(argp, &req32, sizeof(req32)))
return -EFAULT;
diff --git a/drivers/gpu/drm/etnaviv/Kconfig b/drivers/gpu/drm/etnaviv/Kconfig
index 041a77e400d4..21df44b78df3 100644
--- a/drivers/gpu/drm/etnaviv/Kconfig
+++ b/drivers/gpu/drm/etnaviv/Kconfig
@@ -2,7 +2,6 @@
config DRM_ETNAVIV
tristate "ETNAVIV (DRM support for Vivante GPU IP cores)"
depends on DRM
- depends on ARCH_MXC || ARCH_DOVE || (ARM && COMPILE_TEST)
depends on MMU
select SHMEM
select SYNC_FILE
diff --git a/drivers/gpu/drm/etnaviv/etnaviv_cmdbuf.h b/drivers/gpu/drm/etnaviv/etnaviv_cmdbuf.h
index acb68c698363..4d5d1a77eb2a 100644
--- a/drivers/gpu/drm/etnaviv/etnaviv_cmdbuf.h
+++ b/drivers/gpu/drm/etnaviv/etnaviv_cmdbuf.h
@@ -15,8 +15,6 @@ struct etnaviv_perfmon_request;
struct etnaviv_cmdbuf {
/* suballocator this cmdbuf is allocated from */
struct etnaviv_cmdbuf_suballoc *suballoc;
- /* user context key, must be unique between all active users */
- struct etnaviv_file_private *ctx;
/* cmdbuf properties */
int suballoc_offset;
void *vaddr;
diff --git a/drivers/gpu/drm/etnaviv/etnaviv_dump.c b/drivers/gpu/drm/etnaviv/etnaviv_dump.c
index 3fbb4855396c..33854c94cb85 100644
--- a/drivers/gpu/drm/etnaviv/etnaviv_dump.c
+++ b/drivers/gpu/drm/etnaviv/etnaviv_dump.c
@@ -215,7 +215,7 @@ void etnaviv_core_dump(struct etnaviv_gpu *gpu)
mutex_lock(&obj->lock);
pages = etnaviv_gem_get_pages(obj);
mutex_unlock(&obj->lock);
- if (pages) {
+ if (!IS_ERR(pages)) {
int j;
iter.hdr->data[0] = bomap - bomap_start;
diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem.h b/drivers/gpu/drm/etnaviv/etnaviv_gem.h
index 76079c2291f8..f0abb744ef95 100644
--- a/drivers/gpu/drm/etnaviv/etnaviv_gem.h
+++ b/drivers/gpu/drm/etnaviv/etnaviv_gem.h
@@ -95,6 +95,7 @@ struct etnaviv_gem_submit_bo {
struct etnaviv_gem_submit {
struct drm_sched_job sched_job;
struct kref refcount;
+ struct etnaviv_file_private *ctx;
struct etnaviv_gpu *gpu;
struct dma_fence *out_fence, *in_fence;
int out_fence_id;
diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c b/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c
index 0566171f8df2..f21529e635e3 100644
--- a/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c
+++ b/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c
@@ -15,7 +15,7 @@ struct sg_table *etnaviv_gem_prime_get_sg_table(struct drm_gem_object *obj)
int npages = obj->size >> PAGE_SHIFT;
if (WARN_ON(!etnaviv_obj->pages)) /* should have already pinned! */
- return NULL;
+ return ERR_PTR(-EINVAL);
return drm_prime_pages_to_sg(etnaviv_obj->pages, npages);
}
diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c b/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c
index 30875f8f2933..b2fe3446bfbc 100644
--- a/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c
+++ b/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c
@@ -506,7 +506,7 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data,
if (ret)
goto err_submit_objects;
- submit->cmdbuf.ctx = file->driver_priv;
+ submit->ctx = file->driver_priv;
submit->exec_state = args->exec_state;
submit->flags = args->flags;
diff --git a/drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.c b/drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.c
index f1c88d8ad5ba..f794e04be9e6 100644
--- a/drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.c
+++ b/drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.c
@@ -320,8 +320,8 @@ etnaviv_iommuv2_domain_alloc(struct etnaviv_gpu *gpu)
domain = &etnaviv_domain->base;
domain->dev = gpu->dev;
- domain->base = 0;
- domain->size = (u64)SZ_1G * 4;
+ domain->base = SZ_4K;
+ domain->size = (u64)SZ_1G * 4 - SZ_4K;
domain->ops = &etnaviv_iommuv2_ops;
ret = etnaviv_iommuv2_init(etnaviv_domain);
diff --git a/drivers/gpu/drm/etnaviv/etnaviv_perfmon.c b/drivers/gpu/drm/etnaviv/etnaviv_perfmon.c
index 9980d81a26e3..4227a4006c34 100644
--- a/drivers/gpu/drm/etnaviv/etnaviv_perfmon.c
+++ b/drivers/gpu/drm/etnaviv/etnaviv_perfmon.c
@@ -113,7 +113,7 @@ static const struct etnaviv_pm_domain doms_3d[] = {
.name = "PE",
.profile_read = VIVS_MC_PROFILE_PE_READ,
.profile_config = VIVS_MC_PROFILE_CONFIG0,
- .nr_signals = 5,
+ .nr_signals = 4,
.signal = (const struct etnaviv_pm_signal[]) {
{
"PIXEL_COUNT_KILLED_BY_COLOR_PIPE",
@@ -435,7 +435,7 @@ int etnaviv_pm_query_sig(struct etnaviv_gpu *gpu,
dom = meta->domains + signal->domain;
- if (signal->iter > dom->nr_signals)
+ if (signal->iter >= dom->nr_signals)
return -EINVAL;
sig = &dom->signal[signal->iter];
@@ -461,7 +461,7 @@ int etnaviv_pm_req_validate(const struct drm_etnaviv_gem_submit_pmr *r,
dom = meta->domains + r->domain;
- if (r->signal > dom->nr_signals)
+ if (r->signal >= dom->nr_signals)
return -EINVAL;
return 0;
diff --git a/drivers/gpu/drm/etnaviv/etnaviv_sched.c b/drivers/gpu/drm/etnaviv/etnaviv_sched.c
index 67ae26602024..6d24fea1766b 100644
--- a/drivers/gpu/drm/etnaviv/etnaviv_sched.c
+++ b/drivers/gpu/drm/etnaviv/etnaviv_sched.c
@@ -153,7 +153,7 @@ int etnaviv_sched_push_job(struct drm_sched_entity *sched_entity,
mutex_lock(&submit->gpu->fence_lock);
ret = drm_sched_job_init(&submit->sched_job, sched_entity,
- submit->cmdbuf.ctx);
+ submit->ctx);
if (ret)
goto out_unlock;
diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c
index 0573eab0e190..f35e4ab55b27 100644
--- a/drivers/gpu/drm/exynos/exynos_mixer.c
+++ b/drivers/gpu/drm/exynos/exynos_mixer.c
@@ -20,6 +20,7 @@
#include "regs-vp.h"
#include <linux/kernel.h>
+#include <linux/ktime.h>
#include <linux/spinlock.h>
#include <linux/wait.h>
#include <linux/i2c.h>
@@ -352,15 +353,62 @@ static void mixer_cfg_vp_blend(struct mixer_context *ctx, unsigned int alpha)
mixer_reg_write(ctx, MXR_VIDEO_CFG, val);
}
-static void mixer_vsync_set_update(struct mixer_context *ctx, bool enable)
+static bool mixer_is_synced(struct mixer_context *ctx)
{
- /* block update on vsync */
- mixer_reg_writemask(ctx, MXR_STATUS, enable ?
- MXR_STATUS_SYNC_ENABLE : 0, MXR_STATUS_SYNC_ENABLE);
+ u32 base, shadow;
+ if (ctx->mxr_ver == MXR_VER_16_0_33_0 ||
+ ctx->mxr_ver == MXR_VER_128_0_0_184)
+ return !(mixer_reg_read(ctx, MXR_CFG) &
+ MXR_CFG_LAYER_UPDATE_COUNT_MASK);
+
+ if (test_bit(MXR_BIT_VP_ENABLED, &ctx->flags) &&
+ vp_reg_read(ctx, VP_SHADOW_UPDATE))
+ return false;
+
+ base = mixer_reg_read(ctx, MXR_CFG);
+ shadow = mixer_reg_read(ctx, MXR_CFG_S);
+ if (base != shadow)
+ return false;
+
+ base = mixer_reg_read(ctx, MXR_GRAPHIC_BASE(0));
+ shadow = mixer_reg_read(ctx, MXR_GRAPHIC_BASE_S(0));
+ if (base != shadow)
+ return false;
+
+ base = mixer_reg_read(ctx, MXR_GRAPHIC_BASE(1));
+ shadow = mixer_reg_read(ctx, MXR_GRAPHIC_BASE_S(1));
+ if (base != shadow)
+ return false;
+
+ return true;
+}
+
+static int mixer_wait_for_sync(struct mixer_context *ctx)
+{
+ ktime_t timeout = ktime_add_us(ktime_get(), 100000);
+
+ while (!mixer_is_synced(ctx)) {
+ usleep_range(1000, 2000);
+ if (ktime_compare(ktime_get(), timeout) > 0)
+ return -ETIMEDOUT;
+ }
+ return 0;
+}
+
+static void mixer_disable_sync(struct mixer_context *ctx)
+{
+ mixer_reg_writemask(ctx, MXR_STATUS, 0, MXR_STATUS_SYNC_ENABLE);
+}
+
+static void mixer_enable_sync(struct mixer_context *ctx)
+{
+ if (ctx->mxr_ver == MXR_VER_16_0_33_0 ||
+ ctx->mxr_ver == MXR_VER_128_0_0_184)
+ mixer_reg_writemask(ctx, MXR_CFG, ~0, MXR_CFG_LAYER_UPDATE);
+ mixer_reg_writemask(ctx, MXR_STATUS, ~0, MXR_STATUS_SYNC_ENABLE);
if (test_bit(MXR_BIT_VP_ENABLED, &ctx->flags))
- vp_reg_write(ctx, VP_SHADOW_UPDATE, enable ?
- VP_SHADOW_UPDATE_ENABLE : 0);
+ vp_reg_write(ctx, VP_SHADOW_UPDATE, VP_SHADOW_UPDATE_ENABLE);
}
static void mixer_cfg_scan(struct mixer_context *ctx, int width, int height)
@@ -498,7 +546,6 @@ static void vp_video_buffer(struct mixer_context *ctx,
spin_lock_irqsave(&ctx->reg_slock, flags);
- vp_reg_write(ctx, VP_SHADOW_UPDATE, 1);
/* interlace or progressive scan mode */
val = (test_bit(MXR_BIT_INTERLACE, &ctx->flags) ? ~0 : 0);
vp_reg_writemask(ctx, VP_MODE, val, VP_MODE_LINE_SKIP);
@@ -553,11 +600,6 @@ static void vp_video_buffer(struct mixer_context *ctx,
vp_regs_dump(ctx);
}
-static void mixer_layer_update(struct mixer_context *ctx)
-{
- mixer_reg_writemask(ctx, MXR_CFG, ~0, MXR_CFG_LAYER_UPDATE);
-}
-
static void mixer_graph_buffer(struct mixer_context *ctx,
struct exynos_drm_plane *plane)
{
@@ -640,11 +682,6 @@ static void mixer_graph_buffer(struct mixer_context *ctx,
mixer_cfg_layer(ctx, win, priority, true);
mixer_cfg_gfx_blend(ctx, win, pixel_alpha, state->base.alpha);
- /* layer update mandatory for mixer 16.0.33.0 */
- if (ctx->mxr_ver == MXR_VER_16_0_33_0 ||
- ctx->mxr_ver == MXR_VER_128_0_0_184)
- mixer_layer_update(ctx);
-
spin_unlock_irqrestore(&ctx->reg_slock, flags);
mixer_regs_dump(ctx);
@@ -709,7 +746,7 @@ static void mixer_win_reset(struct mixer_context *ctx)
static irqreturn_t mixer_irq_handler(int irq, void *arg)
{
struct mixer_context *ctx = arg;
- u32 val, base, shadow;
+ u32 val;
spin_lock(&ctx->reg_slock);
@@ -723,26 +760,9 @@ static irqreturn_t mixer_irq_handler(int irq, void *arg)
val &= ~MXR_INT_STATUS_VSYNC;
/* interlace scan need to check shadow register */
- if (test_bit(MXR_BIT_INTERLACE, &ctx->flags)) {
- if (test_bit(MXR_BIT_VP_ENABLED, &ctx->flags) &&
- vp_reg_read(ctx, VP_SHADOW_UPDATE))
- goto out;
-
- base = mixer_reg_read(ctx, MXR_CFG);
- shadow = mixer_reg_read(ctx, MXR_CFG_S);
- if (base != shadow)
- goto out;
-
- base = mixer_reg_read(ctx, MXR_GRAPHIC_BASE(0));
- shadow = mixer_reg_read(ctx, MXR_GRAPHIC_BASE_S(0));
- if (base != shadow)
- goto out;
-
- base = mixer_reg_read(ctx, MXR_GRAPHIC_BASE(1));
- shadow = mixer_reg_read(ctx, MXR_GRAPHIC_BASE_S(1));
- if (base != shadow)
- goto out;
- }
+ if (test_bit(MXR_BIT_INTERLACE, &ctx->flags)
+ && !mixer_is_synced(ctx))
+ goto out;
drm_crtc_handle_vblank(&ctx->crtc->base);
}
@@ -917,12 +937,14 @@ static void mixer_disable_vblank(struct exynos_drm_crtc *crtc)
static void mixer_atomic_begin(struct exynos_drm_crtc *crtc)
{
- struct mixer_context *mixer_ctx = crtc->ctx;
+ struct mixer_context *ctx = crtc->ctx;
- if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags))
+ if (!test_bit(MXR_BIT_POWERED, &ctx->flags))
return;
- mixer_vsync_set_update(mixer_ctx, false);
+ if (mixer_wait_for_sync(ctx))
+ dev_err(ctx->dev, "timeout waiting for VSYNC\n");
+ mixer_disable_sync(ctx);
}
static void mixer_update_plane(struct exynos_drm_crtc *crtc,
@@ -964,7 +986,7 @@ static void mixer_atomic_flush(struct exynos_drm_crtc *crtc)
if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags))
return;
- mixer_vsync_set_update(mixer_ctx, true);
+ mixer_enable_sync(mixer_ctx);
exynos_crtc_handle_event(crtc);
}
@@ -979,7 +1001,7 @@ static void mixer_enable(struct exynos_drm_crtc *crtc)
exynos_drm_pipe_clk_enable(crtc, true);
- mixer_vsync_set_update(ctx, false);
+ mixer_disable_sync(ctx);
mixer_reg_writemask(ctx, MXR_STATUS, ~0, MXR_STATUS_SOFT_RESET);
@@ -992,7 +1014,7 @@ static void mixer_enable(struct exynos_drm_crtc *crtc)
mixer_commit(ctx);
- mixer_vsync_set_update(ctx, true);
+ mixer_enable_sync(ctx);
set_bit(MXR_BIT_POWERED, &ctx->flags);
}
diff --git a/drivers/gpu/drm/i915/gvt/cmd_parser.c b/drivers/gpu/drm/i915/gvt/cmd_parser.c
index 35b4ec3f7618..3592d04c33b2 100644
--- a/drivers/gpu/drm/i915/gvt/cmd_parser.c
+++ b/drivers/gpu/drm/i915/gvt/cmd_parser.c
@@ -1441,7 +1441,7 @@ static inline int cmd_address_audit(struct parser_exec_state *s,
}
if (index_mode) {
- if (guest_gma >= I915_GTT_PAGE_SIZE / sizeof(u64)) {
+ if (guest_gma >= I915_GTT_PAGE_SIZE) {
ret = -EFAULT;
goto err;
}
diff --git a/drivers/gpu/drm/i915/gvt/gtt.c b/drivers/gpu/drm/i915/gvt/gtt.c
index c7103dd2d8d5..d7052ab7908c 100644
--- a/drivers/gpu/drm/i915/gvt/gtt.c
+++ b/drivers/gpu/drm/i915/gvt/gtt.c
@@ -1882,7 +1882,11 @@ struct intel_vgpu_mm *intel_vgpu_create_ppgtt_mm(struct intel_vgpu *vgpu,
}
list_add_tail(&mm->ppgtt_mm.list, &vgpu->gtt.ppgtt_mm_list_head);
+
+ mutex_lock(&gvt->gtt.ppgtt_mm_lock);
list_add_tail(&mm->ppgtt_mm.lru_list, &gvt->gtt.ppgtt_mm_lru_list_head);
+ mutex_unlock(&gvt->gtt.ppgtt_mm_lock);
+
return mm;
}
@@ -1967,9 +1971,10 @@ int intel_vgpu_pin_mm(struct intel_vgpu_mm *mm)
if (ret)
return ret;
+ mutex_lock(&mm->vgpu->gvt->gtt.ppgtt_mm_lock);
list_move_tail(&mm->ppgtt_mm.lru_list,
&mm->vgpu->gvt->gtt.ppgtt_mm_lru_list_head);
-
+ mutex_unlock(&mm->vgpu->gvt->gtt.ppgtt_mm_lock);
}
return 0;
@@ -1980,6 +1985,8 @@ static int reclaim_one_ppgtt_mm(struct intel_gvt *gvt)
struct intel_vgpu_mm *mm;
struct list_head *pos, *n;
+ mutex_lock(&gvt->gtt.ppgtt_mm_lock);
+
list_for_each_safe(pos, n, &gvt->gtt.ppgtt_mm_lru_list_head) {
mm = container_of(pos, struct intel_vgpu_mm, ppgtt_mm.lru_list);
@@ -1987,9 +1994,11 @@ static int reclaim_one_ppgtt_mm(struct intel_gvt *gvt)
continue;
list_del_init(&mm->ppgtt_mm.lru_list);
+ mutex_unlock(&gvt->gtt.ppgtt_mm_lock);
invalidate_ppgtt_mm(mm);
return 1;
}
+ mutex_unlock(&gvt->gtt.ppgtt_mm_lock);
return 0;
}
@@ -2659,6 +2668,7 @@ int intel_gvt_init_gtt(struct intel_gvt *gvt)
}
}
INIT_LIST_HEAD(&gvt->gtt.ppgtt_mm_lru_list_head);
+ mutex_init(&gvt->gtt.ppgtt_mm_lock);
return 0;
}
@@ -2699,7 +2709,9 @@ void intel_vgpu_invalidate_ppgtt(struct intel_vgpu *vgpu)
list_for_each_safe(pos, n, &vgpu->gtt.ppgtt_mm_list_head) {
mm = container_of(pos, struct intel_vgpu_mm, ppgtt_mm.list);
if (mm->type == INTEL_GVT_MM_PPGTT) {
+ mutex_lock(&vgpu->gvt->gtt.ppgtt_mm_lock);
list_del_init(&mm->ppgtt_mm.lru_list);
+ mutex_unlock(&vgpu->gvt->gtt.ppgtt_mm_lock);
if (mm->ppgtt_mm.shadowed)
invalidate_ppgtt_mm(mm);
}
diff --git a/drivers/gpu/drm/i915/gvt/gtt.h b/drivers/gpu/drm/i915/gvt/gtt.h
index d8cb04cc946d..edb610dc5d86 100644
--- a/drivers/gpu/drm/i915/gvt/gtt.h
+++ b/drivers/gpu/drm/i915/gvt/gtt.h
@@ -88,6 +88,7 @@ struct intel_gvt_gtt {
void (*mm_free_page_table)(struct intel_vgpu_mm *mm);
struct list_head oos_page_use_list_head;
struct list_head oos_page_free_list_head;
+ struct mutex ppgtt_mm_lock;
struct list_head ppgtt_mm_lru_list_head;
struct page *scratch_page;
diff --git a/drivers/gpu/drm/i915/gvt/mmio_context.c b/drivers/gpu/drm/i915/gvt/mmio_context.c
index 7d84cfb9051a..7902fb162d09 100644
--- a/drivers/gpu/drm/i915/gvt/mmio_context.c
+++ b/drivers/gpu/drm/i915/gvt/mmio_context.c
@@ -132,6 +132,7 @@ static struct engine_mmio gen9_engine_mmio_list[] __cacheline_aligned = {
{RCS, GEN9_GAMT_ECO_REG_RW_IA, 0x0, false}, /* 0x4ab0 */
{RCS, GEN9_CSFE_CHICKEN1_RCS, 0xffff, false}, /* 0x20d4 */
+ {RCS, _MMIO(0x20D8), 0xffff, true}, /* 0x20d8 */
{RCS, GEN8_GARBCNTL, 0x0, false}, /* 0xb004 */
{RCS, GEN7_FF_THREAD_MODE, 0x0, false}, /* 0x20a0 */
diff --git a/drivers/gpu/drm/i915/gvt/scheduler.c b/drivers/gpu/drm/i915/gvt/scheduler.c
index 1bb8f936fdaa..159192c097cc 100644
--- a/drivers/gpu/drm/i915/gvt/scheduler.c
+++ b/drivers/gpu/drm/i915/gvt/scheduler.c
@@ -346,7 +346,7 @@ static int set_context_ppgtt_from_shadow(struct intel_vgpu_workload *workload,
int i = 0;
if (mm->type != INTEL_GVT_MM_PPGTT || !mm->ppgtt_mm.shadowed)
- return -1;
+ return -EINVAL;
if (mm->ppgtt_mm.root_entry_type == GTT_TYPE_PPGTT_ROOT_L4_ENTRY) {
px_dma(&ppgtt->pml4) = mm->ppgtt_mm.shadow_pdps[0];
@@ -410,12 +410,6 @@ int intel_gvt_scan_and_shadow_workload(struct intel_vgpu_workload *workload)
if (workload->shadow)
return 0;
- ret = set_context_ppgtt_from_shadow(workload, shadow_ctx);
- if (ret < 0) {
- gvt_vgpu_err("workload shadow ppgtt isn't ready\n");
- return ret;
- }
-
/* pin shadow context by gvt even the shadow context will be pinned
* when i915 alloc request. That is because gvt will update the guest
* context from shadow context when workload is completed, and at that
@@ -678,6 +672,9 @@ static int dispatch_workload(struct intel_vgpu_workload *workload)
{
struct intel_vgpu *vgpu = workload->vgpu;
struct drm_i915_private *dev_priv = vgpu->gvt->dev_priv;
+ struct intel_vgpu_submission *s = &vgpu->submission;
+ struct i915_gem_context *shadow_ctx = s->shadow_ctx;
+ struct i915_request *rq;
int ring_id = workload->ring_id;
int ret;
@@ -687,6 +684,12 @@ static int dispatch_workload(struct intel_vgpu_workload *workload)
mutex_lock(&vgpu->vgpu_lock);
mutex_lock(&dev_priv->drm.struct_mutex);
+ ret = set_context_ppgtt_from_shadow(workload, shadow_ctx);
+ if (ret < 0) {
+ gvt_vgpu_err("workload shadow ppgtt isn't ready\n");
+ goto err_req;
+ }
+
ret = intel_gvt_workload_req_alloc(workload);
if (ret)
goto err_req;
@@ -703,6 +706,14 @@ static int dispatch_workload(struct intel_vgpu_workload *workload)
ret = prepare_workload(workload);
out:
+ if (ret) {
+ /* We might still need to add request with
+ * clean ctx to retire it properly..
+ */
+ rq = fetch_and_zero(&workload->req);
+ i915_request_put(rq);
+ }
+
if (!IS_ERR_OR_NULL(workload->req)) {
gvt_dbg_sched("ring id %d submit workload to i915 %p\n",
ring_id, workload->req);
@@ -739,7 +750,8 @@ static struct intel_vgpu_workload *pick_next_workload(
goto out;
}
- if (list_empty(workload_q_head(scheduler->current_vgpu, ring_id)))
+ if (!scheduler->current_vgpu->active ||
+ list_empty(workload_q_head(scheduler->current_vgpu, ring_id)))
goto out;
/*
diff --git a/drivers/gpu/drm/i915/i915_active.c b/drivers/gpu/drm/i915/i915_active.c
index 215b6ff8aa73..db7bb5bd5add 100644
--- a/drivers/gpu/drm/i915/i915_active.c
+++ b/drivers/gpu/drm/i915/i915_active.c
@@ -163,17 +163,25 @@ int i915_active_ref(struct i915_active *ref,
struct i915_request *rq)
{
struct i915_active_request *active;
+ int err = 0;
+
+ /* Prevent reaping in case we malloc/wait while building the tree */
+ i915_active_acquire(ref);
active = active_instance(ref, timeline);
- if (IS_ERR(active))
- return PTR_ERR(active);
+ if (IS_ERR(active)) {
+ err = PTR_ERR(active);
+ goto out;
+ }
if (!i915_active_request_isset(active))
ref->count++;
__i915_active_request_set(active, rq);
GEM_BUG_ON(!ref->count);
- return 0;
+out:
+ i915_active_release(ref);
+ return err;
}
bool i915_active_acquire(struct i915_active *ref)
@@ -223,19 +231,25 @@ int i915_request_await_active_request(struct i915_request *rq,
int i915_request_await_active(struct i915_request *rq, struct i915_active *ref)
{
struct active_node *it, *n;
- int ret;
+ int err = 0;
- ret = i915_request_await_active_request(rq, &ref->last);
- if (ret)
- return ret;
+ /* await allocates and so we need to avoid hitting the shrinker */
+ if (i915_active_acquire(ref))
+ goto out; /* was idle */
+
+ err = i915_request_await_active_request(rq, &ref->last);
+ if (err)
+ goto out;
rbtree_postorder_for_each_entry_safe(it, n, &ref->tree, node) {
- ret = i915_request_await_active_request(rq, &it->base);
- if (ret)
- return ret;
+ err = i915_request_await_active_request(rq, &it->base);
+ if (err)
+ goto out;
}
- return 0;
+out:
+ i915_active_release(ref);
+ return err;
}
#if IS_ENABLED(CONFIG_DRM_I915_DEBUG_GEM)
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index 6630212f2faf..9df65d386d11 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -757,39 +757,6 @@ static int i915_kick_out_firmware_fb(struct drm_i915_private *dev_priv)
return ret;
}
-#if !defined(CONFIG_VGA_CONSOLE)
-static int i915_kick_out_vgacon(struct drm_i915_private *dev_priv)
-{
- return 0;
-}
-#elif !defined(CONFIG_DUMMY_CONSOLE)
-static int i915_kick_out_vgacon(struct drm_i915_private *dev_priv)
-{
- return -ENODEV;
-}
-#else
-static int i915_kick_out_vgacon(struct drm_i915_private *dev_priv)
-{
- int ret = 0;
-
- DRM_INFO("Replacing VGA console driver\n");
-
- console_lock();
- if (con_is_bound(&vga_con))
- ret = do_take_over_console(&dummy_con, 0, MAX_NR_CONSOLES - 1, 1);
- if (ret == 0) {
- ret = do_unregister_con_driver(&vga_con);
-
- /* Ignore "already unregistered". */
- if (ret == -ENODEV)
- ret = 0;
- }
- console_unlock();
-
- return ret;
-}
-#endif
-
static void intel_init_dpio(struct drm_i915_private *dev_priv)
{
/*
@@ -1420,7 +1387,7 @@ static int i915_driver_init_hw(struct drm_i915_private *dev_priv)
goto err_ggtt;
}
- ret = i915_kick_out_vgacon(dev_priv);
+ ret = vga_remove_vgacon(pdev);
if (ret) {
DRM_ERROR("failed to remove conflicting VGA console\n");
goto err_ggtt;
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 9adc7bb9e69c..a67a63b5aa84 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -2346,7 +2346,8 @@ static inline unsigned int i915_sg_segment_size(void)
INTEL_DEVID(dev_priv) == 0x5915 || \
INTEL_DEVID(dev_priv) == 0x591E)
#define IS_AML_ULX(dev_priv) (INTEL_DEVID(dev_priv) == 0x591C || \
- INTEL_DEVID(dev_priv) == 0x87C0)
+ INTEL_DEVID(dev_priv) == 0x87C0 || \
+ INTEL_DEVID(dev_priv) == 0x87CA)
#define IS_SKL_GT2(dev_priv) (IS_SKYLAKE(dev_priv) && \
INTEL_INFO(dev_priv)->gt == 2)
#define IS_SKL_GT3(dev_priv) (IS_SKYLAKE(dev_priv) && \
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index 6728ea5c71d4..8558e81fdc2a 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -1688,7 +1688,8 @@ __vma_matches(struct vm_area_struct *vma, struct file *filp,
if (vma->vm_file != filp)
return false;
- return vma->vm_start == addr && (vma->vm_end - vma->vm_start) == size;
+ return vma->vm_start == addr &&
+ (vma->vm_end - vma->vm_start) == PAGE_ALIGN(size);
}
/**
@@ -1733,8 +1734,13 @@ i915_gem_mmap_ioctl(struct drm_device *dev, void *data,
* pages from.
*/
if (!obj->base.filp) {
- i915_gem_object_put(obj);
- return -ENXIO;
+ addr = -ENXIO;
+ goto err;
+ }
+
+ if (range_overflows(args->offset, args->size, (u64)obj->base.size)) {
+ addr = -EINVAL;
+ goto err;
}
addr = vm_mmap(obj->base.filp, 0, args->size,
@@ -1748,8 +1754,8 @@ i915_gem_mmap_ioctl(struct drm_device *dev, void *data,
struct vm_area_struct *vma;
if (down_write_killable(&mm->mmap_sem)) {
- i915_gem_object_put(obj);
- return -EINTR;
+ addr = -EINTR;
+ goto err;
}
vma = find_vma(mm, addr);
if (vma && __vma_matches(vma, obj->base.filp, addr, args->size))
@@ -1767,12 +1773,10 @@ i915_gem_mmap_ioctl(struct drm_device *dev, void *data,
i915_gem_object_put(obj);
args->addr_ptr = (u64)addr;
-
return 0;
err:
i915_gem_object_put(obj);
-
return addr;
}
diff --git a/drivers/gpu/drm/i915/i915_gpu_error.c b/drivers/gpu/drm/i915/i915_gpu_error.c
index 9a65341fec09..aa6791255252 100644
--- a/drivers/gpu/drm/i915/i915_gpu_error.c
+++ b/drivers/gpu/drm/i915/i915_gpu_error.c
@@ -1721,7 +1721,7 @@ error_msg(struct i915_gpu_state *error, unsigned long engines, const char *msg)
i915_error_generate_code(error, engines));
if (engines) {
/* Just show the first executing process, more is confusing */
- i = ffs(engines);
+ i = __ffs(engines);
len += scnprintf(error->error_msg + len,
sizeof(error->error_msg) - len,
", in %s [%d]",
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index 638a586469f9..047855dd8c6b 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -2863,7 +2863,7 @@ enum i915_power_well_id {
#define GEN11_GT_VEBOX_VDBOX_DISABLE _MMIO(0x9140)
#define GEN11_GT_VDBOX_DISABLE_MASK 0xff
#define GEN11_GT_VEBOX_DISABLE_SHIFT 16
-#define GEN11_GT_VEBOX_DISABLE_MASK (0xff << GEN11_GT_VEBOX_DISABLE_SHIFT)
+#define GEN11_GT_VEBOX_DISABLE_MASK (0x0f << GEN11_GT_VEBOX_DISABLE_SHIFT)
#define GEN11_EU_DISABLE _MMIO(0x9134)
#define GEN11_EU_DIS_MASK 0xFF
@@ -9243,7 +9243,7 @@ enum skl_power_gate {
#define TRANS_DDI_FUNC_CTL2(tran) _MMIO_TRANS2(tran, \
_TRANS_DDI_FUNC_CTL2_A)
#define PORT_SYNC_MODE_ENABLE (1 << 4)
-#define PORT_SYNC_MODE_MASTER_SELECT(x) ((x) < 0)
+#define PORT_SYNC_MODE_MASTER_SELECT(x) ((x) << 0)
#define PORT_SYNC_MODE_MASTER_SELECT_MASK (0x7 << 0)
#define PORT_SYNC_MODE_MASTER_SELECT_SHIFT 0
diff --git a/drivers/gpu/drm/i915/i915_scheduler.c b/drivers/gpu/drm/i915/i915_scheduler.c
index d01683167c77..8bc042551692 100644
--- a/drivers/gpu/drm/i915/i915_scheduler.c
+++ b/drivers/gpu/drm/i915/i915_scheduler.c
@@ -223,8 +223,14 @@ out:
return &p->requests[idx];
}
+struct sched_cache {
+ struct list_head *priolist;
+};
+
static struct intel_engine_cs *
-sched_lock_engine(struct i915_sched_node *node, struct intel_engine_cs *locked)
+sched_lock_engine(const struct i915_sched_node *node,
+ struct intel_engine_cs *locked,
+ struct sched_cache *cache)
{
struct intel_engine_cs *engine = node_to_request(node)->engine;
@@ -232,6 +238,7 @@ sched_lock_engine(struct i915_sched_node *node, struct intel_engine_cs *locked)
if (engine != locked) {
spin_unlock(&locked->timeline.lock);
+ memset(cache, 0, sizeof(*cache));
spin_lock(&engine->timeline.lock);
}
@@ -253,11 +260,11 @@ static bool inflight(const struct i915_request *rq,
static void __i915_schedule(struct i915_request *rq,
const struct i915_sched_attr *attr)
{
- struct list_head *uninitialized_var(pl);
- struct intel_engine_cs *engine, *last;
+ struct intel_engine_cs *engine;
struct i915_dependency *dep, *p;
struct i915_dependency stack;
const int prio = attr->priority;
+ struct sched_cache cache;
LIST_HEAD(dfs);
/* Needed in order to use the temporary link inside i915_dependency */
@@ -328,7 +335,7 @@ static void __i915_schedule(struct i915_request *rq,
__list_del_entry(&stack.dfs_link);
}
- last = NULL;
+ memset(&cache, 0, sizeof(cache));
engine = rq->engine;
spin_lock_irq(&engine->timeline.lock);
@@ -338,7 +345,7 @@ static void __i915_schedule(struct i915_request *rq,
INIT_LIST_HEAD(&dep->dfs_link);
- engine = sched_lock_engine(node, engine);
+ engine = sched_lock_engine(node, engine, &cache);
lockdep_assert_held(&engine->timeline.lock);
/* Recheck after acquiring the engine->timeline.lock */
@@ -347,11 +354,11 @@ static void __i915_schedule(struct i915_request *rq,
node->attr.priority = prio;
if (!list_empty(&node->link)) {
- if (last != engine) {
- pl = i915_sched_lookup_priolist(engine, prio);
- last = engine;
- }
- list_move_tail(&node->link, pl);
+ if (!cache.priolist)
+ cache.priolist =
+ i915_sched_lookup_priolist(engine,
+ prio);
+ list_move_tail(&node->link, cache.priolist);
} else {
/*
* If the request is not in the priolist queue because
diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c
index b508d8a735e0..4364f42cac6b 100644
--- a/drivers/gpu/drm/i915/intel_bios.c
+++ b/drivers/gpu/drm/i915/intel_bios.c
@@ -1673,6 +1673,7 @@ init_vbt_missing_defaults(struct drm_i915_private *dev_priv)
info->supports_dvi = (port != PORT_A && port != PORT_E);
info->supports_hdmi = info->supports_dvi;
info->supports_dp = (port != PORT_E);
+ info->supports_edp = (port == PORT_A);
}
}
diff --git a/drivers/gpu/drm/i915/intel_breadcrumbs.c b/drivers/gpu/drm/i915/intel_breadcrumbs.c
index cacaa1d04d17..09ed90c0ba00 100644
--- a/drivers/gpu/drm/i915/intel_breadcrumbs.c
+++ b/drivers/gpu/drm/i915/intel_breadcrumbs.c
@@ -106,16 +106,6 @@ bool intel_engine_breadcrumbs_irq(struct intel_engine_cs *engine)
GEM_BUG_ON(!test_bit(I915_FENCE_FLAG_SIGNAL,
&rq->fence.flags));
- clear_bit(I915_FENCE_FLAG_SIGNAL, &rq->fence.flags);
-
- /*
- * We may race with direct invocation of
- * dma_fence_signal(), e.g. i915_request_retire(),
- * in which case we can skip processing it ourselves.
- */
- if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT,
- &rq->fence.flags))
- continue;
/*
* Queue for execution after dropping the signaling
@@ -123,6 +113,14 @@ bool intel_engine_breadcrumbs_irq(struct intel_engine_cs *engine)
* more signalers to the same context or engine.
*/
i915_request_get(rq);
+
+ /*
+ * We may race with direct invocation of
+ * dma_fence_signal(), e.g. i915_request_retire(),
+ * so we need to acquire our reference to the request
+ * before we cancel the breadcrumb.
+ */
+ clear_bit(I915_FENCE_FLAG_SIGNAL, &rq->fence.flags);
list_add_tail(&rq->signal_link, &signal);
}
diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c
index ca705546a0ab..14d580cdefd3 100644
--- a/drivers/gpu/drm/i915/intel_ddi.c
+++ b/drivers/gpu/drm/i915/intel_ddi.c
@@ -3568,6 +3568,13 @@ static void intel_ddi_update_pipe(struct intel_encoder *encoder,
{
if (!intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI))
intel_ddi_update_pipe_dp(encoder, crtc_state, conn_state);
+
+ if (conn_state->content_protection ==
+ DRM_MODE_CONTENT_PROTECTION_DESIRED)
+ intel_hdcp_enable(to_intel_connector(conn_state->connector));
+ else if (conn_state->content_protection ==
+ DRM_MODE_CONTENT_PROTECTION_UNDESIRED)
+ intel_hdcp_disable(to_intel_connector(conn_state->connector));
}
static void intel_ddi_set_fia_lane_count(struct intel_encoder *encoder,
@@ -3962,12 +3969,7 @@ static int modeset_pipe(struct drm_crtc *crtc,
goto out;
ret = drm_atomic_commit(state);
- if (ret)
- goto out;
-
- return 0;
-
- out:
+out:
drm_atomic_state_put(state);
return ret;
diff --git a/drivers/gpu/drm/i915/selftests/i915_gem_context.c b/drivers/gpu/drm/i915/selftests/i915_gem_context.c
index d00d0bb07784..7eb58a9d1319 100644
--- a/drivers/gpu/drm/i915/selftests/i915_gem_context.c
+++ b/drivers/gpu/drm/i915/selftests/i915_gem_context.c
@@ -710,47 +710,45 @@ __sseu_prepare(struct drm_i915_private *i915,
unsigned int flags,
struct i915_gem_context *ctx,
struct intel_engine_cs *engine,
- struct igt_spinner **spin_out)
+ struct igt_spinner **spin)
{
- int ret = 0;
-
- if (flags & (TEST_BUSY | TEST_RESET)) {
- struct igt_spinner *spin;
- struct i915_request *rq;
+ struct i915_request *rq;
+ int ret;
- spin = kzalloc(sizeof(*spin), GFP_KERNEL);
- if (!spin) {
- ret = -ENOMEM;
- goto out;
- }
+ *spin = NULL;
+ if (!(flags & (TEST_BUSY | TEST_RESET)))
+ return 0;
- ret = igt_spinner_init(spin, i915);
- if (ret)
- return ret;
+ *spin = kzalloc(sizeof(**spin), GFP_KERNEL);
+ if (!*spin)
+ return -ENOMEM;
- rq = igt_spinner_create_request(spin, ctx, engine, MI_NOOP);
- if (IS_ERR(rq)) {
- ret = PTR_ERR(rq);
- igt_spinner_fini(spin);
- kfree(spin);
- goto out;
- }
+ ret = igt_spinner_init(*spin, i915);
+ if (ret)
+ goto err_free;
- i915_request_add(rq);
+ rq = igt_spinner_create_request(*spin, ctx, engine, MI_NOOP);
+ if (IS_ERR(rq)) {
+ ret = PTR_ERR(rq);
+ goto err_fini;
+ }
- if (!igt_wait_for_spinner(spin, rq)) {
- pr_err("%s: Spinner failed to start!\n", name);
- igt_spinner_end(spin);
- igt_spinner_fini(spin);
- kfree(spin);
- ret = -ETIMEDOUT;
- goto out;
- }
+ i915_request_add(rq);
- *spin_out = spin;
+ if (!igt_wait_for_spinner(*spin, rq)) {
+ pr_err("%s: Spinner failed to start!\n", name);
+ ret = -ETIMEDOUT;
+ goto err_end;
}
-out:
+ return 0;
+
+err_end:
+ igt_spinner_end(*spin);
+err_fini:
+ igt_spinner_fini(*spin);
+err_free:
+ kfree(fetch_and_zero(spin));
return ret;
}
@@ -897,22 +895,23 @@ __sseu_test(struct drm_i915_private *i915,
ret = __sseu_prepare(i915, name, flags, ctx, engine, &spin);
if (ret)
- goto out;
+ goto out_context;
ret = __i915_gem_context_reconfigure_sseu(ctx, engine, sseu);
if (ret)
- goto out;
+ goto out_spin;
ret = __sseu_finish(i915, name, flags, ctx, kctx, engine, obj,
hweight32(sseu.slice_mask), spin);
-out:
+out_spin:
if (spin) {
igt_spinner_end(spin);
igt_spinner_fini(spin);
kfree(spin);
}
+out_context:
kernel_context_close(kctx);
return ret;
diff --git a/drivers/gpu/drm/i915/selftests/i915_gem_evict.c b/drivers/gpu/drm/i915/selftests/i915_gem_evict.c
index 32dce7176f63..b9b0ea4e2404 100644
--- a/drivers/gpu/drm/i915/selftests/i915_gem_evict.c
+++ b/drivers/gpu/drm/i915/selftests/i915_gem_evict.c
@@ -455,7 +455,7 @@ static int igt_evict_contexts(void *arg)
struct i915_gem_context *ctx;
ctx = live_context(i915, file);
- if (!ctx)
+ if (IS_ERR(ctx))
break;
/* We will need some GGTT space for the rq's context */
diff --git a/drivers/gpu/drm/meson/meson_drv.c b/drivers/gpu/drm/meson/meson_drv.c
index 2281ed3eb774..8a4ebcb6405c 100644
--- a/drivers/gpu/drm/meson/meson_drv.c
+++ b/drivers/gpu/drm/meson/meson_drv.c
@@ -337,12 +337,14 @@ static int meson_drv_bind_master(struct device *dev, bool has_components)
ret = drm_dev_register(drm, 0);
if (ret)
- goto free_drm;
+ goto uninstall_irq;
drm_fbdev_generic_setup(drm, 32);
return 0;
+uninstall_irq:
+ drm_irq_uninstall(drm);
free_drm:
drm_dev_put(drm);
@@ -356,8 +358,8 @@ static int meson_drv_bind(struct device *dev)
static void meson_drv_unbind(struct device *dev)
{
- struct drm_device *drm = dev_get_drvdata(dev);
- struct meson_drm *priv = drm->dev_private;
+ struct meson_drm *priv = dev_get_drvdata(dev);
+ struct drm_device *drm = priv->drm;
if (priv->canvas) {
meson_canvas_free(priv->canvas, priv->canvas_id_osd1);
@@ -367,6 +369,7 @@ static void meson_drv_unbind(struct device *dev)
}
drm_dev_unregister(drm);
+ drm_irq_uninstall(drm);
drm_kms_helper_poll_fini(drm);
drm_mode_config_cleanup(drm);
drm_dev_put(drm);
diff --git a/drivers/gpu/drm/meson/meson_dw_hdmi.c b/drivers/gpu/drm/meson/meson_dw_hdmi.c
index e28814f4ea6c..563953ec6ad0 100644
--- a/drivers/gpu/drm/meson/meson_dw_hdmi.c
+++ b/drivers/gpu/drm/meson/meson_dw_hdmi.c
@@ -569,7 +569,8 @@ dw_hdmi_mode_valid(struct drm_connector *connector,
DRM_DEBUG_DRIVER("Modeline " DRM_MODE_FMT "\n", DRM_MODE_ARG(mode));
/* If sink max TMDS clock, we reject the mode */
- if (mode->clock > connector->display_info.max_tmds_clock)
+ if (connector->display_info.max_tmds_clock &&
+ mode->clock > connector->display_info.max_tmds_clock)
return MODE_BAD;
/* Check against non-VIC supported modes */
diff --git a/drivers/gpu/drm/nouveau/nouveau_debugfs.c b/drivers/gpu/drm/nouveau/nouveau_debugfs.c
index 88a52f6b39fe..7dfbbbc1beea 100644
--- a/drivers/gpu/drm/nouveau/nouveau_debugfs.c
+++ b/drivers/gpu/drm/nouveau/nouveau_debugfs.c
@@ -181,7 +181,7 @@ nouveau_debugfs_pstate_set(struct file *file, const char __user *ubuf,
}
ret = pm_runtime_get_sync(drm->dev);
- if (IS_ERR_VALUE(ret) && ret != -EACCES)
+ if (ret < 0 && ret != -EACCES)
return ret;
ret = nvif_mthd(ctrl, NVIF_CONTROL_PSTATE_USER, &args, sizeof(args));
pm_runtime_put_autosuspend(drm->dev);
diff --git a/drivers/gpu/drm/nouveau/nouveau_dmem.c b/drivers/gpu/drm/nouveau/nouveau_dmem.c
index 8be7a83ced9b..40c47d6a7d78 100644
--- a/drivers/gpu/drm/nouveau/nouveau_dmem.c
+++ b/drivers/gpu/drm/nouveau/nouveau_dmem.c
@@ -100,12 +100,10 @@ static void
nouveau_dmem_free(struct hmm_devmem *devmem, struct page *page)
{
struct nouveau_dmem_chunk *chunk;
- struct nouveau_drm *drm;
unsigned long idx;
chunk = (void *)hmm_devmem_page_get_drvdata(page);
idx = page_to_pfn(page) - chunk->pfn_first;
- drm = chunk->drm;
/*
* FIXME:
@@ -261,7 +259,7 @@ static const struct migrate_vma_ops nouveau_dmem_fault_migrate_ops = {
.finalize_and_map = nouveau_dmem_fault_finalize_and_map,
};
-static int
+static vm_fault_t
nouveau_dmem_fault(struct hmm_devmem *devmem,
struct vm_area_struct *vma,
unsigned long addr,
@@ -456,11 +454,6 @@ nouveau_dmem_resume(struct nouveau_drm *drm)
/* FIXME handle pin failure */
WARN_ON(ret);
}
- list_for_each_entry (chunk, &drm->dmem->chunk_empty, list) {
- ret = nouveau_bo_pin(chunk->bo, TTM_PL_FLAG_VRAM, false);
- /* FIXME handle pin failure */
- WARN_ON(ret);
- }
mutex_unlock(&drm->dmem->mutex);
}
@@ -479,9 +472,6 @@ nouveau_dmem_suspend(struct nouveau_drm *drm)
list_for_each_entry (chunk, &drm->dmem->chunk_full, list) {
nouveau_bo_unpin(chunk->bo);
}
- list_for_each_entry (chunk, &drm->dmem->chunk_empty, list) {
- nouveau_bo_unpin(chunk->bo);
- }
mutex_unlock(&drm->dmem->mutex);
}
@@ -623,7 +613,7 @@ nouveau_dmem_init(struct nouveau_drm *drm)
*/
drm->dmem->devmem = hmm_devmem_add(&nouveau_dmem_devmem_ops,
device, size);
- if (drm->dmem->devmem == NULL) {
+ if (IS_ERR(drm->dmem->devmem)) {
kfree(drm->dmem);
drm->dmem = NULL;
return;
diff --git a/drivers/gpu/drm/qxl/qxl_drv.c b/drivers/gpu/drm/qxl/qxl_drv.c
index bb81e310eb6d..578d867a81d5 100644
--- a/drivers/gpu/drm/qxl/qxl_drv.c
+++ b/drivers/gpu/drm/qxl/qxl_drv.c
@@ -79,6 +79,10 @@ qxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
if (ret)
goto free_dev;
+ ret = drm_fb_helper_remove_conflicting_pci_framebuffers(pdev, 0, "qxl");
+ if (ret)
+ goto disable_pci;
+
ret = qxl_device_init(qdev, &qxl_driver, pdev);
if (ret)
goto disable_pci;
@@ -94,7 +98,6 @@ qxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
if (ret)
goto modeset_cleanup;
- drm_fb_helper_remove_conflicting_pci_framebuffers(pdev, 0, "qxl");
drm_fbdev_generic_setup(&qdev->ddev, 32);
return 0;
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index c7d4c6073ea5..0d4ade9d4722 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -541,6 +541,18 @@ static void vop_core_clks_disable(struct vop *vop)
clk_disable(vop->hclk);
}
+static void vop_win_disable(struct vop *vop, const struct vop_win_data *win)
+{
+ if (win->phy->scl && win->phy->scl->ext) {
+ VOP_SCL_SET_EXT(vop, win, yrgb_hor_scl_mode, SCALE_NONE);
+ VOP_SCL_SET_EXT(vop, win, yrgb_ver_scl_mode, SCALE_NONE);
+ VOP_SCL_SET_EXT(vop, win, cbcr_hor_scl_mode, SCALE_NONE);
+ VOP_SCL_SET_EXT(vop, win, cbcr_ver_scl_mode, SCALE_NONE);
+ }
+
+ VOP_WIN_SET(vop, win, enable, 0);
+}
+
static int vop_enable(struct drm_crtc *crtc)
{
struct vop *vop = to_vop(crtc);
@@ -586,7 +598,7 @@ static int vop_enable(struct drm_crtc *crtc)
struct vop_win *vop_win = &vop->win[i];
const struct vop_win_data *win = vop_win->data;
- VOP_WIN_SET(vop, win, enable, 0);
+ vop_win_disable(vop, win);
}
spin_unlock(&vop->reg_lock);
@@ -735,7 +747,7 @@ static void vop_plane_atomic_disable(struct drm_plane *plane,
spin_lock(&vop->reg_lock);
- VOP_WIN_SET(vop, win, enable, 0);
+ vop_win_disable(vop, win);
spin_unlock(&vop->reg_lock);
}
@@ -1622,7 +1634,7 @@ static int vop_initial(struct vop *vop)
int channel = i * 2 + 1;
VOP_WIN_SET(vop, win, channel, (channel + 1) << 4 | channel);
- VOP_WIN_SET(vop, win, enable, 0);
+ vop_win_disable(vop, win);
VOP_WIN_SET(vop, win, gate, 1);
}
diff --git a/drivers/gpu/drm/tegra/hub.c b/drivers/gpu/drm/tegra/hub.c
index ba9b3cfb8c3d..b3436c2aed68 100644
--- a/drivers/gpu/drm/tegra/hub.c
+++ b/drivers/gpu/drm/tegra/hub.c
@@ -378,14 +378,16 @@ static int tegra_shared_plane_atomic_check(struct drm_plane *plane,
static void tegra_shared_plane_atomic_disable(struct drm_plane *plane,
struct drm_plane_state *old_state)
{
- struct tegra_dc *dc = to_tegra_dc(old_state->crtc);
struct tegra_plane *p = to_tegra_plane(plane);
+ struct tegra_dc *dc;
u32 value;
/* rien ne va plus */
if (!old_state || !old_state->crtc)
return;
+ dc = to_tegra_dc(old_state->crtc);
+
/*
* XXX Legacy helpers seem to sometimes call ->atomic_disable() even
* on planes that are already disabled. Make sure we fallback to the
diff --git a/drivers/gpu/drm/tegra/vic.c b/drivers/gpu/drm/tegra/vic.c
index 39bfed9623de..982ce37ecde1 100644
--- a/drivers/gpu/drm/tegra/vic.c
+++ b/drivers/gpu/drm/tegra/vic.c
@@ -106,6 +106,7 @@ static int vic_boot(struct vic *vic)
if (vic->booted)
return 0;
+#ifdef CONFIG_IOMMU_API
if (vic->config->supports_sid) {
struct iommu_fwspec *spec = dev_iommu_fwspec_get(vic->dev);
u32 value;
@@ -121,6 +122,7 @@ static int vic_boot(struct vic *vic)
vic_writel(vic, value, VIC_THI_STREAMID1);
}
}
+#endif
/* setup clockgating registers */
vic_writel(vic, CG_IDLE_CG_DLY_CNT(4) |
diff --git a/drivers/gpu/drm/udl/udl_connector.c b/drivers/gpu/drm/udl/udl_connector.c
index 66885c24590f..c1bd5e3d9e4a 100644
--- a/drivers/gpu/drm/udl/udl_connector.c
+++ b/drivers/gpu/drm/udl/udl_connector.c
@@ -18,18 +18,19 @@
#include "udl_connector.h"
#include "udl_drv.h"
-static bool udl_get_edid_block(struct udl_device *udl, int block_idx,
- u8 *buff)
+static int udl_get_edid_block(void *data, u8 *buf, unsigned int block,
+ size_t len)
{
int ret, i;
u8 *read_buff;
+ struct udl_device *udl = data;
read_buff = kmalloc(2, GFP_KERNEL);
if (!read_buff)
- return false;
+ return -1;
- for (i = 0; i < EDID_LENGTH; i++) {
- int bval = (i + block_idx * EDID_LENGTH) << 8;
+ for (i = 0; i < len; i++) {
+ int bval = (i + block * EDID_LENGTH) << 8;
ret = usb_control_msg(udl->udev,
usb_rcvctrlpipe(udl->udev, 0),
(0x02), (0x80 | (0x02 << 5)), bval,
@@ -37,60 +38,13 @@ static bool udl_get_edid_block(struct udl_device *udl, int block_idx,
if (ret < 1) {
DRM_ERROR("Read EDID byte %d failed err %x\n", i, ret);
kfree(read_buff);
- return false;
+ return -1;
}
- buff[i] = read_buff[1];
+ buf[i] = read_buff[1];
}
kfree(read_buff);
- return true;
-}
-
-static bool udl_get_edid(struct udl_device *udl, u8 **result_buff,
- int *result_buff_size)
-{
- int i, extensions;
- u8 *block_buff = NULL, *buff_ptr;
-
- block_buff = kmalloc(EDID_LENGTH, GFP_KERNEL);
- if (block_buff == NULL)
- return false;
-
- if (udl_get_edid_block(udl, 0, block_buff) &&
- memchr_inv(block_buff, 0, EDID_LENGTH)) {
- extensions = ((struct edid *)block_buff)->extensions;
- if (extensions > 0) {
- /* we have to read all extensions one by one */
- *result_buff_size = EDID_LENGTH * (extensions + 1);
- *result_buff = kmalloc(*result_buff_size, GFP_KERNEL);
- buff_ptr = *result_buff;
- if (buff_ptr == NULL) {
- kfree(block_buff);
- return false;
- }
- memcpy(buff_ptr, block_buff, EDID_LENGTH);
- kfree(block_buff);
- buff_ptr += EDID_LENGTH;
- for (i = 1; i < extensions; ++i) {
- if (udl_get_edid_block(udl, i, buff_ptr)) {
- buff_ptr += EDID_LENGTH;
- } else {
- kfree(*result_buff);
- *result_buff = NULL;
- return false;
- }
- }
- return true;
- }
- /* we have only base edid block */
- *result_buff = block_buff;
- *result_buff_size = EDID_LENGTH;
- return true;
- }
-
- kfree(block_buff);
-
- return false;
+ return 0;
}
static int udl_get_modes(struct drm_connector *connector)
@@ -122,8 +76,6 @@ static enum drm_mode_status udl_mode_valid(struct drm_connector *connector,
static enum drm_connector_status
udl_detect(struct drm_connector *connector, bool force)
{
- u8 *edid_buff = NULL;
- int edid_buff_size = 0;
struct udl_device *udl = connector->dev->dev_private;
struct udl_drm_connector *udl_connector =
container_of(connector,
@@ -136,12 +88,10 @@ udl_detect(struct drm_connector *connector, bool force)
udl_connector->edid = NULL;
}
-
- if (!udl_get_edid(udl, &edid_buff, &edid_buff_size))
+ udl_connector->edid = drm_do_get_edid(connector, udl_get_edid_block, udl);
+ if (!udl_connector->edid)
return connector_status_disconnected;
- udl_connector->edid = (struct edid *)edid_buff;
-
return connector_status_connected;
}
diff --git a/drivers/gpu/drm/udl/udl_gem.c b/drivers/gpu/drm/udl/udl_gem.c
index d5a23295dd80..bb7b58407039 100644
--- a/drivers/gpu/drm/udl/udl_gem.c
+++ b/drivers/gpu/drm/udl/udl_gem.c
@@ -224,7 +224,7 @@ int udl_gem_mmap(struct drm_file *file, struct drm_device *dev,
*offset = drm_vma_node_offset_addr(&gobj->base.vma_node);
out:
- drm_gem_object_put(&gobj->base);
+ drm_gem_object_put_unlocked(&gobj->base);
unlock:
mutex_unlock(&udl->gem_lock);
return ret;
diff --git a/drivers/gpu/drm/vgem/vgem_drv.c b/drivers/gpu/drm/vgem/vgem_drv.c
index 5930facd6d2d..11a8f99ba18c 100644
--- a/drivers/gpu/drm/vgem/vgem_drv.c
+++ b/drivers/gpu/drm/vgem/vgem_drv.c
@@ -191,13 +191,9 @@ static struct drm_gem_object *vgem_gem_create(struct drm_device *dev,
ret = drm_gem_handle_create(file, &obj->base, handle);
drm_gem_object_put_unlocked(&obj->base);
if (ret)
- goto err;
+ return ERR_PTR(ret);
return &obj->base;
-
-err:
- __vgem_gem_destroy(obj);
- return ERR_PTR(ret);
}
static int vgem_gem_dumb_create(struct drm_file *file, struct drm_device *dev,
diff --git a/drivers/gpu/drm/vkms/vkms_gem.c b/drivers/gpu/drm/vkms/vkms_gem.c
index 138b0bb325cf..69048e73377d 100644
--- a/drivers/gpu/drm/vkms/vkms_gem.c
+++ b/drivers/gpu/drm/vkms/vkms_gem.c
@@ -111,11 +111,8 @@ struct drm_gem_object *vkms_gem_create(struct drm_device *dev,
ret = drm_gem_handle_create(file, &obj->gem, handle);
drm_gem_object_put_unlocked(&obj->gem);
- if (ret) {
- drm_gem_object_release(&obj->gem);
- kfree(obj);
+ if (ret)
return ERR_PTR(ret);
- }
return &obj->gem;
}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
index b913a56f3426..2a9112515f46 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
@@ -564,11 +564,9 @@ static int vmw_fb_set_par(struct fb_info *info)
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC)
};
- struct drm_display_mode *old_mode;
struct drm_display_mode *mode;
int ret;
- old_mode = par->set_mode;
mode = drm_mode_duplicate(vmw_priv->dev, &new_mode);
if (!mode) {
DRM_ERROR("Could not create new fb mode.\n");
@@ -579,11 +577,7 @@ static int vmw_fb_set_par(struct fb_info *info)
mode->vdisplay = var->yres;
vmw_guess_mode_timing(mode);
- if (old_mode && drm_mode_equal(old_mode, mode)) {
- drm_mode_destroy(vmw_priv->dev, mode);
- mode = old_mode;
- old_mode = NULL;
- } else if (!vmw_kms_validate_mode_vram(vmw_priv,
+ if (!vmw_kms_validate_mode_vram(vmw_priv,
mode->hdisplay *
DIV_ROUND_UP(var->bits_per_pixel, 8),
mode->vdisplay)) {
@@ -620,8 +614,8 @@ static int vmw_fb_set_par(struct fb_info *info)
schedule_delayed_work(&par->local_work, 0);
out_unlock:
- if (old_mode)
- drm_mode_destroy(vmw_priv->dev, old_mode);
+ if (par->set_mode)
+ drm_mode_destroy(vmw_priv->dev, par->set_mode);
par->set_mode = mode;
mutex_unlock(&par->bo_mutex);
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c b/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c
index b93c558dd86e..7da752ca1c34 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c
@@ -57,7 +57,7 @@ static int vmw_gmrid_man_get_node(struct ttm_mem_type_manager *man,
id = ida_alloc_max(&gman->gmr_ida, gman->max_gmr_ids - 1, GFP_KERNEL);
if (id < 0)
- return id;
+ return (id != -ENOMEM ? 0 : id);
spin_lock(&gman->lock);
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c
index 31786b200afc..a3357ff7540d 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c
@@ -311,7 +311,13 @@ static dma_addr_t __vmw_piter_dma_addr(struct vmw_piter *viter)
static dma_addr_t __vmw_piter_sg_addr(struct vmw_piter *viter)
{
- return sg_page_iter_dma_address(&viter->iter);
+ /*
+ * FIXME: This driver wrongly mixes DMA and CPU SG list iteration and
+ * needs revision. See
+ * https://lore.kernel.org/lkml/20190104223531.GA1705@ziepe.ca/
+ */
+ return sg_page_iter_dma_address(
+ container_of(&viter->iter, struct sg_dma_page_iter, base));
}
diff --git a/drivers/gpu/ipu-v3/ipu-cpmem.c b/drivers/gpu/ipu-v3/ipu-cpmem.c
index 163fadb8a33a..d047a6867c59 100644
--- a/drivers/gpu/ipu-v3/ipu-cpmem.c
+++ b/drivers/gpu/ipu-v3/ipu-cpmem.c
@@ -277,9 +277,10 @@ void ipu_cpmem_set_uv_offset(struct ipuv3_channel *ch, u32 u_off, u32 v_off)
}
EXPORT_SYMBOL_GPL(ipu_cpmem_set_uv_offset);
-void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride)
+void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride,
+ u32 pixelformat)
{
- u32 ilo, sly;
+ u32 ilo, sly, sluv;
if (stride < 0) {
stride = -stride;
@@ -290,9 +291,30 @@ void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride)
sly = (stride * 2) - 1;
+ switch (pixelformat) {
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_YVU420:
+ sluv = stride / 2 - 1;
+ break;
+ case V4L2_PIX_FMT_NV12:
+ sluv = stride - 1;
+ break;
+ case V4L2_PIX_FMT_YUV422P:
+ sluv = stride - 1;
+ break;
+ case V4L2_PIX_FMT_NV16:
+ sluv = stride * 2 - 1;
+ break;
+ default:
+ sluv = 0;
+ break;
+ }
+
ipu_ch_param_write_field(ch, IPU_FIELD_SO, 1);
ipu_ch_param_write_field(ch, IPU_FIELD_ILO, ilo);
ipu_ch_param_write_field(ch, IPU_FIELD_SLY, sly);
+ if (sluv)
+ ipu_ch_param_write_field(ch, IPU_FIELD_SLUV, sluv);
};
EXPORT_SYMBOL_GPL(ipu_cpmem_interlaced_scan);
diff --git a/drivers/gpu/ipu-v3/ipu-csi.c b/drivers/gpu/ipu-v3/ipu-csi.c
index aa0e30a2ba18..d1e575571a8d 100644
--- a/drivers/gpu/ipu-v3/ipu-csi.c
+++ b/drivers/gpu/ipu-v3/ipu-csi.c
@@ -325,12 +325,21 @@ static int mbus_code_to_bus_cfg(struct ipu_csi_bus_config *cfg, u32 mbus_code,
return 0;
}
+/* translate alternate field mode based on given standard */
+static inline enum v4l2_field
+ipu_csi_translate_field(enum v4l2_field field, v4l2_std_id std)
+{
+ return (field != V4L2_FIELD_ALTERNATE) ? field :
+ ((std & V4L2_STD_525_60) ?
+ V4L2_FIELD_SEQ_BT : V4L2_FIELD_SEQ_TB);
+}
+
/*
* Fill a CSI bus config struct from mbus_config and mbus_framefmt.
*/
static int fill_csi_bus_cfg(struct ipu_csi_bus_config *csicfg,
- struct v4l2_mbus_config *mbus_cfg,
- struct v4l2_mbus_framefmt *mbus_fmt)
+ const struct v4l2_mbus_config *mbus_cfg,
+ const struct v4l2_mbus_framefmt *mbus_fmt)
{
int ret;
@@ -374,22 +383,76 @@ static int fill_csi_bus_cfg(struct ipu_csi_bus_config *csicfg,
return 0;
}
+static int
+ipu_csi_set_bt_interlaced_codes(struct ipu_csi *csi,
+ const struct v4l2_mbus_framefmt *infmt,
+ const struct v4l2_mbus_framefmt *outfmt,
+ v4l2_std_id std)
+{
+ enum v4l2_field infield, outfield;
+ bool swap_fields;
+
+ /* get translated field type of input and output */
+ infield = ipu_csi_translate_field(infmt->field, std);
+ outfield = ipu_csi_translate_field(outfmt->field, std);
+
+ /*
+ * Write the H-V-F codes the CSI will match against the
+ * incoming data for start/end of active and blanking
+ * field intervals. If input and output field types are
+ * sequential but not the same (one is SEQ_BT and the other
+ * is SEQ_TB), swap the F-bit so that the CSI will capture
+ * field 1 lines before field 0 lines.
+ */
+ swap_fields = (V4L2_FIELD_IS_SEQUENTIAL(infield) &&
+ V4L2_FIELD_IS_SEQUENTIAL(outfield) &&
+ infield != outfield);
+
+ if (!swap_fields) {
+ /*
+ * Field0BlankEnd = 110, Field0BlankStart = 010
+ * Field0ActiveEnd = 100, Field0ActiveStart = 000
+ * Field1BlankEnd = 111, Field1BlankStart = 011
+ * Field1ActiveEnd = 101, Field1ActiveStart = 001
+ */
+ ipu_csi_write(csi, 0x40596 | CSI_CCIR_ERR_DET_EN,
+ CSI_CCIR_CODE_1);
+ ipu_csi_write(csi, 0xD07DF, CSI_CCIR_CODE_2);
+ } else {
+ dev_dbg(csi->ipu->dev, "capture field swap\n");
+
+ /* same as above but with F-bit inverted */
+ ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN,
+ CSI_CCIR_CODE_1);
+ ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2);
+ }
+
+ ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3);
+
+ return 0;
+}
+
+
int ipu_csi_init_interface(struct ipu_csi *csi,
- struct v4l2_mbus_config *mbus_cfg,
- struct v4l2_mbus_framefmt *mbus_fmt)
+ const struct v4l2_mbus_config *mbus_cfg,
+ const struct v4l2_mbus_framefmt *infmt,
+ const struct v4l2_mbus_framefmt *outfmt)
{
struct ipu_csi_bus_config cfg;
unsigned long flags;
u32 width, height, data = 0;
+ v4l2_std_id std;
int ret;
- ret = fill_csi_bus_cfg(&cfg, mbus_cfg, mbus_fmt);
+ ret = fill_csi_bus_cfg(&cfg, mbus_cfg, infmt);
if (ret < 0)
return ret;
/* set default sensor frame width and height */
- width = mbus_fmt->width;
- height = mbus_fmt->height;
+ width = infmt->width;
+ height = infmt->height;
+ if (infmt->field == V4L2_FIELD_ALTERNATE)
+ height *= 2;
/* Set the CSI_SENS_CONF register remaining fields */
data |= cfg.data_width << CSI_SENS_CONF_DATA_WIDTH_SHIFT |
@@ -416,42 +479,22 @@ int ipu_csi_init_interface(struct ipu_csi *csi,
ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3);
break;
case IPU_CSI_CLK_MODE_CCIR656_INTERLACED:
- if (mbus_fmt->width == 720 && mbus_fmt->height == 576) {
- /*
- * PAL case
- *
- * Field0BlankEnd = 0x6, Field0BlankStart = 0x2,
- * Field0ActiveEnd = 0x4, Field0ActiveStart = 0
- * Field1BlankEnd = 0x7, Field1BlankStart = 0x3,
- * Field1ActiveEnd = 0x5, Field1ActiveStart = 0x1
- */
- height = 625; /* framelines for PAL */
-
- ipu_csi_write(csi, 0x40596 | CSI_CCIR_ERR_DET_EN,
- CSI_CCIR_CODE_1);
- ipu_csi_write(csi, 0xD07DF, CSI_CCIR_CODE_2);
- ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3);
- } else if (mbus_fmt->width == 720 && mbus_fmt->height == 480) {
- /*
- * NTSC case
- *
- * Field0BlankEnd = 0x7, Field0BlankStart = 0x3,
- * Field0ActiveEnd = 0x5, Field0ActiveStart = 0x1
- * Field1BlankEnd = 0x6, Field1BlankStart = 0x2,
- * Field1ActiveEnd = 0x4, Field1ActiveStart = 0
- */
- height = 525; /* framelines for NTSC */
-
- ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN,
- CSI_CCIR_CODE_1);
- ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2);
- ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3);
+ if (width == 720 && height == 480) {
+ std = V4L2_STD_NTSC;
+ height = 525;
+ } else if (width == 720 && height == 576) {
+ std = V4L2_STD_PAL;
+ height = 625;
} else {
dev_err(csi->ipu->dev,
- "Unsupported CCIR656 interlaced video mode\n");
- spin_unlock_irqrestore(&csi->lock, flags);
- return -EINVAL;
+ "Unsupported interlaced video mode\n");
+ ret = -EINVAL;
+ goto out_unlock;
}
+
+ ret = ipu_csi_set_bt_interlaced_codes(csi, infmt, outfmt, std);
+ if (ret)
+ goto out_unlock;
break;
case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR:
case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR:
@@ -476,9 +519,10 @@ int ipu_csi_init_interface(struct ipu_csi *csi,
dev_dbg(csi->ipu->dev, "CSI_ACT_FRM_SIZE = 0x%08X\n",
ipu_csi_read(csi, CSI_ACT_FRM_SIZE));
+out_unlock:
spin_unlock_irqrestore(&csi->lock, flags);
- return 0;
+ return ret;
}
EXPORT_SYMBOL_GPL(ipu_csi_init_interface);
diff --git a/drivers/gpu/vga/vgaarb.c b/drivers/gpu/vga/vgaarb.c
index dc8e039bfab5..f2f3ef8af271 100644
--- a/drivers/gpu/vga/vgaarb.c
+++ b/drivers/gpu/vga/vgaarb.c
@@ -48,6 +48,8 @@
#include <linux/miscdevice.h>
#include <linux/slab.h>
#include <linux/screen_info.h>
+#include <linux/vt.h>
+#include <linux/console.h>
#include <linux/uaccess.h>
@@ -168,6 +170,53 @@ void vga_set_default_device(struct pci_dev *pdev)
vga_default = pci_dev_get(pdev);
}
+/**
+ * vga_remove_vgacon - deactivete vga console
+ *
+ * Unbind and unregister vgacon in case pdev is the default vga
+ * device. Can be called by gpu drivers on initialization to make
+ * sure vga register access done by vgacon will not disturb the
+ * device.
+ *
+ * @pdev: pci device.
+ */
+#if !defined(CONFIG_VGA_CONSOLE)
+int vga_remove_vgacon(struct pci_dev *pdev)
+{
+ return 0;
+}
+#elif !defined(CONFIG_DUMMY_CONSOLE)
+int vga_remove_vgacon(struct pci_dev *pdev)
+{
+ return -ENODEV;
+}
+#else
+int vga_remove_vgacon(struct pci_dev *pdev)
+{
+ int ret = 0;
+
+ if (pdev != vga_default)
+ return 0;
+ vgaarb_info(&pdev->dev, "deactivate vga console\n");
+
+ console_lock();
+ if (con_is_bound(&vga_con))
+ ret = do_take_over_console(&dummy_con, 0,
+ MAX_NR_CONSOLES - 1, 1);
+ if (ret == 0) {
+ ret = do_unregister_con_driver(&vga_con);
+
+ /* Ignore "already unregistered". */
+ if (ret == -ENODEV)
+ ret = 0;
+ }
+ console_unlock();
+
+ return ret;
+}
+#endif
+EXPORT_SYMBOL(vga_remove_vgacon);
+
static inline void vga_irq_set_state(struct vga_device *vgadev, bool state)
{
if (vgadev->irq_set_state)
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index aef4a2a690e1..c3c390ca3690 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -150,6 +150,7 @@ config HID_ASUS
tristate "Asus"
depends on LEDS_CLASS
depends on ASUS_WMI || ASUS_WMI=n
+ select POWER_SUPPLY
---help---
Support for Asus notebook built-in keyboard and touchpad via i2c, and
the Asus Republic of Gamers laptop keyboard special keys.
@@ -520,6 +521,7 @@ config HID_LOGITECH
config HID_LOGITECH_DJ
tristate "Logitech Unifying receivers full support"
+ depends on USB_HID
depends on HIDRAW
depends on HID_LOGITECH
select HID_LOGITECH_HIDPP
@@ -1012,6 +1014,22 @@ config HID_UDRAW_PS3
Say Y here if you want to use the THQ uDraw gaming tablet for
the PS3.
+config HID_U2FZERO
+ tristate "U2F Zero LED and RNG support"
+ depends on USB_HID
+ depends on LEDS_CLASS
+ depends on HW_RANDOM
+ help
+ Support for the LED of the U2F Zero device.
+
+ U2F Zero supports custom commands for blinking the LED
+ and getting data from the internal hardware RNG.
+ The internal hardware can be used to feed the enthropy pool.
+
+ U2F Zero only supports blinking its LED, so this driver doesn't
+ allow setting the brightness to anything but 1, which will
+ trigger a single blink and immediately reset to back 0.
+
config HID_WACOM
tristate "Wacom Intuos/Graphire tablet support (USB)"
depends on USB_HID
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index 44b9caea46a7..cc5d827c9164 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -110,6 +110,7 @@ obj-$(CONFIG_HID_THRUSTMASTER) += hid-tmff.o
obj-$(CONFIG_HID_TIVO) += hid-tivo.o
obj-$(CONFIG_HID_TOPSEED) += hid-topseed.o
obj-$(CONFIG_HID_TWINHAN) += hid-twinhan.o
+obj-$(CONFIG_HID_U2FZERO) += hid-u2fzero.o
hid-uclogic-objs := hid-uclogic-core.o \
hid-uclogic-rdesc.o \
hid-uclogic-params.o
@@ -135,3 +136,4 @@ obj-$(CONFIG_USB_KBD) += usbhid/
obj-$(CONFIG_I2C_HID) += i2c-hid/
obj-$(CONFIG_INTEL_ISH_HID) += intel-ish-hid/
+obj-$(INTEL_ISH_FIRMWARE_DOWNLOADER) += intel-ish-hid/
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 9993b692598f..92387fc0bf18 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -30,6 +30,7 @@
#include <linux/vmalloc.h>
#include <linux/sched.h>
#include <linux/semaphore.h>
+#include <linux/async.h>
#include <linux/hid.h>
#include <linux/hiddev.h>
@@ -218,13 +219,14 @@ static unsigned hid_lookup_collection(struct hid_parser *parser, unsigned type)
* Add a usage to the temporary parser table.
*/
-static int hid_add_usage(struct hid_parser *parser, unsigned usage)
+static int hid_add_usage(struct hid_parser *parser, unsigned usage, u8 size)
{
if (parser->local.usage_index >= HID_MAX_USAGES) {
hid_err(parser->device, "usage index exceeded\n");
return -1;
}
parser->local.usage[parser->local.usage_index] = usage;
+ parser->local.usage_size[parser->local.usage_index] = size;
parser->local.collection_index[parser->local.usage_index] =
parser->collection_stack_ptr ?
parser->collection_stack[parser->collection_stack_ptr - 1] : 0;
@@ -486,10 +488,7 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item)
return 0;
}
- if (item->size <= 2)
- data = (parser->global.usage_page << 16) + data;
-
- return hid_add_usage(parser, data);
+ return hid_add_usage(parser, data, item->size);
case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM:
@@ -498,9 +497,6 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item)
return 0;
}
- if (item->size <= 2)
- data = (parser->global.usage_page << 16) + data;
-
parser->local.usage_minimum = data;
return 0;
@@ -511,9 +507,6 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item)
return 0;
}
- if (item->size <= 2)
- data = (parser->global.usage_page << 16) + data;
-
count = data - parser->local.usage_minimum;
if (count + parser->local.usage_index >= HID_MAX_USAGES) {
/*
@@ -533,7 +526,7 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item)
}
for (n = parser->local.usage_minimum; n <= data; n++)
- if (hid_add_usage(parser, n)) {
+ if (hid_add_usage(parser, n, item->size)) {
dbg_hid("hid_add_usage failed\n");
return -1;
}
@@ -548,6 +541,22 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item)
}
/*
+ * Concatenate Usage Pages into Usages where relevant:
+ * As per specification, 6.2.2.8: "When the parser encounters a main item it
+ * concatenates the last declared Usage Page with a Usage to form a complete
+ * usage value."
+ */
+
+static void hid_concatenate_usage_page(struct hid_parser *parser)
+{
+ int i;
+
+ for (i = 0; i < parser->local.usage_index; i++)
+ if (parser->local.usage_size[i] <= 2)
+ parser->local.usage[i] += parser->global.usage_page << 16;
+}
+
+/*
* Process a main item.
*/
@@ -556,6 +565,8 @@ static int hid_parser_main(struct hid_parser *parser, struct hid_item *item)
__u32 data;
int ret;
+ hid_concatenate_usage_page(parser);
+
data = item_udata(item);
switch (item->tag) {
@@ -765,6 +776,8 @@ static int hid_scan_main(struct hid_parser *parser, struct hid_item *item)
__u32 data;
int i;
+ hid_concatenate_usage_page(parser);
+
data = item_udata(item);
switch (item->tag) {
@@ -1301,10 +1314,10 @@ static u32 __extract(u8 *report, unsigned offset, int n)
u32 hid_field_extract(const struct hid_device *hid, u8 *report,
unsigned offset, unsigned n)
{
- if (n > 32) {
- hid_warn(hid, "hid_field_extract() called with n (%d) > 32! (%s)\n",
+ if (n > 256) {
+ hid_warn(hid, "hid_field_extract() called with n (%d) > 256! (%s)\n",
n, current->comm);
- n = 32;
+ n = 256;
}
return __extract(report, offset, n);
@@ -1624,7 +1637,7 @@ static struct hid_report *hid_get_report(struct hid_report_enum *report_enum,
* Implement a generic .request() callback, using .raw_request()
* DO NOT USE in hid drivers directly, but through hid_hw_request instead.
*/
-void __hid_request(struct hid_device *hid, struct hid_report *report,
+int __hid_request(struct hid_device *hid, struct hid_report *report,
int reqtype)
{
char *buf;
@@ -1633,7 +1646,7 @@ void __hid_request(struct hid_device *hid, struct hid_report *report,
buf = hid_alloc_report_buf(report, GFP_KERNEL);
if (!buf)
- return;
+ return -ENOMEM;
len = hid_report_len(report);
@@ -1650,8 +1663,11 @@ void __hid_request(struct hid_device *hid, struct hid_report *report,
if (reqtype == HID_REQ_GET_REPORT)
hid_input_report(hid, report->type, buf, ret, 0);
+ ret = 0;
+
out:
kfree(buf);
+ return ret;
}
EXPORT_SYMBOL_GPL(__hid_request);
@@ -2349,6 +2365,15 @@ int hid_add_device(struct hid_device *hdev)
dev_set_name(&hdev->dev, "%04X:%04X:%04X.%04X", hdev->bus,
hdev->vendor, hdev->product, atomic_inc_return(&id));
+ /*
+ * Try loading the module for the device before the add, so that we do
+ * not first have hid-generic binding only to have it replaced
+ * immediately afterwards with a specialized driver.
+ */
+ if (!current_is_async())
+ request_module("hid:b%04Xg%04Xv%08Xp%08X", hdev->bus,
+ hdev->group, hdev->vendor, hdev->product);
+
hid_debug_register(hdev, dev_name(&hdev->dev));
ret = device_add(&hdev->dev);
if (!ret)
diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c
index ac9fda1b5a72..1384e57182af 100644
--- a/drivers/hid/hid-debug.c
+++ b/drivers/hid/hid-debug.c
@@ -1060,10 +1060,15 @@ static int hid_debug_rdesc_show(struct seq_file *f, void *p)
seq_printf(f, "\n\n");
/* dump parsed data and input mappings */
+ if (down_interruptible(&hdev->driver_input_lock))
+ return 0;
+
hid_dump_device(hdev, f);
seq_printf(f, "\n");
hid_dump_input_mapping(hdev, f);
+ up(&hdev->driver_input_lock);
+
return 0;
}
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index aacc7534b076..1c8c72093b5a 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -323,6 +323,7 @@
#define USB_DEVICE_ID_CYGNAL_RADIO_SI470X 0x818a
#define USB_DEVICE_ID_FOCALTECH_FTXXXX_MULTITOUCH 0x81b9
#define USB_DEVICE_ID_CYGNAL_CP2112 0xea90
+#define USB_DEVICE_ID_U2F_ZERO 0x8acf
#define USB_DEVICE_ID_CYGNAL_RADIO_SI4713 0x8244
@@ -762,8 +763,12 @@
#define USB_DEVICE_ID_S510_RECEIVER_2 0xc517
#define USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500 0xc512
#define USB_DEVICE_ID_MX3000_RECEIVER 0xc513
+#define USB_DEVICE_ID_LOGITECH_27MHZ_MOUSE_RECEIVER 0xc51b
#define USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER 0xc52b
+#define USB_DEVICE_ID_LOGITECH_NANO_RECEIVER 0xc52f
#define USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER_2 0xc532
+#define USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_2 0xc534
+#define USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_GAMING 0xc539
#define USB_DEVICE_ID_SPACETRAVELLER 0xc623
#define USB_DEVICE_ID_SPACENAVIGATOR 0xc626
#define USB_DEVICE_ID_DINOVO_DESKTOP 0xc704
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index b10b1922c5bd..6dd0294e1133 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -998,6 +998,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
case 0x1b8: map_key_clear(KEY_VIDEO); break;
case 0x1bc: map_key_clear(KEY_MESSENGER); break;
case 0x1bd: map_key_clear(KEY_INFO); break;
+ case 0x1cb: map_key_clear(KEY_ASSISTANT); break;
case 0x201: map_key_clear(KEY_NEW); break;
case 0x202: map_key_clear(KEY_OPEN); break;
case 0x203: map_key_clear(KEY_CLOSE); break;
@@ -1541,52 +1542,71 @@ static void hidinput_close(struct input_dev *dev)
hid_hw_close(hid);
}
-static void hidinput_change_resolution_multipliers(struct hid_device *hid)
+static bool __hidinput_change_resolution_multipliers(struct hid_device *hid,
+ struct hid_report *report, bool use_logical_max)
{
- struct hid_report_enum *rep_enum;
- struct hid_report *rep;
struct hid_usage *usage;
+ bool update_needed = false;
int i, j;
- rep_enum = &hid->report_enum[HID_FEATURE_REPORT];
- list_for_each_entry(rep, &rep_enum->report_list, list) {
- bool update_needed = false;
+ if (report->maxfield == 0)
+ return false;
- if (rep->maxfield == 0)
- continue;
+ /*
+ * If we have more than one feature within this report we
+ * need to fill in the bits from the others before we can
+ * overwrite the ones for the Resolution Multiplier.
+ */
+ if (report->maxfield > 1) {
+ hid_hw_request(hid, report, HID_REQ_GET_REPORT);
+ hid_hw_wait(hid);
+ }
- /*
- * If we have more than one feature within this report we
- * need to fill in the bits from the others before we can
- * overwrite the ones for the Resolution Multiplier.
+ for (i = 0; i < report->maxfield; i++) {
+ __s32 value = use_logical_max ?
+ report->field[i]->logical_maximum :
+ report->field[i]->logical_minimum;
+
+ /* There is no good reason for a Resolution
+ * Multiplier to have a count other than 1.
+ * Ignore that case.
*/
- if (rep->maxfield > 1) {
- hid_hw_request(hid, rep, HID_REQ_GET_REPORT);
- hid_hw_wait(hid);
- }
+ if (report->field[i]->report_count != 1)
+ continue;
- for (i = 0; i < rep->maxfield; i++) {
- __s32 logical_max = rep->field[i]->logical_maximum;
+ for (j = 0; j < report->field[i]->maxusage; j++) {
+ usage = &report->field[i]->usage[j];
- /* There is no good reason for a Resolution
- * Multiplier to have a count other than 1.
- * Ignore that case.
- */
- if (rep->field[i]->report_count != 1)
+ if (usage->hid != HID_GD_RESOLUTION_MULTIPLIER)
continue;
- for (j = 0; j < rep->field[i]->maxusage; j++) {
- usage = &rep->field[i]->usage[j];
+ report->field[i]->value[j] = value;
+ update_needed = true;
+ }
+ }
+
+ return update_needed;
+}
- if (usage->hid != HID_GD_RESOLUTION_MULTIPLIER)
- continue;
+static void hidinput_change_resolution_multipliers(struct hid_device *hid)
+{
+ struct hid_report_enum *rep_enum;
+ struct hid_report *rep;
+ int ret;
- *rep->field[i]->value = logical_max;
- update_needed = true;
+ rep_enum = &hid->report_enum[HID_FEATURE_REPORT];
+ list_for_each_entry(rep, &rep_enum->report_list, list) {
+ bool update_needed = __hidinput_change_resolution_multipliers(hid,
+ rep, true);
+
+ if (update_needed) {
+ ret = __hid_request(hid, rep, HID_REQ_SET_REPORT);
+ if (ret) {
+ __hidinput_change_resolution_multipliers(hid,
+ rep, false);
+ return;
}
}
- if (update_needed)
- hid_hw_request(hid, rep, HID_REQ_SET_REPORT);
}
/* refresh our structs */
diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c
index 5d419a95b6c2..36d725fdb199 100644
--- a/drivers/hid/hid-lg.c
+++ b/drivers/hid/hid-lg.c
@@ -876,8 +876,6 @@ static const struct hid_device_id lg_devices[] = {
.driver_data = LG_RDESC | LG_WIRELESS },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER),
.driver_data = LG_RDESC | LG_WIRELESS },
- { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2),
- .driver_data = LG_RDESC | LG_WIRELESS },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RECEIVER),
.driver_data = LG_BAD_RELATIVE_KEYS },
diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c
index 826fa1e1c8d9..b1e894618eed 100644
--- a/drivers/hid/hid-logitech-dj.c
+++ b/drivers/hid/hid-logitech-dj.c
@@ -25,13 +25,14 @@
#include <linux/device.h>
#include <linux/hid.h>
#include <linux/module.h>
-#include <linux/usb.h>
#include <linux/kfifo.h>
+#include <linux/delay.h>
+#include <linux/usb.h> /* For to_usb_interface for kvm extra intf check */
#include <asm/unaligned.h>
#include "hid-ids.h"
#define DJ_MAX_PAIRED_DEVICES 6
-#define DJ_MAX_NUMBER_NOTIFICATIONS 8
+#define DJ_MAX_NUMBER_NOTIFS 8
#define DJ_RECEIVER_INDEX 0
#define DJ_DEVICE_INDEX_MIN 1
#define DJ_DEVICE_INDEX_MAX 6
@@ -74,7 +75,6 @@
/* Device Un-Paired Notification */
#define REPORT_TYPE_NOTIF_DEVICE_UNPAIRED 0x40
-
/* Connection Status Notification */
#define REPORT_TYPE_NOTIF_CONNECTION_STATUS 0x42
#define CONNECTION_STATUS_PARAM_STATUS 0x00
@@ -94,12 +94,43 @@
#define REPORT_TYPE_LEDS 0x0E
/* RF Report types bitfield */
-#define STD_KEYBOARD 0x00000002
-#define STD_MOUSE 0x00000004
-#define MULTIMEDIA 0x00000008
-#define POWER_KEYS 0x00000010
-#define MEDIA_CENTER 0x00000100
-#define KBD_LEDS 0x00004000
+#define STD_KEYBOARD BIT(1)
+#define STD_MOUSE BIT(2)
+#define MULTIMEDIA BIT(3)
+#define POWER_KEYS BIT(4)
+#define MEDIA_CENTER BIT(8)
+#define KBD_LEDS BIT(14)
+/* Fake (bitnr > NUMBER_OF_HID_REPORTS) bit to track HID++ capability */
+#define HIDPP BIT_ULL(63)
+
+/* HID++ Device Connected Notification */
+#define REPORT_TYPE_NOTIF_DEVICE_CONNECTED 0x41
+#define HIDPP_PARAM_PROTO_TYPE 0x00
+#define HIDPP_PARAM_DEVICE_INFO 0x01
+#define HIDPP_PARAM_EQUAD_LSB 0x02
+#define HIDPP_PARAM_EQUAD_MSB 0x03
+#define HIDPP_PARAM_27MHZ_DEVID 0x03
+#define HIDPP_DEVICE_TYPE_MASK GENMASK(3, 0)
+#define HIDPP_LINK_STATUS_MASK BIT(6)
+#define HIDPP_MANUFACTURER_MASK BIT(7)
+
+#define HIDPP_DEVICE_TYPE_KEYBOARD 1
+#define HIDPP_DEVICE_TYPE_MOUSE 2
+
+#define HIDPP_SET_REGISTER 0x80
+#define HIDPP_GET_LONG_REGISTER 0x83
+#define HIDPP_REG_CONNECTION_STATE 0x02
+#define HIDPP_REG_PAIRING_INFORMATION 0xB5
+#define HIDPP_PAIRING_INFORMATION 0x20
+#define HIDPP_FAKE_DEVICE_ARRIVAL 0x02
+
+enum recvr_type {
+ recvr_type_dj,
+ recvr_type_hidpp,
+ recvr_type_gaming_hidpp,
+ recvr_type_27mhz,
+ recvr_type_bluetooth,
+};
struct dj_report {
u8 report_id;
@@ -108,23 +139,51 @@ struct dj_report {
u8 report_params[DJREPORT_SHORT_LENGTH - 3];
};
+struct hidpp_event {
+ u8 report_id;
+ u8 device_index;
+ u8 sub_id;
+ u8 params[HIDPP_REPORT_LONG_LENGTH - 3U];
+} __packed;
+
struct dj_receiver_dev {
- struct hid_device *hdev;
+ struct hid_device *mouse;
+ struct hid_device *keyboard;
+ struct hid_device *hidpp;
struct dj_device *paired_dj_devices[DJ_MAX_PAIRED_DEVICES +
DJ_DEVICE_INDEX_MIN];
+ struct list_head list;
+ struct kref kref;
struct work_struct work;
struct kfifo notif_fifo;
+ unsigned long last_query; /* in jiffies */
+ bool ready;
+ enum recvr_type type;
+ unsigned int unnumbered_application;
spinlock_t lock;
- bool querying_devices;
};
struct dj_device {
struct hid_device *hdev;
struct dj_receiver_dev *dj_receiver_dev;
- u32 reports_supported;
+ u64 reports_supported;
u8 device_index;
};
+#define WORKITEM_TYPE_EMPTY 0
+#define WORKITEM_TYPE_PAIRED 1
+#define WORKITEM_TYPE_UNPAIRED 2
+#define WORKITEM_TYPE_UNKNOWN 255
+
+struct dj_workitem {
+ u8 type; /* WORKITEM_TYPE_* */
+ u8 device_index;
+ u8 device_type;
+ u8 quad_id_msb;
+ u8 quad_id_lsb;
+ u64 reports_supported;
+};
+
/* Keyboard descriptor (1) */
static const char kbd_descriptor[] = {
0x05, 0x01, /* USAGE_PAGE (generic Desktop) */
@@ -200,6 +259,131 @@ static const char mse_descriptor[] = {
0xC0, /* END_COLLECTION */
};
+/* Mouse descriptor (2) for 27 MHz receiver, only 8 buttons */
+static const char mse_27mhz_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) */
+ 0x05, 0x09, /* USAGE_PAGE (buttons) */
+ 0x19, 0x01, /* USAGE_MIN (1) */
+ 0x29, 0x08, /* USAGE_MAX (8) */
+ 0x15, 0x00, /* LOGICAL_MIN (0) */
+ 0x25, 0x01, /* LOGICAL_MAX (1) */
+ 0x95, 0x08, /* REPORT_COUNT (8) */
+ 0x75, 0x01, /* REPORT_SIZE (1) */
+ 0x81, 0x02, /* INPUT (data var abs) */
+ 0x05, 0x01, /* USAGE_PAGE (generic desktop) */
+ 0x16, 0x01, 0xF8, /* LOGICAL_MIN (-2047) */
+ 0x26, 0xFF, 0x07, /* LOGICAL_MAX (2047) */
+ 0x75, 0x0C, /* REPORT_SIZE (12) */
+ 0x95, 0x02, /* REPORT_COUNT (2) */
+ 0x09, 0x30, /* USAGE (X) */
+ 0x09, 0x31, /* USAGE (Y) */
+ 0x81, 0x06, /* INPUT */
+ 0x15, 0x81, /* LOGICAL_MIN (-127) */
+ 0x25, 0x7F, /* LOGICAL_MAX (127) */
+ 0x75, 0x08, /* REPORT_SIZE (8) */
+ 0x95, 0x01, /* REPORT_COUNT (1) */
+ 0x09, 0x38, /* USAGE (wheel) */
+ 0x81, 0x06, /* INPUT */
+ 0x05, 0x0C, /* USAGE_PAGE(consumer) */
+ 0x0A, 0x38, 0x02, /* USAGE(AC Pan) */
+ 0x95, 0x01, /* REPORT_COUNT (1) */
+ 0x81, 0x06, /* INPUT */
+ 0xC0, /* END_COLLECTION */
+ 0xC0, /* END_COLLECTION */
+};
+
+/* Mouse descriptor (2) for Bluetooth receiver, low-res hwheel, 12 buttons */
+static const char mse_bluetooth_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) */
+ 0x05, 0x09, /* USAGE_PAGE (buttons) */
+ 0x19, 0x01, /* USAGE_MIN (1) */
+ 0x29, 0x08, /* USAGE_MAX (8) */
+ 0x15, 0x00, /* LOGICAL_MIN (0) */
+ 0x25, 0x01, /* LOGICAL_MAX (1) */
+ 0x95, 0x08, /* REPORT_COUNT (8) */
+ 0x75, 0x01, /* REPORT_SIZE (1) */
+ 0x81, 0x02, /* INPUT (data var abs) */
+ 0x05, 0x01, /* USAGE_PAGE (generic desktop) */
+ 0x16, 0x01, 0xF8, /* LOGICAL_MIN (-2047) */
+ 0x26, 0xFF, 0x07, /* LOGICAL_MAX (2047) */
+ 0x75, 0x0C, /* REPORT_SIZE (12) */
+ 0x95, 0x02, /* REPORT_COUNT (2) */
+ 0x09, 0x30, /* USAGE (X) */
+ 0x09, 0x31, /* USAGE (Y) */
+ 0x81, 0x06, /* INPUT */
+ 0x15, 0x81, /* LOGICAL_MIN (-127) */
+ 0x25, 0x7F, /* LOGICAL_MAX (127) */
+ 0x75, 0x08, /* REPORT_SIZE (8) */
+ 0x95, 0x01, /* REPORT_COUNT (1) */
+ 0x09, 0x38, /* USAGE (wheel) */
+ 0x81, 0x06, /* INPUT */
+ 0x05, 0x0C, /* USAGE_PAGE(consumer) */
+ 0x0A, 0x38, 0x02, /* USAGE(AC Pan) */
+ 0x15, 0xF9, /* LOGICAL_MIN (-7) */
+ 0x25, 0x07, /* LOGICAL_MAX (7) */
+ 0x75, 0x04, /* REPORT_SIZE (4) */
+ 0x95, 0x01, /* REPORT_COUNT (1) */
+ 0x81, 0x06, /* INPUT */
+ 0x05, 0x09, /* USAGE_PAGE (buttons) */
+ 0x19, 0x09, /* USAGE_MIN (9) */
+ 0x29, 0x0C, /* USAGE_MAX (12) */
+ 0x15, 0x00, /* LOGICAL_MIN (0) */
+ 0x25, 0x01, /* LOGICAL_MAX (1) */
+ 0x75, 0x01, /* REPORT_SIZE (1) */
+ 0x95, 0x04, /* REPORT_COUNT (4) */
+ 0x81, 0x06, /* INPUT */
+ 0xC0, /* END_COLLECTION */
+ 0xC0, /* END_COLLECTION */
+};
+
+/* Gaming Mouse descriptor (2) */
+static const char mse_high_res_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) */
+ 0x05, 0x09, /* USAGE_PAGE (buttons) */
+ 0x19, 0x01, /* USAGE_MIN (1) */
+ 0x29, 0x10, /* USAGE_MAX (16) */
+ 0x15, 0x00, /* LOGICAL_MIN (0) */
+ 0x25, 0x01, /* LOGICAL_MAX (1) */
+ 0x95, 0x10, /* REPORT_COUNT (16) */
+ 0x75, 0x01, /* REPORT_SIZE (1) */
+ 0x81, 0x02, /* INPUT (data var abs) */
+ 0x05, 0x01, /* USAGE_PAGE (generic desktop) */
+ 0x16, 0x01, 0x80, /* LOGICAL_MIN (-32767) */
+ 0x26, 0xFF, 0x7F, /* LOGICAL_MAX (32767) */
+ 0x75, 0x10, /* REPORT_SIZE (16) */
+ 0x95, 0x02, /* REPORT_COUNT (2) */
+ 0x09, 0x30, /* USAGE (X) */
+ 0x09, 0x31, /* USAGE (Y) */
+ 0x81, 0x06, /* INPUT */
+ 0x15, 0x81, /* LOGICAL_MIN (-127) */
+ 0x25, 0x7F, /* LOGICAL_MAX (127) */
+ 0x75, 0x08, /* REPORT_SIZE (8) */
+ 0x95, 0x01, /* REPORT_COUNT (1) */
+ 0x09, 0x38, /* USAGE (wheel) */
+ 0x81, 0x06, /* INPUT */
+ 0x05, 0x0C, /* USAGE_PAGE(consumer) */
+ 0x0A, 0x38, 0x02, /* USAGE(AC Pan) */
+ 0x95, 0x01, /* REPORT_COUNT (1) */
+ 0x81, 0x06, /* INPUT */
+ 0xC0, /* END_COLLECTION */
+ 0xC0, /* END_COLLECTION */
+};
+
/* Consumer Control descriptor (3) */
static const char consumer_descriptor[] = {
0x05, 0x0C, /* USAGE_PAGE (Consumer Devices) */
@@ -308,7 +492,7 @@ static const char hidpp_descriptor[] = {
/* Make sure all descriptors are present here */
#define MAX_RDESC_SIZE \
(sizeof(kbd_descriptor) + \
- sizeof(mse_descriptor) + \
+ sizeof(mse_bluetooth_descriptor) + \
sizeof(consumer_descriptor) + \
sizeof(syscontrol_descriptor) + \
sizeof(media_descriptor) + \
@@ -341,51 +525,160 @@ static const u8 hid_reportid_size_map[NUMBER_OF_HID_REPORTS] = {
static struct hid_ll_driver logi_dj_ll_driver;
static int logi_dj_recv_query_paired_devices(struct dj_receiver_dev *djrcv_dev);
+static void delayedwork_callback(struct work_struct *work);
+
+static LIST_HEAD(dj_hdev_list);
+static DEFINE_MUTEX(dj_hdev_list_lock);
+
+/*
+ * dj/HID++ receivers are really a single logical entity, but for BIOS/Windows
+ * compatibility they have multiple USB interfaces. On HID++ receivers we need
+ * to listen for input reports on both interfaces. The functions below are used
+ * to create a single struct dj_receiver_dev for all interfaces belonging to
+ * a single USB-device / receiver.
+ */
+static struct dj_receiver_dev *dj_find_receiver_dev(struct hid_device *hdev,
+ enum recvr_type type)
+{
+ struct dj_receiver_dev *djrcv_dev;
+ char sep;
+
+ /*
+ * The bluetooth receiver contains a built-in hub and has separate
+ * USB-devices for the keyboard and mouse interfaces.
+ */
+ sep = (type == recvr_type_bluetooth) ? '.' : '/';
+
+ /* Try to find an already-probed interface from the same device */
+ list_for_each_entry(djrcv_dev, &dj_hdev_list, list) {
+ if (djrcv_dev->mouse &&
+ hid_compare_device_paths(hdev, djrcv_dev->mouse, sep)) {
+ kref_get(&djrcv_dev->kref);
+ return djrcv_dev;
+ }
+ if (djrcv_dev->keyboard &&
+ hid_compare_device_paths(hdev, djrcv_dev->keyboard, sep)) {
+ kref_get(&djrcv_dev->kref);
+ return djrcv_dev;
+ }
+ if (djrcv_dev->hidpp &&
+ hid_compare_device_paths(hdev, djrcv_dev->hidpp, sep)) {
+ kref_get(&djrcv_dev->kref);
+ return djrcv_dev;
+ }
+ }
+
+ return NULL;
+}
+
+static void dj_release_receiver_dev(struct kref *kref)
+{
+ struct dj_receiver_dev *djrcv_dev = container_of(kref, struct dj_receiver_dev, kref);
+
+ list_del(&djrcv_dev->list);
+ kfifo_free(&djrcv_dev->notif_fifo);
+ kfree(djrcv_dev);
+}
+
+static void dj_put_receiver_dev(struct hid_device *hdev)
+{
+ struct dj_receiver_dev *djrcv_dev = hid_get_drvdata(hdev);
+
+ mutex_lock(&dj_hdev_list_lock);
+
+ if (djrcv_dev->mouse == hdev)
+ djrcv_dev->mouse = NULL;
+ if (djrcv_dev->keyboard == hdev)
+ djrcv_dev->keyboard = NULL;
+ if (djrcv_dev->hidpp == hdev)
+ djrcv_dev->hidpp = NULL;
+
+ kref_put(&djrcv_dev->kref, dj_release_receiver_dev);
+
+ mutex_unlock(&dj_hdev_list_lock);
+}
+
+static struct dj_receiver_dev *dj_get_receiver_dev(struct hid_device *hdev,
+ enum recvr_type type,
+ unsigned int application,
+ bool is_hidpp)
+{
+ struct dj_receiver_dev *djrcv_dev;
+
+ mutex_lock(&dj_hdev_list_lock);
+
+ djrcv_dev = dj_find_receiver_dev(hdev, type);
+ if (!djrcv_dev) {
+ djrcv_dev = kzalloc(sizeof(*djrcv_dev), GFP_KERNEL);
+ if (!djrcv_dev)
+ goto out;
+
+ INIT_WORK(&djrcv_dev->work, delayedwork_callback);
+ spin_lock_init(&djrcv_dev->lock);
+ if (kfifo_alloc(&djrcv_dev->notif_fifo,
+ DJ_MAX_NUMBER_NOTIFS * sizeof(struct dj_workitem),
+ GFP_KERNEL)) {
+ kfree(djrcv_dev);
+ djrcv_dev = NULL;
+ goto out;
+ }
+ kref_init(&djrcv_dev->kref);
+ list_add_tail(&djrcv_dev->list, &dj_hdev_list);
+ djrcv_dev->last_query = jiffies;
+ djrcv_dev->type = type;
+ }
+
+ if (application == HID_GD_KEYBOARD)
+ djrcv_dev->keyboard = hdev;
+ if (application == HID_GD_MOUSE)
+ djrcv_dev->mouse = hdev;
+ if (is_hidpp)
+ djrcv_dev->hidpp = hdev;
+
+ hid_set_drvdata(hdev, djrcv_dev);
+out:
+ mutex_unlock(&dj_hdev_list_lock);
+ return djrcv_dev;
+}
static void logi_dj_recv_destroy_djhid_device(struct dj_receiver_dev *djrcv_dev,
- struct dj_report *dj_report)
+ struct dj_workitem *workitem)
{
/* Called in delayed work context */
struct dj_device *dj_dev;
unsigned long flags;
spin_lock_irqsave(&djrcv_dev->lock, flags);
- dj_dev = djrcv_dev->paired_dj_devices[dj_report->device_index];
- djrcv_dev->paired_dj_devices[dj_report->device_index] = NULL;
+ dj_dev = djrcv_dev->paired_dj_devices[workitem->device_index];
+ djrcv_dev->paired_dj_devices[workitem->device_index] = NULL;
spin_unlock_irqrestore(&djrcv_dev->lock, flags);
if (dj_dev != NULL) {
hid_destroy_device(dj_dev->hdev);
kfree(dj_dev);
} else {
- dev_err(&djrcv_dev->hdev->dev, "%s: can't destroy a NULL device\n",
+ hid_err(djrcv_dev->hidpp, "%s: can't destroy a NULL device\n",
__func__);
}
}
static void logi_dj_recv_add_djhid_device(struct dj_receiver_dev *djrcv_dev,
- struct dj_report *dj_report)
+ struct dj_workitem *workitem)
{
/* Called in delayed work context */
- struct hid_device *djrcv_hdev = djrcv_dev->hdev;
- struct usb_interface *intf = to_usb_interface(djrcv_hdev->dev.parent);
- struct usb_device *usbdev = interface_to_usbdev(intf);
+ struct hid_device *djrcv_hdev = djrcv_dev->hidpp;
struct hid_device *dj_hiddev;
struct dj_device *dj_dev;
+ u8 device_index = workitem->device_index;
+ unsigned long flags;
/* Device index goes from 1 to 6, we need 3 bytes to store the
* semicolon, the index, and a null terminator
*/
unsigned char tmpstr[3];
- if (dj_report->report_params[DEVICE_PAIRED_PARAM_SPFUNCTION] &
- SPFUNCTION_DEVICE_LIST_EMPTY) {
- dbg_hid("%s: device list is empty\n", __func__);
- djrcv_dev->querying_devices = false;
- return;
- }
-
- if (djrcv_dev->paired_dj_devices[dj_report->device_index]) {
+ /* We are the only one ever adding a device, no need to lock */
+ if (djrcv_dev->paired_dj_devices[device_index]) {
/* The device is already known. No need to reallocate it. */
dbg_hid("%s: device is already known\n", __func__);
return;
@@ -393,8 +686,7 @@ static void logi_dj_recv_add_djhid_device(struct dj_receiver_dev *djrcv_dev,
dj_hiddev = hid_allocate_device();
if (IS_ERR(dj_hiddev)) {
- dev_err(&djrcv_hdev->dev, "%s: hid_allocate_device failed\n",
- __func__);
+ hid_err(djrcv_hdev, "%s: hid_allocate_dev failed\n", __func__);
return;
}
@@ -402,48 +694,67 @@ static void logi_dj_recv_add_djhid_device(struct dj_receiver_dev *djrcv_dev,
dj_hiddev->dev.parent = &djrcv_hdev->dev;
dj_hiddev->bus = BUS_USB;
- dj_hiddev->vendor = le16_to_cpu(usbdev->descriptor.idVendor);
- dj_hiddev->product =
- (dj_report->report_params[DEVICE_PAIRED_PARAM_EQUAD_ID_MSB]
- << 8) |
- dj_report->report_params[DEVICE_PAIRED_PARAM_EQUAD_ID_LSB];
- snprintf(dj_hiddev->name, sizeof(dj_hiddev->name),
- "Logitech Unifying Device. Wireless PID:%04x",
- dj_hiddev->product);
-
- dj_hiddev->group = HID_GROUP_LOGITECH_DJ_DEVICE;
-
- usb_make_path(usbdev, dj_hiddev->phys, sizeof(dj_hiddev->phys));
- snprintf(tmpstr, sizeof(tmpstr), ":%d", dj_report->device_index);
+ dj_hiddev->vendor = djrcv_hdev->vendor;
+ dj_hiddev->product = (workitem->quad_id_msb << 8) |
+ workitem->quad_id_lsb;
+ if (workitem->device_type) {
+ const char *type_str = "Device";
+
+ switch (workitem->device_type) {
+ case 0x01: type_str = "Keyboard"; break;
+ case 0x02: type_str = "Mouse"; break;
+ case 0x03: type_str = "Numpad"; break;
+ case 0x04: type_str = "Presenter"; break;
+ case 0x07: type_str = "Remote Control"; break;
+ case 0x08: type_str = "Trackball"; break;
+ case 0x09: type_str = "Touchpad"; break;
+ }
+ snprintf(dj_hiddev->name, sizeof(dj_hiddev->name),
+ "Logitech Wireless %s PID:%04x",
+ type_str, dj_hiddev->product);
+ } else {
+ snprintf(dj_hiddev->name, sizeof(dj_hiddev->name),
+ "Logitech Unifying Device. Wireless PID:%04x",
+ dj_hiddev->product);
+ }
+
+ if (djrcv_dev->type == recvr_type_27mhz)
+ dj_hiddev->group = HID_GROUP_LOGITECH_27MHZ_DEVICE;
+ else
+ dj_hiddev->group = HID_GROUP_LOGITECH_DJ_DEVICE;
+
+ memcpy(dj_hiddev->phys, djrcv_hdev->phys, sizeof(djrcv_hdev->phys));
+ snprintf(tmpstr, sizeof(tmpstr), ":%d", device_index);
strlcat(dj_hiddev->phys, tmpstr, sizeof(dj_hiddev->phys));
dj_dev = kzalloc(sizeof(struct dj_device), GFP_KERNEL);
if (!dj_dev) {
- dev_err(&djrcv_hdev->dev, "%s: failed allocating dj_device\n",
- __func__);
+ hid_err(djrcv_hdev, "%s: failed allocating dj_dev\n", __func__);
goto dj_device_allocate_fail;
}
- dj_dev->reports_supported = get_unaligned_le32(
- dj_report->report_params + DEVICE_PAIRED_RF_REPORT_TYPE);
+ dj_dev->reports_supported = workitem->reports_supported;
dj_dev->hdev = dj_hiddev;
dj_dev->dj_receiver_dev = djrcv_dev;
- dj_dev->device_index = dj_report->device_index;
+ dj_dev->device_index = device_index;
dj_hiddev->driver_data = dj_dev;
- djrcv_dev->paired_dj_devices[dj_report->device_index] = dj_dev;
+ spin_lock_irqsave(&djrcv_dev->lock, flags);
+ djrcv_dev->paired_dj_devices[device_index] = dj_dev;
+ spin_unlock_irqrestore(&djrcv_dev->lock, flags);
if (hid_add_device(dj_hiddev)) {
- dev_err(&djrcv_hdev->dev, "%s: failed adding dj_device\n",
- __func__);
+ hid_err(djrcv_hdev, "%s: failed adding dj_device\n", __func__);
goto hid_add_device_fail;
}
return;
hid_add_device_fail:
- djrcv_dev->paired_dj_devices[dj_report->device_index] = NULL;
+ spin_lock_irqsave(&djrcv_dev->lock, flags);
+ djrcv_dev->paired_dj_devices[device_index] = NULL;
+ spin_unlock_irqrestore(&djrcv_dev->lock, flags);
kfree(dj_dev);
dj_device_allocate_fail:
hid_destroy_device(dj_hiddev);
@@ -454,7 +765,7 @@ static void delayedwork_callback(struct work_struct *work)
struct dj_receiver_dev *djrcv_dev =
container_of(work, struct dj_receiver_dev, work);
- struct dj_report dj_report;
+ struct dj_workitem workitem;
unsigned long flags;
int count;
int retval;
@@ -463,69 +774,231 @@ static void delayedwork_callback(struct work_struct *work)
spin_lock_irqsave(&djrcv_dev->lock, flags);
- count = kfifo_out(&djrcv_dev->notif_fifo, &dj_report,
- sizeof(struct dj_report));
-
- if (count != sizeof(struct dj_report)) {
- dev_err(&djrcv_dev->hdev->dev, "%s: workitem triggered without "
- "notifications available\n", __func__);
+ /*
+ * Since we attach to multiple interfaces, we may get scheduled before
+ * we are bound to the HID++ interface, catch this.
+ */
+ if (!djrcv_dev->ready) {
+ pr_warn("%s: delayedwork queued before hidpp interface was enumerated\n",
+ __func__);
spin_unlock_irqrestore(&djrcv_dev->lock, flags);
return;
}
- if (!kfifo_is_empty(&djrcv_dev->notif_fifo)) {
- if (schedule_work(&djrcv_dev->work) == 0) {
- dbg_hid("%s: did not schedule the work item, was "
- "already queued\n", __func__);
- }
+ count = kfifo_out(&djrcv_dev->notif_fifo, &workitem, sizeof(workitem));
+
+ if (count != sizeof(workitem)) {
+ spin_unlock_irqrestore(&djrcv_dev->lock, flags);
+ return;
}
+ if (!kfifo_is_empty(&djrcv_dev->notif_fifo))
+ schedule_work(&djrcv_dev->work);
+
spin_unlock_irqrestore(&djrcv_dev->lock, flags);
- switch (dj_report.report_type) {
- case REPORT_TYPE_NOTIF_DEVICE_PAIRED:
- logi_dj_recv_add_djhid_device(djrcv_dev, &dj_report);
+ switch (workitem.type) {
+ case WORKITEM_TYPE_PAIRED:
+ logi_dj_recv_add_djhid_device(djrcv_dev, &workitem);
break;
- case REPORT_TYPE_NOTIF_DEVICE_UNPAIRED:
- logi_dj_recv_destroy_djhid_device(djrcv_dev, &dj_report);
+ case WORKITEM_TYPE_UNPAIRED:
+ logi_dj_recv_destroy_djhid_device(djrcv_dev, &workitem);
break;
- default:
- /* A normal report (i. e. not belonging to a pair/unpair notification)
- * arriving here, means that the report arrived but we did not have a
- * paired dj_device associated to the report's device_index, this
- * means that the original "device paired" notification corresponding
- * to this dj_device never arrived to this driver. The reason is that
- * hid-core discards all packets coming from a device while probe() is
- * executing. */
- if (!djrcv_dev->paired_dj_devices[dj_report.device_index]) {
- /* ok, we don't know the device, just re-ask the
- * receiver for the list of connected devices. */
+ case WORKITEM_TYPE_UNKNOWN:
retval = logi_dj_recv_query_paired_devices(djrcv_dev);
- if (!retval) {
- /* everything went fine, so just leave */
- break;
- }
- dev_err(&djrcv_dev->hdev->dev,
- "%s:logi_dj_recv_query_paired_devices "
- "error:%d\n", __func__, retval);
+ if (retval) {
+ hid_err(djrcv_dev->hidpp, "%s: logi_dj_recv_query_paired_devices error: %d\n",
+ __func__, retval);
}
- dbg_hid("%s: unexpected report type\n", __func__);
+ break;
+ case WORKITEM_TYPE_EMPTY:
+ dbg_hid("%s: device list is empty\n", __func__);
+ break;
}
}
+/*
+ * Sometimes we receive reports for which we do not have a paired dj_device
+ * associated with the device_index or report-type to forward the report to.
+ * This means that the original "device paired" notification corresponding
+ * to the dj_device never arrived to this driver. Possible reasons for this are:
+ * 1) hid-core discards all packets coming from a device during probe().
+ * 2) if the receiver is plugged into a KVM switch then the pairing reports
+ * are only forwarded to it if the focus is on this PC.
+ * This function deals with this by re-asking the receiver for the list of
+ * connected devices in the delayed work callback.
+ * This function MUST be called with djrcv->lock held.
+ */
+static void logi_dj_recv_queue_unknown_work(struct dj_receiver_dev *djrcv_dev)
+{
+ struct dj_workitem workitem = { .type = WORKITEM_TYPE_UNKNOWN };
+
+ /* Rate limit queries done because of unhandeled reports to 2/sec */
+ if (time_before(jiffies, djrcv_dev->last_query + HZ / 2))
+ return;
+
+ kfifo_in(&djrcv_dev->notif_fifo, &workitem, sizeof(workitem));
+ schedule_work(&djrcv_dev->work);
+}
+
static void logi_dj_recv_queue_notification(struct dj_receiver_dev *djrcv_dev,
struct dj_report *dj_report)
{
/* We are called from atomic context (tasklet && djrcv->lock held) */
+ struct dj_workitem workitem = {
+ .device_index = dj_report->device_index,
+ };
+
+ switch (dj_report->report_type) {
+ case REPORT_TYPE_NOTIF_DEVICE_PAIRED:
+ workitem.type = WORKITEM_TYPE_PAIRED;
+ if (dj_report->report_params[DEVICE_PAIRED_PARAM_SPFUNCTION] &
+ SPFUNCTION_DEVICE_LIST_EMPTY) {
+ workitem.type = WORKITEM_TYPE_EMPTY;
+ break;
+ }
+ /* fall-through */
+ case REPORT_TYPE_NOTIF_DEVICE_UNPAIRED:
+ workitem.quad_id_msb =
+ dj_report->report_params[DEVICE_PAIRED_PARAM_EQUAD_ID_MSB];
+ workitem.quad_id_lsb =
+ dj_report->report_params[DEVICE_PAIRED_PARAM_EQUAD_ID_LSB];
+ workitem.reports_supported = get_unaligned_le32(
+ dj_report->report_params +
+ DEVICE_PAIRED_RF_REPORT_TYPE);
+ workitem.reports_supported |= HIDPP;
+ if (dj_report->report_type == REPORT_TYPE_NOTIF_DEVICE_UNPAIRED)
+ workitem.type = WORKITEM_TYPE_UNPAIRED;
+ break;
+ default:
+ logi_dj_recv_queue_unknown_work(djrcv_dev);
+ return;
+ }
- kfifo_in(&djrcv_dev->notif_fifo, dj_report, sizeof(struct dj_report));
+ kfifo_in(&djrcv_dev->notif_fifo, &workitem, sizeof(workitem));
+ schedule_work(&djrcv_dev->work);
+}
- if (schedule_work(&djrcv_dev->work) == 0) {
- dbg_hid("%s: did not schedule the work item, was already "
- "queued\n", __func__);
+static void logi_hidpp_dev_conn_notif_equad(struct hidpp_event *hidpp_report,
+ struct dj_workitem *workitem)
+{
+ workitem->type = WORKITEM_TYPE_PAIRED;
+ workitem->device_type = hidpp_report->params[HIDPP_PARAM_DEVICE_INFO] &
+ HIDPP_DEVICE_TYPE_MASK;
+ workitem->quad_id_msb = hidpp_report->params[HIDPP_PARAM_EQUAD_MSB];
+ workitem->quad_id_lsb = hidpp_report->params[HIDPP_PARAM_EQUAD_LSB];
+ switch (workitem->device_type) {
+ case REPORT_TYPE_KEYBOARD:
+ workitem->reports_supported |= STD_KEYBOARD | MULTIMEDIA |
+ POWER_KEYS | MEDIA_CENTER |
+ HIDPP;
+ break;
+ case REPORT_TYPE_MOUSE:
+ workitem->reports_supported |= STD_MOUSE | HIDPP;
+ break;
+ }
+}
+
+static void logi_hidpp_dev_conn_notif_27mhz(struct hid_device *hdev,
+ struct hidpp_event *hidpp_report,
+ struct dj_workitem *workitem)
+{
+ workitem->type = WORKITEM_TYPE_PAIRED;
+ workitem->quad_id_lsb = hidpp_report->params[HIDPP_PARAM_27MHZ_DEVID];
+ switch (hidpp_report->device_index) {
+ case 1: /* Index 1 is always a mouse */
+ case 2: /* Index 2 is always a mouse */
+ workitem->device_type = HIDPP_DEVICE_TYPE_MOUSE;
+ workitem->reports_supported |= STD_MOUSE | HIDPP;
+ break;
+ case 3: /* Index 3 is always the keyboard */
+ case 4: /* Index 4 is used for an optional separate numpad */
+ workitem->device_type = HIDPP_DEVICE_TYPE_KEYBOARD;
+ workitem->reports_supported |= STD_KEYBOARD | MULTIMEDIA |
+ POWER_KEYS | HIDPP;
+ break;
+ default:
+ hid_warn(hdev, "%s: unexpected device-index %d", __func__,
+ hidpp_report->device_index);
}
}
+static void logi_hidpp_recv_queue_notif(struct hid_device *hdev,
+ struct hidpp_event *hidpp_report)
+{
+ /* We are called from atomic context (tasklet && djrcv->lock held) */
+ struct dj_receiver_dev *djrcv_dev = hid_get_drvdata(hdev);
+ const char *device_type = "UNKNOWN";
+ struct dj_workitem workitem = {
+ .type = WORKITEM_TYPE_EMPTY,
+ .device_index = hidpp_report->device_index,
+ };
+
+ switch (hidpp_report->params[HIDPP_PARAM_PROTO_TYPE]) {
+ case 0x01:
+ device_type = "Bluetooth";
+ /* Bluetooth connect packet contents is the same as (e)QUAD */
+ logi_hidpp_dev_conn_notif_equad(hidpp_report, &workitem);
+ if (!(hidpp_report->params[HIDPP_PARAM_DEVICE_INFO] &
+ HIDPP_MANUFACTURER_MASK)) {
+ hid_info(hdev, "Non Logitech device connected on slot %d\n",
+ hidpp_report->device_index);
+ workitem.reports_supported &= ~HIDPP;
+ }
+ break;
+ case 0x02:
+ device_type = "27 Mhz";
+ logi_hidpp_dev_conn_notif_27mhz(hdev, hidpp_report, &workitem);
+ break;
+ case 0x03:
+ device_type = "QUAD or eQUAD";
+ logi_hidpp_dev_conn_notif_equad(hidpp_report, &workitem);
+ break;
+ case 0x04:
+ device_type = "eQUAD step 4 DJ";
+ logi_hidpp_dev_conn_notif_equad(hidpp_report, &workitem);
+ break;
+ case 0x05:
+ device_type = "DFU Lite";
+ break;
+ case 0x06:
+ device_type = "eQUAD step 4 Lite";
+ logi_hidpp_dev_conn_notif_equad(hidpp_report, &workitem);
+ break;
+ case 0x07:
+ device_type = "eQUAD step 4 Gaming";
+ break;
+ case 0x08:
+ device_type = "eQUAD step 4 for gamepads";
+ break;
+ case 0x0a:
+ device_type = "eQUAD nano Lite";
+ logi_hidpp_dev_conn_notif_equad(hidpp_report, &workitem);
+ break;
+ case 0x0c:
+ device_type = "eQUAD Lightspeed";
+ logi_hidpp_dev_conn_notif_equad(hidpp_report, &workitem);
+ workitem.reports_supported |= STD_KEYBOARD;
+ break;
+ }
+
+ if (workitem.type == WORKITEM_TYPE_EMPTY) {
+ hid_warn(hdev,
+ "unusable device of type %s (0x%02x) connected on slot %d",
+ device_type,
+ hidpp_report->params[HIDPP_PARAM_PROTO_TYPE],
+ hidpp_report->device_index);
+ return;
+ }
+
+ hid_info(hdev, "device of type %s (0x%02x) connected on slot %d",
+ device_type, hidpp_report->params[HIDPP_PARAM_PROTO_TYPE],
+ hidpp_report->device_index);
+
+ kfifo_in(&djrcv_dev->notif_fifo, &workitem, sizeof(workitem));
+ schedule_work(&djrcv_dev->work);
+}
+
static void logi_dj_recv_forward_null_report(struct dj_receiver_dev *djrcv_dev,
struct dj_report *dj_report)
{
@@ -552,8 +1025,8 @@ static void logi_dj_recv_forward_null_report(struct dj_receiver_dev *djrcv_dev,
}
}
-static void logi_dj_recv_forward_report(struct dj_receiver_dev *djrcv_dev,
- struct dj_report *dj_report)
+static void logi_dj_recv_forward_dj(struct dj_receiver_dev *djrcv_dev,
+ struct dj_report *dj_report)
{
/* We are called from atomic context (tasklet && djrcv->lock held) */
struct dj_device *dj_device;
@@ -573,18 +1046,48 @@ static void logi_dj_recv_forward_report(struct dj_receiver_dev *djrcv_dev,
}
}
-static void logi_dj_recv_forward_hidpp(struct dj_device *dj_dev, u8 *data,
- int size)
+static void logi_dj_recv_forward_report(struct dj_device *dj_dev, u8 *data,
+ int size)
{
/* We are called from atomic context (tasklet && djrcv->lock held) */
if (hid_input_report(dj_dev->hdev, HID_INPUT_REPORT, data, size, 1))
dbg_hid("hid_input_report error\n");
}
+static void logi_dj_recv_forward_input_report(struct hid_device *hdev,
+ u8 *data, int size)
+{
+ struct dj_receiver_dev *djrcv_dev = hid_get_drvdata(hdev);
+ struct dj_device *dj_dev;
+ unsigned long flags;
+ u8 report = data[0];
+ int i;
+
+ if (report > REPORT_TYPE_RFREPORT_LAST) {
+ hid_err(hdev, "Unexpected input report number %d\n", report);
+ return;
+ }
+
+ spin_lock_irqsave(&djrcv_dev->lock, flags);
+ for (i = 0; i < (DJ_MAX_PAIRED_DEVICES + DJ_DEVICE_INDEX_MIN); i++) {
+ dj_dev = djrcv_dev->paired_dj_devices[i];
+ if (dj_dev && (dj_dev->reports_supported & BIT(report))) {
+ logi_dj_recv_forward_report(dj_dev, data, size);
+ spin_unlock_irqrestore(&djrcv_dev->lock, flags);
+ return;
+ }
+ }
+
+ logi_dj_recv_queue_unknown_work(djrcv_dev);
+ spin_unlock_irqrestore(&djrcv_dev->lock, flags);
+
+ dbg_hid("No dj-devs handling input report number %d\n", report);
+}
+
static int logi_dj_recv_send_report(struct dj_receiver_dev *djrcv_dev,
struct dj_report *dj_report)
{
- struct hid_device *hdev = djrcv_dev->hdev;
+ struct hid_device *hdev = djrcv_dev->hidpp;
struct hid_report *report;
struct hid_report_enum *output_report_enum;
u8 *data = (u8 *)(&dj_report->device_index);
@@ -594,7 +1097,7 @@ static int logi_dj_recv_send_report(struct dj_receiver_dev *djrcv_dev,
report = output_report_enum->report_id_hash[REPORT_ID_DJ_SHORT];
if (!report) {
- dev_err(&hdev->dev, "%s: unable to find dj report\n", __func__);
+ hid_err(hdev, "%s: unable to find dj report\n", __func__);
return -ENODEV;
}
@@ -606,14 +1109,40 @@ static int logi_dj_recv_send_report(struct dj_receiver_dev *djrcv_dev,
return 0;
}
+static int logi_dj_recv_query_hidpp_devices(struct dj_receiver_dev *djrcv_dev)
+{
+ const u8 template[] = {REPORT_ID_HIDPP_SHORT,
+ HIDPP_RECEIVER_INDEX,
+ HIDPP_SET_REGISTER,
+ HIDPP_REG_CONNECTION_STATE,
+ HIDPP_FAKE_DEVICE_ARRIVAL,
+ 0x00, 0x00};
+ u8 *hidpp_report;
+ int retval;
+
+ hidpp_report = kmemdup(template, sizeof(template), GFP_KERNEL);
+ if (!hidpp_report)
+ return -ENOMEM;
+
+ retval = hid_hw_raw_request(djrcv_dev->hidpp,
+ REPORT_ID_HIDPP_SHORT,
+ hidpp_report, sizeof(template),
+ HID_OUTPUT_REPORT,
+ HID_REQ_SET_REPORT);
+
+ kfree(hidpp_report);
+ return 0;
+}
+
static int logi_dj_recv_query_paired_devices(struct dj_receiver_dev *djrcv_dev)
{
struct dj_report *dj_report;
int retval;
- /* no need to protect djrcv_dev->querying_devices */
- if (djrcv_dev->querying_devices)
- return 0;
+ djrcv_dev->last_query = jiffies;
+
+ if (djrcv_dev->type != recvr_type_dj)
+ return logi_dj_recv_query_hidpp_devices(djrcv_dev);
dj_report = kzalloc(sizeof(struct dj_report), GFP_KERNEL);
if (!dj_report)
@@ -630,27 +1159,33 @@ 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 timeout)
{
- struct hid_device *hdev = djrcv_dev->hdev;
+ struct hid_device *hdev = djrcv_dev->hidpp;
struct dj_report *dj_report;
u8 *buf;
- int retval;
+ int retval = 0;
dj_report = kzalloc(sizeof(struct dj_report), GFP_KERNEL);
if (!dj_report)
return -ENOMEM;
- dj_report->report_id = REPORT_ID_DJ_SHORT;
- dj_report->device_index = 0xFF;
- dj_report->report_type = REPORT_TYPE_CMD_SWITCH;
- dj_report->report_params[CMD_SWITCH_PARAM_DEVBITFIELD] = 0x3F;
- dj_report->report_params[CMD_SWITCH_PARAM_TIMEOUT_SECONDS] = (u8)timeout;
- retval = logi_dj_recv_send_report(djrcv_dev, dj_report);
- /*
- * Ugly sleep to work around a USB 3.0 bug when the receiver is still
- * processing the "switch-to-dj" command while we send an other command.
- * 50 msec should gives enough time to the receiver to be ready.
- */
- msleep(50);
+ if (djrcv_dev->type == recvr_type_dj) {
+ dj_report->report_id = REPORT_ID_DJ_SHORT;
+ dj_report->device_index = 0xFF;
+ dj_report->report_type = REPORT_TYPE_CMD_SWITCH;
+ dj_report->report_params[CMD_SWITCH_PARAM_DEVBITFIELD] = 0x3F;
+ dj_report->report_params[CMD_SWITCH_PARAM_TIMEOUT_SECONDS] =
+ (u8)timeout;
+
+ retval = logi_dj_recv_send_report(djrcv_dev, dj_report);
+
+ /*
+ * Ugly sleep to work around a USB 3.0 bug when the receiver is
+ * still processing the "switch-to-dj" command while we send an
+ * other command.
+ * 50 msec should gives enough time to the receiver to be ready.
+ */
+ msleep(50);
+ }
/*
* Magical bits to set up hidpp notifications when the dj devices
@@ -682,22 +1217,28 @@ static int logi_dj_recv_switch_to_dj_mode(struct dj_receiver_dev *djrcv_dev,
static int logi_dj_ll_open(struct hid_device *hid)
{
- dbg_hid("%s:%s\n", __func__, hid->phys);
+ dbg_hid("%s: %s\n", __func__, hid->phys);
return 0;
}
static void logi_dj_ll_close(struct hid_device *hid)
{
- dbg_hid("%s:%s\n", __func__, hid->phys);
+ dbg_hid("%s: %s\n", __func__, hid->phys);
}
/*
* Register 0xB5 is "pairing information". It is solely intended for the
* receiver, so do not overwrite the device index.
*/
-static u8 unifying_pairing_query[] = {0x10, 0xff, 0x83, 0xb5};
-static u8 unifying_pairing_answer[] = {0x11, 0xff, 0x83, 0xb5};
+static u8 unifying_pairing_query[] = { REPORT_ID_HIDPP_SHORT,
+ HIDPP_RECEIVER_INDEX,
+ HIDPP_GET_LONG_REGISTER,
+ HIDPP_REG_PAIRING_INFORMATION };
+static u8 unifying_pairing_answer[] = { REPORT_ID_HIDPP_LONG,
+ HIDPP_RECEIVER_INDEX,
+ HIDPP_GET_LONG_REGISTER,
+ HIDPP_REG_PAIRING_INFORMATION };
static int logi_dj_ll_raw_request(struct hid_device *hid,
unsigned char reportnum, __u8 *buf,
@@ -721,13 +1262,23 @@ static int logi_dj_ll_raw_request(struct hid_device *hid,
buf[4] = (buf[4] & 0xf0) | (djdev->device_index - 1);
else
buf[1] = djdev->device_index;
- return hid_hw_raw_request(djrcv_dev->hdev, reportnum, buf,
+ return hid_hw_raw_request(djrcv_dev->hidpp, reportnum, buf,
count, report_type, reqtype);
}
if (buf[0] != REPORT_TYPE_LEDS)
return -EINVAL;
+ if (djrcv_dev->type != recvr_type_dj && count >= 2) {
+ if (!djrcv_dev->keyboard) {
+ hid_warn(hid, "Received REPORT_TYPE_LEDS request before the keyboard interface was enumerated\n");
+ return 0;
+ }
+ /* usbhid overrides the report ID and ignores the first byte */
+ return hid_hw_raw_request(djrcv_dev->keyboard, 0, buf, count,
+ report_type, reqtype);
+ }
+
out_buf = kzalloc(DJREPORT_SHORT_LENGTH, GFP_ATOMIC);
if (!out_buf)
return -ENOMEM;
@@ -739,7 +1290,7 @@ static int logi_dj_ll_raw_request(struct hid_device *hid,
out_buf[1] = djdev->device_index;
memcpy(out_buf + 2, buf, count);
- ret = hid_hw_raw_request(djrcv_dev->hdev, out_buf[0], out_buf,
+ ret = hid_hw_raw_request(djrcv_dev->hidpp, out_buf[0], out_buf,
DJREPORT_SHORT_LENGTH, report_type, reqtype);
kfree(out_buf);
@@ -769,41 +1320,55 @@ static int logi_dj_ll_parse(struct hid_device *hid)
return -ENOMEM;
if (djdev->reports_supported & STD_KEYBOARD) {
- dbg_hid("%s: sending a kbd descriptor, reports_supported: %x\n",
+ 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->reports_supported & STD_MOUSE) {
- dbg_hid("%s: sending a mouse descriptor, reports_supported: "
- "%x\n", __func__, djdev->reports_supported);
- rdcat(rdesc, &rsize, mse_descriptor, sizeof(mse_descriptor));
+ dbg_hid("%s: sending a mouse descriptor, reports_supported: %llx\n",
+ __func__, djdev->reports_supported);
+ if (djdev->dj_receiver_dev->type == recvr_type_gaming_hidpp)
+ rdcat(rdesc, &rsize, mse_high_res_descriptor,
+ sizeof(mse_high_res_descriptor));
+ else if (djdev->dj_receiver_dev->type == recvr_type_27mhz)
+ rdcat(rdesc, &rsize, mse_27mhz_descriptor,
+ sizeof(mse_27mhz_descriptor));
+ else if (djdev->dj_receiver_dev->type == recvr_type_bluetooth)
+ rdcat(rdesc, &rsize, mse_bluetooth_descriptor,
+ sizeof(mse_bluetooth_descriptor));
+ else
+ rdcat(rdesc, &rsize, mse_descriptor,
+ sizeof(mse_descriptor));
}
if (djdev->reports_supported & MULTIMEDIA) {
- dbg_hid("%s: sending a multimedia report descriptor: %x\n",
+ dbg_hid("%s: sending a multimedia report descriptor: %llx\n",
__func__, djdev->reports_supported);
rdcat(rdesc, &rsize, consumer_descriptor, sizeof(consumer_descriptor));
}
if (djdev->reports_supported & POWER_KEYS) {
- dbg_hid("%s: sending a power keys report descriptor: %x\n",
+ dbg_hid("%s: sending a power keys report descriptor: %llx\n",
__func__, djdev->reports_supported);
rdcat(rdesc, &rsize, syscontrol_descriptor, sizeof(syscontrol_descriptor));
}
if (djdev->reports_supported & MEDIA_CENTER) {
- dbg_hid("%s: sending a media center report descriptor: %x\n",
+ dbg_hid("%s: sending a media center report descriptor: %llx\n",
__func__, djdev->reports_supported);
rdcat(rdesc, &rsize, media_descriptor, sizeof(media_descriptor));
}
if (djdev->reports_supported & KBD_LEDS) {
- dbg_hid("%s: need to send kbd leds report descriptor: %x\n",
+ dbg_hid("%s: need to send kbd leds report descriptor: %llx\n",
__func__, djdev->reports_supported);
}
- rdcat(rdesc, &rsize, hidpp_descriptor, sizeof(hidpp_descriptor));
+ if (djdev->reports_supported & HIDPP) {
+ rdcat(rdesc, &rsize, hidpp_descriptor,
+ sizeof(hidpp_descriptor));
+ }
retval = hid_parse_report(hid, rdesc, rsize);
kfree(rdesc);
@@ -866,7 +1431,7 @@ static int logi_dj_dj_event(struct hid_device *hdev,
* so ignore those reports too.
*/
if (dj_report->device_index != DJ_RECEIVER_INDEX)
- dev_err(&hdev->dev, "%s: invalid device index:%d\n",
+ hid_err(hdev, "%s: invalid device index:%d\n",
__func__, dj_report->device_index);
return false;
}
@@ -893,7 +1458,7 @@ static int logi_dj_dj_event(struct hid_device *hdev,
}
break;
default:
- logi_dj_recv_forward_report(djrcv_dev, dj_report);
+ logi_dj_recv_forward_dj(djrcv_dev, dj_report);
}
out:
@@ -907,9 +1472,10 @@ static int logi_dj_hidpp_event(struct hid_device *hdev,
int size)
{
struct dj_receiver_dev *djrcv_dev = hid_get_drvdata(hdev);
- struct dj_report *dj_report = (struct dj_report *) data;
+ struct hidpp_event *hidpp_report = (struct hidpp_event *) data;
+ struct dj_device *dj_dev;
unsigned long flags;
- u8 device_index = dj_report->device_index;
+ u8 device_index = hidpp_report->device_index;
if (device_index == HIDPP_RECEIVER_INDEX) {
/* special case were the device wants to know its unifying
@@ -937,21 +1503,42 @@ static int logi_dj_hidpp_event(struct hid_device *hdev,
* This driver can ignore safely the receiver notifications,
* so ignore those reports too.
*/
- dev_err(&hdev->dev, "%s: invalid device index:%d\n",
- __func__, dj_report->device_index);
+ hid_err(hdev, "%s: invalid device index:%d\n", __func__,
+ hidpp_report->device_index);
return false;
}
spin_lock_irqsave(&djrcv_dev->lock, flags);
- if (!djrcv_dev->paired_dj_devices[device_index])
- /* received an event for an unknown device, bail out */
- goto out;
+ dj_dev = djrcv_dev->paired_dj_devices[device_index];
+
+ /*
+ * With 27 MHz receivers, we do not get an explicit unpair event,
+ * remove the old device if the user has paired a *different* device.
+ */
+ if (djrcv_dev->type == recvr_type_27mhz && dj_dev &&
+ hidpp_report->sub_id == REPORT_TYPE_NOTIF_DEVICE_CONNECTED &&
+ hidpp_report->params[HIDPP_PARAM_PROTO_TYPE] == 0x02 &&
+ hidpp_report->params[HIDPP_PARAM_27MHZ_DEVID] !=
+ dj_dev->hdev->product) {
+ struct dj_workitem workitem = {
+ .device_index = hidpp_report->device_index,
+ .type = WORKITEM_TYPE_UNPAIRED,
+ };
+ kfifo_in(&djrcv_dev->notif_fifo, &workitem, sizeof(workitem));
+ /* logi_hidpp_recv_queue_notif will queue the work */
+ dj_dev = NULL;
+ }
- logi_dj_recv_forward_hidpp(djrcv_dev->paired_dj_devices[device_index],
- data, size);
+ if (dj_dev) {
+ logi_dj_recv_forward_report(dj_dev, data, size);
+ } else {
+ if (hidpp_report->sub_id == REPORT_TYPE_NOTIF_DEVICE_CONNECTED)
+ logi_hidpp_recv_queue_notif(hdev, hidpp_report);
+ else
+ logi_dj_recv_queue_unknown_work(djrcv_dev);
+ }
-out:
spin_unlock_irqrestore(&djrcv_dev->lock, flags);
return false;
@@ -961,112 +1548,176 @@ static int logi_dj_raw_event(struct hid_device *hdev,
struct hid_report *report, u8 *data,
int size)
{
+ struct dj_receiver_dev *djrcv_dev = hid_get_drvdata(hdev);
dbg_hid("%s, size:%d\n", __func__, size);
+ if (!djrcv_dev)
+ return 0;
+
+ if (!hdev->report_enum[HID_INPUT_REPORT].numbered) {
+
+ if (djrcv_dev->unnumbered_application == HID_GD_KEYBOARD) {
+ /*
+ * For the keyboard, we can reuse the same report by
+ * using the second byte which is constant in the USB
+ * HID report descriptor.
+ */
+ data[1] = data[0];
+ data[0] = REPORT_TYPE_KEYBOARD;
+
+ logi_dj_recv_forward_input_report(hdev, data, size);
+
+ /* restore previous state */
+ data[0] = data[1];
+ data[1] = 0;
+ }
+ /* The 27 MHz mouse-only receiver sends unnumbered mouse data */
+ if (djrcv_dev->unnumbered_application == HID_GD_MOUSE &&
+ size == 6) {
+ u8 mouse_report[7];
+
+ /* Prepend report id */
+ mouse_report[0] = REPORT_TYPE_MOUSE;
+ memcpy(mouse_report + 1, data, 6);
+ logi_dj_recv_forward_input_report(hdev, mouse_report, 7);
+ }
+
+ return false;
+ }
+
switch (data[0]) {
case REPORT_ID_DJ_SHORT:
if (size != DJREPORT_SHORT_LENGTH) {
- dev_err(&hdev->dev, "DJ report of bad size (%d)", size);
+ hid_err(hdev, "Short DJ report bad size (%d)", size);
+ return false;
+ }
+ return logi_dj_dj_event(hdev, report, data, size);
+ case REPORT_ID_DJ_LONG:
+ if (size != DJREPORT_LONG_LENGTH) {
+ hid_err(hdev, "Long DJ report bad size (%d)", size);
return false;
}
return logi_dj_dj_event(hdev, report, data, size);
case REPORT_ID_HIDPP_SHORT:
if (size != HIDPP_REPORT_SHORT_LENGTH) {
- dev_err(&hdev->dev,
- "Short HID++ report of bad size (%d)", size);
+ hid_err(hdev, "Short HID++ report bad size (%d)", size);
return false;
}
return logi_dj_hidpp_event(hdev, report, data, size);
case REPORT_ID_HIDPP_LONG:
if (size != HIDPP_REPORT_LONG_LENGTH) {
- dev_err(&hdev->dev,
- "Long HID++ report of bad size (%d)", size);
+ hid_err(hdev, "Long HID++ report bad size (%d)", size);
return false;
}
return logi_dj_hidpp_event(hdev, report, data, size);
}
+ logi_dj_recv_forward_input_report(hdev, data, size);
+
return false;
}
static int logi_dj_probe(struct hid_device *hdev,
const struct hid_device_id *id)
{
- struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
+ struct hid_report_enum *rep_enum;
+ struct hid_report *rep;
struct dj_receiver_dev *djrcv_dev;
+ struct usb_interface *intf;
+ unsigned int no_dj_interfaces = 0;
+ bool has_hidpp = false;
+ unsigned long flags;
int retval;
- dbg_hid("%s called for ifnum %d\n", __func__,
- intf->cur_altsetting->desc.bInterfaceNumber);
+ /*
+ * Call to usbhid to fetch the HID descriptors of the current
+ * interface subsequently call to the hid/hid-core to parse the
+ * fetched descriptors.
+ */
+ retval = hid_parse(hdev);
+ if (retval) {
+ hid_err(hdev, "%s: parse failed\n", __func__);
+ return retval;
+ }
- /* Ignore interfaces 0 and 1, they will not carry any data, dont create
- * any hid_device for them */
- if (intf->cur_altsetting->desc.bInterfaceNumber !=
- LOGITECH_DJ_INTERFACE_NUMBER) {
- dbg_hid("%s: ignoring ifnum %d\n", __func__,
- intf->cur_altsetting->desc.bInterfaceNumber);
+ /*
+ * Some KVMs add an extra interface for e.g. mouse emulation. If we
+ * treat these as logitech-dj interfaces then this causes input events
+ * reported through this extra interface to not be reported correctly.
+ * To avoid this, we treat these as generic-hid devices.
+ */
+ switch (id->driver_data) {
+ 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_27mhz: no_dj_interfaces = 2; break;
+ case recvr_type_bluetooth: no_dj_interfaces = 2; break;
+ }
+ if (hid_is_using_ll_driver(hdev, &usb_hid_driver)) {
+ intf = to_usb_interface(hdev->dev.parent);
+ if (intf && intf->altsetting->desc.bInterfaceNumber >=
+ no_dj_interfaces) {
+ hdev->quirks |= HID_QUIRK_INPUT_PER_APP;
+ return hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+ }
+ }
+
+ rep_enum = &hdev->report_enum[HID_INPUT_REPORT];
+
+ /* no input reports, bail out */
+ if (list_empty(&rep_enum->report_list))
return -ENODEV;
+
+ /*
+ * Check for the HID++ application.
+ * Note: we should theoretically check for HID++ and DJ
+ * collections, but this will do.
+ */
+ list_for_each_entry(rep, &rep_enum->report_list, list) {
+ if (rep->application == 0xff000001)
+ has_hidpp = true;
}
- /* Treat interface 2 */
+ /*
+ * Ignore interfaces without DJ/HID++ collection, they will not carry
+ * any data, dont create any hid_device for them.
+ */
+ if (!has_hidpp && id->driver_data == recvr_type_dj)
+ return -ENODEV;
- djrcv_dev = kzalloc(sizeof(struct dj_receiver_dev), GFP_KERNEL);
+ /* get the current application attached to the node */
+ rep = list_first_entry(&rep_enum->report_list, struct hid_report, list);
+ djrcv_dev = dj_get_receiver_dev(hdev, id->driver_data,
+ rep->application, has_hidpp);
if (!djrcv_dev) {
- dev_err(&hdev->dev,
- "%s:failed allocating dj_receiver_dev\n", __func__);
+ hid_err(hdev, "%s: dj_get_receiver_dev failed\n", __func__);
return -ENOMEM;
}
- djrcv_dev->hdev = hdev;
- INIT_WORK(&djrcv_dev->work, delayedwork_callback);
- spin_lock_init(&djrcv_dev->lock);
- if (kfifo_alloc(&djrcv_dev->notif_fifo,
- DJ_MAX_NUMBER_NOTIFICATIONS * sizeof(struct dj_report),
- GFP_KERNEL)) {
- dev_err(&hdev->dev,
- "%s:failed allocating notif_fifo\n", __func__);
- kfree(djrcv_dev);
- return -ENOMEM;
- }
- hid_set_drvdata(hdev, djrcv_dev);
- /* Call to usbhid to fetch the HID descriptors of interface 2 and
- * subsequently call to the hid/hid-core to parse the fetched
- * descriptors, this will in turn create the hidraw and hiddev nodes
- * for interface 2 of the receiver */
- retval = hid_parse(hdev);
- if (retval) {
- dev_err(&hdev->dev,
- "%s:parse of interface 2 failed\n", __func__);
- goto hid_parse_fail;
- }
-
- if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, REPORT_ID_DJ_SHORT,
- 0, DJREPORT_SHORT_LENGTH - 1)) {
- retval = -ENODEV;
- goto hid_parse_fail;
- }
+ if (!rep_enum->numbered)
+ djrcv_dev->unnumbered_application = rep->application;
/* Starts the usb device and connects to upper interfaces hiddev and
* hidraw */
- retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+ retval = hid_hw_start(hdev, HID_CONNECT_HIDRAW|HID_CONNECT_HIDDEV);
if (retval) {
- dev_err(&hdev->dev,
- "%s:hid_hw_start returned error\n", __func__);
+ hid_err(hdev, "%s: hid_hw_start returned error\n", __func__);
goto hid_hw_start_fail;
}
- retval = logi_dj_recv_switch_to_dj_mode(djrcv_dev, 0);
- if (retval < 0) {
- dev_err(&hdev->dev,
- "%s:logi_dj_recv_switch_to_dj_mode returned error:%d\n",
- __func__, retval);
- goto switch_to_dj_mode_fail;
+ 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 is enabling the polling urb on the IN endpoint */
retval = hid_hw_open(hdev);
if (retval < 0) {
- dev_err(&hdev->dev, "%s:hid_hw_open returned error:%d\n",
+ hid_err(hdev, "%s: hid_hw_open returned error:%d\n",
__func__, retval);
goto llopen_failed;
}
@@ -1074,11 +1725,16 @@ static int logi_dj_probe(struct hid_device *hdev,
/* Allow incoming packets to arrive: */
hid_device_io_start(hdev);
- retval = logi_dj_recv_query_paired_devices(djrcv_dev);
- if (retval < 0) {
- dev_err(&hdev->dev, "%s:logi_dj_recv_query_paired_devices "
- "error:%d\n", __func__, retval);
- goto logi_dj_recv_query_paired_devices_failed;
+ if (has_hidpp) {
+ 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);
+ goto logi_dj_recv_query_paired_devices_failed;
+ }
}
return retval;
@@ -1091,12 +1747,8 @@ switch_to_dj_mode_fail:
hid_hw_stop(hdev);
hid_hw_start_fail:
-hid_parse_fail:
- kfifo_free(&djrcv_dev->notif_fifo);
- kfree(djrcv_dev);
- hid_set_drvdata(hdev, NULL);
+ dj_put_receiver_dev(hdev);
return retval;
-
}
#ifdef CONFIG_PM
@@ -1105,10 +1757,12 @@ 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) {
- dev_err(&hdev->dev,
- "%s:logi_dj_recv_switch_to_dj_mode returned error:%d\n",
+ hid_err(hdev, "%s: logi_dj_recv_switch_to_dj_mode returned error:%d\n",
__func__, retval);
}
@@ -1120,39 +1774,83 @@ static void logi_dj_remove(struct hid_device *hdev)
{
struct dj_receiver_dev *djrcv_dev = hid_get_drvdata(hdev);
struct dj_device *dj_dev;
+ unsigned long flags;
int i;
dbg_hid("%s\n", __func__);
+ if (!djrcv_dev)
+ return hid_hw_stop(hdev);
+
+ /*
+ * This ensures that if the work gets requeued from another
+ * interface of the same receiver it will be a no-op.
+ */
+ spin_lock_irqsave(&djrcv_dev->lock, flags);
+ djrcv_dev->ready = false;
+ spin_unlock_irqrestore(&djrcv_dev->lock, flags);
+
cancel_work_sync(&djrcv_dev->work);
hid_hw_close(hdev);
hid_hw_stop(hdev);
- /* I suppose that at this point the only context that can access
- * the djrecv_data is this thread as the work item is guaranteed to
- * have finished and no more raw_event callbacks should arrive after
- * the remove callback was triggered so no locks are put around the
- * code below */
+ /*
+ * For proper operation we need access to all interfaces, so we destroy
+ * the paired devices when we're unbound from any interface.
+ *
+ * Note we may still be bound to other interfaces, sharing the same
+ * djrcv_dev, so we need locking here.
+ */
for (i = 0; i < (DJ_MAX_PAIRED_DEVICES + DJ_DEVICE_INDEX_MIN); i++) {
+ spin_lock_irqsave(&djrcv_dev->lock, flags);
dj_dev = djrcv_dev->paired_dj_devices[i];
+ djrcv_dev->paired_dj_devices[i] = NULL;
+ spin_unlock_irqrestore(&djrcv_dev->lock, flags);
if (dj_dev != NULL) {
hid_destroy_device(dj_dev->hdev);
kfree(dj_dev);
- djrcv_dev->paired_dj_devices[i] = NULL;
}
}
- kfifo_free(&djrcv_dev->notif_fifo);
- kfree(djrcv_dev);
- hid_set_drvdata(hdev, NULL);
+ dj_put_receiver_dev(hdev);
}
static const struct hid_device_id logi_dj_receivers[] = {
{HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
- USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER)},
+ USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER),
+ .driver_data = recvr_type_dj},
{HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
- USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER_2)},
+ USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER_2),
+ .driver_data = recvr_type_dj},
+ { /* Logitech Nano (non DJ) receiver */
+ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
+ USB_DEVICE_ID_LOGITECH_NANO_RECEIVER),
+ .driver_data = recvr_type_hidpp},
+ { /* Logitech Nano (non DJ) receiver */
+ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
+ USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_2),
+ .driver_data = recvr_type_hidpp},
+ { /* Logitech gaming receiver (0xc539) */
+ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
+ USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_GAMING),
+ .driver_data = recvr_type_gaming_hidpp},
+ { /* Logitech 27 MHz HID++ 1.0 receiver (0xc517) */
+ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
+ USB_DEVICE_ID_S510_RECEIVER_2),
+ .driver_data = recvr_type_27mhz},
+ { /* Logitech 27 MHz HID++ 1.0 mouse-only receiver (0xc51b) */
+ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
+ USB_DEVICE_ID_LOGITECH_27MHZ_MOUSE_RECEIVER),
+ .driver_data = recvr_type_27mhz},
+ { /* Logitech MX5000 HID++ / bluetooth receiver keyboard intf. */
+ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
+ 0xc70e),
+ .driver_data = recvr_type_bluetooth},
+ { /* Logitech MX5000 HID++ / bluetooth receiver mouse intf. */
+ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
+ 0xc70a),
+ .driver_data = recvr_type_bluetooth},
{}
};
diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c
index 15ed6177a7a3..72fc9c0566db 100644
--- a/drivers/hid/hid-logitech-hidpp.c
+++ b/drivers/hid/hid-logitech-hidpp.c
@@ -51,7 +51,11 @@ MODULE_PARM_DESC(disable_tap_to_click,
#define HIDPP_REPORT_SHORT_LENGTH 7
#define HIDPP_REPORT_LONG_LENGTH 20
-#define HIDPP_REPORT_VERY_LONG_LENGTH 64
+#define HIDPP_REPORT_VERY_LONG_MAX_LENGTH 64
+
+#define HIDPP_SUB_ID_CONSUMER_VENDOR_KEYS 0x03
+#define HIDPP_SUB_ID_ROLLER 0x05
+#define HIDPP_SUB_ID_MOUSE_EXTRA_BTNS 0x06
#define HIDPP_QUIRK_CLASS_WTP BIT(0)
#define HIDPP_QUIRK_CLASS_M560 BIT(1)
@@ -68,6 +72,13 @@ MODULE_PARM_DESC(disable_tap_to_click,
#define HIDPP_QUIRK_HI_RES_SCROLL_1P0 BIT(26)
#define HIDPP_QUIRK_HI_RES_SCROLL_X2120 BIT(27)
#define HIDPP_QUIRK_HI_RES_SCROLL_X2121 BIT(28)
+#define HIDPP_QUIRK_HIDPP_WHEELS BIT(29)
+#define HIDPP_QUIRK_HIDPP_EXTRA_MOUSE_BTNS BIT(30)
+#define HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS BIT(31)
+
+/* These are just aliases for now */
+#define HIDPP_QUIRK_KBD_SCROLL_WHEEL HIDPP_QUIRK_HIDPP_WHEELS
+#define HIDPP_QUIRK_KBD_ZOOM_WHEEL HIDPP_QUIRK_HIDPP_WHEELS
/* Convenience constant to check for any high-res support. */
#define HIDPP_QUIRK_HI_RES_SCROLL (HIDPP_QUIRK_HI_RES_SCROLL_1P0 | \
@@ -106,13 +117,13 @@ MODULE_PARM_DESC(disable_tap_to_click,
struct fap {
u8 feature_index;
u8 funcindex_clientid;
- u8 params[HIDPP_REPORT_VERY_LONG_LENGTH - 4U];
+ u8 params[HIDPP_REPORT_VERY_LONG_MAX_LENGTH - 4U];
};
struct rap {
u8 sub_id;
u8 reg_address;
- u8 params[HIDPP_REPORT_VERY_LONG_LENGTH - 4U];
+ u8 params[HIDPP_REPORT_VERY_LONG_MAX_LENGTH - 4U];
};
struct hidpp_report {
@@ -149,7 +160,6 @@ struct hidpp_battery {
* @last_time: last event time, used to reset remainder after inactivity
*/
struct hidpp_scroll_counter {
- struct input_dev *dev;
int wheel_multiplier;
int remainder;
int direction;
@@ -158,10 +168,12 @@ struct hidpp_scroll_counter {
struct hidpp_device {
struct hid_device *hid_dev;
+ struct input_dev *input;
struct mutex send_mutex;
void *send_receive_buf;
char *name; /* will never be NULL and should not be freed */
wait_queue_head_t wait;
+ int very_long_report_length;
bool answer_available;
u8 protocol_major;
u8 protocol_minor;
@@ -206,8 +218,6 @@ static int __hidpp_send_report(struct hid_device *hdev,
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
int fields_count, ret;
- hidpp = hid_get_drvdata(hdev);
-
switch (hidpp_report->report_id) {
case REPORT_ID_HIDPP_SHORT:
fields_count = HIDPP_REPORT_SHORT_LENGTH;
@@ -216,7 +226,7 @@ static int __hidpp_send_report(struct hid_device *hdev,
fields_count = HIDPP_REPORT_LONG_LENGTH;
break;
case REPORT_ID_HIDPP_VERY_LONG:
- fields_count = HIDPP_REPORT_VERY_LONG_LENGTH;
+ fields_count = hidpp->very_long_report_length;
break;
default:
return -ENODEV;
@@ -342,7 +352,7 @@ static int hidpp_send_rap_command_sync(struct hidpp_device *hidpp_dev,
max_count = HIDPP_REPORT_LONG_LENGTH - 4;
break;
case REPORT_ID_HIDPP_VERY_LONG:
- max_count = HIDPP_REPORT_VERY_LONG_LENGTH - 4;
+ max_count = hidpp_dev->very_long_report_length - 4;
break;
default:
return -EINVAL;
@@ -434,14 +444,15 @@ static void hidpp_prefix_name(char **name, int name_length)
* emit low-resolution scroll events when appropriate for
* backwards-compatibility with userspace input libraries.
*/
-static void hidpp_scroll_counter_handle_scroll(struct hidpp_scroll_counter *counter,
+static void hidpp_scroll_counter_handle_scroll(struct input_dev *input_dev,
+ struct hidpp_scroll_counter *counter,
int hi_res_value)
{
int low_res_value, remainder, direction;
unsigned long long now, previous;
hi_res_value = hi_res_value * 120/counter->wheel_multiplier;
- input_report_rel(counter->dev, REL_WHEEL_HI_RES, hi_res_value);
+ input_report_rel(input_dev, REL_WHEEL_HI_RES, hi_res_value);
remainder = counter->remainder;
direction = hi_res_value > 0 ? 1 : -1;
@@ -475,7 +486,7 @@ static void hidpp_scroll_counter_handle_scroll(struct hidpp_scroll_counter *coun
low_res_value = remainder / 120;
if (low_res_value == 0)
low_res_value = (hi_res_value > 0 ? 1 : -1);
- input_report_rel(counter->dev, REL_WHEEL, low_res_value);
+ input_report_rel(input_dev, REL_WHEEL, low_res_value);
remainder -= low_res_value * 120;
}
counter->remainder = remainder;
@@ -491,14 +502,16 @@ static void hidpp_scroll_counter_handle_scroll(struct hidpp_scroll_counter *coun
#define HIDPP_GET_LONG_REGISTER 0x83
/**
- * hidpp10_set_register_bit() - Sets a single bit in a HID++ 1.0 register.
+ * hidpp10_set_register - Modify a HID++ 1.0 register.
* @hidpp_dev: the device to set the register on.
* @register_address: the address of the register to modify.
* @byte: the byte of the register to modify. Should be less than 3.
+ * @mask: mask of the bits to modify
+ * @value: new values for the bits in mask
* Return: 0 if successful, otherwise a negative error code.
*/
-static int hidpp10_set_register_bit(struct hidpp_device *hidpp_dev,
- u8 register_address, u8 byte, u8 bit)
+static int hidpp10_set_register(struct hidpp_device *hidpp_dev,
+ u8 register_address, u8 byte, u8 mask, u8 value)
{
struct hidpp_report response;
int ret;
@@ -514,7 +527,8 @@ static int hidpp10_set_register_bit(struct hidpp_device *hidpp_dev,
memcpy(params, response.rap.params, 3);
- params[byte] |= BIT(bit);
+ params[byte] &= ~mask;
+ params[byte] |= value & mask;
return hidpp_send_rap_command_sync(hidpp_dev,
REPORT_ID_HIDPP_SHORT,
@@ -523,20 +537,28 @@ static int hidpp10_set_register_bit(struct hidpp_device *hidpp_dev,
params, 3, &response);
}
-
-#define HIDPP_REG_GENERAL 0x00
+#define HIDPP_REG_ENABLE_REPORTS 0x00
+#define HIDPP_ENABLE_CONSUMER_REPORT BIT(0)
+#define HIDPP_ENABLE_WHEEL_REPORT BIT(2)
+#define HIDPP_ENABLE_MOUSE_EXTRA_BTN_REPORT BIT(3)
+#define HIDPP_ENABLE_BAT_REPORT BIT(4)
+#define HIDPP_ENABLE_HWHEEL_REPORT BIT(5)
static int hidpp10_enable_battery_reporting(struct hidpp_device *hidpp_dev)
{
- return hidpp10_set_register_bit(hidpp_dev, HIDPP_REG_GENERAL, 0, 4);
+ return hidpp10_set_register(hidpp_dev, HIDPP_REG_ENABLE_REPORTS, 0,
+ HIDPP_ENABLE_BAT_REPORT, HIDPP_ENABLE_BAT_REPORT);
}
#define HIDPP_REG_FEATURES 0x01
+#define HIDPP_ENABLE_SPECIAL_BUTTON_FUNC BIT(1)
+#define HIDPP_ENABLE_FAST_SCROLL BIT(6)
/* On HID++ 1.0 devices, high-res scroll was called "scrolling acceleration". */
static int hidpp10_enable_scrolling_acceleration(struct hidpp_device *hidpp_dev)
{
- return hidpp10_set_register_bit(hidpp_dev, HIDPP_REG_FEATURES, 0, 6);
+ return hidpp10_set_register(hidpp_dev, HIDPP_REG_FEATURES, 0,
+ HIDPP_ENABLE_FAST_SCROLL, HIDPP_ENABLE_FAST_SCROLL);
}
#define HIDPP_REG_BATTERY_STATUS 0x07
@@ -741,6 +763,9 @@ static char *hidpp_unifying_get_name(struct hidpp_device *hidpp_dev)
if (2 + len > sizeof(response.rap.params))
return NULL;
+ if (len < 4) /* logitech devices are usually at least Xddd */
+ return NULL;
+
name = kzalloc(len + 1, GFP_KERNEL);
if (!name)
return NULL;
@@ -836,18 +861,21 @@ static int hidpp_root_get_feature(struct hidpp_device *hidpp, u16 feature,
static int hidpp_root_get_protocol_version(struct hidpp_device *hidpp)
{
+ const u8 ping_byte = 0x5a;
+ u8 ping_data[3] = { 0, 0, ping_byte };
struct hidpp_report response;
int ret;
- ret = hidpp_send_fap_command_sync(hidpp,
+ ret = hidpp_send_rap_command_sync(hidpp,
+ REPORT_ID_HIDPP_SHORT,
HIDPP_PAGE_ROOT_IDX,
CMD_ROOT_GET_PROTOCOL_VERSION,
- NULL, 0, &response);
+ ping_data, sizeof(ping_data), &response);
if (ret == HIDPP_ERROR_INVALID_SUBID) {
hidpp->protocol_major = 1;
hidpp->protocol_minor = 0;
- return 0;
+ goto print_version;
}
/* the device might not be connected */
@@ -862,21 +890,19 @@ static int hidpp_root_get_protocol_version(struct hidpp_device *hidpp)
if (ret)
return ret;
- hidpp->protocol_major = response.fap.params[0];
- hidpp->protocol_minor = response.fap.params[1];
-
- return ret;
-}
+ if (response.rap.params[2] != ping_byte) {
+ hid_err(hidpp->hid_dev, "%s: ping mismatch 0x%02x != 0x%02x\n",
+ __func__, response.rap.params[2], ping_byte);
+ return -EPROTO;
+ }
-static bool hidpp_is_connected(struct hidpp_device *hidpp)
-{
- int ret;
+ hidpp->protocol_major = response.rap.params[0];
+ hidpp->protocol_minor = response.rap.params[1];
- ret = hidpp_root_get_protocol_version(hidpp);
- if (!ret)
- hid_dbg(hidpp->hid_dev, "HID++ %u.%u device connected.\n",
- hidpp->protocol_major, hidpp->protocol_minor);
- return ret == 0;
+print_version:
+ hid_info(hidpp->hid_dev, "HID++ %u.%u device connected.\n",
+ hidpp->protocol_major, hidpp->protocol_minor);
+ return 0;
}
/* -------------------------------------------------------------------------- */
@@ -932,7 +958,7 @@ static int hidpp_devicenametype_get_device_name(struct hidpp_device *hidpp,
switch (response.report_id) {
case REPORT_ID_HIDPP_VERY_LONG:
- count = HIDPP_REPORT_VERY_LONG_LENGTH - 4;
+ count = hidpp->very_long_report_length - 4;
break;
case REPORT_ID_HIDPP_LONG:
count = HIDPP_REPORT_LONG_LENGTH - 4;
@@ -1012,7 +1038,11 @@ static int hidpp_map_battery_level(int capacity)
{
if (capacity < 11)
return POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
- else if (capacity < 31)
+ /*
+ * The spec says this should be < 31 but some devices report 30
+ * with brand new batteries and Windows reports 30 as "Good".
+ */
+ else if (capacity < 30)
return POWER_SUPPLY_CAPACITY_LEVEL_LOW;
else if (capacity < 81)
return POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
@@ -2111,6 +2141,13 @@ static int hidpp_ff_init(struct hidpp_device *hidpp, u8 feature_index)
kfree(data);
return -ENOMEM;
}
+ data->wq = create_singlethread_workqueue("hidpp-ff-sendqueue");
+ if (!data->wq) {
+ kfree(data->effect_ids);
+ kfree(data);
+ return -ENOMEM;
+ }
+
data->hidpp = hidpp;
data->feature_index = feature_index;
data->version = version;
@@ -2155,7 +2192,6 @@ static int hidpp_ff_init(struct hidpp_device *hidpp, u8 feature_index)
/* ignore boost value at response.fap.params[2] */
/* init the hardware command queue */
- data->wq = create_singlethread_workqueue("hidpp-ff-sendqueue");
atomic_set(&data->workqueue_size, 0);
/* initialize with zero autocenter to get wheel in usable state */
@@ -2205,7 +2241,6 @@ static int hidpp_ff_deinit(struct hid_device *hid)
#define WTP_MANUAL_RESOLUTION 39
struct wtp_data {
- struct input_dev *input;
u16 x_size, y_size;
u8 finger_count;
u8 mt_feature_index;
@@ -2223,7 +2258,7 @@ static int wtp_input_mapping(struct hid_device *hdev, struct hid_input *hi,
}
static void wtp_populate_input(struct hidpp_device *hidpp,
- struct input_dev *input_dev, bool origin_is_hid_core)
+ struct input_dev *input_dev)
{
struct wtp_data *wd = hidpp->private_data;
@@ -2249,31 +2284,30 @@ static void wtp_populate_input(struct hidpp_device *hidpp,
input_mt_init_slots(input_dev, wd->maxcontacts, INPUT_MT_POINTER |
INPUT_MT_DROP_UNUSED);
-
- wd->input = input_dev;
}
-static void wtp_touch_event(struct wtp_data *wd,
+static void wtp_touch_event(struct hidpp_device *hidpp,
struct hidpp_touchpad_raw_xy_finger *touch_report)
{
+ struct wtp_data *wd = hidpp->private_data;
int slot;
if (!touch_report->finger_id || touch_report->contact_type)
/* no actual data */
return;
- slot = input_mt_get_slot_by_key(wd->input, touch_report->finger_id);
+ slot = input_mt_get_slot_by_key(hidpp->input, touch_report->finger_id);
- input_mt_slot(wd->input, slot);
- input_mt_report_slot_state(wd->input, MT_TOOL_FINGER,
+ input_mt_slot(hidpp->input, slot);
+ input_mt_report_slot_state(hidpp->input, MT_TOOL_FINGER,
touch_report->contact_status);
if (touch_report->contact_status) {
- input_event(wd->input, EV_ABS, ABS_MT_POSITION_X,
+ input_event(hidpp->input, EV_ABS, ABS_MT_POSITION_X,
touch_report->x);
- input_event(wd->input, EV_ABS, ABS_MT_POSITION_Y,
+ input_event(hidpp->input, EV_ABS, ABS_MT_POSITION_Y,
wd->flip_y ? wd->y_size - touch_report->y :
touch_report->y);
- input_event(wd->input, EV_ABS, ABS_MT_PRESSURE,
+ input_event(hidpp->input, EV_ABS, ABS_MT_PRESSURE,
touch_report->area);
}
}
@@ -2281,19 +2315,18 @@ static void wtp_touch_event(struct wtp_data *wd,
static void wtp_send_raw_xy_event(struct hidpp_device *hidpp,
struct hidpp_touchpad_raw_xy *raw)
{
- struct wtp_data *wd = hidpp->private_data;
int i;
for (i = 0; i < 2; i++)
- wtp_touch_event(wd, &(raw->fingers[i]));
+ wtp_touch_event(hidpp, &(raw->fingers[i]));
if (raw->end_of_frame &&
!(hidpp->quirks & HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS))
- input_event(wd->input, EV_KEY, BTN_LEFT, raw->button);
+ input_event(hidpp->input, EV_KEY, BTN_LEFT, raw->button);
if (raw->end_of_frame || raw->finger_count <= 2) {
- input_mt_sync_frame(wd->input);
- input_sync(wd->input);
+ input_mt_sync_frame(hidpp->input);
+ input_sync(hidpp->input);
}
}
@@ -2343,7 +2376,7 @@ static int wtp_raw_event(struct hid_device *hdev, u8 *data, int size)
struct hidpp_report *report = (struct hidpp_report *)data;
struct hidpp_touchpad_raw_xy raw;
- if (!wd || !wd->input)
+ if (!wd || !hidpp->input)
return 1;
switch (data[0]) {
@@ -2354,11 +2387,11 @@ static int wtp_raw_event(struct hid_device *hdev, u8 *data, int size)
return 1;
}
if (hidpp->quirks & HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS) {
- input_event(wd->input, EV_KEY, BTN_LEFT,
+ input_event(hidpp->input, EV_KEY, BTN_LEFT,
!!(data[1] & 0x01));
- input_event(wd->input, EV_KEY, BTN_RIGHT,
+ input_event(hidpp->input, EV_KEY, BTN_RIGHT,
!!(data[1] & 0x02));
- input_sync(wd->input);
+ input_sync(hidpp->input);
return 0;
} else {
if (size < 21)
@@ -2476,10 +2509,6 @@ static int wtp_connect(struct hid_device *hdev, bool connected)
static const u8 m560_config_parameter[] = {0x00, 0xaf, 0x03};
-struct m560_private_data {
- struct input_dev *input;
-};
-
/* how buttons are mapped in the report */
#define M560_MOUSE_BTN_LEFT 0x01
#define M560_MOUSE_BTN_RIGHT 0x02
@@ -2507,28 +2536,12 @@ static int m560_send_config_command(struct hid_device *hdev, bool connected)
);
}
-static int m560_allocate(struct hid_device *hdev)
-{
- struct hidpp_device *hidpp = hid_get_drvdata(hdev);
- struct m560_private_data *d;
-
- d = devm_kzalloc(&hdev->dev, sizeof(struct m560_private_data),
- GFP_KERNEL);
- if (!d)
- return -ENOMEM;
-
- hidpp->private_data = d;
-
- return 0;
-};
-
static int m560_raw_event(struct hid_device *hdev, u8 *data, int size)
{
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
- struct m560_private_data *mydata = hidpp->private_data;
/* sanity check */
- if (!mydata || !mydata->input) {
+ if (!hidpp->input) {
hid_err(hdev, "error in parameter\n");
return -EINVAL;
}
@@ -2555,24 +2568,24 @@ static int m560_raw_event(struct hid_device *hdev, u8 *data, int size)
switch (data[5]) {
case 0xaf:
- input_report_key(mydata->input, BTN_MIDDLE, 1);
+ input_report_key(hidpp->input, BTN_MIDDLE, 1);
break;
case 0xb0:
- input_report_key(mydata->input, BTN_FORWARD, 1);
+ input_report_key(hidpp->input, BTN_FORWARD, 1);
break;
case 0xae:
- input_report_key(mydata->input, BTN_BACK, 1);
+ input_report_key(hidpp->input, BTN_BACK, 1);
break;
case 0x00:
- input_report_key(mydata->input, BTN_BACK, 0);
- input_report_key(mydata->input, BTN_FORWARD, 0);
- input_report_key(mydata->input, BTN_MIDDLE, 0);
+ input_report_key(hidpp->input, BTN_BACK, 0);
+ input_report_key(hidpp->input, BTN_FORWARD, 0);
+ input_report_key(hidpp->input, BTN_MIDDLE, 0);
break;
default:
hid_err(hdev, "error in report\n");
return 0;
}
- input_sync(mydata->input);
+ input_sync(hidpp->input);
} else if (data[0] == 0x02) {
/*
@@ -2586,58 +2599,55 @@ static int m560_raw_event(struct hid_device *hdev, u8 *data, int size)
int v;
- input_report_key(mydata->input, BTN_LEFT,
+ input_report_key(hidpp->input, BTN_LEFT,
!!(data[1] & M560_MOUSE_BTN_LEFT));
- input_report_key(mydata->input, BTN_RIGHT,
+ input_report_key(hidpp->input, BTN_RIGHT,
!!(data[1] & M560_MOUSE_BTN_RIGHT));
if (data[1] & M560_MOUSE_BTN_WHEEL_LEFT) {
- input_report_rel(mydata->input, REL_HWHEEL, -1);
- input_report_rel(mydata->input, REL_HWHEEL_HI_RES,
+ input_report_rel(hidpp->input, REL_HWHEEL, -1);
+ input_report_rel(hidpp->input, REL_HWHEEL_HI_RES,
-120);
} else if (data[1] & M560_MOUSE_BTN_WHEEL_RIGHT) {
- input_report_rel(mydata->input, REL_HWHEEL, 1);
- input_report_rel(mydata->input, REL_HWHEEL_HI_RES,
+ input_report_rel(hidpp->input, REL_HWHEEL, 1);
+ input_report_rel(hidpp->input, REL_HWHEEL_HI_RES,
120);
}
v = hid_snto32(hid_field_extract(hdev, data+3, 0, 12), 12);
- input_report_rel(mydata->input, REL_X, v);
+ input_report_rel(hidpp->input, REL_X, v);
v = hid_snto32(hid_field_extract(hdev, data+3, 12, 12), 12);
- input_report_rel(mydata->input, REL_Y, v);
+ input_report_rel(hidpp->input, REL_Y, v);
v = hid_snto32(data[6], 8);
- hidpp_scroll_counter_handle_scroll(
- &hidpp->vertical_wheel_counter, v);
+ if (v != 0)
+ hidpp_scroll_counter_handle_scroll(hidpp->input,
+ &hidpp->vertical_wheel_counter, v);
- input_sync(mydata->input);
+ input_sync(hidpp->input);
}
return 1;
}
static void m560_populate_input(struct hidpp_device *hidpp,
- struct input_dev *input_dev, bool origin_is_hid_core)
+ struct input_dev *input_dev)
{
- struct m560_private_data *mydata = hidpp->private_data;
-
- mydata->input = input_dev;
-
- __set_bit(EV_KEY, mydata->input->evbit);
- __set_bit(BTN_MIDDLE, mydata->input->keybit);
- __set_bit(BTN_RIGHT, mydata->input->keybit);
- __set_bit(BTN_LEFT, mydata->input->keybit);
- __set_bit(BTN_BACK, mydata->input->keybit);
- __set_bit(BTN_FORWARD, mydata->input->keybit);
+ __set_bit(EV_KEY, input_dev->evbit);
+ __set_bit(BTN_MIDDLE, input_dev->keybit);
+ __set_bit(BTN_RIGHT, input_dev->keybit);
+ __set_bit(BTN_LEFT, input_dev->keybit);
+ __set_bit(BTN_BACK, input_dev->keybit);
+ __set_bit(BTN_FORWARD, input_dev->keybit);
- __set_bit(EV_REL, mydata->input->evbit);
- __set_bit(REL_X, mydata->input->relbit);
- __set_bit(REL_Y, mydata->input->relbit);
- __set_bit(REL_WHEEL, mydata->input->relbit);
- __set_bit(REL_HWHEEL, mydata->input->relbit);
- __set_bit(REL_WHEEL_HI_RES, mydata->input->relbit);
- __set_bit(REL_HWHEEL_HI_RES, mydata->input->relbit);
+ __set_bit(EV_REL, input_dev->evbit);
+ __set_bit(REL_X, input_dev->relbit);
+ __set_bit(REL_Y, input_dev->relbit);
+ __set_bit(REL_WHEEL, input_dev->relbit);
+ __set_bit(REL_HWHEEL, input_dev->relbit);
+ __set_bit(REL_WHEEL_HI_RES, input_dev->relbit);
+ __set_bit(REL_HWHEEL_HI_RES, input_dev->relbit);
}
static int m560_input_mapping(struct hid_device *hdev, struct hid_input *hi,
@@ -2740,6 +2750,175 @@ static int g920_get_config(struct hidpp_device *hidpp)
}
/* -------------------------------------------------------------------------- */
+/* HID++1.0 devices which use HID++ reports for their wheels */
+/* -------------------------------------------------------------------------- */
+static int hidpp10_wheel_connect(struct hidpp_device *hidpp)
+{
+ return hidpp10_set_register(hidpp, HIDPP_REG_ENABLE_REPORTS, 0,
+ HIDPP_ENABLE_WHEEL_REPORT | HIDPP_ENABLE_HWHEEL_REPORT,
+ HIDPP_ENABLE_WHEEL_REPORT | HIDPP_ENABLE_HWHEEL_REPORT);
+}
+
+static int hidpp10_wheel_raw_event(struct hidpp_device *hidpp,
+ u8 *data, int size)
+{
+ s8 value, hvalue;
+
+ if (!hidpp->input)
+ return -EINVAL;
+
+ if (size < 7)
+ return 0;
+
+ if (data[0] != REPORT_ID_HIDPP_SHORT || data[2] != HIDPP_SUB_ID_ROLLER)
+ return 0;
+
+ value = data[3];
+ hvalue = data[4];
+
+ input_report_rel(hidpp->input, REL_WHEEL, value);
+ input_report_rel(hidpp->input, REL_WHEEL_HI_RES, value * 120);
+ input_report_rel(hidpp->input, REL_HWHEEL, hvalue);
+ input_report_rel(hidpp->input, REL_HWHEEL_HI_RES, hvalue * 120);
+ input_sync(hidpp->input);
+
+ return 1;
+}
+
+static void hidpp10_wheel_populate_input(struct hidpp_device *hidpp,
+ struct input_dev *input_dev)
+{
+ __set_bit(EV_REL, input_dev->evbit);
+ __set_bit(REL_WHEEL, input_dev->relbit);
+ __set_bit(REL_WHEEL_HI_RES, input_dev->relbit);
+ __set_bit(REL_HWHEEL, input_dev->relbit);
+ __set_bit(REL_HWHEEL_HI_RES, input_dev->relbit);
+}
+
+/* -------------------------------------------------------------------------- */
+/* HID++1.0 mice which use HID++ reports for extra mouse buttons */
+/* -------------------------------------------------------------------------- */
+static int hidpp10_extra_mouse_buttons_connect(struct hidpp_device *hidpp)
+{
+ return hidpp10_set_register(hidpp, HIDPP_REG_ENABLE_REPORTS, 0,
+ HIDPP_ENABLE_MOUSE_EXTRA_BTN_REPORT,
+ HIDPP_ENABLE_MOUSE_EXTRA_BTN_REPORT);
+}
+
+static int hidpp10_extra_mouse_buttons_raw_event(struct hidpp_device *hidpp,
+ u8 *data, int size)
+{
+ int i;
+
+ if (!hidpp->input)
+ return -EINVAL;
+
+ if (size < 7)
+ return 0;
+
+ if (data[0] != REPORT_ID_HIDPP_SHORT ||
+ data[2] != HIDPP_SUB_ID_MOUSE_EXTRA_BTNS)
+ return 0;
+
+ /*
+ * Buttons are either delivered through the regular mouse report *or*
+ * through the extra buttons report. At least for button 6 how it is
+ * delivered differs per receiver firmware version. Even receivers with
+ * the same usb-id show different behavior, so we handle both cases.
+ */
+ for (i = 0; i < 8; i++)
+ input_report_key(hidpp->input, BTN_MOUSE + i,
+ (data[3] & (1 << i)));
+
+ /* Some mice report events on button 9+, use BTN_MISC */
+ for (i = 0; i < 8; i++)
+ input_report_key(hidpp->input, BTN_MISC + i,
+ (data[4] & (1 << i)));
+
+ input_sync(hidpp->input);
+ return 1;
+}
+
+static void hidpp10_extra_mouse_buttons_populate_input(
+ struct hidpp_device *hidpp, struct input_dev *input_dev)
+{
+ /* BTN_MOUSE - BTN_MOUSE+7 are set already by the descriptor */
+ __set_bit(BTN_0, input_dev->keybit);
+ __set_bit(BTN_1, input_dev->keybit);
+ __set_bit(BTN_2, input_dev->keybit);
+ __set_bit(BTN_3, input_dev->keybit);
+ __set_bit(BTN_4, input_dev->keybit);
+ __set_bit(BTN_5, input_dev->keybit);
+ __set_bit(BTN_6, input_dev->keybit);
+ __set_bit(BTN_7, input_dev->keybit);
+}
+
+/* -------------------------------------------------------------------------- */
+/* HID++1.0 kbds which only report 0x10xx consumer usages through sub-id 0x03 */
+/* -------------------------------------------------------------------------- */
+
+/* Find the consumer-page input report desc and change Maximums to 0x107f */
+static u8 *hidpp10_consumer_keys_report_fixup(struct hidpp_device *hidpp,
+ u8 *_rdesc, unsigned int *rsize)
+{
+ /* Note 0 terminated so we can use strnstr to search for this. */
+ const char consumer_rdesc_start[] = {
+ 0x05, 0x0C, /* USAGE_PAGE (Consumer Devices) */
+ 0x09, 0x01, /* USAGE (Consumer Control) */
+ 0xA1, 0x01, /* COLLECTION (Application) */
+ 0x85, 0x03, /* REPORT_ID = 3 */
+ 0x75, 0x10, /* REPORT_SIZE (16) */
+ 0x95, 0x02, /* REPORT_COUNT (2) */
+ 0x15, 0x01, /* LOGICAL_MIN (1) */
+ 0x26, 0x00 /* LOGICAL_MAX (... */
+ };
+ char *consumer_rdesc, *rdesc = (char *)_rdesc;
+ unsigned int size;
+
+ consumer_rdesc = strnstr(rdesc, consumer_rdesc_start, *rsize);
+ size = *rsize - (consumer_rdesc - rdesc);
+ if (consumer_rdesc && size >= 25) {
+ consumer_rdesc[15] = 0x7f;
+ consumer_rdesc[16] = 0x10;
+ consumer_rdesc[20] = 0x7f;
+ consumer_rdesc[21] = 0x10;
+ }
+ return _rdesc;
+}
+
+static int hidpp10_consumer_keys_connect(struct hidpp_device *hidpp)
+{
+ return hidpp10_set_register(hidpp, HIDPP_REG_ENABLE_REPORTS, 0,
+ HIDPP_ENABLE_CONSUMER_REPORT,
+ HIDPP_ENABLE_CONSUMER_REPORT);
+}
+
+static int hidpp10_consumer_keys_raw_event(struct hidpp_device *hidpp,
+ u8 *data, int size)
+{
+ u8 consumer_report[5];
+
+ if (size < 7)
+ return 0;
+
+ if (data[0] != REPORT_ID_HIDPP_SHORT ||
+ data[2] != HIDPP_SUB_ID_CONSUMER_VENDOR_KEYS)
+ return 0;
+
+ /*
+ * Build a normal consumer report (3) out of the data, this detour
+ * is necessary to get some keyboards to report their 0x10xx usages.
+ */
+ consumer_report[0] = 0x03;
+ memcpy(&consumer_report[1], &data[3], 4);
+ /* We are called from atomic context */
+ hid_report_raw_event(hidpp->hid_dev, HID_INPUT_REPORT,
+ consumer_report, 5, 1);
+
+ return 1;
+}
+
+/* -------------------------------------------------------------------------- */
/* High-resolution scroll wheels */
/* -------------------------------------------------------------------------- */
@@ -2774,12 +2953,31 @@ static int hi_res_scroll_enable(struct hidpp_device *hidpp)
/* Generic HID++ devices */
/* -------------------------------------------------------------------------- */
+static u8 *hidpp_report_fixup(struct hid_device *hdev, u8 *rdesc,
+ unsigned int *rsize)
+{
+ struct hidpp_device *hidpp = hid_get_drvdata(hdev);
+
+ if (!hidpp)
+ return rdesc;
+
+ /* For 27 MHz keyboards the quirk gets set after hid_parse. */
+ if (hdev->group == HID_GROUP_LOGITECH_27MHZ_DEVICE ||
+ (hidpp->quirks & HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS))
+ rdesc = hidpp10_consumer_keys_report_fixup(hidpp, rdesc, rsize);
+
+ return rdesc;
+}
+
static int hidpp_input_mapping(struct hid_device *hdev, struct hid_input *hi,
struct hid_field *field, struct hid_usage *usage,
unsigned long **bit, int *max)
{
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
+ if (!hidpp)
+ return 0;
+
if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)
return wtp_input_mapping(hdev, hi, field, usage, bit, max);
else if (hidpp->quirks & HIDPP_QUIRK_CLASS_M560 &&
@@ -2795,6 +2993,9 @@ static int hidpp_input_mapped(struct hid_device *hdev, struct hid_input *hi,
{
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
+ if (!hidpp)
+ return 0;
+
/* Ensure that Logitech G920 is not given a default fuzz/flat value */
if (hidpp->quirks & HIDPP_QUIRK_CLASS_G920) {
if (usage->type == EV_ABS && (usage->code == ABS_X ||
@@ -2809,15 +3010,20 @@ static int hidpp_input_mapped(struct hid_device *hdev, struct hid_input *hi,
static void hidpp_populate_input(struct hidpp_device *hidpp,
- struct input_dev *input, bool origin_is_hid_core)
+ struct input_dev *input)
{
+ hidpp->input = input;
+
if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)
- wtp_populate_input(hidpp, input, origin_is_hid_core);
+ wtp_populate_input(hidpp, input);
else if (hidpp->quirks & HIDPP_QUIRK_CLASS_M560)
- m560_populate_input(hidpp, input, origin_is_hid_core);
+ m560_populate_input(hidpp, input);
- if (hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL)
- hidpp->vertical_wheel_counter.dev = input;
+ if (hidpp->quirks & HIDPP_QUIRK_HIDPP_WHEELS)
+ hidpp10_wheel_populate_input(hidpp, input);
+
+ if (hidpp->quirks & HIDPP_QUIRK_HIDPP_EXTRA_MOUSE_BTNS)
+ hidpp10_extra_mouse_buttons_populate_input(hidpp, input);
}
static int hidpp_input_configured(struct hid_device *hdev,
@@ -2826,7 +3032,10 @@ static int hidpp_input_configured(struct hid_device *hdev,
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
struct input_dev *input = hidinput->input;
- hidpp_populate_input(hidpp, input, true);
+ if (!hidpp)
+ return 0;
+
+ hidpp_populate_input(hidpp, input);
return 0;
}
@@ -2886,6 +3095,24 @@ static int hidpp_raw_hidpp_event(struct hidpp_device *hidpp, u8 *data,
return ret;
}
+ if (hidpp->quirks & HIDPP_QUIRK_HIDPP_WHEELS) {
+ ret = hidpp10_wheel_raw_event(hidpp, data, size);
+ if (ret != 0)
+ return ret;
+ }
+
+ if (hidpp->quirks & HIDPP_QUIRK_HIDPP_EXTRA_MOUSE_BTNS) {
+ ret = hidpp10_extra_mouse_buttons_raw_event(hidpp, data, size);
+ if (ret != 0)
+ return ret;
+ }
+
+ if (hidpp->quirks & HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS) {
+ ret = hidpp10_consumer_keys_raw_event(hidpp, data, size);
+ if (ret != 0)
+ return ret;
+ }
+
return 0;
}
@@ -2895,10 +3122,13 @@ static int hidpp_raw_event(struct hid_device *hdev, struct hid_report *report,
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
int ret = 0;
+ if (!hidpp)
+ return 0;
+
/* Generic HID++ processing. */
switch (data[0]) {
case REPORT_ID_HIDPP_VERY_LONG:
- if (size != HIDPP_REPORT_VERY_LONG_LENGTH) {
+ if (size != hidpp->very_long_report_length) {
hid_err(hdev, "received hid++ report of bad size (%d)",
size);
return 1;
@@ -2943,17 +3173,22 @@ static int hidpp_event(struct hid_device *hdev, struct hid_field *field,
* restriction imposed in hidpp_usages.
*/
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
- struct hidpp_scroll_counter *counter = &hidpp->vertical_wheel_counter;
+ struct hidpp_scroll_counter *counter;
+
+ if (!hidpp)
+ return 0;
+
+ counter = &hidpp->vertical_wheel_counter;
/* A scroll event may occur before the multiplier has been retrieved or
* the input device set, or high-res scroll enabling may fail. In such
* cases we must return early (falling back to default behaviour) to
* avoid a crash in hidpp_scroll_counter_handle_scroll.
*/
if (!(hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL) || value == 0
- || counter->dev == NULL || counter->wheel_multiplier == 0)
+ || hidpp->input == NULL || counter->wheel_multiplier == 0)
return 0;
- hidpp_scroll_counter_handle_scroll(counter, value);
+ hidpp_scroll_counter_handle_scroll(hidpp->input, counter, value);
return 1;
}
@@ -3125,32 +3360,45 @@ static void hidpp_connect_event(struct hidpp_device *hidpp)
return;
}
+ if (hidpp->quirks & HIDPP_QUIRK_HIDPP_WHEELS) {
+ ret = hidpp10_wheel_connect(hidpp);
+ if (ret)
+ return;
+ }
+
+ if (hidpp->quirks & HIDPP_QUIRK_HIDPP_EXTRA_MOUSE_BTNS) {
+ ret = hidpp10_extra_mouse_buttons_connect(hidpp);
+ if (ret)
+ return;
+ }
+
+ if (hidpp->quirks & HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS) {
+ ret = hidpp10_consumer_keys_connect(hidpp);
+ if (ret)
+ return;
+ }
+
/* the device is already connected, we can ask for its name and
* protocol */
if (!hidpp->protocol_major) {
- ret = !hidpp_is_connected(hidpp);
+ ret = hidpp_root_get_protocol_version(hidpp);
if (ret) {
hid_err(hdev, "Can not get the protocol version.\n");
return;
}
- hid_info(hdev, "HID++ %u.%u device connected.\n",
- hidpp->protocol_major, hidpp->protocol_minor);
}
if (hidpp->name == hdev->name && hidpp->protocol_major >= 2) {
name = hidpp_get_device_name(hidpp);
- if (!name) {
- hid_err(hdev,
- "unable to retrieve the name of the device");
- return;
- }
-
- devm_name = devm_kasprintf(&hdev->dev, GFP_KERNEL, "%s", name);
- kfree(name);
- if (!devm_name)
- return;
+ if (name) {
+ devm_name = devm_kasprintf(&hdev->dev, GFP_KERNEL,
+ "%s", name);
+ kfree(name);
+ if (!devm_name)
+ return;
- hidpp->name = devm_name;
+ hidpp->name = devm_name;
+ }
}
hidpp_initialize_battery(hidpp);
@@ -3181,7 +3429,7 @@ static void hidpp_connect_event(struct hidpp_device *hidpp)
return;
}
- hidpp_populate_input(hidpp, input, false);
+ hidpp_populate_input(hidpp, input);
ret = input_register_device(input);
if (ret)
@@ -3201,6 +3449,60 @@ static const struct attribute_group ps_attribute_group = {
.attrs = sysfs_attrs
};
+static int hidpp_get_report_length(struct hid_device *hdev, int id)
+{
+ struct hid_report_enum *re;
+ struct hid_report *report;
+
+ re = &(hdev->report_enum[HID_OUTPUT_REPORT]);
+ report = re->report_id_hash[id];
+ if (!report)
+ return 0;
+
+ return report->field[0]->report_count + 1;
+}
+
+static bool hidpp_validate_report(struct hid_device *hdev, int id,
+ int expected_length, bool optional)
+{
+ int report_length;
+
+ if (id >= HID_MAX_IDS || id < 0) {
+ hid_err(hdev, "invalid HID report id %u\n", id);
+ return false;
+ }
+
+ report_length = hidpp_get_report_length(hdev, id);
+ if (!report_length)
+ return optional;
+
+ if (report_length < expected_length) {
+ hid_warn(hdev, "not enough values in hidpp report %d\n", id);
+ return false;
+ }
+
+ return true;
+}
+
+static bool hidpp_validate_device(struct hid_device *hdev)
+{
+ return hidpp_validate_report(hdev, REPORT_ID_HIDPP_SHORT,
+ HIDPP_REPORT_SHORT_LENGTH, false) &&
+ hidpp_validate_report(hdev, REPORT_ID_HIDPP_LONG,
+ HIDPP_REPORT_LONG_LENGTH, true);
+}
+
+static bool hidpp_application_equals(struct hid_device *hdev,
+ unsigned int application)
+{
+ struct list_head *report_list;
+ struct hid_report *report;
+
+ report_list = &hdev->report_enum[HID_INPUT_REPORT].report_list;
+ report = list_first_entry_or_null(report_list, struct hid_report, list);
+ return report && report->application == application;
+}
+
static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
struct hidpp_device *hidpp;
@@ -3208,20 +3510,48 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
bool connected;
unsigned int connect_mask = HID_CONNECT_DEFAULT;
- hidpp = devm_kzalloc(&hdev->dev, sizeof(struct hidpp_device),
- GFP_KERNEL);
+ /* report_fixup needs drvdata to be set before we call hid_parse */
+ hidpp = devm_kzalloc(&hdev->dev, sizeof(*hidpp), GFP_KERNEL);
if (!hidpp)
return -ENOMEM;
hidpp->hid_dev = hdev;
hidpp->name = hdev->name;
+ hidpp->quirks = id->driver_data;
hid_set_drvdata(hdev, hidpp);
- hidpp->quirks = id->driver_data;
+ ret = hid_parse(hdev);
+ if (ret) {
+ hid_err(hdev, "%s:parse failed\n", __func__);
+ return ret;
+ }
+
+ /*
+ * Make sure the device is HID++ capable, otherwise treat as generic HID
+ */
+ if (!hidpp_validate_device(hdev)) {
+ hid_set_drvdata(hdev, NULL);
+ devm_kfree(&hdev->dev, hidpp);
+ return hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+ }
+
+ hidpp->very_long_report_length =
+ hidpp_get_report_length(hdev, REPORT_ID_HIDPP_VERY_LONG);
+ if (hidpp->very_long_report_length > HIDPP_REPORT_VERY_LONG_MAX_LENGTH)
+ hidpp->very_long_report_length = HIDPP_REPORT_VERY_LONG_MAX_LENGTH;
if (id->group == HID_GROUP_LOGITECH_DJ_DEVICE)
hidpp->quirks |= HIDPP_QUIRK_UNIFYING;
+ if (id->group == HID_GROUP_LOGITECH_27MHZ_DEVICE &&
+ hidpp_application_equals(hdev, HID_GD_MOUSE))
+ hidpp->quirks |= HIDPP_QUIRK_HIDPP_WHEELS |
+ HIDPP_QUIRK_HIDPP_EXTRA_MOUSE_BTNS;
+
+ if (id->group == HID_GROUP_LOGITECH_27MHZ_DEVICE &&
+ hidpp_application_equals(hdev, HID_GD_KEYBOARD))
+ hidpp->quirks |= HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS;
+
if (disable_raw_mode) {
hidpp->quirks &= ~HIDPP_QUIRK_CLASS_WTP;
hidpp->quirks &= ~HIDPP_QUIRK_NO_HIDINPUT;
@@ -3230,15 +3560,11 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP) {
ret = wtp_allocate(hdev, id);
if (ret)
- goto allocate_fail;
- } else if (hidpp->quirks & HIDPP_QUIRK_CLASS_M560) {
- ret = m560_allocate(hdev);
- if (ret)
- goto allocate_fail;
+ return ret;
} else if (hidpp->quirks & HIDPP_QUIRK_CLASS_K400) {
ret = k400_allocate(hdev);
if (ret)
- goto allocate_fail;
+ return ret;
}
INIT_WORK(&hidpp->work, delayed_work_cb);
@@ -3251,93 +3577,79 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
hid_warn(hdev, "Cannot allocate sysfs group for %s\n",
hdev->name);
- ret = hid_parse(hdev);
+ /*
+ * Plain USB connections need to actually call start and open
+ * on the transport driver to allow incoming data.
+ */
+ ret = hid_hw_start(hdev, 0);
if (ret) {
- hid_err(hdev, "%s:parse failed\n", __func__);
- goto hid_parse_fail;
+ hid_err(hdev, "hw start failed\n");
+ goto hid_hw_start_fail;
}
- if (hidpp->quirks & HIDPP_QUIRK_NO_HIDINPUT)
- connect_mask &= ~HID_CONNECT_HIDINPUT;
-
- if (hidpp->quirks & HIDPP_QUIRK_CLASS_G920) {
- ret = hid_hw_start(hdev, connect_mask);
- if (ret) {
- hid_err(hdev, "hw start failed\n");
- goto hid_hw_start_fail;
- }
- ret = hid_hw_open(hdev);
- if (ret < 0) {
- dev_err(&hdev->dev, "%s:hid_hw_open returned error:%d\n",
- __func__, ret);
- hid_hw_stop(hdev);
- goto hid_hw_start_fail;
- }
+ ret = hid_hw_open(hdev);
+ if (ret < 0) {
+ dev_err(&hdev->dev, "%s:hid_hw_open returned error:%d\n",
+ __func__, ret);
+ hid_hw_stop(hdev);
+ goto hid_hw_open_fail;
}
-
/* Allow incoming packets */
hid_device_io_start(hdev);
if (hidpp->quirks & HIDPP_QUIRK_UNIFYING)
hidpp_unifying_init(hidpp);
- connected = hidpp_is_connected(hidpp);
+ connected = hidpp_root_get_protocol_version(hidpp) == 0;
atomic_set(&hidpp->connected, connected);
if (!(hidpp->quirks & HIDPP_QUIRK_UNIFYING)) {
if (!connected) {
ret = -ENODEV;
hid_err(hdev, "Device not connected");
- goto hid_hw_open_failed;
+ goto hid_hw_init_fail;
}
- hid_info(hdev, "HID++ %u.%u device connected.\n",
- hidpp->protocol_major, hidpp->protocol_minor);
-
hidpp_overwrite_name(hdev);
}
if (connected && (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)) {
ret = wtp_get_config(hidpp);
if (ret)
- goto hid_hw_open_failed;
+ goto hid_hw_init_fail;
} else if (connected && (hidpp->quirks & HIDPP_QUIRK_CLASS_G920)) {
ret = g920_get_config(hidpp);
if (ret)
- goto hid_hw_open_failed;
+ goto hid_hw_init_fail;
}
- /* Block incoming packets */
- hid_device_io_stop(hdev);
+ hidpp_connect_event(hidpp);
- if (!(hidpp->quirks & HIDPP_QUIRK_CLASS_G920)) {
- ret = hid_hw_start(hdev, connect_mask);
- if (ret) {
- hid_err(hdev, "%s:hid_hw_start returned error\n", __func__);
- goto hid_hw_start_fail;
- }
- }
+ /* Reset the HID node state */
+ hid_device_io_stop(hdev);
+ hid_hw_close(hdev);
+ hid_hw_stop(hdev);
- /* Allow incoming packets */
- hid_device_io_start(hdev);
+ if (hidpp->quirks & HIDPP_QUIRK_NO_HIDINPUT)
+ connect_mask &= ~HID_CONNECT_HIDINPUT;
- hidpp_connect_event(hidpp);
+ /* Now export the actual inputs and hidraw nodes to the world */
+ ret = hid_hw_start(hdev, connect_mask);
+ if (ret) {
+ hid_err(hdev, "%s:hid_hw_start returned error\n", __func__);
+ goto hid_hw_start_fail;
+ }
return ret;
-hid_hw_open_failed:
- hid_device_io_stop(hdev);
- if (hidpp->quirks & HIDPP_QUIRK_CLASS_G920) {
- hid_hw_close(hdev);
- hid_hw_stop(hdev);
- }
+hid_hw_init_fail:
+ hid_hw_close(hdev);
+hid_hw_open_fail:
+ hid_hw_stop(hdev);
hid_hw_start_fail:
-hid_parse_fail:
sysfs_remove_group(&hdev->dev.kobj, &ps_attribute_group);
cancel_work_sync(&hidpp->work);
mutex_destroy(&hidpp->send_mutex);
-allocate_fail:
- hid_set_drvdata(hdev, NULL);
return ret;
}
@@ -3345,12 +3657,14 @@ static void hidpp_remove(struct hid_device *hdev)
{
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
+ if (!hidpp)
+ return hid_hw_stop(hdev);
+
sysfs_remove_group(&hdev->dev.kobj, &ps_attribute_group);
- if (hidpp->quirks & HIDPP_QUIRK_CLASS_G920) {
+ if (hidpp->quirks & HIDPP_QUIRK_CLASS_G920)
hidpp_ff_deinit(hdev);
- hid_hw_close(hdev);
- }
+
hid_hw_stop(hdev);
cancel_work_sync(&hidpp->work);
mutex_destroy(&hidpp->send_mutex);
@@ -3360,6 +3674,10 @@ static void hidpp_remove(struct hid_device *hdev)
HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE, \
USB_VENDOR_ID_LOGITECH, (product))
+#define L27MHZ_DEVICE(product) \
+ HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_27MHZ_DEVICE, \
+ USB_VENDOR_ID_LOGITECH, (product))
+
static const struct hid_device_id hidpp_devices[] = {
{ /* wireless touchpad */
LDJ_DEVICE(0x4011),
@@ -3411,11 +3729,37 @@ static const struct hid_device_id hidpp_devices[] = {
{ /* Solar Keyboard Logitech K750 */
LDJ_DEVICE(0x4002),
.driver_data = HIDPP_QUIRK_CLASS_K750 },
+ { /* Keyboard MX5000 (Bluetooth-receiver in HID proxy mode) */
+ LDJ_DEVICE(0xb305),
+ .driver_data = HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS },
{ LDJ_DEVICE(HID_ANY_ID) },
- { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G920_WHEEL),
+ { /* Keyboard LX501 (Y-RR53) */
+ L27MHZ_DEVICE(0x0049),
+ .driver_data = HIDPP_QUIRK_KBD_ZOOM_WHEEL },
+ { /* Keyboard MX3000 (Y-RAM74) */
+ L27MHZ_DEVICE(0x0057),
+ .driver_data = HIDPP_QUIRK_KBD_SCROLL_WHEEL },
+ { /* Keyboard MX3200 (Y-RAV80) */
+ L27MHZ_DEVICE(0x005c),
+ .driver_data = HIDPP_QUIRK_KBD_ZOOM_WHEEL },
+
+ { L27MHZ_DEVICE(HID_ANY_ID) },
+
+ { /* Logitech G403 Gaming Mouse over USB */
+ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC082) },
+ { /* Logitech G700 Gaming Mouse over USB */
+ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC06B) },
+ { /* Logitech G900 Gaming Mouse over USB */
+ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC081) },
+ { /* Logitech G920 Wheel over USB */
+ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G920_WHEEL),
.driver_data = HIDPP_QUIRK_CLASS_G920 | HIDPP_QUIRK_FORCE_OUTPUT_REPORTS},
+
+ { /* MX5000 keyboard over Bluetooth */
+ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb305),
+ .driver_data = HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS },
{}
};
@@ -3429,6 +3773,7 @@ static const struct hid_usage_id hidpp_usages[] = {
static struct hid_driver hidpp_driver = {
.name = "logitech-hidpp-device",
.id_table = hidpp_devices,
+ .report_fixup = hidpp_report_fixup,
.probe = hidpp_probe,
.remove = hidpp_remove,
.raw_event = hidpp_raw_event,
diff --git a/drivers/hid/hid-picolcd_core.c b/drivers/hid/hid-picolcd_core.c
index c1b29a9eb41a..482c24f0e078 100644
--- a/drivers/hid/hid-picolcd_core.c
+++ b/drivers/hid/hid-picolcd_core.c
@@ -28,6 +28,7 @@
#include <linux/completion.h>
#include <linux/uaccess.h>
#include <linux/module.h>
+#include <linux/string.h>
#include "hid-picolcd.h"
@@ -275,27 +276,20 @@ static ssize_t picolcd_operation_mode_store(struct device *dev,
{
struct picolcd_data *data = dev_get_drvdata(dev);
struct hid_report *report = NULL;
- size_t cnt = count;
int timeout = data->opmode_delay;
unsigned long flags;
- if (cnt >= 3 && strncmp("lcd", buf, 3) == 0) {
+ if (sysfs_streq(buf, "lcd")) {
if (data->status & PICOLCD_BOOTLOADER)
report = picolcd_out_report(REPORT_EXIT_FLASHER, data->hdev);
- buf += 3;
- cnt -= 3;
- } else if (cnt >= 10 && strncmp("bootloader", buf, 10) == 0) {
+ } else if (sysfs_streq(buf, "bootloader")) {
if (!(data->status & PICOLCD_BOOTLOADER))
report = picolcd_out_report(REPORT_EXIT_KEYBOARD, data->hdev);
- buf += 10;
- cnt -= 10;
- }
- if (!report || report->maxfield != 1)
+ } else {
return -EINVAL;
+ }
- while (cnt > 0 && (buf[cnt-1] == '\n' || buf[cnt-1] == '\r'))
- cnt--;
- if (cnt != 0)
+ if (!report || report->maxfield != 1)
return -EINVAL;
spin_lock_irqsave(&data->lock, flags);
diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c
index 953908f2267c..fea7f7ff5ab1 100644
--- a/drivers/hid/hid-quirks.c
+++ b/drivers/hid/hid-quirks.c
@@ -432,7 +432,6 @@ static const struct hid_device_id hid_have_special_driver[] = {
#if IS_ENABLED(CONFIG_HID_LOGITECH)
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER) },
- { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RECEIVER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_DESKTOP) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE) },
@@ -464,13 +463,8 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACENAVIGATOR) },
#endif
#if IS_ENABLED(CONFIG_HID_LOGITECH_HIDPP)
- { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_T651) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G920_WHEEL) },
#endif
-#if IS_ENABLED(CONFIG_HID_LOGITECH_DJ)
- { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER) },
- { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER_2) },
-#endif
#if IS_ENABLED(CONFIG_HID_MAGICMOUSE)
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICMOUSE) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICTRACKPAD) },
@@ -715,7 +709,6 @@ static const struct hid_device_id hid_ignore_list[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_DEALEXTREAME, USB_DEVICE_ID_DEALEXTREAME_RADIO_SI4701) },
{ HID_USB_DEVICE(USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EARTHMATE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EM_LT20) },
- { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, 0x0400) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ETT, USB_DEVICE_ID_TC5UH) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ETT, USB_DEVICE_ID_TC4UM) },
@@ -855,7 +848,7 @@ static const struct hid_device_id hid_ignore_list[] = {
{ }
};
-/**
+/*
* hid_mouse_ignore_list - mouse devices which should not be handled by the hid layer
*
* There are composite devices for which we want to ignore only a certain
@@ -996,6 +989,10 @@ bool hid_ignore(struct hid_device *hdev)
if (hdev->product == 0x0401 &&
strncmp(hdev->name, "ELAN0800", 8) != 0)
return true;
+ /* Same with product id 0x0400 */
+ if (hdev->product == 0x0400 &&
+ strncmp(hdev->name, "QTEC0001", 8) != 0)
+ return true;
break;
}
@@ -1042,7 +1039,7 @@ static struct hid_device_id *hid_exists_dquirk(const struct hid_device *hdev)
}
if (bl_entry != NULL)
- dbg_hid("Found dynamic quirk 0x%lx for HID device 0x%hx:0x%hx\n",
+ dbg_hid("Found dynamic quirk 0x%lx for HID device 0x%04x:0x%04x\n",
bl_entry->driver_data, bl_entry->vendor,
bl_entry->product);
@@ -1209,7 +1206,7 @@ static unsigned long hid_gets_squirk(const struct hid_device *hdev)
quirks |= bl_entry->driver_data;
if (quirks)
- dbg_hid("Found squirk 0x%lx for HID device 0x%hx:0x%hx\n",
+ dbg_hid("Found squirk 0x%lx for HID device 0x%04x:0x%04x\n",
quirks, hdev->vendor, hdev->product);
return quirks;
}
diff --git a/drivers/hid/hid-sensor-custom.c b/drivers/hid/hid-sensor-custom.c
index bb012bc032e0..462e653a7bbb 100644
--- a/drivers/hid/hid-sensor-custom.c
+++ b/drivers/hid/hid-sensor-custom.c
@@ -157,8 +157,7 @@ static int usage_id_cmp(const void *p1, const void *p2)
static ssize_t enable_sensor_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct hid_sensor_custom *sensor_inst = platform_get_drvdata(pdev);
+ struct hid_sensor_custom *sensor_inst = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", sensor_inst->enable);
}
@@ -237,8 +236,7 @@ static ssize_t enable_sensor_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct hid_sensor_custom *sensor_inst = platform_get_drvdata(pdev);
+ struct hid_sensor_custom *sensor_inst = dev_get_drvdata(dev);
int value;
int ret = -EINVAL;
@@ -283,8 +281,7 @@ static const struct attribute_group enable_sensor_attr_group = {
static ssize_t show_value(struct device *dev, struct device_attribute *attr,
char *buf)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct hid_sensor_custom *sensor_inst = platform_get_drvdata(pdev);
+ struct hid_sensor_custom *sensor_inst = dev_get_drvdata(dev);
struct hid_sensor_hub_attribute_info *attribute;
int index, usage, field_index;
char name[HID_CUSTOM_NAME_LENGTH];
@@ -392,8 +389,7 @@ static ssize_t show_value(struct device *dev, struct device_attribute *attr,
static ssize_t store_value(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct hid_sensor_custom *sensor_inst = platform_get_drvdata(pdev);
+ struct hid_sensor_custom *sensor_inst = dev_get_drvdata(dev);
int index, field_index, usage;
char name[HID_CUSTOM_NAME_LENGTH];
int value;
diff --git a/drivers/hid/hid-steam.c b/drivers/hid/hid-steam.c
index 8141cadfca0e..8dae0f9b819e 100644
--- a/drivers/hid/hid-steam.c
+++ b/drivers/hid/hid-steam.c
@@ -499,6 +499,7 @@ static void steam_battery_unregister(struct steam_device *steam)
static int steam_register(struct steam_device *steam)
{
int ret;
+ bool client_opened;
/*
* This function can be called several times in a row with the
@@ -511,9 +512,11 @@ static int steam_register(struct steam_device *steam)
* Unlikely, but getting the serial could fail, and it is not so
* important, so make up a serial number and go on.
*/
+ mutex_lock(&steam->mutex);
if (steam_get_serial(steam) < 0)
strlcpy(steam->serial_no, "XXXXXXXXXX",
sizeof(steam->serial_no));
+ mutex_unlock(&steam->mutex);
hid_info(steam->hdev, "Steam Controller '%s' connected",
steam->serial_no);
@@ -528,13 +531,15 @@ static int steam_register(struct steam_device *steam)
}
mutex_lock(&steam->mutex);
- if (!steam->client_opened) {
+ client_opened = steam->client_opened;
+ if (!client_opened)
steam_set_lizard_mode(steam, lizard_mode);
+ mutex_unlock(&steam->mutex);
+
+ if (!client_opened)
ret = steam_input_register(steam);
- } else {
+ else
ret = 0;
- }
- mutex_unlock(&steam->mutex);
return ret;
}
@@ -630,14 +635,21 @@ static void steam_client_ll_close(struct hid_device *hdev)
{
struct steam_device *steam = hdev->driver_data;
+ unsigned long flags;
+ bool connected;
+
+ spin_lock_irqsave(&steam->lock, flags);
+ connected = steam->connected;
+ spin_unlock_irqrestore(&steam->lock, flags);
+
mutex_lock(&steam->mutex);
steam->client_opened = false;
+ if (connected)
+ steam_set_lizard_mode(steam, lizard_mode);
mutex_unlock(&steam->mutex);
- if (steam->connected) {
- steam_set_lizard_mode(steam, lizard_mode);
+ if (connected)
steam_input_register(steam);
- }
}
static int steam_client_ll_raw_request(struct hid_device *hdev,
diff --git a/drivers/hid/hid-u2fzero.c b/drivers/hid/hid-u2fzero.c
new file mode 100644
index 000000000000..95e0807878c7
--- /dev/null
+++ b/drivers/hid/hid-u2fzero.c
@@ -0,0 +1,374 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * U2F Zero LED and RNG driver
+ *
+ * Copyright 2018 Andrej Shadura <andrew@shadura.me>
+ * Loosely based on drivers/hid/hid-led.c
+ * and drivers/usb/misc/chaoskey.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ */
+
+#include <linux/hid.h>
+#include <linux/hidraw.h>
+#include <linux/hw_random.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/usb.h>
+
+#include "usbhid/usbhid.h"
+#include "hid-ids.h"
+
+#define DRIVER_SHORT "u2fzero"
+
+#define HID_REPORT_SIZE 64
+
+/* We only use broadcast (CID-less) messages */
+#define CID_BROADCAST 0xffffffff
+
+struct u2f_hid_msg {
+ u32 cid;
+ union {
+ struct {
+ u8 cmd;
+ u8 bcnth;
+ u8 bcntl;
+ u8 data[HID_REPORT_SIZE - 7];
+ } init;
+ struct {
+ u8 seq;
+ u8 data[HID_REPORT_SIZE - 5];
+ } cont;
+ };
+} __packed;
+
+struct u2f_hid_report {
+ u8 report_type;
+ struct u2f_hid_msg msg;
+} __packed;
+
+#define U2F_HID_MSG_LEN(f) (size_t)(((f).init.bcnth << 8) + (f).init.bcntl)
+
+/* Custom extensions to the U2FHID protocol */
+#define U2F_CUSTOM_GET_RNG 0x21
+#define U2F_CUSTOM_WINK 0x24
+
+struct u2fzero_device {
+ struct hid_device *hdev;
+ struct urb *urb; /* URB for the RNG data */
+ struct led_classdev ldev; /* Embedded struct for led */
+ struct hwrng hwrng; /* Embedded struct for hwrng */
+ char *led_name;
+ char *rng_name;
+ u8 *buf_out;
+ u8 *buf_in;
+ struct mutex lock;
+ bool present;
+};
+
+static int u2fzero_send(struct u2fzero_device *dev, struct u2f_hid_report *req)
+{
+ int ret;
+
+ mutex_lock(&dev->lock);
+
+ memcpy(dev->buf_out, req, sizeof(struct u2f_hid_report));
+
+ ret = hid_hw_output_report(dev->hdev, dev->buf_out,
+ sizeof(struct u2f_hid_msg));
+
+ mutex_unlock(&dev->lock);
+
+ if (ret < 0)
+ return ret;
+
+ return ret == sizeof(struct u2f_hid_msg) ? 0 : -EMSGSIZE;
+}
+
+struct u2fzero_transfer_context {
+ struct completion done;
+ int status;
+};
+
+static void u2fzero_read_callback(struct urb *urb)
+{
+ struct u2fzero_transfer_context *ctx = urb->context;
+
+ ctx->status = urb->status;
+ complete(&ctx->done);
+}
+
+static int u2fzero_recv(struct u2fzero_device *dev,
+ struct u2f_hid_report *req,
+ struct u2f_hid_msg *resp)
+{
+ int ret;
+ struct hid_device *hdev = dev->hdev;
+ struct u2fzero_transfer_context ctx;
+
+ mutex_lock(&dev->lock);
+
+ memcpy(dev->buf_out, req, sizeof(struct u2f_hid_report));
+
+ dev->urb->context = &ctx;
+ init_completion(&ctx.done);
+
+ ret = usb_submit_urb(dev->urb, GFP_NOIO);
+ if (unlikely(ret)) {
+ hid_err(hdev, "usb_submit_urb failed: %d", ret);
+ goto err;
+ }
+
+ ret = hid_hw_output_report(dev->hdev, dev->buf_out,
+ sizeof(struct u2f_hid_msg));
+
+ if (ret < 0) {
+ hid_err(hdev, "hid_hw_output_report failed: %d", ret);
+ goto err;
+ }
+
+ ret = (wait_for_completion_timeout(
+ &ctx.done, msecs_to_jiffies(USB_CTRL_SET_TIMEOUT)));
+ if (ret < 0) {
+ usb_kill_urb(dev->urb);
+ hid_err(hdev, "urb submission timed out");
+ } else {
+ ret = dev->urb->actual_length;
+ memcpy(resp, dev->buf_in, ret);
+ }
+
+err:
+ mutex_unlock(&dev->lock);
+
+ return ret;
+}
+
+static int u2fzero_blink(struct led_classdev *ldev)
+{
+ struct u2fzero_device *dev = container_of(ldev,
+ struct u2fzero_device, ldev);
+ struct u2f_hid_report req = {
+ .report_type = 0,
+ .msg.cid = CID_BROADCAST,
+ .msg.init = {
+ .cmd = U2F_CUSTOM_WINK,
+ .bcnth = 0,
+ .bcntl = 0,
+ .data = {0},
+ }
+ };
+ return u2fzero_send(dev, &req);
+}
+
+static int u2fzero_brightness_set(struct led_classdev *ldev,
+ enum led_brightness brightness)
+{
+ ldev->brightness = LED_OFF;
+ if (brightness)
+ return u2fzero_blink(ldev);
+ else
+ return 0;
+}
+
+static int u2fzero_rng_read(struct hwrng *rng, void *data,
+ size_t max, bool wait)
+{
+ struct u2fzero_device *dev = container_of(rng,
+ struct u2fzero_device, hwrng);
+ struct u2f_hid_report req = {
+ .report_type = 0,
+ .msg.cid = CID_BROADCAST,
+ .msg.init = {
+ .cmd = U2F_CUSTOM_GET_RNG,
+ .bcnth = 0,
+ .bcntl = 0,
+ .data = {0},
+ }
+ };
+ struct u2f_hid_msg resp;
+ int ret;
+ size_t actual_length;
+
+ if (!dev->present) {
+ hid_dbg(dev->hdev, "device not present");
+ return 0;
+ }
+
+ ret = u2fzero_recv(dev, &req, &resp);
+ if (ret < 0)
+ return 0;
+
+ /* only take the minimum amount of data it is safe to take */
+ actual_length = min3((size_t)ret - offsetof(struct u2f_hid_msg,
+ init.data), U2F_HID_MSG_LEN(resp), max);
+
+ memcpy(data, resp.init.data, actual_length);
+
+ return actual_length;
+}
+
+static int u2fzero_init_led(struct u2fzero_device *dev,
+ unsigned int minor)
+{
+ dev->led_name = devm_kasprintf(&dev->hdev->dev, GFP_KERNEL,
+ "%s%u", DRIVER_SHORT, minor);
+ if (dev->led_name == NULL)
+ return -ENOMEM;
+
+ dev->ldev.name = dev->led_name;
+ dev->ldev.max_brightness = LED_ON;
+ dev->ldev.flags = LED_HW_PLUGGABLE;
+ dev->ldev.brightness_set_blocking = u2fzero_brightness_set;
+
+ return devm_led_classdev_register(&dev->hdev->dev, &dev->ldev);
+}
+
+static int u2fzero_init_hwrng(struct u2fzero_device *dev,
+ unsigned int minor)
+{
+ dev->rng_name = devm_kasprintf(&dev->hdev->dev, GFP_KERNEL,
+ "%s-rng%u", DRIVER_SHORT, minor);
+ if (dev->rng_name == NULL)
+ return -ENOMEM;
+
+ dev->hwrng.name = dev->rng_name;
+ dev->hwrng.read = u2fzero_rng_read;
+ dev->hwrng.quality = 1;
+
+ return devm_hwrng_register(&dev->hdev->dev, &dev->hwrng);
+}
+
+static int u2fzero_fill_in_urb(struct u2fzero_device *dev)
+{
+ struct hid_device *hdev = dev->hdev;
+ struct usb_device *udev;
+ struct usbhid_device *usbhid = hdev->driver_data;
+ unsigned int pipe_in;
+ struct usb_host_endpoint *ep;
+
+ if (dev->hdev->bus != BUS_USB)
+ return -EINVAL;
+
+ udev = hid_to_usb_dev(hdev);
+
+ if (!usbhid->urbout || !usbhid->urbin)
+ return -ENODEV;
+
+ ep = usb_pipe_endpoint(udev, usbhid->urbin->pipe);
+ if (!ep)
+ return -ENODEV;
+
+ dev->urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!dev->urb)
+ return -ENOMEM;
+
+ pipe_in = (usbhid->urbin->pipe & ~(3 << 30)) | (PIPE_INTERRUPT << 30);
+
+ usb_fill_int_urb(dev->urb,
+ udev,
+ pipe_in,
+ dev->buf_in,
+ HID_REPORT_SIZE,
+ u2fzero_read_callback,
+ NULL,
+ ep->desc.bInterval);
+
+ return 0;
+}
+
+static int u2fzero_probe(struct hid_device *hdev,
+ const struct hid_device_id *id)
+{
+ struct u2fzero_device *dev;
+ unsigned int minor;
+ int ret;
+
+ if (!hid_is_using_ll_driver(hdev, &usb_hid_driver))
+ return -EINVAL;
+
+ dev = devm_kzalloc(&hdev->dev, sizeof(*dev), GFP_KERNEL);
+ if (dev == NULL)
+ return -ENOMEM;
+
+ dev->buf_out = devm_kmalloc(&hdev->dev,
+ sizeof(struct u2f_hid_report), GFP_KERNEL);
+ if (dev->buf_out == NULL)
+ return -ENOMEM;
+
+ dev->buf_in = devm_kmalloc(&hdev->dev,
+ sizeof(struct u2f_hid_msg), GFP_KERNEL);
+ if (dev->buf_in == NULL)
+ return -ENOMEM;
+
+ ret = hid_parse(hdev);
+ if (ret)
+ return ret;
+
+ dev->hdev = hdev;
+ hid_set_drvdata(hdev, dev);
+ mutex_init(&dev->lock);
+
+ ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
+ if (ret)
+ return ret;
+
+ u2fzero_fill_in_urb(dev);
+
+ dev->present = true;
+
+ minor = ((struct hidraw *) hdev->hidraw)->minor;
+
+ ret = u2fzero_init_led(dev, minor);
+ if (ret) {
+ hid_hw_stop(hdev);
+ return ret;
+ }
+
+ hid_info(hdev, "U2F Zero LED initialised\n");
+
+ ret = u2fzero_init_hwrng(dev, minor);
+ if (ret) {
+ hid_hw_stop(hdev);
+ return ret;
+ }
+
+ hid_info(hdev, "U2F Zero RNG initialised\n");
+
+ return 0;
+}
+
+static void u2fzero_remove(struct hid_device *hdev)
+{
+ struct u2fzero_device *dev = hid_get_drvdata(hdev);
+
+ mutex_lock(&dev->lock);
+ dev->present = false;
+ mutex_unlock(&dev->lock);
+
+ hid_hw_stop(hdev);
+ usb_poison_urb(dev->urb);
+ usb_free_urb(dev->urb);
+}
+
+static const struct hid_device_id u2fzero_table[] = {
+ { HID_USB_DEVICE(USB_VENDOR_ID_CYGNAL,
+ USB_DEVICE_ID_U2F_ZERO) },
+ { }
+};
+MODULE_DEVICE_TABLE(hid, u2fzero_table);
+
+static struct hid_driver u2fzero_driver = {
+ .name = "hid-" DRIVER_SHORT,
+ .probe = u2fzero_probe,
+ .remove = u2fzero_remove,
+ .id_table = u2fzero_table,
+};
+
+module_hid_driver(u2fzero_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Andrej Shadura <andrew@shadura.me>");
+MODULE_DESCRIPTION("U2F Zero LED and RNG driver");
diff --git a/drivers/hid/hid-uclogic-params.c b/drivers/hid/hid-uclogic-params.c
index 7710d9f957da..0187c9f8fc22 100644
--- a/drivers/hid/hid-uclogic-params.c
+++ b/drivers/hid/hid-uclogic-params.c
@@ -735,10 +735,6 @@ static int uclogic_params_huion_init(struct uclogic_params *params,
goto cleanup;
}
rc = usb_string(udev, 201, ver_ptr, ver_len);
- if (ver_ptr == NULL) {
- rc = -ENOMEM;
- goto cleanup;
- }
if (rc == -EPIPE) {
*ver_ptr = '\0';
} else if (rc < 0) {
diff --git a/drivers/hid/intel-ish-hid/Kconfig b/drivers/hid/intel-ish-hid/Kconfig
index 519e4c8b53c4..786adbc97ac5 100644
--- a/drivers/hid/intel-ish-hid/Kconfig
+++ b/drivers/hid/intel-ish-hid/Kconfig
@@ -14,4 +14,19 @@ config INTEL_ISH_HID
Broxton and Kaby Lake.
Say Y here if you want to support Intel ISH. If unsure, say N.
+
+config INTEL_ISH_FIRMWARE_DOWNLOADER
+ tristate "Host Firmware Load feature for Intel ISH"
+ depends on INTEL_ISH_HID
+ depends on X86
+ help
+ The Integrated Sensor Hub (ISH) enables the kernel to offload
+ sensor polling and algorithm processing to a dedicated low power
+ processor in the chipset.
+
+ The Host Firmware Load feature adds support to load the ISH
+ firmware from host file system at boot.
+
+ Say M here if you want to support Host Firmware Loading feature
+ for Intel ISH. If unsure, say N.
endmenu
diff --git a/drivers/hid/intel-ish-hid/Makefile b/drivers/hid/intel-ish-hid/Makefile
index 825b70af672f..2de97e4b7740 100644
--- a/drivers/hid/intel-ish-hid/Makefile
+++ b/drivers/hid/intel-ish-hid/Makefile
@@ -20,4 +20,7 @@ obj-$(CONFIG_INTEL_ISH_HID) += intel-ishtp-hid.o
intel-ishtp-hid-objs := ishtp-hid.o
intel-ishtp-hid-objs += ishtp-hid-client.o
+obj-$(CONFIG_INTEL_ISH_FIRMWARE_DOWNLOADER) += intel-ishtp-loader.o
+intel-ishtp-loader-objs += ishtp-fw-loader.o
+
ccflags-y += -Idrivers/hid/intel-ish-hid/ishtp
diff --git a/drivers/hid/intel-ish-hid/ipc/hw-ish.h b/drivers/hid/intel-ish-hid/ipc/hw-ish.h
index 08a8327dfd22..523c0cbd44a4 100644
--- a/drivers/hid/intel-ish-hid/ipc/hw-ish.h
+++ b/drivers/hid/intel-ish-hid/ipc/hw-ish.h
@@ -31,6 +31,7 @@
#define CNL_H_DEVICE_ID 0xA37C
#define ICL_MOBILE_DEVICE_ID 0x34FC
#define SPT_H_DEVICE_ID 0xA135
+#define CML_LP_DEVICE_ID 0x02FC
#define REVISION_ID_CHT_A0 0x6
#define REVISION_ID_CHT_Ax_SI 0x0
diff --git a/drivers/hid/intel-ish-hid/ipc/pci-ish.c b/drivers/hid/intel-ish-hid/ipc/pci-ish.c
index a6e1ee744f4d..ac0a179daf23 100644
--- a/drivers/hid/intel-ish-hid/ipc/pci-ish.c
+++ b/drivers/hid/intel-ish-hid/ipc/pci-ish.c
@@ -40,6 +40,7 @@ static const struct pci_device_id ish_pci_tbl[] = {
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, CNL_H_DEVICE_ID)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, ICL_MOBILE_DEVICE_ID)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, SPT_H_DEVICE_ID)},
+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, CML_LP_DEVICE_ID)},
{0, }
};
MODULE_DEVICE_TABLE(pci, ish_pci_tbl);
diff --git a/drivers/hid/intel-ish-hid/ishtp-fw-loader.c b/drivers/hid/intel-ish-hid/ishtp-fw-loader.c
new file mode 100644
index 000000000000..22ba21457035
--- /dev/null
+++ b/drivers/hid/intel-ish-hid/ishtp-fw-loader.c
@@ -0,0 +1,1085 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ISH-TP client driver for ISH firmware loading
+ *
+ * Copyright (c) 2019, Intel Corporation.
+ */
+
+#include <linux/firmware.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/intel-ish-client-if.h>
+#include <linux/property.h>
+#include <asm/cacheflush.h>
+
+/* Number of times we attempt to load the firmware before giving up */
+#define MAX_LOAD_ATTEMPTS 3
+
+/* ISH TX/RX ring buffer pool size */
+#define LOADER_CL_RX_RING_SIZE 1
+#define LOADER_CL_TX_RING_SIZE 1
+
+/*
+ * ISH Shim firmware loader reserves 4 Kb buffer in SRAM. The buffer is
+ * used to temporarily hold the data transferred from host to Shim
+ * firmware loader. Reason for the odd size of 3968 bytes? Each IPC
+ * transfer is 128 bytes (= 4 bytes header + 124 bytes payload). So the
+ * 4 Kb buffer can hold maximum of 32 IPC transfers, which means we can
+ * have a max payload of 3968 bytes (= 32 x 124 payload).
+ */
+#define LOADER_SHIM_IPC_BUF_SIZE 3968
+
+/**
+ * enum ish_loader_commands - ISH loader host commands.
+ * LOADER_CMD_XFER_QUERY Query the Shim firmware loader for
+ * capabilities
+ * LOADER_CMD_XFER_FRAGMENT Transfer one firmware image fragment at a
+ * time. The command may be executed
+ * multiple times until the entire firmware
+ * image is downloaded to SRAM.
+ * LOADER_CMD_START Start executing the main firmware.
+ */
+enum ish_loader_commands {
+ LOADER_CMD_XFER_QUERY = 0,
+ LOADER_CMD_XFER_FRAGMENT,
+ LOADER_CMD_START,
+};
+
+/* Command bit mask */
+#define CMD_MASK GENMASK(6, 0)
+#define IS_RESPONSE BIT(7)
+
+/*
+ * ISH firmware max delay for one transmit failure is 1 Hz,
+ * and firmware will retry 2 times, so 3 Hz is used for timeout.
+ */
+#define ISHTP_SEND_TIMEOUT (3 * HZ)
+
+/*
+ * Loader transfer modes:
+ *
+ * LOADER_XFER_MODE_ISHTP mode uses the existing ISH-TP mechanism to
+ * transfer data. This may use IPC or DMA if supported in firmware.
+ * The buffer size is limited to 4 Kb by the IPC/ISH-TP protocol for
+ * both IPC & DMA (legacy).
+ *
+ * LOADER_XFER_MODE_DIRECT_DMA - firmware loading is a bit different
+ * from the sensor data streaming. Here we download a large (300+ Kb)
+ * image directly to ISH SRAM memory. There is limited benefit of
+ * DMA'ing 300 Kb image in 4 Kb chucks limit. Hence, we introduce
+ * this "direct dma" mode, where we do not use ISH-TP for DMA, but
+ * instead manage the DMA directly in kernel driver and Shim firmware
+ * loader (allocate buffer, break in chucks and transfer). This allows
+ * to overcome 4 Kb limit, and optimize the data flow path in firmware.
+ */
+#define LOADER_XFER_MODE_DIRECT_DMA BIT(0)
+#define LOADER_XFER_MODE_ISHTP BIT(1)
+
+/* ISH Transport Loader client unique GUID */
+static const guid_t loader_ishtp_guid =
+ GUID_INIT(0xc804d06a, 0x55bd, 0x4ea7,
+ 0xad, 0xed, 0x1e, 0x31, 0x22, 0x8c, 0x76, 0xdc);
+
+#define FILENAME_SIZE 256
+
+/*
+ * The firmware loading latency will be minimum if we can DMA the
+ * entire ISH firmware image in one go. This requires that we allocate
+ * a large DMA buffer in kernel, which could be problematic on some
+ * platforms. So here we limit the DMA buffer size via a module_param.
+ * We default to 4 pages, but a customer can set it to higher limit if
+ * deemed appropriate for his platform.
+ */
+static int dma_buf_size_limit = 4 * PAGE_SIZE;
+
+/**
+ * struct loader_msg_hdr - Header for ISH Loader commands.
+ * @command: LOADER_CMD* commands. Bit 7 is the response.
+ * @status: Command response status. Non 0, is error
+ * condition.
+ *
+ * This structure is used as header for every command/data sent/received
+ * between Host driver and ISH Shim firmware loader.
+ */
+struct loader_msg_hdr {
+ u8 command;
+ u8 reserved[2];
+ u8 status;
+} __packed;
+
+struct loader_xfer_query {
+ struct loader_msg_hdr hdr;
+ u32 image_size;
+} __packed;
+
+struct ish_fw_version {
+ u16 major;
+ u16 minor;
+ u16 hotfix;
+ u16 build;
+} __packed;
+
+union loader_version {
+ u32 value;
+ struct {
+ u8 major;
+ u8 minor;
+ u8 hotfix;
+ u8 build;
+ };
+} __packed;
+
+struct loader_capability {
+ u32 max_fw_image_size;
+ u32 xfer_mode;
+ u32 max_dma_buf_size; /* only for dma mode, multiples of cacheline */
+} __packed;
+
+struct shim_fw_info {
+ struct ish_fw_version ish_fw_version;
+ u32 protocol_version;
+ union loader_version ldr_version;
+ struct loader_capability ldr_capability;
+} __packed;
+
+struct loader_xfer_query_response {
+ struct loader_msg_hdr hdr;
+ struct shim_fw_info fw_info;
+} __packed;
+
+struct loader_xfer_fragment {
+ struct loader_msg_hdr hdr;
+ u32 xfer_mode;
+ u32 offset;
+ u32 size;
+ u32 is_last;
+} __packed;
+
+struct loader_xfer_ipc_fragment {
+ struct loader_xfer_fragment fragment;
+ u8 data[] ____cacheline_aligned; /* variable length payload here */
+} __packed;
+
+struct loader_xfer_dma_fragment {
+ struct loader_xfer_fragment fragment;
+ u64 ddr_phys_addr;
+} __packed;
+
+struct loader_start {
+ struct loader_msg_hdr hdr;
+} __packed;
+
+/**
+ * struct response_info - Encapsulate firmware response related
+ * information for passing between function
+ * loader_cl_send() and process_recv() callback.
+ * @data Copy the data received from firmware here.
+ * @max_size Max size allocated for the @data buffer. If the
+ * received data exceeds this value, we log an
+ * error.
+ * @size Actual size of data received from firmware.
+ * @error Returns 0 for success, negative error code for a
+ * failure in function process_recv().
+ * @received Set to true on receiving a valid firmware
+ * response to host command
+ * @wait_queue Wait queue for Host firmware loading where the
+ * client sends message to ISH firmware and waits
+ * for response
+ */
+struct response_info {
+ void *data;
+ size_t max_size;
+ size_t size;
+ int error;
+ bool received;
+ wait_queue_head_t wait_queue;
+};
+
+/**
+ * struct ishtp_cl_data - Encapsulate per ISH-TP Client Data.
+ * @work_ishtp_reset: Work queue for reset handling.
+ * @work_fw_load: Work queue for host firmware loading.
+ * @flag_retry Flag for indicating host firmware loading should
+ * be retried.
+ * @retry_count Count the number of retries.
+ *
+ * This structure is used to store data per client.
+ */
+struct ishtp_cl_data {
+ struct ishtp_cl *loader_ishtp_cl;
+ struct ishtp_cl_device *cl_device;
+
+ /*
+ * Used for passing firmware response information between
+ * loader_cl_send() and process_recv() callback.
+ */
+ struct response_info response;
+
+ struct work_struct work_ishtp_reset;
+ struct work_struct work_fw_load;
+
+ /*
+ * In certain failure scenrios, it makes sense to reset the ISH
+ * subsystem and retry Host firmware loading (e.g. bad message
+ * packet, ENOMEM, etc.). On the other hand, failures due to
+ * protocol mismatch, etc., are not recoverable. We do not
+ * retry them.
+ *
+ * If set, the flag indicates that we should re-try the
+ * particular failure.
+ */
+ bool flag_retry;
+ int retry_count;
+};
+
+#define IPC_FRAGMENT_DATA_PREAMBLE \
+ offsetof(struct loader_xfer_ipc_fragment, data)
+
+#define cl_data_to_dev(client_data) ishtp_device((client_data)->cl_device)
+
+/**
+ * get_firmware_variant() - Gets the filename of firmware image to be
+ * loaded based on platform variant.
+ * @client_data Client data instance.
+ * @filename Returns firmware filename.
+ *
+ * Queries the firmware-name device property string.
+ *
+ * Return: 0 for success, negative error code for failure.
+ */
+static int get_firmware_variant(struct ishtp_cl_data *client_data,
+ char *filename)
+{
+ int rv;
+ const char *val;
+ struct device *devc = ishtp_get_pci_device(client_data->cl_device);
+
+ rv = device_property_read_string(devc, "firmware-name", &val);
+ if (rv < 0) {
+ dev_err(devc,
+ "Error: ISH firmware-name device property required\n");
+ return rv;
+ }
+ return snprintf(filename, FILENAME_SIZE, "intel/%s", val);
+}
+
+/**
+ * loader_cl_send() Send message from host to firmware
+ * @client_data: Client data instance
+ * @out_msg Message buffer to be sent to firmware
+ * @out_size Size of out going message
+ * @in_msg Message buffer where the incoming data copied.
+ * This buffer is allocated by calling
+ * @in_size Max size of incoming message
+ *
+ * Return: Number of bytes copied in the in_msg on success, negative
+ * error code on failure.
+ */
+static int loader_cl_send(struct ishtp_cl_data *client_data,
+ u8 *out_msg, size_t out_size,
+ u8 *in_msg, size_t in_size)
+{
+ int rv;
+ struct loader_msg_hdr *out_hdr = (struct loader_msg_hdr *)out_msg;
+ struct ishtp_cl *loader_ishtp_cl = client_data->loader_ishtp_cl;
+
+ dev_dbg(cl_data_to_dev(client_data),
+ "%s: command=%02lx is_response=%u status=%02x\n",
+ __func__,
+ out_hdr->command & CMD_MASK,
+ out_hdr->command & IS_RESPONSE ? 1 : 0,
+ out_hdr->status);
+
+ /* Setup in coming buffer & size */
+ client_data->response.data = in_msg;
+ client_data->response.max_size = in_size;
+ client_data->response.error = 0;
+ client_data->response.received = false;
+
+ rv = ishtp_cl_send(loader_ishtp_cl, out_msg, out_size);
+ if (rv < 0) {
+ dev_err(cl_data_to_dev(client_data),
+ "ishtp_cl_send error %d\n", rv);
+ return rv;
+ }
+
+ wait_event_interruptible_timeout(client_data->response.wait_queue,
+ client_data->response.received,
+ ISHTP_SEND_TIMEOUT);
+ if (!client_data->response.received) {
+ dev_err(cl_data_to_dev(client_data),
+ "Timed out for response to command=%02lx",
+ out_hdr->command & CMD_MASK);
+ return -ETIMEDOUT;
+ }
+
+ if (client_data->response.error < 0)
+ return client_data->response.error;
+
+ return client_data->response.size;
+}
+
+/**
+ * process_recv() - Receive and parse incoming packet
+ * @loader_ishtp_cl: Client instance to get stats
+ * @rb_in_proc: ISH received message buffer
+ *
+ * Parse the incoming packet. If it is a response packet then it will
+ * update received and wake up the caller waiting to for the response.
+ */
+static void process_recv(struct ishtp_cl *loader_ishtp_cl,
+ struct ishtp_cl_rb *rb_in_proc)
+{
+ struct loader_msg_hdr *hdr;
+ size_t data_len = rb_in_proc->buf_idx;
+ struct ishtp_cl_data *client_data =
+ ishtp_get_client_data(loader_ishtp_cl);
+
+ /* Sanity check */
+ if (!client_data->response.data) {
+ dev_err(cl_data_to_dev(client_data),
+ "Receiving buffer is null. Should be allocated by calling function\n");
+ client_data->response.error = -EINVAL;
+ goto end;
+ }
+
+ if (client_data->response.received) {
+ dev_err(cl_data_to_dev(client_data),
+ "Previous firmware message not yet processed\n");
+ client_data->response.error = -EINVAL;
+ goto end;
+ }
+ /*
+ * All firmware messages have a header. Check buffer size
+ * before accessing elements inside.
+ */
+ if (!rb_in_proc->buffer.data) {
+ dev_warn(cl_data_to_dev(client_data),
+ "rb_in_proc->buffer.data returned null");
+ client_data->response.error = -EBADMSG;
+ goto end;
+ }
+
+ if (data_len < sizeof(struct loader_msg_hdr)) {
+ dev_err(cl_data_to_dev(client_data),
+ "data size %zu is less than header %zu\n",
+ data_len, sizeof(struct loader_msg_hdr));
+ client_data->response.error = -EMSGSIZE;
+ goto end;
+ }
+
+ hdr = (struct loader_msg_hdr *)rb_in_proc->buffer.data;
+
+ dev_dbg(cl_data_to_dev(client_data),
+ "%s: command=%02lx is_response=%u status=%02x\n",
+ __func__,
+ hdr->command & CMD_MASK,
+ hdr->command & IS_RESPONSE ? 1 : 0,
+ hdr->status);
+
+ if (((hdr->command & CMD_MASK) != LOADER_CMD_XFER_QUERY) &&
+ ((hdr->command & CMD_MASK) != LOADER_CMD_XFER_FRAGMENT) &&
+ ((hdr->command & CMD_MASK) != LOADER_CMD_START)) {
+ dev_err(cl_data_to_dev(client_data),
+ "Invalid command=%02lx\n",
+ hdr->command & CMD_MASK);
+ client_data->response.error = -EPROTO;
+ goto end;
+ }
+
+ if (data_len > client_data->response.max_size) {
+ dev_err(cl_data_to_dev(client_data),
+ "Received buffer size %zu is larger than allocated buffer %zu\n",
+ data_len, client_data->response.max_size);
+ client_data->response.error = -EMSGSIZE;
+ goto end;
+ }
+
+ /* We expect only "response" messages from firmware */
+ if (!(hdr->command & IS_RESPONSE)) {
+ dev_err(cl_data_to_dev(client_data),
+ "Invalid response to command\n");
+ client_data->response.error = -EIO;
+ goto end;
+ }
+
+ if (hdr->status) {
+ dev_err(cl_data_to_dev(client_data),
+ "Loader returned status %d\n",
+ hdr->status);
+ client_data->response.error = -EIO;
+ goto end;
+ }
+
+ /* Update the actual received buffer size */
+ client_data->response.size = data_len;
+
+ /*
+ * Copy the buffer received in firmware response for the
+ * calling thread.
+ */
+ memcpy(client_data->response.data,
+ rb_in_proc->buffer.data, data_len);
+
+ /* Set flag before waking up the caller */
+ client_data->response.received = true;
+
+end:
+ /* Free the buffer */
+ ishtp_cl_io_rb_recycle(rb_in_proc);
+ rb_in_proc = NULL;
+
+ /* Wake the calling thread */
+ wake_up_interruptible(&client_data->response.wait_queue);
+}
+
+/**
+ * loader_cl_event_cb() - bus driver callback for incoming message
+ * @device: Pointer to the ishtp client device for which this
+ * message is targeted
+ *
+ * Remove the packet from the list and process the message by calling
+ * process_recv
+ */
+static void loader_cl_event_cb(struct ishtp_cl_device *cl_device)
+{
+ struct ishtp_cl_rb *rb_in_proc;
+ struct ishtp_cl *loader_ishtp_cl = ishtp_get_drvdata(cl_device);
+
+ while ((rb_in_proc = ishtp_cl_rx_get_rb(loader_ishtp_cl)) != NULL) {
+ /* Process the data packet from firmware */
+ process_recv(loader_ishtp_cl, rb_in_proc);
+ }
+}
+
+/**
+ * ish_query_loader_prop() - Query ISH Shim firmware loader
+ * @client_data: Client data instance
+ * @fw: Poiner to firmware data struct in host memory
+ * @fw_info: Loader firmware properties
+ *
+ * This function queries the ISH Shim firmware loader for capabilities.
+ *
+ * Return: 0 for success, negative error code for failure.
+ */
+static int ish_query_loader_prop(struct ishtp_cl_data *client_data,
+ const struct firmware *fw,
+ struct shim_fw_info *fw_info)
+{
+ int rv;
+ struct loader_xfer_query ldr_xfer_query;
+ struct loader_xfer_query_response ldr_xfer_query_resp;
+
+ memset(&ldr_xfer_query, 0, sizeof(ldr_xfer_query));
+ ldr_xfer_query.hdr.command = LOADER_CMD_XFER_QUERY;
+ ldr_xfer_query.image_size = fw->size;
+ rv = loader_cl_send(client_data,
+ (u8 *)&ldr_xfer_query,
+ sizeof(ldr_xfer_query),
+ (u8 *)&ldr_xfer_query_resp,
+ sizeof(ldr_xfer_query_resp));
+ if (rv < 0) {
+ client_data->flag_retry = true;
+ return rv;
+ }
+
+ /* On success, the return value is the received buffer size */
+ if (rv != sizeof(struct loader_xfer_query_response)) {
+ dev_err(cl_data_to_dev(client_data),
+ "data size %d is not equal to size of loader_xfer_query_response %zu\n",
+ rv, sizeof(struct loader_xfer_query_response));
+ client_data->flag_retry = true;
+ return -EMSGSIZE;
+ }
+
+ /* Save fw_info for use outside this function */
+ *fw_info = ldr_xfer_query_resp.fw_info;
+
+ /* Loader firmware properties */
+ dev_dbg(cl_data_to_dev(client_data),
+ "ish_fw_version: major=%d minor=%d hotfix=%d build=%d protocol_version=0x%x loader_version=%d\n",
+ fw_info->ish_fw_version.major,
+ fw_info->ish_fw_version.minor,
+ fw_info->ish_fw_version.hotfix,
+ fw_info->ish_fw_version.build,
+ fw_info->protocol_version,
+ fw_info->ldr_version.value);
+
+ dev_dbg(cl_data_to_dev(client_data),
+ "loader_capability: max_fw_image_size=0x%x xfer_mode=%d max_dma_buf_size=0x%x dma_buf_size_limit=0x%x\n",
+ fw_info->ldr_capability.max_fw_image_size,
+ fw_info->ldr_capability.xfer_mode,
+ fw_info->ldr_capability.max_dma_buf_size,
+ dma_buf_size_limit);
+
+ /* Sanity checks */
+ if (fw_info->ldr_capability.max_fw_image_size < fw->size) {
+ dev_err(cl_data_to_dev(client_data),
+ "ISH firmware size %zu is greater than Shim firmware loader max supported %d\n",
+ fw->size,
+ fw_info->ldr_capability.max_fw_image_size);
+ return -ENOSPC;
+ }
+
+ /* For DMA the buffer size should be multiple of cacheline size */
+ if ((fw_info->ldr_capability.xfer_mode & LOADER_XFER_MODE_DIRECT_DMA) &&
+ (fw_info->ldr_capability.max_dma_buf_size % L1_CACHE_BYTES)) {
+ dev_err(cl_data_to_dev(client_data),
+ "Shim firmware loader buffer size %d should be multiple of cacheline\n",
+ fw_info->ldr_capability.max_dma_buf_size);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * ish_fw_xfer_ishtp() Loads ISH firmware using ishtp interface
+ * @client_data: Client data instance
+ * @fw: Pointer to firmware data struct in host memory
+ *
+ * This function uses ISH-TP to transfer ISH firmware from host to
+ * ISH SRAM. Lower layers may use IPC or DMA depending on firmware
+ * support.
+ *
+ * Return: 0 for success, negative error code for failure.
+ */
+static int ish_fw_xfer_ishtp(struct ishtp_cl_data *client_data,
+ const struct firmware *fw)
+{
+ int rv;
+ u32 fragment_offset, fragment_size, payload_max_size;
+ struct loader_xfer_ipc_fragment *ldr_xfer_ipc_frag;
+ struct loader_msg_hdr ldr_xfer_ipc_ack;
+
+ payload_max_size =
+ LOADER_SHIM_IPC_BUF_SIZE - IPC_FRAGMENT_DATA_PREAMBLE;
+
+ ldr_xfer_ipc_frag = kzalloc(LOADER_SHIM_IPC_BUF_SIZE, GFP_KERNEL);
+ if (!ldr_xfer_ipc_frag) {
+ client_data->flag_retry = true;
+ return -ENOMEM;
+ }
+
+ ldr_xfer_ipc_frag->fragment.hdr.command = LOADER_CMD_XFER_FRAGMENT;
+ ldr_xfer_ipc_frag->fragment.xfer_mode = LOADER_XFER_MODE_ISHTP;
+
+ /* Break the firmware image into fragments and send as ISH-TP payload */
+ fragment_offset = 0;
+ while (fragment_offset < fw->size) {
+ if (fragment_offset + payload_max_size < fw->size) {
+ fragment_size = payload_max_size;
+ ldr_xfer_ipc_frag->fragment.is_last = 0;
+ } else {
+ fragment_size = fw->size - fragment_offset;
+ ldr_xfer_ipc_frag->fragment.is_last = 1;
+ }
+
+ ldr_xfer_ipc_frag->fragment.offset = fragment_offset;
+ ldr_xfer_ipc_frag->fragment.size = fragment_size;
+ memcpy(ldr_xfer_ipc_frag->data,
+ &fw->data[fragment_offset],
+ fragment_size);
+
+ dev_dbg(cl_data_to_dev(client_data),
+ "xfer_mode=ipc offset=0x%08x size=0x%08x is_last=%d\n",
+ ldr_xfer_ipc_frag->fragment.offset,
+ ldr_xfer_ipc_frag->fragment.size,
+ ldr_xfer_ipc_frag->fragment.is_last);
+
+ rv = loader_cl_send(client_data,
+ (u8 *)ldr_xfer_ipc_frag,
+ IPC_FRAGMENT_DATA_PREAMBLE + fragment_size,
+ (u8 *)&ldr_xfer_ipc_ack,
+ sizeof(ldr_xfer_ipc_ack));
+ if (rv < 0) {
+ client_data->flag_retry = true;
+ goto end_err_resp_buf_release;
+ }
+
+ fragment_offset += fragment_size;
+ }
+
+ kfree(ldr_xfer_ipc_frag);
+ return 0;
+
+end_err_resp_buf_release:
+ /* Free ISH buffer if not done already, in error case */
+ kfree(ldr_xfer_ipc_frag);
+ return rv;
+}
+
+/**
+ * ish_fw_xfer_direct_dma() - Loads ISH firmware using direct dma
+ * @client_data: Client data instance
+ * @fw: Pointer to firmware data struct in host memory
+ * @fw_info: Loader firmware properties
+ *
+ * Host firmware load is a unique case where we need to download
+ * a large firmware image (200+ Kb). This function implements
+ * direct DMA transfer in kernel and ISH firmware. This allows
+ * us to overcome the ISH-TP 4 Kb limit, and allows us to DMA
+ * directly to ISH UMA at location of choice.
+ * Function depends on corresponding support in ISH firmware.
+ *
+ * Return: 0 for success, negative error code for failure.
+ */
+static int ish_fw_xfer_direct_dma(struct ishtp_cl_data *client_data,
+ const struct firmware *fw,
+ const struct shim_fw_info fw_info)
+{
+ int rv;
+ void *dma_buf;
+ dma_addr_t dma_buf_phy;
+ u32 fragment_offset, fragment_size, payload_max_size;
+ struct loader_msg_hdr ldr_xfer_dma_frag_ack;
+ struct loader_xfer_dma_fragment ldr_xfer_dma_frag;
+ struct device *devc = ishtp_get_pci_device(client_data->cl_device);
+ u32 shim_fw_buf_size =
+ fw_info.ldr_capability.max_dma_buf_size;
+
+ /*
+ * payload_max_size should be set to minimum of
+ * (1) Size of firmware to be loaded,
+ * (2) Max DMA buffer size supported by Shim firmware,
+ * (3) DMA buffer size limit set by boot_param dma_buf_size_limit.
+ */
+ payload_max_size = min3(fw->size,
+ (size_t)shim_fw_buf_size,
+ (size_t)dma_buf_size_limit);
+
+ /*
+ * Buffer size should be multiple of cacheline size
+ * if it's not, select the previous cacheline boundary.
+ */
+ payload_max_size &= ~(L1_CACHE_BYTES - 1);
+
+ dma_buf = kmalloc(payload_max_size, GFP_KERNEL | GFP_DMA32);
+ if (!dma_buf) {
+ client_data->flag_retry = true;
+ return -ENOMEM;
+ }
+
+ dma_buf_phy = dma_map_single(devc, dma_buf, payload_max_size,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(devc, dma_buf_phy)) {
+ dev_err(cl_data_to_dev(client_data), "DMA map failed\n");
+ client_data->flag_retry = true;
+ rv = -ENOMEM;
+ goto end_err_dma_buf_release;
+ }
+
+ ldr_xfer_dma_frag.fragment.hdr.command = LOADER_CMD_XFER_FRAGMENT;
+ ldr_xfer_dma_frag.fragment.xfer_mode = LOADER_XFER_MODE_DIRECT_DMA;
+ ldr_xfer_dma_frag.ddr_phys_addr = (u64)dma_buf_phy;
+
+ /* Send the firmware image in chucks of payload_max_size */
+ fragment_offset = 0;
+ while (fragment_offset < fw->size) {
+ if (fragment_offset + payload_max_size < fw->size) {
+ fragment_size = payload_max_size;
+ ldr_xfer_dma_frag.fragment.is_last = 0;
+ } else {
+ fragment_size = fw->size - fragment_offset;
+ ldr_xfer_dma_frag.fragment.is_last = 1;
+ }
+
+ ldr_xfer_dma_frag.fragment.offset = fragment_offset;
+ ldr_xfer_dma_frag.fragment.size = fragment_size;
+ memcpy(dma_buf, &fw->data[fragment_offset], fragment_size);
+
+ dma_sync_single_for_device(devc, dma_buf_phy,
+ payload_max_size,
+ DMA_TO_DEVICE);
+
+ /*
+ * Flush cache here because the dma_sync_single_for_device()
+ * does not do for x86.
+ */
+ clflush_cache_range(dma_buf, payload_max_size);
+
+ dev_dbg(cl_data_to_dev(client_data),
+ "xfer_mode=dma offset=0x%08x size=0x%x is_last=%d ddr_phys_addr=0x%016llx\n",
+ ldr_xfer_dma_frag.fragment.offset,
+ ldr_xfer_dma_frag.fragment.size,
+ ldr_xfer_dma_frag.fragment.is_last,
+ ldr_xfer_dma_frag.ddr_phys_addr);
+
+ rv = loader_cl_send(client_data,
+ (u8 *)&ldr_xfer_dma_frag,
+ sizeof(ldr_xfer_dma_frag),
+ (u8 *)&ldr_xfer_dma_frag_ack,
+ sizeof(ldr_xfer_dma_frag_ack));
+ if (rv < 0) {
+ client_data->flag_retry = true;
+ goto end_err_resp_buf_release;
+ }
+
+ fragment_offset += fragment_size;
+ }
+
+ dma_unmap_single(devc, dma_buf_phy, payload_max_size, DMA_TO_DEVICE);
+ kfree(dma_buf);
+ return 0;
+
+end_err_resp_buf_release:
+ /* Free ISH buffer if not done already, in error case */
+ dma_unmap_single(devc, dma_buf_phy, payload_max_size, DMA_TO_DEVICE);
+end_err_dma_buf_release:
+ kfree(dma_buf);
+ return rv;
+}
+
+/**
+ * ish_fw_start() Start executing ISH main firmware
+ * @client_data: client data instance
+ *
+ * This function sends message to Shim firmware loader to start
+ * the execution of ISH main firmware.
+ *
+ * Return: 0 for success, negative error code for failure.
+ */
+static int ish_fw_start(struct ishtp_cl_data *client_data)
+{
+ struct loader_start ldr_start;
+ struct loader_msg_hdr ldr_start_ack;
+
+ memset(&ldr_start, 0, sizeof(ldr_start));
+ ldr_start.hdr.command = LOADER_CMD_START;
+ return loader_cl_send(client_data,
+ (u8 *)&ldr_start,
+ sizeof(ldr_start),
+ (u8 *)&ldr_start_ack,
+ sizeof(ldr_start_ack));
+}
+
+/**
+ * load_fw_from_host() Loads ISH firmware from host
+ * @client_data: Client data instance
+ *
+ * This function loads the ISH firmware to ISH SRAM and starts execution
+ *
+ * Return: 0 for success, negative error code for failure.
+ */
+static int load_fw_from_host(struct ishtp_cl_data *client_data)
+{
+ int rv;
+ u32 xfer_mode;
+ char *filename;
+ const struct firmware *fw;
+ struct shim_fw_info fw_info;
+ struct ishtp_cl *loader_ishtp_cl = client_data->loader_ishtp_cl;
+
+ client_data->flag_retry = false;
+
+ filename = kzalloc(FILENAME_SIZE, GFP_KERNEL);
+ if (!filename) {
+ client_data->flag_retry = true;
+ rv = -ENOMEM;
+ goto end_error;
+ }
+
+ /* Get filename of the ISH firmware to be loaded */
+ rv = get_firmware_variant(client_data, filename);
+ if (rv < 0)
+ goto end_err_filename_buf_release;
+
+ rv = request_firmware(&fw, filename, cl_data_to_dev(client_data));
+ if (rv < 0)
+ goto end_err_filename_buf_release;
+
+ /* Step 1: Query Shim firmware loader properties */
+
+ rv = ish_query_loader_prop(client_data, fw, &fw_info);
+ if (rv < 0)
+ goto end_err_fw_release;
+
+ /* Step 2: Send the main firmware image to be loaded, to ISH SRAM */
+
+ xfer_mode = fw_info.ldr_capability.xfer_mode;
+ if (xfer_mode & LOADER_XFER_MODE_DIRECT_DMA) {
+ rv = ish_fw_xfer_direct_dma(client_data, fw, fw_info);
+ } else if (xfer_mode & LOADER_XFER_MODE_ISHTP) {
+ rv = ish_fw_xfer_ishtp(client_data, fw);
+ } else {
+ dev_err(cl_data_to_dev(client_data),
+ "No transfer mode selected in firmware\n");
+ rv = -EINVAL;
+ }
+ if (rv < 0)
+ goto end_err_fw_release;
+
+ /* Step 3: Start ISH main firmware exeuction */
+
+ rv = ish_fw_start(client_data);
+ if (rv < 0)
+ goto end_err_fw_release;
+
+ release_firmware(fw);
+ kfree(filename);
+ dev_info(cl_data_to_dev(client_data), "ISH firmware %s loaded\n",
+ filename);
+ return 0;
+
+end_err_fw_release:
+ release_firmware(fw);
+end_err_filename_buf_release:
+ kfree(filename);
+end_error:
+ /* Keep a count of retries, and give up after 3 attempts */
+ if (client_data->flag_retry &&
+ client_data->retry_count++ < MAX_LOAD_ATTEMPTS) {
+ dev_warn(cl_data_to_dev(client_data),
+ "ISH host firmware load failed %d. Resetting ISH, and trying again..\n",
+ rv);
+ ish_hw_reset(ishtp_get_ishtp_device(loader_ishtp_cl));
+ } else {
+ dev_err(cl_data_to_dev(client_data),
+ "ISH host firmware load failed %d\n", rv);
+ }
+ return rv;
+}
+
+static void load_fw_from_host_handler(struct work_struct *work)
+{
+ struct ishtp_cl_data *client_data;
+
+ client_data = container_of(work, struct ishtp_cl_data,
+ work_fw_load);
+ load_fw_from_host(client_data);
+}
+
+/**
+ * loader_init() - Init function for ISH-TP client
+ * @loader_ishtp_cl: ISH-TP client instance
+ * @reset: true if called for init after reset
+ *
+ * Return: 0 for success, negative error code for failure
+ */
+static int loader_init(struct ishtp_cl *loader_ishtp_cl, int reset)
+{
+ int rv;
+ struct ishtp_fw_client *fw_client;
+ struct ishtp_cl_data *client_data =
+ ishtp_get_client_data(loader_ishtp_cl);
+
+ dev_dbg(cl_data_to_dev(client_data), "reset flag: %d\n", reset);
+
+ rv = ishtp_cl_link(loader_ishtp_cl);
+ if (rv < 0) {
+ dev_err(cl_data_to_dev(client_data), "ishtp_cl_link failed\n");
+ return rv;
+ }
+
+ /* Connect to firmware client */
+ ishtp_set_tx_ring_size(loader_ishtp_cl, LOADER_CL_TX_RING_SIZE);
+ ishtp_set_rx_ring_size(loader_ishtp_cl, LOADER_CL_RX_RING_SIZE);
+
+ fw_client =
+ ishtp_fw_cl_get_client(ishtp_get_ishtp_device(loader_ishtp_cl),
+ &loader_ishtp_guid);
+ if (!fw_client) {
+ dev_err(cl_data_to_dev(client_data),
+ "ISH client uuid not found\n");
+ rv = -ENOENT;
+ goto err_cl_unlink;
+ }
+
+ ishtp_cl_set_fw_client_id(loader_ishtp_cl,
+ ishtp_get_fw_client_id(fw_client));
+ ishtp_set_connection_state(loader_ishtp_cl, ISHTP_CL_CONNECTING);
+
+ rv = ishtp_cl_connect(loader_ishtp_cl);
+ if (rv < 0) {
+ dev_err(cl_data_to_dev(client_data), "Client connect fail\n");
+ goto err_cl_unlink;
+ }
+
+ dev_dbg(cl_data_to_dev(client_data), "Client connected\n");
+
+ ishtp_register_event_cb(client_data->cl_device, loader_cl_event_cb);
+
+ return 0;
+
+err_cl_unlink:
+ ishtp_cl_unlink(loader_ishtp_cl);
+ return rv;
+}
+
+static void loader_deinit(struct ishtp_cl *loader_ishtp_cl)
+{
+ ishtp_set_connection_state(loader_ishtp_cl, ISHTP_CL_DISCONNECTING);
+ ishtp_cl_disconnect(loader_ishtp_cl);
+ ishtp_cl_unlink(loader_ishtp_cl);
+ ishtp_cl_flush_queues(loader_ishtp_cl);
+
+ /* Disband and free all Tx and Rx client-level rings */
+ ishtp_cl_free(loader_ishtp_cl);
+}
+
+static void reset_handler(struct work_struct *work)
+{
+ int rv;
+ struct ishtp_cl_data *client_data;
+ struct ishtp_cl *loader_ishtp_cl;
+ struct ishtp_cl_device *cl_device;
+
+ client_data = container_of(work, struct ishtp_cl_data,
+ work_ishtp_reset);
+
+ loader_ishtp_cl = client_data->loader_ishtp_cl;
+ cl_device = client_data->cl_device;
+
+ /* Unlink, flush queues & start again */
+ ishtp_cl_unlink(loader_ishtp_cl);
+ ishtp_cl_flush_queues(loader_ishtp_cl);
+ ishtp_cl_free(loader_ishtp_cl);
+
+ loader_ishtp_cl = ishtp_cl_allocate(cl_device);
+ if (!loader_ishtp_cl)
+ return;
+
+ ishtp_set_drvdata(cl_device, loader_ishtp_cl);
+ ishtp_set_client_data(loader_ishtp_cl, client_data);
+ client_data->loader_ishtp_cl = loader_ishtp_cl;
+ client_data->cl_device = cl_device;
+
+ rv = loader_init(loader_ishtp_cl, 1);
+ if (rv < 0) {
+ dev_err(ishtp_device(cl_device), "Reset Failed\n");
+ return;
+ }
+
+ /* ISH firmware loading from host */
+ load_fw_from_host(client_data);
+}
+
+/**
+ * loader_ishtp_cl_probe() - ISH-TP client driver probe
+ * @cl_device: ISH-TP client device instance
+ *
+ * This function gets called on device create on ISH-TP bus
+ *
+ * Return: 0 for success, negative error code for failure
+ */
+static int loader_ishtp_cl_probe(struct ishtp_cl_device *cl_device)
+{
+ struct ishtp_cl *loader_ishtp_cl;
+ struct ishtp_cl_data *client_data;
+ int rv;
+
+ client_data = devm_kzalloc(ishtp_device(cl_device),
+ sizeof(*client_data),
+ GFP_KERNEL);
+ if (!client_data)
+ return -ENOMEM;
+
+ loader_ishtp_cl = ishtp_cl_allocate(cl_device);
+ if (!loader_ishtp_cl)
+ return -ENOMEM;
+
+ ishtp_set_drvdata(cl_device, loader_ishtp_cl);
+ ishtp_set_client_data(loader_ishtp_cl, client_data);
+ client_data->loader_ishtp_cl = loader_ishtp_cl;
+ client_data->cl_device = cl_device;
+
+ init_waitqueue_head(&client_data->response.wait_queue);
+
+ INIT_WORK(&client_data->work_ishtp_reset,
+ reset_handler);
+ INIT_WORK(&client_data->work_fw_load,
+ load_fw_from_host_handler);
+
+ rv = loader_init(loader_ishtp_cl, 0);
+ if (rv < 0) {
+ ishtp_cl_free(loader_ishtp_cl);
+ return rv;
+ }
+ ishtp_get_device(cl_device);
+
+ client_data->retry_count = 0;
+
+ /* ISH firmware loading from host */
+ schedule_work(&client_data->work_fw_load);
+
+ return 0;
+}
+
+/**
+ * loader_ishtp_cl_remove() - ISH-TP client driver remove
+ * @cl_device: ISH-TP client device instance
+ *
+ * This function gets called on device remove on ISH-TP bus
+ *
+ * Return: 0
+ */
+static int loader_ishtp_cl_remove(struct ishtp_cl_device *cl_device)
+{
+ struct ishtp_cl_data *client_data;
+ struct ishtp_cl *loader_ishtp_cl = ishtp_get_drvdata(cl_device);
+
+ client_data = ishtp_get_client_data(loader_ishtp_cl);
+
+ /*
+ * The sequence of the following two cancel_work_sync() is
+ * important. The work_fw_load can in turn schedue
+ * work_ishtp_reset, so first cancel work_fw_load then
+ * cancel work_ishtp_reset.
+ */
+ cancel_work_sync(&client_data->work_fw_load);
+ cancel_work_sync(&client_data->work_ishtp_reset);
+ loader_deinit(loader_ishtp_cl);
+ ishtp_put_device(cl_device);
+
+ return 0;
+}
+
+/**
+ * loader_ishtp_cl_reset() - ISH-TP client driver reset
+ * @cl_device: ISH-TP client device instance
+ *
+ * This function gets called on device reset on ISH-TP bus
+ *
+ * Return: 0
+ */
+static int loader_ishtp_cl_reset(struct ishtp_cl_device *cl_device)
+{
+ struct ishtp_cl_data *client_data;
+ struct ishtp_cl *loader_ishtp_cl = ishtp_get_drvdata(cl_device);
+
+ client_data = ishtp_get_client_data(loader_ishtp_cl);
+
+ schedule_work(&client_data->work_ishtp_reset);
+
+ return 0;
+}
+
+static struct ishtp_cl_driver loader_ishtp_cl_driver = {
+ .name = "ish-loader",
+ .guid = &loader_ishtp_guid,
+ .probe = loader_ishtp_cl_probe,
+ .remove = loader_ishtp_cl_remove,
+ .reset = loader_ishtp_cl_reset,
+};
+
+static int __init ish_loader_init(void)
+{
+ return ishtp_cl_driver_register(&loader_ishtp_cl_driver, THIS_MODULE);
+}
+
+static void __exit ish_loader_exit(void)
+{
+ ishtp_cl_driver_unregister(&loader_ishtp_cl_driver);
+}
+
+late_initcall(ish_loader_init);
+module_exit(ish_loader_exit);
+
+module_param(dma_buf_size_limit, int, 0644);
+MODULE_PARM_DESC(dma_buf_size_limit, "Limit the DMA buf size to this value in bytes");
+
+MODULE_DESCRIPTION("ISH ISH-TP Host firmware Loader Client Driver");
+MODULE_AUTHOR("Rushikesh S Kadam <rushikesh.s.kadam@intel.com>");
+
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("ishtp:*");
diff --git a/drivers/hid/intel-ish-hid/ishtp-hid-client.c b/drivers/hid/intel-ish-hid/ishtp-hid-client.c
index 30fe0c5e6fad..56777a43e69c 100644
--- a/drivers/hid/intel-ish-hid/ishtp-hid-client.c
+++ b/drivers/hid/intel-ish-hid/ishtp-hid-client.c
@@ -15,15 +15,16 @@
#include <linux/module.h>
#include <linux/hid.h>
+#include <linux/intel-ish-client-if.h>
#include <linux/sched.h>
-#include "ishtp/ishtp-dev.h"
-#include "ishtp/client.h"
#include "ishtp-hid.h"
/* Rx ring buffer pool size */
#define HID_CL_RX_RING_SIZE 32
#define HID_CL_TX_RING_SIZE 16
+#define cl_data_to_dev(client_data) ishtp_device(client_data->cl_device)
+
/**
* report_bad_packets() - Report bad packets
* @hid_ishtp_cl: Client instance to get stats
@@ -37,9 +38,9 @@ static void report_bad_packet(struct ishtp_cl *hid_ishtp_cl, void *recv_buf,
size_t cur_pos, size_t payload_len)
{
struct hostif_msg *recv_msg = recv_buf;
- struct ishtp_cl_data *client_data = hid_ishtp_cl->client_data;
+ struct ishtp_cl_data *client_data = ishtp_get_client_data(hid_ishtp_cl);
- dev_err(&client_data->cl_device->dev, "[hid-ish]: BAD packet %02X\n"
+ dev_err(cl_data_to_dev(client_data), "[hid-ish]: BAD packet %02X\n"
"total_bad=%u cur_pos=%u\n"
"[%02X %02X %02X %02X]\n"
"payload_len=%u\n"
@@ -69,13 +70,15 @@ static void process_recv(struct ishtp_cl *hid_ishtp_cl, void *recv_buf,
unsigned char *payload;
struct device_info *dev_info;
int i, j;
- size_t payload_len, total_len, cur_pos;
+ size_t payload_len, total_len, cur_pos, raw_len;
int report_type;
struct report_list *reports_list;
char *reports;
size_t report_len;
- struct ishtp_cl_data *client_data = hid_ishtp_cl->client_data;
+ struct ishtp_cl_data *client_data = ishtp_get_client_data(hid_ishtp_cl);
int curr_hid_dev = client_data->cur_hid_dev;
+ struct ishtp_hid_data *hid_data = NULL;
+ struct hid_device *hid = NULL;
payload = recv_buf + sizeof(struct hostif_msg_hdr);
total_len = data_len;
@@ -83,12 +86,12 @@ static void process_recv(struct ishtp_cl *hid_ishtp_cl, void *recv_buf,
do {
if (cur_pos + sizeof(struct hostif_msg) > total_len) {
- dev_err(&client_data->cl_device->dev,
+ dev_err(cl_data_to_dev(client_data),
"[hid-ish]: error, received %u which is less than data header %u\n",
(unsigned int)data_len,
(unsigned int)sizeof(struct hostif_msg_hdr));
++client_data->bad_recv_cnt;
- ish_hw_reset(hid_ishtp_cl->dev);
+ ish_hw_reset(ishtp_get_ishtp_device(hid_ishtp_cl));
break;
}
@@ -101,7 +104,7 @@ static void process_recv(struct ishtp_cl *hid_ishtp_cl, void *recv_buf,
++client_data->bad_recv_cnt;
report_bad_packet(hid_ishtp_cl, recv_msg, cur_pos,
payload_len);
- ish_hw_reset(hid_ishtp_cl->dev);
+ ish_hw_reset(ishtp_get_ishtp_device(hid_ishtp_cl));
break;
}
@@ -116,18 +119,18 @@ static void process_recv(struct ishtp_cl *hid_ishtp_cl, void *recv_buf,
report_bad_packet(hid_ishtp_cl, recv_msg,
cur_pos,
payload_len);
- ish_hw_reset(hid_ishtp_cl->dev);
+ ish_hw_reset(ishtp_get_ishtp_device(hid_ishtp_cl));
break;
}
client_data->hid_dev_count = (unsigned int)*payload;
if (!client_data->hid_devices)
client_data->hid_devices = devm_kcalloc(
- &client_data->cl_device->dev,
+ cl_data_to_dev(client_data),
client_data->hid_dev_count,
sizeof(struct device_info),
GFP_KERNEL);
if (!client_data->hid_devices) {
- dev_err(&client_data->cl_device->dev,
+ dev_err(cl_data_to_dev(client_data),
"Mem alloc failed for hid device info\n");
wake_up_interruptible(&client_data->init_wait);
break;
@@ -135,7 +138,7 @@ static void process_recv(struct ishtp_cl *hid_ishtp_cl, void *recv_buf,
for (i = 0; i < client_data->hid_dev_count; ++i) {
if (1 + sizeof(struct device_info) * i >=
payload_len) {
- dev_err(&client_data->cl_device->dev,
+ dev_err(cl_data_to_dev(client_data),
"[hid-ish]: [ENUM_DEVICES]: content size %zu is bigger than payload_len %zu\n",
1 + sizeof(struct device_info)
* i, payload_len);
@@ -165,12 +168,12 @@ static void process_recv(struct ishtp_cl *hid_ishtp_cl, void *recv_buf,
report_bad_packet(hid_ishtp_cl, recv_msg,
cur_pos,
payload_len);
- ish_hw_reset(hid_ishtp_cl->dev);
+ ish_hw_reset(ishtp_get_ishtp_device(hid_ishtp_cl));
break;
}
if (!client_data->hid_descr[curr_hid_dev])
client_data->hid_descr[curr_hid_dev] =
- devm_kmalloc(&client_data->cl_device->dev,
+ devm_kmalloc(cl_data_to_dev(client_data),
payload_len, GFP_KERNEL);
if (client_data->hid_descr[curr_hid_dev]) {
memcpy(client_data->hid_descr[curr_hid_dev],
@@ -190,12 +193,12 @@ static void process_recv(struct ishtp_cl *hid_ishtp_cl, void *recv_buf,
report_bad_packet(hid_ishtp_cl, recv_msg,
cur_pos,
payload_len);
- ish_hw_reset(hid_ishtp_cl->dev);
+ ish_hw_reset(ishtp_get_ishtp_device(hid_ishtp_cl));
break;
}
if (!client_data->report_descr[curr_hid_dev])
client_data->report_descr[curr_hid_dev] =
- devm_kmalloc(&client_data->cl_device->dev,
+ devm_kmalloc(cl_data_to_dev(client_data),
payload_len, GFP_KERNEL);
if (client_data->report_descr[curr_hid_dev]) {
memcpy(client_data->report_descr[curr_hid_dev],
@@ -219,18 +222,31 @@ do_get_report:
/* Get index of device that matches this id */
for (i = 0; i < client_data->num_hid_devices; ++i) {
if (recv_msg->hdr.device_id ==
- client_data->hid_devices[i].dev_id)
- if (client_data->hid_sensor_hubs[i]) {
- hid_input_report(
- client_data->hid_sensor_hubs[
- i],
- report_type, payload,
- payload_len, 0);
- ishtp_hid_wakeup(
- client_data->hid_sensor_hubs[
- i]);
+ client_data->hid_devices[i].dev_id) {
+ hid = client_data->hid_sensor_hubs[i];
+ if (!hid)
break;
+
+ hid_data = hid->driver_data;
+ if (hid_data->raw_get_req) {
+ raw_len =
+ (hid_data->raw_buf_size <
+ payload_len) ?
+ hid_data->raw_buf_size :
+ payload_len;
+
+ memcpy(hid_data->raw_buf,
+ payload, raw_len);
+ } else {
+ hid_input_report
+ (hid, report_type,
+ payload, payload_len,
+ 0);
}
+
+ ishtp_hid_wakeup(hid);
+ break;
+ }
}
break;
@@ -295,7 +311,7 @@ do_get_report:
++client_data->bad_recv_cnt;
report_bad_packet(hid_ishtp_cl, recv_msg, cur_pos,
payload_len);
- ish_hw_reset(hid_ishtp_cl->dev);
+ ish_hw_reset(ishtp_get_ishtp_device(hid_ishtp_cl));
break;
}
@@ -475,7 +491,7 @@ int ishtp_hid_link_ready_wait(struct ishtp_cl_data *client_data)
static int ishtp_enum_enum_devices(struct ishtp_cl *hid_ishtp_cl)
{
struct hostif_msg msg;
- struct ishtp_cl_data *client_data = hid_ishtp_cl->client_data;
+ struct ishtp_cl_data *client_data = ishtp_get_client_data(hid_ishtp_cl);
int retry_count;
int rv;
@@ -501,18 +517,18 @@ static int ishtp_enum_enum_devices(struct ishtp_cl *hid_ishtp_cl)
sizeof(struct hostif_msg));
}
if (!client_data->enum_devices_done) {
- dev_err(&client_data->cl_device->dev,
+ dev_err(cl_data_to_dev(client_data),
"[hid-ish]: timed out waiting for enum_devices\n");
return -ETIMEDOUT;
}
if (!client_data->hid_devices) {
- dev_err(&client_data->cl_device->dev,
+ dev_err(cl_data_to_dev(client_data),
"[hid-ish]: failed to allocate HID dev structures\n");
return -ENOMEM;
}
client_data->num_hid_devices = client_data->hid_dev_count;
- dev_info(&hid_ishtp_cl->device->dev,
+ dev_info(ishtp_device(client_data->cl_device),
"[hid-ish]: enum_devices_done OK, num_hid_devices=%d\n",
client_data->num_hid_devices);
@@ -531,7 +547,7 @@ static int ishtp_enum_enum_devices(struct ishtp_cl *hid_ishtp_cl)
static int ishtp_get_hid_descriptor(struct ishtp_cl *hid_ishtp_cl, int index)
{
struct hostif_msg msg;
- struct ishtp_cl_data *client_data = hid_ishtp_cl->client_data;
+ struct ishtp_cl_data *client_data = ishtp_get_client_data(hid_ishtp_cl);
int rv;
/* Get HID descriptor */
@@ -549,13 +565,13 @@ static int ishtp_get_hid_descriptor(struct ishtp_cl *hid_ishtp_cl, int index)
client_data->hid_descr_done,
3 * HZ);
if (!client_data->hid_descr_done) {
- dev_err(&client_data->cl_device->dev,
+ dev_err(cl_data_to_dev(client_data),
"[hid-ish]: timed out for hid_descr_done\n");
return -EIO;
}
if (!client_data->hid_descr[index]) {
- dev_err(&client_data->cl_device->dev,
+ dev_err(cl_data_to_dev(client_data),
"[hid-ish]: allocation HID desc fail\n");
return -ENOMEM;
}
@@ -578,7 +594,7 @@ static int ishtp_get_report_descriptor(struct ishtp_cl *hid_ishtp_cl,
int index)
{
struct hostif_msg msg;
- struct ishtp_cl_data *client_data = hid_ishtp_cl->client_data;
+ struct ishtp_cl_data *client_data = ishtp_get_client_data(hid_ishtp_cl);
int rv;
/* Get report descriptor */
@@ -596,12 +612,12 @@ static int ishtp_get_report_descriptor(struct ishtp_cl *hid_ishtp_cl,
client_data->report_descr_done,
3 * HZ);
if (!client_data->report_descr_done) {
- dev_err(&client_data->cl_device->dev,
+ dev_err(cl_data_to_dev(client_data),
"[hid-ish]: timed out for report descr\n");
return -EIO;
}
if (!client_data->report_descr[index]) {
- dev_err(&client_data->cl_device->dev,
+ dev_err(cl_data_to_dev(client_data),
"[hid-ish]: failed to alloc report descr\n");
return -ENOMEM;
}
@@ -626,42 +642,42 @@ static int ishtp_get_report_descriptor(struct ishtp_cl *hid_ishtp_cl,
static int hid_ishtp_cl_init(struct ishtp_cl *hid_ishtp_cl, int reset)
{
struct ishtp_device *dev;
- struct ishtp_cl_data *client_data = hid_ishtp_cl->client_data;
+ struct ishtp_cl_data *client_data = ishtp_get_client_data(hid_ishtp_cl);
struct ishtp_fw_client *fw_client;
int i;
int rv;
- dev_dbg(&client_data->cl_device->dev, "%s\n", __func__);
+ dev_dbg(cl_data_to_dev(client_data), "%s\n", __func__);
hid_ishtp_trace(client_data, "%s reset flag: %d\n", __func__, reset);
- rv = ishtp_cl_link(hid_ishtp_cl, ISHTP_HOST_CLIENT_ID_ANY);
+ rv = ishtp_cl_link(hid_ishtp_cl);
if (rv) {
- dev_err(&client_data->cl_device->dev,
+ dev_err(cl_data_to_dev(client_data),
"ishtp_cl_link failed\n");
return -ENOMEM;
}
client_data->init_done = 0;
- dev = hid_ishtp_cl->dev;
+ dev = ishtp_get_ishtp_device(hid_ishtp_cl);
/* Connect to FW client */
- hid_ishtp_cl->rx_ring_size = HID_CL_RX_RING_SIZE;
- hid_ishtp_cl->tx_ring_size = HID_CL_TX_RING_SIZE;
+ ishtp_set_tx_ring_size(hid_ishtp_cl, HID_CL_TX_RING_SIZE);
+ ishtp_set_rx_ring_size(hid_ishtp_cl, HID_CL_RX_RING_SIZE);
fw_client = ishtp_fw_cl_get_client(dev, &hid_ishtp_guid);
if (!fw_client) {
- dev_err(&client_data->cl_device->dev,
+ dev_err(cl_data_to_dev(client_data),
"ish client uuid not found\n");
return -ENOENT;
}
-
- hid_ishtp_cl->fw_client_id = fw_client->client_id;
- hid_ishtp_cl->state = ISHTP_CL_CONNECTING;
+ ishtp_cl_set_fw_client_id(hid_ishtp_cl,
+ ishtp_get_fw_client_id(fw_client));
+ ishtp_set_connection_state(hid_ishtp_cl, ISHTP_CL_CONNECTING);
rv = ishtp_cl_connect(hid_ishtp_cl);
if (rv) {
- dev_err(&client_data->cl_device->dev,
+ dev_err(cl_data_to_dev(client_data),
"client connect fail\n");
goto err_cl_unlink;
}
@@ -669,7 +685,7 @@ static int hid_ishtp_cl_init(struct ishtp_cl *hid_ishtp_cl, int reset)
hid_ishtp_trace(client_data, "%s client connected\n", __func__);
/* Register read callback */
- ishtp_register_event_cb(hid_ishtp_cl->device, ish_cl_event_cb);
+ ishtp_register_event_cb(client_data->cl_device, ish_cl_event_cb);
rv = ishtp_enum_enum_devices(hid_ishtp_cl);
if (rv)
@@ -692,7 +708,7 @@ static int hid_ishtp_cl_init(struct ishtp_cl *hid_ishtp_cl, int reset)
if (!reset) {
rv = ishtp_hid_probe(i, client_data);
if (rv) {
- dev_err(&client_data->cl_device->dev,
+ dev_err(cl_data_to_dev(client_data),
"[hid-ish]: HID probe for #%u failed: %d\n",
i, rv);
goto err_cl_disconnect;
@@ -707,7 +723,7 @@ static int hid_ishtp_cl_init(struct ishtp_cl *hid_ishtp_cl, int reset)
return 0;
err_cl_disconnect:
- hid_ishtp_cl->state = ISHTP_CL_DISCONNECTING;
+ ishtp_set_connection_state(hid_ishtp_cl, ISHTP_CL_DISCONNECTING);
ishtp_cl_disconnect(hid_ishtp_cl);
err_cl_unlink:
ishtp_cl_unlink(hid_ishtp_cl);
@@ -744,16 +760,16 @@ static void hid_ishtp_cl_reset_handler(struct work_struct *work)
hid_ishtp_trace(client_data, "%s hid_ishtp_cl %p\n", __func__,
hid_ishtp_cl);
- dev_dbg(&cl_device->dev, "%s\n", __func__);
+ dev_dbg(ishtp_device(client_data->cl_device), "%s\n", __func__);
hid_ishtp_cl_deinit(hid_ishtp_cl);
- hid_ishtp_cl = ishtp_cl_allocate(cl_device->ishtp_dev);
+ hid_ishtp_cl = ishtp_cl_allocate(cl_device);
if (!hid_ishtp_cl)
return;
ishtp_set_drvdata(cl_device, hid_ishtp_cl);
- hid_ishtp_cl->client_data = client_data;
+ ishtp_set_client_data(hid_ishtp_cl, client_data);
client_data->hid_ishtp_cl = hid_ishtp_cl;
client_data->num_hid_devices = 0;
@@ -762,15 +778,17 @@ static void hid_ishtp_cl_reset_handler(struct work_struct *work)
rv = hid_ishtp_cl_init(hid_ishtp_cl, 1);
if (!rv)
break;
- dev_err(&client_data->cl_device->dev, "Retry reset init\n");
+ dev_err(cl_data_to_dev(client_data), "Retry reset init\n");
}
if (rv) {
- dev_err(&client_data->cl_device->dev, "Reset Failed\n");
+ dev_err(cl_data_to_dev(client_data), "Reset Failed\n");
hid_ishtp_trace(client_data, "%s Failed hid_ishtp_cl %p\n",
__func__, hid_ishtp_cl);
}
}
+void (*hid_print_trace)(void *unused, const char *format, ...);
+
/**
* hid_ishtp_cl_probe() - ISHTP client driver probe
* @cl_device: ISHTP client device instance
@@ -788,21 +806,18 @@ static int hid_ishtp_cl_probe(struct ishtp_cl_device *cl_device)
if (!cl_device)
return -ENODEV;
- if (!guid_equal(&hid_ishtp_guid,
- &cl_device->fw_client->props.protocol_name))
- return -ENODEV;
-
- client_data = devm_kzalloc(&cl_device->dev, sizeof(*client_data),
+ client_data = devm_kzalloc(ishtp_device(cl_device),
+ sizeof(*client_data),
GFP_KERNEL);
if (!client_data)
return -ENOMEM;
- hid_ishtp_cl = ishtp_cl_allocate(cl_device->ishtp_dev);
+ hid_ishtp_cl = ishtp_cl_allocate(cl_device);
if (!hid_ishtp_cl)
return -ENOMEM;
ishtp_set_drvdata(cl_device, hid_ishtp_cl);
- hid_ishtp_cl->client_data = client_data;
+ ishtp_set_client_data(hid_ishtp_cl, client_data);
client_data->hid_ishtp_cl = hid_ishtp_cl;
client_data->cl_device = cl_device;
@@ -811,6 +826,8 @@ static int hid_ishtp_cl_probe(struct ishtp_cl_device *cl_device)
INIT_WORK(&client_data->work, hid_ishtp_cl_reset_handler);
+ hid_print_trace = ishtp_trace_callback(cl_device);
+
rv = hid_ishtp_cl_init(hid_ishtp_cl, 0);
if (rv) {
ishtp_cl_free(hid_ishtp_cl);
@@ -832,13 +849,13 @@ static int hid_ishtp_cl_probe(struct ishtp_cl_device *cl_device)
static int hid_ishtp_cl_remove(struct ishtp_cl_device *cl_device)
{
struct ishtp_cl *hid_ishtp_cl = ishtp_get_drvdata(cl_device);
- struct ishtp_cl_data *client_data = hid_ishtp_cl->client_data;
+ struct ishtp_cl_data *client_data = ishtp_get_client_data(hid_ishtp_cl);
hid_ishtp_trace(client_data, "%s hid_ishtp_cl %p\n", __func__,
hid_ishtp_cl);
- dev_dbg(&cl_device->dev, "%s\n", __func__);
- hid_ishtp_cl->state = ISHTP_CL_DISCONNECTING;
+ dev_dbg(ishtp_device(cl_device), "%s\n", __func__);
+ ishtp_set_connection_state(hid_ishtp_cl, ISHTP_CL_DISCONNECTING);
ishtp_cl_disconnect(hid_ishtp_cl);
ishtp_put_device(cl_device);
ishtp_hid_remove(client_data);
@@ -862,7 +879,7 @@ static int hid_ishtp_cl_remove(struct ishtp_cl_device *cl_device)
static int hid_ishtp_cl_reset(struct ishtp_cl_device *cl_device)
{
struct ishtp_cl *hid_ishtp_cl = ishtp_get_drvdata(cl_device);
- struct ishtp_cl_data *client_data = hid_ishtp_cl->client_data;
+ struct ishtp_cl_data *client_data = ishtp_get_client_data(hid_ishtp_cl);
hid_ishtp_trace(client_data, "%s hid_ishtp_cl %p\n", __func__,
hid_ishtp_cl);
@@ -872,8 +889,6 @@ static int hid_ishtp_cl_reset(struct ishtp_cl_device *cl_device)
return 0;
}
-#define to_ishtp_cl_device(d) container_of(d, struct ishtp_cl_device, dev)
-
/**
* hid_ishtp_cl_suspend() - ISHTP client driver suspend
* @device: device instance
@@ -884,9 +899,9 @@ static int hid_ishtp_cl_reset(struct ishtp_cl_device *cl_device)
*/
static int hid_ishtp_cl_suspend(struct device *device)
{
- struct ishtp_cl_device *cl_device = to_ishtp_cl_device(device);
+ struct ishtp_cl_device *cl_device = dev_get_drvdata(device);
struct ishtp_cl *hid_ishtp_cl = ishtp_get_drvdata(cl_device);
- struct ishtp_cl_data *client_data = hid_ishtp_cl->client_data;
+ struct ishtp_cl_data *client_data = ishtp_get_client_data(hid_ishtp_cl);
hid_ishtp_trace(client_data, "%s hid_ishtp_cl %p\n", __func__,
hid_ishtp_cl);
@@ -905,9 +920,9 @@ static int hid_ishtp_cl_suspend(struct device *device)
*/
static int hid_ishtp_cl_resume(struct device *device)
{
- struct ishtp_cl_device *cl_device = to_ishtp_cl_device(device);
+ struct ishtp_cl_device *cl_device = dev_get_drvdata(device);
struct ishtp_cl *hid_ishtp_cl = ishtp_get_drvdata(cl_device);
- struct ishtp_cl_data *client_data = hid_ishtp_cl->client_data;
+ struct ishtp_cl_data *client_data = ishtp_get_client_data(hid_ishtp_cl);
hid_ishtp_trace(client_data, "%s hid_ishtp_cl %p\n", __func__,
hid_ishtp_cl);
@@ -922,6 +937,7 @@ static const struct dev_pm_ops hid_ishtp_pm_ops = {
static struct ishtp_cl_driver hid_ishtp_cl_driver = {
.name = "ish-hid",
+ .guid = &hid_ishtp_guid,
.probe = hid_ishtp_cl_probe,
.remove = hid_ishtp_cl_remove,
.reset = hid_ishtp_cl_reset,
@@ -933,7 +949,7 @@ static int __init ish_hid_init(void)
int rv;
/* Register ISHTP client device driver with ISHTP Bus */
- rv = ishtp_cl_driver_register(&hid_ishtp_cl_driver);
+ rv = ishtp_cl_driver_register(&hid_ishtp_cl_driver, THIS_MODULE);
return rv;
diff --git a/drivers/hid/intel-ish-hid/ishtp-hid.c b/drivers/hid/intel-ish-hid/ishtp-hid.c
index bc4c536f3c0d..62c03561adaa 100644
--- a/drivers/hid/intel-ish-hid/ishtp-hid.c
+++ b/drivers/hid/intel-ish-hid/ishtp-hid.c
@@ -14,8 +14,8 @@
*/
#include <linux/hid.h>
+#include <linux/intel-ish-client-if.h>
#include <uapi/linux/input.h>
-#include "ishtp/client.h"
#include "ishtp-hid.h"
/**
@@ -59,10 +59,46 @@ static void ishtp_hid_close(struct hid_device *hid)
{
}
-static int ishtp_raw_request(struct hid_device *hdev, unsigned char reportnum,
- __u8 *buf, size_t len, unsigned char rtype, int reqtype)
+static int ishtp_raw_request(struct hid_device *hid, unsigned char reportnum,
+ __u8 *buf, size_t len, unsigned char rtype,
+ int reqtype)
{
- return 0;
+ struct ishtp_hid_data *hid_data = hid->driver_data;
+ char *ishtp_buf = NULL;
+ size_t ishtp_buf_len;
+ unsigned int header_size = sizeof(struct hostif_msg);
+
+ if (rtype == HID_OUTPUT_REPORT)
+ return -EINVAL;
+
+ hid_data->request_done = false;
+ switch (reqtype) {
+ case HID_REQ_GET_REPORT:
+ hid_data->raw_buf = buf;
+ hid_data->raw_buf_size = len;
+ hid_data->raw_get_req = true;
+
+ hid_ishtp_get_report(hid, reportnum, rtype);
+ break;
+ case HID_REQ_SET_REPORT:
+ /*
+ * Spare 7 bytes for 64b accesses through
+ * get/put_unaligned_le64()
+ */
+ ishtp_buf_len = len + header_size;
+ ishtp_buf = kzalloc(ishtp_buf_len + 7, GFP_KERNEL);
+ if (!ishtp_buf)
+ return -ENOMEM;
+
+ memcpy(ishtp_buf + header_size, buf, len);
+ hid_ishtp_set_feature(hid, ishtp_buf, ishtp_buf_len, reportnum);
+ kfree(ishtp_buf);
+ break;
+ }
+
+ hid_hw_wait(hid);
+
+ return len;
}
/**
@@ -87,6 +123,7 @@ static void ishtp_hid_request(struct hid_device *hid, struct hid_report *rep,
hid_data->request_done = false;
switch (reqtype) {
case HID_REQ_GET_REPORT:
+ hid_data->raw_get_req = false;
hid_ishtp_get_report(hid, rep->id, rep->type);
break;
case HID_REQ_SET_REPORT:
@@ -116,7 +153,6 @@ static void ishtp_hid_request(struct hid_device *hid, struct hid_report *rep,
static int ishtp_wait_for_response(struct hid_device *hid)
{
struct ishtp_hid_data *hid_data = hid->driver_data;
- struct ishtp_cl_data *client_data = hid_data->client_data;
int rv;
hid_ishtp_trace(client_data, "%s hid %p\n", __func__, hid);
@@ -204,7 +240,8 @@ int ishtp_hid_probe(unsigned int cur_hid_dev,
hid->ll_driver = &ishtp_hid_ll_driver;
hid->bus = BUS_INTEL_ISHTP;
- hid->dev.parent = &client_data->cl_device->dev;
+ hid->dev.parent = ishtp_device(client_data->cl_device);
+
hid->version = le16_to_cpu(ISH_HID_VERSION);
hid->vendor = le16_to_cpu(client_data->hid_devices[cur_hid_dev].vid);
hid->product = le16_to_cpu(client_data->hid_devices[cur_hid_dev].pid);
diff --git a/drivers/hid/intel-ish-hid/ishtp-hid.h b/drivers/hid/intel-ish-hid/ishtp-hid.h
index 1cd07a441cd4..e27d3d6c1920 100644
--- a/drivers/hid/intel-ish-hid/ishtp-hid.h
+++ b/drivers/hid/intel-ish-hid/ishtp-hid.h
@@ -24,9 +24,9 @@
#define IS_RESPONSE 0x80
/* Used to dump to Linux trace buffer, if enabled */
-#define hid_ishtp_trace(client, ...) \
- client->cl_device->ishtp_dev->print_log(\
- client->cl_device->ishtp_dev, __VA_ARGS__)
+extern void (*hid_print_trace)(void *unused, const char *format, ...);
+#define hid_ishtp_trace(client, ...) \
+ (hid_print_trace)(NULL, __VA_ARGS__)
/* ISH Transport protocol (ISHTP in short) GUID */
static const guid_t hid_ishtp_guid =
@@ -159,6 +159,9 @@ struct ishtp_cl_data {
* @client_data: Link to the client instance
* @hid_wait: Completion waitq
*
+ * @raw_get_req: Flag indicating raw get request ongoing
+ * @raw_buf: raw request buffer filled on receiving get report
+ * @raw_buf_size: raw request buffer size
* Used to tie hid hid->driver data to driver client instance
*/
struct ishtp_hid_data {
@@ -166,6 +169,11 @@ struct ishtp_hid_data {
bool request_done;
struct ishtp_cl_data *client_data;
wait_queue_head_t hid_wait;
+
+ /* raw request */
+ bool raw_get_req;
+ u8 *raw_buf;
+ size_t raw_buf_size;
};
/* Interface functions between HID LL driver and ISH TP client */
diff --git a/drivers/hid/intel-ish-hid/ishtp/bus.c b/drivers/hid/intel-ish-hid/ishtp/bus.c
index d5f4b6438d86..fb8ca12955b4 100644
--- a/drivers/hid/intel-ish-hid/ishtp/bus.c
+++ b/drivers/hid/intel-ish-hid/ishtp/bus.c
@@ -171,6 +171,19 @@ struct ishtp_fw_client *ishtp_fw_cl_get_client(struct ishtp_device *dev,
EXPORT_SYMBOL(ishtp_fw_cl_get_client);
/**
+ * ishtp_get_fw_client_id() - Get fw client id
+ *
+ * This interface is used to reset HW get FW client id.
+ *
+ * Return: firmware client id.
+ */
+int ishtp_get_fw_client_id(struct ishtp_fw_client *fw_client)
+{
+ return fw_client->client_id;
+}
+EXPORT_SYMBOL(ishtp_get_fw_client_id);
+
+/**
* ishtp_fw_cl_by_id() - return index to fw_clients for client_id
* @dev: the ishtp device structure
* @client_id: fw client id to search
@@ -220,6 +233,26 @@ static int ishtp_cl_device_probe(struct device *dev)
}
/**
+ * ishtp_cl_bus_match() - Bus match() callback
+ * @dev: the device structure
+ * @drv: the driver structure
+ *
+ * This is a bus match callback, called when a new ishtp_cl_device is
+ * registered during ishtp bus client enumeration. Use the guid_t in
+ * drv and dev to decide whether they match or not.
+ *
+ * Return: 1 if dev & drv matches, 0 otherwise.
+ */
+static int ishtp_cl_bus_match(struct device *dev, struct device_driver *drv)
+{
+ struct ishtp_cl_device *device = to_ishtp_cl_device(dev);
+ struct ishtp_cl_driver *driver = to_ishtp_cl_driver(drv);
+
+ return guid_equal(driver->guid,
+ &device->fw_client->props.protocol_name);
+}
+
+/**
* ishtp_cl_device_remove() - Bus remove() callback
* @dev: the device structure
*
@@ -372,6 +405,7 @@ static struct bus_type ishtp_cl_bus_type = {
.name = "ishtp",
.dev_groups = ishtp_cl_dev_groups,
.probe = ishtp_cl_device_probe,
+ .match = ishtp_cl_bus_match,
.remove = ishtp_cl_device_remove,
.pm = &ishtp_cl_bus_dev_pm_ops,
.uevent = ishtp_cl_uevent,
@@ -445,6 +479,7 @@ static struct ishtp_cl_device *ishtp_bus_add_device(struct ishtp_device *dev,
}
ishtp_device_ready = true;
+ dev_set_drvdata(&device->dev, device);
return device;
}
@@ -464,7 +499,7 @@ static void ishtp_bus_remove_device(struct ishtp_cl_device *device)
}
/**
- * __ishtp_cl_driver_register() - Client driver register
+ * ishtp_cl_driver_register() - Client driver register
* @driver: the client driver instance
* @owner: Owner of this driver module
*
@@ -473,8 +508,8 @@ static void ishtp_bus_remove_device(struct ishtp_cl_device *device)
*
* Return: Return value of driver_register or -ENODEV if not ready
*/
-int __ishtp_cl_driver_register(struct ishtp_cl_driver *driver,
- struct module *owner)
+int ishtp_cl_driver_register(struct ishtp_cl_driver *driver,
+ struct module *owner)
{
int err;
@@ -491,7 +526,7 @@ int __ishtp_cl_driver_register(struct ishtp_cl_driver *driver,
return 0;
}
-EXPORT_SYMBOL(__ishtp_cl_driver_register);
+EXPORT_SYMBOL(ishtp_cl_driver_register);
/**
* ishtp_cl_driver_unregister() - Client driver unregister
@@ -807,6 +842,59 @@ int ishtp_use_dma_transfer(void)
}
/**
+ * ishtp_device() - Return device pointer
+ *
+ * This interface is used to return device pointer from ishtp_cl_device
+ * instance.
+ *
+ * Return: device *.
+ */
+struct device *ishtp_device(struct ishtp_cl_device *device)
+{
+ return &device->dev;
+}
+EXPORT_SYMBOL(ishtp_device);
+
+/**
+ * ishtp_get_pci_device() - Return PCI device dev pointer
+ * This interface is used to return PCI device pointer
+ * from ishtp_cl_device instance.
+ *
+ * Return: device *.
+ */
+struct device *ishtp_get_pci_device(struct ishtp_cl_device *device)
+{
+ return device->ishtp_dev->devc;
+}
+EXPORT_SYMBOL(ishtp_get_pci_device);
+
+/**
+ * ishtp_trace_callback() - Return trace callback
+ *
+ * This interface is used to return trace callback function pointer.
+ *
+ * Return: void *.
+ */
+void *ishtp_trace_callback(struct ishtp_cl_device *cl_device)
+{
+ return cl_device->ishtp_dev->print_log;
+}
+EXPORT_SYMBOL(ishtp_trace_callback);
+
+/**
+ * ish_hw_reset() - Call HW reset IPC callback
+ *
+ * This interface is used to reset HW in case of error.
+ *
+ * Return: value from IPC hw_reset callback
+ */
+int ish_hw_reset(struct ishtp_device *dev)
+{
+ return dev->ops->hw_reset(dev);
+}
+EXPORT_SYMBOL(ish_hw_reset);
+
+/**
* ishtp_bus_register() - Function to register bus
*
* This register ishtp bus
diff --git a/drivers/hid/intel-ish-hid/ishtp/bus.h b/drivers/hid/intel-ish-hid/ishtp/bus.h
index 4cf7ad586c37..93d516f5a853 100644
--- a/drivers/hid/intel-ish-hid/ishtp/bus.h
+++ b/drivers/hid/intel-ish-hid/ishtp/bus.h
@@ -17,6 +17,7 @@
#include <linux/device.h>
#include <linux/mod_devicetable.h>
+#include <linux/intel-ish-client-if.h>
struct ishtp_cl;
struct ishtp_cl_device;
@@ -52,25 +53,6 @@ struct ishtp_cl_device {
void (*event_cb)(struct ishtp_cl_device *device);
};
-/**
- * struct ishtp_cl_device - ISHTP device handle
- * @driver: driver instance on a bus
- * @name: Name of the device for probe
- * @probe: driver callback for device probe
- * @remove: driver callback on device removal
- *
- * Client drivers defines to get probed/removed for ISHTP client device.
- */
-struct ishtp_cl_driver {
- struct device_driver driver;
- const char *name;
- int (*probe)(struct ishtp_cl_device *dev);
- int (*remove)(struct ishtp_cl_device *dev);
- int (*reset)(struct ishtp_cl_device *dev);
- const struct dev_pm_ops *pm;
-};
-
-
int ishtp_bus_new_client(struct ishtp_device *dev);
void ishtp_remove_all_clients(struct ishtp_device *dev);
int ishtp_cl_device_bind(struct ishtp_cl *cl);
@@ -98,22 +80,5 @@ void ishtp_recv(struct ishtp_device *dev);
void ishtp_reset_handler(struct ishtp_device *dev);
void ishtp_reset_compl_handler(struct ishtp_device *dev);
-void ishtp_put_device(struct ishtp_cl_device *);
-void ishtp_get_device(struct ishtp_cl_device *);
-
-void ishtp_set_drvdata(struct ishtp_cl_device *cl_device, void *data);
-void *ishtp_get_drvdata(struct ishtp_cl_device *cl_device);
-
-int __ishtp_cl_driver_register(struct ishtp_cl_driver *driver,
- struct module *owner);
-#define ishtp_cl_driver_register(driver) \
- __ishtp_cl_driver_register(driver, THIS_MODULE)
-void ishtp_cl_driver_unregister(struct ishtp_cl_driver *driver);
-
-int ishtp_register_event_cb(struct ishtp_cl_device *device,
- void (*read_cb)(struct ishtp_cl_device *));
int ishtp_fw_cl_by_uuid(struct ishtp_device *dev, const guid_t *cuuid);
-struct ishtp_fw_client *ishtp_fw_cl_get_client(struct ishtp_device *dev,
- const guid_t *uuid);
-
#endif /* _LINUX_ISHTP_CL_BUS_H */
diff --git a/drivers/hid/intel-ish-hid/ishtp/client.c b/drivers/hid/intel-ish-hid/ishtp/client.c
index faeccdb1475b..b7ac5e3b1e82 100644
--- a/drivers/hid/intel-ish-hid/ishtp/client.c
+++ b/drivers/hid/intel-ish-hid/ishtp/client.c
@@ -126,7 +126,7 @@ static void ishtp_cl_init(struct ishtp_cl *cl, struct ishtp_device *dev)
*
* Return: The allocated client instance or NULL on failure
*/
-struct ishtp_cl *ishtp_cl_allocate(struct ishtp_device *dev)
+struct ishtp_cl *ishtp_cl_allocate(struct ishtp_cl_device *cl_device)
{
struct ishtp_cl *cl;
@@ -134,7 +134,7 @@ struct ishtp_cl *ishtp_cl_allocate(struct ishtp_device *dev)
if (!cl)
return NULL;
- ishtp_cl_init(cl, dev);
+ ishtp_cl_init(cl, cl_device->ishtp_dev);
return cl;
}
EXPORT_SYMBOL(ishtp_cl_allocate);
@@ -168,9 +168,6 @@ EXPORT_SYMBOL(ishtp_cl_free);
/**
* ishtp_cl_link() - Reserve a host id and link the client instance
* @cl: client device instance
- * @id: host client id to use. It can be ISHTP_HOST_CLIENT_ID_ANY if any
- * id from the available can be used
- *
*
* This allocates a single bit in the hostmap. This function will make sure
* that not many client sessions are opened at the same time. Once allocated
@@ -179,11 +176,11 @@ EXPORT_SYMBOL(ishtp_cl_free);
*
* Return: 0 or error code on failure
*/
-int ishtp_cl_link(struct ishtp_cl *cl, int id)
+int ishtp_cl_link(struct ishtp_cl *cl)
{
struct ishtp_device *dev;
- unsigned long flags, flags_cl;
- int ret = 0;
+ unsigned long flags, flags_cl;
+ int id, ret = 0;
if (WARN_ON(!cl || !cl->dev))
return -EINVAL;
@@ -197,10 +194,7 @@ int ishtp_cl_link(struct ishtp_cl *cl, int id)
goto unlock_dev;
}
- /* If Id is not assigned get one*/
- if (id == ISHTP_HOST_CLIENT_ID_ANY)
- id = find_first_zero_bit(dev->host_clients_map,
- ISHTP_CLIENTS_MAX);
+ id = find_first_zero_bit(dev->host_clients_map, ISHTP_CLIENTS_MAX);
if (id >= ISHTP_CLIENTS_MAX) {
spin_unlock_irqrestore(&dev->device_lock, flags);
@@ -1069,3 +1063,45 @@ void recv_ishtp_cl_msg_dma(struct ishtp_device *dev, void *msg,
eoi:
return;
}
+
+void *ishtp_get_client_data(struct ishtp_cl *cl)
+{
+ return cl->client_data;
+}
+EXPORT_SYMBOL(ishtp_get_client_data);
+
+void ishtp_set_client_data(struct ishtp_cl *cl, void *data)
+{
+ cl->client_data = data;
+}
+EXPORT_SYMBOL(ishtp_set_client_data);
+
+struct ishtp_device *ishtp_get_ishtp_device(struct ishtp_cl *cl)
+{
+ return cl->dev;
+}
+EXPORT_SYMBOL(ishtp_get_ishtp_device);
+
+void ishtp_set_tx_ring_size(struct ishtp_cl *cl, int size)
+{
+ cl->tx_ring_size = size;
+}
+EXPORT_SYMBOL(ishtp_set_tx_ring_size);
+
+void ishtp_set_rx_ring_size(struct ishtp_cl *cl, int size)
+{
+ cl->rx_ring_size = size;
+}
+EXPORT_SYMBOL(ishtp_set_rx_ring_size);
+
+void ishtp_set_connection_state(struct ishtp_cl *cl, int state)
+{
+ cl->state = state;
+}
+EXPORT_SYMBOL(ishtp_set_connection_state);
+
+void ishtp_cl_set_fw_client_id(struct ishtp_cl *cl, int fw_client_id)
+{
+ cl->fw_client_id = fw_client_id;
+}
+EXPORT_SYMBOL(ishtp_cl_set_fw_client_id);
diff --git a/drivers/hid/intel-ish-hid/ishtp/client.h b/drivers/hid/intel-ish-hid/ishtp/client.h
index e0df3eb611e6..6ed00947d6bc 100644
--- a/drivers/hid/intel-ish-hid/ishtp/client.h
+++ b/drivers/hid/intel-ish-hid/ishtp/client.h
@@ -19,15 +19,6 @@
#include <linux/types.h>
#include "ishtp-dev.h"
-/* Client state */
-enum cl_state {
- ISHTP_CL_INITIALIZING = 0,
- ISHTP_CL_CONNECTING,
- ISHTP_CL_CONNECTED,
- ISHTP_CL_DISCONNECTING,
- ISHTP_CL_DISCONNECTED
-};
-
/* Tx and Rx ring size */
#define CL_DEF_RX_RING_SIZE 2
#define CL_DEF_TX_RING_SIZE 2
@@ -169,19 +160,4 @@ static inline bool ishtp_cl_cmp_id(const struct ishtp_cl *cl1,
(cl1->fw_client_id == cl2->fw_client_id);
}
-/* exported functions from ISHTP under client management scope */
-struct ishtp_cl *ishtp_cl_allocate(struct ishtp_device *dev);
-void ishtp_cl_free(struct ishtp_cl *cl);
-int ishtp_cl_link(struct ishtp_cl *cl, int id);
-void ishtp_cl_unlink(struct ishtp_cl *cl);
-int ishtp_cl_disconnect(struct ishtp_cl *cl);
-int ishtp_cl_connect(struct ishtp_cl *cl);
-int ishtp_cl_send(struct ishtp_cl *cl, uint8_t *buf, size_t length);
-int ishtp_cl_flush_queues(struct ishtp_cl *cl);
-
-/* exported functions from ISHTP client buffer management scope */
-int ishtp_cl_io_rb_recycle(struct ishtp_cl_rb *rb);
-bool ishtp_cl_tx_empty(struct ishtp_cl *cl);
-struct ishtp_cl_rb *ishtp_cl_rx_get_rb(struct ishtp_cl *cl);
-
#endif /* _ISHTP_CLIENT_H_ */
diff --git a/drivers/hid/intel-ish-hid/ishtp/ishtp-dev.h b/drivers/hid/intel-ish-hid/ishtp/ishtp-dev.h
index e54ce1ef27dd..3cfef084b9fc 100644
--- a/drivers/hid/intel-ish-hid/ishtp/ishtp-dev.h
+++ b/drivers/hid/intel-ish-hid/ishtp/ishtp-dev.h
@@ -79,32 +79,6 @@ struct ishtp_fw_client {
uint8_t client_id;
};
-/**
- * struct ishtp_msg_data - ISHTP message data struct
- * @size: Size of data in the *data
- * @data: Pointer to data
- */
-struct ishtp_msg_data {
- uint32_t size;
- unsigned char *data;
-};
-
-/*
- * struct ishtp_cl_rb - request block structure
- * @list: Link to list members
- * @cl: ISHTP client instance
- * @buffer: message header
- * @buf_idx: Index into buffer
- * @read_time: unused at this time
- */
-struct ishtp_cl_rb {
- struct list_head list;
- struct ishtp_cl *cl;
- struct ishtp_msg_data buffer;
- unsigned long buf_idx;
- unsigned long read_time;
-};
-
/*
* Control info for IPC messages ISHTP/IPC sending FIFO -
* list with inline data buffer
@@ -264,11 +238,6 @@ static inline int ish_ipc_reset(struct ishtp_device *dev)
return dev->ops->ipc_reset(dev);
}
-static inline int ish_hw_reset(struct ishtp_device *dev)
-{
- return dev->ops->hw_reset(dev);
-}
-
/* Exported function */
void ishtp_device_init(struct ishtp_device *dev);
int ishtp_start(struct ishtp_device *dev);
diff --git a/drivers/hsi/controllers/omap_ssi_port.c b/drivers/hsi/controllers/omap_ssi_port.c
index b2b3989ccfd2..afbf1345709d 100644
--- a/drivers/hsi/controllers/omap_ssi_port.c
+++ b/drivers/hsi/controllers/omap_ssi_port.c
@@ -162,7 +162,7 @@ static int ssi_div_set(void *data, u64 val)
return 0;
}
-DEFINE_SIMPLE_ATTRIBUTE(ssi_sst_div_fops, ssi_div_get, ssi_div_set, "%llu\n");
+DEFINE_DEBUGFS_ATTRIBUTE(ssi_sst_div_fops, ssi_div_get, ssi_div_set, "%llu\n");
static int ssi_debug_add_port(struct omap_ssi_port *omap_port,
struct dentry *dir)
@@ -177,8 +177,8 @@ static int ssi_debug_add_port(struct omap_ssi_port *omap_port,
dir = debugfs_create_dir("sst", dir);
if (!dir)
return -ENOMEM;
- debugfs_create_file("divisor", S_IRUGO | S_IWUSR, dir, port,
- &ssi_sst_div_fops);
+ debugfs_create_file_unsafe("divisor", 0644, dir, port,
+ &ssi_sst_div_fops);
return 0;
}
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 6f929bfa9fcd..d0f1dfe2bcbb 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -1759,6 +1759,7 @@ config SENSORS_VT8231
config SENSORS_W83773G
tristate "Nuvoton W83773G"
depends on I2C
+ select REGMAP_I2C
help
If you say yes here you get support for the Nuvoton W83773G hardware
monitoring chip.
diff --git a/drivers/hwmon/ntc_thermistor.c b/drivers/hwmon/ntc_thermistor.c
index e4f9f7ce92fa..f9abeeeead9e 100644
--- a/drivers/hwmon/ntc_thermistor.c
+++ b/drivers/hwmon/ntc_thermistor.c
@@ -640,7 +640,7 @@ static const struct hwmon_channel_info ntc_chip = {
};
static const u32 ntc_temp_config[] = {
- HWMON_T_INPUT, HWMON_T_TYPE,
+ HWMON_T_INPUT | HWMON_T_TYPE,
0
};
diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c
index b91a80abf724..4679acb4918e 100644
--- a/drivers/hwmon/occ/common.c
+++ b/drivers/hwmon/occ/common.c
@@ -890,6 +890,8 @@ static int occ_setup_sensor_attrs(struct occ *occ)
s++;
}
}
+
+ s = (sensors->power.num_sensors * 4) + 1;
} else {
for (i = 0; i < sensors->power.num_sensors; ++i) {
s = i + 1;
@@ -918,11 +920,11 @@ static int occ_setup_sensor_attrs(struct occ *occ)
show_power, NULL, 3, i);
attr++;
}
- }
- if (sensors->caps.num_sensors >= 1) {
s = sensors->power.num_sensors + 1;
+ }
+ if (sensors->caps.num_sensors >= 1) {
snprintf(attr->name, sizeof(attr->name), "power%d_label", s);
attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_caps, NULL,
0, 0);
diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c
index 9a63e87ea5f3..be302ec5f66b 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x.c
@@ -871,7 +871,7 @@ static int etm_probe(struct amba_device *adev, const struct amba_id *id)
}
pm_runtime_put(&adev->dev);
- dev_info(dev, "%s initialized\n", (char *)id->data);
+ dev_info(dev, "%s initialized\n", (char *)coresight_get_uci_data(id));
if (boot_enable) {
coresight_enable(drvdata->csdev);
drvdata->boot_enable = true;
@@ -915,36 +915,18 @@ static const struct dev_pm_ops etm_dev_pm_ops = {
};
static const struct amba_id etm_ids[] = {
- { /* ETM 3.3 */
- .id = 0x000bb921,
- .mask = 0x000fffff,
- .data = "ETM 3.3",
- },
- { /* ETM 3.5 - Cortex-A5 */
- .id = 0x000bb955,
- .mask = 0x000fffff,
- .data = "ETM 3.5",
- },
- { /* ETM 3.5 */
- .id = 0x000bb956,
- .mask = 0x000fffff,
- .data = "ETM 3.5",
- },
- { /* PTM 1.0 */
- .id = 0x000bb950,
- .mask = 0x000fffff,
- .data = "PTM 1.0",
- },
- { /* PTM 1.1 */
- .id = 0x000bb95f,
- .mask = 0x000fffff,
- .data = "PTM 1.1",
- },
- { /* PTM 1.1 Qualcomm */
- .id = 0x000b006f,
- .mask = 0x000fffff,
- .data = "PTM 1.1",
- },
+ /* ETM 3.3 */
+ CS_AMBA_ID_DATA(0x000bb921, "ETM 3.3"),
+ /* ETM 3.5 - Cortex-A5 */
+ CS_AMBA_ID_DATA(0x000bb955, "ETM 3.5"),
+ /* ETM 3.5 */
+ CS_AMBA_ID_DATA(0x000bb956, "ETM 3.5"),
+ /* PTM 1.0 */
+ CS_AMBA_ID_DATA(0x000bb950, "PTM 1.0"),
+ /* PTM 1.1 */
+ CS_AMBA_ID_DATA(0x000bb95f, "PTM 1.1"),
+ /* PTM 1.1 Qualcomm */
+ CS_AMBA_ID_DATA(0x000b006f, "PTM 1.1"),
{ 0, 0},
};
diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c
index fe76b176974a..08ce37c9475d 100644
--- a/drivers/hwtracing/coresight/coresight-etm4x.c
+++ b/drivers/hwtracing/coresight/coresight-etm4x.c
@@ -1068,18 +1068,21 @@ err_arch_supported:
return ret;
}
-#define ETM4x_AMBA_ID(pid) \
- { \
- .id = pid, \
- .mask = 0x000fffff, \
+static struct amba_cs_uci_id uci_id_etm4[] = {
+ {
+ /* ETMv4 UCI data */
+ .devarch = 0x47704a13,
+ .devarch_mask = 0xfff0ffff,
+ .devtype = 0x00000013,
}
+};
static const struct amba_id etm4_ids[] = {
- ETM4x_AMBA_ID(0x000bb95d), /* Cortex-A53 */
- ETM4x_AMBA_ID(0x000bb95e), /* Cortex-A57 */
- ETM4x_AMBA_ID(0x000bb95a), /* Cortex-A72 */
- ETM4x_AMBA_ID(0x000bb959), /* Cortex-A73 */
- ETM4x_AMBA_ID(0x000bb9da), /* Cortex-A35 */
+ CS_AMBA_ID(0x000bb95d), /* Cortex-A53 */
+ CS_AMBA_ID(0x000bb95e), /* Cortex-A57 */
+ CS_AMBA_ID(0x000bb95a), /* Cortex-A72 */
+ CS_AMBA_ID(0x000bb959), /* Cortex-A73 */
+ CS_AMBA_UCI_ID(0x000bb9da, uci_id_etm4), /* Cortex-A35 */
{},
};
diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h
index b936c6d7e13f..e0684d06e9ee 100644
--- a/drivers/hwtracing/coresight/coresight-priv.h
+++ b/drivers/hwtracing/coresight/coresight-priv.h
@@ -6,6 +6,7 @@
#ifndef _CORESIGHT_PRIV_H
#define _CORESIGHT_PRIV_H
+#include <linux/amba/bus.h>
#include <linux/bitops.h>
#include <linux/io.h>
#include <linux/coresight.h>
@@ -160,4 +161,43 @@ static inline int etm_readl_cp14(u32 off, unsigned int *val) { return 0; }
static inline int etm_writel_cp14(u32 off, u32 val) { return 0; }
#endif
+/*
+ * Macros and inline functions to handle CoreSight UCI data and driver
+ * private data in AMBA ID table entries, and extract data values.
+ */
+
+/* coresight AMBA ID, no UCI, no driver data: id table entry */
+#define CS_AMBA_ID(pid) \
+ { \
+ .id = pid, \
+ .mask = 0x000fffff, \
+ }
+
+/* coresight AMBA ID, UCI with driver data only: id table entry. */
+#define CS_AMBA_ID_DATA(pid, dval) \
+ { \
+ .id = pid, \
+ .mask = 0x000fffff, \
+ .data = (void *)&(struct amba_cs_uci_id) \
+ { \
+ .data = (void *)dval, \
+ } \
+ }
+
+/* coresight AMBA ID, full UCI structure: id table entry. */
+#define CS_AMBA_UCI_ID(pid, uci_ptr) \
+ { \
+ .id = pid, \
+ .mask = 0x000fffff, \
+ .data = uci_ptr \
+ }
+
+/* extract the data value from a UCI structure given amba_id pointer. */
+static inline void *coresight_get_uci_data(const struct amba_id *id)
+{
+ if (id->data)
+ return ((struct amba_cs_uci_id *)(id->data))->data;
+ return 0;
+}
+
#endif
diff --git a/drivers/hwtracing/coresight/coresight-stm.c b/drivers/hwtracing/coresight/coresight-stm.c
index f07825df5c7a..9f8a844ed7aa 100644
--- a/drivers/hwtracing/coresight/coresight-stm.c
+++ b/drivers/hwtracing/coresight/coresight-stm.c
@@ -870,7 +870,7 @@ static int stm_probe(struct amba_device *adev, const struct amba_id *id)
pm_runtime_put(&adev->dev);
- dev_info(dev, "%s initialized\n", (char *)id->data);
+ dev_info(dev, "%s initialized\n", (char *)coresight_get_uci_data(id));
return 0;
stm_unregister:
@@ -905,16 +905,8 @@ static const struct dev_pm_ops stm_dev_pm_ops = {
};
static const struct amba_id stm_ids[] = {
- {
- .id = 0x000bb962,
- .mask = 0x000fffff,
- .data = "STM32",
- },
- {
- .id = 0x000bb963,
- .mask = 0x000fffff,
- .data = "STM500",
- },
+ CS_AMBA_ID_DATA(0x000bb962, "STM32"),
+ CS_AMBA_ID_DATA(0x000bb963, "STM500"),
{ 0, 0},
};
diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c
index ea249f0bcd73..2a02da3d630f 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.c
+++ b/drivers/hwtracing/coresight/coresight-tmc.c
@@ -443,7 +443,8 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id)
desc.type = CORESIGHT_DEV_TYPE_SINK;
desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER;
desc.ops = &tmc_etr_cs_ops;
- ret = tmc_etr_setup_caps(drvdata, devid, id->data);
+ ret = tmc_etr_setup_caps(drvdata, devid,
+ coresight_get_uci_data(id));
if (ret)
goto out;
break;
@@ -475,26 +476,13 @@ out:
}
static const struct amba_id tmc_ids[] = {
- {
- .id = 0x000bb961,
- .mask = 0x000fffff,
- },
- {
- /* Coresight SoC 600 TMC-ETR/ETS */
- .id = 0x000bb9e8,
- .mask = 0x000fffff,
- .data = (void *)(unsigned long)CORESIGHT_SOC_600_ETR_CAPS,
- },
- {
- /* Coresight SoC 600 TMC-ETB */
- .id = 0x000bb9e9,
- .mask = 0x000fffff,
- },
- {
- /* Coresight SoC 600 TMC-ETF */
- .id = 0x000bb9ea,
- .mask = 0x000fffff,
- },
+ CS_AMBA_ID(0x000bb961),
+ /* Coresight SoC 600 TMC-ETR/ETS */
+ CS_AMBA_ID_DATA(0x000bb9e8, (unsigned long)CORESIGHT_SOC_600_ETR_CAPS),
+ /* Coresight SoC 600 TMC-ETB */
+ CS_AMBA_ID(0x000bb9e9),
+ /* Coresight SoC 600 TMC-ETF */
+ CS_AMBA_ID(0x000bb9ea),
{ 0, 0},
};
diff --git a/drivers/i2c/algos/i2c-algo-bit.c b/drivers/i2c/algos/i2c-algo-bit.c
index c33dcfb87993..5e5990a83da5 100644
--- a/drivers/i2c/algos/i2c-algo-bit.c
+++ b/drivers/i2c/algos/i2c-algo-bit.c
@@ -1,21 +1,12 @@
-/* -------------------------------------------------------------------------
- * i2c-algo-bit.c i2c driver algorithms for bit-shift adapters
- * -------------------------------------------------------------------------
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * i2c-algo-bit.c: i2c driver algorithms for bit-shift adapters
+ *
* Copyright (C) 1995-2000 Simon G. Vogl
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- * ------------------------------------------------------------------------- */
-
-/* With some changes from Frodo Looijaard <frodol@dds.nl>, Kyösti Mälkki
- <kmalkki@cc.hut.fi> and Jean Delvare <jdelvare@suse.de> */
+ *
+ * With some changes from Frodo Looijaard <frodol@dds.nl>, Kyösti Mälkki
+ * <kmalkki@cc.hut.fi> and Jean Delvare <jdelvare@suse.de>
+ */
#include <linux/kernel.h>
#include <linux/module.h>
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index f2c681971201..f8979abb9a19 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -131,6 +131,7 @@ config I2C_I801
Cannon Lake (PCH)
Cedar Fork (PCH)
Ice Lake (PCH)
+ Comet Lake (PCH)
This driver can also be built as a module. If so, the module
will be called i2c-i801.
diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c
index 8dc9161ced38..6c8b38fd6e64 100644
--- a/drivers/i2c/busses/i2c-aspeed.c
+++ b/drivers/i2c/busses/i2c-aspeed.c
@@ -117,6 +117,7 @@
enum aspeed_i2c_master_state {
ASPEED_I2C_MASTER_INACTIVE,
+ ASPEED_I2C_MASTER_PENDING,
ASPEED_I2C_MASTER_START,
ASPEED_I2C_MASTER_TX_FIRST,
ASPEED_I2C_MASTER_TX,
@@ -126,12 +127,13 @@ enum aspeed_i2c_master_state {
};
enum aspeed_i2c_slave_state {
- ASPEED_I2C_SLAVE_STOP,
+ ASPEED_I2C_SLAVE_INACTIVE,
ASPEED_I2C_SLAVE_START,
ASPEED_I2C_SLAVE_READ_REQUESTED,
ASPEED_I2C_SLAVE_READ_PROCESSED,
ASPEED_I2C_SLAVE_WRITE_REQUESTED,
ASPEED_I2C_SLAVE_WRITE_RECEIVED,
+ ASPEED_I2C_SLAVE_STOP,
};
struct aspeed_i2c_bus {
@@ -156,6 +158,8 @@ struct aspeed_i2c_bus {
int cmd_err;
/* Protected only by i2c_lock_bus */
int master_xfer_result;
+ /* Multi-master */
+ bool multi_master;
#if IS_ENABLED(CONFIG_I2C_SLAVE)
struct i2c_client *slave;
enum aspeed_i2c_slave_state slave_state;
@@ -251,7 +255,7 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
}
/* Slave is not currently active, irq was for someone else. */
- if (bus->slave_state == ASPEED_I2C_SLAVE_STOP)
+ if (bus->slave_state == ASPEED_I2C_SLAVE_INACTIVE)
return irq_handled;
dev_dbg(bus->dev, "slave irq status 0x%08x, cmd 0x%08x\n",
@@ -277,16 +281,15 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
irq_handled |= ASPEED_I2CD_INTR_NORMAL_STOP;
bus->slave_state = ASPEED_I2C_SLAVE_STOP;
}
- if (irq_status & ASPEED_I2CD_INTR_TX_NAK) {
+ if (irq_status & ASPEED_I2CD_INTR_TX_NAK &&
+ bus->slave_state == ASPEED_I2C_SLAVE_READ_PROCESSED) {
irq_handled |= ASPEED_I2CD_INTR_TX_NAK;
bus->slave_state = ASPEED_I2C_SLAVE_STOP;
}
- if (irq_status & ASPEED_I2CD_INTR_TX_ACK)
- irq_handled |= ASPEED_I2CD_INTR_TX_ACK;
switch (bus->slave_state) {
case ASPEED_I2C_SLAVE_READ_REQUESTED:
- if (irq_status & ASPEED_I2CD_INTR_TX_ACK)
+ if (unlikely(irq_status & ASPEED_I2CD_INTR_TX_ACK))
dev_err(bus->dev, "Unexpected ACK on read request.\n");
bus->slave_state = ASPEED_I2C_SLAVE_READ_PROCESSED;
i2c_slave_event(slave, I2C_SLAVE_READ_REQUESTED, &value);
@@ -294,9 +297,12 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
writel(ASPEED_I2CD_S_TX_CMD, bus->base + ASPEED_I2C_CMD_REG);
break;
case ASPEED_I2C_SLAVE_READ_PROCESSED:
- if (!(irq_status & ASPEED_I2CD_INTR_TX_ACK))
+ if (unlikely(!(irq_status & ASPEED_I2CD_INTR_TX_ACK))) {
dev_err(bus->dev,
"Expected ACK after processed read.\n");
+ break;
+ }
+ irq_handled |= ASPEED_I2CD_INTR_TX_ACK;
i2c_slave_event(slave, I2C_SLAVE_READ_PROCESSED, &value);
writel(value, bus->base + ASPEED_I2C_BYTE_BUF_REG);
writel(ASPEED_I2CD_S_TX_CMD, bus->base + ASPEED_I2C_CMD_REG);
@@ -310,10 +316,15 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
break;
case ASPEED_I2C_SLAVE_STOP:
i2c_slave_event(slave, I2C_SLAVE_STOP, &value);
+ bus->slave_state = ASPEED_I2C_SLAVE_INACTIVE;
+ break;
+ case ASPEED_I2C_SLAVE_START:
+ /* Slave was just started. Waiting for the next event. */;
break;
default:
- dev_err(bus->dev, "unhandled slave_state: %d\n",
+ dev_err(bus->dev, "unknown slave_state: %d\n",
bus->slave_state);
+ bus->slave_state = ASPEED_I2C_SLAVE_INACTIVE;
break;
}
@@ -329,6 +340,17 @@ static void aspeed_i2c_do_start(struct aspeed_i2c_bus *bus)
u8 slave_addr = i2c_8bit_addr_from_msg(msg);
bus->master_state = ASPEED_I2C_MASTER_START;
+
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ /*
+ * If it's requested in the middle of a slave session, set the master
+ * state to 'pending' then H/W will continue handling this master
+ * command when the bus comes back to the idle state.
+ */
+ if (bus->slave_state != ASPEED_I2C_SLAVE_INACTIVE)
+ bus->master_state = ASPEED_I2C_MASTER_PENDING;
+#endif /* CONFIG_I2C_SLAVE */
+
bus->buf_index = 0;
if (msg->flags & I2C_M_RD) {
@@ -384,10 +406,6 @@ static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
bus->master_state = ASPEED_I2C_MASTER_INACTIVE;
irq_handled |= ASPEED_I2CD_INTR_BUS_RECOVER_DONE;
goto out_complete;
- } else {
- /* Master is not currently active, irq was for someone else. */
- if (bus->master_state == ASPEED_I2C_MASTER_INACTIVE)
- goto out_no_complete;
}
/*
@@ -399,11 +417,32 @@ static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
if (ret) {
dev_dbg(bus->dev, "received error interrupt: 0x%08x\n",
irq_status);
- bus->cmd_err = ret;
- bus->master_state = ASPEED_I2C_MASTER_INACTIVE;
irq_handled |= (irq_status & ASPEED_I2CD_INTR_MASTER_ERRORS);
- goto out_complete;
+ if (bus->master_state != ASPEED_I2C_MASTER_INACTIVE) {
+ bus->cmd_err = ret;
+ bus->master_state = ASPEED_I2C_MASTER_INACTIVE;
+ goto out_complete;
+ }
+ }
+
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ /*
+ * A pending master command will be started by H/W when the bus comes
+ * back to idle state after completing a slave operation so change the
+ * master state from 'pending' to 'start' at here if slave is inactive.
+ */
+ if (bus->master_state == ASPEED_I2C_MASTER_PENDING) {
+ if (bus->slave_state != ASPEED_I2C_SLAVE_INACTIVE)
+ goto out_no_complete;
+
+ bus->master_state = ASPEED_I2C_MASTER_START;
}
+#endif /* CONFIG_I2C_SLAVE */
+
+ /* Master is not currently active, irq was for someone else. */
+ if (bus->master_state == ASPEED_I2C_MASTER_INACTIVE ||
+ bus->master_state == ASPEED_I2C_MASTER_PENDING)
+ goto out_no_complete;
/* We are in an invalid state; reset bus to a known state. */
if (!bus->msgs) {
@@ -423,6 +462,20 @@ static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
* then update the state and handle the new state below.
*/
if (bus->master_state == ASPEED_I2C_MASTER_START) {
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ /*
+ * If a peer master starts a xfer immediately after it queues a
+ * master command, change its state to 'pending' then H/W will
+ * continue the queued master xfer just after completing the
+ * slave mode session.
+ */
+ if (unlikely(irq_status & ASPEED_I2CD_INTR_SLAVE_MATCH)) {
+ bus->master_state = ASPEED_I2C_MASTER_PENDING;
+ dev_dbg(bus->dev,
+ "master goes pending due to a slave start\n");
+ goto out_no_complete;
+ }
+#endif /* CONFIG_I2C_SLAVE */
if (unlikely(!(irq_status & ASPEED_I2CD_INTR_TX_ACK))) {
if (unlikely(!(irq_status & ASPEED_I2CD_INTR_TX_NAK))) {
bus->cmd_err = -ENXIO;
@@ -566,7 +619,8 @@ static irqreturn_t aspeed_i2c_bus_irq(int irq, void *dev_id)
* interrupt bits. Each case needs to be handled using corresponding
* handlers depending on the current state.
*/
- if (bus->master_state != ASPEED_I2C_MASTER_INACTIVE) {
+ if (bus->master_state != ASPEED_I2C_MASTER_INACTIVE &&
+ bus->master_state != ASPEED_I2C_MASTER_PENDING) {
irq_handled = aspeed_i2c_master_irq(bus, irq_remaining);
irq_remaining &= ~irq_handled;
if (irq_remaining)
@@ -601,15 +655,16 @@ static int aspeed_i2c_master_xfer(struct i2c_adapter *adap,
{
struct aspeed_i2c_bus *bus = i2c_get_adapdata(adap);
unsigned long time_left, flags;
- int ret = 0;
spin_lock_irqsave(&bus->lock, flags);
bus->cmd_err = 0;
- /* If bus is busy, attempt recovery. We assume a single master
- * environment.
- */
- if (readl(bus->base + ASPEED_I2C_CMD_REG) & ASPEED_I2CD_BUS_BUSY_STS) {
+ /* If bus is busy in a single master environment, attempt recovery. */
+ if (!bus->multi_master &&
+ (readl(bus->base + ASPEED_I2C_CMD_REG) &
+ ASPEED_I2CD_BUS_BUSY_STS)) {
+ int ret;
+
spin_unlock_irqrestore(&bus->lock, flags);
ret = aspeed_i2c_recover_bus(bus);
if (ret)
@@ -629,10 +684,20 @@ static int aspeed_i2c_master_xfer(struct i2c_adapter *adap,
time_left = wait_for_completion_timeout(&bus->cmd_complete,
bus->adap.timeout);
- if (time_left == 0)
+ if (time_left == 0) {
+ /*
+ * If timed out and bus is still busy in a multi master
+ * environment, attempt recovery at here.
+ */
+ if (bus->multi_master &&
+ (readl(bus->base + ASPEED_I2C_CMD_REG) &
+ ASPEED_I2CD_BUS_BUSY_STS))
+ aspeed_i2c_recover_bus(bus);
+
return -ETIMEDOUT;
- else
- return bus->master_xfer_result;
+ }
+
+ return bus->master_xfer_result;
}
static u32 aspeed_i2c_functionality(struct i2c_adapter *adap)
@@ -672,7 +737,7 @@ static int aspeed_i2c_reg_slave(struct i2c_client *client)
__aspeed_i2c_reg_slave(bus, client->addr);
bus->slave = client;
- bus->slave_state = ASPEED_I2C_SLAVE_STOP;
+ bus->slave_state = ASPEED_I2C_SLAVE_INACTIVE;
spin_unlock_irqrestore(&bus->lock, flags);
return 0;
@@ -827,7 +892,9 @@ static int aspeed_i2c_init(struct aspeed_i2c_bus *bus,
if (ret < 0)
return ret;
- if (!of_property_read_bool(pdev->dev.of_node, "multi-master"))
+ if (of_property_read_bool(pdev->dev.of_node, "multi-master"))
+ bus->multi_master = true;
+ else
fun_ctrl_reg |= ASPEED_I2CD_MULTI_MASTER_DIS;
/* Enable Master Mode */
@@ -930,7 +997,6 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev)
init_completion(&bus->cmd_complete);
bus->adap.owner = THIS_MODULE;
bus->adap.retries = 0;
- bus->adap.timeout = 5 * HZ;
bus->adap.algo = &aspeed_i2c_algo;
bus->adap.dev.parent = &pdev->dev;
bus->adap.dev.of_node = pdev->dev.of_node;
diff --git a/drivers/i2c/busses/i2c-brcmstb.c b/drivers/i2c/busses/i2c-brcmstb.c
index 826d32049996..f4d862234980 100644
--- a/drivers/i2c/busses/i2c-brcmstb.c
+++ b/drivers/i2c/busses/i2c-brcmstb.c
@@ -170,7 +170,6 @@ struct brcmstb_i2c_dev {
struct bsc_regs *bsc_regmap;
struct i2c_adapter adapter;
struct completion done;
- bool is_suspended;
u32 clk_freq_hz;
int data_regsz;
};
@@ -467,9 +466,6 @@ static int brcmstb_i2c_xfer(struct i2c_adapter *adapter,
int xfersz = brcmstb_i2c_get_xfersz(dev);
u32 cond, cond_per_msg;
- if (dev->is_suspended)
- return -EBUSY;
-
/* Loop through all messages */
for (i = 0; i < num; i++) {
pmsg = &msgs[i];
@@ -689,10 +685,7 @@ static int brcmstb_i2c_suspend(struct device *dev)
{
struct brcmstb_i2c_dev *i2c_dev = dev_get_drvdata(dev);
- i2c_lock_bus(&i2c_dev->adapter, I2C_LOCK_ROOT_ADAPTER);
- i2c_dev->is_suspended = true;
- i2c_unlock_bus(&i2c_dev->adapter, I2C_LOCK_ROOT_ADAPTER);
-
+ i2c_mark_adapter_suspended(&i2c_dev->adapter);
return 0;
}
@@ -700,10 +693,8 @@ static int brcmstb_i2c_resume(struct device *dev)
{
struct brcmstb_i2c_dev *i2c_dev = dev_get_drvdata(dev);
- i2c_lock_bus(&i2c_dev->adapter, I2C_LOCK_ROOT_ADAPTER);
brcmstb_i2c_set_bsc_reg_defaults(i2c_dev);
- i2c_dev->is_suspended = false;
- i2c_unlock_bus(&i2c_dev->adapter, I2C_LOCK_ROOT_ADAPTER);
+ i2c_mark_adapter_resumed(&i2c_dev->adapter);
return 0;
}
diff --git a/drivers/i2c/busses/i2c-cbus-gpio.c b/drivers/i2c/busses/i2c-cbus-gpio.c
index b4f91e48948a..72df563477b1 100644
--- a/drivers/i2c/busses/i2c-cbus-gpio.c
+++ b/drivers/i2c/busses/i2c-cbus-gpio.c
@@ -18,16 +18,14 @@
#include <linux/io.h>
#include <linux/i2c.h>
-#include <linux/gpio.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/of_gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
-#include <linux/platform_data/i2c-cbus-gpio.h>
/*
* Bit counts are derived from Nokia implementation. These should be checked
@@ -39,9 +37,9 @@
struct cbus_host {
spinlock_t lock; /* host lock */
struct device *dev;
- int clk_gpio;
- int dat_gpio;
- int sel_gpio;
+ struct gpio_desc *clk;
+ struct gpio_desc *dat;
+ struct gpio_desc *sel;
};
/**
@@ -51,9 +49,9 @@ struct cbus_host {
*/
static void cbus_send_bit(struct cbus_host *host, unsigned bit)
{
- gpio_set_value(host->dat_gpio, bit ? 1 : 0);
- gpio_set_value(host->clk_gpio, 1);
- gpio_set_value(host->clk_gpio, 0);
+ gpiod_set_value(host->dat, bit ? 1 : 0);
+ gpiod_set_value(host->clk, 1);
+ gpiod_set_value(host->clk, 0);
}
/**
@@ -78,9 +76,9 @@ static int cbus_receive_bit(struct cbus_host *host)
{
int ret;
- gpio_set_value(host->clk_gpio, 1);
- ret = gpio_get_value(host->dat_gpio);
- gpio_set_value(host->clk_gpio, 0);
+ gpiod_set_value(host->clk, 1);
+ ret = gpiod_get_value(host->dat);
+ gpiod_set_value(host->clk, 0);
return ret;
}
@@ -123,10 +121,10 @@ static int cbus_transfer(struct cbus_host *host, char rw, unsigned dev,
spin_lock_irqsave(&host->lock, flags);
/* Reset state and start of transfer, SEL stays down during transfer */
- gpio_set_value(host->sel_gpio, 0);
+ gpiod_set_value(host->sel, 0);
/* Set the DAT pin to output */
- gpio_direction_output(host->dat_gpio, 1);
+ gpiod_direction_output(host->dat, 1);
/* Send the device address */
cbus_send_data(host, dev, CBUS_ADDR_BITS);
@@ -141,12 +139,12 @@ static int cbus_transfer(struct cbus_host *host, char rw, unsigned dev,
cbus_send_data(host, data, 16);
ret = 0;
} else {
- ret = gpio_direction_input(host->dat_gpio);
+ ret = gpiod_direction_input(host->dat);
if (ret) {
dev_dbg(host->dev, "failed setting direction\n");
goto out;
}
- gpio_set_value(host->clk_gpio, 1);
+ gpiod_set_value(host->clk, 1);
ret = cbus_receive_word(host);
if (ret < 0) {
@@ -156,9 +154,9 @@ static int cbus_transfer(struct cbus_host *host, char rw, unsigned dev,
}
/* Indicate end of transfer, SEL goes up until next transfer */
- gpio_set_value(host->sel_gpio, 1);
- gpio_set_value(host->clk_gpio, 1);
- gpio_set_value(host->clk_gpio, 0);
+ gpiod_set_value(host->sel, 1);
+ gpiod_set_value(host->clk, 1);
+ gpiod_set_value(host->clk, 0);
out:
spin_unlock_irqrestore(&host->lock, flags);
@@ -214,7 +212,6 @@ static int cbus_i2c_probe(struct platform_device *pdev)
{
struct i2c_adapter *adapter;
struct cbus_host *chost;
- int ret;
adapter = devm_kzalloc(&pdev->dev, sizeof(struct i2c_adapter),
GFP_KERNEL);
@@ -225,22 +222,20 @@ static int cbus_i2c_probe(struct platform_device *pdev)
if (!chost)
return -ENOMEM;
- if (pdev->dev.of_node) {
- struct device_node *dnode = pdev->dev.of_node;
- if (of_gpio_count(dnode) != 3)
- return -ENODEV;
- chost->clk_gpio = of_get_gpio(dnode, 0);
- chost->dat_gpio = of_get_gpio(dnode, 1);
- chost->sel_gpio = of_get_gpio(dnode, 2);
- } else if (dev_get_platdata(&pdev->dev)) {
- struct i2c_cbus_platform_data *pdata =
- dev_get_platdata(&pdev->dev);
- chost->clk_gpio = pdata->clk_gpio;
- chost->dat_gpio = pdata->dat_gpio;
- chost->sel_gpio = pdata->sel_gpio;
- } else {
+ if (gpiod_count(&pdev->dev, NULL) != 3)
return -ENODEV;
- }
+ chost->clk = devm_gpiod_get_index(&pdev->dev, NULL, 0, GPIOD_OUT_LOW);
+ if (IS_ERR(chost->clk))
+ return PTR_ERR(chost->clk);
+ chost->dat = devm_gpiod_get_index(&pdev->dev, NULL, 1, GPIOD_IN);
+ if (IS_ERR(chost->dat))
+ return PTR_ERR(chost->dat);
+ chost->sel = devm_gpiod_get_index(&pdev->dev, NULL, 2, GPIOD_OUT_HIGH);
+ if (IS_ERR(chost->sel))
+ return PTR_ERR(chost->sel);
+ gpiod_set_consumer_name(chost->clk, "CBUS clk");
+ gpiod_set_consumer_name(chost->dat, "CBUS dat");
+ gpiod_set_consumer_name(chost->sel, "CBUS sel");
adapter->owner = THIS_MODULE;
adapter->class = I2C_CLASS_HWMON;
@@ -254,21 +249,6 @@ static int cbus_i2c_probe(struct platform_device *pdev)
spin_lock_init(&chost->lock);
chost->dev = &pdev->dev;
- ret = devm_gpio_request_one(&pdev->dev, chost->clk_gpio,
- GPIOF_OUT_INIT_LOW, "CBUS clk");
- if (ret)
- return ret;
-
- ret = devm_gpio_request_one(&pdev->dev, chost->dat_gpio, GPIOF_IN,
- "CBUS data");
- if (ret)
- return ret;
-
- ret = devm_gpio_request_one(&pdev->dev, chost->sel_gpio,
- GPIOF_OUT_INIT_HIGH, "CBUS sel");
- if (ret)
- return ret;
-
i2c_set_adapdata(adapter, chost);
platform_set_drvdata(pdev, adapter);
diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h
index b4a0b2b99a78..6b4ef1d38fb2 100644
--- a/drivers/i2c/busses/i2c-designware-core.h
+++ b/drivers/i2c/busses/i2c-designware-core.h
@@ -215,6 +215,7 @@
* @disable_int: function to disable all interrupts
* @init: function to initialize the I2C hardware
* @mode: operation mode - DW_IC_MASTER or DW_IC_SLAVE
+ * @suspended: set to true if the controller is suspended
*
* HCNT and LCNT parameters can be used if the platform knows more accurate
* values than the one computed based only on the input clock frequency.
@@ -270,6 +271,7 @@ struct dw_i2c_dev {
int (*set_sda_hold_time)(struct dw_i2c_dev *dev);
int mode;
struct i2c_bus_recovery_info rinfo;
+ bool suspended;
};
#define ACCESS_SWAP 0x00000001
diff --git a/drivers/i2c/busses/i2c-designware-master.c b/drivers/i2c/busses/i2c-designware-master.c
index 8d1bc44d2530..bb8e3f149979 100644
--- a/drivers/i2c/busses/i2c-designware-master.c
+++ b/drivers/i2c/busses/i2c-designware-master.c
@@ -426,6 +426,12 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
pm_runtime_get_sync(dev->dev);
+ if (dev->suspended) {
+ dev_err(dev->dev, "Error %s call while suspended\n", __func__);
+ ret = -ESHUTDOWN;
+ goto done_nolock;
+ }
+
reinit_completion(&dev->cmd_complete);
dev->msgs = msgs;
dev->msgs_num = num;
diff --git a/drivers/i2c/busses/i2c-designware-pcidrv.c b/drivers/i2c/busses/i2c-designware-pcidrv.c
index d50f80487214..76810deb2de6 100644
--- a/drivers/i2c/busses/i2c-designware-pcidrv.c
+++ b/drivers/i2c/busses/i2c-designware-pcidrv.c
@@ -176,6 +176,7 @@ static int i2c_dw_pci_suspend(struct device *dev)
struct pci_dev *pdev = to_pci_dev(dev);
struct dw_i2c_dev *i_dev = pci_get_drvdata(pdev);
+ i_dev->suspended = true;
i_dev->disable(i_dev);
return 0;
@@ -185,8 +186,12 @@ static int i2c_dw_pci_resume(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct dw_i2c_dev *i_dev = pci_get_drvdata(pdev);
+ int ret;
- return i_dev->init(i_dev);
+ ret = i_dev->init(i_dev);
+ i_dev->suspended = false;
+
+ return ret;
}
#endif
diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c
index 9eaac3be1f63..416f89b8f881 100644
--- a/drivers/i2c/busses/i2c-designware-platdrv.c
+++ b/drivers/i2c/busses/i2c-designware-platdrv.c
@@ -86,7 +86,6 @@ static int dw_i2c_acpi_configure(struct platform_device *pdev)
struct i2c_timings *t = &dev->timings;
u32 ss_ht = 0, fp_ht = 0, hs_ht = 0, fs_ht = 0;
- dev->adapter.nr = -1;
dev->tx_fifo_depth = 32;
dev->rx_fifo_depth = 32;
@@ -219,7 +218,7 @@ static void i2c_dw_configure_slave(struct dw_i2c_dev *dev)
dev->mode = DW_IC_SLAVE;
}
-static void dw_i2c_set_fifo_size(struct dw_i2c_dev *dev, int id)
+static void dw_i2c_set_fifo_size(struct dw_i2c_dev *dev)
{
u32 param, tx_fifo_depth, rx_fifo_depth;
@@ -233,7 +232,6 @@ static void dw_i2c_set_fifo_size(struct dw_i2c_dev *dev, int id)
if (!dev->tx_fifo_depth) {
dev->tx_fifo_depth = tx_fifo_depth;
dev->rx_fifo_depth = rx_fifo_depth;
- dev->adapter.nr = id;
} else if (tx_fifo_depth >= 2) {
dev->tx_fifo_depth = min_t(u32, dev->tx_fifo_depth,
tx_fifo_depth);
@@ -358,13 +356,14 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
div_u64(clk_khz * t->sda_hold_ns + 500000, 1000000);
}
- dw_i2c_set_fifo_size(dev, pdev->id);
+ dw_i2c_set_fifo_size(dev);
adap = &dev->adapter;
adap->owner = THIS_MODULE;
adap->class = I2C_CLASS_DEPRECATED;
ACPI_COMPANION_SET(&adap->dev, ACPI_COMPANION(&pdev->dev));
adap->dev.of_node = pdev->dev.of_node;
+ adap->nr = -1;
dev_pm_set_driver_flags(&pdev->dev,
DPM_FLAG_SMART_PREPARE |
@@ -454,6 +453,8 @@ static int dw_i2c_plat_suspend(struct device *dev)
{
struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
+ i_dev->suspended = true;
+
if (i_dev->shared_with_punit)
return 0;
@@ -471,6 +472,7 @@ static int dw_i2c_plat_resume(struct device *dev)
i2c_dw_prepare_clk(i_dev, true);
i_dev->init(i_dev);
+ i_dev->suspended = false;
return 0;
}
diff --git a/drivers/i2c/busses/i2c-eg20t.c b/drivers/i2c/busses/i2c-eg20t.c
index 835d54ac2971..231675b10376 100644
--- a/drivers/i2c/busses/i2c-eg20t.c
+++ b/drivers/i2c/busses/i2c-eg20t.c
@@ -177,7 +177,6 @@ static wait_queue_head_t pch_event;
static DEFINE_MUTEX(pch_mutex);
/* Definition for ML7213 by LAPIS Semiconductor */
-#define PCI_VENDOR_ID_ROHM 0x10DB
#define PCI_DEVICE_ID_ML7213_I2C 0x802D
#define PCI_DEVICE_ID_ML7223_I2C 0x8010
#define PCI_DEVICE_ID_ML7831_I2C 0x8817
diff --git a/drivers/i2c/busses/i2c-exynos5.c b/drivers/i2c/busses/i2c-exynos5.c
index c1ce2299a76e..41de4ee409b6 100644
--- a/drivers/i2c/busses/i2c-exynos5.c
+++ b/drivers/i2c/busses/i2c-exynos5.c
@@ -183,7 +183,6 @@ enum i2c_type_exynos {
struct exynos5_i2c {
struct i2c_adapter adap;
- unsigned int suspended:1;
struct i2c_msg *msg;
struct completion msg_complete;
@@ -715,11 +714,6 @@ static int exynos5_i2c_xfer(struct i2c_adapter *adap,
struct exynos5_i2c *i2c = adap->algo_data;
int i, ret;
- if (i2c->suspended) {
- dev_err(i2c->dev, "HS-I2C is not initialized.\n");
- return -EIO;
- }
-
ret = clk_enable(i2c->clk);
if (ret)
return ret;
@@ -847,8 +841,7 @@ static int exynos5_i2c_suspend_noirq(struct device *dev)
{
struct exynos5_i2c *i2c = dev_get_drvdata(dev);
- i2c->suspended = 1;
-
+ i2c_mark_adapter_suspended(&i2c->adap);
clk_unprepare(i2c->clk);
return 0;
@@ -871,7 +864,7 @@ static int exynos5_i2c_resume_noirq(struct device *dev)
exynos5_i2c_init(i2c);
clk_disable(i2c->clk);
- i2c->suspended = 0;
+ i2c_mark_adapter_resumed(&i2c->adap);
return 0;
}
diff --git a/drivers/i2c/busses/i2c-gpio.c b/drivers/i2c/busses/i2c-gpio.c
index c008d209f0b8..bba5c4627de3 100644
--- a/drivers/i2c/busses/i2c-gpio.c
+++ b/drivers/i2c/busses/i2c-gpio.c
@@ -7,17 +7,19 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
+#include <linux/completion.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
-#include <linux/i2c.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c-algo-bit.h>
-#include <linux/platform_data/i2c-gpio.h>
+#include <linux/i2c.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/platform_device.h>
-#include <linux/gpio/consumer.h>
#include <linux/of.h>
+#include <linux/platform_data/i2c-gpio.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
struct i2c_gpio_private_data {
struct gpio_desc *sda;
@@ -27,6 +29,9 @@ struct i2c_gpio_private_data {
struct i2c_gpio_platform_data pdata;
#ifdef CONFIG_I2C_GPIO_FAULT_INJECTOR
struct dentry *debug_dir;
+ /* these must be protected by bus lock */
+ struct completion scl_irq_completion;
+ u64 scl_irq_data;
#endif
};
@@ -162,6 +167,96 @@ static int fops_incomplete_write_byte_set(void *data, u64 addr)
}
DEFINE_DEBUGFS_ATTRIBUTE(fops_incomplete_write_byte, NULL, fops_incomplete_write_byte_set, "%llu\n");
+static int i2c_gpio_fi_act_on_scl_irq(struct i2c_gpio_private_data *priv,
+ irqreturn_t handler(int, void*))
+{
+ int ret, irq = gpiod_to_irq(priv->scl);
+
+ if (irq < 0)
+ return irq;
+
+ i2c_lock_bus(&priv->adap, I2C_LOCK_ROOT_ADAPTER);
+
+ ret = gpiod_direction_input(priv->scl);
+ if (ret)
+ goto unlock;
+
+ reinit_completion(&priv->scl_irq_completion);
+
+ ret = request_irq(irq, handler, IRQF_TRIGGER_FALLING,
+ "i2c_gpio_fault_injector_scl_irq", priv);
+ if (ret)
+ goto output;
+
+ wait_for_completion_interruptible(&priv->scl_irq_completion);
+
+ free_irq(irq, priv);
+ output:
+ ret = gpiod_direction_output(priv->scl, 1) ?: ret;
+ unlock:
+ i2c_unlock_bus(&priv->adap, I2C_LOCK_ROOT_ADAPTER);
+
+ return ret;
+}
+
+static irqreturn_t lose_arbitration_irq(int irq, void *dev_id)
+{
+ struct i2c_gpio_private_data *priv = dev_id;
+
+ setsda(&priv->bit_data, 0);
+ udelay(priv->scl_irq_data);
+ setsda(&priv->bit_data, 1);
+
+ complete(&priv->scl_irq_completion);
+
+ return IRQ_HANDLED;
+}
+
+static int fops_lose_arbitration_set(void *data, u64 duration)
+{
+ struct i2c_gpio_private_data *priv = data;
+
+ if (duration > 100 * 1000)
+ return -EINVAL;
+
+ priv->scl_irq_data = duration;
+ /*
+ * Interrupt on falling SCL. This ensures that the master under test has
+ * really started the transfer. Interrupt on falling SDA did only
+ * exercise 'bus busy' detection on some HW but not 'arbitration lost'.
+ * Note that the interrupt latency may cause the first bits to be
+ * transmitted correctly.
+ */
+ return i2c_gpio_fi_act_on_scl_irq(priv, lose_arbitration_irq);
+}
+DEFINE_DEBUGFS_ATTRIBUTE(fops_lose_arbitration, NULL, fops_lose_arbitration_set, "%llu\n");
+
+static irqreturn_t inject_panic_irq(int irq, void *dev_id)
+{
+ struct i2c_gpio_private_data *priv = dev_id;
+
+ udelay(priv->scl_irq_data);
+ panic("I2C fault injector induced panic");
+
+ return IRQ_HANDLED;
+}
+
+static int fops_inject_panic_set(void *data, u64 duration)
+{
+ struct i2c_gpio_private_data *priv = data;
+
+ if (duration > 100 * 1000)
+ return -EINVAL;
+
+ priv->scl_irq_data = duration;
+ /*
+ * Interrupt on falling SCL. This ensures that the master under test has
+ * really started the transfer.
+ */
+ return i2c_gpio_fi_act_on_scl_irq(priv, inject_panic_irq);
+}
+DEFINE_DEBUGFS_ATTRIBUTE(fops_inject_panic, NULL, fops_inject_panic_set, "%llu\n");
+
static void i2c_gpio_fault_injector_init(struct platform_device *pdev)
{
struct i2c_gpio_private_data *priv = platform_get_drvdata(pdev);
@@ -181,12 +276,20 @@ static void i2c_gpio_fault_injector_init(struct platform_device *pdev)
if (!priv->debug_dir)
return;
- debugfs_create_file_unsafe("scl", 0600, priv->debug_dir, priv, &fops_scl);
- debugfs_create_file_unsafe("sda", 0600, priv->debug_dir, priv, &fops_sda);
+ init_completion(&priv->scl_irq_completion);
+
debugfs_create_file_unsafe("incomplete_address_phase", 0200, priv->debug_dir,
priv, &fops_incomplete_addr_phase);
debugfs_create_file_unsafe("incomplete_write_byte", 0200, priv->debug_dir,
priv, &fops_incomplete_write_byte);
+ if (priv->bit_data.getscl) {
+ debugfs_create_file_unsafe("inject_panic", 0200, priv->debug_dir,
+ priv, &fops_inject_panic);
+ debugfs_create_file_unsafe("lose_arbitration", 0200, priv->debug_dir,
+ priv, &fops_lose_arbitration);
+ }
+ debugfs_create_file_unsafe("scl", 0600, priv->debug_dir, priv, &fops_scl);
+ debugfs_create_file_unsafe("sda", 0600, priv->debug_dir, priv, &fops_sda);
}
static void i2c_gpio_fault_injector_exit(struct platform_device *pdev)
@@ -286,11 +389,11 @@ static int i2c_gpio_probe(struct platform_device *pdev)
/*
* First get the GPIO pins; if it fails, we'll defer the probe.
- * If the SDA line is marked from platform data or device tree as
- * "open drain" it means something outside of our control is making
- * this line being handled as open drain, and we should just handle
- * it as any other output. Else we enforce open drain as this is
- * required for an I2C bus.
+ * If the SCL/SDA lines are marked "open drain" by platform data or
+ * device tree then this means that something outside of our control is
+ * marking these lines to be handled as open drain, and we should just
+ * handle them as we handle any other output. Else we enforce open
+ * drain as this is required for an I2C bus.
*/
if (pdata->sda_is_open_drain)
gflags = GPIOD_OUT_HIGH;
@@ -300,13 +403,6 @@ static int i2c_gpio_probe(struct platform_device *pdev)
if (IS_ERR(priv->sda))
return PTR_ERR(priv->sda);
- /*
- * If the SCL line is marked from platform data or device tree as
- * "open drain" it means something outside of our control is making
- * this line being handled as open drain, and we should just handle
- * it as any other output. Else we enforce open drain as this is
- * required for an I2C bus.
- */
if (pdata->scl_is_open_drain)
gflags = GPIOD_OUT_HIGH;
else
diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
index c91e145ef5a5..679c6c41f64b 100644
--- a/drivers/i2c/busses/i2c-i801.c
+++ b/drivers/i2c/busses/i2c-i801.c
@@ -71,6 +71,7 @@
* Cannon Lake-LP (PCH) 0x9da3 32 hard yes yes yes
* Cedar Fork (PCH) 0x18df 32 hard yes yes yes
* Ice Lake-LP (PCH) 0x34a3 32 hard yes yes yes
+ * Comet Lake (PCH) 0x02a3 32 hard yes yes yes
*
* Features supported by this driver:
* Software PEC no
@@ -240,6 +241,7 @@
#define PCI_DEVICE_ID_INTEL_LEWISBURG_SSKU_SMBUS 0xa223
#define PCI_DEVICE_ID_INTEL_KABYLAKE_PCH_H_SMBUS 0xa2a3
#define PCI_DEVICE_ID_INTEL_CANNONLAKE_H_SMBUS 0xa323
+#define PCI_DEVICE_ID_INTEL_COMETLAKE_SMBUS 0x02a3
struct i801_mux_config {
char *gpio_chip;
@@ -1038,6 +1040,7 @@ static const struct pci_device_id i801_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CANNONLAKE_H_SMBUS) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CANNONLAKE_LP_SMBUS) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICELAKE_LP_SMBUS) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_COMETLAKE_SMBUS) },
{ 0, }
};
@@ -1534,6 +1537,7 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
case PCI_DEVICE_ID_INTEL_DNV_SMBUS:
case PCI_DEVICE_ID_INTEL_KABYLAKE_PCH_H_SMBUS:
case PCI_DEVICE_ID_INTEL_ICELAKE_LP_SMBUS:
+ case PCI_DEVICE_ID_INTEL_COMETLAKE_SMBUS:
priv->features |= FEATURE_I2C_BLOCK_READ;
priv->features |= FEATURE_IRQ;
priv->features |= FEATURE_SMBUS_PEC;
diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c
index fa9ad53845d9..42fed40198a0 100644
--- a/drivers/i2c/busses/i2c-imx.c
+++ b/drivers/i2c/busses/i2c-imx.c
@@ -273,8 +273,8 @@ static inline unsigned char imx_i2c_read_reg(struct imx_i2c_struct *i2c_imx,
}
/* Functions for DMA support */
-static void i2c_imx_dma_request(struct imx_i2c_struct *i2c_imx,
- dma_addr_t phy_addr)
+static int i2c_imx_dma_request(struct imx_i2c_struct *i2c_imx,
+ dma_addr_t phy_addr)
{
struct imx_i2c_dma *dma;
struct dma_slave_config dma_sconfig;
@@ -283,11 +283,13 @@ static void i2c_imx_dma_request(struct imx_i2c_struct *i2c_imx,
dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL);
if (!dma)
- return;
+ return -ENOMEM;
- dma->chan_tx = dma_request_slave_channel(dev, "tx");
- if (!dma->chan_tx) {
- dev_dbg(dev, "can't request DMA tx channel\n");
+ dma->chan_tx = dma_request_chan(dev, "tx");
+ if (IS_ERR(dma->chan_tx)) {
+ ret = PTR_ERR(dma->chan_tx);
+ if (ret != -ENODEV && ret != -EPROBE_DEFER)
+ dev_err(dev, "can't request DMA tx channel (%d)\n", ret);
goto fail_al;
}
@@ -298,13 +300,15 @@ static void i2c_imx_dma_request(struct imx_i2c_struct *i2c_imx,
dma_sconfig.direction = DMA_MEM_TO_DEV;
ret = dmaengine_slave_config(dma->chan_tx, &dma_sconfig);
if (ret < 0) {
- dev_dbg(dev, "can't configure tx channel\n");
+ dev_err(dev, "can't configure tx channel (%d)\n", ret);
goto fail_tx;
}
- dma->chan_rx = dma_request_slave_channel(dev, "rx");
- if (!dma->chan_rx) {
- dev_dbg(dev, "can't request DMA rx channel\n");
+ dma->chan_rx = dma_request_chan(dev, "rx");
+ if (IS_ERR(dma->chan_rx)) {
+ ret = PTR_ERR(dma->chan_rx);
+ if (ret != -ENODEV && ret != -EPROBE_DEFER)
+ dev_err(dev, "can't request DMA rx channel (%d)\n", ret);
goto fail_tx;
}
@@ -315,7 +319,7 @@ static void i2c_imx_dma_request(struct imx_i2c_struct *i2c_imx,
dma_sconfig.direction = DMA_DEV_TO_MEM;
ret = dmaengine_slave_config(dma->chan_rx, &dma_sconfig);
if (ret < 0) {
- dev_dbg(dev, "can't configure rx channel\n");
+ dev_err(dev, "can't configure rx channel (%d)\n", ret);
goto fail_rx;
}
@@ -324,7 +328,7 @@ static void i2c_imx_dma_request(struct imx_i2c_struct *i2c_imx,
dev_info(dev, "using %s (tx) and %s (rx) for DMA transfers\n",
dma_chan_name(dma->chan_tx), dma_chan_name(dma->chan_rx));
- return;
+ return 0;
fail_rx:
dma_release_channel(dma->chan_rx);
@@ -332,7 +336,8 @@ fail_tx:
dma_release_channel(dma->chan_tx);
fail_al:
devm_kfree(dev, dma);
- dev_info(dev, "can't use DMA, using PIO instead.\n");
+ /* return successfully if there is no dma support */
+ return ret == -ENODEV ? 0 : ret;
}
static void i2c_imx_dma_callback(void *arg)
@@ -1160,11 +1165,13 @@ static int i2c_imx_probe(struct platform_device *pdev)
dev_dbg(&i2c_imx->adapter.dev, "device resources: %pR\n", res);
dev_dbg(&i2c_imx->adapter.dev, "adapter name: \"%s\"\n",
i2c_imx->adapter.name);
- dev_info(&i2c_imx->adapter.dev, "IMX I2C adapter registered\n");
/* Init DMA config if supported */
- i2c_imx_dma_request(i2c_imx, phy_addr);
+ ret = i2c_imx_dma_request(i2c_imx, phy_addr);
+ if (ret < 0)
+ goto clk_notifier_unregister;
+ dev_info(&i2c_imx->adapter.dev, "IMX I2C adapter registered\n");
return 0; /* Return OK */
clk_notifier_unregister:
diff --git a/drivers/i2c/busses/i2c-iop3xx.c b/drivers/i2c/busses/i2c-iop3xx.c
index 85cbe4b55578..a34cb3848280 100644
--- a/drivers/i2c/busses/i2c-iop3xx.c
+++ b/drivers/i2c/busses/i2c-iop3xx.c
@@ -471,6 +471,7 @@ iop3xx_i2c_probe(struct platform_device *pdev)
new_adapter->owner = THIS_MODULE;
new_adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
new_adapter->dev.parent = &pdev->dev;
+ new_adapter->dev.of_node = pdev->dev.of_node;
new_adapter->nr = pdev->id;
/*
@@ -508,12 +509,19 @@ out:
return ret;
}
+static const struct of_device_id i2c_iop3xx_match[] = {
+ { .compatible = "intel,iop3xx-i2c", },
+ { .compatible = "intel,ixp4xx-i2c", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, i2c_iop3xx_match);
static struct platform_driver iop3xx_i2c_driver = {
.probe = iop3xx_i2c_probe,
.remove = iop3xx_i2c_remove,
.driver = {
.name = "IOP3xx-I2C",
+ .of_match_table = i2c_iop3xx_match,
},
};
diff --git a/drivers/i2c/busses/i2c-mt65xx.c b/drivers/i2c/busses/i2c-mt65xx.c
index a74ef76705e0..684d651612b3 100644
--- a/drivers/i2c/busses/i2c-mt65xx.c
+++ b/drivers/i2c/busses/i2c-mt65xx.c
@@ -456,7 +456,7 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
control_reg = readw(i2c->base + OFFSET_CONTROL) &
~(I2C_CONTROL_DIR_CHANGE | I2C_CONTROL_RS);
- if ((i2c->speed_hz > 400000) || (left_num >= 1))
+ if ((i2c->speed_hz > MAX_FS_MODE_SPEED) || (left_num >= 1))
control_reg |= I2C_CONTROL_RS;
if (i2c->op == I2C_MASTER_WRRD)
@@ -465,7 +465,7 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
writew(control_reg, i2c->base + OFFSET_CONTROL);
/* set start condition */
- if (i2c->speed_hz <= 100000)
+ if (i2c->speed_hz <= I2C_DEFAULT_SPEED)
writew(I2C_ST_START_CON, i2c->base + OFFSET_EXT_CONF);
else
writew(I2C_FS_START_CON, i2c->base + OFFSET_EXT_CONF);
@@ -503,7 +503,7 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
writel(I2C_DMA_INT_FLAG_NONE, i2c->pdmabase + OFFSET_INT_FLAG);
writel(I2C_DMA_CON_RX, i2c->pdmabase + OFFSET_CON);
- dma_rd_buf = i2c_get_dma_safe_msg_buf(msgs, 0);
+ dma_rd_buf = i2c_get_dma_safe_msg_buf(msgs, 1);
if (!dma_rd_buf)
return -ENOMEM;
@@ -526,7 +526,7 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
writel(I2C_DMA_INT_FLAG_NONE, i2c->pdmabase + OFFSET_INT_FLAG);
writel(I2C_DMA_CON_TX, i2c->pdmabase + OFFSET_CON);
- dma_wr_buf = i2c_get_dma_safe_msg_buf(msgs, 0);
+ dma_wr_buf = i2c_get_dma_safe_msg_buf(msgs, 1);
if (!dma_wr_buf)
return -ENOMEM;
@@ -549,7 +549,7 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
writel(I2C_DMA_CLR_FLAG, i2c->pdmabase + OFFSET_INT_FLAG);
writel(I2C_DMA_CLR_FLAG, i2c->pdmabase + OFFSET_CON);
- dma_wr_buf = i2c_get_dma_safe_msg_buf(msgs, 0);
+ dma_wr_buf = i2c_get_dma_safe_msg_buf(msgs, 1);
if (!dma_wr_buf)
return -ENOMEM;
@@ -561,7 +561,7 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
return -ENOMEM;
}
- dma_rd_buf = i2c_get_dma_safe_msg_buf((msgs + 1), 0);
+ dma_rd_buf = i2c_get_dma_safe_msg_buf((msgs + 1), 1);
if (!dma_rd_buf) {
dma_unmap_single(i2c->dev, wpaddr,
msgs->len, DMA_TO_DEVICE);
@@ -642,8 +642,6 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
return -ETIMEDOUT;
}
- completion_done(&i2c->msg_complete);
-
if (i2c->irq_stat & (I2C_HS_NACKERR | I2C_ACKERR)) {
dev_dbg(i2c->dev, "addr: %x, transfer ACK error\n", msgs->addr);
mtk_i2c_init_hw(i2c);
diff --git a/drivers/i2c/busses/i2c-ocores.c b/drivers/i2c/busses/i2c-ocores.c
index 87f9caacba85..4e1a077fb688 100644
--- a/drivers/i2c/busses/i2c-ocores.c
+++ b/drivers/i2c/busses/i2c-ocores.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* i2c-ocores.c: I2C bus driver for OpenCores I2C controller
* (https://opencores.org/project/i2c/overview)
@@ -6,13 +7,10 @@
*
* Support for the GRLIB port of the controller by
* Andreas Larsson <andreas@gaisler.com>
- *
- * This file is licensed under the terms of the GNU General Public License
- * version 2. This program is licensed "as is" without any warranty of any
- * kind, whether express or implied.
*/
#include <linux/clk.h>
+#include <linux/delay.h>
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/module.h>
@@ -25,17 +23,28 @@
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/log2.h>
+#include <linux/spinlock.h>
+#include <linux/jiffies.h>
+
+#define OCORES_FLAG_POLL BIT(0)
+/*
+ * 'process_lock' exists because ocores_process() and ocores_process_timeout()
+ * can't run in parallel.
+ */
struct ocores_i2c {
void __iomem *base;
+ int iobase;
u32 reg_shift;
u32 reg_io_width;
+ unsigned long flags;
wait_queue_head_t wait;
struct i2c_adapter adap;
struct i2c_msg *msg;
int pos;
int nmsgs;
int state; /* see STATE_ */
+ spinlock_t process_lock;
struct clk *clk;
int ip_clock_khz;
int bus_clock_khz;
@@ -127,6 +136,16 @@ static inline u8 oc_getreg_32be(struct ocores_i2c *i2c, int reg)
return ioread32be(i2c->base + (reg << i2c->reg_shift));
}
+static void oc_setreg_io_8(struct ocores_i2c *i2c, int reg, u8 value)
+{
+ outb(value, i2c->iobase + reg);
+}
+
+static inline u8 oc_getreg_io_8(struct ocores_i2c *i2c, int reg)
+{
+ return inb(i2c->iobase + reg);
+}
+
static inline void oc_setreg(struct ocores_i2c *i2c, int reg, u8 value)
{
i2c->setreg(i2c, reg, value);
@@ -137,23 +156,29 @@ static inline u8 oc_getreg(struct ocores_i2c *i2c, int reg)
return i2c->getreg(i2c, reg);
}
-static void ocores_process(struct ocores_i2c *i2c)
+static void ocores_process(struct ocores_i2c *i2c, u8 stat)
{
struct i2c_msg *msg = i2c->msg;
- u8 stat = oc_getreg(i2c, OCI2C_STATUS);
+ unsigned long flags;
+
+ /*
+ * If we spin here is because we are in timeout, so we are going
+ * to be in STATE_ERROR. See ocores_process_timeout()
+ */
+ spin_lock_irqsave(&i2c->process_lock, flags);
if ((i2c->state == STATE_DONE) || (i2c->state == STATE_ERROR)) {
/* stop has been sent */
oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_IACK);
wake_up(&i2c->wait);
- return;
+ goto out;
}
/* error? */
if (stat & OCI2C_STAT_ARBLOST) {
i2c->state = STATE_ERROR;
oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP);
- return;
+ goto out;
}
if ((i2c->state == STATE_START) || (i2c->state == STATE_WRITE)) {
@@ -163,10 +188,11 @@ static void ocores_process(struct ocores_i2c *i2c)
if (stat & OCI2C_STAT_NACK) {
i2c->state = STATE_ERROR;
oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP);
- return;
+ goto out;
}
- } else
+ } else {
msg->buf[i2c->pos++] = oc_getreg(i2c, OCI2C_DATA);
+ }
/* end of msg? */
if (i2c->pos == msg->len) {
@@ -183,15 +209,15 @@ static void ocores_process(struct ocores_i2c *i2c)
i2c->state = STATE_START;
oc_setreg(i2c, OCI2C_DATA, addr);
- oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_START);
- return;
- } else
- i2c->state = (msg->flags & I2C_M_RD)
- ? STATE_READ : STATE_WRITE;
+ oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_START);
+ goto out;
+ }
+ i2c->state = (msg->flags & I2C_M_RD)
+ ? STATE_READ : STATE_WRITE;
} else {
i2c->state = STATE_DONE;
oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP);
- return;
+ goto out;
}
}
@@ -202,20 +228,148 @@ static void ocores_process(struct ocores_i2c *i2c)
oc_setreg(i2c, OCI2C_DATA, msg->buf[i2c->pos++]);
oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_WRITE);
}
+
+out:
+ spin_unlock_irqrestore(&i2c->process_lock, flags);
}
static irqreturn_t ocores_isr(int irq, void *dev_id)
{
struct ocores_i2c *i2c = dev_id;
+ u8 stat = oc_getreg(i2c, OCI2C_STATUS);
+
+ if (!(stat & OCI2C_STAT_IF))
+ return IRQ_NONE;
- ocores_process(i2c);
+ ocores_process(i2c, stat);
return IRQ_HANDLED;
}
-static int ocores_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+/**
+ * Process timeout event
+ * @i2c: ocores I2C device instance
+ */
+static void ocores_process_timeout(struct ocores_i2c *i2c)
{
- struct ocores_i2c *i2c = i2c_get_adapdata(adap);
+ unsigned long flags;
+
+ spin_lock_irqsave(&i2c->process_lock, flags);
+ i2c->state = STATE_ERROR;
+ oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP);
+ spin_unlock_irqrestore(&i2c->process_lock, flags);
+}
+
+/**
+ * Wait until something change in a given register
+ * @i2c: ocores I2C device instance
+ * @reg: register to query
+ * @mask: bitmask to apply on register value
+ * @val: expected result
+ * @timeout: timeout in jiffies
+ *
+ * Timeout is necessary to avoid to stay here forever when the chip
+ * does not answer correctly.
+ *
+ * Return: 0 on success, -ETIMEDOUT on timeout
+ */
+static int ocores_wait(struct ocores_i2c *i2c,
+ int reg, u8 mask, u8 val,
+ const unsigned long timeout)
+{
+ unsigned long j;
+
+ j = jiffies + timeout;
+ while (1) {
+ u8 status = oc_getreg(i2c, reg);
+
+ if ((status & mask) == val)
+ break;
+
+ if (time_after(jiffies, j))
+ return -ETIMEDOUT;
+ }
+ return 0;
+}
+
+/**
+ * Wait until is possible to process some data
+ * @i2c: ocores I2C device instance
+ *
+ * Used when the device is in polling mode (interrupts disabled).
+ *
+ * Return: 0 on success, -ETIMEDOUT on timeout
+ */
+static int ocores_poll_wait(struct ocores_i2c *i2c)
+{
+ u8 mask;
+ int err;
+
+ if (i2c->state == STATE_DONE || i2c->state == STATE_ERROR) {
+ /* transfer is over */
+ mask = OCI2C_STAT_BUSY;
+ } else {
+ /* on going transfer */
+ mask = OCI2C_STAT_TIP;
+ /*
+ * We wait for the data to be transferred (8bit),
+ * then we start polling on the ACK/NACK bit
+ */
+ udelay((8 * 1000) / i2c->bus_clock_khz);
+ }
+
+ /*
+ * once we are here we expect to get the expected result immediately
+ * so if after 1ms we timeout then something is broken.
+ */
+ err = ocores_wait(i2c, OCI2C_STATUS, mask, 0, msecs_to_jiffies(1));
+ if (err)
+ dev_warn(i2c->adap.dev.parent,
+ "%s: STATUS timeout, bit 0x%x did not clear in 1ms\n",
+ __func__, mask);
+ return err;
+}
+
+/**
+ * It handles an IRQ-less transfer
+ * @i2c: ocores I2C device instance
+ *
+ * Even if IRQ are disabled, the I2C OpenCore IP behavior is exactly the same
+ * (only that IRQ are not produced). This means that we can re-use entirely
+ * ocores_isr(), we just add our polling code around it.
+ *
+ * It can run in atomic context
+ */
+static void ocores_process_polling(struct ocores_i2c *i2c)
+{
+ while (1) {
+ irqreturn_t ret;
+ int err;
+
+ err = ocores_poll_wait(i2c);
+ if (err) {
+ i2c->state = STATE_ERROR;
+ break; /* timeout */
+ }
+
+ ret = ocores_isr(-1, i2c);
+ if (ret == IRQ_NONE)
+ break; /* all messages have been transferred */
+ }
+}
+
+static int ocores_xfer_core(struct ocores_i2c *i2c,
+ struct i2c_msg *msgs, int num,
+ bool polling)
+{
+ int ret;
+ u8 ctrl;
+
+ ctrl = oc_getreg(i2c, OCI2C_CONTROL);
+ if (polling)
+ oc_setreg(i2c, OCI2C_CONTROL, ctrl & ~OCI2C_CTRL_IEN);
+ else
+ oc_setreg(i2c, OCI2C_CONTROL, ctrl | OCI2C_CTRL_IEN);
i2c->msg = msgs;
i2c->pos = 0;
@@ -225,11 +379,35 @@ static int ocores_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
oc_setreg(i2c, OCI2C_DATA, i2c_8bit_addr_from_msg(i2c->msg));
oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_START);
- if (wait_event_timeout(i2c->wait, (i2c->state == STATE_ERROR) ||
- (i2c->state == STATE_DONE), HZ))
- return (i2c->state == STATE_DONE) ? num : -EIO;
- else
- return -ETIMEDOUT;
+ if (polling) {
+ ocores_process_polling(i2c);
+ } else {
+ ret = wait_event_timeout(i2c->wait,
+ (i2c->state == STATE_ERROR) ||
+ (i2c->state == STATE_DONE), HZ);
+ if (ret == 0) {
+ ocores_process_timeout(i2c);
+ return -ETIMEDOUT;
+ }
+ }
+
+ return (i2c->state == STATE_DONE) ? num : -EIO;
+}
+
+static int ocores_xfer_polling(struct i2c_adapter *adap,
+ struct i2c_msg *msgs, int num)
+{
+ return ocores_xfer_core(i2c_get_adapdata(adap), msgs, num, true);
+}
+
+static int ocores_xfer(struct i2c_adapter *adap,
+ struct i2c_msg *msgs, int num)
+{
+ struct ocores_i2c *i2c = i2c_get_adapdata(adap);
+
+ if (i2c->flags & OCORES_FLAG_POLL)
+ return ocores_xfer_polling(adap, msgs, num);
+ return ocores_xfer_core(i2c, msgs, num, false);
}
static int ocores_init(struct device *dev, struct ocores_i2c *i2c)
@@ -239,7 +417,8 @@ static int ocores_init(struct device *dev, struct ocores_i2c *i2c)
u8 ctrl = oc_getreg(i2c, OCI2C_CONTROL);
/* make sure the device is disabled */
- oc_setreg(i2c, OCI2C_CONTROL, ctrl & ~(OCI2C_CTRL_EN|OCI2C_CTRL_IEN));
+ ctrl &= ~(OCI2C_CTRL_EN | OCI2C_CTRL_IEN);
+ oc_setreg(i2c, OCI2C_CONTROL, ctrl);
prescale = (i2c->ip_clock_khz / (5 * i2c->bus_clock_khz)) - 1;
prescale = clamp(prescale, 0, 0xffff);
@@ -257,7 +436,7 @@ static int ocores_init(struct device *dev, struct ocores_i2c *i2c)
/* Init the device */
oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_IACK);
- oc_setreg(i2c, OCI2C_CONTROL, ctrl | OCI2C_CTRL_IEN | OCI2C_CTRL_EN);
+ oc_setreg(i2c, OCI2C_CONTROL, ctrl | OCI2C_CTRL_EN);
return 0;
}
@@ -294,13 +473,16 @@ static const struct of_device_id ocores_i2c_match[] = {
MODULE_DEVICE_TABLE(of, ocores_i2c_match);
#ifdef CONFIG_OF
-/* Read and write functions for the GRLIB port of the controller. Registers are
+/*
+ * Read and write functions for the GRLIB port of the controller. Registers are
* 32-bit big endian and the PRELOW and PREHIGH registers are merged into one
- * register. The subsequent registers has their offset decreased accordingly. */
+ * register. The subsequent registers have their offsets decreased accordingly.
+ */
static u8 oc_getreg_grlib(struct ocores_i2c *i2c, int reg)
{
u32 rd;
int rreg = reg;
+
if (reg != OCI2C_PRELOW)
rreg--;
rd = ioread32be(i2c->base + (rreg << i2c->reg_shift));
@@ -314,6 +496,7 @@ static void oc_setreg_grlib(struct ocores_i2c *i2c, int reg, u8 value)
{
u32 curr, wr;
int rreg = reg;
+
if (reg != OCI2C_PRELOW)
rreg--;
if (reg == OCI2C_PRELOW || reg == OCI2C_PREHIGH) {
@@ -402,7 +585,7 @@ static int ocores_i2c_of_probe(struct platform_device *pdev,
return 0;
}
#else
-#define ocores_i2c_of_probe(pdev,i2c) -ENODEV
+#define ocores_i2c_of_probe(pdev, i2c) -ENODEV
#endif
static int ocores_i2c_probe(struct platform_device *pdev)
@@ -414,25 +597,41 @@ static int ocores_i2c_probe(struct platform_device *pdev)
int ret;
int i;
- irq = platform_get_irq(pdev, 0);
- if (irq < 0)
- return irq;
-
i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL);
if (!i2c)
return -ENOMEM;
+ spin_lock_init(&i2c->process_lock);
+
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- i2c->base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(i2c->base))
- return PTR_ERR(i2c->base);
+ if (res) {
+ i2c->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(i2c->base))
+ return PTR_ERR(i2c->base);
+ } else {
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (!res)
+ return -EINVAL;
+ i2c->iobase = res->start;
+ if (!devm_request_region(&pdev->dev, res->start,
+ resource_size(res),
+ pdev->name)) {
+ dev_err(&pdev->dev, "Can't get I/O resource.\n");
+ return -EBUSY;
+ }
+ i2c->setreg = oc_setreg_io_8;
+ i2c->getreg = oc_getreg_io_8;
+ }
pdata = dev_get_platdata(&pdev->dev);
if (pdata) {
i2c->reg_shift = pdata->reg_shift;
i2c->reg_io_width = pdata->reg_io_width;
i2c->ip_clock_khz = pdata->clock_khz;
- i2c->bus_clock_khz = 100;
+ if (pdata->bus_khz)
+ i2c->bus_clock_khz = pdata->bus_khz;
+ else
+ i2c->bus_clock_khz = 100;
} else {
ret = ocores_i2c_of_probe(pdev, i2c);
if (ret)
@@ -470,18 +669,29 @@ static int ocores_i2c_probe(struct platform_device *pdev)
}
}
+ init_waitqueue_head(&i2c->wait);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq == -ENXIO) {
+ i2c->flags |= OCORES_FLAG_POLL;
+ } else {
+ if (irq < 0)
+ return irq;
+ }
+
+ if (!(i2c->flags & OCORES_FLAG_POLL)) {
+ ret = devm_request_irq(&pdev->dev, irq, ocores_isr, 0,
+ pdev->name, i2c);
+ if (ret) {
+ dev_err(&pdev->dev, "Cannot claim IRQ\n");
+ goto err_clk;
+ }
+ }
+
ret = ocores_init(&pdev->dev, i2c);
if (ret)
goto err_clk;
- init_waitqueue_head(&i2c->wait);
- ret = devm_request_irq(&pdev->dev, irq, ocores_isr, 0,
- pdev->name, i2c);
- if (ret) {
- dev_err(&pdev->dev, "Cannot claim IRQ\n");
- goto err_clk;
- }
-
/* hook up driver to tree */
platform_set_drvdata(pdev, i2c);
i2c->adap = ocores_adapter;
@@ -510,10 +720,11 @@ err_clk:
static int ocores_i2c_remove(struct platform_device *pdev)
{
struct ocores_i2c *i2c = platform_get_drvdata(pdev);
+ u8 ctrl = oc_getreg(i2c, OCI2C_CONTROL);
/* disable i2c logic */
- oc_setreg(i2c, OCI2C_CONTROL, oc_getreg(i2c, OCI2C_CONTROL)
- & ~(OCI2C_CTRL_EN|OCI2C_CTRL_IEN));
+ ctrl &= ~(OCI2C_CTRL_EN | OCI2C_CTRL_IEN);
+ oc_setreg(i2c, OCI2C_CONTROL, ctrl);
/* remove adapter & data */
i2c_del_adapter(&i2c->adap);
@@ -531,7 +742,8 @@ static int ocores_i2c_suspend(struct device *dev)
u8 ctrl = oc_getreg(i2c, OCI2C_CONTROL);
/* make sure the device is disabled */
- oc_setreg(i2c, OCI2C_CONTROL, ctrl & ~(OCI2C_CTRL_EN|OCI2C_CTRL_IEN));
+ ctrl &= ~(OCI2C_CTRL_EN | OCI2C_CTRL_IEN);
+ oc_setreg(i2c, OCI2C_CONTROL, ctrl);
if (!IS_ERR(i2c->clk))
clk_disable_unprepare(i2c->clk);
diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c
index 254e6219e538..a7578f6da979 100644
--- a/drivers/i2c/busses/i2c-rcar.c
+++ b/drivers/i2c/busses/i2c-rcar.c
@@ -2,8 +2,8 @@
/*
* Driver for the Renesas R-Car I2C unit
*
- * Copyright (C) 2014-15 Wolfram Sang <wsa@sang-engineering.com>
- * Copyright (C) 2011-2015 Renesas Electronics Corporation
+ * Copyright (C) 2014-19 Wolfram Sang <wsa@sang-engineering.com>
+ * Copyright (C) 2011-2019 Renesas Electronics Corporation
*
* Copyright (C) 2012-14 Renesas Solutions Corp.
* Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
@@ -39,8 +39,8 @@
#define ICSAR 0x1C /* slave address */
#define ICMAR 0x20 /* master address */
#define ICRXTX 0x24 /* data port */
-#define ICDMAER 0x3c /* DMA enable */
-#define ICFBSCR 0x38 /* first bit setup cycle */
+#define ICFBSCR 0x38 /* first bit setup cycle (Gen3) */
+#define ICDMAER 0x3c /* DMA enable (Gen3) */
/* ICSCR */
#define SDBS (1 << 3) /* slave data buffer select */
@@ -83,7 +83,6 @@
#define TMDMAE (1 << 0) /* DMA Master Transmitted Enable */
/* ICFBSCR */
-#define TCYC06 0x04 /* 6*Tcyc delay 1st bit between SDA and SCL */
#define TCYC17 0x0f /* 17*Tcyc delay 1st bit between SDA and SCL */
@@ -212,6 +211,10 @@ static void rcar_i2c_init(struct rcar_i2c_priv *priv)
rcar_i2c_write(priv, ICMSR, 0);
/* start clock */
rcar_i2c_write(priv, ICCCR, priv->icccr);
+
+ if (priv->devtype == I2C_RCAR_GEN3)
+ rcar_i2c_write(priv, ICFBSCR, TCYC17);
+
}
static int rcar_i2c_bus_barrier(struct rcar_i2c_priv *priv)
@@ -355,20 +358,11 @@ static void rcar_i2c_next_msg(struct rcar_i2c_priv *priv)
rcar_i2c_prepare_msg(priv);
}
-/*
- * interrupt functions
- */
static void rcar_i2c_dma_unmap(struct rcar_i2c_priv *priv)
{
struct dma_chan *chan = priv->dma_direction == DMA_FROM_DEVICE
? priv->dma_rx : priv->dma_tx;
- /* Disable DMA Master Received/Transmitted */
- rcar_i2c_write(priv, ICDMAER, 0);
-
- /* Reset default delay */
- rcar_i2c_write(priv, ICFBSCR, TCYC06);
-
dma_unmap_single(chan->device->dev, sg_dma_address(&priv->sg),
sg_dma_len(&priv->sg), priv->dma_direction);
@@ -378,6 +372,9 @@ static void rcar_i2c_dma_unmap(struct rcar_i2c_priv *priv)
priv->flags |= ID_P_NO_RXDMA;
priv->dma_direction = DMA_NONE;
+
+ /* Disable DMA Master Received/Transmitted, must be last! */
+ rcar_i2c_write(priv, ICDMAER, 0);
}
static void rcar_i2c_cleanup_dma(struct rcar_i2c_priv *priv)
@@ -464,9 +461,6 @@ static void rcar_i2c_dma(struct rcar_i2c_priv *priv)
return;
}
- /* Set delay for DMA operations */
- rcar_i2c_write(priv, ICFBSCR, TCYC17);
-
/* Enable DMA Master Received/Transmitted */
if (read)
rcar_i2c_write(priv, ICDMAER, RMDMAE);
@@ -617,6 +611,15 @@ static bool rcar_i2c_slave_irq(struct rcar_i2c_priv *priv)
return true;
}
+/*
+ * This driver has a lock-free design because there are IP cores (at least
+ * R-Car Gen2) which have an inherent race condition in their hardware design.
+ * There, we need to clear RCAR_BUS_MASK_DATA bits as soon as possible after
+ * the interrupt was generated, otherwise an unwanted repeated message gets
+ * generated. It turned out that taking a spinlock at the beginning of the ISR
+ * was already causing repeated messages. Thus, this driver was converted to
+ * the now lockless behaviour. Please keep this in mind when hacking the driver.
+ */
static irqreturn_t rcar_i2c_irq(int irq, void *ptr)
{
struct rcar_i2c_priv *priv = ptr;
@@ -1017,10 +1020,37 @@ static int rcar_i2c_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_PM_SLEEP
+static int rcar_i2c_suspend(struct device *dev)
+{
+ struct rcar_i2c_priv *priv = dev_get_drvdata(dev);
+
+ i2c_mark_adapter_suspended(&priv->adap);
+ return 0;
+}
+
+static int rcar_i2c_resume(struct device *dev)
+{
+ struct rcar_i2c_priv *priv = dev_get_drvdata(dev);
+
+ i2c_mark_adapter_resumed(&priv->adap);
+ return 0;
+}
+
+static const struct dev_pm_ops rcar_i2c_pm_ops = {
+ SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(rcar_i2c_suspend, rcar_i2c_resume)
+};
+
+#define DEV_PM_OPS (&rcar_i2c_pm_ops)
+#else
+#define DEV_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
+
static struct platform_driver rcar_i2c_driver = {
.driver = {
.name = "i2c-rcar",
.of_match_table = rcar_i2c_dt_ids,
+ .pm = DEV_PM_OPS,
},
.probe = rcar_i2c_probe,
.remove = rcar_i2c_remove,
diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c
index 2f2e28d60ef5..53bc021f4a5a 100644
--- a/drivers/i2c/busses/i2c-s3c2410.c
+++ b/drivers/i2c/busses/i2c-s3c2410.c
@@ -104,7 +104,6 @@ enum s3c24xx_i2c_state {
struct s3c24xx_i2c {
wait_queue_head_t wait;
kernel_ulong_t quirks;
- unsigned int suspended:1;
struct i2c_msg *msg;
unsigned int msg_num;
@@ -703,9 +702,6 @@ static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c,
unsigned long timeout;
int ret;
- if (i2c->suspended)
- return -EIO;
-
ret = s3c24xx_i2c_set_master(i2c);
if (ret != 0) {
dev_err(i2c->dev, "cannot get bus (error %d)\n", ret);
@@ -1246,7 +1242,7 @@ static int s3c24xx_i2c_suspend_noirq(struct device *dev)
{
struct s3c24xx_i2c *i2c = dev_get_drvdata(dev);
- i2c->suspended = 1;
+ i2c_mark_adapter_suspended(&i2c->adap);
if (!IS_ERR(i2c->sysreg))
regmap_read(i2c->sysreg, EXYNOS5_SYS_I2C_CFG, &i2c->sys_i2c_cfg);
@@ -1267,7 +1263,7 @@ static int s3c24xx_i2c_resume_noirq(struct device *dev)
return ret;
s3c24xx_i2c_init(i2c);
clk_disable(i2c->clk);
- i2c->suspended = 0;
+ i2c_mark_adapter_resumed(&i2c->adap);
return 0;
}
diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c
index a64f2ff3cb49..8777af4c695e 100644
--- a/drivers/i2c/busses/i2c-sh_mobile.c
+++ b/drivers/i2c/busses/i2c-sh_mobile.c
@@ -2,8 +2,7 @@
/*
* SuperH Mobile I2C Controller
*
- * Copyright (C) 2014 Wolfram Sang <wsa@sang-engineering.com>
- *
+ * Copyright (C) 2014-19 Wolfram Sang <wsa@sang-engineering.com>
* Copyright (C) 2008 Magnus Damm
*
* Portions of the code based on out-of-tree driver i2c-sh7343.c
@@ -303,13 +302,12 @@ static int sh_mobile_i2c_v2_init(struct sh_mobile_i2c_data *pd)
return sh_mobile_i2c_check_timing(pd);
}
-static unsigned char i2c_op(struct sh_mobile_i2c_data *pd,
- enum sh_mobile_i2c_op op, unsigned char data)
+static unsigned char i2c_op(struct sh_mobile_i2c_data *pd, enum sh_mobile_i2c_op op)
{
unsigned char ret = 0;
unsigned long flags;
- dev_dbg(pd->dev, "op %d, data in 0x%02x\n", op, data);
+ dev_dbg(pd->dev, "op %d\n", op);
spin_lock_irqsave(&pd->lock, flags);
@@ -317,12 +315,12 @@ static unsigned char i2c_op(struct sh_mobile_i2c_data *pd,
case OP_START: /* issue start and trigger DTE interrupt */
iic_wr(pd, ICCR, ICCR_ICE | ICCR_TRS | ICCR_BBSY);
break;
- case OP_TX_FIRST: /* disable DTE interrupt and write data */
+ case OP_TX_FIRST: /* disable DTE interrupt and write client address */
iic_wr(pd, ICIC, ICIC_WAITE | ICIC_ALE | ICIC_TACKE);
- iic_wr(pd, ICDR, data);
+ iic_wr(pd, ICDR, i2c_8bit_addr_from_msg(pd->msg));
break;
case OP_TX: /* write data */
- iic_wr(pd, ICDR, data);
+ iic_wr(pd, ICDR, pd->msg->buf[pd->pos]);
break;
case OP_TX_STOP: /* issue a stop (or rep_start) */
iic_wr(pd, ICCR, pd->send_stop ? ICCR_ICE | ICCR_TRS
@@ -353,34 +351,17 @@ static unsigned char i2c_op(struct sh_mobile_i2c_data *pd,
return ret;
}
-static bool sh_mobile_i2c_is_first_byte(struct sh_mobile_i2c_data *pd)
-{
- return pd->pos == -1;
-}
-
-static void sh_mobile_i2c_get_data(struct sh_mobile_i2c_data *pd,
- unsigned char *buf)
-{
- switch (pd->pos) {
- case -1:
- *buf = i2c_8bit_addr_from_msg(pd->msg);
- break;
- default:
- *buf = pd->msg->buf[pd->pos];
- }
-}
-
static int sh_mobile_i2c_isr_tx(struct sh_mobile_i2c_data *pd)
{
- unsigned char data;
-
if (pd->pos == pd->msg->len) {
- i2c_op(pd, OP_TX_STOP, 0);
+ i2c_op(pd, OP_TX_STOP);
return 1;
}
- sh_mobile_i2c_get_data(pd, &data);
- i2c_op(pd, sh_mobile_i2c_is_first_byte(pd) ? OP_TX_FIRST : OP_TX, data);
+ if (pd->pos == -1)
+ i2c_op(pd, OP_TX_FIRST);
+ else
+ i2c_op(pd, OP_TX);
pd->pos++;
return 0;
@@ -391,45 +372,32 @@ static int sh_mobile_i2c_isr_rx(struct sh_mobile_i2c_data *pd)
unsigned char data;
int real_pos;
- do {
- if (pd->pos <= -1) {
- sh_mobile_i2c_get_data(pd, &data);
-
- if (sh_mobile_i2c_is_first_byte(pd))
- i2c_op(pd, OP_TX_FIRST, data);
- else
- i2c_op(pd, OP_TX, data);
- break;
- }
-
- if (pd->pos == 0) {
- i2c_op(pd, OP_TX_TO_RX, 0);
- break;
- }
-
- real_pos = pd->pos - 2;
-
- if (pd->pos == pd->msg->len) {
- if (pd->stop_after_dma) {
- /* Simulate PIO end condition after DMA transfer */
- i2c_op(pd, OP_RX_STOP, 0);
- pd->pos++;
- break;
- }
-
- if (real_pos < 0) {
- i2c_op(pd, OP_RX_STOP, 0);
- break;
- }
- data = i2c_op(pd, OP_RX_STOP_DATA, 0);
- } else if (real_pos >= 0) {
- data = i2c_op(pd, OP_RX, 0);
+ /* switch from TX (address) to RX (data) adds two interrupts */
+ real_pos = pd->pos - 2;
+
+ if (pd->pos == -1) {
+ i2c_op(pd, OP_TX_FIRST);
+ } else if (pd->pos == 0) {
+ i2c_op(pd, OP_TX_TO_RX);
+ } else if (pd->pos == pd->msg->len) {
+ if (pd->stop_after_dma) {
+ /* Simulate PIO end condition after DMA transfer */
+ i2c_op(pd, OP_RX_STOP);
+ pd->pos++;
+ goto done;
}
- if (real_pos >= 0)
- pd->msg->buf[real_pos] = data;
- } while (0);
+ if (real_pos < 0)
+ i2c_op(pd, OP_RX_STOP);
+ else
+ data = i2c_op(pd, OP_RX_STOP_DATA);
+ } else if (real_pos >= 0) {
+ data = i2c_op(pd, OP_RX);
+ }
+ if (real_pos >= 0)
+ pd->msg->buf[real_pos] = data;
+ done:
pd->pos++;
return pd->pos == (pd->msg->len + 2);
}
@@ -698,7 +666,7 @@ static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter,
start_ch(pd, msg, do_start);
if (do_start)
- i2c_op(pd, OP_START, 0);
+ i2c_op(pd, OP_START);
/* The interrupt handler takes care of the rest... */
timeout = wait_event_timeout(pd->wait,
@@ -749,8 +717,7 @@ static const struct i2c_adapter_quirks sh_mobile_i2c_quirks = {
};
/*
- * r8a7740 chip has lasting errata on I2C I/O pad reset.
- * this is work-around for it.
+ * r8a7740 has an errata regarding I2C I/O pad reset needing this workaround.
*/
static int sh_mobile_i2c_r8a7740_workaround(struct sh_mobile_i2c_data *pd)
{
@@ -802,15 +769,15 @@ static const struct of_device_id sh_mobile_i2c_dt_ids[] = {
{ .compatible = "renesas,iic-r8a7740", .data = &r8a7740_dt_config },
{ .compatible = "renesas,iic-r8a774c0", .data = &fast_clock_dt_config },
{ .compatible = "renesas,iic-r8a7790", .data = &v2_freq_calc_dt_config },
- { .compatible = "renesas,iic-r8a7791", .data = &fast_clock_dt_config },
- { .compatible = "renesas,iic-r8a7792", .data = &fast_clock_dt_config },
- { .compatible = "renesas,iic-r8a7793", .data = &fast_clock_dt_config },
- { .compatible = "renesas,iic-r8a7794", .data = &fast_clock_dt_config },
- { .compatible = "renesas,rcar-gen2-iic", .data = &fast_clock_dt_config },
- { .compatible = "renesas,iic-r8a7795", .data = &fast_clock_dt_config },
- { .compatible = "renesas,rcar-gen3-iic", .data = &fast_clock_dt_config },
- { .compatible = "renesas,iic-r8a77990", .data = &fast_clock_dt_config },
+ { .compatible = "renesas,iic-r8a7791", .data = &v2_freq_calc_dt_config },
+ { .compatible = "renesas,iic-r8a7792", .data = &v2_freq_calc_dt_config },
+ { .compatible = "renesas,iic-r8a7793", .data = &v2_freq_calc_dt_config },
+ { .compatible = "renesas,iic-r8a7794", .data = &v2_freq_calc_dt_config },
+ { .compatible = "renesas,iic-r8a7795", .data = &v2_freq_calc_dt_config },
+ { .compatible = "renesas,iic-r8a77990", .data = &v2_freq_calc_dt_config },
{ .compatible = "renesas,iic-sh73a0", .data = &fast_clock_dt_config },
+ { .compatible = "renesas,rcar-gen2-iic", .data = &v2_freq_calc_dt_config },
+ { .compatible = "renesas,rcar-gen3-iic", .data = &v2_freq_calc_dt_config },
{ .compatible = "renesas,rmobile-iic", .data = &default_dt_config },
{},
};
diff --git a/drivers/i2c/busses/i2c-sis630.c b/drivers/i2c/busses/i2c-sis630.c
index 1e6805b5cef2..a57aa4fe51a4 100644
--- a/drivers/i2c/busses/i2c-sis630.c
+++ b/drivers/i2c/busses/i2c-sis630.c
@@ -478,7 +478,7 @@ static int sis630_setup(struct pci_dev *sis630_dev)
if (!request_region(smbus_base + SMB_STS, SIS630_SMB_IOREGION,
sis630_driver.name)) {
dev_err(&sis630_dev->dev,
- "I/O Region 0x%04hx-0x%04hx for SMBus already in use.\n",
+ "I/O Region 0x%04x-0x%04x for SMBus already in use.\n",
smbus_base + SMB_STS,
smbus_base + SMB_STS + SIS630_SMB_IOREGION - 1);
retval = -EBUSY;
@@ -528,7 +528,7 @@ static int sis630_probe(struct pci_dev *dev, const struct pci_device_id *id)
sis630_adapter.dev.parent = &dev->dev;
snprintf(sis630_adapter.name, sizeof(sis630_adapter.name),
- "SMBus SIS630 adapter at %04hx", smbus_base + SMB_STS);
+ "SMBus SIS630 adapter at %04x", smbus_base + SMB_STS);
return i2c_add_adapter(&sis630_adapter);
}
diff --git a/drivers/i2c/busses/i2c-sprd.c b/drivers/i2c/busses/i2c-sprd.c
index a94e724f51dc..961123529678 100644
--- a/drivers/i2c/busses/i2c-sprd.c
+++ b/drivers/i2c/busses/i2c-sprd.c
@@ -86,7 +86,6 @@ struct sprd_i2c {
u32 count;
int irq;
int err;
- bool is_suspended;
};
static void sprd_i2c_set_count(struct sprd_i2c *i2c_dev, u32 count)
@@ -284,9 +283,6 @@ static int sprd_i2c_master_xfer(struct i2c_adapter *i2c_adap,
struct sprd_i2c *i2c_dev = i2c_adap->algo_data;
int im, ret;
- if (i2c_dev->is_suspended)
- return -EBUSY;
-
ret = pm_runtime_get_sync(i2c_dev->dev);
if (ret < 0)
return ret;
@@ -586,40 +582,34 @@ static int sprd_i2c_remove(struct platform_device *pdev)
return 0;
}
-static int __maybe_unused sprd_i2c_suspend_noirq(struct device *pdev)
+static int __maybe_unused sprd_i2c_suspend_noirq(struct device *dev)
{
- struct sprd_i2c *i2c_dev = dev_get_drvdata(pdev);
-
- i2c_lock_bus(&i2c_dev->adap, I2C_LOCK_ROOT_ADAPTER);
- i2c_dev->is_suspended = true;
- i2c_unlock_bus(&i2c_dev->adap, I2C_LOCK_ROOT_ADAPTER);
+ struct sprd_i2c *i2c_dev = dev_get_drvdata(dev);
- return pm_runtime_force_suspend(pdev);
+ i2c_mark_adapter_suspended(&i2c_dev->adap);
+ return pm_runtime_force_suspend(dev);
}
-static int __maybe_unused sprd_i2c_resume_noirq(struct device *pdev)
+static int __maybe_unused sprd_i2c_resume_noirq(struct device *dev)
{
- struct sprd_i2c *i2c_dev = dev_get_drvdata(pdev);
-
- i2c_lock_bus(&i2c_dev->adap, I2C_LOCK_ROOT_ADAPTER);
- i2c_dev->is_suspended = false;
- i2c_unlock_bus(&i2c_dev->adap, I2C_LOCK_ROOT_ADAPTER);
+ struct sprd_i2c *i2c_dev = dev_get_drvdata(dev);
- return pm_runtime_force_resume(pdev);
+ i2c_mark_adapter_resumed(&i2c_dev->adap);
+ return pm_runtime_force_resume(dev);
}
-static int __maybe_unused sprd_i2c_runtime_suspend(struct device *pdev)
+static int __maybe_unused sprd_i2c_runtime_suspend(struct device *dev)
{
- struct sprd_i2c *i2c_dev = dev_get_drvdata(pdev);
+ struct sprd_i2c *i2c_dev = dev_get_drvdata(dev);
clk_disable_unprepare(i2c_dev->clk);
return 0;
}
-static int __maybe_unused sprd_i2c_runtime_resume(struct device *pdev)
+static int __maybe_unused sprd_i2c_runtime_resume(struct device *dev)
{
- struct sprd_i2c *i2c_dev = dev_get_drvdata(pdev);
+ struct sprd_i2c *i2c_dev = dev_get_drvdata(dev);
int ret;
ret = clk_prepare_enable(i2c_dev->clk);
diff --git a/drivers/i2c/busses/i2c-stm32f7.c b/drivers/i2c/busses/i2c-stm32f7.c
index 13e1213561d4..4284fc991cfd 100644
--- a/drivers/i2c/busses/i2c-stm32f7.c
+++ b/drivers/i2c/busses/i2c-stm32f7.c
@@ -432,7 +432,7 @@ static int stm32f7_i2c_compute_timing(struct stm32f7_i2c_dev *i2c_dev,
STM32F7_I2C_ANALOG_FILTER_DELAY_MAX : 0);
dnf_delay = setup->dnf * i2cclk;
- sdadel_min = setup->fall_time - i2c_specs[setup->speed].hddat_min -
+ sdadel_min = i2c_specs[setup->speed].hddat_min + setup->fall_time -
af_delay_min - (setup->dnf + 3) * i2cclk;
sdadel_max = i2c_specs[setup->speed].vddat_max - setup->rise_time -
diff --git a/drivers/i2c/busses/i2c-synquacer.c b/drivers/i2c/busses/i2c-synquacer.c
index 2184b7c3580e..d18b0941b71a 100644
--- a/drivers/i2c/busses/i2c-synquacer.c
+++ b/drivers/i2c/busses/i2c-synquacer.c
@@ -144,8 +144,6 @@ struct synquacer_i2c {
u32 timeout_ms;
enum i2c_state state;
struct i2c_adapter adapter;
-
- bool is_suspended;
};
static inline int is_lastmsg(struct synquacer_i2c *i2c)
@@ -316,9 +314,6 @@ static int synquacer_i2c_doxfer(struct synquacer_i2c *i2c,
unsigned long timeout;
int ret;
- if (i2c->is_suspended)
- return -EBUSY;
-
synquacer_i2c_hw_init(i2c);
bsr = readb(i2c->base + SYNQUACER_I2C_REG_BSR);
if (bsr & SYNQUACER_I2C_BSR_BB) {
diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index c77adbbea0c7..ebaa78d17d6e 100644
--- a/drivers/i2c/busses/i2c-tegra.c
+++ b/drivers/i2c/busses/i2c-tegra.c
@@ -6,26 +6,24 @@
* Author: Colin Cross <ccross@android.com>
*/
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/platform_device.h>
#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/i2c.h>
-#include <linux/io.h>
+#include <linux/init.h>
#include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <linux/of_device.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/reset.h>
+#include <linux/of_device.h>
#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
-#include <linux/iopoll.h>
-
-#include <asm/unaligned.h>
+#include <linux/reset.h>
-#define TEGRA_I2C_TIMEOUT (msecs_to_jiffies(1000))
#define BYTES_PER_FIFO_WORD 4
#define I2C_CNFG 0x000
@@ -45,8 +43,8 @@
#define I2C_FIFO_CONTROL 0x05c
#define I2C_FIFO_CONTROL_TX_FLUSH BIT(1)
#define I2C_FIFO_CONTROL_RX_FLUSH BIT(0)
-#define I2C_FIFO_CONTROL_TX_TRIG_SHIFT 5
-#define I2C_FIFO_CONTROL_RX_TRIG_SHIFT 2
+#define I2C_FIFO_CONTROL_TX_TRIG(x) (((x) - 1) << 5)
+#define I2C_FIFO_CONTROL_RX_TRIG(x) (((x) - 1) << 2)
#define I2C_FIFO_STATUS 0x060
#define I2C_FIFO_STATUS_TX_MASK 0xF0
#define I2C_FIFO_STATUS_TX_SHIFT 4
@@ -54,6 +52,7 @@
#define I2C_FIFO_STATUS_RX_SHIFT 0
#define I2C_INT_MASK 0x064
#define I2C_INT_STATUS 0x068
+#define I2C_INT_BUS_CLR_DONE BIT(11)
#define I2C_INT_PACKET_XFER_COMPLETE BIT(7)
#define I2C_INT_ALL_PACKETS_XFER_COMPLETE BIT(6)
#define I2C_INT_TX_FIFO_OVERFLOW BIT(5)
@@ -96,6 +95,15 @@
#define I2C_HEADER_MASTER_ADDR_SHIFT 12
#define I2C_HEADER_SLAVE_ADDR_SHIFT 1
+#define I2C_BUS_CLEAR_CNFG 0x084
+#define I2C_BC_SCLK_THRESHOLD 9
+#define I2C_BC_SCLK_THRESHOLD_SHIFT 16
+#define I2C_BC_STOP_COND BIT(2)
+#define I2C_BC_TERMINATE BIT(1)
+#define I2C_BC_ENABLE BIT(0)
+#define I2C_BUS_CLEAR_STATUS 0x088
+#define I2C_BC_STATUS BIT(0)
+
#define I2C_CONFIG_LOAD 0x08C
#define I2C_MSTR_CONFIG_LOAD BIT(0)
#define I2C_SLV_CONFIG_LOAD BIT(1)
@@ -118,6 +126,25 @@
#define I2C_MST_FIFO_STATUS_TX_MASK 0xff0000
#define I2C_MST_FIFO_STATUS_TX_SHIFT 16
+#define I2C_INTERFACE_TIMING_0 0x94
+#define I2C_THIGH_SHIFT 8
+#define I2C_INTERFACE_TIMING_1 0x98
+
+#define I2C_STANDARD_MODE 100000
+#define I2C_FAST_MODE 400000
+#define I2C_FAST_PLUS_MODE 1000000
+#define I2C_HS_MODE 3500000
+
+/* Packet header size in bytes */
+#define I2C_PACKET_HEADER_SIZE 12
+
+/*
+ * Upto I2C_PIO_MODE_MAX_LEN bytes, controller will use PIO mode,
+ * above this, controller will use DMA to fill FIFO.
+ * MAX PIO len is 20 bytes excluding packet header.
+ */
+#define I2C_PIO_MODE_MAX_LEN 32
+
/*
* msg_end_type: The bus control which need to be send at end of transfer.
* @MSG_END_STOP: Send stop pulse at end of transfer.
@@ -142,7 +169,10 @@ enum msg_end_type {
* @has_config_load_reg: Has the config load register to load the new
* configuration.
* @clk_divisor_hs_mode: Clock divisor in HS mode.
- * @clk_divisor_std_fast_mode: Clock divisor in standard/fast mode. It is
+ * @clk_divisor_std_mode: Clock divisor in standard mode. It is
+ * applicable if there is no fast clock source i.e. single clock
+ * source.
+ * @clk_divisor_fast_mode: Clock divisor in fast mode. It is
* applicable if there is no fast clock source i.e. single clock
* source.
* @clk_divisor_fast_plus_mode: Clock divisor in fast mode plus. It is
@@ -157,6 +187,21 @@ enum msg_end_type {
* be transferred in one go.
* @quirks: i2c adapter quirks for limiting write/read transfer size and not
* allowing 0 length transfers.
+ * @supports_bus_clear: Bus Clear support to recover from bus hang during
+ * SDA stuck low from device for some unknown reasons.
+ * @has_apb_dma: Support of APBDMA on corresponding Tegra chip.
+ * @tlow_std_mode: Low period of the clock in standard mode.
+ * @thigh_std_mode: High period of the clock in standard mode.
+ * @tlow_fast_fastplus_mode: Low period of the clock in fast/fast-plus modes.
+ * @thigh_fast_fastplus_mode: High period of the clock in fast/fast-plus modes.
+ * @setup_hold_time_std_mode: Setup and hold time for start and stop conditions
+ * in standard mode.
+ * @setup_hold_time_fast_fast_plus_mode: Setup and hold time for start and stop
+ * conditions in fast/fast-plus modes.
+ * @setup_hold_time_hs_mode: Setup and hold time for start and stop conditions
+ * in HS mode.
+ * @has_interface_timing_reg: Has interface timing register to program the tuned
+ * timing settings.
*/
struct tegra_i2c_hw_feature {
bool has_continue_xfer_support;
@@ -164,12 +209,23 @@ struct tegra_i2c_hw_feature {
bool has_single_clk_source;
bool has_config_load_reg;
int clk_divisor_hs_mode;
- int clk_divisor_std_fast_mode;
+ int clk_divisor_std_mode;
+ int clk_divisor_fast_mode;
u16 clk_divisor_fast_plus_mode;
bool has_multi_master_mode;
bool has_slcg_override_reg;
bool has_mst_fifo;
const struct i2c_adapter_quirks *quirks;
+ bool supports_bus_clear;
+ bool has_apb_dma;
+ u8 tlow_std_mode;
+ u8 thigh_std_mode;
+ u8 tlow_fast_fastplus_mode;
+ u8 thigh_fast_fastplus_mode;
+ u32 setup_hold_time_std_mode;
+ u32 setup_hold_time_fast_fast_plus_mode;
+ u32 setup_hold_time_hs_mode;
+ bool has_interface_timing_reg;
};
/**
@@ -181,6 +237,7 @@ struct tegra_i2c_hw_feature {
* @fast_clk: clock reference for fast clock of I2C controller
* @rst: reset control for the I2C controller
* @base: ioremapped registers cookie
+ * @base_phys: physical base address of the I2C controller
* @cont_id: I2C controller ID, used for packet header
* @irq: IRQ number of transfer complete interrupt
* @irq_disabled: used to track whether or not the interrupt is enabled
@@ -194,6 +251,13 @@ struct tegra_i2c_hw_feature {
* @clk_divisor_non_hs_mode: clock divider for non-high-speed modes
* @is_multimaster_mode: track if I2C controller is in multi-master mode
* @xfer_lock: lock to serialize transfer submission and processing
+ * @tx_dma_chan: DMA transmit channel
+ * @rx_dma_chan: DMA receive channel
+ * @dma_phys: handle to DMA resources
+ * @dma_buf: pointer to allocated DMA buffer
+ * @dma_buf_size: DMA buffer size
+ * @is_curr_dma_xfer: indicates active DMA transfer
+ * @dma_complete: DMA completion notifier
*/
struct tegra_i2c_dev {
struct device *dev;
@@ -203,6 +267,7 @@ struct tegra_i2c_dev {
struct clk *fast_clk;
struct reset_control *rst;
void __iomem *base;
+ phys_addr_t base_phys;
int cont_id;
int irq;
bool irq_disabled;
@@ -216,6 +281,13 @@ struct tegra_i2c_dev {
u16 clk_divisor_non_hs_mode;
bool is_multimaster_mode;
spinlock_t xfer_lock;
+ struct dma_chan *tx_dma_chan;
+ struct dma_chan *rx_dma_chan;
+ dma_addr_t dma_phys;
+ u32 *dma_buf;
+ unsigned int dma_buf_size;
+ bool is_curr_dma_xfer;
+ struct completion dma_complete;
};
static void dvc_writel(struct tegra_i2c_dev *i2c_dev, u32 val,
@@ -284,6 +356,111 @@ static void tegra_i2c_unmask_irq(struct tegra_i2c_dev *i2c_dev, u32 mask)
i2c_writel(i2c_dev, int_mask, I2C_INT_MASK);
}
+static void tegra_i2c_dma_complete(void *args)
+{
+ struct tegra_i2c_dev *i2c_dev = args;
+
+ complete(&i2c_dev->dma_complete);
+}
+
+static int tegra_i2c_dma_submit(struct tegra_i2c_dev *i2c_dev, size_t len)
+{
+ struct dma_async_tx_descriptor *dma_desc;
+ enum dma_transfer_direction dir;
+ struct dma_chan *chan;
+
+ dev_dbg(i2c_dev->dev, "starting DMA for length: %zu\n", len);
+ reinit_completion(&i2c_dev->dma_complete);
+ dir = i2c_dev->msg_read ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV;
+ chan = i2c_dev->msg_read ? i2c_dev->rx_dma_chan : i2c_dev->tx_dma_chan;
+ dma_desc = dmaengine_prep_slave_single(chan, i2c_dev->dma_phys,
+ len, dir, DMA_PREP_INTERRUPT |
+ DMA_CTRL_ACK);
+ if (!dma_desc) {
+ dev_err(i2c_dev->dev, "failed to get DMA descriptor\n");
+ return -EINVAL;
+ }
+
+ dma_desc->callback = tegra_i2c_dma_complete;
+ dma_desc->callback_param = i2c_dev;
+ dmaengine_submit(dma_desc);
+ dma_async_issue_pending(chan);
+ return 0;
+}
+
+static void tegra_i2c_release_dma(struct tegra_i2c_dev *i2c_dev)
+{
+ if (i2c_dev->dma_buf) {
+ dma_free_coherent(i2c_dev->dev, i2c_dev->dma_buf_size,
+ i2c_dev->dma_buf, i2c_dev->dma_phys);
+ i2c_dev->dma_buf = NULL;
+ }
+
+ if (i2c_dev->tx_dma_chan) {
+ dma_release_channel(i2c_dev->tx_dma_chan);
+ i2c_dev->tx_dma_chan = NULL;
+ }
+
+ if (i2c_dev->rx_dma_chan) {
+ dma_release_channel(i2c_dev->rx_dma_chan);
+ i2c_dev->rx_dma_chan = NULL;
+ }
+}
+
+static int tegra_i2c_init_dma(struct tegra_i2c_dev *i2c_dev)
+{
+ struct dma_chan *chan;
+ u32 *dma_buf;
+ dma_addr_t dma_phys;
+ int err;
+
+ if (!i2c_dev->hw->has_apb_dma)
+ return 0;
+
+ if (!IS_ENABLED(CONFIG_TEGRA20_APB_DMA)) {
+ dev_dbg(i2c_dev->dev, "Support for APB DMA not enabled!\n");
+ return 0;
+ }
+
+ chan = dma_request_slave_channel_reason(i2c_dev->dev, "rx");
+ if (IS_ERR(chan)) {
+ err = PTR_ERR(chan);
+ goto err_out;
+ }
+
+ i2c_dev->rx_dma_chan = chan;
+
+ chan = dma_request_slave_channel_reason(i2c_dev->dev, "tx");
+ if (IS_ERR(chan)) {
+ err = PTR_ERR(chan);
+ goto err_out;
+ }
+
+ i2c_dev->tx_dma_chan = chan;
+
+ dma_buf = dma_alloc_coherent(i2c_dev->dev, i2c_dev->dma_buf_size,
+ &dma_phys, GFP_KERNEL | __GFP_NOWARN);
+ if (!dma_buf) {
+ dev_err(i2c_dev->dev, "failed to allocate the DMA buffer\n");
+ err = -ENOMEM;
+ goto err_out;
+ }
+
+ i2c_dev->dma_buf = dma_buf;
+ i2c_dev->dma_phys = dma_phys;
+ return 0;
+
+err_out:
+ tegra_i2c_release_dma(i2c_dev);
+ if (err != -EPROBE_DEFER) {
+ dev_err(i2c_dev->dev, "cannot use DMA: %d\n", err);
+ dev_err(i2c_dev->dev, "falling back to PIO\n");
+ return 0;
+ }
+
+ return err;
+}
+
static int tegra_i2c_flush_fifos(struct tegra_i2c_dev *i2c_dev)
{
unsigned long timeout = jiffies + HZ;
@@ -518,11 +695,13 @@ static int tegra_i2c_wait_for_config_load(struct tegra_i2c_dev *i2c_dev)
return 0;
}
-static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
+static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev, bool clk_reinit)
{
u32 val;
int err;
- u32 clk_divisor;
+ u32 clk_divisor, clk_multiplier;
+ u32 tsu_thd = 0;
+ u8 tlow, thigh;
err = pm_runtime_get_sync(i2c_dev->dev);
if (err < 0) {
@@ -552,6 +731,41 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
I2C_CLK_DIVISOR_STD_FAST_MODE_SHIFT;
i2c_writel(i2c_dev, clk_divisor, I2C_CLK_DIVISOR);
+ if (i2c_dev->bus_clk_rate > I2C_STANDARD_MODE &&
+ i2c_dev->bus_clk_rate <= I2C_FAST_PLUS_MODE) {
+ tlow = i2c_dev->hw->tlow_fast_fastplus_mode;
+ thigh = i2c_dev->hw->thigh_fast_fastplus_mode;
+ tsu_thd = i2c_dev->hw->setup_hold_time_fast_fast_plus_mode;
+ } else {
+ tlow = i2c_dev->hw->tlow_std_mode;
+ thigh = i2c_dev->hw->thigh_std_mode;
+ tsu_thd = i2c_dev->hw->setup_hold_time_std_mode;
+ }
+
+ if (i2c_dev->hw->has_interface_timing_reg) {
+ val = (thigh << I2C_THIGH_SHIFT) | tlow;
+ i2c_writel(i2c_dev, val, I2C_INTERFACE_TIMING_0);
+ }
+
+ /*
+ * configure setup and hold times only when tsu_thd is non-zero.
+ * otherwise, preserve the chip default values
+ */
+ if (i2c_dev->hw->has_interface_timing_reg && tsu_thd)
+ i2c_writel(i2c_dev, tsu_thd, I2C_INTERFACE_TIMING_1);
+
+ if (!clk_reinit) {
+ clk_multiplier = (tlow + thigh + 2);
+ clk_multiplier *= (i2c_dev->clk_divisor_non_hs_mode + 1);
+ err = clk_set_rate(i2c_dev->div_clk,
+ i2c_dev->bus_clk_rate * clk_multiplier);
+ if (err) {
+ dev_err(i2c_dev->dev,
+ "failed changing clock rate: %d\n", err);
+ goto err;
+ }
+ }
+
if (!i2c_dev->is_dvc) {
u32 sl_cfg = i2c_readl(i2c_dev, I2C_SL_CNFG);
@@ -561,16 +775,6 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
i2c_writel(i2c_dev, 0x00, I2C_SL_ADDR2);
}
- if (i2c_dev->hw->has_mst_fifo) {
- val = I2C_MST_FIFO_CONTROL_TX_TRIG(8) |
- I2C_MST_FIFO_CONTROL_RX_TRIG(1);
- i2c_writel(i2c_dev, val, I2C_MST_FIFO_CONTROL);
- } else {
- val = 7 << I2C_FIFO_CONTROL_TX_TRIG_SHIFT |
- 0 << I2C_FIFO_CONTROL_RX_TRIG_SHIFT;
- i2c_writel(i2c_dev, val, I2C_FIFO_CONTROL);
- }
-
err = tegra_i2c_flush_fifos(i2c_dev);
if (err)
goto err;
@@ -643,25 +847,44 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id)
goto err;
}
- if (i2c_dev->msg_read && (status & I2C_INT_RX_FIFO_DATA_REQ)) {
- if (i2c_dev->msg_buf_remaining)
- tegra_i2c_empty_rx_fifo(i2c_dev);
- else
- BUG();
- }
+ /*
+ * I2C transfer is terminated during the bus clear so skip
+ * processing the other interrupts.
+ */
+ if (i2c_dev->hw->supports_bus_clear && (status & I2C_INT_BUS_CLR_DONE))
+ goto err;
- if (!i2c_dev->msg_read && (status & I2C_INT_TX_FIFO_DATA_REQ)) {
- if (i2c_dev->msg_buf_remaining)
- tegra_i2c_fill_tx_fifo(i2c_dev);
- else
- tegra_i2c_mask_irq(i2c_dev, I2C_INT_TX_FIFO_DATA_REQ);
+ if (!i2c_dev->is_curr_dma_xfer) {
+ if (i2c_dev->msg_read && (status & I2C_INT_RX_FIFO_DATA_REQ)) {
+ if (i2c_dev->msg_buf_remaining)
+ tegra_i2c_empty_rx_fifo(i2c_dev);
+ else
+ BUG();
+ }
+
+ if (!i2c_dev->msg_read && (status & I2C_INT_TX_FIFO_DATA_REQ)) {
+ if (i2c_dev->msg_buf_remaining)
+ tegra_i2c_fill_tx_fifo(i2c_dev);
+ else
+ tegra_i2c_mask_irq(i2c_dev,
+ I2C_INT_TX_FIFO_DATA_REQ);
+ }
}
i2c_writel(i2c_dev, status, I2C_INT_STATUS);
if (i2c_dev->is_dvc)
dvc_writel(i2c_dev, DVC_STATUS_I2C_DONE_INTR, DVC_STATUS);
+ /*
+ * During message read XFER_COMPLETE interrupt is triggered prior to
+ * DMA completion and during message write XFER_COMPLETE interrupt is
+ * triggered after DMA completion.
+ * PACKETS_XFER_COMPLETE indicates completion of all bytes of transfer.
+ * so forcing msg_buf_remaining to 0 in DMA mode.
+ */
if (status & I2C_INT_PACKET_XFER_COMPLETE) {
+ if (i2c_dev->is_curr_dma_xfer)
+ i2c_dev->msg_buf_remaining = 0;
BUG_ON(i2c_dev->msg_buf_remaining);
complete(&i2c_dev->msg_complete);
}
@@ -671,16 +894,135 @@ err:
tegra_i2c_mask_irq(i2c_dev, I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST |
I2C_INT_PACKET_XFER_COMPLETE | I2C_INT_TX_FIFO_DATA_REQ |
I2C_INT_RX_FIFO_DATA_REQ);
+ if (i2c_dev->hw->supports_bus_clear)
+ tegra_i2c_mask_irq(i2c_dev, I2C_INT_BUS_CLR_DONE);
i2c_writel(i2c_dev, status, I2C_INT_STATUS);
if (i2c_dev->is_dvc)
dvc_writel(i2c_dev, DVC_STATUS_I2C_DONE_INTR, DVC_STATUS);
+ if (i2c_dev->is_curr_dma_xfer) {
+ if (i2c_dev->msg_read)
+ dmaengine_terminate_async(i2c_dev->rx_dma_chan);
+ else
+ dmaengine_terminate_async(i2c_dev->tx_dma_chan);
+
+ complete(&i2c_dev->dma_complete);
+ }
+
complete(&i2c_dev->msg_complete);
done:
spin_unlock(&i2c_dev->xfer_lock);
return IRQ_HANDLED;
}
+static void tegra_i2c_config_fifo_trig(struct tegra_i2c_dev *i2c_dev,
+ size_t len)
+{
+ u32 val, reg;
+ u8 dma_burst;
+ struct dma_slave_config slv_config = {0};
+ struct dma_chan *chan;
+ int ret;
+ unsigned long reg_offset;
+
+ if (i2c_dev->hw->has_mst_fifo)
+ reg = I2C_MST_FIFO_CONTROL;
+ else
+ reg = I2C_FIFO_CONTROL;
+
+ if (i2c_dev->is_curr_dma_xfer) {
+ if (len & 0xF)
+ dma_burst = 1;
+ else if (len & 0x10)
+ dma_burst = 4;
+ else
+ dma_burst = 8;
+
+ if (i2c_dev->msg_read) {
+ chan = i2c_dev->rx_dma_chan;
+ reg_offset = tegra_i2c_reg_addr(i2c_dev, I2C_RX_FIFO);
+ slv_config.src_addr = i2c_dev->base_phys + reg_offset;
+ slv_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ slv_config.src_maxburst = dma_burst;
+
+ if (i2c_dev->hw->has_mst_fifo)
+ val = I2C_MST_FIFO_CONTROL_RX_TRIG(dma_burst);
+ else
+ val = I2C_FIFO_CONTROL_RX_TRIG(dma_burst);
+ } else {
+ chan = i2c_dev->tx_dma_chan;
+ reg_offset = tegra_i2c_reg_addr(i2c_dev, I2C_TX_FIFO);
+ slv_config.dst_addr = i2c_dev->base_phys + reg_offset;
+ slv_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ slv_config.dst_maxburst = dma_burst;
+
+ if (i2c_dev->hw->has_mst_fifo)
+ val = I2C_MST_FIFO_CONTROL_TX_TRIG(dma_burst);
+ else
+ val = I2C_FIFO_CONTROL_TX_TRIG(dma_burst);
+ }
+
+ slv_config.device_fc = true;
+ ret = dmaengine_slave_config(chan, &slv_config);
+ if (ret < 0) {
+ dev_err(i2c_dev->dev, "DMA slave config failed: %d\n",
+ ret);
+ dev_err(i2c_dev->dev, "falling back to PIO\n");
+ tegra_i2c_release_dma(i2c_dev);
+ i2c_dev->is_curr_dma_xfer = false;
+ } else {
+ goto out;
+ }
+ }
+
+ if (i2c_dev->hw->has_mst_fifo)
+ val = I2C_MST_FIFO_CONTROL_TX_TRIG(8) |
+ I2C_MST_FIFO_CONTROL_RX_TRIG(1);
+ else
+ val = I2C_FIFO_CONTROL_TX_TRIG(8) |
+ I2C_FIFO_CONTROL_RX_TRIG(1);
+out:
+ i2c_writel(i2c_dev, val, reg);
+}
+
+static int tegra_i2c_issue_bus_clear(struct i2c_adapter *adap)
+{
+ struct tegra_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
+ int err;
+ unsigned long time_left;
+ u32 reg;
+
+ reinit_completion(&i2c_dev->msg_complete);
+ reg = (I2C_BC_SCLK_THRESHOLD << I2C_BC_SCLK_THRESHOLD_SHIFT) |
+ I2C_BC_STOP_COND | I2C_BC_TERMINATE;
+ i2c_writel(i2c_dev, reg, I2C_BUS_CLEAR_CNFG);
+ if (i2c_dev->hw->has_config_load_reg) {
+ err = tegra_i2c_wait_for_config_load(i2c_dev);
+ if (err)
+ return err;
+ }
+
+ reg |= I2C_BC_ENABLE;
+ i2c_writel(i2c_dev, reg, I2C_BUS_CLEAR_CNFG);
+ tegra_i2c_unmask_irq(i2c_dev, I2C_INT_BUS_CLR_DONE);
+
+ time_left = wait_for_completion_timeout(&i2c_dev->msg_complete,
+ msecs_to_jiffies(50));
+ if (time_left == 0) {
+ dev_err(i2c_dev->dev, "timed out for bus clear\n");
+ return -ETIMEDOUT;
+ }
+
+ reg = i2c_readl(i2c_dev, I2C_BUS_CLEAR_STATUS);
+ if (!(reg & I2C_BC_STATUS)) {
+ dev_err(i2c_dev->dev,
+ "un-recovered arbitration lost\n");
+ return -EIO;
+ }
+
+ return -EAGAIN;
+}
+
static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
struct i2c_msg *msg, enum msg_end_type end_state)
{
@@ -688,6 +1030,11 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
u32 int_mask;
unsigned long time_left;
unsigned long flags;
+ size_t xfer_size;
+ u32 *buffer = NULL;
+ int err = 0;
+ bool dma;
+ u16 xfer_time = 100;
tegra_i2c_flush_fifos(i2c_dev);
@@ -697,19 +1044,63 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
i2c_dev->msg_read = (msg->flags & I2C_M_RD);
reinit_completion(&i2c_dev->msg_complete);
+ if (i2c_dev->msg_read)
+ xfer_size = msg->len;
+ else
+ xfer_size = msg->len + I2C_PACKET_HEADER_SIZE;
+
+ xfer_size = ALIGN(xfer_size, BYTES_PER_FIFO_WORD);
+ i2c_dev->is_curr_dma_xfer = (xfer_size > I2C_PIO_MODE_MAX_LEN) &&
+ i2c_dev->dma_buf;
+ tegra_i2c_config_fifo_trig(i2c_dev, xfer_size);
+ dma = i2c_dev->is_curr_dma_xfer;
+ /*
+ * Transfer time in mSec = Total bits / transfer rate
+ * Total bits = 9 bits per byte (including ACK bit) + Start & stop bits
+ */
+ xfer_time += DIV_ROUND_CLOSEST(((xfer_size * 9) + 2) * MSEC_PER_SEC,
+ i2c_dev->bus_clk_rate);
spin_lock_irqsave(&i2c_dev->xfer_lock, flags);
int_mask = I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST;
tegra_i2c_unmask_irq(i2c_dev, int_mask);
+ if (dma) {
+ if (i2c_dev->msg_read) {
+ dma_sync_single_for_device(i2c_dev->dev,
+ i2c_dev->dma_phys,
+ xfer_size,
+ DMA_FROM_DEVICE);
+ err = tegra_i2c_dma_submit(i2c_dev, xfer_size);
+ if (err < 0) {
+ dev_err(i2c_dev->dev,
+ "starting RX DMA failed, err %d\n",
+ err);
+ goto unlock;
+ }
+
+ } else {
+ dma_sync_single_for_cpu(i2c_dev->dev,
+ i2c_dev->dma_phys,
+ xfer_size,
+ DMA_TO_DEVICE);
+ buffer = i2c_dev->dma_buf;
+ }
+ }
packet_header = (0 << PACKET_HEADER0_HEADER_SIZE_SHIFT) |
PACKET_HEADER0_PROTOCOL_I2C |
(i2c_dev->cont_id << PACKET_HEADER0_CONT_ID_SHIFT) |
(1 << PACKET_HEADER0_PACKET_ID_SHIFT);
- i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO);
+ if (dma && !i2c_dev->msg_read)
+ *buffer++ = packet_header;
+ else
+ i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO);
packet_header = msg->len - 1;
- i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO);
+ if (dma && !i2c_dev->msg_read)
+ *buffer++ = packet_header;
+ else
+ i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO);
packet_header = I2C_HEADER_IE_ENABLE;
if (end_state == MSG_END_CONTINUE)
@@ -726,31 +1117,85 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
packet_header |= I2C_HEADER_CONT_ON_NAK;
if (msg->flags & I2C_M_RD)
packet_header |= I2C_HEADER_READ;
- i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO);
-
- if (!(msg->flags & I2C_M_RD))
- tegra_i2c_fill_tx_fifo(i2c_dev);
+ if (dma && !i2c_dev->msg_read)
+ *buffer++ = packet_header;
+ else
+ i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO);
+
+ if (!i2c_dev->msg_read) {
+ if (dma) {
+ memcpy(buffer, msg->buf, msg->len);
+ dma_sync_single_for_device(i2c_dev->dev,
+ i2c_dev->dma_phys,
+ xfer_size,
+ DMA_TO_DEVICE);
+ err = tegra_i2c_dma_submit(i2c_dev, xfer_size);
+ if (err < 0) {
+ dev_err(i2c_dev->dev,
+ "starting TX DMA failed, err %d\n",
+ err);
+ goto unlock;
+ }
+ } else {
+ tegra_i2c_fill_tx_fifo(i2c_dev);
+ }
+ }
if (i2c_dev->hw->has_per_pkt_xfer_complete_irq)
int_mask |= I2C_INT_PACKET_XFER_COMPLETE;
- if (msg->flags & I2C_M_RD)
- int_mask |= I2C_INT_RX_FIFO_DATA_REQ;
- else if (i2c_dev->msg_buf_remaining)
- int_mask |= I2C_INT_TX_FIFO_DATA_REQ;
+ if (!dma) {
+ if (msg->flags & I2C_M_RD)
+ int_mask |= I2C_INT_RX_FIFO_DATA_REQ;
+ else if (i2c_dev->msg_buf_remaining)
+ int_mask |= I2C_INT_TX_FIFO_DATA_REQ;
+ }
tegra_i2c_unmask_irq(i2c_dev, int_mask);
- spin_unlock_irqrestore(&i2c_dev->xfer_lock, flags);
dev_dbg(i2c_dev->dev, "unmasked irq: %02x\n",
i2c_readl(i2c_dev, I2C_INT_MASK));
+unlock:
+ spin_unlock_irqrestore(&i2c_dev->xfer_lock, flags);
+
+ if (dma) {
+ if (err)
+ return err;
+
+ time_left = wait_for_completion_timeout(
+ &i2c_dev->dma_complete,
+ msecs_to_jiffies(xfer_time));
+ if (time_left == 0) {
+ dev_err(i2c_dev->dev, "DMA transfer timeout\n");
+ dmaengine_terminate_sync(i2c_dev->msg_read ?
+ i2c_dev->rx_dma_chan :
+ i2c_dev->tx_dma_chan);
+ tegra_i2c_init(i2c_dev, true);
+ return -ETIMEDOUT;
+ }
+
+ if (i2c_dev->msg_read && i2c_dev->msg_err == I2C_ERR_NONE) {
+ dma_sync_single_for_cpu(i2c_dev->dev,
+ i2c_dev->dma_phys,
+ xfer_size,
+ DMA_FROM_DEVICE);
+ memcpy(i2c_dev->msg_buf, i2c_dev->dma_buf,
+ msg->len);
+ }
+
+ if (i2c_dev->msg_err != I2C_ERR_NONE)
+ dmaengine_synchronize(i2c_dev->msg_read ?
+ i2c_dev->rx_dma_chan :
+ i2c_dev->tx_dma_chan);
+ }
+
time_left = wait_for_completion_timeout(&i2c_dev->msg_complete,
- TEGRA_I2C_TIMEOUT);
+ msecs_to_jiffies(xfer_time));
tegra_i2c_mask_irq(i2c_dev, int_mask);
if (time_left == 0) {
dev_err(i2c_dev->dev, "i2c transfer timed out\n");
- tegra_i2c_init(i2c_dev);
+ tegra_i2c_init(i2c_dev, true);
return -ETIMEDOUT;
}
@@ -758,10 +1203,18 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
time_left, completion_done(&i2c_dev->msg_complete),
i2c_dev->msg_err);
+ i2c_dev->is_curr_dma_xfer = false;
if (likely(i2c_dev->msg_err == I2C_ERR_NONE))
return 0;
- tegra_i2c_init(i2c_dev);
+ tegra_i2c_init(i2c_dev, true);
+ /* start recovery upon arbitration loss in single master mode */
+ if (i2c_dev->msg_err == I2C_ERR_ARBITRATION_LOST) {
+ if (!i2c_dev->is_multimaster_mode)
+ return i2c_recover_bus(&i2c_dev->adapter);
+ return -EAGAIN;
+ }
+
if (i2c_dev->msg_err == I2C_ERR_NO_ACK) {
if (msg->flags & I2C_M_IGNORE_NAK)
return 0;
@@ -836,12 +1289,17 @@ static const struct i2c_algorithm tegra_i2c_algo = {
/* payload size is only 12 bit */
static const struct i2c_adapter_quirks tegra_i2c_quirks = {
.flags = I2C_AQ_NO_ZERO_LEN,
- .max_read_len = 4096,
- .max_write_len = 4096,
+ .max_read_len = SZ_4K,
+ .max_write_len = SZ_4K - I2C_PACKET_HEADER_SIZE,
};
static const struct i2c_adapter_quirks tegra194_i2c_quirks = {
.flags = I2C_AQ_NO_ZERO_LEN,
+ .max_write_len = SZ_64K - I2C_PACKET_HEADER_SIZE,
+};
+
+static struct i2c_bus_recovery_info tegra_i2c_recovery_info = {
+ .recover_bus = tegra_i2c_issue_bus_clear,
};
static const struct tegra_i2c_hw_feature tegra20_i2c_hw = {
@@ -849,13 +1307,24 @@ static const struct tegra_i2c_hw_feature tegra20_i2c_hw = {
.has_per_pkt_xfer_complete_irq = false,
.has_single_clk_source = false,
.clk_divisor_hs_mode = 3,
- .clk_divisor_std_fast_mode = 0,
+ .clk_divisor_std_mode = 0,
+ .clk_divisor_fast_mode = 0,
.clk_divisor_fast_plus_mode = 0,
.has_config_load_reg = false,
.has_multi_master_mode = false,
.has_slcg_override_reg = false,
.has_mst_fifo = false,
.quirks = &tegra_i2c_quirks,
+ .supports_bus_clear = false,
+ .has_apb_dma = true,
+ .tlow_std_mode = 0x4,
+ .thigh_std_mode = 0x2,
+ .tlow_fast_fastplus_mode = 0x4,
+ .thigh_fast_fastplus_mode = 0x2,
+ .setup_hold_time_std_mode = 0x0,
+ .setup_hold_time_fast_fast_plus_mode = 0x0,
+ .setup_hold_time_hs_mode = 0x0,
+ .has_interface_timing_reg = false,
};
static const struct tegra_i2c_hw_feature tegra30_i2c_hw = {
@@ -863,13 +1332,24 @@ static const struct tegra_i2c_hw_feature tegra30_i2c_hw = {
.has_per_pkt_xfer_complete_irq = false,
.has_single_clk_source = false,
.clk_divisor_hs_mode = 3,
- .clk_divisor_std_fast_mode = 0,
+ .clk_divisor_std_mode = 0,
+ .clk_divisor_fast_mode = 0,
.clk_divisor_fast_plus_mode = 0,
.has_config_load_reg = false,
.has_multi_master_mode = false,
.has_slcg_override_reg = false,
.has_mst_fifo = false,
.quirks = &tegra_i2c_quirks,
+ .supports_bus_clear = false,
+ .has_apb_dma = true,
+ .tlow_std_mode = 0x4,
+ .thigh_std_mode = 0x2,
+ .tlow_fast_fastplus_mode = 0x4,
+ .thigh_fast_fastplus_mode = 0x2,
+ .setup_hold_time_std_mode = 0x0,
+ .setup_hold_time_fast_fast_plus_mode = 0x0,
+ .setup_hold_time_hs_mode = 0x0,
+ .has_interface_timing_reg = false,
};
static const struct tegra_i2c_hw_feature tegra114_i2c_hw = {
@@ -877,13 +1357,24 @@ static const struct tegra_i2c_hw_feature tegra114_i2c_hw = {
.has_per_pkt_xfer_complete_irq = true,
.has_single_clk_source = true,
.clk_divisor_hs_mode = 1,
- .clk_divisor_std_fast_mode = 0x19,
+ .clk_divisor_std_mode = 0x19,
+ .clk_divisor_fast_mode = 0x19,
.clk_divisor_fast_plus_mode = 0x10,
.has_config_load_reg = false,
.has_multi_master_mode = false,
.has_slcg_override_reg = false,
.has_mst_fifo = false,
.quirks = &tegra_i2c_quirks,
+ .supports_bus_clear = true,
+ .has_apb_dma = true,
+ .tlow_std_mode = 0x4,
+ .thigh_std_mode = 0x2,
+ .tlow_fast_fastplus_mode = 0x4,
+ .thigh_fast_fastplus_mode = 0x2,
+ .setup_hold_time_std_mode = 0x0,
+ .setup_hold_time_fast_fast_plus_mode = 0x0,
+ .setup_hold_time_hs_mode = 0x0,
+ .has_interface_timing_reg = false,
};
static const struct tegra_i2c_hw_feature tegra124_i2c_hw = {
@@ -891,13 +1382,24 @@ static const struct tegra_i2c_hw_feature tegra124_i2c_hw = {
.has_per_pkt_xfer_complete_irq = true,
.has_single_clk_source = true,
.clk_divisor_hs_mode = 1,
- .clk_divisor_std_fast_mode = 0x19,
+ .clk_divisor_std_mode = 0x19,
+ .clk_divisor_fast_mode = 0x19,
.clk_divisor_fast_plus_mode = 0x10,
.has_config_load_reg = true,
.has_multi_master_mode = false,
.has_slcg_override_reg = true,
.has_mst_fifo = false,
.quirks = &tegra_i2c_quirks,
+ .supports_bus_clear = true,
+ .has_apb_dma = true,
+ .tlow_std_mode = 0x4,
+ .thigh_std_mode = 0x2,
+ .tlow_fast_fastplus_mode = 0x4,
+ .thigh_fast_fastplus_mode = 0x2,
+ .setup_hold_time_std_mode = 0x0,
+ .setup_hold_time_fast_fast_plus_mode = 0x0,
+ .setup_hold_time_hs_mode = 0x0,
+ .has_interface_timing_reg = true,
};
static const struct tegra_i2c_hw_feature tegra210_i2c_hw = {
@@ -905,32 +1407,80 @@ static const struct tegra_i2c_hw_feature tegra210_i2c_hw = {
.has_per_pkt_xfer_complete_irq = true,
.has_single_clk_source = true,
.clk_divisor_hs_mode = 1,
- .clk_divisor_std_fast_mode = 0x19,
+ .clk_divisor_std_mode = 0x19,
+ .clk_divisor_fast_mode = 0x19,
.clk_divisor_fast_plus_mode = 0x10,
.has_config_load_reg = true,
- .has_multi_master_mode = true,
+ .has_multi_master_mode = false,
.has_slcg_override_reg = true,
.has_mst_fifo = false,
.quirks = &tegra_i2c_quirks,
+ .supports_bus_clear = true,
+ .has_apb_dma = true,
+ .tlow_std_mode = 0x4,
+ .thigh_std_mode = 0x2,
+ .tlow_fast_fastplus_mode = 0x4,
+ .thigh_fast_fastplus_mode = 0x2,
+ .setup_hold_time_std_mode = 0,
+ .setup_hold_time_fast_fast_plus_mode = 0,
+ .setup_hold_time_hs_mode = 0,
+ .has_interface_timing_reg = true,
};
-static const struct tegra_i2c_hw_feature tegra194_i2c_hw = {
+static const struct tegra_i2c_hw_feature tegra186_i2c_hw = {
.has_continue_xfer_support = true,
.has_per_pkt_xfer_complete_irq = true,
.has_single_clk_source = true,
.clk_divisor_hs_mode = 1,
- .clk_divisor_std_fast_mode = 0x19,
+ .clk_divisor_std_mode = 0x16,
+ .clk_divisor_fast_mode = 0x19,
.clk_divisor_fast_plus_mode = 0x10,
.has_config_load_reg = true,
+ .has_multi_master_mode = false,
+ .has_slcg_override_reg = true,
+ .has_mst_fifo = false,
+ .quirks = &tegra_i2c_quirks,
+ .supports_bus_clear = true,
+ .has_apb_dma = false,
+ .tlow_std_mode = 0x4,
+ .thigh_std_mode = 0x3,
+ .tlow_fast_fastplus_mode = 0x4,
+ .thigh_fast_fastplus_mode = 0x2,
+ .setup_hold_time_std_mode = 0,
+ .setup_hold_time_fast_fast_plus_mode = 0,
+ .setup_hold_time_hs_mode = 0,
+ .has_interface_timing_reg = true,
+};
+
+static const struct tegra_i2c_hw_feature tegra194_i2c_hw = {
+ .has_continue_xfer_support = true,
+ .has_per_pkt_xfer_complete_irq = true,
+ .has_single_clk_source = true,
+ .clk_divisor_hs_mode = 1,
+ .clk_divisor_std_mode = 0x4f,
+ .clk_divisor_fast_mode = 0x3c,
+ .clk_divisor_fast_plus_mode = 0x16,
+ .has_config_load_reg = true,
.has_multi_master_mode = true,
.has_slcg_override_reg = true,
.has_mst_fifo = true,
.quirks = &tegra194_i2c_quirks,
+ .supports_bus_clear = true,
+ .has_apb_dma = false,
+ .tlow_std_mode = 0x8,
+ .thigh_std_mode = 0x7,
+ .tlow_fast_fastplus_mode = 0x2,
+ .thigh_fast_fastplus_mode = 0x2,
+ .setup_hold_time_std_mode = 0x08080808,
+ .setup_hold_time_fast_fast_plus_mode = 0x02020202,
+ .setup_hold_time_hs_mode = 0x090909,
+ .has_interface_timing_reg = true,
};
/* Match table for of_platform binding */
static const struct of_device_id tegra_i2c_of_match[] = {
{ .compatible = "nvidia,tegra194-i2c", .data = &tegra194_i2c_hw, },
+ { .compatible = "nvidia,tegra186-i2c", .data = &tegra186_i2c_hw, },
{ .compatible = "nvidia,tegra210-i2c", .data = &tegra210_i2c_hw, },
{ .compatible = "nvidia,tegra124-i2c", .data = &tegra124_i2c_hw, },
{ .compatible = "nvidia,tegra114-i2c", .data = &tegra114_i2c_hw, },
@@ -948,11 +1498,12 @@ static int tegra_i2c_probe(struct platform_device *pdev)
struct clk *div_clk;
struct clk *fast_clk;
void __iomem *base;
+ phys_addr_t base_phys;
int irq;
int ret = 0;
- int clk_multiplier = I2C_CLK_MULTIPLIER_STD_FAST_MODE;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base_phys = res->start;
base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(base))
return PTR_ERR(base);
@@ -975,8 +1526,11 @@ static int tegra_i2c_probe(struct platform_device *pdev)
return -ENOMEM;
i2c_dev->base = base;
+ i2c_dev->base_phys = base_phys;
i2c_dev->div_clk = div_clk;
i2c_dev->adapter.algo = &tegra_i2c_algo;
+ i2c_dev->adapter.retries = 1;
+ i2c_dev->adapter.timeout = 6 * HZ;
i2c_dev->irq = irq;
i2c_dev->cont_id = pdev->id;
i2c_dev->dev = &pdev->dev;
@@ -993,7 +1547,10 @@ static int tegra_i2c_probe(struct platform_device *pdev)
i2c_dev->is_dvc = of_device_is_compatible(pdev->dev.of_node,
"nvidia,tegra20-i2c-dvc");
i2c_dev->adapter.quirks = i2c_dev->hw->quirks;
+ i2c_dev->dma_buf_size = i2c_dev->adapter.quirks->max_write_len +
+ I2C_PACKET_HEADER_SIZE;
init_completion(&i2c_dev->msg_complete);
+ init_completion(&i2c_dev->dma_complete);
spin_lock_init(&i2c_dev->xfer_lock);
if (!i2c_dev->hw->has_single_clk_source) {
@@ -1015,20 +1572,17 @@ static int tegra_i2c_probe(struct platform_device *pdev)
}
}
- i2c_dev->clk_divisor_non_hs_mode =
- i2c_dev->hw->clk_divisor_std_fast_mode;
- if (i2c_dev->hw->clk_divisor_fast_plus_mode &&
- (i2c_dev->bus_clk_rate == 1000000))
+ if (i2c_dev->bus_clk_rate > I2C_FAST_MODE &&
+ i2c_dev->bus_clk_rate <= I2C_FAST_PLUS_MODE)
i2c_dev->clk_divisor_non_hs_mode =
- i2c_dev->hw->clk_divisor_fast_plus_mode;
-
- clk_multiplier *= (i2c_dev->clk_divisor_non_hs_mode + 1);
- ret = clk_set_rate(i2c_dev->div_clk,
- i2c_dev->bus_clk_rate * clk_multiplier);
- if (ret) {
- dev_err(i2c_dev->dev, "Clock rate change failed %d\n", ret);
- goto unprepare_fast_clk;
- }
+ i2c_dev->hw->clk_divisor_fast_plus_mode;
+ else if (i2c_dev->bus_clk_rate > I2C_STANDARD_MODE &&
+ i2c_dev->bus_clk_rate <= I2C_FAST_MODE)
+ i2c_dev->clk_divisor_non_hs_mode =
+ i2c_dev->hw->clk_divisor_fast_mode;
+ else
+ i2c_dev->clk_divisor_non_hs_mode =
+ i2c_dev->hw->clk_divisor_std_mode;
ret = clk_prepare(i2c_dev->div_clk);
if (ret < 0) {
@@ -1054,17 +1608,24 @@ static int tegra_i2c_probe(struct platform_device *pdev)
}
}
- ret = tegra_i2c_init(i2c_dev);
+ if (i2c_dev->hw->supports_bus_clear)
+ i2c_dev->adapter.bus_recovery_info = &tegra_i2c_recovery_info;
+
+ ret = tegra_i2c_init_dma(i2c_dev);
+ if (ret < 0)
+ goto disable_div_clk;
+
+ ret = tegra_i2c_init(i2c_dev, false);
if (ret) {
dev_err(&pdev->dev, "Failed to initialize i2c controller\n");
- goto disable_div_clk;
+ goto release_dma;
}
ret = devm_request_irq(&pdev->dev, i2c_dev->irq,
tegra_i2c_isr, 0, dev_name(&pdev->dev), i2c_dev);
if (ret) {
dev_err(&pdev->dev, "Failed to request irq %i\n", i2c_dev->irq);
- goto disable_div_clk;
+ goto release_dma;
}
i2c_set_adapdata(&i2c_dev->adapter, i2c_dev);
@@ -1078,10 +1639,13 @@ static int tegra_i2c_probe(struct platform_device *pdev)
ret = i2c_add_numbered_adapter(&i2c_dev->adapter);
if (ret)
- goto disable_div_clk;
+ goto release_dma;
return 0;
+release_dma:
+ tegra_i2c_release_dma(i2c_dev);
+
disable_div_clk:
if (i2c_dev->is_multimaster_mode)
clk_disable(i2c_dev->div_clk);
@@ -1118,6 +1682,7 @@ static int tegra_i2c_remove(struct platform_device *pdev)
if (!i2c_dev->hw->has_single_clk_source)
clk_unprepare(i2c_dev->fast_clk);
+ tegra_i2c_release_dma(i2c_dev);
return 0;
}
@@ -1141,18 +1706,7 @@ static struct platform_driver tegra_i2c_driver = {
},
};
-static int __init tegra_i2c_init_driver(void)
-{
- return platform_driver_register(&tegra_i2c_driver);
-}
-
-static void __exit tegra_i2c_exit_driver(void)
-{
- platform_driver_unregister(&tegra_i2c_driver);
-}
-
-subsys_initcall(tegra_i2c_init_driver);
-module_exit(tegra_i2c_exit_driver);
+module_platform_driver(tegra_i2c_driver);
MODULE_DESCRIPTION("nVidia Tegra2 I2C Bus Controller driver");
MODULE_AUTHOR("Colin Cross");
diff --git a/drivers/i2c/busses/i2c-zx2967.c b/drivers/i2c/busses/i2c-zx2967.c
index b8f9e020d80e..7b98d97da3c6 100644
--- a/drivers/i2c/busses/i2c-zx2967.c
+++ b/drivers/i2c/busses/i2c-zx2967.c
@@ -66,7 +66,6 @@ struct zx2967_i2c {
int msg_rd;
u8 *cur_trans;
u8 access_cnt;
- bool is_suspended;
int error;
};
@@ -313,9 +312,6 @@ static int zx2967_i2c_xfer(struct i2c_adapter *adap,
int ret;
int i;
- if (i2c->is_suspended)
- return -EBUSY;
-
zx2967_set_addr(i2c, msgs->addr);
for (i = 0; i < num; i++) {
@@ -470,7 +466,7 @@ static int __maybe_unused zx2967_i2c_suspend(struct device *dev)
{
struct zx2967_i2c *i2c = dev_get_drvdata(dev);
- i2c->is_suspended = true;
+ i2c_mark_adapter_suspended(&i2c->adap);
clk_disable_unprepare(i2c->clk);
return 0;
@@ -480,8 +476,8 @@ static int __maybe_unused zx2967_i2c_resume(struct device *dev)
{
struct zx2967_i2c *i2c = dev_get_drvdata(dev);
- i2c->is_suspended = false;
clk_prepare_enable(i2c->clk);
+ i2c_mark_adapter_resumed(&i2c->adap);
return 0;
}
diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c
index 28460f6a60cc..38af18645133 100644
--- a/drivers/i2c/i2c-core-base.c
+++ b/drivers/i2c/i2c-core-base.c
@@ -430,7 +430,7 @@ static int i2c_device_remove(struct device *dev)
dev_pm_clear_wake_irq(&client->dev);
device_init_wakeup(&client->dev, false);
- client->irq = 0;
+ client->irq = client->init_irq;
return status;
}
@@ -741,10 +741,11 @@ i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
client->flags = info->flags;
client->addr = info->addr;
- client->irq = info->irq;
- if (!client->irq)
- client->irq = i2c_dev_irq_from_resources(info->resources,
+ client->init_irq = info->irq;
+ if (!client->init_irq)
+ client->init_irq = i2c_dev_irq_from_resources(info->resources,
info->num_resources);
+ client->irq = client->init_irq;
strlcpy(client->name, info->type, sizeof(client->name));
@@ -1232,6 +1233,7 @@ static int i2c_register_adapter(struct i2c_adapter *adap)
if (!adap->lock_ops)
adap->lock_ops = &i2c_adapter_lock_ops;
+ adap->locked_flags = 0;
rt_mutex_init(&adap->bus_lock);
rt_mutex_init(&adap->mux_lock);
mutex_init(&adap->userspace_clients_lock);
@@ -1865,6 +1867,8 @@ int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
if (WARN_ON(!msgs || num < 1))
return -EINVAL;
+ if (WARN_ON(test_bit(I2C_ALF_IS_SUSPENDED, &adap->locked_flags)))
+ return -ESHUTDOWN;
if (adap->quirks && i2c_check_for_quirks(adap, msgs, num))
return -EOPNOTSUPP;
@@ -2254,7 +2258,8 @@ EXPORT_SYMBOL(i2c_put_adapter);
/**
* i2c_get_dma_safe_msg_buf() - get a DMA safe buffer for the given i2c_msg
* @msg: the message to be checked
- * @threshold: the minimum number of bytes for which using DMA makes sense
+ * @threshold: the minimum number of bytes for which using DMA makes sense.
+ * Should at least be 1.
*
* Return: NULL if a DMA safe buffer was not obtained. Use msg->buf with PIO.
* Or a valid pointer to be used with DMA. After use, release it by
@@ -2264,7 +2269,11 @@ EXPORT_SYMBOL(i2c_put_adapter);
*/
u8 *i2c_get_dma_safe_msg_buf(struct i2c_msg *msg, unsigned int threshold)
{
- if (msg->len < threshold)
+ /* also skip 0-length msgs for bogus thresholds of 0 */
+ if (!threshold)
+ pr_debug("DMA buffer for addr=0x%02x with length 0 is bogus\n",
+ msg->addr);
+ if (msg->len < threshold || msg->len == 0)
return NULL;
if (msg->flags & I2C_M_DMA_SAFE)
diff --git a/drivers/i2c/i2c-core-of.c b/drivers/i2c/i2c-core-of.c
index 6cb7ad608bcd..0f01cdba9d2c 100644
--- a/drivers/i2c/i2c-core-of.c
+++ b/drivers/i2c/i2c-core-of.c
@@ -121,6 +121,17 @@ static int of_dev_node_match(struct device *dev, void *data)
return dev->of_node == data;
}
+static int of_dev_or_parent_node_match(struct device *dev, void *data)
+{
+ if (dev->of_node == data)
+ return 1;
+
+ if (dev->parent)
+ return dev->parent->of_node == data;
+
+ return 0;
+}
+
/* must call put_device() when done with returned i2c_client device */
struct i2c_client *of_find_i2c_device_by_node(struct device_node *node)
{
@@ -145,7 +156,8 @@ struct i2c_adapter *of_find_i2c_adapter_by_node(struct device_node *node)
struct device *dev;
struct i2c_adapter *adapter;
- dev = bus_find_device(&i2c_bus_type, NULL, node, of_dev_node_match);
+ dev = bus_find_device(&i2c_bus_type, NULL, node,
+ of_dev_or_parent_node_match);
if (!dev)
return NULL;
diff --git a/drivers/i2c/i2c-core-smbus.c b/drivers/i2c/i2c-core-smbus.c
index 9cd66cabb84f..132119112596 100644
--- a/drivers/i2c/i2c-core-smbus.c
+++ b/drivers/i2c/i2c-core-smbus.c
@@ -585,7 +585,7 @@ s32 __i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
trace:
/* If enabled, the reply tracepoint is conditional on read_write. */
trace_smbus_reply(adapter, addr, flags, read_write,
- command, protocol, data);
+ command, protocol, data, res);
trace_smbus_result(adapter, addr, flags, read_write,
command, protocol, res);
diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c
index ccd76c71af09..3f7b9af11137 100644
--- a/drivers/i2c/i2c-dev.c
+++ b/drivers/i2c/i2c-dev.c
@@ -52,7 +52,7 @@ struct i2c_dev {
struct cdev cdev;
};
-#define I2C_MINORS MINORMASK
+#define I2C_MINORS (MINORMASK + 1)
static LIST_HEAD(i2c_dev_list);
static DEFINE_SPINLOCK(i2c_dev_list_lock);
diff --git a/drivers/ide/Kconfig b/drivers/ide/Kconfig
index 901b8833847f..19fcd0756f46 100644
--- a/drivers/ide/Kconfig
+++ b/drivers/ide/Kconfig
@@ -320,9 +320,9 @@ config BLK_DEV_OFFBOARD
config BLK_DEV_GENERIC
tristate "Generic PCI IDE Chipset Support"
select BLK_DEV_IDEPCI
- help
- This option provides generic support for various PCI IDE Chipsets
- which otherwise might not be supported.
+ help
+ This option provides generic support for various PCI IDE Chipsets
+ which otherwise might not be supported.
config BLK_DEV_OPTI621
tristate "OPTi 82C621 chipset enhanced support"
@@ -516,7 +516,7 @@ config BLK_DEV_IT8213
tristate "IT8213 IDE support"
select BLK_DEV_IDEDMA_PCI
help
- This driver adds support for the ITE 8213 IDE controller.
+ This driver adds support for the ITE 8213 IDE controller.
config BLK_DEV_IT821X
tristate "IT821X IDE support"
@@ -671,20 +671,20 @@ config BLK_DEV_IDE_PMAC_ATA100FIRST
hard disk and hdc for CD-ROM.
config BLK_DEV_IDE_AU1XXX
- bool "IDE for AMD Alchemy Au1200"
- depends on MIPS_ALCHEMY
- select IDE_XFER_MODE
+ bool "IDE for AMD Alchemy Au1200"
+ depends on MIPS_ALCHEMY
+ select IDE_XFER_MODE
choice
- prompt "IDE Mode for AMD Alchemy Au1200"
- default BLK_DEV_IDE_AU1XXX_PIO_DBDMA
- depends on BLK_DEV_IDE_AU1XXX
+ prompt "IDE Mode for AMD Alchemy Au1200"
+ default BLK_DEV_IDE_AU1XXX_PIO_DBDMA
+ depends on BLK_DEV_IDE_AU1XXX
config BLK_DEV_IDE_AU1XXX_PIO_DBDMA
- bool "PIO+DbDMA IDE for AMD Alchemy Au1200"
+ bool "PIO+DbDMA IDE for AMD Alchemy Au1200"
config BLK_DEV_IDE_AU1XXX_MDMA2_DBDMA
- bool "MDMA2+DbDMA IDE for AMD Alchemy Au1200"
- depends on BLK_DEV_IDE_AU1XXX
+ bool "MDMA2+DbDMA IDE for AMD Alchemy Au1200"
+ depends on BLK_DEV_IDE_AU1XXX
endchoice
config BLK_DEV_IDE_TX4938
diff --git a/drivers/ide/hpt366.c b/drivers/ide/hpt366.c
index 4d565b0c5a6e..0a3f9bcc8b04 100644
--- a/drivers/ide/hpt366.c
+++ b/drivers/ide/hpt366.c
@@ -574,7 +574,7 @@ static u8 hpt3xx_udma_filter(ide_drive_t *drive)
if (!HPT370_ALLOW_ATA100_5 ||
check_in_drive_list(drive, bad_ata100_5))
return ATA_UDMA4;
- /* else: fall through */
+ /* fall through */
case HPT372 :
case HPT372A:
case HPT372N:
@@ -601,7 +601,7 @@ static u8 hpt3xx_mdma_filter(ide_drive_t *drive)
case HPT374 :
if (ata_id_is_sata(drive->id))
return 0x00;
- /* else: fall through */
+ /* fall through */
default:
return 0x07;
}
diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c
index 780d33ccc5d8..1ea2f9e82bf8 100644
--- a/drivers/ide/ide-floppy.c
+++ b/drivers/ide/ide-floppy.c
@@ -427,7 +427,7 @@ static int ide_floppy_get_capacity(ide_drive_t *drive)
* (maintains previous driver behaviour)
*/
break;
- /* else: fall through */
+ /* fall through */
case CAPACITY_CURRENT:
/* Normal Zip/LS-120 disks */
if (memcmp(cap_desc, &floppy->cap_desc, 8))
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 5debc67df70a..76db6e5cc296 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -817,6 +817,13 @@ config STM32_DFSDM_ADC
This driver can also be built as a module. If so, the module
will be called stm32-dfsdm-adc.
+config STMPE_ADC
+ tristate "STMicroelectronics STMPE ADC driver"
+ depends on OF && MFD_STMPE
+ help
+ Say yes here to build support for ST Microelectronics STMPE
+ built-in ADC block (stmpe811).
+
config STX104
tristate "Apex Embedded Systems STX104 driver"
depends on PC104 && X86
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index d50eb47da484..6fcebd167524 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -77,6 +77,7 @@ obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o
obj-$(CONFIG_STM32_ADC) += stm32-adc.o
obj-$(CONFIG_STM32_DFSDM_CORE) += stm32-dfsdm-core.o
obj-$(CONFIG_STM32_DFSDM_ADC) += stm32-dfsdm-adc.o
+obj-$(CONFIG_STMPE_ADC) += stmpe-adc.o
obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o
obj-$(CONFIG_TI_ADC084S021) += ti-adc084s021.o
diff --git a/drivers/iio/adc/stmpe-adc.c b/drivers/iio/adc/stmpe-adc.c
new file mode 100644
index 000000000000..37f4b74a5d32
--- /dev/null
+++ b/drivers/iio/adc/stmpe-adc.c
@@ -0,0 +1,363 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * STMicroelectronics STMPE811 IIO ADC Driver
+ *
+ * 4 channel, 10/12-bit ADC
+ *
+ * Copyright (C) 2013-2018 Toradex AG <stefan.agner@toradex.com>
+ */
+
+#include <linux/completion.h>
+#include <linux/err.h>
+#include <linux/iio/iio.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mfd/stmpe.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+
+#define STMPE_REG_INT_STA 0x0B
+#define STMPE_REG_ADC_INT_EN 0x0E
+#define STMPE_REG_ADC_INT_STA 0x0F
+
+#define STMPE_REG_ADC_CTRL1 0x20
+#define STMPE_REG_ADC_CTRL2 0x21
+#define STMPE_REG_ADC_CAPT 0x22
+#define STMPE_REG_ADC_DATA_CH(channel) (0x30 + 2 * (channel))
+
+#define STMPE_REG_TEMP_CTRL 0x60
+#define STMPE_TEMP_CTRL_ENABLE BIT(0)
+#define STMPE_TEMP_CTRL_ACQ BIT(1)
+#define STMPE_TEMP_CTRL_THRES_EN BIT(3)
+#define STMPE_START_ONE_TEMP_CONV (STMPE_TEMP_CTRL_ENABLE | \
+ STMPE_TEMP_CTRL_ACQ | \
+ STMPE_TEMP_CTRL_THRES_EN)
+#define STMPE_REG_TEMP_DATA 0x61
+#define STMPE_REG_TEMP_TH 0x63
+#define STMPE_ADC_LAST_NR 7
+#define STMPE_TEMP_CHANNEL (STMPE_ADC_LAST_NR + 1)
+
+#define STMPE_ADC_CH(channel) ((1 << (channel)) & 0xff)
+
+#define STMPE_ADC_TIMEOUT msecs_to_jiffies(1000)
+
+struct stmpe_adc {
+ struct stmpe *stmpe;
+ struct clk *clk;
+ struct device *dev;
+ struct mutex lock;
+
+ /* We are allocating plus one for the temperature channel */
+ struct iio_chan_spec stmpe_adc_iio_channels[STMPE_ADC_LAST_NR + 2];
+
+ struct completion completion;
+
+ u8 channel;
+ u32 value;
+};
+
+static int stmpe_read_voltage(struct stmpe_adc *info,
+ struct iio_chan_spec const *chan, int *val)
+{
+ long ret;
+
+ mutex_lock(&info->lock);
+
+ info->channel = (u8)chan->channel;
+
+ if (info->channel > STMPE_ADC_LAST_NR) {
+ mutex_unlock(&info->lock);
+ return -EINVAL;
+ }
+
+ stmpe_reg_write(info->stmpe, STMPE_REG_ADC_INT_EN,
+ STMPE_ADC_CH(info->channel));
+
+ stmpe_reg_write(info->stmpe, STMPE_REG_ADC_CAPT,
+ STMPE_ADC_CH(info->channel));
+
+ *val = info->value;
+
+ ret = wait_for_completion_interruptible_timeout
+ (&info->completion, STMPE_ADC_TIMEOUT);
+
+ if (ret <= 0) {
+ mutex_unlock(&info->lock);
+ if (ret == 0)
+ return -ETIMEDOUT;
+ else
+ return ret;
+ }
+
+ *val = info->value;
+
+ mutex_unlock(&info->lock);
+
+ return 0;
+}
+
+static int stmpe_read_temp(struct stmpe_adc *info,
+ struct iio_chan_spec const *chan, int *val)
+{
+ long ret;
+
+ mutex_lock(&info->lock);
+
+ info->channel = (u8)chan->channel;
+
+ if (info->channel != STMPE_TEMP_CHANNEL) {
+ mutex_unlock(&info->lock);
+ return -EINVAL;
+ }
+
+ stmpe_reg_write(info->stmpe, STMPE_REG_TEMP_CTRL,
+ STMPE_START_ONE_TEMP_CONV);
+
+ ret = wait_for_completion_interruptible_timeout
+ (&info->completion, STMPE_ADC_TIMEOUT);
+
+ if (ret <= 0) {
+ mutex_unlock(&info->lock);
+ if (ret == 0)
+ return -ETIMEDOUT;
+ else
+ return ret;
+ }
+
+ /*
+ * absolute temp = +V3.3 * value /7.51 [K]
+ * scale to [milli °C]
+ */
+ *val = ((449960l * info->value) / 1024l) - 273150;
+
+ mutex_unlock(&info->lock);
+
+ return 0;
+}
+
+static int stmpe_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long mask)
+{
+ struct stmpe_adc *info = iio_priv(indio_dev);
+ long ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ case IIO_CHAN_INFO_PROCESSED:
+
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ ret = stmpe_read_voltage(info, chan, val);
+ break;
+
+ case IIO_TEMP:
+ ret = stmpe_read_temp(info, chan, val);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (ret < 0)
+ return ret;
+
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ *val = 3300;
+ *val2 = info->stmpe->mod_12b ? 12 : 10;
+ return IIO_VAL_FRACTIONAL_LOG2;
+
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static irqreturn_t stmpe_adc_isr(int irq, void *dev_id)
+{
+ struct stmpe_adc *info = (struct stmpe_adc *)dev_id;
+ u16 data;
+
+ if (info->channel > STMPE_TEMP_CHANNEL)
+ return IRQ_NONE;
+
+ if (info->channel <= STMPE_ADC_LAST_NR) {
+ int int_sta;
+
+ int_sta = stmpe_reg_read(info->stmpe, STMPE_REG_ADC_INT_STA);
+
+ /* Is the interrupt relevant */
+ if (!(int_sta & STMPE_ADC_CH(info->channel)))
+ return IRQ_NONE;
+
+ /* Read value */
+ stmpe_block_read(info->stmpe,
+ STMPE_REG_ADC_DATA_CH(info->channel), 2, (u8 *) &data);
+
+ stmpe_reg_write(info->stmpe, STMPE_REG_ADC_INT_STA, int_sta);
+ } else if (info->channel == STMPE_TEMP_CHANNEL) {
+ /* Read value */
+ stmpe_block_read(info->stmpe, STMPE_REG_TEMP_DATA, 2,
+ (u8 *) &data);
+ }
+
+ info->value = (u32) be16_to_cpu(data);
+ complete(&info->completion);
+
+ return IRQ_HANDLED;
+}
+
+static const struct iio_info stmpe_adc_iio_info = {
+ .read_raw = &stmpe_read_raw,
+};
+
+static void stmpe_adc_voltage_chan(struct iio_chan_spec *ics, int chan)
+{
+ ics->type = IIO_VOLTAGE;
+ ics->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
+ ics->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
+ ics->indexed = 1;
+ ics->channel = chan;
+}
+
+static void stmpe_adc_temp_chan(struct iio_chan_spec *ics, int chan)
+{
+ ics->type = IIO_TEMP;
+ ics->info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED);
+ ics->indexed = 1;
+ ics->channel = chan;
+}
+
+static int stmpe_adc_init_hw(struct stmpe_adc *adc)
+{
+ int ret;
+ struct stmpe *stmpe = adc->stmpe;
+
+ ret = stmpe_enable(stmpe, STMPE_BLOCK_ADC);
+ if (ret) {
+ dev_err(stmpe->dev, "Could not enable clock for ADC\n");
+ return ret;
+ }
+
+ ret = stmpe811_adc_common_init(stmpe);
+ if (ret) {
+ stmpe_disable(stmpe, STMPE_BLOCK_ADC);
+ return ret;
+ }
+
+ /* use temp irq for each conversion completion */
+ stmpe_reg_write(stmpe, STMPE_REG_TEMP_TH, 0);
+ stmpe_reg_write(stmpe, STMPE_REG_TEMP_TH + 1, 0);
+
+ return 0;
+}
+
+static int stmpe_adc_probe(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev;
+ struct stmpe_adc *info;
+ struct device_node *np;
+ u32 norequest_mask = 0;
+ int irq_temp, irq_adc;
+ int num_chan = 0;
+ int i = 0;
+ int ret;
+
+ irq_adc = platform_get_irq_byname(pdev, "STMPE_ADC");
+ if (irq_adc < 0)
+ return irq_adc;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct stmpe_adc));
+ if (!indio_dev) {
+ dev_err(&pdev->dev, "failed allocating iio device\n");
+ return -ENOMEM;
+ }
+
+ info = iio_priv(indio_dev);
+ mutex_init(&info->lock);
+
+ init_completion(&info->completion);
+ ret = devm_request_threaded_irq(&pdev->dev, irq_adc, NULL,
+ stmpe_adc_isr, IRQF_ONESHOT,
+ "stmpe-adc", info);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed requesting irq, irq = %d\n",
+ irq_adc);
+ return ret;
+ }
+
+ irq_temp = platform_get_irq_byname(pdev, "STMPE_TEMP_SENS");
+ if (irq_temp >= 0) {
+ ret = devm_request_threaded_irq(&pdev->dev, irq_temp, NULL,
+ stmpe_adc_isr, IRQF_ONESHOT,
+ "stmpe-adc", info);
+ if (ret < 0)
+ dev_warn(&pdev->dev, "failed requesting irq for"
+ " temp sensor, irq = %d\n", irq_temp);
+ }
+
+ platform_set_drvdata(pdev, indio_dev);
+
+ indio_dev->name = dev_name(&pdev->dev);
+ indio_dev->dev.parent = &pdev->dev;
+ indio_dev->info = &stmpe_adc_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ info->stmpe = dev_get_drvdata(pdev->dev.parent);
+
+ np = pdev->dev.of_node;
+
+ if (!np)
+ dev_err(&pdev->dev, "no device tree node found\n");
+
+ of_property_read_u32(np, "st,norequest-mask", &norequest_mask);
+
+ for_each_clear_bit(i, (unsigned long *) &norequest_mask,
+ (STMPE_ADC_LAST_NR + 1)) {
+ stmpe_adc_voltage_chan(&info->stmpe_adc_iio_channels[num_chan], i);
+ num_chan++;
+ }
+ stmpe_adc_temp_chan(&info->stmpe_adc_iio_channels[num_chan], i);
+ num_chan++;
+ indio_dev->channels = info->stmpe_adc_iio_channels;
+ indio_dev->num_channels = num_chan;
+
+ ret = stmpe_adc_init_hw(info);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(&pdev->dev, indio_dev);
+}
+
+static int __maybe_unused stmpe_adc_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct stmpe_adc *info = iio_priv(indio_dev);
+
+ stmpe_adc_init_hw(info);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(stmpe_adc_pm_ops, NULL, stmpe_adc_resume);
+
+static struct platform_driver stmpe_adc_driver = {
+ .probe = stmpe_adc_probe,
+ .driver = {
+ .name = "stmpe-adc",
+ .pm = &stmpe_adc_pm_ops,
+ },
+};
+
+module_platform_driver(stmpe_adc_driver);
+
+MODULE_AUTHOR("Stefan Agner <stefan.agner@toradex.com>");
+MODULE_DESCRIPTION("STMPEXXX ADC driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:stmpe-adc");
diff --git a/drivers/infiniband/Kconfig b/drivers/infiniband/Kconfig
index 0a3ec7c726ec..a1fb840de45d 100644
--- a/drivers/infiniband/Kconfig
+++ b/drivers/infiniband/Kconfig
@@ -89,6 +89,7 @@ config INFINIBAND_ADDR_TRANS_CONFIGFS
This allows the user to config the default GID type that the CM
uses for each device, when initiaing new connections.
+if INFINIBAND_USER_ACCESS || !INFINIBAND_USER_ACCESS
source "drivers/infiniband/hw/mthca/Kconfig"
source "drivers/infiniband/hw/qib/Kconfig"
source "drivers/infiniband/hw/cxgb3/Kconfig"
@@ -101,6 +102,12 @@ source "drivers/infiniband/hw/ocrdma/Kconfig"
source "drivers/infiniband/hw/vmw_pvrdma/Kconfig"
source "drivers/infiniband/hw/usnic/Kconfig"
source "drivers/infiniband/hw/hns/Kconfig"
+source "drivers/infiniband/hw/bnxt_re/Kconfig"
+source "drivers/infiniband/hw/hfi1/Kconfig"
+source "drivers/infiniband/hw/qedr/Kconfig"
+source "drivers/infiniband/sw/rdmavt/Kconfig"
+source "drivers/infiniband/sw/rxe/Kconfig"
+endif
source "drivers/infiniband/ulp/ipoib/Kconfig"
@@ -111,13 +118,5 @@ source "drivers/infiniband/ulp/iser/Kconfig"
source "drivers/infiniband/ulp/isert/Kconfig"
source "drivers/infiniband/ulp/opa_vnic/Kconfig"
-source "drivers/infiniband/sw/rdmavt/Kconfig"
-source "drivers/infiniband/sw/rxe/Kconfig"
-
-source "drivers/infiniband/hw/hfi1/Kconfig"
-
-source "drivers/infiniband/hw/qedr/Kconfig"
-
-source "drivers/infiniband/hw/bnxt_re/Kconfig"
endif # INFINIBAND
diff --git a/drivers/infiniband/core/Makefile b/drivers/infiniband/core/Makefile
index 69dee36e0e89..313f2349b518 100644
--- a/drivers/infiniband/core/Makefile
+++ b/drivers/infiniband/core/Makefile
@@ -15,8 +15,6 @@ ib_core-y := packer.o ud_header.o verbs.o cq.o rw.o sysfs.o \
nldev.o restrack.o
ib_core-$(CONFIG_SECURITY_INFINIBAND) += security.o
-ib_core-$(CONFIG_INFINIBAND_USER_MEM) += umem.o
-ib_core-$(CONFIG_INFINIBAND_ON_DEMAND_PAGING) += umem_odp.o
ib_core-$(CONFIG_CGROUP_RDMA) += cgroup.o
ib_cm-y := cm.o
@@ -39,3 +37,5 @@ ib_uverbs-y := uverbs_main.o uverbs_cmd.o uverbs_marshall.o \
uverbs_std_types_flow_action.o uverbs_std_types_dm.o \
uverbs_std_types_mr.o uverbs_std_types_counters.o \
uverbs_uapi.o uverbs_std_types_device.o
+ib_uverbs-$(CONFIG_INFINIBAND_USER_MEM) += umem.o
+ib_uverbs-$(CONFIG_INFINIBAND_ON_DEMAND_PAGING) += umem_odp.o
diff --git a/drivers/infiniband/core/cache.c b/drivers/infiniband/core/cache.c
index 7b04590f307f..43c67e5f43c6 100644
--- a/drivers/infiniband/core/cache.c
+++ b/drivers/infiniband/core/cache.c
@@ -185,7 +185,7 @@ EXPORT_SYMBOL(ib_cache_gid_parse_type_str);
static struct ib_gid_table *rdma_gid_table(struct ib_device *device, u8 port)
{
- return device->cache.ports[port - rdma_start_port(device)].gid;
+ return device->port_data[port].cache.gid;
}
static bool is_gid_entry_free(const struct ib_gid_table_entry *entry)
@@ -547,21 +547,19 @@ int ib_cache_gid_add(struct ib_device *ib_dev, u8 port,
unsigned long mask;
int ret;
- if (ib_dev->ops.get_netdev) {
- idev = ib_dev->ops.get_netdev(ib_dev, port);
- if (idev && attr->ndev != idev) {
- union ib_gid default_gid;
+ idev = ib_device_get_netdev(ib_dev, port);
+ if (idev && attr->ndev != idev) {
+ union ib_gid default_gid;
- /* Adding default GIDs in not permitted */
- make_default_gid(idev, &default_gid);
- if (!memcmp(gid, &default_gid, sizeof(*gid))) {
- dev_put(idev);
- return -EPERM;
- }
- }
- if (idev)
+ /* Adding default GIDs is not permitted */
+ make_default_gid(idev, &default_gid);
+ if (!memcmp(gid, &default_gid, sizeof(*gid))) {
dev_put(idev);
+ return -EPERM;
+ }
}
+ if (idev)
+ dev_put(idev);
mask = GID_ATTR_FIND_MASK_GID |
GID_ATTR_FIND_MASK_GID_TYPE |
@@ -765,7 +763,7 @@ err_free_table:
return NULL;
}
-static void release_gid_table(struct ib_device *device, u8 port,
+static void release_gid_table(struct ib_device *device,
struct ib_gid_table *table)
{
bool leak = false;
@@ -863,31 +861,27 @@ static void gid_table_reserve_default(struct ib_device *ib_dev, u8 port,
static void gid_table_release_one(struct ib_device *ib_dev)
{
- struct ib_gid_table *table;
- u8 port;
+ unsigned int p;
- for (port = 0; port < ib_dev->phys_port_cnt; port++) {
- table = ib_dev->cache.ports[port].gid;
- release_gid_table(ib_dev, port, table);
- ib_dev->cache.ports[port].gid = NULL;
+ rdma_for_each_port (ib_dev, p) {
+ release_gid_table(ib_dev, ib_dev->port_data[p].cache.gid);
+ ib_dev->port_data[p].cache.gid = NULL;
}
}
static int _gid_table_setup_one(struct ib_device *ib_dev)
{
- u8 port;
struct ib_gid_table *table;
+ unsigned int rdma_port;
- for (port = 0; port < ib_dev->phys_port_cnt; port++) {
- u8 rdma_port = port + rdma_start_port(ib_dev);
-
- table = alloc_gid_table(
- ib_dev->port_immutable[rdma_port].gid_tbl_len);
+ rdma_for_each_port (ib_dev, rdma_port) {
+ table = alloc_gid_table(
+ ib_dev->port_data[rdma_port].immutable.gid_tbl_len);
if (!table)
goto rollback_table_setup;
gid_table_reserve_default(ib_dev, rdma_port, table);
- ib_dev->cache.ports[port].gid = table;
+ ib_dev->port_data[rdma_port].cache.gid = table;
}
return 0;
@@ -898,14 +892,11 @@ rollback_table_setup:
static void gid_table_cleanup_one(struct ib_device *ib_dev)
{
- struct ib_gid_table *table;
- u8 port;
+ unsigned int p;
- for (port = 0; port < ib_dev->phys_port_cnt; port++) {
- table = ib_dev->cache.ports[port].gid;
- cleanup_gid_table_port(ib_dev, port + rdma_start_port(ib_dev),
- table);
- }
+ rdma_for_each_port (ib_dev, p)
+ cleanup_gid_table_port(ib_dev, p,
+ ib_dev->port_data[p].cache.gid);
}
static int gid_table_setup_one(struct ib_device *ib_dev)
@@ -983,17 +974,17 @@ const struct ib_gid_attr *rdma_find_gid(struct ib_device *device,
unsigned long mask = GID_ATTR_FIND_MASK_GID |
GID_ATTR_FIND_MASK_GID_TYPE;
struct ib_gid_attr gid_attr_val = {.ndev = ndev, .gid_type = gid_type};
- u8 p;
+ unsigned int p;
if (ndev)
mask |= GID_ATTR_FIND_MASK_NETDEV;
- for (p = 0; p < device->phys_port_cnt; p++) {
+ rdma_for_each_port(device, p) {
struct ib_gid_table *table;
unsigned long flags;
int index;
- table = device->cache.ports[p].gid;
+ table = device->port_data[p].cache.gid;
read_lock_irqsave(&table->rwlock, flags);
index = find_gid(table, gid, &gid_attr_val, false, mask, NULL);
if (index >= 0) {
@@ -1025,7 +1016,7 @@ int ib_get_cached_pkey(struct ib_device *device,
read_lock_irqsave(&device->cache.lock, flags);
- cache = device->cache.ports[port_num - rdma_start_port(device)].pkey;
+ cache = device->port_data[port_num].cache.pkey;
if (index < 0 || index >= cache->table_len)
ret = -EINVAL;
@@ -1043,14 +1034,12 @@ int ib_get_cached_subnet_prefix(struct ib_device *device,
u64 *sn_pfx)
{
unsigned long flags;
- int p;
if (!rdma_is_port_valid(device, port_num))
return -EINVAL;
- p = port_num - rdma_start_port(device);
read_lock_irqsave(&device->cache.lock, flags);
- *sn_pfx = device->cache.ports[p].subnet_prefix;
+ *sn_pfx = device->port_data[port_num].cache.subnet_prefix;
read_unlock_irqrestore(&device->cache.lock, flags);
return 0;
@@ -1073,7 +1062,7 @@ int ib_find_cached_pkey(struct ib_device *device,
read_lock_irqsave(&device->cache.lock, flags);
- cache = device->cache.ports[port_num - rdma_start_port(device)].pkey;
+ cache = device->port_data[port_num].cache.pkey;
*index = -1;
@@ -1113,7 +1102,7 @@ int ib_find_exact_cached_pkey(struct ib_device *device,
read_lock_irqsave(&device->cache.lock, flags);
- cache = device->cache.ports[port_num - rdma_start_port(device)].pkey;
+ cache = device->port_data[port_num].cache.pkey;
*index = -1;
@@ -1141,7 +1130,7 @@ int ib_get_cached_lmc(struct ib_device *device,
return -EINVAL;
read_lock_irqsave(&device->cache.lock, flags);
- *lmc = device->cache.ports[port_num - rdma_start_port(device)].lmc;
+ *lmc = device->port_data[port_num].cache.lmc;
read_unlock_irqrestore(&device->cache.lock, flags);
return ret;
@@ -1159,8 +1148,7 @@ int ib_get_cached_port_state(struct ib_device *device,
return -EINVAL;
read_lock_irqsave(&device->cache.lock, flags);
- *port_state = device->cache.ports[port_num
- - rdma_start_port(device)].port_state;
+ *port_state = device->port_data[port_num].cache.port_state;
read_unlock_irqrestore(&device->cache.lock, flags);
return ret;
@@ -1361,16 +1349,13 @@ static void ib_cache_update(struct ib_device *device,
write_lock_irq(&device->cache.lock);
- old_pkey_cache = device->cache.ports[port -
- rdma_start_port(device)].pkey;
+ old_pkey_cache = device->port_data[port].cache.pkey;
- device->cache.ports[port - rdma_start_port(device)].pkey = pkey_cache;
- device->cache.ports[port - rdma_start_port(device)].lmc = tprops->lmc;
- device->cache.ports[port - rdma_start_port(device)].port_state =
- tprops->state;
+ device->port_data[port].cache.pkey = pkey_cache;
+ device->port_data[port].cache.lmc = tprops->lmc;
+ device->port_data[port].cache.port_state = tprops->state;
- device->cache.ports[port - rdma_start_port(device)].subnet_prefix =
- tprops->subnet_prefix;
+ device->port_data[port].cache.subnet_prefix = tprops->subnet_prefix;
write_unlock_irq(&device->cache.lock);
if (enforce_security)
@@ -1428,27 +1413,17 @@ static void ib_cache_event(struct ib_event_handler *handler,
int ib_cache_setup_one(struct ib_device *device)
{
- int p;
+ unsigned int p;
int err;
rwlock_init(&device->cache.lock);
- device->cache.ports =
- kcalloc(rdma_end_port(device) - rdma_start_port(device) + 1,
- sizeof(*device->cache.ports),
- GFP_KERNEL);
- if (!device->cache.ports)
- return -ENOMEM;
-
err = gid_table_setup_one(device);
- if (err) {
- kfree(device->cache.ports);
- device->cache.ports = NULL;
+ if (err)
return err;
- }
- for (p = 0; p <= rdma_end_port(device) - rdma_start_port(device); ++p)
- ib_cache_update(device, p + rdma_start_port(device), true);
+ rdma_for_each_port (device, p)
+ ib_cache_update(device, p, true);
INIT_IB_EVENT_HANDLER(&device->cache.event_handler,
device, ib_cache_event);
@@ -1458,7 +1433,7 @@ int ib_cache_setup_one(struct ib_device *device)
void ib_cache_release_one(struct ib_device *device)
{
- int p;
+ unsigned int p;
/*
* The release function frees all the cache elements.
@@ -1466,11 +1441,10 @@ void ib_cache_release_one(struct ib_device *device)
* all the device's resources when the cache could no
* longer be accessed.
*/
- for (p = 0; p <= rdma_end_port(device) - rdma_start_port(device); ++p)
- kfree(device->cache.ports[p].pkey);
+ rdma_for_each_port (device, p)
+ kfree(device->port_data[p].cache.pkey);
gid_table_release_one(device);
- kfree(device->cache.ports);
}
void ib_cache_cleanup_one(struct ib_device *device)
diff --git a/drivers/infiniband/core/cgroup.c b/drivers/infiniband/core/cgroup.c
index 126ac5f99db7..388fd04e5f63 100644
--- a/drivers/infiniband/core/cgroup.c
+++ b/drivers/infiniband/core/cgroup.c
@@ -21,12 +21,11 @@
* Register with the rdma cgroup. Should be called before
* exposing rdma device to user space applications to avoid
* resource accounting leak.
- * Returns 0 on success or otherwise failure code.
*/
-int ib_device_register_rdmacg(struct ib_device *device)
+void ib_device_register_rdmacg(struct ib_device *device)
{
device->cg_device.name = device->name;
- return rdmacg_register_device(&device->cg_device);
+ rdmacg_register_device(&device->cg_device);
}
/**
diff --git a/drivers/infiniband/core/cm.c b/drivers/infiniband/core/cm.c
index 37980c7564c0..b9416a6fca36 100644
--- a/drivers/infiniband/core/cm.c
+++ b/drivers/infiniband/core/cm.c
@@ -4052,8 +4052,7 @@ static void cm_recv_handler(struct ib_mad_agent *mad_agent,
atomic_long_inc(&port->counter_group[CM_RECV].
counter[attr_id - CM_ATTR_ID_OFFSET]);
- work = kmalloc(sizeof(*work) + sizeof(struct sa_path_rec) * paths,
- GFP_KERNEL);
+ work = kmalloc(struct_size(work, path, paths), GFP_KERNEL);
if (!work) {
ib_free_recv_mad(mad_recv_wc);
return;
diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c
index 84f077b2b90a..68c997be2429 100644
--- a/drivers/infiniband/core/cma.c
+++ b/drivers/infiniband/core/cma.c
@@ -659,7 +659,7 @@ static int cma_acquire_dev_by_src_ip(struct rdma_id_private *id_priv)
struct cma_device *cma_dev;
enum ib_gid_type gid_type;
int ret = -ENODEV;
- u8 port;
+ unsigned int port;
if (dev_addr->dev_type != ARPHRD_INFINIBAND &&
id_priv->id.ps == RDMA_PS_IPOIB)
@@ -673,8 +673,7 @@ static int cma_acquire_dev_by_src_ip(struct rdma_id_private *id_priv)
mutex_lock(&lock);
list_for_each_entry(cma_dev, &dev_list, list) {
- for (port = rdma_start_port(cma_dev->device);
- port <= rdma_end_port(cma_dev->device); port++) {
+ rdma_for_each_port (cma_dev->device, port) {
gidp = rdma_protocol_roce(cma_dev->device, port) ?
&iboe_gid : &gid;
gid_type = cma_dev->default_gid_type[port - 1];
@@ -888,6 +887,7 @@ struct rdma_cm_id *__rdma_create_id(struct net *net,
id_priv->id.ps = ps;
id_priv->id.qp_type = qp_type;
id_priv->tos_set = false;
+ id_priv->timeout_set = false;
id_priv->gid_type = IB_GID_TYPE_IB;
spin_lock_init(&id_priv->lock);
mutex_init(&id_priv->qp_mutex);
@@ -1130,6 +1130,9 @@ int rdma_init_qp_attr(struct rdma_cm_id *id, struct ib_qp_attr *qp_attr,
} else
ret = -ENOSYS;
+ if ((*qp_attr_mask & IB_QP_TIMEOUT) && id_priv->timeout_set)
+ qp_attr->timeout = id_priv->timeout;
+
return ret;
}
EXPORT_SYMBOL(rdma_init_qp_attr);
@@ -2410,6 +2413,7 @@ static int cma_iw_listen(struct rdma_id_private *id_priv, int backlog)
return PTR_ERR(id);
id->tos = id_priv->tos;
+ id->tos_set = id_priv->tos_set;
id_priv->cm_id.iw = id;
memcpy(&id_priv->cm_id.iw->local_addr, cma_src_addr(id_priv),
@@ -2462,6 +2466,8 @@ static void cma_listen_on_dev(struct rdma_id_private *id_priv,
atomic_inc(&id_priv->refcount);
dev_id_priv->internal_id = 1;
dev_id_priv->afonly = id_priv->afonly;
+ dev_id_priv->tos_set = id_priv->tos_set;
+ dev_id_priv->tos = id_priv->tos;
ret = rdma_listen(id, id_priv->backlog);
if (ret)
@@ -2490,6 +2496,34 @@ void rdma_set_service_type(struct rdma_cm_id *id, int tos)
}
EXPORT_SYMBOL(rdma_set_service_type);
+/**
+ * rdma_set_ack_timeout() - Set the ack timeout of QP associated
+ * with a connection identifier.
+ * @id: Communication identifier to associated with service type.
+ * @timeout: Ack timeout to set a QP, expressed as 4.096 * 2^(timeout) usec.
+ *
+ * This function should be called before rdma_connect() on active side,
+ * and on passive side before rdma_accept(). It is applicable to primary
+ * path only. The timeout will affect the local side of the QP, it is not
+ * negotiated with remote side and zero disables the timer.
+ *
+ * Return: 0 for success
+ */
+int rdma_set_ack_timeout(struct rdma_cm_id *id, u8 timeout)
+{
+ struct rdma_id_private *id_priv;
+
+ if (id->qp_type != IB_QPT_RC)
+ return -EINVAL;
+
+ id_priv = container_of(id, struct rdma_id_private, id);
+ id_priv->timeout = timeout;
+ id_priv->timeout_set = true;
+
+ return 0;
+}
+EXPORT_SYMBOL(rdma_set_ack_timeout);
+
static void cma_query_handler(int status, struct sa_path_rec *path_rec,
void *context)
{
@@ -2966,13 +3000,22 @@ static void addr_handler(int status, struct sockaddr *src_addr,
{
struct rdma_id_private *id_priv = context;
struct rdma_cm_event event = {};
+ struct sockaddr *addr;
+ struct sockaddr_storage old_addr;
mutex_lock(&id_priv->handler_mutex);
if (!cma_comp_exch(id_priv, RDMA_CM_ADDR_QUERY,
RDMA_CM_ADDR_RESOLVED))
goto out;
- memcpy(cma_src_addr(id_priv), src_addr, rdma_addr_size(src_addr));
+ /*
+ * Store the previous src address, so that if we fail to acquire
+ * matching rdma device, old address can be restored back, which helps
+ * to cancel the cma listen operation correctly.
+ */
+ addr = cma_src_addr(id_priv);
+ memcpy(&old_addr, addr, rdma_addr_size(addr));
+ memcpy(addr, src_addr, rdma_addr_size(src_addr));
if (!status && !id_priv->cma_dev) {
status = cma_acquire_dev_by_src_ip(id_priv);
if (status)
@@ -2983,6 +3026,8 @@ static void addr_handler(int status, struct sockaddr *src_addr,
}
if (status) {
+ memcpy(addr, &old_addr,
+ rdma_addr_size((struct sockaddr *)&old_addr));
if (!cma_comp_exch(id_priv, RDMA_CM_ADDR_RESOLVED,
RDMA_CM_ADDR_BOUND))
goto out;
@@ -3798,6 +3843,7 @@ static int cma_connect_iw(struct rdma_id_private *id_priv,
return PTR_ERR(cm_id);
cm_id->tos = id_priv->tos;
+ cm_id->tos_set = id_priv->tos_set;
id_priv->cm_id.iw = cm_id;
memcpy(&cm_id->local_addr, cma_src_addr(id_priv),
@@ -4501,7 +4547,7 @@ static void cma_add_one(struct ib_device *device)
if (!cma_dev->default_roce_tos)
goto free_gid_type;
- for (i = rdma_start_port(device); i <= rdma_end_port(device); i++) {
+ rdma_for_each_port (device, i) {
supported_gids = roce_gid_type_mask_support(device, i);
WARN_ON(!supported_gids);
if (supported_gids & (1 << CMA_PREFERRED_ROCE_GID_TYPE))
@@ -4605,85 +4651,6 @@ static void cma_remove_one(struct ib_device *device, void *client_data)
kfree(cma_dev);
}
-static int cma_get_id_stats(struct sk_buff *skb, struct netlink_callback *cb)
-{
- struct nlmsghdr *nlh;
- struct rdma_cm_id_stats *id_stats;
- struct rdma_id_private *id_priv;
- struct rdma_cm_id *id = NULL;
- struct cma_device *cma_dev;
- int i_dev = 0, i_id = 0;
-
- /*
- * We export all of the IDs as a sequence of messages. Each
- * ID gets its own netlink message.
- */
- mutex_lock(&lock);
-
- list_for_each_entry(cma_dev, &dev_list, list) {
- if (i_dev < cb->args[0]) {
- i_dev++;
- continue;
- }
-
- i_id = 0;
- list_for_each_entry(id_priv, &cma_dev->id_list, list) {
- if (i_id < cb->args[1]) {
- i_id++;
- continue;
- }
-
- id_stats = ibnl_put_msg(skb, &nlh, cb->nlh->nlmsg_seq,
- sizeof *id_stats, RDMA_NL_RDMA_CM,
- RDMA_NL_RDMA_CM_ID_STATS,
- NLM_F_MULTI);
- if (!id_stats)
- goto out;
-
- memset(id_stats, 0, sizeof *id_stats);
- id = &id_priv->id;
- id_stats->node_type = id->route.addr.dev_addr.dev_type;
- id_stats->port_num = id->port_num;
- id_stats->bound_dev_if =
- id->route.addr.dev_addr.bound_dev_if;
-
- if (ibnl_put_attr(skb, nlh,
- rdma_addr_size(cma_src_addr(id_priv)),
- cma_src_addr(id_priv),
- RDMA_NL_RDMA_CM_ATTR_SRC_ADDR))
- goto out;
- if (ibnl_put_attr(skb, nlh,
- rdma_addr_size(cma_dst_addr(id_priv)),
- cma_dst_addr(id_priv),
- RDMA_NL_RDMA_CM_ATTR_DST_ADDR))
- goto out;
-
- id_stats->pid = task_pid_vnr(id_priv->res.task);
- id_stats->port_space = id->ps;
- id_stats->cm_state = id_priv->state;
- id_stats->qp_num = id_priv->qp_num;
- id_stats->qp_type = id->qp_type;
-
- i_id++;
- nlmsg_end(skb, nlh);
- }
-
- cb->args[1] = 0;
- i_dev++;
- }
-
-out:
- mutex_unlock(&lock);
- cb->args[0] = i_dev;
- cb->args[1] = i_id;
-
- return skb->len;
-}
-
-static const struct rdma_nl_cbs cma_cb_table[RDMA_NL_RDMA_CM_NUM_OPS] = {
- [RDMA_NL_RDMA_CM_ID_STATS] = { .dump = cma_get_id_stats},
-};
-
static int cma_init_net(struct net *net)
{
struct cma_pernet *pernet = cma_pernet(net);
@@ -4732,7 +4699,6 @@ static int __init cma_init(void)
if (ret)
goto err;
- rdma_nl_register(RDMA_NL_RDMA_CM, cma_cb_table);
cma_configfs_init();
return 0;
@@ -4748,7 +4714,6 @@ err_wq:
static void __exit cma_cleanup(void)
{
cma_configfs_exit();
- rdma_nl_unregister(RDMA_NL_RDMA_CM);
ib_unregister_client(&cma_client);
unregister_netdevice_notifier(&cma_nb);
ib_sa_unregister_client(&sa_client);
@@ -4756,7 +4721,5 @@ static void __exit cma_cleanup(void)
destroy_workqueue(cma_wq);
}
-MODULE_ALIAS_RDMA_NETLINK(RDMA_NL_RDMA_CM, 1);
-
module_init(cma_init);
module_exit(cma_cleanup);
diff --git a/drivers/infiniband/core/cma_priv.h b/drivers/infiniband/core/cma_priv.h
index cf47c69436a7..ca7307277518 100644
--- a/drivers/infiniband/core/cma_priv.h
+++ b/drivers/infiniband/core/cma_priv.h
@@ -84,9 +84,11 @@ struct rdma_id_private {
u32 options;
u8 srq;
u8 tos;
- bool tos_set;
+ u8 tos_set:1;
+ u8 timeout_set:1;
u8 reuseaddr;
u8 afonly;
+ u8 timeout;
enum ib_gid_type gid_type;
/*
diff --git a/drivers/infiniband/core/core_priv.h b/drivers/infiniband/core/core_priv.h
index 616734313f0c..08c690249594 100644
--- a/drivers/infiniband/core/core_priv.h
+++ b/drivers/infiniband/core/core_priv.h
@@ -54,9 +54,9 @@ struct pkey_index_qp_list {
struct list_head qp_list;
};
-int ib_device_register_sysfs(struct ib_device *device,
- int (*port_callback)(struct ib_device *,
- u8, struct kobject *));
+extern const struct attribute_group ib_dev_attr_group;
+
+int ib_device_register_sysfs(struct ib_device *device);
void ib_device_unregister_sysfs(struct ib_device *device);
int ib_device_rename(struct ib_device *ibdev, const char *name);
@@ -66,6 +66,9 @@ typedef void (*roce_netdev_callback)(struct ib_device *device, u8 port,
typedef bool (*roce_netdev_filter)(struct ib_device *device, u8 port,
struct net_device *idev, void *cookie);
+struct net_device *ib_device_get_netdev(struct ib_device *ib_dev,
+ unsigned int port);
+
void ib_enum_roce_netdev(struct ib_device *ib_dev,
roce_netdev_filter filter,
void *filter_cookie,
@@ -117,7 +120,7 @@ void ib_cache_cleanup_one(struct ib_device *device);
void ib_cache_release_one(struct ib_device *device);
#ifdef CONFIG_CGROUP_RDMA
-int ib_device_register_rdmacg(struct ib_device *device);
+void ib_device_register_rdmacg(struct ib_device *device);
void ib_device_unregister_rdmacg(struct ib_device *device);
int ib_rdmacg_try_charge(struct ib_rdmacg_object *cg_obj,
@@ -128,21 +131,26 @@ void ib_rdmacg_uncharge(struct ib_rdmacg_object *cg_obj,
struct ib_device *device,
enum rdmacg_resource_type resource_index);
#else
-static inline int ib_device_register_rdmacg(struct ib_device *device)
-{ return 0; }
+static inline void ib_device_register_rdmacg(struct ib_device *device)
+{
+}
static inline void ib_device_unregister_rdmacg(struct ib_device *device)
-{ }
+{
+}
static inline int ib_rdmacg_try_charge(struct ib_rdmacg_object *cg_obj,
struct ib_device *device,
enum rdmacg_resource_type resource_index)
-{ return 0; }
+{
+ return 0;
+}
static inline void ib_rdmacg_uncharge(struct ib_rdmacg_object *cg_obj,
struct ib_device *device,
enum rdmacg_resource_type resource_index)
-{ }
+{
+}
#endif
static inline bool rdma_is_upper_dev_rcu(struct net_device *dev,
@@ -178,7 +186,7 @@ int ib_get_cached_subnet_prefix(struct ib_device *device,
u64 *sn_pfx);
#ifdef CONFIG_SECURITY_INFINIBAND
-void ib_security_destroy_port_pkey_list(struct ib_device *device);
+void ib_security_release_port_pkey_list(struct ib_device *device);
void ib_security_cache_change(struct ib_device *device,
u8 port_num,
@@ -199,8 +207,9 @@ int ib_mad_agent_security_setup(struct ib_mad_agent *agent,
enum ib_qp_type qp_type);
void ib_mad_agent_security_cleanup(struct ib_mad_agent *agent);
int ib_mad_enforce_security(struct ib_mad_agent_private *map, u16 pkey_index);
+void ib_mad_agent_security_change(void);
#else
-static inline void ib_security_destroy_port_pkey_list(struct ib_device *device)
+static inline void ib_security_release_port_pkey_list(struct ib_device *device)
{
}
@@ -264,6 +273,10 @@ static inline int ib_mad_enforce_security(struct ib_mad_agent_private *map,
{
return 0;
}
+
+static inline void ib_mad_agent_security_change(void)
+{
+}
#endif
struct ib_device *ib_device_get_by_index(u32 ifindex);
diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c
index 238ec42778ef..7421ec4883fb 100644
--- a/drivers/infiniband/core/device.c
+++ b/drivers/infiniband/core/device.c
@@ -37,54 +37,111 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/init.h>
-#include <linux/mutex.h>
#include <linux/netdevice.h>
#include <linux/security.h>
#include <linux/notifier.h>
+#include <linux/hashtable.h>
#include <rdma/rdma_netlink.h>
#include <rdma/ib_addr.h>
#include <rdma/ib_cache.h>
#include "core_priv.h"
+#include "restrack.h"
MODULE_AUTHOR("Roland Dreier");
MODULE_DESCRIPTION("core kernel InfiniBand API");
MODULE_LICENSE("Dual BSD/GPL");
-struct ib_client_data {
- struct list_head list;
- struct ib_client *client;
- void * data;
- /* The device or client is going down. Do not call client or device
- * callbacks other than remove(). */
- bool going_down;
-};
-
struct workqueue_struct *ib_comp_wq;
struct workqueue_struct *ib_comp_unbound_wq;
struct workqueue_struct *ib_wq;
EXPORT_SYMBOL_GPL(ib_wq);
-/* The device_list and client_list contain devices and clients after their
- * registration has completed, and the devices and clients are removed
- * during unregistration. */
-static LIST_HEAD(device_list);
-static LIST_HEAD(client_list);
+/*
+ * Each of the three rwsem locks (devices, clients, client_data) protects the
+ * xarray of the same name. Specifically it allows the caller to assert that
+ * the MARK will/will not be changing under the lock, and for devices and
+ * clients, that the value in the xarray is still a valid pointer. Change of
+ * the MARK is linked to the object state, so holding the lock and testing the
+ * MARK also asserts that the contained object is in a certain state.
+ *
+ * This is used to build a two stage register/unregister flow where objects
+ * can continue to be in the xarray even though they are still in progress to
+ * register/unregister.
+ *
+ * The xarray itself provides additional locking, and restartable iteration,
+ * which is also relied on.
+ *
+ * Locks should not be nested, with the exception of client_data, which is
+ * allowed to nest under the read side of the other two locks.
+ *
+ * The devices_rwsem also protects the device name list, any change or
+ * assignment of device name must also hold the write side to guarantee unique
+ * names.
+ */
/*
- * device_mutex and lists_rwsem protect access to both device_list and
- * client_list. device_mutex protects writer access by device and client
- * registration / de-registration. lists_rwsem protects reader access to
- * these lists. Iterators of these lists must lock it for read, while updates
- * to the lists must be done with a write lock. A special case is when the
- * device_mutex is locked. In this case locking the lists for read access is
- * not necessary as the device_mutex implies it.
+ * devices contains devices that have had their names assigned. The
+ * devices may not be registered. Users that care about the registration
+ * status need to call ib_device_try_get() on the device to ensure it is
+ * registered, and keep it registered, for the required duration.
*
- * lists_rwsem also protects access to the client data list.
*/
-static DEFINE_MUTEX(device_mutex);
-static DECLARE_RWSEM(lists_rwsem);
+static DEFINE_XARRAY_FLAGS(devices, XA_FLAGS_ALLOC);
+static DECLARE_RWSEM(devices_rwsem);
+#define DEVICE_REGISTERED XA_MARK_1
+static LIST_HEAD(client_list);
+#define CLIENT_REGISTERED XA_MARK_1
+static DEFINE_XARRAY_FLAGS(clients, XA_FLAGS_ALLOC);
+static DECLARE_RWSEM(clients_rwsem);
+
+/*
+ * If client_data is registered then the corresponding client must also still
+ * be registered.
+ */
+#define CLIENT_DATA_REGISTERED XA_MARK_1
+/*
+ * xarray has this behavior where it won't iterate over NULL values stored in
+ * allocated arrays. So we need our own iterator to see all values stored in
+ * the array. This does the same thing as xa_for_each except that it also
+ * returns NULL valued entries if the array is allocating. Simplified to only
+ * work on simple xarrays.
+ */
+static void *xan_find_marked(struct xarray *xa, unsigned long *indexp,
+ xa_mark_t filter)
+{
+ XA_STATE(xas, xa, *indexp);
+ void *entry;
+
+ rcu_read_lock();
+ do {
+ entry = xas_find_marked(&xas, ULONG_MAX, filter);
+ if (xa_is_zero(entry))
+ break;
+ } while (xas_retry(&xas, entry));
+ rcu_read_unlock();
+
+ if (entry) {
+ *indexp = xas.xa_index;
+ if (xa_is_zero(entry))
+ return NULL;
+ return entry;
+ }
+ return XA_ERROR(-ENOENT);
+}
+#define xan_for_each_marked(xa, index, entry, filter) \
+ for (index = 0, entry = xan_find_marked(xa, &(index), filter); \
+ !xa_is_err(entry); \
+ (index)++, entry = xan_find_marked(xa, &(index), filter))
+
+/* RCU hash table mapping netdevice pointers to struct ib_port_data */
+static DEFINE_SPINLOCK(ndev_hash_lock);
+static DECLARE_HASHTABLE(ndev_hash, 5);
+
+static void free_netdevs(struct ib_device *ib_dev);
+static void ib_unregister_work(struct work_struct *work);
+static void __ib_unregister_device(struct ib_device *device);
static int ib_security_change(struct notifier_block *nb, unsigned long event,
void *lsm_data);
static void ib_policy_change_task(struct work_struct *work);
@@ -94,6 +151,12 @@ static struct notifier_block ibdev_lsm_nb = {
.notifier_call = ib_security_change,
};
+/* Pointer to the RCU head at the start of the ib_port_data array */
+struct ib_port_data_rcu {
+ struct rcu_head rcu_head;
+ struct ib_port_data pdata[];
+};
+
static int ib_device_check_mandatory(struct ib_device *device)
{
#define IB_MANDATORY_FUNC(x) { offsetof(struct ib_device_ops, x), #x }
@@ -121,30 +184,18 @@ static int ib_device_check_mandatory(struct ib_device *device)
};
int i;
+ device->kverbs_provider = true;
for (i = 0; i < ARRAY_SIZE(mandatory_table); ++i) {
if (!*(void **) ((void *) &device->ops +
mandatory_table[i].offset)) {
- dev_warn(&device->dev,
- "Device is missing mandatory function %s\n",
- mandatory_table[i].name);
- return -EINVAL;
+ device->kverbs_provider = false;
+ break;
}
}
return 0;
}
-static struct ib_device *__ib_device_get_by_index(u32 index)
-{
- struct ib_device *device;
-
- list_for_each_entry(device, &device_list, core_list)
- if (device->index == index)
- return device;
-
- return NULL;
-}
-
/*
* Caller must perform ib_device_put() to return the device reference count
* when ib_device_get_by_index() returns valid device pointer.
@@ -153,13 +204,13 @@ struct ib_device *ib_device_get_by_index(u32 index)
{
struct ib_device *device;
- down_read(&lists_rwsem);
- device = __ib_device_get_by_index(index);
+ down_read(&devices_rwsem);
+ device = xa_load(&devices, index);
if (device) {
if (!ib_device_try_get(device))
device = NULL;
}
- up_read(&lists_rwsem);
+ up_read(&devices_rwsem);
return device;
}
@@ -180,28 +231,56 @@ EXPORT_SYMBOL(ib_device_put);
static struct ib_device *__ib_device_get_by_name(const char *name)
{
struct ib_device *device;
+ unsigned long index;
- list_for_each_entry(device, &device_list, core_list)
+ xa_for_each (&devices, index, device)
if (!strcmp(name, dev_name(&device->dev)))
return device;
return NULL;
}
-int ib_device_rename(struct ib_device *ibdev, const char *name)
+/**
+ * ib_device_get_by_name - Find an IB device by name
+ * @name: The name to look for
+ * @driver_id: The driver ID that must match (RDMA_DRIVER_UNKNOWN matches all)
+ *
+ * Find and hold an ib_device by its name. The caller must call
+ * ib_device_put() on the returned pointer.
+ */
+struct ib_device *ib_device_get_by_name(const char *name,
+ enum rdma_driver_id driver_id)
{
struct ib_device *device;
- int ret = 0;
- if (!strcmp(name, dev_name(&ibdev->dev)))
- return ret;
+ down_read(&devices_rwsem);
+ device = __ib_device_get_by_name(name);
+ if (device && driver_id != RDMA_DRIVER_UNKNOWN &&
+ device->driver_id != driver_id)
+ device = NULL;
- mutex_lock(&device_mutex);
- list_for_each_entry(device, &device_list, core_list) {
- if (!strcmp(name, dev_name(&device->dev))) {
- ret = -EEXIST;
- goto out;
- }
+ if (device) {
+ if (!ib_device_try_get(device))
+ device = NULL;
+ }
+ up_read(&devices_rwsem);
+ return device;
+}
+EXPORT_SYMBOL(ib_device_get_by_name);
+
+int ib_device_rename(struct ib_device *ibdev, const char *name)
+{
+ int ret;
+
+ down_write(&devices_rwsem);
+ if (!strcmp(name, dev_name(&ibdev->dev))) {
+ ret = 0;
+ goto out;
+ }
+
+ if (__ib_device_get_by_name(name)) {
+ ret = -EEXIST;
+ goto out;
}
ret = device_rename(&ibdev->dev, name);
@@ -209,53 +288,60 @@ int ib_device_rename(struct ib_device *ibdev, const char *name)
goto out;
strlcpy(ibdev->name, name, IB_DEVICE_NAME_MAX);
out:
- mutex_unlock(&device_mutex);
+ up_write(&devices_rwsem);
return ret;
}
static int alloc_name(struct ib_device *ibdev, const char *name)
{
- unsigned long *inuse;
struct ib_device *device;
+ unsigned long index;
+ struct ida inuse;
+ int rc;
int i;
- inuse = (unsigned long *) get_zeroed_page(GFP_KERNEL);
- if (!inuse)
- return -ENOMEM;
-
- list_for_each_entry(device, &device_list, core_list) {
+ lockdep_assert_held_exclusive(&devices_rwsem);
+ ida_init(&inuse);
+ xa_for_each (&devices, index, device) {
char buf[IB_DEVICE_NAME_MAX];
if (sscanf(dev_name(&device->dev), name, &i) != 1)
continue;
- if (i < 0 || i >= PAGE_SIZE * 8)
+ if (i < 0 || i >= INT_MAX)
continue;
snprintf(buf, sizeof buf, name, i);
- if (!strcmp(buf, dev_name(&device->dev)))
- set_bit(i, inuse);
+ if (strcmp(buf, dev_name(&device->dev)) != 0)
+ continue;
+
+ rc = ida_alloc_range(&inuse, i, i, GFP_KERNEL);
+ if (rc < 0)
+ goto out;
}
- i = find_first_zero_bit(inuse, PAGE_SIZE * 8);
- free_page((unsigned long) inuse);
+ rc = ida_alloc(&inuse, GFP_KERNEL);
+ if (rc < 0)
+ goto out;
- return dev_set_name(&ibdev->dev, name, i);
+ rc = dev_set_name(&ibdev->dev, name, rc);
+out:
+ ida_destroy(&inuse);
+ return rc;
}
static void ib_device_release(struct device *device)
{
struct ib_device *dev = container_of(device, struct ib_device, dev);
- WARN_ON(dev->reg_state == IB_DEV_REGISTERED);
- if (dev->reg_state == IB_DEV_UNREGISTERED) {
- /*
- * In IB_DEV_UNINITIALIZED state, cache or port table
- * is not even created. Free cache and port table only when
- * device reaches UNREGISTERED state.
- */
- ib_cache_release_one(dev);
- kfree(dev->port_immutable);
- }
- kfree(dev);
+ free_netdevs(dev);
+ WARN_ON(refcount_read(&dev->refcount));
+ ib_cache_release_one(dev);
+ ib_security_release_port_pkey_list(dev);
+ xa_destroy(&dev->client_data);
+ if (dev->port_data)
+ kfree_rcu(container_of(dev->port_data, struct ib_port_data_rcu,
+ pdata[0]),
+ rcu_head);
+ kfree_rcu(dev, rcu_head);
}
static int ib_device_uevent(struct device *device,
@@ -278,7 +364,7 @@ static struct class ib_class = {
};
/**
- * ib_alloc_device - allocate an IB device struct
+ * _ib_alloc_device - allocate an IB device struct
* @size:size of structure to allocate
*
* Low-level drivers should use ib_alloc_device() to allocate &struct
@@ -287,7 +373,7 @@ static struct class ib_class = {
* ib_dealloc_device() must be used to free structures allocated with
* ib_alloc_device().
*/
-struct ib_device *ib_alloc_device(size_t size)
+struct ib_device *_ib_alloc_device(size_t size)
{
struct ib_device *device;
@@ -298,23 +384,32 @@ struct ib_device *ib_alloc_device(size_t size)
if (!device)
return NULL;
- rdma_restrack_init(&device->res);
+ if (rdma_restrack_init(device)) {
+ kfree(device);
+ return NULL;
+ }
device->dev.class = &ib_class;
+ device->groups[0] = &ib_dev_attr_group;
+ device->dev.groups = device->groups;
device_initialize(&device->dev);
- dev_set_drvdata(&device->dev, device);
-
INIT_LIST_HEAD(&device->event_handler_list);
spin_lock_init(&device->event_handler_lock);
- rwlock_init(&device->client_data_lock);
- INIT_LIST_HEAD(&device->client_data_list);
+ mutex_init(&device->unregistration_lock);
+ /*
+ * client_data needs to be alloc because we don't want our mark to be
+ * destroyed if the user stores NULL in the client data.
+ */
+ xa_init_flags(&device->client_data, XA_FLAGS_ALLOC);
+ init_rwsem(&device->client_data_rwsem);
INIT_LIST_HEAD(&device->port_list);
init_completion(&device->unreg_completion);
+ INIT_WORK(&device->unregistration_work, ib_unregister_work);
return device;
}
-EXPORT_SYMBOL(ib_alloc_device);
+EXPORT_SYMBOL(_ib_alloc_device);
/**
* ib_dealloc_device - free an IB device struct
@@ -324,32 +419,153 @@ EXPORT_SYMBOL(ib_alloc_device);
*/
void ib_dealloc_device(struct ib_device *device)
{
- WARN_ON(!list_empty(&device->client_data_list));
- WARN_ON(device->reg_state != IB_DEV_UNREGISTERED &&
- device->reg_state != IB_DEV_UNINITIALIZED);
- rdma_restrack_clean(&device->res);
+ if (device->ops.dealloc_driver)
+ device->ops.dealloc_driver(device);
+
+ /*
+ * ib_unregister_driver() requires all devices to remain in the xarray
+ * while their ops are callable. The last op we call is dealloc_driver
+ * above. This is needed to create a fence on op callbacks prior to
+ * allowing the driver module to unload.
+ */
+ down_write(&devices_rwsem);
+ if (xa_load(&devices, device->index) == device)
+ xa_erase(&devices, device->index);
+ up_write(&devices_rwsem);
+
+ /* Expedite releasing netdev references */
+ free_netdevs(device);
+
+ WARN_ON(!xa_empty(&device->client_data));
+ WARN_ON(refcount_read(&device->refcount));
+ rdma_restrack_clean(device);
+ /* Balances with device_initialize */
put_device(&device->dev);
}
EXPORT_SYMBOL(ib_dealloc_device);
-static int add_client_context(struct ib_device *device, struct ib_client *client)
+/*
+ * add_client_context() and remove_client_context() must be safe against
+ * parallel calls on the same device - registration/unregistration of both the
+ * device and client can be occurring in parallel.
+ *
+ * The routines need to be a fence, any caller must not return until the add
+ * or remove is fully completed.
+ */
+static int add_client_context(struct ib_device *device,
+ struct ib_client *client)
{
- struct ib_client_data *context;
+ int ret = 0;
- context = kmalloc(sizeof(*context), GFP_KERNEL);
- if (!context)
- return -ENOMEM;
+ if (!device->kverbs_provider && !client->no_kverbs_req)
+ return 0;
+
+ down_write(&device->client_data_rwsem);
+ /*
+ * Another caller to add_client_context got here first and has already
+ * completely initialized context.
+ */
+ if (xa_get_mark(&device->client_data, client->client_id,
+ CLIENT_DATA_REGISTERED))
+ goto out;
+
+ ret = xa_err(xa_store(&device->client_data, client->client_id, NULL,
+ GFP_KERNEL));
+ if (ret)
+ goto out;
+ downgrade_write(&device->client_data_rwsem);
+ if (client->add)
+ client->add(device);
+
+ /* Readers shall not see a client until add has been completed */
+ xa_set_mark(&device->client_data, client->client_id,
+ CLIENT_DATA_REGISTERED);
+ up_read(&device->client_data_rwsem);
+ return 0;
+
+out:
+ up_write(&device->client_data_rwsem);
+ return ret;
+}
- context->client = client;
- context->data = NULL;
- context->going_down = false;
+static void remove_client_context(struct ib_device *device,
+ unsigned int client_id)
+{
+ struct ib_client *client;
+ void *client_data;
- down_write(&lists_rwsem);
- write_lock_irq(&device->client_data_lock);
- list_add(&context->list, &device->client_data_list);
- write_unlock_irq(&device->client_data_lock);
- up_write(&lists_rwsem);
+ down_write(&device->client_data_rwsem);
+ if (!xa_get_mark(&device->client_data, client_id,
+ CLIENT_DATA_REGISTERED)) {
+ up_write(&device->client_data_rwsem);
+ return;
+ }
+ client_data = xa_load(&device->client_data, client_id);
+ xa_clear_mark(&device->client_data, client_id, CLIENT_DATA_REGISTERED);
+ client = xa_load(&clients, client_id);
+ downgrade_write(&device->client_data_rwsem);
+ /*
+ * Notice we cannot be holding any exclusive locks when calling the
+ * remove callback as the remove callback can recurse back into any
+ * public functions in this module and thus try for any locks those
+ * functions take.
+ *
+ * For this reason clients and drivers should not call the
+ * unregistration functions will holdling any locks.
+ *
+ * It tempting to drop the client_data_rwsem too, but this is required
+ * to ensure that unregister_client does not return until all clients
+ * are completely unregistered, which is required to avoid module
+ * unloading races.
+ */
+ if (client->remove)
+ client->remove(device, client_data);
+
+ xa_erase(&device->client_data, client_id);
+ up_read(&device->client_data_rwsem);
+}
+
+static int alloc_port_data(struct ib_device *device)
+{
+ struct ib_port_data_rcu *pdata_rcu;
+ unsigned int port;
+
+ if (device->port_data)
+ return 0;
+
+ /* This can only be called once the physical port range is defined */
+ if (WARN_ON(!device->phys_port_cnt))
+ return -EINVAL;
+
+ /*
+ * device->port_data is indexed directly by the port number to make
+ * access to this data as efficient as possible.
+ *
+ * Therefore port_data is declared as a 1 based array with potential
+ * empty slots at the beginning.
+ */
+ pdata_rcu = kzalloc(struct_size(pdata_rcu, pdata,
+ rdma_end_port(device) + 1),
+ GFP_KERNEL);
+ if (!pdata_rcu)
+ return -ENOMEM;
+ /*
+ * The rcu_head is put in front of the port data array and the stored
+ * pointer is adjusted since we never need to see that member until
+ * kfree_rcu.
+ */
+ device->port_data = pdata_rcu->pdata;
+
+ rdma_for_each_port (device, port) {
+ struct ib_port_data *pdata = &device->port_data[port];
+
+ pdata->ib_dev = device;
+ spin_lock_init(&pdata->pkey_list_lock);
+ INIT_LIST_HEAD(&pdata->pkey_list);
+ spin_lock_init(&pdata->netdev_lock);
+ INIT_HLIST_NODE(&pdata->ndev_hash_link);
+ }
return 0;
}
@@ -359,29 +575,20 @@ static int verify_immutable(const struct ib_device *dev, u8 port)
rdma_max_mad_size(dev, port) != 0);
}
-static int read_port_immutable(struct ib_device *device)
+static int setup_port_data(struct ib_device *device)
{
+ unsigned int port;
int ret;
- u8 start_port = rdma_start_port(device);
- u8 end_port = rdma_end_port(device);
- u8 port;
- /**
- * device->port_immutable is indexed directly by the port number to make
- * access to this data as efficient as possible.
- *
- * Therefore port_immutable is declared as a 1 based array with
- * potential empty slots at the beginning.
- */
- device->port_immutable = kcalloc(end_port + 1,
- sizeof(*device->port_immutable),
- GFP_KERNEL);
- if (!device->port_immutable)
- return -ENOMEM;
+ ret = alloc_port_data(device);
+ if (ret)
+ return ret;
+
+ rdma_for_each_port (device, port) {
+ struct ib_port_data *pdata = &device->port_data[port];
- for (port = start_port; port <= end_port; ++port) {
- ret = device->ops.get_port_immutable(
- device, port, &device->port_immutable[port]);
+ ret = device->ops.get_port_immutable(device, port,
+ &pdata->immutable);
if (ret)
return ret;
@@ -400,39 +607,16 @@ void ib_get_device_fw_str(struct ib_device *dev, char *str)
}
EXPORT_SYMBOL(ib_get_device_fw_str);
-static int setup_port_pkey_list(struct ib_device *device)
-{
- int i;
-
- /**
- * device->port_pkey_list is indexed directly by the port number,
- * Therefore it is declared as a 1 based array with potential empty
- * slots at the beginning.
- */
- device->port_pkey_list = kcalloc(rdma_end_port(device) + 1,
- sizeof(*device->port_pkey_list),
- GFP_KERNEL);
-
- if (!device->port_pkey_list)
- return -ENOMEM;
-
- for (i = 0; i < (rdma_end_port(device) + 1); i++) {
- spin_lock_init(&device->port_pkey_list[i].list_lock);
- INIT_LIST_HEAD(&device->port_pkey_list[i].pkey_list);
- }
-
- return 0;
-}
-
static void ib_policy_change_task(struct work_struct *work)
{
struct ib_device *dev;
+ unsigned long index;
- down_read(&lists_rwsem);
- list_for_each_entry(dev, &device_list, core_list) {
- int i;
+ down_read(&devices_rwsem);
+ xa_for_each_marked (&devices, index, dev, DEVICE_REGISTERED) {
+ unsigned int i;
- for (i = rdma_start_port(dev); i <= rdma_end_port(dev); i++) {
+ rdma_for_each_port (dev, i) {
u64 sp;
int ret = ib_get_cached_subnet_prefix(dev,
i,
@@ -445,7 +629,7 @@ static void ib_policy_change_task(struct work_struct *work)
ib_security_cache_change(dev, i, sp);
}
}
- up_read(&lists_rwsem);
+ up_read(&devices_rwsem);
}
static int ib_security_change(struct notifier_block *nb, unsigned long event,
@@ -455,32 +639,43 @@ static int ib_security_change(struct notifier_block *nb, unsigned long event,
return NOTIFY_DONE;
schedule_work(&ib_policy_change_work);
+ ib_mad_agent_security_change();
return NOTIFY_OK;
}
-/**
- * __dev_new_index - allocate an device index
- *
- * Returns a suitable unique value for a new device interface
- * number. It assumes that there are less than 2^32-1 ib devices
- * will be present in the system.
+/*
+ * Assign the unique string device name and the unique device index. This is
+ * undone by ib_dealloc_device.
*/
-static u32 __dev_new_index(void)
+static int assign_name(struct ib_device *device, const char *name)
{
- /*
- * The device index to allow stable naming.
- * Similar to struct net -> ifindex.
- */
- static u32 index;
+ static u32 last_id;
+ int ret;
- for (;;) {
- if (!(++index))
- index = 1;
+ down_write(&devices_rwsem);
+ /* Assign a unique name to the device */
+ if (strchr(name, '%'))
+ ret = alloc_name(device, name);
+ else
+ ret = dev_set_name(&device->dev, name);
+ if (ret)
+ goto out;
- if (!__ib_device_get_by_index(index))
- return index;
+ if (__ib_device_get_by_name(dev_name(&device->dev))) {
+ ret = -ENFILE;
+ goto out;
}
+ strlcpy(device->name, dev_name(&device->dev), IB_DEVICE_NAME_MAX);
+
+ ret = xa_alloc_cyclic(&devices, &device->index, device, xa_limit_31b,
+ &last_id, GFP_KERNEL);
+ if (ret > 0)
+ ret = 0;
+
+out:
+ up_write(&devices_rwsem);
+ return ret;
}
static void setup_dma_device(struct ib_device *device)
@@ -518,27 +713,25 @@ static void setup_dma_device(struct ib_device *device)
}
}
-static void cleanup_device(struct ib_device *device)
-{
- ib_cache_cleanup_one(device);
- ib_cache_release_one(device);
- kfree(device->port_pkey_list);
- kfree(device->port_immutable);
-}
-
+/*
+ * setup_device() allocates memory and sets up data that requires calling the
+ * device ops, this is the only reason these actions are not done during
+ * ib_alloc_device. It is undone by ib_dealloc_device().
+ */
static int setup_device(struct ib_device *device)
{
struct ib_udata uhw = {.outlen = 0, .inlen = 0};
int ret;
+ setup_dma_device(device);
+
ret = ib_device_check_mandatory(device);
if (ret)
return ret;
- ret = read_port_immutable(device);
+ ret = setup_port_data(device);
if (ret) {
- dev_warn(&device->dev,
- "Couldn't create per port immutable data\n");
+ dev_warn(&device->dev, "Couldn't create per-port data\n");
return ret;
}
@@ -547,27 +740,76 @@ static int setup_device(struct ib_device *device)
if (ret) {
dev_warn(&device->dev,
"Couldn't query the device attributes\n");
- goto port_cleanup;
+ return ret;
}
- ret = setup_port_pkey_list(device);
- if (ret) {
- dev_warn(&device->dev, "Couldn't create per port_pkey_list\n");
- goto port_cleanup;
+ return 0;
+}
+
+static void disable_device(struct ib_device *device)
+{
+ struct ib_client *client;
+
+ WARN_ON(!refcount_read(&device->refcount));
+
+ down_write(&devices_rwsem);
+ xa_clear_mark(&devices, device->index, DEVICE_REGISTERED);
+ up_write(&devices_rwsem);
+
+ down_read(&clients_rwsem);
+ list_for_each_entry_reverse(client, &client_list, list)
+ remove_client_context(device, client->client_id);
+ up_read(&clients_rwsem);
+
+ /* Pairs with refcount_set in enable_device */
+ ib_device_put(device);
+ wait_for_completion(&device->unreg_completion);
+
+ /* Expedite removing unregistered pointers from the hash table */
+ free_netdevs(device);
+}
+
+/*
+ * An enabled device is visible to all clients and to all the public facing
+ * APIs that return a device pointer. This always returns with a new get, even
+ * if it fails.
+ */
+static int enable_device_and_get(struct ib_device *device)
+{
+ struct ib_client *client;
+ unsigned long index;
+ int ret = 0;
+
+ /*
+ * One ref belongs to the xa and the other belongs to this
+ * thread. This is needed to guard against parallel unregistration.
+ */
+ refcount_set(&device->refcount, 2);
+ down_write(&devices_rwsem);
+ xa_set_mark(&devices, device->index, DEVICE_REGISTERED);
+
+ /*
+ * By using downgrade_write() we ensure that no other thread can clear
+ * DEVICE_REGISTERED while we are completing the client setup.
+ */
+ downgrade_write(&devices_rwsem);
+
+ if (device->ops.enable_driver) {
+ ret = device->ops.enable_driver(device);
+ if (ret)
+ goto out;
}
- ret = ib_cache_setup_one(device);
- if (ret) {
- dev_warn(&device->dev,
- "Couldn't set up InfiniBand P_Key/GID cache\n");
- goto pkey_cleanup;
+ down_read(&clients_rwsem);
+ xa_for_each_marked (&clients, index, client, CLIENT_REGISTERED) {
+ ret = add_client_context(device, client);
+ if (ret)
+ break;
}
- return 0;
+ up_read(&clients_rwsem);
-pkey_cleanup:
- kfree(device->port_pkey_list);
-port_cleanup:
- kfree(device->port_immutable);
+out:
+ up_read(&devices_rwsem);
return ret;
}
@@ -579,133 +821,254 @@ port_cleanup:
* devices with the IB core. All registered clients will receive a
* callback for each device that is added. @device must be allocated
* with ib_alloc_device().
+ *
+ * If the driver uses ops.dealloc_driver and calls any ib_unregister_device()
+ * asynchronously then the device pointer may become freed as soon as this
+ * function returns.
*/
-int ib_register_device(struct ib_device *device, const char *name,
- int (*port_callback)(struct ib_device *, u8,
- struct kobject *))
+int ib_register_device(struct ib_device *device, const char *name)
{
int ret;
- struct ib_client *client;
- setup_dma_device(device);
-
- mutex_lock(&device_mutex);
-
- if (strchr(name, '%')) {
- ret = alloc_name(device, name);
- if (ret)
- goto out;
- } else {
- ret = dev_set_name(&device->dev, name);
- if (ret)
- goto out;
- }
- if (__ib_device_get_by_name(dev_name(&device->dev))) {
- ret = -ENFILE;
- goto out;
- }
- strlcpy(device->name, dev_name(&device->dev), IB_DEVICE_NAME_MAX);
+ ret = assign_name(device, name);
+ if (ret)
+ return ret;
ret = setup_device(device);
if (ret)
- goto out;
-
- device->index = __dev_new_index();
+ return ret;
- ret = ib_device_register_rdmacg(device);
+ ret = ib_cache_setup_one(device);
if (ret) {
dev_warn(&device->dev,
- "Couldn't register device with rdma cgroup\n");
- goto dev_cleanup;
+ "Couldn't set up InfiniBand P_Key/GID cache\n");
+ return ret;
}
- ret = ib_device_register_sysfs(device, port_callback);
+ ib_device_register_rdmacg(device);
+
+ ret = device_add(&device->dev);
+ if (ret)
+ goto cg_cleanup;
+
+ ret = ib_device_register_sysfs(device);
if (ret) {
dev_warn(&device->dev,
"Couldn't register device with driver model\n");
- goto cg_cleanup;
+ goto dev_cleanup;
}
- refcount_set(&device->refcount, 1);
- device->reg_state = IB_DEV_REGISTERED;
+ ret = enable_device_and_get(device);
+ if (ret) {
+ void (*dealloc_fn)(struct ib_device *);
- list_for_each_entry(client, &client_list, list)
- if (!add_client_context(device, client) && client->add)
- client->add(device);
+ /*
+ * If we hit this error flow then we don't want to
+ * automatically dealloc the device since the caller is
+ * expected to call ib_dealloc_device() after
+ * ib_register_device() fails. This is tricky due to the
+ * possibility for a parallel unregistration along with this
+ * error flow. Since we have a refcount here we know any
+ * parallel flow is stopped in disable_device and will see the
+ * NULL pointers, causing the responsibility to
+ * ib_dealloc_device() to revert back to this thread.
+ */
+ dealloc_fn = device->ops.dealloc_driver;
+ device->ops.dealloc_driver = NULL;
+ ib_device_put(device);
+ __ib_unregister_device(device);
+ device->ops.dealloc_driver = dealloc_fn;
+ return ret;
+ }
+ ib_device_put(device);
- down_write(&lists_rwsem);
- list_add_tail(&device->core_list, &device_list);
- up_write(&lists_rwsem);
- mutex_unlock(&device_mutex);
return 0;
+dev_cleanup:
+ device_del(&device->dev);
cg_cleanup:
ib_device_unregister_rdmacg(device);
-dev_cleanup:
- cleanup_device(device);
-out:
- mutex_unlock(&device_mutex);
+ ib_cache_cleanup_one(device);
return ret;
}
EXPORT_SYMBOL(ib_register_device);
+/* Callers must hold a get on the device. */
+static void __ib_unregister_device(struct ib_device *ib_dev)
+{
+ /*
+ * We have a registration lock so that all the calls to unregister are
+ * fully fenced, once any unregister returns the device is truely
+ * unregistered even if multiple callers are unregistering it at the
+ * same time. This also interacts with the registration flow and
+ * provides sane semantics if register and unregister are racing.
+ */
+ mutex_lock(&ib_dev->unregistration_lock);
+ if (!refcount_read(&ib_dev->refcount))
+ goto out;
+
+ disable_device(ib_dev);
+ ib_device_unregister_sysfs(ib_dev);
+ device_del(&ib_dev->dev);
+ ib_device_unregister_rdmacg(ib_dev);
+ ib_cache_cleanup_one(ib_dev);
+
+ /*
+ * Drivers using the new flow may not call ib_dealloc_device except
+ * in error unwind prior to registration success.
+ */
+ if (ib_dev->ops.dealloc_driver) {
+ WARN_ON(kref_read(&ib_dev->dev.kobj.kref) <= 1);
+ ib_dealloc_device(ib_dev);
+ }
+out:
+ mutex_unlock(&ib_dev->unregistration_lock);
+}
+
/**
* ib_unregister_device - Unregister an IB device
- * @device:Device to unregister
+ * @device: The device to unregister
*
* Unregister an IB device. All clients will receive a remove callback.
+ *
+ * Callers should call this routine only once, and protect against races with
+ * registration. Typically it should only be called as part of a remove
+ * callback in an implementation of driver core's struct device_driver and
+ * related.
+ *
+ * If ops.dealloc_driver is used then ib_dev will be freed upon return from
+ * this function.
*/
-void ib_unregister_device(struct ib_device *device)
+void ib_unregister_device(struct ib_device *ib_dev)
{
- struct ib_client_data *context, *tmp;
- unsigned long flags;
+ get_device(&ib_dev->dev);
+ __ib_unregister_device(ib_dev);
+ put_device(&ib_dev->dev);
+}
+EXPORT_SYMBOL(ib_unregister_device);
- /*
- * Wait for all netlink command callers to finish working on the
- * device.
- */
- ib_device_put(device);
- wait_for_completion(&device->unreg_completion);
+/**
+ * ib_unregister_device_and_put - Unregister a device while holding a 'get'
+ * device: The device to unregister
+ *
+ * This is the same as ib_unregister_device(), except it includes an internal
+ * ib_device_put() that should match a 'get' obtained by the caller.
+ *
+ * It is safe to call this routine concurrently from multiple threads while
+ * holding the 'get'. When the function returns the device is fully
+ * unregistered.
+ *
+ * Drivers using this flow MUST use the driver_unregister callback to clean up
+ * their resources associated with the device and dealloc it.
+ */
+void ib_unregister_device_and_put(struct ib_device *ib_dev)
+{
+ WARN_ON(!ib_dev->ops.dealloc_driver);
+ get_device(&ib_dev->dev);
+ ib_device_put(ib_dev);
+ __ib_unregister_device(ib_dev);
+ put_device(&ib_dev->dev);
+}
+EXPORT_SYMBOL(ib_unregister_device_and_put);
- mutex_lock(&device_mutex);
+/**
+ * ib_unregister_driver - Unregister all IB devices for a driver
+ * @driver_id: The driver to unregister
+ *
+ * This implements a fence for device unregistration. It only returns once all
+ * devices associated with the driver_id have fully completed their
+ * unregistration and returned from ib_unregister_device*().
+ *
+ * If device's are not yet unregistered it goes ahead and starts unregistering
+ * them.
+ *
+ * This does not block creation of new devices with the given driver_id, that
+ * is the responsibility of the caller.
+ */
+void ib_unregister_driver(enum rdma_driver_id driver_id)
+{
+ struct ib_device *ib_dev;
+ unsigned long index;
+
+ down_read(&devices_rwsem);
+ xa_for_each (&devices, index, ib_dev) {
+ if (ib_dev->driver_id != driver_id)
+ continue;
+
+ get_device(&ib_dev->dev);
+ up_read(&devices_rwsem);
- down_write(&lists_rwsem);
- list_del(&device->core_list);
- write_lock_irq(&device->client_data_lock);
- list_for_each_entry(context, &device->client_data_list, list)
- context->going_down = true;
- write_unlock_irq(&device->client_data_lock);
- downgrade_write(&lists_rwsem);
+ WARN_ON(!ib_dev->ops.dealloc_driver);
+ __ib_unregister_device(ib_dev);
- list_for_each_entry(context, &device->client_data_list, list) {
- if (context->client->remove)
- context->client->remove(device, context->data);
+ put_device(&ib_dev->dev);
+ down_read(&devices_rwsem);
}
- up_read(&lists_rwsem);
+ up_read(&devices_rwsem);
+}
+EXPORT_SYMBOL(ib_unregister_driver);
- ib_device_unregister_sysfs(device);
- ib_device_unregister_rdmacg(device);
+static void ib_unregister_work(struct work_struct *work)
+{
+ struct ib_device *ib_dev =
+ container_of(work, struct ib_device, unregistration_work);
- mutex_unlock(&device_mutex);
+ __ib_unregister_device(ib_dev);
+ put_device(&ib_dev->dev);
+}
- ib_cache_cleanup_one(device);
+/**
+ * ib_unregister_device_queued - Unregister a device using a work queue
+ * device: The device to unregister
+ *
+ * This schedules an asynchronous unregistration using a WQ for the device. A
+ * driver should use this to avoid holding locks while doing unregistration,
+ * such as holding the RTNL lock.
+ *
+ * Drivers using this API must use ib_unregister_driver before module unload
+ * to ensure that all scheduled unregistrations have completed.
+ */
+void ib_unregister_device_queued(struct ib_device *ib_dev)
+{
+ WARN_ON(!refcount_read(&ib_dev->refcount));
+ WARN_ON(!ib_dev->ops.dealloc_driver);
+ get_device(&ib_dev->dev);
+ if (!queue_work(system_unbound_wq, &ib_dev->unregistration_work))
+ put_device(&ib_dev->dev);
+}
+EXPORT_SYMBOL(ib_unregister_device_queued);
- ib_security_destroy_port_pkey_list(device);
- kfree(device->port_pkey_list);
+static int assign_client_id(struct ib_client *client)
+{
+ int ret;
- down_write(&lists_rwsem);
- write_lock_irqsave(&device->client_data_lock, flags);
- list_for_each_entry_safe(context, tmp, &device->client_data_list,
- list) {
- list_del(&context->list);
- kfree(context);
+ down_write(&clients_rwsem);
+ /*
+ * The add/remove callbacks must be called in FIFO/LIFO order. To
+ * achieve this we assign client_ids so they are sorted in
+ * registration order, and retain a linked list we can reverse iterate
+ * to get the LIFO order. The extra linked list can go away if xarray
+ * learns to reverse iterate.
+ */
+ if (list_empty(&client_list)) {
+ client->client_id = 0;
+ } else {
+ struct ib_client *last;
+
+ last = list_last_entry(&client_list, struct ib_client, list);
+ client->client_id = last->client_id + 1;
}
- write_unlock_irqrestore(&device->client_data_lock, flags);
- up_write(&lists_rwsem);
+ ret = xa_insert(&clients, client->client_id, client, GFP_KERNEL);
+ if (ret)
+ goto out;
- device->reg_state = IB_DEV_UNREGISTERED;
+ xa_set_mark(&clients, client->client_id, CLIENT_REGISTERED);
+ list_add_tail(&client->list, &client_list);
+
+out:
+ up_write(&clients_rwsem);
+ return ret;
}
-EXPORT_SYMBOL(ib_unregister_device);
/**
* ib_register_client - Register an IB client
@@ -723,19 +1086,23 @@ EXPORT_SYMBOL(ib_unregister_device);
int ib_register_client(struct ib_client *client)
{
struct ib_device *device;
+ unsigned long index;
+ int ret;
- mutex_lock(&device_mutex);
-
- list_for_each_entry(device, &device_list, core_list)
- if (!add_client_context(device, client) && client->add)
- client->add(device);
-
- down_write(&lists_rwsem);
- list_add_tail(&client->list, &client_list);
- up_write(&lists_rwsem);
-
- mutex_unlock(&device_mutex);
+ ret = assign_client_id(client);
+ if (ret)
+ return ret;
+ down_read(&devices_rwsem);
+ xa_for_each_marked (&devices, index, device, DEVICE_REGISTERED) {
+ ret = add_client_context(device, client);
+ if (ret) {
+ up_read(&devices_rwsem);
+ ib_unregister_client(client);
+ return ret;
+ }
+ }
+ up_read(&devices_rwsem);
return 0;
}
EXPORT_SYMBOL(ib_register_client);
@@ -747,108 +1114,56 @@ EXPORT_SYMBOL(ib_register_client);
* Upper level users use ib_unregister_client() to remove their client
* registration. When ib_unregister_client() is called, the client
* will receive a remove callback for each IB device still registered.
+ *
+ * This is a full fence, once it returns no client callbacks will be called,
+ * or are running in another thread.
*/
void ib_unregister_client(struct ib_client *client)
{
- struct ib_client_data *context;
struct ib_device *device;
+ unsigned long index;
- mutex_lock(&device_mutex);
+ down_write(&clients_rwsem);
+ xa_clear_mark(&clients, client->client_id, CLIENT_REGISTERED);
+ up_write(&clients_rwsem);
+ /*
+ * Every device still known must be serialized to make sure we are
+ * done with the client callbacks before we return.
+ */
+ down_read(&devices_rwsem);
+ xa_for_each (&devices, index, device)
+ remove_client_context(device, client->client_id);
+ up_read(&devices_rwsem);
- down_write(&lists_rwsem);
+ down_write(&clients_rwsem);
list_del(&client->list);
- up_write(&lists_rwsem);
-
- list_for_each_entry(device, &device_list, core_list) {
- struct ib_client_data *found_context = NULL;
-
- down_write(&lists_rwsem);
- write_lock_irq(&device->client_data_lock);
- list_for_each_entry(context, &device->client_data_list, list)
- if (context->client == client) {
- context->going_down = true;
- found_context = context;
- break;
- }
- write_unlock_irq(&device->client_data_lock);
- up_write(&lists_rwsem);
-
- if (client->remove)
- client->remove(device, found_context ?
- found_context->data : NULL);
-
- if (!found_context) {
- dev_warn(&device->dev,
- "No client context found for %s\n",
- client->name);
- continue;
- }
-
- down_write(&lists_rwsem);
- write_lock_irq(&device->client_data_lock);
- list_del(&found_context->list);
- write_unlock_irq(&device->client_data_lock);
- up_write(&lists_rwsem);
- kfree(found_context);
- }
-
- mutex_unlock(&device_mutex);
+ xa_erase(&clients, client->client_id);
+ up_write(&clients_rwsem);
}
EXPORT_SYMBOL(ib_unregister_client);
/**
- * ib_get_client_data - Get IB client context
- * @device:Device to get context for
- * @client:Client to get context for
- *
- * ib_get_client_data() returns client context set with
- * ib_set_client_data().
- */
-void *ib_get_client_data(struct ib_device *device, struct ib_client *client)
-{
- struct ib_client_data *context;
- void *ret = NULL;
- unsigned long flags;
-
- read_lock_irqsave(&device->client_data_lock, flags);
- list_for_each_entry(context, &device->client_data_list, list)
- if (context->client == client) {
- ret = context->data;
- break;
- }
- read_unlock_irqrestore(&device->client_data_lock, flags);
-
- return ret;
-}
-EXPORT_SYMBOL(ib_get_client_data);
-
-/**
* ib_set_client_data - Set IB client context
* @device:Device to set context for
* @client:Client to set context for
* @data:Context to set
*
- * ib_set_client_data() sets client context that can be retrieved with
- * ib_get_client_data().
+ * ib_set_client_data() sets client context data that can be retrieved with
+ * ib_get_client_data(). This can only be called while the client is
+ * registered to the device, once the ib_client remove() callback returns this
+ * cannot be called.
*/
void ib_set_client_data(struct ib_device *device, struct ib_client *client,
void *data)
{
- struct ib_client_data *context;
- unsigned long flags;
-
- write_lock_irqsave(&device->client_data_lock, flags);
- list_for_each_entry(context, &device->client_data_list, list)
- if (context->client == client) {
- context->data = data;
- goto out;
- }
+ void *rc;
- dev_warn(&device->dev, "No client context found for %s\n",
- client->name);
+ if (WARN_ON(IS_ERR(data)))
+ data = NULL;
-out:
- write_unlock_irqrestore(&device->client_data_lock, flags);
+ rc = xa_store(&device->client_data, client->client_id, data,
+ GFP_KERNEL);
+ WARN_ON(xa_is_err(rc));
}
EXPORT_SYMBOL(ib_set_client_data);
@@ -947,6 +1262,185 @@ int ib_query_port(struct ib_device *device,
}
EXPORT_SYMBOL(ib_query_port);
+static void add_ndev_hash(struct ib_port_data *pdata)
+{
+ unsigned long flags;
+
+ might_sleep();
+
+ spin_lock_irqsave(&ndev_hash_lock, flags);
+ if (hash_hashed(&pdata->ndev_hash_link)) {
+ hash_del_rcu(&pdata->ndev_hash_link);
+ spin_unlock_irqrestore(&ndev_hash_lock, flags);
+ /*
+ * We cannot do hash_add_rcu after a hash_del_rcu until the
+ * grace period
+ */
+ synchronize_rcu();
+ spin_lock_irqsave(&ndev_hash_lock, flags);
+ }
+ if (pdata->netdev)
+ hash_add_rcu(ndev_hash, &pdata->ndev_hash_link,
+ (uintptr_t)pdata->netdev);
+ spin_unlock_irqrestore(&ndev_hash_lock, flags);
+}
+
+/**
+ * ib_device_set_netdev - Associate the ib_dev with an underlying net_device
+ * @ib_dev: Device to modify
+ * @ndev: net_device to affiliate, may be NULL
+ * @port: IB port the net_device is connected to
+ *
+ * Drivers should use this to link the ib_device to a netdev so the netdev
+ * shows up in interfaces like ib_enum_roce_netdev. Only one netdev may be
+ * affiliated with any port.
+ *
+ * The caller must ensure that the given ndev is not unregistered or
+ * unregistering, and that either the ib_device is unregistered or
+ * ib_device_set_netdev() is called with NULL when the ndev sends a
+ * NETDEV_UNREGISTER event.
+ */
+int ib_device_set_netdev(struct ib_device *ib_dev, struct net_device *ndev,
+ unsigned int port)
+{
+ struct net_device *old_ndev;
+ struct ib_port_data *pdata;
+ unsigned long flags;
+ int ret;
+
+ /*
+ * Drivers wish to call this before ib_register_driver, so we have to
+ * setup the port data early.
+ */
+ ret = alloc_port_data(ib_dev);
+ if (ret)
+ return ret;
+
+ if (!rdma_is_port_valid(ib_dev, port))
+ return -EINVAL;
+
+ pdata = &ib_dev->port_data[port];
+ spin_lock_irqsave(&pdata->netdev_lock, flags);
+ old_ndev = rcu_dereference_protected(
+ pdata->netdev, lockdep_is_held(&pdata->netdev_lock));
+ if (old_ndev == ndev) {
+ spin_unlock_irqrestore(&pdata->netdev_lock, flags);
+ return 0;
+ }
+
+ if (ndev)
+ dev_hold(ndev);
+ rcu_assign_pointer(pdata->netdev, ndev);
+ spin_unlock_irqrestore(&pdata->netdev_lock, flags);
+
+ add_ndev_hash(pdata);
+ if (old_ndev)
+ dev_put(old_ndev);
+
+ return 0;
+}
+EXPORT_SYMBOL(ib_device_set_netdev);
+
+static void free_netdevs(struct ib_device *ib_dev)
+{
+ unsigned long flags;
+ unsigned int port;
+
+ rdma_for_each_port (ib_dev, port) {
+ struct ib_port_data *pdata = &ib_dev->port_data[port];
+ struct net_device *ndev;
+
+ spin_lock_irqsave(&pdata->netdev_lock, flags);
+ ndev = rcu_dereference_protected(
+ pdata->netdev, lockdep_is_held(&pdata->netdev_lock));
+ if (ndev) {
+ spin_lock(&ndev_hash_lock);
+ hash_del_rcu(&pdata->ndev_hash_link);
+ spin_unlock(&ndev_hash_lock);
+
+ /*
+ * If this is the last dev_put there is still a
+ * synchronize_rcu before the netdev is kfreed, so we
+ * can continue to rely on unlocked pointer
+ * comparisons after the put
+ */
+ rcu_assign_pointer(pdata->netdev, NULL);
+ dev_put(ndev);
+ }
+ spin_unlock_irqrestore(&pdata->netdev_lock, flags);
+ }
+}
+
+struct net_device *ib_device_get_netdev(struct ib_device *ib_dev,
+ unsigned int port)
+{
+ struct ib_port_data *pdata;
+ struct net_device *res;
+
+ if (!rdma_is_port_valid(ib_dev, port))
+ return NULL;
+
+ pdata = &ib_dev->port_data[port];
+
+ /*
+ * New drivers should use ib_device_set_netdev() not the legacy
+ * get_netdev().
+ */
+ if (ib_dev->ops.get_netdev)
+ res = ib_dev->ops.get_netdev(ib_dev, port);
+ else {
+ spin_lock(&pdata->netdev_lock);
+ res = rcu_dereference_protected(
+ pdata->netdev, lockdep_is_held(&pdata->netdev_lock));
+ if (res)
+ dev_hold(res);
+ spin_unlock(&pdata->netdev_lock);
+ }
+
+ /*
+ * If we are starting to unregister expedite things by preventing
+ * propagation of an unregistering netdev.
+ */
+ if (res && res->reg_state != NETREG_REGISTERED) {
+ dev_put(res);
+ return NULL;
+ }
+
+ return res;
+}
+
+/**
+ * ib_device_get_by_netdev - Find an IB device associated with a netdev
+ * @ndev: netdev to locate
+ * @driver_id: The driver ID that must match (RDMA_DRIVER_UNKNOWN matches all)
+ *
+ * Find and hold an ib_device that is associated with a netdev via
+ * ib_device_set_netdev(). The caller must call ib_device_put() on the
+ * returned pointer.
+ */
+struct ib_device *ib_device_get_by_netdev(struct net_device *ndev,
+ enum rdma_driver_id driver_id)
+{
+ struct ib_device *res = NULL;
+ struct ib_port_data *cur;
+
+ rcu_read_lock();
+ hash_for_each_possible_rcu (ndev_hash, cur, ndev_hash_link,
+ (uintptr_t)ndev) {
+ if (rcu_access_pointer(cur->netdev) == ndev &&
+ (driver_id == RDMA_DRIVER_UNKNOWN ||
+ cur->ib_dev->driver_id == driver_id) &&
+ ib_device_try_get(cur->ib_dev)) {
+ res = cur->ib_dev;
+ break;
+ }
+ }
+ rcu_read_unlock();
+
+ return res;
+}
+EXPORT_SYMBOL(ib_device_get_by_netdev);
+
/**
* ib_enum_roce_netdev - enumerate all RoCE ports
* @ib_dev : IB device we want to query
@@ -965,21 +1459,12 @@ void ib_enum_roce_netdev(struct ib_device *ib_dev,
roce_netdev_callback cb,
void *cookie)
{
- u8 port;
+ unsigned int port;
- for (port = rdma_start_port(ib_dev); port <= rdma_end_port(ib_dev);
- port++)
+ rdma_for_each_port (ib_dev, port)
if (rdma_protocol_roce(ib_dev, port)) {
- struct net_device *idev = NULL;
-
- if (ib_dev->ops.get_netdev)
- idev = ib_dev->ops.get_netdev(ib_dev, port);
-
- if (idev &&
- idev->reg_state >= NETREG_UNREGISTERED) {
- dev_put(idev);
- idev = NULL;
- }
+ struct net_device *idev =
+ ib_device_get_netdev(ib_dev, port);
if (filter(ib_dev, port, idev, filter_cookie))
cb(ib_dev, port, idev, cookie);
@@ -1006,11 +1491,12 @@ void ib_enum_all_roce_netdevs(roce_netdev_filter filter,
void *cookie)
{
struct ib_device *dev;
+ unsigned long index;
- down_read(&lists_rwsem);
- list_for_each_entry(dev, &device_list, core_list)
+ down_read(&devices_rwsem);
+ xa_for_each_marked (&devices, index, dev, DEVICE_REGISTERED)
ib_enum_roce_netdev(dev, filter, filter_cookie, cb, cookie);
- up_read(&lists_rwsem);
+ up_read(&devices_rwsem);
}
/**
@@ -1022,19 +1508,19 @@ void ib_enum_all_roce_netdevs(roce_netdev_filter filter,
int ib_enum_all_devs(nldev_callback nldev_cb, struct sk_buff *skb,
struct netlink_callback *cb)
{
+ unsigned long index;
struct ib_device *dev;
unsigned int idx = 0;
int ret = 0;
- down_read(&lists_rwsem);
- list_for_each_entry(dev, &device_list, core_list) {
+ down_read(&devices_rwsem);
+ xa_for_each_marked (&devices, index, dev, DEVICE_REGISTERED) {
ret = nldev_cb(dev, skb, cb, idx);
if (ret)
break;
idx++;
}
-
- up_read(&lists_rwsem);
+ up_read(&devices_rwsem);
return ret;
}
@@ -1121,13 +1607,15 @@ int ib_find_gid(struct ib_device *device, union ib_gid *gid,
u8 *port_num, u16 *index)
{
union ib_gid tmp_gid;
- int ret, port, i;
+ unsigned int port;
+ int ret, i;
- for (port = rdma_start_port(device); port <= rdma_end_port(device); ++port) {
+ rdma_for_each_port (device, port) {
if (!rdma_protocol_ib(device, port))
continue;
- for (i = 0; i < device->port_immutable[port].gid_tbl_len; ++i) {
+ for (i = 0; i < device->port_data[port].immutable.gid_tbl_len;
+ ++i) {
ret = rdma_query_gid(device, port, i, &tmp_gid);
if (ret)
return ret;
@@ -1159,7 +1647,8 @@ int ib_find_pkey(struct ib_device *device,
u16 tmp_pkey;
int partial_ix = -1;
- for (i = 0; i < device->port_immutable[port_num].pkey_tbl_len; ++i) {
+ for (i = 0; i < device->port_data[port_num].immutable.pkey_tbl_len;
+ ++i) {
ret = ib_query_pkey(device, port_num, i, &tmp_pkey);
if (ret)
return ret;
@@ -1192,6 +1681,7 @@ EXPORT_SYMBOL(ib_find_pkey);
* @gid: A GID that the net_dev uses to communicate.
* @addr: Contains the IP address that the request specified as its
* destination.
+ *
*/
struct net_device *ib_get_net_dev_by_params(struct ib_device *dev,
u8 port,
@@ -1200,29 +1690,30 @@ struct net_device *ib_get_net_dev_by_params(struct ib_device *dev,
const struct sockaddr *addr)
{
struct net_device *net_dev = NULL;
- struct ib_client_data *context;
+ unsigned long index;
+ void *client_data;
if (!rdma_protocol_ib(dev, port))
return NULL;
- down_read(&lists_rwsem);
-
- list_for_each_entry(context, &dev->client_data_list, list) {
- struct ib_client *client = context->client;
+ /*
+ * Holding the read side guarantees that the client will not become
+ * unregistered while we are calling get_net_dev_by_params()
+ */
+ down_read(&dev->client_data_rwsem);
+ xan_for_each_marked (&dev->client_data, index, client_data,
+ CLIENT_DATA_REGISTERED) {
+ struct ib_client *client = xa_load(&clients, index);
- if (context->going_down)
+ if (!client || !client->get_net_dev_by_params)
continue;
- if (client->get_net_dev_by_params) {
- net_dev = client->get_net_dev_by_params(dev, port, pkey,
- gid, addr,
- context->data);
- if (net_dev)
- break;
- }
+ net_dev = client->get_net_dev_by_params(dev, port, pkey, gid,
+ addr, client_data);
+ if (net_dev)
+ break;
}
-
- up_read(&lists_rwsem);
+ up_read(&dev->client_data_rwsem);
return net_dev;
}
@@ -1238,6 +1729,8 @@ void ib_set_device_ops(struct ib_device *dev, const struct ib_device_ops *ops)
(ptr)->name = ops->name; \
} while (0)
+#define SET_OBJ_SIZE(ptr, name) SET_DEVICE_OP(ptr, size_##name)
+
SET_DEVICE_OP(dev_ops, add_gid);
SET_DEVICE_OP(dev_ops, advise_mr);
SET_DEVICE_OP(dev_ops, alloc_dm);
@@ -1261,6 +1754,7 @@ void ib_set_device_ops(struct ib_device *dev, const struct ib_device_ops *ops)
SET_DEVICE_OP(dev_ops, create_srq);
SET_DEVICE_OP(dev_ops, create_wq);
SET_DEVICE_OP(dev_ops, dealloc_dm);
+ SET_DEVICE_OP(dev_ops, dealloc_driver);
SET_DEVICE_OP(dev_ops, dealloc_fmr);
SET_DEVICE_OP(dev_ops, dealloc_mw);
SET_DEVICE_OP(dev_ops, dealloc_pd);
@@ -1281,6 +1775,8 @@ void ib_set_device_ops(struct ib_device *dev, const struct ib_device_ops *ops)
SET_DEVICE_OP(dev_ops, disassociate_ucontext);
SET_DEVICE_OP(dev_ops, drain_rq);
SET_DEVICE_OP(dev_ops, drain_sq);
+ SET_DEVICE_OP(dev_ops, enable_driver);
+ SET_DEVICE_OP(dev_ops, fill_res_entry);
SET_DEVICE_OP(dev_ops, get_dev_fw_str);
SET_DEVICE_OP(dev_ops, get_dma_mr);
SET_DEVICE_OP(dev_ops, get_hw_stats);
@@ -1290,6 +1786,7 @@ void ib_set_device_ops(struct ib_device *dev, const struct ib_device_ops *ops)
SET_DEVICE_OP(dev_ops, get_vector_affinity);
SET_DEVICE_OP(dev_ops, get_vf_config);
SET_DEVICE_OP(dev_ops, get_vf_stats);
+ SET_DEVICE_OP(dev_ops, init_port);
SET_DEVICE_OP(dev_ops, map_mr_sg);
SET_DEVICE_OP(dev_ops, map_phys_fmr);
SET_DEVICE_OP(dev_ops, mmap);
@@ -1325,6 +1822,9 @@ void ib_set_device_ops(struct ib_device *dev, const struct ib_device_ops *ops)
SET_DEVICE_OP(dev_ops, set_vf_guid);
SET_DEVICE_OP(dev_ops, set_vf_link_state);
SET_DEVICE_OP(dev_ops, unmap_fmr);
+
+ SET_OBJ_SIZE(dev_ops, ib_pd);
+ SET_OBJ_SIZE(dev_ops, ib_ucontext);
}
EXPORT_SYMBOL(ib_set_device_ops);
@@ -1443,6 +1943,9 @@ static void __exit ib_core_cleanup(void)
destroy_workqueue(ib_comp_wq);
/* Make sure that any pending umem accounting work is done. */
destroy_workqueue(ib_wq);
+ flush_workqueue(system_unbound_wq);
+ WARN_ON(!xa_empty(&clients));
+ WARN_ON(!xa_empty(&devices));
}
MODULE_ALIAS_RDMA_NETLINK(RDMA_NL_LS, 4);
diff --git a/drivers/infiniband/core/iwcm.c b/drivers/infiniband/core/iwcm.c
index 476abc74178e..732637c913d9 100644
--- a/drivers/infiniband/core/iwcm.c
+++ b/drivers/infiniband/core/iwcm.c
@@ -87,7 +87,8 @@ static struct rdma_nl_cbs iwcm_nl_cb_table[RDMA_NL_IWPM_NUM_OPS] = {
[RDMA_NL_IWPM_REMOTE_INFO] = {.dump = iwpm_remote_info_cb},
[RDMA_NL_IWPM_HANDLE_ERR] = {.dump = iwpm_mapping_error_cb},
[RDMA_NL_IWPM_MAPINFO] = {.dump = iwpm_mapping_info_cb},
- [RDMA_NL_IWPM_MAPINFO_NUM] = {.dump = iwpm_ack_mapping_info_cb}
+ [RDMA_NL_IWPM_MAPINFO_NUM] = {.dump = iwpm_ack_mapping_info_cb},
+ [RDMA_NL_IWPM_HELLO] = {.dump = iwpm_hello_cb}
};
static struct workqueue_struct *iwcm_wq;
@@ -504,7 +505,7 @@ static int iw_cm_map(struct iw_cm_id *cm_id, bool active)
{
const char *devname = dev_name(&cm_id->device->dev);
const char *ifname = cm_id->device->iwcm->ifname;
- struct iwpm_dev_data pm_reg_msg;
+ struct iwpm_dev_data pm_reg_msg = {};
struct iwpm_sa_data pm_msg;
int status;
@@ -515,8 +516,8 @@ static int iw_cm_map(struct iw_cm_id *cm_id, bool active)
cm_id->m_local_addr = cm_id->local_addr;
cm_id->m_remote_addr = cm_id->remote_addr;
- strncpy(pm_reg_msg.dev_name, devname, sizeof(pm_reg_msg.dev_name));
- strncpy(pm_reg_msg.if_name, ifname, sizeof(pm_reg_msg.if_name));
+ strcpy(pm_reg_msg.dev_name, devname);
+ strcpy(pm_reg_msg.if_name, ifname);
if (iwpm_register_pid(&pm_reg_msg, RDMA_NL_IWCM) ||
!iwpm_valid_pid())
@@ -525,6 +526,8 @@ static int iw_cm_map(struct iw_cm_id *cm_id, bool active)
cm_id->mapped = true;
pm_msg.loc_addr = cm_id->local_addr;
pm_msg.rem_addr = cm_id->remote_addr;
+ pm_msg.flags = (cm_id->device->iwcm->driver_flags & IW_F_NO_PORT_MAP) ?
+ IWPM_FLAGS_NO_PORT_MAP : 0;
if (active)
status = iwpm_add_and_query_mapping(&pm_msg,
RDMA_NL_IWCM);
@@ -543,7 +546,7 @@ static int iw_cm_map(struct iw_cm_id *cm_id, bool active)
return iwpm_create_mapinfo(&cm_id->local_addr,
&cm_id->m_local_addr,
- RDMA_NL_IWCM);
+ RDMA_NL_IWCM, pm_msg.flags);
}
/*
diff --git a/drivers/infiniband/core/iwpm_msg.c b/drivers/infiniband/core/iwpm_msg.c
index 8861c052155a..2452b0ddcf0d 100644
--- a/drivers/infiniband/core/iwpm_msg.c
+++ b/drivers/infiniband/core/iwpm_msg.c
@@ -34,18 +34,25 @@
#include "iwpm_util.h"
static const char iwpm_ulib_name[IWPM_ULIBNAME_SIZE] = "iWarpPortMapperUser";
-static int iwpm_ulib_version = 3;
+u16 iwpm_ulib_version = IWPM_UABI_VERSION_MIN;
static int iwpm_user_pid = IWPM_PID_UNDEFINED;
static atomic_t echo_nlmsg_seq;
+/**
+ * iwpm_valid_pid - Check if the userspace iwarp port mapper pid is valid
+ *
+ * Returns true if the pid is greater than zero, otherwise returns false
+ */
int iwpm_valid_pid(void)
{
return iwpm_user_pid > 0;
}
-/*
- * iwpm_register_pid - Send a netlink query to user space
- * for the iwarp port mapper pid
+/**
+ * iwpm_register_pid - Send a netlink query to userspace
+ * to get the iwarp port mapper pid
+ * @pm_msg: Contains driver info to send to the userspace port mapper
+ * @nl_client: The index of the netlink client
*
* nlmsg attributes:
* [IWPM_NLA_REG_PID_SEQ]
@@ -124,12 +131,19 @@ pid_query_error:
return ret;
}
-/*
- * iwpm_add_mapping - Send a netlink add mapping message
- * to the port mapper
+/**
+ * iwpm_add_mapping - Send a netlink add mapping request to
+ * the userspace port mapper
+ * @pm_msg: Contains the local ip/tcp address info to send
+ * @nl_client: The index of the netlink client
+ *
* nlmsg attributes:
* [IWPM_NLA_MANAGE_MAPPING_SEQ]
* [IWPM_NLA_MANAGE_ADDR]
+ * [IWPM_NLA_MANAGE_FLAGS]
+ *
+ * If the request is successful, the pm_msg stores
+ * the port mapper response (mapped address info)
*/
int iwpm_add_mapping(struct iwpm_sa_data *pm_msg, u8 nl_client)
{
@@ -173,6 +187,18 @@ int iwpm_add_mapping(struct iwpm_sa_data *pm_msg, u8 nl_client)
if (ret)
goto add_mapping_error;
+ /* If flags are required and we're not V4, then return a quiet error */
+ if (pm_msg->flags && iwpm_ulib_version == IWPM_UABI_VERSION_MIN) {
+ ret = -EINVAL;
+ goto add_mapping_error_nowarn;
+ }
+ if (iwpm_ulib_version > IWPM_UABI_VERSION_MIN) {
+ ret = ibnl_put_attr(skb, nlh, sizeof(u32), &pm_msg->flags,
+ IWPM_NLA_MANAGE_FLAGS);
+ if (ret)
+ goto add_mapping_error;
+ }
+
nlmsg_end(skb, nlh);
nlmsg_request->req_buffer = pm_msg;
@@ -187,6 +213,7 @@ int iwpm_add_mapping(struct iwpm_sa_data *pm_msg, u8 nl_client)
return ret;
add_mapping_error:
pr_info("%s: %s (client = %d)\n", __func__, err_str, nl_client);
+add_mapping_error_nowarn:
if (skb)
dev_kfree_skb(skb);
if (nlmsg_request)
@@ -194,13 +221,17 @@ add_mapping_error:
return ret;
}
-/*
- * iwpm_add_and_query_mapping - Send a netlink add and query
- * mapping message to the port mapper
+/**
+ * iwpm_add_and_query_mapping - Process the port mapper response to
+ * iwpm_add_and_query_mapping request
+ * @pm_msg: Contains the local ip/tcp address info to send
+ * @nl_client: The index of the netlink client
+ *
* nlmsg attributes:
* [IWPM_NLA_QUERY_MAPPING_SEQ]
* [IWPM_NLA_QUERY_LOCAL_ADDR]
* [IWPM_NLA_QUERY_REMOTE_ADDR]
+ * [IWPM_NLA_QUERY_FLAGS]
*/
int iwpm_add_and_query_mapping(struct iwpm_sa_data *pm_msg, u8 nl_client)
{
@@ -251,6 +282,18 @@ int iwpm_add_and_query_mapping(struct iwpm_sa_data *pm_msg, u8 nl_client)
if (ret)
goto query_mapping_error;
+ /* If flags are required and we're not V4, then return a quite error */
+ if (pm_msg->flags && iwpm_ulib_version == IWPM_UABI_VERSION_MIN) {
+ ret = -EINVAL;
+ goto query_mapping_error_nowarn;
+ }
+ if (iwpm_ulib_version > IWPM_UABI_VERSION_MIN) {
+ ret = ibnl_put_attr(skb, nlh, sizeof(u32), &pm_msg->flags,
+ IWPM_NLA_QUERY_FLAGS);
+ if (ret)
+ goto query_mapping_error;
+ }
+
nlmsg_end(skb, nlh);
nlmsg_request->req_buffer = pm_msg;
@@ -264,6 +307,7 @@ int iwpm_add_and_query_mapping(struct iwpm_sa_data *pm_msg, u8 nl_client)
return ret;
query_mapping_error:
pr_info("%s: %s (client = %d)\n", __func__, err_str, nl_client);
+query_mapping_error_nowarn:
if (skb)
dev_kfree_skb(skb);
if (nlmsg_request)
@@ -271,9 +315,13 @@ query_mapping_error:
return ret;
}
-/*
- * iwpm_remove_mapping - Send a netlink remove mapping message
- * to the port mapper
+/**
+ * iwpm_remove_mapping - Send a netlink remove mapping request
+ * to the userspace port mapper
+ *
+ * @local_addr: Local ip/tcp address to remove
+ * @nl_client: The index of the netlink client
+ *
* nlmsg attributes:
* [IWPM_NLA_MANAGE_MAPPING_SEQ]
* [IWPM_NLA_MANAGE_ADDR]
@@ -344,9 +392,14 @@ static const struct nla_policy resp_reg_policy[IWPM_NLA_RREG_PID_MAX] = {
[IWPM_NLA_RREG_PID_ERR] = { .type = NLA_U16 }
};
-/*
- * iwpm_register_pid_cb - Process a port mapper response to
- * iwpm_register_pid()
+/**
+ * iwpm_register_pid_cb - Process the port mapper response to
+ * iwpm_register_pid query
+ * @skb:
+ * @cb: Contains the received message (payload and netlink header)
+ *
+ * If successful, the function receives the userspace port mapper pid
+ * which is used in future communication with the port mapper
*/
int iwpm_register_pid_cb(struct sk_buff *skb, struct netlink_callback *cb)
{
@@ -379,7 +432,7 @@ int iwpm_register_pid_cb(struct sk_buff *skb, struct netlink_callback *cb)
/* check device name, ulib name and version */
if (strcmp(pm_msg->dev_name, dev_name) ||
strcmp(iwpm_ulib_name, iwpm_name) ||
- iwpm_version != iwpm_ulib_version) {
+ iwpm_version < IWPM_UABI_VERSION_MIN) {
pr_info("%s: Incorrect info (dev = %s name = %s version = %d)\n",
__func__, dev_name, iwpm_name, iwpm_version);
@@ -387,6 +440,10 @@ int iwpm_register_pid_cb(struct sk_buff *skb, struct netlink_callback *cb)
goto register_pid_response_exit;
}
iwpm_user_pid = cb->nlh->nlmsg_pid;
+ iwpm_ulib_version = iwpm_version;
+ if (iwpm_ulib_version < IWPM_UABI_VERSION)
+ pr_warn_once("%s: Down level iwpmd/pid %u. Continuing...",
+ __func__, iwpm_user_pid);
atomic_set(&echo_nlmsg_seq, cb->nlh->nlmsg_seq);
pr_debug("%s: iWarp Port Mapper (pid = %d) is available!\n",
__func__, iwpm_user_pid);
@@ -403,15 +460,19 @@ register_pid_response_exit:
/* netlink attribute policy for the received response to add mapping request */
static const struct nla_policy resp_add_policy[IWPM_NLA_RMANAGE_MAPPING_MAX] = {
- [IWPM_NLA_MANAGE_MAPPING_SEQ] = { .type = NLA_U32 },
- [IWPM_NLA_MANAGE_ADDR] = { .len = sizeof(struct sockaddr_storage) },
- [IWPM_NLA_MANAGE_MAPPED_LOC_ADDR] = { .len = sizeof(struct sockaddr_storage) },
- [IWPM_NLA_RMANAGE_MAPPING_ERR] = { .type = NLA_U16 }
+ [IWPM_NLA_RMANAGE_MAPPING_SEQ] = { .type = NLA_U32 },
+ [IWPM_NLA_RMANAGE_ADDR] = {
+ .len = sizeof(struct sockaddr_storage) },
+ [IWPM_NLA_RMANAGE_MAPPED_LOC_ADDR] = {
+ .len = sizeof(struct sockaddr_storage) },
+ [IWPM_NLA_RMANAGE_MAPPING_ERR] = { .type = NLA_U16 }
};
-/*
- * iwpm_add_mapping_cb - Process a port mapper response to
- * iwpm_add_mapping()
+/**
+ * iwpm_add_mapping_cb - Process the port mapper response to
+ * iwpm_add_mapping request
+ * @skb:
+ * @cb: Contains the received message (payload and netlink header)
*/
int iwpm_add_mapping_cb(struct sk_buff *skb, struct netlink_callback *cb)
{
@@ -430,7 +491,7 @@ int iwpm_add_mapping_cb(struct sk_buff *skb, struct netlink_callback *cb)
atomic_set(&echo_nlmsg_seq, cb->nlh->nlmsg_seq);
- msg_seq = nla_get_u32(nltb[IWPM_NLA_MANAGE_MAPPING_SEQ]);
+ msg_seq = nla_get_u32(nltb[IWPM_NLA_RMANAGE_MAPPING_SEQ]);
nlmsg_request = iwpm_find_nlmsg_request(msg_seq);
if (!nlmsg_request) {
pr_info("%s: Could not find a matching request (seq = %u)\n",
@@ -439,9 +500,9 @@ int iwpm_add_mapping_cb(struct sk_buff *skb, struct netlink_callback *cb)
}
pm_msg = nlmsg_request->req_buffer;
local_sockaddr = (struct sockaddr_storage *)
- nla_data(nltb[IWPM_NLA_MANAGE_ADDR]);
+ nla_data(nltb[IWPM_NLA_RMANAGE_ADDR]);
mapped_sockaddr = (struct sockaddr_storage *)
- nla_data(nltb[IWPM_NLA_MANAGE_MAPPED_LOC_ADDR]);
+ nla_data(nltb[IWPM_NLA_RMANAGE_MAPPED_LOC_ADDR]);
if (iwpm_compare_sockaddr(local_sockaddr, &pm_msg->loc_addr)) {
nlmsg_request->err_code = IWPM_USER_LIB_INFO_ERR;
@@ -472,17 +533,23 @@ add_mapping_response_exit:
/* netlink attribute policy for the response to add and query mapping request
* and response with remote address info */
static const struct nla_policy resp_query_policy[IWPM_NLA_RQUERY_MAPPING_MAX] = {
- [IWPM_NLA_QUERY_MAPPING_SEQ] = { .type = NLA_U32 },
- [IWPM_NLA_QUERY_LOCAL_ADDR] = { .len = sizeof(struct sockaddr_storage) },
- [IWPM_NLA_QUERY_REMOTE_ADDR] = { .len = sizeof(struct sockaddr_storage) },
- [IWPM_NLA_RQUERY_MAPPED_LOC_ADDR] = { .len = sizeof(struct sockaddr_storage) },
- [IWPM_NLA_RQUERY_MAPPED_REM_ADDR] = { .len = sizeof(struct sockaddr_storage) },
+ [IWPM_NLA_RQUERY_MAPPING_SEQ] = { .type = NLA_U32 },
+ [IWPM_NLA_RQUERY_LOCAL_ADDR] = {
+ .len = sizeof(struct sockaddr_storage) },
+ [IWPM_NLA_RQUERY_REMOTE_ADDR] = {
+ .len = sizeof(struct sockaddr_storage) },
+ [IWPM_NLA_RQUERY_MAPPED_LOC_ADDR] = {
+ .len = sizeof(struct sockaddr_storage) },
+ [IWPM_NLA_RQUERY_MAPPED_REM_ADDR] = {
+ .len = sizeof(struct sockaddr_storage) },
[IWPM_NLA_RQUERY_MAPPING_ERR] = { .type = NLA_U16 }
};
-/*
- * iwpm_add_and_query_mapping_cb - Process a port mapper response to
- * iwpm_add_and_query_mapping()
+/**
+ * iwpm_add_and_query_mapping_cb - Process the port mapper response to
+ * iwpm_add_and_query_mapping request
+ * @skb:
+ * @cb: Contains the received message (payload and netlink header)
*/
int iwpm_add_and_query_mapping_cb(struct sk_buff *skb,
struct netlink_callback *cb)
@@ -502,7 +569,7 @@ int iwpm_add_and_query_mapping_cb(struct sk_buff *skb,
return -EINVAL;
atomic_set(&echo_nlmsg_seq, cb->nlh->nlmsg_seq);
- msg_seq = nla_get_u32(nltb[IWPM_NLA_QUERY_MAPPING_SEQ]);
+ msg_seq = nla_get_u32(nltb[IWPM_NLA_RQUERY_MAPPING_SEQ]);
nlmsg_request = iwpm_find_nlmsg_request(msg_seq);
if (!nlmsg_request) {
pr_info("%s: Could not find a matching request (seq = %u)\n",
@@ -511,9 +578,9 @@ int iwpm_add_and_query_mapping_cb(struct sk_buff *skb,
}
pm_msg = nlmsg_request->req_buffer;
local_sockaddr = (struct sockaddr_storage *)
- nla_data(nltb[IWPM_NLA_QUERY_LOCAL_ADDR]);
+ nla_data(nltb[IWPM_NLA_RQUERY_LOCAL_ADDR]);
remote_sockaddr = (struct sockaddr_storage *)
- nla_data(nltb[IWPM_NLA_QUERY_REMOTE_ADDR]);
+ nla_data(nltb[IWPM_NLA_RQUERY_REMOTE_ADDR]);
mapped_loc_sockaddr = (struct sockaddr_storage *)
nla_data(nltb[IWPM_NLA_RQUERY_MAPPED_LOC_ADDR]);
mapped_rem_sockaddr = (struct sockaddr_storage *)
@@ -560,9 +627,13 @@ query_mapping_response_exit:
return 0;
}
-/*
- * iwpm_remote_info_cb - Process a port mapper message, containing
- * the remote connecting peer address info
+/**
+ * iwpm_remote_info_cb - Process remote connecting peer address info, which
+ * the port mapper has received from the connecting peer
+ * @skb:
+ * @cb: Contains the received message (payload and netlink header)
+ *
+ * Stores the IPv4/IPv6 address info in a hash table
*/
int iwpm_remote_info_cb(struct sk_buff *skb, struct netlink_callback *cb)
{
@@ -588,9 +659,9 @@ int iwpm_remote_info_cb(struct sk_buff *skb, struct netlink_callback *cb)
atomic_set(&echo_nlmsg_seq, cb->nlh->nlmsg_seq);
local_sockaddr = (struct sockaddr_storage *)
- nla_data(nltb[IWPM_NLA_QUERY_LOCAL_ADDR]);
+ nla_data(nltb[IWPM_NLA_RQUERY_LOCAL_ADDR]);
remote_sockaddr = (struct sockaddr_storage *)
- nla_data(nltb[IWPM_NLA_QUERY_REMOTE_ADDR]);
+ nla_data(nltb[IWPM_NLA_RQUERY_REMOTE_ADDR]);
mapped_loc_sockaddr = (struct sockaddr_storage *)
nla_data(nltb[IWPM_NLA_RQUERY_MAPPED_LOC_ADDR]);
mapped_rem_sockaddr = (struct sockaddr_storage *)
@@ -635,8 +706,14 @@ static const struct nla_policy resp_mapinfo_policy[IWPM_NLA_MAPINFO_REQ_MAX] = {
[IWPM_NLA_MAPINFO_ULIB_VER] = { .type = NLA_U16 }
};
-/*
- * iwpm_mapping_info_cb - Process a port mapper request for mapping info
+/**
+ * iwpm_mapping_info_cb - Process a notification that the userspace
+ * port mapper daemon is started
+ * @skb:
+ * @cb: Contains the received message (payload and netlink header)
+ *
+ * Using the received port mapper pid, send all the local mapping
+ * info records to the userspace port mapper
*/
int iwpm_mapping_info_cb(struct sk_buff *skb, struct netlink_callback *cb)
{
@@ -655,7 +732,7 @@ int iwpm_mapping_info_cb(struct sk_buff *skb, struct netlink_callback *cb)
iwpm_name = (char *)nla_data(nltb[IWPM_NLA_MAPINFO_ULIB_NAME]);
iwpm_version = nla_get_u16(nltb[IWPM_NLA_MAPINFO_ULIB_VER]);
if (strcmp(iwpm_ulib_name, iwpm_name) ||
- iwpm_version != iwpm_ulib_version) {
+ iwpm_version < IWPM_UABI_VERSION_MIN) {
pr_info("%s: Invalid port mapper name = %s version = %d\n",
__func__, iwpm_name, iwpm_version);
return ret;
@@ -669,6 +746,11 @@ int iwpm_mapping_info_cb(struct sk_buff *skb, struct netlink_callback *cb)
iwpm_set_registration(nl_client, IWPM_REG_INCOMPL);
atomic_set(&echo_nlmsg_seq, cb->nlh->nlmsg_seq);
iwpm_user_pid = cb->nlh->nlmsg_pid;
+
+ if (iwpm_ulib_version < IWPM_UABI_VERSION)
+ pr_warn_once("%s: Down level iwpmd/pid %u. Continuing...",
+ __func__, iwpm_user_pid);
+
if (!iwpm_mapinfo_available())
return 0;
pr_debug("%s: iWarp Port Mapper (pid = %d) is available!\n",
@@ -684,9 +766,11 @@ static const struct nla_policy ack_mapinfo_policy[IWPM_NLA_MAPINFO_NUM_MAX] = {
[IWPM_NLA_MAPINFO_ACK_NUM] = { .type = NLA_U32 }
};
-/*
- * iwpm_ack_mapping_info_cb - Process a port mapper ack for
- * the provided mapping info records
+/**
+ * iwpm_ack_mapping_info_cb - Process the port mapper ack for
+ * the provided local mapping info records
+ * @skb:
+ * @cb: Contains the received message (payload and netlink header)
*/
int iwpm_ack_mapping_info_cb(struct sk_buff *skb, struct netlink_callback *cb)
{
@@ -712,8 +796,11 @@ static const struct nla_policy map_error_policy[IWPM_NLA_ERR_MAX] = {
[IWPM_NLA_ERR_CODE] = { .type = NLA_U16 },
};
-/*
- * iwpm_mapping_error_cb - Process a port mapper error message
+/**
+ * iwpm_mapping_error_cb - Process port mapper notification for error
+ *
+ * @skb:
+ * @cb: Contains the received message (payload and netlink header)
*/
int iwpm_mapping_error_cb(struct sk_buff *skb, struct netlink_callback *cb)
{
@@ -748,3 +835,46 @@ int iwpm_mapping_error_cb(struct sk_buff *skb, struct netlink_callback *cb)
up(&nlmsg_request->sem);
return 0;
}
+
+/* netlink attribute policy for the received hello request */
+static const struct nla_policy hello_policy[IWPM_NLA_HELLO_MAX] = {
+ [IWPM_NLA_HELLO_ABI_VERSION] = { .type = NLA_U16 }
+};
+
+/**
+ * iwpm_hello_cb - Process a hello message from iwpmd
+ *
+ * @skb:
+ * @cb: Contains the received message (payload and netlink header)
+ *
+ * Using the received port mapper pid, send the kernel's abi_version
+ * after adjusting it to support the iwpmd version.
+ */
+int iwpm_hello_cb(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ struct nlattr *nltb[IWPM_NLA_HELLO_MAX];
+ const char *msg_type = "Hello request";
+ u8 nl_client;
+ u16 abi_version;
+ int ret = -EINVAL;
+
+ if (iwpm_parse_nlmsg(cb, IWPM_NLA_HELLO_MAX, hello_policy, nltb,
+ msg_type)) {
+ pr_info("%s: Unable to parse nlmsg\n", __func__);
+ return ret;
+ }
+ abi_version = nla_get_u16(nltb[IWPM_NLA_HELLO_ABI_VERSION]);
+ nl_client = RDMA_NL_GET_CLIENT(cb->nlh->nlmsg_type);
+ if (!iwpm_valid_client(nl_client)) {
+ pr_info("%s: Invalid port mapper client = %d\n",
+ __func__, nl_client);
+ return ret;
+ }
+ iwpm_set_registration(nl_client, IWPM_REG_INCOMPL);
+ atomic_set(&echo_nlmsg_seq, cb->nlh->nlmsg_seq);
+ iwpm_ulib_version = min_t(u16, IWPM_UABI_VERSION, abi_version);
+ pr_debug("Using ABI version %u\n", iwpm_ulib_version);
+ iwpm_user_pid = cb->nlh->nlmsg_pid;
+ ret = iwpm_send_hello(nl_client, iwpm_user_pid, iwpm_ulib_version);
+ return ret;
+}
diff --git a/drivers/infiniband/core/iwpm_util.c b/drivers/infiniband/core/iwpm_util.c
index cdb63f3f4de7..a5d2a20ee697 100644
--- a/drivers/infiniband/core/iwpm_util.c
+++ b/drivers/infiniband/core/iwpm_util.c
@@ -51,6 +51,12 @@ static DEFINE_SPINLOCK(iwpm_reminfo_lock);
static DEFINE_MUTEX(iwpm_admin_lock);
static struct iwpm_admin_data iwpm_admin;
+/**
+ * iwpm_init - Allocate resources for the iwarp port mapper
+ * @nl_client: The index of the netlink client
+ *
+ * Should be called when network interface goes up.
+ */
int iwpm_init(u8 nl_client)
{
int ret = 0;
@@ -87,6 +93,12 @@ init_exit:
static void free_hash_bucket(void);
static void free_reminfo_bucket(void);
+/**
+ * iwpm_exit - Deallocate resources for the iwarp port mapper
+ * @nl_client: The index of the netlink client
+ *
+ * Should be called when network interface goes down.
+ */
int iwpm_exit(u8 nl_client)
{
@@ -112,9 +124,17 @@ int iwpm_exit(u8 nl_client)
static struct hlist_head *get_mapinfo_hash_bucket(struct sockaddr_storage *,
struct sockaddr_storage *);
+/**
+ * iwpm_create_mapinfo - Store local and mapped IPv4/IPv6 address
+ * info in a hash table
+ * @local_addr: Local ip/tcp address
+ * @mapped_addr: Mapped local ip/tcp address
+ * @nl_client: The index of the netlink client
+ * @map_flags: IWPM mapping flags
+ */
int iwpm_create_mapinfo(struct sockaddr_storage *local_sockaddr,
struct sockaddr_storage *mapped_sockaddr,
- u8 nl_client)
+ u8 nl_client, u32 map_flags)
{
struct hlist_head *hash_bucket_head = NULL;
struct iwpm_mapping_info *map_info;
@@ -132,6 +152,7 @@ int iwpm_create_mapinfo(struct sockaddr_storage *local_sockaddr,
memcpy(&map_info->mapped_sockaddr, mapped_sockaddr,
sizeof(struct sockaddr_storage));
map_info->nl_client = nl_client;
+ map_info->map_flags = map_flags;
spin_lock_irqsave(&iwpm_mapinfo_lock, flags);
if (iwpm_hash_bucket) {
@@ -150,6 +171,15 @@ int iwpm_create_mapinfo(struct sockaddr_storage *local_sockaddr,
return ret;
}
+/**
+ * iwpm_remove_mapinfo - Remove local and mapped IPv4/IPv6 address
+ * info from the hash table
+ * @local_addr: Local ip/tcp address
+ * @mapped_local_addr: Mapped local ip/tcp address
+ *
+ * Returns err code if mapping info is not found in the hash table,
+ * otherwise returns 0
+ */
int iwpm_remove_mapinfo(struct sockaddr_storage *local_sockaddr,
struct sockaddr_storage *mapped_local_addr)
{
@@ -250,6 +280,17 @@ void iwpm_add_remote_info(struct iwpm_remote_info *rem_info)
spin_unlock_irqrestore(&iwpm_reminfo_lock, flags);
}
+/**
+ * iwpm_get_remote_info - Get the remote connecting peer address info
+ *
+ * @mapped_loc_addr: Mapped local address of the listening peer
+ * @mapped_rem_addr: Mapped remote address of the connecting peer
+ * @remote_addr: To store the remote address of the connecting peer
+ * @nl_client: The index of the netlink client
+ *
+ * The remote address info is retrieved and provided to the client in
+ * the remote_addr. After that it is removed from the hash table
+ */
int iwpm_get_remote_info(struct sockaddr_storage *mapped_loc_addr,
struct sockaddr_storage *mapped_rem_addr,
struct sockaddr_storage *remote_addr,
@@ -686,6 +727,14 @@ int iwpm_send_mapinfo(u8 nl_client, int iwpm_pid)
if (ret)
goto send_mapping_info_unlock;
+ if (iwpm_ulib_version > IWPM_UABI_VERSION_MIN) {
+ ret = ibnl_put_attr(skb, nlh, sizeof(u32),
+ &map_info->map_flags,
+ IWPM_NLA_MAPINFO_FLAGS);
+ if (ret)
+ goto send_mapping_info_unlock;
+ }
+
nlmsg_end(skb, nlh);
iwpm_print_sockaddr(&map_info->local_sockaddr,
@@ -754,3 +803,38 @@ int iwpm_mapinfo_available(void)
spin_unlock_irqrestore(&iwpm_mapinfo_lock, flags);
return full_bucket;
}
+
+int iwpm_send_hello(u8 nl_client, int iwpm_pid, u16 abi_version)
+{
+ struct sk_buff *skb = NULL;
+ struct nlmsghdr *nlh;
+ const char *err_str = "";
+ int ret = -EINVAL;
+
+ skb = iwpm_create_nlmsg(RDMA_NL_IWPM_HELLO, &nlh, nl_client);
+ if (!skb) {
+ err_str = "Unable to create a nlmsg";
+ goto hello_num_error;
+ }
+ nlh->nlmsg_seq = iwpm_get_nlmsg_seq();
+ err_str = "Unable to put attribute of abi_version into nlmsg";
+ ret = ibnl_put_attr(skb, nlh, sizeof(u16), &abi_version,
+ IWPM_NLA_HELLO_ABI_VERSION);
+ if (ret)
+ goto hello_num_error;
+ nlmsg_end(skb, nlh);
+
+ ret = rdma_nl_unicast(skb, iwpm_pid);
+ if (ret) {
+ skb = NULL;
+ err_str = "Unable to send a nlmsg";
+ goto hello_num_error;
+ }
+ pr_debug("%s: Sent hello abi_version = %u\n", __func__, abi_version);
+ return 0;
+hello_num_error:
+ pr_info("%s: %s\n", __func__, err_str);
+ if (skb)
+ dev_kfree_skb(skb);
+ return ret;
+}
diff --git a/drivers/infiniband/core/iwpm_util.h b/drivers/infiniband/core/iwpm_util.h
index af1fc14a0d3d..7e2bcc72f66c 100644
--- a/drivers/infiniband/core/iwpm_util.h
+++ b/drivers/infiniband/core/iwpm_util.h
@@ -78,6 +78,7 @@ struct iwpm_mapping_info {
struct sockaddr_storage local_sockaddr;
struct sockaddr_storage mapped_sockaddr;
u8 nl_client;
+ u32 map_flags;
};
struct iwpm_remote_info {
@@ -266,4 +267,15 @@ int iwpm_parse_nlmsg(struct netlink_callback *cb, int policy_max,
* @msg: Message to print
*/
void iwpm_print_sockaddr(struct sockaddr_storage *sockaddr, char *msg);
+
+/**
+ * iwpm_send_hello - Send hello response to iwpmd
+ *
+ * @nl_client: The index of the netlink client
+ * @abi_version: The kernel's abi_version
+ *
+ * Returns 0 on success or a negative error code
+ */
+int iwpm_send_hello(u8 nl_client, int iwpm_pid, u16 abi_version);
+extern u16 iwpm_ulib_version;
#endif
diff --git a/drivers/infiniband/core/mad.c b/drivers/infiniband/core/mad.c
index 7870823bac47..e742a6a2c138 100644
--- a/drivers/infiniband/core/mad.c
+++ b/drivers/infiniband/core/mad.c
@@ -3326,9 +3326,9 @@ error:
static void ib_mad_remove_device(struct ib_device *device, void *client_data)
{
- int i;
+ unsigned int i;
- for (i = rdma_start_port(device); i <= rdma_end_port(device); i++) {
+ rdma_for_each_port (device, i) {
if (!rdma_cap_ib_mad(device, i))
continue;
diff --git a/drivers/infiniband/core/netlink.c b/drivers/infiniband/core/netlink.c
index 724f5a62e82f..eecfc0b377c9 100644
--- a/drivers/infiniband/core/netlink.c
+++ b/drivers/infiniband/core/netlink.c
@@ -56,7 +56,6 @@ EXPORT_SYMBOL(rdma_nl_chk_listeners);
static bool is_nl_msg_valid(unsigned int type, unsigned int op)
{
static const unsigned int max_num_ops[RDMA_NL_NUM_CLIENTS] = {
- [RDMA_NL_RDMA_CM] = RDMA_NL_RDMA_CM_NUM_OPS,
[RDMA_NL_IWCM] = RDMA_NL_IWPM_NUM_OPS,
[RDMA_NL_LS] = RDMA_NL_LS_NUM_OPS,
[RDMA_NL_NLDEV] = RDMA_NLDEV_NUM_OPS,
@@ -181,8 +180,7 @@ static int rdma_nl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
return -EINVAL;
}
/* FIXME: Convert IWCM to properly handle doit callbacks */
- if ((nlh->nlmsg_flags & NLM_F_DUMP) || index == RDMA_NL_RDMA_CM ||
- index == RDMA_NL_IWCM) {
+ if ((nlh->nlmsg_flags & NLM_F_DUMP) || index == RDMA_NL_IWCM) {
struct netlink_dump_control c = {
.dump = cb_table[op].dump,
};
diff --git a/drivers/infiniband/core/nldev.c b/drivers/infiniband/core/nldev.c
index 3c97a8b6bf1e..11ed58d3fce5 100644
--- a/drivers/infiniband/core/nldev.c
+++ b/drivers/infiniband/core/nldev.c
@@ -33,12 +33,14 @@
#include <linux/module.h>
#include <linux/pid.h>
#include <linux/pid_namespace.h>
+#include <linux/mutex.h>
#include <net/netlink.h>
#include <rdma/rdma_cm.h>
#include <rdma/rdma_netlink.h>
#include "core_priv.h"
#include "cma_priv.h"
+#include "restrack.h"
static const struct nla_policy nldev_policy[RDMA_NLDEV_ATTR_MAX] = {
[RDMA_NLDEV_ATTR_DEV_INDEX] = { .type = NLA_U32 },
@@ -107,6 +109,13 @@ static const struct nla_policy nldev_policy[RDMA_NLDEV_ATTR_MAX] = {
[RDMA_NLDEV_ATTR_DRIVER_U32] = { .type = NLA_U32 },
[RDMA_NLDEV_ATTR_DRIVER_S64] = { .type = NLA_S64 },
[RDMA_NLDEV_ATTR_DRIVER_U64] = { .type = NLA_U64 },
+ [RDMA_NLDEV_ATTR_RES_PDN] = { .type = NLA_U32 },
+ [RDMA_NLDEV_ATTR_RES_CQN] = { .type = NLA_U32 },
+ [RDMA_NLDEV_ATTR_RES_MRN] = { .type = NLA_U32 },
+ [RDMA_NLDEV_ATTR_RES_CM_IDN] = { .type = NLA_U32 },
+ [RDMA_NLDEV_ATTR_RES_CTXN] = { .type = NLA_U32 },
+ [RDMA_NLDEV_ATTR_LINK_TYPE] = { .type = NLA_NUL_STRING,
+ .len = RDMA_NLDEV_ATTR_ENTRY_STRLEN },
};
static int put_driver_name_print_type(struct sk_buff *msg, const char *name,
@@ -262,9 +271,7 @@ static int fill_port_info(struct sk_buff *msg,
if (nla_put_u8(msg, RDMA_NLDEV_ATTR_PORT_PHYS_STATE, attr.phys_state))
return -EMSGSIZE;
- if (device->ops.get_netdev)
- netdev = device->ops.get_netdev(device, port);
-
+ netdev = ib_device_get_netdev(device, port);
if (netdev && net_eq(dev_net(netdev), net)) {
ret = nla_put_u32(msg,
RDMA_NLDEV_ATTR_NDEV_INDEX, netdev->ifindex);
@@ -314,7 +321,6 @@ static int fill_res_info(struct sk_buff *msg, struct ib_device *device)
[RDMA_RESTRACK_CTX] = "ctx",
};
- struct rdma_restrack_root *res = &device->res;
struct nlattr *table_attr;
int ret, i, curr;
@@ -328,7 +334,8 @@ static int fill_res_info(struct sk_buff *msg, struct ib_device *device)
for (i = 0; i < RDMA_RESTRACK_MAX; i++) {
if (!names[i])
continue;
- curr = rdma_restrack_count(res, i, task_active_pid_ns(current));
+ curr = rdma_restrack_count(device, i,
+ task_active_pid_ns(current));
ret = fill_res_info_entry(msg, names[i], curr);
if (ret)
goto err;
@@ -361,13 +368,20 @@ static int fill_res_name_pid(struct sk_buff *msg,
return 0;
}
-static int fill_res_qp_entry(struct sk_buff *msg, struct netlink_callback *cb,
+static bool fill_res_entry(struct ib_device *dev, struct sk_buff *msg,
+ struct rdma_restrack_entry *res)
+{
+ if (!dev->ops.fill_res_entry)
+ return false;
+ return dev->ops.fill_res_entry(msg, res);
+}
+
+static int fill_res_qp_entry(struct sk_buff *msg, bool has_cap_net_admin,
struct rdma_restrack_entry *res, uint32_t port)
{
struct ib_qp *qp = container_of(res, struct ib_qp, res);
- struct rdma_restrack_root *resroot = &qp->device->res;
+ struct ib_device *dev = qp->device;
struct ib_qp_init_attr qp_init_attr;
- struct nlattr *entry_attr;
struct ib_qp_attr qp_attr;
int ret;
@@ -376,11 +390,7 @@ static int fill_res_qp_entry(struct sk_buff *msg, struct netlink_callback *cb,
return ret;
if (port && port != qp_attr.port_num)
- return 0;
-
- entry_attr = nla_nest_start(msg, RDMA_NLDEV_ATTR_RES_QP_ENTRY);
- if (!entry_attr)
- goto out;
+ return -EAGAIN;
/* In create_qp() port is not set yet */
if (qp_attr.port_num &&
@@ -412,38 +422,32 @@ static int fill_res_qp_entry(struct sk_buff *msg, struct netlink_callback *cb,
if (nla_put_u8(msg, RDMA_NLDEV_ATTR_RES_STATE, qp_attr.qp_state))
goto err;
+ if (!rdma_is_kernel_res(res) &&
+ nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_PDN, qp->pd->res.id))
+ goto err;
+
if (fill_res_name_pid(msg, res))
goto err;
- if (resroot->fill_res_entry(msg, res))
+ if (fill_res_entry(dev, msg, res))
goto err;
- nla_nest_end(msg, entry_attr);
return 0;
-err:
- nla_nest_cancel(msg, entry_attr);
-out:
- return -EMSGSIZE;
+err: return -EMSGSIZE;
}
-static int fill_res_cm_id_entry(struct sk_buff *msg,
- struct netlink_callback *cb,
+static int fill_res_cm_id_entry(struct sk_buff *msg, bool has_cap_net_admin,
struct rdma_restrack_entry *res, uint32_t port)
{
struct rdma_id_private *id_priv =
container_of(res, struct rdma_id_private, res);
- struct rdma_restrack_root *resroot = &id_priv->id.device->res;
+ struct ib_device *dev = id_priv->id.device;
struct rdma_cm_id *cm_id = &id_priv->id;
- struct nlattr *entry_attr;
if (port && port != cm_id->port_num)
return 0;
- entry_attr = nla_nest_start(msg, RDMA_NLDEV_ATTR_RES_CM_ID_ENTRY);
- if (!entry_attr)
- goto out;
-
if (cm_id->port_num &&
nla_put_u32(msg, RDMA_NLDEV_ATTR_PORT_INDEX, cm_id->port_num))
goto err;
@@ -472,31 +476,25 @@ static int fill_res_cm_id_entry(struct sk_buff *msg,
&cm_id->route.addr.dst_addr))
goto err;
+ if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_CM_IDN, res->id))
+ goto err;
+
if (fill_res_name_pid(msg, res))
goto err;
- if (resroot->fill_res_entry(msg, res))
+ if (fill_res_entry(dev, msg, res))
goto err;
- nla_nest_end(msg, entry_attr);
return 0;
-err:
- nla_nest_cancel(msg, entry_attr);
-out:
- return -EMSGSIZE;
+err: return -EMSGSIZE;
}
-static int fill_res_cq_entry(struct sk_buff *msg, struct netlink_callback *cb,
+static int fill_res_cq_entry(struct sk_buff *msg, bool has_cap_net_admin,
struct rdma_restrack_entry *res, uint32_t port)
{
struct ib_cq *cq = container_of(res, struct ib_cq, res);
- struct rdma_restrack_root *resroot = &cq->device->res;
- struct nlattr *entry_attr;
-
- entry_attr = nla_nest_start(msg, RDMA_NLDEV_ATTR_RES_CQ_ENTRY);
- if (!entry_attr)
- goto out;
+ struct ib_device *dev = cq->device;
if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_CQE, cq->cqe))
goto err;
@@ -509,33 +507,31 @@ static int fill_res_cq_entry(struct sk_buff *msg, struct netlink_callback *cb,
nla_put_u8(msg, RDMA_NLDEV_ATTR_RES_POLL_CTX, cq->poll_ctx))
goto err;
+ if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_CQN, res->id))
+ goto err;
+ if (!rdma_is_kernel_res(res) &&
+ nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_CTXN,
+ cq->uobject->context->res.id))
+ goto err;
+
if (fill_res_name_pid(msg, res))
goto err;
- if (resroot->fill_res_entry(msg, res))
+ if (fill_res_entry(dev, msg, res))
goto err;
- nla_nest_end(msg, entry_attr);
return 0;
-err:
- nla_nest_cancel(msg, entry_attr);
-out:
- return -EMSGSIZE;
+err: return -EMSGSIZE;
}
-static int fill_res_mr_entry(struct sk_buff *msg, struct netlink_callback *cb,
+static int fill_res_mr_entry(struct sk_buff *msg, bool has_cap_net_admin,
struct rdma_restrack_entry *res, uint32_t port)
{
struct ib_mr *mr = container_of(res, struct ib_mr, res);
- struct rdma_restrack_root *resroot = &mr->pd->device->res;
- struct nlattr *entry_attr;
-
- entry_attr = nla_nest_start(msg, RDMA_NLDEV_ATTR_RES_MR_ENTRY);
- if (!entry_attr)
- goto out;
+ struct ib_device *dev = mr->pd->device;
- if (netlink_capable(cb->skb, CAP_NET_ADMIN)) {
+ if (has_cap_net_admin) {
if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_RKEY, mr->rkey))
goto err;
if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_LKEY, mr->lkey))
@@ -546,33 +542,31 @@ static int fill_res_mr_entry(struct sk_buff *msg, struct netlink_callback *cb,
RDMA_NLDEV_ATTR_PAD))
goto err;
+ if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_MRN, res->id))
+ goto err;
+
+ if (!rdma_is_kernel_res(res) &&
+ nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_PDN, mr->pd->res.id))
+ goto err;
+
if (fill_res_name_pid(msg, res))
goto err;
- if (resroot->fill_res_entry(msg, res))
+ if (fill_res_entry(dev, msg, res))
goto err;
- nla_nest_end(msg, entry_attr);
return 0;
-err:
- nla_nest_cancel(msg, entry_attr);
-out:
- return -EMSGSIZE;
+err: return -EMSGSIZE;
}
-static int fill_res_pd_entry(struct sk_buff *msg, struct netlink_callback *cb,
+static int fill_res_pd_entry(struct sk_buff *msg, bool has_cap_net_admin,
struct rdma_restrack_entry *res, uint32_t port)
{
struct ib_pd *pd = container_of(res, struct ib_pd, res);
- struct rdma_restrack_root *resroot = &pd->device->res;
- struct nlattr *entry_attr;
+ struct ib_device *dev = pd->device;
- entry_attr = nla_nest_start(msg, RDMA_NLDEV_ATTR_RES_PD_ENTRY);
- if (!entry_attr)
- goto out;
-
- if (netlink_capable(cb->skb, CAP_NET_ADMIN)) {
+ if (has_cap_net_admin) {
if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_LOCAL_DMA_LKEY,
pd->local_dma_lkey))
goto err;
@@ -585,19 +579,23 @@ static int fill_res_pd_entry(struct sk_buff *msg, struct netlink_callback *cb,
atomic_read(&pd->usecnt), RDMA_NLDEV_ATTR_PAD))
goto err;
+ if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_PDN, res->id))
+ goto err;
+
+ if (!rdma_is_kernel_res(res) &&
+ nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_CTXN,
+ pd->uobject->context->res.id))
+ goto err;
+
if (fill_res_name_pid(msg, res))
goto err;
- if (resroot->fill_res_entry(msg, res))
+ if (fill_res_entry(dev, msg, res))
goto err;
- nla_nest_end(msg, entry_attr);
return 0;
-err:
- nla_nest_cancel(msg, entry_attr);
-out:
- return -EMSGSIZE;
+err: return -EMSGSIZE;
}
static int nldev_get_doit(struct sk_buff *skb, struct nlmsghdr *nlh,
@@ -777,7 +775,7 @@ static int nldev_port_get_dumpit(struct sk_buff *skb,
u32 idx = 0;
u32 ifindex;
int err;
- u32 p;
+ unsigned int p;
err = nlmsg_parse(cb->nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1,
nldev_policy, NULL);
@@ -789,7 +787,7 @@ static int nldev_port_get_dumpit(struct sk_buff *skb,
if (!device)
return -EINVAL;
- for (p = rdma_start_port(device); p <= rdma_end_port(device); ++p) {
+ rdma_for_each_port (device, p) {
/*
* The dumpit function returns all information from specific
* index. This specific index is taken from the netlink
@@ -905,10 +903,17 @@ static int nldev_res_get_dumpit(struct sk_buff *skb,
}
struct nldev_fill_res_entry {
- int (*fill_res_func)(struct sk_buff *msg, struct netlink_callback *cb,
+ int (*fill_res_func)(struct sk_buff *msg, bool has_cap_net_admin,
struct rdma_restrack_entry *res, u32 port);
enum rdma_nldev_attr nldev_attr;
enum rdma_nldev_command nldev_cmd;
+ u8 flags;
+ u32 entry;
+ u32 id;
+};
+
+enum nldev_res_flags {
+ NLDEV_PER_DEV = 1 << 0,
};
static const struct nldev_fill_res_entry fill_entries[RDMA_RESTRACK_MAX] = {
@@ -916,29 +921,136 @@ static const struct nldev_fill_res_entry fill_entries[RDMA_RESTRACK_MAX] = {
.fill_res_func = fill_res_qp_entry,
.nldev_cmd = RDMA_NLDEV_CMD_RES_QP_GET,
.nldev_attr = RDMA_NLDEV_ATTR_RES_QP,
+ .entry = RDMA_NLDEV_ATTR_RES_QP_ENTRY,
+ .id = RDMA_NLDEV_ATTR_RES_LQPN,
},
[RDMA_RESTRACK_CM_ID] = {
.fill_res_func = fill_res_cm_id_entry,
.nldev_cmd = RDMA_NLDEV_CMD_RES_CM_ID_GET,
.nldev_attr = RDMA_NLDEV_ATTR_RES_CM_ID,
+ .entry = RDMA_NLDEV_ATTR_RES_CM_ID_ENTRY,
+ .id = RDMA_NLDEV_ATTR_RES_CM_IDN,
},
[RDMA_RESTRACK_CQ] = {
.fill_res_func = fill_res_cq_entry,
.nldev_cmd = RDMA_NLDEV_CMD_RES_CQ_GET,
.nldev_attr = RDMA_NLDEV_ATTR_RES_CQ,
+ .flags = NLDEV_PER_DEV,
+ .entry = RDMA_NLDEV_ATTR_RES_CQ_ENTRY,
+ .id = RDMA_NLDEV_ATTR_RES_CQN,
},
[RDMA_RESTRACK_MR] = {
.fill_res_func = fill_res_mr_entry,
.nldev_cmd = RDMA_NLDEV_CMD_RES_MR_GET,
.nldev_attr = RDMA_NLDEV_ATTR_RES_MR,
+ .flags = NLDEV_PER_DEV,
+ .entry = RDMA_NLDEV_ATTR_RES_MR_ENTRY,
+ .id = RDMA_NLDEV_ATTR_RES_MRN,
},
[RDMA_RESTRACK_PD] = {
.fill_res_func = fill_res_pd_entry,
.nldev_cmd = RDMA_NLDEV_CMD_RES_PD_GET,
.nldev_attr = RDMA_NLDEV_ATTR_RES_PD,
+ .flags = NLDEV_PER_DEV,
+ .entry = RDMA_NLDEV_ATTR_RES_PD_ENTRY,
+ .id = RDMA_NLDEV_ATTR_RES_PDN,
},
};
+static bool is_visible_in_pid_ns(struct rdma_restrack_entry *res)
+{
+ /*
+ * 1. Kern resources should be visible in init name space only
+ * 2. Present only resources visible in the current namespace
+ */
+ if (rdma_is_kernel_res(res))
+ return task_active_pid_ns(current) == &init_pid_ns;
+ return task_active_pid_ns(current) == task_active_pid_ns(res->task);
+}
+
+static int res_get_common_doit(struct sk_buff *skb, struct nlmsghdr *nlh,
+ struct netlink_ext_ack *extack,
+ enum rdma_restrack_type res_type)
+{
+ const struct nldev_fill_res_entry *fe = &fill_entries[res_type];
+ struct nlattr *tb[RDMA_NLDEV_ATTR_MAX];
+ struct rdma_restrack_entry *res;
+ struct ib_device *device;
+ u32 index, id, port = 0;
+ bool has_cap_net_admin;
+ struct sk_buff *msg;
+ int ret;
+
+ ret = nlmsg_parse(nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1,
+ nldev_policy, extack);
+ if (ret || !tb[RDMA_NLDEV_ATTR_DEV_INDEX] || !fe->id || !tb[fe->id])
+ return -EINVAL;
+
+ index = nla_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]);
+ device = ib_device_get_by_index(index);
+ if (!device)
+ return -EINVAL;
+
+ if (tb[RDMA_NLDEV_ATTR_PORT_INDEX]) {
+ port = nla_get_u32(tb[RDMA_NLDEV_ATTR_PORT_INDEX]);
+ if (!rdma_is_port_valid(device, port)) {
+ ret = -EINVAL;
+ goto err;
+ }
+ }
+
+ if ((port && fe->flags & NLDEV_PER_DEV) ||
+ (!port && ~fe->flags & NLDEV_PER_DEV)) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ id = nla_get_u32(tb[fe->id]);
+ res = rdma_restrack_get_byid(device, res_type, id);
+ if (IS_ERR(res)) {
+ ret = PTR_ERR(res);
+ goto err;
+ }
+
+ if (!is_visible_in_pid_ns(res)) {
+ ret = -ENOENT;
+ goto err_get;
+ }
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ nlh = nlmsg_put(msg, NETLINK_CB(skb).portid, nlh->nlmsg_seq,
+ RDMA_NL_GET_TYPE(RDMA_NL_NLDEV, fe->nldev_cmd),
+ 0, 0);
+
+ if (fill_nldev_handle(msg, device)) {
+ ret = -EMSGSIZE;
+ goto err_free;
+ }
+
+ has_cap_net_admin = netlink_capable(skb, CAP_NET_ADMIN);
+ ret = fe->fill_res_func(msg, has_cap_net_admin, res, port);
+ rdma_restrack_put(res);
+ if (ret)
+ goto err_free;
+
+ nlmsg_end(msg, nlh);
+ ib_device_put(device);
+ return rdma_nl_unicast(msg, NETLINK_CB(skb).portid);
+
+err_free:
+ nlmsg_free(msg);
+err_get:
+ rdma_restrack_put(res);
+err:
+ ib_device_put(device);
+ return ret;
+}
+
static int res_get_common_dumpit(struct sk_buff *skb,
struct netlink_callback *cb,
enum rdma_restrack_type res_type)
@@ -946,11 +1058,15 @@ static int res_get_common_dumpit(struct sk_buff *skb,
const struct nldev_fill_res_entry *fe = &fill_entries[res_type];
struct nlattr *tb[RDMA_NLDEV_ATTR_MAX];
struct rdma_restrack_entry *res;
+ struct rdma_restrack_root *rt;
int err, ret = 0, idx = 0;
struct nlattr *table_attr;
+ struct nlattr *entry_attr;
struct ib_device *device;
int start = cb->args[0];
+ bool has_cap_net_admin;
struct nlmsghdr *nlh;
+ unsigned long id;
u32 index, port = 0;
bool filled = false;
@@ -998,55 +1114,51 @@ static int res_get_common_dumpit(struct sk_buff *skb,
goto err;
}
- down_read(&device->res.rwsem);
- hash_for_each_possible(device->res.hash, res, node, res_type) {
- if (idx < start)
- goto next;
+ has_cap_net_admin = netlink_capable(cb->skb, CAP_NET_ADMIN);
- if ((rdma_is_kernel_res(res) &&
- task_active_pid_ns(current) != &init_pid_ns) ||
- (!rdma_is_kernel_res(res) && task_active_pid_ns(current) !=
- task_active_pid_ns(res->task)))
- /*
- * 1. Kern resources should be visible in init
- * namspace only
- * 2. Present only resources visible in the current
- * namespace
- */
- goto next;
+ rt = &device->res[res_type];
+ xa_lock(&rt->xa);
+ /*
+ * FIXME: if the skip ahead is something common this loop should
+ * use xas_for_each & xas_pause to optimize, we can have a lot of
+ * objects.
+ */
+ xa_for_each(&rt->xa, id, res) {
+ if (!is_visible_in_pid_ns(res))
+ continue;
- if (!rdma_restrack_get(res))
- /*
- * Resource is under release now, but we are not
- * relesing lock now, so it will be released in
- * our next pass, once we will get ->next pointer.
- */
+ if (idx < start || !rdma_restrack_get(res))
goto next;
+ xa_unlock(&rt->xa);
+
filled = true;
- up_read(&device->res.rwsem);
- ret = fe->fill_res_func(skb, cb, res, port);
- down_read(&device->res.rwsem);
- /*
- * Return resource back, but it won't be released till
- * the &device->res.rwsem will be released for write.
- */
+ entry_attr = nla_nest_start(skb, fe->entry);
+ if (!entry_attr) {
+ ret = -EMSGSIZE;
+ rdma_restrack_put(res);
+ goto msg_full;
+ }
+
+ ret = fe->fill_res_func(skb, has_cap_net_admin, res, port);
rdma_restrack_put(res);
- if (ret == -EMSGSIZE)
- /*
- * There is a chance to optimize here.
- * It can be done by using list_prepare_entry
- * and list_for_each_entry_continue afterwards.
- */
- break;
- if (ret)
+ if (ret) {
+ nla_nest_cancel(skb, entry_attr);
+ if (ret == -EMSGSIZE)
+ goto msg_full;
+ if (ret == -EAGAIN)
+ goto again;
goto res_err;
+ }
+ nla_nest_end(skb, entry_attr);
+again: xa_lock(&rt->xa);
next: idx++;
}
- up_read(&device->res.rwsem);
+ xa_unlock(&rt->xa);
+msg_full:
nla_nest_end(skb, table_attr);
nlmsg_end(skb, nlh);
cb->args[0] = idx;
@@ -1063,7 +1175,6 @@ next: idx++;
res_err:
nla_nest_cancel(skb, table_attr);
- up_read(&device->res.rwsem);
err:
nlmsg_cancel(skb, nlh);
@@ -1073,34 +1184,132 @@ err_index:
return ret;
}
-static int nldev_res_get_qp_dumpit(struct sk_buff *skb,
- struct netlink_callback *cb)
+#define RES_GET_FUNCS(name, type) \
+ static int nldev_res_get_##name##_dumpit(struct sk_buff *skb, \
+ struct netlink_callback *cb) \
+ { \
+ return res_get_common_dumpit(skb, cb, type); \
+ } \
+ static int nldev_res_get_##name##_doit(struct sk_buff *skb, \
+ struct nlmsghdr *nlh, \
+ struct netlink_ext_ack *extack) \
+ { \
+ return res_get_common_doit(skb, nlh, extack, type); \
+ }
+
+RES_GET_FUNCS(qp, RDMA_RESTRACK_QP);
+RES_GET_FUNCS(cm_id, RDMA_RESTRACK_CM_ID);
+RES_GET_FUNCS(cq, RDMA_RESTRACK_CQ);
+RES_GET_FUNCS(pd, RDMA_RESTRACK_PD);
+RES_GET_FUNCS(mr, RDMA_RESTRACK_MR);
+
+static LIST_HEAD(link_ops);
+static DECLARE_RWSEM(link_ops_rwsem);
+
+static const struct rdma_link_ops *link_ops_get(const char *type)
{
- return res_get_common_dumpit(skb, cb, RDMA_RESTRACK_QP);
+ const struct rdma_link_ops *ops;
+
+ list_for_each_entry(ops, &link_ops, list) {
+ if (!strcmp(ops->type, type))
+ goto out;
+ }
+ ops = NULL;
+out:
+ return ops;
}
-static int nldev_res_get_cm_id_dumpit(struct sk_buff *skb,
- struct netlink_callback *cb)
+void rdma_link_register(struct rdma_link_ops *ops)
{
- return res_get_common_dumpit(skb, cb, RDMA_RESTRACK_CM_ID);
+ down_write(&link_ops_rwsem);
+ if (WARN_ON_ONCE(link_ops_get(ops->type)))
+ goto out;
+ list_add(&ops->list, &link_ops);
+out:
+ up_write(&link_ops_rwsem);
}
+EXPORT_SYMBOL(rdma_link_register);
-static int nldev_res_get_cq_dumpit(struct sk_buff *skb,
- struct netlink_callback *cb)
+void rdma_link_unregister(struct rdma_link_ops *ops)
{
- return res_get_common_dumpit(skb, cb, RDMA_RESTRACK_CQ);
+ down_write(&link_ops_rwsem);
+ list_del(&ops->list);
+ up_write(&link_ops_rwsem);
}
+EXPORT_SYMBOL(rdma_link_unregister);
-static int nldev_res_get_mr_dumpit(struct sk_buff *skb,
- struct netlink_callback *cb)
+static int nldev_newlink(struct sk_buff *skb, struct nlmsghdr *nlh,
+ struct netlink_ext_ack *extack)
{
- return res_get_common_dumpit(skb, cb, RDMA_RESTRACK_MR);
+ struct nlattr *tb[RDMA_NLDEV_ATTR_MAX];
+ char ibdev_name[IB_DEVICE_NAME_MAX];
+ const struct rdma_link_ops *ops;
+ char ndev_name[IFNAMSIZ];
+ struct net_device *ndev;
+ char type[IFNAMSIZ];
+ int err;
+
+ err = nlmsg_parse(nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1,
+ nldev_policy, extack);
+ if (err || !tb[RDMA_NLDEV_ATTR_DEV_NAME] ||
+ !tb[RDMA_NLDEV_ATTR_LINK_TYPE] || !tb[RDMA_NLDEV_ATTR_NDEV_NAME])
+ return -EINVAL;
+
+ nla_strlcpy(ibdev_name, tb[RDMA_NLDEV_ATTR_DEV_NAME],
+ sizeof(ibdev_name));
+ if (strchr(ibdev_name, '%'))
+ return -EINVAL;
+
+ nla_strlcpy(type, tb[RDMA_NLDEV_ATTR_LINK_TYPE], sizeof(type));
+ nla_strlcpy(ndev_name, tb[RDMA_NLDEV_ATTR_NDEV_NAME],
+ sizeof(ndev_name));
+
+ ndev = dev_get_by_name(&init_net, ndev_name);
+ if (!ndev)
+ return -ENODEV;
+
+ down_read(&link_ops_rwsem);
+ ops = link_ops_get(type);
+#ifdef CONFIG_MODULES
+ if (!ops) {
+ up_read(&link_ops_rwsem);
+ request_module("rdma-link-%s", type);
+ down_read(&link_ops_rwsem);
+ ops = link_ops_get(type);
+ }
+#endif
+ err = ops ? ops->newlink(ibdev_name, ndev) : -EINVAL;
+ up_read(&link_ops_rwsem);
+ dev_put(ndev);
+
+ return err;
}
-static int nldev_res_get_pd_dumpit(struct sk_buff *skb,
- struct netlink_callback *cb)
+static int nldev_dellink(struct sk_buff *skb, struct nlmsghdr *nlh,
+ struct netlink_ext_ack *extack)
{
- return res_get_common_dumpit(skb, cb, RDMA_RESTRACK_PD);
+ struct nlattr *tb[RDMA_NLDEV_ATTR_MAX];
+ struct ib_device *device;
+ u32 index;
+ int err;
+
+ err = nlmsg_parse(nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1,
+ nldev_policy, extack);
+ if (err || !tb[RDMA_NLDEV_ATTR_DEV_INDEX])
+ return -EINVAL;
+
+ index = nla_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]);
+ device = ib_device_get_by_index(index);
+ if (!device)
+ return -EINVAL;
+
+ if (!(device->attrs.device_cap_flags & IB_DEVICE_ALLOW_USER_UNREG)) {
+ ib_device_put(device);
+ return -EINVAL;
+ }
+
+ ib_unregister_device_and_put(device);
+ return 0;
}
static const struct rdma_nl_cbs nldev_cb_table[RDMA_NLDEV_NUM_OPS] = {
@@ -1112,6 +1321,14 @@ static const struct rdma_nl_cbs nldev_cb_table[RDMA_NLDEV_NUM_OPS] = {
.doit = nldev_set_doit,
.flags = RDMA_NL_ADMIN_PERM,
},
+ [RDMA_NLDEV_CMD_NEWLINK] = {
+ .doit = nldev_newlink,
+ .flags = RDMA_NL_ADMIN_PERM,
+ },
+ [RDMA_NLDEV_CMD_DELLINK] = {
+ .doit = nldev_dellink,
+ .flags = RDMA_NL_ADMIN_PERM,
+ },
[RDMA_NLDEV_CMD_PORT_GET] = {
.doit = nldev_port_get_doit,
.dump = nldev_port_get_dumpit,
@@ -1121,28 +1338,23 @@ static const struct rdma_nl_cbs nldev_cb_table[RDMA_NLDEV_NUM_OPS] = {
.dump = nldev_res_get_dumpit,
},
[RDMA_NLDEV_CMD_RES_QP_GET] = {
+ .doit = nldev_res_get_qp_doit,
.dump = nldev_res_get_qp_dumpit,
- /*
- * .doit is not implemented yet for two reasons:
- * 1. It is not needed yet.
- * 2. There is a need to provide identifier, while it is easy
- * for the QPs (device index + port index + LQPN), it is not
- * the case for the rest of resources (PD and CQ). Because it
- * is better to provide similar interface for all resources,
- * let's wait till we will have other resources implemented
- * too.
- */
},
[RDMA_NLDEV_CMD_RES_CM_ID_GET] = {
+ .doit = nldev_res_get_cm_id_doit,
.dump = nldev_res_get_cm_id_dumpit,
},
[RDMA_NLDEV_CMD_RES_CQ_GET] = {
+ .doit = nldev_res_get_cq_doit,
.dump = nldev_res_get_cq_dumpit,
},
[RDMA_NLDEV_CMD_RES_MR_GET] = {
+ .doit = nldev_res_get_mr_doit,
.dump = nldev_res_get_mr_dumpit,
},
[RDMA_NLDEV_CMD_RES_PD_GET] = {
+ .doit = nldev_res_get_pd_doit,
.dump = nldev_res_get_pd_dumpit,
},
};
diff --git a/drivers/infiniband/core/rdma_core.c b/drivers/infiniband/core/rdma_core.c
index 6c4747e61d2b..778375ff664e 100644
--- a/drivers/infiniband/core/rdma_core.c
+++ b/drivers/infiniband/core/rdma_core.c
@@ -438,6 +438,38 @@ free:
uverbs_uobject_put(uobj);
return ERR_PTR(ret);
}
+struct ib_uobject *_uobj_get_read(enum uverbs_default_objects type,
+ u32 object_id,
+ struct uverbs_attr_bundle *attrs)
+{
+ struct ib_uobject *uobj;
+
+ uobj = rdma_lookup_get_uobject(uobj_get_type(attrs, type), attrs->ufile,
+ object_id, UVERBS_LOOKUP_READ);
+ if (IS_ERR(uobj))
+ return uobj;
+
+ attrs->context = uobj->context;
+
+ return uobj;
+}
+
+struct ib_uobject *_uobj_get_write(enum uverbs_default_objects type,
+ u32 object_id,
+ struct uverbs_attr_bundle *attrs)
+{
+ struct ib_uobject *uobj;
+
+ uobj = rdma_lookup_get_uobject(uobj_get_type(attrs, type), attrs->ufile,
+ object_id, UVERBS_LOOKUP_WRITE);
+
+ if (IS_ERR(uobj))
+ return uobj;
+
+ attrs->context = uobj->context;
+
+ return uobj;
+}
static struct ib_uobject *
alloc_begin_idr_uobject(const struct uverbs_api_object *obj,
@@ -801,6 +833,7 @@ void uverbs_close_fd(struct file *f)
/* Pairs with filp->private_data in alloc_begin_fd_uobject */
uverbs_uobject_put(uobj);
}
+EXPORT_SYMBOL(uverbs_close_fd);
/*
* Drop the ucontext off the ufile and completely disconnect it from the
@@ -811,7 +844,6 @@ static void ufile_destroy_ucontext(struct ib_uverbs_file *ufile,
{
struct ib_ucontext *ucontext = ufile->ucontext;
struct ib_device *ib_dev = ucontext->device;
- int ret;
/*
* If we are closing the FD then the user mmap VMAs must have
@@ -829,12 +861,8 @@ static void ufile_destroy_ucontext(struct ib_uverbs_file *ufile,
rdma_restrack_del(&ucontext->res);
- /*
- * FIXME: Drivers are not permitted to fail dealloc_ucontext, remove
- * the error return.
- */
- ret = ib_dev->ops.dealloc_ucontext(ucontext);
- WARN_ON(ret);
+ ib_dev->ops.dealloc_ucontext(ucontext);
+ kfree(ucontext);
ufile->ucontext = NULL;
}
diff --git a/drivers/infiniband/core/restrack.c b/drivers/infiniband/core/restrack.c
index 46a5c553c624..3b5ff2f7b5f8 100644
--- a/drivers/infiniband/core/restrack.c
+++ b/drivers/infiniband/core/restrack.c
@@ -11,17 +11,29 @@
#include <linux/pid_namespace.h>
#include "cma_priv.h"
+#include "restrack.h"
-static int fill_res_noop(struct sk_buff *msg,
- struct rdma_restrack_entry *entry)
+/**
+ * rdma_restrack_init() - initialize and allocate resource tracking
+ * @dev: IB device
+ *
+ * Return: 0 on success
+ */
+int rdma_restrack_init(struct ib_device *dev)
{
- return 0;
-}
+ struct rdma_restrack_root *rt;
+ int i;
-void rdma_restrack_init(struct rdma_restrack_root *res)
-{
- init_rwsem(&res->rwsem);
- res->fill_res_entry = fill_res_noop;
+ dev->res = kcalloc(RDMA_RESTRACK_MAX, sizeof(*rt), GFP_KERNEL);
+ if (!dev->res)
+ return -ENOMEM;
+
+ rt = dev->res;
+
+ for (i = 0; i < RDMA_RESTRACK_MAX; i++)
+ xa_init_flags(&rt[i].xa, XA_FLAGS_ALLOC);
+
+ return 0;
}
static const char *type2str(enum rdma_restrack_type type)
@@ -38,55 +50,79 @@ static const char *type2str(enum rdma_restrack_type type)
return names[type];
};
-void rdma_restrack_clean(struct rdma_restrack_root *res)
+/**
+ * rdma_restrack_clean() - clean resource tracking
+ * @dev: IB device
+ */
+void rdma_restrack_clean(struct ib_device *dev)
{
+ struct rdma_restrack_root *rt = dev->res;
struct rdma_restrack_entry *e;
char buf[TASK_COMM_LEN];
- struct ib_device *dev;
+ bool found = false;
const char *owner;
- int bkt;
-
- if (hash_empty(res->hash))
- return;
-
- dev = container_of(res, struct ib_device, res);
- pr_err("restrack: %s", CUT_HERE);
- dev_err(&dev->dev, "BUG: RESTRACK detected leak of resources\n");
- hash_for_each(res->hash, bkt, e, node) {
- if (rdma_is_kernel_res(e)) {
- owner = e->kern_name;
- } else {
- /*
- * There is no need to call get_task_struct here,
- * because we can be here only if there are more
- * get_task_struct() call than put_task_struct().
- */
- get_task_comm(buf, e->task);
- owner = buf;
+ int i;
+
+ for (i = 0 ; i < RDMA_RESTRACK_MAX; i++) {
+ struct xarray *xa = &dev->res[i].xa;
+
+ if (!xa_empty(xa)) {
+ unsigned long index;
+
+ if (!found) {
+ pr_err("restrack: %s", CUT_HERE);
+ dev_err(&dev->dev, "BUG: RESTRACK detected leak of resources\n");
+ }
+ xa_for_each(xa, index, e) {
+ if (rdma_is_kernel_res(e)) {
+ owner = e->kern_name;
+ } else {
+ /*
+ * There is no need to call get_task_struct here,
+ * because we can be here only if there are more
+ * get_task_struct() call than put_task_struct().
+ */
+ get_task_comm(buf, e->task);
+ owner = buf;
+ }
+
+ pr_err("restrack: %s %s object allocated by %s is not freed\n",
+ rdma_is_kernel_res(e) ? "Kernel" :
+ "User",
+ type2str(e->type), owner);
+ }
+ found = true;
}
-
- pr_err("restrack: %s %s object allocated by %s is not freed\n",
- rdma_is_kernel_res(e) ? "Kernel" : "User",
- type2str(e->type), owner);
+ xa_destroy(xa);
}
- pr_err("restrack: %s", CUT_HERE);
+ if (found)
+ pr_err("restrack: %s", CUT_HERE);
+
+ kfree(rt);
}
-int rdma_restrack_count(struct rdma_restrack_root *res,
- enum rdma_restrack_type type,
+/**
+ * rdma_restrack_count() - the current usage of specific object
+ * @dev: IB device
+ * @type: actual type of object to operate
+ * @ns: PID namespace
+ */
+int rdma_restrack_count(struct ib_device *dev, enum rdma_restrack_type type,
struct pid_namespace *ns)
{
+ struct rdma_restrack_root *rt = &dev->res[type];
struct rdma_restrack_entry *e;
+ XA_STATE(xas, &rt->xa, 0);
u32 cnt = 0;
- down_read(&res->rwsem);
- hash_for_each_possible(res->hash, e, node, type) {
+ xa_lock(&rt->xa);
+ xas_for_each(&xas, e, U32_MAX) {
if (ns == &init_pid_ns ||
(!rdma_is_kernel_res(e) &&
ns == task_active_pid_ns(e->task)))
cnt++;
}
- up_read(&res->rwsem);
+ xa_unlock(&rt->xa);
return cnt;
}
EXPORT_SYMBOL(rdma_restrack_count);
@@ -157,28 +193,29 @@ EXPORT_SYMBOL(rdma_restrack_set_task);
static void rdma_restrack_add(struct rdma_restrack_entry *res)
{
struct ib_device *dev = res_to_dev(res);
+ struct rdma_restrack_root *rt;
+ int ret;
if (!dev)
return;
- if (res->type != RDMA_RESTRACK_CM_ID || rdma_is_kernel_res(res))
- res->task = NULL;
-
- if (!rdma_is_kernel_res(res)) {
- if (!res->task)
- rdma_restrack_set_task(res, NULL);
- res->kern_name = NULL;
- } else {
- set_kern_name(res);
- }
+ rt = &dev->res[res->type];
kref_init(&res->kref);
init_completion(&res->comp);
- res->valid = true;
+ if (res->type != RDMA_RESTRACK_QP)
+ ret = xa_alloc_cyclic(&rt->xa, &res->id, res, xa_limit_32b,
+ &rt->next_id, GFP_KERNEL);
+ else {
+ /* Special case to ensure that LQPN points to right QP */
+ struct ib_qp *qp = container_of(res, struct ib_qp, res);
+
+ ret = xa_insert(&rt->xa, qp->qp_num, res, GFP_KERNEL);
+ res->id = ret ? 0 : qp->qp_num;
+ }
- down_write(&dev->res.rwsem);
- hash_add(dev->res.hash, &res->node, res->type);
- up_write(&dev->res.rwsem);
+ if (!ret)
+ res->valid = true;
}
/**
@@ -187,6 +224,8 @@ static void rdma_restrack_add(struct rdma_restrack_entry *res)
*/
void rdma_restrack_kadd(struct rdma_restrack_entry *res)
{
+ res->task = NULL;
+ set_kern_name(res);
res->user = false;
rdma_restrack_add(res);
}
@@ -198,6 +237,13 @@ EXPORT_SYMBOL(rdma_restrack_kadd);
*/
void rdma_restrack_uadd(struct rdma_restrack_entry *res)
{
+ if (res->type != RDMA_RESTRACK_CM_ID)
+ res->task = NULL;
+
+ if (!res->task)
+ rdma_restrack_set_task(res, NULL);
+ res->kern_name = NULL;
+
res->user = true;
rdma_restrack_add(res);
}
@@ -209,6 +255,31 @@ int __must_check rdma_restrack_get(struct rdma_restrack_entry *res)
}
EXPORT_SYMBOL(rdma_restrack_get);
+/**
+ * rdma_restrack_get_byid() - translate from ID to restrack object
+ * @dev: IB device
+ * @type: resource track type
+ * @id: ID to take a look
+ *
+ * Return: Pointer to restrack entry or -ENOENT in case of error.
+ */
+struct rdma_restrack_entry *
+rdma_restrack_get_byid(struct ib_device *dev,
+ enum rdma_restrack_type type, u32 id)
+{
+ struct rdma_restrack_root *rt = &dev->res[type];
+ struct rdma_restrack_entry *res;
+
+ xa_lock(&rt->xa);
+ res = xa_load(&rt->xa, id);
+ if (!res || !rdma_restrack_get(res))
+ res = ERR_PTR(-ENOENT);
+ xa_unlock(&rt->xa);
+
+ return res;
+}
+EXPORT_SYMBOL(rdma_restrack_get_byid);
+
static void restrack_release(struct kref *kref)
{
struct rdma_restrack_entry *res;
@@ -225,23 +296,25 @@ EXPORT_SYMBOL(rdma_restrack_put);
void rdma_restrack_del(struct rdma_restrack_entry *res)
{
+ struct rdma_restrack_entry *old;
+ struct rdma_restrack_root *rt;
struct ib_device *dev;
if (!res->valid)
goto out;
dev = res_to_dev(res);
- if (!dev)
+ if (WARN_ON(!dev))
return;
- rdma_restrack_put(res);
-
- wait_for_completion(&res->comp);
+ rt = &dev->res[res->type];
- down_write(&dev->res.rwsem);
- hash_del(&res->node);
+ old = xa_erase(&rt->xa, res->id);
+ WARN_ON(old != res);
res->valid = false;
- up_write(&dev->res.rwsem);
+
+ rdma_restrack_put(res);
+ wait_for_completion(&res->comp);
out:
if (res->task) {
diff --git a/drivers/infiniband/core/restrack.h b/drivers/infiniband/core/restrack.h
new file mode 100644
index 000000000000..09a1fbdf578e
--- /dev/null
+++ b/drivers/infiniband/core/restrack.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
+/*
+ * Copyright (c) 2017-2019 Mellanox Technologies. All rights reserved.
+ */
+
+#ifndef _RDMA_CORE_RESTRACK_H_
+#define _RDMA_CORE_RESTRACK_H_
+
+#include <linux/mutex.h>
+
+/**
+ * struct rdma_restrack_root - main resource tracking management
+ * entity, per-device
+ */
+struct rdma_restrack_root {
+ /**
+ * @xa: Array of XArray structure to hold restrack entries.
+ */
+ struct xarray xa;
+ /**
+ * @next_id: Next ID to support cyclic allocation
+ */
+ u32 next_id;
+};
+
+int rdma_restrack_init(struct ib_device *dev);
+void rdma_restrack_clean(struct ib_device *dev);
+#endif /* _RDMA_CORE_RESTRACK_H_ */
diff --git a/drivers/infiniband/core/rw.c b/drivers/infiniband/core/rw.c
index d22c4a2ebac6..89a5be3a2f97 100644
--- a/drivers/infiniband/core/rw.c
+++ b/drivers/infiniband/core/rw.c
@@ -179,7 +179,6 @@ static int rdma_rw_init_map_wrs(struct rdma_rw_ctx *ctx, struct ib_qp *qp,
struct scatterlist *sg, u32 sg_cnt, u32 offset,
u64 remote_addr, u32 rkey, enum dma_data_direction dir)
{
- struct ib_device *dev = qp->pd->device;
u32 max_sge = dir == DMA_TO_DEVICE ? qp->max_write_sge :
qp->max_read_sge;
struct ib_sge *sge;
@@ -209,8 +208,8 @@ static int rdma_rw_init_map_wrs(struct rdma_rw_ctx *ctx, struct ib_qp *qp,
rdma_wr->wr.sg_list = sge;
for (j = 0; j < nr_sge; j++, sg = sg_next(sg)) {
- sge->addr = ib_sg_dma_address(dev, sg) + offset;
- sge->length = ib_sg_dma_len(dev, sg) - offset;
+ sge->addr = sg_dma_address(sg) + offset;
+ sge->length = sg_dma_len(sg) - offset;
sge->lkey = qp->pd->local_dma_lkey;
total_len += sge->length;
@@ -236,14 +235,13 @@ static int rdma_rw_init_single_wr(struct rdma_rw_ctx *ctx, struct ib_qp *qp,
struct scatterlist *sg, u32 offset, u64 remote_addr, u32 rkey,
enum dma_data_direction dir)
{
- struct ib_device *dev = qp->pd->device;
struct ib_rdma_wr *rdma_wr = &ctx->single.wr;
ctx->nr_ops = 1;
ctx->single.sge.lkey = qp->pd->local_dma_lkey;
- ctx->single.sge.addr = ib_sg_dma_address(dev, sg) + offset;
- ctx->single.sge.length = ib_sg_dma_len(dev, sg) - offset;
+ ctx->single.sge.addr = sg_dma_address(sg) + offset;
+ ctx->single.sge.length = sg_dma_len(sg) - offset;
memset(rdma_wr, 0, sizeof(*rdma_wr));
if (dir == DMA_TO_DEVICE)
@@ -294,7 +292,7 @@ int rdma_rw_ctx_init(struct rdma_rw_ctx *ctx, struct ib_qp *qp, u8 port_num,
* Skip to the S/G entry that sg_offset falls into:
*/
for (;;) {
- u32 len = ib_sg_dma_len(dev, sg);
+ u32 len = sg_dma_len(sg);
if (sg_offset < len)
break;
diff --git a/drivers/infiniband/core/sa_query.c b/drivers/infiniband/core/sa_query.c
index 97e6d7b69abf..7925e45ea88a 100644
--- a/drivers/infiniband/core/sa_query.c
+++ b/drivers/infiniband/core/sa_query.c
@@ -2342,9 +2342,7 @@ static void ib_sa_add_one(struct ib_device *device)
s = rdma_start_port(device);
e = rdma_end_port(device);
- sa_dev = kzalloc(sizeof *sa_dev +
- (e - s + 1) * sizeof (struct ib_sa_port),
- GFP_KERNEL);
+ sa_dev = kzalloc(struct_size(sa_dev, port, e - s + 1), GFP_KERNEL);
if (!sa_dev)
return;
diff --git a/drivers/infiniband/core/security.c b/drivers/infiniband/core/security.c
index 1efadbccf394..1ab423b19f77 100644
--- a/drivers/infiniband/core/security.c
+++ b/drivers/infiniband/core/security.c
@@ -39,22 +39,25 @@
#include "core_priv.h"
#include "mad_priv.h"
+static LIST_HEAD(mad_agent_list);
+/* Lock to protect mad_agent_list */
+static DEFINE_SPINLOCK(mad_agent_list_lock);
+
static struct pkey_index_qp_list *get_pkey_idx_qp_list(struct ib_port_pkey *pp)
{
struct pkey_index_qp_list *pkey = NULL;
struct pkey_index_qp_list *tmp_pkey;
struct ib_device *dev = pp->sec->dev;
- spin_lock(&dev->port_pkey_list[pp->port_num].list_lock);
- list_for_each_entry(tmp_pkey,
- &dev->port_pkey_list[pp->port_num].pkey_list,
- pkey_index_list) {
+ spin_lock(&dev->port_data[pp->port_num].pkey_list_lock);
+ list_for_each_entry (tmp_pkey, &dev->port_data[pp->port_num].pkey_list,
+ pkey_index_list) {
if (tmp_pkey->pkey_index == pp->pkey_index) {
pkey = tmp_pkey;
break;
}
}
- spin_unlock(&dev->port_pkey_list[pp->port_num].list_lock);
+ spin_unlock(&dev->port_data[pp->port_num].pkey_list_lock);
return pkey;
}
@@ -259,12 +262,12 @@ static int port_pkey_list_insert(struct ib_port_pkey *pp)
if (!pkey)
return -ENOMEM;
- spin_lock(&dev->port_pkey_list[port_num].list_lock);
+ spin_lock(&dev->port_data[port_num].pkey_list_lock);
/* Check for the PKey again. A racing process may
* have created it.
*/
list_for_each_entry(tmp_pkey,
- &dev->port_pkey_list[port_num].pkey_list,
+ &dev->port_data[port_num].pkey_list,
pkey_index_list) {
if (tmp_pkey->pkey_index == pp->pkey_index) {
kfree(pkey);
@@ -279,9 +282,9 @@ static int port_pkey_list_insert(struct ib_port_pkey *pp)
spin_lock_init(&pkey->qp_list_lock);
INIT_LIST_HEAD(&pkey->qp_list);
list_add(&pkey->pkey_index_list,
- &dev->port_pkey_list[port_num].pkey_list);
+ &dev->port_data[port_num].pkey_list);
}
- spin_unlock(&dev->port_pkey_list[port_num].list_lock);
+ spin_unlock(&dev->port_data[port_num].pkey_list_lock);
}
spin_lock(&pkey->qp_list_lock);
@@ -418,12 +421,15 @@ void ib_close_shared_qp_security(struct ib_qp_security *sec)
int ib_create_qp_security(struct ib_qp *qp, struct ib_device *dev)
{
- u8 i = rdma_start_port(dev);
+ unsigned int i;
bool is_ib = false;
int ret;
- while (i <= rdma_end_port(dev) && !is_ib)
+ rdma_for_each_port (dev, i) {
is_ib = rdma_protocol_ib(dev, i++);
+ if (is_ib)
+ break;
+ }
/* If this isn't an IB device don't create the security context */
if (!is_ib)
@@ -544,9 +550,8 @@ void ib_security_cache_change(struct ib_device *device,
{
struct pkey_index_qp_list *pkey;
- list_for_each_entry(pkey,
- &device->port_pkey_list[port_num].pkey_list,
- pkey_index_list) {
+ list_for_each_entry (pkey, &device->port_data[port_num].pkey_list,
+ pkey_index_list) {
check_pkey_qps(pkey,
device,
port_num,
@@ -554,21 +559,19 @@ void ib_security_cache_change(struct ib_device *device,
}
}
-void ib_security_destroy_port_pkey_list(struct ib_device *device)
+void ib_security_release_port_pkey_list(struct ib_device *device)
{
struct pkey_index_qp_list *pkey, *tmp_pkey;
- int i;
+ unsigned int i;
- for (i = rdma_start_port(device); i <= rdma_end_port(device); i++) {
- spin_lock(&device->port_pkey_list[i].list_lock);
+ rdma_for_each_port (device, i) {
list_for_each_entry_safe(pkey,
tmp_pkey,
- &device->port_pkey_list[i].pkey_list,
+ &device->port_data[i].pkey_list,
pkey_index_list) {
list_del(&pkey->pkey_index_list);
kfree(pkey);
}
- spin_unlock(&device->port_pkey_list[i].list_lock);
}
}
@@ -676,19 +679,18 @@ static int ib_security_pkey_access(struct ib_device *dev,
return security_ib_pkey_access(sec, subnet_prefix, pkey);
}
-static int ib_mad_agent_security_change(struct notifier_block *nb,
- unsigned long event,
- void *data)
+void ib_mad_agent_security_change(void)
{
- struct ib_mad_agent *ag = container_of(nb, struct ib_mad_agent, lsm_nb);
-
- if (event != LSM_POLICY_CHANGE)
- return NOTIFY_DONE;
-
- ag->smp_allowed = !security_ib_endport_manage_subnet(
- ag->security, dev_name(&ag->device->dev), ag->port_num);
-
- return NOTIFY_OK;
+ struct ib_mad_agent *ag;
+
+ spin_lock(&mad_agent_list_lock);
+ list_for_each_entry(ag,
+ &mad_agent_list,
+ mad_agent_sec_list)
+ WRITE_ONCE(ag->smp_allowed,
+ !security_ib_endport_manage_subnet(ag->security,
+ dev_name(&ag->device->dev), ag->port_num));
+ spin_unlock(&mad_agent_list_lock);
}
int ib_mad_agent_security_setup(struct ib_mad_agent *agent,
@@ -699,6 +701,8 @@ int ib_mad_agent_security_setup(struct ib_mad_agent *agent,
if (!rdma_protocol_ib(agent->device, agent->port_num))
return 0;
+ INIT_LIST_HEAD(&agent->mad_agent_sec_list);
+
ret = security_ib_alloc_security(&agent->security);
if (ret)
return ret;
@@ -706,20 +710,22 @@ int ib_mad_agent_security_setup(struct ib_mad_agent *agent,
if (qp_type != IB_QPT_SMI)
return 0;
+ spin_lock(&mad_agent_list_lock);
ret = security_ib_endport_manage_subnet(agent->security,
dev_name(&agent->device->dev),
agent->port_num);
if (ret)
- return ret;
+ goto free_security;
- agent->lsm_nb.notifier_call = ib_mad_agent_security_change;
- ret = register_lsm_notifier(&agent->lsm_nb);
- if (ret)
- return ret;
-
- agent->smp_allowed = true;
- agent->lsm_nb_reg = true;
+ WRITE_ONCE(agent->smp_allowed, true);
+ list_add(&agent->mad_agent_sec_list, &mad_agent_list);
+ spin_unlock(&mad_agent_list_lock);
return 0;
+
+free_security:
+ spin_unlock(&mad_agent_list_lock);
+ security_ib_free_security(agent->security);
+ return ret;
}
void ib_mad_agent_security_cleanup(struct ib_mad_agent *agent)
@@ -727,9 +733,13 @@ void ib_mad_agent_security_cleanup(struct ib_mad_agent *agent)
if (!rdma_protocol_ib(agent->device, agent->port_num))
return;
+ if (agent->qp->qp_type == IB_QPT_SMI) {
+ spin_lock(&mad_agent_list_lock);
+ list_del(&agent->mad_agent_sec_list);
+ spin_unlock(&mad_agent_list_lock);
+ }
+
security_ib_free_security(agent->security);
- if (agent->lsm_nb_reg)
- unregister_lsm_notifier(&agent->lsm_nb);
}
int ib_mad_enforce_security(struct ib_mad_agent_private *map, u16 pkey_index)
@@ -738,7 +748,7 @@ int ib_mad_enforce_security(struct ib_mad_agent_private *map, u16 pkey_index)
return 0;
if (map->agent.qp->qp_type == IB_QPT_SMI) {
- if (!map->agent.smp_allowed)
+ if (!READ_ONCE(map->agent.smp_allowed))
return -EACCES;
return 0;
}
diff --git a/drivers/infiniband/core/sysfs.c b/drivers/infiniband/core/sysfs.c
index 80f68eb0ba5c..9b6a065bdfa5 100644
--- a/drivers/infiniband/core/sysfs.c
+++ b/drivers/infiniband/core/sysfs.c
@@ -1015,9 +1015,7 @@ err_free_stats:
return;
}
-static int add_port(struct ib_device *device, int port_num,
- int (*port_callback)(struct ib_device *,
- u8, struct kobject *))
+static int add_port(struct ib_device *device, int port_num)
{
struct ib_port *p;
struct ib_port_attr attr;
@@ -1113,8 +1111,8 @@ static int add_port(struct ib_device *device, int port_num,
if (ret)
goto err_free_pkey;
- if (port_callback) {
- ret = port_callback(device, port_num, &p->kobj);
+ if (device->ops.init_port) {
+ ret = device->ops.init_port(device, port_num, &p->kobj);
if (ret)
goto err_remove_pkey;
}
@@ -1189,7 +1187,7 @@ err_put:
static ssize_t node_type_show(struct device *device,
struct device_attribute *attr, char *buf)
{
- struct ib_device *dev = container_of(device, struct ib_device, dev);
+ struct ib_device *dev = rdma_device_to_ibdev(device);
switch (dev->node_type) {
case RDMA_NODE_IB_CA: return sprintf(buf, "%d: CA\n", dev->node_type);
@@ -1206,7 +1204,7 @@ static DEVICE_ATTR_RO(node_type);
static ssize_t sys_image_guid_show(struct device *device,
struct device_attribute *dev_attr, char *buf)
{
- struct ib_device *dev = container_of(device, struct ib_device, dev);
+ struct ib_device *dev = rdma_device_to_ibdev(device);
return sprintf(buf, "%04x:%04x:%04x:%04x\n",
be16_to_cpu(((__be16 *) &dev->attrs.sys_image_guid)[0]),
@@ -1219,7 +1217,7 @@ static DEVICE_ATTR_RO(sys_image_guid);
static ssize_t node_guid_show(struct device *device,
struct device_attribute *attr, char *buf)
{
- struct ib_device *dev = container_of(device, struct ib_device, dev);
+ struct ib_device *dev = rdma_device_to_ibdev(device);
return sprintf(buf, "%04x:%04x:%04x:%04x\n",
be16_to_cpu(((__be16 *) &dev->node_guid)[0]),
@@ -1232,7 +1230,7 @@ static DEVICE_ATTR_RO(node_guid);
static ssize_t node_desc_show(struct device *device,
struct device_attribute *attr, char *buf)
{
- struct ib_device *dev = container_of(device, struct ib_device, dev);
+ struct ib_device *dev = rdma_device_to_ibdev(device);
return sprintf(buf, "%.64s\n", dev->node_desc);
}
@@ -1241,7 +1239,7 @@ static ssize_t node_desc_store(struct device *device,
struct device_attribute *attr,
const char *buf, size_t count)
{
- struct ib_device *dev = container_of(device, struct ib_device, dev);
+ struct ib_device *dev = rdma_device_to_ibdev(device);
struct ib_device_modify desc = {};
int ret;
@@ -1260,7 +1258,7 @@ static DEVICE_ATTR_RW(node_desc);
static ssize_t fw_ver_show(struct device *device, struct device_attribute *attr,
char *buf)
{
- struct ib_device *dev = container_of(device, struct ib_device, dev);
+ struct ib_device *dev = rdma_device_to_ibdev(device);
ib_get_device_fw_str(dev, buf);
strlcat(buf, "\n", IB_FW_VERSION_NAME_MAX);
@@ -1277,21 +1275,21 @@ static struct attribute *ib_dev_attrs[] = {
NULL,
};
-static const struct attribute_group dev_attr_group = {
+const struct attribute_group ib_dev_attr_group = {
.attrs = ib_dev_attrs,
};
-static void free_port_list_attributes(struct ib_device *device)
+static void ib_free_port_attrs(struct ib_device *device)
{
struct kobject *p, *t;
list_for_each_entry_safe(p, t, &device->port_list, entry) {
struct ib_port *port = container_of(p, struct ib_port, kobj);
+
list_del(&p->entry);
- if (port->hw_stats) {
- kfree(port->hw_stats);
+ if (port->hw_stats_ag)
free_hsag(&port->kobj, port->hw_stats_ag);
- }
+ kfree(port->hw_stats);
if (port->pma_table)
sysfs_remove_group(p, port->pma_table);
@@ -1308,62 +1306,47 @@ static void free_port_list_attributes(struct ib_device *device)
kobject_put(device->ports_kobj);
}
-int ib_device_register_sysfs(struct ib_device *device,
- int (*port_callback)(struct ib_device *,
- u8, struct kobject *))
+static int ib_setup_port_attrs(struct ib_device *device)
{
- struct device *class_dev = &device->dev;
+ unsigned int port;
int ret;
- int i;
-
- device->groups[0] = &dev_attr_group;
- class_dev->groups = device->groups;
- ret = device_add(class_dev);
- if (ret)
- goto err;
-
- device->ports_kobj = kobject_create_and_add("ports", &class_dev->kobj);
- if (!device->ports_kobj) {
- ret = -ENOMEM;
- goto err_put;
- }
+ device->ports_kobj = kobject_create_and_add("ports", &device->dev.kobj);
+ if (!device->ports_kobj)
+ return -ENOMEM;
- if (rdma_cap_ib_switch(device)) {
- ret = add_port(device, 0, port_callback);
+ rdma_for_each_port (device, port) {
+ ret = add_port(device, port);
if (ret)
goto err_put;
- } else {
- for (i = 1; i <= device->phys_port_cnt; ++i) {
- ret = add_port(device, i, port_callback);
- if (ret)
- goto err_put;
- }
}
- if (device->ops.alloc_hw_stats)
- setup_hw_stats(device, NULL, 0);
-
return 0;
err_put:
- free_port_list_attributes(device);
- device_del(class_dev);
-err:
+ ib_free_port_attrs(device);
return ret;
}
-void ib_device_unregister_sysfs(struct ib_device *device)
+int ib_device_register_sysfs(struct ib_device *device)
{
- /* Hold device until ib_dealloc_device() */
- get_device(&device->dev);
+ int ret;
+
+ ret = ib_setup_port_attrs(device);
+ if (ret)
+ return ret;
+
+ if (device->ops.alloc_hw_stats)
+ setup_hw_stats(device, NULL, 0);
- free_port_list_attributes(device);
+ return 0;
+}
- if (device->hw_stats) {
- kfree(device->hw_stats);
+void ib_device_unregister_sysfs(struct ib_device *device)
+{
+ if (device->hw_stats_ag)
free_hsag(&device->dev.kobj, device->hw_stats_ag);
- }
+ kfree(device->hw_stats);
- device_unregister(&device->dev);
+ ib_free_port_attrs(device);
}
diff --git a/drivers/infiniband/core/ucma.c b/drivers/infiniband/core/ucma.c
index 01d68ed46c1b..7468b26b8a01 100644
--- a/drivers/infiniband/core/ucma.c
+++ b/drivers/infiniband/core/ucma.c
@@ -1236,6 +1236,13 @@ static int ucma_set_option_id(struct ucma_context *ctx, int optname,
}
ret = rdma_set_afonly(ctx->cm_id, *((int *) optval) ? 1 : 0);
break;
+ case RDMA_OPTION_ID_ACK_TIMEOUT:
+ if (optlen != sizeof(u8)) {
+ ret = -EINVAL;
+ break;
+ }
+ ret = rdma_set_ack_timeout(ctx->cm_id, *((u8 *)optval));
+ break;
default:
ret = -ENOSYS;
}
diff --git a/drivers/infiniband/core/umem.c b/drivers/infiniband/core/umem.c
index c6144df47ea4..fe5551562dbc 100644
--- a/drivers/infiniband/core/umem.c
+++ b/drivers/infiniband/core/umem.c
@@ -72,15 +72,16 @@ static void __ib_umem_release(struct ib_device *dev, struct ib_umem *umem, int d
* If access flags indicate ODP memory, avoid pinning. Instead, stores
* the mm for future page fault handling in conjunction with MMU notifiers.
*
- * @context: userspace context to pin memory for
+ * @udata: userspace context to pin memory for
* @addr: userspace virtual address to start at
* @size: length of region to pin
* @access: IB_ACCESS_xxx flags for memory being pinned
* @dmasync: flush in-flight DMA when the memory region is written
*/
-struct ib_umem *ib_umem_get(struct ib_ucontext *context, unsigned long addr,
+struct ib_umem *ib_umem_get(struct ib_udata *udata, unsigned long addr,
size_t size, int access, int dmasync)
{
+ struct ib_ucontext *context;
struct ib_umem *umem;
struct page **page_list;
struct vm_area_struct **vma_list;
@@ -95,6 +96,14 @@ struct ib_umem *ib_umem_get(struct ib_ucontext *context, unsigned long addr,
struct scatterlist *sg, *sg_list_start;
unsigned int gup_flags = FOLL_WRITE;
+ if (!udata)
+ return ERR_PTR(-EIO);
+
+ context = container_of(udata, struct uverbs_attr_bundle, driver_udata)
+ ->context;
+ if (!context)
+ return ERR_PTR(-EIO);
+
if (dmasync)
dma_attrs |= DMA_ATTR_WRITE_BARRIER;
@@ -160,15 +169,12 @@ struct ib_umem *ib_umem_get(struct ib_ucontext *context, unsigned long addr,
lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT;
- down_write(&mm->mmap_sem);
- if (check_add_overflow(mm->pinned_vm, npages, &new_pinned) ||
- (new_pinned > lock_limit && !capable(CAP_IPC_LOCK))) {
- up_write(&mm->mmap_sem);
+ new_pinned = atomic64_add_return(npages, &mm->pinned_vm);
+ if (new_pinned > lock_limit && !capable(CAP_IPC_LOCK)) {
+ atomic64_sub(npages, &mm->pinned_vm);
ret = -ENOMEM;
goto out;
}
- mm->pinned_vm = new_pinned;
- up_write(&mm->mmap_sem);
cur_base = addr & PAGE_MASK;
@@ -228,9 +234,7 @@ struct ib_umem *ib_umem_get(struct ib_ucontext *context, unsigned long addr,
umem_release:
__ib_umem_release(context->device, umem, 0);
vma:
- down_write(&mm->mmap_sem);
- mm->pinned_vm -= ib_umem_num_pages(umem);
- up_write(&mm->mmap_sem);
+ atomic64_sub(ib_umem_num_pages(umem), &mm->pinned_vm);
out:
if (vma_list)
free_page((unsigned long) vma_list);
@@ -253,25 +257,12 @@ static void __ib_umem_release_tail(struct ib_umem *umem)
kfree(umem);
}
-static void ib_umem_release_defer(struct work_struct *work)
-{
- struct ib_umem *umem = container_of(work, struct ib_umem, work);
-
- down_write(&umem->owning_mm->mmap_sem);
- umem->owning_mm->pinned_vm -= ib_umem_num_pages(umem);
- up_write(&umem->owning_mm->mmap_sem);
-
- __ib_umem_release_tail(umem);
-}
-
/**
* ib_umem_release - release memory pinned with ib_umem_get
* @umem: umem struct to release
*/
void ib_umem_release(struct ib_umem *umem)
{
- struct ib_ucontext *context = umem->context;
-
if (umem->is_odp) {
ib_umem_odp_release(to_ib_umem_odp(umem));
__ib_umem_release_tail(umem);
@@ -280,26 +271,7 @@ void ib_umem_release(struct ib_umem *umem)
__ib_umem_release(umem->context->device, umem, 1);
- /*
- * We may be called with the mm's mmap_sem already held. This
- * can happen when a userspace munmap() is the call that drops
- * the last reference to our file and calls our release
- * method. If there are memory regions to destroy, we'll end
- * up here and not be able to take the mmap_sem. In that case
- * we defer the vm_locked accounting a workqueue.
- */
- if (context->closing) {
- if (!down_write_trylock(&umem->owning_mm->mmap_sem)) {
- INIT_WORK(&umem->work, ib_umem_release_defer);
- queue_work(ib_wq, &umem->work);
- return;
- }
- } else {
- down_write(&umem->owning_mm->mmap_sem);
- }
- umem->owning_mm->pinned_vm -= ib_umem_num_pages(umem);
- up_write(&umem->owning_mm->mmap_sem);
-
+ atomic64_sub(ib_umem_num_pages(umem), &umem->owning_mm->pinned_vm);
__ib_umem_release_tail(umem);
}
EXPORT_SYMBOL(ib_umem_release);
diff --git a/drivers/infiniband/core/umem_odp.c b/drivers/infiniband/core/umem_odp.c
index acb882f279cb..e6ec79ad9cc8 100644
--- a/drivers/infiniband/core/umem_odp.c
+++ b/drivers/infiniband/core/umem_odp.c
@@ -40,6 +40,7 @@
#include <linux/vmalloc.h>
#include <linux/hugetlb.h>
#include <linux/interval_tree_generic.h>
+#include <linux/pagemap.h>
#include <rdma/ib_verbs.h>
#include <rdma/ib_umem.h>
@@ -299,7 +300,7 @@ static void free_per_mm(struct rcu_head *rcu)
kfree(container_of(rcu, struct ib_ucontext_per_mm, rcu));
}
-void put_per_mm(struct ib_umem_odp *umem_odp)
+static void put_per_mm(struct ib_umem_odp *umem_odp)
{
struct ib_ucontext_per_mm *per_mm = umem_odp->per_mm;
struct ib_ucontext *ctx = umem_odp->umem.context;
@@ -332,9 +333,10 @@ void put_per_mm(struct ib_umem_odp *umem_odp)
mmu_notifier_call_srcu(&per_mm->rcu, free_per_mm);
}
-struct ib_umem_odp *ib_alloc_odp_umem(struct ib_ucontext_per_mm *per_mm,
+struct ib_umem_odp *ib_alloc_odp_umem(struct ib_umem_odp *root,
unsigned long addr, size_t size)
{
+ struct ib_ucontext_per_mm *per_mm = root->per_mm;
struct ib_ucontext *ctx = per_mm->context;
struct ib_umem_odp *odp_data;
struct ib_umem *umem;
@@ -349,7 +351,7 @@ struct ib_umem_odp *ib_alloc_odp_umem(struct ib_ucontext_per_mm *per_mm,
umem->length = size;
umem->address = addr;
umem->page_shift = PAGE_SHIFT;
- umem->writable = 1;
+ umem->writable = root->umem.writable;
umem->is_odp = 1;
odp_data->per_mm = per_mm;
umem->owning_mm = per_mm->mm;
@@ -617,7 +619,7 @@ int ib_umem_odp_map_dma_pages(struct ib_umem_odp *umem_odp, u64 user_virt,
* mmget_not_zero will fail in this case.
*/
owning_process = get_pid_task(umem_odp->per_mm->tgid, PIDTYPE_PID);
- if (WARN_ON(!mmget_not_zero(umem_odp->umem.owning_mm))) {
+ if (!owning_process || !mmget_not_zero(owning_mm)) {
ret = -EINVAL;
goto out_put_task;
}
@@ -684,9 +686,14 @@ int ib_umem_odp_map_dma_pages(struct ib_umem_odp *umem_odp, u64 user_virt,
mutex_unlock(&umem_odp->umem_mutex);
if (ret < 0) {
- /* Release left over pages when handling errors. */
- for (++j; j < npages; ++j)
- put_page(local_page_list[j]);
+ /*
+ * Release pages, remembering that the first page
+ * to hit an error was already released by
+ * ib_umem_odp_map_dma_single_page().
+ */
+ if (npages - (j + 1) > 0)
+ release_pages(&local_page_list[j+1],
+ npages - (j + 1));
break;
}
}
diff --git a/drivers/infiniband/core/user_mad.c b/drivers/infiniband/core/user_mad.c
index de8d31ab8945..02b7947ab215 100644
--- a/drivers/infiniband/core/user_mad.c
+++ b/drivers/infiniband/core/user_mad.c
@@ -957,19 +957,22 @@ static int ib_umad_open(struct inode *inode, struct file *filp)
{
struct ib_umad_port *port;
struct ib_umad_file *file;
- int ret = -ENXIO;
+ int ret = 0;
port = container_of(inode->i_cdev, struct ib_umad_port, cdev);
mutex_lock(&port->file_mutex);
- if (!port->ib_dev)
+ if (!port->ib_dev) {
+ ret = -ENXIO;
goto out;
+ }
- ret = -ENOMEM;
- file = kzalloc(sizeof *file, GFP_KERNEL);
- if (!file)
+ file = kzalloc(sizeof(*file), GFP_KERNEL);
+ if (!file) {
+ ret = -ENOMEM;
goto out;
+ }
mutex_init(&file->mutex);
spin_lock_init(&file->send_lock);
@@ -982,14 +985,7 @@ static int ib_umad_open(struct inode *inode, struct file *filp)
list_add_tail(&file->port_list, &port->file_list);
- ret = nonseekable_open(inode, filp);
- if (ret) {
- list_del(&file->port_list);
- kfree(file);
- goto out;
- }
-
- ib_umad_dev_get(port->umad_dev);
+ nonseekable_open(inode, filp);
out:
mutex_unlock(&port->file_mutex);
return ret;
@@ -998,7 +994,6 @@ out:
static int ib_umad_close(struct inode *inode, struct file *filp)
{
struct ib_umad_file *file = filp->private_data;
- struct ib_umad_device *dev = file->port->umad_dev;
struct ib_umad_packet *packet, *tmp;
int already_dead;
int i;
@@ -1027,7 +1022,6 @@ static int ib_umad_close(struct inode *inode, struct file *filp)
mutex_unlock(&file->port->file_mutex);
kfree(file);
- ib_umad_dev_put(dev);
return 0;
}
@@ -1073,17 +1067,9 @@ static int ib_umad_sm_open(struct inode *inode, struct file *filp)
filp->private_data = port;
- ret = nonseekable_open(inode, filp);
- if (ret)
- goto err_clr_sm_cap;
-
- ib_umad_dev_get(port->umad_dev);
+ nonseekable_open(inode, filp);
return 0;
-err_clr_sm_cap:
- swap(props.set_port_cap_mask, props.clr_port_cap_mask);
- ib_modify_port(port->ib_dev, port->port_num, 0, &props);
-
err_up_sem:
up(&port->sm_sem);
@@ -1106,7 +1092,6 @@ static int ib_umad_sm_close(struct inode *inode, struct file *filp)
up(&port->sm_sem);
- ib_umad_dev_put(port->umad_dev);
return ret;
}
@@ -1283,10 +1268,12 @@ static void ib_umad_kill_port(struct ib_umad_port *port)
mutex_unlock(&port->file_mutex);
cdev_device_del(&port->sm_cdev, &port->sm_dev);
- put_device(&port->sm_dev);
cdev_device_del(&port->cdev, &port->dev);
- put_device(&port->dev);
ida_free(&umad_ida, port->dev_num);
+
+ /* balances device_initialize() */
+ put_device(&port->sm_dev);
+ put_device(&port->dev);
}
static void ib_umad_add_one(struct ib_device *device)
@@ -1329,21 +1316,24 @@ err:
ib_umad_kill_port(&umad_dev->ports[i - s]);
}
free:
+ /* balances kref_init */
ib_umad_dev_put(umad_dev);
}
static void ib_umad_remove_one(struct ib_device *device, void *client_data)
{
struct ib_umad_device *umad_dev = client_data;
- int i;
+ unsigned int i;
if (!umad_dev)
return;
- for (i = 0; i <= rdma_end_port(device) - rdma_start_port(device); ++i) {
- if (rdma_cap_ib_mad(device, i + rdma_start_port(device)))
- ib_umad_kill_port(&umad_dev->ports[i]);
+ rdma_for_each_port (device, i) {
+ if (rdma_cap_ib_mad(device, i))
+ ib_umad_kill_port(
+ &umad_dev->ports[i - rdma_start_port(device)]);
}
+ /* balances kref_init() */
ib_umad_dev_put(umad_dev);
}
diff --git a/drivers/infiniband/core/uverbs_cmd.c b/drivers/infiniband/core/uverbs_cmd.c
index 3317300ab036..062a86c04123 100644
--- a/drivers/infiniband/core/uverbs_cmd.c
+++ b/drivers/infiniband/core/uverbs_cmd.c
@@ -224,12 +224,13 @@ static int ib_uverbs_get_context(struct uverbs_attr_bundle *attrs)
if (ret)
goto err;
- ucontext = ib_dev->ops.alloc_ucontext(ib_dev, &attrs->driver_udata);
- if (IS_ERR(ucontext)) {
- ret = PTR_ERR(ucontext);
+ ucontext = rdma_zalloc_drv_obj(ib_dev, ib_ucontext);
+ if (!ucontext) {
+ ret = -ENOMEM;
goto err_alloc;
}
+ ucontext->res.type = RDMA_RESTRACK_CTX;
ucontext->device = ib_dev;
ucontext->cg_obj = cg_obj;
/* ufile is required when some objects are released */
@@ -238,15 +239,8 @@ static int ib_uverbs_get_context(struct uverbs_attr_bundle *attrs)
ucontext->closing = false;
ucontext->cleanup_retryable = false;
-#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
mutex_init(&ucontext->per_mm_list_lock);
INIT_LIST_HEAD(&ucontext->per_mm_list);
- if (!(ib_dev->attrs.device_cap_flags & IB_DEVICE_ON_DEMAND_PAGING))
- ucontext->invalidate_range = NULL;
-
-#endif
-
- resp.num_comp_vectors = file->device->num_comp_vectors;
ret = get_unused_fd_flags(O_CLOEXEC);
if (ret < 0)
@@ -259,15 +253,22 @@ static int ib_uverbs_get_context(struct uverbs_attr_bundle *attrs)
goto err_fd;
}
+ resp.num_comp_vectors = file->device->num_comp_vectors;
+
ret = uverbs_response(attrs, &resp, sizeof(resp));
if (ret)
goto err_file;
- fd_install(resp.async_fd, filp);
+ ret = ib_dev->ops.alloc_ucontext(ucontext, &attrs->driver_udata);
+ if (ret)
+ goto err_file;
+ if (!(ib_dev->attrs.device_cap_flags & IB_DEVICE_ON_DEMAND_PAGING))
+ ucontext->invalidate_range = NULL;
- ucontext->res.type = RDMA_RESTRACK_CTX;
rdma_restrack_uadd(&ucontext->res);
+ fd_install(resp.async_fd, filp);
+
/*
* Make sure that ib_uverbs_get_ucontext() sees the pointer update
* only after all writes to setup the ucontext have completed
@@ -286,7 +287,7 @@ err_fd:
put_unused_fd(resp.async_fd);
err_free:
- ib_dev->ops.dealloc_ucontext(ucontext);
+ kfree(ucontext);
err_alloc:
ib_rdmacg_uncharge(&cg_obj, ib_dev, RDMACG_RESOURCE_HCA_HANDLE);
@@ -410,9 +411,9 @@ static int ib_uverbs_alloc_pd(struct uverbs_attr_bundle *attrs)
if (IS_ERR(uobj))
return PTR_ERR(uobj);
- pd = ib_dev->ops.alloc_pd(ib_dev, uobj->context, &attrs->driver_udata);
- if (IS_ERR(pd)) {
- ret = PTR_ERR(pd);
+ pd = rdma_zalloc_drv_obj(ib_dev, ib_pd);
+ if (!pd) {
+ ret = -ENOMEM;
goto err;
}
@@ -420,11 +421,15 @@ static int ib_uverbs_alloc_pd(struct uverbs_attr_bundle *attrs)
pd->uobject = uobj;
pd->__internal_mr = NULL;
atomic_set(&pd->usecnt, 0);
+ pd->res.type = RDMA_RESTRACK_PD;
+
+ ret = ib_dev->ops.alloc_pd(pd, uobj->context, &attrs->driver_udata);
+ if (ret)
+ goto err_alloc;
uobj->object = pd;
memset(&resp, 0, sizeof resp);
resp.pd_handle = uobj->id;
- pd->res.type = RDMA_RESTRACK_PD;
rdma_restrack_uadd(&pd->res);
ret = uverbs_response(attrs, &resp, sizeof(resp));
@@ -435,7 +440,9 @@ static int ib_uverbs_alloc_pd(struct uverbs_attr_bundle *attrs)
err_copy:
ib_dealloc_pd(pd);
-
+ pd = NULL;
+err_alloc:
+ kfree(pd);
err:
uobj_alloc_abort(uobj);
return ret;
@@ -822,14 +829,13 @@ static int ib_uverbs_rereg_mr(struct uverbs_attr_bundle *attrs)
cmd.length, cmd.hca_va,
cmd.access_flags, pd,
&attrs->driver_udata);
- if (!ret) {
- if (cmd.flags & IB_MR_REREG_PD) {
- atomic_inc(&pd->usecnt);
- mr->pd = pd;
- atomic_dec(&old_pd->usecnt);
- }
- } else {
+ if (ret)
goto put_uobj_pd;
+
+ if (cmd.flags & IB_MR_REREG_PD) {
+ atomic_inc(&pd->usecnt);
+ mr->pd = pd;
+ atomic_dec(&old_pd->usecnt);
}
memset(&resp, 0, sizeof(resp));
@@ -884,6 +890,11 @@ static int ib_uverbs_alloc_mw(struct uverbs_attr_bundle *attrs)
goto err_free;
}
+ if (cmd.mw_type != IB_MW_TYPE_1 && cmd.mw_type != IB_MW_TYPE_2) {
+ ret = -EINVAL;
+ goto err_put;
+ }
+
mw = pd->device->ops.alloc_mw(pd, cmd.mw_type, &attrs->driver_udata);
if (IS_ERR(mw)) {
ret = PTR_ERR(mw);
@@ -1184,12 +1195,11 @@ static int ib_uverbs_poll_cq(struct uverbs_attr_bundle *attrs)
ret = -EFAULT;
goto out_put;
}
+ ret = 0;
if (uverbs_attr_is_valid(attrs, UVERBS_ATTR_CORE_OUT))
ret = uverbs_output_written(attrs, UVERBS_ATTR_CORE_OUT);
- ret = 0;
-
out_put:
uobj_put_obj_read(cq);
return ret;
@@ -2632,7 +2642,7 @@ void flow_resources_add(struct ib_uflow_resources *uflow_res,
}
EXPORT_SYMBOL(flow_resources_add);
-static int kern_spec_to_ib_spec_action(const struct uverbs_attr_bundle *attrs,
+static int kern_spec_to_ib_spec_action(struct uverbs_attr_bundle *attrs,
struct ib_uverbs_flow_spec *kern_spec,
union ib_flow_spec *ib_spec,
struct ib_uflow_resources *uflow_res)
@@ -3618,7 +3628,6 @@ static int ib_uverbs_ex_query_device(struct uverbs_attr_bundle *attrs)
copy_query_dev_fields(ucontext, &resp.base, &attr);
-#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
resp.odp_caps.general_caps = attr.odp_caps.general_caps;
resp.odp_caps.per_transport_caps.rc_odp_caps =
attr.odp_caps.per_transport_caps.rc_odp_caps;
@@ -3626,7 +3635,7 @@ static int ib_uverbs_ex_query_device(struct uverbs_attr_bundle *attrs)
attr.odp_caps.per_transport_caps.uc_odp_caps;
resp.odp_caps.per_transport_caps.ud_odp_caps =
attr.odp_caps.per_transport_caps.ud_odp_caps;
-#endif
+ resp.xrc_odp_caps = attr.odp_caps.per_transport_caps.xrc_odp_caps;
resp.timestamp_mask = attr.timestamp_mask;
resp.hca_core_clock = attr.hca_core_clock;
diff --git a/drivers/infiniband/core/uverbs_ioctl.c b/drivers/infiniband/core/uverbs_ioctl.c
index 0ca04d224015..e1379949e663 100644
--- a/drivers/infiniband/core/uverbs_ioctl.c
+++ b/drivers/infiniband/core/uverbs_ioctl.c
@@ -213,6 +213,7 @@ static int uverbs_process_idrs_array(struct bundle_priv *pbundle,
ret = PTR_ERR(attr->uobjects[i]);
break;
}
+ pbundle->bundle.context = attr->uobjects[i]->context;
}
attr->len = i;
@@ -330,6 +331,7 @@ static int uverbs_process_attr(struct bundle_priv *pbundle,
uattr->data_s64);
if (IS_ERR(o_attr->uobject))
return PTR_ERR(o_attr->uobject);
+ pbundle->bundle.context = o_attr->uobject->context;
__set_bit(attr_bkey, pbundle->uobj_finalize);
if (spec->u.obj.access == UVERBS_ACCESS_NEW) {
@@ -592,6 +594,7 @@ static int ib_uverbs_cmd_verbs(struct ib_uverbs_file *ufile,
pbundle->method_elm = method_elm;
pbundle->method_key = attrs_iter.index;
pbundle->bundle.ufile = ufile;
+ pbundle->bundle.context = NULL; /* only valid if bundle has uobject */
pbundle->radix = &uapi->radix;
pbundle->radix_slots = slot;
pbundle->radix_slots_len = radix_tree_chunk_size(&attrs_iter);
diff --git a/drivers/infiniband/core/uverbs_main.c b/drivers/infiniband/core/uverbs_main.c
index 5f366838b7ff..70b7d80431a9 100644
--- a/drivers/infiniband/core/uverbs_main.c
+++ b/drivers/infiniband/core/uverbs_main.c
@@ -695,6 +695,7 @@ static ssize_t ib_uverbs_write(struct file *filp, const char __user *buf,
memset(bundle.attr_present, 0, sizeof(bundle.attr_present));
bundle.ufile = file;
+ bundle.context = NULL; /* only valid if bundle has uobject */
if (!method_elm->is_ex) {
size_t in_len = hdr.in_words * 4 - sizeof(hdr);
size_t out_len = hdr.out_words * 4;
@@ -1135,6 +1136,7 @@ static const struct file_operations uverbs_mmap_fops = {
static struct ib_client uverbs_client = {
.name = "uverbs",
+ .no_kverbs_req = true,
.add = ib_uverbs_add_one,
.remove = ib_uverbs_remove_one
};
diff --git a/drivers/infiniband/core/uverbs_std_types.c b/drivers/infiniband/core/uverbs_std_types.c
index cbc72312eb41..f224cb727224 100644
--- a/drivers/infiniband/core/uverbs_std_types.c
+++ b/drivers/infiniband/core/uverbs_std_types.c
@@ -188,7 +188,7 @@ static int uverbs_free_pd(struct ib_uobject *uobject,
if (ret)
return ret;
- ib_dealloc_pd((struct ib_pd *)uobject->object);
+ ib_dealloc_pd(pd);
return 0;
}
diff --git a/drivers/infiniband/core/uverbs_uapi.c b/drivers/infiniband/core/uverbs_uapi.c
index 9ae08e4b78a3..7a987acf0c0b 100644
--- a/drivers/infiniband/core/uverbs_uapi.c
+++ b/drivers/infiniband/core/uverbs_uapi.c
@@ -188,13 +188,18 @@ static int uapi_merge_obj_tree(struct uverbs_api *uapi,
obj_elm->type_attrs = obj->type_attrs;
obj_elm->type_class = obj->type_attrs->type_class;
/*
- * Today drivers are only permitted to use idr_class
- * types. They cannot use FD types because we currently have
- * no way to revoke the fops pointer after device
- * disassociation.
+ * Today drivers are only permitted to use idr_class and
+ * fd_class types. We can revoke the IDR types during
+ * disassociation, and the FD types require the driver to use
+ * struct file_operations.owner to prevent the driver module
+ * code from unloading while the file is open. This provides
+ * enough safety that uverbs_close_fd() will continue to work.
+ * Drivers using FD are responsible to handle disassociation of
+ * the device on their own.
*/
if (WARN_ON(is_driver &&
- obj->type_attrs->type_class != &uverbs_idr_class))
+ obj->type_attrs->type_class != &uverbs_idr_class &&
+ obj->type_attrs->type_class != &uverbs_fd_class))
return -EINVAL;
}
diff --git a/drivers/infiniband/core/verbs.c b/drivers/infiniband/core/verbs.c
index ac011836bb54..5a5e83f5f0fc 100644
--- a/drivers/infiniband/core/verbs.c
+++ b/drivers/infiniband/core/verbs.c
@@ -254,10 +254,11 @@ struct ib_pd *__ib_alloc_pd(struct ib_device *device, unsigned int flags,
{
struct ib_pd *pd;
int mr_access_flags = 0;
+ int ret;
- pd = device->ops.alloc_pd(device, NULL, NULL);
- if (IS_ERR(pd))
- return pd;
+ pd = rdma_zalloc_drv_obj(device, ib_pd);
+ if (!pd)
+ return ERR_PTR(-ENOMEM);
pd->device = device;
pd->uobject = NULL;
@@ -265,6 +266,16 @@ struct ib_pd *__ib_alloc_pd(struct ib_device *device, unsigned int flags,
atomic_set(&pd->usecnt, 0);
pd->flags = flags;
+ pd->res.type = RDMA_RESTRACK_PD;
+ rdma_restrack_set_task(&pd->res, caller);
+
+ ret = device->ops.alloc_pd(pd, NULL, NULL);
+ if (ret) {
+ kfree(pd);
+ return ERR_PTR(ret);
+ }
+ rdma_restrack_kadd(&pd->res);
+
if (device->attrs.device_cap_flags & IB_DEVICE_LOCAL_DMA_LKEY)
pd->local_dma_lkey = device->local_dma_lkey;
else
@@ -275,10 +286,6 @@ struct ib_pd *__ib_alloc_pd(struct ib_device *device, unsigned int flags,
mr_access_flags |= IB_ACCESS_REMOTE_READ | IB_ACCESS_REMOTE_WRITE;
}
- pd->res.type = RDMA_RESTRACK_PD;
- rdma_restrack_set_task(&pd->res, caller);
- rdma_restrack_kadd(&pd->res);
-
if (mr_access_flags) {
struct ib_mr *mr;
@@ -329,10 +336,8 @@ void ib_dealloc_pd(struct ib_pd *pd)
WARN_ON(atomic_read(&pd->usecnt));
rdma_restrack_del(&pd->res);
- /* Making delalloc_pd a void return is a WIP, no driver should return
- an error here. */
- ret = pd->device->ops.dealloc_pd(pd);
- WARN_ONCE(ret, "Infiniband HW driver failed dealloc_pd");
+ pd->device->ops.dealloc_pd(pd);
+ kfree(pd);
}
EXPORT_SYMBOL(ib_dealloc_pd);
@@ -1106,8 +1111,8 @@ struct ib_qp *ib_open_qp(struct ib_xrcd *xrcd,
}
EXPORT_SYMBOL(ib_open_qp);
-static struct ib_qp *ib_create_xrc_qp(struct ib_qp *qp,
- struct ib_qp_init_attr *qp_init_attr)
+static struct ib_qp *create_xrc_qp(struct ib_qp *qp,
+ struct ib_qp_init_attr *qp_init_attr)
{
struct ib_qp *real_qp = qp;
@@ -1122,10 +1127,10 @@ static struct ib_qp *ib_create_xrc_qp(struct ib_qp *qp,
qp = __ib_open_qp(real_qp, qp_init_attr->event_handler,
qp_init_attr->qp_context);
- if (!IS_ERR(qp))
- __ib_insert_xrcd_qp(qp_init_attr->xrcd, real_qp);
- else
- real_qp->device->ops.destroy_qp(real_qp);
+ if (IS_ERR(qp))
+ return qp;
+
+ __ib_insert_xrcd_qp(qp_init_attr->xrcd, real_qp);
return qp;
}
@@ -1156,10 +1161,8 @@ struct ib_qp *ib_create_qp(struct ib_pd *pd,
return qp;
ret = ib_create_qp_security(qp, device);
- if (ret) {
- ib_destroy_qp(qp);
- return ERR_PTR(ret);
- }
+ if (ret)
+ goto err;
qp->real_qp = qp;
qp->qp_type = qp_init_attr->qp_type;
@@ -1172,8 +1175,15 @@ struct ib_qp *ib_create_qp(struct ib_pd *pd,
INIT_LIST_HEAD(&qp->sig_mrs);
qp->port = 0;
- if (qp_init_attr->qp_type == IB_QPT_XRC_TGT)
- return ib_create_xrc_qp(qp, qp_init_attr);
+ if (qp_init_attr->qp_type == IB_QPT_XRC_TGT) {
+ struct ib_qp *xrc_qp = create_xrc_qp(qp, qp_init_attr);
+
+ if (IS_ERR(xrc_qp)) {
+ ret = PTR_ERR(xrc_qp);
+ goto err;
+ }
+ return xrc_qp;
+ }
qp->event_handler = qp_init_attr->event_handler;
qp->qp_context = qp_init_attr->qp_context;
@@ -1200,11 +1210,8 @@ struct ib_qp *ib_create_qp(struct ib_pd *pd,
if (qp_init_attr->cap.max_rdma_ctxs) {
ret = rdma_rw_init_mrs(qp, qp_init_attr);
- if (ret) {
- pr_err("failed to init MR pool ret= %d\n", ret);
- ib_destroy_qp(qp);
- return ERR_PTR(ret);
- }
+ if (ret)
+ goto err;
}
/*
@@ -1217,6 +1224,11 @@ struct ib_qp *ib_create_qp(struct ib_pd *pd,
device->attrs.max_sge_rd);
return qp;
+
+err:
+ ib_destroy_qp(qp);
+ return ERR_PTR(ret);
+
}
EXPORT_SYMBOL(ib_create_qp);
@@ -1711,10 +1723,7 @@ int ib_get_eth_speed(struct ib_device *dev, u8 port_num, u8 *speed, u8 *width)
if (rdma_port_get_link_layer(dev, port_num) != IB_LINK_LAYER_ETHERNET)
return -EINVAL;
- if (!dev->ops.get_netdev)
- return -EOPNOTSUPP;
-
- netdev = dev->ops.get_netdev(dev, port_num);
+ netdev = ib_device_get_netdev(dev, port_num);
if (!netdev)
return -ENODEV;
diff --git a/drivers/infiniband/hw/bnxt_re/Kconfig b/drivers/infiniband/hw/bnxt_re/Kconfig
index 19982a4a9bba..d25439c305f7 100644
--- a/drivers/infiniband/hw/bnxt_re/Kconfig
+++ b/drivers/infiniband/hw/bnxt_re/Kconfig
@@ -1,5 +1,6 @@
config INFINIBAND_BNXT_RE
tristate "Broadcom Netxtreme HCA support"
+ depends on 64BIT
depends on ETHERNET && NETDEVICES && PCI && INET && DCB
select NET_VENDOR_BROADCOM
select BNXT
diff --git a/drivers/infiniband/hw/bnxt_re/Makefile b/drivers/infiniband/hw/bnxt_re/Makefile
index 6e3bc25cc140..ee9bb1be61ea 100644
--- a/drivers/infiniband/hw/bnxt_re/Makefile
+++ b/drivers/infiniband/hw/bnxt_re/Makefile
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
-ccflags-y := -Idrivers/net/ethernet/broadcom/bnxt
+ccflags-y := -I $(srctree)/drivers/net/ethernet/broadcom/bnxt
obj-$(CONFIG_INFINIBAND_BNXT_RE) += bnxt_re.o
bnxt_re-y := main.o ib_verbs.o \
qplib_res.o qplib_rcfw.o \
diff --git a/drivers/infiniband/hw/bnxt_re/bnxt_re.h b/drivers/infiniband/hw/bnxt_re/bnxt_re.h
index 31baa8939a4f..e55a1666c0cd 100644
--- a/drivers/infiniband/hw/bnxt_re/bnxt_re.h
+++ b/drivers/infiniband/hw/bnxt_re/bnxt_re.h
@@ -124,6 +124,7 @@ struct bnxt_re_dev {
#define BNXT_RE_FLAG_ISSUE_ROCE_STATS 29
struct net_device *netdev;
unsigned int version, major, minor;
+ struct bnxt_qplib_chip_ctx chip_ctx;
struct bnxt_en_dev *en_dev;
struct bnxt_msix_entry msix_entries[BNXT_RE_MAX_MSIX];
int num_msix;
diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.c b/drivers/infiniband/hw/bnxt_re/ib_verbs.c
index 1e2515e2eb62..071b2fc38b0b 100644
--- a/drivers/infiniband/hw/bnxt_re/ib_verbs.c
+++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.c
@@ -48,6 +48,7 @@
#include <rdma/ib_addr.h>
#include <rdma/ib_mad.h>
#include <rdma/ib_cache.h>
+#include <rdma/uverbs_ioctl.h>
#include "bnxt_ulp.h"
@@ -563,41 +564,29 @@ fail:
}
/* Protection Domains */
-int bnxt_re_dealloc_pd(struct ib_pd *ib_pd)
+void bnxt_re_dealloc_pd(struct ib_pd *ib_pd)
{
struct bnxt_re_pd *pd = container_of(ib_pd, struct bnxt_re_pd, ib_pd);
struct bnxt_re_dev *rdev = pd->rdev;
- int rc;
bnxt_re_destroy_fence_mr(pd);
- if (pd->qplib_pd.id) {
- rc = bnxt_qplib_dealloc_pd(&rdev->qplib_res,
- &rdev->qplib_res.pd_tbl,
- &pd->qplib_pd);
- if (rc)
- dev_err(rdev_to_dev(rdev), "Failed to deallocate HW PD");
- }
-
- kfree(pd);
- return 0;
+ if (pd->qplib_pd.id)
+ bnxt_qplib_dealloc_pd(&rdev->qplib_res, &rdev->qplib_res.pd_tbl,
+ &pd->qplib_pd);
}
-struct ib_pd *bnxt_re_alloc_pd(struct ib_device *ibdev,
- struct ib_ucontext *ucontext,
- struct ib_udata *udata)
+int bnxt_re_alloc_pd(struct ib_pd *ibpd, struct ib_ucontext *ucontext,
+ struct ib_udata *udata)
{
+ struct ib_device *ibdev = ibpd->device;
struct bnxt_re_dev *rdev = to_bnxt_re_dev(ibdev, ibdev);
struct bnxt_re_ucontext *ucntx = container_of(ucontext,
struct bnxt_re_ucontext,
ib_uctx);
- struct bnxt_re_pd *pd;
+ struct bnxt_re_pd *pd = container_of(ibpd, struct bnxt_re_pd, ib_pd);
int rc;
- pd = kzalloc(sizeof(*pd), GFP_KERNEL);
- if (!pd)
- return ERR_PTR(-ENOMEM);
-
pd->rdev = rdev;
if (bnxt_qplib_alloc_pd(&rdev->qplib_res.pd_tbl, &pd->qplib_pd)) {
dev_err(rdev_to_dev(rdev), "Failed to allocate HW PD");
@@ -637,13 +626,12 @@ struct ib_pd *bnxt_re_alloc_pd(struct ib_device *ibdev,
if (bnxt_re_create_fence_mr(pd))
dev_warn(rdev_to_dev(rdev),
"Failed to create Fence-MR\n");
- return &pd->ib_pd;
+ return 0;
dbfail:
- (void)bnxt_qplib_dealloc_pd(&rdev->qplib_res, &rdev->qplib_res.pd_tbl,
- &pd->qplib_pd);
+ bnxt_qplib_dealloc_pd(&rdev->qplib_res, &rdev->qplib_res.pd_tbl,
+ &pd->qplib_pd);
fail:
- kfree(pd);
- return ERR_PTR(rc);
+ return rc;
}
/* Address Handles */
@@ -663,17 +651,36 @@ int bnxt_re_destroy_ah(struct ib_ah *ib_ah, u32 flags)
return 0;
}
+static u8 bnxt_re_stack_to_dev_nw_type(enum rdma_network_type ntype)
+{
+ u8 nw_type;
+
+ switch (ntype) {
+ case RDMA_NETWORK_IPV4:
+ nw_type = CMDQ_CREATE_AH_TYPE_V2IPV4;
+ break;
+ case RDMA_NETWORK_IPV6:
+ nw_type = CMDQ_CREATE_AH_TYPE_V2IPV6;
+ break;
+ default:
+ nw_type = CMDQ_CREATE_AH_TYPE_V1;
+ break;
+ }
+ return nw_type;
+}
+
struct ib_ah *bnxt_re_create_ah(struct ib_pd *ib_pd,
struct rdma_ah_attr *ah_attr,
u32 flags,
struct ib_udata *udata)
{
struct bnxt_re_pd *pd = container_of(ib_pd, struct bnxt_re_pd, ib_pd);
+ const struct ib_global_route *grh = rdma_ah_read_grh(ah_attr);
struct bnxt_re_dev *rdev = pd->rdev;
+ const struct ib_gid_attr *sgid_attr;
struct bnxt_re_ah *ah;
- const struct ib_global_route *grh = rdma_ah_read_grh(ah_attr);
- int rc;
u8 nw_type;
+ int rc;
if (!(rdma_ah_get_ah_flags(ah_attr) & IB_AH_GRH)) {
dev_err(rdev_to_dev(rdev), "Failed to alloc AH: GRH not set");
@@ -700,28 +707,11 @@ struct ib_ah *bnxt_re_create_ah(struct ib_pd *ib_pd,
ah->qplib_ah.flow_label = grh->flow_label;
ah->qplib_ah.hop_limit = grh->hop_limit;
ah->qplib_ah.sl = rdma_ah_get_sl(ah_attr);
- if (udata &&
- !rdma_is_multicast_addr((struct in6_addr *)
- grh->dgid.raw) &&
- !rdma_link_local_addr((struct in6_addr *)
- grh->dgid.raw)) {
- const struct ib_gid_attr *sgid_attr;
- sgid_attr = grh->sgid_attr;
- /* Get network header type for this GID */
- nw_type = rdma_gid_attr_network_type(sgid_attr);
- switch (nw_type) {
- case RDMA_NETWORK_IPV4:
- ah->qplib_ah.nw_type = CMDQ_CREATE_AH_TYPE_V2IPV4;
- break;
- case RDMA_NETWORK_IPV6:
- ah->qplib_ah.nw_type = CMDQ_CREATE_AH_TYPE_V2IPV6;
- break;
- default:
- ah->qplib_ah.nw_type = CMDQ_CREATE_AH_TYPE_V1;
- break;
- }
- }
+ sgid_attr = grh->sgid_attr;
+ /* Get network header type for this GID */
+ nw_type = rdma_gid_attr_network_type(sgid_attr);
+ ah->qplib_ah.nw_type = bnxt_re_stack_to_dev_nw_type(nw_type);
memcpy(ah->qplib_ah.dmac, ah_attr->roce.dmac, ETH_ALEN);
rc = bnxt_qplib_create_ah(&rdev->qplib_res, &ah->qplib_ah,
@@ -733,12 +723,11 @@ struct ib_ah *bnxt_re_create_ah(struct ib_pd *ib_pd,
/* Write AVID to shared page. */
if (udata) {
- struct ib_ucontext *ib_uctx = ib_pd->uobject->context;
- struct bnxt_re_ucontext *uctx;
+ struct bnxt_re_ucontext *uctx = rdma_udata_to_drv_context(
+ udata, struct bnxt_re_ucontext, ib_uctx);
unsigned long flag;
u32 *wrptr;
- uctx = container_of(ib_uctx, struct bnxt_re_ucontext, ib_uctx);
spin_lock_irqsave(&uctx->sh_lock, flag);
wrptr = (u32 *)(uctx->shpg + BNXT_RE_AVID_OFFT);
*wrptr = ah->qplib_ah.id;
@@ -804,8 +793,8 @@ int bnxt_re_destroy_qp(struct ib_qp *ib_qp)
{
struct bnxt_re_qp *qp = container_of(ib_qp, struct bnxt_re_qp, ib_qp);
struct bnxt_re_dev *rdev = qp->rdev;
- int rc;
unsigned int flags;
+ int rc;
bnxt_qplib_flush_cqn_wq(&qp->qplib_qp);
rc = bnxt_qplib_destroy_qp(&rdev->qplib_res, &qp->qplib_qp);
@@ -814,9 +803,12 @@ int bnxt_re_destroy_qp(struct ib_qp *ib_qp)
return rc;
}
- flags = bnxt_re_lock_cqs(qp);
- bnxt_qplib_clean_qp(&qp->qplib_qp);
- bnxt_re_unlock_cqs(qp, flags);
+ if (rdma_is_kernel_res(&qp->ib_qp.res)) {
+ flags = bnxt_re_lock_cqs(qp);
+ bnxt_qplib_clean_qp(&qp->qplib_qp);
+ bnxt_re_unlock_cqs(qp, flags);
+ }
+
bnxt_qplib_free_qp_res(&rdev->qplib_res, &qp->qplib_qp);
if (ib_qp->qp_type == IB_QPT_GSI && rdev->qp1_sqp) {
@@ -882,21 +874,23 @@ static int bnxt_re_init_user_qp(struct bnxt_re_dev *rdev, struct bnxt_re_pd *pd,
struct bnxt_re_qp_req ureq;
struct bnxt_qplib_qp *qplib_qp = &qp->qplib_qp;
struct ib_umem *umem;
- int bytes = 0;
- struct ib_ucontext *context = pd->ib_pd.uobject->context;
- struct bnxt_re_ucontext *cntx = container_of(context,
- struct bnxt_re_ucontext,
- ib_uctx);
+ int bytes = 0, psn_sz;
+ struct bnxt_re_ucontext *cntx = rdma_udata_to_drv_context(
+ udata, struct bnxt_re_ucontext, ib_uctx);
+
if (ib_copy_from_udata(&ureq, udata, sizeof(ureq)))
return -EFAULT;
bytes = (qplib_qp->sq.max_wqe * BNXT_QPLIB_MAX_SQE_ENTRY_SIZE);
/* Consider mapping PSN search memory only for RC QPs. */
- if (qplib_qp->type == CMDQ_CREATE_QP_TYPE_RC)
- bytes += (qplib_qp->sq.max_wqe * sizeof(struct sq_psn_search));
+ if (qplib_qp->type == CMDQ_CREATE_QP_TYPE_RC) {
+ psn_sz = bnxt_qplib_is_chip_gen_p5(&rdev->chip_ctx) ?
+ sizeof(struct sq_psn_search_ext) :
+ sizeof(struct sq_psn_search);
+ bytes += (qplib_qp->sq.max_wqe * psn_sz);
+ }
bytes = PAGE_ALIGN(bytes);
- umem = ib_umem_get(context, ureq.qpsva, bytes,
- IB_ACCESS_LOCAL_WRITE, 1);
+ umem = ib_umem_get(udata, ureq.qpsva, bytes, IB_ACCESS_LOCAL_WRITE, 1);
if (IS_ERR(umem))
return PTR_ERR(umem);
@@ -908,7 +902,7 @@ static int bnxt_re_init_user_qp(struct bnxt_re_dev *rdev, struct bnxt_re_pd *pd,
if (!qp->qplib_qp.srq) {
bytes = (qplib_qp->rq.max_wqe * BNXT_QPLIB_MAX_RQE_ENTRY_SIZE);
bytes = PAGE_ALIGN(bytes);
- umem = ib_umem_get(context, ureq.qprva, bytes,
+ umem = ib_umem_get(udata, ureq.qprva, bytes,
IB_ACCESS_LOCAL_WRITE, 1);
if (IS_ERR(umem))
goto rqfail;
@@ -1066,12 +1060,17 @@ struct ib_qp *bnxt_re_create_qp(struct ib_pd *ib_pd,
qp->qplib_qp.pd = &pd->qplib_pd;
qp->qplib_qp.qp_handle = (u64)(unsigned long)(&qp->qplib_qp);
qp->qplib_qp.type = __from_ib_qp_type(qp_init_attr->qp_type);
+
+ if (qp_init_attr->qp_type == IB_QPT_GSI &&
+ bnxt_qplib_is_chip_gen_p5(&rdev->chip_ctx))
+ qp->qplib_qp.type = CMDQ_CREATE_QP_TYPE_GSI;
if (qp->qplib_qp.type == IB_QPT_MAX) {
dev_err(rdev_to_dev(rdev), "QP type 0x%x not supported",
qp->qplib_qp.type);
rc = -EINVAL;
goto fail;
}
+
qp->qplib_qp.max_inline_data = qp_init_attr->cap.max_inline_data;
qp->qplib_qp.sig_type = ((qp_init_attr->sq_sig_type ==
IB_SIGNAL_ALL_WR) ? true : false);
@@ -1132,7 +1131,8 @@ struct ib_qp *bnxt_re_create_qp(struct ib_pd *ib_pd,
qp->qplib_qp.mtu = ib_mtu_enum_to_int(iboe_get_mtu(rdev->netdev->mtu));
- if (qp_init_attr->qp_type == IB_QPT_GSI) {
+ if (qp_init_attr->qp_type == IB_QPT_GSI &&
+ !(bnxt_qplib_is_chip_gen_p5(&rdev->chip_ctx))) {
/* Allocate 1 more than what's provided */
entries = roundup_pow_of_two(qp_init_attr->cap.max_send_wr + 1);
qp->qplib_qp.sq.max_wqe = min_t(u32, entries,
@@ -1361,17 +1361,15 @@ static int bnxt_re_init_user_srq(struct bnxt_re_dev *rdev,
struct bnxt_qplib_srq *qplib_srq = &srq->qplib_srq;
struct ib_umem *umem;
int bytes = 0;
- struct ib_ucontext *context = pd->ib_pd.uobject->context;
- struct bnxt_re_ucontext *cntx = container_of(context,
- struct bnxt_re_ucontext,
- ib_uctx);
+ struct bnxt_re_ucontext *cntx = rdma_udata_to_drv_context(
+ udata, struct bnxt_re_ucontext, ib_uctx);
+
if (ib_copy_from_udata(&ureq, udata, sizeof(ureq)))
return -EFAULT;
bytes = (qplib_srq->max_wqe * BNXT_QPLIB_MAX_RQE_ENTRY_SIZE);
bytes = PAGE_ALIGN(bytes);
- umem = ib_umem_get(context, ureq.srqva, bytes,
- IB_ACCESS_LOCAL_WRITE, 1);
+ umem = ib_umem_get(udata, ureq.srqva, bytes, IB_ACCESS_LOCAL_WRITE, 1);
if (IS_ERR(umem))
return PTR_ERR(umem);
@@ -1646,6 +1644,9 @@ int bnxt_re_modify_qp(struct ib_qp *ib_qp, struct ib_qp_attr *qp_attr,
__from_ib_access_flags(qp_attr->qp_access_flags);
/* LOCAL_WRITE access must be set to allow RC receive */
qp->qplib_qp.access |= BNXT_QPLIB_ACCESS_LOCAL_WRITE;
+ /* Temp: Set all params on QP as of now */
+ qp->qplib_qp.access |= CMDQ_MODIFY_QP_ACCESS_REMOTE_WRITE;
+ qp->qplib_qp.access |= CMDQ_MODIFY_QP_ACCESS_REMOTE_READ;
}
if (qp_attr_mask & IB_QP_PKEY_INDEX) {
qp->qplib_qp.modify_flags |= CMDQ_MODIFY_QP_MODIFY_MASK_PKEY;
@@ -2093,7 +2094,8 @@ static int bnxt_re_build_qp1_shadow_qp_recv(struct bnxt_re_qp *qp,
static int is_ud_qp(struct bnxt_re_qp *qp)
{
- return qp->qplib_qp.type == CMDQ_CREATE_QP_TYPE_UD;
+ return (qp->qplib_qp.type == CMDQ_CREATE_QP_TYPE_UD ||
+ qp->qplib_qp.type == CMDQ_CREATE_QP_TYPE_GSI);
}
static int bnxt_re_build_send_wqe(struct bnxt_re_qp *qp,
@@ -2397,7 +2399,7 @@ int bnxt_re_post_send(struct ib_qp *ib_qp, const struct ib_send_wr *wr,
switch (wr->opcode) {
case IB_WR_SEND:
case IB_WR_SEND_WITH_IMM:
- if (ib_qp->qp_type == IB_QPT_GSI) {
+ if (qp->qplib_qp.type == CMDQ_CREATE_QP1_TYPE_GSI) {
rc = bnxt_re_build_qp1_send_v2(qp, wr, &wqe,
payload_sz);
if (rc)
@@ -2527,7 +2529,8 @@ int bnxt_re_post_recv(struct ib_qp *ib_qp, const struct ib_recv_wr *wr,
wqe.wr_id = wr->wr_id;
wqe.type = BNXT_QPLIB_SWQE_TYPE_RECV;
- if (ib_qp->qp_type == IB_QPT_GSI)
+ if (ib_qp->qp_type == IB_QPT_GSI &&
+ qp->qplib_qp.type != CMDQ_CREATE_QP_TYPE_GSI)
rc = bnxt_re_build_qp1_shadow_qp_recv(qp, wr, &wqe,
payload_sz);
if (!rc)
@@ -2622,7 +2625,7 @@ struct ib_cq *bnxt_re_create_cq(struct ib_device *ibdev,
goto fail;
}
- cq->umem = ib_umem_get(context, req.cq_va,
+ cq->umem = ib_umem_get(udata, req.cq_va,
entries * sizeof(struct cq_base),
IB_ACCESS_LOCAL_WRITE, 1);
if (IS_ERR(cq->umem)) {
@@ -3122,19 +3125,33 @@ static void bnxt_re_process_res_shadow_qp_wc(struct bnxt_re_qp *qp,
}
}
-static void bnxt_re_process_res_ud_wc(struct ib_wc *wc,
+static void bnxt_re_process_res_ud_wc(struct bnxt_re_qp *qp,
+ struct ib_wc *wc,
struct bnxt_qplib_cqe *cqe)
{
+ u8 nw_type;
+
wc->opcode = IB_WC_RECV;
wc->status = __rc_to_ib_wc_status(cqe->status);
- if (cqe->flags & CQ_RES_RC_FLAGS_IMM)
+ if (cqe->flags & CQ_RES_UD_FLAGS_IMM)
wc->wc_flags |= IB_WC_WITH_IMM;
- if (cqe->flags & CQ_RES_RC_FLAGS_INV)
- wc->wc_flags |= IB_WC_WITH_INVALIDATE;
- if ((cqe->flags & (CQ_RES_RC_FLAGS_RDMA | CQ_RES_RC_FLAGS_IMM)) ==
- (CQ_RES_RC_FLAGS_RDMA | CQ_RES_RC_FLAGS_IMM))
- wc->opcode = IB_WC_RECV_RDMA_WITH_IMM;
+ /* report only on GSI QP for Thor */
+ if (qp->qplib_qp.type == CMDQ_CREATE_QP_TYPE_GSI) {
+ wc->wc_flags |= IB_WC_GRH;
+ memcpy(wc->smac, cqe->smac, ETH_ALEN);
+ wc->wc_flags |= IB_WC_WITH_SMAC;
+ if (cqe->flags & CQ_RES_UD_FLAGS_META_FORMAT_VLAN) {
+ wc->vlan_id = (cqe->cfa_meta & 0xFFF);
+ if (wc->vlan_id < 0x1000)
+ wc->wc_flags |= IB_WC_WITH_VLAN;
+ }
+ nw_type = (cqe->flags & CQ_RES_UD_FLAGS_ROCE_IP_VER_MASK) >>
+ CQ_RES_UD_FLAGS_ROCE_IP_VER_SFT;
+ wc->network_hdr_type = bnxt_re_to_ib_nw_type(nw_type);
+ wc->wc_flags |= IB_WC_WITH_NETWORK_HDR_TYPE;
+ }
+
}
static int send_phantom_wqe(struct bnxt_re_qp *qp)
@@ -3226,7 +3243,7 @@ int bnxt_re_poll_cq(struct ib_cq *ib_cq, int num_entries, struct ib_wc *wc)
switch (cqe->opcode) {
case CQ_BASE_CQE_TYPE_REQ:
- if (qp->qplib_qp.id ==
+ if (qp->rdev->qp1_sqp && qp->qplib_qp.id ==
qp->rdev->qp1_sqp->qplib_qp.id) {
/* Handle this completion with
* the stored completion
@@ -3261,7 +3278,7 @@ int bnxt_re_poll_cq(struct ib_cq *ib_cq, int num_entries, struct ib_wc *wc)
bnxt_re_process_res_rc_wc(wc, cqe);
break;
case CQ_BASE_CQE_TYPE_RES_UD:
- if (qp->qplib_qp.id ==
+ if (qp->rdev->qp1_sqp && qp->qplib_qp.id ==
qp->rdev->qp1_sqp->qplib_qp.id) {
/* Handle this completion with
* the stored completion
@@ -3274,7 +3291,7 @@ int bnxt_re_poll_cq(struct ib_cq *ib_cq, int num_entries, struct ib_wc *wc)
break;
}
}
- bnxt_re_process_res_ud_wc(wc, cqe);
+ bnxt_re_process_res_ud_wc(qp, wc, cqe);
break;
default:
dev_err(rdev_to_dev(cq->rdev),
@@ -3301,10 +3318,10 @@ int bnxt_re_req_notify_cq(struct ib_cq *ib_cq,
spin_lock_irqsave(&cq->cq_lock, flags);
/* Trigger on the very next completion */
if (ib_cqn_flags & IB_CQ_NEXT_COMP)
- type = DBR_DBR_TYPE_CQ_ARMALL;
+ type = DBC_DBC_TYPE_CQ_ARMALL;
/* Trigger on the next solicited completion */
else if (ib_cqn_flags & IB_CQ_SOLICITED)
- type = DBR_DBR_TYPE_CQ_ARMSE;
+ type = DBC_DBC_TYPE_CQ_ARMSE;
/* Poll to see if there are missed events */
if ((ib_cqn_flags & IB_CQ_REPORT_MISSED_EVENTS) &&
@@ -3537,19 +3554,14 @@ static int fill_umem_pbl_tbl(struct ib_umem *umem, u64 *pbl_tbl_orig,
u64 *pbl_tbl = pbl_tbl_orig;
u64 paddr;
u64 page_mask = (1ULL << page_shift) - 1;
- int i, pages;
- struct scatterlist *sg;
- int entry;
-
- for_each_sg(umem->sg_head.sgl, sg, umem->nmap, entry) {
- pages = sg_dma_len(sg) >> PAGE_SHIFT;
- for (i = 0; i < pages; i++) {
- paddr = sg_dma_address(sg) + (i << PAGE_SHIFT);
- if (pbl_tbl == pbl_tbl_orig)
- *pbl_tbl++ = paddr & ~page_mask;
- else if ((paddr & page_mask) == 0)
- *pbl_tbl++ = paddr;
- }
+ struct sg_dma_page_iter sg_iter;
+
+ for_each_sg_dma_page (umem->sg_head.sgl, &sg_iter, umem->nmap, 0) {
+ paddr = sg_page_iter_dma_address(&sg_iter);
+ if (pbl_tbl == pbl_tbl_orig)
+ *pbl_tbl++ = paddr & ~page_mask;
+ else if ((paddr & page_mask) == 0)
+ *pbl_tbl++ = paddr;
}
return pbl_tbl - pbl_tbl_orig;
}
@@ -3589,8 +3601,7 @@ struct ib_mr *bnxt_re_reg_user_mr(struct ib_pd *ib_pd, u64 start, u64 length,
/* The fixed portion of the rkey is the same as the lkey */
mr->ib_mr.rkey = mr->qplib_mr.rkey;
- umem = ib_umem_get(ib_pd->uobject->context, start, length,
- mr_access_flags, 0);
+ umem = ib_umem_get(udata, start, length, mr_access_flags, 0);
if (IS_ERR(umem)) {
dev_err(rdev_to_dev(rdev), "Failed to get umem");
rc = -EFAULT;
@@ -3613,7 +3624,7 @@ struct ib_mr *bnxt_re_reg_user_mr(struct ib_pd *ib_pd, u64 start, u64 length,
goto free_umem;
}
- page_shift = umem->page_shift;
+ page_shift = PAGE_SHIFT;
if (!bnxt_re_page_size_ok(page_shift)) {
dev_err(rdev_to_dev(rdev), "umem page size unsupported!");
@@ -3660,13 +3671,15 @@ free_mr:
return ERR_PTR(rc);
}
-struct ib_ucontext *bnxt_re_alloc_ucontext(struct ib_device *ibdev,
- struct ib_udata *udata)
+int bnxt_re_alloc_ucontext(struct ib_ucontext *ctx, struct ib_udata *udata)
{
+ struct ib_device *ibdev = ctx->device;
+ struct bnxt_re_ucontext *uctx =
+ container_of(ctx, struct bnxt_re_ucontext, ib_uctx);
struct bnxt_re_dev *rdev = to_bnxt_re_dev(ibdev, ibdev);
- struct bnxt_re_uctx_resp resp;
- struct bnxt_re_ucontext *uctx;
struct bnxt_qplib_dev_attr *dev_attr = &rdev->dev_attr;
+ struct bnxt_re_uctx_resp resp;
+ u32 chip_met_rev_num = 0;
int rc;
dev_dbg(rdev_to_dev(rdev), "ABI version requested %d",
@@ -3675,13 +3688,9 @@ struct ib_ucontext *bnxt_re_alloc_ucontext(struct ib_device *ibdev,
if (ibdev->uverbs_abi_ver != BNXT_RE_ABI_VERSION) {
dev_dbg(rdev_to_dev(rdev), " is different from the device %d ",
BNXT_RE_ABI_VERSION);
- return ERR_PTR(-EPERM);
+ return -EPERM;
}
- uctx = kzalloc(sizeof(*uctx), GFP_KERNEL);
- if (!uctx)
- return ERR_PTR(-ENOMEM);
-
uctx->rdev = rdev;
uctx->shpg = (void *)__get_free_page(GFP_KERNEL);
@@ -3691,37 +3700,45 @@ struct ib_ucontext *bnxt_re_alloc_ucontext(struct ib_device *ibdev,
}
spin_lock_init(&uctx->sh_lock);
- resp.dev_id = rdev->en_dev->pdev->devfn; /*Temp, Use idr_alloc instead*/
+ resp.comp_mask = BNXT_RE_UCNTX_CMASK_HAVE_CCTX;
+ chip_met_rev_num = rdev->chip_ctx.chip_num;
+ chip_met_rev_num |= ((u32)rdev->chip_ctx.chip_rev & 0xFF) <<
+ BNXT_RE_CHIP_ID0_CHIP_REV_SFT;
+ chip_met_rev_num |= ((u32)rdev->chip_ctx.chip_metal & 0xFF) <<
+ BNXT_RE_CHIP_ID0_CHIP_MET_SFT;
+ resp.chip_id0 = chip_met_rev_num;
+ /* Future extension of chip info */
+ resp.chip_id1 = 0;
+ /*Temp, Use idr_alloc instead */
+ resp.dev_id = rdev->en_dev->pdev->devfn;
resp.max_qp = rdev->qplib_ctx.qpc_count;
resp.pg_size = PAGE_SIZE;
resp.cqe_sz = sizeof(struct cq_base);
resp.max_cqd = dev_attr->max_cq_wqes;
resp.rsvd = 0;
- rc = ib_copy_to_udata(udata, &resp, sizeof(resp));
+ rc = ib_copy_to_udata(udata, &resp, min(udata->outlen, sizeof(resp)));
if (rc) {
dev_err(rdev_to_dev(rdev), "Failed to copy user context");
rc = -EFAULT;
goto cfail;
}
- return &uctx->ib_uctx;
+ return 0;
cfail:
free_page((unsigned long)uctx->shpg);
uctx->shpg = NULL;
fail:
- kfree(uctx);
- return ERR_PTR(rc);
+ return rc;
}
-int bnxt_re_dealloc_ucontext(struct ib_ucontext *ib_uctx)
+void bnxt_re_dealloc_ucontext(struct ib_ucontext *ib_uctx)
{
struct bnxt_re_ucontext *uctx = container_of(ib_uctx,
struct bnxt_re_ucontext,
ib_uctx);
struct bnxt_re_dev *rdev = uctx->rdev;
- int rc = 0;
if (uctx->shpg)
free_page((unsigned long)uctx->shpg);
@@ -3730,17 +3747,10 @@ int bnxt_re_dealloc_ucontext(struct ib_ucontext *ib_uctx)
/* Free DPI only if this is the first PD allocated by the
* application and mark the context dpi as NULL
*/
- rc = bnxt_qplib_dealloc_dpi(&rdev->qplib_res,
- &rdev->qplib_res.dpi_tbl,
- &uctx->dpi);
- if (rc)
- dev_err(rdev_to_dev(rdev), "Deallocate HW DPI failed!");
- /* Don't fail, continue*/
+ bnxt_qplib_dealloc_dpi(&rdev->qplib_res,
+ &rdev->qplib_res.dpi_tbl, &uctx->dpi);
uctx->dpi.dbr = NULL;
}
-
- kfree(uctx);
- return 0;
}
/* Helper function to mmap the virtual memory from user app */
diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.h b/drivers/infiniband/hw/bnxt_re/ib_verbs.h
index c4af72604b4f..e45465ed4eee 100644
--- a/drivers/infiniband/hw/bnxt_re/ib_verbs.h
+++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.h
@@ -56,8 +56,8 @@ struct bnxt_re_fence_data {
};
struct bnxt_re_pd {
+ struct ib_pd ib_pd;
struct bnxt_re_dev *rdev;
- struct ib_pd ib_pd;
struct bnxt_qplib_pd qplib_pd;
struct bnxt_re_fence_data fence;
};
@@ -135,8 +135,8 @@ struct bnxt_re_mw {
};
struct bnxt_re_ucontext {
+ struct ib_ucontext ib_uctx;
struct bnxt_re_dev *rdev;
- struct ib_ucontext ib_uctx;
struct bnxt_qplib_dpi dpi;
void *shpg;
spinlock_t sh_lock; /* protect shpg */
@@ -163,10 +163,9 @@ int bnxt_re_query_gid(struct ib_device *ibdev, u8 port_num,
int index, union ib_gid *gid);
enum rdma_link_layer bnxt_re_get_link_layer(struct ib_device *ibdev,
u8 port_num);
-struct ib_pd *bnxt_re_alloc_pd(struct ib_device *ibdev,
- struct ib_ucontext *context,
- struct ib_udata *udata);
-int bnxt_re_dealloc_pd(struct ib_pd *pd);
+int bnxt_re_alloc_pd(struct ib_pd *pd, struct ib_ucontext *context,
+ struct ib_udata *udata);
+void bnxt_re_dealloc_pd(struct ib_pd *pd);
struct ib_ah *bnxt_re_create_ah(struct ib_pd *pd,
struct rdma_ah_attr *ah_attr,
u32 flags,
@@ -216,9 +215,8 @@ int bnxt_re_dealloc_mw(struct ib_mw *mw);
struct ib_mr *bnxt_re_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
u64 virt_addr, int mr_access_flags,
struct ib_udata *udata);
-struct ib_ucontext *bnxt_re_alloc_ucontext(struct ib_device *ibdev,
- struct ib_udata *udata);
-int bnxt_re_dealloc_ucontext(struct ib_ucontext *context);
+int bnxt_re_alloc_ucontext(struct ib_ucontext *ctx, struct ib_udata *udata);
+void bnxt_re_dealloc_ucontext(struct ib_ucontext *context);
int bnxt_re_mmap(struct ib_ucontext *context, struct vm_area_struct *vma);
unsigned long bnxt_re_lock_cqs(struct bnxt_re_qp *qp);
diff --git a/drivers/infiniband/hw/bnxt_re/main.c b/drivers/infiniband/hw/bnxt_re/main.c
index e7a997f2a537..2bd24ac45ee4 100644
--- a/drivers/infiniband/hw/bnxt_re/main.c
+++ b/drivers/infiniband/hw/bnxt_re/main.c
@@ -80,6 +80,29 @@ static DEFINE_MUTEX(bnxt_re_dev_lock);
static struct workqueue_struct *bnxt_re_wq;
static void bnxt_re_ib_unreg(struct bnxt_re_dev *rdev);
+static void bnxt_re_destroy_chip_ctx(struct bnxt_re_dev *rdev)
+{
+ rdev->rcfw.res = NULL;
+ rdev->qplib_res.cctx = NULL;
+}
+
+static int bnxt_re_setup_chip_ctx(struct bnxt_re_dev *rdev)
+{
+ struct bnxt_en_dev *en_dev;
+ struct bnxt *bp;
+
+ en_dev = rdev->en_dev;
+ bp = netdev_priv(en_dev->net);
+
+ rdev->chip_ctx.chip_num = bp->chip_num;
+ /* rest members to follow eventually */
+
+ rdev->qplib_res.cctx = &rdev->chip_ctx;
+ rdev->rcfw.res = &rdev->qplib_res;
+
+ return 0;
+}
+
/* SR-IOV helper functions */
static void bnxt_re_get_sriov_func_type(struct bnxt_re_dev *rdev)
@@ -278,6 +301,7 @@ static int bnxt_re_register_netdev(struct bnxt_re_dev *rdev)
rc = en_dev->en_ops->bnxt_register_device(en_dev, BNXT_ROCE_ULP,
&bnxt_re_ulp_ops, rdev);
+ rdev->qplib_res.pdev = rdev->en_dev->pdev;
return rc;
}
@@ -345,7 +369,8 @@ static void bnxt_re_fill_fw_msg(struct bnxt_fw_msg *fw_msg, void *msg,
fw_msg->timeout = timeout;
}
-static int bnxt_re_net_ring_free(struct bnxt_re_dev *rdev, u16 fw_ring_id)
+static int bnxt_re_net_ring_free(struct bnxt_re_dev *rdev,
+ u16 fw_ring_id, int type)
{
struct bnxt_en_dev *en_dev = rdev->en_dev;
struct hwrm_ring_free_input req = {0};
@@ -359,7 +384,7 @@ static int bnxt_re_net_ring_free(struct bnxt_re_dev *rdev, u16 fw_ring_id)
memset(&fw_msg, 0, sizeof(fw_msg));
bnxt_re_init_hwrm_hdr(rdev, (void *)&req, HWRM_RING_FREE, -1, -1);
- req.ring_type = RING_ALLOC_REQ_RING_TYPE_L2_CMPL;
+ req.ring_type = type;
req.ring_id = cpu_to_le16(fw_ring_id);
bnxt_re_fill_fw_msg(&fw_msg, (void *)&req, sizeof(req), (void *)&resp,
sizeof(resp), DFLT_HWRM_CMD_TIMEOUT);
@@ -396,7 +421,7 @@ static int bnxt_re_net_ring_alloc(struct bnxt_re_dev *rdev, dma_addr_t *dma_arr,
/* Association of ring index with doorbell index and MSIX number */
req.logical_id = cpu_to_le16(map_index);
req.length = cpu_to_le32(ring_mask + 1);
- req.ring_type = RING_ALLOC_REQ_RING_TYPE_L2_CMPL;
+ req.ring_type = type;
req.int_mode = RING_ALLOC_REQ_INT_MODE_MSIX;
bnxt_re_fill_fw_msg(&fw_msg, (void *)&req, sizeof(req), (void *)&resp,
sizeof(resp), DFLT_HWRM_CMD_TIMEOUT);
@@ -538,7 +563,8 @@ static struct bnxt_en_dev *bnxt_re_dev_probe(struct net_device *netdev)
static ssize_t hw_rev_show(struct device *device, struct device_attribute *attr,
char *buf)
{
- struct bnxt_re_dev *rdev = to_bnxt_re_dev(device, ibdev.dev);
+ struct bnxt_re_dev *rdev =
+ rdma_device_to_drv_device(device, struct bnxt_re_dev, ibdev);
return scnprintf(buf, PAGE_SIZE, "0x%x\n", rdev->en_dev->pdev->vendor);
}
@@ -547,7 +573,8 @@ static DEVICE_ATTR_RO(hw_rev);
static ssize_t hca_type_show(struct device *device,
struct device_attribute *attr, char *buf)
{
- struct bnxt_re_dev *rdev = to_bnxt_re_dev(device, ibdev.dev);
+ struct bnxt_re_dev *rdev =
+ rdma_device_to_drv_device(device, struct bnxt_re_dev, ibdev);
return scnprintf(buf, PAGE_SIZE, "%s\n", rdev->ibdev.node_desc);
}
@@ -610,6 +637,8 @@ static const struct ib_device_ops bnxt_re_dev_ops = {
.query_srq = bnxt_re_query_srq,
.reg_user_mr = bnxt_re_reg_user_mr,
.req_notify_cq = bnxt_re_req_notify_cq,
+ INIT_RDMA_OBJ_SIZE(ib_pd, bnxt_re_pd, ib_pd),
+ INIT_RDMA_OBJ_SIZE(ib_ucontext, bnxt_re_ucontext, ib_uctx),
};
static int bnxt_re_register_ib(struct bnxt_re_dev *rdev)
@@ -662,7 +691,7 @@ static int bnxt_re_register_ib(struct bnxt_re_dev *rdev)
rdma_set_device_sysfs_group(ibdev, &bnxt_re_dev_attr_group);
ibdev->driver_id = RDMA_DRIVER_BNXT_RE;
ib_set_device_ops(ibdev, &bnxt_re_dev_ops);
- return ib_register_device(ibdev, "bnxt_re%d", NULL);
+ return ib_register_device(ibdev, "bnxt_re%d");
}
static void bnxt_re_dev_remove(struct bnxt_re_dev *rdev)
@@ -686,7 +715,7 @@ static struct bnxt_re_dev *bnxt_re_dev_add(struct net_device *netdev,
struct bnxt_re_dev *rdev;
/* Allocate bnxt_re_dev instance here */
- rdev = (struct bnxt_re_dev *)ib_alloc_device(sizeof(*rdev));
+ rdev = ib_alloc_device(bnxt_re_dev, ibdev);
if (!rdev) {
dev_err(NULL, "%s: bnxt_re_dev allocation failure!",
ROCE_DRV_MODULE_NAME);
@@ -858,6 +887,12 @@ static int bnxt_re_cqn_handler(struct bnxt_qplib_nq *nq,
return 0;
}
+static u32 bnxt_re_get_nqdb_offset(struct bnxt_re_dev *rdev, u16 indx)
+{
+ return bnxt_qplib_is_chip_gen_p5(&rdev->chip_ctx) ?
+ 0x10000 : rdev->msix_entries[indx].db_offset;
+}
+
static void bnxt_re_cleanup_res(struct bnxt_re_dev *rdev)
{
int i;
@@ -871,18 +906,18 @@ static void bnxt_re_cleanup_res(struct bnxt_re_dev *rdev)
static int bnxt_re_init_res(struct bnxt_re_dev *rdev)
{
- int rc = 0, i;
int num_vec_enabled = 0;
+ int rc = 0, i;
+ u32 db_offt;
bnxt_qplib_init_res(&rdev->qplib_res);
for (i = 1; i < rdev->num_msix ; i++) {
+ db_offt = bnxt_re_get_nqdb_offset(rdev, i);
rc = bnxt_qplib_enable_nq(rdev->en_dev->pdev, &rdev->nq[i - 1],
i - 1, rdev->msix_entries[i].vector,
- rdev->msix_entries[i].db_offset,
- &bnxt_re_cqn_handler,
+ db_offt, &bnxt_re_cqn_handler,
&bnxt_re_srqn_handler);
-
if (rc) {
dev_err(rdev_to_dev(rdev),
"Failed to enable NQ with rc = 0x%x", rc);
@@ -894,16 +929,18 @@ static int bnxt_re_init_res(struct bnxt_re_dev *rdev)
fail:
for (i = num_vec_enabled; i >= 0; i--)
bnxt_qplib_disable_nq(&rdev->nq[i]);
-
return rc;
}
static void bnxt_re_free_nq_res(struct bnxt_re_dev *rdev)
{
+ u8 type;
int i;
for (i = 0; i < rdev->num_msix - 1; i++) {
- bnxt_re_net_ring_free(rdev, rdev->nq[i].ring_id);
+ type = bnxt_qplib_get_ring_type(&rdev->chip_ctx);
+ bnxt_re_net_ring_free(rdev, rdev->nq[i].ring_id, type);
+ rdev->nq[i].res = NULL;
bnxt_qplib_free_nq(&rdev->nq[i]);
}
}
@@ -925,8 +962,11 @@ static void bnxt_re_free_res(struct bnxt_re_dev *rdev)
static int bnxt_re_alloc_res(struct bnxt_re_dev *rdev)
{
- int rc = 0, i;
int num_vec_created = 0;
+ dma_addr_t *pg_map;
+ int rc = 0, i;
+ int pages;
+ u8 type;
/* Configure and allocate resources for qplib */
rdev->qplib_res.rcfw = &rdev->rcfw;
@@ -947,6 +987,7 @@ static int bnxt_re_alloc_res(struct bnxt_re_dev *rdev)
goto dealloc_res;
for (i = 0; i < rdev->num_msix - 1; i++) {
+ rdev->nq[i].res = &rdev->qplib_res;
rdev->nq[i].hwq.max_elements = BNXT_RE_MAX_CQ_COUNT +
BNXT_RE_MAX_SRQC_COUNT + 2;
rc = bnxt_qplib_alloc_nq(rdev->en_dev->pdev, &rdev->nq[i]);
@@ -955,13 +996,13 @@ static int bnxt_re_alloc_res(struct bnxt_re_dev *rdev)
i, rc);
goto free_nq;
}
- rc = bnxt_re_net_ring_alloc
- (rdev, rdev->nq[i].hwq.pbl[PBL_LVL_0].pg_map_arr,
- rdev->nq[i].hwq.pbl[rdev->nq[i].hwq.level].pg_count,
- HWRM_RING_ALLOC_CMPL,
- BNXT_QPLIB_NQE_MAX_CNT - 1,
- rdev->msix_entries[i + 1].ring_idx,
- &rdev->nq[i].ring_id);
+ type = bnxt_qplib_get_ring_type(&rdev->chip_ctx);
+ pg_map = rdev->nq[i].hwq.pbl[PBL_LVL_0].pg_map_arr;
+ pages = rdev->nq[i].hwq.pbl[rdev->nq[i].hwq.level].pg_count;
+ rc = bnxt_re_net_ring_alloc(rdev, pg_map, pages, type,
+ BNXT_QPLIB_NQE_MAX_CNT - 1,
+ rdev->msix_entries[i + 1].ring_idx,
+ &rdev->nq[i].ring_id);
if (rc) {
dev_err(rdev_to_dev(rdev),
"Failed to allocate NQ fw id with rc = 0x%x",
@@ -974,7 +1015,8 @@ static int bnxt_re_alloc_res(struct bnxt_re_dev *rdev)
return 0;
free_nq:
for (i = num_vec_created; i >= 0; i--) {
- bnxt_re_net_ring_free(rdev, rdev->nq[i].ring_id);
+ type = bnxt_qplib_get_ring_type(&rdev->chip_ctx);
+ bnxt_re_net_ring_free(rdev, rdev->nq[i].ring_id, type);
bnxt_qplib_free_nq(&rdev->nq[i]);
}
bnxt_qplib_dealloc_dpi(&rdev->qplib_res,
@@ -1228,6 +1270,7 @@ static void bnxt_re_query_hwrm_intf_version(struct bnxt_re_dev *rdev)
static void bnxt_re_ib_unreg(struct bnxt_re_dev *rdev)
{
+ u8 type;
int rc;
if (test_and_clear_bit(BNXT_RE_FLAG_IBDEV_REGISTERED, &rdev->flags)) {
@@ -1251,7 +1294,8 @@ static void bnxt_re_ib_unreg(struct bnxt_re_dev *rdev)
bnxt_re_net_stats_ctx_free(rdev, rdev->qplib_ctx.stats.fw_id);
bnxt_qplib_free_ctx(rdev->en_dev->pdev, &rdev->qplib_ctx);
bnxt_qplib_disable_rcfw_channel(&rdev->rcfw);
- bnxt_re_net_ring_free(rdev, rdev->rcfw.creq_ring_id);
+ type = bnxt_qplib_get_ring_type(&rdev->chip_ctx);
+ bnxt_re_net_ring_free(rdev, rdev->rcfw.creq_ring_id, type);
bnxt_qplib_free_rcfw_channel(&rdev->rcfw);
}
if (test_and_clear_bit(BNXT_RE_FLAG_GOT_MSIX, &rdev->flags)) {
@@ -1260,6 +1304,8 @@ static void bnxt_re_ib_unreg(struct bnxt_re_dev *rdev)
dev_warn(rdev_to_dev(rdev),
"Failed to free MSI-X vectors: %#x", rc);
}
+
+ bnxt_re_destroy_chip_ctx(rdev);
if (test_and_clear_bit(BNXT_RE_FLAG_NETDEV_REGISTERED, &rdev->flags)) {
rc = bnxt_re_unregister_netdev(rdev);
if (rc)
@@ -1280,9 +1326,12 @@ static void bnxt_re_worker(struct work_struct *work)
static int bnxt_re_ib_reg(struct bnxt_re_dev *rdev)
{
- int rc;
-
+ dma_addr_t *pg_map;
+ u32 db_offt, ridx;
+ int pages, vid;
bool locked;
+ u8 type;
+ int rc;
/* Acquire rtnl lock through out this function */
rtnl_lock();
@@ -1297,6 +1346,12 @@ static int bnxt_re_ib_reg(struct bnxt_re_dev *rdev)
}
set_bit(BNXT_RE_FLAG_NETDEV_REGISTERED, &rdev->flags);
+ rc = bnxt_re_setup_chip_ctx(rdev);
+ if (rc) {
+ dev_err(rdev_to_dev(rdev), "Failed to get chip context\n");
+ return -EINVAL;
+ }
+
/* Check whether VF or PF */
bnxt_re_get_sriov_func_type(rdev);
@@ -1320,21 +1375,22 @@ static int bnxt_re_ib_reg(struct bnxt_re_dev *rdev)
pr_err("Failed to allocate RCFW Channel: %#x\n", rc);
goto fail;
}
- rc = bnxt_re_net_ring_alloc
- (rdev, rdev->rcfw.creq.pbl[PBL_LVL_0].pg_map_arr,
- rdev->rcfw.creq.pbl[rdev->rcfw.creq.level].pg_count,
- HWRM_RING_ALLOC_CMPL, BNXT_QPLIB_CREQE_MAX_CNT - 1,
- rdev->msix_entries[BNXT_RE_AEQ_IDX].ring_idx,
- &rdev->rcfw.creq_ring_id);
+ type = bnxt_qplib_get_ring_type(&rdev->chip_ctx);
+ pg_map = rdev->rcfw.creq.pbl[PBL_LVL_0].pg_map_arr;
+ pages = rdev->rcfw.creq.pbl[rdev->rcfw.creq.level].pg_count;
+ ridx = rdev->msix_entries[BNXT_RE_AEQ_IDX].ring_idx;
+ rc = bnxt_re_net_ring_alloc(rdev, pg_map, pages, type,
+ BNXT_QPLIB_CREQE_MAX_CNT - 1,
+ ridx, &rdev->rcfw.creq_ring_id);
if (rc) {
pr_err("Failed to allocate CREQ: %#x\n", rc);
goto free_rcfw;
}
- rc = bnxt_qplib_enable_rcfw_channel
- (rdev->en_dev->pdev, &rdev->rcfw,
- rdev->msix_entries[BNXT_RE_AEQ_IDX].vector,
- rdev->msix_entries[BNXT_RE_AEQ_IDX].db_offset,
- rdev->is_virtfn, &bnxt_re_aeq_handler);
+ db_offt = bnxt_re_get_nqdb_offset(rdev, BNXT_RE_AEQ_IDX);
+ vid = rdev->msix_entries[BNXT_RE_AEQ_IDX].vector;
+ rc = bnxt_qplib_enable_rcfw_channel(rdev->en_dev->pdev, &rdev->rcfw,
+ vid, db_offt, rdev->is_virtfn,
+ &bnxt_re_aeq_handler);
if (rc) {
pr_err("Failed to enable RCFW channel: %#x\n", rc);
goto free_ring;
@@ -1347,7 +1403,8 @@ static int bnxt_re_ib_reg(struct bnxt_re_dev *rdev)
if (!rdev->is_virtfn)
bnxt_re_set_resource_limits(rdev);
- rc = bnxt_qplib_alloc_ctx(rdev->en_dev->pdev, &rdev->qplib_ctx, 0);
+ rc = bnxt_qplib_alloc_ctx(rdev->en_dev->pdev, &rdev->qplib_ctx, 0,
+ bnxt_qplib_is_chip_gen_p5(&rdev->chip_ctx));
if (rc) {
pr_err("Failed to allocate QPLIB context: %#x\n", rc);
goto disable_rcfw;
@@ -1418,7 +1475,8 @@ free_ctx:
disable_rcfw:
bnxt_qplib_disable_rcfw_channel(&rdev->rcfw);
free_ring:
- bnxt_re_net_ring_free(rdev, rdev->rcfw.creq_ring_id);
+ type = bnxt_qplib_get_ring_type(&rdev->chip_ctx);
+ bnxt_re_net_ring_free(rdev, rdev->rcfw.creq_ring_id, type);
free_rcfw:
bnxt_qplib_free_rcfw_channel(&rdev->rcfw);
fail:
diff --git a/drivers/infiniband/hw/bnxt_re/qplib_fp.c b/drivers/infiniband/hw/bnxt_re/qplib_fp.c
index b98b054148cd..71c34d5b0ac0 100644
--- a/drivers/infiniband/hw/bnxt_re/qplib_fp.c
+++ b/drivers/infiniband/hw/bnxt_re/qplib_fp.c
@@ -44,6 +44,7 @@
#include <linux/slab.h>
#include <linux/pci.h>
#include <linux/prefetch.h>
+#include <linux/if_ether.h>
#include "roce_hsi.h"
@@ -244,6 +245,7 @@ static void bnxt_qplib_service_nq(unsigned long data)
u16 type;
int budget = nq->budget;
uintptr_t q_handle;
+ bool gen_p5 = bnxt_qplib_is_chip_gen_p5(nq->res->cctx);
/* Service the NQ until empty */
raw_cons = hwq->cons;
@@ -290,7 +292,7 @@ static void bnxt_qplib_service_nq(unsigned long data)
q_handle |= (u64)le32_to_cpu(nqsrqe->srq_handle_high)
<< 32;
bnxt_qplib_arm_srq((struct bnxt_qplib_srq *)q_handle,
- DBR_DBR_TYPE_SRQ_ARMENA);
+ DBC_DBC_TYPE_SRQ_ARMENA);
if (!nq->srqn_handler(nq,
(struct bnxt_qplib_srq *)q_handle,
nqsrqe->event))
@@ -312,7 +314,9 @@ static void bnxt_qplib_service_nq(unsigned long data)
}
if (hwq->cons != raw_cons) {
hwq->cons = raw_cons;
- NQ_DB_REARM(nq->bar_reg_iomem, hwq->cons, hwq->max_elements);
+ bnxt_qplib_ring_nq_db_rearm(nq->bar_reg_iomem, hwq->cons,
+ hwq->max_elements, nq->ring_id,
+ gen_p5);
}
}
@@ -336,9 +340,11 @@ static irqreturn_t bnxt_qplib_nq_irq(int irq, void *dev_instance)
void bnxt_qplib_nq_stop_irq(struct bnxt_qplib_nq *nq, bool kill)
{
+ bool gen_p5 = bnxt_qplib_is_chip_gen_p5(nq->res->cctx);
tasklet_disable(&nq->worker);
/* Mask h/w interrupt */
- NQ_DB(nq->bar_reg_iomem, nq->hwq.cons, nq->hwq.max_elements);
+ bnxt_qplib_ring_nq_db(nq->bar_reg_iomem, nq->hwq.cons,
+ nq->hwq.max_elements, nq->ring_id, gen_p5);
/* Sync with last running IRQ handler */
synchronize_irq(nq->vector);
if (kill)
@@ -373,6 +379,7 @@ void bnxt_qplib_disable_nq(struct bnxt_qplib_nq *nq)
int bnxt_qplib_nq_start_irq(struct bnxt_qplib_nq *nq, int nq_indx,
int msix_vector, bool need_init)
{
+ bool gen_p5 = bnxt_qplib_is_chip_gen_p5(nq->res->cctx);
int rc;
if (nq->requested)
@@ -399,7 +406,8 @@ int bnxt_qplib_nq_start_irq(struct bnxt_qplib_nq *nq, int nq_indx,
nq->vector, nq_indx);
}
nq->requested = true;
- NQ_DB_REARM(nq->bar_reg_iomem, nq->hwq.cons, nq->hwq.max_elements);
+ bnxt_qplib_ring_nq_db_rearm(nq->bar_reg_iomem, nq->hwq.cons,
+ nq->hwq.max_elements, nq->ring_id, gen_p5);
return rc;
}
@@ -433,7 +441,8 @@ int bnxt_qplib_enable_nq(struct pci_dev *pdev, struct bnxt_qplib_nq *nq,
rc = -ENOMEM;
goto fail;
}
- nq->bar_reg_iomem = ioremap_nocache(nq_base + nq->bar_reg_off, 4);
+ /* Unconditionally map 8 bytes to support 57500 series */
+ nq->bar_reg_iomem = ioremap_nocache(nq_base + nq->bar_reg_off, 8);
if (!nq->bar_reg_iomem) {
rc = -ENOMEM;
goto fail;
@@ -462,15 +471,17 @@ void bnxt_qplib_free_nq(struct bnxt_qplib_nq *nq)
int bnxt_qplib_alloc_nq(struct pci_dev *pdev, struct bnxt_qplib_nq *nq)
{
+ u8 hwq_type;
+
nq->pdev = pdev;
if (!nq->hwq.max_elements ||
nq->hwq.max_elements > BNXT_QPLIB_NQE_MAX_CNT)
nq->hwq.max_elements = BNXT_QPLIB_NQE_MAX_CNT;
-
+ hwq_type = bnxt_qplib_get_hwq_type(nq->res);
if (bnxt_qplib_alloc_init_hwq(nq->pdev, &nq->hwq, NULL, 0,
&nq->hwq.max_elements,
BNXT_QPLIB_MAX_NQE_ENTRY_SIZE, 0,
- PAGE_SIZE, HWQ_TYPE_L2_CMPL))
+ PAGE_SIZE, hwq_type))
return -ENOMEM;
nq->budget = 8;
@@ -481,21 +492,19 @@ int bnxt_qplib_alloc_nq(struct pci_dev *pdev, struct bnxt_qplib_nq *nq)
static void bnxt_qplib_arm_srq(struct bnxt_qplib_srq *srq, u32 arm_type)
{
struct bnxt_qplib_hwq *srq_hwq = &srq->hwq;
- struct dbr_dbr db_msg = { 0 };
void __iomem *db;
- u32 sw_prod = 0;
+ u32 sw_prod;
+ u64 val = 0;
/* Ring DB */
- sw_prod = (arm_type == DBR_DBR_TYPE_SRQ_ARM) ? srq->threshold :
- HWQ_CMP(srq_hwq->prod, srq_hwq);
- db_msg.index = cpu_to_le32((sw_prod << DBR_DBR_INDEX_SFT) &
- DBR_DBR_INDEX_MASK);
- db_msg.type_xid = cpu_to_le32(((srq->id << DBR_DBR_XID_SFT) &
- DBR_DBR_XID_MASK) | arm_type);
- db = (arm_type == DBR_DBR_TYPE_SRQ_ARMENA) ?
- srq->dbr_base : srq->dpi->dbr;
- wmb(); /* barrier before db ring */
- __iowrite64_copy(db, &db_msg, sizeof(db_msg) / sizeof(u64));
+ sw_prod = (arm_type == DBC_DBC_TYPE_SRQ_ARM) ?
+ srq->threshold : HWQ_CMP(srq_hwq->prod, srq_hwq);
+ db = (arm_type == DBC_DBC_TYPE_SRQ_ARMENA) ? srq->dbr_base :
+ srq->dpi->dbr;
+ val = ((srq->id << DBC_DBC_XID_SFT) & DBC_DBC_XID_MASK) | arm_type;
+ val <<= 32;
+ val |= (sw_prod << DBC_DBC_INDEX_SFT) & DBC_DBC_INDEX_MASK;
+ writeq(val, db);
}
int bnxt_qplib_destroy_srq(struct bnxt_qplib_res *res,
@@ -590,7 +599,7 @@ int bnxt_qplib_create_srq(struct bnxt_qplib_res *res,
srq->id = le32_to_cpu(resp.xid);
srq->dbr_base = res->dpi_tbl.dbr_bar_reg_iomem;
if (srq->threshold)
- bnxt_qplib_arm_srq(srq, DBR_DBR_TYPE_SRQ_ARMENA);
+ bnxt_qplib_arm_srq(srq, DBC_DBC_TYPE_SRQ_ARMENA);
srq->arm_req = false;
return 0;
@@ -614,7 +623,7 @@ int bnxt_qplib_modify_srq(struct bnxt_qplib_res *res,
srq_hwq->max_elements - sw_cons + sw_prod;
if (count > srq->threshold) {
srq->arm_req = false;
- bnxt_qplib_arm_srq(srq, DBR_DBR_TYPE_SRQ_ARM);
+ bnxt_qplib_arm_srq(srq, DBC_DBC_TYPE_SRQ_ARM);
} else {
/* Deferred arming */
srq->arm_req = true;
@@ -702,10 +711,10 @@ int bnxt_qplib_post_srq_recv(struct bnxt_qplib_srq *srq,
srq_hwq->max_elements - sw_cons + sw_prod;
spin_unlock(&srq_hwq->lock);
/* Ring DB */
- bnxt_qplib_arm_srq(srq, DBR_DBR_TYPE_SRQ);
+ bnxt_qplib_arm_srq(srq, DBC_DBC_TYPE_SRQ);
if (srq->arm_req == true && count > srq->threshold) {
srq->arm_req = false;
- bnxt_qplib_arm_srq(srq, DBR_DBR_TYPE_SRQ_ARM);
+ bnxt_qplib_arm_srq(srq, DBC_DBC_TYPE_SRQ_ARM);
}
done:
return rc;
@@ -853,18 +862,19 @@ exit:
int bnxt_qplib_create_qp(struct bnxt_qplib_res *res, struct bnxt_qplib_qp *qp)
{
struct bnxt_qplib_rcfw *rcfw = res->rcfw;
- struct sq_send *hw_sq_send_hdr, **hw_sq_send_ptr;
- struct cmdq_create_qp req;
- struct creq_create_qp_resp resp;
- struct bnxt_qplib_pbl *pbl;
- struct sq_psn_search **psn_search_ptr;
unsigned long int psn_search, poff = 0;
+ struct sq_psn_search **psn_search_ptr;
struct bnxt_qplib_q *sq = &qp->sq;
struct bnxt_qplib_q *rq = &qp->rq;
+ int i, rc, req_size, psn_sz = 0;
+ struct sq_send **hw_sq_send_ptr;
+ struct creq_create_qp_resp resp;
struct bnxt_qplib_hwq *xrrq;
- int i, rc, req_size, psn_sz;
u16 cmd_flags = 0, max_ssge;
- u32 sw_prod, qp_flags = 0;
+ struct cmdq_create_qp req;
+ struct bnxt_qplib_pbl *pbl;
+ u32 qp_flags = 0;
+ u16 max_rsge;
RCFW_CMD_PREP(req, CREATE_QP, cmd_flags);
@@ -874,8 +884,11 @@ int bnxt_qplib_create_qp(struct bnxt_qplib_res *res, struct bnxt_qplib_qp *qp)
req.qp_handle = cpu_to_le64(qp->qp_handle);
/* SQ */
- psn_sz = (qp->type == CMDQ_CREATE_QP_TYPE_RC) ?
- sizeof(struct sq_psn_search) : 0;
+ if (qp->type == CMDQ_CREATE_QP_TYPE_RC) {
+ psn_sz = bnxt_qplib_is_chip_gen_p5(res->cctx) ?
+ sizeof(struct sq_psn_search_ext) :
+ sizeof(struct sq_psn_search);
+ }
sq->hwq.max_elements = sq->max_wqe;
rc = bnxt_qplib_alloc_init_hwq(res->pdev, &sq->hwq, sq->sglist,
sq->nmap, &sq->hwq.max_elements,
@@ -905,10 +918,16 @@ int bnxt_qplib_create_qp(struct bnxt_qplib_res *res, struct bnxt_qplib_qp *qp)
poff = (psn_search & ~PAGE_MASK) /
BNXT_QPLIB_MAX_PSNE_ENTRY_SIZE;
}
- for (i = 0; i < sq->hwq.max_elements; i++)
+ for (i = 0; i < sq->hwq.max_elements; i++) {
sq->swq[i].psn_search =
&psn_search_ptr[get_psne_pg(i + poff)]
[get_psne_idx(i + poff)];
+ /*psns_ext will be used only for P5 chips. */
+ sq->swq[i].psn_ext =
+ (struct sq_psn_search_ext *)
+ &psn_search_ptr[get_psne_pg(i + poff)]
+ [get_psne_idx(i + poff)];
+ }
}
pbl = &sq->hwq.pbl[PBL_LVL_0];
req.sq_pbl = cpu_to_le64(pbl->pg_map_arr[0]);
@@ -929,14 +948,6 @@ int bnxt_qplib_create_qp(struct bnxt_qplib_res *res, struct bnxt_qplib_qp *qp)
CMDQ_CREATE_QP_SQ_PG_SIZE_PG_1G :
CMDQ_CREATE_QP_SQ_PG_SIZE_PG_4K);
- /* initialize all SQ WQEs to LOCAL_INVALID (sq prep for hw fetch) */
- hw_sq_send_ptr = (struct sq_send **)sq->hwq.pbl_ptr;
- for (sw_prod = 0; sw_prod < sq->hwq.max_elements; sw_prod++) {
- hw_sq_send_hdr = &hw_sq_send_ptr[get_sqe_pg(sw_prod)]
- [get_sqe_idx(sw_prod)];
- hw_sq_send_hdr->wqe_type = SQ_BASE_WQE_TYPE_LOCAL_INVALID;
- }
-
if (qp->scq)
req.scq_cid = cpu_to_le32(qp->scq->id);
@@ -1007,8 +1018,9 @@ int bnxt_qplib_create_qp(struct bnxt_qplib_res *res, struct bnxt_qplib_qp *qp)
req.sq_fwo_sq_sge = cpu_to_le16(
((max_ssge & CMDQ_CREATE_QP_SQ_SGE_MASK)
<< CMDQ_CREATE_QP_SQ_SGE_SFT) | 0);
+ max_rsge = bnxt_qplib_is_chip_gen_p5(res->cctx) ? 6 : rq->max_sge;
req.rq_fwo_rq_sge = cpu_to_le16(
- ((rq->max_sge & CMDQ_CREATE_QP_RQ_SGE_MASK)
+ ((max_rsge & CMDQ_CREATE_QP_RQ_SGE_MASK)
<< CMDQ_CREATE_QP_RQ_SGE_SFT) | 0);
/* ORRQ and IRRQ */
if (psn_sz) {
@@ -1053,6 +1065,7 @@ int bnxt_qplib_create_qp(struct bnxt_qplib_res *res, struct bnxt_qplib_qp *qp)
qp->id = le32_to_cpu(resp.xid);
qp->cur_qp_state = CMDQ_MODIFY_QP_NEW_STATE_RESET;
+ qp->cctx = res->cctx;
INIT_LIST_HEAD(&qp->sq_flush);
INIT_LIST_HEAD(&qp->rq_flush);
rcfw->qp_tbl[qp->id].qp_id = qp->id;
@@ -1494,19 +1507,16 @@ void *bnxt_qplib_get_qp1_rq_buf(struct bnxt_qplib_qp *qp,
void bnxt_qplib_post_send_db(struct bnxt_qplib_qp *qp)
{
struct bnxt_qplib_q *sq = &qp->sq;
- struct dbr_dbr db_msg = { 0 };
u32 sw_prod;
+ u64 val = 0;
+ val = (((qp->id << DBC_DBC_XID_SFT) & DBC_DBC_XID_MASK) |
+ DBC_DBC_TYPE_SQ);
+ val <<= 32;
sw_prod = HWQ_CMP(sq->hwq.prod, &sq->hwq);
-
- db_msg.index = cpu_to_le32((sw_prod << DBR_DBR_INDEX_SFT) &
- DBR_DBR_INDEX_MASK);
- db_msg.type_xid =
- cpu_to_le32(((qp->id << DBR_DBR_XID_SFT) & DBR_DBR_XID_MASK) |
- DBR_DBR_TYPE_SQ);
+ val |= (sw_prod << DBC_DBC_INDEX_SFT) & DBC_DBC_INDEX_MASK;
/* Flush all the WQE writes to HW */
- wmb();
- __iowrite64_copy(qp->dpi->dbr, &db_msg, sizeof(db_msg) / sizeof(u64));
+ writeq(val, qp->dpi->dbr);
}
int bnxt_qplib_post_send(struct bnxt_qplib_qp *qp,
@@ -1617,7 +1627,8 @@ int bnxt_qplib_post_send(struct bnxt_qplib_qp *qp,
((offsetof(typeof(*sqe), data) + 15) >> 4);
sqe->inv_key_or_imm_data = cpu_to_le32(
wqe->send.inv_key);
- if (qp->type == CMDQ_CREATE_QP_TYPE_UD) {
+ if (qp->type == CMDQ_CREATE_QP_TYPE_UD ||
+ qp->type == CMDQ_CREATE_QP_TYPE_GSI) {
sqe->q_key = cpu_to_le32(wqe->send.q_key);
sqe->dst_qp = cpu_to_le32(
wqe->send.dst_qp & SQ_SEND_DST_QP_MASK);
@@ -1741,14 +1752,26 @@ int bnxt_qplib_post_send(struct bnxt_qplib_qp *qp,
}
swq->next_psn = sq->psn & BTH_PSN_MASK;
if (swq->psn_search) {
- swq->psn_search->opcode_start_psn = cpu_to_le32(
- ((swq->start_psn << SQ_PSN_SEARCH_START_PSN_SFT) &
- SQ_PSN_SEARCH_START_PSN_MASK) |
- ((wqe->type << SQ_PSN_SEARCH_OPCODE_SFT) &
- SQ_PSN_SEARCH_OPCODE_MASK));
- swq->psn_search->flags_next_psn = cpu_to_le32(
- ((swq->next_psn << SQ_PSN_SEARCH_NEXT_PSN_SFT) &
- SQ_PSN_SEARCH_NEXT_PSN_MASK));
+ u32 opcd_spsn;
+ u32 flg_npsn;
+
+ opcd_spsn = ((swq->start_psn << SQ_PSN_SEARCH_START_PSN_SFT) &
+ SQ_PSN_SEARCH_START_PSN_MASK);
+ opcd_spsn |= ((wqe->type << SQ_PSN_SEARCH_OPCODE_SFT) &
+ SQ_PSN_SEARCH_OPCODE_MASK);
+ flg_npsn = ((swq->next_psn << SQ_PSN_SEARCH_NEXT_PSN_SFT) &
+ SQ_PSN_SEARCH_NEXT_PSN_MASK);
+ if (bnxt_qplib_is_chip_gen_p5(qp->cctx)) {
+ swq->psn_ext->opcode_start_psn =
+ cpu_to_le32(opcd_spsn);
+ swq->psn_ext->flags_next_psn =
+ cpu_to_le32(flg_npsn);
+ } else {
+ swq->psn_search->opcode_start_psn =
+ cpu_to_le32(opcd_spsn);
+ swq->psn_search->flags_next_psn =
+ cpu_to_le32(flg_npsn);
+ }
}
queue_err:
if (sch_handler) {
@@ -1785,19 +1808,16 @@ done:
void bnxt_qplib_post_recv_db(struct bnxt_qplib_qp *qp)
{
struct bnxt_qplib_q *rq = &qp->rq;
- struct dbr_dbr db_msg = { 0 };
u32 sw_prod;
+ u64 val = 0;
+ val = (((qp->id << DBC_DBC_XID_SFT) & DBC_DBC_XID_MASK) |
+ DBC_DBC_TYPE_RQ);
+ val <<= 32;
sw_prod = HWQ_CMP(rq->hwq.prod, &rq->hwq);
- db_msg.index = cpu_to_le32((sw_prod << DBR_DBR_INDEX_SFT) &
- DBR_DBR_INDEX_MASK);
- db_msg.type_xid =
- cpu_to_le32(((qp->id << DBR_DBR_XID_SFT) & DBR_DBR_XID_MASK) |
- DBR_DBR_TYPE_RQ);
-
+ val |= (sw_prod << DBC_DBC_INDEX_SFT) & DBC_DBC_INDEX_MASK;
/* Flush the writes to HW Rx WQE before the ringing Rx DB */
- wmb();
- __iowrite64_copy(qp->dpi->dbr, &db_msg, sizeof(db_msg) / sizeof(u64));
+ writeq(val, qp->dpi->dbr);
}
int bnxt_qplib_post_recv(struct bnxt_qplib_qp *qp,
@@ -1881,32 +1901,28 @@ done:
/* Spinlock must be held */
static void bnxt_qplib_arm_cq_enable(struct bnxt_qplib_cq *cq)
{
- struct dbr_dbr db_msg = { 0 };
+ u64 val = 0;
- db_msg.type_xid =
- cpu_to_le32(((cq->id << DBR_DBR_XID_SFT) & DBR_DBR_XID_MASK) |
- DBR_DBR_TYPE_CQ_ARMENA);
+ val = ((cq->id << DBC_DBC_XID_SFT) & DBC_DBC_XID_MASK) |
+ DBC_DBC_TYPE_CQ_ARMENA;
+ val <<= 32;
/* Flush memory writes before enabling the CQ */
- wmb();
- __iowrite64_copy(cq->dbr_base, &db_msg, sizeof(db_msg) / sizeof(u64));
+ writeq(val, cq->dbr_base);
}
static void bnxt_qplib_arm_cq(struct bnxt_qplib_cq *cq, u32 arm_type)
{
struct bnxt_qplib_hwq *cq_hwq = &cq->hwq;
- struct dbr_dbr db_msg = { 0 };
u32 sw_cons;
+ u64 val = 0;
/* Ring DB */
+ val = ((cq->id << DBC_DBC_XID_SFT) & DBC_DBC_XID_MASK) | arm_type;
+ val <<= 32;
sw_cons = HWQ_CMP(cq_hwq->cons, cq_hwq);
- db_msg.index = cpu_to_le32((sw_cons << DBR_DBR_INDEX_SFT) &
- DBR_DBR_INDEX_MASK);
- db_msg.type_xid =
- cpu_to_le32(((cq->id << DBR_DBR_XID_SFT) & DBR_DBR_XID_MASK) |
- arm_type);
+ val |= (sw_cons << DBC_DBC_INDEX_SFT) & DBC_DBC_INDEX_MASK;
/* flush memory writes before arming the CQ */
- wmb();
- __iowrite64_copy(cq->dpi->dbr, &db_msg, sizeof(db_msg) / sizeof(u64));
+ writeq(val, cq->dpi->dbr);
}
int bnxt_qplib_create_cq(struct bnxt_qplib_res *res, struct bnxt_qplib_cq *cq)
@@ -2053,6 +2069,7 @@ static int __flush_rq(struct bnxt_qplib_q *rq, struct bnxt_qplib_qp *qp,
opcode = CQ_BASE_CQE_TYPE_RES_RC;
break;
case CMDQ_CREATE_QP_TYPE_UD:
+ case CMDQ_CREATE_QP_TYPE_GSI:
opcode = CQ_BASE_CQE_TYPE_RES_UD;
break;
}
@@ -2125,7 +2142,7 @@ static int do_wa9060(struct bnxt_qplib_qp *qp, struct bnxt_qplib_cq *cq,
sq->send_phantom = true;
/* TODO: Only ARM if the previous SQE is ARMALL */
- bnxt_qplib_arm_cq(cq, DBR_DBR_TYPE_CQ_ARMALL);
+ bnxt_qplib_arm_cq(cq, DBC_DBC_TYPE_CQ_ARMALL);
rc = -EAGAIN;
goto out;
@@ -2410,12 +2427,14 @@ static int bnxt_qplib_cq_process_res_ud(struct bnxt_qplib_cq *cq,
}
cqe = *pcqe;
cqe->opcode = hwcqe->cqe_type_toggle & CQ_BASE_CQE_TYPE_MASK;
- cqe->length = le32_to_cpu(hwcqe->length);
+ cqe->length = (u32)le16_to_cpu(hwcqe->length);
+ cqe->cfa_meta = le16_to_cpu(hwcqe->cfa_metadata);
cqe->invrkey = le32_to_cpu(hwcqe->imm_data);
cqe->flags = le16_to_cpu(hwcqe->flags);
cqe->status = hwcqe->status;
cqe->qp_handle = (u64)(unsigned long)qp;
- memcpy(cqe->smac, hwcqe->src_mac, 6);
+ /*FIXME: Endianness fix needed for smace */
+ memcpy(cqe->smac, hwcqe->src_mac, ETH_ALEN);
wr_id_idx = le32_to_cpu(hwcqe->src_qp_high_srq_or_rq_wr_id)
& CQ_RES_UD_SRQ_OR_RQ_WR_ID_MASK;
cqe->src_qp = le16_to_cpu(hwcqe->src_qp_low) |
@@ -2794,7 +2813,7 @@ int bnxt_qplib_poll_cq(struct bnxt_qplib_cq *cq, struct bnxt_qplib_cqe *cqe,
}
if (cq->hwq.cons != raw_cons) {
cq->hwq.cons = raw_cons;
- bnxt_qplib_arm_cq(cq, DBR_DBR_TYPE_CQ);
+ bnxt_qplib_arm_cq(cq, DBC_DBC_TYPE_CQ);
}
exit:
return num_cqes - budget;
diff --git a/drivers/infiniband/hw/bnxt_re/qplib_fp.h b/drivers/infiniband/hw/bnxt_re/qplib_fp.h
index 72352ca80ace..3f618b5f1f06 100644
--- a/drivers/infiniband/hw/bnxt_re/qplib_fp.h
+++ b/drivers/infiniband/hw/bnxt_re/qplib_fp.h
@@ -106,6 +106,7 @@ struct bnxt_qplib_swq {
u32 start_psn;
u32 next_psn;
struct sq_psn_search *psn_search;
+ struct sq_psn_search_ext *psn_ext;
};
struct bnxt_qplib_swqe {
@@ -254,6 +255,7 @@ struct bnxt_qplib_q {
struct bnxt_qplib_qp {
struct bnxt_qplib_pd *pd;
struct bnxt_qplib_dpi *dpi;
+ struct bnxt_qplib_chip_ctx *cctx;
u64 qp_handle;
#define BNXT_QPLIB_QP_ID_INVALID 0xFFFFFFFF
u32 id;
@@ -347,6 +349,7 @@ struct bnxt_qplib_cqe {
u8 type;
u8 opcode;
u32 length;
+ u16 cfa_meta;
u64 wr_id;
union {
__be32 immdata;
@@ -432,13 +435,47 @@ struct bnxt_qplib_cq {
#define NQ_DB_CP_FLAGS (NQ_DB_KEY_CP | \
NQ_DB_IDX_VALID | \
NQ_DB_IRQ_DIS)
-#define NQ_DB_REARM(db, raw_cons, cp_bit) \
- writel(NQ_DB_CP_FLAGS_REARM | ((raw_cons) & ((cp_bit) - 1)), db)
-#define NQ_DB(db, raw_cons, cp_bit) \
- writel(NQ_DB_CP_FLAGS | ((raw_cons) & ((cp_bit) - 1)), db)
+
+static inline void bnxt_qplib_ring_nq_db64(void __iomem *db, u32 index,
+ u32 xid, bool arm)
+{
+ u64 val;
+
+ val = xid & DBC_DBC_XID_MASK;
+ val |= DBC_DBC_PATH_ROCE;
+ val |= arm ? DBC_DBC_TYPE_NQ_ARM : DBC_DBC_TYPE_NQ;
+ val <<= 32;
+ val |= index & DBC_DBC_INDEX_MASK;
+ writeq(val, db);
+}
+
+static inline void bnxt_qplib_ring_nq_db_rearm(void __iomem *db, u32 raw_cons,
+ u32 max_elements, u32 xid,
+ bool gen_p5)
+{
+ u32 index = raw_cons & (max_elements - 1);
+
+ if (gen_p5)
+ bnxt_qplib_ring_nq_db64(db, index, xid, true);
+ else
+ writel(NQ_DB_CP_FLAGS_REARM | (index & DBC_DBC32_XID_MASK), db);
+}
+
+static inline void bnxt_qplib_ring_nq_db(void __iomem *db, u32 raw_cons,
+ u32 max_elements, u32 xid,
+ bool gen_p5)
+{
+ u32 index = raw_cons & (max_elements - 1);
+
+ if (gen_p5)
+ bnxt_qplib_ring_nq_db64(db, index, xid, false);
+ else
+ writel(NQ_DB_CP_FLAGS | (index & DBC_DBC32_XID_MASK), db);
+}
struct bnxt_qplib_nq {
struct pci_dev *pdev;
+ struct bnxt_qplib_res *res;
int vector;
cpumask_t mask;
@@ -448,7 +485,7 @@ struct bnxt_qplib_nq {
struct bnxt_qplib_hwq hwq;
u16 bar_reg;
- u16 bar_reg_off;
+ u32 bar_reg_off;
u16 ring_id;
void __iomem *bar_reg_iomem;
diff --git a/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c b/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c
index 19551aa43850..c6461e957078 100644
--- a/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c
+++ b/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c
@@ -359,11 +359,12 @@ static int bnxt_qplib_process_qp_event(struct bnxt_qplib_rcfw *rcfw,
static void bnxt_qplib_service_creq(unsigned long data)
{
struct bnxt_qplib_rcfw *rcfw = (struct bnxt_qplib_rcfw *)data;
+ bool gen_p5 = bnxt_qplib_is_chip_gen_p5(rcfw->res->cctx);
struct bnxt_qplib_hwq *creq = &rcfw->creq;
+ u32 type, budget = CREQ_ENTRY_POLL_BUDGET;
struct creq_base *creqe, **creq_ptr;
u32 sw_cons, raw_cons;
unsigned long flags;
- u32 type, budget = CREQ_ENTRY_POLL_BUDGET;
/* Service the CREQ until budget is over */
spin_lock_irqsave(&creq->lock, flags);
@@ -407,8 +408,9 @@ static void bnxt_qplib_service_creq(unsigned long data)
if (creq->cons != raw_cons) {
creq->cons = raw_cons;
- CREQ_DB_REARM(rcfw->creq_bar_reg_iomem, raw_cons,
- creq->max_elements);
+ bnxt_qplib_ring_creq_db_rearm(rcfw->creq_bar_reg_iomem,
+ raw_cons, creq->max_elements,
+ rcfw->creq_ring_id, gen_p5);
}
spin_unlock_irqrestore(&creq->lock, flags);
}
@@ -480,11 +482,13 @@ int bnxt_qplib_init_rcfw(struct bnxt_qplib_rcfw *rcfw,
req.log2_dbr_pg_size = cpu_to_le16(PAGE_SHIFT -
RCFW_DBR_BASE_PAGE_SHIFT);
/*
- * VFs need not setup the HW context area, PF
+ * Gen P5 devices doesn't require this allocation
+ * as the L2 driver does the same for RoCE also.
+ * Also, VFs need not setup the HW context area, PF
* shall setup this area for VF. Skipping the
* HW programming
*/
- if (is_virtfn)
+ if (is_virtfn || bnxt_qplib_is_chip_gen_p5(rcfw->res->cctx))
goto skip_ctx_setup;
level = ctx->qpc_tbl.level;
@@ -560,12 +564,15 @@ int bnxt_qplib_alloc_rcfw_channel(struct pci_dev *pdev,
struct bnxt_qplib_ctx *ctx,
int qp_tbl_sz)
{
+ u8 hwq_type;
+
rcfw->pdev = pdev;
rcfw->creq.max_elements = BNXT_QPLIB_CREQE_MAX_CNT;
+ hwq_type = bnxt_qplib_get_hwq_type(rcfw->res);
if (bnxt_qplib_alloc_init_hwq(rcfw->pdev, &rcfw->creq, NULL, 0,
&rcfw->creq.max_elements,
- BNXT_QPLIB_CREQE_UNITS, 0, PAGE_SIZE,
- HWQ_TYPE_L2_CMPL)) {
+ BNXT_QPLIB_CREQE_UNITS,
+ 0, PAGE_SIZE, hwq_type)) {
dev_err(&rcfw->pdev->dev,
"HW channel CREQ allocation failed\n");
goto fail;
@@ -607,10 +614,13 @@ fail:
void bnxt_qplib_rcfw_stop_irq(struct bnxt_qplib_rcfw *rcfw, bool kill)
{
+ bool gen_p5 = bnxt_qplib_is_chip_gen_p5(rcfw->res->cctx);
+
tasklet_disable(&rcfw->worker);
/* Mask h/w interrupts */
- CREQ_DB(rcfw->creq_bar_reg_iomem, rcfw->creq.cons,
- rcfw->creq.max_elements);
+ bnxt_qplib_ring_creq_db(rcfw->creq_bar_reg_iomem, rcfw->creq.cons,
+ rcfw->creq.max_elements, rcfw->creq_ring_id,
+ gen_p5);
/* Sync with last running IRQ-handler */
synchronize_irq(rcfw->vector);
if (kill)
@@ -647,6 +657,7 @@ void bnxt_qplib_disable_rcfw_channel(struct bnxt_qplib_rcfw *rcfw)
int bnxt_qplib_rcfw_start_irq(struct bnxt_qplib_rcfw *rcfw, int msix_vector,
bool need_init)
{
+ bool gen_p5 = bnxt_qplib_is_chip_gen_p5(rcfw->res->cctx);
int rc;
if (rcfw->requested)
@@ -663,8 +674,9 @@ int bnxt_qplib_rcfw_start_irq(struct bnxt_qplib_rcfw *rcfw, int msix_vector,
if (rc)
return rc;
rcfw->requested = true;
- CREQ_DB_REARM(rcfw->creq_bar_reg_iomem, rcfw->creq.cons,
- rcfw->creq.max_elements);
+ bnxt_qplib_ring_creq_db_rearm(rcfw->creq_bar_reg_iomem,
+ rcfw->creq.cons, rcfw->creq.max_elements,
+ rcfw->creq_ring_id, gen_p5);
return 0;
}
@@ -684,8 +696,7 @@ int bnxt_qplib_enable_rcfw_channel(struct pci_dev *pdev,
/* General */
rcfw->seq_num = 0;
set_bit(FIRMWARE_FIRST_FLAG, &rcfw->flags);
- bmap_size = BITS_TO_LONGS(rcfw->cmdq_depth *
- sizeof(unsigned long));
+ bmap_size = BITS_TO_LONGS(rcfw->cmdq_depth) * sizeof(unsigned long);
rcfw->cmdq_bitmap = kzalloc(bmap_size, GFP_KERNEL);
if (!rcfw->cmdq_bitmap)
return -ENOMEM;
@@ -718,8 +729,9 @@ int bnxt_qplib_enable_rcfw_channel(struct pci_dev *pdev,
dev_err(&rcfw->pdev->dev,
"CREQ BAR region %d resc start is 0!\n",
rcfw->creq_bar_reg);
+ /* Unconditionally map 8 bytes to support 57500 series */
rcfw->creq_bar_reg_iomem = ioremap_nocache(res_base + cp_bar_reg_off,
- 4);
+ 8);
if (!rcfw->creq_bar_reg_iomem) {
dev_err(&rcfw->pdev->dev, "CREQ BAR region %d mapping failed\n",
rcfw->creq_bar_reg);
diff --git a/drivers/infiniband/hw/bnxt_re/qplib_rcfw.h b/drivers/infiniband/hw/bnxt_re/qplib_rcfw.h
index be0ef0e8c53e..2138533bb642 100644
--- a/drivers/infiniband/hw/bnxt_re/qplib_rcfw.h
+++ b/drivers/infiniband/hw/bnxt_re/qplib_rcfw.h
@@ -157,10 +157,46 @@ static inline u32 get_creq_idx(u32 val)
#define CREQ_DB_CP_FLAGS (CREQ_DB_KEY_CP | \
CREQ_DB_IDX_VALID | \
CREQ_DB_IRQ_DIS)
-#define CREQ_DB_REARM(db, raw_cons, cp_bit) \
- writel(CREQ_DB_CP_FLAGS_REARM | ((raw_cons) & ((cp_bit) - 1)), db)
-#define CREQ_DB(db, raw_cons, cp_bit) \
- writel(CREQ_DB_CP_FLAGS | ((raw_cons) & ((cp_bit) - 1)), db)
+
+static inline void bnxt_qplib_ring_creq_db64(void __iomem *db, u32 index,
+ u32 xid, bool arm)
+{
+ u64 val = 0;
+
+ val = xid & DBC_DBC_XID_MASK;
+ val |= DBC_DBC_PATH_ROCE;
+ val |= arm ? DBC_DBC_TYPE_NQ_ARM : DBC_DBC_TYPE_NQ;
+ val <<= 32;
+ val |= index & DBC_DBC_INDEX_MASK;
+
+ writeq(val, db);
+}
+
+static inline void bnxt_qplib_ring_creq_db_rearm(void __iomem *db, u32 raw_cons,
+ u32 max_elements, u32 xid,
+ bool gen_p5)
+{
+ u32 index = raw_cons & (max_elements - 1);
+
+ if (gen_p5)
+ bnxt_qplib_ring_creq_db64(db, index, xid, true);
+ else
+ writel(CREQ_DB_CP_FLAGS_REARM | (index & DBC_DBC32_XID_MASK),
+ db);
+}
+
+static inline void bnxt_qplib_ring_creq_db(void __iomem *db, u32 raw_cons,
+ u32 max_elements, u32 xid,
+ bool gen_p5)
+{
+ u32 index = raw_cons & (max_elements - 1);
+
+ if (gen_p5)
+ bnxt_qplib_ring_creq_db64(db, index, xid, true);
+ else
+ writel(CREQ_DB_CP_FLAGS | (index & DBC_DBC32_XID_MASK),
+ db);
+}
#define CREQ_ENTRY_POLL_BUDGET 0x100
@@ -187,6 +223,7 @@ struct bnxt_qplib_qp_node {
/* RCFW Communication Channels */
struct bnxt_qplib_rcfw {
struct pci_dev *pdev;
+ struct bnxt_qplib_res *res;
int vector;
struct tasklet_struct worker;
bool requested;
diff --git a/drivers/infiniband/hw/bnxt_re/qplib_res.c b/drivers/infiniband/hw/bnxt_re/qplib_res.c
index 57d4951679cb..0bc24f934829 100644
--- a/drivers/infiniband/hw/bnxt_re/qplib_res.c
+++ b/drivers/infiniband/hw/bnxt_re/qplib_res.c
@@ -85,7 +85,7 @@ static void __free_pbl(struct pci_dev *pdev, struct bnxt_qplib_pbl *pbl,
static int __alloc_pbl(struct pci_dev *pdev, struct bnxt_qplib_pbl *pbl,
struct scatterlist *sghead, u32 pages, u32 pg_size)
{
- struct scatterlist *sg;
+ struct sg_dma_page_iter sg_iter;
bool is_umem = false;
int i;
@@ -116,13 +116,11 @@ static int __alloc_pbl(struct pci_dev *pdev, struct bnxt_qplib_pbl *pbl,
} else {
i = 0;
is_umem = true;
- for_each_sg(sghead, sg, pages, i) {
- pbl->pg_map_arr[i] = sg_dma_address(sg);
- pbl->pg_arr[i] = sg_virt(sg);
- if (!pbl->pg_arr[i])
- goto fail;
-
+ for_each_sg_dma_page (sghead, &sg_iter, pages, 0) {
+ pbl->pg_map_arr[i] = sg_page_iter_dma_address(&sg_iter);
+ pbl->pg_arr[i] = NULL;
pbl->pg_count++;
+ i++;
}
}
@@ -330,13 +328,13 @@ void bnxt_qplib_free_ctx(struct pci_dev *pdev,
*/
int bnxt_qplib_alloc_ctx(struct pci_dev *pdev,
struct bnxt_qplib_ctx *ctx,
- bool virt_fn)
+ bool virt_fn, bool is_p5)
{
int i, j, k, rc = 0;
int fnz_idx = -1;
__le64 **pbl_ptr;
- if (virt_fn)
+ if (virt_fn || is_p5)
goto stats_alloc;
/* QPC Tables */
@@ -762,7 +760,11 @@ static int bnxt_qplib_alloc_stats_ctx(struct pci_dev *pdev,
{
memset(stats, 0, sizeof(*stats));
stats->fw_id = -1;
- stats->size = sizeof(struct ctx_hw_stats);
+ /* 128 byte aligned context memory is required only for 57500.
+ * However making this unconditional, it does not harm previous
+ * generation.
+ */
+ stats->size = ALIGN(sizeof(struct ctx_hw_stats), 128);
stats->dma = dma_alloc_coherent(&pdev->dev, stats->size,
&stats->dma_map, GFP_KERNEL);
if (!stats->dma) {
diff --git a/drivers/infiniband/hw/bnxt_re/qplib_res.h b/drivers/infiniband/hw/bnxt_re/qplib_res.h
index 1e80aa7bbcce..32cebd0f1436 100644
--- a/drivers/infiniband/hw/bnxt_re/qplib_res.h
+++ b/drivers/infiniband/hw/bnxt_re/qplib_res.h
@@ -180,12 +180,20 @@ struct bnxt_qplib_ctx {
u64 hwrm_intf_ver;
};
+struct bnxt_qplib_chip_ctx {
+ u16 chip_num;
+ u8 chip_rev;
+ u8 chip_metal;
+};
+
+#define CHIP_NUM_57500 0x1750
+
struct bnxt_qplib_res {
struct pci_dev *pdev;
+ struct bnxt_qplib_chip_ctx *cctx;
struct net_device *netdev;
struct bnxt_qplib_rcfw *rcfw;
-
struct bnxt_qplib_pd_tbl pd_tbl;
struct bnxt_qplib_sgid_tbl sgid_tbl;
struct bnxt_qplib_pkey_tbl pkey_tbl;
@@ -193,6 +201,24 @@ struct bnxt_qplib_res {
bool prio;
};
+static inline bool bnxt_qplib_is_chip_gen_p5(struct bnxt_qplib_chip_ctx *cctx)
+{
+ return (cctx->chip_num == CHIP_NUM_57500);
+}
+
+static inline u8 bnxt_qplib_get_hwq_type(struct bnxt_qplib_res *res)
+{
+ return bnxt_qplib_is_chip_gen_p5(res->cctx) ?
+ HWQ_TYPE_QUEUE : HWQ_TYPE_L2_CMPL;
+}
+
+static inline u8 bnxt_qplib_get_ring_type(struct bnxt_qplib_chip_ctx *cctx)
+{
+ return bnxt_qplib_is_chip_gen_p5(cctx) ?
+ RING_ALLOC_REQ_RING_TYPE_NQ :
+ RING_ALLOC_REQ_RING_TYPE_ROCE_CMPL;
+}
+
#define to_bnxt_qplib(ptr, type, member) \
container_of(ptr, type, member)
@@ -226,5 +252,5 @@ void bnxt_qplib_free_ctx(struct pci_dev *pdev,
struct bnxt_qplib_ctx *ctx);
int bnxt_qplib_alloc_ctx(struct pci_dev *pdev,
struct bnxt_qplib_ctx *ctx,
- bool virt_fn);
+ bool virt_fn, bool is_p5);
#endif /* __BNXT_QPLIB_RES_H__ */
diff --git a/drivers/infiniband/hw/bnxt_re/qplib_sp.c b/drivers/infiniband/hw/bnxt_re/qplib_sp.c
index efa0f2949dc7..e9c53e406404 100644
--- a/drivers/infiniband/hw/bnxt_re/qplib_sp.c
+++ b/drivers/infiniband/hw/bnxt_re/qplib_sp.c
@@ -119,7 +119,8 @@ int bnxt_qplib_get_dev_attr(struct bnxt_qplib_rcfw *rcfw,
* reporting the max number
*/
attr->max_qp_wqes -= BNXT_QPLIB_RESERVED_QP_WRS;
- attr->max_qp_sges = sb->max_sge;
+ attr->max_qp_sges = bnxt_qplib_is_chip_gen_p5(rcfw->res->cctx) ?
+ 6 : sb->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;
diff --git a/drivers/infiniband/hw/bnxt_re/roce_hsi.h b/drivers/infiniband/hw/bnxt_re/roce_hsi.h
index 8a9ead419ac2..e4b09e7c2175 100644
--- a/drivers/infiniband/hw/bnxt_re/roce_hsi.h
+++ b/drivers/infiniband/hw/bnxt_re/roce_hsi.h
@@ -49,11 +49,11 @@ struct cmpl_doorbell {
#define CMPL_DOORBELL_IDX_SFT 0
#define CMPL_DOORBELL_RESERVED_MASK 0x3000000UL
#define CMPL_DOORBELL_RESERVED_SFT 24
- #define CMPL_DOORBELL_IDX_VALID 0x4000000UL
+ #define CMPL_DOORBELL_IDX_VALID 0x4000000UL
#define CMPL_DOORBELL_MASK 0x8000000UL
#define CMPL_DOORBELL_KEY_MASK 0xf0000000UL
#define CMPL_DOORBELL_KEY_SFT 28
- #define CMPL_DOORBELL_KEY_CMPL (0x2UL << 28)
+ #define CMPL_DOORBELL_KEY_CMPL (0x2UL << 28)
};
/* Status Door Bell Format (4 bytes) */
@@ -71,46 +71,56 @@ struct status_doorbell {
/* RoCE Host Structures */
/* Doorbell Structures */
-/* 64b Doorbell Format (8 bytes) */
-struct dbr_dbr {
- __le32 index;
- #define DBR_DBR_INDEX_MASK 0xfffffUL
- #define DBR_DBR_INDEX_SFT 0
- #define DBR_DBR_RESERVED12_MASK 0xfff00000UL
- #define DBR_DBR_RESERVED12_SFT 20
- __le32 type_xid;
- #define DBR_DBR_XID_MASK 0xfffffUL
- #define DBR_DBR_XID_SFT 0
- #define DBR_DBR_RESERVED8_MASK 0xff00000UL
- #define DBR_DBR_RESERVED8_SFT 20
- #define DBR_DBR_TYPE_MASK 0xf0000000UL
- #define DBR_DBR_TYPE_SFT 28
- #define DBR_DBR_TYPE_SQ (0x0UL << 28)
- #define DBR_DBR_TYPE_RQ (0x1UL << 28)
- #define DBR_DBR_TYPE_SRQ (0x2UL << 28)
- #define DBR_DBR_TYPE_SRQ_ARM (0x3UL << 28)
- #define DBR_DBR_TYPE_CQ (0x4UL << 28)
- #define DBR_DBR_TYPE_CQ_ARMSE (0x5UL << 28)
- #define DBR_DBR_TYPE_CQ_ARMALL (0x6UL << 28)
- #define DBR_DBR_TYPE_CQ_ARMENA (0x7UL << 28)
- #define DBR_DBR_TYPE_SRQ_ARMENA (0x8UL << 28)
- #define DBR_DBR_TYPE_CQ_CUTOFF_ACK (0x9UL << 28)
- #define DBR_DBR_TYPE_NULL (0xfUL << 28)
-};
-
-/* 32b Doorbell Format (4 bytes) */
-struct dbr_dbr32 {
- __le32 type_abs_incr_xid;
- #define DBR_DBR32_XID_MASK 0xfffffUL
- #define DBR_DBR32_XID_SFT 0
- #define DBR_DBR32_RESERVED4_MASK 0xf00000UL
- #define DBR_DBR32_RESERVED4_SFT 20
- #define DBR_DBR32_INCR_MASK 0xf000000UL
- #define DBR_DBR32_INCR_SFT 24
- #define DBR_DBR32_ABS 0x10000000UL
- #define DBR_DBR32_TYPE_MASK 0xe0000000UL
- #define DBR_DBR32_TYPE_SFT 29
- #define DBR_DBR32_TYPE_SQ (0x0UL << 29)
+/* dbc_dbc (size:64b/8B) */
+struct dbc_dbc {
+ __le32 index;
+ #define DBC_DBC_INDEX_MASK 0xffffffUL
+ #define DBC_DBC_INDEX_SFT 0
+ __le32 type_path_xid;
+ #define DBC_DBC_XID_MASK 0xfffffUL
+ #define DBC_DBC_XID_SFT 0
+ #define DBC_DBC_PATH_MASK 0x3000000UL
+ #define DBC_DBC_PATH_SFT 24
+ #define DBC_DBC_PATH_ROCE (0x0UL << 24)
+ #define DBC_DBC_PATH_L2 (0x1UL << 24)
+ #define DBC_DBC_PATH_ENGINE (0x2UL << 24)
+ #define DBC_DBC_PATH_LAST DBC_DBC_PATH_ENGINE
+ #define DBC_DBC_DEBUG_TRACE 0x8000000UL
+ #define DBC_DBC_TYPE_MASK 0xf0000000UL
+ #define DBC_DBC_TYPE_SFT 28
+ #define DBC_DBC_TYPE_SQ (0x0UL << 28)
+ #define DBC_DBC_TYPE_RQ (0x1UL << 28)
+ #define DBC_DBC_TYPE_SRQ (0x2UL << 28)
+ #define DBC_DBC_TYPE_SRQ_ARM (0x3UL << 28)
+ #define DBC_DBC_TYPE_CQ (0x4UL << 28)
+ #define DBC_DBC_TYPE_CQ_ARMSE (0x5UL << 28)
+ #define DBC_DBC_TYPE_CQ_ARMALL (0x6UL << 28)
+ #define DBC_DBC_TYPE_CQ_ARMENA (0x7UL << 28)
+ #define DBC_DBC_TYPE_SRQ_ARMENA (0x8UL << 28)
+ #define DBC_DBC_TYPE_CQ_CUTOFF_ACK (0x9UL << 28)
+ #define DBC_DBC_TYPE_NQ (0xaUL << 28)
+ #define DBC_DBC_TYPE_NQ_ARM (0xbUL << 28)
+ #define DBC_DBC_TYPE_NULL (0xfUL << 28)
+ #define DBC_DBC_TYPE_LAST DBC_DBC_TYPE_NULL
+};
+
+/* dbc_dbc32 (size:32b/4B) */
+struct dbc_dbc32 {
+ __le32 type_abs_incr_xid;
+ #define DBC_DBC32_XID_MASK 0xfffffUL
+ #define DBC_DBC32_XID_SFT 0
+ #define DBC_DBC32_PATH_MASK 0xc00000UL
+ #define DBC_DBC32_PATH_SFT 22
+ #define DBC_DBC32_PATH_ROCE (0x0UL << 22)
+ #define DBC_DBC32_PATH_L2 (0x1UL << 22)
+ #define DBC_DBC32_PATH_LAST DBC_DBC32_PATH_L2
+ #define DBC_DBC32_INCR_MASK 0xf000000UL
+ #define DBC_DBC32_INCR_SFT 24
+ #define DBC_DBC32_ABS 0x10000000UL
+ #define DBC_DBC32_TYPE_MASK 0xe0000000UL
+ #define DBC_DBC32_TYPE_SFT 29
+ #define DBC_DBC32_TYPE_SQ (0x0UL << 29)
+ #define DBC_DBC32_TYPE_LAST DBC_DBC32_TYPE_SQ
};
/* SQ WQE Structures */
@@ -149,7 +159,24 @@ struct sq_psn_search {
#define SQ_PSN_SEARCH_NEXT_PSN_MASK 0xffffffUL
#define SQ_PSN_SEARCH_NEXT_PSN_SFT 0
#define SQ_PSN_SEARCH_FLAGS_MASK 0xff000000UL
- #define SQ_PSN_SEARCH_FLAGS_SFT 24
+ #define SQ_PSN_SEARCH_FLAGS_SFT 24
+};
+
+/* sq_psn_search_ext (size:128b/16B) */
+struct sq_psn_search_ext {
+ __le32 opcode_start_psn;
+ #define SQ_PSN_SEARCH_EXT_START_PSN_MASK 0xffffffUL
+ #define SQ_PSN_SEARCH_EXT_START_PSN_SFT 0
+ #define SQ_PSN_SEARCH_EXT_OPCODE_MASK 0xff000000UL
+ #define SQ_PSN_SEARCH_EXT_OPCODE_SFT 24
+ __le32 flags_next_psn;
+ #define SQ_PSN_SEARCH_EXT_NEXT_PSN_MASK 0xffffffUL
+ #define SQ_PSN_SEARCH_EXT_NEXT_PSN_SFT 0
+ #define SQ_PSN_SEARCH_EXT_FLAGS_MASK 0xff000000UL
+ #define SQ_PSN_SEARCH_EXT_FLAGS_SFT 24
+ __le16 start_slot_idx;
+ __le16 reserved16;
+ __le32 reserved32;
};
/* Send SQ WQE (40 bytes) */
@@ -505,22 +532,24 @@ struct cq_res_rc {
/* Responder UD CQE (32 bytes) */
struct cq_res_ud {
- __le32 length;
+ __le16 length;
#define CQ_RES_UD_LENGTH_MASK 0x3fffUL
#define CQ_RES_UD_LENGTH_SFT 0
- #define CQ_RES_UD_RESERVED18_MASK 0xffffc000UL
- #define CQ_RES_UD_RESERVED18_SFT 14
+ __le16 cfa_metadata;
+ #define CQ_RES_UD_CFA_METADATA_VID_MASK 0xfffUL
+ #define CQ_RES_UD_CFA_METADATA_VID_SFT 0
+ #define CQ_RES_UD_CFA_METADATA_DE 0x1000UL
+ #define CQ_RES_UD_CFA_METADATA_PRI_MASK 0xe000UL
+ #define CQ_RES_UD_CFA_METADATA_PRI_SFT 13
__le32 imm_data;
__le64 qp_handle;
__le16 src_mac[3];
__le16 src_qp_low;
u8 cqe_type_toggle;
- #define CQ_RES_UD_TOGGLE 0x1UL
- #define CQ_RES_UD_CQE_TYPE_MASK 0x1eUL
- #define CQ_RES_UD_CQE_TYPE_SFT 1
+ #define CQ_RES_UD_TOGGLE 0x1UL
+ #define CQ_RES_UD_CQE_TYPE_MASK 0x1eUL
+ #define CQ_RES_UD_CQE_TYPE_SFT 1
#define CQ_RES_UD_CQE_TYPE_RES_UD (0x2UL << 1)
- #define CQ_RES_UD_RESERVED3_MASK 0xe0UL
- #define CQ_RES_UD_RESERVED3_SFT 5
u8 status;
#define CQ_RES_UD_STATUS_OK 0x0UL
#define CQ_RES_UD_STATUS_LOCAL_ACCESS_ERROR 0x1UL
@@ -536,18 +565,30 @@ struct cq_res_ud {
#define CQ_RES_UD_FLAGS_SRQ_SRQ (0x1UL << 0)
#define CQ_RES_UD_FLAGS_SRQ_LAST CQ_RES_UD_FLAGS_SRQ_SRQ
#define CQ_RES_UD_FLAGS_IMM 0x2UL
- #define CQ_RES_UD_FLAGS_ROCE_IP_VER_MASK 0xcUL
- #define CQ_RES_UD_FLAGS_ROCE_IP_VER_SFT 2
- #define CQ_RES_UD_FLAGS_ROCE_IP_VER_V1 (0x0UL << 2)
- #define CQ_RES_UD_FLAGS_ROCE_IP_VER_V2IPV4 (0x2UL << 2)
- #define CQ_RES_UD_FLAGS_ROCE_IP_VER_V2IPV6 (0x3UL << 2)
+ #define CQ_RES_UD_FLAGS_UNUSED_MASK 0xcUL
+ #define CQ_RES_UD_FLAGS_UNUSED_SFT 2
+ #define CQ_RES_UD_FLAGS_ROCE_IP_VER_MASK 0x30UL
+ #define CQ_RES_UD_FLAGS_ROCE_IP_VER_SFT 4
+ #define CQ_RES_UD_FLAGS_ROCE_IP_VER_V1 (0x0UL << 4)
+ #define CQ_RES_UD_FLAGS_ROCE_IP_VER_V2IPV4 (0x2UL << 4)
+ #define CQ_RES_UD_FLAGS_ROCE_IP_VER_V2IPV6 (0x3UL << 4)
#define CQ_RES_UD_FLAGS_ROCE_IP_VER_LAST \
CQ_RES_UD_FLAGS_ROCE_IP_VER_V2IPV6
+ #define CQ_RES_UD_FLAGS_META_FORMAT_MASK 0x3c0UL
+ #define CQ_RES_UD_FLAGS_META_FORMAT_SFT 6
+ #define CQ_RES_UD_FLAGS_META_FORMAT_NONE (0x0UL << 6)
+ #define CQ_RES_UD_FLAGS_META_FORMAT_VLAN (0x1UL << 6)
+ #define CQ_RES_UD_FLAGS_META_FORMAT_TUNNEL_ID (0x2UL << 6)
+ #define CQ_RES_UD_FLAGS_META_FORMAT_CHDR_DATA (0x3UL << 6)
+ #define CQ_RES_UD_FLAGS_META_FORMAT_HDR_OFFSET (0x4UL << 6)
+ #define CQ_RES_UD_FLAGS_META_FORMAT_LAST \
+ CQ_RES_UD_FLAGS_META_FORMAT_HDR_OFFSET
+ #define CQ_RES_UD_FLAGS_EXT_META_FORMAT_MASK 0xc00UL
+ #define CQ_RES_UD_FLAGS_EXT_META_FORMAT_SFT 10
+
__le32 src_qp_high_srq_or_rq_wr_id;
#define CQ_RES_UD_SRQ_OR_RQ_WR_ID_MASK 0xfffffUL
#define CQ_RES_UD_SRQ_OR_RQ_WR_ID_SFT 0
- #define CQ_RES_UD_RESERVED4_MASK 0xf00000UL
- #define CQ_RES_UD_RESERVED4_SFT 20
#define CQ_RES_UD_SRC_QP_HIGH_MASK 0xff000000UL
#define CQ_RES_UD_SRC_QP_HIGH_SFT 24
};
@@ -983,6 +1024,7 @@ struct cmdq_create_qp {
#define CMDQ_CREATE_QP_TYPE_RC 0x2UL
#define CMDQ_CREATE_QP_TYPE_UD 0x4UL
#define CMDQ_CREATE_QP_TYPE_RAW_ETHERTYPE 0x6UL
+ #define CMDQ_CREATE_QP_TYPE_GSI 0x7UL
u8 sq_pg_size_sq_lvl;
#define CMDQ_CREATE_QP_SQ_LVL_MASK 0xfUL
#define CMDQ_CREATE_QP_SQ_LVL_SFT 0
@@ -2719,6 +2761,8 @@ struct creq_query_func_resp_sb {
__le16 max_srq;
__le32 max_gid;
__le32 tqm_alloc_reqs[12];
+ __le32 max_dpi;
+ __le32 reserved_32;
};
/* Set resources command response (16 bytes) */
diff --git a/drivers/infiniband/hw/cxgb3/Makefile b/drivers/infiniband/hw/cxgb3/Makefile
index 66fe0917aba0..34bb86a6ae3a 100644
--- a/drivers/infiniband/hw/cxgb3/Makefile
+++ b/drivers/infiniband/hw/cxgb3/Makefile
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
-ccflags-y := -Idrivers/net/ethernet/chelsio/cxgb3
+ccflags-y := -I $(srctree)/drivers/net/ethernet/chelsio/cxgb3
obj-$(CONFIG_INFINIBAND_CXGB3) += iw_cxgb3.o
diff --git a/drivers/infiniband/hw/cxgb3/iwch.c b/drivers/infiniband/hw/cxgb3/iwch.c
index 591de319c178..fb03bc492ef7 100644
--- a/drivers/infiniband/hw/cxgb3/iwch.c
+++ b/drivers/infiniband/hw/cxgb3/iwch.c
@@ -146,7 +146,7 @@ static void open_rnic_dev(struct t3cdev *tdev)
pr_debug("%s t3cdev %p\n", __func__, tdev);
pr_info_once("Chelsio T3 RDMA Driver - version %s\n", DRV_VERSION);
- rnicp = (struct iwch_dev *)ib_alloc_device(sizeof(*rnicp));
+ rnicp = ib_alloc_device(iwch_dev, ibdev);
if (!rnicp) {
pr_err("Cannot allocate ib device\n");
return;
diff --git a/drivers/infiniband/hw/cxgb3/iwch_provider.c b/drivers/infiniband/hw/cxgb3/iwch_provider.c
index b34b1a1bd94b..4accf7b3dcf2 100644
--- a/drivers/infiniband/hw/cxgb3/iwch_provider.c
+++ b/drivers/infiniband/hw/cxgb3/iwch_provider.c
@@ -53,6 +53,7 @@
#include <rdma/ib_smi.h>
#include <rdma/ib_umem.h>
#include <rdma/ib_user_verbs.h>
+#include <rdma/uverbs_ioctl.h>
#include "cxio_hal.h"
#include "iwch.h"
@@ -61,7 +62,7 @@
#include <rdma/cxgb3-abi.h>
#include "common.h"
-static int iwch_dealloc_ucontext(struct ib_ucontext *context)
+static void iwch_dealloc_ucontext(struct ib_ucontext *context)
{
struct iwch_dev *rhp = to_iwch_dev(context->device);
struct iwch_ucontext *ucontext = to_iwch_ucontext(context);
@@ -71,24 +72,20 @@ static int iwch_dealloc_ucontext(struct ib_ucontext *context)
list_for_each_entry_safe(mm, tmp, &ucontext->mmaps, entry)
kfree(mm);
cxio_release_ucontext(&rhp->rdev, &ucontext->uctx);
- kfree(ucontext);
- return 0;
}
-static struct ib_ucontext *iwch_alloc_ucontext(struct ib_device *ibdev,
- struct ib_udata *udata)
+static int iwch_alloc_ucontext(struct ib_ucontext *ucontext,
+ struct ib_udata *udata)
{
- struct iwch_ucontext *context;
+ struct ib_device *ibdev = ucontext->device;
+ struct iwch_ucontext *context = to_iwch_ucontext(ucontext);
struct iwch_dev *rhp = to_iwch_dev(ibdev);
pr_debug("%s ibdev %p\n", __func__, ibdev);
- context = kzalloc(sizeof(*context), GFP_KERNEL);
- if (!context)
- return ERR_PTR(-ENOMEM);
cxio_init_ucontext(&rhp->rdev, &context->uctx);
INIT_LIST_HEAD(&context->mmaps);
spin_lock_init(&context->mmap_lock);
- return &context->ibucontext;
+ return 0;
}
static int iwch_destroy_cq(struct ib_cq *ib_cq)
@@ -370,7 +367,7 @@ static int iwch_mmap(struct ib_ucontext *context, struct vm_area_struct *vma)
return ret;
}
-static int iwch_deallocate_pd(struct ib_pd *pd)
+static void iwch_deallocate_pd(struct ib_pd *pd)
{
struct iwch_dev *rhp;
struct iwch_pd *php;
@@ -379,15 +376,13 @@ static int iwch_deallocate_pd(struct ib_pd *pd)
rhp = php->rhp;
pr_debug("%s ibpd %p pdid 0x%x\n", __func__, pd, php->pdid);
cxio_hal_put_pdid(rhp->rdev.rscp, php->pdid);
- kfree(php);
- return 0;
}
-static struct ib_pd *iwch_allocate_pd(struct ib_device *ibdev,
- struct ib_ucontext *context,
- struct ib_udata *udata)
+static int iwch_allocate_pd(struct ib_pd *pd, struct ib_ucontext *context,
+ struct ib_udata *udata)
{
- struct iwch_pd *php;
+ struct iwch_pd *php = to_iwch_pd(pd);
+ struct ib_device *ibdev = pd->device;
u32 pdid;
struct iwch_dev *rhp;
@@ -395,12 +390,8 @@ static struct ib_pd *iwch_allocate_pd(struct ib_device *ibdev,
rhp = (struct iwch_dev *) ibdev;
pdid = cxio_hal_get_pdid(rhp->rdev.rscp);
if (!pdid)
- return ERR_PTR(-EINVAL);
- php = kzalloc(sizeof(*php), GFP_KERNEL);
- if (!php) {
- cxio_hal_put_pdid(rhp->rdev.rscp, pdid);
- return ERR_PTR(-ENOMEM);
- }
+ return -EINVAL;
+
php->pdid = pdid;
php->rhp = rhp;
if (context) {
@@ -408,11 +399,11 @@ static struct ib_pd *iwch_allocate_pd(struct ib_device *ibdev,
if (ib_copy_to_udata(udata, &resp, sizeof(resp))) {
iwch_deallocate_pd(&php->ibpd);
- return ERR_PTR(-EFAULT);
+ return -EFAULT;
}
}
pr_debug("%s pdid 0x%0x ptr 0x%p\n", __func__, pdid, php);
- return &php->ibpd;
+ return 0;
}
static int iwch_dereg_mr(struct ib_mr *ib_mr)
@@ -522,14 +513,13 @@ static struct ib_mr *iwch_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
u64 virt, int acc, struct ib_udata *udata)
{
__be64 *pages;
- int shift, n, len;
- int i, k, entry;
+ int shift, n, i;
int err = 0;
struct iwch_dev *rhp;
struct iwch_pd *php;
struct iwch_mr *mhp;
struct iwch_reg_user_mr_resp uresp;
- struct scatterlist *sg;
+ struct sg_dma_page_iter sg_iter;
pr_debug("%s ib_pd %p\n", __func__, pd);
php = to_iwch_pd(pd);
@@ -540,14 +530,14 @@ static struct ib_mr *iwch_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
mhp->rhp = rhp;
- mhp->umem = ib_umem_get(pd->uobject->context, start, length, acc, 0);
+ mhp->umem = ib_umem_get(udata, start, length, acc, 0);
if (IS_ERR(mhp->umem)) {
err = PTR_ERR(mhp->umem);
kfree(mhp);
return ERR_PTR(err);
}
- shift = mhp->umem->page_shift;
+ shift = PAGE_SHIFT;
n = mhp->umem->nmap;
@@ -563,19 +553,15 @@ static struct ib_mr *iwch_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
i = n = 0;
- for_each_sg(mhp->umem->sg_head.sgl, sg, mhp->umem->nmap, entry) {
- len = sg_dma_len(sg) >> shift;
- for (k = 0; k < len; ++k) {
- pages[i++] = cpu_to_be64(sg_dma_address(sg) +
- (k << shift));
- if (i == PAGE_SIZE / sizeof *pages) {
- err = iwch_write_pbl(mhp, pages, i, n);
- if (err)
- goto pbl_done;
- n += i;
- i = 0;
- }
- }
+ for_each_sg_dma_page(mhp->umem->sg_head.sgl, &sg_iter, mhp->umem->nmap, 0) {
+ pages[i++] = cpu_to_be64(sg_page_iter_dma_address(&sg_iter));
+ if (i == PAGE_SIZE / sizeof *pages) {
+ err = iwch_write_pbl(mhp, pages, i, n);
+ if (err)
+ goto pbl_done;
+ n += i;
+ i = 0;
+ }
}
if (i)
@@ -836,7 +822,8 @@ static struct ib_qp *iwch_create_qp(struct ib_pd *pd,
* Kernel users need more wq space for fastreg WRs which can take
* 2 WR fragments.
*/
- ucontext = udata ? to_iwch_ucontext(pd->uobject->context) : NULL;
+ ucontext = rdma_udata_to_drv_context(udata, struct iwch_ucontext,
+ ibucontext);
if (!ucontext && wqsize < (rqsize + (2 * sqsize)))
wqsize = roundup_pow_of_two(rqsize +
roundup_pow_of_two(attrs->cap.max_send_wr * 2));
@@ -1130,8 +1117,9 @@ static int iwch_query_port(struct ib_device *ibdev,
static ssize_t hw_rev_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct iwch_dev *iwch_dev = container_of(dev, struct iwch_dev,
- ibdev.dev);
+ struct iwch_dev *iwch_dev =
+ rdma_device_to_drv_device(dev, struct iwch_dev, ibdev);
+
pr_debug("%s dev 0x%p\n", __func__, dev);
return sprintf(buf, "%d\n", iwch_dev->rdev.t3cdev_p->type);
}
@@ -1140,8 +1128,8 @@ static DEVICE_ATTR_RO(hw_rev);
static ssize_t hca_type_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct iwch_dev *iwch_dev = container_of(dev, struct iwch_dev,
- ibdev.dev);
+ struct iwch_dev *iwch_dev =
+ rdma_device_to_drv_device(dev, struct iwch_dev, ibdev);
struct ethtool_drvinfo info;
struct net_device *lldev = iwch_dev->rdev.t3cdev_p->lldev;
@@ -1154,8 +1142,9 @@ static DEVICE_ATTR_RO(hca_type);
static ssize_t board_id_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct iwch_dev *iwch_dev = container_of(dev, struct iwch_dev,
- ibdev.dev);
+ struct iwch_dev *iwch_dev =
+ rdma_device_to_drv_device(dev, struct iwch_dev, ibdev);
+
pr_debug("%s dev 0x%p\n", __func__, dev);
return sprintf(buf, "%x.%x\n", iwch_dev->rdev.rnic_info.pdev->vendor,
iwch_dev->rdev.rnic_info.pdev->device);
@@ -1348,6 +1337,8 @@ static const struct ib_device_ops iwch_dev_ops = {
.reg_user_mr = iwch_reg_user_mr,
.req_notify_cq = iwch_arm_cq,
.resize_cq = iwch_resize_cq,
+ INIT_RDMA_OBJ_SIZE(ib_pd, iwch_pd, ibpd),
+ INIT_RDMA_OBJ_SIZE(ib_ucontext, iwch_ucontext, ibucontext),
};
int iwch_register_device(struct iwch_dev *dev)
@@ -1391,7 +1382,7 @@ int iwch_register_device(struct iwch_dev *dev)
dev->ibdev.dev.parent = &dev->rdev.rnic_info.pdev->dev;
dev->ibdev.uverbs_abi_ver = IWCH_UVERBS_ABI_VERSION;
- dev->ibdev.iwcm = kmalloc(sizeof(struct iw_cm_verbs), GFP_KERNEL);
+ dev->ibdev.iwcm = kzalloc(sizeof(struct iw_cm_verbs), GFP_KERNEL);
if (!dev->ibdev.iwcm)
return -ENOMEM;
@@ -1409,7 +1400,7 @@ int iwch_register_device(struct iwch_dev *dev)
dev->ibdev.driver_id = RDMA_DRIVER_CXGB3;
rdma_set_device_sysfs_group(&dev->ibdev, &iwch_attr_group);
ib_set_device_ops(&dev->ibdev, &iwch_dev_ops);
- ret = ib_register_device(&dev->ibdev, "cxgb3_%d", NULL);
+ ret = ib_register_device(&dev->ibdev, "cxgb3_%d");
if (ret)
kfree(dev->ibdev.iwcm);
return ret;
diff --git a/drivers/infiniband/hw/cxgb4/Makefile b/drivers/infiniband/hw/cxgb4/Makefile
index 9edd92023e18..31a87d90a40b 100644
--- a/drivers/infiniband/hw/cxgb4/Makefile
+++ b/drivers/infiniband/hw/cxgb4/Makefile
@@ -1,5 +1,5 @@
-ccflags-y := -Idrivers/net/ethernet/chelsio/cxgb4
-ccflags-y += -Idrivers/net/ethernet/chelsio/libcxgb
+ccflags-y := -I $(srctree)/drivers/net/ethernet/chelsio/cxgb4
+ccflags-y += -I $(srctree)/drivers/net/ethernet/chelsio/libcxgb
obj-$(CONFIG_INFINIBAND_CXGB4) += iw_cxgb4.o
diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c
index 8221813219e5..4d232bdf9e97 100644
--- a/drivers/infiniband/hw/cxgb4/cm.c
+++ b/drivers/infiniband/hw/cxgb4/cm.c
@@ -655,7 +655,33 @@ static int send_halfclose(struct c4iw_ep *ep)
return c4iw_l2t_send(&ep->com.dev->rdev, skb, ep->l2t);
}
-static int send_abort(struct c4iw_ep *ep)
+static void read_tcb(struct c4iw_ep *ep)
+{
+ struct sk_buff *skb;
+ struct cpl_get_tcb *req;
+ int wrlen = roundup(sizeof(*req), 16);
+
+ skb = get_skb(NULL, sizeof(*req), GFP_KERNEL);
+ if (WARN_ON(!skb))
+ return;
+
+ set_wr_txq(skb, CPL_PRIORITY_CONTROL, ep->ctrlq_idx);
+ req = (struct cpl_get_tcb *) skb_put(skb, wrlen);
+ memset(req, 0, wrlen);
+ INIT_TP_WR(req, ep->hwtid);
+ OPCODE_TID(req) = cpu_to_be32(MK_OPCODE_TID(CPL_GET_TCB, ep->hwtid));
+ req->reply_ctrl = htons(REPLY_CHAN_V(0) | QUEUENO_V(ep->rss_qid));
+
+ /*
+ * keep a ref on the ep so the tcb is not unlocked before this
+ * cpl completes. The ref is released in read_tcb_rpl().
+ */
+ c4iw_get_ep(&ep->com);
+ if (WARN_ON(c4iw_ofld_send(&ep->com.dev->rdev, skb)))
+ c4iw_put_ep(&ep->com);
+}
+
+static int send_abort_req(struct c4iw_ep *ep)
{
u32 wrlen = roundup(sizeof(struct cpl_abort_req), 16);
struct sk_buff *req_skb = skb_dequeue(&ep->com.ep_skb_list);
@@ -670,6 +696,17 @@ static int send_abort(struct c4iw_ep *ep)
return c4iw_l2t_send(&ep->com.dev->rdev, req_skb, ep->l2t);
}
+static int send_abort(struct c4iw_ep *ep)
+{
+ if (!ep->com.qp || !ep->com.qp->srq) {
+ send_abort_req(ep);
+ return 0;
+ }
+ set_bit(ABORT_REQ_IN_PROGRESS, &ep->com.flags);
+ read_tcb(ep);
+ return 0;
+}
+
static int send_connect(struct c4iw_ep *ep)
{
struct cpl_act_open_req *req = NULL;
@@ -1851,14 +1888,11 @@ static int rx_data(struct c4iw_dev *dev, struct sk_buff *skb)
return 0;
}
-static void complete_cached_srq_buffers(struct c4iw_ep *ep,
- __be32 srqidx_status)
+static void complete_cached_srq_buffers(struct c4iw_ep *ep, u32 srqidx)
{
enum chip_type adapter_type;
- u32 srqidx;
adapter_type = ep->com.dev->rdev.lldi.adapter_type;
- srqidx = ABORT_RSS_SRQIDX_G(be32_to_cpu(srqidx_status));
/*
* If this TCB had a srq buffer cached, then we must complete
@@ -1876,6 +1910,7 @@ static void complete_cached_srq_buffers(struct c4iw_ep *ep,
static int abort_rpl(struct c4iw_dev *dev, struct sk_buff *skb)
{
+ u32 srqidx;
struct c4iw_ep *ep;
struct cpl_abort_rpl_rss6 *rpl = cplhdr(skb);
int release = 0;
@@ -1887,7 +1922,10 @@ static int abort_rpl(struct c4iw_dev *dev, struct sk_buff *skb)
return 0;
}
- complete_cached_srq_buffers(ep, rpl->srqidx_status);
+ if (ep->com.qp && ep->com.qp->srq) {
+ srqidx = ABORT_RSS_SRQIDX_G(be32_to_cpu(rpl->srqidx_status));
+ complete_cached_srq_buffers(ep, srqidx ? srqidx : ep->srqe_idx);
+ }
pr_debug("ep %p tid %u\n", ep, ep->hwtid);
mutex_lock(&ep->com.mutex);
@@ -1903,8 +1941,10 @@ static int abort_rpl(struct c4iw_dev *dev, struct sk_buff *skb)
}
mutex_unlock(&ep->com.mutex);
- if (release)
+ if (release) {
+ close_complete_upcall(ep, -ECONNRESET);
release_ep_resources(ep);
+ }
c4iw_put_ep(&ep->com);
return 0;
}
@@ -2072,7 +2112,7 @@ static int import_ep(struct c4iw_ep *ep, int iptype, __u8 *peer_ip,
} else {
pdev = get_real_dev(n->dev);
ep->l2t = cxgb4_l2t_get(cdev->rdev.lldi.l2t,
- n, pdev, 0);
+ n, pdev, rt_tos2priority(tos));
if (!ep->l2t)
goto out;
ep->mtu = dst_mtu(dst);
@@ -2161,7 +2201,8 @@ static int c4iw_reconnect(struct c4iw_ep *ep)
laddr6->sin6_addr.s6_addr,
raddr6->sin6_addr.s6_addr,
laddr6->sin6_port,
- raddr6->sin6_port, 0,
+ raddr6->sin6_port,
+ ep->com.cm_id->tos,
raddr6->sin6_scope_id);
iptype = 6;
ra = (__u8 *)&raddr6->sin6_addr;
@@ -2476,7 +2517,7 @@ static int pass_accept_req(struct c4iw_dev *dev, struct sk_buff *skb)
u16 peer_mss = ntohs(req->tcpopt.mss);
int iptype;
unsigned short hdrs;
- u8 tos = PASS_OPEN_TOS_G(ntohl(req->tos_stid));
+ u8 tos;
parent_ep = (struct c4iw_ep *)get_ep_from_stid(dev, stid);
if (!parent_ep) {
@@ -2490,6 +2531,11 @@ static int pass_accept_req(struct c4iw_dev *dev, struct sk_buff *skb)
goto reject;
}
+ if (parent_ep->com.cm_id->tos_set)
+ tos = parent_ep->com.cm_id->tos;
+ else
+ tos = PASS_OPEN_TOS_G(ntohl(req->tos_stid));
+
cxgb_get_4tuple(req, parent_ep->com.dev->rdev.lldi.adapter_type,
&iptype, local_ip, peer_ip, &local_port, &peer_port);
@@ -2509,7 +2555,7 @@ static int pass_accept_req(struct c4iw_dev *dev, struct sk_buff *skb)
ntohs(peer_port), peer_mss);
dst = cxgb_find_route6(&dev->rdev.lldi, get_real_dev,
local_ip, peer_ip, local_port, peer_port,
- PASS_OPEN_TOS_G(ntohl(req->tos_stid)),
+ tos,
((struct sockaddr_in6 *)
&parent_ep->com.local_addr)->sin6_scope_id);
}
@@ -2740,6 +2786,21 @@ static int peer_close(struct c4iw_dev *dev, struct sk_buff *skb)
return 0;
}
+static void finish_peer_abort(struct c4iw_dev *dev, struct c4iw_ep *ep)
+{
+ complete_cached_srq_buffers(ep, ep->srqe_idx);
+ if (ep->com.cm_id && ep->com.qp) {
+ struct c4iw_qp_attributes attrs;
+
+ attrs.next_state = C4IW_QP_STATE_ERROR;
+ c4iw_modify_qp(ep->com.qp->rhp, ep->com.qp,
+ C4IW_QP_ATTR_NEXT_STATE, &attrs, 1);
+ }
+ peer_abort_upcall(ep);
+ release_ep_resources(ep);
+ c4iw_put_ep(&ep->com);
+}
+
static int peer_abort(struct c4iw_dev *dev, struct sk_buff *skb)
{
struct cpl_abort_req_rss6 *req = cplhdr(skb);
@@ -2750,6 +2811,7 @@ static int peer_abort(struct c4iw_dev *dev, struct sk_buff *skb)
int release = 0;
unsigned int tid = GET_TID(req);
u8 status;
+ u32 srqidx;
u32 len = roundup(sizeof(struct cpl_abort_rpl), 16);
@@ -2769,8 +2831,6 @@ static int peer_abort(struct c4iw_dev *dev, struct sk_buff *skb)
goto deref_ep;
}
- complete_cached_srq_buffers(ep, req->srqidx_status);
-
pr_debug("ep %p tid %u state %u\n", ep, ep->hwtid,
ep->com.state);
set_bit(PEER_ABORT, &ep->com.history);
@@ -2819,6 +2879,23 @@ static int peer_abort(struct c4iw_dev *dev, struct sk_buff *skb)
stop_ep_timer(ep);
/*FALLTHROUGH*/
case FPDU_MODE:
+ if (ep->com.qp && ep->com.qp->srq) {
+ srqidx = ABORT_RSS_SRQIDX_G(
+ be32_to_cpu(req->srqidx_status));
+ if (srqidx) {
+ complete_cached_srq_buffers(ep,
+ req->srqidx_status);
+ } else {
+ /* Hold ep ref until finish_peer_abort() */
+ c4iw_get_ep(&ep->com);
+ __state_set(&ep->com, ABORTING);
+ set_bit(PEER_ABORT_IN_PROGRESS, &ep->com.flags);
+ read_tcb(ep);
+ break;
+
+ }
+ }
+
if (ep->com.cm_id && ep->com.qp) {
attrs.next_state = C4IW_QP_STATE_ERROR;
ret = c4iw_modify_qp(ep->com.qp->rhp,
@@ -2942,15 +3019,18 @@ static int terminate(struct c4iw_dev *dev, struct sk_buff *skb)
ep = get_ep_from_tid(dev, tid);
- if (ep && ep->com.qp) {
- pr_warn("TERM received tid %u qpid %u\n",
- tid, ep->com.qp->wq.sq.qid);
- attrs.next_state = C4IW_QP_STATE_TERMINATE;
- c4iw_modify_qp(ep->com.qp->rhp, ep->com.qp,
- C4IW_QP_ATTR_NEXT_STATE, &attrs, 1);
+ if (ep) {
+ if (ep->com.qp) {
+ pr_warn("TERM received tid %u qpid %u\n", tid,
+ ep->com.qp->wq.sq.qid);
+ attrs.next_state = C4IW_QP_STATE_TERMINATE;
+ c4iw_modify_qp(ep->com.qp->rhp, ep->com.qp,
+ C4IW_QP_ATTR_NEXT_STATE, &attrs, 1);
+ }
+
+ c4iw_put_ep(&ep->com);
} else
pr_warn("TERM received tid %u no ep/qp\n", tid);
- c4iw_put_ep(&ep->com);
return 0;
}
@@ -3318,7 +3398,7 @@ int c4iw_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
laddr6->sin6_addr.s6_addr,
raddr6->sin6_addr.s6_addr,
laddr6->sin6_port,
- raddr6->sin6_port, 0,
+ raddr6->sin6_port, cm_id->tos,
raddr6->sin6_scope_id);
}
if (!ep->dst) {
@@ -3606,7 +3686,6 @@ int c4iw_ep_disconnect(struct c4iw_ep *ep, int abrupt, gfp_t gfp)
if (close) {
if (abrupt) {
set_bit(EP_DISC_ABORT, &ep->com.history);
- close_complete_upcall(ep, -ECONNRESET);
ret = send_abort(ep);
} else {
set_bit(EP_DISC_CLOSE, &ep->com.history);
@@ -3717,6 +3796,80 @@ static void passive_ofld_conn_reply(struct c4iw_dev *dev, struct sk_buff *skb,
return;
}
+static inline u64 t4_tcb_get_field64(__be64 *tcb, u16 word)
+{
+ u64 tlo = be64_to_cpu(tcb[((31 - word) / 2)]);
+ u64 thi = be64_to_cpu(tcb[((31 - word) / 2) - 1]);
+ u64 t;
+ u32 shift = 32;
+
+ t = (thi << shift) | (tlo >> shift);
+
+ return t;
+}
+
+static inline u32 t4_tcb_get_field32(__be64 *tcb, u16 word, u32 mask, u32 shift)
+{
+ u32 v;
+ u64 t = be64_to_cpu(tcb[(31 - word) / 2]);
+
+ if (word & 0x1)
+ shift += 32;
+ v = (t >> shift) & mask;
+ return v;
+}
+
+static int read_tcb_rpl(struct c4iw_dev *dev, struct sk_buff *skb)
+{
+ struct cpl_get_tcb_rpl *rpl = cplhdr(skb);
+ __be64 *tcb = (__be64 *)(rpl + 1);
+ unsigned int tid = GET_TID(rpl);
+ struct c4iw_ep *ep;
+ u64 t_flags_64;
+ u32 rx_pdu_out;
+
+ ep = get_ep_from_tid(dev, tid);
+ if (!ep)
+ return 0;
+ /* Examine the TF_RX_PDU_OUT (bit 49 of the t_flags) in order to
+ * determine if there's a rx PDU feedback event pending.
+ *
+ * If that bit is set, it means we'll need to re-read the TCB's
+ * rq_start value. The final value is the one present in a TCB
+ * with the TF_RX_PDU_OUT bit cleared.
+ */
+
+ t_flags_64 = t4_tcb_get_field64(tcb, TCB_T_FLAGS_W);
+ rx_pdu_out = (t_flags_64 & TF_RX_PDU_OUT_V(1)) >> TF_RX_PDU_OUT_S;
+
+ c4iw_put_ep(&ep->com); /* from get_ep_from_tid() */
+ c4iw_put_ep(&ep->com); /* from read_tcb() */
+
+ /* If TF_RX_PDU_OUT bit is set, re-read the TCB */
+ if (rx_pdu_out) {
+ if (++ep->rx_pdu_out_cnt >= 2) {
+ WARN_ONCE(1, "tcb re-read() reached the guard limit, finishing the cleanup\n");
+ goto cleanup;
+ }
+ read_tcb(ep);
+ return 0;
+ }
+
+ ep->srqe_idx = t4_tcb_get_field32(tcb, TCB_RQ_START_W, TCB_RQ_START_W,
+ TCB_RQ_START_S);
+cleanup:
+ pr_debug("ep %p tid %u %016x\n", ep, ep->hwtid, ep->srqe_idx);
+
+ if (test_bit(PEER_ABORT_IN_PROGRESS, &ep->com.flags))
+ finish_peer_abort(dev, ep);
+ else if (test_bit(ABORT_REQ_IN_PROGRESS, &ep->com.flags))
+ send_abort_req(ep);
+ else
+ WARN_ONCE(1, "unexpected state!");
+
+ return 0;
+}
+
static int deferred_fw6_msg(struct c4iw_dev *dev, struct sk_buff *skb)
{
struct cpl_fw6_msg *rpl = cplhdr(skb);
@@ -4037,6 +4190,7 @@ static c4iw_handler_func work_handlers[NUM_CPL_CMDS + NUM_FAKE_CPLS] = {
[CPL_CLOSE_CON_RPL] = close_con_rpl,
[CPL_RDMA_TERMINATE] = terminate,
[CPL_FW4_ACK] = fw4_ack,
+ [CPL_GET_TCB_RPL] = read_tcb_rpl,
[CPL_FW6_MSG] = deferred_fw6_msg,
[CPL_RX_PKT] = rx_pkt,
[FAKE_CPL_PUT_EP_SAFE] = _put_ep_safe,
@@ -4268,6 +4422,7 @@ c4iw_handler_func c4iw_handlers[NUM_CPL_CMDS] = {
[CPL_RDMA_TERMINATE] = sched,
[CPL_FW4_ACK] = sched,
[CPL_SET_TCB_RPL] = set_tcb_rpl,
+ [CPL_GET_TCB_RPL] = sched,
[CPL_FW6_MSG] = fw6_msg,
[CPL_RX_PKT] = sched
};
diff --git a/drivers/infiniband/hw/cxgb4/device.c b/drivers/infiniband/hw/cxgb4/device.c
index d499cd61c0e8..c79cf63fb0bb 100644
--- a/drivers/infiniband/hw/cxgb4/device.c
+++ b/drivers/infiniband/hw/cxgb4/device.c
@@ -720,11 +720,8 @@ static const struct file_operations ep_debugfs_fops = {
.read = debugfs_read,
};
-static int setup_debugfs(struct c4iw_dev *devp)
+static void setup_debugfs(struct c4iw_dev *devp)
{
- if (!devp->debugfs_root)
- return -1;
-
debugfs_create_file_size("qps", S_IWUSR, devp->debugfs_root,
(void *)devp, &qp_debugfs_fops, 4096);
@@ -740,7 +737,6 @@ static int setup_debugfs(struct c4iw_dev *devp)
if (c4iw_wr_log)
debugfs_create_file_size("wr_log", S_IWUSR, devp->debugfs_root,
(void *)devp, &wr_log_debugfs_fops, 4096);
- return 0;
}
void c4iw_release_dev_ucontext(struct c4iw_rdev *rdev,
@@ -981,7 +977,7 @@ static struct c4iw_dev *c4iw_alloc(const struct cxgb4_lld_info *infop)
pr_info("%s: On-Chip Queues not supported on this device\n",
pci_name(infop->pdev));
- devp = (struct c4iw_dev *)ib_alloc_device(sizeof(*devp));
+ devp = ib_alloc_device(c4iw_dev, ibdev);
if (!devp) {
pr_err("Cannot allocate ib device\n");
return ERR_PTR(-ENOMEM);
@@ -1564,8 +1560,6 @@ static int __init c4iw_init_module(void)
return err;
c4iw_debugfs_root = debugfs_create_dir(DRV_NAME, NULL);
- if (!c4iw_debugfs_root)
- pr_warn("could not create debugfs entry, continuing\n");
reg_workq = create_singlethread_workqueue("Register_iWARP_device");
if (!reg_workq) {
diff --git a/drivers/infiniband/hw/cxgb4/iw_cxgb4.h b/drivers/infiniband/hw/cxgb4/iw_cxgb4.h
index f0fceadd0d12..5a5da41faef6 100644
--- a/drivers/infiniband/hw/cxgb4/iw_cxgb4.h
+++ b/drivers/infiniband/hw/cxgb4/iw_cxgb4.h
@@ -589,7 +589,6 @@ struct c4iw_ucontext {
u32 key;
spinlock_t mmap_lock;
struct list_head mmaps;
- struct kref kref;
bool is_32b_cqe;
};
@@ -598,18 +597,6 @@ static inline struct c4iw_ucontext *to_c4iw_ucontext(struct ib_ucontext *c)
return container_of(c, struct c4iw_ucontext, ibucontext);
}
-void _c4iw_free_ucontext(struct kref *kref);
-
-static inline void c4iw_put_ucontext(struct c4iw_ucontext *ucontext)
-{
- kref_put(&ucontext->kref, _c4iw_free_ucontext);
-}
-
-static inline void c4iw_get_ucontext(struct c4iw_ucontext *ucontext)
-{
- kref_get(&ucontext->kref);
-}
-
struct c4iw_mm_entry {
struct list_head entry;
u64 addr;
@@ -982,6 +969,9 @@ struct c4iw_ep {
int rcv_win;
u32 snd_wscale;
struct c4iw_ep_stats stats;
+ u32 srqe_idx;
+ u32 rx_pdu_out_cnt;
+ struct sk_buff *peer_abort_skb;
};
static inline struct c4iw_ep *to_ep(struct iw_cm_id *cm_id)
diff --git a/drivers/infiniband/hw/cxgb4/mem.c b/drivers/infiniband/hw/cxgb4/mem.c
index 7b76e6f81aeb..5baa31ab6366 100644
--- a/drivers/infiniband/hw/cxgb4/mem.c
+++ b/drivers/infiniband/hw/cxgb4/mem.c
@@ -502,10 +502,9 @@ struct ib_mr *c4iw_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
u64 virt, int acc, struct ib_udata *udata)
{
__be64 *pages;
- int shift, n, len;
- int i, k, entry;
+ int shift, n, i;
int err = -ENOMEM;
- struct scatterlist *sg;
+ struct sg_dma_page_iter sg_iter;
struct c4iw_dev *rhp;
struct c4iw_pd *php;
struct c4iw_mr *mhp;
@@ -537,11 +536,11 @@ struct ib_mr *c4iw_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
mhp->rhp = rhp;
- mhp->umem = ib_umem_get(pd->uobject->context, start, length, acc, 0);
+ mhp->umem = ib_umem_get(udata, start, length, acc, 0);
if (IS_ERR(mhp->umem))
goto err_free_skb;
- shift = mhp->umem->page_shift;
+ shift = PAGE_SHIFT;
n = mhp->umem->nmap;
err = alloc_pbl(mhp, n);
@@ -556,21 +555,16 @@ struct ib_mr *c4iw_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
i = n = 0;
- for_each_sg(mhp->umem->sg_head.sgl, sg, mhp->umem->nmap, entry) {
- len = sg_dma_len(sg) >> shift;
- for (k = 0; k < len; ++k) {
- pages[i++] = cpu_to_be64(sg_dma_address(sg) +
- (k << shift));
- if (i == PAGE_SIZE / sizeof *pages) {
- err = write_pbl(&mhp->rhp->rdev,
- pages,
- mhp->attr.pbl_addr + (n << 3), i,
- mhp->wr_waitp);
- if (err)
- goto pbl_done;
- n += i;
- i = 0;
- }
+ for_each_sg_dma_page(mhp->umem->sg_head.sgl, &sg_iter, mhp->umem->nmap, 0) {
+ pages[i++] = cpu_to_be64(sg_page_iter_dma_address(&sg_iter));
+ if (i == PAGE_SIZE / sizeof(*pages)) {
+ err = write_pbl(&mhp->rhp->rdev, pages,
+ mhp->attr.pbl_addr + (n << 3), i,
+ mhp->wr_waitp);
+ if (err)
+ goto pbl_done;
+ n += i;
+ i = 0;
}
}
@@ -684,8 +678,8 @@ int c4iw_dealloc_mw(struct ib_mw *mw)
mhp->wr_waitp);
kfree_skb(mhp->dereg_skb);
c4iw_put_wr_wait(mhp->wr_waitp);
- kfree(mhp);
pr_debug("ib_mw %p mmid 0x%x ptr %p\n", mw, mmid, mhp);
+ kfree(mhp);
return 0;
}
diff --git a/drivers/infiniband/hw/cxgb4/provider.c b/drivers/infiniband/hw/cxgb4/provider.c
index 586b0c37481f..507c54572cc9 100644
--- a/drivers/infiniband/hw/cxgb4/provider.c
+++ b/drivers/infiniband/hw/cxgb4/provider.c
@@ -58,51 +58,34 @@ static int fastreg_support = 1;
module_param(fastreg_support, int, 0644);
MODULE_PARM_DESC(fastreg_support, "Advertise fastreg support (default=1)");
-void _c4iw_free_ucontext(struct kref *kref)
+static void c4iw_dealloc_ucontext(struct ib_ucontext *context)
{
- struct c4iw_ucontext *ucontext;
+ struct c4iw_ucontext *ucontext = to_c4iw_ucontext(context);
struct c4iw_dev *rhp;
struct c4iw_mm_entry *mm, *tmp;
- ucontext = container_of(kref, struct c4iw_ucontext, kref);
+ pr_debug("context %p\n", context);
rhp = to_c4iw_dev(ucontext->ibucontext.device);
- pr_debug("ucontext %p\n", ucontext);
list_for_each_entry_safe(mm, tmp, &ucontext->mmaps, entry)
kfree(mm);
c4iw_release_dev_ucontext(&rhp->rdev, &ucontext->uctx);
- kfree(ucontext);
}
-static int c4iw_dealloc_ucontext(struct ib_ucontext *context)
+static int c4iw_alloc_ucontext(struct ib_ucontext *ucontext,
+ struct ib_udata *udata)
{
- struct c4iw_ucontext *ucontext = to_c4iw_ucontext(context);
-
- pr_debug("context %p\n", context);
- c4iw_put_ucontext(ucontext);
- return 0;
-}
-
-static struct ib_ucontext *c4iw_alloc_ucontext(struct ib_device *ibdev,
- struct ib_udata *udata)
-{
- struct c4iw_ucontext *context;
+ struct ib_device *ibdev = ucontext->device;
+ struct c4iw_ucontext *context = to_c4iw_ucontext(ucontext);
struct c4iw_dev *rhp = to_c4iw_dev(ibdev);
struct c4iw_alloc_ucontext_resp uresp;
int ret = 0;
struct c4iw_mm_entry *mm = NULL;
pr_debug("ibdev %p\n", ibdev);
- context = kzalloc(sizeof(*context), GFP_KERNEL);
- if (!context) {
- ret = -ENOMEM;
- goto err;
- }
-
c4iw_init_dev_ucontext(&rhp->rdev, &context->uctx);
INIT_LIST_HEAD(&context->mmaps);
spin_lock_init(&context->mmap_lock);
- kref_init(&context->kref);
if (udata->outlen < sizeof(uresp) - sizeof(uresp.reserved)) {
pr_err_once("Warning - downlevel libcxgb4 (non-fatal), device status page disabled\n");
@@ -111,7 +94,7 @@ static struct ib_ucontext *c4iw_alloc_ucontext(struct ib_device *ibdev,
mm = kmalloc(sizeof(*mm), GFP_KERNEL);
if (!mm) {
ret = -ENOMEM;
- goto err_free;
+ goto err;
}
uresp.status_page_size = PAGE_SIZE;
@@ -131,13 +114,11 @@ static struct ib_ucontext *c4iw_alloc_ucontext(struct ib_device *ibdev,
mm->len = PAGE_SIZE;
insert_mmap(context, mm);
}
- return &context->ibucontext;
+ return 0;
err_mm:
kfree(mm);
-err_free:
- kfree(context);
err:
- return ERR_PTR(ret);
+ return ret;
}
static int c4iw_mmap(struct ib_ucontext *context, struct vm_area_struct *vma)
@@ -209,7 +190,7 @@ static int c4iw_mmap(struct ib_ucontext *context, struct vm_area_struct *vma)
return ret;
}
-static int c4iw_deallocate_pd(struct ib_pd *pd)
+static void c4iw_deallocate_pd(struct ib_pd *pd)
{
struct c4iw_dev *rhp;
struct c4iw_pd *php;
@@ -221,15 +202,13 @@ static int c4iw_deallocate_pd(struct ib_pd *pd)
mutex_lock(&rhp->rdev.stats.lock);
rhp->rdev.stats.pd.cur--;
mutex_unlock(&rhp->rdev.stats.lock);
- kfree(php);
- return 0;
}
-static struct ib_pd *c4iw_allocate_pd(struct ib_device *ibdev,
- struct ib_ucontext *context,
- struct ib_udata *udata)
+static int c4iw_allocate_pd(struct ib_pd *pd, struct ib_ucontext *context,
+ struct ib_udata *udata)
{
- struct c4iw_pd *php;
+ struct c4iw_pd *php = to_c4iw_pd(pd);
+ struct ib_device *ibdev = pd->device;
u32 pdid;
struct c4iw_dev *rhp;
@@ -237,12 +216,8 @@ static struct ib_pd *c4iw_allocate_pd(struct ib_device *ibdev,
rhp = (struct c4iw_dev *) ibdev;
pdid = c4iw_get_resource(&rhp->rdev.resource.pdid_table);
if (!pdid)
- return ERR_PTR(-EINVAL);
- php = kzalloc(sizeof(*php), GFP_KERNEL);
- if (!php) {
- c4iw_put_resource(&rhp->rdev.resource.pdid_table, pdid);
- return ERR_PTR(-ENOMEM);
- }
+ return -EINVAL;
+
php->pdid = pdid;
php->rhp = rhp;
if (context) {
@@ -250,7 +225,7 @@ static struct ib_pd *c4iw_allocate_pd(struct ib_device *ibdev,
if (ib_copy_to_udata(udata, &uresp, sizeof(uresp))) {
c4iw_deallocate_pd(&php->ibpd);
- return ERR_PTR(-EFAULT);
+ return -EFAULT;
}
}
mutex_lock(&rhp->rdev.stats.lock);
@@ -259,7 +234,7 @@ static struct ib_pd *c4iw_allocate_pd(struct ib_device *ibdev,
rhp->rdev.stats.pd.max = rhp->rdev.stats.pd.cur;
mutex_unlock(&rhp->rdev.stats.lock);
pr_debug("pdid 0x%0x ptr 0x%p\n", pdid, php);
- return &php->ibpd;
+ return 0;
}
static int c4iw_query_pkey(struct ib_device *ibdev, u8 port, u16 index,
@@ -376,8 +351,9 @@ static int c4iw_query_port(struct ib_device *ibdev, u8 port,
static ssize_t hw_rev_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct c4iw_dev *c4iw_dev = container_of(dev, struct c4iw_dev,
- ibdev.dev);
+ struct c4iw_dev *c4iw_dev =
+ rdma_device_to_drv_device(dev, struct c4iw_dev, ibdev);
+
pr_debug("dev 0x%p\n", dev);
return sprintf(buf, "%d\n",
CHELSIO_CHIP_RELEASE(c4iw_dev->rdev.lldi.adapter_type));
@@ -387,8 +363,8 @@ static DEVICE_ATTR_RO(hw_rev);
static ssize_t hca_type_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct c4iw_dev *c4iw_dev = container_of(dev, struct c4iw_dev,
- ibdev.dev);
+ struct c4iw_dev *c4iw_dev =
+ rdma_device_to_drv_device(dev, struct c4iw_dev, ibdev);
struct ethtool_drvinfo info;
struct net_device *lldev = c4iw_dev->rdev.lldi.ports[0];
@@ -401,8 +377,9 @@ static DEVICE_ATTR_RO(hca_type);
static ssize_t board_id_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
- struct c4iw_dev *c4iw_dev = container_of(dev, struct c4iw_dev,
- ibdev.dev);
+ struct c4iw_dev *c4iw_dev =
+ rdma_device_to_drv_device(dev, struct c4iw_dev, ibdev);
+
pr_debug("dev 0x%p\n", dev);
return sprintf(buf, "%x.%x\n", c4iw_dev->rdev.lldi.pdev->vendor,
c4iw_dev->rdev.lldi.pdev->device);
@@ -547,6 +524,7 @@ static const struct ib_device_ops c4iw_dev_ops = {
.destroy_cq = c4iw_destroy_cq,
.destroy_qp = c4iw_destroy_qp,
.destroy_srq = c4iw_destroy_srq,
+ .fill_res_entry = fill_res_entry,
.get_dev_fw_str = get_dev_fw_str,
.get_dma_mr = c4iw_get_dma_mr,
.get_hw_stats = c4iw_get_mib,
@@ -567,6 +545,8 @@ static const struct ib_device_ops c4iw_dev_ops = {
.query_qp = c4iw_ib_query_qp,
.reg_user_mr = c4iw_reg_user_mr,
.req_notify_cq = c4iw_arm_cq,
+ INIT_RDMA_OBJ_SIZE(ib_pd, c4iw_pd, ibpd),
+ INIT_RDMA_OBJ_SIZE(ib_ucontext, c4iw_ucontext, ibucontext),
};
void c4iw_register_device(struct work_struct *work)
@@ -613,7 +593,7 @@ void c4iw_register_device(struct work_struct *work)
dev->ibdev.dev.parent = &dev->rdev.lldi.pdev->dev;
dev->ibdev.uverbs_abi_ver = C4IW_UVERBS_ABI_VERSION;
- dev->ibdev.iwcm = kmalloc(sizeof(struct iw_cm_verbs), GFP_KERNEL);
+ dev->ibdev.iwcm = kzalloc(sizeof(struct iw_cm_verbs), GFP_KERNEL);
if (!dev->ibdev.iwcm) {
ret = -ENOMEM;
goto err_dealloc_ctx;
@@ -627,14 +607,13 @@ void c4iw_register_device(struct work_struct *work)
dev->ibdev.iwcm->add_ref = c4iw_qp_add_ref;
dev->ibdev.iwcm->rem_ref = c4iw_qp_rem_ref;
dev->ibdev.iwcm->get_qp = c4iw_get_qp;
- dev->ibdev.res.fill_res_entry = fill_res_entry;
memcpy(dev->ibdev.iwcm->ifname, dev->rdev.lldi.ports[0]->name,
sizeof(dev->ibdev.iwcm->ifname));
rdma_set_device_sysfs_group(&dev->ibdev, &c4iw_attr_group);
dev->ibdev.driver_id = RDMA_DRIVER_CXGB4;
ib_set_device_ops(&dev->ibdev, &c4iw_dev_ops);
- ret = ib_register_device(&dev->ibdev, "cxgb4_%d", NULL);
+ ret = ib_register_device(&dev->ibdev, "cxgb4_%d");
if (ret)
goto err_kfree_iwcm;
return;
diff --git a/drivers/infiniband/hw/cxgb4/qp.c b/drivers/infiniband/hw/cxgb4/qp.c
index 504cf525508f..d3a82839f5ea 100644
--- a/drivers/infiniband/hw/cxgb4/qp.c
+++ b/drivers/infiniband/hw/cxgb4/qp.c
@@ -31,6 +31,7 @@
*/
#include <linux/module.h>
+#include <rdma/uverbs_ioctl.h>
#include "iw_cxgb4.h"
@@ -632,7 +633,10 @@ static void build_rdma_write_cmpl(struct t4_sq *sq,
wcwr->stag_sink = cpu_to_be32(rdma_wr(wr)->rkey);
wcwr->to_sink = cpu_to_be64(rdma_wr(wr)->remote_addr);
- wcwr->stag_inv = cpu_to_be32(wr->next->ex.invalidate_rkey);
+ if (wr->next->opcode == IB_WR_SEND)
+ wcwr->stag_inv = 0;
+ else
+ wcwr->stag_inv = cpu_to_be32(wr->next->ex.invalidate_rkey);
wcwr->r2 = 0;
wcwr->r3 = 0;
@@ -726,7 +730,10 @@ static void post_write_cmpl(struct c4iw_qp *qhp, const struct ib_send_wr *wr)
/* SEND_WITH_INV swsqe */
swsqe = &qhp->wq.sq.sw_sq[qhp->wq.sq.pidx];
- swsqe->opcode = FW_RI_SEND_WITH_INV;
+ if (wr->next->opcode == IB_WR_SEND)
+ swsqe->opcode = FW_RI_SEND;
+ else
+ swsqe->opcode = FW_RI_SEND_WITH_INV;
swsqe->idx = qhp->wq.sq.pidx;
swsqe->complete = 0;
swsqe->signaled = send_signaled;
@@ -897,8 +904,6 @@ static void free_qp_work(struct work_struct *work)
destroy_qp(&rhp->rdev, &qhp->wq,
ucontext ? &ucontext->uctx : &rhp->rdev.uctx, !qhp->srq);
- if (ucontext)
- c4iw_put_ucontext(ucontext);
c4iw_put_wr_wait(qhp->wr_waitp);
kfree(qhp);
}
@@ -1133,9 +1138,9 @@ int c4iw_post_send(struct ib_qp *ibqp, const struct ib_send_wr *wr,
/*
* Fastpath for NVMe-oF target WRITE + SEND_WITH_INV wr chain which is
* the response for small NVMEe-oF READ requests. If the chain is
- * exactly a WRITE->SEND_WITH_INV and the sgl depths and lengths
- * meet the requirements of the fw_ri_write_cmpl_wr work request,
- * then build and post the write_cmpl WR. If any of the tests
+ * exactly a WRITE->SEND_WITH_INV or a WRITE->SEND and the sgl depths
+ * and lengths meet the requirements of the fw_ri_write_cmpl_wr work
+ * request, then build and post the write_cmpl WR. If any of the tests
* below are not true, then we continue on with the tradtional WRITE
* and SEND WRs.
*/
@@ -1145,7 +1150,8 @@ int c4iw_post_send(struct ib_qp *ibqp, const struct ib_send_wr *wr,
wr && wr->next && !wr->next->next &&
wr->opcode == IB_WR_RDMA_WRITE &&
wr->sg_list[0].length && wr->num_sge <= T4_WRITE_CMPL_MAX_SGL &&
- wr->next->opcode == IB_WR_SEND_WITH_INV &&
+ (wr->next->opcode == IB_WR_SEND ||
+ wr->next->opcode == IB_WR_SEND_WITH_INV) &&
wr->next->sg_list[0].length == T4_WRITE_CMPL_MAX_CQE &&
wr->next->num_sge == 1 && num_wrs >= 2) {
post_write_cmpl(qhp, wr);
@@ -2129,7 +2135,8 @@ struct ib_qp *c4iw_create_qp(struct ib_pd *pd, struct ib_qp_init_attr *attrs,
struct c4iw_cq *rchp;
struct c4iw_create_qp_resp uresp;
unsigned int sqsize, rqsize = 0;
- struct c4iw_ucontext *ucontext;
+ struct c4iw_ucontext *ucontext = rdma_udata_to_drv_context(
+ udata, struct c4iw_ucontext, ibucontext);
int ret;
struct c4iw_mm_entry *sq_key_mm, *rq_key_mm = NULL, *sq_db_key_mm;
struct c4iw_mm_entry *rq_db_key_mm = NULL, *ma_sync_key_mm = NULL;
@@ -2163,8 +2170,6 @@ struct ib_qp *c4iw_create_qp(struct ib_pd *pd, struct ib_qp_init_attr *attrs,
if (sqsize < 8)
sqsize = 8;
- ucontext = udata ? to_c4iw_ucontext(pd->uobject->context) : NULL;
-
qhp = kzalloc(sizeof(*qhp), GFP_KERNEL);
if (!qhp)
return ERR_PTR(-ENOMEM);
@@ -2331,7 +2336,6 @@ struct ib_qp *c4iw_create_qp(struct ib_pd *pd, struct ib_qp_init_attr *attrs,
insert_mmap(ucontext, ma_sync_key_mm);
}
- c4iw_get_ucontext(ucontext);
qhp->ucontext = ucontext;
}
if (!attrs->srq) {
@@ -2589,7 +2593,7 @@ static int alloc_srq_queue(struct c4iw_srq *srq, struct c4iw_dev_ucontext *uctx,
/* build fw_ri_res_wr */
wr_len = sizeof(*res_wr) + sizeof(*res);
- skb = alloc_skb(wr_len, GFP_KERNEL | __GFP_NOFAIL);
+ skb = alloc_skb(wr_len, GFP_KERNEL);
if (!skb)
goto err_free_queue;
set_wr_txq(skb, CPL_PRIORITY_CONTROL, 0);
@@ -2711,7 +2715,8 @@ struct ib_srq *c4iw_create_srq(struct ib_pd *pd, struct ib_srq_init_attr *attrs,
rqsize = attrs->attr.max_wr + 1;
rqsize = roundup_pow_of_two(max_t(u16, rqsize, 16));
- ucontext = udata ? to_c4iw_ucontext(pd->uobject->context) : NULL;
+ ucontext = rdma_udata_to_drv_context(udata, struct c4iw_ucontext,
+ ibucontext);
srq = kzalloc(sizeof(*srq), GFP_KERNEL);
if (!srq)
diff --git a/drivers/infiniband/hw/cxgb4/t4.h b/drivers/infiniband/hw/cxgb4/t4.h
index fff6d48d262f..b170817b2741 100644
--- a/drivers/infiniband/hw/cxgb4/t4.h
+++ b/drivers/infiniband/hw/cxgb4/t4.h
@@ -35,6 +35,7 @@
#include "t4_regs.h"
#include "t4_values.h"
#include "t4_msg.h"
+#include "t4_tcb.h"
#include "t4fw_ri_api.h"
#define T4_MAX_NUM_PD 65536
diff --git a/drivers/infiniband/hw/hfi1/Makefile b/drivers/infiniband/hw/hfi1/Makefile
index 3ce9dc8c3463..4044a8c8dbf4 100644
--- a/drivers/infiniband/hw/hfi1/Makefile
+++ b/drivers/infiniband/hw/hfi1/Makefile
@@ -24,6 +24,7 @@ hfi1-y := \
mad.o \
mmu_rb.o \
msix.o \
+ opfn.o \
pcie.o \
pio.o \
pio_copy.o \
diff --git a/drivers/infiniband/hw/hfi1/chip.c b/drivers/infiniband/hw/hfi1/chip.c
index b443642eac02..612f04190ed8 100644
--- a/drivers/infiniband/hw/hfi1/chip.c
+++ b/drivers/infiniband/hw/hfi1/chip.c
@@ -4253,6 +4253,8 @@ static struct cntr_entry dev_cntrs[DEV_CNTR_LAST] = {
access_sw_pio_drain),
[C_SW_KMEM_WAIT] = CNTR_ELEM("KmemWait", 0, 0, CNTR_NORMAL,
access_sw_kmem_wait),
+[C_SW_TID_WAIT] = CNTR_ELEM("TidWait", 0, 0, CNTR_NORMAL,
+ hfi1_access_sw_tid_wait),
[C_SW_SEND_SCHED] = CNTR_ELEM("SendSched", 0, 0, CNTR_NORMAL,
access_sw_send_schedule),
[C_SDMA_DESC_FETCHED_CNT] = CNTR_ELEM("SDEDscFdCn",
@@ -5222,6 +5224,17 @@ int is_bx(struct hfi1_devdata *dd)
return (chip_rev_minor & 0xF0) == 0x10;
}
+/* return true is kernel urg disabled for rcd */
+bool is_urg_masked(struct hfi1_ctxtdata *rcd)
+{
+ u64 mask;
+ u32 is = IS_RCVURGENT_START + rcd->ctxt;
+ u8 bit = is % 64;
+
+ mask = read_csr(rcd->dd, CCE_INT_MASK + (8 * (is / 64)));
+ return !(mask & BIT_ULL(bit));
+}
+
/*
* Append string s to buffer buf. Arguments curp and len are the current
* position and remaining length, respectively.
diff --git a/drivers/infiniband/hw/hfi1/chip.h b/drivers/infiniband/hw/hfi1/chip.h
index 6b9c8f12dff8..6c27c1c6a868 100644
--- a/drivers/infiniband/hw/hfi1/chip.h
+++ b/drivers/infiniband/hw/hfi1/chip.h
@@ -1,7 +1,7 @@
#ifndef _CHIP_H
#define _CHIP_H
/*
- * Copyright(c) 2015 - 2017 Intel Corporation.
+ * Copyright(c) 2015 - 2018 Intel Corporation.
*
* This file is provided under a dual BSD/GPLv2 license. When using or
* redistributing this file, you may do so under either license.
@@ -804,6 +804,7 @@ void clear_linkup_counters(struct hfi1_devdata *dd);
u32 hdrqempty(struct hfi1_ctxtdata *rcd);
int is_ax(struct hfi1_devdata *dd);
int is_bx(struct hfi1_devdata *dd);
+bool is_urg_masked(struct hfi1_ctxtdata *rcd);
u32 read_physical_state(struct hfi1_devdata *dd);
u32 chip_to_opa_pstate(struct hfi1_devdata *dd, u32 chip_pstate);
const char *opa_lstate_name(u32 lstate);
@@ -926,6 +927,7 @@ enum {
C_SW_PIO_WAIT,
C_SW_PIO_DRAIN,
C_SW_KMEM_WAIT,
+ C_SW_TID_WAIT,
C_SW_SEND_SCHED,
C_SDMA_DESC_FETCHED_CNT,
C_SDMA_INT_CNT,
diff --git a/drivers/infiniband/hw/hfi1/common.h b/drivers/infiniband/hw/hfi1/common.h
index 40d3cfb58bd1..7310a5dba420 100644
--- a/drivers/infiniband/hw/hfi1/common.h
+++ b/drivers/infiniband/hw/hfi1/common.h
@@ -340,6 +340,10 @@ struct diag_pkt {
#define HFI1_PSM_IOC_BASE_SEQ 0x0
+/* Number of BTH.PSN bits used for sequence number in expected rcvs */
+#define HFI1_KDETH_BTH_SEQ_SHIFT 11
+#define HFI1_KDETH_BTH_SEQ_MASK (BIT(HFI1_KDETH_BTH_SEQ_SHIFT) - 1)
+
static inline __u64 rhf_to_cpu(const __le32 *rbuf)
{
return __le64_to_cpu(*((__le64 *)rbuf));
diff --git a/drivers/infiniband/hw/hfi1/debugfs.c b/drivers/infiniband/hw/hfi1/debugfs.c
index 0a557795563c..427ba0ce74a5 100644
--- a/drivers/infiniband/hw/hfi1/debugfs.c
+++ b/drivers/infiniband/hw/hfi1/debugfs.c
@@ -1167,6 +1167,7 @@ void hfi1_dbg_ibdev_init(struct hfi1_ibdev *ibd)
char link[10];
struct hfi1_devdata *dd = dd_from_dev(ibd);
struct hfi1_pportdata *ppd;
+ struct dentry *root;
int unit = dd->unit;
int i, j;
@@ -1174,31 +1175,29 @@ void hfi1_dbg_ibdev_init(struct hfi1_ibdev *ibd)
return;
snprintf(name, sizeof(name), "%s_%d", class_name(), unit);
snprintf(link, sizeof(link), "%d", unit);
- ibd->hfi1_ibdev_dbg = debugfs_create_dir(name, hfi1_dbg_root);
- if (!ibd->hfi1_ibdev_dbg) {
- pr_warn("create of %s failed\n", name);
- return;
- }
+ root = debugfs_create_dir(name, hfi1_dbg_root);
+ ibd->hfi1_ibdev_dbg = root;
+
ibd->hfi1_ibdev_link =
debugfs_create_symlink(link, hfi1_dbg_root, name);
- if (!ibd->hfi1_ibdev_link) {
- pr_warn("create of %s symlink failed\n", name);
- return;
- }
- DEBUGFS_SEQ_FILE_CREATE(opcode_stats, ibd->hfi1_ibdev_dbg, ibd);
- DEBUGFS_SEQ_FILE_CREATE(tx_opcode_stats, ibd->hfi1_ibdev_dbg, ibd);
- DEBUGFS_SEQ_FILE_CREATE(ctx_stats, ibd->hfi1_ibdev_dbg, ibd);
- DEBUGFS_SEQ_FILE_CREATE(qp_stats, ibd->hfi1_ibdev_dbg, ibd);
- DEBUGFS_SEQ_FILE_CREATE(sdes, ibd->hfi1_ibdev_dbg, ibd);
- DEBUGFS_SEQ_FILE_CREATE(rcds, ibd->hfi1_ibdev_dbg, ibd);
- DEBUGFS_SEQ_FILE_CREATE(pios, ibd->hfi1_ibdev_dbg, ibd);
- DEBUGFS_SEQ_FILE_CREATE(sdma_cpu_list, ibd->hfi1_ibdev_dbg, ibd);
+
+ debugfs_create_file("opcode_stats", 0444, root, ibd,
+ &_opcode_stats_file_ops);
+ debugfs_create_file("tx_opcode_stats", 0444, root, ibd,
+ &_tx_opcode_stats_file_ops);
+ debugfs_create_file("ctx_stats", 0444, root, ibd, &_ctx_stats_file_ops);
+ debugfs_create_file("qp_stats", 0444, root, ibd, &_qp_stats_file_ops);
+ debugfs_create_file("sdes", 0444, root, ibd, &_sdes_file_ops);
+ debugfs_create_file("rcds", 0444, root, ibd, &_rcds_file_ops);
+ debugfs_create_file("pios", 0444, root, ibd, &_pios_file_ops);
+ debugfs_create_file("sdma_cpu_list", 0444, root, ibd,
+ &_sdma_cpu_list_file_ops);
+
/* dev counter files */
for (i = 0; i < ARRAY_SIZE(cntr_ops); i++)
- DEBUGFS_FILE_CREATE(cntr_ops[i].name,
- ibd->hfi1_ibdev_dbg,
- dd,
- &cntr_ops[i].ops, S_IRUGO);
+ debugfs_create_file(cntr_ops[i].name, 0444, root, dd,
+ &cntr_ops[i].ops);
+
/* per port files */
for (ppd = dd->pport, j = 0; j < dd->num_pports; j++, ppd++)
for (i = 0; i < ARRAY_SIZE(port_cntr_ops); i++) {
@@ -1206,12 +1205,11 @@ void hfi1_dbg_ibdev_init(struct hfi1_ibdev *ibd)
sizeof(name),
port_cntr_ops[i].name,
j + 1);
- DEBUGFS_FILE_CREATE(name,
- ibd->hfi1_ibdev_dbg,
- ppd,
- &port_cntr_ops[i].ops,
+ debugfs_create_file(name,
!port_cntr_ops[i].ops.write ?
- S_IRUGO : S_IRUGO | S_IWUSR);
+ S_IRUGO :
+ S_IRUGO | S_IWUSR,
+ root, ppd, &port_cntr_ops[i].ops);
}
hfi1_fault_init_debugfs(ibd);
@@ -1341,10 +1339,10 @@ DEBUGFS_FILE_OPS(driver_stats);
void hfi1_dbg_init(void)
{
hfi1_dbg_root = debugfs_create_dir(DRIVER_NAME, NULL);
- if (!hfi1_dbg_root)
- pr_warn("init of debugfs failed\n");
- DEBUGFS_SEQ_FILE_CREATE(driver_stats_names, hfi1_dbg_root, NULL);
- DEBUGFS_SEQ_FILE_CREATE(driver_stats, hfi1_dbg_root, NULL);
+ debugfs_create_file("driver_stats_names", 0444, hfi1_dbg_root, NULL,
+ &_driver_stats_names_file_ops);
+ debugfs_create_file("driver_stats", 0444, hfi1_dbg_root, NULL,
+ &_driver_stats_file_ops);
}
void hfi1_dbg_exit(void)
diff --git a/drivers/infiniband/hw/hfi1/debugfs.h b/drivers/infiniband/hw/hfi1/debugfs.h
index d5d824459fcc..57e582caa5eb 100644
--- a/drivers/infiniband/hw/hfi1/debugfs.h
+++ b/drivers/infiniband/hw/hfi1/debugfs.h
@@ -49,16 +49,6 @@
struct hfi1_ibdev;
-#define DEBUGFS_FILE_CREATE(name, parent, data, ops, mode) \
-do { \
- struct dentry *ent; \
- const char *__name = name; \
- ent = debugfs_create_file(__name, mode, parent, \
- data, ops); \
- if (!ent) \
- pr_warn("create of %s failed\n", __name); \
-} while (0)
-
#define DEBUGFS_SEQ_FILE_OPS(name) \
static const struct seq_operations _##name##_seq_ops = { \
.start = _##name##_seq_start, \
@@ -89,8 +79,6 @@ static const struct file_operations _##name##_file_ops = { \
.release = seq_release \
}
-#define DEBUGFS_SEQ_FILE_CREATE(name, parent, data) \
- DEBUGFS_FILE_CREATE(#name, parent, data, &_##name##_file_ops, 0444)
ssize_t hfi1_seq_read(struct file *file, char __user *buf, size_t size,
loff_t *ppos);
diff --git a/drivers/infiniband/hw/hfi1/driver.c b/drivers/infiniband/hw/hfi1/driver.c
index a8ad70730203..2a9d2912f5db 100644
--- a/drivers/infiniband/hw/hfi1/driver.c
+++ b/drivers/infiniband/hw/hfi1/driver.c
@@ -1575,25 +1575,32 @@ drop:
return -EINVAL;
}
-void handle_eflags(struct hfi1_packet *packet)
+static void show_eflags_errs(struct hfi1_packet *packet)
{
struct hfi1_ctxtdata *rcd = packet->rcd;
u32 rte = rhf_rcv_type_err(packet->rhf);
+ dd_dev_err(rcd->dd,
+ "receive context %d: rhf 0x%016llx, errs [ %s%s%s%s%s%s%s%s] rte 0x%x\n",
+ rcd->ctxt, packet->rhf,
+ packet->rhf & RHF_K_HDR_LEN_ERR ? "k_hdr_len " : "",
+ packet->rhf & RHF_DC_UNC_ERR ? "dc_unc " : "",
+ packet->rhf & RHF_DC_ERR ? "dc " : "",
+ packet->rhf & RHF_TID_ERR ? "tid " : "",
+ packet->rhf & RHF_LEN_ERR ? "len " : "",
+ packet->rhf & RHF_ECC_ERR ? "ecc " : "",
+ packet->rhf & RHF_VCRC_ERR ? "vcrc " : "",
+ packet->rhf & RHF_ICRC_ERR ? "icrc " : "",
+ rte);
+}
+
+void handle_eflags(struct hfi1_packet *packet)
+{
+ struct hfi1_ctxtdata *rcd = packet->rcd;
+
rcv_hdrerr(rcd, rcd->ppd, packet);
if (rhf_err_flags(packet->rhf))
- dd_dev_err(rcd->dd,
- "receive context %d: rhf 0x%016llx, errs [ %s%s%s%s%s%s%s%s] rte 0x%x\n",
- rcd->ctxt, packet->rhf,
- packet->rhf & RHF_K_HDR_LEN_ERR ? "k_hdr_len " : "",
- packet->rhf & RHF_DC_UNC_ERR ? "dc_unc " : "",
- packet->rhf & RHF_DC_ERR ? "dc " : "",
- packet->rhf & RHF_TID_ERR ? "tid " : "",
- packet->rhf & RHF_LEN_ERR ? "len " : "",
- packet->rhf & RHF_ECC_ERR ? "ecc " : "",
- packet->rhf & RHF_VCRC_ERR ? "vcrc " : "",
- packet->rhf & RHF_ICRC_ERR ? "icrc " : "",
- rte);
+ show_eflags_errs(packet);
}
/*
@@ -1699,11 +1706,14 @@ static int kdeth_process_expected(struct hfi1_packet *packet)
if (unlikely(hfi1_dbg_should_fault_rx(packet)))
return RHF_RCV_CONTINUE;
- if (unlikely(rhf_err_flags(packet->rhf)))
- handle_eflags(packet);
+ if (unlikely(rhf_err_flags(packet->rhf))) {
+ struct hfi1_ctxtdata *rcd = packet->rcd;
- dd_dev_err(packet->rcd->dd,
- "Unhandled expected packet received. Dropping.\n");
+ if (hfi1_handle_kdeth_eflags(rcd, rcd->ppd, packet))
+ return RHF_RCV_CONTINUE;
+ }
+
+ hfi1_kdeth_expected_rcv(packet);
return RHF_RCV_CONTINUE;
}
@@ -1712,11 +1722,17 @@ static int kdeth_process_eager(struct hfi1_packet *packet)
hfi1_setup_9B_packet(packet);
if (unlikely(hfi1_dbg_should_fault_rx(packet)))
return RHF_RCV_CONTINUE;
- if (unlikely(rhf_err_flags(packet->rhf)))
- handle_eflags(packet);
- dd_dev_err(packet->rcd->dd,
- "Unhandled eager packet received. Dropping.\n");
+ trace_hfi1_rcvhdr(packet);
+ if (unlikely(rhf_err_flags(packet->rhf))) {
+ struct hfi1_ctxtdata *rcd = packet->rcd;
+
+ show_eflags_errs(packet);
+ if (hfi1_handle_kdeth_eflags(rcd, rcd->ppd, packet))
+ return RHF_RCV_CONTINUE;
+ }
+
+ hfi1_kdeth_eager_rcv(packet);
return RHF_RCV_CONTINUE;
}
diff --git a/drivers/infiniband/hw/hfi1/fault.c b/drivers/infiniband/hw/hfi1/fault.c
index e2290f32c8d9..3fd3315d0fb0 100644
--- a/drivers/infiniband/hw/hfi1/fault.c
+++ b/drivers/infiniband/hw/hfi1/fault.c
@@ -250,6 +250,7 @@ void hfi1_fault_exit_debugfs(struct hfi1_ibdev *ibd)
int hfi1_fault_init_debugfs(struct hfi1_ibdev *ibd)
{
struct dentry *parent = ibd->hfi1_ibdev_dbg;
+ struct dentry *fault_dir;
ibd->fault = kzalloc(sizeof(*ibd->fault), GFP_KERNEL);
if (!ibd->fault)
@@ -269,45 +270,31 @@ int hfi1_fault_init_debugfs(struct hfi1_ibdev *ibd)
bitmap_zero(ibd->fault->opcodes,
sizeof(ibd->fault->opcodes) * BITS_PER_BYTE);
- ibd->fault->dir =
- fault_create_debugfs_attr("fault", parent,
- &ibd->fault->attr);
- if (IS_ERR(ibd->fault->dir)) {
+ fault_dir =
+ fault_create_debugfs_attr("fault", parent, &ibd->fault->attr);
+ if (IS_ERR(fault_dir)) {
kfree(ibd->fault);
ibd->fault = NULL;
return -ENOENT;
}
-
- DEBUGFS_SEQ_FILE_CREATE(fault_stats, ibd->fault->dir, ibd);
- if (!debugfs_create_bool("enable", 0600, ibd->fault->dir,
- &ibd->fault->enable))
- goto fail;
- if (!debugfs_create_bool("suppress_err", 0600,
- ibd->fault->dir,
- &ibd->fault->suppress_err))
- goto fail;
- if (!debugfs_create_bool("opcode_mode", 0600, ibd->fault->dir,
- &ibd->fault->opcode))
- goto fail;
- if (!debugfs_create_file("opcodes", 0600, ibd->fault->dir,
- ibd->fault, &__fault_opcodes_fops))
- goto fail;
- if (!debugfs_create_u64("skip_pkts", 0600,
- ibd->fault->dir,
- &ibd->fault->fault_skip))
- goto fail;
- if (!debugfs_create_u64("skip_usec", 0600,
- ibd->fault->dir,
- &ibd->fault->fault_skip_usec))
- goto fail;
- if (!debugfs_create_u8("direction", 0600, ibd->fault->dir,
- &ibd->fault->direction))
- goto fail;
+ ibd->fault->dir = fault_dir;
+
+ debugfs_create_file("fault_stats", 0444, fault_dir, ibd,
+ &_fault_stats_file_ops);
+ debugfs_create_bool("enable", 0600, fault_dir, &ibd->fault->enable);
+ debugfs_create_bool("suppress_err", 0600, fault_dir,
+ &ibd->fault->suppress_err);
+ debugfs_create_bool("opcode_mode", 0600, fault_dir,
+ &ibd->fault->opcode);
+ debugfs_create_file("opcodes", 0600, fault_dir, ibd->fault,
+ &__fault_opcodes_fops);
+ debugfs_create_u64("skip_pkts", 0600, fault_dir,
+ &ibd->fault->fault_skip);
+ debugfs_create_u64("skip_usec", 0600, fault_dir,
+ &ibd->fault->fault_skip_usec);
+ debugfs_create_u8("direction", 0600, fault_dir, &ibd->fault->direction);
return 0;
-fail:
- hfi1_fault_exit_debugfs(ibd);
- return -ENOMEM;
}
bool hfi1_dbg_fault_suppress_err(struct hfi1_ibdev *ibd)
diff --git a/drivers/infiniband/hw/hfi1/hfi.h b/drivers/infiniband/hw/hfi1/hfi.h
index 6db2276f5c13..048b5d73ba39 100644
--- a/drivers/infiniband/hw/hfi1/hfi.h
+++ b/drivers/infiniband/hw/hfi1/hfi.h
@@ -73,6 +73,7 @@
#include "chip_registers.h"
#include "common.h"
+#include "opfn.h"
#include "verbs.h"
#include "pio.h"
#include "chip.h"
@@ -98,6 +99,8 @@
#define NEIGHBOR_TYPE_HFI 0
#define NEIGHBOR_TYPE_SWITCH 1
+#define HFI1_MAX_ACTIVE_WORKQUEUE_ENTRIES 5
+
extern unsigned long hfi1_cap_mask;
#define HFI1_CAP_KGET_MASK(mask, cap) ((mask) & HFI1_CAP_##cap)
#define HFI1_CAP_UGET_MASK(mask, cap) \
@@ -195,6 +198,14 @@ struct exp_tid_set {
};
typedef int (*rhf_rcv_function_ptr)(struct hfi1_packet *packet);
+
+struct tid_queue {
+ struct list_head queue_head;
+ /* queue head for QP TID resource waiters */
+ u32 enqueue; /* count of tid enqueues */
+ u32 dequeue; /* count of tid dequeues */
+};
+
struct hfi1_ctxtdata {
/* rcvhdrq base, needs mmap before useful */
void *rcvhdrq;
@@ -288,6 +299,12 @@ struct hfi1_ctxtdata {
/* PSM Specific fields */
/* lock protecting all Expected TID data */
struct mutex exp_mutex;
+ /* lock protecting all Expected TID data of kernel contexts */
+ spinlock_t exp_lock;
+ /* Queue for QP's waiting for HW TID flows */
+ struct tid_queue flow_queue;
+ /* Queue for QP's waiting for HW receive array entries */
+ struct tid_queue rarr_queue;
/* when waiting for rcv or pioavail */
wait_queue_head_t wait;
/* uuid from PSM */
@@ -320,6 +337,9 @@ struct hfi1_ctxtdata {
*/
u8 subctxt_cnt;
+ /* Bit mask to track free TID RDMA HW flows */
+ unsigned long flow_mask;
+ struct tid_flow_state flows[RXE_NUM_TID_FLOWS];
};
/**
@@ -1435,7 +1455,7 @@ void hfi1_init_pportdata(struct pci_dev *pdev, struct hfi1_pportdata *ppd,
struct hfi1_devdata *dd, u8 hw_pidx, u8 port);
void hfi1_free_ctxtdata(struct hfi1_devdata *dd, struct hfi1_ctxtdata *rcd);
int hfi1_rcd_put(struct hfi1_ctxtdata *rcd);
-void hfi1_rcd_get(struct hfi1_ctxtdata *rcd);
+int hfi1_rcd_get(struct hfi1_ctxtdata *rcd);
struct hfi1_ctxtdata *hfi1_rcd_get_by_index_safe(struct hfi1_devdata *dd,
u16 ctxt);
struct hfi1_ctxtdata *hfi1_rcd_get_by_index(struct hfi1_devdata *dd, u16 ctxt);
@@ -2100,7 +2120,7 @@ static inline u64 hfi1_pkt_default_send_ctxt_mask(struct hfi1_devdata *dd,
SEND_CTXT_CHECK_ENABLE_DISALLOW_PBC_TEST_SMASK |
#endif
HFI1_PKT_USER_SC_INTEGRITY;
- else
+ else if (ctxt_type != SC_KERNEL)
base_sc_integrity |= HFI1_PKT_KERNEL_SC_INTEGRITY;
/* turn on send-side job key checks if !A0 */
diff --git a/drivers/infiniband/hw/hfi1/init.c b/drivers/infiniband/hw/hfi1/init.c
index 441b06e2a154..faaaac8fbc55 100644
--- a/drivers/infiniband/hw/hfi1/init.c
+++ b/drivers/infiniband/hw/hfi1/init.c
@@ -73,7 +73,6 @@
#undef pr_fmt
#define pr_fmt(fmt) DRIVER_NAME ": " fmt
-#define HFI1_MAX_ACTIVE_WORKQUEUE_ENTRIES 5
/*
* min buffers we want to have per context, after driver
*/
@@ -216,12 +215,12 @@ static void hfi1_rcd_free(struct kref *kref)
struct hfi1_ctxtdata *rcd =
container_of(kref, struct hfi1_ctxtdata, kref);
- hfi1_free_ctxtdata(rcd->dd, rcd);
-
spin_lock_irqsave(&rcd->dd->uctxt_lock, flags);
rcd->dd->rcd[rcd->ctxt] = NULL;
spin_unlock_irqrestore(&rcd->dd->uctxt_lock, flags);
+ hfi1_free_ctxtdata(rcd->dd, rcd);
+
kfree(rcd);
}
@@ -244,10 +243,13 @@ int hfi1_rcd_put(struct hfi1_ctxtdata *rcd)
* @rcd: pointer to an initialized rcd data structure
*
* Use this to get a reference after the init.
+ *
+ * Return : reflect kref_get_unless_zero(), which returns non-zero on
+ * increment, otherwise 0.
*/
-void hfi1_rcd_get(struct hfi1_ctxtdata *rcd)
+int hfi1_rcd_get(struct hfi1_ctxtdata *rcd)
{
- kref_get(&rcd->kref);
+ return kref_get_unless_zero(&rcd->kref);
}
/**
@@ -327,7 +329,8 @@ struct hfi1_ctxtdata *hfi1_rcd_get_by_index(struct hfi1_devdata *dd, u16 ctxt)
spin_lock_irqsave(&dd->uctxt_lock, flags);
if (dd->rcd[ctxt]) {
rcd = dd->rcd[ctxt];
- hfi1_rcd_get(rcd);
+ if (!hfi1_rcd_get(rcd))
+ rcd = NULL;
}
spin_unlock_irqrestore(&dd->uctxt_lock, flags);
@@ -372,6 +375,9 @@ int hfi1_create_ctxtdata(struct hfi1_pportdata *ppd, int numa,
rcd->rhf_rcv_function_map = normal_rhf_rcv_functions;
mutex_init(&rcd->exp_mutex);
+ spin_lock_init(&rcd->exp_lock);
+ INIT_LIST_HEAD(&rcd->flow_queue.queue_head);
+ INIT_LIST_HEAD(&rcd->rarr_queue.queue_head);
hfi1_cdbg(PROC, "setting up context %u\n", rcd->ctxt);
@@ -474,6 +480,9 @@ int hfi1_create_ctxtdata(struct hfi1_pportdata *ppd, int numa,
GFP_KERNEL, numa);
if (!rcd->opstats)
goto bail;
+
+ /* Initialize TID flow generations for the context */
+ hfi1_kern_init_ctxt_generations(rcd);
}
*context = rcd;
@@ -773,6 +782,8 @@ static void enable_chip(struct hfi1_devdata *dd)
rcvmask |= HFI1_RCVCTRL_NO_RHQ_DROP_ENB;
if (HFI1_CAP_KGET_MASK(rcd->flags, NODROP_EGR_FULL))
rcvmask |= HFI1_RCVCTRL_NO_EGR_DROP_ENB;
+ if (HFI1_CAP_IS_KSET(TID_RDMA))
+ rcvmask |= HFI1_RCVCTRL_TIDFLOW_ENB;
hfi1_rcvctrl(dd, rcvmask, rcd);
sc_enable(rcd->sc);
hfi1_rcd_put(rcd);
@@ -928,6 +939,8 @@ int hfi1_init(struct hfi1_devdata *dd, int reinit)
lastfail = hfi1_create_rcvhdrq(dd, rcd);
if (!lastfail)
lastfail = hfi1_setup_eagerbufs(rcd);
+ if (!lastfail)
+ lastfail = hfi1_kern_exp_rcv_init(rcd, reinit);
if (lastfail) {
dd_dev_err(dd,
"failed to allocate kernel ctxt's rcvhdrq and/or egr bufs\n");
@@ -1498,6 +1511,13 @@ static int __init hfi1_mod_init(void)
/* sanitize link CRC options */
link_crc_mask &= SUPPORTED_CRCS;
+ ret = opfn_init();
+ if (ret < 0) {
+ pr_err("Failed to allocate opfn_wq");
+ goto bail_dev;
+ }
+
+ hfi1_compute_tid_rdma_flow_wt();
/*
* These must be called before the driver is registered with
* the PCI subsystem.
@@ -1528,6 +1548,7 @@ module_init(hfi1_mod_init);
static void __exit hfi1_mod_cleanup(void)
{
pci_unregister_driver(&hfi1_pci_driver);
+ opfn_exit();
node_affinity_destroy_all();
hfi1_dbg_exit();
@@ -1582,7 +1603,7 @@ static void cleanup_device_data(struct hfi1_devdata *dd)
struct hfi1_ctxtdata *rcd = dd->rcd[ctxt];
if (rcd) {
- hfi1_clear_tids(rcd);
+ hfi1_free_ctxt_rcv_groups(rcd);
hfi1_free_ctxt(rcd);
}
}
diff --git a/drivers/infiniband/hw/hfi1/iowait.c b/drivers/infiniband/hw/hfi1/iowait.c
index 582f1ba136ff..adb4a1ba921b 100644
--- a/drivers/infiniband/hw/hfi1/iowait.c
+++ b/drivers/infiniband/hw/hfi1/iowait.c
@@ -6,6 +6,9 @@
#include "iowait.h"
#include "trace_iowait.h"
+/* 1 priority == 16 starve_cnt */
+#define IOWAIT_PRIORITY_STARVE_SHIFT 4
+
void iowait_set_flag(struct iowait *wait, u32 flag)
{
trace_hfi1_iowait_set(wait, flag);
@@ -44,7 +47,8 @@ void iowait_init(struct iowait *wait, u32 tx_limit,
uint seq,
bool pkts_sent),
void (*wakeup)(struct iowait *wait, int reason),
- void (*sdma_drained)(struct iowait *wait))
+ void (*sdma_drained)(struct iowait *wait),
+ void (*init_priority)(struct iowait *wait))
{
int i;
@@ -58,6 +62,7 @@ void iowait_init(struct iowait *wait, u32 tx_limit,
wait->sleep = sleep;
wait->wakeup = wakeup;
wait->sdma_drained = sdma_drained;
+ wait->init_priority = init_priority;
wait->flags = 0;
for (i = 0; i < IOWAIT_SES; i++) {
wait->wait[i].iow = wait;
@@ -92,3 +97,30 @@ int iowait_set_work_flag(struct iowait_work *w)
iowait_set_flag(w->iow, IOWAIT_PENDING_TID);
return IOWAIT_TID_SE;
}
+
+/**
+ * iowait_priority_update_top - update the top priority entry
+ * @w: the iowait struct
+ * @top: a pointer to the top priority entry
+ * @idx: the index of the current iowait in an array
+ * @top_idx: the array index for the iowait entry that has the top priority
+ *
+ * This function is called to compare the priority of a given
+ * iowait with the given top priority entry. The top index will
+ * be returned.
+ */
+uint iowait_priority_update_top(struct iowait *w,
+ struct iowait *top,
+ uint idx, uint top_idx)
+{
+ u8 cnt, tcnt;
+
+ /* Convert priority into starve_cnt and compare the total.*/
+ cnt = (w->priority << IOWAIT_PRIORITY_STARVE_SHIFT) + w->starved_cnt;
+ tcnt = (top->priority << IOWAIT_PRIORITY_STARVE_SHIFT) +
+ top->starved_cnt;
+ if (cnt > tcnt)
+ return idx;
+ else
+ return top_idx;
+}
diff --git a/drivers/infiniband/hw/hfi1/iowait.h b/drivers/infiniband/hw/hfi1/iowait.h
index 23a58ac0d47c..07847cb72169 100644
--- a/drivers/infiniband/hw/hfi1/iowait.h
+++ b/drivers/infiniband/hw/hfi1/iowait.h
@@ -100,6 +100,7 @@ struct iowait_work {
* @sleep: no space callback
* @wakeup: space callback wakeup
* @sdma_drained: sdma count drained
+ * @init_priority: callback to manipulate priority
* @lock: lock protected head of wait queue
* @iowork: workqueue overhead
* @wait_dma: wait for sdma_busy == 0
@@ -109,7 +110,7 @@ struct iowait_work {
* @tx_limit: limit for overflow queuing
* @tx_count: number of tx entry's in tx_head'ed list
* @flags: wait flags (one per QP)
- * @wait: SE array
+ * @wait: SE array for multiple legs
*
* This is to be embedded in user's state structure
* (QP or PQ).
@@ -120,10 +121,13 @@ struct iowait_work {
* are callbacks for the ULP to implement
* what ever queuing/dequeuing of
* the embedded iowait and its containing struct
- * when a resource shortage like SDMA ring space is seen.
+ * when a resource shortage like SDMA ring space
+ * or PIO credit space is seen.
*
* Both potentially have locks help
- * so sleeping is not allowed.
+ * so sleeping is not allowed and it is not
+ * supported to submit txreqs from the wakeup
+ * call directly because of lock conflicts.
*
* The wait_dma member along with the iow
*
@@ -143,6 +147,7 @@ struct iowait {
);
void (*wakeup)(struct iowait *wait, int reason);
void (*sdma_drained)(struct iowait *wait);
+ void (*init_priority)(struct iowait *wait);
seqlock_t *lock;
wait_queue_head_t wait_dma;
wait_queue_head_t wait_pio;
@@ -152,6 +157,7 @@ struct iowait {
u32 tx_limit;
u32 tx_count;
u8 starved_cnt;
+ u8 priority;
unsigned long flags;
struct iowait_work wait[IOWAIT_SES];
};
@@ -171,7 +177,8 @@ void iowait_init(struct iowait *wait, u32 tx_limit,
uint seq,
bool pkts_sent),
void (*wakeup)(struct iowait *wait, int reason),
- void (*sdma_drained)(struct iowait *wait));
+ void (*sdma_drained)(struct iowait *wait),
+ void (*init_priority)(struct iowait *wait));
/**
* iowait_schedule() - schedule the default send engine work
@@ -186,6 +193,18 @@ static inline bool iowait_schedule(struct iowait *wait,
}
/**
+ * iowait_tid_schedule - schedule the tid SE
+ * @wait: the iowait structure
+ * @wq: the work queue
+ * @cpu: the cpu
+ */
+static inline bool iowait_tid_schedule(struct iowait *wait,
+ struct workqueue_struct *wq, int cpu)
+{
+ return !!queue_work_on(cpu, wq, &wait->wait[IOWAIT_TID_SE].iowork);
+}
+
+/**
* iowait_sdma_drain() - wait for DMAs to drain
*
* @wait: iowait structure
@@ -327,6 +346,8 @@ static inline u16 iowait_get_desc(struct iowait_work *w)
tx = list_first_entry(&w->tx_head, struct sdma_txreq,
list);
num_desc = tx->num_desc;
+ if (tx->flags & SDMA_TXREQ_F_VIP)
+ w->iow->priority++;
}
return num_desc;
}
@@ -340,6 +361,37 @@ static inline u32 iowait_get_all_desc(struct iowait *w)
return num_desc;
}
+static inline void iowait_update_priority(struct iowait_work *w)
+{
+ struct sdma_txreq *tx = NULL;
+
+ if (!list_empty(&w->tx_head)) {
+ tx = list_first_entry(&w->tx_head, struct sdma_txreq,
+ list);
+ if (tx->flags & SDMA_TXREQ_F_VIP)
+ w->iow->priority++;
+ }
+}
+
+static inline void iowait_update_all_priority(struct iowait *w)
+{
+ iowait_update_priority(&w->wait[IOWAIT_IB_SE]);
+ iowait_update_priority(&w->wait[IOWAIT_TID_SE]);
+}
+
+static inline void iowait_init_priority(struct iowait *w)
+{
+ w->priority = 0;
+ if (w->init_priority)
+ w->init_priority(w);
+}
+
+static inline void iowait_get_priority(struct iowait *w)
+{
+ iowait_init_priority(w);
+ iowait_update_all_priority(w);
+}
+
/**
* iowait_queue - Put the iowait on a wait queue
* @pkts_sent: have some packets been sent before queuing?
@@ -356,14 +408,18 @@ static inline void iowait_queue(bool pkts_sent, struct iowait *w,
/*
* To play fair, insert the iowait at the tail of the wait queue if it
* has already sent some packets; Otherwise, put it at the head.
+ * However, if it has priority packets to send, also put it at the
+ * head.
*/
- if (pkts_sent) {
- list_add_tail(&w->list, wait_head);
+ if (pkts_sent)
w->starved_cnt = 0;
- } else {
- list_add(&w->list, wait_head);
+ else
w->starved_cnt++;
- }
+
+ if (w->priority > 0 || !pkts_sent)
+ list_add(&w->list, wait_head);
+ else
+ list_add_tail(&w->list, wait_head);
}
/**
@@ -380,27 +436,10 @@ static inline void iowait_starve_clear(bool pkts_sent, struct iowait *w)
w->starved_cnt = 0;
}
-/**
- * iowait_starve_find_max - Find the maximum of the starve count
- * @w: the iowait struct
- * @max: a variable containing the max starve count
- * @idx: the index of the current iowait in an array
- * @max_idx: a variable containing the array index for the
- * iowait entry that has the max starve count
- *
- * This function is called to compare the starve count of a
- * given iowait with the given max starve count. The max starve
- * count and the index will be updated if the iowait's start
- * count is larger.
- */
-static inline void iowait_starve_find_max(struct iowait *w, u8 *max,
- uint idx, uint *max_idx)
-{
- if (w->starved_cnt > *max) {
- *max = w->starved_cnt;
- *max_idx = idx;
- }
-}
+/* Update the top priority index */
+uint iowait_priority_update_top(struct iowait *w,
+ struct iowait *top,
+ uint idx, uint top_idx);
/**
* iowait_packet_queued() - determine if a packet is queued
diff --git a/drivers/infiniband/hw/hfi1/opfn.c b/drivers/infiniband/hw/hfi1/opfn.c
new file mode 100644
index 000000000000..370a5a8eaa71
--- /dev/null
+++ b/drivers/infiniband/hw/hfi1/opfn.c
@@ -0,0 +1,323 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+/*
+ * Copyright(c) 2018 Intel Corporation.
+ *
+ */
+#include "hfi.h"
+#include "trace.h"
+#include "qp.h"
+#include "opfn.h"
+
+#define IB_BTHE_E BIT(IB_BTHE_E_SHIFT)
+
+#define OPFN_CODE(code) BIT((code) - 1)
+#define OPFN_MASK(code) OPFN_CODE(STL_VERBS_EXTD_##code)
+
+struct hfi1_opfn_type {
+ bool (*request)(struct rvt_qp *qp, u64 *data);
+ bool (*response)(struct rvt_qp *qp, u64 *data);
+ bool (*reply)(struct rvt_qp *qp, u64 data);
+ void (*error)(struct rvt_qp *qp);
+};
+
+static struct hfi1_opfn_type hfi1_opfn_handlers[STL_VERBS_EXTD_MAX] = {
+ [STL_VERBS_EXTD_TID_RDMA] = {
+ .request = tid_rdma_conn_req,
+ .response = tid_rdma_conn_resp,
+ .reply = tid_rdma_conn_reply,
+ .error = tid_rdma_conn_error,
+ },
+};
+
+static struct workqueue_struct *opfn_wq;
+
+static void opfn_schedule_conn_request(struct rvt_qp *qp);
+
+static bool hfi1_opfn_extended(u32 bth1)
+{
+ return !!(bth1 & IB_BTHE_E);
+}
+
+static void opfn_conn_request(struct rvt_qp *qp)
+{
+ struct hfi1_qp_priv *priv = qp->priv;
+ struct ib_atomic_wr wr;
+ u16 mask, capcode;
+ struct hfi1_opfn_type *extd;
+ u64 data;
+ unsigned long flags;
+ int ret = 0;
+
+ trace_hfi1_opfn_state_conn_request(qp);
+ spin_lock_irqsave(&priv->opfn.lock, flags);
+ /*
+ * Exit if the extended bit is not set, or if nothing is requested, or
+ * if we have completed all requests, or if a previous request is in
+ * progress
+ */
+ if (!priv->opfn.extended || !priv->opfn.requested ||
+ priv->opfn.requested == priv->opfn.completed || priv->opfn.curr)
+ goto done;
+
+ mask = priv->opfn.requested & ~priv->opfn.completed;
+ capcode = ilog2(mask & ~(mask - 1)) + 1;
+ if (capcode >= STL_VERBS_EXTD_MAX) {
+ priv->opfn.completed |= OPFN_CODE(capcode);
+ goto done;
+ }
+
+ extd = &hfi1_opfn_handlers[capcode];
+ if (!extd || !extd->request || !extd->request(qp, &data)) {
+ /*
+ * Either there is no handler for this capability or the request
+ * packet could not be generated. Either way, mark it as done so
+ * we don't keep attempting to complete it.
+ */
+ priv->opfn.completed |= OPFN_CODE(capcode);
+ goto done;
+ }
+
+ trace_hfi1_opfn_data_conn_request(qp, capcode, data);
+ data = (data & ~0xf) | capcode;
+
+ memset(&wr, 0, sizeof(wr));
+ wr.wr.opcode = IB_WR_OPFN;
+ wr.remote_addr = HFI1_VERBS_E_ATOMIC_VADDR;
+ wr.compare_add = data;
+
+ priv->opfn.curr = capcode; /* A new request is now in progress */
+ /* Drop opfn.lock before calling ib_post_send() */
+ spin_unlock_irqrestore(&priv->opfn.lock, flags);
+
+ ret = ib_post_send(&qp->ibqp, &wr.wr, NULL);
+ if (ret)
+ goto err;
+ trace_hfi1_opfn_state_conn_request(qp);
+ return;
+err:
+ trace_hfi1_msg_opfn_conn_request(qp, "ib_ost_send failed: ret = ",
+ (u64)ret);
+ spin_lock_irqsave(&priv->opfn.lock, flags);
+ /*
+ * In case of an unexpected error return from ib_post_send
+ * clear opfn.curr and reschedule to try again
+ */
+ priv->opfn.curr = STL_VERBS_EXTD_NONE;
+ opfn_schedule_conn_request(qp);
+done:
+ spin_unlock_irqrestore(&priv->opfn.lock, flags);
+}
+
+void opfn_send_conn_request(struct work_struct *work)
+{
+ struct hfi1_opfn_data *od;
+ struct hfi1_qp_priv *qpriv;
+
+ od = container_of(work, struct hfi1_opfn_data, opfn_work);
+ qpriv = container_of(od, struct hfi1_qp_priv, opfn);
+
+ opfn_conn_request(qpriv->owner);
+}
+
+/*
+ * When QP s_lock is held in the caller, the OPFN request must be scheduled
+ * to a different workqueue to avoid double locking QP s_lock in call to
+ * ib_post_send in opfn_conn_request
+ */
+static void opfn_schedule_conn_request(struct rvt_qp *qp)
+{
+ struct hfi1_qp_priv *priv = qp->priv;
+
+ trace_hfi1_opfn_state_sched_conn_request(qp);
+ queue_work(opfn_wq, &priv->opfn.opfn_work);
+}
+
+void opfn_conn_response(struct rvt_qp *qp, struct rvt_ack_entry *e,
+ struct ib_atomic_eth *ateth)
+{
+ struct hfi1_qp_priv *priv = qp->priv;
+ u64 data = be64_to_cpu(ateth->compare_data);
+ struct hfi1_opfn_type *extd;
+ u8 capcode;
+ unsigned long flags;
+
+ trace_hfi1_opfn_state_conn_response(qp);
+ capcode = data & 0xf;
+ trace_hfi1_opfn_data_conn_response(qp, capcode, data);
+ if (!capcode || capcode >= STL_VERBS_EXTD_MAX)
+ return;
+
+ extd = &hfi1_opfn_handlers[capcode];
+
+ if (!extd || !extd->response) {
+ e->atomic_data = capcode;
+ return;
+ }
+
+ spin_lock_irqsave(&priv->opfn.lock, flags);
+ if (priv->opfn.completed & OPFN_CODE(capcode)) {
+ /*
+ * We are receiving a request for a feature that has already
+ * been negotiated. This may mean that the other side has reset
+ */
+ priv->opfn.completed &= ~OPFN_CODE(capcode);
+ if (extd->error)
+ extd->error(qp);
+ }
+
+ if (extd->response(qp, &data))
+ priv->opfn.completed |= OPFN_CODE(capcode);
+ e->atomic_data = (data & ~0xf) | capcode;
+ trace_hfi1_opfn_state_conn_response(qp);
+ spin_unlock_irqrestore(&priv->opfn.lock, flags);
+}
+
+void opfn_conn_reply(struct rvt_qp *qp, u64 data)
+{
+ struct hfi1_qp_priv *priv = qp->priv;
+ struct hfi1_opfn_type *extd;
+ u8 capcode;
+ unsigned long flags;
+
+ trace_hfi1_opfn_state_conn_reply(qp);
+ capcode = data & 0xf;
+ trace_hfi1_opfn_data_conn_reply(qp, capcode, data);
+ if (!capcode || capcode >= STL_VERBS_EXTD_MAX)
+ return;
+
+ spin_lock_irqsave(&priv->opfn.lock, flags);
+ /*
+ * Either there is no previous request or the reply is not for the
+ * current request
+ */
+ if (!priv->opfn.curr || capcode != priv->opfn.curr)
+ goto done;
+
+ extd = &hfi1_opfn_handlers[capcode];
+
+ if (!extd || !extd->reply)
+ goto clear;
+
+ if (extd->reply(qp, data))
+ priv->opfn.completed |= OPFN_CODE(capcode);
+clear:
+ /*
+ * Clear opfn.curr to indicate that the previous request is no longer in
+ * progress
+ */
+ priv->opfn.curr = STL_VERBS_EXTD_NONE;
+ trace_hfi1_opfn_state_conn_reply(qp);
+done:
+ spin_unlock_irqrestore(&priv->opfn.lock, flags);
+}
+
+void opfn_conn_error(struct rvt_qp *qp)
+{
+ struct hfi1_qp_priv *priv = qp->priv;
+ struct hfi1_opfn_type *extd = NULL;
+ unsigned long flags;
+ u16 capcode;
+
+ trace_hfi1_opfn_state_conn_error(qp);
+ trace_hfi1_msg_opfn_conn_error(qp, "error. qp state ", (u64)qp->state);
+ /*
+ * The QP has gone into the Error state. We have to invalidate all
+ * negotiated feature, including the one in progress (if any). The RC
+ * QP handling will clean the WQE for the connection request.
+ */
+ spin_lock_irqsave(&priv->opfn.lock, flags);
+ while (priv->opfn.completed) {
+ capcode = priv->opfn.completed & ~(priv->opfn.completed - 1);
+ extd = &hfi1_opfn_handlers[ilog2(capcode) + 1];
+ if (extd->error)
+ extd->error(qp);
+ priv->opfn.completed &= ~OPFN_CODE(capcode);
+ }
+ priv->opfn.extended = 0;
+ priv->opfn.requested = 0;
+ priv->opfn.curr = STL_VERBS_EXTD_NONE;
+ spin_unlock_irqrestore(&priv->opfn.lock, flags);
+}
+
+void opfn_qp_init(struct rvt_qp *qp, struct ib_qp_attr *attr, int attr_mask)
+{
+ struct ib_qp *ibqp = &qp->ibqp;
+ struct hfi1_qp_priv *priv = qp->priv;
+ unsigned long flags;
+
+ if (attr_mask & IB_QP_RETRY_CNT)
+ priv->s_retry = attr->retry_cnt;
+
+ spin_lock_irqsave(&priv->opfn.lock, flags);
+ if (ibqp->qp_type == IB_QPT_RC && HFI1_CAP_IS_KSET(TID_RDMA)) {
+ struct tid_rdma_params *local = &priv->tid_rdma.local;
+
+ if (attr_mask & IB_QP_TIMEOUT)
+ priv->tid_retry_timeout_jiffies = qp->timeout_jiffies;
+ if (qp->pmtu == enum_to_mtu(OPA_MTU_4096) ||
+ qp->pmtu == enum_to_mtu(OPA_MTU_8192)) {
+ tid_rdma_opfn_init(qp, local);
+ /*
+ * We only want to set the OPFN requested bit when the
+ * QP transitions to RTS.
+ */
+ if (attr_mask & IB_QP_STATE &&
+ attr->qp_state == IB_QPS_RTS) {
+ priv->opfn.requested |= OPFN_MASK(TID_RDMA);
+ /*
+ * If the QP is transitioning to RTS and the
+ * opfn.completed for TID RDMA has already been
+ * set, the QP is being moved *back* into RTS.
+ * We can now renegotiate the TID RDMA
+ * parameters.
+ */
+ if (priv->opfn.completed &
+ OPFN_MASK(TID_RDMA)) {
+ priv->opfn.completed &=
+ ~OPFN_MASK(TID_RDMA);
+ /*
+ * Since the opfn.completed bit was
+ * already set, it is safe to assume
+ * that the opfn.extended is also set.
+ */
+ opfn_schedule_conn_request(qp);
+ }
+ }
+ } else {
+ memset(local, 0, sizeof(*local));
+ }
+ }
+ spin_unlock_irqrestore(&priv->opfn.lock, flags);
+}
+
+void opfn_trigger_conn_request(struct rvt_qp *qp, u32 bth1)
+{
+ struct hfi1_qp_priv *priv = qp->priv;
+
+ if (!priv->opfn.extended && hfi1_opfn_extended(bth1) &&
+ HFI1_CAP_IS_KSET(OPFN)) {
+ priv->opfn.extended = 1;
+ if (qp->state == IB_QPS_RTS)
+ opfn_conn_request(qp);
+ }
+}
+
+int opfn_init(void)
+{
+ opfn_wq = alloc_workqueue("hfi_opfn",
+ WQ_SYSFS | WQ_HIGHPRI | WQ_CPU_INTENSIVE |
+ WQ_MEM_RECLAIM,
+ HFI1_MAX_ACTIVE_WORKQUEUE_ENTRIES);
+ if (!opfn_wq)
+ return -ENOMEM;
+
+ return 0;
+}
+
+void opfn_exit(void)
+{
+ if (opfn_wq) {
+ destroy_workqueue(opfn_wq);
+ opfn_wq = NULL;
+ }
+}
diff --git a/drivers/infiniband/hw/hfi1/opfn.h b/drivers/infiniband/hw/hfi1/opfn.h
new file mode 100644
index 000000000000..5f2011cabc25
--- /dev/null
+++ b/drivers/infiniband/hw/hfi1/opfn.h
@@ -0,0 +1,85 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+/*
+ * Copyright(c) 2018 Intel Corporation.
+ *
+ */
+#ifndef _HFI1_OPFN_H
+#define _HFI1_OPFN_H
+
+/**
+ * DOC: Omni Path Feature Negotion (OPFN)
+ *
+ * OPFN is a discovery protocol for Intel Omni-Path fabric that
+ * allows two RC QPs to negotiate a common feature that both QPs
+ * can support. Currently, the only OPA feature that OPFN
+ * supports is TID RDMA.
+ *
+ * Architecture
+ *
+ * OPFN involves the communication between two QPs on the HFI
+ * level on an Omni-Path fabric, and ULPs have no knowledge of
+ * OPFN at all.
+ *
+ * Implementation
+ *
+ * OPFN extends the existing IB RC protocol with the following
+ * changes:
+ * -- Uses Bit 24 (reserved) of DWORD 1 of Base Transport
+ * Header (BTH1) to indicate that the RC QP supports OPFN;
+ * -- Uses a combination of RC COMPARE_SWAP opcode (0x13) and
+ * the address U64_MAX (0xFFFFFFFFFFFFFFFF) as an OPFN
+ * request; The 64-bit data carried with the request/response
+ * contains the parameters for negotiation and will be
+ * defined in tid_rdma.c file;
+ * -- Defines IB_WR_RESERVED3 as IB_WR_OPFN.
+ *
+ * The OPFN communication will be triggered when an RC QP
+ * receives a request with Bit 24 of BTH1 set. The responder QP
+ * will then post send an OPFN request with its local
+ * parameters, which will be sent to the requester QP once all
+ * existing requests on the responder QP side have been sent.
+ * Once the requester QP receives the OPFN request, it will
+ * keep a copy of the responder QP's parameters, and return a
+ * response packet with its own local parameters. The responder
+ * QP receives the response packet and keeps a copy of the requester
+ * QP's parameters. After this exchange, each side has the parameters
+ * for both sides and therefore can select the right parameters
+ * for future transactions
+ */
+
+/* STL Verbs Extended */
+#define IB_BTHE_E_SHIFT 24
+#define HFI1_VERBS_E_ATOMIC_VADDR U64_MAX
+
+struct ib_atomic_eth;
+
+enum hfi1_opfn_codes {
+ STL_VERBS_EXTD_NONE = 0,
+ STL_VERBS_EXTD_TID_RDMA,
+ STL_VERBS_EXTD_MAX
+};
+
+struct hfi1_opfn_data {
+ u8 extended;
+ u16 requested;
+ u16 completed;
+ enum hfi1_opfn_codes curr;
+ /* serialize opfn function calls */
+ spinlock_t lock;
+ struct work_struct opfn_work;
+};
+
+/* WR opcode for OPFN */
+#define IB_WR_OPFN IB_WR_RESERVED3
+
+void opfn_send_conn_request(struct work_struct *work);
+void opfn_conn_response(struct rvt_qp *qp, struct rvt_ack_entry *e,
+ struct ib_atomic_eth *ateth);
+void opfn_conn_reply(struct rvt_qp *qp, u64 data);
+void opfn_conn_error(struct rvt_qp *qp);
+void opfn_qp_init(struct rvt_qp *qp, struct ib_qp_attr *attr, int attr_mask);
+void opfn_trigger_conn_request(struct rvt_qp *qp, u32 bth1);
+int opfn_init(void);
+void opfn_exit(void);
+
+#endif /* _HFI1_OPFN_H */
diff --git a/drivers/infiniband/hw/hfi1/pio.c b/drivers/infiniband/hw/hfi1/pio.c
index 04126d7e318d..a1de566fe95e 100644
--- a/drivers/infiniband/hw/hfi1/pio.c
+++ b/drivers/infiniband/hw/hfi1/pio.c
@@ -1599,8 +1599,7 @@ static void sc_piobufavail(struct send_context *sc)
struct rvt_qp *qp;
struct hfi1_qp_priv *priv;
unsigned long flags;
- uint i, n = 0, max_idx = 0;
- u8 max_starved_cnt = 0;
+ uint i, n = 0, top_idx = 0;
if (dd->send_contexts[sc->sw_index].type != SC_KERNEL &&
dd->send_contexts[sc->sw_index].type != SC_VL15)
@@ -1619,11 +1618,18 @@ static void sc_piobufavail(struct send_context *sc)
if (n == ARRAY_SIZE(qps))
break;
wait = list_first_entry(list, struct iowait, list);
+ iowait_get_priority(wait);
qp = iowait_to_qp(wait);
priv = qp->priv;
list_del_init(&priv->s_iowait.list);
priv->s_iowait.lock = NULL;
- iowait_starve_find_max(wait, &max_starved_cnt, n, &max_idx);
+ if (n) {
+ priv = qps[top_idx]->priv;
+ top_idx = iowait_priority_update_top(wait,
+ &priv->s_iowait,
+ n, top_idx);
+ }
+
/* refcount held until actual wake up */
qps[n++] = qp;
}
@@ -1638,12 +1644,12 @@ static void sc_piobufavail(struct send_context *sc)
}
write_sequnlock_irqrestore(&sc->waitlock, flags);
- /* Wake up the most starved one first */
+ /* Wake up the top-priority one first */
if (n)
- hfi1_qp_wakeup(qps[max_idx],
+ hfi1_qp_wakeup(qps[top_idx],
RVT_S_WAIT_PIO | HFI1_S_WAIT_PIO_DRAIN);
for (i = 0; i < n; i++)
- if (i != max_idx)
+ if (i != top_idx)
hfi1_qp_wakeup(qps[i],
RVT_S_WAIT_PIO | HFI1_S_WAIT_PIO_DRAIN);
}
diff --git a/drivers/infiniband/hw/hfi1/qp.c b/drivers/infiniband/hw/hfi1/qp.c
index 5344e8993b28..9b643c2409cf 100644
--- a/drivers/infiniband/hw/hfi1/qp.c
+++ b/drivers/infiniband/hw/hfi1/qp.c
@@ -132,6 +132,18 @@ const struct rvt_operation_params hfi1_post_parms[RVT_OPERATION_MAX] = {
.qpt_support = BIT(IB_QPT_RC),
},
+[IB_WR_OPFN] = {
+ .length = sizeof(struct ib_atomic_wr),
+ .qpt_support = BIT(IB_QPT_RC),
+ .flags = RVT_OPERATION_USE_RESERVE,
+},
+
+[IB_WR_TID_RDMA_WRITE] = {
+ .length = sizeof(struct ib_rdma_wr),
+ .qpt_support = BIT(IB_QPT_RC),
+ .flags = RVT_OPERATION_IGN_RNR_CNT,
+},
+
};
static void flush_list_head(struct list_head *l)
@@ -285,6 +297,8 @@ void hfi1_modify_qp(struct rvt_qp *qp, struct ib_qp_attr *attr,
priv->s_sendcontext = qp_to_send_context(qp, priv->s_sc);
qp_set_16b(qp);
}
+
+ opfn_qp_init(qp, attr, attr_mask);
}
/**
@@ -311,6 +325,8 @@ int hfi1_setup_wqe(struct rvt_qp *qp, struct rvt_swqe *wqe, bool *call_send)
switch (qp->ibqp.qp_type) {
case IB_QPT_RC:
+ hfi1_setup_tid_rdma_wqe(qp, wqe);
+ /* fall through */
case IB_QPT_UC:
if (wqe->length > 0x80000000U)
return -EINVAL;
@@ -422,6 +438,11 @@ static void hfi1_qp_schedule(struct rvt_qp *qp)
if (ret)
iowait_clear_flag(&priv->s_iowait, IOWAIT_PENDING_IB);
}
+ if (iowait_flag_set(&priv->s_iowait, IOWAIT_PENDING_TID)) {
+ ret = hfi1_schedule_tid_send(qp);
+ if (ret)
+ iowait_clear_flag(&priv->s_iowait, IOWAIT_PENDING_TID);
+ }
}
void hfi1_qp_wakeup(struct rvt_qp *qp, u32 flag)
@@ -441,8 +462,27 @@ void hfi1_qp_wakeup(struct rvt_qp *qp, u32 flag)
void hfi1_qp_unbusy(struct rvt_qp *qp, struct iowait_work *wait)
{
- if (iowait_set_work_flag(wait) == IOWAIT_IB_SE)
+ struct hfi1_qp_priv *priv = qp->priv;
+
+ if (iowait_set_work_flag(wait) == IOWAIT_IB_SE) {
qp->s_flags &= ~RVT_S_BUSY;
+ /*
+ * If we are sending a first-leg packet from the second leg,
+ * we need to clear the busy flag from priv->s_flags to
+ * avoid a race condition when the qp wakes up before
+ * the call to hfi1_verbs_send() returns to the second
+ * leg. In that case, the second leg will terminate without
+ * being re-scheduled, resulting in failure to send TID RDMA
+ * WRITE DATA and TID RDMA ACK packets.
+ */
+ if (priv->s_flags & HFI1_S_TID_BUSY_SET) {
+ priv->s_flags &= ~(HFI1_S_TID_BUSY_SET |
+ RVT_S_BUSY);
+ iowait_set_flag(&priv->s_iowait, IOWAIT_PENDING_TID);
+ }
+ } else {
+ priv->s_flags &= ~RVT_S_BUSY;
+ }
}
static int iowait_sleep(
@@ -479,6 +519,7 @@ static int iowait_sleep(
ibp->rvp.n_dmawait++;
qp->s_flags |= RVT_S_WAIT_DMA_DESC;
+ iowait_get_priority(&priv->s_iowait);
iowait_queue(pkts_sent, &priv->s_iowait,
&sde->dmawait);
priv->s_iowait.lock = &sde->waitlock;
@@ -528,6 +569,17 @@ static void iowait_sdma_drained(struct iowait *wait)
spin_unlock_irqrestore(&qp->s_lock, flags);
}
+static void hfi1_init_priority(struct iowait *w)
+{
+ struct rvt_qp *qp = iowait_to_qp(w);
+ struct hfi1_qp_priv *priv = qp->priv;
+
+ if (qp->s_flags & RVT_S_ACK_PENDING)
+ w->priority++;
+ if (priv->s_flags & RVT_S_ACK_PENDING)
+ w->priority++;
+}
+
/**
* qp_to_sdma_engine - map a qp to a send engine
* @qp: the QP
@@ -685,10 +737,11 @@ void *qp_priv_alloc(struct rvt_dev_info *rdi, struct rvt_qp *qp)
&priv->s_iowait,
1,
_hfi1_do_send,
- NULL,
+ _hfi1_do_tid_send,
iowait_sleep,
iowait_wakeup,
- iowait_sdma_drained);
+ iowait_sdma_drained,
+ hfi1_init_priority);
return priv;
}
@@ -696,6 +749,7 @@ void qp_priv_free(struct rvt_dev_info *rdi, struct rvt_qp *qp)
{
struct hfi1_qp_priv *priv = qp->priv;
+ hfi1_qp_priv_tid_free(rdi, qp);
kfree(priv->s_ahg);
kfree(priv);
}
@@ -729,6 +783,7 @@ void flush_qp_waiters(struct rvt_qp *qp)
{
lockdep_assert_held(&qp->s_lock);
flush_iowait(qp);
+ hfi1_tid_rdma_flush_wait(qp);
}
void stop_send_queue(struct rvt_qp *qp)
@@ -736,12 +791,16 @@ void stop_send_queue(struct rvt_qp *qp)
struct hfi1_qp_priv *priv = qp->priv;
iowait_cancel_work(&priv->s_iowait);
+ if (cancel_work_sync(&priv->tid_rdma.trigger_work))
+ rvt_put_qp(qp);
}
void quiesce_qp(struct rvt_qp *qp)
{
struct hfi1_qp_priv *priv = qp->priv;
+ hfi1_del_tid_reap_timer(qp);
+ hfi1_del_tid_retry_timer(qp);
iowait_sdma_drain(&priv->s_iowait);
qp_pio_drain(qp);
flush_tx_list(qp);
@@ -749,8 +808,13 @@ void quiesce_qp(struct rvt_qp *qp)
void notify_qp_reset(struct rvt_qp *qp)
{
+ hfi1_qp_kern_exp_rcv_clear_all(qp);
qp->r_adefered = 0;
clear_ahg(qp);
+
+ /* Clear any OPFN state */
+ if (qp->ibqp.qp_type == IB_QPT_RC)
+ opfn_conn_error(qp);
}
/*
@@ -832,7 +896,8 @@ void notify_error_qp(struct rvt_qp *qp)
if (lock) {
write_seqlock(lock);
if (!list_empty(&priv->s_iowait.list) &&
- !(qp->s_flags & RVT_S_BUSY)) {
+ !(qp->s_flags & RVT_S_BUSY) &&
+ !(priv->s_flags & RVT_S_BUSY)) {
qp->s_flags &= ~RVT_S_ANY_WAIT_IO;
list_del_init(&priv->s_iowait.list);
priv->s_iowait.lock = NULL;
@@ -841,7 +906,8 @@ void notify_error_qp(struct rvt_qp *qp)
write_sequnlock(lock);
}
- if (!(qp->s_flags & RVT_S_BUSY)) {
+ if (!(qp->s_flags & RVT_S_BUSY) && !(priv->s_flags & RVT_S_BUSY)) {
+ qp->s_hdrwords = 0;
if (qp->s_rdma_mr) {
rvt_put_mr(qp->s_rdma_mr);
qp->s_rdma_mr = NULL;
diff --git a/drivers/infiniband/hw/hfi1/qp.h b/drivers/infiniband/hw/hfi1/qp.h
index 7adb6dff6813..b670321365d3 100644
--- a/drivers/infiniband/hw/hfi1/qp.h
+++ b/drivers/infiniband/hw/hfi1/qp.h
@@ -63,11 +63,17 @@ extern const struct rvt_operation_params hfi1_post_parms[];
* HFI1_S_AHG_VALID - ahg header valid on chip
* HFI1_S_AHG_CLEAR - have send engine clear ahg state
* HFI1_S_WAIT_PIO_DRAIN - qp waiting for PIOs to drain
+ * HFI1_S_WAIT_TID_SPACE - a QP is waiting for TID resource
+ * HFI1_S_WAIT_TID_RESP - waiting for a TID RDMA WRITE response
+ * HFI1_S_WAIT_HALT - halt the first leg send engine
* HFI1_S_MIN_BIT_MASK - the lowest bit that can be used by hfi1
*/
#define HFI1_S_AHG_VALID 0x80000000
#define HFI1_S_AHG_CLEAR 0x40000000
#define HFI1_S_WAIT_PIO_DRAIN 0x20000000
+#define HFI1_S_WAIT_TID_SPACE 0x10000000
+#define HFI1_S_WAIT_TID_RESP 0x08000000
+#define HFI1_S_WAIT_HALT 0x04000000
#define HFI1_S_MIN_BIT_MASK 0x01000000
/*
@@ -76,6 +82,7 @@ extern const struct rvt_operation_params hfi1_post_parms[];
#define HFI1_S_ANY_WAIT_IO (RVT_S_ANY_WAIT_IO | HFI1_S_WAIT_PIO_DRAIN)
#define HFI1_S_ANY_WAIT (HFI1_S_ANY_WAIT_IO | RVT_S_ANY_WAIT_SEND)
+#define HFI1_S_ANY_TID_WAIT_SEND (RVT_S_WAIT_SSN_CREDIT | RVT_S_WAIT_DMA)
/*
* Send if not busy or waiting for I/O and either
diff --git a/drivers/infiniband/hw/hfi1/rc.c b/drivers/infiniband/hw/hfi1/rc.c
index be603f35d7e4..e6726c1ab866 100644
--- a/drivers/infiniband/hw/hfi1/rc.c
+++ b/drivers/infiniband/hw/hfi1/rc.c
@@ -51,24 +51,48 @@
#include "hfi.h"
#include "qp.h"
+#include "rc.h"
#include "verbs_txreq.h"
#include "trace.h"
-/* cut down ridiculously long IB macro names */
-#define OP(x) RC_OP(x)
-
-static u32 restart_sge(struct rvt_sge_state *ss, struct rvt_swqe *wqe,
- u32 psn, u32 pmtu)
+struct rvt_ack_entry *find_prev_entry(struct rvt_qp *qp, u32 psn, u8 *prev,
+ u8 *prev_ack, bool *scheduled)
+ __must_hold(&qp->s_lock)
{
- u32 len;
-
- len = delta_psn(psn, wqe->psn) * pmtu;
- ss->sge = wqe->sg_list[0];
- ss->sg_list = wqe->sg_list + 1;
- ss->num_sge = wqe->wr.num_sge;
- ss->total_len = wqe->length;
- rvt_skip_sge(ss, len, false);
- return wqe->length - len;
+ struct rvt_ack_entry *e = NULL;
+ u8 i, p;
+ bool s = true;
+
+ for (i = qp->r_head_ack_queue; ; i = p) {
+ if (i == qp->s_tail_ack_queue)
+ s = false;
+ if (i)
+ p = i - 1;
+ else
+ p = rvt_size_atomic(ib_to_rvt(qp->ibqp.device));
+ if (p == qp->r_head_ack_queue) {
+ e = NULL;
+ break;
+ }
+ e = &qp->s_ack_queue[p];
+ if (!e->opcode) {
+ e = NULL;
+ break;
+ }
+ if (cmp_psn(psn, e->psn) >= 0) {
+ if (p == qp->s_tail_ack_queue &&
+ cmp_psn(psn, e->lpsn) <= 0)
+ s = false;
+ break;
+ }
+ }
+ if (prev)
+ *prev = p;
+ if (prev_ack)
+ *prev_ack = i;
+ if (scheduled)
+ *scheduled = s;
+ return e;
}
/**
@@ -87,20 +111,25 @@ static int make_rc_ack(struct hfi1_ibdev *dev, struct rvt_qp *qp,
struct hfi1_pkt_state *ps)
{
struct rvt_ack_entry *e;
- u32 hwords;
- u32 len;
- u32 bth0;
- u32 bth2;
+ u32 hwords, hdrlen;
+ u32 len = 0;
+ u32 bth0 = 0, bth2 = 0;
+ u32 bth1 = qp->remote_qpn | (HFI1_CAP_IS_KSET(OPFN) << IB_BTHE_E_SHIFT);
int middle = 0;
u32 pmtu = qp->pmtu;
- struct hfi1_qp_priv *priv = qp->priv;
+ struct hfi1_qp_priv *qpriv = qp->priv;
+ bool last_pkt;
+ u32 delta;
+ u8 next = qp->s_tail_ack_queue;
+ struct tid_rdma_request *req;
+ trace_hfi1_rsp_make_rc_ack(qp, 0);
lockdep_assert_held(&qp->s_lock);
/* Don't send an ACK if we aren't supposed to. */
if (!(ib_rvt_state_ops[qp->state] & RVT_PROCESS_RECV_OK))
goto bail;
- if (priv->hdr_type == HFI1_PKT_TYPE_9B)
+ if (qpriv->hdr_type == HFI1_PKT_TYPE_9B)
/* header size in 32-bit words LRH+BTH = (8+12)/4. */
hwords = 5;
else
@@ -122,8 +151,18 @@ static int make_rc_ack(struct hfi1_ibdev *dev, struct rvt_qp *qp,
* response has been sent instead of only being
* constructed.
*/
- if (++qp->s_tail_ack_queue > HFI1_MAX_RDMA_ATOMIC)
- qp->s_tail_ack_queue = 0;
+ if (++next > rvt_size_atomic(&dev->rdi))
+ next = 0;
+ /*
+ * Only advance the s_acked_ack_queue pointer if there
+ * have been no TID RDMA requests.
+ */
+ e = &qp->s_ack_queue[qp->s_tail_ack_queue];
+ if (e->opcode != TID_OP(WRITE_REQ) &&
+ qp->s_acked_ack_queue == qp->s_tail_ack_queue)
+ qp->s_acked_ack_queue = next;
+ qp->s_tail_ack_queue = next;
+ trace_hfi1_rsp_make_rc_ack(qp, e->psn);
/* FALLTHROUGH */
case OP(SEND_ONLY):
case OP(ACKNOWLEDGE):
@@ -135,6 +174,12 @@ static int make_rc_ack(struct hfi1_ibdev *dev, struct rvt_qp *qp,
}
e = &qp->s_ack_queue[qp->s_tail_ack_queue];
+ /* Check for tid write fence */
+ if ((qpriv->s_flags & HFI1_R_TID_WAIT_INTERLCK) ||
+ hfi1_tid_rdma_ack_interlock(qp, e)) {
+ iowait_set_flag(&qpriv->s_iowait, IOWAIT_PENDING_IB);
+ goto bail;
+ }
if (e->opcode == OP(RDMA_READ_REQUEST)) {
/*
* If a RDMA read response is being resent and
@@ -144,6 +189,10 @@ static int make_rc_ack(struct hfi1_ibdev *dev, struct rvt_qp *qp,
*/
len = e->rdma_sge.sge_length;
if (len && !e->rdma_sge.mr) {
+ if (qp->s_acked_ack_queue ==
+ qp->s_tail_ack_queue)
+ qp->s_acked_ack_queue =
+ qp->r_head_ack_queue;
qp->s_tail_ack_queue = qp->r_head_ack_queue;
goto bail;
}
@@ -165,6 +214,45 @@ static int make_rc_ack(struct hfi1_ibdev *dev, struct rvt_qp *qp,
hwords++;
qp->s_ack_rdma_psn = e->psn;
bth2 = mask_psn(qp->s_ack_rdma_psn++);
+ } else if (e->opcode == TID_OP(WRITE_REQ)) {
+ /*
+ * If a TID RDMA WRITE RESP is being resent, we have to
+ * wait for the actual request. All requests that are to
+ * be resent will have their state set to
+ * TID_REQUEST_RESEND. When the new request arrives, the
+ * state will be changed to TID_REQUEST_RESEND_ACTIVE.
+ */
+ req = ack_to_tid_req(e);
+ if (req->state == TID_REQUEST_RESEND ||
+ req->state == TID_REQUEST_INIT_RESEND)
+ goto bail;
+ qp->s_ack_state = TID_OP(WRITE_RESP);
+ qp->s_ack_rdma_psn = mask_psn(e->psn + req->cur_seg);
+ goto write_resp;
+ } else if (e->opcode == TID_OP(READ_REQ)) {
+ /*
+ * If a TID RDMA read response is being resent and
+ * we haven't seen the duplicate request yet,
+ * then stop sending the remaining responses the
+ * responder has seen until the requester re-sends it.
+ */
+ len = e->rdma_sge.sge_length;
+ if (len && !e->rdma_sge.mr) {
+ if (qp->s_acked_ack_queue ==
+ qp->s_tail_ack_queue)
+ qp->s_acked_ack_queue =
+ qp->r_head_ack_queue;
+ qp->s_tail_ack_queue = qp->r_head_ack_queue;
+ goto bail;
+ }
+ /* Copy SGE state in case we need to resend */
+ ps->s_txreq->mr = e->rdma_sge.mr;
+ if (ps->s_txreq->mr)
+ rvt_get_mr(ps->s_txreq->mr);
+ qp->s_ack_rdma_sge.sge = e->rdma_sge;
+ qp->s_ack_rdma_sge.num_sge = 1;
+ qp->s_ack_state = TID_OP(READ_RESP);
+ goto read_resp;
} else {
/* COMPARE_SWAP or FETCH_ADD */
ps->s_txreq->ss = NULL;
@@ -176,6 +264,7 @@ static int make_rc_ack(struct hfi1_ibdev *dev, struct rvt_qp *qp,
bth2 = mask_psn(e->psn);
e->sent = 1;
}
+ trace_hfi1_tid_write_rsp_make_rc_ack(qp);
bth0 = qp->s_ack_state << 24;
break;
@@ -202,6 +291,83 @@ static int make_rc_ack(struct hfi1_ibdev *dev, struct rvt_qp *qp,
bth2 = mask_psn(qp->s_ack_rdma_psn++);
break;
+ case TID_OP(WRITE_RESP):
+write_resp:
+ /*
+ * 1. Check if RVT_S_ACK_PENDING is set. If yes,
+ * goto normal.
+ * 2. Attempt to allocate TID resources.
+ * 3. Remove RVT_S_RESP_PENDING flags from s_flags
+ * 4. If resources not available:
+ * 4.1 Set RVT_S_WAIT_TID_SPACE
+ * 4.2 Queue QP on RCD TID queue
+ * 4.3 Put QP on iowait list.
+ * 4.4 Build IB RNR NAK with appropriate timeout value
+ * 4.5 Return indication progress made.
+ * 5. If resources are available:
+ * 5.1 Program HW flow CSRs
+ * 5.2 Build TID RDMA WRITE RESP packet
+ * 5.3 If more resources needed, do 2.1 - 2.3.
+ * 5.4 Wake up next QP on RCD TID queue.
+ * 5.5 Return indication progress made.
+ */
+
+ e = &qp->s_ack_queue[qp->s_tail_ack_queue];
+ req = ack_to_tid_req(e);
+
+ /*
+ * Send scheduled RNR NAK's. RNR NAK's need to be sent at
+ * segment boundaries, not at request boundaries. Don't change
+ * s_ack_state because we are still in the middle of a request
+ */
+ if (qpriv->rnr_nak_state == TID_RNR_NAK_SEND &&
+ qp->s_tail_ack_queue == qpriv->r_tid_alloc &&
+ req->cur_seg == req->alloc_seg) {
+ qpriv->rnr_nak_state = TID_RNR_NAK_SENT;
+ goto normal_no_state;
+ }
+
+ bth2 = mask_psn(qp->s_ack_rdma_psn);
+ hdrlen = hfi1_build_tid_rdma_write_resp(qp, e, ohdr, &bth1,
+ bth2, &len,
+ &ps->s_txreq->ss);
+ if (!hdrlen)
+ return 0;
+
+ hwords += hdrlen;
+ bth0 = qp->s_ack_state << 24;
+ qp->s_ack_rdma_psn++;
+ trace_hfi1_tid_req_make_rc_ack_write(qp, 0, e->opcode, e->psn,
+ e->lpsn, req);
+ if (req->cur_seg != req->total_segs)
+ break;
+
+ e->sent = 1;
+ qp->s_ack_state = OP(RDMA_READ_RESPONSE_LAST);
+ break;
+
+ case TID_OP(READ_RESP):
+read_resp:
+ e = &qp->s_ack_queue[qp->s_tail_ack_queue];
+ ps->s_txreq->ss = &qp->s_ack_rdma_sge;
+ delta = hfi1_build_tid_rdma_read_resp(qp, e, ohdr, &bth0,
+ &bth1, &bth2, &len,
+ &last_pkt);
+ if (delta == 0)
+ goto error_qp;
+ hwords += delta;
+ if (last_pkt) {
+ e->sent = 1;
+ /*
+ * Increment qp->s_tail_ack_queue through s_ack_state
+ * transition.
+ */
+ qp->s_ack_state = OP(RDMA_READ_RESPONSE_LAST);
+ }
+ break;
+ case TID_OP(READ_REQ):
+ goto bail;
+
default:
normal:
/*
@@ -211,8 +377,7 @@ normal:
* (see above).
*/
qp->s_ack_state = OP(SEND_ONLY);
- qp->s_flags &= ~RVT_S_ACK_PENDING;
- ps->s_txreq->ss = NULL;
+normal_no_state:
if (qp->s_nak_state)
ohdr->u.aeth =
cpu_to_be32((qp->r_msn & IB_MSN_MASK) |
@@ -224,14 +389,24 @@ normal:
len = 0;
bth0 = OP(ACKNOWLEDGE) << 24;
bth2 = mask_psn(qp->s_ack_psn);
+ qp->s_flags &= ~RVT_S_ACK_PENDING;
+ ps->s_txreq->txreq.flags |= SDMA_TXREQ_F_VIP;
+ ps->s_txreq->ss = NULL;
}
qp->s_rdma_ack_cnt++;
- ps->s_txreq->sde = priv->s_sde;
+ ps->s_txreq->sde = qpriv->s_sde;
ps->s_txreq->s_cur_size = len;
ps->s_txreq->hdr_dwords = hwords;
- hfi1_make_ruc_header(qp, ohdr, bth0, bth2, middle, ps);
+ hfi1_make_ruc_header(qp, ohdr, bth0, bth1, bth2, middle, ps);
return 1;
-
+error_qp:
+ spin_unlock_irqrestore(&qp->s_lock, ps->flags);
+ spin_lock_irqsave(&qp->r_lock, ps->flags);
+ spin_lock(&qp->s_lock);
+ rvt_error_qp(qp, IB_WC_WR_FLUSH_ERR);
+ spin_unlock(&qp->s_lock);
+ spin_unlock_irqrestore(&qp->r_lock, ps->flags);
+ spin_lock_irqsave(&qp->s_lock, ps->flags);
bail:
qp->s_ack_state = OP(ACKNOWLEDGE);
/*
@@ -258,17 +433,23 @@ int hfi1_make_rc_req(struct rvt_qp *qp, struct hfi1_pkt_state *ps)
struct hfi1_qp_priv *priv = qp->priv;
struct hfi1_ibdev *dev = to_idev(qp->ibqp.device);
struct ib_other_headers *ohdr;
- struct rvt_sge_state *ss;
+ struct rvt_sge_state *ss = NULL;
struct rvt_swqe *wqe;
- u32 hwords;
- u32 len;
- u32 bth0 = 0;
- u32 bth2;
+ struct hfi1_swqe_priv *wpriv;
+ struct tid_rdma_request *req = NULL;
+ /* header size in 32-bit words LRH+BTH = (8+12)/4. */
+ u32 hwords = 5;
+ u32 len = 0;
+ u32 bth0 = 0, bth2 = 0;
+ u32 bth1 = qp->remote_qpn | (HFI1_CAP_IS_KSET(OPFN) << IB_BTHE_E_SHIFT);
u32 pmtu = qp->pmtu;
char newreq;
int middle = 0;
int delta;
+ struct tid_rdma_flow *flow = NULL;
+ struct tid_rdma_params *remote;
+ trace_hfi1_sender_make_rc_req(qp);
lockdep_assert_held(&qp->s_lock);
ps->s_txreq = get_txreq(ps->dev, qp);
if (!ps->s_txreq)
@@ -309,13 +490,13 @@ int hfi1_make_rc_req(struct rvt_qp *qp, struct hfi1_pkt_state *ps)
}
clear_ahg(qp);
wqe = rvt_get_swqe_ptr(qp, qp->s_last);
- rvt_send_complete(qp, wqe, qp->s_last != qp->s_acked ?
- IB_WC_SUCCESS : IB_WC_WR_FLUSH_ERR);
+ hfi1_trdma_send_complete(qp, wqe, qp->s_last != qp->s_acked ?
+ IB_WC_SUCCESS : IB_WC_WR_FLUSH_ERR);
/* will get called again */
goto done_free_tx;
}
- if (qp->s_flags & (RVT_S_WAIT_RNR | RVT_S_WAIT_ACK))
+ if (qp->s_flags & (RVT_S_WAIT_RNR | RVT_S_WAIT_ACK | HFI1_S_WAIT_HALT))
goto bail;
if (cmp_psn(qp->s_psn, qp->s_sending_hpsn) <= 0) {
@@ -329,6 +510,7 @@ int hfi1_make_rc_req(struct rvt_qp *qp, struct hfi1_pkt_state *ps)
/* Send a request. */
wqe = rvt_get_swqe_ptr(qp, qp->s_cur);
+check_s_state:
switch (qp->s_state) {
default:
if (!(ib_rvt_state_ops[qp->state] & RVT_PROCESS_NEXT_SEND_OK))
@@ -350,9 +532,13 @@ int hfi1_make_rc_req(struct rvt_qp *qp, struct hfi1_pkt_state *ps)
/*
* If a fence is requested, wait for previous
* RDMA read and atomic operations to finish.
+ * However, there is no need to guard against
+ * TID RDMA READ after TID RDMA READ.
*/
if ((wqe->wr.send_flags & IB_SEND_FENCE) &&
- qp->s_num_rd_atomic) {
+ qp->s_num_rd_atomic &&
+ (wqe->wr.opcode != IB_WR_TID_RDMA_READ ||
+ priv->pending_tid_r_segs < qp->s_num_rd_atomic)) {
qp->s_flags |= RVT_S_WAIT_FENCE;
goto bail;
}
@@ -397,6 +583,15 @@ int hfi1_make_rc_req(struct rvt_qp *qp, struct hfi1_pkt_state *ps)
len = wqe->length;
ss = &qp->s_sge;
bth2 = mask_psn(qp->s_psn);
+
+ /*
+ * Interlock between various IB requests and TID RDMA
+ * if necessary.
+ */
+ if ((priv->s_flags & HFI1_S_TID_WAIT_INTERLCK) ||
+ hfi1_tid_rdma_wqe_interlock(qp, wqe))
+ goto bail;
+
switch (wqe->wr.opcode) {
case IB_WR_SEND:
case IB_WR_SEND_WITH_IMM:
@@ -473,21 +668,126 @@ no_flow_control:
qp->s_cur = 0;
break;
+ case IB_WR_TID_RDMA_WRITE:
+ if (newreq) {
+ /*
+ * Limit the number of TID RDMA WRITE requests.
+ */
+ if (atomic_read(&priv->n_tid_requests) >=
+ HFI1_TID_RDMA_WRITE_CNT)
+ goto bail;
+
+ if (!(qp->s_flags & RVT_S_UNLIMITED_CREDIT))
+ qp->s_lsn++;
+ }
+
+ hwords += hfi1_build_tid_rdma_write_req(qp, wqe, ohdr,
+ &bth1, &bth2,
+ &len);
+ ss = NULL;
+ if (priv->s_tid_cur == HFI1_QP_WQE_INVALID) {
+ priv->s_tid_cur = qp->s_cur;
+ if (priv->s_tid_tail == HFI1_QP_WQE_INVALID) {
+ priv->s_tid_tail = qp->s_cur;
+ priv->s_state = TID_OP(WRITE_RESP);
+ }
+ } else if (priv->s_tid_cur == priv->s_tid_head) {
+ struct rvt_swqe *__w;
+ struct tid_rdma_request *__r;
+
+ __w = rvt_get_swqe_ptr(qp, priv->s_tid_cur);
+ __r = wqe_to_tid_req(__w);
+
+ /*
+ * The s_tid_cur pointer is advanced to s_cur if
+ * any of the following conditions about the WQE
+ * to which s_ti_cur currently points to are
+ * satisfied:
+ * 1. The request is not a TID RDMA WRITE
+ * request,
+ * 2. The request is in the INACTIVE or
+ * COMPLETE states (TID RDMA READ requests
+ * stay at INACTIVE and TID RDMA WRITE
+ * transition to COMPLETE when done),
+ * 3. The request is in the ACTIVE or SYNC
+ * state and the number of completed
+ * segments is equal to the total segment
+ * count.
+ * (If ACTIVE, the request is waiting for
+ * ACKs. If SYNC, the request has not
+ * received any responses because it's
+ * waiting on a sync point.)
+ */
+ if (__w->wr.opcode != IB_WR_TID_RDMA_WRITE ||
+ __r->state == TID_REQUEST_INACTIVE ||
+ __r->state == TID_REQUEST_COMPLETE ||
+ ((__r->state == TID_REQUEST_ACTIVE ||
+ __r->state == TID_REQUEST_SYNC) &&
+ __r->comp_seg == __r->total_segs)) {
+ if (priv->s_tid_tail ==
+ priv->s_tid_cur &&
+ priv->s_state ==
+ TID_OP(WRITE_DATA_LAST)) {
+ priv->s_tid_tail = qp->s_cur;
+ priv->s_state =
+ TID_OP(WRITE_RESP);
+ }
+ priv->s_tid_cur = qp->s_cur;
+ }
+ /*
+ * A corner case: when the last TID RDMA WRITE
+ * request was completed, s_tid_head,
+ * s_tid_cur, and s_tid_tail all point to the
+ * same location. Other requests are posted and
+ * s_cur wraps around to the same location,
+ * where a new TID RDMA WRITE is posted. In
+ * this case, none of the indices need to be
+ * updated. However, the priv->s_state should.
+ */
+ if (priv->s_tid_tail == qp->s_cur &&
+ priv->s_state == TID_OP(WRITE_DATA_LAST))
+ priv->s_state = TID_OP(WRITE_RESP);
+ }
+ req = wqe_to_tid_req(wqe);
+ if (newreq) {
+ priv->s_tid_head = qp->s_cur;
+ priv->pending_tid_w_resp += req->total_segs;
+ atomic_inc(&priv->n_tid_requests);
+ atomic_dec(&priv->n_requests);
+ } else {
+ req->state = TID_REQUEST_RESEND;
+ req->comp_seg = delta_psn(bth2, wqe->psn);
+ /*
+ * Pull back any segments since we are going
+ * to re-receive them.
+ */
+ req->setup_head = req->clear_tail;
+ priv->pending_tid_w_resp +=
+ delta_psn(wqe->lpsn, bth2) + 1;
+ }
+
+ trace_hfi1_tid_write_sender_make_req(qp, newreq);
+ trace_hfi1_tid_req_make_req_write(qp, newreq,
+ wqe->wr.opcode,
+ wqe->psn, wqe->lpsn,
+ req);
+ if (++qp->s_cur == qp->s_size)
+ qp->s_cur = 0;
+ break;
+
case IB_WR_RDMA_READ:
/*
* Don't allow more operations to be started
* than the QP limits allow.
*/
- if (newreq) {
- if (qp->s_num_rd_atomic >=
- qp->s_max_rd_atomic) {
- qp->s_flags |= RVT_S_WAIT_RDMAR;
- goto bail;
- }
- qp->s_num_rd_atomic++;
- if (!(qp->s_flags & RVT_S_UNLIMITED_CREDIT))
- qp->s_lsn++;
+ if (qp->s_num_rd_atomic >=
+ qp->s_max_rd_atomic) {
+ qp->s_flags |= RVT_S_WAIT_RDMAR;
+ goto bail;
}
+ qp->s_num_rd_atomic++;
+ if (newreq && !(qp->s_flags & RVT_S_UNLIMITED_CREDIT))
+ qp->s_lsn++;
put_ib_reth_vaddr(
wqe->rdma_wr.remote_addr,
&ohdr->u.rc.reth);
@@ -503,23 +803,99 @@ no_flow_control:
qp->s_cur = 0;
break;
+ case IB_WR_TID_RDMA_READ:
+ trace_hfi1_tid_read_sender_make_req(qp, newreq);
+ wpriv = wqe->priv;
+ req = wqe_to_tid_req(wqe);
+ trace_hfi1_tid_req_make_req_read(qp, newreq,
+ wqe->wr.opcode,
+ wqe->psn, wqe->lpsn,
+ req);
+ delta = cmp_psn(qp->s_psn, wqe->psn);
+
+ /*
+ * Don't allow more operations to be started
+ * than the QP limits allow. We could get here under
+ * three conditions; (1) It's a new request; (2) We are
+ * sending the second or later segment of a request,
+ * but the qp->s_state is set to OP(RDMA_READ_REQUEST)
+ * when the last segment of a previous request is
+ * received just before this; (3) We are re-sending a
+ * request.
+ */
+ if (qp->s_num_rd_atomic >= qp->s_max_rd_atomic) {
+ qp->s_flags |= RVT_S_WAIT_RDMAR;
+ goto bail;
+ }
+ if (newreq) {
+ struct tid_rdma_flow *flow =
+ &req->flows[req->setup_head];
+
+ /*
+ * Set up s_sge as it is needed for TID
+ * allocation. However, if the pages have been
+ * walked and mapped, skip it. An earlier try
+ * has failed to allocate the TID entries.
+ */
+ if (!flow->npagesets) {
+ qp->s_sge.sge = wqe->sg_list[0];
+ qp->s_sge.sg_list = wqe->sg_list + 1;
+ qp->s_sge.num_sge = wqe->wr.num_sge;
+ qp->s_sge.total_len = wqe->length;
+ qp->s_len = wqe->length;
+ req->isge = 0;
+ req->clear_tail = req->setup_head;
+ req->flow_idx = req->setup_head;
+ req->state = TID_REQUEST_ACTIVE;
+ }
+ } else if (delta == 0) {
+ /* Re-send a request */
+ req->cur_seg = 0;
+ req->comp_seg = 0;
+ req->ack_pending = 0;
+ req->flow_idx = req->clear_tail;
+ req->state = TID_REQUEST_RESEND;
+ }
+ req->s_next_psn = qp->s_psn;
+ /* Read one segment at a time */
+ len = min_t(u32, req->seg_len,
+ wqe->length - req->seg_len * req->cur_seg);
+ delta = hfi1_build_tid_rdma_read_req(qp, wqe, ohdr,
+ &bth1, &bth2,
+ &len);
+ if (delta <= 0) {
+ /* Wait for TID space */
+ goto bail;
+ }
+ if (newreq && !(qp->s_flags & RVT_S_UNLIMITED_CREDIT))
+ qp->s_lsn++;
+ hwords += delta;
+ ss = &wpriv->ss;
+ /* Check if this is the last segment */
+ if (req->cur_seg >= req->total_segs &&
+ ++qp->s_cur == qp->s_size)
+ qp->s_cur = 0;
+ break;
+
case IB_WR_ATOMIC_CMP_AND_SWP:
case IB_WR_ATOMIC_FETCH_AND_ADD:
/*
* Don't allow more operations to be started
* than the QP limits allow.
*/
- if (newreq) {
- if (qp->s_num_rd_atomic >=
- qp->s_max_rd_atomic) {
- qp->s_flags |= RVT_S_WAIT_RDMAR;
- goto bail;
- }
- qp->s_num_rd_atomic++;
- if (!(qp->s_flags & RVT_S_UNLIMITED_CREDIT))
- qp->s_lsn++;
+ if (qp->s_num_rd_atomic >=
+ qp->s_max_rd_atomic) {
+ qp->s_flags |= RVT_S_WAIT_RDMAR;
+ goto bail;
}
- if (wqe->wr.opcode == IB_WR_ATOMIC_CMP_AND_SWP) {
+ qp->s_num_rd_atomic++;
+
+ /* FALLTHROUGH */
+ case IB_WR_OPFN:
+ if (newreq && !(qp->s_flags & RVT_S_UNLIMITED_CREDIT))
+ qp->s_lsn++;
+ if (wqe->wr.opcode == IB_WR_ATOMIC_CMP_AND_SWP ||
+ wqe->wr.opcode == IB_WR_OPFN) {
qp->s_state = OP(COMPARE_SWAP);
put_ib_ateth_swap(wqe->atomic_wr.swap,
&ohdr->u.atomic_eth);
@@ -546,18 +922,23 @@ no_flow_control:
default:
goto bail;
}
- qp->s_sge.sge = wqe->sg_list[0];
- qp->s_sge.sg_list = wqe->sg_list + 1;
- qp->s_sge.num_sge = wqe->wr.num_sge;
- qp->s_sge.total_len = wqe->length;
- qp->s_len = wqe->length;
+ if (wqe->wr.opcode != IB_WR_TID_RDMA_READ) {
+ qp->s_sge.sge = wqe->sg_list[0];
+ qp->s_sge.sg_list = wqe->sg_list + 1;
+ qp->s_sge.num_sge = wqe->wr.num_sge;
+ qp->s_sge.total_len = wqe->length;
+ qp->s_len = wqe->length;
+ }
if (newreq) {
qp->s_tail++;
if (qp->s_tail >= qp->s_size)
qp->s_tail = 0;
}
- if (wqe->wr.opcode == IB_WR_RDMA_READ)
+ if (wqe->wr.opcode == IB_WR_RDMA_READ ||
+ wqe->wr.opcode == IB_WR_TID_RDMA_WRITE)
qp->s_psn = wqe->lpsn + 1;
+ else if (wqe->wr.opcode == IB_WR_TID_RDMA_READ)
+ qp->s_psn = req->s_next_psn;
else
qp->s_psn++;
break;
@@ -674,10 +1055,137 @@ no_flow_control:
if (qp->s_cur == qp->s_size)
qp->s_cur = 0;
break;
+
+ case TID_OP(WRITE_RESP):
+ /*
+ * This value for s_state is used for restarting a TID RDMA
+ * WRITE request. See comment in OP(RDMA_READ_RESPONSE_MIDDLE
+ * for more).
+ */
+ req = wqe_to_tid_req(wqe);
+ req->state = TID_REQUEST_RESEND;
+ rcu_read_lock();
+ remote = rcu_dereference(priv->tid_rdma.remote);
+ req->comp_seg = delta_psn(qp->s_psn, wqe->psn);
+ len = wqe->length - (req->comp_seg * remote->max_len);
+ rcu_read_unlock();
+
+ bth2 = mask_psn(qp->s_psn);
+ hwords += hfi1_build_tid_rdma_write_req(qp, wqe, ohdr, &bth1,
+ &bth2, &len);
+ qp->s_psn = wqe->lpsn + 1;
+ ss = NULL;
+ qp->s_state = TID_OP(WRITE_REQ);
+ priv->pending_tid_w_resp += delta_psn(wqe->lpsn, bth2) + 1;
+ priv->s_tid_cur = qp->s_cur;
+ if (++qp->s_cur == qp->s_size)
+ qp->s_cur = 0;
+ trace_hfi1_tid_req_make_req_write(qp, 0, wqe->wr.opcode,
+ wqe->psn, wqe->lpsn, req);
+ break;
+
+ case TID_OP(READ_RESP):
+ if (wqe->wr.opcode != IB_WR_TID_RDMA_READ)
+ goto bail;
+ /* This is used to restart a TID read request */
+ req = wqe_to_tid_req(wqe);
+ wpriv = wqe->priv;
+ /*
+ * Back down. The field qp->s_psn has been set to the psn with
+ * which the request should be restart. It's OK to use division
+ * as this is on the retry path.
+ */
+ req->cur_seg = delta_psn(qp->s_psn, wqe->psn) / priv->pkts_ps;
+
+ /*
+ * The following function need to be redefined to return the
+ * status to make sure that we find the flow. At the same
+ * time, we can use the req->state change to check if the
+ * call succeeds or not.
+ */
+ req->state = TID_REQUEST_RESEND;
+ hfi1_tid_rdma_restart_req(qp, wqe, &bth2);
+ if (req->state != TID_REQUEST_ACTIVE) {
+ /*
+ * Failed to find the flow. Release all allocated tid
+ * resources.
+ */
+ hfi1_kern_exp_rcv_clear_all(req);
+ hfi1_kern_clear_hw_flow(priv->rcd, qp);
+
+ hfi1_trdma_send_complete(qp, wqe, IB_WC_LOC_QP_OP_ERR);
+ goto bail;
+ }
+ req->state = TID_REQUEST_RESEND;
+ len = min_t(u32, req->seg_len,
+ wqe->length - req->seg_len * req->cur_seg);
+ flow = &req->flows[req->flow_idx];
+ len -= flow->sent;
+ req->s_next_psn = flow->flow_state.ib_lpsn + 1;
+ delta = hfi1_build_tid_rdma_read_packet(wqe, ohdr, &bth1,
+ &bth2, &len);
+ if (delta <= 0) {
+ /* Wait for TID space */
+ goto bail;
+ }
+ hwords += delta;
+ ss = &wpriv->ss;
+ /* Check if this is the last segment */
+ if (req->cur_seg >= req->total_segs &&
+ ++qp->s_cur == qp->s_size)
+ qp->s_cur = 0;
+ qp->s_psn = req->s_next_psn;
+ trace_hfi1_tid_req_make_req_read(qp, 0, wqe->wr.opcode,
+ wqe->psn, wqe->lpsn, req);
+ break;
+ case TID_OP(READ_REQ):
+ req = wqe_to_tid_req(wqe);
+ delta = cmp_psn(qp->s_psn, wqe->psn);
+ /*
+ * If the current WR is not TID RDMA READ, or this is the start
+ * of a new request, we need to change the qp->s_state so that
+ * the request can be set up properly.
+ */
+ if (wqe->wr.opcode != IB_WR_TID_RDMA_READ || delta == 0 ||
+ qp->s_cur == qp->s_tail) {
+ qp->s_state = OP(RDMA_READ_REQUEST);
+ if (delta == 0 || qp->s_cur == qp->s_tail)
+ goto check_s_state;
+ else
+ goto bail;
+ }
+
+ /* Rate limiting */
+ if (qp->s_num_rd_atomic >= qp->s_max_rd_atomic) {
+ qp->s_flags |= RVT_S_WAIT_RDMAR;
+ goto bail;
+ }
+
+ wpriv = wqe->priv;
+ /* Read one segment at a time */
+ len = min_t(u32, req->seg_len,
+ wqe->length - req->seg_len * req->cur_seg);
+ delta = hfi1_build_tid_rdma_read_req(qp, wqe, ohdr, &bth1,
+ &bth2, &len);
+ if (delta <= 0) {
+ /* Wait for TID space */
+ goto bail;
+ }
+ hwords += delta;
+ ss = &wpriv->ss;
+ /* Check if this is the last segment */
+ if (req->cur_seg >= req->total_segs &&
+ ++qp->s_cur == qp->s_size)
+ qp->s_cur = 0;
+ qp->s_psn = req->s_next_psn;
+ trace_hfi1_tid_req_make_req_read(qp, 0, wqe->wr.opcode,
+ wqe->psn, wqe->lpsn, req);
+ break;
}
qp->s_sending_hpsn = bth2;
delta = delta_psn(bth2, wqe->psn);
- if (delta && delta % HFI1_PSN_CREDIT == 0)
+ if (delta && delta % HFI1_PSN_CREDIT == 0 &&
+ wqe->wr.opcode != IB_WR_TID_RDMA_WRITE)
bth2 |= IB_BTH_REQ_ACK;
if (qp->s_flags & RVT_S_SEND_ONE) {
qp->s_flags &= ~RVT_S_SEND_ONE;
@@ -693,6 +1201,7 @@ no_flow_control:
qp,
ohdr,
bth0 | (qp->s_state << 24),
+ bth1,
bth2,
middle,
ps);
@@ -709,6 +1218,12 @@ bail:
bail_no_tx:
ps->s_txreq = NULL;
qp->s_flags &= ~RVT_S_BUSY;
+ /*
+ * If we didn't get a txreq, the QP will be woken up later to try
+ * again. Set the flags to indicate which work item to wake
+ * up.
+ */
+ iowait_set_flag(&priv->s_iowait, IOWAIT_PENDING_IB);
return 0;
}
@@ -796,6 +1311,11 @@ static inline void hfi1_make_rc_ack_9B(struct hfi1_packet *packet,
if (qp->s_mig_state == IB_MIG_MIGRATED)
bth0 |= IB_BTH_MIG_REQ;
bth1 = (!!is_fecn) << IB_BECN_SHIFT;
+ /*
+ * Inline ACKs go out without the use of the Verbs send engine, so
+ * we need to set the STL Verbs Extended bit here
+ */
+ bth1 |= HFI1_CAP_IS_KSET(OPFN) << IB_BTHE_E_SHIFT;
hfi1_make_bth_aeth(qp, ohdr, bth0, bth1);
}
@@ -936,6 +1456,43 @@ void hfi1_send_rc_ack(struct hfi1_packet *packet, bool is_fecn)
}
/**
+ * update_num_rd_atomic - update the qp->s_num_rd_atomic
+ * @qp: the QP
+ * @psn: the packet sequence number to restart at
+ * @wqe: the wqe
+ *
+ * This is called from reset_psn() to update qp->s_num_rd_atomic
+ * for the current wqe.
+ * Called at interrupt level with the QP s_lock held.
+ */
+static void update_num_rd_atomic(struct rvt_qp *qp, u32 psn,
+ struct rvt_swqe *wqe)
+{
+ u32 opcode = wqe->wr.opcode;
+
+ if (opcode == IB_WR_RDMA_READ ||
+ opcode == IB_WR_ATOMIC_CMP_AND_SWP ||
+ opcode == IB_WR_ATOMIC_FETCH_AND_ADD) {
+ qp->s_num_rd_atomic++;
+ } else if (opcode == IB_WR_TID_RDMA_READ) {
+ struct tid_rdma_request *req = wqe_to_tid_req(wqe);
+ struct hfi1_qp_priv *priv = qp->priv;
+
+ if (cmp_psn(psn, wqe->lpsn) <= 0) {
+ u32 cur_seg;
+
+ cur_seg = (psn - wqe->psn) / priv->pkts_ps;
+ req->ack_pending = cur_seg - req->comp_seg;
+ priv->pending_tid_r_segs += req->ack_pending;
+ qp->s_num_rd_atomic += req->ack_pending;
+ } else {
+ priv->pending_tid_r_segs += req->total_segs;
+ qp->s_num_rd_atomic += req->total_segs;
+ }
+ }
+}
+
+/**
* reset_psn - reset the QP state to send starting from PSN
* @qp: the QP
* @psn: the packet sequence number to restart at
@@ -949,9 +1506,13 @@ static void reset_psn(struct rvt_qp *qp, u32 psn)
u32 n = qp->s_acked;
struct rvt_swqe *wqe = rvt_get_swqe_ptr(qp, n);
u32 opcode;
+ struct hfi1_qp_priv *priv = qp->priv;
lockdep_assert_held(&qp->s_lock);
qp->s_cur = n;
+ priv->pending_tid_r_segs = 0;
+ priv->pending_tid_w_resp = 0;
+ qp->s_num_rd_atomic = 0;
/*
* If we are starting the request from the beginning,
@@ -961,9 +1522,9 @@ static void reset_psn(struct rvt_qp *qp, u32 psn)
qp->s_state = OP(SEND_LAST);
goto done;
}
+ update_num_rd_atomic(qp, psn, wqe);
/* Find the work request opcode corresponding to the given PSN. */
- opcode = wqe->wr.opcode;
for (;;) {
int diff;
@@ -973,8 +1534,11 @@ static void reset_psn(struct rvt_qp *qp, u32 psn)
break;
wqe = rvt_get_swqe_ptr(qp, n);
diff = cmp_psn(psn, wqe->psn);
- if (diff < 0)
+ if (diff < 0) {
+ /* Point wqe back to the previous one*/
+ wqe = rvt_get_swqe_ptr(qp, qp->s_cur);
break;
+ }
qp->s_cur = n;
/*
* If we are starting the request from the beginning,
@@ -984,8 +1548,10 @@ static void reset_psn(struct rvt_qp *qp, u32 psn)
qp->s_state = OP(SEND_LAST);
goto done;
}
- opcode = wqe->wr.opcode;
+
+ update_num_rd_atomic(qp, psn, wqe);
}
+ opcode = wqe->wr.opcode;
/*
* Set the state to restart in the middle of a request.
@@ -1003,10 +1569,18 @@ static void reset_psn(struct rvt_qp *qp, u32 psn)
qp->s_state = OP(RDMA_READ_RESPONSE_LAST);
break;
+ case IB_WR_TID_RDMA_WRITE:
+ qp->s_state = TID_OP(WRITE_RESP);
+ break;
+
case IB_WR_RDMA_READ:
qp->s_state = OP(RDMA_READ_RESPONSE_MIDDLE);
break;
+ case IB_WR_TID_RDMA_READ:
+ qp->s_state = TID_OP(READ_RESP);
+ break;
+
default:
/*
* This case shouldn't happen since its only
@@ -1015,6 +1589,7 @@ static void reset_psn(struct rvt_qp *qp, u32 psn)
qp->s_state = OP(SEND_LAST);
}
done:
+ priv->s_flags &= ~HFI1_S_TID_WAIT_INTERLCK;
qp->s_psn = psn;
/*
* Set RVT_S_WAIT_PSN as rc_complete() may start the timer
@@ -1025,6 +1600,7 @@ done:
(cmp_psn(qp->s_sending_psn, qp->s_sending_hpsn) <= 0))
qp->s_flags |= RVT_S_WAIT_PSN;
qp->s_flags &= ~HFI1_S_AHG_VALID;
+ trace_hfi1_sender_reset_psn(qp);
}
/*
@@ -1033,18 +1609,47 @@ done:
*/
void hfi1_restart_rc(struct rvt_qp *qp, u32 psn, int wait)
{
+ struct hfi1_qp_priv *priv = qp->priv;
struct rvt_swqe *wqe = rvt_get_swqe_ptr(qp, qp->s_acked);
struct hfi1_ibport *ibp;
lockdep_assert_held(&qp->r_lock);
lockdep_assert_held(&qp->s_lock);
+ trace_hfi1_sender_restart_rc(qp);
if (qp->s_retry == 0) {
if (qp->s_mig_state == IB_MIG_ARMED) {
hfi1_migrate_qp(qp);
qp->s_retry = qp->s_retry_cnt;
} else if (qp->s_last == qp->s_acked) {
- rvt_send_complete(qp, wqe, IB_WC_RETRY_EXC_ERR);
- rvt_error_qp(qp, IB_WC_WR_FLUSH_ERR);
+ /*
+ * We need special handling for the OPFN request WQEs as
+ * they are not allowed to generate real user errors
+ */
+ if (wqe->wr.opcode == IB_WR_OPFN) {
+ struct hfi1_ibport *ibp =
+ to_iport(qp->ibqp.device, qp->port_num);
+ /*
+ * Call opfn_conn_reply() with capcode and
+ * remaining data as 0 to close out the
+ * current request
+ */
+ opfn_conn_reply(qp, priv->opfn.curr);
+ wqe = do_rc_completion(qp, wqe, ibp);
+ qp->s_flags &= ~RVT_S_WAIT_ACK;
+ } else {
+ trace_hfi1_tid_write_sender_restart_rc(qp, 0);
+ if (wqe->wr.opcode == IB_WR_TID_RDMA_READ) {
+ struct tid_rdma_request *req;
+
+ req = wqe_to_tid_req(wqe);
+ hfi1_kern_exp_rcv_clear_all(req);
+ hfi1_kern_clear_hw_flow(priv->rcd, qp);
+ }
+
+ hfi1_trdma_send_complete(qp, wqe,
+ IB_WC_RETRY_EXC_ERR);
+ rvt_error_qp(qp, IB_WC_WR_FLUSH_ERR);
+ }
return;
} else { /* need to handle delayed completion */
return;
@@ -1054,14 +1659,15 @@ void hfi1_restart_rc(struct rvt_qp *qp, u32 psn, int wait)
}
ibp = to_iport(qp->ibqp.device, qp->port_num);
- if (wqe->wr.opcode == IB_WR_RDMA_READ)
+ if (wqe->wr.opcode == IB_WR_RDMA_READ ||
+ wqe->wr.opcode == IB_WR_TID_RDMA_READ)
ibp->rvp.n_rc_resends++;
else
ibp->rvp.n_rc_resends += delta_psn(qp->s_psn, psn);
qp->s_flags &= ~(RVT_S_WAIT_FENCE | RVT_S_WAIT_RDMAR |
RVT_S_WAIT_SSN_CREDIT | RVT_S_WAIT_PSN |
- RVT_S_WAIT_ACK);
+ RVT_S_WAIT_ACK | HFI1_S_WAIT_TID_RESP);
if (wait)
qp->s_flags |= RVT_S_SEND_ONE;
reset_psn(qp, psn);
@@ -1069,7 +1675,8 @@ void hfi1_restart_rc(struct rvt_qp *qp, u32 psn, int wait)
/*
* Set qp->s_sending_psn to the next PSN after the given one.
- * This would be psn+1 except when RDMA reads are present.
+ * This would be psn+1 except when RDMA reads or TID RDMA ops
+ * are present.
*/
static void reset_sending_psn(struct rvt_qp *qp, u32 psn)
{
@@ -1081,7 +1688,9 @@ static void reset_sending_psn(struct rvt_qp *qp, u32 psn)
for (;;) {
wqe = rvt_get_swqe_ptr(qp, n);
if (cmp_psn(psn, wqe->lpsn) <= 0) {
- if (wqe->wr.opcode == IB_WR_RDMA_READ)
+ if (wqe->wr.opcode == IB_WR_RDMA_READ ||
+ wqe->wr.opcode == IB_WR_TID_RDMA_READ ||
+ wqe->wr.opcode == IB_WR_TID_RDMA_WRITE)
qp->s_sending_psn = wqe->lpsn + 1;
else
qp->s_sending_psn = psn + 1;
@@ -1104,8 +1713,9 @@ void hfi1_rc_send_complete(struct rvt_qp *qp, struct hfi1_opa_header *opah)
struct rvt_swqe *wqe;
struct ib_header *hdr = NULL;
struct hfi1_16b_header *hdr_16b = NULL;
- u32 opcode;
+ u32 opcode, head, tail;
u32 psn;
+ struct tid_rdma_request *req;
lockdep_assert_held(&qp->s_lock);
if (!(ib_rvt_state_ops[qp->state] & RVT_SEND_OR_FLUSH_OR_RECV_OK))
@@ -1130,25 +1740,85 @@ void hfi1_rc_send_complete(struct rvt_qp *qp, struct hfi1_opa_header *opah)
}
opcode = ib_bth_get_opcode(ohdr);
- if (opcode >= OP(RDMA_READ_RESPONSE_FIRST) &&
- opcode <= OP(ATOMIC_ACKNOWLEDGE)) {
+ if ((opcode >= OP(RDMA_READ_RESPONSE_FIRST) &&
+ opcode <= OP(ATOMIC_ACKNOWLEDGE)) ||
+ opcode == TID_OP(READ_RESP) ||
+ opcode == TID_OP(WRITE_RESP)) {
WARN_ON(!qp->s_rdma_ack_cnt);
qp->s_rdma_ack_cnt--;
return;
}
psn = ib_bth_get_psn(ohdr);
- reset_sending_psn(qp, psn);
+ /*
+ * Don't attempt to reset the sending PSN for packets in the
+ * KDETH PSN space since the PSN does not match anything.
+ */
+ if (opcode != TID_OP(WRITE_DATA) &&
+ opcode != TID_OP(WRITE_DATA_LAST) &&
+ opcode != TID_OP(ACK) && opcode != TID_OP(RESYNC))
+ reset_sending_psn(qp, psn);
+
+ /* Handle TID RDMA WRITE packets differently */
+ if (opcode >= TID_OP(WRITE_REQ) &&
+ opcode <= TID_OP(WRITE_DATA_LAST)) {
+ head = priv->s_tid_head;
+ tail = priv->s_tid_cur;
+ /*
+ * s_tid_cur is set to s_tid_head in the case, where
+ * a new TID RDMA request is being started and all
+ * previous ones have been completed.
+ * Therefore, we need to do a secondary check in order
+ * to properly determine whether we should start the
+ * RC timer.
+ */
+ wqe = rvt_get_swqe_ptr(qp, tail);
+ req = wqe_to_tid_req(wqe);
+ if (head == tail && req->comp_seg < req->total_segs) {
+ if (tail == 0)
+ tail = qp->s_size - 1;
+ else
+ tail -= 1;
+ }
+ } else {
+ head = qp->s_tail;
+ tail = qp->s_acked;
+ }
/*
* Start timer after a packet requesting an ACK has been sent and
* there are still requests that haven't been acked.
*/
- if ((psn & IB_BTH_REQ_ACK) && qp->s_acked != qp->s_tail &&
+ if ((psn & IB_BTH_REQ_ACK) && tail != head &&
+ opcode != TID_OP(WRITE_DATA) && opcode != TID_OP(WRITE_DATA_LAST) &&
+ opcode != TID_OP(RESYNC) &&
!(qp->s_flags &
- (RVT_S_TIMER | RVT_S_WAIT_RNR | RVT_S_WAIT_PSN)) &&
- (ib_rvt_state_ops[qp->state] & RVT_PROCESS_RECV_OK))
- rvt_add_retry_timer(qp);
+ (RVT_S_TIMER | RVT_S_WAIT_RNR | RVT_S_WAIT_PSN)) &&
+ (ib_rvt_state_ops[qp->state] & RVT_PROCESS_RECV_OK)) {
+ if (opcode == TID_OP(READ_REQ))
+ rvt_add_retry_timer_ext(qp, priv->timeout_shift);
+ else
+ rvt_add_retry_timer(qp);
+ }
+
+ /* Start TID RDMA ACK timer */
+ if ((opcode == TID_OP(WRITE_DATA) ||
+ opcode == TID_OP(WRITE_DATA_LAST) ||
+ opcode == TID_OP(RESYNC)) &&
+ (psn & IB_BTH_REQ_ACK) &&
+ !(priv->s_flags & HFI1_S_TID_RETRY_TIMER) &&
+ (ib_rvt_state_ops[qp->state] & RVT_PROCESS_RECV_OK)) {
+ /*
+ * The TID RDMA ACK packet could be received before this
+ * function is called. Therefore, add the timer only if TID
+ * RDMA ACK packets are actually pending.
+ */
+ wqe = rvt_get_swqe_ptr(qp, qp->s_acked);
+ req = wqe_to_tid_req(wqe);
+ if (wqe->wr.opcode == IB_WR_TID_RDMA_WRITE &&
+ req->ack_seg < req->cur_seg)
+ hfi1_add_tid_retry_timer(qp);
+ }
while (qp->s_last != qp->s_acked) {
u32 s_last;
@@ -1157,6 +1827,7 @@ void hfi1_rc_send_complete(struct rvt_qp *qp, struct hfi1_opa_header *opah)
if (cmp_psn(wqe->lpsn, qp->s_sending_psn) >= 0 &&
cmp_psn(qp->s_sending_psn, qp->s_sending_hpsn) <= 0)
break;
+ trdma_clean_swqe(qp, wqe);
rvt_qp_wqe_unreserve(qp, wqe);
s_last = qp->s_last;
trace_hfi1_qp_send_completion(qp, wqe, s_last);
@@ -1195,20 +1866,24 @@ static inline void update_last_psn(struct rvt_qp *qp, u32 psn)
* This is similar to hfi1_send_complete but has to check to be sure
* that the SGEs are not being referenced if the SWQE is being resent.
*/
-static struct rvt_swqe *do_rc_completion(struct rvt_qp *qp,
- struct rvt_swqe *wqe,
- struct hfi1_ibport *ibp)
+struct rvt_swqe *do_rc_completion(struct rvt_qp *qp,
+ struct rvt_swqe *wqe,
+ struct hfi1_ibport *ibp)
{
+ struct hfi1_qp_priv *priv = qp->priv;
+
lockdep_assert_held(&qp->s_lock);
/*
* Don't decrement refcount and don't generate a
* completion if the SWQE is being resent until the send
* is finished.
*/
+ trace_hfi1_rc_completion(qp, wqe->lpsn);
if (cmp_psn(wqe->lpsn, qp->s_sending_psn) < 0 ||
cmp_psn(qp->s_sending_psn, qp->s_sending_hpsn) > 0) {
u32 s_last;
+ trdma_clean_swqe(qp, wqe);
rvt_put_swqe(wqe);
rvt_qp_wqe_unreserve(qp, wqe);
s_last = qp->s_last;
@@ -1243,7 +1918,16 @@ static struct rvt_swqe *do_rc_completion(struct rvt_qp *qp,
}
qp->s_retry = qp->s_retry_cnt;
- update_last_psn(qp, wqe->lpsn);
+ /*
+ * Don't update the last PSN if the request being completed is
+ * a TID RDMA WRITE request.
+ * Completion of the TID RDMA WRITE requests are done by the
+ * TID RDMA ACKs and as such could be for a request that has
+ * already been ACKed as far as the IB state machine is
+ * concerned.
+ */
+ if (wqe->wr.opcode != IB_WR_TID_RDMA_WRITE)
+ update_last_psn(qp, wqe->lpsn);
/*
* If we are completing a request which is in the process of
@@ -1266,9 +1950,61 @@ static struct rvt_swqe *do_rc_completion(struct rvt_qp *qp,
qp->s_draining = 0;
wqe = rvt_get_swqe_ptr(qp, qp->s_acked);
}
+ if (priv->s_flags & HFI1_S_TID_WAIT_INTERLCK) {
+ priv->s_flags &= ~HFI1_S_TID_WAIT_INTERLCK;
+ hfi1_schedule_send(qp);
+ }
return wqe;
}
+static void set_restart_qp(struct rvt_qp *qp, struct hfi1_ctxtdata *rcd)
+{
+ /* Retry this request. */
+ if (!(qp->r_flags & RVT_R_RDMAR_SEQ)) {
+ qp->r_flags |= RVT_R_RDMAR_SEQ;
+ hfi1_restart_rc(qp, qp->s_last_psn + 1, 0);
+ if (list_empty(&qp->rspwait)) {
+ qp->r_flags |= RVT_R_RSP_SEND;
+ rvt_get_qp(qp);
+ list_add_tail(&qp->rspwait, &rcd->qp_wait_list);
+ }
+ }
+}
+
+/**
+ * update_qp_retry_state - Update qp retry state.
+ * @qp: the QP
+ * @psn: the packet sequence number of the TID RDMA WRITE RESP.
+ * @spsn: The start psn for the given TID RDMA WRITE swqe.
+ * @lpsn: The last psn for the given TID RDMA WRITE swqe.
+ *
+ * This function is called to update the qp retry state upon
+ * receiving a TID WRITE RESP after the qp is scheduled to retry
+ * a request.
+ */
+static void update_qp_retry_state(struct rvt_qp *qp, u32 psn, u32 spsn,
+ u32 lpsn)
+{
+ struct hfi1_qp_priv *qpriv = qp->priv;
+
+ qp->s_psn = psn + 1;
+ /*
+ * If this is the first TID RDMA WRITE RESP packet for the current
+ * request, change the s_state so that the retry will be processed
+ * correctly. Similarly, if this is the last TID RDMA WRITE RESP
+ * packet, change the s_state and advance the s_cur.
+ */
+ if (cmp_psn(psn, lpsn) >= 0) {
+ qp->s_cur = qpriv->s_tid_cur + 1;
+ if (qp->s_cur >= qp->s_size)
+ qp->s_cur = 0;
+ qp->s_state = TID_OP(WRITE_REQ);
+ } else if (!cmp_psn(psn, spsn)) {
+ qp->s_cur = qpriv->s_tid_cur;
+ qp->s_state = TID_OP(WRITE_RESP);
+ }
+}
+
/**
* do_rc_ack - process an incoming RC ACK
* @qp: the QP the ACK came in on
@@ -1280,15 +2016,17 @@ static struct rvt_swqe *do_rc_completion(struct rvt_qp *qp,
* May be called at interrupt level, with the QP s_lock held.
* Returns 1 if OK, 0 if current operation should be aborted (NAK).
*/
-static int do_rc_ack(struct rvt_qp *qp, u32 aeth, u32 psn, int opcode,
- u64 val, struct hfi1_ctxtdata *rcd)
+int do_rc_ack(struct rvt_qp *qp, u32 aeth, u32 psn, int opcode,
+ u64 val, struct hfi1_ctxtdata *rcd)
{
struct hfi1_ibport *ibp;
enum ib_wc_status status;
+ struct hfi1_qp_priv *qpriv = qp->priv;
struct rvt_swqe *wqe;
int ret = 0;
u32 ack_psn;
int diff;
+ struct rvt_dev_info *rdi;
lockdep_assert_held(&qp->s_lock);
/*
@@ -1331,20 +2069,14 @@ static int do_rc_ack(struct rvt_qp *qp, u32 aeth, u32 psn, int opcode,
*/
if ((wqe->wr.opcode == IB_WR_RDMA_READ &&
(opcode != OP(RDMA_READ_RESPONSE_LAST) || diff != 0)) ||
+ (wqe->wr.opcode == IB_WR_TID_RDMA_READ &&
+ (opcode != TID_OP(READ_RESP) || diff != 0)) ||
((wqe->wr.opcode == IB_WR_ATOMIC_CMP_AND_SWP ||
wqe->wr.opcode == IB_WR_ATOMIC_FETCH_AND_ADD) &&
- (opcode != OP(ATOMIC_ACKNOWLEDGE) || diff != 0))) {
- /* Retry this request. */
- if (!(qp->r_flags & RVT_R_RDMAR_SEQ)) {
- qp->r_flags |= RVT_R_RDMAR_SEQ;
- hfi1_restart_rc(qp, qp->s_last_psn + 1, 0);
- if (list_empty(&qp->rspwait)) {
- qp->r_flags |= RVT_R_RSP_SEND;
- rvt_get_qp(qp);
- list_add_tail(&qp->rspwait,
- &rcd->qp_wait_list);
- }
- }
+ (opcode != OP(ATOMIC_ACKNOWLEDGE) || diff != 0)) ||
+ (wqe->wr.opcode == IB_WR_TID_RDMA_WRITE &&
+ (delta_psn(psn, qp->s_last_psn) != 1))) {
+ set_restart_qp(qp, rcd);
/*
* No need to process the ACK/NAK since we are
* restarting an earlier request.
@@ -1356,6 +2088,9 @@ static int do_rc_ack(struct rvt_qp *qp, u32 aeth, u32 psn, int opcode,
u64 *vaddr = wqe->sg_list[0].vaddr;
*vaddr = val;
}
+ if (wqe->wr.opcode == IB_WR_OPFN)
+ opfn_conn_reply(qp, val);
+
if (qp->s_num_rd_atomic &&
(wqe->wr.opcode == IB_WR_RDMA_READ ||
wqe->wr.opcode == IB_WR_ATOMIC_CMP_AND_SWP ||
@@ -1373,26 +2108,85 @@ static int do_rc_ack(struct rvt_qp *qp, u32 aeth, u32 psn, int opcode,
hfi1_schedule_send(qp);
}
}
+
+ /*
+ * TID RDMA WRITE requests will be completed by the TID RDMA
+ * ACK packet handler (see tid_rdma.c).
+ */
+ if (wqe->wr.opcode == IB_WR_TID_RDMA_WRITE)
+ break;
+
wqe = do_rc_completion(qp, wqe, ibp);
if (qp->s_acked == qp->s_tail)
break;
}
+ trace_hfi1_rc_ack_do(qp, aeth, psn, wqe);
+ trace_hfi1_sender_do_rc_ack(qp);
switch (aeth >> IB_AETH_NAK_SHIFT) {
case 0: /* ACK */
this_cpu_inc(*ibp->rvp.rc_acks);
- if (qp->s_acked != qp->s_tail) {
- /*
- * We are expecting more ACKs so
- * mod the retry timer.
- */
- rvt_mod_retry_timer(qp);
+ if (wqe->wr.opcode == IB_WR_TID_RDMA_READ) {
+ if (wqe_to_tid_req(wqe)->ack_pending)
+ rvt_mod_retry_timer_ext(qp,
+ qpriv->timeout_shift);
+ else
+ rvt_stop_rc_timers(qp);
+ } else if (qp->s_acked != qp->s_tail) {
+ struct rvt_swqe *__w = NULL;
+
+ if (qpriv->s_tid_cur != HFI1_QP_WQE_INVALID)
+ __w = rvt_get_swqe_ptr(qp, qpriv->s_tid_cur);
+
/*
- * We can stop re-sending the earlier packets and
- * continue with the next packet the receiver wants.
+ * Stop timers if we've received all of the TID RDMA
+ * WRITE * responses.
*/
- if (cmp_psn(qp->s_psn, psn) <= 0)
- reset_psn(qp, psn + 1);
+ if (__w && __w->wr.opcode == IB_WR_TID_RDMA_WRITE &&
+ opcode == TID_OP(WRITE_RESP)) {
+ /*
+ * Normally, the loop above would correctly
+ * process all WQEs from s_acked onward and
+ * either complete them or check for correct
+ * PSN sequencing.
+ * However, for TID RDMA, due to pipelining,
+ * the response may not be for the request at
+ * s_acked so the above look would just be
+ * skipped. This does not allow for checking
+ * the PSN sequencing. It has to be done
+ * separately.
+ */
+ if (cmp_psn(psn, qp->s_last_psn + 1)) {
+ set_restart_qp(qp, rcd);
+ goto bail_stop;
+ }
+ /*
+ * If the psn is being resent, stop the
+ * resending.
+ */
+ if (qp->s_cur != qp->s_tail &&
+ cmp_psn(qp->s_psn, psn) <= 0)
+ update_qp_retry_state(qp, psn,
+ __w->psn,
+ __w->lpsn);
+ else if (--qpriv->pending_tid_w_resp)
+ rvt_mod_retry_timer(qp);
+ else
+ rvt_stop_rc_timers(qp);
+ } else {
+ /*
+ * We are expecting more ACKs so
+ * mod the retry timer.
+ */
+ rvt_mod_retry_timer(qp);
+ /*
+ * We can stop re-sending the earlier packets
+ * and continue with the next packet the
+ * receiver wants.
+ */
+ if (cmp_psn(qp->s_psn, psn) <= 0)
+ reset_psn(qp, psn + 1);
+ }
} else {
/* No more acks - kill all timers */
rvt_stop_rc_timers(qp);
@@ -1408,6 +2202,15 @@ static int do_rc_ack(struct rvt_qp *qp, u32 aeth, u32 psn, int opcode,
rvt_get_credit(qp, aeth);
qp->s_rnr_retry = qp->s_rnr_retry_cnt;
qp->s_retry = qp->s_retry_cnt;
+ /*
+ * If the current request is a TID RDMA WRITE request and the
+ * response is not a TID RDMA WRITE RESP packet, s_last_psn
+ * can't be advanced.
+ */
+ if (wqe->wr.opcode == IB_WR_TID_RDMA_WRITE &&
+ opcode != TID_OP(WRITE_RESP) &&
+ cmp_psn(psn, wqe->psn) >= 0)
+ return 1;
update_last_psn(qp, psn);
return 1;
@@ -1417,20 +2220,31 @@ static int do_rc_ack(struct rvt_qp *qp, u32 aeth, u32 psn, int opcode,
goto bail_stop;
if (qp->s_flags & RVT_S_WAIT_RNR)
goto bail_stop;
- if (qp->s_rnr_retry == 0) {
+ rdi = ib_to_rvt(qp->ibqp.device);
+ if (qp->s_rnr_retry == 0 &&
+ !((rdi->post_parms[wqe->wr.opcode].flags &
+ RVT_OPERATION_IGN_RNR_CNT) &&
+ qp->s_rnr_retry_cnt == 0)) {
status = IB_WC_RNR_RETRY_EXC_ERR;
goto class_b;
}
- if (qp->s_rnr_retry_cnt < 7)
+ if (qp->s_rnr_retry_cnt < 7 && qp->s_rnr_retry_cnt > 0)
qp->s_rnr_retry--;
- /* The last valid PSN is the previous PSN. */
- update_last_psn(qp, psn - 1);
+ /*
+ * The last valid PSN is the previous PSN. For TID RDMA WRITE
+ * request, s_last_psn should be incremented only when a TID
+ * RDMA WRITE RESP is received to avoid skipping lost TID RDMA
+ * WRITE RESP packets.
+ */
+ if (wqe->wr.opcode == IB_WR_TID_RDMA_WRITE) {
+ reset_psn(qp, qp->s_last_psn + 1);
+ } else {
+ update_last_psn(qp, psn - 1);
+ reset_psn(qp, psn);
+ }
ibp->rvp.n_rc_resends += delta_psn(qp->s_psn, psn);
-
- reset_psn(qp, psn);
-
qp->s_flags &= ~(RVT_S_WAIT_SSN_CREDIT | RVT_S_WAIT_ACK);
rvt_stop_rc_timers(qp);
rvt_add_rnr_timer(qp, aeth);
@@ -1470,7 +2284,10 @@ static int do_rc_ack(struct rvt_qp *qp, u32 aeth, u32 psn, int opcode,
ibp->rvp.n_other_naks++;
class_b:
if (qp->s_last == qp->s_acked) {
- rvt_send_complete(qp, wqe, status);
+ if (wqe->wr.opcode == IB_WR_TID_RDMA_READ)
+ hfi1_kern_read_tid_flow_free(qp);
+
+ hfi1_trdma_send_complete(qp, wqe, status);
rvt_error_qp(qp, IB_WC_WR_FLUSH_ERR);
}
break;
@@ -1511,6 +2328,8 @@ static void rdma_seq_err(struct rvt_qp *qp, struct hfi1_ibport *ibp, u32 psn,
while (cmp_psn(psn, wqe->lpsn) > 0) {
if (wqe->wr.opcode == IB_WR_RDMA_READ ||
+ wqe->wr.opcode == IB_WR_TID_RDMA_READ ||
+ wqe->wr.opcode == IB_WR_TID_RDMA_WRITE ||
wqe->wr.opcode == IB_WR_ATOMIC_CMP_AND_SWP ||
wqe->wr.opcode == IB_WR_ATOMIC_FETCH_AND_ADD)
break;
@@ -1717,16 +2536,6 @@ bail:
return;
}
-static inline void rc_defered_ack(struct hfi1_ctxtdata *rcd,
- struct rvt_qp *qp)
-{
- if (list_empty(&qp->rspwait)) {
- qp->r_flags |= RVT_R_RSP_NAK;
- rvt_get_qp(qp);
- list_add_tail(&qp->rspwait, &rcd->qp_wait_list);
- }
-}
-
static inline void rc_cancel_ack(struct rvt_qp *qp)
{
qp->r_adefered = 0;
@@ -1759,8 +2568,9 @@ static noinline int rc_rcv_error(struct ib_other_headers *ohdr, void *data,
struct hfi1_ibport *ibp = rcd_to_iport(rcd);
struct rvt_ack_entry *e;
unsigned long flags;
- u8 i, prev;
- int old_req;
+ u8 prev;
+ u8 mra; /* most recent ACK */
+ bool old_req;
trace_hfi1_rcv_error(qp, psn);
if (diff > 0) {
@@ -1806,29 +2616,8 @@ static noinline int rc_rcv_error(struct ib_other_headers *ohdr, void *data,
spin_lock_irqsave(&qp->s_lock, flags);
- for (i = qp->r_head_ack_queue; ; i = prev) {
- if (i == qp->s_tail_ack_queue)
- old_req = 0;
- if (i)
- prev = i - 1;
- else
- prev = HFI1_MAX_RDMA_ATOMIC;
- if (prev == qp->r_head_ack_queue) {
- e = NULL;
- break;
- }
- e = &qp->s_ack_queue[prev];
- if (!e->opcode) {
- e = NULL;
- break;
- }
- if (cmp_psn(psn, e->psn) >= 0) {
- if (prev == qp->s_tail_ack_queue &&
- cmp_psn(psn, e->lpsn) <= 0)
- old_req = 0;
- break;
- }
- }
+ e = find_prev_entry(qp, psn, &prev, &mra, &old_req);
+
switch (opcode) {
case OP(RDMA_READ_REQUEST): {
struct ib_reth *reth;
@@ -1875,6 +2664,8 @@ static noinline int rc_rcv_error(struct ib_other_headers *ohdr, void *data,
e->psn = psn;
if (old_req)
goto unlock_done;
+ if (qp->s_acked_ack_queue == qp->s_tail_ack_queue)
+ qp->s_acked_ack_queue = prev;
qp->s_tail_ack_queue = prev;
break;
}
@@ -1888,6 +2679,8 @@ static noinline int rc_rcv_error(struct ib_other_headers *ohdr, void *data,
*/
if (!e || e->opcode != (u8)opcode || old_req)
goto unlock_done;
+ if (qp->s_tail_ack_queue == qp->s_acked_ack_queue)
+ qp->s_acked_ack_queue = prev;
qp->s_tail_ack_queue = prev;
break;
}
@@ -1903,7 +2696,7 @@ static noinline int rc_rcv_error(struct ib_other_headers *ohdr, void *data,
* Resend the most recent ACK if this request is
* after all the previous RDMA reads and atomics.
*/
- if (i == qp->r_head_ack_queue) {
+ if (mra == qp->r_head_ack_queue) {
spin_unlock_irqrestore(&qp->s_lock, flags);
qp->r_nak_state = 0;
qp->r_ack_psn = qp->r_psn - 1;
@@ -1914,7 +2707,9 @@ static noinline int rc_rcv_error(struct ib_other_headers *ohdr, void *data,
* Resend the RDMA read or atomic op which
* ACKs this duplicate request.
*/
- qp->s_tail_ack_queue = i;
+ if (qp->s_tail_ack_queue == qp->s_acked_ack_queue)
+ qp->s_acked_ack_queue = mra;
+ qp->s_tail_ack_queue = mra;
break;
}
qp->s_ack_state = OP(ACKNOWLEDGE);
@@ -1931,17 +2726,6 @@ send_ack:
return 0;
}
-static inline void update_ack_queue(struct rvt_qp *qp, unsigned n)
-{
- unsigned next;
-
- next = n + 1;
- if (next > HFI1_MAX_RDMA_ATOMIC)
- next = 0;
- qp->s_tail_ack_queue = next;
- qp->s_ack_state = OP(ACKNOWLEDGE);
-}
-
static void log_cca_event(struct hfi1_pportdata *ppd, u8 sl, u32 rlid,
u32 lqpn, u32 rqpn, u8 svc_type)
{
@@ -2039,6 +2823,7 @@ void hfi1_rc_rcv(struct hfi1_packet *packet)
void *data = packet->payload;
u32 tlen = packet->tlen;
struct rvt_qp *qp = packet->qp;
+ struct hfi1_qp_priv *qpriv = qp->priv;
struct hfi1_ibport *ibp = rcd_to_iport(rcd);
struct ib_other_headers *ohdr = packet->ohdr;
u32 opcode = packet->opcode;
@@ -2061,6 +2846,7 @@ void hfi1_rc_rcv(struct hfi1_packet *packet)
return;
fecn = process_ecn(qp, packet);
+ opfn_trigger_conn_request(qp, be32_to_cpu(ohdr->bth[1]));
/*
* Process responses (ACKs) before anything else. Note that the
@@ -2292,11 +3078,11 @@ send_last:
if (unlikely(!(qp->qp_access_flags & IB_ACCESS_REMOTE_READ)))
goto nack_inv;
next = qp->r_head_ack_queue + 1;
- /* s_ack_queue is size HFI1_MAX_RDMA_ATOMIC+1 so use > not >= */
- if (next > HFI1_MAX_RDMA_ATOMIC)
+ /* s_ack_queue is size rvt_size_atomic()+1 so use > not >= */
+ if (next > rvt_size_atomic(ib_to_rvt(qp->ibqp.device)))
next = 0;
spin_lock_irqsave(&qp->s_lock, flags);
- if (unlikely(next == qp->s_tail_ack_queue)) {
+ if (unlikely(next == qp->s_acked_ack_queue)) {
if (!qp->s_ack_queue[next].sent)
goto nack_inv_unlck;
update_ack_queue(qp, next);
@@ -2343,6 +3129,7 @@ send_last:
qp->r_state = opcode;
qp->r_nak_state = 0;
qp->r_head_ack_queue = next;
+ qpriv->r_tid_alloc = qp->r_head_ack_queue;
/* Schedule the send engine. */
qp->s_flags |= RVT_S_RESP_PENDING;
@@ -2356,21 +3143,24 @@ send_last:
case OP(COMPARE_SWAP):
case OP(FETCH_ADD): {
- struct ib_atomic_eth *ateth;
+ struct ib_atomic_eth *ateth = &ohdr->u.atomic_eth;
+ u64 vaddr = get_ib_ateth_vaddr(ateth);
+ bool opfn = opcode == OP(COMPARE_SWAP) &&
+ vaddr == HFI1_VERBS_E_ATOMIC_VADDR;
struct rvt_ack_entry *e;
- u64 vaddr;
atomic64_t *maddr;
u64 sdata;
u32 rkey;
u8 next;
- if (unlikely(!(qp->qp_access_flags & IB_ACCESS_REMOTE_ATOMIC)))
+ if (unlikely(!(qp->qp_access_flags & IB_ACCESS_REMOTE_ATOMIC) &&
+ !opfn))
goto nack_inv;
next = qp->r_head_ack_queue + 1;
- if (next > HFI1_MAX_RDMA_ATOMIC)
+ if (next > rvt_size_atomic(ib_to_rvt(qp->ibqp.device)))
next = 0;
spin_lock_irqsave(&qp->s_lock, flags);
- if (unlikely(next == qp->s_tail_ack_queue)) {
+ if (unlikely(next == qp->s_acked_ack_queue)) {
if (!qp->s_ack_queue[next].sent)
goto nack_inv_unlck;
update_ack_queue(qp, next);
@@ -2380,8 +3170,11 @@ send_last:
rvt_put_mr(e->rdma_sge.mr);
e->rdma_sge.mr = NULL;
}
- ateth = &ohdr->u.atomic_eth;
- vaddr = get_ib_ateth_vaddr(ateth);
+ /* Process OPFN special virtual address */
+ if (opfn) {
+ opfn_conn_response(qp, e, ateth);
+ goto ack;
+ }
if (unlikely(vaddr & (sizeof(u64) - 1)))
goto nack_inv_unlck;
rkey = be32_to_cpu(ateth->rkey);
@@ -2400,6 +3193,7 @@ send_last:
sdata);
rvt_put_mr(qp->r_sge.sge.mr);
qp->r_sge.num_sge = 0;
+ack:
e->opcode = opcode;
e->sent = 0;
e->psn = psn;
@@ -2409,6 +3203,7 @@ send_last:
qp->r_state = opcode;
qp->r_nak_state = 0;
qp->r_head_ack_queue = next;
+ qpriv->r_tid_alloc = qp->r_head_ack_queue;
/* Schedule the send engine. */
qp->s_flags |= RVT_S_RESP_PENDING;
diff --git a/drivers/infiniband/hw/hfi1/rc.h b/drivers/infiniband/hw/hfi1/rc.h
new file mode 100644
index 000000000000..8e0935b9bf2a
--- /dev/null
+++ b/drivers/infiniband/hw/hfi1/rc.h
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+/*
+ * Copyright(c) 2018 Intel Corporation.
+ *
+ */
+
+#ifndef HFI1_RC_H
+#define HFI1_RC_H
+
+/* cut down ridiculously long IB macro names */
+#define OP(x) IB_OPCODE_RC_##x
+
+static inline void update_ack_queue(struct rvt_qp *qp, unsigned int n)
+{
+ unsigned int next;
+
+ next = n + 1;
+ if (next > rvt_size_atomic(ib_to_rvt(qp->ibqp.device)))
+ next = 0;
+ qp->s_tail_ack_queue = next;
+ qp->s_acked_ack_queue = next;
+ qp->s_ack_state = OP(ACKNOWLEDGE);
+}
+
+static inline void rc_defered_ack(struct hfi1_ctxtdata *rcd,
+ struct rvt_qp *qp)
+{
+ if (list_empty(&qp->rspwait)) {
+ qp->r_flags |= RVT_R_RSP_NAK;
+ rvt_get_qp(qp);
+ list_add_tail(&qp->rspwait, &rcd->qp_wait_list);
+ }
+}
+
+static inline u32 restart_sge(struct rvt_sge_state *ss, struct rvt_swqe *wqe,
+ u32 psn, u32 pmtu)
+{
+ u32 len;
+
+ len = delta_psn(psn, wqe->psn) * pmtu;
+ return rvt_restart_sge(ss, wqe, len);
+}
+
+struct rvt_ack_entry *find_prev_entry(struct rvt_qp *qp, u32 psn, u8 *prev,
+ u8 *prev_ack, bool *scheduled);
+int do_rc_ack(struct rvt_qp *qp, u32 aeth, u32 psn, int opcode, u64 val,
+ struct hfi1_ctxtdata *rcd);
+struct rvt_swqe *do_rc_completion(struct rvt_qp *qp, struct rvt_swqe *wqe,
+ struct hfi1_ibport *ibp);
+
+#endif /* HFI1_RC_H */
diff --git a/drivers/infiniband/hw/hfi1/ruc.c b/drivers/infiniband/hw/hfi1/ruc.c
index 7fb317c711df..124a3ec1e15c 100644
--- a/drivers/infiniband/hw/hfi1/ruc.c
+++ b/drivers/infiniband/hw/hfi1/ruc.c
@@ -250,7 +250,6 @@ static inline void hfi1_make_ruc_bth(struct rvt_qp *qp,
struct ib_other_headers *ohdr,
u32 bth0, u32 bth1, u32 bth2)
{
- bth1 |= qp->remote_qpn;
ohdr->bth[0] = cpu_to_be32(bth0);
ohdr->bth[1] = cpu_to_be32(bth1);
ohdr->bth[2] = cpu_to_be32(bth2);
@@ -272,13 +271,13 @@ static inline void hfi1_make_ruc_bth(struct rvt_qp *qp,
*/
static inline void hfi1_make_ruc_header_16B(struct rvt_qp *qp,
struct ib_other_headers *ohdr,
- u32 bth0, u32 bth2, int middle,
+ u32 bth0, u32 bth1, u32 bth2,
+ int middle,
struct hfi1_pkt_state *ps)
{
struct hfi1_qp_priv *priv = qp->priv;
struct hfi1_ibport *ibp = ps->ibp;
struct hfi1_pportdata *ppd = ppd_from_ibp(ibp);
- u32 bth1 = 0;
u32 slid;
u16 pkey = hfi1_get_pkey(ibp, qp->s_pkey_index);
u8 l4 = OPA_16B_L4_IB_LOCAL;
@@ -360,12 +359,12 @@ static inline void hfi1_make_ruc_header_16B(struct rvt_qp *qp,
*/
static inline void hfi1_make_ruc_header_9B(struct rvt_qp *qp,
struct ib_other_headers *ohdr,
- u32 bth0, u32 bth2, int middle,
+ u32 bth0, u32 bth1, u32 bth2,
+ int middle,
struct hfi1_pkt_state *ps)
{
struct hfi1_qp_priv *priv = qp->priv;
struct hfi1_ibport *ibp = ps->ibp;
- u32 bth1 = 0;
u16 pkey = hfi1_get_pkey(ibp, qp->s_pkey_index);
u16 lrh0 = HFI1_LRH_BTH;
u8 extra_bytes = -ps->s_txreq->s_cur_size & 3;
@@ -415,7 +414,7 @@ static inline void hfi1_make_ruc_header_9B(struct rvt_qp *qp,
typedef void (*hfi1_make_ruc_hdr)(struct rvt_qp *qp,
struct ib_other_headers *ohdr,
- u32 bth0, u32 bth2, int middle,
+ u32 bth0, u32 bth1, u32 bth2, int middle,
struct hfi1_pkt_state *ps);
/* We support only two types - 9B and 16B for now */
@@ -425,7 +424,7 @@ static const hfi1_make_ruc_hdr hfi1_ruc_header_tbl[2] = {
};
void hfi1_make_ruc_header(struct rvt_qp *qp, struct ib_other_headers *ohdr,
- u32 bth0, u32 bth2, int middle,
+ u32 bth0, u32 bth1, u32 bth2, int middle,
struct hfi1_pkt_state *ps)
{
struct hfi1_qp_priv *priv = qp->priv;
@@ -446,18 +445,21 @@ void hfi1_make_ruc_header(struct rvt_qp *qp, struct ib_other_headers *ohdr,
priv->s_ahg->ahgidx = 0;
/* Make the appropriate header */
- hfi1_ruc_header_tbl[priv->hdr_type](qp, ohdr, bth0, bth2, middle, ps);
+ hfi1_ruc_header_tbl[priv->hdr_type](qp, ohdr, bth0, bth1, bth2, middle,
+ ps);
}
/* when sending, force a reschedule every one of these periods */
#define SEND_RESCHED_TIMEOUT (5 * HZ) /* 5s in jiffies */
/**
- * schedule_send_yield - test for a yield required for QP send engine
+ * hfi1_schedule_send_yield - test for a yield required for QP
+ * send engine
* @timeout: Final time for timeout slice for jiffies
* @qp: a pointer to QP
* @ps: a pointer to a structure with commonly lookup values for
* the the send engine progress
+ * @tid - true if it is the tid leg
*
* This routine checks if the time slice for the QP has expired
* for RC QPs, if so an additional work entry is queued. At this
@@ -465,8 +467,8 @@ void hfi1_make_ruc_header(struct rvt_qp *qp, struct ib_other_headers *ohdr,
* returns true if a yield is required, otherwise, false
* is returned.
*/
-static bool schedule_send_yield(struct rvt_qp *qp,
- struct hfi1_pkt_state *ps)
+bool hfi1_schedule_send_yield(struct rvt_qp *qp, struct hfi1_pkt_state *ps,
+ bool tid)
{
ps->pkts_sent = true;
@@ -474,8 +476,24 @@ static bool schedule_send_yield(struct rvt_qp *qp,
if (!ps->in_thread ||
workqueue_congested(ps->cpu, ps->ppd->hfi1_wq)) {
spin_lock_irqsave(&qp->s_lock, ps->flags);
- qp->s_flags &= ~RVT_S_BUSY;
- hfi1_schedule_send(qp);
+ if (!tid) {
+ qp->s_flags &= ~RVT_S_BUSY;
+ hfi1_schedule_send(qp);
+ } else {
+ struct hfi1_qp_priv *priv = qp->priv;
+
+ if (priv->s_flags &
+ HFI1_S_TID_BUSY_SET) {
+ qp->s_flags &= ~RVT_S_BUSY;
+ priv->s_flags &=
+ ~(HFI1_S_TID_BUSY_SET |
+ RVT_S_BUSY);
+ } else {
+ priv->s_flags &= ~RVT_S_BUSY;
+ }
+ hfi1_schedule_tid_send(qp);
+ }
+
spin_unlock_irqrestore(&qp->s_lock, ps->flags);
this_cpu_inc(*ps->ppd->dd->send_schedule);
trace_hfi1_rc_expired_time_slice(qp, true);
@@ -576,6 +594,8 @@ void hfi1_do_send(struct rvt_qp *qp, bool in_thread)
do {
/* Check for a constructed packet to be sent. */
if (ps.s_txreq) {
+ if (priv->s_flags & HFI1_S_TID_BUSY_SET)
+ qp->s_flags |= RVT_S_BUSY;
spin_unlock_irqrestore(&qp->s_lock, ps.flags);
/*
* If the packet cannot be sent now, return and
@@ -585,7 +605,7 @@ void hfi1_do_send(struct rvt_qp *qp, bool in_thread)
return;
/* allow other tasks to run */
- if (schedule_send_yield(qp, &ps))
+ if (hfi1_schedule_send_yield(qp, &ps, false))
return;
spin_lock_irqsave(&qp->s_lock, ps.flags);
diff --git a/drivers/infiniband/hw/hfi1/sdma.c b/drivers/infiniband/hw/hfi1/sdma.c
index 96897a91fb0a..b0110728f541 100644
--- a/drivers/infiniband/hw/hfi1/sdma.c
+++ b/drivers/infiniband/hw/hfi1/sdma.c
@@ -1747,10 +1747,9 @@ retry:
*/
static void sdma_desc_avail(struct sdma_engine *sde, uint avail)
{
- struct iowait *wait, *nw;
+ struct iowait *wait, *nw, *twait;
struct iowait *waits[SDMA_WAIT_BATCH_SIZE];
- uint i, n = 0, seq, max_idx = 0;
- u8 max_starved_cnt = 0;
+ uint i, n = 0, seq, tidx = 0;
#ifdef CONFIG_SDMA_VERBOSITY
dd_dev_err(sde->dd, "CONFIG SDMA(%u) %s:%d %s()\n", sde->this_idx,
@@ -1775,13 +1774,20 @@ static void sdma_desc_avail(struct sdma_engine *sde, uint avail)
continue;
if (n == ARRAY_SIZE(waits))
break;
+ iowait_init_priority(wait);
num_desc = iowait_get_all_desc(wait);
if (num_desc > avail)
break;
avail -= num_desc;
- /* Find the most starved wait memeber */
- iowait_starve_find_max(wait, &max_starved_cnt,
- n, &max_idx);
+ /* Find the top-priority wait memeber */
+ if (n) {
+ twait = waits[tidx];
+ tidx =
+ iowait_priority_update_top(wait,
+ twait,
+ n,
+ tidx);
+ }
list_del_init(&wait->list);
waits[n++] = wait;
}
@@ -1790,12 +1796,12 @@ static void sdma_desc_avail(struct sdma_engine *sde, uint avail)
}
} while (read_seqretry(&sde->waitlock, seq));
- /* Schedule the most starved one first */
+ /* Schedule the top-priority entry first */
if (n)
- waits[max_idx]->wakeup(waits[max_idx], SDMA_AVAIL_REASON);
+ waits[tidx]->wakeup(waits[tidx], SDMA_AVAIL_REASON);
for (i = 0; i < n; i++)
- if (i != max_idx)
+ if (i != tidx)
waits[i]->wakeup(waits[i], SDMA_AVAIL_REASON);
}
diff --git a/drivers/infiniband/hw/hfi1/sdma_txreq.h b/drivers/infiniband/hw/hfi1/sdma_txreq.h
index bf7d777d756e..514a4784566b 100644
--- a/drivers/infiniband/hw/hfi1/sdma_txreq.h
+++ b/drivers/infiniband/hw/hfi1/sdma_txreq.h
@@ -91,6 +91,7 @@ struct sdma_desc {
#define SDMA_TXREQ_F_URGENT 0x0001
#define SDMA_TXREQ_F_AHG_COPY 0x0002
#define SDMA_TXREQ_F_USE_AHG 0x0004
+#define SDMA_TXREQ_F_VIP 0x0010
struct sdma_txreq;
typedef void (*callback_t)(struct sdma_txreq *, int);
diff --git a/drivers/infiniband/hw/hfi1/sysfs.c b/drivers/infiniband/hw/hfi1/sysfs.c
index 2be513d4c9da..90f62c4bddba 100644
--- a/drivers/infiniband/hw/hfi1/sysfs.c
+++ b/drivers/infiniband/hw/hfi1/sysfs.c
@@ -498,7 +498,7 @@ static ssize_t hw_rev_show(struct device *device, struct device_attribute *attr,
char *buf)
{
struct hfi1_ibdev *dev =
- container_of(device, struct hfi1_ibdev, rdi.ibdev.dev);
+ rdma_device_to_drv_device(device, struct hfi1_ibdev, rdi.ibdev);
return sprintf(buf, "%x\n", dd_from_dev(dev)->minrev);
}
@@ -508,7 +508,7 @@ static ssize_t board_id_show(struct device *device,
struct device_attribute *attr, char *buf)
{
struct hfi1_ibdev *dev =
- container_of(device, struct hfi1_ibdev, rdi.ibdev.dev);
+ rdma_device_to_drv_device(device, struct hfi1_ibdev, rdi.ibdev);
struct hfi1_devdata *dd = dd_from_dev(dev);
int ret;
@@ -524,7 +524,7 @@ static ssize_t boardversion_show(struct device *device,
struct device_attribute *attr, char *buf)
{
struct hfi1_ibdev *dev =
- container_of(device, struct hfi1_ibdev, rdi.ibdev.dev);
+ rdma_device_to_drv_device(device, struct hfi1_ibdev, rdi.ibdev);
struct hfi1_devdata *dd = dd_from_dev(dev);
/* The string printed here is already newline-terminated. */
@@ -536,7 +536,7 @@ static ssize_t nctxts_show(struct device *device,
struct device_attribute *attr, char *buf)
{
struct hfi1_ibdev *dev =
- container_of(device, struct hfi1_ibdev, rdi.ibdev.dev);
+ rdma_device_to_drv_device(device, struct hfi1_ibdev, rdi.ibdev);
struct hfi1_devdata *dd = dd_from_dev(dev);
/*
@@ -555,7 +555,7 @@ static ssize_t nfreectxts_show(struct device *device,
struct device_attribute *attr, char *buf)
{
struct hfi1_ibdev *dev =
- container_of(device, struct hfi1_ibdev, rdi.ibdev.dev);
+ rdma_device_to_drv_device(device, struct hfi1_ibdev, rdi.ibdev);
struct hfi1_devdata *dd = dd_from_dev(dev);
/* Return the number of free user ports (contexts) available. */
@@ -567,7 +567,7 @@ static ssize_t serial_show(struct device *device,
struct device_attribute *attr, char *buf)
{
struct hfi1_ibdev *dev =
- container_of(device, struct hfi1_ibdev, rdi.ibdev.dev);
+ rdma_device_to_drv_device(device, struct hfi1_ibdev, rdi.ibdev);
struct hfi1_devdata *dd = dd_from_dev(dev);
return scnprintf(buf, PAGE_SIZE, "%s", dd->serial);
@@ -579,7 +579,7 @@ static ssize_t chip_reset_store(struct device *device,
size_t count)
{
struct hfi1_ibdev *dev =
- container_of(device, struct hfi1_ibdev, rdi.ibdev.dev);
+ rdma_device_to_drv_device(device, struct hfi1_ibdev, rdi.ibdev);
struct hfi1_devdata *dd = dd_from_dev(dev);
int ret;
@@ -609,7 +609,7 @@ static ssize_t tempsense_show(struct device *device,
struct device_attribute *attr, char *buf)
{
struct hfi1_ibdev *dev =
- container_of(device, struct hfi1_ibdev, rdi.ibdev.dev);
+ rdma_device_to_drv_device(device, struct hfi1_ibdev, rdi.ibdev);
struct hfi1_devdata *dd = dd_from_dev(dev);
struct hfi1_temp temp;
int ret;
diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.c b/drivers/infiniband/hw/hfi1/tid_rdma.c
index da1ecb68a928..fdda33aca77f 100644
--- a/drivers/infiniband/hw/hfi1/tid_rdma.c
+++ b/drivers/infiniband/hw/hfi1/tid_rdma.c
@@ -5,8 +5,282 @@
*/
#include "hfi.h"
+#include "qp.h"
+#include "rc.h"
#include "verbs.h"
#include "tid_rdma.h"
+#include "exp_rcv.h"
+#include "trace.h"
+
+/**
+ * DOC: TID RDMA READ protocol
+ *
+ * This is an end-to-end protocol at the hfi1 level between two nodes that
+ * improves performance by avoiding data copy on the requester side. It
+ * converts a qualified RDMA READ request into a TID RDMA READ request on
+ * the requester side and thereafter handles the request and response
+ * differently. To be qualified, the RDMA READ request should meet the
+ * following:
+ * -- The total data length should be greater than 256K;
+ * -- The total data length should be a multiple of 4K page size;
+ * -- Each local scatter-gather entry should be 4K page aligned;
+ * -- Each local scatter-gather entry should be a multiple of 4K page size;
+ */
+
+#define RCV_TID_FLOW_TABLE_CTRL_FLOW_VALID_SMASK BIT_ULL(32)
+#define RCV_TID_FLOW_TABLE_CTRL_HDR_SUPP_EN_SMASK BIT_ULL(33)
+#define RCV_TID_FLOW_TABLE_CTRL_KEEP_AFTER_SEQ_ERR_SMASK BIT_ULL(34)
+#define RCV_TID_FLOW_TABLE_CTRL_KEEP_ON_GEN_ERR_SMASK BIT_ULL(35)
+#define RCV_TID_FLOW_TABLE_STATUS_SEQ_MISMATCH_SMASK BIT_ULL(37)
+#define RCV_TID_FLOW_TABLE_STATUS_GEN_MISMATCH_SMASK BIT_ULL(38)
+
+/* Maximum number of packets within a flow generation. */
+#define MAX_TID_FLOW_PSN BIT(HFI1_KDETH_BTH_SEQ_SHIFT)
+
+#define GENERATION_MASK 0xFFFFF
+
+static u32 mask_generation(u32 a)
+{
+ return a & GENERATION_MASK;
+}
+
+/* Reserved generation value to set to unused flows for kernel contexts */
+#define KERN_GENERATION_RESERVED mask_generation(U32_MAX)
+
+/*
+ * J_KEY for kernel contexts when TID RDMA is used.
+ * See generate_jkey() in hfi.h for more information.
+ */
+#define TID_RDMA_JKEY 32
+#define HFI1_KERNEL_MIN_JKEY HFI1_ADMIN_JKEY_RANGE
+#define HFI1_KERNEL_MAX_JKEY (2 * HFI1_ADMIN_JKEY_RANGE - 1)
+
+/* Maximum number of segments in flight per QP request. */
+#define TID_RDMA_MAX_READ_SEGS_PER_REQ 6
+#define TID_RDMA_MAX_WRITE_SEGS_PER_REQ 4
+#define MAX_REQ max_t(u16, TID_RDMA_MAX_READ_SEGS_PER_REQ, \
+ TID_RDMA_MAX_WRITE_SEGS_PER_REQ)
+#define MAX_FLOWS roundup_pow_of_two(MAX_REQ + 1)
+
+#define MAX_EXPECTED_PAGES (MAX_EXPECTED_BUFFER / PAGE_SIZE)
+
+#define TID_RDMA_DESTQP_FLOW_SHIFT 11
+#define TID_RDMA_DESTQP_FLOW_MASK 0x1f
+
+#define TID_FLOW_SW_PSN BIT(0)
+
+#define TID_OPFN_QP_CTXT_MASK 0xff
+#define TID_OPFN_QP_CTXT_SHIFT 56
+#define TID_OPFN_QP_KDETH_MASK 0xff
+#define TID_OPFN_QP_KDETH_SHIFT 48
+#define TID_OPFN_MAX_LEN_MASK 0x7ff
+#define TID_OPFN_MAX_LEN_SHIFT 37
+#define TID_OPFN_TIMEOUT_MASK 0x1f
+#define TID_OPFN_TIMEOUT_SHIFT 32
+#define TID_OPFN_RESERVED_MASK 0x3f
+#define TID_OPFN_RESERVED_SHIFT 26
+#define TID_OPFN_URG_MASK 0x1
+#define TID_OPFN_URG_SHIFT 25
+#define TID_OPFN_VER_MASK 0x7
+#define TID_OPFN_VER_SHIFT 22
+#define TID_OPFN_JKEY_MASK 0x3f
+#define TID_OPFN_JKEY_SHIFT 16
+#define TID_OPFN_MAX_READ_MASK 0x3f
+#define TID_OPFN_MAX_READ_SHIFT 10
+#define TID_OPFN_MAX_WRITE_MASK 0x3f
+#define TID_OPFN_MAX_WRITE_SHIFT 4
+
+/*
+ * OPFN TID layout
+ *
+ * 63 47 31 15
+ * NNNNNNNNKKKKKKKK MMMMMMMMMMMTTTTT DDDDDDUVVVJJJJJJ RRRRRRWWWWWWCCCC
+ * 3210987654321098 7654321098765432 1098765432109876 5432109876543210
+ * N - the context Number
+ * K - the Kdeth_qp
+ * M - Max_len
+ * T - Timeout
+ * D - reserveD
+ * V - version
+ * U - Urg capable
+ * J - Jkey
+ * R - max_Read
+ * W - max_Write
+ * C - Capcode
+ */
+
+static u32 tid_rdma_flow_wt;
+
+static void tid_rdma_trigger_resume(struct work_struct *work);
+static void hfi1_kern_exp_rcv_free_flows(struct tid_rdma_request *req);
+static int hfi1_kern_exp_rcv_alloc_flows(struct tid_rdma_request *req,
+ gfp_t gfp);
+static void hfi1_init_trdma_req(struct rvt_qp *qp,
+ struct tid_rdma_request *req);
+static void hfi1_tid_write_alloc_resources(struct rvt_qp *qp, bool intr_ctx);
+static void hfi1_tid_timeout(struct timer_list *t);
+static void hfi1_add_tid_reap_timer(struct rvt_qp *qp);
+static void hfi1_mod_tid_reap_timer(struct rvt_qp *qp);
+static void hfi1_mod_tid_retry_timer(struct rvt_qp *qp);
+static int hfi1_stop_tid_retry_timer(struct rvt_qp *qp);
+static void hfi1_tid_retry_timeout(struct timer_list *t);
+static int make_tid_rdma_ack(struct rvt_qp *qp,
+ struct ib_other_headers *ohdr,
+ struct hfi1_pkt_state *ps);
+static void hfi1_do_tid_send(struct rvt_qp *qp);
+
+static u64 tid_rdma_opfn_encode(struct tid_rdma_params *p)
+{
+ return
+ (((u64)p->qp & TID_OPFN_QP_CTXT_MASK) <<
+ TID_OPFN_QP_CTXT_SHIFT) |
+ ((((u64)p->qp >> 16) & TID_OPFN_QP_KDETH_MASK) <<
+ TID_OPFN_QP_KDETH_SHIFT) |
+ (((u64)((p->max_len >> PAGE_SHIFT) - 1) &
+ TID_OPFN_MAX_LEN_MASK) << TID_OPFN_MAX_LEN_SHIFT) |
+ (((u64)p->timeout & TID_OPFN_TIMEOUT_MASK) <<
+ TID_OPFN_TIMEOUT_SHIFT) |
+ (((u64)p->urg & TID_OPFN_URG_MASK) << TID_OPFN_URG_SHIFT) |
+ (((u64)p->jkey & TID_OPFN_JKEY_MASK) << TID_OPFN_JKEY_SHIFT) |
+ (((u64)p->max_read & TID_OPFN_MAX_READ_MASK) <<
+ TID_OPFN_MAX_READ_SHIFT) |
+ (((u64)p->max_write & TID_OPFN_MAX_WRITE_MASK) <<
+ TID_OPFN_MAX_WRITE_SHIFT);
+}
+
+static void tid_rdma_opfn_decode(struct tid_rdma_params *p, u64 data)
+{
+ p->max_len = (((data >> TID_OPFN_MAX_LEN_SHIFT) &
+ TID_OPFN_MAX_LEN_MASK) + 1) << PAGE_SHIFT;
+ p->jkey = (data >> TID_OPFN_JKEY_SHIFT) & TID_OPFN_JKEY_MASK;
+ p->max_write = (data >> TID_OPFN_MAX_WRITE_SHIFT) &
+ TID_OPFN_MAX_WRITE_MASK;
+ p->max_read = (data >> TID_OPFN_MAX_READ_SHIFT) &
+ TID_OPFN_MAX_READ_MASK;
+ p->qp =
+ ((((data >> TID_OPFN_QP_KDETH_SHIFT) & TID_OPFN_QP_KDETH_MASK)
+ << 16) |
+ ((data >> TID_OPFN_QP_CTXT_SHIFT) & TID_OPFN_QP_CTXT_MASK));
+ p->urg = (data >> TID_OPFN_URG_SHIFT) & TID_OPFN_URG_MASK;
+ p->timeout = (data >> TID_OPFN_TIMEOUT_SHIFT) & TID_OPFN_TIMEOUT_MASK;
+}
+
+void tid_rdma_opfn_init(struct rvt_qp *qp, struct tid_rdma_params *p)
+{
+ struct hfi1_qp_priv *priv = qp->priv;
+
+ p->qp = (kdeth_qp << 16) | priv->rcd->ctxt;
+ p->max_len = TID_RDMA_MAX_SEGMENT_SIZE;
+ p->jkey = priv->rcd->jkey;
+ p->max_read = TID_RDMA_MAX_READ_SEGS_PER_REQ;
+ p->max_write = TID_RDMA_MAX_WRITE_SEGS_PER_REQ;
+ p->timeout = qp->timeout;
+ p->urg = is_urg_masked(priv->rcd);
+}
+
+bool tid_rdma_conn_req(struct rvt_qp *qp, u64 *data)
+{
+ struct hfi1_qp_priv *priv = qp->priv;
+
+ *data = tid_rdma_opfn_encode(&priv->tid_rdma.local);
+ return true;
+}
+
+bool tid_rdma_conn_reply(struct rvt_qp *qp, u64 data)
+{
+ struct hfi1_qp_priv *priv = qp->priv;
+ struct tid_rdma_params *remote, *old;
+ bool ret = true;
+
+ old = rcu_dereference_protected(priv->tid_rdma.remote,
+ lockdep_is_held(&priv->opfn.lock));
+ data &= ~0xfULL;
+ /*
+ * If data passed in is zero, return true so as not to continue the
+ * negotiation process
+ */
+ if (!data || !HFI1_CAP_IS_KSET(TID_RDMA))
+ goto null;
+ /*
+ * If kzalloc fails, return false. This will result in:
+ * * at the requester a new OPFN request being generated to retry
+ * the negotiation
+ * * at the responder, 0 being returned to the requester so as to
+ * disable TID RDMA at both the requester and the responder
+ */
+ remote = kzalloc(sizeof(*remote), GFP_ATOMIC);
+ if (!remote) {
+ ret = false;
+ goto null;
+ }
+
+ tid_rdma_opfn_decode(remote, data);
+ priv->tid_timer_timeout_jiffies =
+ usecs_to_jiffies((((4096UL * (1UL << remote->timeout)) /
+ 1000UL) << 3) * 7);
+ trace_hfi1_opfn_param(qp, 0, &priv->tid_rdma.local);
+ trace_hfi1_opfn_param(qp, 1, remote);
+ rcu_assign_pointer(priv->tid_rdma.remote, remote);
+ /*
+ * A TID RDMA READ request's segment size is not equal to
+ * remote->max_len only when the request's data length is smaller
+ * than remote->max_len. In that case, there will be only one segment.
+ * Therefore, when priv->pkts_ps is used to calculate req->cur_seg
+ * during retry, it will lead to req->cur_seg = 0, which is exactly
+ * what is expected.
+ */
+ priv->pkts_ps = (u16)rvt_div_mtu(qp, remote->max_len);
+ priv->timeout_shift = ilog2(priv->pkts_ps - 1) + 1;
+ goto free;
+null:
+ RCU_INIT_POINTER(priv->tid_rdma.remote, NULL);
+ priv->timeout_shift = 0;
+free:
+ if (old)
+ kfree_rcu(old, rcu_head);
+ return ret;
+}
+
+bool tid_rdma_conn_resp(struct rvt_qp *qp, u64 *data)
+{
+ bool ret;
+
+ ret = tid_rdma_conn_reply(qp, *data);
+ *data = 0;
+ /*
+ * If tid_rdma_conn_reply() returns error, set *data as 0 to indicate
+ * TID RDMA could not be enabled. This will result in TID RDMA being
+ * disabled at the requester too.
+ */
+ if (ret)
+ (void)tid_rdma_conn_req(qp, data);
+ return ret;
+}
+
+void tid_rdma_conn_error(struct rvt_qp *qp)
+{
+ struct hfi1_qp_priv *priv = qp->priv;
+ struct tid_rdma_params *old;
+
+ old = rcu_dereference_protected(priv->tid_rdma.remote,
+ lockdep_is_held(&priv->opfn.lock));
+ RCU_INIT_POINTER(priv->tid_rdma.remote, NULL);
+ if (old)
+ kfree_rcu(old, rcu_head);
+}
+
+/* This is called at context initialization time */
+int hfi1_kern_exp_rcv_init(struct hfi1_ctxtdata *rcd, int reinit)
+{
+ if (reinit)
+ return 0;
+
+ BUILD_BUG_ON(TID_RDMA_JKEY < HFI1_KERNEL_MIN_JKEY);
+ BUILD_BUG_ON(TID_RDMA_JKEY > HFI1_KERNEL_MAX_JKEY);
+ rcd->jkey = TID_RDMA_JKEY;
+ hfi1_set_ctxt_jkey(rcd->dd, rcd, rcd->jkey);
+ return hfi1_alloc_ctxt_rcv_groups(rcd);
+}
/**
* qp_to_rcd - determine the receive context used by a qp
@@ -41,8 +315,5152 @@ int hfi1_qp_priv_init(struct rvt_dev_info *rdi, struct rvt_qp *qp,
struct ib_qp_init_attr *init_attr)
{
struct hfi1_qp_priv *qpriv = qp->priv;
+ int i, ret;
qpriv->rcd = qp_to_rcd(rdi, qp);
+ spin_lock_init(&qpriv->opfn.lock);
+ INIT_WORK(&qpriv->opfn.opfn_work, opfn_send_conn_request);
+ INIT_WORK(&qpriv->tid_rdma.trigger_work, tid_rdma_trigger_resume);
+ qpriv->flow_state.psn = 0;
+ qpriv->flow_state.index = RXE_NUM_TID_FLOWS;
+ qpriv->flow_state.last_index = RXE_NUM_TID_FLOWS;
+ qpriv->flow_state.generation = KERN_GENERATION_RESERVED;
+ qpriv->s_state = TID_OP(WRITE_RESP);
+ qpriv->s_tid_cur = HFI1_QP_WQE_INVALID;
+ qpriv->s_tid_head = HFI1_QP_WQE_INVALID;
+ qpriv->s_tid_tail = HFI1_QP_WQE_INVALID;
+ qpriv->rnr_nak_state = TID_RNR_NAK_INIT;
+ qpriv->r_tid_head = HFI1_QP_WQE_INVALID;
+ qpriv->r_tid_tail = HFI1_QP_WQE_INVALID;
+ qpriv->r_tid_ack = HFI1_QP_WQE_INVALID;
+ qpriv->r_tid_alloc = HFI1_QP_WQE_INVALID;
+ atomic_set(&qpriv->n_requests, 0);
+ atomic_set(&qpriv->n_tid_requests, 0);
+ timer_setup(&qpriv->s_tid_timer, hfi1_tid_timeout, 0);
+ timer_setup(&qpriv->s_tid_retry_timer, hfi1_tid_retry_timeout, 0);
+ INIT_LIST_HEAD(&qpriv->tid_wait);
+
+ if (init_attr->qp_type == IB_QPT_RC && HFI1_CAP_IS_KSET(TID_RDMA)) {
+ struct hfi1_devdata *dd = qpriv->rcd->dd;
+
+ qpriv->pages = kzalloc_node(TID_RDMA_MAX_PAGES *
+ sizeof(*qpriv->pages),
+ GFP_KERNEL, dd->node);
+ if (!qpriv->pages)
+ return -ENOMEM;
+ for (i = 0; i < qp->s_size; i++) {
+ struct hfi1_swqe_priv *priv;
+ struct rvt_swqe *wqe = rvt_get_swqe_ptr(qp, i);
+
+ priv = kzalloc_node(sizeof(*priv), GFP_KERNEL,
+ dd->node);
+ if (!priv)
+ return -ENOMEM;
+
+ hfi1_init_trdma_req(qp, &priv->tid_req);
+ priv->tid_req.e.swqe = wqe;
+ wqe->priv = priv;
+ }
+ for (i = 0; i < rvt_max_atomic(rdi); i++) {
+ struct hfi1_ack_priv *priv;
+
+ priv = kzalloc_node(sizeof(*priv), GFP_KERNEL,
+ dd->node);
+ if (!priv)
+ return -ENOMEM;
+
+ hfi1_init_trdma_req(qp, &priv->tid_req);
+ priv->tid_req.e.ack = &qp->s_ack_queue[i];
+
+ ret = hfi1_kern_exp_rcv_alloc_flows(&priv->tid_req,
+ GFP_KERNEL);
+ if (ret) {
+ kfree(priv);
+ return ret;
+ }
+ qp->s_ack_queue[i].priv = priv;
+ }
+ }
+
return 0;
}
+
+void hfi1_qp_priv_tid_free(struct rvt_dev_info *rdi, struct rvt_qp *qp)
+{
+ struct hfi1_qp_priv *qpriv = qp->priv;
+ struct rvt_swqe *wqe;
+ u32 i;
+
+ if (qp->ibqp.qp_type == IB_QPT_RC && HFI1_CAP_IS_KSET(TID_RDMA)) {
+ for (i = 0; i < qp->s_size; i++) {
+ wqe = rvt_get_swqe_ptr(qp, i);
+ kfree(wqe->priv);
+ wqe->priv = NULL;
+ }
+ for (i = 0; i < rvt_max_atomic(rdi); i++) {
+ struct hfi1_ack_priv *priv = qp->s_ack_queue[i].priv;
+
+ if (priv)
+ hfi1_kern_exp_rcv_free_flows(&priv->tid_req);
+ kfree(priv);
+ qp->s_ack_queue[i].priv = NULL;
+ }
+ cancel_work_sync(&qpriv->opfn.opfn_work);
+ kfree(qpriv->pages);
+ qpriv->pages = NULL;
+ }
+}
+
+/* Flow and tid waiter functions */
+/**
+ * DOC: lock ordering
+ *
+ * There are two locks involved with the queuing
+ * routines: the qp s_lock and the exp_lock.
+ *
+ * Since the tid space allocation is called from
+ * the send engine, the qp s_lock is already held.
+ *
+ * The allocation routines will get the exp_lock.
+ *
+ * The first_qp() call is provided to allow the head of
+ * the rcd wait queue to be fetched under the exp_lock and
+ * followed by a drop of the exp_lock.
+ *
+ * Any qp in the wait list will have the qp reference count held
+ * to hold the qp in memory.
+ */
+
+/*
+ * return head of rcd wait list
+ *
+ * Must hold the exp_lock.
+ *
+ * Get a reference to the QP to hold the QP in memory.
+ *
+ * The caller must release the reference when the local
+ * is no longer being used.
+ */
+static struct rvt_qp *first_qp(struct hfi1_ctxtdata *rcd,
+ struct tid_queue *queue)
+ __must_hold(&rcd->exp_lock)
+{
+ struct hfi1_qp_priv *priv;
+
+ lockdep_assert_held(&rcd->exp_lock);
+ priv = list_first_entry_or_null(&queue->queue_head,
+ struct hfi1_qp_priv,
+ tid_wait);
+ if (!priv)
+ return NULL;
+ rvt_get_qp(priv->owner);
+ return priv->owner;
+}
+
+/**
+ * kernel_tid_waiters - determine rcd wait
+ * @rcd: the receive context
+ * @qp: the head of the qp being processed
+ *
+ * This routine will return false IFF
+ * the list is NULL or the head of the
+ * list is the indicated qp.
+ *
+ * Must hold the qp s_lock and the exp_lock.
+ *
+ * Return:
+ * false if either of the conditions below are statisfied:
+ * 1. The list is empty or
+ * 2. The indicated qp is at the head of the list and the
+ * HFI1_S_WAIT_TID_SPACE bit is set in qp->s_flags.
+ * true is returned otherwise.
+ */
+static bool kernel_tid_waiters(struct hfi1_ctxtdata *rcd,
+ struct tid_queue *queue, struct rvt_qp *qp)
+ __must_hold(&rcd->exp_lock) __must_hold(&qp->s_lock)
+{
+ struct rvt_qp *fqp;
+ bool ret = true;
+
+ lockdep_assert_held(&qp->s_lock);
+ lockdep_assert_held(&rcd->exp_lock);
+ fqp = first_qp(rcd, queue);
+ if (!fqp || (fqp == qp && (qp->s_flags & HFI1_S_WAIT_TID_SPACE)))
+ ret = false;
+ rvt_put_qp(fqp);
+ return ret;
+}
+
+/**
+ * dequeue_tid_waiter - dequeue the qp from the list
+ * @qp - the qp to remove the wait list
+ *
+ * This routine removes the indicated qp from the
+ * wait list if it is there.
+ *
+ * This should be done after the hardware flow and
+ * tid array resources have been allocated.
+ *
+ * Must hold the qp s_lock and the rcd exp_lock.
+ *
+ * It assumes the s_lock to protect the s_flags
+ * field and to reliably test the HFI1_S_WAIT_TID_SPACE flag.
+ */
+static void dequeue_tid_waiter(struct hfi1_ctxtdata *rcd,
+ struct tid_queue *queue, struct rvt_qp *qp)
+ __must_hold(&rcd->exp_lock) __must_hold(&qp->s_lock)
+{
+ struct hfi1_qp_priv *priv = qp->priv;
+
+ lockdep_assert_held(&qp->s_lock);
+ lockdep_assert_held(&rcd->exp_lock);
+ if (list_empty(&priv->tid_wait))
+ return;
+ list_del_init(&priv->tid_wait);
+ qp->s_flags &= ~HFI1_S_WAIT_TID_SPACE;
+ queue->dequeue++;
+ rvt_put_qp(qp);
+}
+
+/**
+ * queue_qp_for_tid_wait - suspend QP on tid space
+ * @rcd: the receive context
+ * @qp: the qp
+ *
+ * The qp is inserted at the tail of the rcd
+ * wait queue and the HFI1_S_WAIT_TID_SPACE s_flag is set.
+ *
+ * Must hold the qp s_lock and the exp_lock.
+ */
+static void queue_qp_for_tid_wait(struct hfi1_ctxtdata *rcd,
+ struct tid_queue *queue, struct rvt_qp *qp)
+ __must_hold(&rcd->exp_lock) __must_hold(&qp->s_lock)
+{
+ struct hfi1_qp_priv *priv = qp->priv;
+
+ lockdep_assert_held(&qp->s_lock);
+ lockdep_assert_held(&rcd->exp_lock);
+ if (list_empty(&priv->tid_wait)) {
+ qp->s_flags |= HFI1_S_WAIT_TID_SPACE;
+ list_add_tail(&priv->tid_wait, &queue->queue_head);
+ priv->tid_enqueue = ++queue->enqueue;
+ rcd->dd->verbs_dev.n_tidwait++;
+ trace_hfi1_qpsleep(qp, HFI1_S_WAIT_TID_SPACE);
+ rvt_get_qp(qp);
+ }
+}
+
+/**
+ * __trigger_tid_waiter - trigger tid waiter
+ * @qp: the qp
+ *
+ * This is a private entrance to schedule the qp
+ * assuming the caller is holding the qp->s_lock.
+ */
+static void __trigger_tid_waiter(struct rvt_qp *qp)
+ __must_hold(&qp->s_lock)
+{
+ lockdep_assert_held(&qp->s_lock);
+ if (!(qp->s_flags & HFI1_S_WAIT_TID_SPACE))
+ return;
+ trace_hfi1_qpwakeup(qp, HFI1_S_WAIT_TID_SPACE);
+ hfi1_schedule_send(qp);
+}
+
+/**
+ * tid_rdma_schedule_tid_wakeup - schedule wakeup for a qp
+ * @qp - the qp
+ *
+ * trigger a schedule or a waiting qp in a deadlock
+ * safe manner. The qp reference is held prior
+ * to this call via first_qp().
+ *
+ * If the qp trigger was already scheduled (!rval)
+ * the the reference is dropped, otherwise the resume
+ * or the destroy cancel will dispatch the reference.
+ */
+static void tid_rdma_schedule_tid_wakeup(struct rvt_qp *qp)
+{
+ struct hfi1_qp_priv *priv;
+ struct hfi1_ibport *ibp;
+ struct hfi1_pportdata *ppd;
+ struct hfi1_devdata *dd;
+ bool rval;
+
+ if (!qp)
+ return;
+
+ priv = qp->priv;
+ ibp = to_iport(qp->ibqp.device, qp->port_num);
+ ppd = ppd_from_ibp(ibp);
+ dd = dd_from_ibdev(qp->ibqp.device);
+
+ rval = queue_work_on(priv->s_sde ?
+ priv->s_sde->cpu :
+ cpumask_first(cpumask_of_node(dd->node)),
+ ppd->hfi1_wq,
+ &priv->tid_rdma.trigger_work);
+ if (!rval)
+ rvt_put_qp(qp);
+}
+
+/**
+ * tid_rdma_trigger_resume - field a trigger work request
+ * @work - the work item
+ *
+ * Complete the off qp trigger processing by directly
+ * calling the progress routine.
+ */
+static void tid_rdma_trigger_resume(struct work_struct *work)
+{
+ struct tid_rdma_qp_params *tr;
+ struct hfi1_qp_priv *priv;
+ struct rvt_qp *qp;
+
+ tr = container_of(work, struct tid_rdma_qp_params, trigger_work);
+ priv = container_of(tr, struct hfi1_qp_priv, tid_rdma);
+ qp = priv->owner;
+ spin_lock_irq(&qp->s_lock);
+ if (qp->s_flags & HFI1_S_WAIT_TID_SPACE) {
+ spin_unlock_irq(&qp->s_lock);
+ hfi1_do_send(priv->owner, true);
+ } else {
+ spin_unlock_irq(&qp->s_lock);
+ }
+ rvt_put_qp(qp);
+}
+
+/**
+ * tid_rdma_flush_wait - unwind any tid space wait
+ *
+ * This is called when resetting a qp to
+ * allow a destroy or reset to get rid
+ * of any tid space linkage and reference counts.
+ */
+static void _tid_rdma_flush_wait(struct rvt_qp *qp, struct tid_queue *queue)
+ __must_hold(&qp->s_lock)
+{
+ struct hfi1_qp_priv *priv;
+
+ if (!qp)
+ return;
+ lockdep_assert_held(&qp->s_lock);
+ priv = qp->priv;
+ qp->s_flags &= ~HFI1_S_WAIT_TID_SPACE;
+ spin_lock(&priv->rcd->exp_lock);
+ if (!list_empty(&priv->tid_wait)) {
+ list_del_init(&priv->tid_wait);
+ qp->s_flags &= ~HFI1_S_WAIT_TID_SPACE;
+ queue->dequeue++;
+ rvt_put_qp(qp);
+ }
+ spin_unlock(&priv->rcd->exp_lock);
+}
+
+void hfi1_tid_rdma_flush_wait(struct rvt_qp *qp)
+ __must_hold(&qp->s_lock)
+{
+ struct hfi1_qp_priv *priv = qp->priv;
+
+ _tid_rdma_flush_wait(qp, &priv->rcd->flow_queue);
+ _tid_rdma_flush_wait(qp, &priv->rcd->rarr_queue);
+}
+
+/* Flow functions */
+/**
+ * kern_reserve_flow - allocate a hardware flow
+ * @rcd - the context to use for allocation
+ * @last - the index of the preferred flow. Use RXE_NUM_TID_FLOWS to
+ * signify "don't care".
+ *
+ * Use a bit mask based allocation to reserve a hardware
+ * flow for use in receiving KDETH data packets. If a preferred flow is
+ * specified the function will attempt to reserve that flow again, if
+ * available.
+ *
+ * The exp_lock must be held.
+ *
+ * Return:
+ * On success: a value postive value between 0 and RXE_NUM_TID_FLOWS - 1
+ * On failure: -EAGAIN
+ */
+static int kern_reserve_flow(struct hfi1_ctxtdata *rcd, int last)
+ __must_hold(&rcd->exp_lock)
+{
+ int nr;
+
+ /* Attempt to reserve the preferred flow index */
+ if (last >= 0 && last < RXE_NUM_TID_FLOWS &&
+ !test_and_set_bit(last, &rcd->flow_mask))
+ return last;
+
+ nr = ffz(rcd->flow_mask);
+ BUILD_BUG_ON(RXE_NUM_TID_FLOWS >=
+ (sizeof(rcd->flow_mask) * BITS_PER_BYTE));
+ if (nr > (RXE_NUM_TID_FLOWS - 1))
+ return -EAGAIN;
+ set_bit(nr, &rcd->flow_mask);
+ return nr;
+}
+
+static void kern_set_hw_flow(struct hfi1_ctxtdata *rcd, u32 generation,
+ u32 flow_idx)
+{
+ u64 reg;
+
+ reg = ((u64)generation << HFI1_KDETH_BTH_SEQ_SHIFT) |
+ RCV_TID_FLOW_TABLE_CTRL_FLOW_VALID_SMASK |
+ RCV_TID_FLOW_TABLE_CTRL_KEEP_AFTER_SEQ_ERR_SMASK |
+ RCV_TID_FLOW_TABLE_CTRL_KEEP_ON_GEN_ERR_SMASK |
+ RCV_TID_FLOW_TABLE_STATUS_SEQ_MISMATCH_SMASK |
+ RCV_TID_FLOW_TABLE_STATUS_GEN_MISMATCH_SMASK;
+
+ if (generation != KERN_GENERATION_RESERVED)
+ reg |= RCV_TID_FLOW_TABLE_CTRL_HDR_SUPP_EN_SMASK;
+
+ write_uctxt_csr(rcd->dd, rcd->ctxt,
+ RCV_TID_FLOW_TABLE + 8 * flow_idx, reg);
+}
+
+static u32 kern_setup_hw_flow(struct hfi1_ctxtdata *rcd, u32 flow_idx)
+ __must_hold(&rcd->exp_lock)
+{
+ u32 generation = rcd->flows[flow_idx].generation;
+
+ kern_set_hw_flow(rcd, generation, flow_idx);
+ return generation;
+}
+
+static u32 kern_flow_generation_next(u32 gen)
+{
+ u32 generation = mask_generation(gen + 1);
+
+ if (generation == KERN_GENERATION_RESERVED)
+ generation = mask_generation(generation + 1);
+ return generation;
+}
+
+static void kern_clear_hw_flow(struct hfi1_ctxtdata *rcd, u32 flow_idx)
+ __must_hold(&rcd->exp_lock)
+{
+ rcd->flows[flow_idx].generation =
+ kern_flow_generation_next(rcd->flows[flow_idx].generation);
+ kern_set_hw_flow(rcd, KERN_GENERATION_RESERVED, flow_idx);
+}
+
+int hfi1_kern_setup_hw_flow(struct hfi1_ctxtdata *rcd, struct rvt_qp *qp)
+{
+ struct hfi1_qp_priv *qpriv = (struct hfi1_qp_priv *)qp->priv;
+ struct tid_flow_state *fs = &qpriv->flow_state;
+ struct rvt_qp *fqp;
+ unsigned long flags;
+ int ret = 0;
+
+ /* The QP already has an allocated flow */
+ if (fs->index != RXE_NUM_TID_FLOWS)
+ return ret;
+
+ spin_lock_irqsave(&rcd->exp_lock, flags);
+ if (kernel_tid_waiters(rcd, &rcd->flow_queue, qp))
+ goto queue;
+
+ ret = kern_reserve_flow(rcd, fs->last_index);
+ if (ret < 0)
+ goto queue;
+ fs->index = ret;
+ fs->last_index = fs->index;
+
+ /* Generation received in a RESYNC overrides default flow generation */
+ if (fs->generation != KERN_GENERATION_RESERVED)
+ rcd->flows[fs->index].generation = fs->generation;
+ fs->generation = kern_setup_hw_flow(rcd, fs->index);
+ fs->psn = 0;
+ fs->flags = 0;
+ dequeue_tid_waiter(rcd, &rcd->flow_queue, qp);
+ /* get head before dropping lock */
+ fqp = first_qp(rcd, &rcd->flow_queue);
+ spin_unlock_irqrestore(&rcd->exp_lock, flags);
+
+ tid_rdma_schedule_tid_wakeup(fqp);
+ return 0;
+queue:
+ queue_qp_for_tid_wait(rcd, &rcd->flow_queue, qp);
+ spin_unlock_irqrestore(&rcd->exp_lock, flags);
+ return -EAGAIN;
+}
+
+void hfi1_kern_clear_hw_flow(struct hfi1_ctxtdata *rcd, struct rvt_qp *qp)
+{
+ struct hfi1_qp_priv *qpriv = (struct hfi1_qp_priv *)qp->priv;
+ struct tid_flow_state *fs = &qpriv->flow_state;
+ struct rvt_qp *fqp;
+ unsigned long flags;
+
+ if (fs->index >= RXE_NUM_TID_FLOWS)
+ return;
+ spin_lock_irqsave(&rcd->exp_lock, flags);
+ kern_clear_hw_flow(rcd, fs->index);
+ clear_bit(fs->index, &rcd->flow_mask);
+ fs->index = RXE_NUM_TID_FLOWS;
+ fs->psn = 0;
+ fs->generation = KERN_GENERATION_RESERVED;
+
+ /* get head before dropping lock */
+ fqp = first_qp(rcd, &rcd->flow_queue);
+ spin_unlock_irqrestore(&rcd->exp_lock, flags);
+
+ if (fqp == qp) {
+ __trigger_tid_waiter(fqp);
+ rvt_put_qp(fqp);
+ } else {
+ tid_rdma_schedule_tid_wakeup(fqp);
+ }
+}
+
+void hfi1_kern_init_ctxt_generations(struct hfi1_ctxtdata *rcd)
+{
+ int i;
+
+ for (i = 0; i < RXE_NUM_TID_FLOWS; i++) {
+ rcd->flows[i].generation = mask_generation(prandom_u32());
+ kern_set_hw_flow(rcd, KERN_GENERATION_RESERVED, i);
+ }
+}
+
+/* TID allocation functions */
+static u8 trdma_pset_order(struct tid_rdma_pageset *s)
+{
+ u8 count = s->count;
+
+ return ilog2(count) + 1;
+}
+
+/**
+ * tid_rdma_find_phys_blocks_4k - get groups base on mr info
+ * @npages - number of pages
+ * @pages - pointer to an array of page structs
+ * @list - page set array to return
+ *
+ * This routine returns the number of groups associated with
+ * the current sge information. This implementation is based
+ * on the expected receive find_phys_blocks() adjusted to
+ * use the MR information vs. the pfn.
+ *
+ * Return:
+ * the number of RcvArray entries
+ */
+static u32 tid_rdma_find_phys_blocks_4k(struct tid_rdma_flow *flow,
+ struct page **pages,
+ u32 npages,
+ struct tid_rdma_pageset *list)
+{
+ u32 pagecount, pageidx, setcount = 0, i;
+ void *vaddr, *this_vaddr;
+
+ if (!npages)
+ return 0;
+
+ /*
+ * Look for sets of physically contiguous pages in the user buffer.
+ * This will allow us to optimize Expected RcvArray entry usage by
+ * using the bigger supported sizes.
+ */
+ vaddr = page_address(pages[0]);
+ trace_hfi1_tid_flow_page(flow->req->qp, flow, 0, 0, 0, vaddr);
+ for (pageidx = 0, pagecount = 1, i = 1; i <= npages; i++) {
+ this_vaddr = i < npages ? page_address(pages[i]) : NULL;
+ trace_hfi1_tid_flow_page(flow->req->qp, flow, i, 0, 0,
+ this_vaddr);
+ /*
+ * If the vaddr's are not sequential, pages are not physically
+ * contiguous.
+ */
+ if (this_vaddr != (vaddr + PAGE_SIZE)) {
+ /*
+ * At this point we have to loop over the set of
+ * physically contiguous pages and break them down it
+ * sizes supported by the HW.
+ * There are two main constraints:
+ * 1. The max buffer size is MAX_EXPECTED_BUFFER.
+ * If the total set size is bigger than that
+ * program only a MAX_EXPECTED_BUFFER chunk.
+ * 2. The buffer size has to be a power of two. If
+ * it is not, round down to the closes power of
+ * 2 and program that size.
+ */
+ while (pagecount) {
+ int maxpages = pagecount;
+ u32 bufsize = pagecount * PAGE_SIZE;
+
+ if (bufsize > MAX_EXPECTED_BUFFER)
+ maxpages =
+ MAX_EXPECTED_BUFFER >>
+ PAGE_SHIFT;
+ else if (!is_power_of_2(bufsize))
+ maxpages =
+ rounddown_pow_of_two(bufsize) >>
+ PAGE_SHIFT;
+
+ list[setcount].idx = pageidx;
+ list[setcount].count = maxpages;
+ trace_hfi1_tid_pageset(flow->req->qp, setcount,
+ list[setcount].idx,
+ list[setcount].count);
+ pagecount -= maxpages;
+ pageidx += maxpages;
+ setcount++;
+ }
+ pageidx = i;
+ pagecount = 1;
+ vaddr = this_vaddr;
+ } else {
+ vaddr += PAGE_SIZE;
+ pagecount++;
+ }
+ }
+ /* insure we always return an even number of sets */
+ if (setcount & 1)
+ list[setcount++].count = 0;
+ return setcount;
+}
+
+/**
+ * tid_flush_pages - dump out pages into pagesets
+ * @list - list of pagesets
+ * @idx - pointer to current page index
+ * @pages - number of pages to dump
+ * @sets - current number of pagesset
+ *
+ * This routine flushes out accumuated pages.
+ *
+ * To insure an even number of sets the
+ * code may add a filler.
+ *
+ * This can happen with when pages is not
+ * a power of 2 or pages is a power of 2
+ * less than the maximum pages.
+ *
+ * Return:
+ * The new number of sets
+ */
+
+static u32 tid_flush_pages(struct tid_rdma_pageset *list,
+ u32 *idx, u32 pages, u32 sets)
+{
+ while (pages) {
+ u32 maxpages = pages;
+
+ if (maxpages > MAX_EXPECTED_PAGES)
+ maxpages = MAX_EXPECTED_PAGES;
+ else if (!is_power_of_2(maxpages))
+ maxpages = rounddown_pow_of_two(maxpages);
+ list[sets].idx = *idx;
+ list[sets++].count = maxpages;
+ *idx += maxpages;
+ pages -= maxpages;
+ }
+ /* might need a filler */
+ if (sets & 1)
+ list[sets++].count = 0;
+ return sets;
+}
+
+/**
+ * tid_rdma_find_phys_blocks_8k - get groups base on mr info
+ * @pages - pointer to an array of page structs
+ * @npages - number of pages
+ * @list - page set array to return
+ *
+ * This routine parses an array of pages to compute pagesets
+ * in an 8k compatible way.
+ *
+ * pages are tested two at a time, i, i + 1 for contiguous
+ * pages and i - 1 and i contiguous pages.
+ *
+ * If any condition is false, any accumlated pages are flushed and
+ * v0,v1 are emitted as separate PAGE_SIZE pagesets
+ *
+ * Otherwise, the current 8k is totaled for a future flush.
+ *
+ * Return:
+ * The number of pagesets
+ * list set with the returned number of pagesets
+ *
+ */
+static u32 tid_rdma_find_phys_blocks_8k(struct tid_rdma_flow *flow,
+ struct page **pages,
+ u32 npages,
+ struct tid_rdma_pageset *list)
+{
+ u32 idx, sets = 0, i;
+ u32 pagecnt = 0;
+ void *v0, *v1, *vm1;
+
+ if (!npages)
+ return 0;
+ for (idx = 0, i = 0, vm1 = NULL; i < npages; i += 2) {
+ /* get a new v0 */
+ v0 = page_address(pages[i]);
+ trace_hfi1_tid_flow_page(flow->req->qp, flow, i, 1, 0, v0);
+ v1 = i + 1 < npages ?
+ page_address(pages[i + 1]) : NULL;
+ trace_hfi1_tid_flow_page(flow->req->qp, flow, i, 1, 1, v1);
+ /* compare i, i + 1 vaddr */
+ if (v1 != (v0 + PAGE_SIZE)) {
+ /* flush out pages */
+ sets = tid_flush_pages(list, &idx, pagecnt, sets);
+ /* output v0,v1 as two pagesets */
+ list[sets].idx = idx++;
+ list[sets++].count = 1;
+ if (v1) {
+ list[sets].count = 1;
+ list[sets++].idx = idx++;
+ } else {
+ list[sets++].count = 0;
+ }
+ vm1 = NULL;
+ pagecnt = 0;
+ continue;
+ }
+ /* i,i+1 consecutive, look at i-1,i */
+ if (vm1 && v0 != (vm1 + PAGE_SIZE)) {
+ /* flush out pages */
+ sets = tid_flush_pages(list, &idx, pagecnt, sets);
+ pagecnt = 0;
+ }
+ /* pages will always be a multiple of 8k */
+ pagecnt += 2;
+ /* save i-1 */
+ vm1 = v1;
+ /* move to next pair */
+ }
+ /* dump residual pages at end */
+ sets = tid_flush_pages(list, &idx, npages - idx, sets);
+ /* by design cannot be odd sets */
+ WARN_ON(sets & 1);
+ return sets;
+}
+
+/**
+ * Find pages for one segment of a sge array represented by @ss. The function
+ * does not check the sge, the sge must have been checked for alignment with a
+ * prior call to hfi1_kern_trdma_ok. Other sge checking is done as part of
+ * rvt_lkey_ok and rvt_rkey_ok. Also, the function only modifies the local sge
+ * copy maintained in @ss->sge, the original sge is not modified.
+ *
+ * Unlike IB RDMA WRITE, we can't decrement ss->num_sge here because we are not
+ * releasing the MR reference count at the same time. Otherwise, we'll "leak"
+ * references to the MR. This difference requires that we keep track of progress
+ * into the sg_list. This is done by the cur_seg cursor in the tid_rdma_request
+ * structure.
+ */
+static u32 kern_find_pages(struct tid_rdma_flow *flow,
+ struct page **pages,
+ struct rvt_sge_state *ss, bool *last)
+{
+ struct tid_rdma_request *req = flow->req;
+ struct rvt_sge *sge = &ss->sge;
+ u32 length = flow->req->seg_len;
+ u32 len = PAGE_SIZE;
+ u32 i = 0;
+
+ while (length && req->isge < ss->num_sge) {
+ pages[i++] = virt_to_page(sge->vaddr);
+
+ sge->vaddr += len;
+ sge->length -= len;
+ sge->sge_length -= len;
+ if (!sge->sge_length) {
+ if (++req->isge < ss->num_sge)
+ *sge = ss->sg_list[req->isge - 1];
+ } else if (sge->length == 0 && sge->mr->lkey) {
+ if (++sge->n >= RVT_SEGSZ) {
+ ++sge->m;
+ sge->n = 0;
+ }
+ sge->vaddr = sge->mr->map[sge->m]->segs[sge->n].vaddr;
+ sge->length = sge->mr->map[sge->m]->segs[sge->n].length;
+ }
+ length -= len;
+ }
+
+ flow->length = flow->req->seg_len - length;
+ *last = req->isge == ss->num_sge ? false : true;
+ return i;
+}
+
+static void dma_unmap_flow(struct tid_rdma_flow *flow)
+{
+ struct hfi1_devdata *dd;
+ int i;
+ struct tid_rdma_pageset *pset;
+
+ dd = flow->req->rcd->dd;
+ for (i = 0, pset = &flow->pagesets[0]; i < flow->npagesets;
+ i++, pset++) {
+ if (pset->count && pset->addr) {
+ dma_unmap_page(&dd->pcidev->dev,
+ pset->addr,
+ PAGE_SIZE * pset->count,
+ DMA_FROM_DEVICE);
+ pset->mapped = 0;
+ }
+ }
+}
+
+static int dma_map_flow(struct tid_rdma_flow *flow, struct page **pages)
+{
+ int i;
+ struct hfi1_devdata *dd = flow->req->rcd->dd;
+ struct tid_rdma_pageset *pset;
+
+ for (i = 0, pset = &flow->pagesets[0]; i < flow->npagesets;
+ i++, pset++) {
+ if (pset->count) {
+ pset->addr = dma_map_page(&dd->pcidev->dev,
+ pages[pset->idx],
+ 0,
+ PAGE_SIZE * pset->count,
+ DMA_FROM_DEVICE);
+
+ if (dma_mapping_error(&dd->pcidev->dev, pset->addr)) {
+ dma_unmap_flow(flow);
+ return -ENOMEM;
+ }
+ pset->mapped = 1;
+ }
+ }
+ return 0;
+}
+
+static inline bool dma_mapped(struct tid_rdma_flow *flow)
+{
+ return !!flow->pagesets[0].mapped;
+}
+
+/*
+ * Get pages pointers and identify contiguous physical memory chunks for a
+ * segment. All segments are of length flow->req->seg_len.
+ */
+static int kern_get_phys_blocks(struct tid_rdma_flow *flow,
+ struct page **pages,
+ struct rvt_sge_state *ss, bool *last)
+{
+ u8 npages;
+
+ /* Reuse previously computed pagesets, if any */
+ if (flow->npagesets) {
+ trace_hfi1_tid_flow_alloc(flow->req->qp, flow->req->setup_head,
+ flow);
+ if (!dma_mapped(flow))
+ return dma_map_flow(flow, pages);
+ return 0;
+ }
+
+ npages = kern_find_pages(flow, pages, ss, last);
+
+ if (flow->req->qp->pmtu == enum_to_mtu(OPA_MTU_4096))
+ flow->npagesets =
+ tid_rdma_find_phys_blocks_4k(flow, pages, npages,
+ flow->pagesets);
+ else
+ flow->npagesets =
+ tid_rdma_find_phys_blocks_8k(flow, pages, npages,
+ flow->pagesets);
+
+ return dma_map_flow(flow, pages);
+}
+
+static inline void kern_add_tid_node(struct tid_rdma_flow *flow,
+ struct hfi1_ctxtdata *rcd, char *s,
+ struct tid_group *grp, u8 cnt)
+{
+ struct kern_tid_node *node = &flow->tnode[flow->tnode_cnt++];
+
+ WARN_ON_ONCE(flow->tnode_cnt >=
+ (TID_RDMA_MAX_SEGMENT_SIZE >> PAGE_SHIFT));
+ if (WARN_ON_ONCE(cnt & 1))
+ dd_dev_err(rcd->dd,
+ "unexpected odd allocation cnt %u map 0x%x used %u",
+ cnt, grp->map, grp->used);
+
+ node->grp = grp;
+ node->map = grp->map;
+ node->cnt = cnt;
+ trace_hfi1_tid_node_add(flow->req->qp, s, flow->tnode_cnt - 1,
+ grp->base, grp->map, grp->used, cnt);
+}
+
+/*
+ * Try to allocate pageset_count TID's from TID groups for a context
+ *
+ * This function allocates TID's without moving groups between lists or
+ * modifying grp->map. This is done as follows, being cogizant of the lists
+ * between which the TID groups will move:
+ * 1. First allocate complete groups of 8 TID's since this is more efficient,
+ * these groups will move from group->full without affecting used
+ * 2. If more TID's are needed allocate from used (will move from used->full or
+ * stay in used)
+ * 3. If we still don't have the required number of TID's go back and look again
+ * at a complete group (will move from group->used)
+ */
+static int kern_alloc_tids(struct tid_rdma_flow *flow)
+{
+ struct hfi1_ctxtdata *rcd = flow->req->rcd;
+ struct hfi1_devdata *dd = rcd->dd;
+ u32 ngroups, pageidx = 0;
+ struct tid_group *group = NULL, *used;
+ u8 use;
+
+ flow->tnode_cnt = 0;
+ ngroups = flow->npagesets / dd->rcv_entries.group_size;
+ if (!ngroups)
+ goto used_list;
+
+ /* First look at complete groups */
+ list_for_each_entry(group, &rcd->tid_group_list.list, list) {
+ kern_add_tid_node(flow, rcd, "complete groups", group,
+ group->size);
+
+ pageidx += group->size;
+ if (!--ngroups)
+ break;
+ }
+
+ if (pageidx >= flow->npagesets)
+ goto ok;
+
+used_list:
+ /* Now look at partially used groups */
+ list_for_each_entry(used, &rcd->tid_used_list.list, list) {
+ use = min_t(u32, flow->npagesets - pageidx,
+ used->size - used->used);
+ kern_add_tid_node(flow, rcd, "used groups", used, use);
+
+ pageidx += use;
+ if (pageidx >= flow->npagesets)
+ goto ok;
+ }
+
+ /*
+ * Look again at a complete group, continuing from where we left.
+ * However, if we are at the head, we have reached the end of the
+ * complete groups list from the first loop above
+ */
+ if (group && &group->list == &rcd->tid_group_list.list)
+ goto bail_eagain;
+ group = list_prepare_entry(group, &rcd->tid_group_list.list,
+ list);
+ if (list_is_last(&group->list, &rcd->tid_group_list.list))
+ goto bail_eagain;
+ group = list_next_entry(group, list);
+ use = min_t(u32, flow->npagesets - pageidx, group->size);
+ kern_add_tid_node(flow, rcd, "complete continue", group, use);
+ pageidx += use;
+ if (pageidx >= flow->npagesets)
+ goto ok;
+bail_eagain:
+ trace_hfi1_msg_alloc_tids(flow->req->qp, " insufficient tids: needed ",
+ (u64)flow->npagesets);
+ return -EAGAIN;
+ok:
+ return 0;
+}
+
+static void kern_program_rcv_group(struct tid_rdma_flow *flow, int grp_num,
+ u32 *pset_idx)
+{
+ struct hfi1_ctxtdata *rcd = flow->req->rcd;
+ struct hfi1_devdata *dd = rcd->dd;
+ struct kern_tid_node *node = &flow->tnode[grp_num];
+ struct tid_group *grp = node->grp;
+ struct tid_rdma_pageset *pset;
+ u32 pmtu_pg = flow->req->qp->pmtu >> PAGE_SHIFT;
+ u32 rcventry, npages = 0, pair = 0, tidctrl;
+ u8 i, cnt = 0;
+
+ for (i = 0; i < grp->size; i++) {
+ rcventry = grp->base + i;
+
+ if (node->map & BIT(i) || cnt >= node->cnt) {
+ rcv_array_wc_fill(dd, rcventry);
+ continue;
+ }
+ pset = &flow->pagesets[(*pset_idx)++];
+ if (pset->count) {
+ hfi1_put_tid(dd, rcventry, PT_EXPECTED,
+ pset->addr, trdma_pset_order(pset));
+ } else {
+ hfi1_put_tid(dd, rcventry, PT_INVALID, 0, 0);
+ }
+ npages += pset->count;
+
+ rcventry -= rcd->expected_base;
+ tidctrl = pair ? 0x3 : rcventry & 0x1 ? 0x2 : 0x1;
+ /*
+ * A single TID entry will be used to use a rcvarr pair (with
+ * tidctrl 0x3), if ALL these are true (a) the bit pos is even
+ * (b) the group map shows current and the next bits as free
+ * indicating two consecutive rcvarry entries are available (c)
+ * we actually need 2 more entries
+ */
+ pair = !(i & 0x1) && !((node->map >> i) & 0x3) &&
+ node->cnt >= cnt + 2;
+ if (!pair) {
+ if (!pset->count)
+ tidctrl = 0x1;
+ flow->tid_entry[flow->tidcnt++] =
+ EXP_TID_SET(IDX, rcventry >> 1) |
+ EXP_TID_SET(CTRL, tidctrl) |
+ EXP_TID_SET(LEN, npages);
+ trace_hfi1_tid_entry_alloc(/* entry */
+ flow->req->qp, flow->tidcnt - 1,
+ flow->tid_entry[flow->tidcnt - 1]);
+
+ /* Efficient DIV_ROUND_UP(npages, pmtu_pg) */
+ flow->npkts += (npages + pmtu_pg - 1) >> ilog2(pmtu_pg);
+ npages = 0;
+ }
+
+ if (grp->used == grp->size - 1)
+ tid_group_move(grp, &rcd->tid_used_list,
+ &rcd->tid_full_list);
+ else if (!grp->used)
+ tid_group_move(grp, &rcd->tid_group_list,
+ &rcd->tid_used_list);
+
+ grp->used++;
+ grp->map |= BIT(i);
+ cnt++;
+ }
+}
+
+static void kern_unprogram_rcv_group(struct tid_rdma_flow *flow, int grp_num)
+{
+ struct hfi1_ctxtdata *rcd = flow->req->rcd;
+ struct hfi1_devdata *dd = rcd->dd;
+ struct kern_tid_node *node = &flow->tnode[grp_num];
+ struct tid_group *grp = node->grp;
+ u32 rcventry;
+ u8 i, cnt = 0;
+
+ for (i = 0; i < grp->size; i++) {
+ rcventry = grp->base + i;
+
+ if (node->map & BIT(i) || cnt >= node->cnt) {
+ rcv_array_wc_fill(dd, rcventry);
+ continue;
+ }
+
+ hfi1_put_tid(dd, rcventry, PT_INVALID, 0, 0);
+
+ grp->used--;
+ grp->map &= ~BIT(i);
+ cnt++;
+
+ if (grp->used == grp->size - 1)
+ tid_group_move(grp, &rcd->tid_full_list,
+ &rcd->tid_used_list);
+ else if (!grp->used)
+ tid_group_move(grp, &rcd->tid_used_list,
+ &rcd->tid_group_list);
+ }
+ if (WARN_ON_ONCE(cnt & 1)) {
+ struct hfi1_ctxtdata *rcd = flow->req->rcd;
+ struct hfi1_devdata *dd = rcd->dd;
+
+ dd_dev_err(dd, "unexpected odd free cnt %u map 0x%x used %u",
+ cnt, grp->map, grp->used);
+ }
+}
+
+static void kern_program_rcvarray(struct tid_rdma_flow *flow)
+{
+ u32 pset_idx = 0;
+ int i;
+
+ flow->npkts = 0;
+ flow->tidcnt = 0;
+ for (i = 0; i < flow->tnode_cnt; i++)
+ kern_program_rcv_group(flow, i, &pset_idx);
+ trace_hfi1_tid_flow_alloc(flow->req->qp, flow->req->setup_head, flow);
+}
+
+/**
+ * hfi1_kern_exp_rcv_setup() - setup TID's and flow for one segment of a
+ * TID RDMA request
+ *
+ * @req: TID RDMA request for which the segment/flow is being set up
+ * @ss: sge state, maintains state across successive segments of a sge
+ * @last: set to true after the last sge segment has been processed
+ *
+ * This function
+ * (1) finds a free flow entry in the flow circular buffer
+ * (2) finds pages and continuous physical chunks constituing one segment
+ * of an sge
+ * (3) allocates TID group entries for those chunks
+ * (4) programs rcvarray entries in the hardware corresponding to those
+ * TID's
+ * (5) computes a tidarray with formatted TID entries which can be sent
+ * to the sender
+ * (6) Reserves and programs HW flows.
+ * (7) It also manages queing the QP when TID/flow resources are not
+ * available.
+ *
+ * @req points to struct tid_rdma_request of which the segments are a part. The
+ * function uses qp, rcd and seg_len members of @req. In the absence of errors,
+ * req->flow_idx is the index of the flow which has been prepared in this
+ * invocation of function call. With flow = &req->flows[req->flow_idx],
+ * flow->tid_entry contains the TID array which the sender can use for TID RDMA
+ * sends and flow->npkts contains number of packets required to send the
+ * segment.
+ *
+ * hfi1_check_sge_align should be called prior to calling this function and if
+ * it signals error TID RDMA cannot be used for this sge and this function
+ * should not be called.
+ *
+ * For the queuing, caller must hold the flow->req->qp s_lock from the send
+ * engine and the function will procure the exp_lock.
+ *
+ * Return:
+ * The function returns -EAGAIN if sufficient number of TID/flow resources to
+ * map the segment could not be allocated. In this case the function should be
+ * called again with previous arguments to retry the TID allocation. There are
+ * no other error returns. The function returns 0 on success.
+ */
+int hfi1_kern_exp_rcv_setup(struct tid_rdma_request *req,
+ struct rvt_sge_state *ss, bool *last)
+ __must_hold(&req->qp->s_lock)
+{
+ struct tid_rdma_flow *flow = &req->flows[req->setup_head];
+ struct hfi1_ctxtdata *rcd = req->rcd;
+ struct hfi1_qp_priv *qpriv = req->qp->priv;
+ unsigned long flags;
+ struct rvt_qp *fqp;
+ u16 clear_tail = req->clear_tail;
+
+ lockdep_assert_held(&req->qp->s_lock);
+ /*
+ * We return error if either (a) we don't have space in the flow
+ * circular buffer, or (b) we already have max entries in the buffer.
+ * Max entries depend on the type of request we are processing and the
+ * negotiated TID RDMA parameters.
+ */
+ if (!CIRC_SPACE(req->setup_head, clear_tail, MAX_FLOWS) ||
+ CIRC_CNT(req->setup_head, clear_tail, MAX_FLOWS) >=
+ req->n_flows)
+ return -EINVAL;
+
+ /*
+ * Get pages, identify contiguous physical memory chunks for the segment
+ * If we can not determine a DMA address mapping we will treat it just
+ * like if we ran out of space above.
+ */
+ if (kern_get_phys_blocks(flow, qpriv->pages, ss, last)) {
+ hfi1_wait_kmem(flow->req->qp);
+ return -ENOMEM;
+ }
+
+ spin_lock_irqsave(&rcd->exp_lock, flags);
+ if (kernel_tid_waiters(rcd, &rcd->rarr_queue, flow->req->qp))
+ goto queue;
+
+ /*
+ * At this point we know the number of pagesets and hence the number of
+ * TID's to map the segment. Allocate the TID's from the TID groups. If
+ * we cannot allocate the required number we exit and try again later
+ */
+ if (kern_alloc_tids(flow))
+ goto queue;
+ /*
+ * Finally program the TID entries with the pagesets, compute the
+ * tidarray and enable the HW flow
+ */
+ kern_program_rcvarray(flow);
+
+ /*
+ * Setup the flow state with relevant information.
+ * This information is used for tracking the sequence of data packets
+ * for the segment.
+ * The flow is setup here as this is the most accurate time and place
+ * to do so. Doing at a later time runs the risk of the flow data in
+ * qpriv getting out of sync.
+ */
+ memset(&flow->flow_state, 0x0, sizeof(flow->flow_state));
+ flow->idx = qpriv->flow_state.index;
+ flow->flow_state.generation = qpriv->flow_state.generation;
+ flow->flow_state.spsn = qpriv->flow_state.psn;
+ flow->flow_state.lpsn = flow->flow_state.spsn + flow->npkts - 1;
+ flow->flow_state.r_next_psn =
+ full_flow_psn(flow, flow->flow_state.spsn);
+ qpriv->flow_state.psn += flow->npkts;
+
+ dequeue_tid_waiter(rcd, &rcd->rarr_queue, flow->req->qp);
+ /* get head before dropping lock */
+ fqp = first_qp(rcd, &rcd->rarr_queue);
+ spin_unlock_irqrestore(&rcd->exp_lock, flags);
+ tid_rdma_schedule_tid_wakeup(fqp);
+
+ req->setup_head = (req->setup_head + 1) & (MAX_FLOWS - 1);
+ return 0;
+queue:
+ queue_qp_for_tid_wait(rcd, &rcd->rarr_queue, flow->req->qp);
+ spin_unlock_irqrestore(&rcd->exp_lock, flags);
+ return -EAGAIN;
+}
+
+static void hfi1_tid_rdma_reset_flow(struct tid_rdma_flow *flow)
+{
+ flow->npagesets = 0;
+}
+
+/*
+ * This function is called after one segment has been successfully sent to
+ * release the flow and TID HW/SW resources for that segment. The segments for a
+ * TID RDMA request are setup and cleared in FIFO order which is managed using a
+ * circular buffer.
+ */
+int hfi1_kern_exp_rcv_clear(struct tid_rdma_request *req)
+ __must_hold(&req->qp->s_lock)
+{
+ struct tid_rdma_flow *flow = &req->flows[req->clear_tail];
+ struct hfi1_ctxtdata *rcd = req->rcd;
+ unsigned long flags;
+ int i;
+ struct rvt_qp *fqp;
+
+ lockdep_assert_held(&req->qp->s_lock);
+ /* Exit if we have nothing in the flow circular buffer */
+ if (!CIRC_CNT(req->setup_head, req->clear_tail, MAX_FLOWS))
+ return -EINVAL;
+
+ spin_lock_irqsave(&rcd->exp_lock, flags);
+
+ for (i = 0; i < flow->tnode_cnt; i++)
+ kern_unprogram_rcv_group(flow, i);
+ /* To prevent double unprogramming */
+ flow->tnode_cnt = 0;
+ /* get head before dropping lock */
+ fqp = first_qp(rcd, &rcd->rarr_queue);
+ spin_unlock_irqrestore(&rcd->exp_lock, flags);
+
+ dma_unmap_flow(flow);
+
+ hfi1_tid_rdma_reset_flow(flow);
+ req->clear_tail = (req->clear_tail + 1) & (MAX_FLOWS - 1);
+
+ if (fqp == req->qp) {
+ __trigger_tid_waiter(fqp);
+ rvt_put_qp(fqp);
+ } else {
+ tid_rdma_schedule_tid_wakeup(fqp);
+ }
+
+ return 0;
+}
+
+/*
+ * This function is called to release all the tid entries for
+ * a request.
+ */
+void hfi1_kern_exp_rcv_clear_all(struct tid_rdma_request *req)
+ __must_hold(&req->qp->s_lock)
+{
+ /* Use memory barrier for proper ordering */
+ while (CIRC_CNT(req->setup_head, req->clear_tail, MAX_FLOWS)) {
+ if (hfi1_kern_exp_rcv_clear(req))
+ break;
+ }
+}
+
+/**
+ * hfi1_kern_exp_rcv_free_flows - free priviously allocated flow information
+ * @req - the tid rdma request to be cleaned
+ */
+static void hfi1_kern_exp_rcv_free_flows(struct tid_rdma_request *req)
+{
+ kfree(req->flows);
+ req->flows = NULL;
+}
+
+/**
+ * __trdma_clean_swqe - clean up for large sized QPs
+ * @qp: the queue patch
+ * @wqe: the send wqe
+ */
+void __trdma_clean_swqe(struct rvt_qp *qp, struct rvt_swqe *wqe)
+{
+ struct hfi1_swqe_priv *p = wqe->priv;
+
+ hfi1_kern_exp_rcv_free_flows(&p->tid_req);
+}
+
+/*
+ * This can be called at QP create time or in the data path.
+ */
+static int hfi1_kern_exp_rcv_alloc_flows(struct tid_rdma_request *req,
+ gfp_t gfp)
+{
+ struct tid_rdma_flow *flows;
+ int i;
+
+ if (likely(req->flows))
+ return 0;
+ flows = kmalloc_node(MAX_FLOWS * sizeof(*flows), gfp,
+ req->rcd->numa_id);
+ if (!flows)
+ return -ENOMEM;
+ /* mini init */
+ for (i = 0; i < MAX_FLOWS; i++) {
+ flows[i].req = req;
+ flows[i].npagesets = 0;
+ flows[i].pagesets[0].mapped = 0;
+ }
+ req->flows = flows;
+ return 0;
+}
+
+static void hfi1_init_trdma_req(struct rvt_qp *qp,
+ struct tid_rdma_request *req)
+{
+ struct hfi1_qp_priv *qpriv = qp->priv;
+
+ /*
+ * Initialize various TID RDMA request variables.
+ * These variables are "static", which is why they
+ * can be pre-initialized here before the WRs has
+ * even been submitted.
+ * However, non-NULL values for these variables do not
+ * imply that this WQE has been enabled for TID RDMA.
+ * Drivers should check the WQE's opcode to determine
+ * if a request is a TID RDMA one or not.
+ */
+ req->qp = qp;
+ req->rcd = qpriv->rcd;
+}
+
+u64 hfi1_access_sw_tid_wait(const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = context;
+
+ return dd->verbs_dev.n_tidwait;
+}
+
+static struct tid_rdma_flow *find_flow_ib(struct tid_rdma_request *req,
+ u32 psn, u16 *fidx)
+{
+ u16 head, tail;
+ struct tid_rdma_flow *flow;
+
+ head = req->setup_head;
+ tail = req->clear_tail;
+ for ( ; CIRC_CNT(head, tail, MAX_FLOWS);
+ tail = CIRC_NEXT(tail, MAX_FLOWS)) {
+ flow = &req->flows[tail];
+ if (cmp_psn(psn, flow->flow_state.ib_spsn) >= 0 &&
+ cmp_psn(psn, flow->flow_state.ib_lpsn) <= 0) {
+ if (fidx)
+ *fidx = tail;
+ return flow;
+ }
+ }
+ return NULL;
+}
+
+static struct tid_rdma_flow *
+__find_flow_ranged(struct tid_rdma_request *req, u16 head, u16 tail,
+ u32 psn, u16 *fidx)
+{
+ for ( ; CIRC_CNT(head, tail, MAX_FLOWS);
+ tail = CIRC_NEXT(tail, MAX_FLOWS)) {
+ struct tid_rdma_flow *flow = &req->flows[tail];
+ u32 spsn, lpsn;
+
+ spsn = full_flow_psn(flow, flow->flow_state.spsn);
+ lpsn = full_flow_psn(flow, flow->flow_state.lpsn);
+
+ if (cmp_psn(psn, spsn) >= 0 && cmp_psn(psn, lpsn) <= 0) {
+ if (fidx)
+ *fidx = tail;
+ return flow;
+ }
+ }
+ return NULL;
+}
+
+static struct tid_rdma_flow *find_flow(struct tid_rdma_request *req,
+ u32 psn, u16 *fidx)
+{
+ return __find_flow_ranged(req, req->setup_head, req->clear_tail, psn,
+ fidx);
+}
+
+/* TID RDMA READ functions */
+u32 hfi1_build_tid_rdma_read_packet(struct rvt_swqe *wqe,
+ struct ib_other_headers *ohdr, u32 *bth1,
+ u32 *bth2, u32 *len)
+{
+ struct tid_rdma_request *req = wqe_to_tid_req(wqe);
+ struct tid_rdma_flow *flow = &req->flows[req->flow_idx];
+ struct rvt_qp *qp = req->qp;
+ struct hfi1_qp_priv *qpriv = qp->priv;
+ struct hfi1_swqe_priv *wpriv = wqe->priv;
+ struct tid_rdma_read_req *rreq = &ohdr->u.tid_rdma.r_req;
+ struct tid_rdma_params *remote;
+ u32 req_len = 0;
+ void *req_addr = NULL;
+
+ /* This is the IB psn used to send the request */
+ *bth2 = mask_psn(flow->flow_state.ib_spsn + flow->pkt);
+ trace_hfi1_tid_flow_build_read_pkt(qp, req->flow_idx, flow);
+
+ /* TID Entries for TID RDMA READ payload */
+ req_addr = &flow->tid_entry[flow->tid_idx];
+ req_len = sizeof(*flow->tid_entry) *
+ (flow->tidcnt - flow->tid_idx);
+
+ memset(&ohdr->u.tid_rdma.r_req, 0, sizeof(ohdr->u.tid_rdma.r_req));
+ wpriv->ss.sge.vaddr = req_addr;
+ wpriv->ss.sge.sge_length = req_len;
+ wpriv->ss.sge.length = wpriv->ss.sge.sge_length;
+ /*
+ * We can safely zero these out. Since the first SGE covers the
+ * entire packet, nothing else should even look at the MR.
+ */
+ wpriv->ss.sge.mr = NULL;
+ wpriv->ss.sge.m = 0;
+ wpriv->ss.sge.n = 0;
+
+ wpriv->ss.sg_list = NULL;
+ wpriv->ss.total_len = wpriv->ss.sge.sge_length;
+ wpriv->ss.num_sge = 1;
+
+ /* Construct the TID RDMA READ REQ packet header */
+ rcu_read_lock();
+ remote = rcu_dereference(qpriv->tid_rdma.remote);
+
+ KDETH_RESET(rreq->kdeth0, KVER, 0x1);
+ KDETH_RESET(rreq->kdeth1, JKEY, remote->jkey);
+ rreq->reth.vaddr = cpu_to_be64(wqe->rdma_wr.remote_addr +
+ req->cur_seg * req->seg_len + flow->sent);
+ rreq->reth.rkey = cpu_to_be32(wqe->rdma_wr.rkey);
+ rreq->reth.length = cpu_to_be32(*len);
+ rreq->tid_flow_psn =
+ cpu_to_be32((flow->flow_state.generation <<
+ HFI1_KDETH_BTH_SEQ_SHIFT) |
+ ((flow->flow_state.spsn + flow->pkt) &
+ HFI1_KDETH_BTH_SEQ_MASK));
+ rreq->tid_flow_qp =
+ cpu_to_be32(qpriv->tid_rdma.local.qp |
+ ((flow->idx & TID_RDMA_DESTQP_FLOW_MASK) <<
+ TID_RDMA_DESTQP_FLOW_SHIFT) |
+ qpriv->rcd->ctxt);
+ rreq->verbs_qp = cpu_to_be32(qp->remote_qpn);
+ *bth1 &= ~RVT_QPN_MASK;
+ *bth1 |= remote->qp;
+ *bth2 |= IB_BTH_REQ_ACK;
+ rcu_read_unlock();
+
+ /* We are done with this segment */
+ flow->sent += *len;
+ req->cur_seg++;
+ qp->s_state = TID_OP(READ_REQ);
+ req->ack_pending++;
+ req->flow_idx = (req->flow_idx + 1) & (MAX_FLOWS - 1);
+ qpriv->pending_tid_r_segs++;
+ qp->s_num_rd_atomic++;
+
+ /* Set the TID RDMA READ request payload size */
+ *len = req_len;
+
+ return sizeof(ohdr->u.tid_rdma.r_req) / sizeof(u32);
+}
+
+/*
+ * @len: contains the data length to read upon entry and the read request
+ * payload length upon exit.
+ */
+u32 hfi1_build_tid_rdma_read_req(struct rvt_qp *qp, struct rvt_swqe *wqe,
+ struct ib_other_headers *ohdr, u32 *bth1,
+ u32 *bth2, u32 *len)
+ __must_hold(&qp->s_lock)
+{
+ struct hfi1_qp_priv *qpriv = qp->priv;
+ struct tid_rdma_request *req = wqe_to_tid_req(wqe);
+ struct tid_rdma_flow *flow = NULL;
+ u32 hdwords = 0;
+ bool last;
+ bool retry = true;
+ u32 npkts = rvt_div_round_up_mtu(qp, *len);
+
+ trace_hfi1_tid_req_build_read_req(qp, 0, wqe->wr.opcode, wqe->psn,
+ wqe->lpsn, req);
+ /*
+ * Check sync conditions. Make sure that there are no pending
+ * segments before freeing the flow.
+ */
+sync_check:
+ if (req->state == TID_REQUEST_SYNC) {
+ if (qpriv->pending_tid_r_segs)
+ goto done;
+
+ hfi1_kern_clear_hw_flow(req->rcd, qp);
+ req->state = TID_REQUEST_ACTIVE;
+ }
+
+ /*
+ * If the request for this segment is resent, the tid resources should
+ * have been allocated before. In this case, req->flow_idx should
+ * fall behind req->setup_head.
+ */
+ if (req->flow_idx == req->setup_head) {
+ retry = false;
+ if (req->state == TID_REQUEST_RESEND) {
+ /*
+ * This is the first new segment for a request whose
+ * earlier segments have been re-sent. We need to
+ * set up the sge pointer correctly.
+ */
+ restart_sge(&qp->s_sge, wqe, req->s_next_psn,
+ qp->pmtu);
+ req->isge = 0;
+ req->state = TID_REQUEST_ACTIVE;
+ }
+
+ /*
+ * Check sync. The last PSN of each generation is reserved for
+ * RESYNC.
+ */
+ if ((qpriv->flow_state.psn + npkts) > MAX_TID_FLOW_PSN - 1) {
+ req->state = TID_REQUEST_SYNC;
+ goto sync_check;
+ }
+
+ /* Allocate the flow if not yet */
+ if (hfi1_kern_setup_hw_flow(qpriv->rcd, qp))
+ goto done;
+
+ /*
+ * The following call will advance req->setup_head after
+ * allocating the tid entries.
+ */
+ if (hfi1_kern_exp_rcv_setup(req, &qp->s_sge, &last)) {
+ req->state = TID_REQUEST_QUEUED;
+
+ /*
+ * We don't have resources for this segment. The QP has
+ * already been queued.
+ */
+ goto done;
+ }
+ }
+
+ /* req->flow_idx should only be one slot behind req->setup_head */
+ flow = &req->flows[req->flow_idx];
+ flow->pkt = 0;
+ flow->tid_idx = 0;
+ flow->sent = 0;
+ if (!retry) {
+ /* Set the first and last IB PSN for the flow in use.*/
+ flow->flow_state.ib_spsn = req->s_next_psn;
+ flow->flow_state.ib_lpsn =
+ flow->flow_state.ib_spsn + flow->npkts - 1;
+ }
+
+ /* Calculate the next segment start psn.*/
+ req->s_next_psn += flow->npkts;
+
+ /* Build the packet header */
+ hdwords = hfi1_build_tid_rdma_read_packet(wqe, ohdr, bth1, bth2, len);
+done:
+ return hdwords;
+}
+
+/*
+ * Validate and accept the TID RDMA READ request parameters.
+ * Return 0 if the request is accepted successfully;
+ * Return 1 otherwise.
+ */
+static int tid_rdma_rcv_read_request(struct rvt_qp *qp,
+ struct rvt_ack_entry *e,
+ struct hfi1_packet *packet,
+ struct ib_other_headers *ohdr,
+ u32 bth0, u32 psn, u64 vaddr, u32 len)
+{
+ struct hfi1_qp_priv *qpriv = qp->priv;
+ struct tid_rdma_request *req;
+ struct tid_rdma_flow *flow;
+ u32 flow_psn, i, tidlen = 0, pktlen, tlen;
+
+ req = ack_to_tid_req(e);
+
+ /* Validate the payload first */
+ flow = &req->flows[req->setup_head];
+
+ /* payload length = packet length - (header length + ICRC length) */
+ pktlen = packet->tlen - (packet->hlen + 4);
+ if (pktlen > sizeof(flow->tid_entry))
+ return 1;
+ memcpy(flow->tid_entry, packet->ebuf, pktlen);
+ flow->tidcnt = pktlen / sizeof(*flow->tid_entry);
+
+ /*
+ * Walk the TID_ENTRY list to make sure we have enough space for a
+ * complete segment. Also calculate the number of required packets.
+ */
+ flow->npkts = rvt_div_round_up_mtu(qp, len);
+ for (i = 0; i < flow->tidcnt; i++) {
+ trace_hfi1_tid_entry_rcv_read_req(qp, i,
+ flow->tid_entry[i]);
+ tlen = EXP_TID_GET(flow->tid_entry[i], LEN);
+ if (!tlen)
+ return 1;
+
+ /*
+ * For tid pair (tidctr == 3), the buffer size of the pair
+ * should be the sum of the buffer size described by each
+ * tid entry. However, only the first entry needs to be
+ * specified in the request (see WFR HAS Section 8.5.7.1).
+ */
+ tidlen += tlen;
+ }
+ if (tidlen * PAGE_SIZE < len)
+ return 1;
+
+ /* Empty the flow array */
+ req->clear_tail = req->setup_head;
+ flow->pkt = 0;
+ flow->tid_idx = 0;
+ flow->tid_offset = 0;
+ flow->sent = 0;
+ flow->tid_qpn = be32_to_cpu(ohdr->u.tid_rdma.r_req.tid_flow_qp);
+ flow->idx = (flow->tid_qpn >> TID_RDMA_DESTQP_FLOW_SHIFT) &
+ TID_RDMA_DESTQP_FLOW_MASK;
+ flow_psn = mask_psn(be32_to_cpu(ohdr->u.tid_rdma.r_req.tid_flow_psn));
+ flow->flow_state.generation = flow_psn >> HFI1_KDETH_BTH_SEQ_SHIFT;
+ flow->flow_state.spsn = flow_psn & HFI1_KDETH_BTH_SEQ_MASK;
+ flow->length = len;
+
+ flow->flow_state.lpsn = flow->flow_state.spsn +
+ flow->npkts - 1;
+ flow->flow_state.ib_spsn = psn;
+ flow->flow_state.ib_lpsn = flow->flow_state.ib_spsn + flow->npkts - 1;
+
+ trace_hfi1_tid_flow_rcv_read_req(qp, req->setup_head, flow);
+ /* Set the initial flow index to the current flow. */
+ req->flow_idx = req->setup_head;
+
+ /* advance circular buffer head */
+ req->setup_head = (req->setup_head + 1) & (MAX_FLOWS - 1);
+
+ /*
+ * Compute last PSN for request.
+ */
+ e->opcode = (bth0 >> 24) & 0xff;
+ e->psn = psn;
+ e->lpsn = psn + flow->npkts - 1;
+ e->sent = 0;
+
+ req->n_flows = qpriv->tid_rdma.local.max_read;
+ req->state = TID_REQUEST_ACTIVE;
+ req->cur_seg = 0;
+ req->comp_seg = 0;
+ req->ack_seg = 0;
+ req->isge = 0;
+ req->seg_len = qpriv->tid_rdma.local.max_len;
+ req->total_len = len;
+ req->total_segs = 1;
+ req->r_flow_psn = e->psn;
+
+ trace_hfi1_tid_req_rcv_read_req(qp, 0, e->opcode, e->psn, e->lpsn,
+ req);
+ return 0;
+}
+
+static int tid_rdma_rcv_error(struct hfi1_packet *packet,
+ struct ib_other_headers *ohdr,
+ struct rvt_qp *qp, u32 psn, int diff)
+{
+ struct hfi1_ibport *ibp = to_iport(qp->ibqp.device, qp->port_num);
+ struct hfi1_ctxtdata *rcd = ((struct hfi1_qp_priv *)qp->priv)->rcd;
+ struct hfi1_ibdev *dev = to_idev(qp->ibqp.device);
+ struct hfi1_qp_priv *qpriv = qp->priv;
+ struct rvt_ack_entry *e;
+ struct tid_rdma_request *req;
+ unsigned long flags;
+ u8 prev;
+ bool old_req;
+
+ trace_hfi1_rsp_tid_rcv_error(qp, psn);
+ trace_hfi1_tid_rdma_rcv_err(qp, 0, psn, diff);
+ if (diff > 0) {
+ /* sequence error */
+ if (!qp->r_nak_state) {
+ ibp->rvp.n_rc_seqnak++;
+ qp->r_nak_state = IB_NAK_PSN_ERROR;
+ qp->r_ack_psn = qp->r_psn;
+ rc_defered_ack(rcd, qp);
+ }
+ goto done;
+ }
+
+ ibp->rvp.n_rc_dupreq++;
+
+ spin_lock_irqsave(&qp->s_lock, flags);
+ e = find_prev_entry(qp, psn, &prev, NULL, &old_req);
+ if (!e || (e->opcode != TID_OP(READ_REQ) &&
+ e->opcode != TID_OP(WRITE_REQ)))
+ goto unlock;
+
+ req = ack_to_tid_req(e);
+ req->r_flow_psn = psn;
+ trace_hfi1_tid_req_rcv_err(qp, 0, e->opcode, e->psn, e->lpsn, req);
+ if (e->opcode == TID_OP(READ_REQ)) {
+ struct ib_reth *reth;
+ u32 offset;
+ u32 len;
+ u32 rkey;
+ u64 vaddr;
+ int ok;
+ u32 bth0;
+
+ reth = &ohdr->u.tid_rdma.r_req.reth;
+ /*
+ * The requester always restarts from the start of the original
+ * request.
+ */
+ offset = delta_psn(psn, e->psn) * qp->pmtu;
+ len = be32_to_cpu(reth->length);
+ if (psn != e->psn || len != req->total_len)
+ goto unlock;
+
+ if (e->rdma_sge.mr) {
+ rvt_put_mr(e->rdma_sge.mr);
+ e->rdma_sge.mr = NULL;
+ }
+
+ rkey = be32_to_cpu(reth->rkey);
+ vaddr = get_ib_reth_vaddr(reth);
+
+ qp->r_len = len;
+ ok = rvt_rkey_ok(qp, &e->rdma_sge, len, vaddr, rkey,
+ IB_ACCESS_REMOTE_READ);
+ if (unlikely(!ok))
+ goto unlock;
+
+ /*
+ * If all the response packets for the current request have
+ * been sent out and this request is complete (old_request
+ * == false) and the TID flow may be unusable (the
+ * req->clear_tail is advanced). However, when an earlier
+ * request is received, this request will not be complete any
+ * more (qp->s_tail_ack_queue is moved back, see below).
+ * Consequently, we need to update the TID flow info everytime
+ * a duplicate request is received.
+ */
+ bth0 = be32_to_cpu(ohdr->bth[0]);
+ if (tid_rdma_rcv_read_request(qp, e, packet, ohdr, bth0, psn,
+ vaddr, len))
+ goto unlock;
+
+ /*
+ * True if the request is already scheduled (between
+ * qp->s_tail_ack_queue and qp->r_head_ack_queue);
+ */
+ if (old_req)
+ goto unlock;
+ } else {
+ struct flow_state *fstate;
+ bool schedule = false;
+ u8 i;
+
+ if (req->state == TID_REQUEST_RESEND) {
+ req->state = TID_REQUEST_RESEND_ACTIVE;
+ } else if (req->state == TID_REQUEST_INIT_RESEND) {
+ req->state = TID_REQUEST_INIT;
+ schedule = true;
+ }
+
+ /*
+ * True if the request is already scheduled (between
+ * qp->s_tail_ack_queue and qp->r_head_ack_queue).
+ * Also, don't change requests, which are at the SYNC
+ * point and haven't generated any responses yet.
+ * There is nothing to retransmit for them yet.
+ */
+ if (old_req || req->state == TID_REQUEST_INIT ||
+ (req->state == TID_REQUEST_SYNC && !req->cur_seg)) {
+ for (i = prev + 1; ; i++) {
+ if (i > rvt_size_atomic(&dev->rdi))
+ i = 0;
+ if (i == qp->r_head_ack_queue)
+ break;
+ e = &qp->s_ack_queue[i];
+ req = ack_to_tid_req(e);
+ if (e->opcode == TID_OP(WRITE_REQ) &&
+ req->state == TID_REQUEST_INIT)
+ req->state = TID_REQUEST_INIT_RESEND;
+ }
+ /*
+ * If the state of the request has been changed,
+ * the first leg needs to get scheduled in order to
+ * pick up the change. Otherwise, normal response
+ * processing should take care of it.
+ */
+ if (!schedule)
+ goto unlock;
+ }
+
+ /*
+ * If there is no more allocated segment, just schedule the qp
+ * without changing any state.
+ */
+ if (req->clear_tail == req->setup_head)
+ goto schedule;
+ /*
+ * If this request has sent responses for segments, which have
+ * not received data yet (flow_idx != clear_tail), the flow_idx
+ * pointer needs to be adjusted so the same responses can be
+ * re-sent.
+ */
+ if (CIRC_CNT(req->flow_idx, req->clear_tail, MAX_FLOWS)) {
+ fstate = &req->flows[req->clear_tail].flow_state;
+ qpriv->pending_tid_w_segs -=
+ CIRC_CNT(req->flow_idx, req->clear_tail,
+ MAX_FLOWS);
+ req->flow_idx =
+ CIRC_ADD(req->clear_tail,
+ delta_psn(psn, fstate->resp_ib_psn),
+ MAX_FLOWS);
+ qpriv->pending_tid_w_segs +=
+ delta_psn(psn, fstate->resp_ib_psn);
+ /*
+ * When flow_idx == setup_head, we've gotten a duplicate
+ * request for a segment, which has not been allocated
+ * yet. In that case, don't adjust this request.
+ * However, we still want to go through the loop below
+ * to adjust all subsequent requests.
+ */
+ if (CIRC_CNT(req->setup_head, req->flow_idx,
+ MAX_FLOWS)) {
+ req->cur_seg = delta_psn(psn, e->psn);
+ req->state = TID_REQUEST_RESEND_ACTIVE;
+ }
+ }
+
+ for (i = prev + 1; ; i++) {
+ /*
+ * Look at everything up to and including
+ * s_tail_ack_queue
+ */
+ if (i > rvt_size_atomic(&dev->rdi))
+ i = 0;
+ if (i == qp->r_head_ack_queue)
+ break;
+ e = &qp->s_ack_queue[i];
+ req = ack_to_tid_req(e);
+ trace_hfi1_tid_req_rcv_err(qp, 0, e->opcode, e->psn,
+ e->lpsn, req);
+ if (e->opcode != TID_OP(WRITE_REQ) ||
+ req->cur_seg == req->comp_seg ||
+ req->state == TID_REQUEST_INIT ||
+ req->state == TID_REQUEST_INIT_RESEND) {
+ if (req->state == TID_REQUEST_INIT)
+ req->state = TID_REQUEST_INIT_RESEND;
+ continue;
+ }
+ qpriv->pending_tid_w_segs -=
+ CIRC_CNT(req->flow_idx,
+ req->clear_tail,
+ MAX_FLOWS);
+ req->flow_idx = req->clear_tail;
+ req->state = TID_REQUEST_RESEND;
+ req->cur_seg = req->comp_seg;
+ }
+ qpriv->s_flags &= ~HFI1_R_TID_WAIT_INTERLCK;
+ }
+ /* Re-process old requests.*/
+ if (qp->s_acked_ack_queue == qp->s_tail_ack_queue)
+ qp->s_acked_ack_queue = prev;
+ qp->s_tail_ack_queue = prev;
+ /*
+ * Since the qp->s_tail_ack_queue is modified, the
+ * qp->s_ack_state must be changed to re-initialize
+ * qp->s_ack_rdma_sge; Otherwise, we will end up in
+ * wrong memory region.
+ */
+ qp->s_ack_state = OP(ACKNOWLEDGE);
+schedule:
+ /*
+ * It's possible to receive a retry psn that is earlier than an RNRNAK
+ * psn. In this case, the rnrnak state should be cleared.
+ */
+ if (qpriv->rnr_nak_state) {
+ qp->s_nak_state = 0;
+ qpriv->rnr_nak_state = TID_RNR_NAK_INIT;
+ qp->r_psn = e->lpsn + 1;
+ hfi1_tid_write_alloc_resources(qp, true);
+ }
+
+ qp->r_state = e->opcode;
+ qp->r_nak_state = 0;
+ qp->s_flags |= RVT_S_RESP_PENDING;
+ hfi1_schedule_send(qp);
+unlock:
+ spin_unlock_irqrestore(&qp->s_lock, flags);
+done:
+ return 1;
+}
+
+void hfi1_rc_rcv_tid_rdma_read_req(struct hfi1_packet *packet)
+{
+ /* HANDLER FOR TID RDMA READ REQUEST packet (Responder side)*/
+
+ /*
+ * 1. Verify TID RDMA READ REQ as per IB_OPCODE_RC_RDMA_READ
+ * (see hfi1_rc_rcv())
+ * 2. Put TID RDMA READ REQ into the response queueu (s_ack_queue)
+ * - Setup struct tid_rdma_req with request info
+ * - Initialize struct tid_rdma_flow info;
+ * - Copy TID entries;
+ * 3. Set the qp->s_ack_state.
+ * 4. Set RVT_S_RESP_PENDING in s_flags.
+ * 5. Kick the send engine (hfi1_schedule_send())
+ */
+ struct hfi1_ctxtdata *rcd = packet->rcd;
+ struct rvt_qp *qp = packet->qp;
+ struct hfi1_ibport *ibp = to_iport(qp->ibqp.device, qp->port_num);
+ struct ib_other_headers *ohdr = packet->ohdr;
+ struct rvt_ack_entry *e;
+ unsigned long flags;
+ struct ib_reth *reth;
+ struct hfi1_qp_priv *qpriv = qp->priv;
+ u32 bth0, psn, len, rkey;
+ bool is_fecn;
+ u8 next;
+ u64 vaddr;
+ int diff;
+ u8 nack_state = IB_NAK_INVALID_REQUEST;
+
+ bth0 = be32_to_cpu(ohdr->bth[0]);
+ if (hfi1_ruc_check_hdr(ibp, packet))
+ return;
+
+ is_fecn = process_ecn(qp, packet);
+ psn = mask_psn(be32_to_cpu(ohdr->bth[2]));
+ trace_hfi1_rsp_rcv_tid_read_req(qp, psn);
+
+ if (qp->state == IB_QPS_RTR && !(qp->r_flags & RVT_R_COMM_EST))
+ rvt_comm_est(qp);
+
+ if (unlikely(!(qp->qp_access_flags & IB_ACCESS_REMOTE_READ)))
+ goto nack_inv;
+
+ reth = &ohdr->u.tid_rdma.r_req.reth;
+ vaddr = be64_to_cpu(reth->vaddr);
+ len = be32_to_cpu(reth->length);
+ /* The length needs to be in multiples of PAGE_SIZE */
+ if (!len || len & ~PAGE_MASK || len > qpriv->tid_rdma.local.max_len)
+ goto nack_inv;
+
+ diff = delta_psn(psn, qp->r_psn);
+ if (unlikely(diff)) {
+ if (tid_rdma_rcv_error(packet, ohdr, qp, psn, diff))
+ return;
+ goto send_ack;
+ }
+
+ /* We've verified the request, insert it into the ack queue. */
+ next = qp->r_head_ack_queue + 1;
+ if (next > rvt_size_atomic(ib_to_rvt(qp->ibqp.device)))
+ next = 0;
+ spin_lock_irqsave(&qp->s_lock, flags);
+ if (unlikely(next == qp->s_tail_ack_queue)) {
+ if (!qp->s_ack_queue[next].sent) {
+ nack_state = IB_NAK_REMOTE_OPERATIONAL_ERROR;
+ goto nack_inv_unlock;
+ }
+ update_ack_queue(qp, next);
+ }
+ e = &qp->s_ack_queue[qp->r_head_ack_queue];
+ if (e->rdma_sge.mr) {
+ rvt_put_mr(e->rdma_sge.mr);
+ e->rdma_sge.mr = NULL;
+ }
+
+ rkey = be32_to_cpu(reth->rkey);
+ qp->r_len = len;
+
+ if (unlikely(!rvt_rkey_ok(qp, &e->rdma_sge, qp->r_len, vaddr,
+ rkey, IB_ACCESS_REMOTE_READ)))
+ goto nack_acc;
+
+ /* Accept the request parameters */
+ if (tid_rdma_rcv_read_request(qp, e, packet, ohdr, bth0, psn, vaddr,
+ len))
+ goto nack_inv_unlock;
+
+ qp->r_state = e->opcode;
+ qp->r_nak_state = 0;
+ /*
+ * We need to increment the MSN here instead of when we
+ * finish sending the result since a duplicate request would
+ * increment it more than once.
+ */
+ qp->r_msn++;
+ qp->r_psn += e->lpsn - e->psn + 1;
+
+ qp->r_head_ack_queue = next;
+
+ /*
+ * For all requests other than TID WRITE which are added to the ack
+ * queue, qpriv->r_tid_alloc follows qp->r_head_ack_queue. It is ok to
+ * do this because of interlocks between these and TID WRITE
+ * requests. The same change has also been made in hfi1_rc_rcv().
+ */
+ qpriv->r_tid_alloc = qp->r_head_ack_queue;
+
+ /* Schedule the send tasklet. */
+ qp->s_flags |= RVT_S_RESP_PENDING;
+ hfi1_schedule_send(qp);
+
+ spin_unlock_irqrestore(&qp->s_lock, flags);
+ if (is_fecn)
+ goto send_ack;
+ return;
+
+nack_inv_unlock:
+ spin_unlock_irqrestore(&qp->s_lock, flags);
+nack_inv:
+ rvt_rc_error(qp, IB_WC_LOC_QP_OP_ERR);
+ qp->r_nak_state = nack_state;
+ qp->r_ack_psn = qp->r_psn;
+ /* Queue NAK for later */
+ rc_defered_ack(rcd, qp);
+ return;
+nack_acc:
+ spin_unlock_irqrestore(&qp->s_lock, flags);
+ rvt_rc_error(qp, IB_WC_LOC_PROT_ERR);
+ qp->r_nak_state = IB_NAK_REMOTE_ACCESS_ERROR;
+ qp->r_ack_psn = qp->r_psn;
+send_ack:
+ hfi1_send_rc_ack(packet, is_fecn);
+}
+
+u32 hfi1_build_tid_rdma_read_resp(struct rvt_qp *qp, struct rvt_ack_entry *e,
+ struct ib_other_headers *ohdr, u32 *bth0,
+ u32 *bth1, u32 *bth2, u32 *len, bool *last)
+{
+ struct hfi1_ack_priv *epriv = e->priv;
+ struct tid_rdma_request *req = &epriv->tid_req;
+ struct hfi1_qp_priv *qpriv = qp->priv;
+ struct tid_rdma_flow *flow = &req->flows[req->clear_tail];
+ u32 tidentry = flow->tid_entry[flow->tid_idx];
+ u32 tidlen = EXP_TID_GET(tidentry, LEN) << PAGE_SHIFT;
+ struct tid_rdma_read_resp *resp = &ohdr->u.tid_rdma.r_rsp;
+ u32 next_offset, om = KDETH_OM_LARGE;
+ bool last_pkt;
+ u32 hdwords = 0;
+ struct tid_rdma_params *remote;
+
+ *len = min_t(u32, qp->pmtu, tidlen - flow->tid_offset);
+ flow->sent += *len;
+ next_offset = flow->tid_offset + *len;
+ last_pkt = (flow->sent >= flow->length);
+
+ trace_hfi1_tid_entry_build_read_resp(qp, flow->tid_idx, tidentry);
+ trace_hfi1_tid_flow_build_read_resp(qp, req->clear_tail, flow);
+
+ rcu_read_lock();
+ remote = rcu_dereference(qpriv->tid_rdma.remote);
+ if (!remote) {
+ rcu_read_unlock();
+ goto done;
+ }
+ KDETH_RESET(resp->kdeth0, KVER, 0x1);
+ KDETH_SET(resp->kdeth0, SH, !last_pkt);
+ KDETH_SET(resp->kdeth0, INTR, !!(!last_pkt && remote->urg));
+ KDETH_SET(resp->kdeth0, TIDCTRL, EXP_TID_GET(tidentry, CTRL));
+ KDETH_SET(resp->kdeth0, TID, EXP_TID_GET(tidentry, IDX));
+ KDETH_SET(resp->kdeth0, OM, om == KDETH_OM_LARGE);
+ KDETH_SET(resp->kdeth0, OFFSET, flow->tid_offset / om);
+ KDETH_RESET(resp->kdeth1, JKEY, remote->jkey);
+ resp->verbs_qp = cpu_to_be32(qp->remote_qpn);
+ rcu_read_unlock();
+
+ resp->aeth = rvt_compute_aeth(qp);
+ resp->verbs_psn = cpu_to_be32(mask_psn(flow->flow_state.ib_spsn +
+ flow->pkt));
+
+ *bth0 = TID_OP(READ_RESP) << 24;
+ *bth1 = flow->tid_qpn;
+ *bth2 = mask_psn(((flow->flow_state.spsn + flow->pkt++) &
+ HFI1_KDETH_BTH_SEQ_MASK) |
+ (flow->flow_state.generation <<
+ HFI1_KDETH_BTH_SEQ_SHIFT));
+ *last = last_pkt;
+ if (last_pkt)
+ /* Advance to next flow */
+ req->clear_tail = (req->clear_tail + 1) &
+ (MAX_FLOWS - 1);
+
+ if (next_offset >= tidlen) {
+ flow->tid_offset = 0;
+ flow->tid_idx++;
+ } else {
+ flow->tid_offset = next_offset;
+ }
+
+ hdwords = sizeof(ohdr->u.tid_rdma.r_rsp) / sizeof(u32);
+
+done:
+ return hdwords;
+}
+
+static inline struct tid_rdma_request *
+find_tid_request(struct rvt_qp *qp, u32 psn, enum ib_wr_opcode opcode)
+ __must_hold(&qp->s_lock)
+{
+ struct rvt_swqe *wqe;
+ struct tid_rdma_request *req = NULL;
+ u32 i, end;
+
+ end = qp->s_cur + 1;
+ if (end == qp->s_size)
+ end = 0;
+ for (i = qp->s_acked; i != end;) {
+ wqe = rvt_get_swqe_ptr(qp, i);
+ if (cmp_psn(psn, wqe->psn) >= 0 &&
+ cmp_psn(psn, wqe->lpsn) <= 0) {
+ if (wqe->wr.opcode == opcode)
+ req = wqe_to_tid_req(wqe);
+ break;
+ }
+ if (++i == qp->s_size)
+ i = 0;
+ }
+
+ return req;
+}
+
+void hfi1_rc_rcv_tid_rdma_read_resp(struct hfi1_packet *packet)
+{
+ /* HANDLER FOR TID RDMA READ RESPONSE packet (Requestor side */
+
+ /*
+ * 1. Find matching SWQE
+ * 2. Check that the entire segment has been read.
+ * 3. Remove HFI1_S_WAIT_TID_RESP from s_flags.
+ * 4. Free the TID flow resources.
+ * 5. Kick the send engine (hfi1_schedule_send())
+ */
+ struct ib_other_headers *ohdr = packet->ohdr;
+ struct rvt_qp *qp = packet->qp;
+ struct hfi1_qp_priv *priv = qp->priv;
+ struct hfi1_ctxtdata *rcd = packet->rcd;
+ struct tid_rdma_request *req;
+ struct tid_rdma_flow *flow;
+ u32 opcode, aeth;
+ bool is_fecn;
+ unsigned long flags;
+ u32 kpsn, ipsn;
+
+ trace_hfi1_sender_rcv_tid_read_resp(qp);
+ is_fecn = process_ecn(qp, packet);
+ kpsn = mask_psn(be32_to_cpu(ohdr->bth[2]));
+ aeth = be32_to_cpu(ohdr->u.tid_rdma.r_rsp.aeth);
+ opcode = (be32_to_cpu(ohdr->bth[0]) >> 24) & 0xff;
+
+ spin_lock_irqsave(&qp->s_lock, flags);
+ ipsn = mask_psn(be32_to_cpu(ohdr->u.tid_rdma.r_rsp.verbs_psn));
+ req = find_tid_request(qp, ipsn, IB_WR_TID_RDMA_READ);
+ if (unlikely(!req))
+ goto ack_op_err;
+
+ flow = &req->flows[req->clear_tail];
+ /* When header suppression is disabled */
+ if (cmp_psn(ipsn, flow->flow_state.ib_lpsn))
+ goto ack_done;
+ req->ack_pending--;
+ priv->pending_tid_r_segs--;
+ qp->s_num_rd_atomic--;
+ if ((qp->s_flags & RVT_S_WAIT_FENCE) &&
+ !qp->s_num_rd_atomic) {
+ qp->s_flags &= ~(RVT_S_WAIT_FENCE |
+ RVT_S_WAIT_ACK);
+ hfi1_schedule_send(qp);
+ }
+ if (qp->s_flags & RVT_S_WAIT_RDMAR) {
+ qp->s_flags &= ~(RVT_S_WAIT_RDMAR | RVT_S_WAIT_ACK);
+ hfi1_schedule_send(qp);
+ }
+
+ trace_hfi1_ack(qp, ipsn);
+ trace_hfi1_tid_req_rcv_read_resp(qp, 0, req->e.swqe->wr.opcode,
+ req->e.swqe->psn, req->e.swqe->lpsn,
+ req);
+ trace_hfi1_tid_flow_rcv_read_resp(qp, req->clear_tail, flow);
+
+ /* Release the tid resources */
+ hfi1_kern_exp_rcv_clear(req);
+
+ if (!do_rc_ack(qp, aeth, ipsn, opcode, 0, rcd))
+ goto ack_done;
+
+ /* If not done yet, build next read request */
+ if (++req->comp_seg >= req->total_segs) {
+ priv->tid_r_comp++;
+ req->state = TID_REQUEST_COMPLETE;
+ }
+
+ /*
+ * Clear the hw flow under two conditions:
+ * 1. This request is a sync point and it is complete;
+ * 2. Current request is completed and there are no more requests.
+ */
+ if ((req->state == TID_REQUEST_SYNC &&
+ req->comp_seg == req->cur_seg) ||
+ priv->tid_r_comp == priv->tid_r_reqs) {
+ hfi1_kern_clear_hw_flow(priv->rcd, qp);
+ if (req->state == TID_REQUEST_SYNC)
+ req->state = TID_REQUEST_ACTIVE;
+ }
+
+ hfi1_schedule_send(qp);
+ goto ack_done;
+
+ack_op_err:
+ /*
+ * The test indicates that the send engine has finished its cleanup
+ * after sending the request and it's now safe to put the QP into error
+ * state. However, if the wqe queue is empty (qp->s_acked == qp->s_tail
+ * == qp->s_head), it would be unsafe to complete the wqe pointed by
+ * qp->s_acked here. Putting the qp into error state will safely flush
+ * all remaining requests.
+ */
+ if (qp->s_last == qp->s_acked)
+ rvt_error_qp(qp, IB_WC_WR_FLUSH_ERR);
+
+ack_done:
+ spin_unlock_irqrestore(&qp->s_lock, flags);
+ if (is_fecn)
+ hfi1_send_rc_ack(packet, is_fecn);
+}
+
+void hfi1_kern_read_tid_flow_free(struct rvt_qp *qp)
+ __must_hold(&qp->s_lock)
+{
+ u32 n = qp->s_acked;
+ struct rvt_swqe *wqe;
+ struct tid_rdma_request *req;
+ struct hfi1_qp_priv *priv = qp->priv;
+
+ lockdep_assert_held(&qp->s_lock);
+ /* Free any TID entries */
+ while (n != qp->s_tail) {
+ wqe = rvt_get_swqe_ptr(qp, n);
+ if (wqe->wr.opcode == IB_WR_TID_RDMA_READ) {
+ req = wqe_to_tid_req(wqe);
+ hfi1_kern_exp_rcv_clear_all(req);
+ }
+
+ if (++n == qp->s_size)
+ n = 0;
+ }
+ /* Free flow */
+ hfi1_kern_clear_hw_flow(priv->rcd, qp);
+}
+
+static bool tid_rdma_tid_err(struct hfi1_ctxtdata *rcd,
+ struct hfi1_packet *packet, u8 rcv_type,
+ u8 opcode)
+{
+ struct rvt_qp *qp = packet->qp;
+ struct hfi1_qp_priv *qpriv = qp->priv;
+ u32 ipsn;
+ struct ib_other_headers *ohdr = packet->ohdr;
+ struct rvt_ack_entry *e;
+ struct tid_rdma_request *req;
+ struct rvt_dev_info *rdi = ib_to_rvt(qp->ibqp.device);
+ u32 i;
+
+ if (rcv_type >= RHF_RCV_TYPE_IB)
+ goto done;
+
+ spin_lock(&qp->s_lock);
+
+ /*
+ * We've ran out of space in the eager buffer.
+ * Eagerly received KDETH packets which require space in the
+ * Eager buffer (packet that have payload) are TID RDMA WRITE
+ * response packets. In this case, we have to re-transmit the
+ * TID RDMA WRITE request.
+ */
+ if (rcv_type == RHF_RCV_TYPE_EAGER) {
+ hfi1_restart_rc(qp, qp->s_last_psn + 1, 1);
+ hfi1_schedule_send(qp);
+ goto done_unlock;
+ }
+
+ /*
+ * For TID READ response, error out QP after freeing the tid
+ * resources.
+ */
+ if (opcode == TID_OP(READ_RESP)) {
+ ipsn = mask_psn(be32_to_cpu(ohdr->u.tid_rdma.r_rsp.verbs_psn));
+ if (cmp_psn(ipsn, qp->s_last_psn) > 0 &&
+ cmp_psn(ipsn, qp->s_psn) < 0) {
+ hfi1_kern_read_tid_flow_free(qp);
+ spin_unlock(&qp->s_lock);
+ rvt_rc_error(qp, IB_WC_LOC_QP_OP_ERR);
+ goto done;
+ }
+ goto done_unlock;
+ }
+
+ /*
+ * Error out the qp for TID RDMA WRITE
+ */
+ hfi1_kern_clear_hw_flow(qpriv->rcd, qp);
+ for (i = 0; i < rvt_max_atomic(rdi); i++) {
+ e = &qp->s_ack_queue[i];
+ if (e->opcode == TID_OP(WRITE_REQ)) {
+ req = ack_to_tid_req(e);
+ hfi1_kern_exp_rcv_clear_all(req);
+ }
+ }
+ spin_unlock(&qp->s_lock);
+ rvt_rc_error(qp, IB_WC_LOC_LEN_ERR);
+ goto done;
+
+done_unlock:
+ spin_unlock(&qp->s_lock);
+done:
+ return true;
+}
+
+static void restart_tid_rdma_read_req(struct hfi1_ctxtdata *rcd,
+ struct rvt_qp *qp, struct rvt_swqe *wqe)
+{
+ struct tid_rdma_request *req;
+ struct tid_rdma_flow *flow;
+
+ /* Start from the right segment */
+ qp->r_flags |= RVT_R_RDMAR_SEQ;
+ req = wqe_to_tid_req(wqe);
+ flow = &req->flows[req->clear_tail];
+ hfi1_restart_rc(qp, flow->flow_state.ib_spsn, 0);
+ if (list_empty(&qp->rspwait)) {
+ qp->r_flags |= RVT_R_RSP_SEND;
+ rvt_get_qp(qp);
+ list_add_tail(&qp->rspwait, &rcd->qp_wait_list);
+ }
+}
+
+/*
+ * Handle the KDETH eflags for TID RDMA READ response.
+ *
+ * Return true if the last packet for a segment has been received and it is
+ * time to process the response normally; otherwise, return true.
+ *
+ * The caller must hold the packet->qp->r_lock and the rcu_read_lock.
+ */
+static bool handle_read_kdeth_eflags(struct hfi1_ctxtdata *rcd,
+ struct hfi1_packet *packet, u8 rcv_type,
+ u8 rte, u32 psn, u32 ibpsn)
+ __must_hold(&packet->qp->r_lock) __must_hold(RCU)
+{
+ struct hfi1_pportdata *ppd = rcd->ppd;
+ struct hfi1_devdata *dd = ppd->dd;
+ struct hfi1_ibport *ibp;
+ struct rvt_swqe *wqe;
+ struct tid_rdma_request *req;
+ struct tid_rdma_flow *flow;
+ u32 ack_psn;
+ struct rvt_qp *qp = packet->qp;
+ struct hfi1_qp_priv *priv = qp->priv;
+ bool ret = true;
+ int diff = 0;
+ u32 fpsn;
+
+ lockdep_assert_held(&qp->r_lock);
+ /* If the psn is out of valid range, drop the packet */
+ if (cmp_psn(ibpsn, qp->s_last_psn) < 0 ||
+ cmp_psn(ibpsn, qp->s_psn) > 0)
+ return ret;
+
+ spin_lock(&qp->s_lock);
+ /*
+ * Note that NAKs implicitly ACK outstanding SEND and RDMA write
+ * requests and implicitly NAK RDMA read and atomic requests issued
+ * before the NAK'ed request.
+ */
+ ack_psn = ibpsn - 1;
+ wqe = rvt_get_swqe_ptr(qp, qp->s_acked);
+ ibp = to_iport(qp->ibqp.device, qp->port_num);
+
+ /* Complete WQEs that the PSN finishes. */
+ while ((int)delta_psn(ack_psn, wqe->lpsn) >= 0) {
+ /*
+ * If this request is a RDMA read or atomic, and the NACK is
+ * for a later operation, this NACK NAKs the RDMA read or
+ * atomic.
+ */
+ if (wqe->wr.opcode == IB_WR_RDMA_READ ||
+ wqe->wr.opcode == IB_WR_TID_RDMA_READ ||
+ wqe->wr.opcode == IB_WR_ATOMIC_CMP_AND_SWP ||
+ wqe->wr.opcode == IB_WR_ATOMIC_FETCH_AND_ADD) {
+ /* Retry this request. */
+ if (!(qp->r_flags & RVT_R_RDMAR_SEQ)) {
+ qp->r_flags |= RVT_R_RDMAR_SEQ;
+ if (wqe->wr.opcode == IB_WR_TID_RDMA_READ) {
+ restart_tid_rdma_read_req(rcd, qp,
+ wqe);
+ } else {
+ hfi1_restart_rc(qp, qp->s_last_psn + 1,
+ 0);
+ if (list_empty(&qp->rspwait)) {
+ qp->r_flags |= RVT_R_RSP_SEND;
+ rvt_get_qp(qp);
+ list_add_tail(/* wait */
+ &qp->rspwait,
+ &rcd->qp_wait_list);
+ }
+ }
+ }
+ /*
+ * No need to process the NAK since we are
+ * restarting an earlier request.
+ */
+ break;
+ }
+
+ wqe = do_rc_completion(qp, wqe, ibp);
+ if (qp->s_acked == qp->s_tail)
+ break;
+ }
+
+ /* Handle the eflags for the request */
+ if (wqe->wr.opcode != IB_WR_TID_RDMA_READ)
+ goto s_unlock;
+
+ req = wqe_to_tid_req(wqe);
+ switch (rcv_type) {
+ case RHF_RCV_TYPE_EXPECTED:
+ switch (rte) {
+ case RHF_RTE_EXPECTED_FLOW_SEQ_ERR:
+ /*
+ * On the first occurrence of a Flow Sequence error,
+ * the flag TID_FLOW_SW_PSN is set.
+ *
+ * After that, the flow is *not* reprogrammed and the
+ * protocol falls back to SW PSN checking. This is done
+ * to prevent continuous Flow Sequence errors for any
+ * packets that could be still in the fabric.
+ */
+ flow = find_flow(req, psn, NULL);
+ if (!flow) {
+ /*
+ * We can't find the IB PSN matching the
+ * received KDETH PSN. The only thing we can
+ * do at this point is report the error to
+ * the QP.
+ */
+ hfi1_kern_read_tid_flow_free(qp);
+ spin_unlock(&qp->s_lock);
+ rvt_rc_error(qp, IB_WC_LOC_QP_OP_ERR);
+ return ret;
+ }
+ if (priv->flow_state.flags & TID_FLOW_SW_PSN) {
+ diff = cmp_psn(psn,
+ priv->flow_state.r_next_psn);
+ if (diff > 0) {
+ if (!(qp->r_flags & RVT_R_RDMAR_SEQ))
+ restart_tid_rdma_read_req(rcd,
+ qp,
+ wqe);
+
+ /* Drop the packet.*/
+ goto s_unlock;
+ } else if (diff < 0) {
+ /*
+ * If a response packet for a restarted
+ * request has come back, reset the
+ * restart flag.
+ */
+ if (qp->r_flags & RVT_R_RDMAR_SEQ)
+ qp->r_flags &=
+ ~RVT_R_RDMAR_SEQ;
+
+ /* Drop the packet.*/
+ goto s_unlock;
+ }
+
+ /*
+ * If SW PSN verification is successful and
+ * this is the last packet in the segment, tell
+ * the caller to process it as a normal packet.
+ */
+ fpsn = full_flow_psn(flow,
+ flow->flow_state.lpsn);
+ if (cmp_psn(fpsn, psn) == 0) {
+ ret = false;
+ if (qp->r_flags & RVT_R_RDMAR_SEQ)
+ qp->r_flags &=
+ ~RVT_R_RDMAR_SEQ;
+ }
+ priv->flow_state.r_next_psn++;
+ } else {
+ u64 reg;
+ u32 last_psn;
+
+ /*
+ * The only sane way to get the amount of
+ * progress is to read the HW flow state.
+ */
+ reg = read_uctxt_csr(dd, rcd->ctxt,
+ RCV_TID_FLOW_TABLE +
+ (8 * flow->idx));
+ last_psn = mask_psn(reg);
+
+ priv->flow_state.r_next_psn = last_psn;
+ priv->flow_state.flags |= TID_FLOW_SW_PSN;
+ /*
+ * If no request has been restarted yet,
+ * restart the current one.
+ */
+ if (!(qp->r_flags & RVT_R_RDMAR_SEQ))
+ restart_tid_rdma_read_req(rcd, qp,
+ wqe);
+ }
+
+ break;
+
+ case RHF_RTE_EXPECTED_FLOW_GEN_ERR:
+ /*
+ * Since the TID flow is able to ride through
+ * generation mismatch, drop this stale packet.
+ */
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case RHF_RCV_TYPE_ERROR:
+ switch (rte) {
+ case RHF_RTE_ERROR_OP_CODE_ERR:
+ case RHF_RTE_ERROR_KHDR_MIN_LEN_ERR:
+ case RHF_RTE_ERROR_KHDR_HCRC_ERR:
+ case RHF_RTE_ERROR_KHDR_KVER_ERR:
+ case RHF_RTE_ERROR_CONTEXT_ERR:
+ case RHF_RTE_ERROR_KHDR_TID_ERR:
+ default:
+ break;
+ }
+ default:
+ break;
+ }
+s_unlock:
+ spin_unlock(&qp->s_lock);
+ return ret;
+}
+
+bool hfi1_handle_kdeth_eflags(struct hfi1_ctxtdata *rcd,
+ struct hfi1_pportdata *ppd,
+ struct hfi1_packet *packet)
+{
+ struct hfi1_ibport *ibp = &ppd->ibport_data;
+ struct hfi1_devdata *dd = ppd->dd;
+ struct rvt_dev_info *rdi = &dd->verbs_dev.rdi;
+ u8 rcv_type = rhf_rcv_type(packet->rhf);
+ u8 rte = rhf_rcv_type_err(packet->rhf);
+ struct ib_header *hdr = packet->hdr;
+ struct ib_other_headers *ohdr = NULL;
+ int lnh = be16_to_cpu(hdr->lrh[0]) & 3;
+ u16 lid = be16_to_cpu(hdr->lrh[1]);
+ u8 opcode;
+ u32 qp_num, psn, ibpsn;
+ struct rvt_qp *qp;
+ struct hfi1_qp_priv *qpriv;
+ unsigned long flags;
+ bool ret = true;
+ struct rvt_ack_entry *e;
+ struct tid_rdma_request *req;
+ struct tid_rdma_flow *flow;
+
+ trace_hfi1_msg_handle_kdeth_eflags(NULL, "Kdeth error: rhf ",
+ packet->rhf);
+ if (packet->rhf & (RHF_VCRC_ERR | RHF_ICRC_ERR))
+ return ret;
+
+ packet->ohdr = &hdr->u.oth;
+ ohdr = packet->ohdr;
+ trace_input_ibhdr(rcd->dd, packet, !!(rhf_dc_info(packet->rhf)));
+
+ /* Get the destination QP number. */
+ qp_num = be32_to_cpu(ohdr->u.tid_rdma.r_rsp.verbs_qp) &
+ RVT_QPN_MASK;
+ if (lid >= be16_to_cpu(IB_MULTICAST_LID_BASE))
+ goto drop;
+
+ psn = mask_psn(be32_to_cpu(ohdr->bth[2]));
+ opcode = (be32_to_cpu(ohdr->bth[0]) >> 24) & 0xff;
+
+ rcu_read_lock();
+ qp = rvt_lookup_qpn(rdi, &ibp->rvp, qp_num);
+ if (!qp)
+ goto rcu_unlock;
+
+ packet->qp = qp;
+
+ /* Check for valid receive state. */
+ spin_lock_irqsave(&qp->r_lock, flags);
+ if (!(ib_rvt_state_ops[qp->state] & RVT_PROCESS_RECV_OK)) {
+ ibp->rvp.n_pkt_drops++;
+ goto r_unlock;
+ }
+
+ if (packet->rhf & RHF_TID_ERR) {
+ /* For TIDERR and RC QPs preemptively schedule a NAK */
+ u32 tlen = rhf_pkt_len(packet->rhf); /* in bytes */
+
+ /* Sanity check packet */
+ if (tlen < 24)
+ goto r_unlock;
+
+ /*
+ * Check for GRH. We should never get packets with GRH in this
+ * path.
+ */
+ if (lnh == HFI1_LRH_GRH)
+ goto r_unlock;
+
+ if (tid_rdma_tid_err(rcd, packet, rcv_type, opcode))
+ goto r_unlock;
+ }
+
+ /* handle TID RDMA READ */
+ if (opcode == TID_OP(READ_RESP)) {
+ ibpsn = be32_to_cpu(ohdr->u.tid_rdma.r_rsp.verbs_psn);
+ ibpsn = mask_psn(ibpsn);
+ ret = handle_read_kdeth_eflags(rcd, packet, rcv_type, rte, psn,
+ ibpsn);
+ goto r_unlock;
+ }
+
+ /*
+ * qp->s_tail_ack_queue points to the rvt_ack_entry currently being
+ * processed. These a completed sequentially so we can be sure that
+ * the pointer will not change until the entire request has completed.
+ */
+ spin_lock(&qp->s_lock);
+ qpriv = qp->priv;
+ e = &qp->s_ack_queue[qpriv->r_tid_tail];
+ req = ack_to_tid_req(e);
+ flow = &req->flows[req->clear_tail];
+ trace_hfi1_eflags_err_write(qp, rcv_type, rte, psn);
+ trace_hfi1_rsp_handle_kdeth_eflags(qp, psn);
+ trace_hfi1_tid_write_rsp_handle_kdeth_eflags(qp);
+ trace_hfi1_tid_req_handle_kdeth_eflags(qp, 0, e->opcode, e->psn,
+ e->lpsn, req);
+ trace_hfi1_tid_flow_handle_kdeth_eflags(qp, req->clear_tail, flow);
+
+ switch (rcv_type) {
+ case RHF_RCV_TYPE_EXPECTED:
+ switch (rte) {
+ case RHF_RTE_EXPECTED_FLOW_SEQ_ERR:
+ if (!(qpriv->s_flags & HFI1_R_TID_SW_PSN)) {
+ u64 reg;
+
+ qpriv->s_flags |= HFI1_R_TID_SW_PSN;
+ /*
+ * The only sane way to get the amount of
+ * progress is to read the HW flow state.
+ */
+ reg = read_uctxt_csr(dd, rcd->ctxt,
+ RCV_TID_FLOW_TABLE +
+ (8 * flow->idx));
+ flow->flow_state.r_next_psn = mask_psn(reg);
+ qpriv->r_next_psn_kdeth =
+ flow->flow_state.r_next_psn;
+ goto nak_psn;
+ } else {
+ /*
+ * If the received PSN does not match the next
+ * expected PSN, NAK the packet.
+ * However, only do that if we know that the a
+ * NAK has already been sent. Otherwise, this
+ * mismatch could be due to packets that were
+ * already in flight.
+ */
+ if (psn != flow->flow_state.r_next_psn) {
+ psn = flow->flow_state.r_next_psn;
+ goto nak_psn;
+ }
+
+ qpriv->s_nak_state = 0;
+ /*
+ * If SW PSN verification is successful and this
+ * is the last packet in the segment, tell the
+ * caller to process it as a normal packet.
+ */
+ if (psn == full_flow_psn(flow,
+ flow->flow_state.lpsn))
+ ret = false;
+ qpriv->r_next_psn_kdeth =
+ ++flow->flow_state.r_next_psn;
+ }
+ break;
+
+ case RHF_RTE_EXPECTED_FLOW_GEN_ERR:
+ goto nak_psn;
+
+ default:
+ break;
+ }
+ break;
+
+ case RHF_RCV_TYPE_ERROR:
+ switch (rte) {
+ case RHF_RTE_ERROR_OP_CODE_ERR:
+ case RHF_RTE_ERROR_KHDR_MIN_LEN_ERR:
+ case RHF_RTE_ERROR_KHDR_HCRC_ERR:
+ case RHF_RTE_ERROR_KHDR_KVER_ERR:
+ case RHF_RTE_ERROR_CONTEXT_ERR:
+ case RHF_RTE_ERROR_KHDR_TID_ERR:
+ default:
+ break;
+ }
+ default:
+ break;
+ }
+
+unlock:
+ spin_unlock(&qp->s_lock);
+r_unlock:
+ spin_unlock_irqrestore(&qp->r_lock, flags);
+rcu_unlock:
+ rcu_read_unlock();
+drop:
+ return ret;
+nak_psn:
+ ibp->rvp.n_rc_seqnak++;
+ if (!qpriv->s_nak_state) {
+ qpriv->s_nak_state = IB_NAK_PSN_ERROR;
+ /* We are NAK'ing the next expected PSN */
+ qpriv->s_nak_psn = mask_psn(flow->flow_state.r_next_psn);
+ qpriv->s_flags |= RVT_S_ACK_PENDING;
+ if (qpriv->r_tid_ack == HFI1_QP_WQE_INVALID)
+ qpriv->r_tid_ack = qpriv->r_tid_tail;
+ hfi1_schedule_tid_send(qp);
+ }
+ goto unlock;
+}
+
+/*
+ * "Rewind" the TID request information.
+ * This means that we reset the state back to ACTIVE,
+ * find the proper flow, set the flow index to that flow,
+ * and reset the flow information.
+ */
+void hfi1_tid_rdma_restart_req(struct rvt_qp *qp, struct rvt_swqe *wqe,
+ u32 *bth2)
+{
+ struct tid_rdma_request *req = wqe_to_tid_req(wqe);
+ struct tid_rdma_flow *flow;
+ struct hfi1_qp_priv *qpriv = qp->priv;
+ int diff, delta_pkts;
+ u32 tididx = 0, i;
+ u16 fidx;
+
+ if (wqe->wr.opcode == IB_WR_TID_RDMA_READ) {
+ *bth2 = mask_psn(qp->s_psn);
+ flow = find_flow_ib(req, *bth2, &fidx);
+ if (!flow) {
+ trace_hfi1_msg_tid_restart_req(/* msg */
+ qp, "!!!!!! Could not find flow to restart: bth2 ",
+ (u64)*bth2);
+ trace_hfi1_tid_req_restart_req(qp, 0, wqe->wr.opcode,
+ wqe->psn, wqe->lpsn,
+ req);
+ return;
+ }
+ } else {
+ fidx = req->acked_tail;
+ flow = &req->flows[fidx];
+ *bth2 = mask_psn(req->r_ack_psn);
+ }
+
+ if (wqe->wr.opcode == IB_WR_TID_RDMA_READ)
+ delta_pkts = delta_psn(*bth2, flow->flow_state.ib_spsn);
+ else
+ delta_pkts = delta_psn(*bth2,
+ full_flow_psn(flow,
+ flow->flow_state.spsn));
+
+ trace_hfi1_tid_flow_restart_req(qp, fidx, flow);
+ diff = delta_pkts + flow->resync_npkts;
+
+ flow->sent = 0;
+ flow->pkt = 0;
+ flow->tid_idx = 0;
+ flow->tid_offset = 0;
+ if (diff) {
+ for (tididx = 0; tididx < flow->tidcnt; tididx++) {
+ u32 tidentry = flow->tid_entry[tididx], tidlen,
+ tidnpkts, npkts;
+
+ flow->tid_offset = 0;
+ tidlen = EXP_TID_GET(tidentry, LEN) * PAGE_SIZE;
+ tidnpkts = rvt_div_round_up_mtu(qp, tidlen);
+ npkts = min_t(u32, diff, tidnpkts);
+ flow->pkt += npkts;
+ flow->sent += (npkts == tidnpkts ? tidlen :
+ npkts * qp->pmtu);
+ flow->tid_offset += npkts * qp->pmtu;
+ diff -= npkts;
+ if (!diff)
+ break;
+ }
+ }
+ if (wqe->wr.opcode == IB_WR_TID_RDMA_WRITE) {
+ rvt_skip_sge(&qpriv->tid_ss, (req->cur_seg * req->seg_len) +
+ flow->sent, 0);
+ /*
+ * Packet PSN is based on flow_state.spsn + flow->pkt. However,
+ * during a RESYNC, the generation is incremented and the
+ * sequence is reset to 0. Since we've adjusted the npkts in the
+ * flow and the SGE has been sufficiently advanced, we have to
+ * adjust flow->pkt in order to calculate the correct PSN.
+ */
+ flow->pkt -= flow->resync_npkts;
+ }
+
+ if (flow->tid_offset ==
+ EXP_TID_GET(flow->tid_entry[tididx], LEN) * PAGE_SIZE) {
+ tididx++;
+ flow->tid_offset = 0;
+ }
+ flow->tid_idx = tididx;
+ if (wqe->wr.opcode == IB_WR_TID_RDMA_READ)
+ /* Move flow_idx to correct index */
+ req->flow_idx = fidx;
+ else
+ req->clear_tail = fidx;
+
+ trace_hfi1_tid_flow_restart_req(qp, fidx, flow);
+ trace_hfi1_tid_req_restart_req(qp, 0, wqe->wr.opcode, wqe->psn,
+ wqe->lpsn, req);
+ req->state = TID_REQUEST_ACTIVE;
+ if (wqe->wr.opcode == IB_WR_TID_RDMA_WRITE) {
+ /* Reset all the flows that we are going to resend */
+ fidx = CIRC_NEXT(fidx, MAX_FLOWS);
+ i = qpriv->s_tid_tail;
+ do {
+ for (; CIRC_CNT(req->setup_head, fidx, MAX_FLOWS);
+ fidx = CIRC_NEXT(fidx, MAX_FLOWS)) {
+ req->flows[fidx].sent = 0;
+ req->flows[fidx].pkt = 0;
+ req->flows[fidx].tid_idx = 0;
+ req->flows[fidx].tid_offset = 0;
+ req->flows[fidx].resync_npkts = 0;
+ }
+ if (i == qpriv->s_tid_cur)
+ break;
+ do {
+ i = (++i == qp->s_size ? 0 : i);
+ wqe = rvt_get_swqe_ptr(qp, i);
+ } while (wqe->wr.opcode != IB_WR_TID_RDMA_WRITE);
+ req = wqe_to_tid_req(wqe);
+ req->cur_seg = req->ack_seg;
+ fidx = req->acked_tail;
+ /* Pull req->clear_tail back */
+ req->clear_tail = fidx;
+ } while (1);
+ }
+}
+
+void hfi1_qp_kern_exp_rcv_clear_all(struct rvt_qp *qp)
+{
+ int i, ret;
+ struct hfi1_qp_priv *qpriv = qp->priv;
+ struct tid_flow_state *fs;
+
+ if (qp->ibqp.qp_type != IB_QPT_RC || !HFI1_CAP_IS_KSET(TID_RDMA))
+ return;
+
+ /*
+ * First, clear the flow to help prevent any delayed packets from
+ * being delivered.
+ */
+ fs = &qpriv->flow_state;
+ if (fs->index != RXE_NUM_TID_FLOWS)
+ hfi1_kern_clear_hw_flow(qpriv->rcd, qp);
+
+ for (i = qp->s_acked; i != qp->s_head;) {
+ struct rvt_swqe *wqe = rvt_get_swqe_ptr(qp, i);
+
+ if (++i == qp->s_size)
+ i = 0;
+ /* Free only locally allocated TID entries */
+ if (wqe->wr.opcode != IB_WR_TID_RDMA_READ)
+ continue;
+ do {
+ struct hfi1_swqe_priv *priv = wqe->priv;
+
+ ret = hfi1_kern_exp_rcv_clear(&priv->tid_req);
+ } while (!ret);
+ }
+ for (i = qp->s_acked_ack_queue; i != qp->r_head_ack_queue;) {
+ struct rvt_ack_entry *e = &qp->s_ack_queue[i];
+
+ if (++i == rvt_max_atomic(ib_to_rvt(qp->ibqp.device)))
+ i = 0;
+ /* Free only locally allocated TID entries */
+ if (e->opcode != TID_OP(WRITE_REQ))
+ continue;
+ do {
+ struct hfi1_ack_priv *priv = e->priv;
+
+ ret = hfi1_kern_exp_rcv_clear(&priv->tid_req);
+ } while (!ret);
+ }
+}
+
+bool hfi1_tid_rdma_wqe_interlock(struct rvt_qp *qp, struct rvt_swqe *wqe)
+{
+ struct rvt_swqe *prev;
+ struct hfi1_qp_priv *priv = qp->priv;
+ u32 s_prev;
+ struct tid_rdma_request *req;
+
+ s_prev = (qp->s_cur == 0 ? qp->s_size : qp->s_cur) - 1;
+ prev = rvt_get_swqe_ptr(qp, s_prev);
+
+ switch (wqe->wr.opcode) {
+ case IB_WR_SEND:
+ case IB_WR_SEND_WITH_IMM:
+ case IB_WR_SEND_WITH_INV:
+ case IB_WR_ATOMIC_CMP_AND_SWP:
+ case IB_WR_ATOMIC_FETCH_AND_ADD:
+ case IB_WR_RDMA_WRITE:
+ switch (prev->wr.opcode) {
+ case IB_WR_TID_RDMA_WRITE:
+ req = wqe_to_tid_req(prev);
+ if (req->ack_seg != req->total_segs)
+ goto interlock;
+ default:
+ break;
+ }
+ break;
+ case IB_WR_RDMA_READ:
+ if (prev->wr.opcode != IB_WR_TID_RDMA_WRITE)
+ break;
+ /* fall through */
+ case IB_WR_TID_RDMA_READ:
+ switch (prev->wr.opcode) {
+ case IB_WR_RDMA_READ:
+ if (qp->s_acked != qp->s_cur)
+ goto interlock;
+ break;
+ case IB_WR_TID_RDMA_WRITE:
+ req = wqe_to_tid_req(prev);
+ if (req->ack_seg != req->total_segs)
+ goto interlock;
+ default:
+ break;
+ }
+ default:
+ break;
+ }
+ return false;
+
+interlock:
+ priv->s_flags |= HFI1_S_TID_WAIT_INTERLCK;
+ return true;
+}
+
+/* Does @sge meet the alignment requirements for tid rdma? */
+static inline bool hfi1_check_sge_align(struct rvt_qp *qp,
+ struct rvt_sge *sge, int num_sge)
+{
+ int i;
+
+ for (i = 0; i < num_sge; i++, sge++) {
+ trace_hfi1_sge_check_align(qp, i, sge);
+ if ((u64)sge->vaddr & ~PAGE_MASK ||
+ sge->sge_length & ~PAGE_MASK)
+ return false;
+ }
+ return true;
+}
+
+void setup_tid_rdma_wqe(struct rvt_qp *qp, struct rvt_swqe *wqe)
+{
+ struct hfi1_qp_priv *qpriv = (struct hfi1_qp_priv *)qp->priv;
+ struct hfi1_swqe_priv *priv = wqe->priv;
+ struct tid_rdma_params *remote;
+ enum ib_wr_opcode new_opcode;
+ bool do_tid_rdma = false;
+ struct hfi1_pportdata *ppd = qpriv->rcd->ppd;
+
+ if ((rdma_ah_get_dlid(&qp->remote_ah_attr) & ~((1 << ppd->lmc) - 1)) ==
+ ppd->lid)
+ return;
+ if (qpriv->hdr_type != HFI1_PKT_TYPE_9B)
+ return;
+
+ rcu_read_lock();
+ remote = rcu_dereference(qpriv->tid_rdma.remote);
+ /*
+ * If TID RDMA is disabled by the negotiation, don't
+ * use it.
+ */
+ if (!remote)
+ goto exit;
+
+ if (wqe->wr.opcode == IB_WR_RDMA_READ) {
+ if (hfi1_check_sge_align(qp, &wqe->sg_list[0],
+ wqe->wr.num_sge)) {
+ new_opcode = IB_WR_TID_RDMA_READ;
+ do_tid_rdma = true;
+ }
+ } else if (wqe->wr.opcode == IB_WR_RDMA_WRITE) {
+ /*
+ * TID RDMA is enabled for this RDMA WRITE request iff:
+ * 1. The remote address is page-aligned,
+ * 2. The length is larger than the minimum segment size,
+ * 3. The length is page-multiple.
+ */
+ if (!(wqe->rdma_wr.remote_addr & ~PAGE_MASK) &&
+ !(wqe->length & ~PAGE_MASK)) {
+ new_opcode = IB_WR_TID_RDMA_WRITE;
+ do_tid_rdma = true;
+ }
+ }
+
+ if (do_tid_rdma) {
+ if (hfi1_kern_exp_rcv_alloc_flows(&priv->tid_req, GFP_ATOMIC))
+ goto exit;
+ wqe->wr.opcode = new_opcode;
+ priv->tid_req.seg_len =
+ min_t(u32, remote->max_len, wqe->length);
+ priv->tid_req.total_segs =
+ DIV_ROUND_UP(wqe->length, priv->tid_req.seg_len);
+ /* Compute the last PSN of the request */
+ wqe->lpsn = wqe->psn;
+ if (wqe->wr.opcode == IB_WR_TID_RDMA_READ) {
+ priv->tid_req.n_flows = remote->max_read;
+ qpriv->tid_r_reqs++;
+ wqe->lpsn += rvt_div_round_up_mtu(qp, wqe->length) - 1;
+ } else {
+ wqe->lpsn += priv->tid_req.total_segs - 1;
+ atomic_inc(&qpriv->n_requests);
+ }
+
+ priv->tid_req.cur_seg = 0;
+ priv->tid_req.comp_seg = 0;
+ priv->tid_req.ack_seg = 0;
+ priv->tid_req.state = TID_REQUEST_INACTIVE;
+ /*
+ * Reset acked_tail.
+ * TID RDMA READ does not have ACKs so it does not
+ * update the pointer. We have to reset it so TID RDMA
+ * WRITE does not get confused.
+ */
+ priv->tid_req.acked_tail = priv->tid_req.setup_head;
+ trace_hfi1_tid_req_setup_tid_wqe(qp, 1, wqe->wr.opcode,
+ wqe->psn, wqe->lpsn,
+ &priv->tid_req);
+ }
+exit:
+ rcu_read_unlock();
+}
+
+/* TID RDMA WRITE functions */
+
+u32 hfi1_build_tid_rdma_write_req(struct rvt_qp *qp, struct rvt_swqe *wqe,
+ struct ib_other_headers *ohdr,
+ u32 *bth1, u32 *bth2, u32 *len)
+{
+ struct hfi1_qp_priv *qpriv = qp->priv;
+ struct tid_rdma_request *req = wqe_to_tid_req(wqe);
+ struct tid_rdma_params *remote;
+
+ rcu_read_lock();
+ remote = rcu_dereference(qpriv->tid_rdma.remote);
+ /*
+ * Set the number of flow to be used based on negotiated
+ * parameters.
+ */
+ req->n_flows = remote->max_write;
+ req->state = TID_REQUEST_ACTIVE;
+
+ KDETH_RESET(ohdr->u.tid_rdma.w_req.kdeth0, KVER, 0x1);
+ KDETH_RESET(ohdr->u.tid_rdma.w_req.kdeth1, JKEY, remote->jkey);
+ ohdr->u.tid_rdma.w_req.reth.vaddr =
+ cpu_to_be64(wqe->rdma_wr.remote_addr + (wqe->length - *len));
+ ohdr->u.tid_rdma.w_req.reth.rkey =
+ cpu_to_be32(wqe->rdma_wr.rkey);
+ ohdr->u.tid_rdma.w_req.reth.length = cpu_to_be32(*len);
+ ohdr->u.tid_rdma.w_req.verbs_qp = cpu_to_be32(qp->remote_qpn);
+ *bth1 &= ~RVT_QPN_MASK;
+ *bth1 |= remote->qp;
+ qp->s_state = TID_OP(WRITE_REQ);
+ qp->s_flags |= HFI1_S_WAIT_TID_RESP;
+ *bth2 |= IB_BTH_REQ_ACK;
+ *len = 0;
+
+ rcu_read_unlock();
+ return sizeof(ohdr->u.tid_rdma.w_req) / sizeof(u32);
+}
+
+void hfi1_compute_tid_rdma_flow_wt(void)
+{
+ /*
+ * Heuristic for computing the RNR timeout when waiting on the flow
+ * queue. Rather than a computationaly expensive exact estimate of when
+ * a flow will be available, we assume that if a QP is at position N in
+ * the flow queue it has to wait approximately (N + 1) * (number of
+ * segments between two sync points), assuming PMTU of 4K. The rationale
+ * for this is that flows are released and recycled at each sync point.
+ */
+ tid_rdma_flow_wt = MAX_TID_FLOW_PSN * enum_to_mtu(OPA_MTU_4096) /
+ TID_RDMA_MAX_SEGMENT_SIZE;
+}
+
+static u32 position_in_queue(struct hfi1_qp_priv *qpriv,
+ struct tid_queue *queue)
+{
+ return qpriv->tid_enqueue - queue->dequeue;
+}
+
+/*
+ * @qp: points to rvt_qp context.
+ * @to_seg: desired RNR timeout in segments.
+ * Return: index of the next highest timeout in the ib_hfi1_rnr_table[]
+ */
+static u32 hfi1_compute_tid_rnr_timeout(struct rvt_qp *qp, u32 to_seg)
+{
+ struct hfi1_qp_priv *qpriv = qp->priv;
+ u64 timeout;
+ u32 bytes_per_us;
+ u8 i;
+
+ bytes_per_us = active_egress_rate(qpriv->rcd->ppd) / 8;
+ timeout = (to_seg * TID_RDMA_MAX_SEGMENT_SIZE) / bytes_per_us;
+ /*
+ * Find the next highest value in the RNR table to the required
+ * timeout. This gives the responder some padding.
+ */
+ for (i = 1; i <= IB_AETH_CREDIT_MASK; i++)
+ if (rvt_rnr_tbl_to_usec(i) >= timeout)
+ return i;
+ return 0;
+}
+
+/**
+ * Central place for resource allocation at TID write responder,
+ * is called from write_req and write_data interrupt handlers as
+ * well as the send thread when a queued QP is scheduled for
+ * resource allocation.
+ *
+ * Iterates over (a) segments of a request and then (b) queued requests
+ * themselves to allocate resources for up to local->max_write
+ * segments across multiple requests. Stop allocating when we
+ * hit a sync point, resume allocating after data packets at
+ * sync point have been received.
+ *
+ * Resource allocation and sending of responses is decoupled. The
+ * request/segment which are being allocated and sent are as follows.
+ * Resources are allocated for:
+ * [request: qpriv->r_tid_alloc, segment: req->alloc_seg]
+ * The send thread sends:
+ * [request: qp->s_tail_ack_queue, segment:req->cur_seg]
+ */
+static void hfi1_tid_write_alloc_resources(struct rvt_qp *qp, bool intr_ctx)
+{
+ struct tid_rdma_request *req;
+ struct hfi1_qp_priv *qpriv = qp->priv;
+ struct hfi1_ctxtdata *rcd = qpriv->rcd;
+ struct tid_rdma_params *local = &qpriv->tid_rdma.local;
+ struct rvt_ack_entry *e;
+ u32 npkts, to_seg;
+ bool last;
+ int ret = 0;
+
+ lockdep_assert_held(&qp->s_lock);
+
+ while (1) {
+ trace_hfi1_rsp_tid_write_alloc_res(qp, 0);
+ trace_hfi1_tid_write_rsp_alloc_res(qp);
+ /*
+ * Don't allocate more segments if a RNR NAK has already been
+ * scheduled to avoid messing up qp->r_psn: the RNR NAK will
+ * be sent only when all allocated segments have been sent.
+ * However, if more segments are allocated before that, TID RDMA
+ * WRITE RESP packets will be sent out for these new segments
+ * before the RNR NAK packet. When the requester receives the
+ * RNR NAK packet, it will restart with qp->s_last_psn + 1,
+ * which does not match qp->r_psn and will be dropped.
+ * Consequently, the requester will exhaust its retries and
+ * put the qp into error state.
+ */
+ if (qpriv->rnr_nak_state == TID_RNR_NAK_SEND)
+ break;
+
+ /* No requests left to process */
+ if (qpriv->r_tid_alloc == qpriv->r_tid_head) {
+ /* If all data has been received, clear the flow */
+ if (qpriv->flow_state.index < RXE_NUM_TID_FLOWS &&
+ !qpriv->alloc_w_segs)
+ hfi1_kern_clear_hw_flow(rcd, qp);
+ break;
+ }
+
+ e = &qp->s_ack_queue[qpriv->r_tid_alloc];
+ if (e->opcode != TID_OP(WRITE_REQ))
+ goto next_req;
+ req = ack_to_tid_req(e);
+ trace_hfi1_tid_req_write_alloc_res(qp, 0, e->opcode, e->psn,
+ e->lpsn, req);
+ /* Finished allocating for all segments of this request */
+ if (req->alloc_seg >= req->total_segs)
+ goto next_req;
+
+ /* Can allocate only a maximum of local->max_write for a QP */
+ if (qpriv->alloc_w_segs >= local->max_write)
+ break;
+
+ /* Don't allocate at a sync point with data packets pending */
+ if (qpriv->sync_pt && qpriv->alloc_w_segs)
+ break;
+
+ /* All data received at the sync point, continue */
+ if (qpriv->sync_pt && !qpriv->alloc_w_segs) {
+ hfi1_kern_clear_hw_flow(rcd, qp);
+ qpriv->sync_pt = false;
+ if (qpriv->s_flags & HFI1_R_TID_SW_PSN)
+ qpriv->s_flags &= ~HFI1_R_TID_SW_PSN;
+ }
+
+ /* Allocate flow if we don't have one */
+ if (qpriv->flow_state.index >= RXE_NUM_TID_FLOWS) {
+ ret = hfi1_kern_setup_hw_flow(qpriv->rcd, qp);
+ if (ret) {
+ to_seg = tid_rdma_flow_wt *
+ position_in_queue(qpriv,
+ &rcd->flow_queue);
+ break;
+ }
+ }
+
+ npkts = rvt_div_round_up_mtu(qp, req->seg_len);
+
+ /*
+ * We are at a sync point if we run out of KDETH PSN space.
+ * Last PSN of every generation is reserved for RESYNC.
+ */
+ if (qpriv->flow_state.psn + npkts > MAX_TID_FLOW_PSN - 1) {
+ qpriv->sync_pt = true;
+ break;
+ }
+
+ /*
+ * If overtaking req->acked_tail, send an RNR NAK. Because the
+ * QP is not queued in this case, and the issue can only be
+ * caused due a delay in scheduling the second leg which we
+ * cannot estimate, we use a rather arbitrary RNR timeout of
+ * (MAX_FLOWS / 2) segments
+ */
+ if (!CIRC_SPACE(req->setup_head, req->acked_tail,
+ MAX_FLOWS)) {
+ ret = -EAGAIN;
+ to_seg = MAX_FLOWS >> 1;
+ qpriv->s_flags |= RVT_S_ACK_PENDING;
+ hfi1_schedule_tid_send(qp);
+ break;
+ }
+
+ /* Try to allocate rcv array / TID entries */
+ ret = hfi1_kern_exp_rcv_setup(req, &req->ss, &last);
+ if (ret == -EAGAIN)
+ to_seg = position_in_queue(qpriv, &rcd->rarr_queue);
+ if (ret)
+ break;
+
+ qpriv->alloc_w_segs++;
+ req->alloc_seg++;
+ continue;
+next_req:
+ /* Begin processing the next request */
+ if (++qpriv->r_tid_alloc >
+ rvt_size_atomic(ib_to_rvt(qp->ibqp.device)))
+ qpriv->r_tid_alloc = 0;
+ }
+
+ /*
+ * Schedule an RNR NAK to be sent if (a) flow or rcv array allocation
+ * has failed (b) we are called from the rcv handler interrupt context
+ * (c) an RNR NAK has not already been scheduled
+ */
+ if (ret == -EAGAIN && intr_ctx && !qp->r_nak_state)
+ goto send_rnr_nak;
+
+ return;
+
+send_rnr_nak:
+ lockdep_assert_held(&qp->r_lock);
+
+ /* Set r_nak_state to prevent unrelated events from generating NAK's */
+ qp->r_nak_state = hfi1_compute_tid_rnr_timeout(qp, to_seg) | IB_RNR_NAK;
+
+ /* Pull back r_psn to the segment being RNR NAK'd */
+ qp->r_psn = e->psn + req->alloc_seg;
+ qp->r_ack_psn = qp->r_psn;
+ /*
+ * Pull back r_head_ack_queue to the ack entry following the request
+ * being RNR NAK'd. This allows resources to be allocated to the request
+ * if the queued QP is scheduled.
+ */
+ qp->r_head_ack_queue = qpriv->r_tid_alloc + 1;
+ if (qp->r_head_ack_queue > rvt_size_atomic(ib_to_rvt(qp->ibqp.device)))
+ qp->r_head_ack_queue = 0;
+ qpriv->r_tid_head = qp->r_head_ack_queue;
+ /*
+ * These send side fields are used in make_rc_ack(). They are set in
+ * hfi1_send_rc_ack() but must be set here before dropping qp->s_lock
+ * for consistency
+ */
+ qp->s_nak_state = qp->r_nak_state;
+ qp->s_ack_psn = qp->r_ack_psn;
+ /*
+ * Clear the ACK PENDING flag to prevent unwanted ACK because we
+ * have modified qp->s_ack_psn here.
+ */
+ qp->s_flags &= ~(RVT_S_ACK_PENDING);
+
+ trace_hfi1_rsp_tid_write_alloc_res(qp, qp->r_psn);
+ /*
+ * qpriv->rnr_nak_state is used to determine when the scheduled RNR NAK
+ * has actually been sent. qp->s_flags RVT_S_ACK_PENDING bit cannot be
+ * used for this because qp->s_lock is dropped before calling
+ * hfi1_send_rc_ack() leading to inconsistency between the receive
+ * interrupt handlers and the send thread in make_rc_ack()
+ */
+ qpriv->rnr_nak_state = TID_RNR_NAK_SEND;
+
+ /*
+ * Schedule RNR NAK to be sent. RNR NAK's are scheduled from the receive
+ * interrupt handlers but will be sent from the send engine behind any
+ * previous responses that may have been scheduled
+ */
+ rc_defered_ack(rcd, qp);
+}
+
+void hfi1_rc_rcv_tid_rdma_write_req(struct hfi1_packet *packet)
+{
+ /* HANDLER FOR TID RDMA WRITE REQUEST packet (Responder side)*/
+
+ /*
+ * 1. Verify TID RDMA WRITE REQ as per IB_OPCODE_RC_RDMA_WRITE_FIRST
+ * (see hfi1_rc_rcv())
+ * - Don't allow 0-length requests.
+ * 2. Put TID RDMA WRITE REQ into the response queueu (s_ack_queue)
+ * - Setup struct tid_rdma_req with request info
+ * - Prepare struct tid_rdma_flow array?
+ * 3. Set the qp->s_ack_state as state diagram in design doc.
+ * 4. Set RVT_S_RESP_PENDING in s_flags.
+ * 5. Kick the send engine (hfi1_schedule_send())
+ */
+ struct hfi1_ctxtdata *rcd = packet->rcd;
+ struct rvt_qp *qp = packet->qp;
+ struct hfi1_ibport *ibp = to_iport(qp->ibqp.device, qp->port_num);
+ struct ib_other_headers *ohdr = packet->ohdr;
+ struct rvt_ack_entry *e;
+ unsigned long flags;
+ struct ib_reth *reth;
+ struct hfi1_qp_priv *qpriv = qp->priv;
+ struct tid_rdma_request *req;
+ u32 bth0, psn, len, rkey, num_segs;
+ bool is_fecn;
+ u8 next;
+ u64 vaddr;
+ int diff;
+
+ bth0 = be32_to_cpu(ohdr->bth[0]);
+ if (hfi1_ruc_check_hdr(ibp, packet))
+ return;
+
+ is_fecn = process_ecn(qp, packet);
+ psn = mask_psn(be32_to_cpu(ohdr->bth[2]));
+ trace_hfi1_rsp_rcv_tid_write_req(qp, psn);
+
+ if (qp->state == IB_QPS_RTR && !(qp->r_flags & RVT_R_COMM_EST))
+ rvt_comm_est(qp);
+
+ if (unlikely(!(qp->qp_access_flags & IB_ACCESS_REMOTE_WRITE)))
+ goto nack_inv;
+
+ reth = &ohdr->u.tid_rdma.w_req.reth;
+ vaddr = be64_to_cpu(reth->vaddr);
+ len = be32_to_cpu(reth->length);
+
+ num_segs = DIV_ROUND_UP(len, qpriv->tid_rdma.local.max_len);
+ diff = delta_psn(psn, qp->r_psn);
+ if (unlikely(diff)) {
+ if (tid_rdma_rcv_error(packet, ohdr, qp, psn, diff))
+ return;
+ goto send_ack;
+ }
+
+ /*
+ * The resent request which was previously RNR NAK'd is inserted at the
+ * location of the original request, which is one entry behind
+ * r_head_ack_queue
+ */
+ if (qpriv->rnr_nak_state)
+ qp->r_head_ack_queue = qp->r_head_ack_queue ?
+ qp->r_head_ack_queue - 1 :
+ rvt_size_atomic(ib_to_rvt(qp->ibqp.device));
+
+ /* We've verified the request, insert it into the ack queue. */
+ next = qp->r_head_ack_queue + 1;
+ if (next > rvt_size_atomic(ib_to_rvt(qp->ibqp.device)))
+ next = 0;
+ spin_lock_irqsave(&qp->s_lock, flags);
+ if (unlikely(next == qp->s_acked_ack_queue)) {
+ if (!qp->s_ack_queue[next].sent)
+ goto nack_inv_unlock;
+ update_ack_queue(qp, next);
+ }
+ e = &qp->s_ack_queue[qp->r_head_ack_queue];
+ req = ack_to_tid_req(e);
+
+ /* Bring previously RNR NAK'd request back to life */
+ if (qpriv->rnr_nak_state) {
+ qp->r_nak_state = 0;
+ qp->s_nak_state = 0;
+ qpriv->rnr_nak_state = TID_RNR_NAK_INIT;
+ qp->r_psn = e->lpsn + 1;
+ req->state = TID_REQUEST_INIT;
+ goto update_head;
+ }
+
+ if (e->rdma_sge.mr) {
+ rvt_put_mr(e->rdma_sge.mr);
+ e->rdma_sge.mr = NULL;
+ }
+
+ /* The length needs to be in multiples of PAGE_SIZE */
+ if (!len || len & ~PAGE_MASK)
+ goto nack_inv_unlock;
+
+ rkey = be32_to_cpu(reth->rkey);
+ qp->r_len = len;
+
+ if (e->opcode == TID_OP(WRITE_REQ) &&
+ (req->setup_head != req->clear_tail ||
+ req->clear_tail != req->acked_tail))
+ goto nack_inv_unlock;
+
+ if (unlikely(!rvt_rkey_ok(qp, &e->rdma_sge, qp->r_len, vaddr,
+ rkey, IB_ACCESS_REMOTE_WRITE)))
+ goto nack_acc;
+
+ qp->r_psn += num_segs - 1;
+
+ e->opcode = (bth0 >> 24) & 0xff;
+ e->psn = psn;
+ e->lpsn = qp->r_psn;
+ e->sent = 0;
+
+ req->n_flows = min_t(u16, num_segs, qpriv->tid_rdma.local.max_write);
+ req->state = TID_REQUEST_INIT;
+ req->cur_seg = 0;
+ req->comp_seg = 0;
+ req->ack_seg = 0;
+ req->alloc_seg = 0;
+ req->isge = 0;
+ req->seg_len = qpriv->tid_rdma.local.max_len;
+ req->total_len = len;
+ req->total_segs = num_segs;
+ req->r_flow_psn = e->psn;
+ req->ss.sge = e->rdma_sge;
+ req->ss.num_sge = 1;
+
+ req->flow_idx = req->setup_head;
+ req->clear_tail = req->setup_head;
+ req->acked_tail = req->setup_head;
+
+ qp->r_state = e->opcode;
+ qp->r_nak_state = 0;
+ /*
+ * We need to increment the MSN here instead of when we
+ * finish sending the result since a duplicate request would
+ * increment it more than once.
+ */
+ qp->r_msn++;
+ qp->r_psn++;
+
+ trace_hfi1_tid_req_rcv_write_req(qp, 0, e->opcode, e->psn, e->lpsn,
+ req);
+
+ if (qpriv->r_tid_tail == HFI1_QP_WQE_INVALID) {
+ qpriv->r_tid_tail = qp->r_head_ack_queue;
+ } else if (qpriv->r_tid_tail == qpriv->r_tid_head) {
+ struct tid_rdma_request *ptr;
+
+ e = &qp->s_ack_queue[qpriv->r_tid_tail];
+ ptr = ack_to_tid_req(e);
+
+ if (e->opcode != TID_OP(WRITE_REQ) ||
+ ptr->comp_seg == ptr->total_segs) {
+ if (qpriv->r_tid_tail == qpriv->r_tid_ack)
+ qpriv->r_tid_ack = qp->r_head_ack_queue;
+ qpriv->r_tid_tail = qp->r_head_ack_queue;
+ }
+ }
+update_head:
+ qp->r_head_ack_queue = next;
+ qpriv->r_tid_head = qp->r_head_ack_queue;
+
+ hfi1_tid_write_alloc_resources(qp, true);
+ trace_hfi1_tid_write_rsp_rcv_req(qp);
+
+ /* Schedule the send tasklet. */
+ qp->s_flags |= RVT_S_RESP_PENDING;
+ hfi1_schedule_send(qp);
+
+ spin_unlock_irqrestore(&qp->s_lock, flags);
+ if (is_fecn)
+ goto send_ack;
+ return;
+
+nack_inv_unlock:
+ spin_unlock_irqrestore(&qp->s_lock, flags);
+nack_inv:
+ rvt_rc_error(qp, IB_WC_LOC_QP_OP_ERR);
+ qp->r_nak_state = IB_NAK_INVALID_REQUEST;
+ qp->r_ack_psn = qp->r_psn;
+ /* Queue NAK for later */
+ rc_defered_ack(rcd, qp);
+ return;
+nack_acc:
+ spin_unlock_irqrestore(&qp->s_lock, flags);
+ rvt_rc_error(qp, IB_WC_LOC_PROT_ERR);
+ qp->r_nak_state = IB_NAK_REMOTE_ACCESS_ERROR;
+ qp->r_ack_psn = qp->r_psn;
+send_ack:
+ hfi1_send_rc_ack(packet, is_fecn);
+}
+
+u32 hfi1_build_tid_rdma_write_resp(struct rvt_qp *qp, struct rvt_ack_entry *e,
+ struct ib_other_headers *ohdr, u32 *bth1,
+ u32 bth2, u32 *len,
+ struct rvt_sge_state **ss)
+{
+ struct hfi1_ack_priv *epriv = e->priv;
+ struct tid_rdma_request *req = &epriv->tid_req;
+ struct hfi1_qp_priv *qpriv = qp->priv;
+ struct tid_rdma_flow *flow = NULL;
+ u32 resp_len = 0, hdwords = 0;
+ void *resp_addr = NULL;
+ struct tid_rdma_params *remote;
+
+ trace_hfi1_tid_req_build_write_resp(qp, 0, e->opcode, e->psn, e->lpsn,
+ req);
+ trace_hfi1_tid_write_rsp_build_resp(qp);
+ trace_hfi1_rsp_build_tid_write_resp(qp, bth2);
+ flow = &req->flows[req->flow_idx];
+ switch (req->state) {
+ default:
+ /*
+ * Try to allocate resources here in case QP was queued and was
+ * later scheduled when resources became available
+ */
+ hfi1_tid_write_alloc_resources(qp, false);
+
+ /* We've already sent everything which is ready */
+ if (req->cur_seg >= req->alloc_seg)
+ goto done;
+
+ /*
+ * Resources can be assigned but responses cannot be sent in
+ * rnr_nak state, till the resent request is received
+ */
+ if (qpriv->rnr_nak_state == TID_RNR_NAK_SENT)
+ goto done;
+
+ req->state = TID_REQUEST_ACTIVE;
+ trace_hfi1_tid_flow_build_write_resp(qp, req->flow_idx, flow);
+ req->flow_idx = CIRC_NEXT(req->flow_idx, MAX_FLOWS);
+ hfi1_add_tid_reap_timer(qp);
+ break;
+
+ case TID_REQUEST_RESEND_ACTIVE:
+ case TID_REQUEST_RESEND:
+ trace_hfi1_tid_flow_build_write_resp(qp, req->flow_idx, flow);
+ req->flow_idx = CIRC_NEXT(req->flow_idx, MAX_FLOWS);
+ if (!CIRC_CNT(req->setup_head, req->flow_idx, MAX_FLOWS))
+ req->state = TID_REQUEST_ACTIVE;
+
+ hfi1_mod_tid_reap_timer(qp);
+ break;
+ }
+ flow->flow_state.resp_ib_psn = bth2;
+ resp_addr = (void *)flow->tid_entry;
+ resp_len = sizeof(*flow->tid_entry) * flow->tidcnt;
+ req->cur_seg++;
+
+ memset(&ohdr->u.tid_rdma.w_rsp, 0, sizeof(ohdr->u.tid_rdma.w_rsp));
+ epriv->ss.sge.vaddr = resp_addr;
+ epriv->ss.sge.sge_length = resp_len;
+ epriv->ss.sge.length = epriv->ss.sge.sge_length;
+ /*
+ * We can safely zero these out. Since the first SGE covers the
+ * entire packet, nothing else should even look at the MR.
+ */
+ epriv->ss.sge.mr = NULL;
+ epriv->ss.sge.m = 0;
+ epriv->ss.sge.n = 0;
+
+ epriv->ss.sg_list = NULL;
+ epriv->ss.total_len = epriv->ss.sge.sge_length;
+ epriv->ss.num_sge = 1;
+
+ *ss = &epriv->ss;
+ *len = epriv->ss.total_len;
+
+ /* Construct the TID RDMA WRITE RESP packet header */
+ rcu_read_lock();
+ remote = rcu_dereference(qpriv->tid_rdma.remote);
+
+ KDETH_RESET(ohdr->u.tid_rdma.w_rsp.kdeth0, KVER, 0x1);
+ KDETH_RESET(ohdr->u.tid_rdma.w_rsp.kdeth1, JKEY, remote->jkey);
+ ohdr->u.tid_rdma.w_rsp.aeth = rvt_compute_aeth(qp);
+ ohdr->u.tid_rdma.w_rsp.tid_flow_psn =
+ cpu_to_be32((flow->flow_state.generation <<
+ HFI1_KDETH_BTH_SEQ_SHIFT) |
+ (flow->flow_state.spsn &
+ HFI1_KDETH_BTH_SEQ_MASK));
+ ohdr->u.tid_rdma.w_rsp.tid_flow_qp =
+ cpu_to_be32(qpriv->tid_rdma.local.qp |
+ ((flow->idx & TID_RDMA_DESTQP_FLOW_MASK) <<
+ TID_RDMA_DESTQP_FLOW_SHIFT) |
+ qpriv->rcd->ctxt);
+ ohdr->u.tid_rdma.w_rsp.verbs_qp = cpu_to_be32(qp->remote_qpn);
+ *bth1 = remote->qp;
+ rcu_read_unlock();
+ hdwords = sizeof(ohdr->u.tid_rdma.w_rsp) / sizeof(u32);
+ qpriv->pending_tid_w_segs++;
+done:
+ return hdwords;
+}
+
+static void hfi1_add_tid_reap_timer(struct rvt_qp *qp)
+{
+ struct hfi1_qp_priv *qpriv = qp->priv;
+
+ lockdep_assert_held(&qp->s_lock);
+ if (!(qpriv->s_flags & HFI1_R_TID_RSC_TIMER)) {
+ qpriv->s_flags |= HFI1_R_TID_RSC_TIMER;
+ qpriv->s_tid_timer.expires = jiffies +
+ qpriv->tid_timer_timeout_jiffies;
+ add_timer(&qpriv->s_tid_timer);
+ }
+}
+
+static void hfi1_mod_tid_reap_timer(struct rvt_qp *qp)
+{
+ struct hfi1_qp_priv *qpriv = qp->priv;
+
+ lockdep_assert_held(&qp->s_lock);
+ qpriv->s_flags |= HFI1_R_TID_RSC_TIMER;
+ mod_timer(&qpriv->s_tid_timer, jiffies +
+ qpriv->tid_timer_timeout_jiffies);
+}
+
+static int hfi1_stop_tid_reap_timer(struct rvt_qp *qp)
+{
+ struct hfi1_qp_priv *qpriv = qp->priv;
+ int rval = 0;
+
+ lockdep_assert_held(&qp->s_lock);
+ if (qpriv->s_flags & HFI1_R_TID_RSC_TIMER) {
+ rval = del_timer(&qpriv->s_tid_timer);
+ qpriv->s_flags &= ~HFI1_R_TID_RSC_TIMER;
+ }
+ return rval;
+}
+
+void hfi1_del_tid_reap_timer(struct rvt_qp *qp)
+{
+ struct hfi1_qp_priv *qpriv = qp->priv;
+
+ del_timer_sync(&qpriv->s_tid_timer);
+ qpriv->s_flags &= ~HFI1_R_TID_RSC_TIMER;
+}
+
+static void hfi1_tid_timeout(struct timer_list *t)
+{
+ struct hfi1_qp_priv *qpriv = from_timer(qpriv, t, s_tid_timer);
+ struct rvt_qp *qp = qpriv->owner;
+ struct rvt_dev_info *rdi = ib_to_rvt(qp->ibqp.device);
+ unsigned long flags;
+ u32 i;
+
+ spin_lock_irqsave(&qp->r_lock, flags);
+ spin_lock(&qp->s_lock);
+ if (qpriv->s_flags & HFI1_R_TID_RSC_TIMER) {
+ dd_dev_warn(dd_from_ibdev(qp->ibqp.device), "[QP%u] %s %d\n",
+ qp->ibqp.qp_num, __func__, __LINE__);
+ trace_hfi1_msg_tid_timeout(/* msg */
+ qp, "resource timeout = ",
+ (u64)qpriv->tid_timer_timeout_jiffies);
+ hfi1_stop_tid_reap_timer(qp);
+ /*
+ * Go though the entire ack queue and clear any outstanding
+ * HW flow and RcvArray resources.
+ */
+ hfi1_kern_clear_hw_flow(qpriv->rcd, qp);
+ for (i = 0; i < rvt_max_atomic(rdi); i++) {
+ struct tid_rdma_request *req =
+ ack_to_tid_req(&qp->s_ack_queue[i]);
+
+ hfi1_kern_exp_rcv_clear_all(req);
+ }
+ spin_unlock(&qp->s_lock);
+ if (qp->ibqp.event_handler) {
+ struct ib_event ev;
+
+ ev.device = qp->ibqp.device;
+ ev.element.qp = &qp->ibqp;
+ ev.event = IB_EVENT_QP_FATAL;
+ qp->ibqp.event_handler(&ev, qp->ibqp.qp_context);
+ }
+ rvt_rc_error(qp, IB_WC_RESP_TIMEOUT_ERR);
+ goto unlock_r_lock;
+ }
+ spin_unlock(&qp->s_lock);
+unlock_r_lock:
+ spin_unlock_irqrestore(&qp->r_lock, flags);
+}
+
+void hfi1_rc_rcv_tid_rdma_write_resp(struct hfi1_packet *packet)
+{
+ /* HANDLER FOR TID RDMA WRITE RESPONSE packet (Requestor side */
+
+ /*
+ * 1. Find matching SWQE
+ * 2. Check that TIDENTRY array has enough space for a complete
+ * segment. If not, put QP in error state.
+ * 3. Save response data in struct tid_rdma_req and struct tid_rdma_flow
+ * 4. Remove HFI1_S_WAIT_TID_RESP from s_flags.
+ * 5. Set qp->s_state
+ * 6. Kick the send engine (hfi1_schedule_send())
+ */
+ struct ib_other_headers *ohdr = packet->ohdr;
+ struct rvt_qp *qp = packet->qp;
+ struct hfi1_qp_priv *qpriv = qp->priv;
+ struct hfi1_ctxtdata *rcd = packet->rcd;
+ struct rvt_swqe *wqe;
+ struct tid_rdma_request *req;
+ struct tid_rdma_flow *flow;
+ enum ib_wc_status status;
+ u32 opcode, aeth, psn, flow_psn, i, tidlen = 0, pktlen;
+ bool is_fecn;
+ unsigned long flags;
+
+ is_fecn = process_ecn(qp, packet);
+ psn = mask_psn(be32_to_cpu(ohdr->bth[2]));
+ aeth = be32_to_cpu(ohdr->u.tid_rdma.w_rsp.aeth);
+ opcode = (be32_to_cpu(ohdr->bth[0]) >> 24) & 0xff;
+
+ spin_lock_irqsave(&qp->s_lock, flags);
+
+ /* Ignore invalid responses */
+ if (cmp_psn(psn, qp->s_next_psn) >= 0)
+ goto ack_done;
+
+ /* Ignore duplicate responses. */
+ if (unlikely(cmp_psn(psn, qp->s_last_psn) <= 0))
+ goto ack_done;
+
+ if (unlikely(qp->s_acked == qp->s_tail))
+ goto ack_done;
+
+ /*
+ * If we are waiting for a particular packet sequence number
+ * due to a request being resent, check for it. Otherwise,
+ * ensure that we haven't missed anything.
+ */
+ if (qp->r_flags & RVT_R_RDMAR_SEQ) {
+ if (cmp_psn(psn, qp->s_last_psn + 1) != 0)
+ goto ack_done;
+ qp->r_flags &= ~RVT_R_RDMAR_SEQ;
+ }
+
+ wqe = rvt_get_swqe_ptr(qp, qpriv->s_tid_cur);
+ if (unlikely(wqe->wr.opcode != IB_WR_TID_RDMA_WRITE))
+ goto ack_op_err;
+
+ req = wqe_to_tid_req(wqe);
+ /*
+ * If we've lost ACKs and our acked_tail pointer is too far
+ * behind, don't overwrite segments. Just drop the packet and
+ * let the reliability protocol take care of it.
+ */
+ if (!CIRC_SPACE(req->setup_head, req->acked_tail, MAX_FLOWS))
+ goto ack_done;
+
+ /*
+ * The call to do_rc_ack() should be last in the chain of
+ * packet checks because it will end up updating the QP state.
+ * Therefore, anything that would prevent the packet from
+ * being accepted as a successful response should be prior
+ * to it.
+ */
+ if (!do_rc_ack(qp, aeth, psn, opcode, 0, rcd))
+ goto ack_done;
+
+ trace_hfi1_ack(qp, psn);
+
+ flow = &req->flows[req->setup_head];
+ flow->pkt = 0;
+ flow->tid_idx = 0;
+ flow->tid_offset = 0;
+ flow->sent = 0;
+ flow->resync_npkts = 0;
+ flow->tid_qpn = be32_to_cpu(ohdr->u.tid_rdma.w_rsp.tid_flow_qp);
+ flow->idx = (flow->tid_qpn >> TID_RDMA_DESTQP_FLOW_SHIFT) &
+ TID_RDMA_DESTQP_FLOW_MASK;
+ flow_psn = mask_psn(be32_to_cpu(ohdr->u.tid_rdma.w_rsp.tid_flow_psn));
+ flow->flow_state.generation = flow_psn >> HFI1_KDETH_BTH_SEQ_SHIFT;
+ flow->flow_state.spsn = flow_psn & HFI1_KDETH_BTH_SEQ_MASK;
+ flow->flow_state.resp_ib_psn = psn;
+ flow->length = min_t(u32, req->seg_len,
+ (wqe->length - (req->comp_seg * req->seg_len)));
+
+ flow->npkts = rvt_div_round_up_mtu(qp, flow->length);
+ flow->flow_state.lpsn = flow->flow_state.spsn +
+ flow->npkts - 1;
+ /* payload length = packet length - (header length + ICRC length) */
+ pktlen = packet->tlen - (packet->hlen + 4);
+ if (pktlen > sizeof(flow->tid_entry)) {
+ status = IB_WC_LOC_LEN_ERR;
+ goto ack_err;
+ }
+ memcpy(flow->tid_entry, packet->ebuf, pktlen);
+ flow->tidcnt = pktlen / sizeof(*flow->tid_entry);
+ trace_hfi1_tid_flow_rcv_write_resp(qp, req->setup_head, flow);
+
+ req->comp_seg++;
+ trace_hfi1_tid_write_sender_rcv_resp(qp, 0);
+ /*
+ * Walk the TID_ENTRY list to make sure we have enough space for a
+ * complete segment.
+ */
+ for (i = 0; i < flow->tidcnt; i++) {
+ trace_hfi1_tid_entry_rcv_write_resp(/* entry */
+ qp, i, flow->tid_entry[i]);
+ if (!EXP_TID_GET(flow->tid_entry[i], LEN)) {
+ status = IB_WC_LOC_LEN_ERR;
+ goto ack_err;
+ }
+ tidlen += EXP_TID_GET(flow->tid_entry[i], LEN);
+ }
+ if (tidlen * PAGE_SIZE < flow->length) {
+ status = IB_WC_LOC_LEN_ERR;
+ goto ack_err;
+ }
+
+ trace_hfi1_tid_req_rcv_write_resp(qp, 0, wqe->wr.opcode, wqe->psn,
+ wqe->lpsn, req);
+ /*
+ * If this is the first response for this request, set the initial
+ * flow index to the current flow.
+ */
+ if (!cmp_psn(psn, wqe->psn)) {
+ req->r_last_acked = mask_psn(wqe->psn - 1);
+ /* Set acked flow index to head index */
+ req->acked_tail = req->setup_head;
+ }
+
+ /* advance circular buffer head */
+ req->setup_head = CIRC_NEXT(req->setup_head, MAX_FLOWS);
+ req->state = TID_REQUEST_ACTIVE;
+
+ /*
+ * If all responses for this TID RDMA WRITE request have been received
+ * advance the pointer to the next one.
+ * Since TID RDMA requests could be mixed in with regular IB requests,
+ * they might not appear sequentially in the queue. Therefore, the
+ * next request needs to be "found".
+ */
+ if (qpriv->s_tid_cur != qpriv->s_tid_head &&
+ req->comp_seg == req->total_segs) {
+ for (i = qpriv->s_tid_cur + 1; ; i++) {
+ if (i == qp->s_size)
+ i = 0;
+ wqe = rvt_get_swqe_ptr(qp, i);
+ if (i == qpriv->s_tid_head)
+ break;
+ if (wqe->wr.opcode == IB_WR_TID_RDMA_WRITE)
+ break;
+ }
+ qpriv->s_tid_cur = i;
+ }
+ qp->s_flags &= ~HFI1_S_WAIT_TID_RESP;
+
+ hfi1_schedule_tid_send(qp);
+ goto ack_done;
+
+ack_op_err:
+ status = IB_WC_LOC_QP_OP_ERR;
+ack_err:
+ rvt_error_qp(qp, status);
+ack_done:
+ spin_unlock_irqrestore(&qp->s_lock, flags);
+ if (is_fecn)
+ hfi1_send_rc_ack(packet, is_fecn);
+}
+
+bool hfi1_build_tid_rdma_packet(struct rvt_swqe *wqe,
+ struct ib_other_headers *ohdr,
+ u32 *bth1, u32 *bth2, u32 *len)
+{
+ struct tid_rdma_request *req = wqe_to_tid_req(wqe);
+ struct tid_rdma_flow *flow = &req->flows[req->clear_tail];
+ struct tid_rdma_params *remote;
+ struct rvt_qp *qp = req->qp;
+ struct hfi1_qp_priv *qpriv = qp->priv;
+ u32 tidentry = flow->tid_entry[flow->tid_idx];
+ u32 tidlen = EXP_TID_GET(tidentry, LEN) << PAGE_SHIFT;
+ struct tid_rdma_write_data *wd = &ohdr->u.tid_rdma.w_data;
+ u32 next_offset, om = KDETH_OM_LARGE;
+ bool last_pkt;
+
+ if (!tidlen) {
+ hfi1_trdma_send_complete(qp, wqe, IB_WC_REM_INV_RD_REQ_ERR);
+ rvt_error_qp(qp, IB_WC_REM_INV_RD_REQ_ERR);
+ }
+
+ *len = min_t(u32, qp->pmtu, tidlen - flow->tid_offset);
+ flow->sent += *len;
+ next_offset = flow->tid_offset + *len;
+ last_pkt = (flow->tid_idx == (flow->tidcnt - 1) &&
+ next_offset >= tidlen) || (flow->sent >= flow->length);
+ trace_hfi1_tid_entry_build_write_data(qp, flow->tid_idx, tidentry);
+ trace_hfi1_tid_flow_build_write_data(qp, req->clear_tail, flow);
+
+ rcu_read_lock();
+ remote = rcu_dereference(qpriv->tid_rdma.remote);
+ KDETH_RESET(wd->kdeth0, KVER, 0x1);
+ KDETH_SET(wd->kdeth0, SH, !last_pkt);
+ KDETH_SET(wd->kdeth0, INTR, !!(!last_pkt && remote->urg));
+ KDETH_SET(wd->kdeth0, TIDCTRL, EXP_TID_GET(tidentry, CTRL));
+ KDETH_SET(wd->kdeth0, TID, EXP_TID_GET(tidentry, IDX));
+ KDETH_SET(wd->kdeth0, OM, om == KDETH_OM_LARGE);
+ KDETH_SET(wd->kdeth0, OFFSET, flow->tid_offset / om);
+ KDETH_RESET(wd->kdeth1, JKEY, remote->jkey);
+ wd->verbs_qp = cpu_to_be32(qp->remote_qpn);
+ rcu_read_unlock();
+
+ *bth1 = flow->tid_qpn;
+ *bth2 = mask_psn(((flow->flow_state.spsn + flow->pkt++) &
+ HFI1_KDETH_BTH_SEQ_MASK) |
+ (flow->flow_state.generation <<
+ HFI1_KDETH_BTH_SEQ_SHIFT));
+ if (last_pkt) {
+ /* PSNs are zero-based, so +1 to count number of packets */
+ if (flow->flow_state.lpsn + 1 +
+ rvt_div_round_up_mtu(qp, req->seg_len) >
+ MAX_TID_FLOW_PSN)
+ req->state = TID_REQUEST_SYNC;
+ *bth2 |= IB_BTH_REQ_ACK;
+ }
+
+ if (next_offset >= tidlen) {
+ flow->tid_offset = 0;
+ flow->tid_idx++;
+ } else {
+ flow->tid_offset = next_offset;
+ }
+ return last_pkt;
+}
+
+void hfi1_rc_rcv_tid_rdma_write_data(struct hfi1_packet *packet)
+{
+ struct rvt_qp *qp = packet->qp;
+ struct hfi1_qp_priv *priv = qp->priv;
+ struct hfi1_ctxtdata *rcd = priv->rcd;
+ struct ib_other_headers *ohdr = packet->ohdr;
+ struct rvt_ack_entry *e;
+ struct tid_rdma_request *req;
+ struct tid_rdma_flow *flow;
+ struct hfi1_ibdev *dev = to_idev(qp->ibqp.device);
+ unsigned long flags;
+ u32 psn, next;
+ u8 opcode;
+
+ psn = mask_psn(be32_to_cpu(ohdr->bth[2]));
+ opcode = (be32_to_cpu(ohdr->bth[0]) >> 24) & 0xff;
+
+ /*
+ * All error handling should be done by now. If we are here, the packet
+ * is either good or been accepted by the error handler.
+ */
+ spin_lock_irqsave(&qp->s_lock, flags);
+ e = &qp->s_ack_queue[priv->r_tid_tail];
+ req = ack_to_tid_req(e);
+ flow = &req->flows[req->clear_tail];
+ if (cmp_psn(psn, full_flow_psn(flow, flow->flow_state.lpsn))) {
+ if (cmp_psn(psn, flow->flow_state.r_next_psn))
+ goto send_nak;
+ flow->flow_state.r_next_psn++;
+ goto exit;
+ }
+ flow->flow_state.r_next_psn = mask_psn(psn + 1);
+ hfi1_kern_exp_rcv_clear(req);
+ priv->alloc_w_segs--;
+ rcd->flows[flow->idx].psn = psn & HFI1_KDETH_BTH_SEQ_MASK;
+ req->comp_seg++;
+ priv->s_nak_state = 0;
+
+ /*
+ * Release the flow if one of the following conditions has been met:
+ * - The request has reached a sync point AND all outstanding
+ * segments have been completed, or
+ * - The entire request is complete and there are no more requests
+ * (of any kind) in the queue.
+ */
+ trace_hfi1_rsp_rcv_tid_write_data(qp, psn);
+ trace_hfi1_tid_req_rcv_write_data(qp, 0, e->opcode, e->psn, e->lpsn,
+ req);
+ trace_hfi1_tid_write_rsp_rcv_data(qp);
+ if (priv->r_tid_ack == HFI1_QP_WQE_INVALID)
+ priv->r_tid_ack = priv->r_tid_tail;
+
+ if (opcode == TID_OP(WRITE_DATA_LAST)) {
+ for (next = priv->r_tid_tail + 1; ; next++) {
+ if (next > rvt_size_atomic(&dev->rdi))
+ next = 0;
+ if (next == priv->r_tid_head)
+ break;
+ e = &qp->s_ack_queue[next];
+ if (e->opcode == TID_OP(WRITE_REQ))
+ break;
+ }
+ priv->r_tid_tail = next;
+ if (++qp->s_acked_ack_queue > rvt_size_atomic(&dev->rdi))
+ qp->s_acked_ack_queue = 0;
+ }
+
+ hfi1_tid_write_alloc_resources(qp, true);
+
+ /*
+ * If we need to generate more responses, schedule the
+ * send engine.
+ */
+ if (req->cur_seg < req->total_segs ||
+ qp->s_tail_ack_queue != qp->r_head_ack_queue) {
+ qp->s_flags |= RVT_S_RESP_PENDING;
+ hfi1_schedule_send(qp);
+ }
+
+ priv->pending_tid_w_segs--;
+ if (priv->s_flags & HFI1_R_TID_RSC_TIMER) {
+ if (priv->pending_tid_w_segs)
+ hfi1_mod_tid_reap_timer(req->qp);
+ else
+ hfi1_stop_tid_reap_timer(req->qp);
+ }
+
+done:
+ priv->s_flags |= RVT_S_ACK_PENDING;
+ hfi1_schedule_tid_send(qp);
+exit:
+ priv->r_next_psn_kdeth = flow->flow_state.r_next_psn;
+ spin_unlock_irqrestore(&qp->s_lock, flags);
+ return;
+
+send_nak:
+ if (!priv->s_nak_state) {
+ priv->s_nak_state = IB_NAK_PSN_ERROR;
+ priv->s_nak_psn = flow->flow_state.r_next_psn;
+ priv->s_flags |= RVT_S_ACK_PENDING;
+ if (priv->r_tid_ack == HFI1_QP_WQE_INVALID)
+ priv->r_tid_ack = priv->r_tid_tail;
+ hfi1_schedule_tid_send(qp);
+ }
+ goto done;
+}
+
+static bool hfi1_tid_rdma_is_resync_psn(u32 psn)
+{
+ return (bool)((psn & HFI1_KDETH_BTH_SEQ_MASK) ==
+ HFI1_KDETH_BTH_SEQ_MASK);
+}
+
+u32 hfi1_build_tid_rdma_write_ack(struct rvt_qp *qp, struct rvt_ack_entry *e,
+ struct ib_other_headers *ohdr, u16 iflow,
+ u32 *bth1, u32 *bth2)
+{
+ struct hfi1_qp_priv *qpriv = qp->priv;
+ struct tid_flow_state *fs = &qpriv->flow_state;
+ struct tid_rdma_request *req = ack_to_tid_req(e);
+ struct tid_rdma_flow *flow = &req->flows[iflow];
+ struct tid_rdma_params *remote;
+
+ rcu_read_lock();
+ remote = rcu_dereference(qpriv->tid_rdma.remote);
+ KDETH_RESET(ohdr->u.tid_rdma.ack.kdeth1, JKEY, remote->jkey);
+ ohdr->u.tid_rdma.ack.verbs_qp = cpu_to_be32(qp->remote_qpn);
+ *bth1 = remote->qp;
+ rcu_read_unlock();
+
+ if (qpriv->resync) {
+ *bth2 = mask_psn((fs->generation <<
+ HFI1_KDETH_BTH_SEQ_SHIFT) - 1);
+ ohdr->u.tid_rdma.ack.aeth = rvt_compute_aeth(qp);
+ } else if (qpriv->s_nak_state) {
+ *bth2 = mask_psn(qpriv->s_nak_psn);
+ ohdr->u.tid_rdma.ack.aeth =
+ cpu_to_be32((qp->r_msn & IB_MSN_MASK) |
+ (qpriv->s_nak_state <<
+ IB_AETH_CREDIT_SHIFT));
+ } else {
+ *bth2 = full_flow_psn(flow, flow->flow_state.lpsn);
+ ohdr->u.tid_rdma.ack.aeth = rvt_compute_aeth(qp);
+ }
+ KDETH_RESET(ohdr->u.tid_rdma.ack.kdeth0, KVER, 0x1);
+ ohdr->u.tid_rdma.ack.tid_flow_qp =
+ cpu_to_be32(qpriv->tid_rdma.local.qp |
+ ((flow->idx & TID_RDMA_DESTQP_FLOW_MASK) <<
+ TID_RDMA_DESTQP_FLOW_SHIFT) |
+ qpriv->rcd->ctxt);
+
+ ohdr->u.tid_rdma.ack.tid_flow_psn = 0;
+ ohdr->u.tid_rdma.ack.verbs_psn =
+ cpu_to_be32(flow->flow_state.resp_ib_psn);
+
+ if (qpriv->resync) {
+ /*
+ * If the PSN before the current expect KDETH PSN is the
+ * RESYNC PSN, then we never received a good TID RDMA WRITE
+ * DATA packet after a previous RESYNC.
+ * In this case, the next expected KDETH PSN stays the same.
+ */
+ if (hfi1_tid_rdma_is_resync_psn(qpriv->r_next_psn_kdeth - 1)) {
+ ohdr->u.tid_rdma.ack.tid_flow_psn =
+ cpu_to_be32(qpriv->r_next_psn_kdeth_save);
+ } else {
+ /*
+ * Because the KDETH PSNs jump during a RESYNC, it's
+ * not possible to infer (or compute) the previous value
+ * of r_next_psn_kdeth in the case of back-to-back
+ * RESYNC packets. Therefore, we save it.
+ */
+ qpriv->r_next_psn_kdeth_save =
+ qpriv->r_next_psn_kdeth - 1;
+ ohdr->u.tid_rdma.ack.tid_flow_psn =
+ cpu_to_be32(qpriv->r_next_psn_kdeth_save);
+ qpriv->r_next_psn_kdeth = mask_psn(*bth2 + 1);
+ }
+ qpriv->resync = false;
+ }
+
+ return sizeof(ohdr->u.tid_rdma.ack) / sizeof(u32);
+}
+
+void hfi1_rc_rcv_tid_rdma_ack(struct hfi1_packet *packet)
+{
+ struct ib_other_headers *ohdr = packet->ohdr;
+ struct rvt_qp *qp = packet->qp;
+ struct hfi1_qp_priv *qpriv = qp->priv;
+ struct rvt_swqe *wqe;
+ struct tid_rdma_request *req;
+ struct tid_rdma_flow *flow;
+ u32 aeth, psn, req_psn, ack_psn, fspsn, resync_psn, ack_kpsn;
+ bool is_fecn;
+ unsigned long flags;
+ u16 fidx;
+
+ trace_hfi1_tid_write_sender_rcv_tid_ack(qp, 0);
+ is_fecn = process_ecn(qp, packet);
+ psn = mask_psn(be32_to_cpu(ohdr->bth[2]));
+ aeth = be32_to_cpu(ohdr->u.tid_rdma.ack.aeth);
+ req_psn = mask_psn(be32_to_cpu(ohdr->u.tid_rdma.ack.verbs_psn));
+ resync_psn = mask_psn(be32_to_cpu(ohdr->u.tid_rdma.ack.tid_flow_psn));
+
+ spin_lock_irqsave(&qp->s_lock, flags);
+ trace_hfi1_rcv_tid_ack(qp, aeth, psn, req_psn, resync_psn);
+
+ /* If we are waiting for an ACK to RESYNC, drop any other packets */
+ if ((qp->s_flags & HFI1_S_WAIT_HALT) &&
+ cmp_psn(psn, qpriv->s_resync_psn))
+ goto ack_op_err;
+
+ ack_psn = req_psn;
+ if (hfi1_tid_rdma_is_resync_psn(psn))
+ ack_kpsn = resync_psn;
+ else
+ ack_kpsn = psn;
+ if (aeth >> 29) {
+ ack_psn--;
+ ack_kpsn--;
+ }
+
+ wqe = rvt_get_swqe_ptr(qp, qp->s_acked);
+
+ if (wqe->wr.opcode != IB_WR_TID_RDMA_WRITE)
+ goto ack_op_err;
+
+ req = wqe_to_tid_req(wqe);
+ trace_hfi1_tid_req_rcv_tid_ack(qp, 0, wqe->wr.opcode, wqe->psn,
+ wqe->lpsn, req);
+ flow = &req->flows[req->acked_tail];
+ trace_hfi1_tid_flow_rcv_tid_ack(qp, req->acked_tail, flow);
+
+ /* Drop stale ACK/NAK */
+ if (cmp_psn(psn, full_flow_psn(flow, flow->flow_state.spsn)) < 0)
+ goto ack_op_err;
+
+ while (cmp_psn(ack_kpsn,
+ full_flow_psn(flow, flow->flow_state.lpsn)) >= 0 &&
+ req->ack_seg < req->cur_seg) {
+ req->ack_seg++;
+ /* advance acked segment pointer */
+ req->acked_tail = CIRC_NEXT(req->acked_tail, MAX_FLOWS);
+ req->r_last_acked = flow->flow_state.resp_ib_psn;
+ trace_hfi1_tid_req_rcv_tid_ack(qp, 0, wqe->wr.opcode, wqe->psn,
+ wqe->lpsn, req);
+ if (req->ack_seg == req->total_segs) {
+ req->state = TID_REQUEST_COMPLETE;
+ wqe = do_rc_completion(qp, wqe,
+ to_iport(qp->ibqp.device,
+ qp->port_num));
+ trace_hfi1_sender_rcv_tid_ack(qp);
+ atomic_dec(&qpriv->n_tid_requests);
+ if (qp->s_acked == qp->s_tail)
+ break;
+ if (wqe->wr.opcode != IB_WR_TID_RDMA_WRITE)
+ break;
+ req = wqe_to_tid_req(wqe);
+ }
+ flow = &req->flows[req->acked_tail];
+ trace_hfi1_tid_flow_rcv_tid_ack(qp, req->acked_tail, flow);
+ }
+
+ trace_hfi1_tid_req_rcv_tid_ack(qp, 0, wqe->wr.opcode, wqe->psn,
+ wqe->lpsn, req);
+ switch (aeth >> 29) {
+ case 0: /* ACK */
+ if (qpriv->s_flags & RVT_S_WAIT_ACK)
+ qpriv->s_flags &= ~RVT_S_WAIT_ACK;
+ if (!hfi1_tid_rdma_is_resync_psn(psn)) {
+ /* Check if there is any pending TID ACK */
+ if (wqe->wr.opcode == IB_WR_TID_RDMA_WRITE &&
+ req->ack_seg < req->cur_seg)
+ hfi1_mod_tid_retry_timer(qp);
+ else
+ hfi1_stop_tid_retry_timer(qp);
+ hfi1_schedule_send(qp);
+ } else {
+ u32 spsn, fpsn, last_acked, generation;
+ struct tid_rdma_request *rptr;
+
+ /* ACK(RESYNC) */
+ hfi1_stop_tid_retry_timer(qp);
+ /* Allow new requests (see hfi1_make_tid_rdma_pkt) */
+ qp->s_flags &= ~HFI1_S_WAIT_HALT;
+ /*
+ * Clear RVT_S_SEND_ONE flag in case that the TID RDMA
+ * ACK is received after the TID retry timer is fired
+ * again. In this case, do not send any more TID
+ * RESYNC request or wait for any more TID ACK packet.
+ */
+ qpriv->s_flags &= ~RVT_S_SEND_ONE;
+ hfi1_schedule_send(qp);
+
+ if ((qp->s_acked == qpriv->s_tid_tail &&
+ req->ack_seg == req->total_segs) ||
+ qp->s_acked == qp->s_tail) {
+ qpriv->s_state = TID_OP(WRITE_DATA_LAST);
+ goto done;
+ }
+
+ if (req->ack_seg == req->comp_seg) {
+ qpriv->s_state = TID_OP(WRITE_DATA);
+ goto done;
+ }
+
+ /*
+ * The PSN to start with is the next PSN after the
+ * RESYNC PSN.
+ */
+ psn = mask_psn(psn + 1);
+ generation = psn >> HFI1_KDETH_BTH_SEQ_SHIFT;
+ spsn = 0;
+
+ /*
+ * Update to the correct WQE when we get an ACK(RESYNC)
+ * in the middle of a request.
+ */
+ if (delta_psn(ack_psn, wqe->lpsn))
+ wqe = rvt_get_swqe_ptr(qp, qp->s_acked);
+ req = wqe_to_tid_req(wqe);
+ flow = &req->flows[req->acked_tail];
+ /*
+ * RESYNC re-numbers the PSN ranges of all remaining
+ * segments. Also, PSN's start from 0 in the middle of a
+ * segment and the first segment size is less than the
+ * default number of packets. flow->resync_npkts is used
+ * to track the number of packets from the start of the
+ * real segment to the point of 0 PSN after the RESYNC
+ * in order to later correctly rewind the SGE.
+ */
+ fpsn = full_flow_psn(flow, flow->flow_state.spsn);
+ req->r_ack_psn = psn;
+ flow->resync_npkts +=
+ delta_psn(mask_psn(resync_psn + 1), fpsn);
+ /*
+ * Renumber all packet sequence number ranges
+ * based on the new generation.
+ */
+ last_acked = qp->s_acked;
+ rptr = req;
+ while (1) {
+ /* start from last acked segment */
+ for (fidx = rptr->acked_tail;
+ CIRC_CNT(rptr->setup_head, fidx,
+ MAX_FLOWS);
+ fidx = CIRC_NEXT(fidx, MAX_FLOWS)) {
+ u32 lpsn;
+ u32 gen;
+
+ flow = &rptr->flows[fidx];
+ gen = flow->flow_state.generation;
+ if (WARN_ON(gen == generation &&
+ flow->flow_state.spsn !=
+ spsn))
+ continue;
+ lpsn = flow->flow_state.lpsn;
+ lpsn = full_flow_psn(flow, lpsn);
+ flow->npkts =
+ delta_psn(lpsn,
+ mask_psn(resync_psn)
+ );
+ flow->flow_state.generation =
+ generation;
+ flow->flow_state.spsn = spsn;
+ flow->flow_state.lpsn =
+ flow->flow_state.spsn +
+ flow->npkts - 1;
+ flow->pkt = 0;
+ spsn += flow->npkts;
+ resync_psn += flow->npkts;
+ trace_hfi1_tid_flow_rcv_tid_ack(qp,
+ fidx,
+ flow);
+ }
+ if (++last_acked == qpriv->s_tid_cur + 1)
+ break;
+ if (last_acked == qp->s_size)
+ last_acked = 0;
+ wqe = rvt_get_swqe_ptr(qp, last_acked);
+ rptr = wqe_to_tid_req(wqe);
+ }
+ req->cur_seg = req->ack_seg;
+ qpriv->s_tid_tail = qp->s_acked;
+ qpriv->s_state = TID_OP(WRITE_REQ);
+ hfi1_schedule_tid_send(qp);
+ }
+done:
+ qpriv->s_retry = qp->s_retry_cnt;
+ break;
+
+ case 3: /* NAK */
+ hfi1_stop_tid_retry_timer(qp);
+ switch ((aeth >> IB_AETH_CREDIT_SHIFT) &
+ IB_AETH_CREDIT_MASK) {
+ case 0: /* PSN sequence error */
+ flow = &req->flows[req->acked_tail];
+ fspsn = full_flow_psn(flow, flow->flow_state.spsn);
+ trace_hfi1_tid_flow_rcv_tid_ack(qp, req->acked_tail,
+ flow);
+ req->r_ack_psn = mask_psn(be32_to_cpu(ohdr->bth[2]));
+ req->cur_seg = req->ack_seg;
+ qpriv->s_tid_tail = qp->s_acked;
+ qpriv->s_state = TID_OP(WRITE_REQ);
+ qpriv->s_retry = qp->s_retry_cnt;
+ hfi1_schedule_tid_send(qp);
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ack_op_err:
+ spin_unlock_irqrestore(&qp->s_lock, flags);
+}
+
+void hfi1_add_tid_retry_timer(struct rvt_qp *qp)
+{
+ struct hfi1_qp_priv *priv = qp->priv;
+ struct ib_qp *ibqp = &qp->ibqp;
+ struct rvt_dev_info *rdi = ib_to_rvt(ibqp->device);
+
+ lockdep_assert_held(&qp->s_lock);
+ if (!(priv->s_flags & HFI1_S_TID_RETRY_TIMER)) {
+ priv->s_flags |= HFI1_S_TID_RETRY_TIMER;
+ priv->s_tid_retry_timer.expires = jiffies +
+ priv->tid_retry_timeout_jiffies + rdi->busy_jiffies;
+ add_timer(&priv->s_tid_retry_timer);
+ }
+}
+
+static void hfi1_mod_tid_retry_timer(struct rvt_qp *qp)
+{
+ struct hfi1_qp_priv *priv = qp->priv;
+ struct ib_qp *ibqp = &qp->ibqp;
+ struct rvt_dev_info *rdi = ib_to_rvt(ibqp->device);
+
+ lockdep_assert_held(&qp->s_lock);
+ priv->s_flags |= HFI1_S_TID_RETRY_TIMER;
+ mod_timer(&priv->s_tid_retry_timer, jiffies +
+ priv->tid_retry_timeout_jiffies + rdi->busy_jiffies);
+}
+
+static int hfi1_stop_tid_retry_timer(struct rvt_qp *qp)
+{
+ struct hfi1_qp_priv *priv = qp->priv;
+ int rval = 0;
+
+ lockdep_assert_held(&qp->s_lock);
+ if (priv->s_flags & HFI1_S_TID_RETRY_TIMER) {
+ rval = del_timer(&priv->s_tid_retry_timer);
+ priv->s_flags &= ~HFI1_S_TID_RETRY_TIMER;
+ }
+ return rval;
+}
+
+void hfi1_del_tid_retry_timer(struct rvt_qp *qp)
+{
+ struct hfi1_qp_priv *priv = qp->priv;
+
+ del_timer_sync(&priv->s_tid_retry_timer);
+ priv->s_flags &= ~HFI1_S_TID_RETRY_TIMER;
+}
+
+static void hfi1_tid_retry_timeout(struct timer_list *t)
+{
+ struct hfi1_qp_priv *priv = from_timer(priv, t, s_tid_retry_timer);
+ struct rvt_qp *qp = priv->owner;
+ struct rvt_swqe *wqe;
+ unsigned long flags;
+ struct tid_rdma_request *req;
+
+ spin_lock_irqsave(&qp->r_lock, flags);
+ spin_lock(&qp->s_lock);
+ trace_hfi1_tid_write_sender_retry_timeout(qp, 0);
+ if (priv->s_flags & HFI1_S_TID_RETRY_TIMER) {
+ hfi1_stop_tid_retry_timer(qp);
+ if (!priv->s_retry) {
+ trace_hfi1_msg_tid_retry_timeout(/* msg */
+ qp,
+ "Exhausted retries. Tid retry timeout = ",
+ (u64)priv->tid_retry_timeout_jiffies);
+
+ wqe = rvt_get_swqe_ptr(qp, qp->s_acked);
+ hfi1_trdma_send_complete(qp, wqe, IB_WC_RETRY_EXC_ERR);
+ rvt_error_qp(qp, IB_WC_WR_FLUSH_ERR);
+ } else {
+ wqe = rvt_get_swqe_ptr(qp, qp->s_acked);
+ req = wqe_to_tid_req(wqe);
+ trace_hfi1_tid_req_tid_retry_timeout(/* req */
+ qp, 0, wqe->wr.opcode, wqe->psn, wqe->lpsn, req);
+
+ priv->s_flags &= ~RVT_S_WAIT_ACK;
+ /* Only send one packet (the RESYNC) */
+ priv->s_flags |= RVT_S_SEND_ONE;
+ /*
+ * No additional request shall be made by this QP until
+ * the RESYNC has been complete.
+ */
+ qp->s_flags |= HFI1_S_WAIT_HALT;
+ priv->s_state = TID_OP(RESYNC);
+ priv->s_retry--;
+ hfi1_schedule_tid_send(qp);
+ }
+ }
+ spin_unlock(&qp->s_lock);
+ spin_unlock_irqrestore(&qp->r_lock, flags);
+}
+
+u32 hfi1_build_tid_rdma_resync(struct rvt_qp *qp, struct rvt_swqe *wqe,
+ struct ib_other_headers *ohdr, u32 *bth1,
+ u32 *bth2, u16 fidx)
+{
+ struct hfi1_qp_priv *qpriv = qp->priv;
+ struct tid_rdma_params *remote;
+ struct tid_rdma_request *req = wqe_to_tid_req(wqe);
+ struct tid_rdma_flow *flow = &req->flows[fidx];
+ u32 generation;
+
+ rcu_read_lock();
+ remote = rcu_dereference(qpriv->tid_rdma.remote);
+ KDETH_RESET(ohdr->u.tid_rdma.ack.kdeth1, JKEY, remote->jkey);
+ ohdr->u.tid_rdma.ack.verbs_qp = cpu_to_be32(qp->remote_qpn);
+ *bth1 = remote->qp;
+ rcu_read_unlock();
+
+ generation = kern_flow_generation_next(flow->flow_state.generation);
+ *bth2 = mask_psn((generation << HFI1_KDETH_BTH_SEQ_SHIFT) - 1);
+ qpriv->s_resync_psn = *bth2;
+ *bth2 |= IB_BTH_REQ_ACK;
+ KDETH_RESET(ohdr->u.tid_rdma.ack.kdeth0, KVER, 0x1);
+
+ return sizeof(ohdr->u.tid_rdma.resync) / sizeof(u32);
+}
+
+void hfi1_rc_rcv_tid_rdma_resync(struct hfi1_packet *packet)
+{
+ struct ib_other_headers *ohdr = packet->ohdr;
+ struct rvt_qp *qp = packet->qp;
+ struct hfi1_qp_priv *qpriv = qp->priv;
+ struct hfi1_ctxtdata *rcd = qpriv->rcd;
+ struct hfi1_ibdev *dev = to_idev(qp->ibqp.device);
+ struct rvt_ack_entry *e;
+ struct tid_rdma_request *req;
+ struct tid_rdma_flow *flow;
+ struct tid_flow_state *fs = &qpriv->flow_state;
+ u32 psn, generation, idx, gen_next;
+ bool is_fecn;
+ unsigned long flags;
+
+ is_fecn = process_ecn(qp, packet);
+ psn = mask_psn(be32_to_cpu(ohdr->bth[2]));
+
+ generation = mask_psn(psn + 1) >> HFI1_KDETH_BTH_SEQ_SHIFT;
+ spin_lock_irqsave(&qp->s_lock, flags);
+
+ gen_next = (fs->generation == KERN_GENERATION_RESERVED) ?
+ generation : kern_flow_generation_next(fs->generation);
+ /*
+ * RESYNC packet contains the "next" generation and can only be
+ * from the current or previous generations
+ */
+ if (generation != mask_generation(gen_next - 1) &&
+ generation != gen_next)
+ goto bail;
+ /* Already processing a resync */
+ if (qpriv->resync)
+ goto bail;
+
+ spin_lock(&rcd->exp_lock);
+ if (fs->index >= RXE_NUM_TID_FLOWS) {
+ /*
+ * If we don't have a flow, save the generation so it can be
+ * applied when a new flow is allocated
+ */
+ fs->generation = generation;
+ } else {
+ /* Reprogram the QP flow with new generation */
+ rcd->flows[fs->index].generation = generation;
+ fs->generation = kern_setup_hw_flow(rcd, fs->index);
+ }
+ fs->psn = 0;
+ /*
+ * Disable SW PSN checking since a RESYNC is equivalent to a
+ * sync point and the flow has/will be reprogrammed
+ */
+ qpriv->s_flags &= ~HFI1_R_TID_SW_PSN;
+ trace_hfi1_tid_write_rsp_rcv_resync(qp);
+
+ /*
+ * Reset all TID flow information with the new generation.
+ * This is done for all requests and segments after the
+ * last received segment
+ */
+ for (idx = qpriv->r_tid_tail; ; idx++) {
+ u16 flow_idx;
+
+ if (idx > rvt_size_atomic(&dev->rdi))
+ idx = 0;
+ e = &qp->s_ack_queue[idx];
+ if (e->opcode == TID_OP(WRITE_REQ)) {
+ req = ack_to_tid_req(e);
+ trace_hfi1_tid_req_rcv_resync(qp, 0, e->opcode, e->psn,
+ e->lpsn, req);
+
+ /* start from last unacked segment */
+ for (flow_idx = req->clear_tail;
+ CIRC_CNT(req->setup_head, flow_idx,
+ MAX_FLOWS);
+ flow_idx = CIRC_NEXT(flow_idx, MAX_FLOWS)) {
+ u32 lpsn;
+ u32 next;
+
+ flow = &req->flows[flow_idx];
+ lpsn = full_flow_psn(flow,
+ flow->flow_state.lpsn);
+ next = flow->flow_state.r_next_psn;
+ flow->npkts = delta_psn(lpsn, next - 1);
+ flow->flow_state.generation = fs->generation;
+ flow->flow_state.spsn = fs->psn;
+ flow->flow_state.lpsn =
+ flow->flow_state.spsn + flow->npkts - 1;
+ flow->flow_state.r_next_psn =
+ full_flow_psn(flow,
+ flow->flow_state.spsn);
+ fs->psn += flow->npkts;
+ trace_hfi1_tid_flow_rcv_resync(qp, flow_idx,
+ flow);
+ }
+ }
+ if (idx == qp->s_tail_ack_queue)
+ break;
+ }
+
+ spin_unlock(&rcd->exp_lock);
+ qpriv->resync = true;
+ /* RESYNC request always gets a TID RDMA ACK. */
+ qpriv->s_nak_state = 0;
+ qpriv->s_flags |= RVT_S_ACK_PENDING;
+ hfi1_schedule_tid_send(qp);
+bail:
+ spin_unlock_irqrestore(&qp->s_lock, flags);
+}
+
+/*
+ * Call this function when the last TID RDMA WRITE DATA packet for a request
+ * is built.
+ */
+static void update_tid_tail(struct rvt_qp *qp)
+ __must_hold(&qp->s_lock)
+{
+ struct hfi1_qp_priv *priv = qp->priv;
+ u32 i;
+ struct rvt_swqe *wqe;
+
+ lockdep_assert_held(&qp->s_lock);
+ /* Can't move beyond s_tid_cur */
+ if (priv->s_tid_tail == priv->s_tid_cur)
+ return;
+ for (i = priv->s_tid_tail + 1; ; i++) {
+ if (i == qp->s_size)
+ i = 0;
+
+ if (i == priv->s_tid_cur)
+ break;
+ wqe = rvt_get_swqe_ptr(qp, i);
+ if (wqe->wr.opcode == IB_WR_TID_RDMA_WRITE)
+ break;
+ }
+ priv->s_tid_tail = i;
+ priv->s_state = TID_OP(WRITE_RESP);
+}
+
+int hfi1_make_tid_rdma_pkt(struct rvt_qp *qp, struct hfi1_pkt_state *ps)
+ __must_hold(&qp->s_lock)
+{
+ struct hfi1_qp_priv *priv = qp->priv;
+ struct rvt_swqe *wqe;
+ u32 bth1 = 0, bth2 = 0, hwords = 5, len, middle = 0;
+ struct ib_other_headers *ohdr;
+ struct rvt_sge_state *ss = &qp->s_sge;
+ struct rvt_ack_entry *e = &qp->s_ack_queue[qp->s_tail_ack_queue];
+ struct tid_rdma_request *req = ack_to_tid_req(e);
+ bool last = false;
+ u8 opcode = TID_OP(WRITE_DATA);
+
+ lockdep_assert_held(&qp->s_lock);
+ trace_hfi1_tid_write_sender_make_tid_pkt(qp, 0);
+ /*
+ * Prioritize the sending of the requests and responses over the
+ * sending of the TID RDMA data packets.
+ */
+ if (((atomic_read(&priv->n_tid_requests) < HFI1_TID_RDMA_WRITE_CNT) &&
+ atomic_read(&priv->n_requests) &&
+ !(qp->s_flags & (RVT_S_BUSY | RVT_S_WAIT_ACK |
+ HFI1_S_ANY_WAIT_IO))) ||
+ (e->opcode == TID_OP(WRITE_REQ) && req->cur_seg < req->alloc_seg &&
+ !(qp->s_flags & (RVT_S_BUSY | HFI1_S_ANY_WAIT_IO)))) {
+ struct iowait_work *iowork;
+
+ iowork = iowait_get_ib_work(&priv->s_iowait);
+ ps->s_txreq = get_waiting_verbs_txreq(iowork);
+ if (ps->s_txreq || hfi1_make_rc_req(qp, ps)) {
+ priv->s_flags |= HFI1_S_TID_BUSY_SET;
+ return 1;
+ }
+ }
+
+ ps->s_txreq = get_txreq(ps->dev, qp);
+ if (!ps->s_txreq)
+ goto bail_no_tx;
+
+ ohdr = &ps->s_txreq->phdr.hdr.ibh.u.oth;
+
+ if ((priv->s_flags & RVT_S_ACK_PENDING) &&
+ make_tid_rdma_ack(qp, ohdr, ps))
+ return 1;
+
+ if (!(ib_rvt_state_ops[qp->state] & RVT_PROCESS_SEND_OK)) {
+ if (!(ib_rvt_state_ops[qp->state] & RVT_FLUSH_SEND))
+ goto bail;
+ /* We are in the error state, flush the work request. */
+ if (qp->s_last == READ_ONCE(qp->s_head))
+ goto bail;
+ /* If DMAs are in progress, we can't flush immediately. */
+ if (iowait_sdma_pending(&priv->s_iowait)) {
+ qp->s_flags |= RVT_S_WAIT_DMA;
+ goto bail;
+ }
+ clear_ahg(qp);
+ wqe = rvt_get_swqe_ptr(qp, qp->s_last);
+ hfi1_trdma_send_complete(qp, wqe, qp->s_last != qp->s_acked ?
+ IB_WC_SUCCESS : IB_WC_WR_FLUSH_ERR);
+ /* will get called again */
+ goto done_free_tx;
+ }
+
+ if (priv->s_flags & RVT_S_WAIT_ACK)
+ goto bail;
+
+ /* Check whether there is anything to do. */
+ if (priv->s_tid_tail == HFI1_QP_WQE_INVALID)
+ goto bail;
+ wqe = rvt_get_swqe_ptr(qp, priv->s_tid_tail);
+ req = wqe_to_tid_req(wqe);
+ trace_hfi1_tid_req_make_tid_pkt(qp, 0, wqe->wr.opcode, wqe->psn,
+ wqe->lpsn, req);
+ switch (priv->s_state) {
+ case TID_OP(WRITE_REQ):
+ case TID_OP(WRITE_RESP):
+ priv->tid_ss.sge = wqe->sg_list[0];
+ priv->tid_ss.sg_list = wqe->sg_list + 1;
+ priv->tid_ss.num_sge = wqe->wr.num_sge;
+ priv->tid_ss.total_len = wqe->length;
+
+ if (priv->s_state == TID_OP(WRITE_REQ))
+ hfi1_tid_rdma_restart_req(qp, wqe, &bth2);
+ priv->s_state = TID_OP(WRITE_DATA);
+ /* fall through */
+
+ case TID_OP(WRITE_DATA):
+ /*
+ * 1. Check whether TID RDMA WRITE RESP available.
+ * 2. If no:
+ * 2.1 If have more segments and no TID RDMA WRITE RESP,
+ * set HFI1_S_WAIT_TID_RESP
+ * 2.2 Return indicating no progress made.
+ * 3. If yes:
+ * 3.1 Build TID RDMA WRITE DATA packet.
+ * 3.2 If last packet in segment:
+ * 3.2.1 Change KDETH header bits
+ * 3.2.2 Advance RESP pointers.
+ * 3.3 Return indicating progress made.
+ */
+ trace_hfi1_sender_make_tid_pkt(qp);
+ trace_hfi1_tid_write_sender_make_tid_pkt(qp, 0);
+ wqe = rvt_get_swqe_ptr(qp, priv->s_tid_tail);
+ req = wqe_to_tid_req(wqe);
+ len = wqe->length;
+
+ if (!req->comp_seg || req->cur_seg == req->comp_seg)
+ goto bail;
+
+ trace_hfi1_tid_req_make_tid_pkt(qp, 0, wqe->wr.opcode,
+ wqe->psn, wqe->lpsn, req);
+ last = hfi1_build_tid_rdma_packet(wqe, ohdr, &bth1, &bth2,
+ &len);
+
+ if (last) {
+ /* move pointer to next flow */
+ req->clear_tail = CIRC_NEXT(req->clear_tail,
+ MAX_FLOWS);
+ if (++req->cur_seg < req->total_segs) {
+ if (!CIRC_CNT(req->setup_head, req->clear_tail,
+ MAX_FLOWS))
+ qp->s_flags |= HFI1_S_WAIT_TID_RESP;
+ } else {
+ priv->s_state = TID_OP(WRITE_DATA_LAST);
+ opcode = TID_OP(WRITE_DATA_LAST);
+
+ /* Advance the s_tid_tail now */
+ update_tid_tail(qp);
+ }
+ }
+ hwords += sizeof(ohdr->u.tid_rdma.w_data) / sizeof(u32);
+ ss = &priv->tid_ss;
+ break;
+
+ case TID_OP(RESYNC):
+ trace_hfi1_sender_make_tid_pkt(qp);
+ /* Use generation from the most recently received response */
+ wqe = rvt_get_swqe_ptr(qp, priv->s_tid_cur);
+ req = wqe_to_tid_req(wqe);
+ /* If no responses for this WQE look at the previous one */
+ if (!req->comp_seg) {
+ wqe = rvt_get_swqe_ptr(qp,
+ (!priv->s_tid_cur ? qp->s_size :
+ priv->s_tid_cur) - 1);
+ req = wqe_to_tid_req(wqe);
+ }
+ hwords += hfi1_build_tid_rdma_resync(qp, wqe, ohdr, &bth1,
+ &bth2,
+ CIRC_PREV(req->setup_head,
+ MAX_FLOWS));
+ ss = NULL;
+ len = 0;
+ opcode = TID_OP(RESYNC);
+ break;
+
+ default:
+ goto bail;
+ }
+ if (priv->s_flags & RVT_S_SEND_ONE) {
+ priv->s_flags &= ~RVT_S_SEND_ONE;
+ priv->s_flags |= RVT_S_WAIT_ACK;
+ bth2 |= IB_BTH_REQ_ACK;
+ }
+ qp->s_len -= len;
+ ps->s_txreq->hdr_dwords = hwords;
+ ps->s_txreq->sde = priv->s_sde;
+ ps->s_txreq->ss = ss;
+ ps->s_txreq->s_cur_size = len;
+ hfi1_make_ruc_header(qp, ohdr, (opcode << 24), bth1, bth2,
+ middle, ps);
+ return 1;
+done_free_tx:
+ hfi1_put_txreq(ps->s_txreq);
+ ps->s_txreq = NULL;
+ return 1;
+
+bail:
+ hfi1_put_txreq(ps->s_txreq);
+bail_no_tx:
+ ps->s_txreq = NULL;
+ priv->s_flags &= ~RVT_S_BUSY;
+ /*
+ * If we didn't get a txreq, the QP will be woken up later to try
+ * again, set the flags to the the wake up which work item to wake
+ * up.
+ * (A better algorithm should be found to do this and generalize the
+ * sleep/wakeup flags.)
+ */
+ iowait_set_flag(&priv->s_iowait, IOWAIT_PENDING_TID);
+ return 0;
+}
+
+static int make_tid_rdma_ack(struct rvt_qp *qp,
+ struct ib_other_headers *ohdr,
+ struct hfi1_pkt_state *ps)
+{
+ struct rvt_ack_entry *e;
+ struct hfi1_qp_priv *qpriv = qp->priv;
+ struct hfi1_ibdev *dev = to_idev(qp->ibqp.device);
+ u32 hwords, next;
+ u32 len = 0;
+ u32 bth1 = 0, bth2 = 0;
+ int middle = 0;
+ u16 flow;
+ struct tid_rdma_request *req, *nreq;
+
+ trace_hfi1_tid_write_rsp_make_tid_ack(qp);
+ /* Don't send an ACK if we aren't supposed to. */
+ if (!(ib_rvt_state_ops[qp->state] & RVT_PROCESS_RECV_OK))
+ goto bail;
+
+ /* header size in 32-bit words LRH+BTH = (8+12)/4. */
+ hwords = 5;
+
+ e = &qp->s_ack_queue[qpriv->r_tid_ack];
+ req = ack_to_tid_req(e);
+ /*
+ * In the RESYNC case, we are exactly one segment past the
+ * previously sent ack or at the previously sent NAK. So to send
+ * the resync ack, we go back one segment (which might be part of
+ * the previous request) and let the do-while loop execute again.
+ * The advantage of executing the do-while loop is that any data
+ * received after the previous ack is automatically acked in the
+ * RESYNC ack. It turns out that for the do-while loop we only need
+ * to pull back qpriv->r_tid_ack, not the segment
+ * indices/counters. The scheme works even if the previous request
+ * was not a TID WRITE request.
+ */
+ if (qpriv->resync) {
+ if (!req->ack_seg || req->ack_seg == req->total_segs)
+ qpriv->r_tid_ack = !qpriv->r_tid_ack ?
+ rvt_size_atomic(&dev->rdi) :
+ qpriv->r_tid_ack - 1;
+ e = &qp->s_ack_queue[qpriv->r_tid_ack];
+ req = ack_to_tid_req(e);
+ }
+
+ trace_hfi1_rsp_make_tid_ack(qp, e->psn);
+ trace_hfi1_tid_req_make_tid_ack(qp, 0, e->opcode, e->psn, e->lpsn,
+ req);
+ /*
+ * If we've sent all the ACKs that we can, we are done
+ * until we get more segments...
+ */
+ if (!qpriv->s_nak_state && !qpriv->resync &&
+ req->ack_seg == req->comp_seg)
+ goto bail;
+
+ do {
+ /*
+ * To deal with coalesced ACKs, the acked_tail pointer
+ * into the flow array is used. The distance between it
+ * and the clear_tail is the number of flows that are
+ * being ACK'ed.
+ */
+ req->ack_seg +=
+ /* Get up-to-date value */
+ CIRC_CNT(req->clear_tail, req->acked_tail,
+ MAX_FLOWS);
+ /* Advance acked index */
+ req->acked_tail = req->clear_tail;
+
+ /*
+ * req->clear_tail points to the segment currently being
+ * received. So, when sending an ACK, the previous
+ * segment is being ACK'ed.
+ */
+ flow = CIRC_PREV(req->acked_tail, MAX_FLOWS);
+ if (req->ack_seg != req->total_segs)
+ break;
+ req->state = TID_REQUEST_COMPLETE;
+
+ next = qpriv->r_tid_ack + 1;
+ if (next > rvt_size_atomic(&dev->rdi))
+ next = 0;
+ qpriv->r_tid_ack = next;
+ if (qp->s_ack_queue[next].opcode != TID_OP(WRITE_REQ))
+ break;
+ nreq = ack_to_tid_req(&qp->s_ack_queue[next]);
+ if (!nreq->comp_seg || nreq->ack_seg == nreq->comp_seg)
+ break;
+
+ /* Move to the next ack entry now */
+ e = &qp->s_ack_queue[qpriv->r_tid_ack];
+ req = ack_to_tid_req(e);
+ } while (1);
+
+ /*
+ * At this point qpriv->r_tid_ack == qpriv->r_tid_tail but e and
+ * req could be pointing at the previous ack queue entry
+ */
+ if (qpriv->s_nak_state ||
+ (qpriv->resync &&
+ !hfi1_tid_rdma_is_resync_psn(qpriv->r_next_psn_kdeth - 1) &&
+ (cmp_psn(qpriv->r_next_psn_kdeth - 1,
+ full_flow_psn(&req->flows[flow],
+ req->flows[flow].flow_state.lpsn)) > 0))) {
+ /*
+ * A NAK will implicitly acknowledge all previous TID RDMA
+ * requests. Therefore, we NAK with the req->acked_tail
+ * segment for the request at qpriv->r_tid_ack (same at
+ * this point as the req->clear_tail segment for the
+ * qpriv->r_tid_tail request)
+ */
+ e = &qp->s_ack_queue[qpriv->r_tid_ack];
+ req = ack_to_tid_req(e);
+ flow = req->acked_tail;
+ } else if (req->ack_seg == req->total_segs &&
+ qpriv->s_flags & HFI1_R_TID_WAIT_INTERLCK)
+ qpriv->s_flags &= ~HFI1_R_TID_WAIT_INTERLCK;
+
+ trace_hfi1_tid_write_rsp_make_tid_ack(qp);
+ trace_hfi1_tid_req_make_tid_ack(qp, 0, e->opcode, e->psn, e->lpsn,
+ req);
+ hwords += hfi1_build_tid_rdma_write_ack(qp, e, ohdr, flow, &bth1,
+ &bth2);
+ len = 0;
+ qpriv->s_flags &= ~RVT_S_ACK_PENDING;
+ ps->s_txreq->hdr_dwords = hwords;
+ ps->s_txreq->sde = qpriv->s_sde;
+ ps->s_txreq->s_cur_size = len;
+ ps->s_txreq->ss = NULL;
+ hfi1_make_ruc_header(qp, ohdr, (TID_OP(ACK) << 24), bth1, bth2, middle,
+ ps);
+ ps->s_txreq->txreq.flags |= SDMA_TXREQ_F_VIP;
+ return 1;
+bail:
+ /*
+ * Ensure s_rdma_ack_cnt changes are committed prior to resetting
+ * RVT_S_RESP_PENDING
+ */
+ smp_wmb();
+ qpriv->s_flags &= ~RVT_S_ACK_PENDING;
+ return 0;
+}
+
+static int hfi1_send_tid_ok(struct rvt_qp *qp)
+{
+ struct hfi1_qp_priv *priv = qp->priv;
+
+ return !(priv->s_flags & RVT_S_BUSY ||
+ qp->s_flags & HFI1_S_ANY_WAIT_IO) &&
+ (verbs_txreq_queued(iowait_get_tid_work(&priv->s_iowait)) ||
+ (priv->s_flags & RVT_S_RESP_PENDING) ||
+ !(qp->s_flags & HFI1_S_ANY_TID_WAIT_SEND));
+}
+
+void _hfi1_do_tid_send(struct work_struct *work)
+{
+ struct iowait_work *w = container_of(work, struct iowait_work, iowork);
+ struct rvt_qp *qp = iowait_to_qp(w->iow);
+
+ hfi1_do_tid_send(qp);
+}
+
+static void hfi1_do_tid_send(struct rvt_qp *qp)
+{
+ struct hfi1_pkt_state ps;
+ struct hfi1_qp_priv *priv = qp->priv;
+
+ ps.dev = to_idev(qp->ibqp.device);
+ ps.ibp = to_iport(qp->ibqp.device, qp->port_num);
+ ps.ppd = ppd_from_ibp(ps.ibp);
+ ps.wait = iowait_get_tid_work(&priv->s_iowait);
+ ps.in_thread = false;
+ ps.timeout_int = qp->timeout_jiffies / 8;
+
+ trace_hfi1_rc_do_tid_send(qp, false);
+ spin_lock_irqsave(&qp->s_lock, ps.flags);
+
+ /* Return if we are already busy processing a work request. */
+ if (!hfi1_send_tid_ok(qp)) {
+ if (qp->s_flags & HFI1_S_ANY_WAIT_IO)
+ iowait_set_flag(&priv->s_iowait, IOWAIT_PENDING_TID);
+ spin_unlock_irqrestore(&qp->s_lock, ps.flags);
+ return;
+ }
+
+ priv->s_flags |= RVT_S_BUSY;
+
+ ps.timeout = jiffies + ps.timeout_int;
+ ps.cpu = priv->s_sde ? priv->s_sde->cpu :
+ cpumask_first(cpumask_of_node(ps.ppd->dd->node));
+ ps.pkts_sent = false;
+
+ /* insure a pre-built packet is handled */
+ ps.s_txreq = get_waiting_verbs_txreq(ps.wait);
+ do {
+ /* Check for a constructed packet to be sent. */
+ if (ps.s_txreq) {
+ if (priv->s_flags & HFI1_S_TID_BUSY_SET) {
+ qp->s_flags |= RVT_S_BUSY;
+ ps.wait = iowait_get_ib_work(&priv->s_iowait);
+ }
+ spin_unlock_irqrestore(&qp->s_lock, ps.flags);
+
+ /*
+ * If the packet cannot be sent now, return and
+ * the send tasklet will be woken up later.
+ */
+ if (hfi1_verbs_send(qp, &ps))
+ return;
+
+ /* allow other tasks to run */
+ if (hfi1_schedule_send_yield(qp, &ps, true))
+ return;
+
+ spin_lock_irqsave(&qp->s_lock, ps.flags);
+ if (priv->s_flags & HFI1_S_TID_BUSY_SET) {
+ qp->s_flags &= ~RVT_S_BUSY;
+ priv->s_flags &= ~HFI1_S_TID_BUSY_SET;
+ ps.wait = iowait_get_tid_work(&priv->s_iowait);
+ if (iowait_flag_set(&priv->s_iowait,
+ IOWAIT_PENDING_IB))
+ hfi1_schedule_send(qp);
+ }
+ }
+ } while (hfi1_make_tid_rdma_pkt(qp, &ps));
+ iowait_starve_clear(ps.pkts_sent, &priv->s_iowait);
+ spin_unlock_irqrestore(&qp->s_lock, ps.flags);
+}
+
+static bool _hfi1_schedule_tid_send(struct rvt_qp *qp)
+{
+ struct hfi1_qp_priv *priv = qp->priv;
+ struct hfi1_ibport *ibp =
+ to_iport(qp->ibqp.device, qp->port_num);
+ struct hfi1_pportdata *ppd = ppd_from_ibp(ibp);
+ struct hfi1_devdata *dd = dd_from_ibdev(qp->ibqp.device);
+
+ return iowait_tid_schedule(&priv->s_iowait, ppd->hfi1_wq,
+ priv->s_sde ?
+ priv->s_sde->cpu :
+ cpumask_first(cpumask_of_node(dd->node)));
+}
+
+/**
+ * hfi1_schedule_tid_send - schedule progress on TID RDMA state machine
+ * @qp: the QP
+ *
+ * This schedules qp progress on the TID RDMA state machine. Caller
+ * should hold the s_lock.
+ * Unlike hfi1_schedule_send(), this cannot use hfi1_send_ok() because
+ * the two state machines can step on each other with respect to the
+ * RVT_S_BUSY flag.
+ * Therefore, a modified test is used.
+ * @return true if the second leg is scheduled;
+ * false if the second leg is not scheduled.
+ */
+bool hfi1_schedule_tid_send(struct rvt_qp *qp)
+{
+ lockdep_assert_held(&qp->s_lock);
+ if (hfi1_send_tid_ok(qp)) {
+ /*
+ * The following call returns true if the qp is not on the
+ * queue and false if the qp is already on the queue before
+ * this call. Either way, the qp will be on the queue when the
+ * call returns.
+ */
+ _hfi1_schedule_tid_send(qp);
+ return true;
+ }
+ if (qp->s_flags & HFI1_S_ANY_WAIT_IO)
+ iowait_set_flag(&((struct hfi1_qp_priv *)qp->priv)->s_iowait,
+ IOWAIT_PENDING_TID);
+ return false;
+}
+
+bool hfi1_tid_rdma_ack_interlock(struct rvt_qp *qp, struct rvt_ack_entry *e)
+{
+ struct rvt_ack_entry *prev;
+ struct tid_rdma_request *req;
+ struct hfi1_ibdev *dev = to_idev(qp->ibqp.device);
+ struct hfi1_qp_priv *priv = qp->priv;
+ u32 s_prev;
+
+ s_prev = qp->s_tail_ack_queue == 0 ? rvt_size_atomic(&dev->rdi) :
+ (qp->s_tail_ack_queue - 1);
+ prev = &qp->s_ack_queue[s_prev];
+
+ if ((e->opcode == TID_OP(READ_REQ) ||
+ e->opcode == OP(RDMA_READ_REQUEST)) &&
+ prev->opcode == TID_OP(WRITE_REQ)) {
+ req = ack_to_tid_req(prev);
+ if (req->ack_seg != req->total_segs) {
+ priv->s_flags |= HFI1_R_TID_WAIT_INTERLCK;
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.h b/drivers/infiniband/hw/hfi1/tid_rdma.h
index 6fcd3adcdcc3..53ab24ef4f02 100644
--- a/drivers/infiniband/hw/hfi1/tid_rdma.h
+++ b/drivers/infiniband/hw/hfi1/tid_rdma.h
@@ -6,8 +6,317 @@
#ifndef HFI1_TID_RDMA_H
#define HFI1_TID_RDMA_H
+#include <linux/circ_buf.h>
+#include "common.h"
+
+/* Add a convenience helper */
+#define CIRC_ADD(val, add, size) (((val) + (add)) & ((size) - 1))
+#define CIRC_NEXT(val, size) CIRC_ADD(val, 1, size)
+#define CIRC_PREV(val, size) CIRC_ADD(val, -1, size)
+
+#define TID_RDMA_MIN_SEGMENT_SIZE BIT(18) /* 256 KiB (for now) */
+#define TID_RDMA_MAX_SEGMENT_SIZE BIT(18) /* 256 KiB (for now) */
+#define TID_RDMA_MAX_PAGES (BIT(18) >> PAGE_SHIFT)
+
+/*
+ * Bit definitions for priv->s_flags.
+ * These bit flags overload the bit flags defined for the QP's s_flags.
+ * Due to the fact that these bit fields are used only for the QP priv
+ * s_flags, there are no collisions.
+ *
+ * HFI1_S_TID_WAIT_INTERLCK - QP is waiting for requester interlock
+ * HFI1_R_TID_WAIT_INTERLCK - QP is waiting for responder interlock
+ */
+#define HFI1_S_TID_BUSY_SET BIT(0)
+/* BIT(1) reserved for RVT_S_BUSY. */
+#define HFI1_R_TID_RSC_TIMER BIT(2)
+/* BIT(3) reserved for RVT_S_RESP_PENDING. */
+/* BIT(4) reserved for RVT_S_ACK_PENDING. */
+#define HFI1_S_TID_WAIT_INTERLCK BIT(5)
+#define HFI1_R_TID_WAIT_INTERLCK BIT(6)
+/* BIT(7) - BIT(15) reserved for RVT_S_WAIT_*. */
+/* BIT(16) reserved for RVT_S_SEND_ONE */
+#define HFI1_S_TID_RETRY_TIMER BIT(17)
+/* BIT(18) reserved for RVT_S_ECN. */
+#define HFI1_R_TID_SW_PSN BIT(19)
+/* BIT(26) reserved for HFI1_S_WAIT_HALT */
+/* BIT(27) reserved for HFI1_S_WAIT_TID_RESP */
+/* BIT(28) reserved for HFI1_S_WAIT_TID_SPACE */
+
+/*
+ * Unlike regular IB RDMA VERBS, which do not require an entry
+ * in the s_ack_queue, TID RDMA WRITE requests do because they
+ * generate responses.
+ * Therefore, the s_ack_queue needs to be extended by a certain
+ * amount. The key point is that the queue needs to be extended
+ * without letting the "user" know so they user doesn't end up
+ * using these extra entries.
+ */
+#define HFI1_TID_RDMA_WRITE_CNT 8
+
+struct tid_rdma_params {
+ struct rcu_head rcu_head;
+ u32 qp;
+ u32 max_len;
+ u16 jkey;
+ u8 max_read;
+ u8 max_write;
+ u8 timeout;
+ u8 urg;
+ u8 version;
+};
+
+struct tid_rdma_qp_params {
+ struct work_struct trigger_work;
+ struct tid_rdma_params local;
+ struct tid_rdma_params __rcu *remote;
+};
+
+/* Track state for each hardware flow */
+struct tid_flow_state {
+ u32 generation;
+ u32 psn;
+ u32 r_next_psn; /* next PSN to be received (in TID space) */
+ u8 index;
+ u8 last_index;
+ u8 flags;
+};
+
+enum tid_rdma_req_state {
+ TID_REQUEST_INACTIVE = 0,
+ TID_REQUEST_INIT,
+ TID_REQUEST_INIT_RESEND,
+ TID_REQUEST_ACTIVE,
+ TID_REQUEST_RESEND,
+ TID_REQUEST_RESEND_ACTIVE,
+ TID_REQUEST_QUEUED,
+ TID_REQUEST_SYNC,
+ TID_REQUEST_RNR_NAK,
+ TID_REQUEST_COMPLETE,
+};
+
+struct tid_rdma_request {
+ struct rvt_qp *qp;
+ struct hfi1_ctxtdata *rcd;
+ union {
+ struct rvt_swqe *swqe;
+ struct rvt_ack_entry *ack;
+ } e;
+
+ struct tid_rdma_flow *flows; /* array of tid flows */
+ struct rvt_sge_state ss; /* SGE state for TID RDMA requests */
+ u16 n_flows; /* size of the flow buffer window */
+ u16 setup_head; /* flow index we are setting up */
+ u16 clear_tail; /* flow index we are clearing */
+ u16 flow_idx; /* flow index most recently set up */
+ u16 acked_tail;
+
+ u32 seg_len;
+ u32 total_len;
+ u32 r_ack_psn; /* next expected ack PSN */
+ u32 r_flow_psn; /* IB PSN of next segment start */
+ u32 r_last_acked; /* IB PSN of last ACK'ed packet */
+ u32 s_next_psn; /* IB PSN of next segment start for read */
+
+ u32 total_segs; /* segments required to complete a request */
+ u32 cur_seg; /* index of current segment */
+ u32 comp_seg; /* index of last completed segment */
+ u32 ack_seg; /* index of last ack'ed segment */
+ u32 alloc_seg; /* index of next segment to be allocated */
+ u32 isge; /* index of "current" sge */
+ u32 ack_pending; /* num acks pending for this request */
+
+ enum tid_rdma_req_state state;
+};
+
+/*
+ * When header suppression is used, PSNs associated with a "flow" are
+ * relevant (and not the PSNs maintained by verbs). Track per-flow
+ * PSNs here for a TID RDMA segment.
+ *
+ */
+struct flow_state {
+ u32 flags;
+ u32 resp_ib_psn; /* The IB PSN of the response for this flow */
+ u32 generation; /* generation of flow */
+ u32 spsn; /* starting PSN in TID space */
+ u32 lpsn; /* last PSN in TID space */
+ u32 r_next_psn; /* next PSN to be received (in TID space) */
+
+ /* For tid rdma read */
+ u32 ib_spsn; /* starting PSN in Verbs space */
+ u32 ib_lpsn; /* last PSn in Verbs space */
+};
+
+struct tid_rdma_pageset {
+ dma_addr_t addr : 48; /* Only needed for the first page */
+ u8 idx: 8;
+ u8 count : 7;
+ u8 mapped: 1;
+};
+
+/**
+ * kern_tid_node - used for managing TID's in TID groups
+ *
+ * @grp_idx: rcd relative index to tid_group
+ * @map: grp->map captured prior to programming this TID group in HW
+ * @cnt: Only @cnt of available group entries are actually programmed
+ */
+struct kern_tid_node {
+ struct tid_group *grp;
+ u8 map;
+ u8 cnt;
+};
+
+/* Overall info for a TID RDMA segment */
+struct tid_rdma_flow {
+ /*
+ * While a TID RDMA segment is being transferred, it uses a QP number
+ * from the "KDETH section of QP numbers" (which is different from the
+ * QP number that originated the request). Bits 11-15 of these QP
+ * numbers identify the "TID flow" for the segment.
+ */
+ struct flow_state flow_state;
+ struct tid_rdma_request *req;
+ u32 tid_qpn;
+ u32 tid_offset;
+ u32 length;
+ u32 sent;
+ u8 tnode_cnt;
+ u8 tidcnt;
+ u8 tid_idx;
+ u8 idx;
+ u8 npagesets;
+ u8 npkts;
+ u8 pkt;
+ u8 resync_npkts;
+ struct kern_tid_node tnode[TID_RDMA_MAX_PAGES];
+ struct tid_rdma_pageset pagesets[TID_RDMA_MAX_PAGES];
+ u32 tid_entry[TID_RDMA_MAX_PAGES];
+};
+
+enum tid_rnr_nak_state {
+ TID_RNR_NAK_INIT = 0,
+ TID_RNR_NAK_SEND,
+ TID_RNR_NAK_SENT,
+};
+
+bool tid_rdma_conn_req(struct rvt_qp *qp, u64 *data);
+bool tid_rdma_conn_reply(struct rvt_qp *qp, u64 data);
+bool tid_rdma_conn_resp(struct rvt_qp *qp, u64 *data);
+void tid_rdma_conn_error(struct rvt_qp *qp);
+void tid_rdma_opfn_init(struct rvt_qp *qp, struct tid_rdma_params *p);
+
+int hfi1_kern_exp_rcv_init(struct hfi1_ctxtdata *rcd, int reinit);
+int hfi1_kern_exp_rcv_setup(struct tid_rdma_request *req,
+ struct rvt_sge_state *ss, bool *last);
+int hfi1_kern_exp_rcv_clear(struct tid_rdma_request *req);
+void hfi1_kern_exp_rcv_clear_all(struct tid_rdma_request *req);
+void __trdma_clean_swqe(struct rvt_qp *qp, struct rvt_swqe *wqe);
+
+/**
+ * trdma_clean_swqe - clean flows for swqe if large send queue
+ * @qp: the qp
+ * @wqe: the send wqe
+ */
+static inline void trdma_clean_swqe(struct rvt_qp *qp, struct rvt_swqe *wqe)
+{
+ if (!wqe->priv)
+ return;
+ __trdma_clean_swqe(qp, wqe);
+}
+
+void hfi1_kern_read_tid_flow_free(struct rvt_qp *qp);
+
int hfi1_qp_priv_init(struct rvt_dev_info *rdi, struct rvt_qp *qp,
struct ib_qp_init_attr *init_attr);
+void hfi1_qp_priv_tid_free(struct rvt_dev_info *rdi, struct rvt_qp *qp);
-#endif /* HFI1_TID_RDMA_H */
+void hfi1_tid_rdma_flush_wait(struct rvt_qp *qp);
+
+int hfi1_kern_setup_hw_flow(struct hfi1_ctxtdata *rcd, struct rvt_qp *qp);
+void hfi1_kern_clear_hw_flow(struct hfi1_ctxtdata *rcd, struct rvt_qp *qp);
+void hfi1_kern_init_ctxt_generations(struct hfi1_ctxtdata *rcd);
+
+struct cntr_entry;
+u64 hfi1_access_sw_tid_wait(const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data);
+
+u32 hfi1_build_tid_rdma_read_packet(struct rvt_swqe *wqe,
+ struct ib_other_headers *ohdr,
+ u32 *bth1, u32 *bth2, u32 *len);
+u32 hfi1_build_tid_rdma_read_req(struct rvt_qp *qp, struct rvt_swqe *wqe,
+ struct ib_other_headers *ohdr, u32 *bth1,
+ u32 *bth2, u32 *len);
+void hfi1_rc_rcv_tid_rdma_read_req(struct hfi1_packet *packet);
+u32 hfi1_build_tid_rdma_read_resp(struct rvt_qp *qp, struct rvt_ack_entry *e,
+ struct ib_other_headers *ohdr, u32 *bth0,
+ u32 *bth1, u32 *bth2, u32 *len, bool *last);
+void hfi1_rc_rcv_tid_rdma_read_resp(struct hfi1_packet *packet);
+bool hfi1_handle_kdeth_eflags(struct hfi1_ctxtdata *rcd,
+ struct hfi1_pportdata *ppd,
+ struct hfi1_packet *packet);
+void hfi1_tid_rdma_restart_req(struct rvt_qp *qp, struct rvt_swqe *wqe,
+ u32 *bth2);
+void hfi1_qp_kern_exp_rcv_clear_all(struct rvt_qp *qp);
+bool hfi1_tid_rdma_wqe_interlock(struct rvt_qp *qp, struct rvt_swqe *wqe);
+
+void setup_tid_rdma_wqe(struct rvt_qp *qp, struct rvt_swqe *wqe);
+static inline void hfi1_setup_tid_rdma_wqe(struct rvt_qp *qp,
+ struct rvt_swqe *wqe)
+{
+ if (wqe->priv &&
+ (wqe->wr.opcode == IB_WR_RDMA_READ ||
+ wqe->wr.opcode == IB_WR_RDMA_WRITE) &&
+ wqe->length >= TID_RDMA_MIN_SEGMENT_SIZE)
+ setup_tid_rdma_wqe(qp, wqe);
+}
+
+u32 hfi1_build_tid_rdma_write_req(struct rvt_qp *qp, struct rvt_swqe *wqe,
+ struct ib_other_headers *ohdr,
+ u32 *bth1, u32 *bth2, u32 *len);
+
+void hfi1_compute_tid_rdma_flow_wt(void);
+
+void hfi1_rc_rcv_tid_rdma_write_req(struct hfi1_packet *packet);
+
+u32 hfi1_build_tid_rdma_write_resp(struct rvt_qp *qp, struct rvt_ack_entry *e,
+ struct ib_other_headers *ohdr, u32 *bth1,
+ u32 bth2, u32 *len,
+ struct rvt_sge_state **ss);
+void hfi1_del_tid_reap_timer(struct rvt_qp *qp);
+
+void hfi1_rc_rcv_tid_rdma_write_resp(struct hfi1_packet *packet);
+
+bool hfi1_build_tid_rdma_packet(struct rvt_swqe *wqe,
+ struct ib_other_headers *ohdr,
+ u32 *bth1, u32 *bth2, u32 *len);
+
+void hfi1_rc_rcv_tid_rdma_write_data(struct hfi1_packet *packet);
+
+u32 hfi1_build_tid_rdma_write_ack(struct rvt_qp *qp, struct rvt_ack_entry *e,
+ struct ib_other_headers *ohdr, u16 iflow,
+ u32 *bth1, u32 *bth2);
+
+void hfi1_rc_rcv_tid_rdma_ack(struct hfi1_packet *packet);
+
+void hfi1_add_tid_retry_timer(struct rvt_qp *qp);
+void hfi1_del_tid_retry_timer(struct rvt_qp *qp);
+
+u32 hfi1_build_tid_rdma_resync(struct rvt_qp *qp, struct rvt_swqe *wqe,
+ struct ib_other_headers *ohdr, u32 *bth1,
+ u32 *bth2, u16 fidx);
+
+void hfi1_rc_rcv_tid_rdma_resync(struct hfi1_packet *packet);
+
+struct hfi1_pkt_state;
+int hfi1_make_tid_rdma_pkt(struct rvt_qp *qp, struct hfi1_pkt_state *ps);
+
+void _hfi1_do_tid_send(struct work_struct *work);
+
+bool hfi1_schedule_tid_send(struct rvt_qp *qp);
+
+bool hfi1_tid_rdma_ack_interlock(struct rvt_qp *qp, struct rvt_ack_entry *e);
+
+#endif /* HFI1_TID_RDMA_H */
diff --git a/drivers/infiniband/hw/hfi1/trace.c b/drivers/infiniband/hw/hfi1/trace.c
index 7c8aed0ffc07..9a3d236bcc88 100644
--- a/drivers/infiniband/hw/hfi1/trace.c
+++ b/drivers/infiniband/hw/hfi1/trace.c
@@ -46,6 +46,7 @@
*/
#define CREATE_TRACE_POINTS
#include "trace.h"
+#include "exp_rcv.h"
static u8 __get_ib_hdr_len(struct ib_header *hdr)
{
@@ -128,6 +129,15 @@ const char *hfi1_trace_get_packet_l2_str(u8 l2)
#define IETH_PRN "ieth rkey:0x%.8x"
#define ATOMICACKETH_PRN "origdata:%llx"
#define ATOMICETH_PRN "vaddr:0x%llx rkey:0x%.8x sdata:%llx cdata:%llx"
+#define TID_RDMA_KDETH "kdeth0 0x%x kdeth1 0x%x"
+#define TID_RDMA_KDETH_DATA "kdeth0 0x%x: kver %u sh %u intr %u tidctrl %u tid %x offset %x kdeth1 0x%x: jkey %x"
+#define TID_READ_REQ_PRN "tid_flow_psn 0x%x tid_flow_qp 0x%x verbs_qp 0x%x"
+#define TID_READ_RSP_PRN "verbs_qp 0x%x"
+#define TID_WRITE_REQ_PRN "original_qp 0x%x"
+#define TID_WRITE_RSP_PRN "tid_flow_psn 0x%x tid_flow_qp 0x%x verbs_qp 0x%x"
+#define TID_WRITE_DATA_PRN "verbs_qp 0x%x"
+#define TID_ACK_PRN "tid_flow_psn 0x%x verbs_psn 0x%x tid_flow_qp 0x%x verbs_qp 0x%x"
+#define TID_RESYNC_PRN "verbs_qp 0x%x"
#define OP(transport, op) IB_OPCODE_## transport ## _ ## op
@@ -322,6 +332,99 @@ const char *parse_everbs_hdrs(
parse_syndrome(be32_to_cpu(eh->aeth) >> 24),
be32_to_cpu(eh->aeth) & IB_MSN_MASK);
break;
+ case OP(TID_RDMA, WRITE_REQ):
+ trace_seq_printf(p, TID_RDMA_KDETH " " RETH_PRN " "
+ TID_WRITE_REQ_PRN,
+ le32_to_cpu(eh->tid_rdma.w_req.kdeth0),
+ le32_to_cpu(eh->tid_rdma.w_req.kdeth1),
+ ib_u64_get(&eh->tid_rdma.w_req.reth.vaddr),
+ be32_to_cpu(eh->tid_rdma.w_req.reth.rkey),
+ be32_to_cpu(eh->tid_rdma.w_req.reth.length),
+ be32_to_cpu(eh->tid_rdma.w_req.verbs_qp));
+ break;
+ case OP(TID_RDMA, WRITE_RESP):
+ trace_seq_printf(p, TID_RDMA_KDETH " " AETH_PRN " "
+ TID_WRITE_RSP_PRN,
+ le32_to_cpu(eh->tid_rdma.w_rsp.kdeth0),
+ le32_to_cpu(eh->tid_rdma.w_rsp.kdeth1),
+ be32_to_cpu(eh->tid_rdma.w_rsp.aeth) >> 24,
+ parse_syndrome(/* aeth */
+ be32_to_cpu(eh->tid_rdma.w_rsp.aeth)
+ >> 24),
+ (be32_to_cpu(eh->tid_rdma.w_rsp.aeth) &
+ IB_MSN_MASK),
+ be32_to_cpu(eh->tid_rdma.w_rsp.tid_flow_psn),
+ be32_to_cpu(eh->tid_rdma.w_rsp.tid_flow_qp),
+ be32_to_cpu(eh->tid_rdma.w_rsp.verbs_qp));
+ break;
+ case OP(TID_RDMA, WRITE_DATA_LAST):
+ case OP(TID_RDMA, WRITE_DATA):
+ trace_seq_printf(p, TID_RDMA_KDETH_DATA " " TID_WRITE_DATA_PRN,
+ le32_to_cpu(eh->tid_rdma.w_data.kdeth0),
+ KDETH_GET(eh->tid_rdma.w_data.kdeth0, KVER),
+ KDETH_GET(eh->tid_rdma.w_data.kdeth0, SH),
+ KDETH_GET(eh->tid_rdma.w_data.kdeth0, INTR),
+ KDETH_GET(eh->tid_rdma.w_data.kdeth0, TIDCTRL),
+ KDETH_GET(eh->tid_rdma.w_data.kdeth0, TID),
+ KDETH_GET(eh->tid_rdma.w_data.kdeth0, OFFSET),
+ le32_to_cpu(eh->tid_rdma.w_data.kdeth1),
+ KDETH_GET(eh->tid_rdma.w_data.kdeth1, JKEY),
+ be32_to_cpu(eh->tid_rdma.w_data.verbs_qp));
+ break;
+ case OP(TID_RDMA, READ_REQ):
+ trace_seq_printf(p, TID_RDMA_KDETH " " RETH_PRN " "
+ TID_READ_REQ_PRN,
+ le32_to_cpu(eh->tid_rdma.r_req.kdeth0),
+ le32_to_cpu(eh->tid_rdma.r_req.kdeth1),
+ ib_u64_get(&eh->tid_rdma.r_req.reth.vaddr),
+ be32_to_cpu(eh->tid_rdma.r_req.reth.rkey),
+ be32_to_cpu(eh->tid_rdma.r_req.reth.length),
+ be32_to_cpu(eh->tid_rdma.r_req.tid_flow_psn),
+ be32_to_cpu(eh->tid_rdma.r_req.tid_flow_qp),
+ be32_to_cpu(eh->tid_rdma.r_req.verbs_qp));
+ break;
+ case OP(TID_RDMA, READ_RESP):
+ trace_seq_printf(p, TID_RDMA_KDETH_DATA " " AETH_PRN " "
+ TID_READ_RSP_PRN,
+ le32_to_cpu(eh->tid_rdma.r_rsp.kdeth0),
+ KDETH_GET(eh->tid_rdma.r_rsp.kdeth0, KVER),
+ KDETH_GET(eh->tid_rdma.r_rsp.kdeth0, SH),
+ KDETH_GET(eh->tid_rdma.r_rsp.kdeth0, INTR),
+ KDETH_GET(eh->tid_rdma.r_rsp.kdeth0, TIDCTRL),
+ KDETH_GET(eh->tid_rdma.r_rsp.kdeth0, TID),
+ KDETH_GET(eh->tid_rdma.r_rsp.kdeth0, OFFSET),
+ le32_to_cpu(eh->tid_rdma.r_rsp.kdeth1),
+ KDETH_GET(eh->tid_rdma.r_rsp.kdeth1, JKEY),
+ be32_to_cpu(eh->tid_rdma.r_rsp.aeth) >> 24,
+ parse_syndrome(/* aeth */
+ be32_to_cpu(eh->tid_rdma.r_rsp.aeth)
+ >> 24),
+ (be32_to_cpu(eh->tid_rdma.r_rsp.aeth) &
+ IB_MSN_MASK),
+ be32_to_cpu(eh->tid_rdma.r_rsp.verbs_qp));
+ break;
+ case OP(TID_RDMA, ACK):
+ trace_seq_printf(p, TID_RDMA_KDETH " " AETH_PRN " "
+ TID_ACK_PRN,
+ le32_to_cpu(eh->tid_rdma.ack.kdeth0),
+ le32_to_cpu(eh->tid_rdma.ack.kdeth1),
+ be32_to_cpu(eh->tid_rdma.ack.aeth) >> 24,
+ parse_syndrome(/* aeth */
+ be32_to_cpu(eh->tid_rdma.ack.aeth)
+ >> 24),
+ (be32_to_cpu(eh->tid_rdma.ack.aeth) &
+ IB_MSN_MASK),
+ be32_to_cpu(eh->tid_rdma.ack.tid_flow_psn),
+ be32_to_cpu(eh->tid_rdma.ack.verbs_psn),
+ be32_to_cpu(eh->tid_rdma.ack.tid_flow_qp),
+ be32_to_cpu(eh->tid_rdma.ack.verbs_qp));
+ break;
+ case OP(TID_RDMA, RESYNC):
+ trace_seq_printf(p, TID_RDMA_KDETH " " TID_RESYNC_PRN,
+ le32_to_cpu(eh->tid_rdma.resync.kdeth0),
+ le32_to_cpu(eh->tid_rdma.resync.kdeth1),
+ be32_to_cpu(eh->tid_rdma.resync.verbs_qp));
+ break;
/* aeth + atomicacketh */
case OP(RC, ATOMIC_ACKNOWLEDGE):
trace_seq_printf(p, AETH_PRN " " ATOMICACKETH_PRN,
@@ -394,6 +497,21 @@ const char *print_u32_array(
return ret;
}
+u8 hfi1_trace_get_tid_ctrl(u32 ent)
+{
+ return EXP_TID_GET(ent, CTRL);
+}
+
+u16 hfi1_trace_get_tid_len(u32 ent)
+{
+ return EXP_TID_GET(ent, LEN);
+}
+
+u16 hfi1_trace_get_tid_idx(u32 ent)
+{
+ return EXP_TID_GET(ent, IDX);
+}
+
__hfi1_trace_fn(AFFINITY);
__hfi1_trace_fn(PKT);
__hfi1_trace_fn(PROC);
diff --git a/drivers/infiniband/hw/hfi1/trace.h b/drivers/infiniband/hw/hfi1/trace.h
index 84458f1325e1..1ce551864118 100644
--- a/drivers/infiniband/hw/hfi1/trace.h
+++ b/drivers/infiniband/hw/hfi1/trace.h
@@ -63,3 +63,4 @@ __print_symbolic(etype, \
#include "trace_tx.h"
#include "trace_mmu.h"
#include "trace_iowait.h"
+#include "trace_tid.h"
diff --git a/drivers/infiniband/hw/hfi1/trace_ibhdrs.h b/drivers/infiniband/hw/hfi1/trace_ibhdrs.h
index 1dc2c28fc96e..d1372cc66de6 100644
--- a/drivers/infiniband/hw/hfi1/trace_ibhdrs.h
+++ b/drivers/infiniband/hw/hfi1/trace_ibhdrs.h
@@ -79,6 +79,14 @@ __print_symbolic(opcode, \
ib_opcode_name(RC_ATOMIC_ACKNOWLEDGE), \
ib_opcode_name(RC_COMPARE_SWAP), \
ib_opcode_name(RC_FETCH_ADD), \
+ ib_opcode_name(TID_RDMA_WRITE_REQ), \
+ ib_opcode_name(TID_RDMA_WRITE_RESP), \
+ ib_opcode_name(TID_RDMA_WRITE_DATA), \
+ ib_opcode_name(TID_RDMA_WRITE_DATA_LAST), \
+ ib_opcode_name(TID_RDMA_READ_REQ), \
+ ib_opcode_name(TID_RDMA_READ_RESP), \
+ ib_opcode_name(TID_RDMA_RESYNC), \
+ ib_opcode_name(TID_RDMA_ACK), \
ib_opcode_name(UC_SEND_FIRST), \
ib_opcode_name(UC_SEND_MIDDLE), \
ib_opcode_name(UC_SEND_LAST), \
diff --git a/drivers/infiniband/hw/hfi1/trace_rc.h b/drivers/infiniband/hw/hfi1/trace_rc.h
index 8ce476570462..1ebca37862e0 100644
--- a/drivers/infiniband/hw/hfi1/trace_rc.h
+++ b/drivers/infiniband/hw/hfi1/trace_rc.h
@@ -109,6 +109,54 @@ DEFINE_EVENT(hfi1_rc_template, hfi1_rcv_error,
TP_ARGS(qp, psn)
);
+DEFINE_EVENT(/* event */
+ hfi1_rc_template, hfi1_rc_completion,
+ TP_PROTO(struct rvt_qp *qp, u32 psn),
+ TP_ARGS(qp, psn)
+);
+
+DECLARE_EVENT_CLASS(/* rc_ack */
+ hfi1_rc_ack_template,
+ TP_PROTO(struct rvt_qp *qp, u32 aeth, u32 psn,
+ struct rvt_swqe *wqe),
+ TP_ARGS(qp, aeth, psn, wqe),
+ TP_STRUCT__entry(/* entry */
+ DD_DEV_ENTRY(dd_from_ibdev(qp->ibqp.device))
+ __field(u32, qpn)
+ __field(u32, aeth)
+ __field(u32, psn)
+ __field(u8, opcode)
+ __field(u32, spsn)
+ __field(u32, lpsn)
+ ),
+ TP_fast_assign(/* assign */
+ DD_DEV_ASSIGN(dd_from_ibdev(qp->ibqp.device))
+ __entry->qpn = qp->ibqp.qp_num;
+ __entry->aeth = aeth;
+ __entry->psn = psn;
+ __entry->opcode = wqe->wr.opcode;
+ __entry->spsn = wqe->psn;
+ __entry->lpsn = wqe->lpsn;
+ ),
+ TP_printk(/* print */
+ "[%s] qpn 0x%x aeth 0x%x psn 0x%x opcode 0x%x spsn 0x%x lpsn 0x%x",
+ __get_str(dev),
+ __entry->qpn,
+ __entry->aeth,
+ __entry->psn,
+ __entry->opcode,
+ __entry->spsn,
+ __entry->lpsn
+ )
+);
+
+DEFINE_EVENT(/* do_rc_ack */
+ hfi1_rc_ack_template, hfi1_rc_ack_do,
+ TP_PROTO(struct rvt_qp *qp, u32 aeth, u32 psn,
+ struct rvt_swqe *wqe),
+ TP_ARGS(qp, aeth, psn, wqe)
+);
+
#endif /* __HFI1_TRACE_RC_H */
#undef TRACE_INCLUDE_PATH
diff --git a/drivers/infiniband/hw/hfi1/trace_rx.h b/drivers/infiniband/hw/hfi1/trace_rx.h
index 7eceb57e0415..3cec960e9674 100644
--- a/drivers/infiniband/hw/hfi1/trace_rx.h
+++ b/drivers/infiniband/hw/hfi1/trace_rx.h
@@ -1,5 +1,5 @@
/*
- * Copyright(c) 2015 - 2017 Intel Corporation.
+ * Copyright(c) 2015 - 2018 Intel Corporation.
*
* This file is provided under a dual BSD/GPLv2 license. When using or
* redistributing this file, you may do so under either license.
@@ -128,111 +128,6 @@ TRACE_EVENT(hfi1_receive_interrupt,
)
);
-DECLARE_EVENT_CLASS(
- hfi1_exp_tid_reg_unreg,
- TP_PROTO(unsigned int ctxt, u16 subctxt, u32 rarr,
- u32 npages, unsigned long va, unsigned long pa,
- dma_addr_t dma),
- TP_ARGS(ctxt, subctxt, rarr, npages, va, pa, dma),
- TP_STRUCT__entry(
- __field(unsigned int, ctxt)
- __field(u16, subctxt)
- __field(u32, rarr)
- __field(u32, npages)
- __field(unsigned long, va)
- __field(unsigned long, pa)
- __field(dma_addr_t, dma)
- ),
- TP_fast_assign(
- __entry->ctxt = ctxt;
- __entry->subctxt = subctxt;
- __entry->rarr = rarr;
- __entry->npages = npages;
- __entry->va = va;
- __entry->pa = pa;
- __entry->dma = dma;
- ),
- TP_printk("[%u:%u] entry:%u, %u pages @ 0x%lx, va:0x%lx dma:0x%llx",
- __entry->ctxt,
- __entry->subctxt,
- __entry->rarr,
- __entry->npages,
- __entry->pa,
- __entry->va,
- __entry->dma
- )
- );
-
-DEFINE_EVENT(
- hfi1_exp_tid_reg_unreg, hfi1_exp_tid_unreg,
- TP_PROTO(unsigned int ctxt, u16 subctxt, u32 rarr, u32 npages,
- unsigned long va, unsigned long pa, dma_addr_t dma),
- TP_ARGS(ctxt, subctxt, rarr, npages, va, pa, dma));
-
-DEFINE_EVENT(
- hfi1_exp_tid_reg_unreg, hfi1_exp_tid_reg,
- TP_PROTO(unsigned int ctxt, u16 subctxt, u32 rarr, u32 npages,
- unsigned long va, unsigned long pa, dma_addr_t dma),
- TP_ARGS(ctxt, subctxt, rarr, npages, va, pa, dma));
-
-TRACE_EVENT(
- hfi1_put_tid,
- TP_PROTO(struct hfi1_devdata *dd,
- u32 index, u32 type, unsigned long pa, u16 order),
- TP_ARGS(dd, index, type, pa, order),
- TP_STRUCT__entry(
- DD_DEV_ENTRY(dd)
- __field(unsigned long, pa);
- __field(u32, index);
- __field(u32, type);
- __field(u16, order);
- ),
- TP_fast_assign(
- DD_DEV_ASSIGN(dd);
- __entry->pa = pa;
- __entry->index = index;
- __entry->type = type;
- __entry->order = order;
- ),
- TP_printk("[%s] type %s pa %lx index %u order %u",
- __get_str(dev),
- show_tidtype(__entry->type),
- __entry->pa,
- __entry->index,
- __entry->order
- )
-);
-
-TRACE_EVENT(hfi1_exp_tid_inval,
- TP_PROTO(unsigned int ctxt, u16 subctxt, unsigned long va, u32 rarr,
- u32 npages, dma_addr_t dma),
- TP_ARGS(ctxt, subctxt, va, rarr, npages, dma),
- TP_STRUCT__entry(
- __field(unsigned int, ctxt)
- __field(u16, subctxt)
- __field(unsigned long, va)
- __field(u32, rarr)
- __field(u32, npages)
- __field(dma_addr_t, dma)
- ),
- TP_fast_assign(
- __entry->ctxt = ctxt;
- __entry->subctxt = subctxt;
- __entry->va = va;
- __entry->rarr = rarr;
- __entry->npages = npages;
- __entry->dma = dma;
- ),
- TP_printk("[%u:%u] entry:%u, %u pages @ 0x%lx dma: 0x%llx",
- __entry->ctxt,
- __entry->subctxt,
- __entry->rarr,
- __entry->npages,
- __entry->va,
- __entry->dma
- )
- );
-
TRACE_EVENT(hfi1_mmu_invalidate,
TP_PROTO(unsigned int ctxt, u16 subctxt, const char *type,
unsigned long start, unsigned long end),
diff --git a/drivers/infiniband/hw/hfi1/trace_tid.h b/drivers/infiniband/hw/hfi1/trace_tid.h
new file mode 100644
index 000000000000..548dfc45a407
--- /dev/null
+++ b/drivers/infiniband/hw/hfi1/trace_tid.h
@@ -0,0 +1,1610 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+/*
+ * Copyright(c) 2018 Intel Corporation.
+ *
+ */
+#if !defined(__HFI1_TRACE_TID_H) || defined(TRACE_HEADER_MULTI_READ)
+#define __HFI1_TRACE_TID_H
+
+#include <linux/tracepoint.h>
+#include <linux/trace_seq.h>
+
+#include "hfi.h"
+
+#define tidtype_name(type) { PT_##type, #type }
+#define show_tidtype(type) \
+__print_symbolic(type, \
+ tidtype_name(EXPECTED), \
+ tidtype_name(EAGER), \
+ tidtype_name(INVALID)) \
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM hfi1_tid
+
+u8 hfi1_trace_get_tid_ctrl(u32 ent);
+u16 hfi1_trace_get_tid_len(u32 ent);
+u16 hfi1_trace_get_tid_idx(u32 ent);
+
+#define OPFN_PARAM_PRN "[%s] qpn 0x%x %s OPFN: qp 0x%x, max read %u, " \
+ "max write %u, max length %u, jkey 0x%x timeout %u " \
+ "urg %u"
+
+#define TID_FLOW_PRN "[%s] qpn 0x%x flow %d: idx %d resp_ib_psn 0x%x " \
+ "generation 0x%x fpsn 0x%x-%x r_next_psn 0x%x " \
+ "ib_psn 0x%x-%x npagesets %u tnode_cnt %u " \
+ "tidcnt %u tid_idx %u tid_offset %u length %u sent %u"
+
+#define TID_NODE_PRN "[%s] qpn 0x%x %s idx %u grp base 0x%x map 0x%x " \
+ "used %u cnt %u"
+
+#define RSP_INFO_PRN "[%s] qpn 0x%x state 0x%x s_state 0x%x psn 0x%x " \
+ "r_psn 0x%x r_state 0x%x r_flags 0x%x " \
+ "r_head_ack_queue %u s_tail_ack_queue %u " \
+ "s_acked_ack_queue %u s_ack_state 0x%x " \
+ "s_nak_state 0x%x s_flags 0x%x ps_flags 0x%x " \
+ "iow_flags 0x%lx"
+
+#define SENDER_INFO_PRN "[%s] qpn 0x%x state 0x%x s_cur %u s_tail %u " \
+ "s_head %u s_acked %u s_last %u s_psn 0x%x " \
+ "s_last_psn 0x%x s_flags 0x%x ps_flags 0x%x " \
+ "iow_flags 0x%lx s_state 0x%x s_num_rd %u s_retry %u"
+
+#define TID_READ_SENDER_PRN "[%s] qpn 0x%x newreq %u tid_r_reqs %u " \
+ "tid_r_comp %u pending_tid_r_segs %u " \
+ "s_flags 0x%x ps_flags 0x%x iow_flags 0x%lx " \
+ "s_state 0x%x hw_flow_index %u generation 0x%x " \
+ "fpsn 0x%x flow_flags 0x%x"
+
+#define TID_REQ_PRN "[%s] qpn 0x%x newreq %u opcode 0x%x psn 0x%x lpsn 0x%x " \
+ "cur_seg %u comp_seg %u ack_seg %u alloc_seg %u " \
+ "total_segs %u setup_head %u clear_tail %u flow_idx %u " \
+ "acked_tail %u state %u r_ack_psn 0x%x r_flow_psn 0x%x " \
+ "r_last_ackd 0x%x s_next_psn 0x%x"
+
+#define RCV_ERR_PRN "[%s] qpn 0x%x s_flags 0x%x state 0x%x " \
+ "s_acked_ack_queue %u s_tail_ack_queue %u " \
+ "r_head_ack_queue %u opcode 0x%x psn 0x%x r_psn 0x%x " \
+ " diff %d"
+
+#define TID_WRITE_RSPDR_PRN "[%s] qpn 0x%x r_tid_head %u r_tid_tail %u " \
+ "r_tid_ack %u r_tid_alloc %u alloc_w_segs %u " \
+ "pending_tid_w_segs %u sync_pt %s " \
+ "ps_nak_psn 0x%x ps_nak_state 0x%x " \
+ "prnr_nak_state 0x%x hw_flow_index %u generation "\
+ "0x%x fpsn 0x%x flow_flags 0x%x resync %s" \
+ "r_next_psn_kdeth 0x%x"
+
+#define TID_WRITE_SENDER_PRN "[%s] qpn 0x%x newreq %u s_tid_cur %u " \
+ "s_tid_tail %u s_tid_head %u " \
+ "pending_tid_w_resp %u n_requests %u " \
+ "n_tid_requests %u s_flags 0x%x ps_flags 0x%x "\
+ "iow_flags 0x%lx s_state 0x%x s_retry %u"
+
+#define KDETH_EFLAGS_ERR_PRN "[%s] qpn 0x%x TID ERR: RcvType 0x%x " \
+ "RcvTypeError 0x%x PSN 0x%x"
+
+DECLARE_EVENT_CLASS(/* class */
+ hfi1_exp_tid_reg_unreg,
+ TP_PROTO(unsigned int ctxt, u16 subctxt, u32 rarr, u32 npages,
+ unsigned long va, unsigned long pa, dma_addr_t dma),
+ TP_ARGS(ctxt, subctxt, rarr, npages, va, pa, dma),
+ TP_STRUCT__entry(/* entry */
+ __field(unsigned int, ctxt)
+ __field(u16, subctxt)
+ __field(u32, rarr)
+ __field(u32, npages)
+ __field(unsigned long, va)
+ __field(unsigned long, pa)
+ __field(dma_addr_t, dma)
+ ),
+ TP_fast_assign(/* assign */
+ __entry->ctxt = ctxt;
+ __entry->subctxt = subctxt;
+ __entry->rarr = rarr;
+ __entry->npages = npages;
+ __entry->va = va;
+ __entry->pa = pa;
+ __entry->dma = dma;
+ ),
+ TP_printk("[%u:%u] entry:%u, %u pages @ 0x%lx, va:0x%lx dma:0x%llx",
+ __entry->ctxt,
+ __entry->subctxt,
+ __entry->rarr,
+ __entry->npages,
+ __entry->pa,
+ __entry->va,
+ __entry->dma
+ )
+);
+
+DEFINE_EVENT(/* exp_tid_unreg */
+ hfi1_exp_tid_reg_unreg, hfi1_exp_tid_unreg,
+ TP_PROTO(unsigned int ctxt, u16 subctxt, u32 rarr, u32 npages,
+ unsigned long va, unsigned long pa, dma_addr_t dma),
+ TP_ARGS(ctxt, subctxt, rarr, npages, va, pa, dma)
+);
+
+DEFINE_EVENT(/* exp_tid_reg */
+ hfi1_exp_tid_reg_unreg, hfi1_exp_tid_reg,
+ TP_PROTO(unsigned int ctxt, u16 subctxt, u32 rarr, u32 npages,
+ unsigned long va, unsigned long pa, dma_addr_t dma),
+ TP_ARGS(ctxt, subctxt, rarr, npages, va, pa, dma)
+);
+
+TRACE_EVENT(/* put_tid */
+ hfi1_put_tid,
+ TP_PROTO(struct hfi1_devdata *dd,
+ u32 index, u32 type, unsigned long pa, u16 order),
+ TP_ARGS(dd, index, type, pa, order),
+ TP_STRUCT__entry(/* entry */
+ DD_DEV_ENTRY(dd)
+ __field(unsigned long, pa);
+ __field(u32, index);
+ __field(u32, type);
+ __field(u16, order);
+ ),
+ TP_fast_assign(/* assign */
+ DD_DEV_ASSIGN(dd);
+ __entry->pa = pa;
+ __entry->index = index;
+ __entry->type = type;
+ __entry->order = order;
+ ),
+ TP_printk("[%s] type %s pa %lx index %u order %u",
+ __get_str(dev),
+ show_tidtype(__entry->type),
+ __entry->pa,
+ __entry->index,
+ __entry->order
+ )
+);
+
+TRACE_EVENT(/* exp_tid_inval */
+ hfi1_exp_tid_inval,
+ TP_PROTO(unsigned int ctxt, u16 subctxt, unsigned long va, u32 rarr,
+ u32 npages, dma_addr_t dma),
+ TP_ARGS(ctxt, subctxt, va, rarr, npages, dma),
+ TP_STRUCT__entry(/* entry */
+ __field(unsigned int, ctxt)
+ __field(u16, subctxt)
+ __field(unsigned long, va)
+ __field(u32, rarr)
+ __field(u32, npages)
+ __field(dma_addr_t, dma)
+ ),
+ TP_fast_assign(/* assign */
+ __entry->ctxt = ctxt;
+ __entry->subctxt = subctxt;
+ __entry->va = va;
+ __entry->rarr = rarr;
+ __entry->npages = npages;
+ __entry->dma = dma;
+ ),
+ TP_printk("[%u:%u] entry:%u, %u pages @ 0x%lx dma: 0x%llx",
+ __entry->ctxt,
+ __entry->subctxt,
+ __entry->rarr,
+ __entry->npages,
+ __entry->va,
+ __entry->dma
+ )
+);
+
+DECLARE_EVENT_CLASS(/* opfn_state */
+ hfi1_opfn_state_template,
+ TP_PROTO(struct rvt_qp *qp),
+ TP_ARGS(qp),
+ TP_STRUCT__entry(/* entry */
+ DD_DEV_ENTRY(dd_from_ibdev(qp->ibqp.device))
+ __field(u32, qpn)
+ __field(u16, requested)
+ __field(u16, completed)
+ __field(u8, curr)
+ ),
+ TP_fast_assign(/* assign */
+ struct hfi1_qp_priv *priv = qp->priv;
+
+ DD_DEV_ASSIGN(dd_from_ibdev(qp->ibqp.device));
+ __entry->qpn = qp->ibqp.qp_num;
+ __entry->requested = priv->opfn.requested;
+ __entry->completed = priv->opfn.completed;
+ __entry->curr = priv->opfn.curr;
+ ),
+ TP_printk(/* print */
+ "[%s] qpn 0x%x requested 0x%x completed 0x%x curr 0x%x",
+ __get_str(dev),
+ __entry->qpn,
+ __entry->requested,
+ __entry->completed,
+ __entry->curr
+ )
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_opfn_state_template, hfi1_opfn_state_conn_request,
+ TP_PROTO(struct rvt_qp *qp),
+ TP_ARGS(qp)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_opfn_state_template, hfi1_opfn_state_sched_conn_request,
+ TP_PROTO(struct rvt_qp *qp),
+ TP_ARGS(qp)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_opfn_state_template, hfi1_opfn_state_conn_response,
+ TP_PROTO(struct rvt_qp *qp),
+ TP_ARGS(qp)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_opfn_state_template, hfi1_opfn_state_conn_reply,
+ TP_PROTO(struct rvt_qp *qp),
+ TP_ARGS(qp)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_opfn_state_template, hfi1_opfn_state_conn_error,
+ TP_PROTO(struct rvt_qp *qp),
+ TP_ARGS(qp)
+);
+
+DECLARE_EVENT_CLASS(/* opfn_data */
+ hfi1_opfn_data_template,
+ TP_PROTO(struct rvt_qp *qp, u8 capcode, u64 data),
+ TP_ARGS(qp, capcode, data),
+ TP_STRUCT__entry(/* entry */
+ DD_DEV_ENTRY(dd_from_ibdev(qp->ibqp.device))
+ __field(u32, qpn)
+ __field(u32, state)
+ __field(u8, capcode)
+ __field(u64, data)
+ ),
+ TP_fast_assign(/* assign */
+ DD_DEV_ASSIGN(dd_from_ibdev(qp->ibqp.device));
+ __entry->qpn = qp->ibqp.qp_num;
+ __entry->state = qp->state;
+ __entry->capcode = capcode;
+ __entry->data = data;
+ ),
+ TP_printk(/* printk */
+ "[%s] qpn 0x%x (state 0x%x) Capcode %u data 0x%llx",
+ __get_str(dev),
+ __entry->qpn,
+ __entry->state,
+ __entry->capcode,
+ __entry->data
+ )
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_opfn_data_template, hfi1_opfn_data_conn_request,
+ TP_PROTO(struct rvt_qp *qp, u8 capcode, u64 data),
+ TP_ARGS(qp, capcode, data)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_opfn_data_template, hfi1_opfn_data_conn_response,
+ TP_PROTO(struct rvt_qp *qp, u8 capcode, u64 data),
+ TP_ARGS(qp, capcode, data)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_opfn_data_template, hfi1_opfn_data_conn_reply,
+ TP_PROTO(struct rvt_qp *qp, u8 capcode, u64 data),
+ TP_ARGS(qp, capcode, data)
+);
+
+DECLARE_EVENT_CLASS(/* opfn_param */
+ hfi1_opfn_param_template,
+ TP_PROTO(struct rvt_qp *qp, char remote,
+ struct tid_rdma_params *param),
+ TP_ARGS(qp, remote, param),
+ TP_STRUCT__entry(/* entry */
+ DD_DEV_ENTRY(dd_from_ibdev(qp->ibqp.device))
+ __field(u32, qpn)
+ __field(char, remote)
+ __field(u32, param_qp)
+ __field(u32, max_len)
+ __field(u16, jkey)
+ __field(u8, max_read)
+ __field(u8, max_write)
+ __field(u8, timeout)
+ __field(u8, urg)
+ ),
+ TP_fast_assign(/* assign */
+ DD_DEV_ASSIGN(dd_from_ibdev(qp->ibqp.device));
+ __entry->qpn = qp->ibqp.qp_num;
+ __entry->remote = remote;
+ __entry->param_qp = param->qp;
+ __entry->max_len = param->max_len;
+ __entry->jkey = param->jkey;
+ __entry->max_read = param->max_read;
+ __entry->max_write = param->max_write;
+ __entry->timeout = param->timeout;
+ __entry->urg = param->urg;
+ ),
+ TP_printk(/* print */
+ OPFN_PARAM_PRN,
+ __get_str(dev),
+ __entry->qpn,
+ __entry->remote ? "remote" : "local",
+ __entry->param_qp,
+ __entry->max_read,
+ __entry->max_write,
+ __entry->max_len,
+ __entry->jkey,
+ __entry->timeout,
+ __entry->urg
+ )
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_opfn_param_template, hfi1_opfn_param,
+ TP_PROTO(struct rvt_qp *qp, char remote,
+ struct tid_rdma_params *param),
+ TP_ARGS(qp, remote, param)
+);
+
+DECLARE_EVENT_CLASS(/* msg */
+ hfi1_msg_template,
+ TP_PROTO(struct rvt_qp *qp, const char *msg, u64 more),
+ TP_ARGS(qp, msg, more),
+ TP_STRUCT__entry(/* entry */
+ __field(u32, qpn)
+ __string(msg, msg)
+ __field(u64, more)
+ ),
+ TP_fast_assign(/* assign */
+ __entry->qpn = qp ? qp->ibqp.qp_num : 0;
+ __assign_str(msg, msg);
+ __entry->more = more;
+ ),
+ TP_printk(/* print */
+ "qpn 0x%x %s 0x%llx",
+ __entry->qpn,
+ __get_str(msg),
+ __entry->more
+ )
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_msg_template, hfi1_msg_opfn_conn_request,
+ TP_PROTO(struct rvt_qp *qp, const char *msg, u64 more),
+ TP_ARGS(qp, msg, more)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_msg_template, hfi1_msg_opfn_conn_error,
+ TP_PROTO(struct rvt_qp *qp, const char *msg, u64 more),
+ TP_ARGS(qp, msg, more)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_msg_template, hfi1_msg_alloc_tids,
+ TP_PROTO(struct rvt_qp *qp, const char *msg, u64 more),
+ TP_ARGS(qp, msg, more)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_msg_template, hfi1_msg_tid_restart_req,
+ TP_PROTO(struct rvt_qp *qp, const char *msg, u64 more),
+ TP_ARGS(qp, msg, more)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_msg_template, hfi1_msg_handle_kdeth_eflags,
+ TP_PROTO(struct rvt_qp *qp, const char *msg, u64 more),
+ TP_ARGS(qp, msg, more)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_msg_template, hfi1_msg_tid_timeout,
+ TP_PROTO(struct rvt_qp *qp, const char *msg, u64 more),
+ TP_ARGS(qp, msg, more)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_msg_template, hfi1_msg_tid_retry_timeout,
+ TP_PROTO(struct rvt_qp *qp, const char *msg, u64 more),
+ TP_ARGS(qp, msg, more)
+);
+
+DECLARE_EVENT_CLASS(/* tid_flow_page */
+ hfi1_tid_flow_page_template,
+ TP_PROTO(struct rvt_qp *qp, struct tid_rdma_flow *flow, u32 index,
+ char mtu8k, char v1, void *vaddr),
+ TP_ARGS(qp, flow, index, mtu8k, v1, vaddr),
+ TP_STRUCT__entry(/* entry */
+ DD_DEV_ENTRY(dd_from_ibdev(qp->ibqp.device))
+ __field(u32, qpn)
+ __field(char, mtu8k)
+ __field(char, v1)
+ __field(u32, index)
+ __field(u64, page)
+ __field(u64, vaddr)
+ ),
+ TP_fast_assign(/* assign */
+ DD_DEV_ASSIGN(dd_from_ibdev(qp->ibqp.device));
+ __entry->qpn = qp->ibqp.qp_num;
+ __entry->mtu8k = mtu8k;
+ __entry->v1 = v1;
+ __entry->index = index;
+ __entry->page = vaddr ? (u64)virt_to_page(vaddr) : 0ULL;
+ __entry->vaddr = (u64)vaddr;
+ ),
+ TP_printk(/* print */
+ "[%s] qpn 0x%x page[%u]: page 0x%llx %s 0x%llx",
+ __get_str(dev),
+ __entry->qpn,
+ __entry->index,
+ __entry->page,
+ __entry->mtu8k ? (__entry->v1 ? "v1" : "v0") : "vaddr",
+ __entry->vaddr
+ )
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_tid_flow_page_template, hfi1_tid_flow_page,
+ TP_PROTO(struct rvt_qp *qp, struct tid_rdma_flow *flow, u32 index,
+ char mtu8k, char v1, void *vaddr),
+ TP_ARGS(qp, flow, index, mtu8k, v1, vaddr)
+);
+
+DECLARE_EVENT_CLASS(/* tid_pageset */
+ hfi1_tid_pageset_template,
+ TP_PROTO(struct rvt_qp *qp, u32 index, u16 idx, u16 count),
+ TP_ARGS(qp, index, idx, count),
+ TP_STRUCT__entry(/* entry */
+ DD_DEV_ENTRY(dd_from_ibdev(qp->ibqp.device))
+ __field(u32, qpn)
+ __field(u32, index)
+ __field(u16, idx)
+ __field(u16, count)
+ ),
+ TP_fast_assign(/* assign */
+ DD_DEV_ASSIGN(dd_from_ibdev(qp->ibqp.device));
+ __entry->qpn = qp->ibqp.qp_num;
+ __entry->index = index;
+ __entry->idx = idx;
+ __entry->count = count;
+ ),
+ TP_printk(/* print */
+ "[%s] qpn 0x%x list[%u]: idx %u count %u",
+ __get_str(dev),
+ __entry->qpn,
+ __entry->index,
+ __entry->idx,
+ __entry->count
+ )
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_tid_pageset_template, hfi1_tid_pageset,
+ TP_PROTO(struct rvt_qp *qp, u32 index, u16 idx, u16 count),
+ TP_ARGS(qp, index, idx, count)
+);
+
+DECLARE_EVENT_CLASS(/* tid_fow */
+ hfi1_tid_flow_template,
+ TP_PROTO(struct rvt_qp *qp, int index, struct tid_rdma_flow *flow),
+ TP_ARGS(qp, index, flow),
+ TP_STRUCT__entry(/* entry */
+ DD_DEV_ENTRY(dd_from_ibdev(qp->ibqp.device))
+ __field(u32, qpn)
+ __field(int, index)
+ __field(int, idx)
+ __field(u32, resp_ib_psn)
+ __field(u32, generation)
+ __field(u32, fspsn)
+ __field(u32, flpsn)
+ __field(u32, r_next_psn)
+ __field(u32, ib_spsn)
+ __field(u32, ib_lpsn)
+ __field(u32, npagesets)
+ __field(u32, tnode_cnt)
+ __field(u32, tidcnt)
+ __field(u32, tid_idx)
+ __field(u32, tid_offset)
+ __field(u32, length)
+ __field(u32, sent)
+ ),
+ TP_fast_assign(/* assign */
+ DD_DEV_ASSIGN(dd_from_ibdev(qp->ibqp.device));
+ __entry->qpn = qp->ibqp.qp_num;
+ __entry->index = index;
+ __entry->idx = flow->idx;
+ __entry->resp_ib_psn = flow->flow_state.resp_ib_psn;
+ __entry->generation = flow->flow_state.generation;
+ __entry->fspsn = full_flow_psn(flow,
+ flow->flow_state.spsn);
+ __entry->flpsn = full_flow_psn(flow,
+ flow->flow_state.lpsn);
+ __entry->r_next_psn = flow->flow_state.r_next_psn;
+ __entry->ib_spsn = flow->flow_state.ib_spsn;
+ __entry->ib_lpsn = flow->flow_state.ib_lpsn;
+ __entry->npagesets = flow->npagesets;
+ __entry->tnode_cnt = flow->tnode_cnt;
+ __entry->tidcnt = flow->tidcnt;
+ __entry->tid_idx = flow->tid_idx;
+ __entry->tid_offset = flow->tid_offset;
+ __entry->length = flow->length;
+ __entry->sent = flow->sent;
+ ),
+ TP_printk(/* print */
+ TID_FLOW_PRN,
+ __get_str(dev),
+ __entry->qpn,
+ __entry->index,
+ __entry->idx,
+ __entry->resp_ib_psn,
+ __entry->generation,
+ __entry->fspsn,
+ __entry->flpsn,
+ __entry->r_next_psn,
+ __entry->ib_spsn,
+ __entry->ib_lpsn,
+ __entry->npagesets,
+ __entry->tnode_cnt,
+ __entry->tidcnt,
+ __entry->tid_idx,
+ __entry->tid_offset,
+ __entry->length,
+ __entry->sent
+ )
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_tid_flow_template, hfi1_tid_flow_alloc,
+ TP_PROTO(struct rvt_qp *qp, int index, struct tid_rdma_flow *flow),
+ TP_ARGS(qp, index, flow)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_tid_flow_template, hfi1_tid_flow_build_read_pkt,
+ TP_PROTO(struct rvt_qp *qp, int index, struct tid_rdma_flow *flow),
+ TP_ARGS(qp, index, flow)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_tid_flow_template, hfi1_tid_flow_build_read_resp,
+ TP_PROTO(struct rvt_qp *qp, int index, struct tid_rdma_flow *flow),
+ TP_ARGS(qp, index, flow)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_tid_flow_template, hfi1_tid_flow_rcv_read_req,
+ TP_PROTO(struct rvt_qp *qp, int index, struct tid_rdma_flow *flow),
+ TP_ARGS(qp, index, flow)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_tid_flow_template, hfi1_tid_flow_rcv_read_resp,
+ TP_PROTO(struct rvt_qp *qp, int index, struct tid_rdma_flow *flow),
+ TP_ARGS(qp, index, flow)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_tid_flow_template, hfi1_tid_flow_restart_req,
+ TP_PROTO(struct rvt_qp *qp, int index, struct tid_rdma_flow *flow),
+ TP_ARGS(qp, index, flow)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_tid_flow_template, hfi1_tid_flow_build_write_resp,
+ TP_PROTO(struct rvt_qp *qp, int index, struct tid_rdma_flow *flow),
+ TP_ARGS(qp, index, flow)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_tid_flow_template, hfi1_tid_flow_rcv_write_resp,
+ TP_PROTO(struct rvt_qp *qp, int index, struct tid_rdma_flow *flow),
+ TP_ARGS(qp, index, flow)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_tid_flow_template, hfi1_tid_flow_build_write_data,
+ TP_PROTO(struct rvt_qp *qp, int index, struct tid_rdma_flow *flow),
+ TP_ARGS(qp, index, flow)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_tid_flow_template, hfi1_tid_flow_rcv_tid_ack,
+ TP_PROTO(struct rvt_qp *qp, int index, struct tid_rdma_flow *flow),
+ TP_ARGS(qp, index, flow)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_tid_flow_template, hfi1_tid_flow_rcv_resync,
+ TP_PROTO(struct rvt_qp *qp, int index, struct tid_rdma_flow *flow),
+ TP_ARGS(qp, index, flow)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_tid_flow_template, hfi1_tid_flow_handle_kdeth_eflags,
+ TP_PROTO(struct rvt_qp *qp, int index, struct tid_rdma_flow *flow),
+ TP_ARGS(qp, index, flow)
+);
+
+DECLARE_EVENT_CLASS(/* tid_node */
+ hfi1_tid_node_template,
+ TP_PROTO(struct rvt_qp *qp, const char *msg, u32 index, u32 base,
+ u8 map, u8 used, u8 cnt),
+ TP_ARGS(qp, msg, index, base, map, used, cnt),
+ TP_STRUCT__entry(/* entry */
+ DD_DEV_ENTRY(dd_from_ibdev(qp->ibqp.device))
+ __field(u32, qpn)
+ __string(msg, msg)
+ __field(u32, index)
+ __field(u32, base)
+ __field(u8, map)
+ __field(u8, used)
+ __field(u8, cnt)
+ ),
+ TP_fast_assign(/* assign */
+ DD_DEV_ASSIGN(dd_from_ibdev(qp->ibqp.device));
+ __entry->qpn = qp->ibqp.qp_num;
+ __assign_str(msg, msg);
+ __entry->index = index;
+ __entry->base = base;
+ __entry->map = map;
+ __entry->used = used;
+ __entry->cnt = cnt;
+ ),
+ TP_printk(/* print */
+ TID_NODE_PRN,
+ __get_str(dev),
+ __entry->qpn,
+ __get_str(msg),
+ __entry->index,
+ __entry->base,
+ __entry->map,
+ __entry->used,
+ __entry->cnt
+ )
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_tid_node_template, hfi1_tid_node_add,
+ TP_PROTO(struct rvt_qp *qp, const char *msg, u32 index, u32 base,
+ u8 map, u8 used, u8 cnt),
+ TP_ARGS(qp, msg, index, base, map, used, cnt)
+);
+
+DECLARE_EVENT_CLASS(/* tid_entry */
+ hfi1_tid_entry_template,
+ TP_PROTO(struct rvt_qp *qp, int index, u32 ent),
+ TP_ARGS(qp, index, ent),
+ TP_STRUCT__entry(/* entry */
+ DD_DEV_ENTRY(dd_from_ibdev(qp->ibqp.device))
+ __field(u32, qpn)
+ __field(int, index)
+ __field(u8, ctrl)
+ __field(u16, idx)
+ __field(u16, len)
+ ),
+ TP_fast_assign(/* assign */
+ DD_DEV_ASSIGN(dd_from_ibdev(qp->ibqp.device));
+ __entry->qpn = qp->ibqp.qp_num;
+ __entry->index = index;
+ __entry->ctrl = hfi1_trace_get_tid_ctrl(ent);
+ __entry->idx = hfi1_trace_get_tid_idx(ent);
+ __entry->len = hfi1_trace_get_tid_len(ent);
+ ),
+ TP_printk(/* print */
+ "[%s] qpn 0x%x TID entry %d: idx %u len %u ctrl 0x%x",
+ __get_str(dev),
+ __entry->qpn,
+ __entry->index,
+ __entry->idx,
+ __entry->len,
+ __entry->ctrl
+ )
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_tid_entry_template, hfi1_tid_entry_alloc,
+ TP_PROTO(struct rvt_qp *qp, int index, u32 entry),
+ TP_ARGS(qp, index, entry)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_tid_entry_template, hfi1_tid_entry_build_read_resp,
+ TP_PROTO(struct rvt_qp *qp, int index, u32 ent),
+ TP_ARGS(qp, index, ent)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_tid_entry_template, hfi1_tid_entry_rcv_read_req,
+ TP_PROTO(struct rvt_qp *qp, int index, u32 ent),
+ TP_ARGS(qp, index, ent)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_tid_entry_template, hfi1_tid_entry_rcv_write_resp,
+ TP_PROTO(struct rvt_qp *qp, int index, u32 entry),
+ TP_ARGS(qp, index, entry)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_tid_entry_template, hfi1_tid_entry_build_write_data,
+ TP_PROTO(struct rvt_qp *qp, int index, u32 entry),
+ TP_ARGS(qp, index, entry)
+);
+
+DECLARE_EVENT_CLASS(/* rsp_info */
+ hfi1_responder_info_template,
+ TP_PROTO(struct rvt_qp *qp, u32 psn),
+ TP_ARGS(qp, psn),
+ TP_STRUCT__entry(/* entry */
+ DD_DEV_ENTRY(dd_from_ibdev(qp->ibqp.device))
+ __field(u32, qpn)
+ __field(u8, state)
+ __field(u8, s_state)
+ __field(u32, psn)
+ __field(u32, r_psn)
+ __field(u8, r_state)
+ __field(u8, r_flags)
+ __field(u8, r_head_ack_queue)
+ __field(u8, s_tail_ack_queue)
+ __field(u8, s_acked_ack_queue)
+ __field(u8, s_ack_state)
+ __field(u8, s_nak_state)
+ __field(u8, r_nak_state)
+ __field(u32, s_flags)
+ __field(u32, ps_flags)
+ __field(unsigned long, iow_flags)
+ ),
+ TP_fast_assign(/* assign */
+ struct hfi1_qp_priv *priv = qp->priv;
+
+ DD_DEV_ASSIGN(dd_from_ibdev(qp->ibqp.device));
+ __entry->qpn = qp->ibqp.qp_num;
+ __entry->state = qp->state;
+ __entry->s_state = qp->s_state;
+ __entry->psn = psn;
+ __entry->r_psn = qp->r_psn;
+ __entry->r_state = qp->r_state;
+ __entry->r_flags = qp->r_flags;
+ __entry->r_head_ack_queue = qp->r_head_ack_queue;
+ __entry->s_tail_ack_queue = qp->s_tail_ack_queue;
+ __entry->s_acked_ack_queue = qp->s_acked_ack_queue;
+ __entry->s_ack_state = qp->s_ack_state;
+ __entry->s_nak_state = qp->s_nak_state;
+ __entry->s_flags = qp->s_flags;
+ __entry->ps_flags = priv->s_flags;
+ __entry->iow_flags = priv->s_iowait.flags;
+ ),
+ TP_printk(/* print */
+ RSP_INFO_PRN,
+ __get_str(dev),
+ __entry->qpn,
+ __entry->state,
+ __entry->s_state,
+ __entry->psn,
+ __entry->r_psn,
+ __entry->r_state,
+ __entry->r_flags,
+ __entry->r_head_ack_queue,
+ __entry->s_tail_ack_queue,
+ __entry->s_acked_ack_queue,
+ __entry->s_ack_state,
+ __entry->s_nak_state,
+ __entry->s_flags,
+ __entry->ps_flags,
+ __entry->iow_flags
+ )
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_responder_info_template, hfi1_rsp_make_rc_ack,
+ TP_PROTO(struct rvt_qp *qp, u32 psn),
+ TP_ARGS(qp, psn)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_responder_info_template, hfi1_rsp_rcv_tid_read_req,
+ TP_PROTO(struct rvt_qp *qp, u32 psn),
+ TP_ARGS(qp, psn)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_responder_info_template, hfi1_rsp_tid_rcv_error,
+ TP_PROTO(struct rvt_qp *qp, u32 psn),
+ TP_ARGS(qp, psn)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_responder_info_template, hfi1_rsp_tid_write_alloc_res,
+ TP_PROTO(struct rvt_qp *qp, u32 psn),
+ TP_ARGS(qp, psn)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_responder_info_template, hfi1_rsp_rcv_tid_write_req,
+ TP_PROTO(struct rvt_qp *qp, u32 psn),
+ TP_ARGS(qp, psn)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_responder_info_template, hfi1_rsp_build_tid_write_resp,
+ TP_PROTO(struct rvt_qp *qp, u32 psn),
+ TP_ARGS(qp, psn)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_responder_info_template, hfi1_rsp_rcv_tid_write_data,
+ TP_PROTO(struct rvt_qp *qp, u32 psn),
+ TP_ARGS(qp, psn)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_responder_info_template, hfi1_rsp_make_tid_ack,
+ TP_PROTO(struct rvt_qp *qp, u32 psn),
+ TP_ARGS(qp, psn)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_responder_info_template, hfi1_rsp_handle_kdeth_eflags,
+ TP_PROTO(struct rvt_qp *qp, u32 psn),
+ TP_ARGS(qp, psn)
+);
+
+DECLARE_EVENT_CLASS(/* sender_info */
+ hfi1_sender_info_template,
+ TP_PROTO(struct rvt_qp *qp),
+ TP_ARGS(qp),
+ TP_STRUCT__entry(/* entry */
+ DD_DEV_ENTRY(dd_from_ibdev(qp->ibqp.device))
+ __field(u32, qpn)
+ __field(u8, state)
+ __field(u32, s_cur)
+ __field(u32, s_tail)
+ __field(u32, s_head)
+ __field(u32, s_acked)
+ __field(u32, s_last)
+ __field(u32, s_psn)
+ __field(u32, s_last_psn)
+ __field(u32, s_flags)
+ __field(u32, ps_flags)
+ __field(unsigned long, iow_flags)
+ __field(u8, s_state)
+ __field(u8, s_num_rd)
+ __field(u8, s_retry)
+ ),
+ TP_fast_assign(/* assign */
+ DD_DEV_ASSIGN(dd_from_ibdev(qp->ibqp.device))
+ __entry->qpn = qp->ibqp.qp_num;
+ __entry->state = qp->state;
+ __entry->s_cur = qp->s_cur;
+ __entry->s_tail = qp->s_tail;
+ __entry->s_head = qp->s_head;
+ __entry->s_acked = qp->s_acked;
+ __entry->s_last = qp->s_last;
+ __entry->s_psn = qp->s_psn;
+ __entry->s_last_psn = qp->s_last_psn;
+ __entry->s_flags = qp->s_flags;
+ __entry->ps_flags = ((struct hfi1_qp_priv *)qp->priv)->s_flags;
+ __entry->iow_flags =
+ ((struct hfi1_qp_priv *)qp->priv)->s_iowait.flags;
+ __entry->s_state = qp->s_state;
+ __entry->s_num_rd = qp->s_num_rd_atomic;
+ __entry->s_retry = qp->s_retry;
+ ),
+ TP_printk(/* print */
+ SENDER_INFO_PRN,
+ __get_str(dev),
+ __entry->qpn,
+ __entry->state,
+ __entry->s_cur,
+ __entry->s_tail,
+ __entry->s_head,
+ __entry->s_acked,
+ __entry->s_last,
+ __entry->s_psn,
+ __entry->s_last_psn,
+ __entry->s_flags,
+ __entry->ps_flags,
+ __entry->iow_flags,
+ __entry->s_state,
+ __entry->s_num_rd,
+ __entry->s_retry
+ )
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_sender_info_template, hfi1_sender_make_rc_req,
+ TP_PROTO(struct rvt_qp *qp),
+ TP_ARGS(qp)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_sender_info_template, hfi1_sender_reset_psn,
+ TP_PROTO(struct rvt_qp *qp),
+ TP_ARGS(qp)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_sender_info_template, hfi1_sender_restart_rc,
+ TP_PROTO(struct rvt_qp *qp),
+ TP_ARGS(qp)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_sender_info_template, hfi1_sender_do_rc_ack,
+ TP_PROTO(struct rvt_qp *qp),
+ TP_ARGS(qp)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_sender_info_template, hfi1_sender_rcv_tid_read_resp,
+ TP_PROTO(struct rvt_qp *qp),
+ TP_ARGS(qp)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_sender_info_template, hfi1_sender_rcv_tid_ack,
+ TP_PROTO(struct rvt_qp *qp),
+ TP_ARGS(qp)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_sender_info_template, hfi1_sender_make_tid_pkt,
+ TP_PROTO(struct rvt_qp *qp),
+ TP_ARGS(qp)
+);
+
+DECLARE_EVENT_CLASS(/* tid_read_sender */
+ hfi1_tid_read_sender_template,
+ TP_PROTO(struct rvt_qp *qp, char newreq),
+ TP_ARGS(qp, newreq),
+ TP_STRUCT__entry(/* entry */
+ DD_DEV_ENTRY(dd_from_ibdev(qp->ibqp.device))
+ __field(u32, qpn)
+ __field(char, newreq)
+ __field(u32, tid_r_reqs)
+ __field(u32, tid_r_comp)
+ __field(u32, pending_tid_r_segs)
+ __field(u32, s_flags)
+ __field(u32, ps_flags)
+ __field(unsigned long, iow_flags)
+ __field(u8, s_state)
+ __field(u32, hw_flow_index)
+ __field(u32, generation)
+ __field(u32, fpsn)
+ __field(u32, flow_flags)
+ ),
+ TP_fast_assign(/* assign */
+ struct hfi1_qp_priv *priv = qp->priv;
+
+ DD_DEV_ASSIGN(dd_from_ibdev(qp->ibqp.device));
+ __entry->qpn = qp->ibqp.qp_num;
+ __entry->newreq = newreq;
+ __entry->tid_r_reqs = priv->tid_r_reqs;
+ __entry->tid_r_comp = priv->tid_r_comp;
+ __entry->pending_tid_r_segs = priv->pending_tid_r_segs;
+ __entry->s_flags = qp->s_flags;
+ __entry->ps_flags = priv->s_flags;
+ __entry->iow_flags = priv->s_iowait.flags;
+ __entry->s_state = priv->s_state;
+ __entry->hw_flow_index = priv->flow_state.index;
+ __entry->generation = priv->flow_state.generation;
+ __entry->fpsn = priv->flow_state.psn;
+ __entry->flow_flags = priv->flow_state.flags;
+ ),
+ TP_printk(/* print */
+ TID_READ_SENDER_PRN,
+ __get_str(dev),
+ __entry->qpn,
+ __entry->newreq,
+ __entry->tid_r_reqs,
+ __entry->tid_r_comp,
+ __entry->pending_tid_r_segs,
+ __entry->s_flags,
+ __entry->ps_flags,
+ __entry->iow_flags,
+ __entry->s_state,
+ __entry->hw_flow_index,
+ __entry->generation,
+ __entry->fpsn,
+ __entry->flow_flags
+ )
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_tid_read_sender_template, hfi1_tid_read_sender_make_req,
+ TP_PROTO(struct rvt_qp *qp, char newreq),
+ TP_ARGS(qp, newreq)
+);
+
+DECLARE_EVENT_CLASS(/* tid_rdma_request */
+ hfi1_tid_rdma_request_template,
+ TP_PROTO(struct rvt_qp *qp, char newreq, u8 opcode, u32 psn, u32 lpsn,
+ struct tid_rdma_request *req),
+ TP_ARGS(qp, newreq, opcode, psn, lpsn, req),
+ TP_STRUCT__entry(/* entry */
+ DD_DEV_ENTRY(dd_from_ibdev(qp->ibqp.device))
+ __field(u32, qpn)
+ __field(char, newreq)
+ __field(u8, opcode)
+ __field(u32, psn)
+ __field(u32, lpsn)
+ __field(u32, cur_seg)
+ __field(u32, comp_seg)
+ __field(u32, ack_seg)
+ __field(u32, alloc_seg)
+ __field(u32, total_segs)
+ __field(u16, setup_head)
+ __field(u16, clear_tail)
+ __field(u16, flow_idx)
+ __field(u16, acked_tail)
+ __field(u32, state)
+ __field(u32, r_ack_psn)
+ __field(u32, r_flow_psn)
+ __field(u32, r_last_acked)
+ __field(u32, s_next_psn)
+ ),
+ TP_fast_assign(/* assign */
+ DD_DEV_ASSIGN(dd_from_ibdev(qp->ibqp.device));
+ __entry->qpn = qp->ibqp.qp_num;
+ __entry->newreq = newreq;
+ __entry->opcode = opcode;
+ __entry->psn = psn;
+ __entry->lpsn = lpsn;
+ __entry->cur_seg = req->cur_seg;
+ __entry->comp_seg = req->comp_seg;
+ __entry->ack_seg = req->ack_seg;
+ __entry->alloc_seg = req->alloc_seg;
+ __entry->total_segs = req->total_segs;
+ __entry->setup_head = req->setup_head;
+ __entry->clear_tail = req->clear_tail;
+ __entry->flow_idx = req->flow_idx;
+ __entry->acked_tail = req->acked_tail;
+ __entry->state = req->state;
+ __entry->r_ack_psn = req->r_ack_psn;
+ __entry->r_flow_psn = req->r_flow_psn;
+ __entry->r_last_acked = req->r_last_acked;
+ __entry->s_next_psn = req->s_next_psn;
+ ),
+ TP_printk(/* print */
+ TID_REQ_PRN,
+ __get_str(dev),
+ __entry->qpn,
+ __entry->newreq,
+ __entry->opcode,
+ __entry->psn,
+ __entry->lpsn,
+ __entry->cur_seg,
+ __entry->comp_seg,
+ __entry->ack_seg,
+ __entry->alloc_seg,
+ __entry->total_segs,
+ __entry->setup_head,
+ __entry->clear_tail,
+ __entry->flow_idx,
+ __entry->acked_tail,
+ __entry->state,
+ __entry->r_ack_psn,
+ __entry->r_flow_psn,
+ __entry->r_last_acked,
+ __entry->s_next_psn
+ )
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_tid_rdma_request_template, hfi1_tid_req_make_req_read,
+ TP_PROTO(struct rvt_qp *qp, char newreq, u8 opcode, u32 psn, u32 lpsn,
+ struct tid_rdma_request *req),
+ TP_ARGS(qp, newreq, opcode, psn, lpsn, req)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_tid_rdma_request_template, hfi1_tid_req_build_read_req,
+ TP_PROTO(struct rvt_qp *qp, char newreq, u8 opcode, u32 psn, u32 lpsn,
+ struct tid_rdma_request *req),
+ TP_ARGS(qp, newreq, opcode, psn, lpsn, req)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_tid_rdma_request_template, hfi1_tid_req_rcv_read_req,
+ TP_PROTO(struct rvt_qp *qp, char newreq, u8 opcode, u32 psn, u32 lpsn,
+ struct tid_rdma_request *req),
+ TP_ARGS(qp, newreq, opcode, psn, lpsn, req)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_tid_rdma_request_template, hfi1_tid_req_rcv_read_resp,
+ TP_PROTO(struct rvt_qp *qp, char newreq, u8 opcode, u32 psn, u32 lpsn,
+ struct tid_rdma_request *req),
+ TP_ARGS(qp, newreq, opcode, psn, lpsn, req)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_tid_rdma_request_template, hfi1_tid_req_rcv_err,
+ TP_PROTO(struct rvt_qp *qp, char newreq, u8 opcode, u32 psn, u32 lpsn,
+ struct tid_rdma_request *req),
+ TP_ARGS(qp, newreq, opcode, psn, lpsn, req)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_tid_rdma_request_template, hfi1_tid_req_restart_req,
+ TP_PROTO(struct rvt_qp *qp, char newreq, u8 opcode, u32 psn, u32 lpsn,
+ struct tid_rdma_request *req),
+ TP_ARGS(qp, newreq, opcode, psn, lpsn, req)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_tid_rdma_request_template, hfi1_tid_req_setup_tid_wqe,
+ TP_PROTO(struct rvt_qp *qp, char newreq, u8 opcode, u32 psn, u32 lpsn,
+ struct tid_rdma_request *req),
+ TP_ARGS(qp, newreq, opcode, psn, lpsn, req)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_tid_rdma_request_template, hfi1_tid_req_write_alloc_res,
+ TP_PROTO(struct rvt_qp *qp, char newreq, u8 opcode, u32 psn, u32 lpsn,
+ struct tid_rdma_request *req),
+ TP_ARGS(qp, newreq, opcode, psn, lpsn, req)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_tid_rdma_request_template, hfi1_tid_req_rcv_write_req,
+ TP_PROTO(struct rvt_qp *qp, char newreq, u8 opcode, u32 psn, u32 lpsn,
+ struct tid_rdma_request *req),
+ TP_ARGS(qp, newreq, opcode, psn, lpsn, req)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_tid_rdma_request_template, hfi1_tid_req_build_write_resp,
+ TP_PROTO(struct rvt_qp *qp, char newreq, u8 opcode, u32 psn, u32 lpsn,
+ struct tid_rdma_request *req),
+ TP_ARGS(qp, newreq, opcode, psn, lpsn, req)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_tid_rdma_request_template, hfi1_tid_req_rcv_write_resp,
+ TP_PROTO(struct rvt_qp *qp, char newreq, u8 opcode, u32 psn, u32 lpsn,
+ struct tid_rdma_request *req),
+ TP_ARGS(qp, newreq, opcode, psn, lpsn, req)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_tid_rdma_request_template, hfi1_tid_req_rcv_write_data,
+ TP_PROTO(struct rvt_qp *qp, char newreq, u8 opcode, u32 psn, u32 lpsn,
+ struct tid_rdma_request *req),
+ TP_ARGS(qp, newreq, opcode, psn, lpsn, req)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_tid_rdma_request_template, hfi1_tid_req_rcv_tid_ack,
+ TP_PROTO(struct rvt_qp *qp, char newreq, u8 opcode, u32 psn, u32 lpsn,
+ struct tid_rdma_request *req),
+ TP_ARGS(qp, newreq, opcode, psn, lpsn, req)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_tid_rdma_request_template, hfi1_tid_req_tid_retry_timeout,
+ TP_PROTO(struct rvt_qp *qp, char newreq, u8 opcode, u32 psn, u32 lpsn,
+ struct tid_rdma_request *req),
+ TP_ARGS(qp, newreq, opcode, psn, lpsn, req)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_tid_rdma_request_template, hfi1_tid_req_rcv_resync,
+ TP_PROTO(struct rvt_qp *qp, char newreq, u8 opcode, u32 psn, u32 lpsn,
+ struct tid_rdma_request *req),
+ TP_ARGS(qp, newreq, opcode, psn, lpsn, req)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_tid_rdma_request_template, hfi1_tid_req_make_tid_pkt,
+ TP_PROTO(struct rvt_qp *qp, char newreq, u8 opcode, u32 psn, u32 lpsn,
+ struct tid_rdma_request *req),
+ TP_ARGS(qp, newreq, opcode, psn, lpsn, req)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_tid_rdma_request_template, hfi1_tid_req_make_tid_ack,
+ TP_PROTO(struct rvt_qp *qp, char newreq, u8 opcode, u32 psn, u32 lpsn,
+ struct tid_rdma_request *req),
+ TP_ARGS(qp, newreq, opcode, psn, lpsn, req)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_tid_rdma_request_template, hfi1_tid_req_handle_kdeth_eflags,
+ TP_PROTO(struct rvt_qp *qp, char newreq, u8 opcode, u32 psn, u32 lpsn,
+ struct tid_rdma_request *req),
+ TP_ARGS(qp, newreq, opcode, psn, lpsn, req)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_tid_rdma_request_template, hfi1_tid_req_make_rc_ack_write,
+ TP_PROTO(struct rvt_qp *qp, char newreq, u8 opcode, u32 psn, u32 lpsn,
+ struct tid_rdma_request *req),
+ TP_ARGS(qp, newreq, opcode, psn, lpsn, req)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_tid_rdma_request_template, hfi1_tid_req_make_req_write,
+ TP_PROTO(struct rvt_qp *qp, char newreq, u8 opcode, u32 psn, u32 lpsn,
+ struct tid_rdma_request *req),
+ TP_ARGS(qp, newreq, opcode, psn, lpsn, req)
+);
+
+DECLARE_EVENT_CLASS(/* rc_rcv_err */
+ hfi1_rc_rcv_err_template,
+ TP_PROTO(struct rvt_qp *qp, u32 opcode, u32 psn, int diff),
+ TP_ARGS(qp, opcode, psn, diff),
+ TP_STRUCT__entry(/* entry */
+ DD_DEV_ENTRY(dd_from_ibdev(qp->ibqp.device))
+ __field(u32, qpn)
+ __field(u32, s_flags)
+ __field(u8, state)
+ __field(u8, s_acked_ack_queue)
+ __field(u8, s_tail_ack_queue)
+ __field(u8, r_head_ack_queue)
+ __field(u32, opcode)
+ __field(u32, psn)
+ __field(u32, r_psn)
+ __field(int, diff)
+ ),
+ TP_fast_assign(/* assign */
+ DD_DEV_ASSIGN(dd_from_ibdev(qp->ibqp.device))
+ __entry->qpn = qp->ibqp.qp_num;
+ __entry->s_flags = qp->s_flags;
+ __entry->state = qp->state;
+ __entry->s_acked_ack_queue = qp->s_acked_ack_queue;
+ __entry->s_tail_ack_queue = qp->s_tail_ack_queue;
+ __entry->r_head_ack_queue = qp->r_head_ack_queue;
+ __entry->opcode = opcode;
+ __entry->psn = psn;
+ __entry->r_psn = qp->r_psn;
+ __entry->diff = diff;
+ ),
+ TP_printk(/* print */
+ RCV_ERR_PRN,
+ __get_str(dev),
+ __entry->qpn,
+ __entry->s_flags,
+ __entry->state,
+ __entry->s_acked_ack_queue,
+ __entry->s_tail_ack_queue,
+ __entry->r_head_ack_queue,
+ __entry->opcode,
+ __entry->psn,
+ __entry->r_psn,
+ __entry->diff
+ )
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_rc_rcv_err_template, hfi1_tid_rdma_rcv_err,
+ TP_PROTO(struct rvt_qp *qp, u32 opcode, u32 psn, int diff),
+ TP_ARGS(qp, opcode, psn, diff)
+);
+
+DECLARE_EVENT_CLASS(/* sge */
+ hfi1_sge_template,
+ TP_PROTO(struct rvt_qp *qp, int index, struct rvt_sge *sge),
+ TP_ARGS(qp, index, sge),
+ TP_STRUCT__entry(/* entry */
+ DD_DEV_ENTRY(dd_from_ibdev(qp->ibqp.device))
+ __field(u32, qpn)
+ __field(int, index)
+ __field(u64, vaddr)
+ __field(u32, sge_length)
+ ),
+ TP_fast_assign(/* assign */
+ DD_DEV_ASSIGN(dd_from_ibdev(qp->ibqp.device));
+ __entry->qpn = qp->ibqp.qp_num;
+ __entry->index = index;
+ __entry->vaddr = (u64)sge->vaddr;
+ __entry->sge_length = sge->sge_length;
+ ),
+ TP_printk(/* print */
+ "[%s] qpn 0x%x sge %d: vaddr 0x%llx sge_length %u",
+ __get_str(dev),
+ __entry->qpn,
+ __entry->index,
+ __entry->vaddr,
+ __entry->sge_length
+ )
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_sge_template, hfi1_sge_check_align,
+ TP_PROTO(struct rvt_qp *qp, int index, struct rvt_sge *sge),
+ TP_ARGS(qp, index, sge)
+);
+
+DECLARE_EVENT_CLASS(/* tid_write_sp */
+ hfi1_tid_write_rsp_template,
+ TP_PROTO(struct rvt_qp *qp),
+ TP_ARGS(qp),
+ TP_STRUCT__entry(/* entry */
+ DD_DEV_ENTRY(dd_from_ibdev(qp->ibqp.device))
+ __field(u32, qpn)
+ __field(u32, r_tid_head)
+ __field(u32, r_tid_tail)
+ __field(u32, r_tid_ack)
+ __field(u32, r_tid_alloc)
+ __field(u32, alloc_w_segs)
+ __field(u32, pending_tid_w_segs)
+ __field(bool, sync_pt)
+ __field(u32, ps_nak_psn)
+ __field(u8, ps_nak_state)
+ __field(u8, prnr_nak_state)
+ __field(u32, hw_flow_index)
+ __field(u32, generation)
+ __field(u32, fpsn)
+ __field(u32, flow_flags)
+ __field(bool, resync)
+ __field(u32, r_next_psn_kdeth)
+ ),
+ TP_fast_assign(/* assign */
+ struct hfi1_qp_priv *priv = qp->priv;
+
+ DD_DEV_ASSIGN(dd_from_ibdev(qp->ibqp.device));
+ __entry->qpn = qp->ibqp.qp_num;
+ __entry->r_tid_head = priv->r_tid_head;
+ __entry->r_tid_tail = priv->r_tid_tail;
+ __entry->r_tid_ack = priv->r_tid_ack;
+ __entry->r_tid_alloc = priv->r_tid_alloc;
+ __entry->alloc_w_segs = priv->alloc_w_segs;
+ __entry->pending_tid_w_segs = priv->pending_tid_w_segs;
+ __entry->sync_pt = priv->sync_pt;
+ __entry->ps_nak_psn = priv->s_nak_psn;
+ __entry->ps_nak_state = priv->s_nak_state;
+ __entry->prnr_nak_state = priv->rnr_nak_state;
+ __entry->hw_flow_index = priv->flow_state.index;
+ __entry->generation = priv->flow_state.generation;
+ __entry->fpsn = priv->flow_state.psn;
+ __entry->flow_flags = priv->flow_state.flags;
+ __entry->resync = priv->resync;
+ __entry->r_next_psn_kdeth = priv->r_next_psn_kdeth;
+ ),
+ TP_printk(/* print */
+ TID_WRITE_RSPDR_PRN,
+ __get_str(dev),
+ __entry->qpn,
+ __entry->r_tid_head,
+ __entry->r_tid_tail,
+ __entry->r_tid_ack,
+ __entry->r_tid_alloc,
+ __entry->alloc_w_segs,
+ __entry->pending_tid_w_segs,
+ __entry->sync_pt ? "yes" : "no",
+ __entry->ps_nak_psn,
+ __entry->ps_nak_state,
+ __entry->prnr_nak_state,
+ __entry->hw_flow_index,
+ __entry->generation,
+ __entry->fpsn,
+ __entry->flow_flags,
+ __entry->resync ? "yes" : "no",
+ __entry->r_next_psn_kdeth
+ )
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_tid_write_rsp_template, hfi1_tid_write_rsp_alloc_res,
+ TP_PROTO(struct rvt_qp *qp),
+ TP_ARGS(qp)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_tid_write_rsp_template, hfi1_tid_write_rsp_rcv_req,
+ TP_PROTO(struct rvt_qp *qp),
+ TP_ARGS(qp)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_tid_write_rsp_template, hfi1_tid_write_rsp_build_resp,
+ TP_PROTO(struct rvt_qp *qp),
+ TP_ARGS(qp)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_tid_write_rsp_template, hfi1_tid_write_rsp_rcv_data,
+ TP_PROTO(struct rvt_qp *qp),
+ TP_ARGS(qp)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_tid_write_rsp_template, hfi1_tid_write_rsp_rcv_resync,
+ TP_PROTO(struct rvt_qp *qp),
+ TP_ARGS(qp)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_tid_write_rsp_template, hfi1_tid_write_rsp_make_tid_ack,
+ TP_PROTO(struct rvt_qp *qp),
+ TP_ARGS(qp)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_tid_write_rsp_template, hfi1_tid_write_rsp_handle_kdeth_eflags,
+ TP_PROTO(struct rvt_qp *qp),
+ TP_ARGS(qp)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_tid_write_rsp_template, hfi1_tid_write_rsp_make_rc_ack,
+ TP_PROTO(struct rvt_qp *qp),
+ TP_ARGS(qp)
+);
+
+DECLARE_EVENT_CLASS(/* tid_write_sender */
+ hfi1_tid_write_sender_template,
+ TP_PROTO(struct rvt_qp *qp, char newreq),
+ TP_ARGS(qp, newreq),
+ TP_STRUCT__entry(/* entry */
+ DD_DEV_ENTRY(dd_from_ibdev(qp->ibqp.device))
+ __field(u32, qpn)
+ __field(char, newreq)
+ __field(u32, s_tid_cur)
+ __field(u32, s_tid_tail)
+ __field(u32, s_tid_head)
+ __field(u32, pending_tid_w_resp)
+ __field(u32, n_requests)
+ __field(u32, n_tid_requests)
+ __field(u32, s_flags)
+ __field(u32, ps_flags)
+ __field(unsigned long, iow_flags)
+ __field(u8, s_state)
+ __field(u8, s_retry)
+ ),
+ TP_fast_assign(/* assign */
+ struct hfi1_qp_priv *priv = qp->priv;
+
+ DD_DEV_ASSIGN(dd_from_ibdev(qp->ibqp.device));
+ __entry->qpn = qp->ibqp.qp_num;
+ __entry->newreq = newreq;
+ __entry->s_tid_cur = priv->s_tid_cur;
+ __entry->s_tid_tail = priv->s_tid_tail;
+ __entry->s_tid_head = priv->s_tid_head;
+ __entry->pending_tid_w_resp = priv->pending_tid_w_resp;
+ __entry->n_requests = atomic_read(&priv->n_requests);
+ __entry->n_tid_requests = atomic_read(&priv->n_tid_requests);
+ __entry->s_flags = qp->s_flags;
+ __entry->ps_flags = priv->s_flags;
+ __entry->iow_flags = priv->s_iowait.flags;
+ __entry->s_state = priv->s_state;
+ __entry->s_retry = priv->s_retry;
+ ),
+ TP_printk(/* print */
+ TID_WRITE_SENDER_PRN,
+ __get_str(dev),
+ __entry->qpn,
+ __entry->newreq,
+ __entry->s_tid_cur,
+ __entry->s_tid_tail,
+ __entry->s_tid_head,
+ __entry->pending_tid_w_resp,
+ __entry->n_requests,
+ __entry->n_tid_requests,
+ __entry->s_flags,
+ __entry->ps_flags,
+ __entry->iow_flags,
+ __entry->s_state,
+ __entry->s_retry
+ )
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_tid_write_sender_template, hfi1_tid_write_sender_rcv_resp,
+ TP_PROTO(struct rvt_qp *qp, char newreq),
+ TP_ARGS(qp, newreq)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_tid_write_sender_template, hfi1_tid_write_sender_rcv_tid_ack,
+ TP_PROTO(struct rvt_qp *qp, char newreq),
+ TP_ARGS(qp, newreq)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_tid_write_sender_template, hfi1_tid_write_sender_retry_timeout,
+ TP_PROTO(struct rvt_qp *qp, char newreq),
+ TP_ARGS(qp, newreq)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_tid_write_sender_template, hfi1_tid_write_sender_make_tid_pkt,
+ TP_PROTO(struct rvt_qp *qp, char newreq),
+ TP_ARGS(qp, newreq)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_tid_write_sender_template, hfi1_tid_write_sender_make_req,
+ TP_PROTO(struct rvt_qp *qp, char newreq),
+ TP_ARGS(qp, newreq)
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_tid_write_sender_template, hfi1_tid_write_sender_restart_rc,
+ TP_PROTO(struct rvt_qp *qp, char newreq),
+ TP_ARGS(qp, newreq)
+);
+
+DECLARE_EVENT_CLASS(/* tid_ack */
+ hfi1_tid_ack_template,
+ TP_PROTO(struct rvt_qp *qp, u32 aeth, u32 psn,
+ u32 req_psn, u32 resync_psn),
+ TP_ARGS(qp, aeth, psn, req_psn, resync_psn),
+ TP_STRUCT__entry(/* entry */
+ DD_DEV_ENTRY(dd_from_ibdev(qp->ibqp.device))
+ __field(u32, qpn)
+ __field(u32, aeth)
+ __field(u32, psn)
+ __field(u32, req_psn)
+ __field(u32, resync_psn)
+ ),
+ TP_fast_assign(/* assign */
+ DD_DEV_ASSIGN(dd_from_ibdev(qp->ibqp.device))
+ __entry->qpn = qp->ibqp.qp_num;
+ __entry->aeth = aeth;
+ __entry->psn = psn;
+ __entry->req_psn = req_psn;
+ __entry->resync_psn = resync_psn;
+ ),
+ TP_printk(/* print */
+ "[%s] qpn 0x%x aeth 0x%x psn 0x%x req_psn 0x%x resync_psn 0x%x",
+ __get_str(dev),
+ __entry->qpn,
+ __entry->aeth,
+ __entry->psn,
+ __entry->req_psn,
+ __entry->resync_psn
+ )
+);
+
+DEFINE_EVENT(/* rcv_tid_ack */
+ hfi1_tid_ack_template, hfi1_rcv_tid_ack,
+ TP_PROTO(struct rvt_qp *qp, u32 aeth, u32 psn,
+ u32 req_psn, u32 resync_psn),
+ TP_ARGS(qp, aeth, psn, req_psn, resync_psn)
+);
+
+DECLARE_EVENT_CLASS(/* kdeth_eflags_error */
+ hfi1_kdeth_eflags_error_template,
+ TP_PROTO(struct rvt_qp *qp, u8 rcv_type, u8 rte, u32 psn),
+ TP_ARGS(qp, rcv_type, rte, psn),
+ TP_STRUCT__entry(/* entry */
+ DD_DEV_ENTRY(dd_from_ibdev(qp->ibqp.device))
+ __field(u32, qpn)
+ __field(u8, rcv_type)
+ __field(u8, rte)
+ __field(u32, psn)
+ ),
+ TP_fast_assign(/* assign */
+ DD_DEV_ASSIGN(dd_from_ibdev(qp->ibqp.device));
+ __entry->qpn = qp->ibqp.qp_num;
+ __entry->rcv_type = rcv_type;
+ __entry->rte = rte;
+ __entry->psn = psn;
+ ),
+ TP_printk(/* print */
+ KDETH_EFLAGS_ERR_PRN,
+ __get_str(dev),
+ __entry->qpn,
+ __entry->rcv_type,
+ __entry->rte,
+ __entry->psn
+ )
+);
+
+DEFINE_EVENT(/* event */
+ hfi1_kdeth_eflags_error_template, hfi1_eflags_err_write,
+ TP_PROTO(struct rvt_qp *qp, u8 rcv_type, u8 rte, u32 psn),
+ TP_ARGS(qp, rcv_type, rte, psn)
+);
+
+#endif /* __HFI1_TRACE_TID_H */
+
+#undef TRACE_INCLUDE_PATH
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_PATH .
+#define TRACE_INCLUDE_FILE trace_tid
+#include <trace/define_trace.h>
diff --git a/drivers/infiniband/hw/hfi1/trace_tx.h b/drivers/infiniband/hw/hfi1/trace_tx.h
index c57af3b31fe1..09eb0c9ada00 100644
--- a/drivers/infiniband/hw/hfi1/trace_tx.h
+++ b/drivers/infiniband/hw/hfi1/trace_tx.h
@@ -114,19 +114,27 @@ DECLARE_EVENT_CLASS(hfi1_qpsleepwakeup_template,
__field(u32, qpn)
__field(u32, flags)
__field(u32, s_flags)
+ __field(u32, ps_flags)
+ __field(unsigned long, iow_flags)
),
TP_fast_assign(
DD_DEV_ASSIGN(dd_from_ibdev(qp->ibqp.device))
__entry->flags = flags;
__entry->qpn = qp->ibqp.qp_num;
__entry->s_flags = qp->s_flags;
+ __entry->ps_flags =
+ ((struct hfi1_qp_priv *)qp->priv)->s_flags;
+ __entry->iow_flags =
+ ((struct hfi1_qp_priv *)qp->priv)->s_iowait.flags;
),
TP_printk(
- "[%s] qpn 0x%x flags 0x%x s_flags 0x%x",
+ "[%s] qpn 0x%x flags 0x%x s_flags 0x%x ps_flags 0x%x iow_flags 0x%lx",
__get_str(dev),
__entry->qpn,
__entry->flags,
- __entry->s_flags
+ __entry->s_flags,
+ __entry->ps_flags,
+ __entry->iow_flags
)
);
@@ -838,6 +846,12 @@ DEFINE_EVENT(
TP_ARGS(qp, flag)
);
+DEFINE_EVENT(/* event */
+ hfi1_do_send_template, hfi1_rc_do_tid_send,
+ TP_PROTO(struct rvt_qp *qp, bool flag),
+ TP_ARGS(qp, flag)
+);
+
DEFINE_EVENT(
hfi1_do_send_template, hfi1_rc_expired_time_slice,
TP_PROTO(struct rvt_qp *qp, bool flag),
diff --git a/drivers/infiniband/hw/hfi1/uc.c b/drivers/infiniband/hw/hfi1/uc.c
index 6ba47037c424..4ed4fcfabd6c 100644
--- a/drivers/infiniband/hw/hfi1/uc.c
+++ b/drivers/infiniband/hw/hfi1/uc.c
@@ -271,7 +271,8 @@ int hfi1_make_uc_req(struct rvt_qp *qp, struct hfi1_pkt_state *ps)
ps->s_txreq->ss = &qp->s_sge;
ps->s_txreq->s_cur_size = len;
hfi1_make_ruc_header(qp, ohdr, bth0 | (qp->s_state << 24),
- mask_psn(qp->s_psn++), middle, ps);
+ qp->remote_qpn, mask_psn(qp->s_psn++),
+ middle, ps);
return 1;
done_free_tx:
diff --git a/drivers/infiniband/hw/hfi1/ud.c b/drivers/infiniband/hw/hfi1/ud.c
index bf96067876c9..f88ad425664a 100644
--- a/drivers/infiniband/hw/hfi1/ud.c
+++ b/drivers/infiniband/hw/hfi1/ud.c
@@ -222,31 +222,11 @@ static void ud_loopback(struct rvt_qp *sqp, struct rvt_swqe *swqe)
ssge.num_sge = swqe->wr.num_sge;
sge = &ssge.sge;
while (length) {
- u32 len = sge->length;
+ u32 len = rvt_get_sge_length(sge, length);
- if (len > length)
- len = length;
- if (len > sge->sge_length)
- len = sge->sge_length;
WARN_ON_ONCE(len == 0);
rvt_copy_sge(qp, &qp->r_sge, sge->vaddr, len, true, false);
- sge->vaddr += len;
- sge->length -= len;
- sge->sge_length -= len;
- if (sge->sge_length == 0) {
- if (--ssge.num_sge)
- *sge = *ssge.sg_list++;
- } else if (sge->length == 0 && sge->mr->lkey) {
- if (++sge->n >= RVT_SEGSZ) {
- if (++sge->m >= sge->mr->mapsz)
- break;
- sge->n = 0;
- }
- sge->vaddr =
- sge->mr->map[sge->m]->segs[sge->n].vaddr;
- sge->length =
- sge->mr->map[sge->m]->segs[sge->n].length;
- }
+ rvt_update_sge(&ssge, len, false);
length -= len;
}
rvt_put_ss(&qp->r_sge);
diff --git a/drivers/infiniband/hw/hfi1/user_exp_rcv.h b/drivers/infiniband/hw/hfi1/user_exp_rcv.h
index e383cc01a2bf..43b105de1d54 100644
--- a/drivers/infiniband/hw/hfi1/user_exp_rcv.h
+++ b/drivers/infiniband/hw/hfi1/user_exp_rcv.h
@@ -48,7 +48,6 @@
*/
#include "hfi.h"
-
#include "exp_rcv.h"
struct tid_pageset {
diff --git a/drivers/infiniband/hw/hfi1/user_pages.c b/drivers/infiniband/hw/hfi1/user_pages.c
index e341e6dcc388..24b592c6522e 100644
--- a/drivers/infiniband/hw/hfi1/user_pages.c
+++ b/drivers/infiniband/hw/hfi1/user_pages.c
@@ -91,9 +91,7 @@ bool hfi1_can_pin_pages(struct hfi1_devdata *dd, struct mm_struct *mm,
/* Convert to number of pages */
size = DIV_ROUND_UP(size, PAGE_SIZE);
- down_read(&mm->mmap_sem);
- pinned = mm->pinned_vm;
- up_read(&mm->mmap_sem);
+ pinned = atomic64_read(&mm->pinned_vm);
/* First, check the absolute limit against all pinned pages. */
if (pinned + npages >= ulimit && !can_lock)
@@ -111,9 +109,7 @@ int hfi1_acquire_user_pages(struct mm_struct *mm, unsigned long vaddr, size_t np
if (ret < 0)
return ret;
- down_write(&mm->mmap_sem);
- mm->pinned_vm += ret;
- up_write(&mm->mmap_sem);
+ atomic64_add(ret, &mm->pinned_vm);
return ret;
}
@@ -130,8 +126,6 @@ void hfi1_release_user_pages(struct mm_struct *mm, struct page **p,
}
if (mm) { /* during close after signal, mm can be NULL */
- down_write(&mm->mmap_sem);
- mm->pinned_vm -= npages;
- up_write(&mm->mmap_sem);
+ atomic64_sub(npages, &mm->pinned_vm);
}
}
diff --git a/drivers/infiniband/hw/hfi1/user_sdma.c b/drivers/infiniband/hw/hfi1/user_sdma.c
index e5e7fad09f32..8bfbc6d7ea34 100644
--- a/drivers/infiniband/hw/hfi1/user_sdma.c
+++ b/drivers/infiniband/hw/hfi1/user_sdma.c
@@ -144,8 +144,10 @@ static int defer_packet_queue(
*/
xchg(&pq->state, SDMA_PKT_Q_DEFERRED);
write_seqlock(&sde->waitlock);
- if (list_empty(&pq->busy.list))
+ if (list_empty(&pq->busy.list)) {
+ iowait_get_priority(&pq->busy);
iowait_queue(pkts_sent, &pq->busy, &sde->dmawait);
+ }
write_sequnlock(&sde->waitlock);
return -EBUSY;
eagain:
@@ -191,7 +193,7 @@ int hfi1_user_sdma_alloc_queues(struct hfi1_ctxtdata *uctxt,
pq->mm = fd->mm;
iowait_init(&pq->busy, 0, NULL, NULL, defer_packet_queue,
- activate_packet_queue, NULL);
+ activate_packet_queue, NULL, NULL);
pq->reqidx = 0;
pq->reqs = kcalloc(hfi1_sdma_comp_ring_size,
@@ -1126,7 +1128,8 @@ static inline u32 set_pkt_bth_psn(__be32 bthpsn, u8 expct, u32 frags)
0xffffffull),
psn = val & mask;
if (expct)
- psn = (psn & ~BTH_SEQ_MASK) | ((psn + frags) & BTH_SEQ_MASK);
+ psn = (psn & ~HFI1_KDETH_BTH_SEQ_MASK) |
+ ((psn + frags) & HFI1_KDETH_BTH_SEQ_MASK);
else
psn = psn + frags;
return psn & mask;
diff --git a/drivers/infiniband/hw/hfi1/verbs.c b/drivers/infiniband/hw/hfi1/verbs.c
index ec582d86025f..55a56b3d7f83 100644
--- a/drivers/infiniband/hw/hfi1/verbs.c
+++ b/drivers/infiniband/hw/hfi1/verbs.c
@@ -161,10 +161,12 @@ MODULE_PARM_DESC(wss_clean_period, "Count of verbs copies before an entry in the
*/
const enum ib_wc_opcode ib_hfi1_wc_opcode[] = {
[IB_WR_RDMA_WRITE] = IB_WC_RDMA_WRITE,
+ [IB_WR_TID_RDMA_WRITE] = IB_WC_RDMA_WRITE,
[IB_WR_RDMA_WRITE_WITH_IMM] = IB_WC_RDMA_WRITE,
[IB_WR_SEND] = IB_WC_SEND,
[IB_WR_SEND_WITH_IMM] = IB_WC_SEND,
[IB_WR_RDMA_READ] = IB_WC_RDMA_READ,
+ [IB_WR_TID_RDMA_READ] = IB_WC_RDMA_READ,
[IB_WR_ATOMIC_CMP_AND_SWP] = IB_WC_COMP_SWAP,
[IB_WR_ATOMIC_FETCH_AND_ADD] = IB_WC_FETCH_ADD,
[IB_WR_SEND_WITH_INV] = IB_WC_SEND,
@@ -200,6 +202,14 @@ const u8 hdr_len_by_opcode[256] = {
[IB_OPCODE_RC_FETCH_ADD] = 12 + 8 + 28,
[IB_OPCODE_RC_SEND_LAST_WITH_INVALIDATE] = 12 + 8 + 4,
[IB_OPCODE_RC_SEND_ONLY_WITH_INVALIDATE] = 12 + 8 + 4,
+ [IB_OPCODE_TID_RDMA_READ_REQ] = 12 + 8 + 36,
+ [IB_OPCODE_TID_RDMA_READ_RESP] = 12 + 8 + 36,
+ [IB_OPCODE_TID_RDMA_WRITE_REQ] = 12 + 8 + 36,
+ [IB_OPCODE_TID_RDMA_WRITE_RESP] = 12 + 8 + 36,
+ [IB_OPCODE_TID_RDMA_WRITE_DATA] = 12 + 8 + 36,
+ [IB_OPCODE_TID_RDMA_WRITE_DATA_LAST] = 12 + 8 + 36,
+ [IB_OPCODE_TID_RDMA_ACK] = 12 + 8 + 36,
+ [IB_OPCODE_TID_RDMA_RESYNC] = 12 + 8 + 36,
/* UC */
[IB_OPCODE_UC_SEND_FIRST] = 12 + 8,
[IB_OPCODE_UC_SEND_MIDDLE] = 12 + 8,
@@ -243,6 +253,17 @@ static const opcode_handler opcode_handler_tbl[256] = {
[IB_OPCODE_RC_FETCH_ADD] = &hfi1_rc_rcv,
[IB_OPCODE_RC_SEND_LAST_WITH_INVALIDATE] = &hfi1_rc_rcv,
[IB_OPCODE_RC_SEND_ONLY_WITH_INVALIDATE] = &hfi1_rc_rcv,
+
+ /* TID RDMA has separate handlers for different opcodes.*/
+ [IB_OPCODE_TID_RDMA_WRITE_REQ] = &hfi1_rc_rcv_tid_rdma_write_req,
+ [IB_OPCODE_TID_RDMA_WRITE_RESP] = &hfi1_rc_rcv_tid_rdma_write_resp,
+ [IB_OPCODE_TID_RDMA_WRITE_DATA] = &hfi1_rc_rcv_tid_rdma_write_data,
+ [IB_OPCODE_TID_RDMA_WRITE_DATA_LAST] = &hfi1_rc_rcv_tid_rdma_write_data,
+ [IB_OPCODE_TID_RDMA_READ_REQ] = &hfi1_rc_rcv_tid_rdma_read_req,
+ [IB_OPCODE_TID_RDMA_READ_RESP] = &hfi1_rc_rcv_tid_rdma_read_resp,
+ [IB_OPCODE_TID_RDMA_RESYNC] = &hfi1_rc_rcv_tid_rdma_resync,
+ [IB_OPCODE_TID_RDMA_ACK] = &hfi1_rc_rcv_tid_rdma_ack,
+
/* UC */
[IB_OPCODE_UC_SEND_FIRST] = &hfi1_uc_rcv,
[IB_OPCODE_UC_SEND_MIDDLE] = &hfi1_uc_rcv,
@@ -308,7 +329,7 @@ static inline opcode_handler qp_ok(struct hfi1_packet *packet)
static u64 hfi1_fault_tx(struct rvt_qp *qp, u8 opcode, u64 pbc)
{
#ifdef CONFIG_FAULT_INJECTION
- if ((opcode & IB_OPCODE_MSP) == IB_OPCODE_MSP)
+ if ((opcode & IB_OPCODE_MSP) == IB_OPCODE_MSP) {
/*
* In order to drop non-IB traffic we
* set PbcInsertHrc to NONE (0x2).
@@ -319,8 +340,9 @@ static u64 hfi1_fault_tx(struct rvt_qp *qp, u8 opcode, u64 pbc)
* packet will not be delivered to the
* correct context.
*/
+ pbc &= ~PBC_INSERT_HCRC_SMASK;
pbc |= (u64)PBC_IHCRC_NONE << PBC_INSERT_HCRC_SHIFT;
- else
+ } else {
/*
* In order to drop regular verbs
* traffic we set the PbcTestEbp
@@ -330,10 +352,129 @@ static u64 hfi1_fault_tx(struct rvt_qp *qp, u8 opcode, u64 pbc)
* triggered and will be dropped.
*/
pbc |= PBC_TEST_EBP;
+ }
#endif
return pbc;
}
+static opcode_handler tid_qp_ok(int opcode, struct hfi1_packet *packet)
+{
+ if (packet->qp->ibqp.qp_type != IB_QPT_RC ||
+ !(ib_rvt_state_ops[packet->qp->state] & RVT_PROCESS_RECV_OK))
+ return NULL;
+ if ((opcode & RVT_OPCODE_QP_MASK) == IB_OPCODE_TID_RDMA)
+ return opcode_handler_tbl[opcode];
+ return NULL;
+}
+
+void hfi1_kdeth_eager_rcv(struct hfi1_packet *packet)
+{
+ struct hfi1_ctxtdata *rcd = packet->rcd;
+ struct ib_header *hdr = packet->hdr;
+ u32 tlen = packet->tlen;
+ struct hfi1_pportdata *ppd = rcd->ppd;
+ struct hfi1_ibport *ibp = &ppd->ibport_data;
+ struct rvt_dev_info *rdi = &ppd->dd->verbs_dev.rdi;
+ opcode_handler opcode_handler;
+ unsigned long flags;
+ u32 qp_num;
+ int lnh;
+ u8 opcode;
+
+ /* DW == LRH (2) + BTH (3) + KDETH (9) + CRC (1) */
+ if (unlikely(tlen < 15 * sizeof(u32)))
+ goto drop;
+
+ lnh = be16_to_cpu(hdr->lrh[0]) & 3;
+ if (lnh != HFI1_LRH_BTH)
+ goto drop;
+
+ packet->ohdr = &hdr->u.oth;
+ trace_input_ibhdr(rcd->dd, packet, !!(rhf_dc_info(packet->rhf)));
+
+ opcode = (be32_to_cpu(packet->ohdr->bth[0]) >> 24);
+ inc_opstats(tlen, &rcd->opstats->stats[opcode]);
+
+ /* verbs_qp can be picked up from any tid_rdma header struct */
+ qp_num = be32_to_cpu(packet->ohdr->u.tid_rdma.r_req.verbs_qp) &
+ RVT_QPN_MASK;
+
+ rcu_read_lock();
+ packet->qp = rvt_lookup_qpn(rdi, &ibp->rvp, qp_num);
+ if (!packet->qp)
+ goto drop_rcu;
+ spin_lock_irqsave(&packet->qp->r_lock, flags);
+ opcode_handler = tid_qp_ok(opcode, packet);
+ if (likely(opcode_handler))
+ opcode_handler(packet);
+ else
+ goto drop_unlock;
+ spin_unlock_irqrestore(&packet->qp->r_lock, flags);
+ rcu_read_unlock();
+
+ return;
+drop_unlock:
+ spin_unlock_irqrestore(&packet->qp->r_lock, flags);
+drop_rcu:
+ rcu_read_unlock();
+drop:
+ ibp->rvp.n_pkt_drops++;
+}
+
+void hfi1_kdeth_expected_rcv(struct hfi1_packet *packet)
+{
+ struct hfi1_ctxtdata *rcd = packet->rcd;
+ struct ib_header *hdr = packet->hdr;
+ u32 tlen = packet->tlen;
+ struct hfi1_pportdata *ppd = rcd->ppd;
+ struct hfi1_ibport *ibp = &ppd->ibport_data;
+ struct rvt_dev_info *rdi = &ppd->dd->verbs_dev.rdi;
+ opcode_handler opcode_handler;
+ unsigned long flags;
+ u32 qp_num;
+ int lnh;
+ u8 opcode;
+
+ /* DW == LRH (2) + BTH (3) + KDETH (9) + CRC (1) */
+ if (unlikely(tlen < 15 * sizeof(u32)))
+ goto drop;
+
+ lnh = be16_to_cpu(hdr->lrh[0]) & 3;
+ if (lnh != HFI1_LRH_BTH)
+ goto drop;
+
+ packet->ohdr = &hdr->u.oth;
+ trace_input_ibhdr(rcd->dd, packet, !!(rhf_dc_info(packet->rhf)));
+
+ opcode = (be32_to_cpu(packet->ohdr->bth[0]) >> 24);
+ inc_opstats(tlen, &rcd->opstats->stats[opcode]);
+
+ /* verbs_qp can be picked up from any tid_rdma header struct */
+ qp_num = be32_to_cpu(packet->ohdr->u.tid_rdma.r_rsp.verbs_qp) &
+ RVT_QPN_MASK;
+
+ rcu_read_lock();
+ packet->qp = rvt_lookup_qpn(rdi, &ibp->rvp, qp_num);
+ if (!packet->qp)
+ goto drop_rcu;
+ spin_lock_irqsave(&packet->qp->r_lock, flags);
+ opcode_handler = tid_qp_ok(opcode, packet);
+ if (likely(opcode_handler))
+ opcode_handler(packet);
+ else
+ goto drop_unlock;
+ spin_unlock_irqrestore(&packet->qp->r_lock, flags);
+ rcu_read_unlock();
+
+ return;
+drop_unlock:
+ spin_unlock_irqrestore(&packet->qp->r_lock, flags);
+drop_rcu:
+ rcu_read_unlock();
+drop:
+ ibp->rvp.n_pkt_drops++;
+}
+
static int hfi1_do_pkey_check(struct hfi1_packet *packet)
{
struct hfi1_ctxtdata *rcd = packet->rcd;
@@ -504,11 +645,28 @@ static void verbs_sdma_complete(
hfi1_put_txreq(tx);
}
+void hfi1_wait_kmem(struct rvt_qp *qp)
+{
+ struct hfi1_qp_priv *priv = qp->priv;
+ struct ib_qp *ibqp = &qp->ibqp;
+ struct ib_device *ibdev = ibqp->device;
+ struct hfi1_ibdev *dev = to_idev(ibdev);
+
+ if (list_empty(&priv->s_iowait.list)) {
+ if (list_empty(&dev->memwait))
+ mod_timer(&dev->mem_timer, jiffies + 1);
+ qp->s_flags |= RVT_S_WAIT_KMEM;
+ list_add_tail(&priv->s_iowait.list, &dev->memwait);
+ priv->s_iowait.lock = &dev->iowait_lock;
+ trace_hfi1_qpsleep(qp, RVT_S_WAIT_KMEM);
+ rvt_get_qp(qp);
+ }
+}
+
static int wait_kmem(struct hfi1_ibdev *dev,
struct rvt_qp *qp,
struct hfi1_pkt_state *ps)
{
- struct hfi1_qp_priv *priv = qp->priv;
unsigned long flags;
int ret = 0;
@@ -517,15 +675,7 @@ static int wait_kmem(struct hfi1_ibdev *dev,
write_seqlock(&dev->iowait_lock);
list_add_tail(&ps->s_txreq->txreq.list,
&ps->wait->tx_head);
- if (list_empty(&priv->s_iowait.list)) {
- if (list_empty(&dev->memwait))
- mod_timer(&dev->mem_timer, jiffies + 1);
- qp->s_flags |= RVT_S_WAIT_KMEM;
- list_add_tail(&priv->s_iowait.list, &dev->memwait);
- priv->s_iowait.lock = &dev->iowait_lock;
- trace_hfi1_qpsleep(qp, RVT_S_WAIT_KMEM);
- rvt_get_qp(qp);
- }
+ hfi1_wait_kmem(qp);
write_sequnlock(&dev->iowait_lock);
hfi1_qp_unbusy(qp, ps->wait);
ret = -EBUSY;
@@ -553,11 +703,7 @@ static noinline int build_verbs_ulp_payload(
int ret = 0;
while (length) {
- len = ss->sge.length;
- if (len > length)
- len = length;
- if (len > ss->sge.sge_length)
- len = ss->sge.sge_length;
+ len = rvt_get_sge_length(&ss->sge, length);
WARN_ON_ONCE(len == 0);
ret = sdma_txadd_kvaddr(
sde->dd,
@@ -678,6 +824,15 @@ bail_txadd:
return ret;
}
+static u64 update_hcrc(u8 opcode, u64 pbc)
+{
+ if ((opcode & IB_OPCODE_TID_RDMA) == IB_OPCODE_TID_RDMA) {
+ pbc &= ~PBC_INSERT_HCRC_SMASK;
+ pbc |= (u64)PBC_IHCRC_LKDETH << PBC_INSERT_HCRC_SHIFT;
+ }
+ return pbc;
+}
+
int hfi1_verbs_send_dma(struct rvt_qp *qp, struct hfi1_pkt_state *ps,
u64 pbc)
{
@@ -723,6 +878,9 @@ int hfi1_verbs_send_dma(struct rvt_qp *qp, struct hfi1_pkt_state *ps,
qp->srate_mbps,
vl,
plen);
+
+ /* Update HCRC based on packet opcode */
+ pbc = update_hcrc(ps->opcode, pbc);
}
tx->wqe = qp->s_wqe;
ret = build_verbs_tx_desc(tx->sde, len, tx, ahg_info, pbc);
@@ -787,6 +945,7 @@ static int pio_wait(struct rvt_qp *qp,
dev->n_piodrain += !!(flag & HFI1_S_WAIT_PIO_DRAIN);
qp->s_flags |= flag;
was_empty = list_empty(&sc->piowait);
+ iowait_get_priority(&priv->s_iowait);
iowait_queue(ps->pkts_sent, &priv->s_iowait,
&sc->piowait);
priv->s_iowait.lock = &sc->waitlock;
@@ -871,6 +1030,9 @@ int hfi1_verbs_send_pio(struct rvt_qp *qp, struct hfi1_pkt_state *ps,
if (unlikely(hfi1_dbg_should_fault_tx(qp, ps->opcode)))
pbc = hfi1_fault_tx(qp, ps->opcode, pbc);
pbc = create_pbc(ppd, pbc, qp->srate_mbps, vl, plen);
+
+ /* Update HCRC based on packet opcode */
+ pbc = update_hcrc(ps->opcode, pbc);
}
if (cb)
iowait_pio_inc(&priv->s_iowait);
@@ -914,12 +1076,8 @@ int hfi1_verbs_send_pio(struct rvt_qp *qp, struct hfi1_pkt_state *ps,
if (ss) {
while (len) {
void *addr = ss->sge.vaddr;
- u32 slen = ss->sge.length;
+ u32 slen = rvt_get_sge_length(&ss->sge, len);
- if (slen > len)
- slen = len;
- if (slen > ss->sge.sge_length)
- slen = ss->sge.sge_length;
rvt_update_sge(ss, slen, false);
seg_pio_copy_mid(pbuf, addr, slen);
len -= slen;
@@ -1188,7 +1346,9 @@ static void hfi1_fill_device_attr(struct hfi1_devdata *dd)
rdi->dparms.props.max_mr_size = U64_MAX;
rdi->dparms.props.max_fast_reg_page_list_len = UINT_MAX;
rdi->dparms.props.max_qp = hfi1_max_qps;
- rdi->dparms.props.max_qp_wr = hfi1_max_qp_wrs;
+ rdi->dparms.props.max_qp_wr =
+ (hfi1_max_qp_wrs >= HFI1_QP_WQE_INVALID ?
+ HFI1_QP_WQE_INVALID - 1 : hfi1_max_qp_wrs);
rdi->dparms.props.max_send_sge = hfi1_max_sges;
rdi->dparms.props.max_recv_sge = hfi1_max_sges;
rdi->dparms.props.max_sge_rd = hfi1_max_sges;
@@ -1622,6 +1782,7 @@ static const struct ib_device_ops hfi1_dev_ops = {
.alloc_rdma_netdev = hfi1_vnic_alloc_rn,
.get_dev_fw_str = hfi1_get_dev_fw_str,
.get_hw_stats = get_hw_stats,
+ .init_port = hfi1_create_port_files,
.modify_device = modify_device,
/* keep process mad in the driver */
.process_mad = hfi1_process_mad,
@@ -1679,7 +1840,6 @@ int hfi1_register_ib_device(struct hfi1_devdata *dd)
/*
* Fill in rvt info object.
*/
- dd->verbs_dev.rdi.driver_f.port_callback = hfi1_create_port_files;
dd->verbs_dev.rdi.driver_f.get_pci_dev = get_pci_dev;
dd->verbs_dev.rdi.driver_f.check_ah = hfi1_check_ah;
dd->verbs_dev.rdi.driver_f.notify_new_ah = hfi1_notify_new_ah;
@@ -1743,6 +1903,8 @@ int hfi1_register_ib_device(struct hfi1_devdata *dd)
dd->verbs_dev.rdi.dparms.sge_copy_mode = sge_copy_mode;
dd->verbs_dev.rdi.dparms.wss_threshold = wss_threshold;
dd->verbs_dev.rdi.dparms.wss_clean_period = wss_clean_period;
+ dd->verbs_dev.rdi.dparms.reserved_operations = 1;
+ dd->verbs_dev.rdi.dparms.extra_rdma_atomic = HFI1_TID_RDMA_WRITE_CNT;
/* post send table */
dd->verbs_dev.rdi.post_parms = hfi1_post_parms;
diff --git a/drivers/infiniband/hw/hfi1/verbs.h b/drivers/infiniband/hw/hfi1/verbs.h
index 1ad0b14bdb3c..62ace0b2d17a 100644
--- a/drivers/infiniband/hw/hfi1/verbs.h
+++ b/drivers/infiniband/hw/hfi1/verbs.h
@@ -72,6 +72,7 @@ struct hfi1_packet;
#include "iowait.h"
#include "tid_rdma.h"
+#include "opfn.h"
#define HFI1_MAX_RDMA_ATOMIC 16
@@ -158,10 +159,68 @@ struct hfi1_qp_priv {
struct sdma_engine *s_sde; /* current sde */
struct send_context *s_sendcontext; /* current sendcontext */
struct hfi1_ctxtdata *rcd; /* QP's receive context */
+ struct page **pages; /* for TID page scan */
+ u32 tid_enqueue; /* saved when tid waited */
u8 s_sc; /* SC[0..4] for next packet */
struct iowait s_iowait;
+ struct timer_list s_tid_timer; /* for timing tid wait */
+ struct timer_list s_tid_retry_timer; /* for timing tid ack */
+ struct list_head tid_wait; /* for queueing tid space */
+ struct hfi1_opfn_data opfn;
+ struct tid_flow_state flow_state;
+ struct tid_rdma_qp_params tid_rdma;
struct rvt_qp *owner;
u8 hdr_type; /* 9B or 16B */
+ struct rvt_sge_state tid_ss; /* SGE state pointer for 2nd leg */
+ atomic_t n_requests; /* # of TID RDMA requests in the */
+ /* queue */
+ atomic_t n_tid_requests; /* # of sent TID RDMA requests */
+ unsigned long tid_timer_timeout_jiffies;
+ unsigned long tid_retry_timeout_jiffies;
+
+ /* variables for the TID RDMA SE state machine */
+ u8 s_state;
+ u8 s_retry;
+ u8 rnr_nak_state; /* RNR NAK state */
+ u8 s_nak_state;
+ u32 s_nak_psn;
+ u32 s_flags;
+ u32 s_tid_cur;
+ u32 s_tid_head;
+ u32 s_tid_tail;
+ u32 r_tid_head; /* Most recently added TID RDMA request */
+ u32 r_tid_tail; /* the last completed TID RDMA request */
+ u32 r_tid_ack; /* the TID RDMA request to be ACK'ed */
+ u32 r_tid_alloc; /* Request for which we are allocating resources */
+ u32 pending_tid_w_segs; /* Num of pending tid write segments */
+ u32 pending_tid_w_resp; /* Num of pending tid write responses */
+ u32 alloc_w_segs; /* Number of segments for which write */
+ /* resources have been allocated for this QP */
+
+ /* For TID RDMA READ */
+ u32 tid_r_reqs; /* Num of tid reads requested */
+ u32 tid_r_comp; /* Num of tid reads completed */
+ u32 pending_tid_r_segs; /* Num of pending tid read segments */
+ u16 pkts_ps; /* packets per segment */
+ u8 timeout_shift; /* account for number of packets per segment */
+
+ u32 r_next_psn_kdeth;
+ u32 r_next_psn_kdeth_save;
+ u32 s_resync_psn;
+ u8 sync_pt; /* Set when QP reaches sync point */
+ u8 resync;
+};
+
+#define HFI1_QP_WQE_INVALID ((u32)-1)
+
+struct hfi1_swqe_priv {
+ struct tid_rdma_request tid_req;
+ struct rvt_sge_state ss; /* Used for TID RDMA READ Request */
+};
+
+struct hfi1_ack_priv {
+ struct rvt_sge_state ss; /* used for TID WRITE RESP */
+ struct tid_rdma_request tid_req;
};
/*
@@ -225,6 +284,7 @@ struct hfi1_ibdev {
struct kmem_cache *verbs_txreq_cache;
u64 n_txwait;
u64 n_kmem_wait;
+ u64 n_tidwait;
/* protect iowait lists */
seqlock_t iowait_lock ____cacheline_aligned_in_smp;
@@ -312,6 +372,31 @@ static inline u32 delta_psn(u32 a, u32 b)
return (((int)a - (int)b) << PSN_SHIFT) >> PSN_SHIFT;
}
+static inline struct tid_rdma_request *wqe_to_tid_req(struct rvt_swqe *wqe)
+{
+ return &((struct hfi1_swqe_priv *)wqe->priv)->tid_req;
+}
+
+static inline struct tid_rdma_request *ack_to_tid_req(struct rvt_ack_entry *e)
+{
+ return &((struct hfi1_ack_priv *)e->priv)->tid_req;
+}
+
+/*
+ * Look through all the active flows for a TID RDMA request and find
+ * the one (if it exists) that contains the specified PSN.
+ */
+static inline u32 __full_flow_psn(struct flow_state *state, u32 psn)
+{
+ return mask_psn((state->generation << HFI1_KDETH_BTH_SEQ_SHIFT) |
+ (psn & HFI1_KDETH_BTH_SEQ_MASK));
+}
+
+static inline u32 full_flow_psn(struct tid_rdma_flow *flow, u32 psn)
+{
+ return __full_flow_psn(&flow->flow_state, psn);
+}
+
struct verbs_txreq;
void hfi1_put_txreq(struct verbs_txreq *tx);
@@ -356,9 +441,12 @@ u32 hfi1_make_grh(struct hfi1_ibport *ibp, struct ib_grh *hdr,
const struct ib_global_route *grh, u32 hwords, u32 nwords);
void hfi1_make_ruc_header(struct rvt_qp *qp, struct ib_other_headers *ohdr,
- u32 bth0, u32 bth2, int middle,
+ u32 bth0, u32 bth1, u32 bth2, int middle,
struct hfi1_pkt_state *ps);
+bool hfi1_schedule_send_yield(struct rvt_qp *qp, struct hfi1_pkt_state *ps,
+ bool tid);
+
void _hfi1_do_send(struct work_struct *work);
void hfi1_do_send_from_rvt(struct rvt_qp *qp);
@@ -377,6 +465,10 @@ int hfi1_register_ib_device(struct hfi1_devdata *);
void hfi1_unregister_ib_device(struct hfi1_devdata *);
+void hfi1_kdeth_eager_rcv(struct hfi1_packet *packet);
+
+void hfi1_kdeth_expected_rcv(struct hfi1_packet *packet);
+
void hfi1_ib_rcv(struct hfi1_packet *packet);
void hfi1_16B_rcv(struct hfi1_packet *packet);
@@ -394,6 +486,16 @@ static inline bool opa_bth_is_migration(struct ib_other_headers *ohdr)
return ohdr->bth[1] & cpu_to_be32(OPA_BTH_MIG_REQ);
}
+void hfi1_wait_kmem(struct rvt_qp *qp);
+
+static inline void hfi1_trdma_send_complete(struct rvt_qp *qp,
+ struct rvt_swqe *wqe,
+ enum ib_wc_status status)
+{
+ trdma_clean_swqe(qp, wqe);
+ rvt_send_complete(qp, wqe, status);
+}
+
extern const enum ib_wc_opcode ib_hfi1_wc_opcode[];
extern const u8 hdr_len_by_opcode[];
diff --git a/drivers/infiniband/hw/hfi1/verbs_txreq.h b/drivers/infiniband/hw/hfi1/verbs_txreq.h
index 2a77af26a231..b002e96eb335 100644
--- a/drivers/infiniband/hw/hfi1/verbs_txreq.h
+++ b/drivers/infiniband/hw/hfi1/verbs_txreq.h
@@ -94,6 +94,7 @@ static inline struct verbs_txreq *get_txreq(struct hfi1_ibdev *dev,
tx->txreq.num_desc = 0;
/* Set the header type */
tx->phdr.hdr.hdr_type = priv->hdr_type;
+ tx->txreq.flags = 0;
return tx;
}
diff --git a/drivers/infiniband/hw/hfi1/vnic_sdma.c b/drivers/infiniband/hw/hfi1/vnic_sdma.c
index 1f81c480e028..af1b1ffcb38e 100644
--- a/drivers/infiniband/hw/hfi1/vnic_sdma.c
+++ b/drivers/infiniband/hw/hfi1/vnic_sdma.c
@@ -240,8 +240,10 @@ static int hfi1_vnic_sdma_sleep(struct sdma_engine *sde,
}
vnic_sdma->state = HFI1_VNIC_SDMA_Q_DEFERRED;
- if (list_empty(&vnic_sdma->wait.list))
+ if (list_empty(&vnic_sdma->wait.list)) {
+ iowait_get_priority(wait->iow);
iowait_queue(pkts_sent, wait->iow, &sde->dmawait);
+ }
write_sequnlock(&sde->waitlock);
return -EBUSY;
}
@@ -281,7 +283,7 @@ void hfi1_vnic_sdma_init(struct hfi1_vnic_vport_info *vinfo)
iowait_init(&vnic_sdma->wait, 0, NULL, NULL,
hfi1_vnic_sdma_sleep,
- hfi1_vnic_sdma_wakeup, NULL);
+ hfi1_vnic_sdma_wakeup, NULL, NULL);
vnic_sdma->sde = &vinfo->dd->per_sdma[i];
vnic_sdma->dd = vinfo->dd;
vnic_sdma->vinfo = vinfo;
diff --git a/drivers/infiniband/hw/hns/Kconfig b/drivers/infiniband/hw/hns/Kconfig
index 21c2100b2ea9..fddb5fdf92de 100644
--- a/drivers/infiniband/hw/hns/Kconfig
+++ b/drivers/infiniband/hw/hns/Kconfig
@@ -1,7 +1,6 @@
config INFINIBAND_HNS
tristate "HNS RoCE Driver"
depends on NET_VENDOR_HISILICON
- depends on INFINIBAND_USER_ACCESS || !INFINIBAND_USER_ACCESS
depends on ARM64 || (COMPILE_TEST && 64BIT)
---help---
This is a RoCE/RDMA driver for the Hisilicon RoCE engine. The engine
diff --git a/drivers/infiniband/hw/hns/Makefile b/drivers/infiniband/hw/hns/Makefile
index 004c88b32e13..e2a7f1488f76 100644
--- a/drivers/infiniband/hw/hns/Makefile
+++ b/drivers/infiniband/hw/hns/Makefile
@@ -2,7 +2,7 @@
# Makefile for the Hisilicon RoCE drivers.
#
-ccflags-y := -Idrivers/net/ethernet/hisilicon/hns3
+ccflags-y := -I $(srctree)/drivers/net/ethernet/hisilicon/hns3
obj-$(CONFIG_INFINIBAND_HNS) += hns-roce.o
hns-roce-objs := hns_roce_main.o hns_roce_cmd.o hns_roce_pd.o \
diff --git a/drivers/infiniband/hw/hns/hns_roce_cmd.c b/drivers/infiniband/hw/hns/hns_roce_cmd.c
index a0ba19d4a10e..2acf946d02e5 100644
--- a/drivers/infiniband/hw/hns/hns_roce_cmd.c
+++ b/drivers/infiniband/hw/hns/hns_roce_cmd.c
@@ -176,17 +176,33 @@ int hns_roce_cmd_mbox(struct hns_roce_dev *hr_dev, u64 in_param, u64 out_param,
unsigned long in_modifier, u8 op_modifier, u16 op,
unsigned long timeout)
{
- if (hr_dev->is_reset)
- return 0;
+ int ret;
+
+ if (hr_dev->hw->rst_prc_mbox) {
+ ret = hr_dev->hw->rst_prc_mbox(hr_dev);
+ if (ret == CMD_RST_PRC_SUCCESS)
+ return 0;
+ else if (ret == CMD_RST_PRC_EBUSY)
+ return -EBUSY;
+ }
if (hr_dev->cmd.use_events)
- return hns_roce_cmd_mbox_wait(hr_dev, in_param, out_param,
- in_modifier, op_modifier, op,
- timeout);
+ ret = hns_roce_cmd_mbox_wait(hr_dev, in_param, out_param,
+ in_modifier, op_modifier, op,
+ timeout);
else
- return hns_roce_cmd_mbox_poll(hr_dev, in_param, out_param,
- in_modifier, op_modifier, op,
- timeout);
+ ret = hns_roce_cmd_mbox_poll(hr_dev, in_param, out_param,
+ in_modifier, op_modifier, op,
+ timeout);
+
+ if (ret == CMD_RST_PRC_EBUSY)
+ return -EBUSY;
+
+ if (ret && (hr_dev->hw->rst_prc_mbox &&
+ hr_dev->hw->rst_prc_mbox(hr_dev) == CMD_RST_PRC_SUCCESS))
+ return 0;
+
+ return ret;
}
EXPORT_SYMBOL_GPL(hns_roce_cmd_mbox);
diff --git a/drivers/infiniband/hw/hns/hns_roce_cmd.h b/drivers/infiniband/hw/hns/hns_roce_cmd.h
index 927701df5eff..059fd1da493e 100644
--- a/drivers/infiniband/hw/hns/hns_roce_cmd.h
+++ b/drivers/infiniband/hw/hns/hns_roce_cmd.h
@@ -75,6 +75,10 @@ enum {
HNS_ROCE_CMD_DESTROY_MPT_BT1 = 0x29,
HNS_ROCE_CMD_DESTROY_MPT_BT2 = 0x2a,
+ /* CQC TIMER commands */
+ HNS_ROCE_CMD_WRITE_CQC_TIMER_BT0 = 0x23,
+ HNS_ROCE_CMD_READ_CQC_TIMER_BT0 = 0x27,
+
/* MPT commands */
HNS_ROCE_CMD_QUERY_MPT = 0x62,
@@ -89,6 +93,10 @@ enum {
HNS_ROCE_CMD_DESTROY_SRQC_BT1 = 0x39,
HNS_ROCE_CMD_DESTROY_SRQC_BT2 = 0x3a,
+ /* QPC TIMER commands */
+ HNS_ROCE_CMD_WRITE_QPC_TIMER_BT0 = 0x33,
+ HNS_ROCE_CMD_READ_QPC_TIMER_BT0 = 0x37,
+
/* EQC commands */
HNS_ROCE_CMD_CREATE_AEQC = 0x80,
HNS_ROCE_CMD_MODIFY_AEQC = 0x81,
@@ -98,6 +106,10 @@ enum {
HNS_ROCE_CMD_MODIFY_CEQC = 0x91,
HNS_ROCE_CMD_QUERY_CEQC = 0x92,
HNS_ROCE_CMD_DESTROY_CEQC = 0x93,
+
+ /* SCC CTX BT commands */
+ HNS_ROCE_CMD_READ_SCCC_BT0 = 0xa4,
+ HNS_ROCE_CMD_WRITE_SCCC_BT0 = 0xa5,
};
enum {
diff --git a/drivers/infiniband/hw/hns/hns_roce_cq.c b/drivers/infiniband/hw/hns/hns_roce_cq.c
index 3a485f50fede..1dfe5627006c 100644
--- a/drivers/infiniband/hw/hns/hns_roce_cq.c
+++ b/drivers/infiniband/hw/hns/hns_roce_cq.c
@@ -215,7 +215,7 @@ void hns_roce_free_cq(struct hns_roce_dev *hr_dev, struct hns_roce_cq *hr_cq)
EXPORT_SYMBOL_GPL(hns_roce_free_cq);
static int hns_roce_ib_get_cq_umem(struct hns_roce_dev *hr_dev,
- struct ib_ucontext *context,
+ struct ib_udata *udata,
struct hns_roce_cq_buf *buf,
struct ib_umem **umem, u64 buf_addr, int cqe)
{
@@ -223,7 +223,7 @@ static int hns_roce_ib_get_cq_umem(struct hns_roce_dev *hr_dev,
u32 page_shift;
u32 npages;
- *umem = ib_umem_get(context, buf_addr, cqe * hr_dev->caps.cq_entry_sz,
+ *umem = ib_umem_get(udata, buf_addr, cqe * hr_dev->caps.cq_entry_sz,
IB_ACCESS_LOCAL_WRITE, 1);
if (IS_ERR(*umem))
return PTR_ERR(*umem);
@@ -347,7 +347,7 @@ struct ib_cq *hns_roce_ib_create_cq(struct ib_device *ib_dev,
}
/* Get user space address, write it into mtt table */
- ret = hns_roce_ib_get_cq_umem(hr_dev, context, &hr_cq->hr_buf,
+ ret = hns_roce_ib_get_cq_umem(hr_dev, udata, &hr_cq->hr_buf,
&hr_cq->umem, ucmd.buf_addr,
cq_entries);
if (ret) {
@@ -358,7 +358,8 @@ struct ib_cq *hns_roce_ib_create_cq(struct ib_device *ib_dev,
if ((hr_dev->caps.flags & HNS_ROCE_CAP_FLAG_RECORD_DB) &&
(udata->outlen >= sizeof(resp))) {
ret = hns_roce_db_map_user(to_hr_ucontext(context),
- ucmd.db_addr, &hr_cq->db);
+ udata, ucmd.db_addr,
+ &hr_cq->db);
if (ret) {
dev_err(dev, "cq record doorbell map failed!\n");
goto err_mtt;
diff --git a/drivers/infiniband/hw/hns/hns_roce_db.c b/drivers/infiniband/hw/hns/hns_roce_db.c
index e2f93c1ce86a..0c6c1fe87705 100644
--- a/drivers/infiniband/hw/hns/hns_roce_db.c
+++ b/drivers/infiniband/hw/hns/hns_roce_db.c
@@ -8,7 +8,8 @@
#include <rdma/ib_umem.h>
#include "hns_roce_device.h"
-int hns_roce_db_map_user(struct hns_roce_ucontext *context, unsigned long virt,
+int hns_roce_db_map_user(struct hns_roce_ucontext *context,
+ struct ib_udata *udata, unsigned long virt,
struct hns_roce_db *db)
{
struct hns_roce_user_db_page *page;
@@ -28,8 +29,7 @@ int hns_roce_db_map_user(struct hns_roce_ucontext *context, unsigned long virt,
refcount_set(&page->refcount, 1);
page->user_virt = (virt & PAGE_MASK);
- page->umem = ib_umem_get(&context->ibucontext, virt & PAGE_MASK,
- PAGE_SIZE, 0, 0);
+ page->umem = ib_umem_get(udata, virt & PAGE_MASK, PAGE_SIZE, 0, 0);
if (IS_ERR(page->umem)) {
ret = PTR_ERR(page->umem);
kfree(page);
diff --git a/drivers/infiniband/hw/hns/hns_roce_device.h b/drivers/infiniband/hw/hns/hns_roce_device.h
index 509e467843f6..9ee86daf1700 100644
--- a/drivers/infiniband/hw/hns/hns_roce_device.h
+++ b/drivers/infiniband/hw/hns/hns_roce_device.h
@@ -202,6 +202,7 @@ enum {
HNS_ROCE_CAP_FLAG_SRQ = BIT(5),
HNS_ROCE_CAP_FLAG_MW = BIT(7),
HNS_ROCE_CAP_FLAG_FRMR = BIT(8),
+ HNS_ROCE_CAP_FLAG_QP_FLOW_CTRL = BIT(9),
HNS_ROCE_CAP_FLAG_ATOMIC = BIT(10),
};
@@ -216,6 +217,32 @@ enum {
HNS_ROCE_DB_PER_PAGE = PAGE_SIZE / 4
};
+enum hns_roce_reset_stage {
+ HNS_ROCE_STATE_NON_RST,
+ HNS_ROCE_STATE_RST_BEF_DOWN,
+ HNS_ROCE_STATE_RST_DOWN,
+ HNS_ROCE_STATE_RST_UNINIT,
+ HNS_ROCE_STATE_RST_INIT,
+ HNS_ROCE_STATE_RST_INITED,
+};
+
+enum hns_roce_instance_state {
+ HNS_ROCE_STATE_NON_INIT,
+ HNS_ROCE_STATE_INIT,
+ HNS_ROCE_STATE_INITED,
+ HNS_ROCE_STATE_UNINIT,
+};
+
+enum {
+ HNS_ROCE_RST_DIRECT_RETURN = 0,
+};
+
+enum {
+ CMD_RST_PRC_OTHERS,
+ CMD_RST_PRC_SUCCESS,
+ CMD_RST_PRC_EBUSY,
+};
+
#define HNS_ROCE_CMD_SUCCESS 1
#define HNS_ROCE_PORT_DOWN 0
@@ -482,6 +509,8 @@ struct hns_roce_qp_table {
struct hns_roce_hem_table qp_table;
struct hns_roce_hem_table irrl_table;
struct hns_roce_hem_table trrl_table;
+ struct hns_roce_hem_table sccc_table;
+ struct mutex scc_mutex;
};
struct hns_roce_cq_table {
@@ -729,6 +758,8 @@ struct hns_roce_caps {
u32 max_extend_sg;
int num_qps; /* 256k */
int reserved_qps;
+ int num_qpc_timer;
+ int num_cqc_timer;
u32 max_srq_sg;
int num_srqs;
u32 max_wqes; /* 16k */
@@ -768,6 +799,9 @@ struct hns_roce_caps {
int irrl_entry_sz;
int trrl_entry_sz;
int cqc_entry_sz;
+ int sccc_entry_sz;
+ int qpc_timer_entry_sz;
+ int cqc_timer_entry_sz;
int srqc_entry_sz;
int idx_entry_sz;
u32 pbl_ba_pg_sz;
@@ -777,9 +811,12 @@ struct hns_roce_caps {
int ceqe_depth;
enum ib_mtu max_mtu;
u32 qpc_bt_num;
+ u32 qpc_timer_bt_num;
u32 srqc_bt_num;
u32 cqc_bt_num;
+ u32 cqc_timer_bt_num;
u32 mpt_bt_num;
+ u32 sccc_bt_num;
u32 qpc_ba_pg_sz;
u32 qpc_buf_pg_sz;
u32 qpc_hop_num;
@@ -795,6 +832,15 @@ struct hns_roce_caps {
u32 mtt_ba_pg_sz;
u32 mtt_buf_pg_sz;
u32 mtt_hop_num;
+ u32 sccc_ba_pg_sz;
+ u32 sccc_buf_pg_sz;
+ u32 sccc_hop_num;
+ u32 qpc_timer_ba_pg_sz;
+ u32 qpc_timer_buf_pg_sz;
+ u32 qpc_timer_hop_num;
+ u32 cqc_timer_ba_pg_sz;
+ u32 cqc_timer_buf_pg_sz;
+ u32 cqc_timer_hop_num;
u32 cqe_ba_pg_sz;
u32 cqe_buf_pg_sz;
u32 cqe_hop_num;
@@ -834,6 +880,7 @@ struct hns_roce_hw {
u64 out_param, u32 in_modifier, u8 op_modifier, u16 op,
u16 token, int event);
int (*chk_mbox)(struct hns_roce_dev *hr_dev, unsigned long timeout);
+ int (*rst_prc_mbox)(struct hns_roce_dev *hr_dev);
int (*set_gid)(struct hns_roce_dev *hr_dev, u8 port, int gid_index,
const union ib_gid *gid, const struct ib_gid_attr *attr);
int (*set_mac)(struct hns_roce_dev *hr_dev, u8 phy_port, u8 *addr);
@@ -861,6 +908,8 @@ struct hns_roce_hw {
int attr_mask, enum ib_qp_state cur_state,
enum ib_qp_state new_state);
int (*destroy_qp)(struct ib_qp *ibqp);
+ int (*qp_flow_control_init)(struct hns_roce_dev *hr_dev,
+ struct hns_roce_qp *hr_qp);
int (*post_send)(struct ib_qp *ibqp, const struct ib_send_wr *wr,
const struct ib_send_wr **bad_wr);
int (*post_recv)(struct ib_qp *qp, const struct ib_recv_wr *recv_wr,
@@ -898,6 +947,8 @@ struct hns_roce_dev {
spinlock_t bt_cmd_lock;
bool active;
bool is_reset;
+ bool dis_db;
+ unsigned long reset_cnt;
struct hns_roce_ib_iboe iboe;
struct list_head pgdir_list;
@@ -922,6 +973,8 @@ struct hns_roce_dev {
struct hns_roce_srq_table srq_table;
struct hns_roce_qp_table qp_table;
struct hns_roce_eq_table eq_table;
+ struct hns_roce_hem_table qpc_timer_table;
+ struct hns_roce_hem_table cqc_timer_table;
int cmd_mod;
int loop_idc;
@@ -1061,10 +1114,9 @@ struct ib_ah *hns_roce_create_ah(struct ib_pd *pd,
int hns_roce_query_ah(struct ib_ah *ibah, struct rdma_ah_attr *ah_attr);
int hns_roce_destroy_ah(struct ib_ah *ah, u32 flags);
-struct ib_pd *hns_roce_alloc_pd(struct ib_device *ib_dev,
- struct ib_ucontext *context,
- struct ib_udata *udata);
-int hns_roce_dealloc_pd(struct ib_pd *pd);
+int hns_roce_alloc_pd(struct ib_pd *pd, struct ib_ucontext *context,
+ struct ib_udata *udata);
+void hns_roce_dealloc_pd(struct ib_pd *pd);
struct ib_mr *hns_roce_get_dma_mr(struct ib_pd *pd, int acc);
struct ib_mr *hns_roce_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
@@ -1133,7 +1185,8 @@ struct ib_cq *hns_roce_ib_create_cq(struct ib_device *ib_dev,
int hns_roce_ib_destroy_cq(struct ib_cq *ib_cq);
void hns_roce_free_cq(struct hns_roce_dev *hr_dev, struct hns_roce_cq *hr_cq);
-int hns_roce_db_map_user(struct hns_roce_ucontext *context, unsigned long virt,
+int hns_roce_db_map_user(struct hns_roce_ucontext *context,
+ struct ib_udata *udata, unsigned long virt,
struct hns_roce_db *db);
void hns_roce_db_unmap_user(struct hns_roce_ucontext *context,
struct hns_roce_db *db);
diff --git a/drivers/infiniband/hw/hns/hns_roce_hem.c b/drivers/infiniband/hw/hns/hns_roce_hem.c
index 4cdbcafa5915..f1fec56f3ff4 100644
--- a/drivers/infiniband/hw/hns/hns_roce_hem.c
+++ b/drivers/infiniband/hw/hns/hns_roce_hem.c
@@ -45,6 +45,9 @@ bool hns_roce_check_whether_mhop(struct hns_roce_dev *hr_dev, u32 type)
(hr_dev->caps.mpt_hop_num && type == HEM_TYPE_MTPT) ||
(hr_dev->caps.cqc_hop_num && type == HEM_TYPE_CQC) ||
(hr_dev->caps.srqc_hop_num && type == HEM_TYPE_SRQC) ||
+ (hr_dev->caps.sccc_hop_num && type == HEM_TYPE_SCCC) ||
+ (hr_dev->caps.qpc_timer_hop_num && type == HEM_TYPE_QPC_TIMER) ||
+ (hr_dev->caps.cqc_timer_hop_num && type == HEM_TYPE_CQC_TIMER) ||
(hr_dev->caps.cqe_hop_num && type == HEM_TYPE_CQE) ||
(hr_dev->caps.mtt_hop_num && type == HEM_TYPE_MTT) ||
(hr_dev->caps.srqwqe_hop_num && type == HEM_TYPE_SRQWQE) ||
@@ -125,6 +128,30 @@ int hns_roce_calc_hem_mhop(struct hns_roce_dev *hr_dev,
mhop->ba_l0_num = hr_dev->caps.cqc_bt_num;
mhop->hop_num = hr_dev->caps.cqc_hop_num;
break;
+ case HEM_TYPE_SCCC:
+ mhop->buf_chunk_size = 1 << (hr_dev->caps.sccc_buf_pg_sz
+ + PAGE_SHIFT);
+ mhop->bt_chunk_size = 1 << (hr_dev->caps.sccc_ba_pg_sz
+ + PAGE_SHIFT);
+ mhop->ba_l0_num = hr_dev->caps.sccc_bt_num;
+ mhop->hop_num = hr_dev->caps.sccc_hop_num;
+ break;
+ case HEM_TYPE_QPC_TIMER:
+ mhop->buf_chunk_size = 1 << (hr_dev->caps.qpc_timer_buf_pg_sz
+ + PAGE_SHIFT);
+ mhop->bt_chunk_size = 1 << (hr_dev->caps.qpc_timer_ba_pg_sz
+ + PAGE_SHIFT);
+ mhop->ba_l0_num = hr_dev->caps.qpc_timer_bt_num;
+ mhop->hop_num = hr_dev->caps.qpc_timer_hop_num;
+ break;
+ case HEM_TYPE_CQC_TIMER:
+ mhop->buf_chunk_size = 1 << (hr_dev->caps.cqc_timer_buf_pg_sz
+ + PAGE_SHIFT);
+ mhop->bt_chunk_size = 1 << (hr_dev->caps.cqc_timer_ba_pg_sz
+ + PAGE_SHIFT);
+ mhop->ba_l0_num = hr_dev->caps.cqc_timer_bt_num;
+ mhop->hop_num = hr_dev->caps.cqc_timer_hop_num;
+ break;
case HEM_TYPE_SRQC:
mhop->buf_chunk_size = 1 << (hr_dev->caps.srqc_buf_pg_sz
+ PAGE_SHIFT);
@@ -175,7 +202,7 @@ int hns_roce_calc_hem_mhop(struct hns_roce_dev *hr_dev,
return 0;
/*
- * QPC/MTPT/CQC/SRQC alloc hem for buffer pages.
+ * QPC/MTPT/CQC/SRQC/SCCC alloc hem for buffer pages.
* MTT/CQE alloc hem for bt pages.
*/
bt_num = hns_roce_get_bt_num(table->type, mhop->hop_num);
@@ -486,7 +513,7 @@ static int hns_roce_table_mhop_get(struct hns_roce_dev *hr_dev,
}
/*
- * alloc buffer space chunk for QPC/MTPT/CQC/SRQC.
+ * alloc buffer space chunk for QPC/MTPT/CQC/SRQC/SCCC.
* alloc bt space chunk for MTT/CQE.
*/
size = table->type < HEM_TYPE_MTT ? buf_chunk_size : bt_chunk_size;
@@ -593,6 +620,7 @@ out:
mutex_unlock(&table->mutex);
return ret;
}
+EXPORT_SYMBOL_GPL(hns_roce_table_get);
static void hns_roce_table_mhop_put(struct hns_roce_dev *hr_dev,
struct hns_roce_hem_table *table,
@@ -658,7 +686,7 @@ static void hns_roce_table_mhop_put(struct hns_roce_dev *hr_dev,
}
/*
- * free buffer space chunk for QPC/MTPT/CQC/SRQC.
+ * free buffer space chunk for QPC/MTPT/CQC/SRQC/SCCC.
* free bt space chunk for MTT/CQE.
*/
hns_roce_free_hem(hr_dev, table->hem[hem_idx]);
@@ -735,6 +763,7 @@ void hns_roce_table_put(struct hns_roce_dev *hr_dev,
mutex_unlock(&table->mutex);
}
+EXPORT_SYMBOL_GPL(hns_roce_table_put);
void *hns_roce_table_find(struct hns_roce_dev *hr_dev,
struct hns_roce_hem_table *table,
@@ -904,6 +933,30 @@ int hns_roce_init_hem_table(struct hns_roce_dev *hr_dev,
num_bt_l0 = hr_dev->caps.cqc_bt_num;
hop_num = hr_dev->caps.cqc_hop_num;
break;
+ case HEM_TYPE_SCCC:
+ buf_chunk_size = 1 << (hr_dev->caps.sccc_buf_pg_sz
+ + PAGE_SHIFT);
+ bt_chunk_size = 1 << (hr_dev->caps.sccc_ba_pg_sz
+ + PAGE_SHIFT);
+ num_bt_l0 = hr_dev->caps.sccc_bt_num;
+ hop_num = hr_dev->caps.sccc_hop_num;
+ break;
+ case HEM_TYPE_QPC_TIMER:
+ buf_chunk_size = 1 << (hr_dev->caps.qpc_timer_buf_pg_sz
+ + PAGE_SHIFT);
+ bt_chunk_size = 1 << (hr_dev->caps.qpc_timer_ba_pg_sz
+ + PAGE_SHIFT);
+ num_bt_l0 = hr_dev->caps.qpc_timer_bt_num;
+ hop_num = hr_dev->caps.qpc_timer_hop_num;
+ break;
+ case HEM_TYPE_CQC_TIMER:
+ buf_chunk_size = 1 << (hr_dev->caps.cqc_timer_buf_pg_sz
+ + PAGE_SHIFT);
+ bt_chunk_size = 1 << (hr_dev->caps.cqc_timer_ba_pg_sz
+ + PAGE_SHIFT);
+ num_bt_l0 = hr_dev->caps.cqc_timer_bt_num;
+ hop_num = hr_dev->caps.cqc_timer_hop_num;
+ break;
case HEM_TYPE_SRQC:
buf_chunk_size = 1 << (hr_dev->caps.srqc_buf_pg_sz
+ PAGE_SHIFT);
@@ -1081,6 +1134,15 @@ void hns_roce_cleanup_hem(struct hns_roce_dev *hr_dev)
hns_roce_cleanup_hem_table(hr_dev,
&hr_dev->srq_table.table);
hns_roce_cleanup_hem_table(hr_dev, &hr_dev->cq_table.table);
+ if (hr_dev->caps.qpc_timer_entry_sz)
+ hns_roce_cleanup_hem_table(hr_dev,
+ &hr_dev->qpc_timer_table);
+ if (hr_dev->caps.cqc_timer_entry_sz)
+ hns_roce_cleanup_hem_table(hr_dev,
+ &hr_dev->cqc_timer_table);
+ if (hr_dev->caps.sccc_entry_sz)
+ hns_roce_cleanup_hem_table(hr_dev,
+ &hr_dev->qp_table.sccc_table);
if (hr_dev->caps.trrl_entry_sz)
hns_roce_cleanup_hem_table(hr_dev,
&hr_dev->qp_table.trrl_table);
diff --git a/drivers/infiniband/hw/hns/hns_roce_hem.h b/drivers/infiniband/hw/hns/hns_roce_hem.h
index a650278c6fbd..d9d668992e49 100644
--- a/drivers/infiniband/hw/hns/hns_roce_hem.h
+++ b/drivers/infiniband/hw/hns/hns_roce_hem.h
@@ -44,6 +44,9 @@ enum {
HEM_TYPE_MTPT,
HEM_TYPE_CQC,
HEM_TYPE_SRQC,
+ HEM_TYPE_SCCC,
+ HEM_TYPE_QPC_TIMER,
+ HEM_TYPE_CQC_TIMER,
/* UNMAP HEM */
HEM_TYPE_MTT,
diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v1.c b/drivers/infiniband/hw/hns/hns_roce_hw_v1.c
index b74c742b000c..97515c340134 100644
--- a/drivers/infiniband/hw/hns/hns_roce_hw_v1.c
+++ b/drivers/infiniband/hw/hns/hns_roce_hw_v1.c
@@ -711,13 +711,14 @@ static int hns_roce_v1_rsv_lp_qp(struct hns_roce_dev *hr_dev)
struct ib_qp_attr attr = { 0 };
struct hns_roce_v1_priv *priv;
struct hns_roce_qp *hr_qp;
+ struct ib_device *ibdev;
struct ib_cq *cq;
struct ib_pd *pd;
union ib_gid dgid;
u64 subnet_prefix;
int attr_mask = 0;
+ int ret = -ENOMEM;
int i, j;
- int ret;
u8 queue_en[HNS_ROCE_V1_RESV_QP] = { 0 };
u8 phy_port;
u8 port = 0;
@@ -742,12 +743,16 @@ static int hns_roce_v1_rsv_lp_qp(struct hns_roce_dev *hr_dev)
free_mr->mr_free_cq->ib_cq.cq_context = NULL;
atomic_set(&free_mr->mr_free_cq->ib_cq.usecnt, 0);
- pd = hns_roce_alloc_pd(&hr_dev->ib_dev, NULL, NULL);
- if (IS_ERR(pd)) {
- dev_err(dev, "Create pd for reserved loop qp failed!");
- ret = -ENOMEM;
+ ibdev = &hr_dev->ib_dev;
+ pd = rdma_zalloc_drv_obj(ibdev, ib_pd);
+ if (!pd)
+ goto alloc_mem_failed;
+
+ pd->device = ibdev;
+ ret = hns_roce_alloc_pd(pd, NULL, NULL);
+ if (ret)
goto alloc_pd_failed;
- }
+
free_mr->mr_free_pd = to_hr_pd(pd);
free_mr->mr_free_pd->ibpd.device = &hr_dev->ib_dev;
free_mr->mr_free_pd->ibpd.uobject = NULL;
@@ -854,10 +859,12 @@ create_lp_qp_failed:
dev_err(dev, "Destroy qp %d for mr free failed!\n", i);
}
- if (hns_roce_dealloc_pd(pd))
- dev_err(dev, "Destroy pd for create_lp_qp failed!\n");
+ hns_roce_dealloc_pd(pd);
alloc_pd_failed:
+ kfree(pd);
+
+alloc_mem_failed:
if (hns_roce_ib_destroy_cq(cq))
dev_err(dev, "Destroy cq for create_lp_qp failed!\n");
@@ -891,9 +898,7 @@ static void hns_roce_v1_release_lp_qp(struct hns_roce_dev *hr_dev)
if (ret)
dev_err(dev, "Destroy cq for mr_free failed(%d)!\n", ret);
- ret = hns_roce_dealloc_pd(&free_mr->mr_free_pd->ibpd);
- if (ret)
- dev_err(dev, "Destroy pd for mr_free failed(%d)!\n", ret);
+ hns_roce_dealloc_pd(&free_mr->mr_free_pd->ibpd);
}
static int hns_roce_db_init(struct hns_roce_dev *hr_dev)
@@ -1866,9 +1871,8 @@ static int hns_roce_v1_write_mtpt(void *mb_buf, struct hns_roce_mr *mr,
unsigned long mtpt_idx)
{
struct hns_roce_v1_mpt_entry *mpt_entry;
- struct scatterlist *sg;
+ struct sg_dma_page_iter sg_iter;
u64 *pages;
- int entry;
int i;
/* MPT filled into mailbox buf */
@@ -1923,8 +1927,8 @@ static int hns_roce_v1_write_mtpt(void *mb_buf, struct hns_roce_mr *mr,
return -ENOMEM;
i = 0;
- for_each_sg(mr->umem->sg_head.sgl, sg, mr->umem->nmap, entry) {
- pages[i] = ((u64)sg_dma_address(sg)) >> 12;
+ for_each_sg_dma_page(mr->umem->sg_head.sgl, &sg_iter, mr->umem->nmap, 0) {
+ pages[i] = ((u64)sg_page_iter_dma_address(&sg_iter)) >> 12;
/* Directly record to MTPT table firstly 7 entry */
if (i >= HNS_ROCE_MAX_INNER_MTPT_NUM)
@@ -5002,7 +5006,7 @@ static int hns_roce_probe(struct platform_device *pdev)
struct hns_roce_dev *hr_dev;
struct device *dev = &pdev->dev;
- hr_dev = (struct hns_roce_dev *)ib_alloc_device(sizeof(*hr_dev));
+ hr_dev = ib_alloc_device(hns_roce_dev, ib_dev);
if (!hr_dev)
return -ENOMEM;
diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c
index 543fa1504cd3..1c54390e1c85 100644
--- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c
+++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c
@@ -587,7 +587,7 @@ out:
roce_set_field(sq_db.parameter, V2_DB_PARAMETER_SL_M,
V2_DB_PARAMETER_SL_S, qp->sl);
- hns_roce_write64_k((__le32 *)&sq_db, qp->sq.db_reg_l);
+ hns_roce_write64(hr_dev, (__le32 *)&sq_db, qp->sq.db_reg_l);
qp->sq_next_wqe = ind;
qp->next_sge = sge_ind;
@@ -712,6 +712,113 @@ out:
return ret;
}
+static int hns_roce_v2_cmd_hw_reseted(struct hns_roce_dev *hr_dev,
+ unsigned long instance_stage,
+ unsigned long reset_stage)
+{
+ /* When hardware reset has been completed once or more, we should stop
+ * sending mailbox&cmq&doorbell to hardware. If now in .init_instance()
+ * function, we should exit with error. If now at HNAE3_INIT_CLIENT
+ * stage of soft reset process, we should exit with error, and then
+ * HNAE3_INIT_CLIENT related process can rollback the operation like
+ * notifing hardware to free resources, HNAE3_INIT_CLIENT related
+ * process will exit with error to notify NIC driver to reschedule soft
+ * reset process once again.
+ */
+ hr_dev->is_reset = true;
+ hr_dev->dis_db = true;
+
+ if (reset_stage == HNS_ROCE_STATE_RST_INIT ||
+ instance_stage == HNS_ROCE_STATE_INIT)
+ return CMD_RST_PRC_EBUSY;
+
+ return CMD_RST_PRC_SUCCESS;
+}
+
+static int hns_roce_v2_cmd_hw_resetting(struct hns_roce_dev *hr_dev,
+ unsigned long instance_stage,
+ unsigned long reset_stage)
+{
+ struct hns_roce_v2_priv *priv = (struct hns_roce_v2_priv *)hr_dev->priv;
+ struct hnae3_handle *handle = priv->handle;
+ const struct hnae3_ae_ops *ops = handle->ae_algo->ops;
+
+ /* When hardware reset is detected, we should stop sending mailbox&cmq&
+ * doorbell to hardware. If now in .init_instance() function, we should
+ * exit with error. If now at HNAE3_INIT_CLIENT stage of soft reset
+ * process, we should exit with error, and then HNAE3_INIT_CLIENT
+ * related process can rollback the operation like notifing hardware to
+ * free resources, HNAE3_INIT_CLIENT related process will exit with
+ * error to notify NIC driver to reschedule soft reset process once
+ * again.
+ */
+ hr_dev->dis_db = true;
+ if (!ops->get_hw_reset_stat(handle))
+ hr_dev->is_reset = true;
+
+ if (!hr_dev->is_reset || reset_stage == HNS_ROCE_STATE_RST_INIT ||
+ instance_stage == HNS_ROCE_STATE_INIT)
+ return CMD_RST_PRC_EBUSY;
+
+ return CMD_RST_PRC_SUCCESS;
+}
+
+static int hns_roce_v2_cmd_sw_resetting(struct hns_roce_dev *hr_dev)
+{
+ struct hns_roce_v2_priv *priv = (struct hns_roce_v2_priv *)hr_dev->priv;
+ struct hnae3_handle *handle = priv->handle;
+ const struct hnae3_ae_ops *ops = handle->ae_algo->ops;
+
+ /* When software reset is detected at .init_instance() function, we
+ * should stop sending mailbox&cmq&doorbell to hardware, and exit
+ * with error.
+ */
+ hr_dev->dis_db = true;
+ if (ops->ae_dev_reset_cnt(handle) != hr_dev->reset_cnt)
+ hr_dev->is_reset = true;
+
+ return CMD_RST_PRC_EBUSY;
+}
+
+static int hns_roce_v2_rst_process_cmd(struct hns_roce_dev *hr_dev)
+{
+ struct hns_roce_v2_priv *priv = (struct hns_roce_v2_priv *)hr_dev->priv;
+ struct hnae3_handle *handle = priv->handle;
+ const struct hnae3_ae_ops *ops = handle->ae_algo->ops;
+ unsigned long instance_stage; /* the current instance stage */
+ unsigned long reset_stage; /* the current reset stage */
+ unsigned long reset_cnt;
+ bool sw_resetting;
+ bool hw_resetting;
+
+ if (hr_dev->is_reset)
+ return CMD_RST_PRC_SUCCESS;
+
+ /* Get information about reset from NIC driver or RoCE driver itself,
+ * the meaning of the following variables from NIC driver are described
+ * as below:
+ * reset_cnt -- The count value of completed hardware reset.
+ * hw_resetting -- Whether hardware device is resetting now.
+ * sw_resetting -- Whether NIC's software reset process is running now.
+ */
+ instance_stage = handle->rinfo.instance_state;
+ reset_stage = handle->rinfo.reset_state;
+ reset_cnt = ops->ae_dev_reset_cnt(handle);
+ hw_resetting = ops->get_hw_reset_stat(handle);
+ sw_resetting = ops->ae_dev_resetting(handle);
+
+ if (reset_cnt != hr_dev->reset_cnt)
+ return hns_roce_v2_cmd_hw_reseted(hr_dev, instance_stage,
+ reset_stage);
+ else if (hw_resetting)
+ return hns_roce_v2_cmd_hw_resetting(hr_dev, instance_stage,
+ reset_stage);
+ else if (sw_resetting && instance_stage == HNS_ROCE_STATE_INIT)
+ return hns_roce_v2_cmd_sw_resetting(hr_dev);
+
+ return 0;
+}
+
static int hns_roce_cmq_space(struct hns_roce_v2_cmq_ring *ring)
{
int ntu = ring->next_to_use;
@@ -892,8 +999,8 @@ static int hns_roce_cmq_csq_clean(struct hns_roce_dev *hr_dev)
return clean;
}
-static int hns_roce_cmq_send(struct hns_roce_dev *hr_dev,
- struct hns_roce_cmq_desc *desc, int num)
+static int __hns_roce_cmq_send(struct hns_roce_dev *hr_dev,
+ struct hns_roce_cmq_desc *desc, int num)
{
struct hns_roce_v2_priv *priv = (struct hns_roce_v2_priv *)hr_dev->priv;
struct hns_roce_v2_cmq_ring *csq = &priv->cmq.csq;
@@ -905,9 +1012,6 @@ static int hns_roce_cmq_send(struct hns_roce_dev *hr_dev,
int ret = 0;
int ntc;
- if (hr_dev->is_reset)
- return 0;
-
spin_lock_bh(&csq->lock);
if (num > hns_roce_cmq_space(csq)) {
@@ -982,6 +1086,30 @@ static int hns_roce_cmq_send(struct hns_roce_dev *hr_dev,
return ret;
}
+int hns_roce_cmq_send(struct hns_roce_dev *hr_dev,
+ struct hns_roce_cmq_desc *desc, int num)
+{
+ int retval;
+ int ret;
+
+ ret = hns_roce_v2_rst_process_cmd(hr_dev);
+ if (ret == CMD_RST_PRC_SUCCESS)
+ return 0;
+ if (ret == CMD_RST_PRC_EBUSY)
+ return ret;
+
+ ret = __hns_roce_cmq_send(hr_dev, desc, num);
+ if (ret) {
+ retval = hns_roce_v2_rst_process_cmd(hr_dev);
+ if (retval == CMD_RST_PRC_SUCCESS)
+ return 0;
+ else if (retval == CMD_RST_PRC_EBUSY)
+ return retval;
+ }
+
+ return ret;
+}
+
static int hns_roce_cmq_query_hw_info(struct hns_roce_dev *hr_dev)
{
struct hns_roce_query_version *resp;
@@ -1078,6 +1206,44 @@ static int hns_roce_query_pf_resource(struct hns_roce_dev *hr_dev)
hr_dev->caps.sl_num = roce_get_field(req_b->qid_idx_sl_num,
PF_RES_DATA_3_PF_SL_NUM_M,
PF_RES_DATA_3_PF_SL_NUM_S);
+ hr_dev->caps.sccc_bt_num = roce_get_field(req_b->sccc_bt_idx_num,
+ PF_RES_DATA_4_PF_SCCC_BT_NUM_M,
+ PF_RES_DATA_4_PF_SCCC_BT_NUM_S);
+
+ return 0;
+}
+
+static int hns_roce_query_pf_timer_resource(struct hns_roce_dev *hr_dev)
+{
+ struct hns_roce_pf_timer_res_a *req_a;
+ struct hns_roce_cmq_desc desc[2];
+ int ret, i;
+
+ for (i = 0; i < 2; i++) {
+ hns_roce_cmq_setup_basic_desc(&desc[i],
+ HNS_ROCE_OPC_QUERY_PF_TIMER_RES,
+ true);
+
+ if (i == 0)
+ desc[i].flag |= cpu_to_le16(HNS_ROCE_CMD_FLAG_NEXT);
+ else
+ desc[i].flag &= ~cpu_to_le16(HNS_ROCE_CMD_FLAG_NEXT);
+ }
+
+ ret = hns_roce_cmq_send(hr_dev, desc, 2);
+ if (ret)
+ return ret;
+
+ req_a = (struct hns_roce_pf_timer_res_a *)desc[0].data;
+
+ hr_dev->caps.qpc_timer_bt_num =
+ roce_get_field(req_a->qpc_timer_bt_idx_num,
+ PF_RES_DATA_1_PF_QPC_TIMER_BT_NUM_M,
+ PF_RES_DATA_1_PF_QPC_TIMER_BT_NUM_S);
+ hr_dev->caps.cqc_timer_bt_num =
+ roce_get_field(req_a->cqc_timer_bt_idx_num,
+ PF_RES_DATA_2_PF_CQC_TIMER_BT_NUM_M,
+ PF_RES_DATA_2_PF_CQC_TIMER_BT_NUM_S);
return 0;
}
@@ -1193,6 +1359,14 @@ static int hns_roce_alloc_vf_resource(struct hns_roce_dev *hr_dev)
VF_RES_B_DATA_3_VF_SL_NUM_M,
VF_RES_B_DATA_3_VF_SL_NUM_S,
HNS_ROCE_VF_SL_NUM);
+
+ roce_set_field(req_b->vf_sccc_idx_num,
+ VF_RES_B_DATA_4_VF_SCCC_BT_IDX_M,
+ VF_RES_B_DATA_4_VF_SCCC_BT_IDX_S, 0);
+ roce_set_field(req_b->vf_sccc_idx_num,
+ VF_RES_B_DATA_4_VF_SCCC_BT_NUM_M,
+ VF_RES_B_DATA_4_VF_SCCC_BT_NUM_S,
+ HNS_ROCE_VF_SCCC_BT_NUM);
}
}
@@ -1205,6 +1379,7 @@ static int hns_roce_v2_set_bt(struct hns_roce_dev *hr_dev)
u8 qpc_hop_num = hr_dev->caps.qpc_hop_num;
u8 cqc_hop_num = hr_dev->caps.cqc_hop_num;
u8 mpt_hop_num = hr_dev->caps.mpt_hop_num;
+ u8 sccc_hop_num = hr_dev->caps.sccc_hop_num;
struct hns_roce_cfg_bt_attr *req;
struct hns_roce_cmq_desc desc;
@@ -1252,6 +1427,20 @@ static int hns_roce_v2_set_bt(struct hns_roce_dev *hr_dev)
CFG_BT_ATTR_DATA_3_VF_MPT_HOPNUM_S,
mpt_hop_num == HNS_ROCE_HOP_NUM_0 ? 0 : mpt_hop_num);
+ roce_set_field(req->vf_sccc_cfg,
+ CFG_BT_ATTR_DATA_4_VF_SCCC_BA_PGSZ_M,
+ CFG_BT_ATTR_DATA_4_VF_SCCC_BA_PGSZ_S,
+ hr_dev->caps.sccc_ba_pg_sz + PG_SHIFT_OFFSET);
+ roce_set_field(req->vf_sccc_cfg,
+ CFG_BT_ATTR_DATA_4_VF_SCCC_BUF_PGSZ_M,
+ CFG_BT_ATTR_DATA_4_VF_SCCC_BUF_PGSZ_S,
+ hr_dev->caps.sccc_buf_pg_sz + PG_SHIFT_OFFSET);
+ roce_set_field(req->vf_sccc_cfg,
+ CFG_BT_ATTR_DATA_4_VF_SCCC_HOPNUM_M,
+ CFG_BT_ATTR_DATA_4_VF_SCCC_HOPNUM_S,
+ sccc_hop_num ==
+ HNS_ROCE_HOP_NUM_0 ? 0 : sccc_hop_num);
+
return hns_roce_cmq_send(hr_dev, &desc, 1);
}
@@ -1289,6 +1478,16 @@ static int hns_roce_v2_profile(struct hns_roce_dev *hr_dev)
return ret;
}
+ if (hr_dev->pci_dev->revision == 0x21) {
+ ret = hns_roce_query_pf_timer_resource(hr_dev);
+ if (ret) {
+ dev_err(hr_dev->dev,
+ "Query pf timer resource fail, ret = %d.\n",
+ ret);
+ return ret;
+ }
+ }
+
ret = hns_roce_alloc_vf_resource(hr_dev);
if (ret) {
dev_err(hr_dev->dev, "Allocate vf resource fail, ret = %d.\n",
@@ -1313,6 +1512,7 @@ static int hns_roce_v2_profile(struct hns_roce_dev *hr_dev)
caps->max_wqes = HNS_ROCE_V2_MAX_WQE_NUM;
caps->num_cqs = HNS_ROCE_V2_MAX_CQ_NUM;
caps->num_srqs = HNS_ROCE_V2_MAX_SRQ_NUM;
+ caps->min_cqes = HNS_ROCE_MIN_CQE_NUM;
caps->max_cqes = HNS_ROCE_V2_MAX_CQE_NUM;
caps->max_srqwqes = HNS_ROCE_V2_MAX_SRQWQE_NUM;
caps->max_sq_sg = HNS_ROCE_V2_MAX_SQ_SGE_NUM;
@@ -1366,7 +1566,7 @@ static int hns_roce_v2_profile(struct hns_roce_dev *hr_dev)
caps->mpt_ba_pg_sz = 0;
caps->mpt_buf_pg_sz = 0;
caps->mpt_hop_num = HNS_ROCE_CONTEXT_HOP_NUM;
- caps->pbl_ba_pg_sz = 0;
+ caps->pbl_ba_pg_sz = 2;
caps->pbl_buf_pg_sz = 0;
caps->pbl_hop_num = HNS_ROCE_PBL_HOP_NUM;
caps->mtt_ba_pg_sz = 0;
@@ -1408,9 +1608,27 @@ static int hns_roce_v2_profile(struct hns_roce_dev *hr_dev)
caps->max_srq_wrs = HNS_ROCE_V2_MAX_SRQ_WR;
caps->max_srq_sges = HNS_ROCE_V2_MAX_SRQ_SGE;
- if (hr_dev->pci_dev->revision == 0x21)
+ if (hr_dev->pci_dev->revision == 0x21) {
caps->flags |= HNS_ROCE_CAP_FLAG_ATOMIC |
- HNS_ROCE_CAP_FLAG_SRQ;
+ HNS_ROCE_CAP_FLAG_SRQ |
+ HNS_ROCE_CAP_FLAG_QP_FLOW_CTRL;
+
+ caps->num_qpc_timer = HNS_ROCE_V2_MAX_QPC_TIMER_NUM;
+ caps->qpc_timer_entry_sz = HNS_ROCE_V2_QPC_TIMER_ENTRY_SZ;
+ caps->qpc_timer_ba_pg_sz = 0;
+ caps->qpc_timer_buf_pg_sz = 0;
+ caps->qpc_timer_hop_num = HNS_ROCE_HOP_NUM_0;
+ caps->num_cqc_timer = HNS_ROCE_V2_MAX_CQC_TIMER_NUM;
+ caps->cqc_timer_entry_sz = HNS_ROCE_V2_CQC_TIMER_ENTRY_SZ;
+ caps->cqc_timer_ba_pg_sz = 0;
+ caps->cqc_timer_buf_pg_sz = 0;
+ caps->cqc_timer_hop_num = HNS_ROCE_HOP_NUM_0;
+
+ caps->sccc_entry_sz = HNS_ROCE_V2_SCCC_ENTRY_SZ;
+ caps->sccc_ba_pg_sz = 0;
+ caps->sccc_buf_pg_sz = 0;
+ caps->sccc_hop_num = HNS_ROCE_SCCC_HOP_NUM;
+ }
ret = hns_roce_v2_set_bt(hr_dev);
if (ret)
@@ -1611,7 +1829,8 @@ static void hns_roce_free_link_table(struct hns_roce_dev *hr_dev,
static int hns_roce_v2_init(struct hns_roce_dev *hr_dev)
{
struct hns_roce_v2_priv *priv = hr_dev->priv;
- int ret;
+ int qpc_count, cqc_count;
+ int ret, i;
/* TSQ includes SQ doorbell and ack doorbell */
ret = hns_roce_init_link_table(hr_dev, TSQ_LINK_TABLE);
@@ -1626,8 +1845,40 @@ static int hns_roce_v2_init(struct hns_roce_dev *hr_dev)
goto err_tpq_init_failed;
}
+ /* Alloc memory for QPC Timer buffer space chunk*/
+ for (qpc_count = 0; qpc_count < hr_dev->caps.qpc_timer_bt_num;
+ qpc_count++) {
+ ret = hns_roce_table_get(hr_dev, &hr_dev->qpc_timer_table,
+ qpc_count);
+ if (ret) {
+ dev_err(hr_dev->dev, "QPC Timer get failed\n");
+ goto err_qpc_timer_failed;
+ }
+ }
+
+ /* Alloc memory for CQC Timer buffer space chunk*/
+ for (cqc_count = 0; cqc_count < hr_dev->caps.cqc_timer_bt_num;
+ cqc_count++) {
+ ret = hns_roce_table_get(hr_dev, &hr_dev->cqc_timer_table,
+ cqc_count);
+ if (ret) {
+ dev_err(hr_dev->dev, "CQC Timer get failed\n");
+ goto err_cqc_timer_failed;
+ }
+ }
+
return 0;
+err_cqc_timer_failed:
+ for (i = 0; i < cqc_count; i++)
+ hns_roce_table_put(hr_dev, &hr_dev->cqc_timer_table, i);
+
+err_qpc_timer_failed:
+ for (i = 0; i < qpc_count; i++)
+ hns_roce_table_put(hr_dev, &hr_dev->qpc_timer_table, i);
+
+ hns_roce_free_link_table(hr_dev, &priv->tpq);
+
err_tpq_init_failed:
hns_roce_free_link_table(hr_dev, &priv->tsq);
@@ -1735,6 +1986,9 @@ static int hns_roce_v2_chk_mbox(struct hns_roce_dev *hr_dev,
status = hns_roce_v2_cmd_complete(hr_dev);
if (status != 0x1) {
+ if (status == CMD_RST_PRC_EBUSY)
+ return status;
+
dev_err(dev, "mailbox status 0x%x!\n", status);
return -EBUSY;
}
@@ -1831,12 +2085,10 @@ static int hns_roce_v2_set_mac(struct hns_roce_dev *hr_dev, u8 phy_port,
static int set_mtpt_pbl(struct hns_roce_v2_mpt_entry *mpt_entry,
struct hns_roce_mr *mr)
{
- struct scatterlist *sg;
+ struct sg_dma_page_iter sg_iter;
u64 page_addr;
u64 *pages;
- int i, j;
- int len;
- int entry;
+ int i;
mpt_entry->pbl_size = cpu_to_le32(mr->pbl_size);
mpt_entry->pbl_ba_l = cpu_to_le32(lower_32_bits(mr->pbl_ba >> 3));
@@ -1849,17 +2101,14 @@ static int set_mtpt_pbl(struct hns_roce_v2_mpt_entry *mpt_entry,
return -ENOMEM;
i = 0;
- for_each_sg(mr->umem->sg_head.sgl, sg, mr->umem->nmap, entry) {
- len = sg_dma_len(sg) >> PAGE_SHIFT;
- for (j = 0; j < len; ++j) {
- page_addr = sg_dma_address(sg) +
- (j << mr->umem->page_shift);
- pages[i] = page_addr >> 6;
- /* Record the first 2 entry directly to MTPT table */
- if (i >= HNS_ROCE_V2_MAX_INNER_MTPT_NUM - 1)
- goto found;
- i++;
- }
+ for_each_sg_dma_page(mr->umem->sg_head.sgl, &sg_iter, mr->umem->nmap, 0) {
+ page_addr = sg_page_iter_dma_address(&sg_iter);
+ pages[i] = page_addr >> 6;
+
+ /* Record the first 2 entry directly to MTPT table */
+ if (i >= HNS_ROCE_V2_MAX_INNER_MTPT_NUM - 1)
+ goto found;
+ i++;
}
found:
mpt_entry->pa0_l = cpu_to_le32(lower_32_bits(pages[0]));
@@ -1941,6 +2190,9 @@ static int hns_roce_v2_rereg_write_mtpt(struct hns_roce_dev *hr_dev,
struct hns_roce_v2_mpt_entry *mpt_entry = mb_buf;
int ret = 0;
+ roce_set_field(mpt_entry->byte_4_pd_hop_st, V2_MPT_BYTE_4_MPT_ST_M,
+ V2_MPT_BYTE_4_MPT_ST_S, V2_MPT_ST_VALID);
+
if (flags & IB_MR_REREG_PD) {
roce_set_field(mpt_entry->byte_4_pd_hop_st, V2_MPT_BYTE_4_PD_M,
V2_MPT_BYTE_4_PD_S, pdn);
@@ -2245,6 +2497,7 @@ static void hns_roce_v2_write_cqc(struct hns_roce_dev *hr_dev,
static int hns_roce_v2_req_notify_cq(struct ib_cq *ibcq,
enum ib_cq_notify_flags flags)
{
+ struct hns_roce_dev *hr_dev = to_hr_dev(ibcq->device);
struct hns_roce_cq *hr_cq = to_hr_cq(ibcq);
u32 notification_flag;
u32 doorbell[2];
@@ -2270,7 +2523,7 @@ static int hns_roce_v2_req_notify_cq(struct ib_cq *ibcq,
roce_set_bit(doorbell[1], V2_CQ_DB_PARAMETER_NOTIFY_S,
notification_flag);
- hns_roce_write64_k(doorbell, hr_cq->cq_db_l);
+ hns_roce_write64(hr_dev, doorbell, hr_cq->cq_db_l);
return 0;
}
@@ -2663,17 +2916,33 @@ static int hns_roce_v2_set_hem(struct hns_roce_dev *hr_dev,
case HEM_TYPE_SRQC:
op = HNS_ROCE_CMD_WRITE_SRQC_BT0;
break;
+ case HEM_TYPE_SCCC:
+ op = HNS_ROCE_CMD_WRITE_SCCC_BT0;
+ break;
+ case HEM_TYPE_QPC_TIMER:
+ op = HNS_ROCE_CMD_WRITE_QPC_TIMER_BT0;
+ break;
+ case HEM_TYPE_CQC_TIMER:
+ op = HNS_ROCE_CMD_WRITE_CQC_TIMER_BT0;
+ break;
default:
dev_warn(dev, "Table %d not to be written by mailbox!\n",
table->type);
return 0;
}
+
+ if (table->type == HEM_TYPE_SCCC && step_idx)
+ return 0;
+
op += step_idx;
mailbox = hns_roce_alloc_cmd_mailbox(hr_dev);
if (IS_ERR(mailbox))
return PTR_ERR(mailbox);
+ if (table->type == HEM_TYPE_SCCC)
+ obj = mhop.l0_idx;
+
if (check_whether_last_step(hop_num, step_idx)) {
hem = table->hem[hem_idx];
for (hns_roce_hem_first(hem, &iter);
@@ -2722,6 +2991,10 @@ static int hns_roce_v2_clear_hem(struct hns_roce_dev *hr_dev,
case HEM_TYPE_CQC:
op = HNS_ROCE_CMD_DESTROY_CQC_BT0;
break;
+ case HEM_TYPE_SCCC:
+ case HEM_TYPE_QPC_TIMER:
+ case HEM_TYPE_CQC_TIMER:
+ break;
case HEM_TYPE_SRQC:
op = HNS_ROCE_CMD_DESTROY_SRQC_BT0;
break;
@@ -2730,6 +3003,12 @@ static int hns_roce_v2_clear_hem(struct hns_roce_dev *hr_dev,
table->type);
return 0;
}
+
+ if (table->type == HEM_TYPE_SCCC ||
+ table->type == HEM_TYPE_QPC_TIMER ||
+ table->type == HEM_TYPE_CQC_TIMER)
+ return 0;
+
op += step_idx;
mailbox = hns_roce_alloc_cmd_mailbox(hr_dev);
@@ -3686,10 +3965,16 @@ static int modify_qp_rtr_to_rts(struct ib_qp *ibqp,
V2_QPC_BYTE_212_LSN_S, 0);
if (attr_mask & IB_QP_TIMEOUT) {
- roce_set_field(context->byte_28_at_fl, V2_QPC_BYTE_28_AT_M,
- V2_QPC_BYTE_28_AT_S, attr->timeout);
- roce_set_field(qpc_mask->byte_28_at_fl, V2_QPC_BYTE_28_AT_M,
- V2_QPC_BYTE_28_AT_S, 0);
+ if (attr->timeout < 31) {
+ roce_set_field(context->byte_28_at_fl,
+ V2_QPC_BYTE_28_AT_M, V2_QPC_BYTE_28_AT_S,
+ attr->timeout);
+ roce_set_field(qpc_mask->byte_28_at_fl,
+ V2_QPC_BYTE_28_AT_M, V2_QPC_BYTE_28_AT_S,
+ 0);
+ } else {
+ dev_warn(dev, "Local ACK timeout shall be 0 to 30.\n");
+ }
}
roce_set_field(context->byte_172_sq_psn, V2_QPC_BYTE_172_SQ_CUR_PSN_M,
@@ -3742,7 +4027,7 @@ static int hns_roce_v2_modify_qp(struct ib_qp *ibqp,
struct device *dev = hr_dev->dev;
int ret = -EINVAL;
- context = kcalloc(2, sizeof(*context), GFP_KERNEL);
+ context = kcalloc(2, sizeof(*context), GFP_ATOMIC);
if (!context)
return -ENOMEM;
@@ -3789,13 +4074,16 @@ static int hns_roce_v2_modify_qp(struct ib_qp *ibqp,
roce_set_field(qpc_mask->byte_160_sq_ci_pi,
V2_QPC_BYTE_160_SQ_PRODUCER_IDX_M,
V2_QPC_BYTE_160_SQ_PRODUCER_IDX_S, 0);
- roce_set_field(context->byte_84_rq_ci_pi,
+
+ if (!ibqp->srq) {
+ roce_set_field(context->byte_84_rq_ci_pi,
V2_QPC_BYTE_84_RQ_PRODUCER_IDX_M,
V2_QPC_BYTE_84_RQ_PRODUCER_IDX_S,
hr_qp->rq.head);
- roce_set_field(qpc_mask->byte_84_rq_ci_pi,
+ roce_set_field(qpc_mask->byte_84_rq_ci_pi,
V2_QPC_BYTE_84_RQ_PRODUCER_IDX_M,
V2_QPC_BYTE_84_RQ_PRODUCER_IDX_S, 0);
+ }
}
if (attr_mask & IB_QP_AV) {
@@ -4224,6 +4512,59 @@ static int hns_roce_v2_destroy_qp(struct ib_qp *ibqp)
return 0;
}
+static int hns_roce_v2_qp_flow_control_init(struct hns_roce_dev *hr_dev,
+ struct hns_roce_qp *hr_qp)
+{
+ struct hns_roce_sccc_clr_done *resp;
+ struct hns_roce_sccc_clr *clr;
+ struct hns_roce_cmq_desc desc;
+ int ret, i;
+
+ mutex_lock(&hr_dev->qp_table.scc_mutex);
+
+ /* set scc ctx clear done flag */
+ hns_roce_cmq_setup_basic_desc(&desc, HNS_ROCE_OPC_RESET_SCCC, false);
+ ret = hns_roce_cmq_send(hr_dev, &desc, 1);
+ if (ret) {
+ dev_err(hr_dev->dev, "Reset SCC ctx failed(%d)\n", ret);
+ goto out;
+ }
+
+ /* clear scc context */
+ hns_roce_cmq_setup_basic_desc(&desc, HNS_ROCE_OPC_CLR_SCCC, false);
+ clr = (struct hns_roce_sccc_clr *)desc.data;
+ clr->qpn = cpu_to_le32(hr_qp->qpn);
+ ret = hns_roce_cmq_send(hr_dev, &desc, 1);
+ if (ret) {
+ dev_err(hr_dev->dev, "Clear SCC ctx failed(%d)\n", ret);
+ goto out;
+ }
+
+ /* query scc context clear is done or not */
+ resp = (struct hns_roce_sccc_clr_done *)desc.data;
+ for (i = 0; i <= HNS_ROCE_CMQ_SCC_CLR_DONE_CNT; i++) {
+ hns_roce_cmq_setup_basic_desc(&desc,
+ HNS_ROCE_OPC_QUERY_SCCC, true);
+ ret = hns_roce_cmq_send(hr_dev, &desc, 1);
+ if (ret) {
+ dev_err(hr_dev->dev, "Query clr cmq failed(%d)\n", ret);
+ goto out;
+ }
+
+ if (resp->clr_done)
+ goto out;
+
+ msleep(20);
+ }
+
+ dev_err(hr_dev->dev, "Query SCC clr done flag overtime.\n");
+ ret = -ETIMEDOUT;
+
+out:
+ mutex_unlock(&hr_dev->qp_table.scc_mutex);
+ return ret;
+}
+
static int hns_roce_v2_modify_cq(struct ib_cq *cq, u16 cq_count, u16 cq_period)
{
struct hns_roce_dev *hr_dev = to_hr_dev(cq->device);
@@ -4281,7 +4622,8 @@ static void hns_roce_set_qps_to_err(struct hns_roce_dev *hr_dev, u32 qpn)
if (hr_qp->ibqp.uobject) {
if (hr_qp->sdb_en == 1) {
hr_qp->sq.head = *(int *)(hr_qp->sdb.virt_addr);
- hr_qp->rq.head = *(int *)(hr_qp->rdb.virt_addr);
+ if (hr_qp->rdb_en == 1)
+ hr_qp->rq.head = *(int *)(hr_qp->rdb.virt_addr);
} else {
dev_warn(hr_dev->dev, "flush cqe is unsupported in userspace!\n");
return;
@@ -4319,64 +4661,19 @@ static void hns_roce_irq_work_handle(struct work_struct *work)
dev_warn(dev, "Send queue drained.\n");
break;
case HNS_ROCE_EVENT_TYPE_WQ_CATAS_ERROR:
- dev_err(dev, "Local work queue catastrophic error.\n");
+ dev_err(dev, "Local work queue 0x%x catas error, sub_type:%d\n",
+ qpn, irq_work->sub_type);
hns_roce_set_qps_to_err(irq_work->hr_dev, qpn);
- switch (irq_work->sub_type) {
- case HNS_ROCE_LWQCE_QPC_ERROR:
- dev_err(dev, "QP %d, QPC error.\n", qpn);
- break;
- case HNS_ROCE_LWQCE_MTU_ERROR:
- dev_err(dev, "QP %d, MTU error.\n", qpn);
- break;
- case HNS_ROCE_LWQCE_WQE_BA_ADDR_ERROR:
- dev_err(dev, "QP %d, WQE BA addr error.\n", qpn);
- break;
- case HNS_ROCE_LWQCE_WQE_ADDR_ERROR:
- dev_err(dev, "QP %d, WQE addr error.\n", qpn);
- break;
- case HNS_ROCE_LWQCE_SQ_WQE_SHIFT_ERROR:
- dev_err(dev, "QP %d, WQE shift error.\n", qpn);
- break;
- default:
- dev_err(dev, "Unhandled sub_event type %d.\n",
- irq_work->sub_type);
- break;
- }
break;
case HNS_ROCE_EVENT_TYPE_INV_REQ_LOCAL_WQ_ERROR:
- dev_err(dev, "Invalid request local work queue error.\n");
+ dev_err(dev, "Invalid request local work queue 0x%x error.\n",
+ qpn);
hns_roce_set_qps_to_err(irq_work->hr_dev, qpn);
break;
case HNS_ROCE_EVENT_TYPE_LOCAL_WQ_ACCESS_ERROR:
- dev_err(dev, "Local access violation work queue error.\n");
+ dev_err(dev, "Local access violation work queue 0x%x error, sub_type:%d\n",
+ qpn, irq_work->sub_type);
hns_roce_set_qps_to_err(irq_work->hr_dev, qpn);
- switch (irq_work->sub_type) {
- case HNS_ROCE_LAVWQE_R_KEY_VIOLATION:
- dev_err(dev, "QP %d, R_key violation.\n", qpn);
- break;
- case HNS_ROCE_LAVWQE_LENGTH_ERROR:
- dev_err(dev, "QP %d, length error.\n", qpn);
- break;
- case HNS_ROCE_LAVWQE_VA_ERROR:
- dev_err(dev, "QP %d, VA error.\n", qpn);
- break;
- case HNS_ROCE_LAVWQE_PD_ERROR:
- dev_err(dev, "QP %d, PD error.\n", qpn);
- break;
- case HNS_ROCE_LAVWQE_RW_ACC_ERROR:
- dev_err(dev, "QP %d, rw acc error.\n", qpn);
- break;
- case HNS_ROCE_LAVWQE_KEY_STATE_ERROR:
- dev_err(dev, "QP %d, key state error.\n", qpn);
- break;
- case HNS_ROCE_LAVWQE_MR_OPERATION_ERROR:
- dev_err(dev, "QP %d, MR operation error.\n", qpn);
- break;
- default:
- dev_err(dev, "Unhandled sub_event type %d.\n",
- irq_work->sub_type);
- break;
- }
break;
case HNS_ROCE_EVENT_TYPE_SRQ_LIMIT_REACH:
dev_warn(dev, "SRQ limit reach.\n");
@@ -4427,6 +4724,7 @@ static void hns_roce_v2_init_irq_work(struct hns_roce_dev *hr_dev,
static void set_eq_cons_index_v2(struct hns_roce_eq *eq)
{
+ struct hns_roce_dev *hr_dev = eq->hr_dev;
u32 doorbell[2];
doorbell[0] = 0;
@@ -4453,7 +4751,7 @@ static void set_eq_cons_index_v2(struct hns_roce_eq *eq)
HNS_ROCE_V2_EQ_DB_PARA_S,
(eq->cons_index & HNS_ROCE_V2_CONS_IDX_M));
- hns_roce_write64_k(doorbell, eq->doorbell);
+ hns_roce_write64(hr_dev, doorbell, eq->doorbell);
}
static struct hns_roce_aeqe *get_aeqe_v2(struct hns_roce_eq *eq, u32 entry)
@@ -4568,7 +4866,7 @@ static int hns_roce_v2_aeq_int(struct hns_roce_dev *hr_dev,
dev_err(dev, "Unhandled event %d on EQ %d at idx %u.\n",
event_type, eq->eqn, eq->cons_index);
break;
- };
+ }
eq->event_type = event_type;
eq->sub_type = sub_type;
@@ -4692,11 +4990,22 @@ static irqreturn_t hns_roce_v2_msix_interrupt_abn(int irq, void *dev_id)
int_en = roce_read(hr_dev, ROCEE_VF_ABN_INT_EN_REG);
if (roce_get_bit(int_st, HNS_ROCE_V2_VF_INT_ST_AEQ_OVERFLOW_S)) {
+ struct pci_dev *pdev = hr_dev->pci_dev;
+ struct hnae3_ae_dev *ae_dev = pci_get_drvdata(pdev);
+ const struct hnae3_ae_ops *ops = ae_dev->ops;
+
dev_err(dev, "AEQ overflow!\n");
roce_set_bit(int_st, HNS_ROCE_V2_VF_INT_ST_AEQ_OVERFLOW_S, 1);
roce_write(hr_dev, ROCEE_VF_ABN_INT_ST_REG, int_st);
+ /* Set reset level for reset_event() */
+ if (ops->set_default_reset_request)
+ ops->set_default_reset_request(ae_dev,
+ HNAE3_FUNC_RESET);
+ if (ops->reset_event)
+ ops->reset_event(pdev, NULL);
+
roce_set_bit(int_en, HNS_ROCE_V2_VF_ABN_INT_EN_S, 1);
roce_write(hr_dev, ROCEE_VF_ABN_INT_EN_REG, int_en);
@@ -5599,7 +5908,7 @@ static int hns_roce_v2_modify_srq(struct ib_srq *ibsrq,
return 0;
}
-int hns_roce_v2_query_srq(struct ib_srq *ibsrq, struct ib_srq_attr *attr)
+static int hns_roce_v2_query_srq(struct ib_srq *ibsrq, struct ib_srq_attr *attr)
{
struct hns_roce_dev *hr_dev = to_hr_dev(ibsrq->device);
struct hns_roce_srq *srq = to_hr_srq(ibsrq);
@@ -5664,6 +5973,7 @@ static int hns_roce_v2_post_srq_recv(struct ib_srq *ibsrq,
const struct ib_recv_wr *wr,
const struct ib_recv_wr **bad_wr)
{
+ struct hns_roce_dev *hr_dev = to_hr_dev(ibsrq->device);
struct hns_roce_srq *srq = to_hr_srq(ibsrq);
struct hns_roce_v2_wqe_data_seg *dseg;
struct hns_roce_v2_db srq_db;
@@ -5725,7 +6035,7 @@ static int hns_roce_v2_post_srq_recv(struct ib_srq *ibsrq,
srq_db.byte_4 = HNS_ROCE_V2_SRQ_DB << 24 | srq->srqn;
srq_db.parameter = srq->head;
- hns_roce_write64_k((__le32 *)&srq_db, srq->db_reg_l);
+ hns_roce_write64(hr_dev, (__le32 *)&srq_db, srq->db_reg_l);
}
@@ -5758,6 +6068,7 @@ static const struct hns_roce_hw hns_roce_hw_v2 = {
.hw_exit = hns_roce_v2_exit,
.post_mbox = hns_roce_v2_post_mbox,
.chk_mbox = hns_roce_v2_chk_mbox,
+ .rst_prc_mbox = hns_roce_v2_rst_process_cmd,
.set_gid = hns_roce_v2_set_gid,
.set_mac = hns_roce_v2_set_mac,
.write_mtpt = hns_roce_v2_write_mtpt,
@@ -5770,6 +6081,7 @@ static const struct hns_roce_hw hns_roce_hw_v2 = {
.modify_qp = hns_roce_v2_modify_qp,
.query_qp = hns_roce_v2_query_qp,
.destroy_qp = hns_roce_v2_destroy_qp,
+ .qp_flow_control_init = hns_roce_v2_qp_flow_control_init,
.modify_cq = hns_roce_v2_modify_cq,
.post_send = hns_roce_v2_post_send,
.post_recv = hns_roce_v2_post_recv,
@@ -5800,6 +6112,7 @@ MODULE_DEVICE_TABLE(pci, hns_roce_hw_v2_pci_tbl);
static int hns_roce_hw_v2_get_cfg(struct hns_roce_dev *hr_dev,
struct hnae3_handle *handle)
{
+ struct hns_roce_v2_priv *priv = hr_dev->priv;
const struct pci_device_id *id;
int i;
@@ -5830,15 +6143,18 @@ static int hns_roce_hw_v2_get_cfg(struct hns_roce_dev *hr_dev,
hr_dev->cmd_mod = 1;
hr_dev->loop_idc = 0;
+ hr_dev->reset_cnt = handle->ae_algo->ops->ae_dev_reset_cnt(handle);
+ priv->handle = handle;
+
return 0;
}
-static int hns_roce_hw_v2_init_instance(struct hnae3_handle *handle)
+static int __hns_roce_hw_v2_init_instance(struct hnae3_handle *handle)
{
struct hns_roce_dev *hr_dev;
int ret;
- hr_dev = (struct hns_roce_dev *)ib_alloc_device(sizeof(*hr_dev));
+ hr_dev = ib_alloc_device(hns_roce_dev, ib_dev);
if (!hr_dev)
return -ENOMEM;
@@ -5850,7 +6166,6 @@ static int hns_roce_hw_v2_init_instance(struct hnae3_handle *handle)
hr_dev->pci_dev = handle->pdev;
hr_dev->dev = &handle->pdev->dev;
- handle->priv = hr_dev;
ret = hns_roce_hw_v2_get_cfg(hr_dev, handle);
if (ret) {
@@ -5864,6 +6179,8 @@ static int hns_roce_hw_v2_init_instance(struct hnae3_handle *handle)
goto error_failed_get_cfg;
}
+ handle->priv = hr_dev;
+
return 0;
error_failed_get_cfg:
@@ -5875,7 +6192,7 @@ error_failed_kzalloc:
return ret;
}
-static void hns_roce_hw_v2_uninit_instance(struct hnae3_handle *handle,
+static void __hns_roce_hw_v2_uninit_instance(struct hnae3_handle *handle,
bool reset)
{
struct hns_roce_dev *hr_dev = (struct hns_roce_dev *)handle->priv;
@@ -5883,24 +6200,79 @@ static void hns_roce_hw_v2_uninit_instance(struct hnae3_handle *handle,
if (!hr_dev)
return;
+ handle->priv = NULL;
hns_roce_exit(hr_dev);
kfree(hr_dev->priv);
ib_dealloc_device(&hr_dev->ib_dev);
}
+static int hns_roce_hw_v2_init_instance(struct hnae3_handle *handle)
+{
+ const struct hnae3_ae_ops *ops = handle->ae_algo->ops;
+ struct device *dev = &handle->pdev->dev;
+ int ret;
+
+ handle->rinfo.instance_state = HNS_ROCE_STATE_INIT;
+
+ if (ops->ae_dev_resetting(handle) || ops->get_hw_reset_stat(handle)) {
+ handle->rinfo.instance_state = HNS_ROCE_STATE_NON_INIT;
+ goto reset_chk_err;
+ }
+
+ ret = __hns_roce_hw_v2_init_instance(handle);
+ if (ret) {
+ handle->rinfo.instance_state = HNS_ROCE_STATE_NON_INIT;
+ dev_err(dev, "RoCE instance init failed! ret = %d\n", ret);
+ if (ops->ae_dev_resetting(handle) ||
+ ops->get_hw_reset_stat(handle))
+ goto reset_chk_err;
+ else
+ return ret;
+ }
+
+ handle->rinfo.instance_state = HNS_ROCE_STATE_INITED;
+
+
+ return 0;
+
+reset_chk_err:
+ dev_err(dev, "Device is busy in resetting state.\n"
+ "please retry later.\n");
+
+ return -EBUSY;
+}
+
+static void hns_roce_hw_v2_uninit_instance(struct hnae3_handle *handle,
+ bool reset)
+{
+ if (handle->rinfo.instance_state != HNS_ROCE_STATE_INITED)
+ return;
+
+ handle->rinfo.instance_state = HNS_ROCE_STATE_UNINIT;
+
+ __hns_roce_hw_v2_uninit_instance(handle, reset);
+
+ handle->rinfo.instance_state = HNS_ROCE_STATE_NON_INIT;
+}
static int hns_roce_hw_v2_reset_notify_down(struct hnae3_handle *handle)
{
- struct hns_roce_dev *hr_dev = (struct hns_roce_dev *)handle->priv;
+ struct hns_roce_dev *hr_dev;
struct ib_event event;
- if (!hr_dev) {
- dev_err(&handle->pdev->dev,
- "Input parameter handle->priv is NULL!\n");
- return -EINVAL;
+ if (handle->rinfo.instance_state != HNS_ROCE_STATE_INITED) {
+ set_bit(HNS_ROCE_RST_DIRECT_RETURN, &handle->rinfo.state);
+ return 0;
}
+ handle->rinfo.reset_state = HNS_ROCE_STATE_RST_DOWN;
+ clear_bit(HNS_ROCE_RST_DIRECT_RETURN, &handle->rinfo.state);
+
+ hr_dev = (struct hns_roce_dev *)handle->priv;
+ if (!hr_dev)
+ return 0;
+
hr_dev->active = false;
- hr_dev->is_reset = true;
+ hr_dev->dis_db = true;
event.event = IB_EVENT_DEVICE_FATAL;
event.device = &hr_dev->ib_dev;
@@ -5912,17 +6284,29 @@ static int hns_roce_hw_v2_reset_notify_down(struct hnae3_handle *handle)
static int hns_roce_hw_v2_reset_notify_init(struct hnae3_handle *handle)
{
+ struct device *dev = &handle->pdev->dev;
int ret;
- ret = hns_roce_hw_v2_init_instance(handle);
+ if (test_and_clear_bit(HNS_ROCE_RST_DIRECT_RETURN,
+ &handle->rinfo.state)) {
+ handle->rinfo.reset_state = HNS_ROCE_STATE_RST_INITED;
+ return 0;
+ }
+
+ handle->rinfo.reset_state = HNS_ROCE_STATE_RST_INIT;
+
+ dev_info(&handle->pdev->dev, "In reset process RoCE client reinit.\n");
+ ret = __hns_roce_hw_v2_init_instance(handle);
if (ret) {
/* when reset notify type is HNAE3_INIT_CLIENT In reset notify
* callback function, RoCE Engine reinitialize. If RoCE reinit
* failed, we should inform NIC driver.
*/
handle->priv = NULL;
- dev_err(&handle->pdev->dev,
- "In reset process RoCE reinit failed %d.\n", ret);
+ dev_err(dev, "In reset process RoCE reinit failed %d.\n", ret);
+ } else {
+ handle->rinfo.reset_state = HNS_ROCE_STATE_RST_INITED;
+ dev_info(dev, "Reset done, RoCE client reinit finished.\n");
}
return ret;
@@ -5930,8 +6314,14 @@ static int hns_roce_hw_v2_reset_notify_init(struct hnae3_handle *handle)
static int hns_roce_hw_v2_reset_notify_uninit(struct hnae3_handle *handle)
{
+ if (test_bit(HNS_ROCE_RST_DIRECT_RETURN, &handle->rinfo.state))
+ return 0;
+
+ handle->rinfo.reset_state = HNS_ROCE_STATE_RST_UNINIT;
+ dev_info(&handle->pdev->dev, "In reset process RoCE client uninit.\n");
msleep(100);
- hns_roce_hw_v2_uninit_instance(handle, false);
+ __hns_roce_hw_v2_uninit_instance(handle, false);
+
return 0;
}
diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v2.h b/drivers/infiniband/hw/hns/hns_roce_hw_v2.h
index b72d0443c835..f1f1b75812f9 100644
--- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.h
+++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.h
@@ -36,6 +36,7 @@
#include <linux/bitops.h>
#define HNS_ROCE_VF_QPC_BT_NUM 256
+#define HNS_ROCE_VF_SCCC_BT_NUM 64
#define HNS_ROCE_VF_SRQC_BT_NUM 64
#define HNS_ROCE_VF_CQC_BT_NUM 64
#define HNS_ROCE_VF_MPT_BT_NUM 64
@@ -44,12 +45,14 @@
#define HNS_ROCE_VF_SGID_NUM 32
#define HNS_ROCE_VF_SL_NUM 8
-#define HNS_ROCE_V2_MAX_QP_NUM 0x2000
+#define HNS_ROCE_V2_MAX_QP_NUM 0x100000
+#define HNS_ROCE_V2_MAX_QPC_TIMER_NUM 0x200
#define HNS_ROCE_V2_MAX_WQE_NUM 0x8000
#define HNS_ROCE_V2_MAX_SRQ 0x100000
#define HNS_ROCE_V2_MAX_SRQ_WR 0x8000
#define HNS_ROCE_V2_MAX_SRQ_SGE 0x100
-#define HNS_ROCE_V2_MAX_CQ_NUM 0x8000
+#define HNS_ROCE_V2_MAX_CQ_NUM 0x100000
+#define HNS_ROCE_V2_MAX_CQC_TIMER_NUM 0x100
#define HNS_ROCE_V2_MAX_SRQ_NUM 0x100000
#define HNS_ROCE_V2_MAX_CQE_NUM 0x10000
#define HNS_ROCE_V2_MAX_SRQWQE_NUM 0x8000
@@ -64,7 +67,7 @@
#define HNS_ROCE_V2_COMP_VEC_NUM 63
#define HNS_ROCE_V2_AEQE_VEC_NUM 1
#define HNS_ROCE_V2_ABNORMAL_VEC_NUM 1
-#define HNS_ROCE_V2_MAX_MTPT_NUM 0x8000
+#define HNS_ROCE_V2_MAX_MTPT_NUM 0x100000
#define HNS_ROCE_V2_MAX_MTT_SEGS 0x1000000
#define HNS_ROCE_V2_MAX_CQE_SEGS 0x1000000
#define HNS_ROCE_V2_MAX_SRQWQE_SEGS 0x1000000
@@ -83,6 +86,9 @@
#define HNS_ROCE_V2_MTPT_ENTRY_SZ 64
#define HNS_ROCE_V2_MTT_ENTRY_SZ 64
#define HNS_ROCE_V2_CQE_ENTRY_SIZE 32
+#define HNS_ROCE_V2_SCCC_ENTRY_SZ 32
+#define HNS_ROCE_V2_QPC_TIMER_ENTRY_SZ 4096
+#define HNS_ROCE_V2_CQC_TIMER_ENTRY_SZ 4096
#define HNS_ROCE_V2_PAGE_SIZE_SUPPORTED 0xFFFFF000
#define HNS_ROCE_V2_MAX_INNER_MTPT_NUM 2
#define HNS_ROCE_INVALID_LKEY 0x100
@@ -90,7 +96,10 @@
#define HNS_ROCE_V2_UC_RC_SGE_NUM_IN_WQE 2
#define HNS_ROCE_V2_RSV_QPS 8
+#define HNS_ROCE_V2_HW_RST_TIMEOUT 1000
+
#define HNS_ROCE_CONTEXT_HOP_NUM 1
+#define HNS_ROCE_SCCC_HOP_NUM 1
#define HNS_ROCE_MTT_HOP_NUM 1
#define HNS_ROCE_CQE_HOP_NUM 1
#define HNS_ROCE_SRQWQE_HOP_NUM 1
@@ -120,6 +129,8 @@
#define HNS_ROCE_CMQ_EN_B 16
#define HNS_ROCE_CMQ_ENABLE BIT(HNS_ROCE_CMQ_EN_B)
+#define HNS_ROCE_CMQ_SCC_CLR_DONE_CNT 5
+
#define check_whether_last_step(hop_num, step_idx) \
((step_idx == 0 && hop_num == HNS_ROCE_HOP_NUM_0) || \
(step_idx == 1 && hop_num == 1) || \
@@ -224,11 +235,15 @@ enum hns_roce_opcode_type {
HNS_ROCE_OPC_ALLOC_VF_RES = 0x8401,
HNS_ROCE_OPC_CFG_EXT_LLM = 0x8403,
HNS_ROCE_OPC_CFG_TMOUT_LLM = 0x8404,
+ HNS_ROCE_OPC_QUERY_PF_TIMER_RES = 0x8406,
HNS_ROCE_OPC_CFG_SGID_TB = 0x8500,
HNS_ROCE_OPC_CFG_SMAC_TB = 0x8501,
HNS_ROCE_OPC_POST_MB = 0x8504,
HNS_ROCE_OPC_QUERY_MB_ST = 0x8505,
HNS_ROCE_OPC_CFG_BT_ATTR = 0x8506,
+ HNS_ROCE_OPC_CLR_SCCC = 0x8509,
+ HNS_ROCE_OPC_QUERY_SCCC = 0x850a,
+ HNS_ROCE_OPC_RESET_SCCC = 0x850b,
HNS_SWITCH_PARAMETER_CFG = 0x1033,
};
@@ -1300,7 +1315,8 @@ struct hns_roce_pf_res_b {
__le32 smac_idx_num;
__le32 sgid_idx_num;
__le32 qid_idx_sl_num;
- __le32 rsv[2];
+ __le32 sccc_bt_idx_num;
+ __le32 rsv;
};
#define PF_RES_DATA_1_PF_SMAC_IDX_S 0
@@ -1321,6 +1337,31 @@ struct hns_roce_pf_res_b {
#define PF_RES_DATA_3_PF_SL_NUM_S 16
#define PF_RES_DATA_3_PF_SL_NUM_M GENMASK(26, 16)
+#define PF_RES_DATA_4_PF_SCCC_BT_IDX_S 0
+#define PF_RES_DATA_4_PF_SCCC_BT_IDX_M GENMASK(8, 0)
+
+#define PF_RES_DATA_4_PF_SCCC_BT_NUM_S 9
+#define PF_RES_DATA_4_PF_SCCC_BT_NUM_M GENMASK(17, 9)
+
+struct hns_roce_pf_timer_res_a {
+ __le32 rsv0;
+ __le32 qpc_timer_bt_idx_num;
+ __le32 cqc_timer_bt_idx_num;
+ __le32 rsv[3];
+};
+
+#define PF_RES_DATA_1_PF_QPC_TIMER_BT_IDX_S 0
+#define PF_RES_DATA_1_PF_QPC_TIMER_BT_IDX_M GENMASK(11, 0)
+
+#define PF_RES_DATA_1_PF_QPC_TIMER_BT_NUM_S 16
+#define PF_RES_DATA_1_PF_QPC_TIMER_BT_NUM_M GENMASK(28, 16)
+
+#define PF_RES_DATA_2_PF_CQC_TIMER_BT_IDX_S 0
+#define PF_RES_DATA_2_PF_CQC_TIMER_BT_IDX_M GENMASK(10, 0)
+
+#define PF_RES_DATA_2_PF_CQC_TIMER_BT_NUM_S 16
+#define PF_RES_DATA_2_PF_CQC_TIMER_BT_NUM_M GENMASK(27, 16)
+
struct hns_roce_vf_res_a {
__le32 vf_id;
__le32 vf_qpc_bt_idx_num;
@@ -1365,7 +1406,8 @@ struct hns_roce_vf_res_b {
__le32 vf_smac_idx_num;
__le32 vf_sgid_idx_num;
__le32 vf_qid_idx_sl_num;
- __le32 rsv[2];
+ __le32 vf_sccc_idx_num;
+ __le32 rsv1;
};
#define VF_RES_B_DATA_0_VF_ID_S 0
@@ -1389,6 +1431,12 @@ struct hns_roce_vf_res_b {
#define VF_RES_B_DATA_3_VF_SL_NUM_S 16
#define VF_RES_B_DATA_3_VF_SL_NUM_M GENMASK(19, 16)
+#define VF_RES_B_DATA_4_VF_SCCC_BT_IDX_S 0
+#define VF_RES_B_DATA_4_VF_SCCC_BT_IDX_M GENMASK(8, 0)
+
+#define VF_RES_B_DATA_4_VF_SCCC_BT_NUM_S 9
+#define VF_RES_B_DATA_4_VF_SCCC_BT_NUM_M GENMASK(17, 9)
+
struct hns_roce_vf_switch {
__le32 rocee_sel;
__le32 fun_id;
@@ -1424,7 +1472,8 @@ struct hns_roce_cfg_bt_attr {
__le32 vf_srqc_cfg;
__le32 vf_cqc_cfg;
__le32 vf_mpt_cfg;
- __le32 rsv[2];
+ __le32 vf_sccc_cfg;
+ __le32 rsv;
};
#define CFG_BT_ATTR_DATA_0_VF_QPC_BA_PGSZ_S 0
@@ -1463,6 +1512,15 @@ struct hns_roce_cfg_bt_attr {
#define CFG_BT_ATTR_DATA_3_VF_MPT_HOPNUM_S 8
#define CFG_BT_ATTR_DATA_3_VF_MPT_HOPNUM_M GENMASK(9, 8)
+#define CFG_BT_ATTR_DATA_4_VF_SCCC_BA_PGSZ_S 0
+#define CFG_BT_ATTR_DATA_4_VF_SCCC_BA_PGSZ_M GENMASK(3, 0)
+
+#define CFG_BT_ATTR_DATA_4_VF_SCCC_BUF_PGSZ_S 4
+#define CFG_BT_ATTR_DATA_4_VF_SCCC_BUF_PGSZ_M GENMASK(7, 4)
+
+#define CFG_BT_ATTR_DATA_4_VF_SCCC_HOPNUM_S 8
+#define CFG_BT_ATTR_DATA_4_VF_SCCC_HOPNUM_M GENMASK(9, 8)
+
struct hns_roce_cfg_sgid_tb {
__le32 table_idx_rsv;
__le32 vf_sgid_l;
@@ -1546,6 +1604,7 @@ struct hns_roce_link_table_entry {
#define HNS_ROCE_LINK_TABLE_NXT_PTR_M GENMASK(31, 20)
struct hns_roce_v2_priv {
+ struct hnae3_handle *handle;
struct hns_roce_v2_cmq cmq;
struct hns_roce_link_table tsq;
struct hns_roce_link_table tpq;
@@ -1730,4 +1789,25 @@ struct hns_roce_wqe_atomic_seg {
__le64 cmp_data;
};
+struct hns_roce_sccc_clr {
+ __le32 qpn;
+ __le32 rsv[5];
+};
+
+struct hns_roce_sccc_clr_done {
+ __le32 clr_done;
+ __le32 rsv[5];
+};
+
+static inline void hns_roce_write64(struct hns_roce_dev *hr_dev, __le32 val[2],
+ void __iomem *dest)
+{
+ struct hns_roce_v2_priv *priv = (struct hns_roce_v2_priv *)hr_dev->priv;
+ struct hnae3_handle *handle = priv->handle;
+ const struct hnae3_ae_ops *ops = handle->ae_algo->ops;
+
+ if (!hr_dev->dis_db && !ops->get_hw_reset_stat(handle))
+ hns_roce_write64_k(val, dest);
+}
+
#endif
diff --git a/drivers/infiniband/hw/hns/hns_roce_main.c b/drivers/infiniband/hw/hns/hns_roce_main.c
index c79054ba9495..c929125da84b 100644
--- a/drivers/infiniband/hw/hns/hns_roce_main.c
+++ b/drivers/infiniband/hw/hns/hns_roce_main.c
@@ -226,6 +226,11 @@ static int hns_roce_query_device(struct ib_device *ib_dev,
props->max_srq_sge = hr_dev->caps.max_srq_sges;
}
+ if (hr_dev->caps.flags & HNS_ROCE_CAP_FLAG_FRMR) {
+ props->device_cap_flags |= IB_DEVICE_MEM_MGT_EXTENSIONS;
+ props->max_fast_reg_page_list_len = HNS_ROCE_FRMR_MAX_PA;
+ }
+
return 0;
}
@@ -330,23 +335,19 @@ static int hns_roce_modify_port(struct ib_device *ib_dev, u8 port_num, int mask,
return 0;
}
-static struct ib_ucontext *hns_roce_alloc_ucontext(struct ib_device *ib_dev,
- struct ib_udata *udata)
+static int hns_roce_alloc_ucontext(struct ib_ucontext *uctx,
+ struct ib_udata *udata)
{
int ret = 0;
- struct hns_roce_ucontext *context;
+ struct hns_roce_ucontext *context = to_hr_ucontext(uctx);
struct hns_roce_ib_alloc_ucontext_resp resp = {};
- struct hns_roce_dev *hr_dev = to_hr_dev(ib_dev);
+ struct hns_roce_dev *hr_dev = to_hr_dev(uctx->device);
if (!hr_dev->active)
- return ERR_PTR(-EAGAIN);
+ return -EAGAIN;
resp.qp_tab_size = hr_dev->caps.num_qps;
- context = kmalloc(sizeof(*context), GFP_KERNEL);
- if (!context)
- return ERR_PTR(-ENOMEM);
-
ret = hns_roce_uar_alloc(hr_dev, &context->uar);
if (ret)
goto error_fail_uar_alloc;
@@ -360,25 +361,20 @@ static struct ib_ucontext *hns_roce_alloc_ucontext(struct ib_device *ib_dev,
if (ret)
goto error_fail_copy_to_udata;
- return &context->ibucontext;
+ return 0;
error_fail_copy_to_udata:
hns_roce_uar_free(hr_dev, &context->uar);
error_fail_uar_alloc:
- kfree(context);
-
- return ERR_PTR(ret);
+ return ret;
}
-static int hns_roce_dealloc_ucontext(struct ib_ucontext *ibcontext)
+static void hns_roce_dealloc_ucontext(struct ib_ucontext *ibcontext)
{
struct hns_roce_ucontext *context = to_hr_ucontext(ibcontext);
hns_roce_uar_free(to_hr_dev(ibcontext->device), &context->uar);
- kfree(context);
-
- return 0;
}
static int hns_roce_mmap(struct ib_ucontext *context,
@@ -472,6 +468,8 @@ static const struct ib_device_ops hns_roce_dev_ops = {
.query_pkey = hns_roce_query_pkey,
.query_port = hns_roce_query_port,
.reg_user_mr = hns_roce_reg_user_mr,
+ INIT_RDMA_OBJ_SIZE(ib_pd, hns_roce_pd, ibpd),
+ INIT_RDMA_OBJ_SIZE(ib_ucontext, hns_roce_ucontext, ibucontext),
};
static const struct ib_device_ops hns_roce_dev_mr_ops = {
@@ -564,7 +562,7 @@ static int hns_roce_register_device(struct hns_roce_dev *hr_dev)
ib_dev->driver_id = RDMA_DRIVER_HNS;
ib_set_device_ops(ib_dev, hr_dev->hw->hns_roce_dev_ops);
ib_set_device_ops(ib_dev, &hns_roce_dev_ops);
- ret = ib_register_device(ib_dev, "hns_%d", NULL);
+ ret = ib_register_device(ib_dev, "hns_%d");
if (ret) {
dev_err(dev, "ib_register_device failed!\n");
return ret;
@@ -702,8 +700,62 @@ static int hns_roce_init_hem(struct hns_roce_dev *hr_dev)
}
}
+ if (hr_dev->caps.sccc_entry_sz) {
+ ret = hns_roce_init_hem_table(hr_dev,
+ &hr_dev->qp_table.sccc_table,
+ HEM_TYPE_SCCC,
+ hr_dev->caps.sccc_entry_sz,
+ hr_dev->caps.num_qps, 1);
+ if (ret) {
+ dev_err(dev,
+ "Failed to init SCC context memory, aborting.\n");
+ goto err_unmap_idx;
+ }
+ }
+
+ if (hr_dev->caps.qpc_timer_entry_sz) {
+ ret = hns_roce_init_hem_table(hr_dev,
+ &hr_dev->qpc_timer_table,
+ HEM_TYPE_QPC_TIMER,
+ hr_dev->caps.qpc_timer_entry_sz,
+ hr_dev->caps.num_qpc_timer, 1);
+ if (ret) {
+ dev_err(dev,
+ "Failed to init QPC timer memory, aborting.\n");
+ goto err_unmap_ctx;
+ }
+ }
+
+ if (hr_dev->caps.cqc_timer_entry_sz) {
+ ret = hns_roce_init_hem_table(hr_dev,
+ &hr_dev->cqc_timer_table,
+ HEM_TYPE_CQC_TIMER,
+ hr_dev->caps.cqc_timer_entry_sz,
+ hr_dev->caps.num_cqc_timer, 1);
+ if (ret) {
+ dev_err(dev,
+ "Failed to init CQC timer memory, aborting.\n");
+ goto err_unmap_qpc_timer;
+ }
+ }
+
return 0;
+err_unmap_qpc_timer:
+ if (hr_dev->caps.qpc_timer_entry_sz)
+ hns_roce_cleanup_hem_table(hr_dev,
+ &hr_dev->qpc_timer_table);
+
+err_unmap_ctx:
+ if (hr_dev->caps.sccc_entry_sz)
+ hns_roce_cleanup_hem_table(hr_dev,
+ &hr_dev->qp_table.sccc_table);
+
+err_unmap_idx:
+ if (hr_dev->caps.num_idx_segs)
+ hns_roce_cleanup_hem_table(hr_dev,
+ &hr_dev->mr_table.mtt_idx_table);
+
err_unmap_srqwqe:
if (hr_dev->caps.num_srqwqe_segs)
hns_roce_cleanup_hem_table(hr_dev,
diff --git a/drivers/infiniband/hw/hns/hns_roce_mr.c b/drivers/infiniband/hw/hns/hns_roce_mr.c
index ee5991bd4171..b09f1cde2ff5 100644
--- a/drivers/infiniband/hw/hns/hns_roce_mr.c
+++ b/drivers/infiniband/hw/hns/hns_roce_mr.c
@@ -976,12 +976,11 @@ int hns_roce_ib_umem_write_mtt(struct hns_roce_dev *hr_dev,
struct hns_roce_mtt *mtt, struct ib_umem *umem)
{
struct device *dev = hr_dev->dev;
- struct scatterlist *sg;
+ struct sg_dma_page_iter sg_iter;
unsigned int order;
- int i, k, entry;
int npage = 0;
int ret = 0;
- int len;
+ int i;
u64 page_addr;
u64 *pages;
u32 bt_page_size;
@@ -1014,29 +1013,25 @@ int hns_roce_ib_umem_write_mtt(struct hns_roce_dev *hr_dev,
i = n = 0;
- for_each_sg(umem->sg_head.sgl, sg, umem->nmap, entry) {
- len = sg_dma_len(sg) >> PAGE_SHIFT;
- for (k = 0; k < len; ++k) {
- page_addr =
- sg_dma_address(sg) + (k << umem->page_shift);
- if (!(npage % (1 << (mtt->page_shift - PAGE_SHIFT)))) {
- if (page_addr & ((1 << mtt->page_shift) - 1)) {
- dev_err(dev, "page_addr 0x%llx is not page_shift %d alignment!\n",
- page_addr, mtt->page_shift);
- ret = -EINVAL;
- goto out;
- }
- pages[i++] = page_addr;
- }
- npage++;
- if (i == bt_page_size / sizeof(u64)) {
- ret = hns_roce_write_mtt(hr_dev, mtt, n, i,
- pages);
- if (ret)
- goto out;
- n += i;
- i = 0;
+ for_each_sg_dma_page(umem->sg_head.sgl, &sg_iter, umem->nmap, 0) {
+ page_addr = sg_page_iter_dma_address(&sg_iter);
+ if (!(npage % (1 << (mtt->page_shift - PAGE_SHIFT)))) {
+ if (page_addr & ((1 << mtt->page_shift) - 1)) {
+ dev_err(dev,
+ "page_addr 0x%llx is not page_shift %d alignment!\n",
+ page_addr, mtt->page_shift);
+ ret = -EINVAL;
+ goto out;
}
+ pages[i++] = page_addr;
+ }
+ npage++;
+ if (i == bt_page_size / sizeof(u64)) {
+ ret = hns_roce_write_mtt(hr_dev, mtt, n, i, pages);
+ if (ret)
+ goto out;
+ n += i;
+ i = 0;
}
}
@@ -1052,10 +1047,8 @@ static int hns_roce_ib_umem_write_mr(struct hns_roce_dev *hr_dev,
struct hns_roce_mr *mr,
struct ib_umem *umem)
{
- struct scatterlist *sg;
- int i = 0, j = 0, k;
- int entry;
- int len;
+ struct sg_dma_page_iter sg_iter;
+ int i = 0, j = 0;
u64 page_addr;
u32 pbl_bt_sz;
@@ -1063,27 +1056,22 @@ static int hns_roce_ib_umem_write_mr(struct hns_roce_dev *hr_dev,
return 0;
pbl_bt_sz = 1 << (hr_dev->caps.pbl_ba_pg_sz + PAGE_SHIFT);
- for_each_sg(umem->sg_head.sgl, sg, umem->nmap, entry) {
- len = sg_dma_len(sg) >> PAGE_SHIFT;
- for (k = 0; k < len; ++k) {
- page_addr = sg_dma_address(sg) +
- (k << umem->page_shift);
-
- if (!hr_dev->caps.pbl_hop_num) {
- mr->pbl_buf[i++] = page_addr >> 12;
- } else if (hr_dev->caps.pbl_hop_num == 1) {
- mr->pbl_buf[i++] = page_addr;
- } else {
- if (hr_dev->caps.pbl_hop_num == 2)
- mr->pbl_bt_l1[i][j] = page_addr;
- else if (hr_dev->caps.pbl_hop_num == 3)
- mr->pbl_bt_l2[i][j] = page_addr;
-
- j++;
- if (j >= (pbl_bt_sz / 8)) {
- i++;
- j = 0;
- }
+ for_each_sg_dma_page(umem->sg_head.sgl, &sg_iter, umem->nmap, 0) {
+ page_addr = sg_page_iter_dma_address(&sg_iter);
+ if (!hr_dev->caps.pbl_hop_num) {
+ mr->pbl_buf[i++] = page_addr >> 12;
+ } else if (hr_dev->caps.pbl_hop_num == 1) {
+ mr->pbl_buf[i++] = page_addr;
+ } else {
+ if (hr_dev->caps.pbl_hop_num == 2)
+ mr->pbl_bt_l1[i][j] = page_addr;
+ else if (hr_dev->caps.pbl_hop_num == 3)
+ mr->pbl_bt_l2[i][j] = page_addr;
+
+ j++;
+ if (j >= (pbl_bt_sz / 8)) {
+ i++;
+ j = 0;
}
}
}
@@ -1110,8 +1098,7 @@ struct ib_mr *hns_roce_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
if (!mr)
return ERR_PTR(-ENOMEM);
- mr->umem = ib_umem_get(pd->uobject->context, start, length,
- access_flags, 0);
+ mr->umem = ib_umem_get(udata, start, length, access_flags, 0);
if (IS_ERR(mr->umem)) {
ret = PTR_ERR(mr->umem);
goto err_free;
@@ -1220,8 +1207,8 @@ int hns_roce_rereg_user_mr(struct ib_mr *ibmr, int flags, u64 start, u64 length,
}
ib_umem_release(mr->umem);
- mr->umem = ib_umem_get(ibmr->uobject->context, start, length,
- mr_access_flags, 0);
+ mr->umem =
+ ib_umem_get(udata, start, length, mr_access_flags, 0);
if (IS_ERR(mr->umem)) {
ret = PTR_ERR(mr->umem);
mr->umem = NULL;
diff --git a/drivers/infiniband/hw/hns/hns_roce_pd.c b/drivers/infiniband/hw/hns/hns_roce_pd.c
index e11c149da04d..b9b97c5e97e6 100644
--- a/drivers/infiniband/hw/hns/hns_roce_pd.c
+++ b/drivers/infiniband/hw/hns/hns_roce_pd.c
@@ -57,24 +57,19 @@ void hns_roce_cleanup_pd_table(struct hns_roce_dev *hr_dev)
hns_roce_bitmap_cleanup(&hr_dev->pd_bitmap);
}
-struct ib_pd *hns_roce_alloc_pd(struct ib_device *ib_dev,
- struct ib_ucontext *context,
- struct ib_udata *udata)
+int hns_roce_alloc_pd(struct ib_pd *ibpd, struct ib_ucontext *context,
+ struct ib_udata *udata)
{
+ struct ib_device *ib_dev = ibpd->device;
struct hns_roce_dev *hr_dev = to_hr_dev(ib_dev);
struct device *dev = hr_dev->dev;
- struct hns_roce_pd *pd;
+ struct hns_roce_pd *pd = to_hr_pd(ibpd);
int ret;
- pd = kmalloc(sizeof(*pd), GFP_KERNEL);
- if (!pd)
- return ERR_PTR(-ENOMEM);
-
ret = hns_roce_pd_alloc(to_hr_dev(ib_dev), &pd->pdn);
if (ret) {
- kfree(pd);
dev_err(dev, "[alloc_pd]hns_roce_pd_alloc failed!\n");
- return ERR_PTR(ret);
+ return ret;
}
if (context) {
@@ -83,21 +78,17 @@ struct ib_pd *hns_roce_alloc_pd(struct ib_device *ib_dev,
if (ib_copy_to_udata(udata, &uresp, sizeof(uresp))) {
hns_roce_pd_free(to_hr_dev(ib_dev), pd->pdn);
dev_err(dev, "[alloc_pd]ib_copy_to_udata failed!\n");
- kfree(pd);
- return ERR_PTR(-EFAULT);
+ return -EFAULT;
}
}
- return &pd->ibpd;
+ return 0;
}
EXPORT_SYMBOL_GPL(hns_roce_alloc_pd);
-int hns_roce_dealloc_pd(struct ib_pd *pd)
+void hns_roce_dealloc_pd(struct ib_pd *pd)
{
hns_roce_pd_free(to_hr_dev(pd->device), to_hr_pd(pd)->pdn);
- kfree(to_hr_pd(pd));
-
- return 0;
}
EXPORT_SYMBOL_GPL(hns_roce_dealloc_pd);
diff --git a/drivers/infiniband/hw/hns/hns_roce_qp.c b/drivers/infiniband/hw/hns/hns_roce_qp.c
index 54031c5b53fa..57c76eafef2f 100644
--- a/drivers/infiniband/hw/hns/hns_roce_qp.c
+++ b/drivers/infiniband/hw/hns/hns_roce_qp.c
@@ -35,6 +35,7 @@
#include <linux/platform_device.h>
#include <rdma/ib_addr.h>
#include <rdma/ib_umem.h>
+#include <rdma/uverbs_ioctl.h>
#include "hns_roce_common.h"
#include "hns_roce_device.h"
#include "hns_roce_hem.h"
@@ -209,13 +210,23 @@ static int hns_roce_qp_alloc(struct hns_roce_dev *hr_dev, unsigned long qpn,
}
}
+ if (hr_dev->caps.sccc_entry_sz) {
+ /* Alloc memory for SCC CTX */
+ ret = hns_roce_table_get(hr_dev, &qp_table->sccc_table,
+ hr_qp->qpn);
+ if (ret) {
+ dev_err(dev, "SCC CTX table get failed\n");
+ goto err_put_trrl;
+ }
+ }
+
spin_lock_irq(&qp_table->lock);
ret = radix_tree_insert(&hr_dev->qp_table_tree,
hr_qp->qpn & (hr_dev->caps.num_qps - 1), hr_qp);
spin_unlock_irq(&qp_table->lock);
if (ret) {
dev_err(dev, "QPC radix_tree_insert failed\n");
- goto err_put_trrl;
+ goto err_put_sccc;
}
atomic_set(&hr_qp->refcount, 1);
@@ -223,6 +234,11 @@ static int hns_roce_qp_alloc(struct hns_roce_dev *hr_dev, unsigned long qpn,
return 0;
+err_put_sccc:
+ if (hr_dev->caps.sccc_entry_sz)
+ hns_roce_table_put(hr_dev, &qp_table->sccc_table,
+ hr_qp->qpn);
+
err_put_trrl:
if (hr_dev->caps.trrl_entry_sz)
hns_roce_table_put(hr_dev, &qp_table->trrl_table, hr_qp->qpn);
@@ -258,6 +274,9 @@ void hns_roce_qp_free(struct hns_roce_dev *hr_dev, struct hns_roce_qp *hr_qp)
wait_for_completion(&hr_qp->free);
if ((hr_qp->ibqp.qp_type) != IB_QPT_GSI) {
+ if (hr_dev->caps.sccc_entry_sz)
+ hns_roce_table_put(hr_dev, &qp_table->sccc_table,
+ hr_qp->qpn);
if (hr_dev->caps.trrl_entry_sz)
hns_roce_table_put(hr_dev, &qp_table->trrl_table,
hr_qp->qpn);
@@ -526,7 +545,8 @@ static int hns_roce_qp_has_sq(struct ib_qp_init_attr *attr)
static int hns_roce_qp_has_rq(struct ib_qp_init_attr *attr)
{
if (attr->qp_type == IB_QPT_XRC_INI ||
- attr->qp_type == IB_QPT_XRC_TGT || attr->srq)
+ attr->qp_type == IB_QPT_XRC_TGT || attr->srq ||
+ !attr->cap.max_recv_wr)
return 0;
return 1;
@@ -541,6 +561,8 @@ static int hns_roce_create_qp_common(struct hns_roce_dev *hr_dev,
struct device *dev = hr_dev->dev;
struct hns_roce_ib_create_qp ucmd;
struct hns_roce_ib_create_qp_resp resp = {};
+ struct hns_roce_ucontext *uctx = rdma_udata_to_drv_context(
+ udata, struct hns_roce_ucontext, ibucontext);
unsigned long qpn = 0;
int ret = 0;
u32 page_shift;
@@ -612,9 +634,8 @@ static int hns_roce_create_qp_common(struct hns_roce_dev *hr_dev,
goto err_rq_sge_list;
}
- hr_qp->umem = ib_umem_get(ib_pd->uobject->context,
- ucmd.buf_addr, hr_qp->buff_size, 0,
- 0);
+ hr_qp->umem = ib_umem_get(udata, ucmd.buf_addr,
+ hr_qp->buff_size, 0, 0);
if (IS_ERR(hr_qp->umem)) {
dev_err(dev, "ib_umem_get error for create qp\n");
ret = PTR_ERR(hr_qp->umem);
@@ -622,19 +643,19 @@ static int hns_roce_create_qp_common(struct hns_roce_dev *hr_dev,
}
hr_qp->mtt.mtt_type = MTT_TYPE_WQE;
+ page_shift = PAGE_SHIFT;
if (hr_dev->caps.mtt_buf_pg_sz) {
npages = (ib_umem_page_count(hr_qp->umem) +
(1 << hr_dev->caps.mtt_buf_pg_sz) - 1) /
- (1 << hr_dev->caps.mtt_buf_pg_sz);
- page_shift = PAGE_SHIFT + hr_dev->caps.mtt_buf_pg_sz;
+ (1 << hr_dev->caps.mtt_buf_pg_sz);
+ page_shift += hr_dev->caps.mtt_buf_pg_sz;
ret = hns_roce_mtt_init(hr_dev, npages,
page_shift,
&hr_qp->mtt);
} else {
ret = hns_roce_mtt_init(hr_dev,
- ib_umem_page_count(hr_qp->umem),
- hr_qp->umem->page_shift,
- &hr_qp->mtt);
+ ib_umem_page_count(hr_qp->umem),
+ page_shift, &hr_qp->mtt);
}
if (ret) {
dev_err(dev, "hns_roce_mtt_init error for create qp\n");
@@ -652,9 +673,8 @@ static int hns_roce_create_qp_common(struct hns_roce_dev *hr_dev,
(udata->inlen >= sizeof(ucmd)) &&
(udata->outlen >= sizeof(resp)) &&
hns_roce_qp_has_sq(init_attr)) {
- ret = hns_roce_db_map_user(
- to_hr_ucontext(ib_pd->uobject->context),
- ucmd.sdb_addr, &hr_qp->sdb);
+ ret = hns_roce_db_map_user(uctx, udata, ucmd.sdb_addr,
+ &hr_qp->sdb);
if (ret) {
dev_err(dev, "sq record doorbell map failed!\n");
goto err_mtt;
@@ -668,13 +688,16 @@ static int hns_roce_create_qp_common(struct hns_roce_dev *hr_dev,
if ((hr_dev->caps.flags & HNS_ROCE_CAP_FLAG_RECORD_DB) &&
(udata->outlen >= sizeof(resp)) &&
hns_roce_qp_has_rq(init_attr)) {
- ret = hns_roce_db_map_user(
- to_hr_ucontext(ib_pd->uobject->context),
- ucmd.db_addr, &hr_qp->rdb);
+ ret = hns_roce_db_map_user(uctx, udata, ucmd.db_addr,
+ &hr_qp->rdb);
if (ret) {
dev_err(dev, "rq record doorbell map failed!\n");
goto err_sq_dbmap;
}
+
+ /* indicate kernel supports rq record db */
+ resp.cap_flags |= HNS_ROCE_SUPPORT_RQ_RECORD_DB;
+ hr_qp->rdb_en = 1;
}
} else {
if (init_attr->create_flags &
@@ -741,10 +764,10 @@ static int hns_roce_create_qp_common(struct hns_roce_dev *hr_dev,
goto err_mtt;
}
- hr_qp->sq.wrid = kmalloc_array(hr_qp->sq.wqe_cnt, sizeof(u64),
- GFP_KERNEL);
- hr_qp->rq.wrid = kmalloc_array(hr_qp->rq.wqe_cnt, sizeof(u64),
- GFP_KERNEL);
+ hr_qp->sq.wrid = kcalloc(hr_qp->sq.wqe_cnt, sizeof(u64),
+ GFP_KERNEL);
+ hr_qp->rq.wrid = kcalloc(hr_qp->rq.wqe_cnt, sizeof(u64),
+ GFP_KERNEL);
if (!hr_qp->sq.wrid || !hr_qp->rq.wrid) {
ret = -ENOMEM;
goto err_wrid;
@@ -783,17 +806,19 @@ static int hns_roce_create_qp_common(struct hns_roce_dev *hr_dev,
else
hr_qp->doorbell_qpn = cpu_to_le64(hr_qp->qpn);
- if (udata && (udata->outlen >= sizeof(resp)) &&
- (hr_dev->caps.flags & HNS_ROCE_CAP_FLAG_RECORD_DB)) {
-
- /* indicate kernel supports rq record db */
- resp.cap_flags |= HNS_ROCE_SUPPORT_RQ_RECORD_DB;
- ret = ib_copy_to_udata(udata, &resp, sizeof(resp));
+ if (udata) {
+ ret = ib_copy_to_udata(udata, &resp,
+ min(udata->outlen, sizeof(resp)));
if (ret)
goto err_qp;
+ }
- hr_qp->rdb_en = 1;
+ if (hr_dev->caps.flags & HNS_ROCE_CAP_FLAG_QP_FLOW_CTRL) {
+ ret = hr_dev->hw->qp_flow_control_init(hr_dev, hr_qp);
+ if (ret)
+ goto err_qp;
}
+
hr_qp->event = hns_roce_ib_qp_event;
return 0;
@@ -814,9 +839,7 @@ err_wrid:
if ((hr_dev->caps.flags & HNS_ROCE_CAP_FLAG_RECORD_DB) &&
(udata->outlen >= sizeof(resp)) &&
hns_roce_qp_has_rq(init_attr))
- hns_roce_db_unmap_user(
- to_hr_ucontext(ib_pd->uobject->context),
- &hr_qp->rdb);
+ hns_roce_db_unmap_user(uctx, &hr_qp->rdb);
} else {
kfree(hr_qp->sq.wrid);
kfree(hr_qp->rq.wrid);
@@ -828,9 +851,7 @@ err_sq_dbmap:
(udata->inlen >= sizeof(ucmd)) &&
(udata->outlen >= sizeof(resp)) &&
hns_roce_qp_has_sq(init_attr))
- hns_roce_db_unmap_user(
- to_hr_ucontext(ib_pd->uobject->context),
- &hr_qp->sdb);
+ hns_roce_db_unmap_user(uctx, &hr_qp->sdb);
err_mtt:
hns_roce_mtt_cleanup(hr_dev, &hr_qp->mtt);
@@ -969,7 +990,9 @@ int hns_roce_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
(attr_mask & IB_QP_STATE) && new_state == IB_QPS_ERR) {
if (hr_qp->sdb_en == 1) {
hr_qp->sq.head = *(int *)(hr_qp->sdb.virt_addr);
- hr_qp->rq.head = *(int *)(hr_qp->rdb.virt_addr);
+
+ if (hr_qp->rdb_en == 1)
+ hr_qp->rq.head = *(int *)(hr_qp->rdb.virt_addr);
} else {
dev_warn(dev, "flush cqe is not supported in userspace!\n");
goto out;
@@ -1133,6 +1156,7 @@ int hns_roce_init_qp_table(struct hns_roce_dev *hr_dev)
int reserved_from_bot;
int ret;
+ mutex_init(&qp_table->scc_mutex);
spin_lock_init(&qp_table->lock);
INIT_RADIX_TREE(&hr_dev->qp_table_tree, GFP_ATOMIC);
diff --git a/drivers/infiniband/hw/hns/hns_roce_srq.c b/drivers/infiniband/hw/hns/hns_roce_srq.c
index 12deacf442cf..a8ee2f6da967 100644
--- a/drivers/infiniband/hw/hns/hns_roce_srq.c
+++ b/drivers/infiniband/hw/hns/hns_roce_srq.c
@@ -78,9 +78,9 @@ static int hns_roce_hw2sw_srq(struct hns_roce_dev *dev,
HNS_ROCE_CMD_TIMEOUT_MSECS);
}
-int hns_roce_srq_alloc(struct hns_roce_dev *hr_dev, u32 pdn, u32 cqn, u16 xrcd,
- struct hns_roce_mtt *hr_mtt, u64 db_rec_addr,
- struct hns_roce_srq *srq)
+static int hns_roce_srq_alloc(struct hns_roce_dev *hr_dev, u32 pdn, u32 cqn,
+ u16 xrcd, struct hns_roce_mtt *hr_mtt,
+ u64 db_rec_addr, struct hns_roce_srq *srq)
{
struct hns_roce_srq_table *srq_table = &hr_dev->srq_table;
struct hns_roce_cmd_mailbox *mailbox;
@@ -155,7 +155,8 @@ err_out:
return ret;
}
-void hns_roce_srq_free(struct hns_roce_dev *hr_dev, struct hns_roce_srq *srq)
+static void hns_roce_srq_free(struct hns_roce_dev *hr_dev,
+ struct hns_roce_srq *srq)
{
struct hns_roce_srq_table *srq_table = &hr_dev->srq_table;
int ret;
@@ -253,8 +254,8 @@ struct ib_srq *hns_roce_create_srq(struct ib_pd *pd,
goto err_srq;
}
- srq->umem = ib_umem_get(pd->uobject->context, ucmd.buf_addr,
- srq_buf_size, 0, 0);
+ srq->umem =
+ ib_umem_get(udata, ucmd.buf_addr, srq_buf_size, 0, 0);
if (IS_ERR(srq->umem)) {
ret = PTR_ERR(srq->umem);
goto err_srq;
@@ -281,8 +282,7 @@ struct ib_srq *hns_roce_create_srq(struct ib_pd *pd,
goto err_srq_mtt;
/* config index queue BA */
- srq->idx_que.umem = ib_umem_get(pd->uobject->context,
- ucmd.que_addr,
+ srq->idx_que.umem = ib_umem_get(udata, ucmd.que_addr,
srq->idx_que.buf_size, 0, 0);
if (IS_ERR(srq->idx_que.umem)) {
dev_err(hr_dev->dev,
diff --git a/drivers/infiniband/hw/i40iw/Makefile b/drivers/infiniband/hw/i40iw/Makefile
index 5a8a7a3f28ae..8942f8229945 100644
--- a/drivers/infiniband/hw/i40iw/Makefile
+++ b/drivers/infiniband/hw/i40iw/Makefile
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
-ccflags-y := -Idrivers/net/ethernet/intel/i40e
+ccflags-y := -I $(srctree)/drivers/net/ethernet/intel/i40e
obj-$(CONFIG_INFINIBAND_I40IW) += i40iw.o
diff --git a/drivers/infiniband/hw/i40iw/i40iw_utils.c b/drivers/infiniband/hw/i40iw/i40iw_utils.c
index 59e978141ad4..337410f40860 100644
--- a/drivers/infiniband/hw/i40iw/i40iw_utils.c
+++ b/drivers/infiniband/hw/i40iw/i40iw_utils.c
@@ -173,7 +173,12 @@ int i40iw_inetaddr_event(struct notifier_block *notifier,
rcu_read_lock();
in = __in_dev_get_rcu(upper_dev);
- local_ipaddr = ntohl(in->ifa_list->ifa_address);
+
+ if (!in->ifa_list)
+ local_ipaddr = 0;
+ else
+ local_ipaddr = ntohl(in->ifa_list->ifa_address);
+
rcu_read_unlock();
} else {
local_ipaddr = ntohl(ifa->ifa_address);
@@ -185,6 +190,11 @@ int i40iw_inetaddr_event(struct notifier_block *notifier,
case NETDEV_UP:
/* Fall through */
case NETDEV_CHANGEADDR:
+
+ /* Just skip if no need to handle ARP cache */
+ if (!local_ipaddr)
+ break;
+
i40iw_manage_arp_cache(iwdev,
netdev->dev_addr,
&local_ipaddr,
@@ -601,7 +611,6 @@ void i40iw_rem_pdusecount(struct i40iw_pd *iwpd, struct i40iw_device *iwdev)
if (!atomic_dec_and_test(&iwpd->usecount))
return;
i40iw_free_resource(iwdev, iwdev->allocated_pds, iwpd->sc_pd.pd_id);
- kfree(iwpd);
}
/**
diff --git a/drivers/infiniband/hw/i40iw/i40iw_verbs.c b/drivers/infiniband/hw/i40iw/i40iw_verbs.c
index 0b675b0742c2..a8352e3ca23d 100644
--- a/drivers/infiniband/hw/i40iw/i40iw_verbs.c
+++ b/drivers/infiniband/hw/i40iw/i40iw_verbs.c
@@ -45,6 +45,7 @@
#include <rdma/iw_cm.h>
#include <rdma/ib_user_verbs.h>
#include <rdma/ib_umem.h>
+#include <rdma/uverbs_ioctl.h>
#include "i40iw.h"
/**
@@ -120,78 +121,55 @@ static int i40iw_query_port(struct ib_device *ibdev,
/**
* i40iw_alloc_ucontext - Allocate the user context data structure
- * @ibdev: device pointer from stack
+ * @uctx: Uverbs context pointer from stack
* @udata: user data
*
* This keeps track of all objects associated with a particular
* user-mode client.
*/
-static struct ib_ucontext *i40iw_alloc_ucontext(struct ib_device *ibdev,
- struct ib_udata *udata)
+static int i40iw_alloc_ucontext(struct ib_ucontext *uctx,
+ struct ib_udata *udata)
{
+ struct ib_device *ibdev = uctx->device;
struct i40iw_device *iwdev = to_iwdev(ibdev);
struct i40iw_alloc_ucontext_req req;
- struct i40iw_alloc_ucontext_resp uresp;
- struct i40iw_ucontext *ucontext;
+ struct i40iw_alloc_ucontext_resp uresp = {};
+ struct i40iw_ucontext *ucontext = to_ucontext(uctx);
if (ib_copy_from_udata(&req, udata, sizeof(req)))
- return ERR_PTR(-EINVAL);
+ return -EINVAL;
if (req.userspace_ver < 4 || req.userspace_ver > I40IW_ABI_VER) {
i40iw_pr_err("Unsupported provider library version %u.\n", req.userspace_ver);
- return ERR_PTR(-EINVAL);
+ return -EINVAL;
}
- memset(&uresp, 0, sizeof(uresp));
uresp.max_qps = iwdev->max_qp;
uresp.max_pds = iwdev->max_pd;
uresp.wq_size = iwdev->max_qp_wr * 2;
uresp.kernel_ver = req.userspace_ver;
- ucontext = kzalloc(sizeof(*ucontext), GFP_KERNEL);
- if (!ucontext)
- return ERR_PTR(-ENOMEM);
-
ucontext->iwdev = iwdev;
ucontext->abi_ver = req.userspace_ver;
- if (ib_copy_to_udata(udata, &uresp, sizeof(uresp))) {
- kfree(ucontext);
- return ERR_PTR(-EFAULT);
- }
+ if (ib_copy_to_udata(udata, &uresp, sizeof(uresp)))
+ return -EFAULT;
INIT_LIST_HEAD(&ucontext->cq_reg_mem_list);
spin_lock_init(&ucontext->cq_reg_mem_list_lock);
INIT_LIST_HEAD(&ucontext->qp_reg_mem_list);
spin_lock_init(&ucontext->qp_reg_mem_list_lock);
- return &ucontext->ibucontext;
+ return 0;
}
/**
* i40iw_dealloc_ucontext - deallocate the user context data structure
* @context: user context created during alloc
*/
-static int i40iw_dealloc_ucontext(struct ib_ucontext *context)
+static void i40iw_dealloc_ucontext(struct ib_ucontext *context)
{
- struct i40iw_ucontext *ucontext = to_ucontext(context);
- unsigned long flags;
-
- spin_lock_irqsave(&ucontext->cq_reg_mem_list_lock, flags);
- if (!list_empty(&ucontext->cq_reg_mem_list)) {
- spin_unlock_irqrestore(&ucontext->cq_reg_mem_list_lock, flags);
- return -EBUSY;
- }
- spin_unlock_irqrestore(&ucontext->cq_reg_mem_list_lock, flags);
- spin_lock_irqsave(&ucontext->qp_reg_mem_list_lock, flags);
- if (!list_empty(&ucontext->qp_reg_mem_list)) {
- spin_unlock_irqrestore(&ucontext->qp_reg_mem_list_lock, flags);
- return -EBUSY;
- }
- spin_unlock_irqrestore(&ucontext->qp_reg_mem_list_lock, flags);
-
- kfree(ucontext);
- return 0;
+ return;
}
/**
@@ -312,16 +290,15 @@ static void i40iw_dealloc_push_page(struct i40iw_device *iwdev, struct i40iw_sc_
/**
* i40iw_alloc_pd - allocate protection domain
- * @ibdev: device pointer from stack
+ * @pd: PD pointer
* @context: user context created during alloc
* @udata: user data
*/
-static struct ib_pd *i40iw_alloc_pd(struct ib_device *ibdev,
- struct ib_ucontext *context,
- struct ib_udata *udata)
+static int i40iw_alloc_pd(struct ib_pd *pd, struct ib_ucontext *context,
+ struct ib_udata *udata)
{
- struct i40iw_pd *iwpd;
- struct i40iw_device *iwdev = to_iwdev(ibdev);
+ struct i40iw_pd *iwpd = to_iwpd(pd);
+ struct i40iw_device *iwdev = to_iwdev(pd->device);
struct i40iw_sc_dev *dev = &iwdev->sc_dev;
struct i40iw_alloc_pd_resp uresp;
struct i40iw_sc_pd *sc_pd;
@@ -330,19 +307,13 @@ static struct ib_pd *i40iw_alloc_pd(struct ib_device *ibdev,
int err;
if (iwdev->closing)
- return ERR_PTR(-ENODEV);
+ return -ENODEV;
err = i40iw_alloc_resource(iwdev, iwdev->allocated_pds,
iwdev->max_pd, &pd_id, &iwdev->next_pd);
if (err) {
i40iw_pr_err("alloc resource failed\n");
- return ERR_PTR(err);
- }
-
- iwpd = kzalloc(sizeof(*iwpd), GFP_KERNEL);
- if (!iwpd) {
- err = -ENOMEM;
- goto free_res;
+ return err;
}
sc_pd = &iwpd->sc_pd;
@@ -361,25 +332,23 @@ static struct ib_pd *i40iw_alloc_pd(struct ib_device *ibdev,
}
i40iw_add_pdusecount(iwpd);
- return &iwpd->ibpd;
+ return 0;
+
error:
- kfree(iwpd);
-free_res:
i40iw_free_resource(iwdev, iwdev->allocated_pds, pd_id);
- return ERR_PTR(err);
+ return err;
}
/**
* i40iw_dealloc_pd - deallocate pd
* @ibpd: ptr of pd to be deallocated
*/
-static int i40iw_dealloc_pd(struct ib_pd *ibpd)
+static void i40iw_dealloc_pd(struct ib_pd *ibpd)
{
struct i40iw_pd *iwpd = to_iwpd(ibpd);
struct i40iw_device *iwdev = to_iwdev(ibpd->device);
i40iw_rem_pdusecount(iwpd, iwdev);
- return 0;
}
/**
@@ -565,7 +534,8 @@ static struct ib_qp *i40iw_create_qp(struct ib_pd *ibpd,
struct i40iw_device *iwdev = to_iwdev(ibpd->device);
struct i40iw_cqp *iwcqp = &iwdev->cqp;
struct i40iw_qp *iwqp;
- struct i40iw_ucontext *ucontext;
+ struct i40iw_ucontext *ucontext = rdma_udata_to_drv_context(
+ udata, struct i40iw_ucontext, ibucontext);
struct i40iw_create_qp_req req;
struct i40iw_create_qp_resp uresp;
u32 qp_num = 0;
@@ -674,7 +644,6 @@ static struct ib_qp *i40iw_create_qp(struct ib_pd *ibpd,
}
iwqp->ctx_info.qp_compl_ctx = req.user_compl_ctx;
iwqp->user_mode = 1;
- ucontext = to_ucontext(ibpd->uobject->context);
if (req.user_wqe_buffers) {
struct i40iw_pbl *iwpbl;
@@ -1369,32 +1338,29 @@ static void i40iw_copy_user_pgaddrs(struct i40iw_mr *iwmr,
{
struct ib_umem *region = iwmr->region;
struct i40iw_pbl *iwpbl = &iwmr->iwpbl;
- int chunk_pages, entry, i;
struct i40iw_pble_alloc *palloc = &iwpbl->pble_alloc;
struct i40iw_pble_info *pinfo;
- struct scatterlist *sg;
+ struct sg_dma_page_iter sg_iter;
u64 pg_addr = 0;
u32 idx = 0;
+ bool first_pg = true;
pinfo = (level == I40IW_LEVEL_1) ? NULL : palloc->level2.leaf;
- for_each_sg(region->sg_head.sgl, sg, region->nmap, entry) {
- chunk_pages = sg_dma_len(sg) >> region->page_shift;
- if ((iwmr->type == IW_MEMREG_TYPE_QP) &&
- !iwpbl->qp_mr.sq_page)
- iwpbl->qp_mr.sq_page = sg_page(sg);
- for (i = 0; i < chunk_pages; i++) {
- pg_addr = sg_dma_address(sg) +
- (i << region->page_shift);
-
- if ((entry + i) == 0)
- *pbl = cpu_to_le64(pg_addr & iwmr->page_msk);
- else if (!(pg_addr & ~iwmr->page_msk))
- *pbl = cpu_to_le64(pg_addr);
- else
- continue;
- pbl = i40iw_next_pbl_addr(pbl, &pinfo, &idx);
- }
+ if (iwmr->type == IW_MEMREG_TYPE_QP)
+ iwpbl->qp_mr.sq_page = sg_page(region->sg_head.sgl);
+
+ for_each_sg_dma_page (region->sg_head.sgl, &sg_iter, region->nmap, 0) {
+ pg_addr = sg_page_iter_dma_address(&sg_iter);
+ if (first_pg)
+ *pbl = cpu_to_le64(pg_addr & iwmr->page_msk);
+ else if (!(pg_addr & ~iwmr->page_msk))
+ *pbl = cpu_to_le64(pg_addr);
+ else
+ continue;
+
+ first_pg = false;
+ pbl = i40iw_next_pbl_addr(pbl, &pinfo, &idx);
}
}
@@ -1831,7 +1797,8 @@ static struct ib_mr *i40iw_reg_user_mr(struct ib_pd *pd,
{
struct i40iw_pd *iwpd = to_iwpd(pd);
struct i40iw_device *iwdev = to_iwdev(pd->device);
- struct i40iw_ucontext *ucontext;
+ struct i40iw_ucontext *ucontext = rdma_udata_to_drv_context(
+ udata, struct i40iw_ucontext, ibucontext);
struct i40iw_pble_alloc *palloc;
struct i40iw_pbl *iwpbl;
struct i40iw_mr *iwmr;
@@ -1852,7 +1819,7 @@ static struct ib_mr *i40iw_reg_user_mr(struct ib_pd *pd,
if (length > I40IW_MAX_MR_SIZE)
return ERR_PTR(-EINVAL);
- region = ib_umem_get(pd->uobject->context, start, length, acc, 0);
+ region = ib_umem_get(udata, start, length, acc, 0);
if (IS_ERR(region))
return (struct ib_mr *)region;
@@ -1872,7 +1839,6 @@ static struct ib_mr *i40iw_reg_user_mr(struct ib_pd *pd,
iwmr->region = region;
iwmr->ibmr.pd = pd;
iwmr->ibmr.device = pd->device;
- ucontext = to_ucontext(pd->uobject->context);
iwmr->page_size = PAGE_SIZE;
iwmr->page_msk = PAGE_MASK;
@@ -2139,9 +2105,8 @@ static int i40iw_dereg_mr(struct ib_mr *ib_mr)
static ssize_t hw_rev_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct i40iw_ib_device *iwibdev = container_of(dev,
- struct i40iw_ib_device,
- ibdev.dev);
+ struct i40iw_ib_device *iwibdev =
+ rdma_device_to_drv_device(dev, struct i40iw_ib_device, ibdev);
u32 hw_rev = iwibdev->iwdev->sc_dev.hw_rev;
return sprintf(buf, "%x\n", hw_rev);
@@ -2751,6 +2716,8 @@ static const struct ib_device_ops i40iw_dev_ops = {
.query_qp = i40iw_query_qp,
.reg_user_mr = i40iw_reg_user_mr,
.req_notify_cq = i40iw_req_notify_cq,
+ INIT_RDMA_OBJ_SIZE(ib_pd, i40iw_pd, ibpd),
+ INIT_RDMA_OBJ_SIZE(ib_ucontext, i40iw_ucontext, ibucontext),
};
/**
@@ -2763,7 +2730,7 @@ static struct i40iw_ib_device *i40iw_init_rdma_device(struct i40iw_device *iwdev
struct net_device *netdev = iwdev->netdev;
struct pci_dev *pcidev = (struct pci_dev *)iwdev->hw.dev_context;
- iwibdev = (struct i40iw_ib_device *)ib_alloc_device(sizeof(*iwibdev));
+ iwibdev = ib_alloc_device(i40iw_ib_device, ibdev);
if (!iwibdev) {
i40iw_pr_err("iwdev == NULL\n");
return NULL;
@@ -2868,7 +2835,7 @@ int i40iw_register_rdma_device(struct i40iw_device *iwdev)
iwibdev = iwdev->iwibdev;
rdma_set_device_sysfs_group(&iwibdev->ibdev, &i40iw_attr_group);
iwibdev->ibdev.driver_id = RDMA_DRIVER_I40IW;
- ret = ib_register_device(&iwibdev->ibdev, "i40iw%d", NULL);
+ ret = ib_register_device(&iwibdev->ibdev, "i40iw%d");
if (ret)
goto error;
diff --git a/drivers/infiniband/hw/mlx4/Kconfig b/drivers/infiniband/hw/mlx4/Kconfig
index 4e9936731867..fc01deac1d3c 100644
--- a/drivers/infiniband/hw/mlx4/Kconfig
+++ b/drivers/infiniband/hw/mlx4/Kconfig
@@ -1,7 +1,6 @@
config MLX4_INFINIBAND
tristate "Mellanox ConnectX HCA support"
depends on NETDEVICES && ETHERNET && PCI && INET
- depends on INFINIBAND_USER_ACCESS || !INFINIBAND_USER_ACCESS
select NET_VENDOR_MELLANOX
select MLX4_CORE
---help---
diff --git a/drivers/infiniband/hw/mlx4/alias_GUID.c b/drivers/infiniband/hw/mlx4/alias_GUID.c
index 782499abcd98..2a0b59a4b6eb 100644
--- a/drivers/infiniband/hw/mlx4/alias_GUID.c
+++ b/drivers/infiniband/hw/mlx4/alias_GUID.c
@@ -804,8 +804,8 @@ void mlx4_ib_destroy_alias_guid_service(struct mlx4_ib_dev *dev)
unsigned long flags;
for (i = 0 ; i < dev->num_ports; i++) {
- cancel_delayed_work(&dev->sriov.alias_guid.ports_guid[i].alias_guid_work);
det = &sriov->alias_guid.ports_guid[i];
+ cancel_delayed_work_sync(&det->alias_guid_work);
spin_lock_irqsave(&sriov->alias_guid.ag_work_lock, flags);
while (!list_empty(&det->cb_list)) {
cb_ctx = list_entry(det->cb_list.next,
diff --git a/drivers/infiniband/hw/mlx4/cm.c b/drivers/infiniband/hw/mlx4/cm.c
index fedaf8260105..8c79a480f2b7 100644
--- a/drivers/infiniband/hw/mlx4/cm.c
+++ b/drivers/infiniband/hw/mlx4/cm.c
@@ -39,7 +39,7 @@
#include "mlx4_ib.h"
-#define CM_CLEANUP_CACHE_TIMEOUT (5 * HZ)
+#define CM_CLEANUP_CACHE_TIMEOUT (30 * HZ)
struct id_map_entry {
struct rb_node node;
diff --git a/drivers/infiniband/hw/mlx4/cq.c b/drivers/infiniband/hw/mlx4/cq.c
index 43512347b4f0..03ac72339dd2 100644
--- a/drivers/infiniband/hw/mlx4/cq.c
+++ b/drivers/infiniband/hw/mlx4/cq.c
@@ -134,16 +134,16 @@ static void mlx4_ib_free_cq_buf(struct mlx4_ib_dev *dev, struct mlx4_ib_cq_buf *
mlx4_buf_free(dev->dev, (cqe + 1) * buf->entry_size, &buf->buf);
}
-static int mlx4_ib_get_cq_umem(struct mlx4_ib_dev *dev, struct ib_ucontext *context,
- struct mlx4_ib_cq_buf *buf, struct ib_umem **umem,
- u64 buf_addr, int cqe)
+static int mlx4_ib_get_cq_umem(struct mlx4_ib_dev *dev, struct ib_udata *udata,
+ struct mlx4_ib_cq_buf *buf,
+ struct ib_umem **umem, u64 buf_addr, int cqe)
{
int err;
int cqe_size = dev->dev->caps.cqe_size;
int shift;
int n;
- *umem = ib_umem_get(context, buf_addr, cqe * cqe_size,
+ *umem = ib_umem_get(udata, buf_addr, cqe * cqe_size,
IB_ACCESS_LOCAL_WRITE, 1);
if (IS_ERR(*umem))
return PTR_ERR(*umem);
@@ -190,7 +190,7 @@ struct ib_cq *mlx4_ib_create_cq(struct ib_device *ibdev,
if (attr->flags & ~CQ_CREATE_FLAGS_SUPPORTED)
return ERR_PTR(-EINVAL);
- cq = kmalloc(sizeof *cq, GFP_KERNEL);
+ cq = kzalloc(sizeof(*cq), GFP_KERNEL);
if (!cq)
return ERR_PTR(-ENOMEM);
@@ -213,14 +213,13 @@ struct ib_cq *mlx4_ib_create_cq(struct ib_device *ibdev,
}
buf_addr = (void *)(unsigned long)ucmd.buf_addr;
-
- err = mlx4_ib_get_cq_umem(dev, context, &cq->buf, &cq->umem,
+ err = mlx4_ib_get_cq_umem(dev, udata, &cq->buf, &cq->umem,
ucmd.buf_addr, entries);
if (err)
goto err_cq;
- err = mlx4_ib_db_map_user(to_mucontext(context), ucmd.db_addr,
- &cq->db);
+ err = mlx4_ib_db_map_user(to_mucontext(context), udata,
+ ucmd.db_addr, &cq->db);
if (err)
goto err_mtt;
@@ -336,7 +335,7 @@ static int mlx4_alloc_resize_umem(struct mlx4_ib_dev *dev, struct mlx4_ib_cq *cq
if (!cq->resize_buf)
return -ENOMEM;
- err = mlx4_ib_get_cq_umem(dev, cq->umem->context, &cq->resize_buf->buf,
+ err = mlx4_ib_get_cq_umem(dev, udata, &cq->resize_buf->buf,
&cq->resize_umem, ucmd.buf_addr, entries);
if (err) {
kfree(cq->resize_buf);
diff --git a/drivers/infiniband/hw/mlx4/doorbell.c b/drivers/infiniband/hw/mlx4/doorbell.c
index c51740986367..3aab71b29ce8 100644
--- a/drivers/infiniband/hw/mlx4/doorbell.c
+++ b/drivers/infiniband/hw/mlx4/doorbell.c
@@ -41,7 +41,8 @@ struct mlx4_ib_user_db_page {
int refcnt;
};
-int mlx4_ib_db_map_user(struct mlx4_ib_ucontext *context, unsigned long virt,
+int mlx4_ib_db_map_user(struct mlx4_ib_ucontext *context,
+ struct ib_udata *udata, unsigned long virt,
struct mlx4_db *db)
{
struct mlx4_ib_user_db_page *page;
@@ -61,8 +62,7 @@ int mlx4_ib_db_map_user(struct mlx4_ib_ucontext *context, unsigned long virt,
page->user_virt = (virt & PAGE_MASK);
page->refcnt = 0;
- page->umem = ib_umem_get(&context->ibucontext, virt & PAGE_MASK,
- PAGE_SIZE, 0, 0);
+ page->umem = ib_umem_get(udata, virt & PAGE_MASK, PAGE_SIZE, 0, 0);
if (IS_ERR(page->umem)) {
err = PTR_ERR(page->umem);
kfree(page);
diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c
index 1f15ec3e2b83..733f7bbd5901 100644
--- a/drivers/infiniband/hw/mlx4/main.c
+++ b/drivers/infiniband/hw/mlx4/main.c
@@ -1076,17 +1076,18 @@ out:
return err;
}
-static struct ib_ucontext *mlx4_ib_alloc_ucontext(struct ib_device *ibdev,
- struct ib_udata *udata)
+static int mlx4_ib_alloc_ucontext(struct ib_ucontext *uctx,
+ struct ib_udata *udata)
{
+ struct ib_device *ibdev = uctx->device;
struct mlx4_ib_dev *dev = to_mdev(ibdev);
- struct mlx4_ib_ucontext *context;
+ struct mlx4_ib_ucontext *context = to_mucontext(uctx);
struct mlx4_ib_alloc_ucontext_resp_v3 resp_v3;
struct mlx4_ib_alloc_ucontext_resp resp;
int err;
if (!dev->ib_active)
- return ERR_PTR(-EAGAIN);
+ return -EAGAIN;
if (ibdev->uverbs_abi_ver == MLX4_IB_UVERBS_NO_DEV_CAPS_ABI_VERSION) {
resp_v3.qp_tab_size = dev->dev->caps.num_qps;
@@ -1100,15 +1101,9 @@ static struct ib_ucontext *mlx4_ib_alloc_ucontext(struct ib_device *ibdev,
resp.cqe_size = dev->dev->caps.cqe_size;
}
- context = kzalloc(sizeof(*context), GFP_KERNEL);
- if (!context)
- return ERR_PTR(-ENOMEM);
-
err = mlx4_uar_alloc(to_mdev(ibdev)->dev, &context->uar);
- if (err) {
- kfree(context);
- return ERR_PTR(err);
- }
+ if (err)
+ return err;
INIT_LIST_HEAD(&context->db_page_list);
mutex_init(&context->db_page_mutex);
@@ -1123,21 +1118,17 @@ static struct ib_ucontext *mlx4_ib_alloc_ucontext(struct ib_device *ibdev,
if (err) {
mlx4_uar_free(to_mdev(ibdev)->dev, &context->uar);
- kfree(context);
- return ERR_PTR(-EFAULT);
+ return -EFAULT;
}
- return &context->ibucontext;
+ return err;
}
-static int mlx4_ib_dealloc_ucontext(struct ib_ucontext *ibcontext)
+static void mlx4_ib_dealloc_ucontext(struct ib_ucontext *ibcontext)
{
struct mlx4_ib_ucontext *context = to_mucontext(ibcontext);
mlx4_uar_free(to_mdev(ibcontext->device)->dev, &context->uar);
- kfree(context);
-
- return 0;
}
static void mlx4_ib_disassociate_ucontext(struct ib_ucontext *ibcontext)
@@ -1186,38 +1177,27 @@ static int mlx4_ib_mmap(struct ib_ucontext *context, struct vm_area_struct *vma)
}
}
-static struct ib_pd *mlx4_ib_alloc_pd(struct ib_device *ibdev,
- struct ib_ucontext *context,
- struct ib_udata *udata)
+static int mlx4_ib_alloc_pd(struct ib_pd *ibpd, struct ib_ucontext *context,
+ struct ib_udata *udata)
{
- struct mlx4_ib_pd *pd;
+ struct mlx4_ib_pd *pd = to_mpd(ibpd);
+ struct ib_device *ibdev = ibpd->device;
int err;
- pd = kzalloc(sizeof(*pd), GFP_KERNEL);
- if (!pd)
- return ERR_PTR(-ENOMEM);
-
err = mlx4_pd_alloc(to_mdev(ibdev)->dev, &pd->pdn);
- if (err) {
- kfree(pd);
- return ERR_PTR(err);
- }
+ if (err)
+ return err;
- if (context)
- if (ib_copy_to_udata(udata, &pd->pdn, sizeof (__u32))) {
- mlx4_pd_free(to_mdev(ibdev)->dev, pd->pdn);
- kfree(pd);
- return ERR_PTR(-EFAULT);
- }
- return &pd->ibpd;
+ if (context && ib_copy_to_udata(udata, &pd->pdn, sizeof(__u32))) {
+ mlx4_pd_free(to_mdev(ibdev)->dev, pd->pdn);
+ return -EFAULT;
+ }
+ return 0;
}
-static int mlx4_ib_dealloc_pd(struct ib_pd *pd)
+static void mlx4_ib_dealloc_pd(struct ib_pd *pd)
{
mlx4_pd_free(to_mdev(pd->device)->dev, to_mpd(pd)->pdn);
- kfree(pd);
-
- return 0;
}
static struct ib_xrcd *mlx4_ib_alloc_xrcd(struct ib_device *ibdev,
@@ -2043,7 +2023,7 @@ static ssize_t hca_type_show(struct device *device,
struct device_attribute *attr, char *buf)
{
struct mlx4_ib_dev *dev =
- container_of(device, struct mlx4_ib_dev, ib_dev.dev);
+ rdma_device_to_drv_device(device, struct mlx4_ib_dev, ib_dev);
return sprintf(buf, "MT%d\n", dev->dev->persist->pdev->device);
}
static DEVICE_ATTR_RO(hca_type);
@@ -2052,7 +2032,7 @@ static ssize_t hw_rev_show(struct device *device,
struct device_attribute *attr, char *buf)
{
struct mlx4_ib_dev *dev =
- container_of(device, struct mlx4_ib_dev, ib_dev.dev);
+ rdma_device_to_drv_device(device, struct mlx4_ib_dev, ib_dev);
return sprintf(buf, "%x\n", dev->dev->rev_id);
}
static DEVICE_ATTR_RO(hw_rev);
@@ -2061,7 +2041,8 @@ static ssize_t board_id_show(struct device *device,
struct device_attribute *attr, char *buf)
{
struct mlx4_ib_dev *dev =
- container_of(device, struct mlx4_ib_dev, ib_dev.dev);
+ rdma_device_to_drv_device(device, struct mlx4_ib_dev, ib_dev);
+
return sprintf(buf, "%.*s\n", MLX4_BOARD_ID_LEN,
dev->dev->board_id);
}
@@ -2579,6 +2560,8 @@ static const struct ib_device_ops mlx4_ib_dev_ops = {
.req_notify_cq = mlx4_ib_arm_cq,
.rereg_user_mr = mlx4_ib_rereg_user_mr,
.resize_cq = mlx4_ib_resize_cq,
+ INIT_RDMA_OBJ_SIZE(ib_pd, mlx4_ib_pd, ibpd),
+ INIT_RDMA_OBJ_SIZE(ib_ucontext, mlx4_ib_ucontext, ibucontext),
};
static const struct ib_device_ops mlx4_ib_dev_wq_ops = {
@@ -2634,7 +2617,7 @@ static void *mlx4_ib_add(struct mlx4_dev *dev)
if (num_ports == 0)
return NULL;
- ibdev = (struct mlx4_ib_dev *) ib_alloc_device(sizeof *ibdev);
+ ibdev = ib_alloc_device(mlx4_ib_dev, ib_dev);
if (!ibdev) {
dev_err(&dev->persist->pdev->dev,
"Device struct alloc failed\n");
@@ -2856,7 +2839,7 @@ static void *mlx4_ib_add(struct mlx4_dev *dev)
rdma_set_device_sysfs_group(&ibdev->ib_dev, &mlx4_attr_group);
ibdev->ib_dev.driver_id = RDMA_DRIVER_MLX4;
- if (ib_register_device(&ibdev->ib_dev, "mlx4_%d", NULL))
+ if (ib_register_device(&ibdev->ib_dev, "mlx4_%d"))
goto err_diag_counters;
if (mlx4_ib_mad_init(ibdev))
diff --git a/drivers/infiniband/hw/mlx4/mlx4_ib.h b/drivers/infiniband/hw/mlx4/mlx4_ib.h
index e491f3eda6e7..60dc1347c5ab 100644
--- a/drivers/infiniband/hw/mlx4/mlx4_ib.h
+++ b/drivers/infiniband/hw/mlx4/mlx4_ib.h
@@ -722,7 +722,8 @@ static inline u8 mlx4_ib_bond_next_port(struct mlx4_ib_dev *dev)
int mlx4_ib_init_sriov(struct mlx4_ib_dev *dev);
void mlx4_ib_close_sriov(struct mlx4_ib_dev *dev);
-int mlx4_ib_db_map_user(struct mlx4_ib_ucontext *context, unsigned long virt,
+int mlx4_ib_db_map_user(struct mlx4_ib_ucontext *context,
+ struct ib_udata *udata, unsigned long virt,
struct mlx4_db *db);
void mlx4_ib_db_unmap_user(struct mlx4_ib_ucontext *context, struct mlx4_db *db);
diff --git a/drivers/infiniband/hw/mlx4/mr.c b/drivers/infiniband/hw/mlx4/mr.c
index c7c85c22e4e3..395379a480cb 100644
--- a/drivers/infiniband/hw/mlx4/mr.c
+++ b/drivers/infiniband/hw/mlx4/mr.c
@@ -367,7 +367,7 @@ end:
return block_shift;
}
-static struct ib_umem *mlx4_get_umem_mr(struct ib_ucontext *context, u64 start,
+static struct ib_umem *mlx4_get_umem_mr(struct ib_udata *udata, u64 start,
u64 length, u64 virt_addr,
int access_flags)
{
@@ -398,7 +398,7 @@ static struct ib_umem *mlx4_get_umem_mr(struct ib_ucontext *context, u64 start,
up_read(&current->mm->mmap_sem);
}
- return ib_umem_get(context, start, length, access_flags, 0);
+ return ib_umem_get(udata, start, length, access_flags, 0);
}
struct ib_mr *mlx4_ib_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
@@ -415,8 +415,8 @@ struct ib_mr *mlx4_ib_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
if (!mr)
return ERR_PTR(-ENOMEM);
- mr->umem = mlx4_get_umem_mr(pd->uobject->context, start, length,
- virt_addr, access_flags);
+ mr->umem =
+ mlx4_get_umem_mr(udata, start, length, virt_addr, access_flags);
if (IS_ERR(mr->umem)) {
err = PTR_ERR(mr->umem);
goto err_free;
@@ -505,9 +505,8 @@ int mlx4_ib_rereg_user_mr(struct ib_mr *mr, int flags,
mlx4_mr_rereg_mem_cleanup(dev->dev, &mmr->mmr);
ib_umem_release(mmr->umem);
- mmr->umem =
- mlx4_get_umem_mr(mr->uobject->context, start, length,
- virt_addr, mr_access_flags);
+ mmr->umem = mlx4_get_umem_mr(udata, start, length, virt_addr,
+ mr_access_flags);
if (IS_ERR(mmr->umem)) {
err = PTR_ERR(mmr->umem);
/* Prevent mlx4_ib_dereg_mr from free'ing invalid pointer */
diff --git a/drivers/infiniband/hw/mlx4/qp.c b/drivers/infiniband/hw/mlx4/qp.c
index 971e9a9ebdaf..429a59c5801c 100644
--- a/drivers/infiniband/hw/mlx4/qp.c
+++ b/drivers/infiniband/hw/mlx4/qp.c
@@ -41,6 +41,7 @@
#include <rdma/ib_pack.h>
#include <rdma/ib_addr.h>
#include <rdma/ib_mad.h>
+#include <rdma/uverbs_ioctl.h>
#include <linux/mlx4/driver.h>
#include <linux/mlx4/qp.h>
@@ -52,7 +53,8 @@ static void mlx4_ib_lock_cqs(struct mlx4_ib_cq *send_cq,
struct mlx4_ib_cq *recv_cq);
static void mlx4_ib_unlock_cqs(struct mlx4_ib_cq *send_cq,
struct mlx4_ib_cq *recv_cq);
-static int _mlx4_ib_modify_wq(struct ib_wq *ibwq, enum ib_wq_state new_state);
+static int _mlx4_ib_modify_wq(struct ib_wq *ibwq, enum ib_wq_state new_state,
+ struct ib_udata *udata);
enum {
MLX4_IB_ACK_REQ_FREQ = 8,
@@ -863,6 +865,8 @@ static int create_qp_common(struct mlx4_ib_dev *dev, struct ib_pd *pd,
int err;
struct mlx4_ib_sqp *sqp = NULL;
struct mlx4_ib_qp *qp;
+ struct mlx4_ib_ucontext *context = rdma_udata_to_drv_context(
+ udata, struct mlx4_ib_ucontext, ibucontext);
enum mlx4_ib_qp_type qp_type = (enum mlx4_ib_qp_type) init_attr->qp_type;
struct mlx4_ib_cq *mcq;
unsigned long flags;
@@ -1015,9 +1019,11 @@ static int create_qp_common(struct mlx4_ib_dev *dev, struct ib_pd *pd,
(qp->sq.wqe_cnt << qp->sq.wqe_shift);
}
- qp->umem = ib_umem_get(pd->uobject->context,
- (src == MLX4_IB_QP_SRC) ? ucmd.qp.buf_addr :
- ucmd.wq.buf_addr, qp->buf_size, 0, 0);
+ qp->umem =
+ ib_umem_get(udata,
+ (src == MLX4_IB_QP_SRC) ? ucmd.qp.buf_addr :
+ ucmd.wq.buf_addr,
+ qp->buf_size, 0, 0);
if (IS_ERR(qp->umem)) {
err = PTR_ERR(qp->umem);
goto err;
@@ -1035,9 +1041,11 @@ static int create_qp_common(struct mlx4_ib_dev *dev, struct ib_pd *pd,
goto err_mtt;
if (qp_has_rq(init_attr)) {
- err = mlx4_ib_db_map_user(to_mucontext(pd->uobject->context),
+ err = mlx4_ib_db_map_user(
+ context, udata,
(src == MLX4_IB_QP_SRC) ? ucmd.qp.db_addr :
- ucmd.wq.db_addr, &qp->db);
+ ucmd.wq.db_addr,
+ &qp->db);
if (err)
goto err_mtt;
}
@@ -1108,8 +1116,7 @@ static int create_qp_common(struct mlx4_ib_dev *dev, struct ib_pd *pd,
}
}
} else if (src == MLX4_IB_RWQ_SRC) {
- err = mlx4_ib_alloc_wqn(to_mucontext(pd->uobject->context), qp,
- range_size, &qpn);
+ err = mlx4_ib_alloc_wqn(context, qp, range_size, &qpn);
if (err)
goto err_wrid;
} else {
@@ -1180,8 +1187,7 @@ err_qpn:
if (qp->flags & MLX4_IB_QP_NETIF)
mlx4_ib_steer_qp_free(dev, qpn, 1);
else if (src == MLX4_IB_RWQ_SRC)
- mlx4_ib_release_wqn(to_mucontext(pd->uobject->context),
- qp, 0);
+ mlx4_ib_release_wqn(context, qp, 0);
else
mlx4_qp_release_range(dev->dev, qpn, 1);
}
@@ -1191,7 +1197,7 @@ err_proxy:
err_wrid:
if (udata) {
if (qp_has_rq(init_attr))
- mlx4_ib_db_unmap_user(to_mucontext(pd->uobject->context), &qp->db);
+ mlx4_ib_db_unmap_user(context, &qp->db);
} else {
kvfree(qp->sq.wrid);
kvfree(qp->rq.wrid);
@@ -1938,7 +1944,8 @@ static u8 gid_type_to_qpc(enum ib_gid_type gid_type)
* Go over all RSS QP's childes (WQs) and apply their HW state according to
* their logic state if the RSS QP is the first RSS QP associated for the WQ.
*/
-static int bringup_rss_rwqs(struct ib_rwq_ind_table *ind_tbl, u8 port_num)
+static int bringup_rss_rwqs(struct ib_rwq_ind_table *ind_tbl, u8 port_num,
+ struct ib_udata *udata)
{
int err = 0;
int i;
@@ -1962,7 +1969,7 @@ static int bringup_rss_rwqs(struct ib_rwq_ind_table *ind_tbl, u8 port_num)
}
wq->port = port_num;
if ((wq->rss_usecnt == 0) && (ibwq->state == IB_WQS_RDY)) {
- err = _mlx4_ib_modify_wq(ibwq, IB_WQS_RDY);
+ err = _mlx4_ib_modify_wq(ibwq, IB_WQS_RDY, udata);
if (err) {
mutex_unlock(&wq->mutex);
break;
@@ -1984,7 +1991,8 @@ static int bringup_rss_rwqs(struct ib_rwq_ind_table *ind_tbl, u8 port_num)
if ((wq->rss_usecnt == 1) &&
(ibwq->state == IB_WQS_RDY))
- if (_mlx4_ib_modify_wq(ibwq, IB_WQS_RESET))
+ if (_mlx4_ib_modify_wq(ibwq, IB_WQS_RESET,
+ udata))
pr_warn("failed to reverse WQN=0x%06x\n",
ibwq->wq_num);
wq->rss_usecnt--;
@@ -1996,7 +2004,8 @@ static int bringup_rss_rwqs(struct ib_rwq_ind_table *ind_tbl, u8 port_num)
return err;
}
-static void bring_down_rss_rwqs(struct ib_rwq_ind_table *ind_tbl)
+static void bring_down_rss_rwqs(struct ib_rwq_ind_table *ind_tbl,
+ struct ib_udata *udata)
{
int i;
@@ -2007,7 +2016,7 @@ static void bring_down_rss_rwqs(struct ib_rwq_ind_table *ind_tbl)
mutex_lock(&wq->mutex);
if ((wq->rss_usecnt == 1) && (ibwq->state == IB_WQS_RDY))
- if (_mlx4_ib_modify_wq(ibwq, IB_WQS_RESET))
+ if (_mlx4_ib_modify_wq(ibwq, IB_WQS_RESET, udata))
pr_warn("failed to reverse WQN=%x\n",
ibwq->wq_num);
wq->rss_usecnt--;
@@ -2039,9 +2048,10 @@ static void fill_qp_rss_context(struct mlx4_qp_context *context,
static int __mlx4_ib_modify_qp(void *src, enum mlx4_ib_source_type src_type,
const struct ib_qp_attr *attr, int attr_mask,
- enum ib_qp_state cur_state, enum ib_qp_state new_state)
+ enum ib_qp_state cur_state,
+ enum ib_qp_state new_state,
+ struct ib_udata *udata)
{
- struct ib_uobject *ibuobject;
struct ib_srq *ibsrq;
const struct ib_gid_attr *gid_attr = NULL;
struct ib_rwq_ind_table *rwq_ind_tbl;
@@ -2050,6 +2060,8 @@ static int __mlx4_ib_modify_qp(void *src, enum mlx4_ib_source_type src_type,
struct mlx4_ib_qp *qp;
struct mlx4_ib_pd *pd;
struct mlx4_ib_cq *send_cq, *recv_cq;
+ struct mlx4_ib_ucontext *ucontext = rdma_udata_to_drv_context(
+ udata, struct mlx4_ib_ucontext, ibucontext);
struct mlx4_qp_context *context;
enum mlx4_qp_optpar optpar = 0;
int sqd_event;
@@ -2061,7 +2073,6 @@ static int __mlx4_ib_modify_qp(void *src, enum mlx4_ib_source_type src_type,
struct ib_wq *ibwq;
ibwq = (struct ib_wq *)src;
- ibuobject = ibwq->uobject;
ibsrq = NULL;
rwq_ind_tbl = NULL;
qp_type = IB_QPT_RAW_PACKET;
@@ -2072,7 +2083,6 @@ static int __mlx4_ib_modify_qp(void *src, enum mlx4_ib_source_type src_type,
struct ib_qp *ibqp;
ibqp = (struct ib_qp *)src;
- ibuobject = ibqp->uobject;
ibsrq = ibqp->srq;
rwq_ind_tbl = ibqp->rwq_ind_tbl;
qp_type = ibqp->qp_type;
@@ -2157,11 +2167,9 @@ static int __mlx4_ib_modify_qp(void *src, enum mlx4_ib_source_type src_type,
context->param3 |= cpu_to_be32(1 << 30);
}
- if (ibuobject)
+ if (ucontext)
context->usr_page = cpu_to_be32(
- mlx4_to_hw_uar_index(dev->dev,
- to_mucontext(ibuobject->context)
- ->uar.index));
+ mlx4_to_hw_uar_index(dev->dev, ucontext->uar.index));
else
context->usr_page = cpu_to_be32(
mlx4_to_hw_uar_index(dev->dev, dev->priv_uar.index));
@@ -2293,7 +2301,7 @@ static int __mlx4_ib_modify_qp(void *src, enum mlx4_ib_source_type src_type,
context->cqn_recv = cpu_to_be32(recv_cq->mcq.cqn);
/* Set "fast registration enabled" for all kernel QPs */
- if (!ibuobject)
+ if (!ucontext)
context->params1 |= cpu_to_be32(1 << 11);
if (attr_mask & IB_QP_RNR_RETRY) {
@@ -2430,7 +2438,7 @@ static int __mlx4_ib_modify_qp(void *src, enum mlx4_ib_source_type src_type,
else
sqd_event = 0;
- if (!ibuobject &&
+ if (!ucontext &&
cur_state == IB_QPS_RESET &&
new_state == IB_QPS_INIT)
context->rlkey_roce_mode |= (1 << 4);
@@ -2441,7 +2449,7 @@ static int __mlx4_ib_modify_qp(void *src, enum mlx4_ib_source_type src_type,
* headroom is stamped so that the hardware doesn't start
* processing stale work requests.
*/
- if (!ibuobject &&
+ if (!ucontext &&
cur_state == IB_QPS_RESET &&
new_state == IB_QPS_INIT) {
struct mlx4_wqe_ctrl_seg *ctrl;
@@ -2505,7 +2513,7 @@ static int __mlx4_ib_modify_qp(void *src, enum mlx4_ib_source_type src_type,
* entries and reinitialize the QP.
*/
if (new_state == IB_QPS_RESET) {
- if (!ibuobject) {
+ if (!ucontext) {
mlx4_ib_cq_clean(recv_cq, qp->mqp.qpn,
ibsrq ? to_msrq(ibsrq) : NULL);
if (send_cq != recv_cq)
@@ -2731,16 +2739,17 @@ static int _mlx4_ib_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
}
if (ibqp->rwq_ind_tbl && (new_state == IB_QPS_INIT)) {
- err = bringup_rss_rwqs(ibqp->rwq_ind_tbl, attr->port_num);
+ err = bringup_rss_rwqs(ibqp->rwq_ind_tbl, attr->port_num,
+ udata);
if (err)
goto out;
}
err = __mlx4_ib_modify_qp(ibqp, MLX4_IB_QP_SRC, attr, attr_mask,
- cur_state, new_state);
+ cur_state, new_state, udata);
if (ibqp->rwq_ind_tbl && err)
- bring_down_rss_rwqs(ibqp->rwq_ind_tbl);
+ bring_down_rss_rwqs(ibqp->rwq_ind_tbl, udata);
if (mlx4_is_bonded(dev->dev) && (attr_mask & IB_QP_PORT))
attr->port_num = 1;
@@ -4118,7 +4127,8 @@ static int ib_wq2qp_state(enum ib_wq_state state)
}
}
-static int _mlx4_ib_modify_wq(struct ib_wq *ibwq, enum ib_wq_state new_state)
+static int _mlx4_ib_modify_wq(struct ib_wq *ibwq, enum ib_wq_state new_state,
+ struct ib_udata *udata)
{
struct mlx4_ib_qp *qp = to_mqp((struct ib_qp *)ibwq);
enum ib_qp_state qp_cur_state;
@@ -4142,7 +4152,8 @@ static int _mlx4_ib_modify_wq(struct ib_wq *ibwq, enum ib_wq_state new_state)
attr_mask = IB_QP_PORT;
err = __mlx4_ib_modify_qp(ibwq, MLX4_IB_RWQ_SRC, &attr,
- attr_mask, IB_QPS_RESET, IB_QPS_INIT);
+ attr_mask, IB_QPS_RESET, IB_QPS_INIT,
+ udata);
if (err) {
pr_debug("WQN=0x%06x failed to apply RST->INIT on the HW QP\n",
ibwq->wq_num);
@@ -4154,12 +4165,13 @@ static int _mlx4_ib_modify_wq(struct ib_wq *ibwq, enum ib_wq_state new_state)
attr_mask = 0;
err = __mlx4_ib_modify_qp(ibwq, MLX4_IB_RWQ_SRC, NULL, attr_mask,
- qp_cur_state, qp_new_state);
+ qp_cur_state, qp_new_state, udata);
if (err && (qp_cur_state == IB_QPS_INIT)) {
qp_new_state = IB_QPS_RESET;
if (__mlx4_ib_modify_qp(ibwq, MLX4_IB_RWQ_SRC, NULL,
- attr_mask, IB_QPS_INIT, IB_QPS_RESET)) {
+ attr_mask, IB_QPS_INIT, IB_QPS_RESET,
+ udata)) {
pr_warn("WQN=0x%06x failed with reverting HW's resources failure\n",
ibwq->wq_num);
qp_new_state = IB_QPS_INIT;
@@ -4222,7 +4234,7 @@ int mlx4_ib_modify_wq(struct ib_wq *ibwq, struct ib_wq_attr *wq_attr,
* WQ, so we can apply its port on the WQ.
*/
if (qp->rss_usecnt)
- err = _mlx4_ib_modify_wq(ibwq, new_state);
+ err = _mlx4_ib_modify_wq(ibwq, new_state, udata);
if (!err)
ibwq->state = new_state;
diff --git a/drivers/infiniband/hw/mlx4/srq.c b/drivers/infiniband/hw/mlx4/srq.c
index 4456f1b8921d..381cf899bcef 100644
--- a/drivers/infiniband/hw/mlx4/srq.c
+++ b/drivers/infiniband/hw/mlx4/srq.c
@@ -37,6 +37,7 @@
#include "mlx4_ib.h"
#include <rdma/mlx4-abi.h>
+#include <rdma/uverbs_ioctl.h>
static void *get_wqe(struct mlx4_ib_srq *srq, int n)
{
@@ -73,6 +74,8 @@ struct ib_srq *mlx4_ib_create_srq(struct ib_pd *pd,
struct ib_udata *udata)
{
struct mlx4_ib_dev *dev = to_mdev(pd->device);
+ struct mlx4_ib_ucontext *ucontext = rdma_udata_to_drv_context(
+ udata, struct mlx4_ib_ucontext, ibucontext);
struct mlx4_ib_srq *srq;
struct mlx4_wqe_srq_next_seg *next;
struct mlx4_wqe_data_seg *scatter;
@@ -113,8 +116,7 @@ struct ib_srq *mlx4_ib_create_srq(struct ib_pd *pd,
goto err_srq;
}
- srq->umem = ib_umem_get(pd->uobject->context, ucmd.buf_addr,
- buf_size, 0, 0);
+ srq->umem = ib_umem_get(udata, ucmd.buf_addr, buf_size, 0, 0);
if (IS_ERR(srq->umem)) {
err = PTR_ERR(srq->umem);
goto err_srq;
@@ -129,8 +131,8 @@ struct ib_srq *mlx4_ib_create_srq(struct ib_pd *pd,
if (err)
goto err_mtt;
- err = mlx4_ib_db_map_user(to_mucontext(pd->uobject->context),
- ucmd.db_addr, &srq->db);
+ err = mlx4_ib_db_map_user(ucontext, udata, ucmd.db_addr,
+ &srq->db);
if (err)
goto err_mtt;
} else {
@@ -203,7 +205,7 @@ struct ib_srq *mlx4_ib_create_srq(struct ib_pd *pd,
err_wrid:
if (udata)
- mlx4_ib_db_unmap_user(to_mucontext(pd->uobject->context), &srq->db);
+ mlx4_ib_db_unmap_user(ucontext, &srq->db);
else
kvfree(srq->wrid);
diff --git a/drivers/infiniband/hw/mlx5/Kconfig b/drivers/infiniband/hw/mlx5/Kconfig
index 0440966bc6ec..8d651c05de62 100644
--- a/drivers/infiniband/hw/mlx5/Kconfig
+++ b/drivers/infiniband/hw/mlx5/Kconfig
@@ -1,7 +1,6 @@
config MLX5_INFINIBAND
tristate "Mellanox 5th generation network adapters (ConnectX series) support"
depends on NETDEVICES && ETHERNET && PCI && MLX5_CORE
- depends on INFINIBAND_USER_ACCESS || INFINIBAND_USER_ACCESS=n
---help---
This driver provides low-level InfiniBand support for
Mellanox Connect-IB PCI Express host channel adapters (HCAs).
diff --git a/drivers/infiniband/hw/mlx5/cong.c b/drivers/infiniband/hw/mlx5/cong.c
index 7e4e358a4fd8..8ba439fabf7f 100644
--- a/drivers/infiniband/hw/mlx5/cong.c
+++ b/drivers/infiniband/hw/mlx5/cong.c
@@ -389,19 +389,19 @@ void mlx5_ib_cleanup_cong_debugfs(struct mlx5_ib_dev *dev, u8 port_num)
dev->port[port_num].dbg_cc_params = NULL;
}
-int mlx5_ib_init_cong_debugfs(struct mlx5_ib_dev *dev, u8 port_num)
+void mlx5_ib_init_cong_debugfs(struct mlx5_ib_dev *dev, u8 port_num)
{
struct mlx5_ib_dbg_cc_params *dbg_cc_params;
struct mlx5_core_dev *mdev;
int i;
if (!mlx5_debugfs_root)
- goto out;
+ return;
/* Takes a 1-based port number */
mdev = mlx5_ib_get_native_port_mdev(dev, port_num + 1, NULL);
if (!mdev)
- goto out;
+ return;
if (!MLX5_CAP_GEN(mdev, cc_query_allowed) ||
!MLX5_CAP_GEN(mdev, cc_modify_allowed))
@@ -415,8 +415,6 @@ int mlx5_ib_init_cong_debugfs(struct mlx5_ib_dev *dev, u8 port_num)
dbg_cc_params->root = debugfs_create_dir("cc_params",
mdev->priv.dbg_root);
- if (!dbg_cc_params->root)
- goto err;
for (i = 0; i < MLX5_IB_DBG_CC_MAX; i++) {
dbg_cc_params->params[i].offset = i;
@@ -427,14 +425,11 @@ int mlx5_ib_init_cong_debugfs(struct mlx5_ib_dev *dev, u8 port_num)
0600, dbg_cc_params->root,
&dbg_cc_params->params[i],
&dbg_cc_fops);
- if (!dbg_cc_params->params[i].dentry)
- goto err;
}
put_mdev:
mlx5_ib_put_native_port_mdev(dev, port_num + 1);
-out:
- return 0;
+ return;
err:
mlx5_ib_warn(dev, "cong debugfs failure\n");
@@ -445,5 +440,5 @@ err:
* We don't want to fail driver if debugfs failed to initialize,
* so we are not forwarding error to the user.
*/
- return 0;
+ return;
}
diff --git a/drivers/infiniband/hw/mlx5/cq.c b/drivers/infiniband/hw/mlx5/cq.c
index 90f1b0bae5b5..18704e503508 100644
--- a/drivers/infiniband/hw/mlx5/cq.c
+++ b/drivers/infiniband/hw/mlx5/cq.c
@@ -187,8 +187,8 @@ static void handle_responder(struct ib_wc *wc, struct mlx5_cqe64 *cqe,
wqe_ctr = be16_to_cpu(cqe->wqe_counter);
wc->wr_id = srq->wrid[wqe_ctr];
mlx5_ib_free_srq_wqe(srq, wqe_ctr);
- if (msrq && atomic_dec_and_test(&msrq->refcount))
- complete(&msrq->free);
+ if (msrq)
+ mlx5_core_res_put(&msrq->common);
}
} else {
wq = &qp->rq;
@@ -707,15 +707,15 @@ static int create_cq_user(struct mlx5_ib_dev *dev, struct ib_udata *udata,
*cqe_size = ucmd.cqe_size;
- cq->buf.umem = ib_umem_get(context, ucmd.buf_addr,
- entries * ucmd.cqe_size,
- IB_ACCESS_LOCAL_WRITE, 1);
+ cq->buf.umem =
+ ib_umem_get(udata, ucmd.buf_addr, entries * ucmd.cqe_size,
+ IB_ACCESS_LOCAL_WRITE, 1);
if (IS_ERR(cq->buf.umem)) {
err = PTR_ERR(cq->buf.umem);
return err;
}
- err = mlx5_ib_db_map_user(to_mucontext(context), ucmd.db_addr,
+ err = mlx5_ib_db_map_user(to_mucontext(context), udata, ucmd.db_addr,
&cq->db);
if (err)
goto err_umem;
@@ -1111,7 +1111,6 @@ static int resize_user(struct mlx5_ib_dev *dev, struct mlx5_ib_cq *cq,
struct ib_umem *umem;
int err;
int npages;
- struct ib_ucontext *context = cq->buf.umem->context;
err = ib_copy_from_udata(&ucmd, udata, sizeof(ucmd));
if (err)
@@ -1124,7 +1123,7 @@ static int resize_user(struct mlx5_ib_dev *dev, struct mlx5_ib_cq *cq,
if (ucmd.cqe_size && SIZE_MAX / ucmd.cqe_size <= entries - 1)
return -EINVAL;
- umem = ib_umem_get(context, ucmd.buf_addr,
+ umem = ib_umem_get(udata, ucmd.buf_addr,
(size_t)ucmd.cqe_size * entries,
IB_ACCESS_LOCAL_WRITE, 1);
if (IS_ERR(umem)) {
diff --git a/drivers/infiniband/hw/mlx5/devx.c b/drivers/infiniband/hw/mlx5/devx.c
index 5a588f3cfb1b..9e08df7914aa 100644
--- a/drivers/infiniband/hw/mlx5/devx.c
+++ b/drivers/infiniband/hw/mlx5/devx.c
@@ -8,6 +8,7 @@
#include <rdma/uverbs_types.h>
#include <rdma/uverbs_ioctl.h>
#include <rdma/mlx5_user_ioctl_cmds.h>
+#include <rdma/mlx5_user_ioctl_verbs.h>
#include <rdma/ib_umem.h>
#include <rdma/uverbs_std_types.h>
#include <linux/mlx5/driver.h>
@@ -17,12 +18,32 @@
#define UVERBS_MODULE_NAME mlx5_ib
#include <rdma/uverbs_named_ioctl.h>
+enum devx_obj_flags {
+ DEVX_OBJ_FLAGS_INDIRECT_MKEY = 1 << 0,
+ DEVX_OBJ_FLAGS_DCT = 1 << 1,
+};
+
+struct devx_async_data {
+ struct mlx5_ib_dev *mdev;
+ struct list_head list;
+ struct ib_uobject *fd_uobj;
+ struct mlx5_async_work cb_work;
+ u16 cmd_out_len;
+ /* must be last field in this structure */
+ struct mlx5_ib_uapi_devx_async_cmd_hdr hdr;
+};
+
#define MLX5_MAX_DESTROY_INBOX_SIZE_DW MLX5_ST_SZ_DW(delete_fte_in)
struct devx_obj {
struct mlx5_core_dev *mdev;
u64 obj_id;
u32 dinlen; /* destroy inbox length */
u32 dinbox[MLX5_MAX_DESTROY_INBOX_SIZE_DW];
+ u32 flags;
+ union {
+ struct mlx5_ib_devx_mr devx_mr;
+ struct mlx5_core_dct core_dct;
+ };
};
struct devx_umem {
@@ -330,7 +351,6 @@ static u64 devx_get_obj_id(const void *in)
obj_id = get_enc_obj_id(MLX5_CMD_OP_CREATE_RQ,
MLX5_GET(arm_rq_in, in, srq_number));
break;
- case MLX5_CMD_OP_DRAIN_DCT:
case MLX5_CMD_OP_ARM_DCT_FOR_KEY_VIOLATION:
obj_id = get_enc_obj_id(MLX5_CMD_OP_CREATE_DCT,
MLX5_GET(drain_dct_in, in, dctn));
@@ -601,7 +621,6 @@ static bool devx_is_obj_modify_cmd(const void *in)
case MLX5_CMD_OP_2RST_QP:
case MLX5_CMD_OP_ARM_XRC_SRQ:
case MLX5_CMD_OP_ARM_RQ:
- case MLX5_CMD_OP_DRAIN_DCT:
case MLX5_CMD_OP_ARM_DCT_FOR_KEY_VIOLATION:
case MLX5_CMD_OP_ARM_XRQ:
case MLX5_CMD_OP_SET_XRQ_DC_PARAMS_ENTRY:
@@ -1011,6 +1030,92 @@ static void devx_obj_build_destroy_cmd(void *in, void *out, void *din,
}
}
+static int devx_handle_mkey_indirect(struct devx_obj *obj,
+ struct mlx5_ib_dev *dev,
+ void *in, void *out)
+{
+ struct mlx5_mkey_table *table = &dev->mdev->priv.mkey_table;
+ struct mlx5_ib_devx_mr *devx_mr = &obj->devx_mr;
+ unsigned long flags;
+ struct mlx5_core_mkey *mkey;
+ void *mkc;
+ u8 key;
+ int err;
+
+ mkey = &devx_mr->mmkey;
+ mkc = MLX5_ADDR_OF(create_mkey_in, in, memory_key_mkey_entry);
+ key = MLX5_GET(mkc, mkc, mkey_7_0);
+ mkey->key = mlx5_idx_to_mkey(
+ MLX5_GET(create_mkey_out, out, mkey_index)) | key;
+ mkey->type = MLX5_MKEY_INDIRECT_DEVX;
+ mkey->iova = MLX5_GET64(mkc, mkc, start_addr);
+ mkey->size = MLX5_GET64(mkc, mkc, len);
+ mkey->pd = MLX5_GET(mkc, mkc, pd);
+ devx_mr->ndescs = MLX5_GET(mkc, mkc, translations_octword_size);
+
+ write_lock_irqsave(&table->lock, flags);
+ err = radix_tree_insert(&table->tree, mlx5_base_mkey(mkey->key),
+ mkey);
+ write_unlock_irqrestore(&table->lock, flags);
+ return err;
+}
+
+static int devx_handle_mkey_create(struct mlx5_ib_dev *dev,
+ struct devx_obj *obj,
+ void *in, int in_len)
+{
+ int min_len = MLX5_BYTE_OFF(create_mkey_in, memory_key_mkey_entry) +
+ MLX5_FLD_SZ_BYTES(create_mkey_in,
+ memory_key_mkey_entry);
+ void *mkc;
+ u8 access_mode;
+
+ if (in_len < min_len)
+ return -EINVAL;
+
+ mkc = MLX5_ADDR_OF(create_mkey_in, in, memory_key_mkey_entry);
+
+ access_mode = MLX5_GET(mkc, mkc, access_mode_1_0);
+ access_mode |= MLX5_GET(mkc, mkc, access_mode_4_2) << 2;
+
+ if (access_mode == MLX5_MKC_ACCESS_MODE_KLMS ||
+ access_mode == MLX5_MKC_ACCESS_MODE_KSM) {
+ if (IS_ENABLED(CONFIG_INFINIBAND_ON_DEMAND_PAGING))
+ obj->flags |= DEVX_OBJ_FLAGS_INDIRECT_MKEY;
+ return 0;
+ }
+
+ MLX5_SET(create_mkey_in, in, mkey_umem_valid, 1);
+ return 0;
+}
+
+static void devx_free_indirect_mkey(struct rcu_head *rcu)
+{
+ kfree(container_of(rcu, struct devx_obj, devx_mr.rcu));
+}
+
+/* This function to delete from the radix tree needs to be called before
+ * destroying the underlying mkey. Otherwise a race might occur in case that
+ * other thread will get the same mkey before this one will be deleted,
+ * in that case it will fail via inserting to the tree its own data.
+ *
+ * Note:
+ * An error in the destroy is not expected unless there is some other indirect
+ * mkey which points to this one. In a kernel cleanup flow it will be just
+ * destroyed in the iterative destruction call. In a user flow, in case
+ * the application didn't close in the expected order it's its own problem,
+ * the mkey won't be part of the tree, in both cases the kernel is safe.
+ */
+static void devx_cleanup_mkey(struct devx_obj *obj)
+{
+ struct mlx5_mkey_table *table = &obj->mdev->priv.mkey_table;
+ unsigned long flags;
+
+ write_lock_irqsave(&table->lock, flags);
+ radix_tree_delete(&table->tree, mlx5_base_mkey(obj->devx_mr.mmkey.key));
+ write_unlock_irqrestore(&table->lock, flags);
+}
+
static int devx_obj_cleanup(struct ib_uobject *uobject,
enum rdma_remove_reason why)
{
@@ -1018,10 +1123,25 @@ static int devx_obj_cleanup(struct ib_uobject *uobject,
struct devx_obj *obj = uobject->object;
int ret;
- ret = mlx5_cmd_exec(obj->mdev, obj->dinbox, obj->dinlen, out, sizeof(out));
+ if (obj->flags & DEVX_OBJ_FLAGS_INDIRECT_MKEY)
+ devx_cleanup_mkey(obj);
+
+ if (obj->flags & DEVX_OBJ_FLAGS_DCT)
+ ret = mlx5_core_destroy_dct(obj->mdev, &obj->core_dct);
+ else
+ ret = mlx5_cmd_exec(obj->mdev, obj->dinbox, obj->dinlen, out,
+ sizeof(out));
if (ib_is_destroy_retryable(ret, why, uobject))
return ret;
+ if (obj->flags & DEVX_OBJ_FLAGS_INDIRECT_MKEY) {
+ struct mlx5_ib_dev *dev = to_mdev(uobject->context->device);
+
+ call_srcu(&dev->mr_srcu, &obj->devx_mr.rcu,
+ devx_free_indirect_mkey);
+ return ret;
+ }
+
kfree(obj);
return ret;
}
@@ -1032,10 +1152,13 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_DEVX_OBJ_CREATE)(
void *cmd_in = uverbs_attr_get_alloced_ptr(attrs, MLX5_IB_ATTR_DEVX_OBJ_CREATE_CMD_IN);
int cmd_out_len = uverbs_attr_get_len(attrs,
MLX5_IB_ATTR_DEVX_OBJ_CREATE_CMD_OUT);
+ int cmd_in_len = uverbs_attr_get_len(attrs,
+ MLX5_IB_ATTR_DEVX_OBJ_CREATE_CMD_IN);
void *cmd_out;
struct ib_uobject *uobj = uverbs_attr_get_uobject(
attrs, MLX5_IB_ATTR_DEVX_OBJ_CREATE_HANDLE);
- struct mlx5_ib_ucontext *c = to_mucontext(uobj->context);
+ struct mlx5_ib_ucontext *c = rdma_udata_to_drv_context(
+ &attrs->driver_udata, struct mlx5_ib_ucontext, ibucontext);
struct mlx5_ib_dev *dev = to_mdev(c->ibucontext.device);
u32 out[MLX5_ST_SZ_DW(general_obj_out_cmd_hdr)];
struct devx_obj *obj;
@@ -1060,11 +1183,25 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_DEVX_OBJ_CREATE)(
return -ENOMEM;
MLX5_SET(general_obj_in_cmd_hdr, cmd_in, uid, uid);
- devx_set_umem_valid(cmd_in);
+ if (opcode == MLX5_CMD_OP_CREATE_MKEY) {
+ err = devx_handle_mkey_create(dev, obj, cmd_in, cmd_in_len);
+ if (err)
+ goto obj_free;
+ } else {
+ devx_set_umem_valid(cmd_in);
+ }
+
+ if (opcode == MLX5_CMD_OP_CREATE_DCT) {
+ obj->flags |= DEVX_OBJ_FLAGS_DCT;
+ err = mlx5_core_create_dct(dev->mdev, &obj->core_dct,
+ cmd_in, cmd_in_len,
+ cmd_out, cmd_out_len);
+ } else {
+ err = mlx5_cmd_exec(dev->mdev, cmd_in,
+ cmd_in_len,
+ cmd_out, cmd_out_len);
+ }
- err = mlx5_cmd_exec(dev->mdev, cmd_in,
- uverbs_attr_get_len(attrs, MLX5_IB_ATTR_DEVX_OBJ_CREATE_CMD_IN),
- cmd_out, cmd_out_len);
if (err)
goto obj_free;
@@ -1074,15 +1211,28 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_DEVX_OBJ_CREATE)(
&obj_id);
WARN_ON(obj->dinlen > MLX5_MAX_DESTROY_INBOX_SIZE_DW * sizeof(u32));
+ if (obj->flags & DEVX_OBJ_FLAGS_INDIRECT_MKEY) {
+ err = devx_handle_mkey_indirect(obj, dev, cmd_in, cmd_out);
+ if (err)
+ goto obj_destroy;
+ }
+
err = uverbs_copy_to(attrs, MLX5_IB_ATTR_DEVX_OBJ_CREATE_CMD_OUT, cmd_out, cmd_out_len);
if (err)
- goto obj_destroy;
+ goto err_copy;
obj->obj_id = get_enc_obj_id(opcode, obj_id);
return 0;
+err_copy:
+ if (obj->flags & DEVX_OBJ_FLAGS_INDIRECT_MKEY)
+ devx_cleanup_mkey(obj);
obj_destroy:
- mlx5_cmd_exec(obj->mdev, obj->dinbox, obj->dinlen, out, sizeof(out));
+ if (obj->flags & DEVX_OBJ_FLAGS_DCT)
+ mlx5_core_destroy_dct(obj->mdev, &obj->core_dct);
+ else
+ mlx5_cmd_exec(obj->mdev, obj->dinbox, obj->dinlen, out,
+ sizeof(out));
obj_free:
kfree(obj);
return err;
@@ -1096,8 +1246,9 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_DEVX_OBJ_MODIFY)(
MLX5_IB_ATTR_DEVX_OBJ_MODIFY_CMD_OUT);
struct ib_uobject *uobj = uverbs_attr_get_uobject(attrs,
MLX5_IB_ATTR_DEVX_OBJ_MODIFY_HANDLE);
- struct mlx5_ib_ucontext *c = to_mucontext(uobj->context);
- struct mlx5_ib_dev *mdev = to_mdev(uobj->context->device);
+ struct mlx5_ib_ucontext *c = rdma_udata_to_drv_context(
+ &attrs->driver_udata, struct mlx5_ib_ucontext, ibucontext);
+ struct mlx5_ib_dev *mdev = to_mdev(c->ibucontext.device);
void *cmd_out;
int err;
int uid;
@@ -1137,11 +1288,12 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_DEVX_OBJ_QUERY)(
MLX5_IB_ATTR_DEVX_OBJ_QUERY_CMD_OUT);
struct ib_uobject *uobj = uverbs_attr_get_uobject(attrs,
MLX5_IB_ATTR_DEVX_OBJ_QUERY_HANDLE);
- struct mlx5_ib_ucontext *c = to_mucontext(uobj->context);
+ struct mlx5_ib_ucontext *c = rdma_udata_to_drv_context(
+ &attrs->driver_udata, struct mlx5_ib_ucontext, ibucontext);
void *cmd_out;
int err;
int uid;
- struct mlx5_ib_dev *mdev = to_mdev(uobj->context->device);
+ struct mlx5_ib_dev *mdev = to_mdev(c->ibucontext.device);
uid = devx_get_uid(c, cmd_in);
if (uid < 0)
@@ -1168,6 +1320,154 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_DEVX_OBJ_QUERY)(
cmd_out, cmd_out_len);
}
+struct devx_async_event_queue {
+ spinlock_t lock;
+ wait_queue_head_t poll_wait;
+ struct list_head event_list;
+ atomic_t bytes_in_use;
+ u8 is_destroyed:1;
+};
+
+struct devx_async_cmd_event_file {
+ struct ib_uobject uobj;
+ struct devx_async_event_queue ev_queue;
+ struct mlx5_async_ctx async_ctx;
+};
+
+static void devx_init_event_queue(struct devx_async_event_queue *ev_queue)
+{
+ spin_lock_init(&ev_queue->lock);
+ INIT_LIST_HEAD(&ev_queue->event_list);
+ init_waitqueue_head(&ev_queue->poll_wait);
+ atomic_set(&ev_queue->bytes_in_use, 0);
+ ev_queue->is_destroyed = 0;
+}
+
+static int UVERBS_HANDLER(MLX5_IB_METHOD_DEVX_ASYNC_CMD_FD_ALLOC)(
+ struct uverbs_attr_bundle *attrs)
+{
+ struct devx_async_cmd_event_file *ev_file;
+
+ struct ib_uobject *uobj = uverbs_attr_get_uobject(
+ attrs, MLX5_IB_ATTR_DEVX_ASYNC_CMD_FD_ALLOC_HANDLE);
+ struct mlx5_ib_dev *mdev = to_mdev(uobj->context->device);
+
+ ev_file = container_of(uobj, struct devx_async_cmd_event_file,
+ uobj);
+ devx_init_event_queue(&ev_file->ev_queue);
+ mlx5_cmd_init_async_ctx(mdev->mdev, &ev_file->async_ctx);
+ return 0;
+}
+
+static void devx_query_callback(int status, struct mlx5_async_work *context)
+{
+ struct devx_async_data *async_data =
+ container_of(context, struct devx_async_data, cb_work);
+ struct ib_uobject *fd_uobj = async_data->fd_uobj;
+ struct devx_async_cmd_event_file *ev_file;
+ struct devx_async_event_queue *ev_queue;
+ unsigned long flags;
+
+ ev_file = container_of(fd_uobj, struct devx_async_cmd_event_file,
+ uobj);
+ ev_queue = &ev_file->ev_queue;
+
+ spin_lock_irqsave(&ev_queue->lock, flags);
+ list_add_tail(&async_data->list, &ev_queue->event_list);
+ spin_unlock_irqrestore(&ev_queue->lock, flags);
+
+ wake_up_interruptible(&ev_queue->poll_wait);
+ fput(fd_uobj->object);
+}
+
+#define MAX_ASYNC_BYTES_IN_USE (1024 * 1024) /* 1MB */
+
+static int UVERBS_HANDLER(MLX5_IB_METHOD_DEVX_OBJ_ASYNC_QUERY)(
+ struct uverbs_attr_bundle *attrs)
+{
+ void *cmd_in = uverbs_attr_get_alloced_ptr(attrs,
+ MLX5_IB_ATTR_DEVX_OBJ_QUERY_ASYNC_CMD_IN);
+ struct ib_uobject *uobj = uverbs_attr_get_uobject(
+ attrs,
+ MLX5_IB_ATTR_DEVX_OBJ_QUERY_ASYNC_HANDLE);
+ u16 cmd_out_len;
+ struct mlx5_ib_ucontext *c = rdma_udata_to_drv_context(
+ &attrs->driver_udata, struct mlx5_ib_ucontext, ibucontext);
+ struct ib_uobject *fd_uobj;
+ int err;
+ int uid;
+ struct mlx5_ib_dev *mdev = to_mdev(c->ibucontext.device);
+ struct devx_async_cmd_event_file *ev_file;
+ struct devx_async_data *async_data;
+
+ uid = devx_get_uid(c, cmd_in);
+ if (uid < 0)
+ return uid;
+
+ if (!devx_is_obj_query_cmd(cmd_in))
+ return -EINVAL;
+
+ err = uverbs_get_const(&cmd_out_len, attrs,
+ MLX5_IB_ATTR_DEVX_OBJ_QUERY_ASYNC_OUT_LEN);
+ if (err)
+ return err;
+
+ if (!devx_is_valid_obj_id(uobj, cmd_in))
+ return -EINVAL;
+
+ fd_uobj = uverbs_attr_get_uobject(attrs,
+ MLX5_IB_ATTR_DEVX_OBJ_QUERY_ASYNC_FD);
+ if (IS_ERR(fd_uobj))
+ return PTR_ERR(fd_uobj);
+
+ ev_file = container_of(fd_uobj, struct devx_async_cmd_event_file,
+ uobj);
+
+ if (atomic_add_return(cmd_out_len, &ev_file->ev_queue.bytes_in_use) >
+ MAX_ASYNC_BYTES_IN_USE) {
+ atomic_sub(cmd_out_len, &ev_file->ev_queue.bytes_in_use);
+ return -EAGAIN;
+ }
+
+ async_data = kvzalloc(struct_size(async_data, hdr.out_data,
+ cmd_out_len), GFP_KERNEL);
+ if (!async_data) {
+ err = -ENOMEM;
+ goto sub_bytes;
+ }
+
+ err = uverbs_copy_from(&async_data->hdr.wr_id, attrs,
+ MLX5_IB_ATTR_DEVX_OBJ_QUERY_ASYNC_WR_ID);
+ if (err)
+ goto free_async;
+
+ async_data->cmd_out_len = cmd_out_len;
+ async_data->mdev = mdev;
+ async_data->fd_uobj = fd_uobj;
+
+ get_file(fd_uobj->object);
+ MLX5_SET(general_obj_in_cmd_hdr, cmd_in, uid, uid);
+ err = mlx5_cmd_exec_cb(&ev_file->async_ctx, cmd_in,
+ uverbs_attr_get_len(attrs,
+ MLX5_IB_ATTR_DEVX_OBJ_QUERY_ASYNC_CMD_IN),
+ async_data->hdr.out_data,
+ async_data->cmd_out_len,
+ devx_query_callback, &async_data->cb_work);
+
+ if (err)
+ goto cb_err;
+
+ return 0;
+
+cb_err:
+ fput(fd_uobj->object);
+free_async:
+ kvfree(async_data);
+sub_bytes:
+ atomic_sub(cmd_out_len, &ev_file->ev_queue.bytes_in_use);
+ return err;
+}
+
static int devx_umem_get(struct mlx5_ib_dev *dev, struct ib_ucontext *ucontext,
struct uverbs_attr_bundle *attrs,
struct devx_umem *obj)
@@ -1195,7 +1495,7 @@ static int devx_umem_get(struct mlx5_ib_dev *dev, struct ib_ucontext *ucontext,
if (err)
return err;
- obj->umem = ib_umem_get(ucontext, addr, size, access, 0);
+ obj->umem = ib_umem_get(&attrs->driver_udata, addr, size, access, 0);
if (IS_ERR(obj->umem))
return PTR_ERR(obj->umem);
@@ -1252,7 +1552,8 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_DEVX_UMEM_REG)(
struct ib_uobject *uobj = uverbs_attr_get_uobject(
attrs, MLX5_IB_ATTR_DEVX_UMEM_REG_HANDLE);
u32 obj_id;
- struct mlx5_ib_ucontext *c = to_mucontext(uobj->context);
+ struct mlx5_ib_ucontext *c = rdma_udata_to_drv_context(
+ &attrs->driver_udata, struct mlx5_ib_ucontext, ibucontext);
struct mlx5_ib_dev *dev = to_mdev(c->ibucontext.device);
int err;
@@ -1313,6 +1614,123 @@ static int devx_umem_cleanup(struct ib_uobject *uobject,
return 0;
}
+static ssize_t devx_async_cmd_event_read(struct file *filp, char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct devx_async_cmd_event_file *comp_ev_file = filp->private_data;
+ struct devx_async_event_queue *ev_queue = &comp_ev_file->ev_queue;
+ struct devx_async_data *event;
+ int ret = 0;
+ size_t eventsz;
+
+ spin_lock_irq(&ev_queue->lock);
+
+ while (list_empty(&ev_queue->event_list)) {
+ spin_unlock_irq(&ev_queue->lock);
+
+ if (filp->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+
+ if (wait_event_interruptible(
+ ev_queue->poll_wait,
+ (!list_empty(&ev_queue->event_list) ||
+ ev_queue->is_destroyed))) {
+ return -ERESTARTSYS;
+ }
+
+ if (list_empty(&ev_queue->event_list) &&
+ ev_queue->is_destroyed)
+ return -EIO;
+
+ spin_lock_irq(&ev_queue->lock);
+ }
+
+ event = list_entry(ev_queue->event_list.next,
+ struct devx_async_data, list);
+ eventsz = event->cmd_out_len +
+ sizeof(struct mlx5_ib_uapi_devx_async_cmd_hdr);
+
+ if (eventsz > count) {
+ spin_unlock_irq(&ev_queue->lock);
+ return -ENOSPC;
+ }
+
+ list_del(ev_queue->event_list.next);
+ spin_unlock_irq(&ev_queue->lock);
+
+ if (copy_to_user(buf, &event->hdr, eventsz))
+ ret = -EFAULT;
+ else
+ ret = eventsz;
+
+ atomic_sub(event->cmd_out_len, &ev_queue->bytes_in_use);
+ kvfree(event);
+ return ret;
+}
+
+static int devx_async_cmd_event_close(struct inode *inode, struct file *filp)
+{
+ struct ib_uobject *uobj = filp->private_data;
+ struct devx_async_cmd_event_file *comp_ev_file = container_of(
+ uobj, struct devx_async_cmd_event_file, uobj);
+ struct devx_async_data *entry, *tmp;
+
+ spin_lock_irq(&comp_ev_file->ev_queue.lock);
+ list_for_each_entry_safe(entry, tmp,
+ &comp_ev_file->ev_queue.event_list, list)
+ kvfree(entry);
+ spin_unlock_irq(&comp_ev_file->ev_queue.lock);
+
+ uverbs_close_fd(filp);
+ return 0;
+}
+
+static __poll_t devx_async_cmd_event_poll(struct file *filp,
+ struct poll_table_struct *wait)
+{
+ struct devx_async_cmd_event_file *comp_ev_file = filp->private_data;
+ struct devx_async_event_queue *ev_queue = &comp_ev_file->ev_queue;
+ __poll_t pollflags = 0;
+
+ poll_wait(filp, &ev_queue->poll_wait, wait);
+
+ spin_lock_irq(&ev_queue->lock);
+ if (ev_queue->is_destroyed)
+ pollflags = EPOLLIN | EPOLLRDNORM | EPOLLRDHUP;
+ else if (!list_empty(&ev_queue->event_list))
+ pollflags = EPOLLIN | EPOLLRDNORM;
+ spin_unlock_irq(&ev_queue->lock);
+
+ return pollflags;
+}
+
+const struct file_operations devx_async_cmd_event_fops = {
+ .owner = THIS_MODULE,
+ .read = devx_async_cmd_event_read,
+ .poll = devx_async_cmd_event_poll,
+ .release = devx_async_cmd_event_close,
+ .llseek = no_llseek,
+};
+
+static int devx_hot_unplug_async_cmd_event_file(struct ib_uobject *uobj,
+ enum rdma_remove_reason why)
+{
+ struct devx_async_cmd_event_file *comp_ev_file =
+ container_of(uobj, struct devx_async_cmd_event_file,
+ uobj);
+ struct devx_async_event_queue *ev_queue = &comp_ev_file->ev_queue;
+
+ spin_lock_irq(&ev_queue->lock);
+ ev_queue->is_destroyed = 1;
+ spin_unlock_irq(&ev_queue->lock);
+
+ if (why == RDMA_REMOVE_DRIVER_REMOVE)
+ wake_up_interruptible(&ev_queue->poll_wait);
+
+ mlx5_cmd_cleanup_async_ctx(&comp_ev_file->async_ctx);
+ return 0;
+};
+
DECLARE_UVERBS_NAMED_METHOD(
MLX5_IB_METHOD_DEVX_UMEM_REG,
UVERBS_ATTR_IDR(MLX5_IB_ATTR_DEVX_UMEM_REG_HANDLE,
@@ -1423,6 +1841,27 @@ DECLARE_UVERBS_NAMED_METHOD(
UVERBS_ATTR_MIN_SIZE(MLX5_ST_SZ_BYTES(general_obj_out_cmd_hdr)),
UA_MANDATORY));
+DECLARE_UVERBS_NAMED_METHOD(
+ MLX5_IB_METHOD_DEVX_OBJ_ASYNC_QUERY,
+ UVERBS_ATTR_IDR(MLX5_IB_ATTR_DEVX_OBJ_QUERY_HANDLE,
+ UVERBS_IDR_ANY_OBJECT,
+ UVERBS_ACCESS_READ,
+ UA_MANDATORY),
+ UVERBS_ATTR_PTR_IN(
+ MLX5_IB_ATTR_DEVX_OBJ_QUERY_CMD_IN,
+ UVERBS_ATTR_MIN_SIZE(MLX5_ST_SZ_BYTES(general_obj_in_cmd_hdr)),
+ UA_MANDATORY,
+ UA_ALLOC_AND_COPY),
+ UVERBS_ATTR_CONST_IN(MLX5_IB_ATTR_DEVX_OBJ_QUERY_ASYNC_OUT_LEN,
+ u16, UA_MANDATORY),
+ UVERBS_ATTR_FD(MLX5_IB_ATTR_DEVX_OBJ_QUERY_ASYNC_FD,
+ MLX5_IB_OBJECT_DEVX_ASYNC_CMD_FD,
+ UVERBS_ACCESS_READ,
+ UA_MANDATORY),
+ UVERBS_ATTR_PTR_IN(MLX5_IB_ATTR_DEVX_OBJ_QUERY_ASYNC_WR_ID,
+ UVERBS_ATTR_TYPE(u64),
+ UA_MANDATORY));
+
DECLARE_UVERBS_GLOBAL_METHODS(MLX5_IB_OBJECT_DEVX,
&UVERBS_METHOD(MLX5_IB_METHOD_DEVX_OTHER),
&UVERBS_METHOD(MLX5_IB_METHOD_DEVX_QUERY_UAR),
@@ -1433,13 +1872,30 @@ DECLARE_UVERBS_NAMED_OBJECT(MLX5_IB_OBJECT_DEVX_OBJ,
&UVERBS_METHOD(MLX5_IB_METHOD_DEVX_OBJ_CREATE),
&UVERBS_METHOD(MLX5_IB_METHOD_DEVX_OBJ_DESTROY),
&UVERBS_METHOD(MLX5_IB_METHOD_DEVX_OBJ_MODIFY),
- &UVERBS_METHOD(MLX5_IB_METHOD_DEVX_OBJ_QUERY));
+ &UVERBS_METHOD(MLX5_IB_METHOD_DEVX_OBJ_QUERY),
+ &UVERBS_METHOD(MLX5_IB_METHOD_DEVX_OBJ_ASYNC_QUERY));
DECLARE_UVERBS_NAMED_OBJECT(MLX5_IB_OBJECT_DEVX_UMEM,
UVERBS_TYPE_ALLOC_IDR(devx_umem_cleanup),
&UVERBS_METHOD(MLX5_IB_METHOD_DEVX_UMEM_REG),
&UVERBS_METHOD(MLX5_IB_METHOD_DEVX_UMEM_DEREG));
+
+DECLARE_UVERBS_NAMED_METHOD(
+ MLX5_IB_METHOD_DEVX_ASYNC_CMD_FD_ALLOC,
+ UVERBS_ATTR_FD(MLX5_IB_ATTR_DEVX_ASYNC_CMD_FD_ALLOC_HANDLE,
+ MLX5_IB_OBJECT_DEVX_ASYNC_CMD_FD,
+ UVERBS_ACCESS_NEW,
+ UA_MANDATORY));
+
+DECLARE_UVERBS_NAMED_OBJECT(
+ MLX5_IB_OBJECT_DEVX_ASYNC_CMD_FD,
+ UVERBS_TYPE_ALLOC_FD(sizeof(struct devx_async_cmd_event_file),
+ devx_hot_unplug_async_cmd_event_file,
+ &devx_async_cmd_event_fops, "[devx_async_cmd]",
+ O_RDONLY),
+ &UVERBS_METHOD(MLX5_IB_METHOD_DEVX_ASYNC_CMD_FD_ALLOC));
+
static bool devx_is_supported(struct ib_device *device)
{
struct mlx5_ib_dev *dev = to_mdev(device);
@@ -1457,5 +1913,8 @@ const struct uapi_definition mlx5_ib_devx_defs[] = {
UAPI_DEF_CHAIN_OBJ_TREE_NAMED(
MLX5_IB_OBJECT_DEVX_UMEM,
UAPI_DEF_IS_OBJ_SUPPORTED(devx_is_supported)),
+ UAPI_DEF_CHAIN_OBJ_TREE_NAMED(
+ MLX5_IB_OBJECT_DEVX_ASYNC_CMD_FD,
+ UAPI_DEF_IS_OBJ_SUPPORTED(devx_is_supported)),
{},
};
diff --git a/drivers/infiniband/hw/mlx5/doorbell.c b/drivers/infiniband/hw/mlx5/doorbell.c
index a0e4e6ddb71a..8f4e5f22b84c 100644
--- a/drivers/infiniband/hw/mlx5/doorbell.c
+++ b/drivers/infiniband/hw/mlx5/doorbell.c
@@ -43,7 +43,8 @@ struct mlx5_ib_user_db_page {
int refcnt;
};
-int mlx5_ib_db_map_user(struct mlx5_ib_ucontext *context, unsigned long virt,
+int mlx5_ib_db_map_user(struct mlx5_ib_ucontext *context,
+ struct ib_udata *udata, unsigned long virt,
struct mlx5_db *db)
{
struct mlx5_ib_user_db_page *page;
@@ -63,8 +64,7 @@ int mlx5_ib_db_map_user(struct mlx5_ib_ucontext *context, unsigned long virt,
page->user_virt = (virt & PAGE_MASK);
page->refcnt = 0;
- page->umem = ib_umem_get(&context->ibucontext, virt & PAGE_MASK,
- PAGE_SIZE, 0, 0);
+ page->umem = ib_umem_get(udata, virt & PAGE_MASK, PAGE_SIZE, 0, 0);
if (IS_ERR(page->umem)) {
err = PTR_ERR(page->umem);
kfree(page);
diff --git a/drivers/infiniband/hw/mlx5/ib_rep.c b/drivers/infiniband/hw/mlx5/ib_rep.c
index 4700cffb5a00..b8639ac71336 100644
--- a/drivers/infiniband/hw/mlx5/ib_rep.c
+++ b/drivers/infiniband/hw/mlx5/ib_rep.c
@@ -57,7 +57,7 @@ mlx5_ib_vport_rep_load(struct mlx5_core_dev *dev, struct mlx5_eswitch_rep *rep)
else
profile = &vf_rep_profile;
- ibdev = (struct mlx5_ib_dev *)ib_alloc_device(sizeof(*ibdev));
+ ibdev = ib_alloc_device(mlx5_ib_dev, ib_dev);
if (!ibdev)
return -ENOMEM;
@@ -65,8 +65,10 @@ mlx5_ib_vport_rep_load(struct mlx5_core_dev *dev, struct mlx5_eswitch_rep *rep)
ibdev->mdev = dev;
ibdev->num_ports = max(MLX5_CAP_GEN(dev, num_ports),
MLX5_CAP_GEN(dev, num_vhca_ports));
- if (!__mlx5_ib_add(ibdev, profile))
+ if (!__mlx5_ib_add(ibdev, profile)) {
+ ib_dealloc_device(&ibdev->ib_dev);
return -EINVAL;
+ }
rep->rep_if[REP_IB].priv = ibdev;
diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c
index 581ae11e2fc9..531ff20b32ad 100644
--- a/drivers/infiniband/hw/mlx5/main.c
+++ b/drivers/infiniband/hw/mlx5/main.c
@@ -415,10 +415,17 @@ static int translate_eth_ext_proto_oper(u32 eth_proto_oper, u8 *active_speed,
*active_speed = IB_SPEED_EDR;
break;
case MLX5E_PROT_MASK(MLX5E_50GAUI_2_LAUI_2_50GBASE_CR2_KR2):
+ *active_width = IB_WIDTH_2X;
+ *active_speed = IB_SPEED_EDR;
+ break;
case MLX5E_PROT_MASK(MLX5E_50GAUI_1_LAUI_1_50GBASE_CR_KR):
*active_width = IB_WIDTH_1X;
*active_speed = IB_SPEED_HDR;
break;
+ case MLX5E_PROT_MASK(MLX5E_CAUI_4_100GBASE_CR4_KR4):
+ *active_width = IB_WIDTH_4X;
+ *active_speed = IB_SPEED_EDR;
+ break;
case MLX5E_PROT_MASK(MLX5E_100GAUI_2_100GBASE_CR2_KR2):
*active_width = IB_WIDTH_2X;
*active_speed = IB_SPEED_HDR;
@@ -535,24 +542,51 @@ out:
return err;
}
+struct mlx5_ib_vlan_info {
+ u16 vlan_id;
+ bool vlan;
+};
+
+static int get_lower_dev_vlan(struct net_device *lower_dev, void *data)
+{
+ struct mlx5_ib_vlan_info *vlan_info = data;
+
+ if (is_vlan_dev(lower_dev)) {
+ vlan_info->vlan = true;
+ vlan_info->vlan_id = vlan_dev_vlan_id(lower_dev);
+ }
+ /* We are interested only in first level vlan device, so
+ * always return 1 to stop iterating over next level devices.
+ */
+ return 1;
+}
+
static int set_roce_addr(struct mlx5_ib_dev *dev, u8 port_num,
unsigned int index, const union ib_gid *gid,
const struct ib_gid_attr *attr)
{
enum ib_gid_type gid_type = IB_GID_TYPE_IB;
+ struct mlx5_ib_vlan_info vlan_info = { };
u8 roce_version = 0;
u8 roce_l3_type = 0;
- bool vlan = false;
u8 mac[ETH_ALEN];
- u16 vlan_id = 0;
if (gid) {
gid_type = attr->gid_type;
ether_addr_copy(mac, attr->ndev->dev_addr);
if (is_vlan_dev(attr->ndev)) {
- vlan = true;
- vlan_id = vlan_dev_vlan_id(attr->ndev);
+ vlan_info.vlan = true;
+ vlan_info.vlan_id = vlan_dev_vlan_id(attr->ndev);
+ } else {
+ /* If the netdev is upper device and if it's lower
+ * lower device is vlan device, consider vlan id of
+ * the lower vlan device for this gid entry.
+ */
+ rcu_read_lock();
+ netdev_walk_all_lower_dev_rcu(attr->ndev,
+ get_lower_dev_vlan, &vlan_info);
+ rcu_read_unlock();
}
}
@@ -573,8 +607,9 @@ static int set_roce_addr(struct mlx5_ib_dev *dev, u8 port_num,
}
return mlx5_core_roce_gid_set(dev->mdev, index, roce_version,
- roce_l3_type, gid->raw, mac, vlan,
- vlan_id, port_num);
+ roce_l3_type, gid->raw, mac,
+ vlan_info.vlan, vlan_info.vlan_id,
+ port_num);
}
static int mlx5_ib_add_gid(const struct ib_gid_attr *attr,
@@ -982,11 +1017,11 @@ static int mlx5_ib_query_device(struct ib_device *ibdev,
props->hca_core_clock = MLX5_CAP_GEN(mdev, device_frequency_khz);
props->timestamp_mask = 0x7FFFFFFFFFFFFFFFULL;
-#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
- if (MLX5_CAP_GEN(mdev, pg))
- props->device_cap_flags |= IB_DEVICE_ON_DEMAND_PAGING;
- props->odp_caps = dev->odp_caps;
-#endif
+ if (IS_ENABLED(CONFIG_INFINIBAND_ON_DEMAND_PAGING)) {
+ if (MLX5_CAP_GEN(mdev, pg))
+ props->device_cap_flags |= IB_DEVICE_ON_DEMAND_PAGING;
+ props->odp_caps = dev->odp_caps;
+ }
if (MLX5_CAP_GEN(mdev, cd))
props->device_cap_flags |= IB_DEVICE_CROSS_CHANNEL;
@@ -1717,14 +1752,15 @@ static void mlx5_ib_dealloc_transport_domain(struct mlx5_ib_dev *dev, u32 tdn,
mlx5_ib_disable_lb(dev, true, false);
}
-static struct ib_ucontext *mlx5_ib_alloc_ucontext(struct ib_device *ibdev,
- struct ib_udata *udata)
+static int mlx5_ib_alloc_ucontext(struct ib_ucontext *uctx,
+ struct ib_udata *udata)
{
+ struct ib_device *ibdev = uctx->device;
struct mlx5_ib_dev *dev = to_mdev(ibdev);
struct mlx5_ib_alloc_ucontext_req_v2 req = {};
struct mlx5_ib_alloc_ucontext_resp resp = {};
struct mlx5_core_dev *mdev = dev->mdev;
- struct mlx5_ib_ucontext *context;
+ struct mlx5_ib_ucontext *context = to_mucontext(uctx);
struct mlx5_bfreg_info *bfregi;
int ver;
int err;
@@ -1734,29 +1770,29 @@ static struct ib_ucontext *mlx5_ib_alloc_ucontext(struct ib_device *ibdev,
bool lib_uar_4k;
if (!dev->ib_active)
- return ERR_PTR(-EAGAIN);
+ return -EAGAIN;
if (udata->inlen == sizeof(struct mlx5_ib_alloc_ucontext_req))
ver = 0;
else if (udata->inlen >= min_req_v2)
ver = 2;
else
- return ERR_PTR(-EINVAL);
+ return -EINVAL;
err = ib_copy_from_udata(&req, udata, min(udata->inlen, sizeof(req)));
if (err)
- return ERR_PTR(err);
+ return err;
if (req.flags & ~MLX5_IB_ALLOC_UCTX_DEVX)
- return ERR_PTR(-EOPNOTSUPP);
+ return -EOPNOTSUPP;
if (req.comp_mask || req.reserved0 || req.reserved1 || req.reserved2)
- return ERR_PTR(-EOPNOTSUPP);
+ return -EOPNOTSUPP;
req.total_num_bfregs = ALIGN(req.total_num_bfregs,
MLX5_NON_FP_BFREGS_PER_UAR);
if (req.num_low_latency_bfregs > req.total_num_bfregs - 1)
- return ERR_PTR(-EINVAL);
+ return -EINVAL;
resp.qp_tab_size = 1 << MLX5_CAP_GEN(dev->mdev, log_max_qp);
if (mlx5_core_is_pf(dev->mdev) && MLX5_CAP_GEN(dev->mdev, bf))
@@ -1789,10 +1825,6 @@ static struct ib_ucontext *mlx5_ib_alloc_ucontext(struct ib_device *ibdev,
/* MLX5_USER_ALLOC_UCONTEXT_FLOW_ACTION_FLAGS_ESP_AES_GCM_FULL_OFFLOAD is currently always 0 */
}
- context = kzalloc(sizeof(*context), GFP_KERNEL);
- if (!context)
- return ERR_PTR(-ENOMEM);
-
lib_uar_4k = req.lib_caps & MLX5_LIB_CAP_4K_UAR;
bfregi = &context->bfregi;
@@ -1822,9 +1854,9 @@ static struct ib_ucontext *mlx5_ib_alloc_ucontext(struct ib_device *ibdev,
if (err)
goto out_sys_pages;
-#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
- context->ibucontext.invalidate_range = &mlx5_ib_invalidate_range;
-#endif
+ if (ibdev->attrs.device_cap_flags & IB_DEVICE_ON_DEMAND_PAGING)
+ context->ibucontext.invalidate_range =
+ &mlx5_ib_invalidate_range;
if (req.flags & MLX5_IB_ALLOC_UCTX_DEVX) {
err = mlx5_ib_devx_create(dev, true);
@@ -1927,7 +1959,7 @@ static struct ib_ucontext *mlx5_ib_alloc_ucontext(struct ib_device *ibdev,
1, &dev->roce[port].tx_port_affinity));
}
- return &context->ibucontext;
+ return 0;
out_mdev:
mlx5_ib_dealloc_transport_domain(dev, context->tdn, context->devx_uid);
@@ -1945,23 +1977,19 @@ out_count:
kfree(bfregi->count);
out_ctx:
- kfree(context);
-
- return ERR_PTR(err);
+ return err;
}
-static int mlx5_ib_dealloc_ucontext(struct ib_ucontext *ibcontext)
+static void mlx5_ib_dealloc_ucontext(struct ib_ucontext *ibcontext)
{
struct mlx5_ib_ucontext *context = to_mucontext(ibcontext);
struct mlx5_ib_dev *dev = to_mdev(ibcontext->device);
struct mlx5_bfreg_info *bfregi;
-#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
/* All umem's must be destroyed before destroying the ucontext. */
mutex_lock(&ibcontext->per_mm_list_lock);
WARN_ON(!list_empty(&ibcontext->per_mm_list));
mutex_unlock(&ibcontext->per_mm_list_lock);
-#endif
bfregi = &context->bfregi;
mlx5_ib_dealloc_transport_domain(dev, context->tdn, context->devx_uid);
@@ -1972,9 +2000,6 @@ static int mlx5_ib_dealloc_ucontext(struct ib_ucontext *ibcontext)
deallocate_uars(dev, context);
kfree(bfregi->sys_pages);
kfree(bfregi->count);
- kfree(context);
-
- return 0;
}
static phys_addr_t uar_index2pfn(struct mlx5_ib_dev *dev,
@@ -2313,30 +2338,24 @@ int mlx5_ib_dealloc_dm(struct ib_dm *ibdm)
return 0;
}
-static struct ib_pd *mlx5_ib_alloc_pd(struct ib_device *ibdev,
- struct ib_ucontext *context,
- struct ib_udata *udata)
+static int mlx5_ib_alloc_pd(struct ib_pd *ibpd, struct ib_ucontext *context,
+ struct ib_udata *udata)
{
+ struct mlx5_ib_pd *pd = to_mpd(ibpd);
+ struct ib_device *ibdev = ibpd->device;
struct mlx5_ib_alloc_pd_resp resp;
- struct mlx5_ib_pd *pd;
int err;
u32 out[MLX5_ST_SZ_DW(alloc_pd_out)] = {};
u32 in[MLX5_ST_SZ_DW(alloc_pd_in)] = {};
u16 uid = 0;
- pd = kmalloc(sizeof(*pd), GFP_KERNEL);
- if (!pd)
- return ERR_PTR(-ENOMEM);
-
uid = context ? to_mucontext(context)->devx_uid : 0;
MLX5_SET(alloc_pd_in, in, opcode, MLX5_CMD_OP_ALLOC_PD);
MLX5_SET(alloc_pd_in, in, uid, uid);
err = mlx5_cmd_exec(to_mdev(ibdev)->mdev, in, sizeof(in),
out, sizeof(out));
- if (err) {
- kfree(pd);
- return ERR_PTR(err);
- }
+ if (err)
+ return err;
pd->pdn = MLX5_GET(alloc_pd_out, out, pd);
pd->uid = uid;
@@ -2344,23 +2363,19 @@ static struct ib_pd *mlx5_ib_alloc_pd(struct ib_device *ibdev,
resp.pdn = pd->pdn;
if (ib_copy_to_udata(udata, &resp, sizeof(resp))) {
mlx5_cmd_dealloc_pd(to_mdev(ibdev)->mdev, pd->pdn, uid);
- kfree(pd);
- return ERR_PTR(-EFAULT);
+ return -EFAULT;
}
}
- return &pd->ibpd;
+ return 0;
}
-static int mlx5_ib_dealloc_pd(struct ib_pd *pd)
+static void mlx5_ib_dealloc_pd(struct ib_pd *pd)
{
struct mlx5_ib_dev *mdev = to_mdev(pd->device);
struct mlx5_ib_pd *mpd = to_mpd(pd);
mlx5_cmd_dealloc_pd(mdev->mdev, mpd->pdn, mpd->uid);
- kfree(mpd);
-
- return 0;
}
enum {
@@ -2394,10 +2409,29 @@ static u8 get_match_criteria_enable(u32 *match_criteria)
return match_criteria_enable;
}
-static void set_proto(void *outer_c, void *outer_v, u8 mask, u8 val)
+static int set_proto(void *outer_c, void *outer_v, u8 mask, u8 val)
{
- MLX5_SET(fte_match_set_lyr_2_4, outer_c, ip_protocol, mask);
- MLX5_SET(fte_match_set_lyr_2_4, outer_v, ip_protocol, val);
+ u8 entry_mask;
+ u8 entry_val;
+ int err = 0;
+
+ if (!mask)
+ goto out;
+
+ entry_mask = MLX5_GET(fte_match_set_lyr_2_4, outer_c,
+ ip_protocol);
+ entry_val = MLX5_GET(fte_match_set_lyr_2_4, outer_v,
+ ip_protocol);
+ if (!entry_mask) {
+ MLX5_SET(fte_match_set_lyr_2_4, outer_c, ip_protocol, mask);
+ MLX5_SET(fte_match_set_lyr_2_4, outer_v, ip_protocol, val);
+ goto out;
+ }
+ /* Don't override existing ip protocol */
+ if (mask != entry_mask || val != entry_val)
+ err = -EINVAL;
+out:
+ return err;
}
static void set_flow_label(void *misc_c, void *misc_v, u32 mask, u32 val,
@@ -2631,8 +2665,10 @@ static int parse_flow_attr(struct mlx5_core_dev *mdev, u32 *match_c,
set_tos(headers_c, headers_v,
ib_spec->ipv4.mask.tos, ib_spec->ipv4.val.tos);
- set_proto(headers_c, headers_v,
- ib_spec->ipv4.mask.proto, ib_spec->ipv4.val.proto);
+ if (set_proto(headers_c, headers_v,
+ ib_spec->ipv4.mask.proto,
+ ib_spec->ipv4.val.proto))
+ return -EINVAL;
break;
case IB_FLOW_SPEC_IPV6:
if (FIELDS_NOT_SUPPORTED(ib_spec->ipv6.mask, LAST_IPV6_FIELD))
@@ -2671,9 +2707,10 @@ static int parse_flow_attr(struct mlx5_core_dev *mdev, u32 *match_c,
ib_spec->ipv6.mask.traffic_class,
ib_spec->ipv6.val.traffic_class);
- set_proto(headers_c, headers_v,
- ib_spec->ipv6.mask.next_hdr,
- ib_spec->ipv6.val.next_hdr);
+ if (set_proto(headers_c, headers_v,
+ ib_spec->ipv6.mask.next_hdr,
+ ib_spec->ipv6.val.next_hdr))
+ return -EINVAL;
set_flow_label(misc_params_c, misc_params_v,
ntohl(ib_spec->ipv6.mask.flow_label),
@@ -2694,10 +2731,8 @@ static int parse_flow_attr(struct mlx5_core_dev *mdev, u32 *match_c,
LAST_TCP_UDP_FIELD))
return -EOPNOTSUPP;
- MLX5_SET(fte_match_set_lyr_2_4, headers_c, ip_protocol,
- 0xff);
- MLX5_SET(fte_match_set_lyr_2_4, headers_v, ip_protocol,
- IPPROTO_TCP);
+ if (set_proto(headers_c, headers_v, 0xff, IPPROTO_TCP))
+ return -EINVAL;
MLX5_SET(fte_match_set_lyr_2_4, headers_c, tcp_sport,
ntohs(ib_spec->tcp_udp.mask.src_port));
@@ -2714,10 +2749,8 @@ static int parse_flow_attr(struct mlx5_core_dev *mdev, u32 *match_c,
LAST_TCP_UDP_FIELD))
return -EOPNOTSUPP;
- MLX5_SET(fte_match_set_lyr_2_4, headers_c, ip_protocol,
- 0xff);
- MLX5_SET(fte_match_set_lyr_2_4, headers_v, ip_protocol,
- IPPROTO_UDP);
+ if (set_proto(headers_c, headers_v, 0xff, IPPROTO_UDP))
+ return -EINVAL;
MLX5_SET(fte_match_set_lyr_2_4, headers_c, udp_sport,
ntohs(ib_spec->tcp_udp.mask.src_port));
@@ -2733,6 +2766,9 @@ static int parse_flow_attr(struct mlx5_core_dev *mdev, u32 *match_c,
if (ib_spec->gre.mask.c_ks_res0_ver)
return -EOPNOTSUPP;
+ if (set_proto(headers_c, headers_v, 0xff, IPPROTO_GRE))
+ return -EINVAL;
+
MLX5_SET(fte_match_set_lyr_2_4, headers_c, ip_protocol,
0xff);
MLX5_SET(fte_match_set_lyr_2_4, headers_v, ip_protocol,
@@ -3884,7 +3920,7 @@ mlx5_ib_raw_fs_rule_add(struct mlx5_ib_dev *dev,
if (fs_matcher->priority > MLX5_IB_FLOW_LAST_PRIO)
return ERR_PTR(-ENOMEM);
- dst = kzalloc(sizeof(*dst) * 2, GFP_KERNEL);
+ dst = kcalloc(2, sizeof(*dst), GFP_KERNEL);
if (!dst)
return ERR_PTR(-ENOMEM);
@@ -4165,7 +4201,7 @@ static ssize_t fw_pages_show(struct device *device,
struct device_attribute *attr, char *buf)
{
struct mlx5_ib_dev *dev =
- container_of(device, struct mlx5_ib_dev, ib_dev.dev);
+ rdma_device_to_drv_device(device, struct mlx5_ib_dev, ib_dev);
return sprintf(buf, "%d\n", dev->mdev->priv.fw_pages);
}
@@ -4175,7 +4211,7 @@ static ssize_t reg_pages_show(struct device *device,
struct device_attribute *attr, char *buf)
{
struct mlx5_ib_dev *dev =
- container_of(device, struct mlx5_ib_dev, ib_dev.dev);
+ rdma_device_to_drv_device(device, struct mlx5_ib_dev, ib_dev);
return sprintf(buf, "%d\n", atomic_read(&dev->mdev->priv.reg_pages));
}
@@ -4185,7 +4221,8 @@ static ssize_t hca_type_show(struct device *device,
struct device_attribute *attr, char *buf)
{
struct mlx5_ib_dev *dev =
- container_of(device, struct mlx5_ib_dev, ib_dev.dev);
+ rdma_device_to_drv_device(device, struct mlx5_ib_dev, ib_dev);
+
return sprintf(buf, "MT%d\n", dev->mdev->pdev->device);
}
static DEVICE_ATTR_RO(hca_type);
@@ -4194,7 +4231,8 @@ static ssize_t hw_rev_show(struct device *device,
struct device_attribute *attr, char *buf)
{
struct mlx5_ib_dev *dev =
- container_of(device, struct mlx5_ib_dev, ib_dev.dev);
+ rdma_device_to_drv_device(device, struct mlx5_ib_dev, ib_dev);
+
return sprintf(buf, "%x\n", dev->mdev->rev_id);
}
static DEVICE_ATTR_RO(hw_rev);
@@ -4203,7 +4241,8 @@ static ssize_t board_id_show(struct device *device,
struct device_attribute *attr, char *buf)
{
struct mlx5_ib_dev *dev =
- container_of(device, struct mlx5_ib_dev, ib_dev.dev);
+ rdma_device_to_drv_device(device, struct mlx5_ib_dev, ib_dev);
+
return sprintf(buf, "%.*s\n", MLX5_BOARD_ID_LEN,
dev->mdev->board_id);
}
@@ -4689,23 +4728,28 @@ static int create_dev_resources(struct mlx5_ib_resources *devr)
{
struct ib_srq_init_attr attr;
struct mlx5_ib_dev *dev;
+ struct ib_device *ibdev;
struct ib_cq_init_attr cq_attr = {.cqe = 1};
int port;
int ret = 0;
dev = container_of(devr, struct mlx5_ib_dev, devr);
+ ibdev = &dev->ib_dev;
mutex_init(&devr->mutex);
- devr->p0 = mlx5_ib_alloc_pd(&dev->ib_dev, NULL, NULL);
- if (IS_ERR(devr->p0)) {
- ret = PTR_ERR(devr->p0);
- goto error0;
- }
- devr->p0->device = &dev->ib_dev;
+ devr->p0 = rdma_zalloc_drv_obj(ibdev, ib_pd);
+ if (!devr->p0)
+ return -ENOMEM;
+
+ devr->p0->device = ibdev;
devr->p0->uobject = NULL;
atomic_set(&devr->p0->usecnt, 0);
+ ret = mlx5_ib_alloc_pd(devr->p0, NULL, NULL);
+ if (ret)
+ goto error0;
+
devr->c0 = mlx5_ib_create_cq(&dev->ib_dev, &cq_attr, NULL, NULL);
if (IS_ERR(devr->c0)) {
ret = PTR_ERR(devr->c0);
@@ -4803,6 +4847,7 @@ error2:
error1:
mlx5_ib_dealloc_pd(devr->p0);
error0:
+ kfree(devr->p0);
return ret;
}
@@ -4818,6 +4863,7 @@ static void destroy_dev_resources(struct mlx5_ib_resources *devr)
mlx5_ib_dealloc_xrcd(devr->x1);
mlx5_ib_destroy_cq(devr->c0);
mlx5_ib_dealloc_pd(devr->p0);
+ kfree(devr->p0);
/* Make sure no change P_Key work items are still executing */
for (port = 0; port < dev->num_ports; ++port)
@@ -5567,9 +5613,7 @@ static bool mlx5_ib_bind_slave_port(struct mlx5_ib_dev *ibdev,
mpi->mdev_events.notifier_call = mlx5_ib_event_slave_port;
mlx5_notifier_register(mpi->mdev, &mpi->mdev_events);
- err = mlx5_ib_init_cong_debugfs(ibdev, port_num);
- if (err)
- goto unbind;
+ mlx5_ib_init_cong_debugfs(ibdev, port_num);
return true;
@@ -5781,11 +5825,10 @@ static struct ib_counters *mlx5_ib_create_counters(struct ib_device *device,
void mlx5_ib_stage_init_cleanup(struct mlx5_ib_dev *dev)
{
mlx5_ib_cleanup_multiport_master(dev);
-#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
- cleanup_srcu_struct(&dev->mr_srcu);
- drain_workqueue(dev->advise_mr_wq);
- destroy_workqueue(dev->advise_mr_wq);
-#endif
+ if (IS_ENABLED(CONFIG_INFINIBAND_ON_DEMAND_PAGING)) {
+ srcu_barrier(&dev->mr_srcu);
+ cleanup_srcu_struct(&dev->mr_srcu);
+ }
kfree(dev->port);
}
@@ -5838,19 +5881,11 @@ int mlx5_ib_stage_init_init(struct mlx5_ib_dev *dev)
spin_lock_init(&dev->memic.memic_lock);
dev->memic.dev = mdev;
-#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
- dev->advise_mr_wq = alloc_ordered_workqueue("mlx5_ib_advise_mr_wq", 0);
- if (!dev->advise_mr_wq) {
- err = -ENOMEM;
- goto err_mp;
- }
-
- err = init_srcu_struct(&dev->mr_srcu);
- if (err) {
- destroy_workqueue(dev->advise_mr_wq);
- goto err_mp;
+ if (IS_ENABLED(CONFIG_INFINIBAND_ON_DEMAND_PAGING)) {
+ err = init_srcu_struct(&dev->mr_srcu);
+ if (err)
+ goto err_mp;
}
-#endif
return 0;
err_mp:
@@ -5947,6 +5982,8 @@ static const struct ib_device_ops mlx5_ib_dev_ops = {
.req_notify_cq = mlx5_ib_arm_cq,
.rereg_user_mr = mlx5_ib_rereg_user_mr,
.resize_cq = mlx5_ib_resize_cq,
+ INIT_RDMA_OBJ_SIZE(ib_pd, mlx5_ib_pd, ibpd),
+ INIT_RDMA_OBJ_SIZE(ib_ucontext, mlx5_ib_ucontext, ibucontext),
};
static const struct ib_device_ops mlx5_ib_dev_flow_ipsec_ops = {
@@ -6213,7 +6250,7 @@ static int mlx5_ib_stage_odp_init(struct mlx5_ib_dev *dev)
return mlx5_ib_odp_init_one(dev);
}
-void mlx5_ib_stage_odp_cleanup(struct mlx5_ib_dev *dev)
+static void mlx5_ib_stage_odp_cleanup(struct mlx5_ib_dev *dev)
{
mlx5_ib_odp_cleanup_one(dev);
}
@@ -6242,8 +6279,9 @@ void mlx5_ib_stage_counters_cleanup(struct mlx5_ib_dev *dev)
static int mlx5_ib_stage_cong_debugfs_init(struct mlx5_ib_dev *dev)
{
- return mlx5_ib_init_cong_debugfs(dev,
- mlx5_core_native_port_num(dev->mdev) - 1);
+ mlx5_ib_init_cong_debugfs(dev,
+ mlx5_core_native_port_num(dev->mdev) - 1);
+ return 0;
}
static void mlx5_ib_stage_cong_debugfs_cleanup(struct mlx5_ib_dev *dev)
@@ -6293,7 +6331,7 @@ int mlx5_ib_stage_ib_reg_init(struct mlx5_ib_dev *dev)
name = "mlx5_%d";
else
name = "mlx5_bond_%d";
- return ib_register_device(&dev->ib_dev, name, NULL);
+ return ib_register_device(&dev->ib_dev, name);
}
void mlx5_ib_stage_pre_ib_reg_umr_cleanup(struct mlx5_ib_dev *dev)
@@ -6550,7 +6588,7 @@ static void *mlx5_ib_add(struct mlx5_core_dev *mdev)
if (mlx5_core_is_mp_slave(mdev) && ll == IB_LINK_LAYER_ETHERNET)
return mlx5_ib_add_slave_port(mdev);
- dev = (struct mlx5_ib_dev *)ib_alloc_device(sizeof(*dev));
+ dev = ib_alloc_device(mlx5_ib_dev, ib_dev);
if (!dev)
return NULL;
diff --git a/drivers/infiniband/hw/mlx5/mem.c b/drivers/infiniband/hw/mlx5/mem.c
index 549234988bb4..9f90be296ee0 100644
--- a/drivers/infiniband/hw/mlx5/mem.c
+++ b/drivers/infiniband/hw/mlx5/mem.c
@@ -111,7 +111,6 @@ void mlx5_ib_cont_pages(struct ib_umem *umem, u64 addr,
*count = i;
}
-#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
static u64 umem_dma_to_mtt(dma_addr_t umem_dma)
{
u64 mtt_entry = umem_dma & ODP_DMA_ADDR_MASK;
@@ -123,7 +122,6 @@ static u64 umem_dma_to_mtt(dma_addr_t umem_dma)
return mtt_entry;
}
-#endif
/*
* Populate the given array with bus addresses from the umem.
@@ -151,7 +149,7 @@ void __mlx5_ib_populate_pas(struct mlx5_ib_dev *dev, struct ib_umem *umem,
int len;
struct scatterlist *sg;
int entry;
-#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
+
if (umem->is_odp) {
WARN_ON(shift != 0);
WARN_ON(access_flags != (MLX5_IB_MTT_READ | MLX5_IB_MTT_WRITE));
@@ -164,7 +162,6 @@ void __mlx5_ib_populate_pas(struct mlx5_ib_dev *dev, struct ib_umem *umem,
}
return;
}
-#endif
i = 0;
for_each_sg(umem->sg_head.sgl, sg, umem->nmap, entry) {
diff --git a/drivers/infiniband/hw/mlx5/mlx5_ib.h b/drivers/infiniband/hw/mlx5/mlx5_ib.h
index eedba0d2ec4b..4a617d78eae1 100644
--- a/drivers/infiniband/hw/mlx5/mlx5_ib.h
+++ b/drivers/infiniband/hw/mlx5/mlx5_ib.h
@@ -36,6 +36,7 @@
#include <linux/kernel.h>
#include <linux/sched.h>
#include <rdma/ib_verbs.h>
+#include <rdma/ib_umem.h>
#include <rdma/ib_smi.h>
#include <linux/mlx5/driver.h>
#include <linux/mlx5/cq.h>
@@ -588,14 +589,27 @@ struct mlx5_ib_mr {
atomic_t num_leaf_free;
wait_queue_head_t q_leaf_free;
struct mlx5_async_work cb_work;
+ atomic_t num_pending_prefetch;
};
+static inline bool is_odp_mr(struct mlx5_ib_mr *mr)
+{
+ return IS_ENABLED(CONFIG_INFINIBAND_ON_DEMAND_PAGING) && mr->umem &&
+ mr->umem->is_odp;
+}
+
struct mlx5_ib_mw {
struct ib_mw ibmw;
struct mlx5_core_mkey mmkey;
int ndescs;
};
+struct mlx5_ib_devx_mr {
+ struct mlx5_core_mkey mmkey;
+ int ndescs;
+ struct rcu_head rcu;
+};
+
struct mlx5_ib_umr_context {
struct ib_cqe cqe;
enum ib_wc_status status;
@@ -624,7 +638,6 @@ struct mlx5_cache_ent {
spinlock_t lock;
- struct dentry *dir;
char name[4];
u32 order;
u32 xlt;
@@ -636,11 +649,6 @@ struct mlx5_cache_ent {
u32 miss;
u32 limit;
- struct dentry *fsize;
- struct dentry *fcur;
- struct dentry *fmiss;
- struct dentry *flimit;
-
struct mlx5_ib_dev *dev;
struct work_struct work;
struct delayed_work dwork;
@@ -912,7 +920,6 @@ struct mlx5_ib_dev {
/* Prevents soft lock on massive reg MRs */
struct mutex slow_path_mutex;
int fill_delay;
-#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
struct ib_odp_caps odp_caps;
u64 odp_max_size;
struct mlx5_ib_pf_eq odp_pf_eq;
@@ -923,8 +930,6 @@ struct mlx5_ib_dev {
*/
struct srcu_struct mr_srcu;
u32 null_mkey;
- struct workqueue_struct *advise_mr_wq;
-#endif
struct mlx5_ib_flow_db *flow_db;
/* protect resources needed as part of reset flow */
spinlock_t reset_flow_resource_lock;
@@ -1034,7 +1039,8 @@ to_mflow_act(struct ib_flow_action *ibact)
return container_of(ibact, struct mlx5_ib_flow_action, ib_action);
}
-int mlx5_ib_db_map_user(struct mlx5_ib_ucontext *context, unsigned long virt,
+int mlx5_ib_db_map_user(struct mlx5_ib_ucontext *context,
+ struct ib_udata *udata, unsigned long virt,
struct mlx5_db *db);
void mlx5_ib_db_unmap_user(struct mlx5_ib_ucontext *context, struct mlx5_db *db);
void __mlx5_ib_cq_clean(struct mlx5_ib_cq *cq, u32 qpn, struct mlx5_ib_srq *srq);
@@ -1069,9 +1075,12 @@ int mlx5_ib_post_send(struct ib_qp *ibqp, const struct ib_send_wr *wr,
const struct ib_send_wr **bad_wr);
int mlx5_ib_post_recv(struct ib_qp *ibqp, const struct ib_recv_wr *wr,
const struct ib_recv_wr **bad_wr);
-int mlx5_ib_read_user_wqe(struct mlx5_ib_qp *qp, int send, int wqe_index,
- void *buffer, u32 length,
- struct mlx5_ib_qp_base *base);
+int mlx5_ib_read_user_wqe_sq(struct mlx5_ib_qp *qp, int wqe_index, void *buffer,
+ int buflen, size_t *bc);
+int mlx5_ib_read_user_wqe_rq(struct mlx5_ib_qp *qp, int wqe_index, void *buffer,
+ int buflen, size_t *bc);
+int mlx5_ib_read_user_wqe_srq(struct mlx5_ib_srq *srq, int wqe_index,
+ void *buffer, int buflen, size_t *bc);
struct ib_cq *mlx5_ib_create_cq(struct ib_device *ibdev,
const struct ib_cq_init_attr *attr,
struct ib_ucontext *context,
@@ -1097,6 +1106,7 @@ int mlx5_ib_dealloc_mw(struct ib_mw *mw);
int mlx5_ib_update_xlt(struct mlx5_ib_mr *mr, u64 idx, int npages,
int page_shift, int flags);
struct mlx5_ib_mr *mlx5_ib_alloc_implicit_mr(struct mlx5_ib_pd *pd,
+ struct ib_udata *udata,
int access_flags);
void mlx5_ib_free_implicit_mr(struct mlx5_ib_mr *mr);
int mlx5_ib_rereg_user_mr(struct ib_mr *ib_mr, int flags, u64 start,
@@ -1214,6 +1224,9 @@ mlx5_ib_advise_mr_prefetch(struct ib_pd *pd,
{
return -EOPNOTSUPP;
}
+static inline void mlx5_ib_invalidate_range(struct ib_umem_odp *umem_odp,
+ unsigned long start,
+ unsigned long end){};
#endif /* CONFIG_INFINIBAND_ON_DEMAND_PAGING */
/* Needed for rep profile */
@@ -1253,7 +1266,7 @@ __be16 mlx5_get_roce_udp_sport(struct mlx5_ib_dev *dev,
const struct ib_gid_attr *attr);
void mlx5_ib_cleanup_cong_debugfs(struct mlx5_ib_dev *dev, u8 port_num);
-int mlx5_ib_init_cong_debugfs(struct mlx5_ib_dev *dev, u8 port_num);
+void mlx5_ib_init_cong_debugfs(struct mlx5_ib_dev *dev, u8 port_num);
/* GSI QP helper functions */
struct ib_qp *mlx5_ib_gsi_create_qp(struct ib_pd *pd,
diff --git a/drivers/infiniband/hw/mlx5/mr.c b/drivers/infiniband/hw/mlx5/mr.c
index bf2b6ea23851..c85f00255884 100644
--- a/drivers/infiniband/hw/mlx5/mr.c
+++ b/drivers/infiniband/hw/mlx5/mr.c
@@ -71,10 +71,9 @@ static int destroy_mkey(struct mlx5_ib_dev *dev, struct mlx5_ib_mr *mr)
{
int err = mlx5_core_destroy_mkey(dev->mdev, &mr->mmkey);
-#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
- /* Wait until all page fault handlers using the mr complete. */
- synchronize_srcu(&dev->mr_srcu);
-#endif
+ if (IS_ENABLED(CONFIG_INFINIBAND_ON_DEMAND_PAGING))
+ /* Wait until all page fault handlers using the mr complete. */
+ synchronize_srcu(&dev->mr_srcu);
return err;
}
@@ -95,10 +94,9 @@ static bool use_umr_mtt_update(struct mlx5_ib_mr *mr, u64 start, u64 length)
length + (start & (MLX5_ADAPTER_PAGE_SIZE - 1));
}
-#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
static void update_odp_mr(struct mlx5_ib_mr *mr)
{
- if (mr->umem->is_odp) {
+ if (is_odp_mr(mr)) {
/*
* This barrier prevents the compiler from moving the
* setting of umem->odp_data->private to point to our
@@ -121,7 +119,6 @@ static void update_odp_mr(struct mlx5_ib_mr *mr)
smp_wmb();
}
}
-#endif
static void reg_mr_callback(int status, struct mlx5_async_work *context)
{
@@ -257,9 +254,8 @@ static void remove_keys(struct mlx5_ib_dev *dev, int c, int num)
mlx5_core_destroy_mkey(dev->mdev, &mr->mmkey);
}
-#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
- synchronize_srcu(&dev->mr_srcu);
-#endif
+ if (IS_ENABLED(CONFIG_INFINIBAND_ON_DEMAND_PAGING))
+ synchronize_srcu(&dev->mr_srcu);
list_for_each_entry_safe(mr, tmp_mr, &del_list, list) {
list_del(&mr->list);
@@ -611,52 +607,27 @@ static void mlx5_mr_cache_debugfs_cleanup(struct mlx5_ib_dev *dev)
dev->cache.root = NULL;
}
-static int mlx5_mr_cache_debugfs_init(struct mlx5_ib_dev *dev)
+static void mlx5_mr_cache_debugfs_init(struct mlx5_ib_dev *dev)
{
struct mlx5_mr_cache *cache = &dev->cache;
struct mlx5_cache_ent *ent;
+ struct dentry *dir;
int i;
if (!mlx5_debugfs_root || dev->rep)
- return 0;
+ return;
cache->root = debugfs_create_dir("mr_cache", dev->mdev->priv.dbg_root);
- if (!cache->root)
- return -ENOMEM;
for (i = 0; i < MAX_MR_CACHE_ENTRIES; i++) {
ent = &cache->ent[i];
sprintf(ent->name, "%d", ent->order);
- ent->dir = debugfs_create_dir(ent->name, cache->root);
- if (!ent->dir)
- goto err;
-
- ent->fsize = debugfs_create_file("size", 0600, ent->dir, ent,
- &size_fops);
- if (!ent->fsize)
- goto err;
-
- ent->flimit = debugfs_create_file("limit", 0600, ent->dir, ent,
- &limit_fops);
- if (!ent->flimit)
- goto err;
-
- ent->fcur = debugfs_create_u32("cur", 0400, ent->dir,
- &ent->cur);
- if (!ent->fcur)
- goto err;
-
- ent->fmiss = debugfs_create_u32("miss", 0600, ent->dir,
- &ent->miss);
- if (!ent->fmiss)
- goto err;
+ dir = debugfs_create_dir(ent->name, cache->root);
+ debugfs_create_file("size", 0600, dir, ent, &size_fops);
+ debugfs_create_file("limit", 0600, dir, ent, &limit_fops);
+ debugfs_create_u32("cur", 0400, dir, &ent->cur);
+ debugfs_create_u32("miss", 0600, dir, &ent->miss);
}
-
- return 0;
-err:
- mlx5_mr_cache_debugfs_cleanup(dev);
-
- return -ENOMEM;
}
static void delay_time_func(struct timer_list *t)
@@ -670,7 +641,6 @@ int mlx5_mr_cache_init(struct mlx5_ib_dev *dev)
{
struct mlx5_mr_cache *cache = &dev->cache;
struct mlx5_cache_ent *ent;
- int err;
int i;
mutex_init(&dev->slow_path_mutex);
@@ -715,14 +685,7 @@ int mlx5_mr_cache_init(struct mlx5_ib_dev *dev)
queue_work(cache->wq, &ent->work);
}
- err = mlx5_mr_cache_debugfs_init(dev);
- if (err)
- mlx5_ib_warn(dev, "cache debugfs failure\n");
-
- /*
- * We don't want to fail driver if debugfs failed to initialize,
- * so we are not forwarding error to the user.
- */
+ mlx5_mr_cache_debugfs_init(dev);
return 0;
}
@@ -822,18 +785,17 @@ static int mr_cache_max_order(struct mlx5_ib_dev *dev)
return MLX5_MAX_UMR_SHIFT;
}
-static int mr_umem_get(struct ib_pd *pd, u64 start, u64 length,
- int access_flags, struct ib_umem **umem,
- int *npages, int *page_shift, int *ncont,
- int *order)
+static int mr_umem_get(struct mlx5_ib_dev *dev, struct ib_udata *udata,
+ u64 start, u64 length, int access_flags,
+ struct ib_umem **umem, int *npages, int *page_shift,
+ int *ncont, int *order)
{
- struct mlx5_ib_dev *dev = to_mdev(pd->device);
struct ib_umem *u;
int err;
*umem = NULL;
- u = ib_umem_get(pd->uobject->context, start, length, access_flags, 0);
+ u = ib_umem_get(udata, start, length, access_flags, 0);
err = PTR_ERR_OR_ZERO(u);
if (err) {
mlx5_ib_dbg(dev, "umem get failed (%d)\n", err);
@@ -1306,21 +1268,20 @@ struct ib_mr *mlx5_ib_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
mlx5_ib_dbg(dev, "start 0x%llx, virt_addr 0x%llx, length 0x%llx, access_flags 0x%x\n",
start, virt_addr, length, access_flags);
-#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
- if (!start && length == U64_MAX) {
+ if (IS_ENABLED(CONFIG_INFINIBAND_ON_DEMAND_PAGING) && !start &&
+ length == U64_MAX) {
if (!(access_flags & IB_ACCESS_ON_DEMAND) ||
!(dev->odp_caps.general_caps & IB_ODP_SUPPORT_IMPLICIT))
return ERR_PTR(-EINVAL);
- mr = mlx5_ib_alloc_implicit_mr(to_mpd(pd), access_flags);
+ mr = mlx5_ib_alloc_implicit_mr(to_mpd(pd), udata, access_flags);
if (IS_ERR(mr))
return ERR_CAST(mr);
return &mr->ibmr;
}
-#endif
- err = mr_umem_get(pd, start, length, access_flags, &umem, &npages,
- &page_shift, &ncont, &order);
+ err = mr_umem_get(dev, udata, start, length, access_flags, &umem,
+ &npages, &page_shift, &ncont, &order);
if (err < 0)
return ERR_PTR(err);
@@ -1361,9 +1322,7 @@ struct ib_mr *mlx5_ib_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
mr->umem = umem;
set_mr_fields(dev, mr, npages, length, access_flags);
-#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
update_odp_mr(mr);
-#endif
if (!populate_mtts) {
int update_xlt_flags = MLX5_IB_UPD_XLT_ENABLE;
@@ -1380,9 +1339,11 @@ struct ib_mr *mlx5_ib_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
}
}
-#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
- mr->live = 1;
-#endif
+ if (IS_ENABLED(CONFIG_INFINIBAND_ON_DEMAND_PAGING)) {
+ mr->live = 1;
+ atomic_set(&mr->num_pending_prefetch, 0);
+ }
+
return &mr->ibmr;
error:
ib_umem_release(umem);
@@ -1470,8 +1431,9 @@ int mlx5_ib_rereg_user_mr(struct ib_mr *ib_mr, int flags, u64 start,
flags |= IB_MR_REREG_TRANS;
ib_umem_release(mr->umem);
mr->umem = NULL;
- err = mr_umem_get(pd, addr, len, access_flags, &mr->umem,
- &npages, &page_shift, &ncont, &order);
+ err = mr_umem_get(dev, udata, addr, len, access_flags,
+ &mr->umem, &npages, &page_shift, &ncont,
+ &order);
if (err)
goto err;
}
@@ -1497,9 +1459,8 @@ int mlx5_ib_rereg_user_mr(struct ib_mr *ib_mr, int flags, u64 start,
}
mr->allocated_from_cache = 0;
-#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
- mr->live = 1;
-#endif
+ if (IS_ENABLED(CONFIG_INFINIBAND_ON_DEMAND_PAGING))
+ mr->live = 1;
} else {
/*
* Send a UMR WQE
@@ -1528,9 +1489,7 @@ int mlx5_ib_rereg_user_mr(struct ib_mr *ib_mr, int flags, u64 start,
set_mr_fields(dev, mr, npages, len, access_flags);
-#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
update_odp_mr(mr);
-#endif
return 0;
err:
@@ -1616,12 +1575,19 @@ static void dereg_mr(struct mlx5_ib_dev *dev, struct mlx5_ib_mr *mr)
int npages = mr->npages;
struct ib_umem *umem = mr->umem;
-#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
- if (umem && umem->is_odp) {
+ if (is_odp_mr(mr)) {
struct ib_umem_odp *umem_odp = to_ib_umem_odp(umem);
- /* Prevent new page faults from succeeding */
+ /* Prevent new page faults and
+ * prefetch requests from succeeding
+ */
mr->live = 0;
+
+ /* dequeue pending prefetch requests for the mr */
+ if (atomic_read(&mr->num_pending_prefetch))
+ flush_workqueue(system_unbound_wq);
+ WARN_ON(atomic_read(&mr->num_pending_prefetch));
+
/* Wait for all running page-fault handlers to finish. */
synchronize_srcu(&dev->mr_srcu);
/* Destroy all page mappings */
@@ -1641,7 +1607,7 @@ static void dereg_mr(struct mlx5_ib_dev *dev, struct mlx5_ib_mr *mr)
/* Avoid double-freeing the umem. */
umem = NULL;
}
-#endif
+
clean_mr(dev, mr);
/*
diff --git a/drivers/infiniband/hw/mlx5/odp.c b/drivers/infiniband/hw/mlx5/odp.c
index 4ee32964e1dd..c20bfc41ecf1 100644
--- a/drivers/infiniband/hw/mlx5/odp.c
+++ b/drivers/infiniband/hw/mlx5/odp.c
@@ -101,9 +101,9 @@ static int check_parent(struct ib_umem_odp *odp,
return mr && mr->parent == parent && !odp->dying;
}
-struct ib_ucontext_per_mm *mr_to_per_mm(struct mlx5_ib_mr *mr)
+static struct ib_ucontext_per_mm *mr_to_per_mm(struct mlx5_ib_mr *mr)
{
- if (WARN_ON(!mr || !mr->umem || !mr->umem->is_odp))
+ if (WARN_ON(!mr || !is_odp_mr(mr)))
return NULL;
return to_ib_umem_odp(mr->umem)->per_mm;
@@ -315,6 +315,9 @@ void mlx5_ib_internal_fill_odp_caps(struct mlx5_ib_dev *dev)
if (MLX5_CAP_ODP(dev->mdev, ud_odp_caps.send))
caps->per_transport_caps.ud_odp_caps |= IB_ODP_SUPPORT_SEND;
+ if (MLX5_CAP_ODP(dev->mdev, ud_odp_caps.srq_receive))
+ caps->per_transport_caps.ud_odp_caps |= IB_ODP_SUPPORT_SRQ_RECV;
+
if (MLX5_CAP_ODP(dev->mdev, rc_odp_caps.send))
caps->per_transport_caps.rc_odp_caps |= IB_ODP_SUPPORT_SEND;
@@ -330,6 +333,27 @@ void mlx5_ib_internal_fill_odp_caps(struct mlx5_ib_dev *dev)
if (MLX5_CAP_ODP(dev->mdev, rc_odp_caps.atomic))
caps->per_transport_caps.rc_odp_caps |= IB_ODP_SUPPORT_ATOMIC;
+ if (MLX5_CAP_ODP(dev->mdev, rc_odp_caps.srq_receive))
+ caps->per_transport_caps.rc_odp_caps |= IB_ODP_SUPPORT_SRQ_RECV;
+
+ if (MLX5_CAP_ODP(dev->mdev, xrc_odp_caps.send))
+ caps->per_transport_caps.xrc_odp_caps |= IB_ODP_SUPPORT_SEND;
+
+ if (MLX5_CAP_ODP(dev->mdev, xrc_odp_caps.receive))
+ caps->per_transport_caps.xrc_odp_caps |= IB_ODP_SUPPORT_RECV;
+
+ if (MLX5_CAP_ODP(dev->mdev, xrc_odp_caps.write))
+ caps->per_transport_caps.xrc_odp_caps |= IB_ODP_SUPPORT_WRITE;
+
+ if (MLX5_CAP_ODP(dev->mdev, xrc_odp_caps.read))
+ caps->per_transport_caps.xrc_odp_caps |= IB_ODP_SUPPORT_READ;
+
+ if (MLX5_CAP_ODP(dev->mdev, xrc_odp_caps.atomic))
+ caps->per_transport_caps.xrc_odp_caps |= IB_ODP_SUPPORT_ATOMIC;
+
+ if (MLX5_CAP_ODP(dev->mdev, xrc_odp_caps.srq_receive))
+ caps->per_transport_caps.xrc_odp_caps |= IB_ODP_SUPPORT_SRQ_RECV;
+
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))
@@ -439,7 +463,7 @@ next_mr:
if (nentries)
nentries++;
} else {
- odp = ib_alloc_odp_umem(odp_mr->per_mm, addr,
+ odp = ib_alloc_odp_umem(odp_mr, addr,
MLX5_IMR_MTT_SIZE);
if (IS_ERR(odp)) {
mutex_unlock(&odp_mr->umem_mutex);
@@ -492,13 +516,13 @@ next_mr:
}
struct mlx5_ib_mr *mlx5_ib_alloc_implicit_mr(struct mlx5_ib_pd *pd,
+ struct ib_udata *udata,
int access_flags)
{
- struct ib_ucontext *ctx = pd->ibpd.uobject->context;
struct mlx5_ib_mr *imr;
struct ib_umem *umem;
- umem = ib_umem_get(ctx, 0, 0, IB_ACCESS_ON_DEMAND, 0);
+ umem = ib_umem_get(udata, 0, 0, access_flags, 0);
if (IS_ERR(umem))
return ERR_CAST(umem);
@@ -511,6 +535,7 @@ struct mlx5_ib_mr *mlx5_ib_alloc_implicit_mr(struct mlx5_ib_pd *pd,
imr->umem = umem;
init_waitqueue_head(&imr->q_leaf_free);
atomic_set(&imr->num_leaf_free, 0);
+ atomic_set(&imr->num_pending_prefetch, 0);
return imr;
}
@@ -685,6 +710,21 @@ struct pf_frame {
int depth;
};
+static int get_indirect_num_descs(struct mlx5_core_mkey *mmkey)
+{
+ struct mlx5_ib_mw *mw;
+ struct mlx5_ib_devx_mr *devx_mr;
+
+ if (mmkey->type == MLX5_MKEY_MW) {
+ mw = container_of(mmkey, struct mlx5_ib_mw, mmkey);
+ return mw->ndescs;
+ }
+
+ devx_mr = container_of(mmkey, struct mlx5_ib_devx_mr,
+ mmkey);
+ return devx_mr->ndescs;
+}
+
/*
* Handle a single data segment in a page-fault WQE or RDMA region.
*
@@ -696,7 +736,8 @@ struct pf_frame {
* -EFAULT when there's an error mapping the requested pages. The caller will
* abort the page fault handling.
*/
-static int pagefault_single_data_segment(struct mlx5_ib_dev *dev, u32 key,
+static int pagefault_single_data_segment(struct mlx5_ib_dev *dev,
+ struct ib_pd *pd, u32 key,
u64 io_virt, size_t bcnt,
u32 *bytes_committed,
u32 *bytes_mapped, u32 flags)
@@ -705,11 +746,11 @@ static int pagefault_single_data_segment(struct mlx5_ib_dev *dev, u32 key,
bool prefetch = flags & MLX5_PF_FLAGS_PREFETCH;
struct pf_frame *head = NULL, *frame;
struct mlx5_core_mkey *mmkey;
- struct mlx5_ib_mw *mw;
struct mlx5_ib_mr *mr;
struct mlx5_klm *pklm;
u32 *out = NULL;
size_t offset;
+ int ndescs;
srcu_key = srcu_read_lock(&dev->mr_srcu);
@@ -739,12 +780,18 @@ next_mr:
goto srcu_unlock;
}
- if (prefetch && !mr->umem->is_odp) {
- ret = -EINVAL;
- goto srcu_unlock;
+ if (prefetch) {
+ if (!is_odp_mr(mr) ||
+ mr->ibmr.pd != pd) {
+ mlx5_ib_dbg(dev, "Invalid prefetch request: %s\n",
+ is_odp_mr(mr) ? "MR is not ODP" :
+ "PD is not of the MR");
+ ret = -EINVAL;
+ goto srcu_unlock;
+ }
}
- if (!mr->umem->is_odp) {
+ if (!is_odp_mr(mr)) {
mlx5_ib_dbg(dev, "skipping non ODP MR (lkey=0x%06x) in page fault handler.\n",
key);
if (bytes_mapped)
@@ -762,7 +809,8 @@ next_mr:
break;
case MLX5_MKEY_MW:
- mw = container_of(mmkey, struct mlx5_ib_mw, mmkey);
+ case MLX5_MKEY_INDIRECT_DEVX:
+ ndescs = get_indirect_num_descs(mmkey);
if (depth >= MLX5_CAP_GEN(dev->mdev, max_indirection)) {
mlx5_ib_dbg(dev, "indirection level exceeded\n");
@@ -771,7 +819,7 @@ next_mr:
}
outlen = MLX5_ST_SZ_BYTES(query_mkey_out) +
- sizeof(*pklm) * (mw->ndescs - 2);
+ sizeof(*pklm) * (ndescs - 2);
if (outlen > cur_outlen) {
kfree(out);
@@ -786,14 +834,14 @@ next_mr:
pklm = (struct mlx5_klm *)MLX5_ADDR_OF(query_mkey_out, out,
bsf0_klm0_pas_mtt0_1);
- ret = mlx5_core_query_mkey(dev->mdev, &mw->mmkey, out, outlen);
+ ret = mlx5_core_query_mkey(dev->mdev, mmkey, out, outlen);
if (ret)
goto srcu_unlock;
offset = io_virt - MLX5_GET64(query_mkey_out, out,
memory_key_mkey_entry.start_addr);
- for (i = 0; bcnt && i < mw->ndescs; i++, pklm++) {
+ for (i = 0; bcnt && i < ndescs; i++, pklm++) {
if (offset >= be32_to_cpu(pklm->bcount)) {
offset -= be32_to_cpu(pklm->bcount);
continue;
@@ -853,7 +901,6 @@ srcu_unlock:
/**
* Parse a series of data segments for page fault handling.
*
- * @qp the QP on which the fault occurred.
* @pfault contains page fault information.
* @wqe points at the first data segment in the WQE.
* @wqe_end points after the end of the WQE.
@@ -870,7 +917,7 @@ srcu_unlock:
*/
static int pagefault_data_segments(struct mlx5_ib_dev *dev,
struct mlx5_pagefault *pfault,
- struct mlx5_ib_qp *qp, void *wqe,
+ void *wqe,
void *wqe_end, u32 *bytes_mapped,
u32 *total_wqe_bytes, int receive_queue)
{
@@ -881,10 +928,6 @@ static int pagefault_data_segments(struct mlx5_ib_dev *dev,
size_t bcnt;
int inline_segment;
- /* Skip SRQ next-WQE segment. */
- if (receive_queue && qp->ibqp.srq)
- wqe += sizeof(struct mlx5_wqe_srq_next_seg);
-
if (bytes_mapped)
*bytes_mapped = 0;
if (total_wqe_bytes)
@@ -928,7 +971,8 @@ static int pagefault_data_segments(struct mlx5_ib_dev *dev,
continue;
}
- ret = pagefault_single_data_segment(dev, key, io_virt, bcnt,
+ ret = pagefault_single_data_segment(dev, NULL, key,
+ io_virt, bcnt,
&pfault->bytes_committed,
bytes_mapped, 0);
if (ret < 0)
@@ -1009,6 +1053,10 @@ static int mlx5_ib_mr_initiator_pfault_handler(
MLX5_WQE_CTRL_OPCODE_MASK;
switch (qp->ibqp.qp_type) {
+ case IB_QPT_XRC_INI:
+ *wqe += sizeof(struct mlx5_wqe_xrc_seg);
+ transport_caps = dev->odp_caps.per_transport_caps.xrc_odp_caps;
+ break;
case IB_QPT_RC:
transport_caps = dev->odp_caps.per_transport_caps.rc_odp_caps;
break;
@@ -1028,7 +1076,7 @@ static int mlx5_ib_mr_initiator_pfault_handler(
return -EFAULT;
}
- if (qp->ibqp.qp_type != IB_QPT_RC) {
+ if (qp->ibqp.qp_type == IB_QPT_UD) {
av = *wqe;
if (av->dqp_dct & cpu_to_be32(MLX5_EXTENDED_UD_AV))
*wqe += sizeof(struct mlx5_av);
@@ -1053,21 +1101,34 @@ static int mlx5_ib_mr_initiator_pfault_handler(
}
/*
- * Parse responder WQE. Advances the wqe pointer to point at the
- * scatter-gather list, and set wqe_end to the end of the WQE.
+ * Parse responder WQE and set wqe_end to the end of the WQE.
*/
-static int mlx5_ib_mr_responder_pfault_handler(
- struct mlx5_ib_dev *dev, struct mlx5_pagefault *pfault,
- struct mlx5_ib_qp *qp, void **wqe, void **wqe_end, int wqe_length)
+static int mlx5_ib_mr_responder_pfault_handler_srq(struct mlx5_ib_dev *dev,
+ struct mlx5_ib_srq *srq,
+ void **wqe, void **wqe_end,
+ int wqe_length)
{
- struct mlx5_ib_wq *wq = &qp->rq;
- int wqe_size = 1 << wq->wqe_shift;
+ int wqe_size = 1 << srq->msrq.wqe_shift;
- if (qp->ibqp.srq) {
- mlx5_ib_err(dev, "ODP fault on SRQ is not supported\n");
+ if (wqe_size > wqe_length) {
+ mlx5_ib_err(dev, "Couldn't read all of the receive WQE's content\n");
return -EFAULT;
}
+ *wqe_end = *wqe + wqe_size;
+ *wqe += sizeof(struct mlx5_wqe_srq_next_seg);
+
+ return 0;
+}
+
+static int mlx5_ib_mr_responder_pfault_handler_rq(struct mlx5_ib_dev *dev,
+ struct mlx5_ib_qp *qp,
+ void *wqe, void **wqe_end,
+ int wqe_length)
+{
+ struct mlx5_ib_wq *wq = &qp->rq;
+ int wqe_size = 1 << wq->wqe_shift;
+
if (qp->wq_sig) {
mlx5_ib_err(dev, "ODP fault with WQE signatures is not supported\n");
return -EFAULT;
@@ -1091,7 +1152,7 @@ invalid_transport_or_opcode:
return -EFAULT;
}
- *wqe_end = *wqe + wqe_size;
+ *wqe_end = wqe + wqe_size;
return 0;
}
@@ -1099,22 +1160,25 @@ invalid_transport_or_opcode:
static inline struct mlx5_core_rsc_common *odp_get_rsc(struct mlx5_ib_dev *dev,
u32 wq_num, int pf_type)
{
- enum mlx5_res_type res_type;
+ struct mlx5_core_rsc_common *common = NULL;
+ struct mlx5_core_srq *srq;
switch (pf_type) {
case MLX5_WQE_PF_TYPE_RMP:
- res_type = MLX5_RES_SRQ;
+ srq = mlx5_cmd_get_srq(dev, wq_num);
+ if (srq)
+ common = &srq->common;
break;
case MLX5_WQE_PF_TYPE_REQ_SEND_OR_WRITE:
case MLX5_WQE_PF_TYPE_RESP:
case MLX5_WQE_PF_TYPE_REQ_READ_OR_ATOMIC:
- res_type = MLX5_RES_QP;
+ common = mlx5_core_res_hold(dev->mdev, wq_num, MLX5_RES_QP);
break;
default:
- return NULL;
+ break;
}
- return mlx5_core_res_hold(dev->mdev, wq_num, res_type);
+ return common;
}
static inline struct mlx5_ib_qp *res_to_qp(struct mlx5_core_rsc_common *res)
@@ -1124,6 +1188,14 @@ static inline struct mlx5_ib_qp *res_to_qp(struct mlx5_core_rsc_common *res)
return to_mibqp(mqp);
}
+static inline struct mlx5_ib_srq *res_to_srq(struct mlx5_core_rsc_common *res)
+{
+ struct mlx5_core_srq *msrq =
+ container_of(res, struct mlx5_core_srq, common);
+
+ return to_mibsrq(msrq);
+}
+
static void mlx5_ib_mr_wqe_pfault_handler(struct mlx5_ib_dev *dev,
struct mlx5_pagefault *pfault)
{
@@ -1134,8 +1206,10 @@ static void mlx5_ib_mr_wqe_pfault_handler(struct mlx5_ib_dev *dev,
int resume_with_error = 1;
u16 wqe_index = pfault->wqe.wqe_index;
int requestor = pfault->type & MLX5_PFAULT_REQUESTOR;
- struct mlx5_core_rsc_common *res;
- struct mlx5_ib_qp *qp;
+ struct mlx5_core_rsc_common *res = NULL;
+ struct mlx5_ib_qp *qp = NULL;
+ struct mlx5_ib_srq *srq = NULL;
+ size_t bytes_copied;
res = odp_get_rsc(dev, pfault->wqe.wq_num, pfault->type);
if (!res) {
@@ -1147,6 +1221,10 @@ static void mlx5_ib_mr_wqe_pfault_handler(struct mlx5_ib_dev *dev,
case MLX5_RES_QP:
qp = res_to_qp(res);
break;
+ case MLX5_RES_SRQ:
+ case MLX5_RES_XSRQ:
+ srq = res_to_srq(res);
+ break;
default:
mlx5_ib_err(dev, "wqe page fault for unsupported type %d\n", pfault->type);
goto resolve_page_fault;
@@ -1158,9 +1236,23 @@ static void mlx5_ib_mr_wqe_pfault_handler(struct mlx5_ib_dev *dev,
goto resolve_page_fault;
}
- ret = mlx5_ib_read_user_wqe(qp, requestor, wqe_index, buffer,
- PAGE_SIZE, &qp->trans_qp.base);
- if (ret < 0) {
+ if (qp) {
+ if (requestor) {
+ ret = mlx5_ib_read_user_wqe_sq(qp, wqe_index,
+ buffer, PAGE_SIZE,
+ &bytes_copied);
+ } else {
+ ret = mlx5_ib_read_user_wqe_rq(qp, wqe_index,
+ buffer, PAGE_SIZE,
+ &bytes_copied);
+ }
+ } else {
+ ret = mlx5_ib_read_user_wqe_srq(srq, wqe_index,
+ buffer, PAGE_SIZE,
+ &bytes_copied);
+ }
+
+ if (ret) {
mlx5_ib_err(dev, "Failed reading a WQE following page fault, error=%d, wqe_index=%x, qpn=%x\n",
ret, wqe_index, pfault->token);
goto resolve_page_fault;
@@ -1168,11 +1260,18 @@ static void mlx5_ib_mr_wqe_pfault_handler(struct mlx5_ib_dev *dev,
wqe = buffer;
if (requestor)
- ret = mlx5_ib_mr_initiator_pfault_handler(dev, pfault, qp, &wqe,
- &wqe_end, ret);
+ ret = mlx5_ib_mr_initiator_pfault_handler(dev, pfault, qp,
+ &wqe, &wqe_end,
+ bytes_copied);
+ else if (qp)
+ ret = mlx5_ib_mr_responder_pfault_handler_rq(dev, qp,
+ wqe, &wqe_end,
+ bytes_copied);
else
- ret = mlx5_ib_mr_responder_pfault_handler(dev, pfault, qp, &wqe,
- &wqe_end, ret);
+ ret = mlx5_ib_mr_responder_pfault_handler_srq(dev, srq,
+ &wqe, &wqe_end,
+ bytes_copied);
+
if (ret < 0)
goto resolve_page_fault;
@@ -1181,7 +1280,7 @@ static void mlx5_ib_mr_wqe_pfault_handler(struct mlx5_ib_dev *dev,
goto resolve_page_fault;
}
- ret = pagefault_data_segments(dev, pfault, qp, wqe, wqe_end,
+ ret = pagefault_data_segments(dev, pfault, wqe, wqe_end,
&bytes_mapped, &total_wqe_bytes,
!requestor);
if (ret == -EAGAIN) {
@@ -1240,7 +1339,7 @@ static void mlx5_ib_mr_rdma_pfault_handler(struct mlx5_ib_dev *dev,
prefetch_len = min(MAX_PREFETCH_LEN, prefetch_len);
}
- ret = pagefault_single_data_segment(dev, rkey, address, length,
+ ret = pagefault_single_data_segment(dev, NULL, rkey, address, length,
&pfault->bytes_committed, NULL,
0);
if (ret == -EAGAIN) {
@@ -1267,7 +1366,7 @@ static void mlx5_ib_mr_rdma_pfault_handler(struct mlx5_ib_dev *dev,
if (prefetch_activated) {
u32 bytes_committed = 0;
- ret = pagefault_single_data_segment(dev, rkey, address,
+ ret = pagefault_single_data_segment(dev, NULL, rkey, address,
prefetch_len,
&bytes_committed, NULL,
0);
@@ -1564,30 +1663,98 @@ int mlx5_ib_odp_init(void)
struct prefetch_mr_work {
struct work_struct work;
- struct mlx5_ib_dev *dev;
+ struct ib_pd *pd;
u32 pf_flags;
u32 num_sge;
struct ib_sge sg_list[0];
};
-static int mlx5_ib_prefetch_sg_list(struct mlx5_ib_dev *dev, u32 pf_flags,
+static void num_pending_prefetch_dec(struct mlx5_ib_dev *dev,
+ struct ib_sge *sg_list, u32 num_sge,
+ u32 from)
+{
+ u32 i;
+ int srcu_key;
+
+ srcu_key = srcu_read_lock(&dev->mr_srcu);
+
+ for (i = from; i < num_sge; ++i) {
+ struct mlx5_core_mkey *mmkey;
+ struct mlx5_ib_mr *mr;
+
+ mmkey = __mlx5_mr_lookup(dev->mdev,
+ mlx5_base_mkey(sg_list[i].lkey));
+ mr = container_of(mmkey, struct mlx5_ib_mr, mmkey);
+ atomic_dec(&mr->num_pending_prefetch);
+ }
+
+ srcu_read_unlock(&dev->mr_srcu, srcu_key);
+}
+
+static bool num_pending_prefetch_inc(struct ib_pd *pd,
+ struct ib_sge *sg_list, u32 num_sge)
+{
+ struct mlx5_ib_dev *dev = to_mdev(pd->device);
+ bool ret = true;
+ u32 i;
+
+ for (i = 0; i < num_sge; ++i) {
+ struct mlx5_core_mkey *mmkey;
+ struct mlx5_ib_mr *mr;
+
+ mmkey = __mlx5_mr_lookup(dev->mdev,
+ mlx5_base_mkey(sg_list[i].lkey));
+ if (!mmkey || mmkey->key != sg_list[i].lkey) {
+ ret = false;
+ break;
+ }
+
+ if (mmkey->type != MLX5_MKEY_MR) {
+ ret = false;
+ break;
+ }
+
+ mr = container_of(mmkey, struct mlx5_ib_mr, mmkey);
+
+ if (mr->ibmr.pd != pd) {
+ ret = false;
+ break;
+ }
+
+ if (!mr->live) {
+ ret = false;
+ break;
+ }
+
+ atomic_inc(&mr->num_pending_prefetch);
+ }
+
+ if (!ret)
+ num_pending_prefetch_dec(dev, sg_list, i, 0);
+
+ return ret;
+}
+
+static int mlx5_ib_prefetch_sg_list(struct ib_pd *pd, u32 pf_flags,
struct ib_sge *sg_list, u32 num_sge)
{
- int i;
+ u32 i;
+ int ret = 0;
+ struct mlx5_ib_dev *dev = to_mdev(pd->device);
for (i = 0; i < num_sge; ++i) {
struct ib_sge *sg = &sg_list[i];
int bytes_committed = 0;
- int ret;
- ret = pagefault_single_data_segment(dev, sg->lkey, sg->addr,
+ ret = pagefault_single_data_segment(dev, pd, sg->lkey, sg->addr,
sg->length,
&bytes_committed, NULL,
pf_flags);
if (ret < 0)
- return ret;
+ break;
}
- return 0;
+
+ return ret < 0 ? ret : 0;
}
static void mlx5_ib_prefetch_mr_work(struct work_struct *work)
@@ -1595,12 +1762,14 @@ static void mlx5_ib_prefetch_mr_work(struct work_struct *work)
struct prefetch_mr_work *w =
container_of(work, struct prefetch_mr_work, work);
- if (ib_device_try_get(&w->dev->ib_dev)) {
- mlx5_ib_prefetch_sg_list(w->dev, w->pf_flags, w->sg_list,
+ if (ib_device_try_get(w->pd->device)) {
+ mlx5_ib_prefetch_sg_list(w->pd, w->pf_flags, w->sg_list,
w->num_sge);
- ib_device_put(&w->dev->ib_dev);
+ ib_device_put(w->pd->device);
}
- put_device(&w->dev->ib_dev.dev);
+
+ num_pending_prefetch_dec(to_mdev(w->pd->device), w->sg_list,
+ w->num_sge, 0);
kfree(w);
}
@@ -1611,12 +1780,14 @@ int mlx5_ib_advise_mr_prefetch(struct ib_pd *pd,
struct mlx5_ib_dev *dev = to_mdev(pd->device);
u32 pf_flags = MLX5_PF_FLAGS_PREFETCH;
struct prefetch_mr_work *work;
+ bool valid_req;
+ int srcu_key;
if (advice == IB_UVERBS_ADVISE_MR_ADVICE_PREFETCH)
pf_flags |= MLX5_PF_FLAGS_DOWNGRADE;
if (flags & IB_UVERBS_ADVISE_MR_FLAG_FLUSH)
- return mlx5_ib_prefetch_sg_list(dev, pf_flags, sg_list,
+ return mlx5_ib_prefetch_sg_list(pd, pf_flags, sg_list,
num_sge);
work = kvzalloc(struct_size(work, sg_list, num_sge), GFP_KERNEL);
@@ -1625,12 +1796,25 @@ int mlx5_ib_advise_mr_prefetch(struct ib_pd *pd,
memcpy(work->sg_list, sg_list, num_sge * sizeof(struct ib_sge));
- get_device(&dev->ib_dev.dev);
- work->dev = dev;
+ /* It is guaranteed that the pd when work is executed is the pd when
+ * work was queued since pd can't be destroyed while it holds MRs and
+ * destroying a MR leads to flushing the workquque
+ */
+ work->pd = pd;
work->pf_flags = pf_flags;
work->num_sge = num_sge;
INIT_WORK(&work->work, mlx5_ib_prefetch_mr_work);
- schedule_work(&work->work);
- return 0;
+
+ srcu_key = srcu_read_lock(&dev->mr_srcu);
+
+ valid_req = num_pending_prefetch_inc(pd, sg_list, num_sge);
+ if (valid_req)
+ queue_work(system_unbound_wq, &work->work);
+ else
+ kfree(work);
+
+ srcu_read_unlock(&dev->mr_srcu, srcu_key);
+
+ return valid_req ? 0 : -EINVAL;
}
diff --git a/drivers/infiniband/hw/mlx5/qp.c b/drivers/infiniband/hw/mlx5/qp.c
index 7db778d96ef5..7cd006da1dae 100644
--- a/drivers/infiniband/hw/mlx5/qp.c
+++ b/drivers/infiniband/hw/mlx5/qp.c
@@ -109,75 +109,173 @@ static int is_sqp(enum ib_qp_type qp_type)
}
/**
- * mlx5_ib_read_user_wqe() - Copy a user-space WQE to kernel space.
+ * mlx5_ib_read_user_wqe_common() - Copy a WQE (or part of) from user WQ
+ * to kernel buffer
*
- * @qp: QP to copy from.
- * @send: copy from the send queue when non-zero, use the receive queue
- * otherwise.
- * @wqe_index: index to start copying from. For send work queues, the
- * wqe_index is in units of MLX5_SEND_WQE_BB.
- * For receive work queue, it is the number of work queue
- * element in the queue.
- * @buffer: destination buffer.
- * @length: maximum number of bytes to copy.
+ * @umem: User space memory where the WQ is
+ * @buffer: buffer to copy to
+ * @buflen: buffer length
+ * @wqe_index: index of WQE to copy from
+ * @wq_offset: offset to start of WQ
+ * @wq_wqe_cnt: number of WQEs in WQ
+ * @wq_wqe_shift: log2 of WQE size
+ * @bcnt: number of bytes to copy
+ * @bytes_copied: number of bytes to copy (return value)
*
- * Copies at least a single WQE, but may copy more data.
+ * Copies from start of WQE bcnt or less bytes.
+ * Does not gurantee to copy the entire WQE.
*
- * Return: the number of bytes copied, or an error code.
+ * Return: zero on success, or an error code.
*/
-int mlx5_ib_read_user_wqe(struct mlx5_ib_qp *qp, int send, int wqe_index,
- void *buffer, u32 length,
- struct mlx5_ib_qp_base *base)
+static int mlx5_ib_read_user_wqe_common(struct ib_umem *umem,
+ void *buffer,
+ u32 buflen,
+ int wqe_index,
+ int wq_offset,
+ int wq_wqe_cnt,
+ int wq_wqe_shift,
+ int bcnt,
+ size_t *bytes_copied)
+{
+ size_t offset = wq_offset + ((wqe_index % wq_wqe_cnt) << wq_wqe_shift);
+ size_t wq_end = wq_offset + (wq_wqe_cnt << wq_wqe_shift);
+ size_t copy_length;
+ int ret;
+
+ /* don't copy more than requested, more than buffer length or
+ * beyond WQ end
+ */
+ copy_length = min_t(u32, buflen, wq_end - offset);
+ copy_length = min_t(u32, copy_length, bcnt);
+
+ ret = ib_umem_copy_from(buffer, umem, offset, copy_length);
+ if (ret)
+ return ret;
+
+ if (!ret && bytes_copied)
+ *bytes_copied = copy_length;
+
+ return 0;
+}
+
+int mlx5_ib_read_user_wqe_sq(struct mlx5_ib_qp *qp,
+ int wqe_index,
+ void *buffer,
+ int buflen,
+ size_t *bc)
{
- struct ib_device *ibdev = qp->ibqp.device;
- struct mlx5_ib_dev *dev = to_mdev(ibdev);
- struct mlx5_ib_wq *wq = send ? &qp->sq : &qp->rq;
- size_t offset;
- size_t wq_end;
+ struct mlx5_ib_qp_base *base = &qp->trans_qp.base;
struct ib_umem *umem = base->ubuffer.umem;
- u32 first_copy_length;
- int wqe_length;
+ struct mlx5_ib_wq *wq = &qp->sq;
+ struct mlx5_wqe_ctrl_seg *ctrl;
+ size_t bytes_copied;
+ size_t bytes_copied2;
+ size_t wqe_length;
int ret;
+ int ds;
- if (wq->wqe_cnt == 0) {
- mlx5_ib_dbg(dev, "mlx5_ib_read_user_wqe for a QP with wqe_cnt == 0. qp_type: 0x%x\n",
- qp->ibqp.qp_type);
+ if (buflen < sizeof(*ctrl))
return -EINVAL;
- }
- offset = wq->offset + ((wqe_index % wq->wqe_cnt) << wq->wqe_shift);
- wq_end = wq->offset + (wq->wqe_cnt << wq->wqe_shift);
+ /* at first read as much as possible */
+ ret = mlx5_ib_read_user_wqe_common(umem,
+ buffer,
+ buflen,
+ wqe_index,
+ wq->offset,
+ wq->wqe_cnt,
+ wq->wqe_shift,
+ buflen,
+ &bytes_copied);
+ if (ret)
+ return ret;
- if (send && length < sizeof(struct mlx5_wqe_ctrl_seg))
+ /* we need at least control segment size to proceed */
+ if (bytes_copied < sizeof(*ctrl))
return -EINVAL;
- if (offset > umem->length ||
- (send && offset + sizeof(struct mlx5_wqe_ctrl_seg) > umem->length))
- return -EINVAL;
+ ctrl = buffer;
+ ds = be32_to_cpu(ctrl->qpn_ds) & MLX5_WQE_CTRL_DS_MASK;
+ wqe_length = ds * MLX5_WQE_DS_UNITS;
+
+ /* if we copied enough then we are done */
+ if (bytes_copied >= wqe_length) {
+ *bc = bytes_copied;
+ return 0;
+ }
+
+ /* otherwise this a wrapped around wqe
+ * so read the remaining bytes starting
+ * from wqe_index 0
+ */
+ ret = mlx5_ib_read_user_wqe_common(umem,
+ buffer + bytes_copied,
+ buflen - bytes_copied,
+ 0,
+ wq->offset,
+ wq->wqe_cnt,
+ wq->wqe_shift,
+ wqe_length - bytes_copied,
+ &bytes_copied2);
- first_copy_length = min_t(u32, offset + length, wq_end) - offset;
- ret = ib_umem_copy_from(buffer, umem, offset, first_copy_length);
if (ret)
return ret;
+ *bc = bytes_copied + bytes_copied2;
+ return 0;
+}
- if (send) {
- struct mlx5_wqe_ctrl_seg *ctrl = buffer;
- int ds = be32_to_cpu(ctrl->qpn_ds) & MLX5_WQE_CTRL_DS_MASK;
-
- wqe_length = ds * MLX5_WQE_DS_UNITS;
- } else {
- wqe_length = 1 << wq->wqe_shift;
- }
+int mlx5_ib_read_user_wqe_rq(struct mlx5_ib_qp *qp,
+ int wqe_index,
+ void *buffer,
+ int buflen,
+ size_t *bc)
+{
+ struct mlx5_ib_qp_base *base = &qp->trans_qp.base;
+ struct ib_umem *umem = base->ubuffer.umem;
+ struct mlx5_ib_wq *wq = &qp->rq;
+ size_t bytes_copied;
+ int ret;
- if (wqe_length <= first_copy_length)
- return first_copy_length;
+ ret = mlx5_ib_read_user_wqe_common(umem,
+ buffer,
+ buflen,
+ wqe_index,
+ wq->offset,
+ wq->wqe_cnt,
+ wq->wqe_shift,
+ buflen,
+ &bytes_copied);
- ret = ib_umem_copy_from(buffer + first_copy_length, umem, wq->offset,
- wqe_length - first_copy_length);
if (ret)
return ret;
+ *bc = bytes_copied;
+ return 0;
+}
- return wqe_length;
+int mlx5_ib_read_user_wqe_srq(struct mlx5_ib_srq *srq,
+ int wqe_index,
+ void *buffer,
+ int buflen,
+ size_t *bc)
+{
+ struct ib_umem *umem = srq->umem;
+ size_t bytes_copied;
+ int ret;
+
+ ret = mlx5_ib_read_user_wqe_common(umem,
+ buffer,
+ buflen,
+ wqe_index,
+ 0,
+ srq->msrq.max,
+ srq->msrq.wqe_shift,
+ buflen,
+ &bytes_copied);
+
+ if (ret)
+ return ret;
+ *bc = bytes_copied;
+ return 0;
}
static void mlx5_ib_qp_event(struct mlx5_core_qp *qp, int type)
@@ -435,9 +533,9 @@ static int set_user_buf_size(struct mlx5_ib_dev *dev,
return -EINVAL;
}
- if (ucmd->sq_wqe_count && ((1 << ilog2(ucmd->sq_wqe_count)) != ucmd->sq_wqe_count)) {
- mlx5_ib_warn(dev, "sq_wqe_count %d, sq_wqe_count %d\n",
- ucmd->sq_wqe_count, ucmd->sq_wqe_count);
+ if (ucmd->sq_wqe_count && !is_power_of_2(ucmd->sq_wqe_count)) {
+ mlx5_ib_warn(dev, "sq_wqe_count %d is not a power of two\n",
+ ucmd->sq_wqe_count);
return -EINVAL;
}
@@ -645,16 +743,14 @@ int bfregn_to_uar_index(struct mlx5_ib_dev *dev,
return bfregi->sys_pages[index_of_sys_page] + offset;
}
-static int mlx5_ib_umem_get(struct mlx5_ib_dev *dev,
- struct ib_pd *pd,
+static int mlx5_ib_umem_get(struct mlx5_ib_dev *dev, struct ib_udata *udata,
unsigned long addr, size_t size,
- struct ib_umem **umem,
- int *npages, int *page_shift, int *ncont,
- u32 *offset)
+ struct ib_umem **umem, int *npages, int *page_shift,
+ int *ncont, u32 *offset)
{
int err;
- *umem = ib_umem_get(pd->uobject->context, addr, size, 0, 0);
+ *umem = ib_umem_get(udata, addr, size, 0, 0);
if (IS_ERR(*umem)) {
mlx5_ib_dbg(dev, "umem_get failed\n");
return PTR_ERR(*umem);
@@ -695,10 +791,11 @@ static void destroy_user_rq(struct mlx5_ib_dev *dev, struct ib_pd *pd,
}
static int create_user_rq(struct mlx5_ib_dev *dev, struct ib_pd *pd,
- struct mlx5_ib_rwq *rwq,
+ struct ib_udata *udata, struct mlx5_ib_rwq *rwq,
struct mlx5_ib_create_wq *ucmd)
{
- struct mlx5_ib_ucontext *context;
+ struct mlx5_ib_ucontext *ucontext = rdma_udata_to_drv_context(
+ udata, struct mlx5_ib_ucontext, ibucontext);
int page_shift = 0;
int npages;
u32 offset = 0;
@@ -708,9 +805,7 @@ static int create_user_rq(struct mlx5_ib_dev *dev, struct ib_pd *pd,
if (!ucmd->buf_addr)
return -EINVAL;
- context = to_mucontext(pd->uobject->context);
- rwq->umem = ib_umem_get(pd->uobject->context, ucmd->buf_addr,
- rwq->buf_size, 0, 0);
+ rwq->umem = ib_umem_get(udata, ucmd->buf_addr, rwq->buf_size, 0, 0);
if (IS_ERR(rwq->umem)) {
mlx5_ib_dbg(dev, "umem_get failed\n");
err = PTR_ERR(rwq->umem);
@@ -735,7 +830,7 @@ static int create_user_rq(struct mlx5_ib_dev *dev, struct ib_pd *pd,
(unsigned long long)ucmd->buf_addr, rwq->buf_size,
npages, page_shift, ncont, offset);
- err = mlx5_ib_db_map_user(context, ucmd->db_addr, &rwq->db);
+ err = mlx5_ib_db_map_user(ucontext, udata, ucmd->db_addr, &rwq->db);
if (err) {
mlx5_ib_dbg(dev, "map failed\n");
goto err_umem;
@@ -783,7 +878,8 @@ static int create_user_qp(struct mlx5_ib_dev *dev, struct ib_pd *pd,
return err;
}
- context = to_mucontext(pd->uobject->context);
+ context = rdma_udata_to_drv_context(udata, struct mlx5_ib_ucontext,
+ ibucontext);
if (ucmd.flags & MLX5_QP_FLAG_BFREG_INDEX) {
uar_index = bfregn_to_uar_index(dev, &context->bfregi,
ucmd.bfreg_index, true);
@@ -819,10 +915,9 @@ static int create_user_qp(struct mlx5_ib_dev *dev, struct ib_pd *pd,
if (ucmd.buf_addr && ubuffer->buf_size) {
ubuffer->buf_addr = ucmd.buf_addr;
- err = mlx5_ib_umem_get(dev, pd, ubuffer->buf_addr,
- ubuffer->buf_size,
- &ubuffer->umem, &npages, &page_shift,
- &ncont, &offset);
+ err = mlx5_ib_umem_get(dev, udata, ubuffer->buf_addr,
+ ubuffer->buf_size, &ubuffer->umem,
+ &npages, &page_shift, &ncont, &offset);
if (err)
goto err_bfreg;
} else {
@@ -856,7 +951,7 @@ static int create_user_qp(struct mlx5_ib_dev *dev, struct ib_pd *pd,
resp->bfreg_index = MLX5_IB_INVALID_BFREG;
qp->bfregn = bfregn;
- err = mlx5_ib_db_map_user(context, ucmd.db_addr, &qp->db);
+ err = mlx5_ib_db_map_user(context, udata, ucmd.db_addr, &qp->db);
if (err) {
mlx5_ib_dbg(dev, "map failed\n");
goto err_free;
@@ -1119,6 +1214,7 @@ static void destroy_flow_rule_vport_sq(struct mlx5_ib_dev *dev,
}
static int create_raw_packet_qp_sq(struct mlx5_ib_dev *dev,
+ struct ib_udata *udata,
struct mlx5_ib_sq *sq, void *qpin,
struct ib_pd *pd)
{
@@ -1135,9 +1231,9 @@ static int create_raw_packet_qp_sq(struct mlx5_ib_dev *dev,
int ncont = 0;
u32 offset = 0;
- err = mlx5_ib_umem_get(dev, pd, ubuffer->buf_addr, ubuffer->buf_size,
- &sq->ubuffer.umem, &npages, &page_shift,
- &ncont, &offset);
+ err = mlx5_ib_umem_get(dev, udata, ubuffer->buf_addr, ubuffer->buf_size,
+ &sq->ubuffer.umem, &npages, &page_shift, &ncont,
+ &offset);
if (err)
return err;
@@ -1362,9 +1458,8 @@ static int create_raw_packet_qp(struct mlx5_ib_dev *dev, struct mlx5_ib_qp *qp,
struct mlx5_ib_raw_packet_qp *raw_packet_qp = &qp->raw_packet_qp;
struct mlx5_ib_sq *sq = &raw_packet_qp->sq;
struct mlx5_ib_rq *rq = &raw_packet_qp->rq;
- struct ib_uobject *uobj = pd->uobject;
- struct ib_ucontext *ucontext = uobj->context;
- struct mlx5_ib_ucontext *mucontext = to_mucontext(ucontext);
+ struct mlx5_ib_ucontext *mucontext = rdma_udata_to_drv_context(
+ udata, struct mlx5_ib_ucontext, ibucontext);
int err;
u32 tdn = mucontext->tdn;
u16 uid = to_mpd(pd)->uid;
@@ -1374,7 +1469,7 @@ static int create_raw_packet_qp(struct mlx5_ib_dev *dev, struct mlx5_ib_qp *qp,
if (err)
return err;
- err = create_raw_packet_qp_sq(dev, sq, in, pd);
+ err = create_raw_packet_qp_sq(dev, udata, sq, in, pd);
if (err)
goto err_destroy_tis;
@@ -1478,9 +1573,8 @@ static int create_rss_raw_qp_tir(struct mlx5_ib_dev *dev, struct mlx5_ib_qp *qp,
struct ib_qp_init_attr *init_attr,
struct ib_udata *udata)
{
- struct ib_uobject *uobj = pd->uobject;
- struct ib_ucontext *ucontext = uobj->context;
- struct mlx5_ib_ucontext *mucontext = to_mucontext(ucontext);
+ struct mlx5_ib_ucontext *mucontext = rdma_udata_to_drv_context(
+ udata, struct mlx5_ib_ucontext, ibucontext);
struct mlx5_ib_create_qp_resp resp = {};
int inlen;
int err;
@@ -1822,6 +1916,8 @@ static int create_qp_common(struct mlx5_ib_dev *dev, struct ib_pd *pd,
int inlen = MLX5_ST_SZ_BYTES(create_qp_in);
struct mlx5_core_dev *mdev = dev->mdev;
struct mlx5_ib_create_qp_resp resp = {};
+ struct mlx5_ib_ucontext *ucontext = rdma_udata_to_drv_context(
+ udata, struct mlx5_ib_ucontext, ibucontext);
struct mlx5_ib_cq *send_cq;
struct mlx5_ib_cq *recv_cq;
unsigned long flags;
@@ -1924,8 +2020,7 @@ static int create_qp_common(struct mlx5_ib_dev *dev, struct ib_pd *pd,
MLX5_QP_FLAG_TYPE_DCT))
return -EINVAL;
- err = get_qp_user_index(to_mucontext(pd->uobject->context),
- &ucmd, udata->inlen, &uidx);
+ err = get_qp_user_index(ucontext, &ucmd, udata->inlen, &uidx);
if (err)
return err;
@@ -2409,8 +2504,11 @@ static const char *ib_qp_type_str(enum ib_qp_type type)
static struct ib_qp *mlx5_ib_create_dct(struct ib_pd *pd,
struct ib_qp_init_attr *attr,
- struct mlx5_ib_create_qp *ucmd)
+ struct mlx5_ib_create_qp *ucmd,
+ struct ib_udata *udata)
{
+ struct mlx5_ib_ucontext *ucontext = rdma_udata_to_drv_context(
+ udata, struct mlx5_ib_ucontext, ibucontext);
struct mlx5_ib_qp *qp;
int err = 0;
u32 uidx = MLX5_IB_DEFAULT_UIDX;
@@ -2419,8 +2517,7 @@ static struct ib_qp *mlx5_ib_create_dct(struct ib_pd *pd,
if (!attr->srq || !attr->recv_cq)
return ERR_PTR(-EINVAL);
- err = get_qp_user_index(to_mucontext(pd->uobject->context),
- ucmd, sizeof(*ucmd), &uidx);
+ err = get_qp_user_index(ucontext, ucmd, sizeof(*ucmd), &uidx);
if (err)
return ERR_PTR(err);
@@ -2502,15 +2599,17 @@ struct ib_qp *mlx5_ib_create_qp(struct ib_pd *pd,
int err;
struct ib_qp_init_attr mlx_init_attr;
struct ib_qp_init_attr *init_attr = verbs_init_attr;
+ struct mlx5_ib_ucontext *ucontext = rdma_udata_to_drv_context(
+ udata, struct mlx5_ib_ucontext, ibucontext);
if (pd) {
dev = to_mdev(pd->device);
if (init_attr->qp_type == IB_QPT_RAW_PACKET) {
- if (!udata) {
+ if (!ucontext) {
mlx5_ib_dbg(dev, "Raw Packet QP is not supported for kernel consumers\n");
return ERR_PTR(-EINVAL);
- } else if (!to_mucontext(pd->uobject->context)->cqe_version) {
+ } else if (!ucontext->cqe_version) {
mlx5_ib_dbg(dev, "Raw Packet QP is only supported for CQE version > 0\n");
return ERR_PTR(-EINVAL);
}
@@ -2542,7 +2641,7 @@ struct ib_qp *mlx5_ib_create_qp(struct ib_pd *pd,
return ERR_PTR(-EINVAL);
}
} else {
- return mlx5_ib_create_dct(pd, init_attr, &ucmd);
+ return mlx5_ib_create_dct(pd, init_attr, &ucmd, udata);
}
}
@@ -2653,10 +2752,10 @@ int mlx5_ib_destroy_qp(struct ib_qp *qp)
static int to_mlx5_access_flags(struct mlx5_ib_qp *qp,
const struct ib_qp_attr *attr,
- int attr_mask, __be32 *hw_access_flags)
+ int attr_mask, __be32 *hw_access_flags_be)
{
u8 dest_rd_atomic;
- u32 access_flags;
+ u32 access_flags, hw_access_flags = 0;
struct mlx5_ib_dev *dev = to_mdev(qp->ibqp.device);
@@ -2674,7 +2773,7 @@ static int to_mlx5_access_flags(struct mlx5_ib_qp *qp,
access_flags &= IB_ACCESS_REMOTE_WRITE;
if (access_flags & IB_ACCESS_REMOTE_READ)
- *hw_access_flags |= MLX5_QP_BIT_RRE;
+ hw_access_flags |= MLX5_QP_BIT_RRE;
if (access_flags & IB_ACCESS_REMOTE_ATOMIC) {
int atomic_mode;
@@ -2682,14 +2781,14 @@ static int to_mlx5_access_flags(struct mlx5_ib_qp *qp,
if (atomic_mode < 0)
return -EOPNOTSUPP;
- *hw_access_flags |= MLX5_QP_BIT_RAE;
- *hw_access_flags |= atomic_mode << MLX5_ATOMIC_MODE_OFFSET;
+ hw_access_flags |= MLX5_QP_BIT_RAE;
+ hw_access_flags |= atomic_mode << MLX5_ATOMIC_MODE_OFFSET;
}
if (access_flags & IB_ACCESS_REMOTE_WRITE)
- *hw_access_flags |= MLX5_QP_BIT_RWE;
+ hw_access_flags |= MLX5_QP_BIT_RWE;
- *hw_access_flags = cpu_to_be32(*hw_access_flags);
+ *hw_access_flags_be = cpu_to_be32(hw_access_flags);
return 0;
}
@@ -3180,14 +3279,12 @@ static int modify_raw_packet_qp(struct mlx5_ib_dev *dev, struct mlx5_ib_qp *qp,
static unsigned int get_tx_affinity(struct mlx5_ib_dev *dev,
struct mlx5_ib_pd *pd,
struct mlx5_ib_qp_base *qp_base,
- u8 port_num)
+ u8 port_num, struct ib_udata *udata)
{
- struct mlx5_ib_ucontext *ucontext = NULL;
+ struct mlx5_ib_ucontext *ucontext = rdma_udata_to_drv_context(
+ udata, struct mlx5_ib_ucontext, ibucontext);
unsigned int tx_port_affinity;
- if (pd && pd->ibpd.uobject && pd->ibpd.uobject->context)
- ucontext = to_mucontext(pd->ibpd.uobject->context);
-
if (ucontext) {
tx_port_affinity = (unsigned int)atomic_add_return(
1, &ucontext->tx_port_affinity) %
@@ -3210,8 +3307,10 @@ static unsigned int get_tx_affinity(struct mlx5_ib_dev *dev,
static int __mlx5_ib_modify_qp(struct ib_qp *ibqp,
const struct ib_qp_attr *attr, int attr_mask,
- enum ib_qp_state cur_state, enum ib_qp_state new_state,
- const struct mlx5_ib_modify_qp *ucmd)
+ enum ib_qp_state cur_state,
+ enum ib_qp_state new_state,
+ const struct mlx5_ib_modify_qp *ucmd,
+ struct ib_udata *udata)
{
static const u16 optab[MLX5_QP_NUM_STATE][MLX5_QP_NUM_STATE] = {
[MLX5_QP_STATE_RST] = {
@@ -3302,7 +3401,8 @@ static int __mlx5_ib_modify_qp(struct ib_qp *ibqp,
(ibqp->qp_type == IB_QPT_XRC_TGT)) {
if (dev->lag_active) {
u8 p = mlx5_core_native_port_num(dev->mdev);
- tx_affinity = get_tx_affinity(dev, pd, base, p);
+ tx_affinity = get_tx_affinity(dev, pd, base, p,
+ udata);
context->flags |= cpu_to_be32(tx_affinity << 24);
}
}
@@ -3390,7 +3490,7 @@ static int __mlx5_ib_modify_qp(struct ib_qp *ibqp,
}
if (attr_mask & (IB_QP_ACCESS_FLAGS | IB_QP_MAX_DEST_RD_ATOMIC)) {
- __be32 access_flags = 0;
+ __be32 access_flags;
err = to_mlx5_access_flags(qp, attr, attr_mask, &access_flags);
if (err)
@@ -3629,6 +3729,7 @@ static int mlx5_ib_modify_dct(struct ib_qp *ibqp, struct ib_qp_attr *attr,
} else if (cur_state == IB_QPS_INIT && new_state == IB_QPS_RTR) {
struct mlx5_ib_modify_qp_resp resp = {};
+ u32 out[MLX5_ST_SZ_DW(create_dct_out)] = {0};
u32 min_resp_len = offsetof(typeof(resp), dctn) +
sizeof(resp.dctn);
@@ -3647,7 +3748,8 @@ static int mlx5_ib_modify_dct(struct ib_qp *ibqp, struct ib_qp_attr *attr,
MLX5_SET(dctc, dctc, hop_limit, attr->ah_attr.grh.hop_limit);
err = mlx5_core_create_dct(dev->mdev, &qp->dct.mdct, qp->dct.in,
- MLX5_ST_SZ_BYTES(create_dct_in));
+ MLX5_ST_SZ_BYTES(create_dct_in), out,
+ sizeof(out));
if (err)
return err;
resp.dctn = qp->dct.mdct.mqp.qpn;
@@ -3785,7 +3887,7 @@ int mlx5_ib_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
}
err = __mlx5_ib_modify_qp(ibqp, attr, attr_mask, cur_state,
- new_state, &ucmd);
+ new_state, &ucmd, udata);
out:
mutex_unlock(&qp->mutex);
@@ -5795,7 +5897,7 @@ static int prepare_user_rq(struct ib_pd *pd,
return err;
}
- err = create_user_rq(dev, pd, rwq, &ucmd);
+ err = create_user_rq(dev, pd, udata, rwq, &ucmd);
if (err) {
mlx5_ib_dbg(dev, "err %d\n", err);
return err;
diff --git a/drivers/infiniband/hw/mlx5/srq.c b/drivers/infiniband/hw/mlx5/srq.c
index 4e8d18009f58..1ec1beb1296b 100644
--- a/drivers/infiniband/hw/mlx5/srq.c
+++ b/drivers/infiniband/hw/mlx5/srq.c
@@ -47,6 +47,8 @@ static int create_srq_user(struct ib_pd *pd, struct mlx5_ib_srq *srq,
{
struct mlx5_ib_dev *dev = to_mdev(pd->device);
struct mlx5_ib_create_srq ucmd = {};
+ struct mlx5_ib_ucontext *ucontext = rdma_udata_to_drv_context(
+ udata, struct mlx5_ib_ucontext, ibucontext);
size_t ucmdlen;
int err;
int npages;
@@ -71,16 +73,14 @@ static int create_srq_user(struct ib_pd *pd, struct mlx5_ib_srq *srq,
return -EINVAL;
if (in->type != IB_SRQT_BASIC) {
- err = get_srq_user_index(to_mucontext(pd->uobject->context),
- &ucmd, udata->inlen, &uidx);
+ err = get_srq_user_index(ucontext, &ucmd, udata->inlen, &uidx);
if (err)
return err;
}
srq->wq_sig = !!(ucmd.flags & MLX5_SRQ_FLAG_SIGNATURE);
- srq->umem = ib_umem_get(pd->uobject->context, ucmd.buf_addr, buf_size,
- 0, 0);
+ srq->umem = ib_umem_get(udata, ucmd.buf_addr, buf_size, 0, 0);
if (IS_ERR(srq->umem)) {
mlx5_ib_dbg(dev, "failed umem get, size %d\n", buf_size);
err = PTR_ERR(srq->umem);
@@ -104,8 +104,7 @@ static int create_srq_user(struct ib_pd *pd, struct mlx5_ib_srq *srq,
mlx5_ib_populate_pas(dev, srq->umem, page_shift, in->pas, 0);
- err = mlx5_ib_db_map_user(to_mucontext(pd->uobject->context),
- ucmd.db_addr, &srq->db);
+ err = mlx5_ib_db_map_user(ucontext, udata, ucmd.db_addr, &srq->db);
if (err) {
mlx5_ib_dbg(dev, "map doorbell failed\n");
goto err_in;
diff --git a/drivers/infiniband/hw/mlx5/srq.h b/drivers/infiniband/hw/mlx5/srq.h
index 75eb5839ae95..c330af35ff10 100644
--- a/drivers/infiniband/hw/mlx5/srq.h
+++ b/drivers/infiniband/hw/mlx5/srq.h
@@ -46,8 +46,6 @@ struct mlx5_core_srq {
int wqe_shift;
void (*event)(struct mlx5_core_srq *srq, enum mlx5_event e);
- atomic_t refcount;
- struct completion free;
u16 uid;
};
diff --git a/drivers/infiniband/hw/mlx5/srq_cmd.c b/drivers/infiniband/hw/mlx5/srq_cmd.c
index 7aaaffbd4afa..63ac38bb3498 100644
--- a/drivers/infiniband/hw/mlx5/srq_cmd.c
+++ b/drivers/infiniband/hw/mlx5/srq_cmd.c
@@ -87,7 +87,7 @@ struct mlx5_core_srq *mlx5_cmd_get_srq(struct mlx5_ib_dev *dev, u32 srqn)
srq = radix_tree_lookup(&table->tree, srqn);
if (srq)
- atomic_inc(&srq->refcount);
+ atomic_inc(&srq->common.refcount);
spin_unlock(&table->lock);
@@ -594,8 +594,8 @@ int mlx5_cmd_create_srq(struct mlx5_ib_dev *dev, struct mlx5_core_srq *srq,
if (err)
return err;
- atomic_set(&srq->refcount, 1);
- init_completion(&srq->free);
+ atomic_set(&srq->common.refcount, 1);
+ init_completion(&srq->common.free);
spin_lock_irq(&table->lock);
err = radix_tree_insert(&table->tree, srq->srqn, srq);
@@ -627,9 +627,8 @@ int mlx5_cmd_destroy_srq(struct mlx5_ib_dev *dev, struct mlx5_core_srq *srq)
if (err)
return err;
- if (atomic_dec_and_test(&srq->refcount))
- complete(&srq->free);
- wait_for_completion(&srq->free);
+ mlx5_core_res_put(&srq->common);
+ wait_for_completion(&srq->common.free);
return 0;
}
@@ -685,7 +684,7 @@ static int srq_event_notifier(struct notifier_block *nb,
srq = radix_tree_lookup(&table->tree, srqn);
if (srq)
- atomic_inc(&srq->refcount);
+ atomic_inc(&srq->common.refcount);
spin_unlock(&table->lock);
@@ -694,8 +693,7 @@ static int srq_event_notifier(struct notifier_block *nb,
srq->event(srq, eqe->type);
- if (atomic_dec_and_test(&srq->refcount))
- complete(&srq->free);
+ mlx5_core_res_put(&srq->common);
return NOTIFY_OK;
}
diff --git a/drivers/infiniband/hw/mthca/mthca_main.c b/drivers/infiniband/hw/mthca/mthca_main.c
index 92c49bff22bc..fe9654a7af71 100644
--- a/drivers/infiniband/hw/mthca/mthca_main.c
+++ b/drivers/infiniband/hw/mthca/mthca_main.c
@@ -961,7 +961,7 @@ static int __mthca_init_one(struct pci_dev *pdev, int hca_type)
/* We can handle large RDMA requests, so allow larger segments. */
dma_set_max_seg_size(&pdev->dev, 1024 * 1024 * 1024);
- mdev = (struct mthca_dev *) ib_alloc_device(sizeof *mdev);
+ mdev = ib_alloc_device(mthca_dev, ib_dev);
if (!mdev) {
dev_err(&pdev->dev, "Device struct alloc failed, "
"aborting.\n");
diff --git a/drivers/infiniband/hw/mthca/mthca_provider.c b/drivers/infiniband/hw/mthca/mthca_provider.c
index e3e9dd54caa2..d063d7a37762 100644
--- a/drivers/infiniband/hw/mthca/mthca_provider.c
+++ b/drivers/infiniband/hw/mthca/mthca_provider.c
@@ -37,6 +37,7 @@
#include <rdma/ib_smi.h>
#include <rdma/ib_umem.h>
#include <rdma/ib_user_verbs.h>
+#include <rdma/uverbs_ioctl.h>
#include <linux/sched.h>
#include <linux/slab.h>
@@ -300,17 +301,16 @@ static int mthca_query_gid(struct ib_device *ibdev, u8 port,
return err;
}
-static struct ib_ucontext *mthca_alloc_ucontext(struct ib_device *ibdev,
- struct ib_udata *udata)
+static int mthca_alloc_ucontext(struct ib_ucontext *uctx,
+ struct ib_udata *udata)
{
- struct mthca_alloc_ucontext_resp uresp;
- struct mthca_ucontext *context;
+ struct ib_device *ibdev = uctx->device;
+ struct mthca_alloc_ucontext_resp uresp = {};
+ struct mthca_ucontext *context = to_mucontext(uctx);
int err;
if (!(to_mdev(ibdev)->active))
- return ERR_PTR(-EAGAIN);
-
- memset(&uresp, 0, sizeof uresp);
+ return -EAGAIN;
uresp.qp_tab_size = to_mdev(ibdev)->limits.num_qps;
if (mthca_is_memfree(to_mdev(ibdev)))
@@ -318,44 +318,33 @@ static struct ib_ucontext *mthca_alloc_ucontext(struct ib_device *ibdev,
else
uresp.uarc_size = 0;
- context = kmalloc(sizeof *context, GFP_KERNEL);
- if (!context)
- return ERR_PTR(-ENOMEM);
-
err = mthca_uar_alloc(to_mdev(ibdev), &context->uar);
- if (err) {
- kfree(context);
- return ERR_PTR(err);
- }
+ if (err)
+ return err;
context->db_tab = mthca_init_user_db_tab(to_mdev(ibdev));
if (IS_ERR(context->db_tab)) {
err = PTR_ERR(context->db_tab);
mthca_uar_free(to_mdev(ibdev), &context->uar);
- kfree(context);
- return ERR_PTR(err);
+ return err;
}
- if (ib_copy_to_udata(udata, &uresp, sizeof uresp)) {
+ if (ib_copy_to_udata(udata, &uresp, sizeof(uresp))) {
mthca_cleanup_user_db_tab(to_mdev(ibdev), &context->uar, context->db_tab);
mthca_uar_free(to_mdev(ibdev), &context->uar);
- kfree(context);
- return ERR_PTR(-EFAULT);
+ return -EFAULT;
}
context->reg_mr_warned = 0;
- return &context->ibucontext;
+ return 0;
}
-static int mthca_dealloc_ucontext(struct ib_ucontext *context)
+static void mthca_dealloc_ucontext(struct ib_ucontext *context)
{
mthca_cleanup_user_db_tab(to_mdev(context->device), &to_mucontext(context)->uar,
to_mucontext(context)->db_tab);
mthca_uar_free(to_mdev(context->device), &to_mucontext(context)->uar);
- kfree(to_mucontext(context));
-
- return 0;
}
static int mthca_mmap_uar(struct ib_ucontext *context,
@@ -374,40 +363,30 @@ static int mthca_mmap_uar(struct ib_ucontext *context,
return 0;
}
-static struct ib_pd *mthca_alloc_pd(struct ib_device *ibdev,
- struct ib_ucontext *context,
- struct ib_udata *udata)
+static int mthca_alloc_pd(struct ib_pd *ibpd, struct ib_ucontext *context,
+ struct ib_udata *udata)
{
- struct mthca_pd *pd;
+ struct ib_device *ibdev = ibpd->device;
+ struct mthca_pd *pd = to_mpd(ibpd);
int err;
- pd = kmalloc(sizeof *pd, GFP_KERNEL);
- if (!pd)
- return ERR_PTR(-ENOMEM);
-
err = mthca_pd_alloc(to_mdev(ibdev), !context, pd);
- if (err) {
- kfree(pd);
- return ERR_PTR(err);
- }
+ if (err)
+ return err;
if (context) {
if (ib_copy_to_udata(udata, &pd->pd_num, sizeof (__u32))) {
mthca_pd_free(to_mdev(ibdev), pd);
- kfree(pd);
- return ERR_PTR(-EFAULT);
+ return -EFAULT;
}
}
- return &pd->ibpd;
+ return 0;
}
-static int mthca_dealloc_pd(struct ib_pd *pd)
+static void mthca_dealloc_pd(struct ib_pd *pd)
{
mthca_pd_free(to_mdev(pd->device), to_mpd(pd));
- kfree(pd);
-
- return 0;
}
static struct ib_ah *mthca_ah_create(struct ib_pd *pd,
@@ -445,7 +424,8 @@ static struct ib_srq *mthca_create_srq(struct ib_pd *pd,
struct ib_udata *udata)
{
struct mthca_create_srq ucmd;
- struct mthca_ucontext *context = NULL;
+ struct mthca_ucontext *context = rdma_udata_to_drv_context(
+ udata, struct mthca_ucontext, ibucontext);
struct mthca_srq *srq;
int err;
@@ -457,8 +437,6 @@ static struct ib_srq *mthca_create_srq(struct ib_pd *pd,
return ERR_PTR(-ENOMEM);
if (udata) {
- context = to_mucontext(pd->uobject->context);
-
if (ib_copy_from_udata(&ucmd, udata, sizeof ucmd)) {
err = -EFAULT;
goto err_free;
@@ -520,6 +498,8 @@ static struct ib_qp *mthca_create_qp(struct ib_pd *pd,
struct ib_qp_init_attr *init_attr,
struct ib_udata *udata)
{
+ struct mthca_ucontext *context = rdma_udata_to_drv_context(
+ udata, struct mthca_ucontext, ibucontext);
struct mthca_create_qp ucmd;
struct mthca_qp *qp;
int err;
@@ -532,15 +512,11 @@ static struct ib_qp *mthca_create_qp(struct ib_pd *pd,
case IB_QPT_UC:
case IB_QPT_UD:
{
- struct mthca_ucontext *context;
-
qp = kzalloc(sizeof(*qp), GFP_KERNEL);
if (!qp)
return ERR_PTR(-ENOMEM);
if (udata) {
- context = to_mucontext(pd->uobject->context);
-
if (ib_copy_from_udata(&ucmd, udata, sizeof ucmd)) {
kfree(qp);
return ERR_PTR(-EFAULT);
@@ -578,8 +554,6 @@ static struct ib_qp *mthca_create_qp(struct ib_pd *pd,
&init_attr->cap, qp, udata);
if (err && udata) {
- context = to_mucontext(pd->uobject->context);
-
mthca_unmap_user_db(to_mdev(pd->device),
&context->uar,
context->db_tab,
@@ -684,7 +658,7 @@ static struct ib_cq *mthca_create_cq(struct ib_device *ibdev,
goto err_unmap_set;
}
- cq = kmalloc(sizeof *cq, GFP_KERNEL);
+ cq = kzalloc(sizeof(*cq), GFP_KERNEL);
if (!cq) {
err = -ENOMEM;
goto err_unmap_arm;
@@ -907,22 +881,23 @@ static struct ib_mr *mthca_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
u64 virt, int acc, struct ib_udata *udata)
{
struct mthca_dev *dev = to_mdev(pd->device);
- struct scatterlist *sg;
+ struct sg_dma_page_iter sg_iter;
+ struct mthca_ucontext *context = rdma_udata_to_drv_context(
+ udata, struct mthca_ucontext, ibucontext);
struct mthca_mr *mr;
struct mthca_reg_mr ucmd;
u64 *pages;
- int shift, n, len;
- int i, k, entry;
+ int n, i;
int err = 0;
int write_mtt_size;
if (udata->inlen < sizeof ucmd) {
- if (!to_mucontext(pd->uobject->context)->reg_mr_warned) {
+ if (!context->reg_mr_warned) {
mthca_warn(dev, "Process '%s' did not pass in MR attrs.\n",
current->comm);
mthca_warn(dev, " Update libmthca to fix this.\n");
}
- ++to_mucontext(pd->uobject->context)->reg_mr_warned;
+ ++context->reg_mr_warned;
ucmd.mr_attrs = 0;
} else if (ib_copy_from_udata(&ucmd, udata, sizeof ucmd))
return ERR_PTR(-EFAULT);
@@ -931,7 +906,7 @@ static struct ib_mr *mthca_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
if (!mr)
return ERR_PTR(-ENOMEM);
- mr->umem = ib_umem_get(pd->uobject->context, start, length, acc,
+ mr->umem = ib_umem_get(udata, start, length, acc,
ucmd.mr_attrs & MTHCA_MR_DMASYNC);
if (IS_ERR(mr->umem)) {
@@ -939,7 +914,6 @@ static struct ib_mr *mthca_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
goto err;
}
- shift = mr->umem->page_shift;
n = mr->umem->nmap;
mr->mtt = mthca_alloc_mtt(dev, n);
@@ -958,21 +932,19 @@ static struct ib_mr *mthca_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
write_mtt_size = min(mthca_write_mtt_size(dev), (int) (PAGE_SIZE / sizeof *pages));
- for_each_sg(mr->umem->sg_head.sgl, sg, mr->umem->nmap, entry) {
- len = sg_dma_len(sg) >> shift;
- for (k = 0; k < len; ++k) {
- pages[i++] = sg_dma_address(sg) + (k << shift);
- /*
- * Be friendly to write_mtt and pass it chunks
- * of appropriate size.
- */
- if (i == write_mtt_size) {
- err = mthca_write_mtt(dev, mr->mtt, n, pages, i);
- if (err)
- goto mtt_done;
- n += i;
- i = 0;
- }
+ for_each_sg_dma_page(mr->umem->sg_head.sgl, &sg_iter, mr->umem->nmap, 0) {
+ pages[i++] = sg_page_iter_dma_address(&sg_iter);
+
+ /*
+ * Be friendly to write_mtt and pass it chunks
+ * of appropriate size.
+ */
+ if (i == write_mtt_size) {
+ err = mthca_write_mtt(dev, mr->mtt, n, pages, i);
+ if (err)
+ goto mtt_done;
+ n += i;
+ i = 0;
}
}
@@ -983,7 +955,7 @@ mtt_done:
if (err)
goto err_mtt;
- err = mthca_mr_alloc(dev, to_mpd(pd)->pd_num, shift, virt, length,
+ err = mthca_mr_alloc(dev, to_mpd(pd)->pd_num, PAGE_SHIFT, virt, length,
convert_access(acc), mr);
if (err)
@@ -1081,7 +1053,8 @@ static ssize_t hw_rev_show(struct device *device,
struct device_attribute *attr, char *buf)
{
struct mthca_dev *dev =
- container_of(device, struct mthca_dev, ib_dev.dev);
+ rdma_device_to_drv_device(device, struct mthca_dev, ib_dev);
+
return sprintf(buf, "%x\n", dev->rev_id);
}
static DEVICE_ATTR_RO(hw_rev);
@@ -1090,7 +1063,8 @@ static ssize_t hca_type_show(struct device *device,
struct device_attribute *attr, char *buf)
{
struct mthca_dev *dev =
- container_of(device, struct mthca_dev, ib_dev.dev);
+ rdma_device_to_drv_device(device, struct mthca_dev, ib_dev);
+
switch (dev->pdev->device) {
case PCI_DEVICE_ID_MELLANOX_TAVOR:
return sprintf(buf, "MT23108\n");
@@ -1111,7 +1085,8 @@ static ssize_t board_id_show(struct device *device,
struct device_attribute *attr, char *buf)
{
struct mthca_dev *dev =
- container_of(device, struct mthca_dev, ib_dev.dev);
+ rdma_device_to_drv_device(device, struct mthca_dev, ib_dev);
+
return sprintf(buf, "%.*s\n", MTHCA_BOARD_ID_LEN, dev->board_id);
}
static DEVICE_ATTR_RO(board_id);
@@ -1225,6 +1200,8 @@ static const struct ib_device_ops mthca_dev_ops = {
.query_qp = mthca_query_qp,
.reg_user_mr = mthca_reg_user_mr,
.resize_cq = mthca_resize_cq,
+ INIT_RDMA_OBJ_SIZE(ib_pd, mthca_pd, ibpd),
+ INIT_RDMA_OBJ_SIZE(ib_ucontext, mthca_ucontext, ibucontext),
};
static const struct ib_device_ops mthca_dev_arbel_srq_ops = {
@@ -1338,7 +1315,7 @@ int mthca_register_device(struct mthca_dev *dev)
rdma_set_device_sysfs_group(&dev->ib_dev, &mthca_attr_group);
dev->ib_dev.driver_id = RDMA_DRIVER_MTHCA;
- ret = ib_register_device(&dev->ib_dev, "mthca%d", NULL);
+ ret = ib_register_device(&dev->ib_dev, "mthca%d");
if (ret)
return ret;
diff --git a/drivers/infiniband/hw/mthca/mthca_qp.c b/drivers/infiniband/hw/mthca/mthca_qp.c
index 4e5b5cc17f1d..7a5b25d13faa 100644
--- a/drivers/infiniband/hw/mthca/mthca_qp.c
+++ b/drivers/infiniband/hw/mthca/mthca_qp.c
@@ -42,6 +42,7 @@
#include <rdma/ib_verbs.h>
#include <rdma/ib_cache.h>
#include <rdma/ib_pack.h>
+#include <rdma/uverbs_ioctl.h>
#include "mthca_dev.h"
#include "mthca_cmd.h"
@@ -554,10 +555,14 @@ static int mthca_path_set(struct mthca_dev *dev, const struct rdma_ah_attr *ah,
static int __mthca_modify_qp(struct ib_qp *ibqp,
const struct ib_qp_attr *attr, int attr_mask,
- enum ib_qp_state cur_state, enum ib_qp_state new_state)
+ enum ib_qp_state cur_state,
+ enum ib_qp_state new_state,
+ struct ib_udata *udata)
{
struct mthca_dev *dev = to_mdev(ibqp->device);
struct mthca_qp *qp = to_mqp(ibqp);
+ struct mthca_ucontext *context = rdma_udata_to_drv_context(
+ udata, struct mthca_ucontext, ibucontext);
struct mthca_mailbox *mailbox;
struct mthca_qp_param *qp_param;
struct mthca_qp_context *qp_context;
@@ -619,8 +624,7 @@ static int __mthca_modify_qp(struct ib_qp *ibqp,
/* leave arbel_sched_queue as 0 */
if (qp->ibqp.uobject)
- qp_context->usr_page =
- cpu_to_be32(to_mucontext(qp->ibqp.uobject->context)->uar.index);
+ qp_context->usr_page = cpu_to_be32(context->uar.index);
else
qp_context->usr_page = cpu_to_be32(dev->driver_uar.index);
qp_context->local_qpn = cpu_to_be32(qp->qpn);
@@ -913,7 +917,8 @@ int mthca_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, int attr_mask,
goto out;
}
- err = __mthca_modify_qp(ibqp, attr, attr_mask, cur_state, new_state);
+ err = __mthca_modify_qp(ibqp, attr, attr_mask, cur_state, new_state,
+ udata);
out:
mutex_unlock(&qp->mutex);
diff --git a/drivers/infiniband/hw/mthca/mthca_srq.c b/drivers/infiniband/hw/mthca/mthca_srq.c
index b8333c79e3fa..06b920385512 100644
--- a/drivers/infiniband/hw/mthca/mthca_srq.c
+++ b/drivers/infiniband/hw/mthca/mthca_srq.c
@@ -36,6 +36,8 @@
#include <asm/io.h>
+#include <rdma/uverbs_ioctl.h>
+
#include "mthca_dev.h"
#include "mthca_cmd.h"
#include "mthca_memfree.h"
@@ -96,17 +98,19 @@ static void mthca_tavor_init_srq_context(struct mthca_dev *dev,
struct mthca_pd *pd,
struct mthca_srq *srq,
struct mthca_tavor_srq_context *context,
- bool is_user)
+ struct ib_udata *udata)
{
+ struct mthca_ucontext *ucontext = rdma_udata_to_drv_context(
+ udata, struct mthca_ucontext, ibucontext);
+
memset(context, 0, sizeof *context);
context->wqe_base_ds = cpu_to_be64(1 << (srq->wqe_shift - 4));
context->state_pd = cpu_to_be32(pd->pd_num);
context->lkey = cpu_to_be32(srq->mr.ibmr.lkey);
- if (is_user)
- context->uar =
- cpu_to_be32(to_mucontext(pd->ibpd.uobject->context)->uar.index);
+ if (udata)
+ context->uar = cpu_to_be32(ucontext->uar.index);
else
context->uar = cpu_to_be32(dev->driver_uar.index);
}
@@ -115,8 +119,10 @@ static void mthca_arbel_init_srq_context(struct mthca_dev *dev,
struct mthca_pd *pd,
struct mthca_srq *srq,
struct mthca_arbel_srq_context *context,
- bool is_user)
+ struct ib_udata *udata)
{
+ struct mthca_ucontext *ucontext = rdma_udata_to_drv_context(
+ udata, struct mthca_ucontext, ibucontext);
int logsize, max;
memset(context, 0, sizeof *context);
@@ -131,9 +137,8 @@ static void mthca_arbel_init_srq_context(struct mthca_dev *dev,
context->lkey = cpu_to_be32(srq->mr.ibmr.lkey);
context->db_index = cpu_to_be32(srq->db_index);
context->logstride_usrpage = cpu_to_be32((srq->wqe_shift - 4) << 29);
- if (is_user)
- context->logstride_usrpage |=
- cpu_to_be32(to_mucontext(pd->ibpd.uobject->context)->uar.index);
+ if (udata)
+ context->logstride_usrpage |= cpu_to_be32(ucontext->uar.index);
else
context->logstride_usrpage |= cpu_to_be32(dev->driver_uar.index);
context->eq_pd = cpu_to_be32(MTHCA_EQ_ASYNC << 24 | pd->pd_num);
diff --git a/drivers/infiniband/hw/nes/Kconfig b/drivers/infiniband/hw/nes/Kconfig
index 7964eba8e7ed..52caae954e4a 100644
--- a/drivers/infiniband/hw/nes/Kconfig
+++ b/drivers/infiniband/hw/nes/Kconfig
@@ -1,6 +1,6 @@
config INFINIBAND_NES
tristate "NetEffect RNIC Driver"
- depends on PCI && INET && INFINIBAND
+ depends on PCI && INET
select LIBCRC32C
---help---
This is the RDMA Network Interface Card (RNIC) driver for
diff --git a/drivers/infiniband/hw/nes/nes_verbs.c b/drivers/infiniband/hw/nes/nes_verbs.c
index 4e7f08ee1907..828e4af3f951 100644
--- a/drivers/infiniband/hw/nes/nes_verbs.c
+++ b/drivers/infiniband/hw/nes/nes_verbs.c
@@ -41,6 +41,7 @@
#include <rdma/ib_verbs.h>
#include <rdma/iw_cm.h>
#include <rdma/ib_user_verbs.h>
+#include <rdma/uverbs_ioctl.h>
#include "nes.h"
@@ -528,42 +529,36 @@ static int nes_query_gid(struct ib_device *ibdev, u8 port,
* nes_alloc_ucontext - Allocate the user context data structure. This keeps track
* of all objects associated with a particular user-mode client.
*/
-static struct ib_ucontext *nes_alloc_ucontext(struct ib_device *ibdev,
- struct ib_udata *udata)
+static int nes_alloc_ucontext(struct ib_ucontext *uctx, struct ib_udata *udata)
{
+ struct ib_device *ibdev = uctx->device;
struct nes_vnic *nesvnic = to_nesvnic(ibdev);
struct nes_device *nesdev = nesvnic->nesdev;
struct nes_adapter *nesadapter = nesdev->nesadapter;
struct nes_alloc_ucontext_req req;
- struct nes_alloc_ucontext_resp uresp;
- struct nes_ucontext *nes_ucontext;
+ struct nes_alloc_ucontext_resp uresp = {};
+ struct nes_ucontext *nes_ucontext = to_nesucontext(uctx);
struct nes_ib_device *nesibdev = nesvnic->nesibdev;
if (ib_copy_from_udata(&req, udata, sizeof(struct nes_alloc_ucontext_req))) {
printk(KERN_ERR PFX "Invalid structure size on allocate user context.\n");
- return ERR_PTR(-EINVAL);
+ return -EINVAL;
}
if (req.userspace_ver != NES_ABI_USERSPACE_VER) {
printk(KERN_ERR PFX "Invalid userspace driver version detected. Detected version %d, should be %d\n",
req.userspace_ver, NES_ABI_USERSPACE_VER);
- return ERR_PTR(-EINVAL);
+ return -EINVAL;
}
- memset(&uresp, 0, sizeof uresp);
-
uresp.max_qps = nesibdev->max_qp;
uresp.max_pds = nesibdev->max_pd;
uresp.wq_size = nesdev->nesadapter->max_qp_wr * 2;
uresp.virtwq = nesadapter->virtwq;
uresp.kernel_ver = NES_ABI_KERNEL_VER;
- nes_ucontext = kzalloc(sizeof *nes_ucontext, GFP_KERNEL);
- if (!nes_ucontext)
- return ERR_PTR(-ENOMEM);
-
nes_ucontext->nesdev = nesdev;
nes_ucontext->mmap_wq_offset = uresp.max_pds;
nes_ucontext->mmap_cq_offset = nes_ucontext->mmap_wq_offset +
@@ -571,34 +566,22 @@ static struct ib_ucontext *nes_alloc_ucontext(struct ib_device *ibdev,
PAGE_SIZE;
- if (ib_copy_to_udata(udata, &uresp, sizeof uresp)) {
- kfree(nes_ucontext);
- return ERR_PTR(-EFAULT);
- }
+ if (ib_copy_to_udata(udata, &uresp, sizeof(uresp)))
+ return -EFAULT;
INIT_LIST_HEAD(&nes_ucontext->cq_reg_mem_list);
INIT_LIST_HEAD(&nes_ucontext->qp_reg_mem_list);
- atomic_set(&nes_ucontext->usecnt, 1);
- return &nes_ucontext->ibucontext;
+ return 0;
}
-
/**
* nes_dealloc_ucontext
*/
-static int nes_dealloc_ucontext(struct ib_ucontext *context)
+static void nes_dealloc_ucontext(struct ib_ucontext *context)
{
- /* struct nes_vnic *nesvnic = to_nesvnic(context->device); */
- /* struct nes_device *nesdev = nesvnic->nesdev; */
- struct nes_ucontext *nes_ucontext = to_nesucontext(context);
-
- if (!atomic_dec_and_test(&nes_ucontext->usecnt))
- return 0;
- kfree(nes_ucontext);
- return 0;
+ return;
}
-
/**
* nes_mmap
*/
@@ -658,10 +641,11 @@ static int nes_mmap(struct ib_ucontext *context, struct vm_area_struct *vma)
/**
* nes_alloc_pd
*/
-static struct ib_pd *nes_alloc_pd(struct ib_device *ibdev,
- struct ib_ucontext *context, struct ib_udata *udata)
+static int nes_alloc_pd(struct ib_pd *pd, struct ib_ucontext *context,
+ struct ib_udata *udata)
{
- struct nes_pd *nespd;
+ struct ib_device *ibdev = pd->device;
+ struct nes_pd *nespd = to_nespd(pd);
struct nes_vnic *nesvnic = to_nesvnic(ibdev);
struct nes_device *nesdev = nesvnic->nesdev;
struct nes_adapter *nesadapter = nesdev->nesadapter;
@@ -676,15 +660,8 @@ static struct ib_pd *nes_alloc_pd(struct ib_device *ibdev,
err = nes_alloc_resource(nesadapter, nesadapter->allocated_pds,
nesadapter->max_pd, &pd_num, &nesadapter->next_pd, NES_RESOURCE_PD);
- if (err) {
- return ERR_PTR(err);
- }
-
- nespd = kzalloc(sizeof (struct nes_pd), GFP_KERNEL);
- if (!nespd) {
- nes_free_resource(nesadapter, nesadapter->allocated_pds, pd_num);
- return ERR_PTR(-ENOMEM);
- }
+ if (err)
+ return err;
nes_debug(NES_DBG_PD, "Allocating PD (%p) for ib device %s\n",
nespd, dev_name(&nesvnic->nesibdev->ibdev.dev));
@@ -700,16 +677,14 @@ static struct ib_pd *nes_alloc_pd(struct ib_device *ibdev,
if (nespd->mmap_db_index >= NES_MAX_USER_DB_REGIONS) {
nes_debug(NES_DBG_PD, "mmap_db_index > MAX\n");
nes_free_resource(nesadapter, nesadapter->allocated_pds, pd_num);
- kfree(nespd);
- return ERR_PTR(-ENOMEM);
+ return -ENOMEM;
}
uresp.pd_id = nespd->pd_id;
uresp.mmap_db_index = nespd->mmap_db_index;
if (ib_copy_to_udata(udata, &uresp, sizeof (struct nes_alloc_pd_resp))) {
nes_free_resource(nesadapter, nesadapter->allocated_pds, pd_num);
- kfree(nespd);
- return ERR_PTR(-EFAULT);
+ return -EFAULT;
}
set_bit(nespd->mmap_db_index, nesucontext->allocated_doorbells);
@@ -718,14 +693,14 @@ static struct ib_pd *nes_alloc_pd(struct ib_device *ibdev,
}
nes_debug(NES_DBG_PD, "PD%u structure located @%p.\n", nespd->pd_id, nespd);
- return &nespd->ibpd;
+ return 0;
}
/**
* nes_dealloc_pd
*/
-static int nes_dealloc_pd(struct ib_pd *ibpd)
+static void nes_dealloc_pd(struct ib_pd *ibpd)
{
struct nes_ucontext *nesucontext;
struct nes_pd *nespd = to_nespd(ibpd);
@@ -748,9 +723,6 @@ static int nes_dealloc_pd(struct ib_pd *ibpd)
nespd->pd_id, nespd);
nes_free_resource(nesadapter, nesadapter->allocated_pds,
(nespd->pd_id-nesadapter->base_pd)>>(PAGE_SHIFT-12));
- kfree(nespd);
-
- return 0;
}
@@ -985,7 +957,8 @@ static struct ib_qp *nes_create_qp(struct ib_pd *ibpd,
struct nes_adapter *nesadapter = nesdev->nesadapter;
struct nes_qp *nesqp;
struct nes_cq *nescq;
- struct nes_ucontext *nes_ucontext;
+ struct nes_ucontext *nes_ucontext = rdma_udata_to_drv_context(
+ udata, struct nes_ucontext, ibucontext);
struct nes_hw_cqp_wqe *cqp_wqe;
struct nes_cqp_request *cqp_request;
struct nes_create_qp_req req;
@@ -1066,9 +1039,8 @@ static struct ib_qp *nes_create_qp(struct ib_pd *ibpd,
}
if (req.user_qp_buffer)
nesqp->nesuqp_addr = req.user_qp_buffer;
- if (udata && (ibpd->uobject->context)) {
+ if (udata) {
nesqp->user_mode = 1;
- nes_ucontext = to_nesucontext(ibpd->uobject->context);
if (virt_wqs) {
err = 1;
list_for_each_entry(nespbl, &nes_ucontext->qp_reg_mem_list, list) {
@@ -1089,7 +1061,6 @@ static struct ib_qp *nes_create_qp(struct ib_pd *ibpd,
}
}
- nes_ucontext = to_nesucontext(ibpd->uobject->context);
nesqp->mmap_sq_db_index =
find_next_zero_bit(nes_ucontext->allocated_wqs,
NES_MAX_USER_WQ_REGIONS, nes_ucontext->first_free_wq);
@@ -2109,18 +2080,18 @@ static struct ib_mr *nes_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
struct nes_device *nesdev = nesvnic->nesdev;
struct nes_adapter *nesadapter = nesdev->nesadapter;
struct ib_mr *ibmr = ERR_PTR(-EINVAL);
- struct scatterlist *sg;
- struct nes_ucontext *nes_ucontext;
+ struct sg_dma_page_iter dma_iter;
+ struct nes_ucontext *nes_ucontext = rdma_udata_to_drv_context(
+ udata, struct nes_ucontext, ibucontext);
struct nes_pbl *nespbl;
struct nes_mr *nesmr;
struct ib_umem *region;
struct nes_mem_reg_req req;
struct nes_vpbl vpbl;
struct nes_root_vpbl root_vpbl;
- int entry, page_index;
+ int page_index;
int page_count = 0;
int err, pbl_depth = 0;
- int chunk_pages;
int ret;
u32 stag;
u32 stag_index = 0;
@@ -2132,9 +2103,8 @@ static struct ib_mr *nes_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
u16 pbl_count;
u8 single_page = 1;
u8 stag_key;
- int first_page = 1;
- region = ib_umem_get(pd->uobject->context, start, length, acc, 0);
+ region = ib_umem_get(udata, start, length, acc, 0);
if (IS_ERR(region)) {
return (struct ib_mr *)region;
}
@@ -2183,127 +2153,99 @@ static struct ib_mr *nes_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
}
nesmr->region = region;
- for_each_sg(region->sg_head.sgl, sg, region->nmap, entry) {
- if (sg_dma_address(sg) & ~PAGE_MASK) {
- ib_umem_release(region);
- nes_free_resource(nesadapter, nesadapter->allocated_mrs, stag_index);
- nes_debug(NES_DBG_MR, "Unaligned Memory Buffer: 0x%x\n",
- (unsigned int) sg_dma_address(sg));
- ibmr = ERR_PTR(-EINVAL);
- kfree(nesmr);
- goto reg_user_mr_err;
- }
+ for_each_sg_dma_page (region->sg_head.sgl, &dma_iter, region->nmap, 0) {
- if (!sg_dma_len(sg)) {
- ib_umem_release(region);
- nes_free_resource(nesadapter, nesadapter->allocated_mrs,
- stag_index);
- nes_debug(NES_DBG_MR, "Invalid Buffer Size\n");
- ibmr = ERR_PTR(-EINVAL);
- kfree(nesmr);
- goto reg_user_mr_err;
- }
-
- region_length += sg_dma_len(sg);
- chunk_pages = sg_dma_len(sg) >> 12;
+ region_length += PAGE_SIZE;
region_length -= skip_pages << 12;
- for (page_index = skip_pages; page_index < chunk_pages; page_index++) {
- skip_pages = 0;
- if ((page_count != 0) && (page_count << 12) - (ib_umem_offset(region) & (4096 - 1)) >= region->length)
- goto enough_pages;
- if ((page_count&0x01FF) == 0) {
- if (page_count >= 1024 * 512) {
+ skip_pages = 0;
+ if ((page_count != 0) && (page_count << 12) - (ib_umem_offset(region) & (4096 - 1)) >= region->length)
+ goto enough_pages;
+ if ((page_count & 0x01FF) == 0) {
+ if (page_count >= 1024 * 512) {
+ ib_umem_release(region);
+ nes_free_resource(nesadapter,
+ nesadapter->allocated_mrs, stag_index);
+ kfree(nesmr);
+ ibmr = ERR_PTR(-E2BIG);
+ goto reg_user_mr_err;
+ }
+ if (root_pbl_index == 1) {
+ root_vpbl.pbl_vbase = pci_alloc_consistent(nesdev->pcidev,
+ 8192, &root_vpbl.pbl_pbase);
+ nes_debug(NES_DBG_MR, "Allocating root PBL, va = %p, pa = 0x%08X\n",
+ root_vpbl.pbl_vbase, (unsigned int)root_vpbl.pbl_pbase);
+ if (!root_vpbl.pbl_vbase) {
ib_umem_release(region);
- nes_free_resource(nesadapter,
- nesadapter->allocated_mrs, stag_index);
+ pci_free_consistent(nesdev->pcidev, 4096, vpbl.pbl_vbase,
+ vpbl.pbl_pbase);
+ nes_free_resource(nesadapter, nesadapter->allocated_mrs,
+ stag_index);
kfree(nesmr);
- ibmr = ERR_PTR(-E2BIG);
+ ibmr = ERR_PTR(-ENOMEM);
goto reg_user_mr_err;
}
- if (root_pbl_index == 1) {
- root_vpbl.pbl_vbase = pci_alloc_consistent(nesdev->pcidev,
- 8192, &root_vpbl.pbl_pbase);
- nes_debug(NES_DBG_MR, "Allocating root PBL, va = %p, pa = 0x%08X\n",
- root_vpbl.pbl_vbase, (unsigned int)root_vpbl.pbl_pbase);
- if (!root_vpbl.pbl_vbase) {
- ib_umem_release(region);
- pci_free_consistent(nesdev->pcidev, 4096, vpbl.pbl_vbase,
- vpbl.pbl_pbase);
- nes_free_resource(nesadapter, nesadapter->allocated_mrs,
- stag_index);
- kfree(nesmr);
- ibmr = ERR_PTR(-ENOMEM);
- goto reg_user_mr_err;
- }
- root_vpbl.leaf_vpbl = kcalloc(1024,
- sizeof(*root_vpbl.leaf_vpbl),
- GFP_KERNEL);
- if (!root_vpbl.leaf_vpbl) {
- ib_umem_release(region);
- pci_free_consistent(nesdev->pcidev, 8192, root_vpbl.pbl_vbase,
- root_vpbl.pbl_pbase);
- pci_free_consistent(nesdev->pcidev, 4096, vpbl.pbl_vbase,
- vpbl.pbl_pbase);
- nes_free_resource(nesadapter, nesadapter->allocated_mrs,
- stag_index);
- kfree(nesmr);
- ibmr = ERR_PTR(-ENOMEM);
- goto reg_user_mr_err;
- }
- root_vpbl.pbl_vbase[0].pa_low =
- cpu_to_le32((u32)vpbl.pbl_pbase);
- root_vpbl.pbl_vbase[0].pa_high =
- cpu_to_le32((u32)((((u64)vpbl.pbl_pbase) >> 32)));
- root_vpbl.leaf_vpbl[0] = vpbl;
- }
- vpbl.pbl_vbase = pci_alloc_consistent(nesdev->pcidev, 4096,
- &vpbl.pbl_pbase);
- nes_debug(NES_DBG_MR, "Allocating leaf PBL, va = %p, pa = 0x%08X\n",
- vpbl.pbl_vbase, (unsigned int)vpbl.pbl_pbase);
- if (!vpbl.pbl_vbase) {
+ root_vpbl.leaf_vpbl = kcalloc(1024,
+ sizeof(*root_vpbl.leaf_vpbl),
+ GFP_KERNEL);
+ if (!root_vpbl.leaf_vpbl) {
ib_umem_release(region);
- nes_free_resource(nesadapter, nesadapter->allocated_mrs, stag_index);
- ibmr = ERR_PTR(-ENOMEM);
+ pci_free_consistent(nesdev->pcidev, 8192, root_vpbl.pbl_vbase,
+ root_vpbl.pbl_pbase);
+ pci_free_consistent(nesdev->pcidev, 4096, vpbl.pbl_vbase,
+ vpbl.pbl_pbase);
+ nes_free_resource(nesadapter, nesadapter->allocated_mrs,
+ stag_index);
kfree(nesmr);
+ ibmr = ERR_PTR(-ENOMEM);
goto reg_user_mr_err;
}
- if (1 <= root_pbl_index) {
- root_vpbl.pbl_vbase[root_pbl_index].pa_low =
- cpu_to_le32((u32)vpbl.pbl_pbase);
- root_vpbl.pbl_vbase[root_pbl_index].pa_high =
- cpu_to_le32((u32)((((u64)vpbl.pbl_pbase)>>32)));
- root_vpbl.leaf_vpbl[root_pbl_index] = vpbl;
- }
- root_pbl_index++;
- cur_pbl_index = 0;
+ root_vpbl.pbl_vbase[0].pa_low =
+ cpu_to_le32((u32)vpbl.pbl_pbase);
+ root_vpbl.pbl_vbase[0].pa_high =
+ cpu_to_le32((u32)((((u64)vpbl.pbl_pbase) >> 32)));
+ root_vpbl.leaf_vpbl[0] = vpbl;
}
- if (single_page) {
- if (page_count != 0) {
- if ((last_dma_addr+4096) !=
- (sg_dma_address(sg)+
- (page_index*4096)))
- single_page = 0;
- last_dma_addr = sg_dma_address(sg)+
- (page_index*4096);
- } else {
- first_dma_addr = sg_dma_address(sg)+
- (page_index*4096);
- last_dma_addr = first_dma_addr;
- }
+ vpbl.pbl_vbase = pci_alloc_consistent(nesdev->pcidev, 4096,
+ &vpbl.pbl_pbase);
+ nes_debug(NES_DBG_MR, "Allocating leaf PBL, va = %p, pa = 0x%08X\n",
+ vpbl.pbl_vbase, (unsigned int)vpbl.pbl_pbase);
+ if (!vpbl.pbl_vbase) {
+ ib_umem_release(region);
+ nes_free_resource(nesadapter, nesadapter->allocated_mrs, stag_index);
+ ibmr = ERR_PTR(-ENOMEM);
+ kfree(nesmr);
+ goto reg_user_mr_err;
}
-
- vpbl.pbl_vbase[cur_pbl_index].pa_low =
- cpu_to_le32((u32)(sg_dma_address(sg)+
- (page_index*4096)));
- vpbl.pbl_vbase[cur_pbl_index].pa_high =
- cpu_to_le32((u32)((((u64)(sg_dma_address(sg)+
- (page_index*4096))) >> 32)));
- cur_pbl_index++;
- page_count++;
+ if (1 <= root_pbl_index) {
+ root_vpbl.pbl_vbase[root_pbl_index].pa_low =
+ cpu_to_le32((u32)vpbl.pbl_pbase);
+ root_vpbl.pbl_vbase[root_pbl_index].pa_high =
+ cpu_to_le32((u32)((((u64)vpbl.pbl_pbase) >> 32)));
+ root_vpbl.leaf_vpbl[root_pbl_index] = vpbl;
+ }
+ root_pbl_index++;
+ cur_pbl_index = 0;
}
+ if (single_page) {
+ if (page_count != 0) {
+ if ((last_dma_addr + 4096) != sg_page_iter_dma_address(&dma_iter))
+ single_page = 0;
+ last_dma_addr = sg_page_iter_dma_address(&dma_iter);
+ } else {
+ first_dma_addr = sg_page_iter_dma_address(&dma_iter);
+ last_dma_addr = first_dma_addr;
+ }
+ }
+
+ vpbl.pbl_vbase[cur_pbl_index].pa_low =
+ cpu_to_le32((u32)(sg_page_iter_dma_address(&dma_iter)));
+ vpbl.pbl_vbase[cur_pbl_index].pa_high =
+ cpu_to_le32((u32)((u64)(sg_page_iter_dma_address(&dma_iter))));
+ cur_pbl_index++;
+ page_count++;
}
- enough_pages:
+enough_pages:
nes_debug(NES_DBG_MR, "calculating stag, stag_index=0x%08x, driver_key=0x%08x,"
" stag_key=0x%08x\n",
stag_index, driver_key, stag_key);
@@ -2345,7 +2287,7 @@ static struct ib_mr *nes_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
ibmr = ERR_PTR(-ENOMEM);
}
- reg_user_mr_err:
+reg_user_mr_err:
/* free the resources */
if (root_pbl_index == 1) {
pci_free_consistent(nesdev->pcidev, 4096, vpbl.pbl_vbase,
@@ -2383,7 +2325,6 @@ static struct ib_mr *nes_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
return ERR_PTR(-ENOMEM);
}
nesmr->region = region;
- nes_ucontext = to_nesucontext(pd->uobject->context);
pbl_depth = region->length >> 12;
pbl_depth += (region->length & (4096-1)) ? 1 : 0;
nespbl->pbl_size = pbl_depth*sizeof(u64);
@@ -2412,26 +2353,14 @@ static struct ib_mr *nes_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
nespbl->pbl_size, (unsigned long) nespbl->pbl_pbase,
(void *) nespbl->pbl_vbase, nespbl->user_base);
- for_each_sg(region->sg_head.sgl, sg, region->nmap, entry) {
- chunk_pages = sg_dma_len(sg) >> 12;
- chunk_pages += (sg_dma_len(sg) & (4096-1)) ? 1 : 0;
- if (first_page) {
- nespbl->page = sg_page(sg);
- first_page = 0;
- }
-
- for (page_index = 0; page_index < chunk_pages; page_index++) {
- ((__le32 *)pbl)[0] = cpu_to_le32((u32)
- (sg_dma_address(sg)+
- (page_index*4096)));
- ((__le32 *)pbl)[1] = cpu_to_le32(((u64)
- (sg_dma_address(sg)+
- (page_index*4096)))>>32);
- nes_debug(NES_DBG_MR, "pbl=%p, *pbl=0x%016llx, 0x%08x%08x\n", pbl,
- (unsigned long long)*pbl,
- le32_to_cpu(((__le32 *)pbl)[1]), le32_to_cpu(((__le32 *)pbl)[0]));
- pbl++;
- }
+ nespbl->page = sg_page(region->sg_head.sgl);
+ for_each_sg_dma_page(region->sg_head.sgl, &dma_iter, region->nmap, 0) {
+ ((__le32 *)pbl)[0] = cpu_to_le32((u32)(sg_page_iter_dma_address(&dma_iter)));
+ ((__le32 *)pbl)[1] = cpu_to_le32(((u64)(sg_page_iter_dma_address(&dma_iter)))>>32);
+ nes_debug(NES_DBG_MR, "pbl=%p, *pbl=0x%016llx, 0x%08x%08x\n", pbl,
+ (unsigned long long)*pbl,
+ le32_to_cpu(((__le32 *)pbl)[1]), le32_to_cpu(((__le32 *)pbl)[0]));
+ pbl++;
}
if (req.reg_type == IWNES_MEMREG_TYPE_QP) {
@@ -2560,7 +2489,7 @@ static ssize_t hw_rev_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct nes_ib_device *nesibdev =
- container_of(dev, struct nes_ib_device, ibdev.dev);
+ rdma_device_to_drv_device(dev, struct nes_ib_device, ibdev);
struct nes_vnic *nesvnic = nesibdev->nesvnic;
nes_debug(NES_DBG_INIT, "\n");
@@ -3658,6 +3587,8 @@ static const struct ib_device_ops nes_dev_ops = {
.query_qp = nes_query_qp,
.reg_user_mr = nes_reg_user_mr,
.req_notify_cq = nes_req_notify_cq,
+ INIT_RDMA_OBJ_SIZE(ib_pd, nes_pd, ibpd),
+ INIT_RDMA_OBJ_SIZE(ib_ucontext, nes_ucontext, ibucontext),
};
/**
@@ -3669,7 +3600,7 @@ struct nes_ib_device *nes_init_ofa_device(struct net_device *netdev)
struct nes_vnic *nesvnic = netdev_priv(netdev);
struct nes_device *nesdev = nesvnic->nesdev;
- nesibdev = (struct nes_ib_device *)ib_alloc_device(sizeof(struct nes_ib_device));
+ nesibdev = ib_alloc_device(nes_ib_device, ibdev);
if (nesibdev == NULL) {
return NULL;
}
@@ -3801,7 +3732,7 @@ int nes_register_ofa_device(struct nes_ib_device *nesibdev)
rdma_set_device_sysfs_group(&nesvnic->nesibdev->ibdev, &nes_attr_group);
nesvnic->nesibdev->ibdev.driver_id = RDMA_DRIVER_NES;
- ret = ib_register_device(&nesvnic->nesibdev->ibdev, "nes%d", NULL);
+ ret = ib_register_device(&nesvnic->nesibdev->ibdev, "nes%d");
if (ret) {
return ret;
}
diff --git a/drivers/infiniband/hw/nes/nes_verbs.h b/drivers/infiniband/hw/nes/nes_verbs.h
index e02a5662dc20..114a9b59fefd 100644
--- a/drivers/infiniband/hw/nes/nes_verbs.h
+++ b/drivers/infiniband/hw/nes/nes_verbs.h
@@ -59,7 +59,6 @@ struct nes_ucontext {
struct list_head cq_reg_mem_list;
struct list_head qp_reg_mem_list;
u32 mcrqf;
- atomic_t usecnt;
};
struct nes_pd {
diff --git a/drivers/infiniband/hw/ocrdma/Makefile b/drivers/infiniband/hw/ocrdma/Makefile
index d1bfd4f4cdde..e3f20ca15462 100644
--- a/drivers/infiniband/hw/ocrdma/Makefile
+++ b/drivers/infiniband/hw/ocrdma/Makefile
@@ -1,4 +1,4 @@
-ccflags-y := -Idrivers/net/ethernet/emulex/benet
+ccflags-y := -I $(srctree)/drivers/net/ethernet/emulex/benet
obj-$(CONFIG_INFINIBAND_OCRDMA) += ocrdma.o
diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_main.c b/drivers/infiniband/hw/ocrdma/ocrdma_main.c
index 1f393842453a..b9e10d55a58e 100644
--- a/drivers/infiniband/hw/ocrdma/ocrdma_main.c
+++ b/drivers/infiniband/hw/ocrdma/ocrdma_main.c
@@ -118,7 +118,8 @@ static void get_dev_fw_str(struct ib_device *device, char *str)
static ssize_t hw_rev_show(struct device *device,
struct device_attribute *attr, char *buf)
{
- struct ocrdma_dev *dev = dev_get_drvdata(device);
+ struct ocrdma_dev *dev =
+ rdma_device_to_drv_device(device, struct ocrdma_dev, ibdev);
return scnprintf(buf, PAGE_SIZE, "0x%x\n", dev->nic_info.pdev->vendor);
}
@@ -127,7 +128,8 @@ static DEVICE_ATTR_RO(hw_rev);
static ssize_t hca_type_show(struct device *device,
struct device_attribute *attr, char *buf)
{
- struct ocrdma_dev *dev = dev_get_drvdata(device);
+ struct ocrdma_dev *dev =
+ rdma_device_to_drv_device(device, struct ocrdma_dev, ibdev);
return scnprintf(buf, PAGE_SIZE, "%s\n", &dev->model_number[0]);
}
@@ -177,6 +179,8 @@ static const struct ib_device_ops ocrdma_dev_ops = {
.reg_user_mr = ocrdma_reg_user_mr,
.req_notify_cq = ocrdma_arm_cq,
.resize_cq = ocrdma_resize_cq,
+ INIT_RDMA_OBJ_SIZE(ib_pd, ocrdma_pd, ibpd),
+ INIT_RDMA_OBJ_SIZE(ib_ucontext, ocrdma_ucontext, ibucontext),
};
static const struct ib_device_ops ocrdma_dev_srq_ops = {
@@ -243,7 +247,7 @@ static int ocrdma_register_device(struct ocrdma_dev *dev)
}
rdma_set_device_sysfs_group(&dev->ibdev, &ocrdma_attr_group);
dev->ibdev.driver_id = RDMA_DRIVER_OCRDMA;
- return ib_register_device(&dev->ibdev, "ocrdma%d", NULL);
+ return ib_register_device(&dev->ibdev, "ocrdma%d");
}
static int ocrdma_alloc_resources(struct ocrdma_dev *dev)
@@ -295,7 +299,7 @@ static struct ocrdma_dev *ocrdma_add(struct be_dev_info *dev_info)
u8 lstate = 0;
struct ocrdma_dev *dev;
- dev = (struct ocrdma_dev *)ib_alloc_device(sizeof(struct ocrdma_dev));
+ dev = ib_alloc_device(ocrdma_dev, ibdev);
if (!dev) {
pr_err("Unable to allocate ib device\n");
return NULL;
diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_stats.c b/drivers/infiniband/hw/ocrdma/ocrdma_stats.c
index 6be0ea109138..a902942adb5d 100644
--- a/drivers/infiniband/hw/ocrdma/ocrdma_stats.c
+++ b/drivers/infiniband/hw/ocrdma/ocrdma_stats.c
@@ -767,88 +767,65 @@ void ocrdma_add_port_stats(struct ocrdma_dev *dev)
/* Create post stats base dir */
dev->dir = debugfs_create_dir(pci_name(pdev), ocrdma_dbgfs_dir);
- if (!dev->dir)
- goto err;
dev->rsrc_stats.type = OCRDMA_RSRC_STATS;
dev->rsrc_stats.dev = dev;
- if (!debugfs_create_file("resource_stats", S_IRUSR, dev->dir,
- &dev->rsrc_stats, &ocrdma_dbg_ops))
- goto err;
+ debugfs_create_file("resource_stats", S_IRUSR, dev->dir,
+ &dev->rsrc_stats, &ocrdma_dbg_ops);
dev->rx_stats.type = OCRDMA_RXSTATS;
dev->rx_stats.dev = dev;
- if (!debugfs_create_file("rx_stats", S_IRUSR, dev->dir,
- &dev->rx_stats, &ocrdma_dbg_ops))
- goto err;
+ debugfs_create_file("rx_stats", S_IRUSR, dev->dir, &dev->rx_stats,
+ &ocrdma_dbg_ops);
dev->wqe_stats.type = OCRDMA_WQESTATS;
dev->wqe_stats.dev = dev;
- if (!debugfs_create_file("wqe_stats", S_IRUSR, dev->dir,
- &dev->wqe_stats, &ocrdma_dbg_ops))
- goto err;
+ debugfs_create_file("wqe_stats", S_IRUSR, dev->dir, &dev->wqe_stats,
+ &ocrdma_dbg_ops);
dev->tx_stats.type = OCRDMA_TXSTATS;
dev->tx_stats.dev = dev;
- if (!debugfs_create_file("tx_stats", S_IRUSR, dev->dir,
- &dev->tx_stats, &ocrdma_dbg_ops))
- goto err;
+ debugfs_create_file("tx_stats", S_IRUSR, dev->dir, &dev->tx_stats,
+ &ocrdma_dbg_ops);
dev->db_err_stats.type = OCRDMA_DB_ERRSTATS;
dev->db_err_stats.dev = dev;
- if (!debugfs_create_file("db_err_stats", S_IRUSR, dev->dir,
- &dev->db_err_stats, &ocrdma_dbg_ops))
- goto err;
-
+ debugfs_create_file("db_err_stats", S_IRUSR, dev->dir,
+ &dev->db_err_stats, &ocrdma_dbg_ops);
dev->tx_qp_err_stats.type = OCRDMA_TXQP_ERRSTATS;
dev->tx_qp_err_stats.dev = dev;
- if (!debugfs_create_file("tx_qp_err_stats", S_IRUSR, dev->dir,
- &dev->tx_qp_err_stats, &ocrdma_dbg_ops))
- goto err;
+ debugfs_create_file("tx_qp_err_stats", S_IRUSR, dev->dir,
+ &dev->tx_qp_err_stats, &ocrdma_dbg_ops);
dev->rx_qp_err_stats.type = OCRDMA_RXQP_ERRSTATS;
dev->rx_qp_err_stats.dev = dev;
- if (!debugfs_create_file("rx_qp_err_stats", S_IRUSR, dev->dir,
- &dev->rx_qp_err_stats, &ocrdma_dbg_ops))
- goto err;
-
+ debugfs_create_file("rx_qp_err_stats", S_IRUSR, dev->dir,
+ &dev->rx_qp_err_stats, &ocrdma_dbg_ops);
dev->tx_dbg_stats.type = OCRDMA_TX_DBG_STATS;
dev->tx_dbg_stats.dev = dev;
- if (!debugfs_create_file("tx_dbg_stats", S_IRUSR, dev->dir,
- &dev->tx_dbg_stats, &ocrdma_dbg_ops))
- goto err;
+ debugfs_create_file("tx_dbg_stats", S_IRUSR, dev->dir,
+ &dev->tx_dbg_stats, &ocrdma_dbg_ops);
dev->rx_dbg_stats.type = OCRDMA_RX_DBG_STATS;
dev->rx_dbg_stats.dev = dev;
- if (!debugfs_create_file("rx_dbg_stats", S_IRUSR, dev->dir,
- &dev->rx_dbg_stats, &ocrdma_dbg_ops))
- goto err;
+ debugfs_create_file("rx_dbg_stats", S_IRUSR, dev->dir,
+ &dev->rx_dbg_stats, &ocrdma_dbg_ops);
dev->driver_stats.type = OCRDMA_DRV_STATS;
dev->driver_stats.dev = dev;
- if (!debugfs_create_file("driver_dbg_stats", S_IRUSR, dev->dir,
- &dev->driver_stats, &ocrdma_dbg_ops))
- goto err;
+ debugfs_create_file("driver_dbg_stats", S_IRUSR, dev->dir,
+ &dev->driver_stats, &ocrdma_dbg_ops);
dev->reset_stats.type = OCRDMA_RESET_STATS;
dev->reset_stats.dev = dev;
- if (!debugfs_create_file("reset_stats", 0200, dev->dir,
- &dev->reset_stats, &ocrdma_dbg_ops))
- goto err;
-
-
- return;
-err:
- debugfs_remove_recursive(dev->dir);
- dev->dir = NULL;
+ debugfs_create_file("reset_stats", 0200, dev->dir, &dev->reset_stats,
+ &ocrdma_dbg_ops);
}
void ocrdma_rem_port_stats(struct ocrdma_dev *dev)
{
- if (!dev->dir)
- return;
debugfs_remove_recursive(dev->dir);
}
diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c
index 287c332ff0e6..b4e1777c2c97 100644
--- a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c
+++ b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c
@@ -55,7 +55,7 @@
int ocrdma_query_pkey(struct ib_device *ibdev, u8 port, u16 index, u16 *pkey)
{
- if (index > 1)
+ if (index > 0)
return -EINVAL;
*pkey = 0xffff;
@@ -367,17 +367,12 @@ static int ocrdma_get_pd_num(struct ocrdma_dev *dev, struct ocrdma_pd *pd)
return status;
}
-static struct ocrdma_pd *_ocrdma_alloc_pd(struct ocrdma_dev *dev,
- struct ocrdma_ucontext *uctx,
- struct ib_udata *udata)
+static int _ocrdma_alloc_pd(struct ocrdma_dev *dev, struct ocrdma_pd *pd,
+ struct ocrdma_ucontext *uctx,
+ struct ib_udata *udata)
{
- struct ocrdma_pd *pd = NULL;
int status;
- pd = kzalloc(sizeof(*pd), GFP_KERNEL);
- if (!pd)
- return ERR_PTR(-ENOMEM);
-
if (udata && uctx && dev->attr.max_dpp_pds) {
pd->dpp_enabled =
ocrdma_get_asic_type(dev) == OCRDMA_ASIC_GEN_SKH_R;
@@ -386,15 +381,8 @@ static struct ocrdma_pd *_ocrdma_alloc_pd(struct ocrdma_dev *dev,
dev->attr.wqe_size) : 0;
}
- if (dev->pd_mgr->pd_prealloc_valid) {
- status = ocrdma_get_pd_num(dev, pd);
- if (status == 0) {
- return pd;
- } else {
- kfree(pd);
- return ERR_PTR(status);
- }
- }
+ if (dev->pd_mgr->pd_prealloc_valid)
+ return ocrdma_get_pd_num(dev, pd);
retry:
status = ocrdma_mbx_alloc_pd(dev, pd);
@@ -403,13 +391,11 @@ retry:
pd->dpp_enabled = false;
pd->num_dpp_qp = 0;
goto retry;
- } else {
- kfree(pd);
- return ERR_PTR(status);
}
+ return status;
}
- return pd;
+ return 0;
}
static inline int is_ucontext_pd(struct ocrdma_ucontext *uctx,
@@ -418,30 +404,33 @@ static inline int is_ucontext_pd(struct ocrdma_ucontext *uctx,
return (uctx->cntxt_pd == pd);
}
-static int _ocrdma_dealloc_pd(struct ocrdma_dev *dev,
+static void _ocrdma_dealloc_pd(struct ocrdma_dev *dev,
struct ocrdma_pd *pd)
{
- int status;
-
if (dev->pd_mgr->pd_prealloc_valid)
- status = ocrdma_put_pd_num(dev, pd->id, pd->dpp_enabled);
+ ocrdma_put_pd_num(dev, pd->id, pd->dpp_enabled);
else
- status = ocrdma_mbx_dealloc_pd(dev, pd);
-
- kfree(pd);
- return status;
+ ocrdma_mbx_dealloc_pd(dev, pd);
}
static int ocrdma_alloc_ucontext_pd(struct ocrdma_dev *dev,
struct ocrdma_ucontext *uctx,
struct ib_udata *udata)
{
- int status = 0;
+ struct ib_device *ibdev = &dev->ibdev;
+ struct ib_pd *pd;
+ int status;
+
+ pd = rdma_zalloc_drv_obj(ibdev, ib_pd);
+ if (!pd)
+ return -ENOMEM;
- uctx->cntxt_pd = _ocrdma_alloc_pd(dev, uctx, udata);
- if (IS_ERR(uctx->cntxt_pd)) {
- status = PTR_ERR(uctx->cntxt_pd);
- uctx->cntxt_pd = NULL;
+ pd->device = ibdev;
+ uctx->cntxt_pd = get_ocrdma_pd(pd);
+
+ status = _ocrdma_alloc_pd(dev, uctx->cntxt_pd, uctx, udata);
+ if (status) {
+ kfree(uctx->cntxt_pd);
goto err;
}
@@ -451,7 +440,7 @@ err:
return status;
}
-static int ocrdma_dealloc_ucontext_pd(struct ocrdma_ucontext *uctx)
+static void ocrdma_dealloc_ucontext_pd(struct ocrdma_ucontext *uctx)
{
struct ocrdma_pd *pd = uctx->cntxt_pd;
struct ocrdma_dev *dev = get_ocrdma_dev(pd->ibpd.device);
@@ -460,9 +449,9 @@ static int ocrdma_dealloc_ucontext_pd(struct ocrdma_ucontext *uctx)
pr_err("%s(%d) Freeing in use pdid=0x%x.\n",
__func__, dev->id, pd->id);
}
+ kfree(uctx->cntxt_pd);
uctx->cntxt_pd = NULL;
- (void)_ocrdma_dealloc_pd(dev, pd);
- return 0;
+ _ocrdma_dealloc_pd(dev, pd);
}
static struct ocrdma_pd *ocrdma_get_ucontext_pd(struct ocrdma_ucontext *uctx)
@@ -486,33 +475,28 @@ static void ocrdma_release_ucontext_pd(struct ocrdma_ucontext *uctx)
mutex_unlock(&uctx->mm_list_lock);
}
-struct ib_ucontext *ocrdma_alloc_ucontext(struct ib_device *ibdev,
- struct ib_udata *udata)
+int ocrdma_alloc_ucontext(struct ib_ucontext *uctx, struct ib_udata *udata)
{
+ struct ib_device *ibdev = uctx->device;
int status;
- struct ocrdma_ucontext *ctx;
- struct ocrdma_alloc_ucontext_resp resp;
+ struct ocrdma_ucontext *ctx = get_ocrdma_ucontext(uctx);
+ struct ocrdma_alloc_ucontext_resp resp = {};
struct ocrdma_dev *dev = get_ocrdma_dev(ibdev);
struct pci_dev *pdev = dev->nic_info.pdev;
u32 map_len = roundup(sizeof(u32) * 2048, PAGE_SIZE);
if (!udata)
- return ERR_PTR(-EFAULT);
- ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
- if (!ctx)
- return ERR_PTR(-ENOMEM);
+ return -EFAULT;
INIT_LIST_HEAD(&ctx->mm_head);
mutex_init(&ctx->mm_list_lock);
ctx->ah_tbl.va = dma_alloc_coherent(&pdev->dev, map_len,
&ctx->ah_tbl.pa, GFP_KERNEL);
- if (!ctx->ah_tbl.va) {
- kfree(ctx);
- return ERR_PTR(-ENOMEM);
- }
+ if (!ctx->ah_tbl.va)
+ return -ENOMEM;
+
ctx->ah_tbl.len = map_len;
- memset(&resp, 0, sizeof(resp));
resp.ah_tbl_len = ctx->ah_tbl.len;
resp.ah_tbl_page = virt_to_phys(ctx->ah_tbl.va);
@@ -534,27 +518,26 @@ struct ib_ucontext *ocrdma_alloc_ucontext(struct ib_device *ibdev,
status = ib_copy_to_udata(udata, &resp, sizeof(resp));
if (status)
goto cpy_err;
- return &ctx->ibucontext;
+ return 0;
cpy_err:
+ ocrdma_dealloc_ucontext_pd(ctx);
pd_err:
ocrdma_del_mmap(ctx, ctx->ah_tbl.pa, ctx->ah_tbl.len);
map_err:
dma_free_coherent(&pdev->dev, ctx->ah_tbl.len, ctx->ah_tbl.va,
ctx->ah_tbl.pa);
- kfree(ctx);
- return ERR_PTR(status);
+ return status;
}
-int ocrdma_dealloc_ucontext(struct ib_ucontext *ibctx)
+void ocrdma_dealloc_ucontext(struct ib_ucontext *ibctx)
{
- int status;
struct ocrdma_mm *mm, *tmp;
struct ocrdma_ucontext *uctx = get_ocrdma_ucontext(ibctx);
struct ocrdma_dev *dev = get_ocrdma_dev(ibctx->device);
struct pci_dev *pdev = dev->nic_info.pdev;
- status = ocrdma_dealloc_ucontext_pd(uctx);
+ ocrdma_dealloc_ucontext_pd(uctx);
ocrdma_del_mmap(uctx, uctx->ah_tbl.pa, uctx->ah_tbl.len);
dma_free_coherent(&pdev->dev, uctx->ah_tbl.len, uctx->ah_tbl.va,
@@ -564,8 +547,6 @@ int ocrdma_dealloc_ucontext(struct ib_ucontext *ibctx)
list_del(&mm->entry);
kfree(mm);
}
- kfree(uctx);
- return status;
}
int ocrdma_mmap(struct ib_ucontext *context, struct vm_area_struct *vma)
@@ -658,10 +639,10 @@ dpp_map_err:
return status;
}
-struct ib_pd *ocrdma_alloc_pd(struct ib_device *ibdev,
- struct ib_ucontext *context,
- struct ib_udata *udata)
+int ocrdma_alloc_pd(struct ib_pd *ibpd, struct ib_ucontext *context,
+ struct ib_udata *udata)
{
+ struct ib_device *ibdev = ibpd->device;
struct ocrdma_dev *dev = get_ocrdma_dev(ibdev);
struct ocrdma_pd *pd;
struct ocrdma_ucontext *uctx = NULL;
@@ -677,11 +658,10 @@ struct ib_pd *ocrdma_alloc_pd(struct ib_device *ibdev,
}
}
- pd = _ocrdma_alloc_pd(dev, uctx, udata);
- if (IS_ERR(pd)) {
- status = PTR_ERR(pd);
+ pd = get_ocrdma_pd(ibpd);
+ status = _ocrdma_alloc_pd(dev, pd, uctx, udata);
+ if (status)
goto exit;
- }
pd_mapping:
if (udata && context) {
@@ -689,25 +669,22 @@ pd_mapping:
if (status)
goto err;
}
- return &pd->ibpd;
+ return 0;
err:
- if (is_uctx_pd) {
+ if (is_uctx_pd)
ocrdma_release_ucontext_pd(uctx);
- } else {
- if (_ocrdma_dealloc_pd(dev, pd))
- pr_err("%s: _ocrdma_dealloc_pd() failed\n", __func__);
- }
+ else
+ _ocrdma_dealloc_pd(dev, pd);
exit:
- return ERR_PTR(status);
+ return status;
}
-int ocrdma_dealloc_pd(struct ib_pd *ibpd)
+void ocrdma_dealloc_pd(struct ib_pd *ibpd)
{
struct ocrdma_pd *pd = get_ocrdma_pd(ibpd);
struct ocrdma_dev *dev = get_ocrdma_dev(ibpd->device);
struct ocrdma_ucontext *uctx = NULL;
- int status = 0;
u64 usr_db;
uctx = pd->uctx;
@@ -721,11 +698,10 @@ int ocrdma_dealloc_pd(struct ib_pd *ibpd)
if (is_ucontext_pd(uctx, pd)) {
ocrdma_release_ucontext_pd(uctx);
- return status;
+ return;
}
}
- status = _ocrdma_dealloc_pd(dev, pd);
- return status;
+ _ocrdma_dealloc_pd(dev, pd);
}
static int ocrdma_alloc_lkey(struct ocrdma_dev *dev, struct ocrdma_mr *mr,
@@ -854,10 +830,11 @@ static void build_user_pbes(struct ocrdma_dev *dev, struct ocrdma_mr *mr,
u32 num_pbes)
{
struct ocrdma_pbe *pbe;
- struct scatterlist *sg;
+ struct sg_dma_page_iter sg_iter;
struct ocrdma_pbl *pbl_tbl = mr->hwmr.pbl_table;
struct ib_umem *umem = mr->umem;
- int shift, pg_cnt, pages, pbe_cnt, entry, total_num_pbes = 0;
+ int pbe_cnt, total_num_pbes = 0;
+ u64 pg_addr;
if (!mr->hwmr.num_pbes)
return;
@@ -865,36 +842,26 @@ static void build_user_pbes(struct ocrdma_dev *dev, struct ocrdma_mr *mr,
pbe = (struct ocrdma_pbe *)pbl_tbl->va;
pbe_cnt = 0;
- shift = umem->page_shift;
-
- for_each_sg(umem->sg_head.sgl, sg, umem->nmap, entry) {
- pages = sg_dma_len(sg) >> shift;
- for (pg_cnt = 0; pg_cnt < pages; pg_cnt++) {
- /* store the page address in pbe */
- pbe->pa_lo =
- cpu_to_le32(sg_dma_address(sg) +
- (pg_cnt << shift));
- pbe->pa_hi =
- cpu_to_le32(upper_32_bits(sg_dma_address(sg) +
- (pg_cnt << shift)));
- pbe_cnt += 1;
- total_num_pbes += 1;
- pbe++;
-
- /* if done building pbes, issue the mbx cmd. */
- if (total_num_pbes == num_pbes)
- return;
-
- /* if the given pbl is full storing the pbes,
- * move to next pbl.
- */
- if (pbe_cnt ==
- (mr->hwmr.pbl_size / sizeof(u64))) {
- pbl_tbl++;
- pbe = (struct ocrdma_pbe *)pbl_tbl->va;
- pbe_cnt = 0;
- }
+ for_each_sg_dma_page (umem->sg_head.sgl, &sg_iter, umem->nmap, 0) {
+ /* store the page address in pbe */
+ pg_addr = sg_page_iter_dma_address(&sg_iter);
+ pbe->pa_lo = cpu_to_le32(pg_addr);
+ pbe->pa_hi = cpu_to_le32(upper_32_bits(pg_addr));
+ pbe_cnt += 1;
+ total_num_pbes += 1;
+ pbe++;
+
+ /* if done building pbes, issue the mbx cmd. */
+ if (total_num_pbes == num_pbes)
+ return;
+ /* if the given pbl is full storing the pbes,
+ * move to next pbl.
+ */
+ if (pbe_cnt == (mr->hwmr.pbl_size / sizeof(u64))) {
+ pbl_tbl++;
+ pbe = (struct ocrdma_pbe *)pbl_tbl->va;
+ pbe_cnt = 0;
}
}
}
@@ -916,7 +883,7 @@ struct ib_mr *ocrdma_reg_user_mr(struct ib_pd *ibpd, u64 start, u64 len,
mr = kzalloc(sizeof(*mr), GFP_KERNEL);
if (!mr)
return ERR_PTR(status);
- mr->umem = ib_umem_get(ibpd->uobject->context, start, len, acc, 0);
+ mr->umem = ib_umem_get(udata, start, len, acc, 0);
if (IS_ERR(mr->umem)) {
status = -EFAULT;
goto umem_err;
@@ -926,7 +893,7 @@ struct ib_mr *ocrdma_reg_user_mr(struct ib_pd *ibpd, u64 start, u64 len,
if (status)
goto umem_err;
- mr->hwmr.pbe_size = BIT(mr->umem->page_shift);
+ mr->hwmr.pbe_size = PAGE_SIZE;
mr->hwmr.fbo = ib_umem_offset(mr->umem);
mr->hwmr.va = usr_addr;
mr->hwmr.len = len;
diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.h b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.h
index b69cfdce7970..4c04ab40798e 100644
--- a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.h
+++ b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.h
@@ -64,15 +64,14 @@ void ocrdma_get_guid(struct ocrdma_dev *, u8 *guid);
struct net_device *ocrdma_get_netdev(struct ib_device *device, u8 port_num);
int ocrdma_query_pkey(struct ib_device *, u8 port, u16 index, u16 *pkey);
-struct ib_ucontext *ocrdma_alloc_ucontext(struct ib_device *,
- struct ib_udata *);
-int ocrdma_dealloc_ucontext(struct ib_ucontext *);
+int ocrdma_alloc_ucontext(struct ib_ucontext *uctx, struct ib_udata *udata);
+void ocrdma_dealloc_ucontext(struct ib_ucontext *uctx);
int ocrdma_mmap(struct ib_ucontext *, struct vm_area_struct *vma);
-struct ib_pd *ocrdma_alloc_pd(struct ib_device *,
- struct ib_ucontext *, struct ib_udata *);
-int ocrdma_dealloc_pd(struct ib_pd *pd);
+int ocrdma_alloc_pd(struct ib_pd *pd, struct ib_ucontext *uctx,
+ struct ib_udata *udata);
+void ocrdma_dealloc_pd(struct ib_pd *pd);
struct ib_cq *ocrdma_create_cq(struct ib_device *ibdev,
const struct ib_cq_init_attr *attr,
diff --git a/drivers/infiniband/hw/qedr/main.c b/drivers/infiniband/hw/qedr/main.c
index 75940e2a8791..996d9ecd93e0 100644
--- a/drivers/infiniband/hw/qedr/main.c
+++ b/drivers/infiniband/hw/qedr/main.c
@@ -137,7 +137,8 @@ static int qedr_iw_port_immutable(struct ib_device *ibdev, u8 port_num,
static ssize_t hw_rev_show(struct device *device, struct device_attribute *attr,
char *buf)
{
- struct qedr_dev *dev = dev_get_drvdata(device);
+ struct qedr_dev *dev =
+ rdma_device_to_drv_device(device, struct qedr_dev, ibdev);
return scnprintf(buf, PAGE_SIZE, "0x%x\n", dev->pdev->vendor);
}
@@ -238,6 +239,8 @@ static const struct ib_device_ops qedr_dev_ops = {
.reg_user_mr = qedr_reg_user_mr,
.req_notify_cq = qedr_arm_cq,
.resize_cq = qedr_resize_cq,
+ INIT_RDMA_OBJ_SIZE(ib_pd, qedr_pd, ibpd),
+ INIT_RDMA_OBJ_SIZE(ib_ucontext, qedr_ucontext, ibucontext),
};
static int qedr_register_device(struct qedr_dev *dev)
@@ -290,7 +293,7 @@ static int qedr_register_device(struct qedr_dev *dev)
ib_set_device_ops(&dev->ibdev, &qedr_dev_ops);
dev->ibdev.driver_id = RDMA_DRIVER_QEDR;
- return ib_register_device(&dev->ibdev, "qedr%d", NULL);
+ return ib_register_device(&dev->ibdev, "qedr%d");
}
/* This function allocates fast-path status block memory */
@@ -852,7 +855,7 @@ static struct qedr_dev *qedr_add(struct qed_dev *cdev, struct pci_dev *pdev,
struct qedr_dev *dev;
int rc = 0;
- dev = (struct qedr_dev *)ib_alloc_device(sizeof(*dev));
+ dev = ib_alloc_device(qedr_dev, ibdev);
if (!dev) {
pr_err("Unable to allocate ib device\n");
return NULL;
diff --git a/drivers/infiniband/hw/qedr/qedr_iw_cm.c b/drivers/infiniband/hw/qedr/qedr_iw_cm.c
index 93b16237b767..0555e5a8c9ed 100644
--- a/drivers/infiniband/hw/qedr/qedr_iw_cm.c
+++ b/drivers/infiniband/hw/qedr/qedr_iw_cm.c
@@ -349,7 +349,7 @@ qedr_iw_event_handler(void *context, struct qed_iwarp_cm_event_params *params)
default:
DP_NOTICE(dev, "Unknown event received %d\n", params->event);
break;
- };
+ }
return 0;
}
diff --git a/drivers/infiniband/hw/qedr/verbs.c b/drivers/infiniband/hw/qedr/verbs.c
index e1ccf32b1c3d..59ad4202422c 100644
--- a/drivers/infiniband/hw/qedr/verbs.c
+++ b/drivers/infiniband/hw/qedr/verbs.c
@@ -67,7 +67,7 @@ static inline int qedr_ib_copy_to_udata(struct ib_udata *udata, void *src,
int qedr_query_pkey(struct ib_device *ibdev, u8 port, u16 index, u16 *pkey)
{
- if (index > QEDR_ROCE_PKEY_TABLE_LEN)
+ if (index >= QEDR_ROCE_PKEY_TABLE_LEN)
return -EINVAL;
*pkey = QEDR_ROCE_PKEY_DEFAULT;
@@ -316,28 +316,24 @@ static bool qedr_search_mmap(struct qedr_ucontext *uctx, u64 phy_addr,
return found;
}
-struct ib_ucontext *qedr_alloc_ucontext(struct ib_device *ibdev,
- struct ib_udata *udata)
+int qedr_alloc_ucontext(struct ib_ucontext *uctx, struct ib_udata *udata)
{
+ struct ib_device *ibdev = uctx->device;
int rc;
- struct qedr_ucontext *ctx;
- struct qedr_alloc_ucontext_resp uresp;
+ struct qedr_ucontext *ctx = get_qedr_ucontext(uctx);
+ struct qedr_alloc_ucontext_resp uresp = {};
struct qedr_dev *dev = get_qedr_dev(ibdev);
struct qed_rdma_add_user_out_params oparams;
if (!udata)
- return ERR_PTR(-EFAULT);
-
- ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
- if (!ctx)
- return ERR_PTR(-ENOMEM);
+ return -EFAULT;
rc = dev->ops->rdma_add_user(dev->rdma_ctx, &oparams);
if (rc) {
DP_ERR(dev,
"failed to allocate a DPI for a new RoCE application, rc=%d. To overcome this consider to increase the number of DPIs, increase the doorbell BAR size or just close unnecessary RoCE applications. In order to increase the number of DPIs consult the qedr readme\n",
rc);
- goto err;
+ return rc;
}
ctx->dpi = oparams.dpi;
@@ -347,8 +343,6 @@ struct ib_ucontext *qedr_alloc_ucontext(struct ib_device *ibdev,
INIT_LIST_HEAD(&ctx->mm_head);
mutex_init(&ctx->mm_list_lock);
- memset(&uresp, 0, sizeof(uresp));
-
uresp.dpm_enabled = dev->user_dpm_enabled;
uresp.wids_enabled = 1;
uresp.wid_count = oparams.wid_count;
@@ -364,28 +358,23 @@ struct ib_ucontext *qedr_alloc_ucontext(struct ib_device *ibdev,
rc = qedr_ib_copy_to_udata(udata, &uresp, sizeof(uresp));
if (rc)
- goto err;
+ return rc;
ctx->dev = dev;
rc = qedr_add_mmap(ctx, ctx->dpi_phys_addr, ctx->dpi_size);
if (rc)
- goto err;
+ return rc;
DP_DEBUG(dev, QEDR_MSG_INIT, "Allocating user context %p\n",
&ctx->ibucontext);
- return &ctx->ibucontext;
-
-err:
- kfree(ctx);
- return ERR_PTR(rc);
+ return 0;
}
-int qedr_dealloc_ucontext(struct ib_ucontext *ibctx)
+void qedr_dealloc_ucontext(struct ib_ucontext *ibctx)
{
struct qedr_ucontext *uctx = get_qedr_ucontext(ibctx);
struct qedr_mm *mm, *tmp;
- int status = 0;
DP_DEBUG(uctx->dev, QEDR_MSG_INIT, "Deallocating user context %p\n",
uctx);
@@ -398,9 +387,6 @@ int qedr_dealloc_ucontext(struct ib_ucontext *ibctx)
list_del(&mm->entry);
kfree(mm);
}
-
- kfree(uctx);
- return status;
}
int qedr_mmap(struct ib_ucontext *context, struct vm_area_struct *vma)
@@ -450,11 +436,12 @@ int qedr_mmap(struct ib_ucontext *context, struct vm_area_struct *vma)
vma->vm_page_prot);
}
-struct ib_pd *qedr_alloc_pd(struct ib_device *ibdev,
- struct ib_ucontext *context, struct ib_udata *udata)
+int qedr_alloc_pd(struct ib_pd *ibpd, struct ib_ucontext *context,
+ struct ib_udata *udata)
{
+ struct ib_device *ibdev = ibpd->device;
struct qedr_dev *dev = get_qedr_dev(ibdev);
- struct qedr_pd *pd;
+ struct qedr_pd *pd = get_qedr_pd(ibpd);
u16 pd_id;
int rc;
@@ -463,16 +450,12 @@ struct ib_pd *qedr_alloc_pd(struct ib_device *ibdev,
if (!dev->rdma_ctx) {
DP_ERR(dev, "invalid RDMA context\n");
- return ERR_PTR(-EINVAL);
+ return -EINVAL;
}
- pd = kzalloc(sizeof(*pd), GFP_KERNEL);
- if (!pd)
- return ERR_PTR(-ENOMEM);
-
rc = dev->ops->rdma_alloc_pd(dev->rdma_ctx, &pd_id);
if (rc)
- goto err;
+ return rc;
pd->pd_id = pd_id;
@@ -485,36 +468,23 @@ struct ib_pd *qedr_alloc_pd(struct ib_device *ibdev,
if (rc) {
DP_ERR(dev, "copy error pd_id=0x%x.\n", pd_id);
dev->ops->rdma_dealloc_pd(dev->rdma_ctx, pd_id);
- goto err;
+ return rc;
}
pd->uctx = get_qedr_ucontext(context);
pd->uctx->pd = pd;
}
- return &pd->ibpd;
-
-err:
- kfree(pd);
- return ERR_PTR(rc);
+ return 0;
}
-int qedr_dealloc_pd(struct ib_pd *ibpd)
+void qedr_dealloc_pd(struct ib_pd *ibpd)
{
struct qedr_dev *dev = get_qedr_dev(ibpd->device);
struct qedr_pd *pd = get_qedr_pd(ibpd);
- if (!pd) {
- pr_err("Invalid PD received in dealloc_pd\n");
- return -EINVAL;
- }
-
DP_DEBUG(dev, QEDR_MSG_INIT, "Deallocating PD %d\n", pd->pd_id);
dev->ops->rdma_dealloc_pd(dev->rdma_ctx, pd->pd_id);
-
- kfree(pd);
-
- return 0;
}
static void qedr_free_pbl(struct qedr_dev *dev,
@@ -636,13 +606,12 @@ static void qedr_populate_pbls(struct qedr_dev *dev, struct ib_umem *umem,
struct qedr_pbl *pbl,
struct qedr_pbl_info *pbl_info, u32 pg_shift)
{
- int shift, pg_cnt, pages, pbe_cnt, total_num_pbes = 0;
+ int pbe_cnt, total_num_pbes = 0;
u32 fw_pg_cnt, fw_pg_per_umem_pg;
struct qedr_pbl *pbl_tbl;
- struct scatterlist *sg;
+ struct sg_dma_page_iter sg_iter;
struct regpair *pbe;
u64 pg_addr;
- int entry;
if (!pbl_info->num_pbes)
return;
@@ -663,38 +632,32 @@ static void qedr_populate_pbls(struct qedr_dev *dev, struct ib_umem *umem,
pbe_cnt = 0;
- shift = umem->page_shift;
-
- fw_pg_per_umem_pg = BIT(umem->page_shift - pg_shift);
-
- for_each_sg(umem->sg_head.sgl, sg, umem->nmap, entry) {
- pages = sg_dma_len(sg) >> shift;
- pg_addr = sg_dma_address(sg);
- for (pg_cnt = 0; pg_cnt < pages; pg_cnt++) {
- for (fw_pg_cnt = 0; fw_pg_cnt < fw_pg_per_umem_pg;) {
- pbe->lo = cpu_to_le32(pg_addr);
- pbe->hi = cpu_to_le32(upper_32_bits(pg_addr));
-
- pg_addr += BIT(pg_shift);
- pbe_cnt++;
- total_num_pbes++;
- pbe++;
-
- if (total_num_pbes == pbl_info->num_pbes)
- return;
-
- /* If the given pbl is full storing the pbes,
- * move to next pbl.
- */
- if (pbe_cnt ==
- (pbl_info->pbl_size / sizeof(u64))) {
- pbl_tbl++;
- pbe = (struct regpair *)pbl_tbl->va;
- pbe_cnt = 0;
- }
+ fw_pg_per_umem_pg = BIT(PAGE_SHIFT - pg_shift);
+
+ for_each_sg_dma_page (umem->sg_head.sgl, &sg_iter, umem->nmap, 0) {
+ pg_addr = sg_page_iter_dma_address(&sg_iter);
+ for (fw_pg_cnt = 0; fw_pg_cnt < fw_pg_per_umem_pg;) {
+ pbe->lo = cpu_to_le32(pg_addr);
+ pbe->hi = cpu_to_le32(upper_32_bits(pg_addr));
+
+ pg_addr += BIT(pg_shift);
+ pbe_cnt++;
+ total_num_pbes++;
+ pbe++;
+
+ if (total_num_pbes == pbl_info->num_pbes)
+ return;
- fw_pg_cnt++;
+ /* If the given pbl is full storing the pbes,
+ * move to next pbl.
+ */
+ if (pbe_cnt == (pbl_info->pbl_size / sizeof(u64))) {
+ pbl_tbl++;
+ pbe = (struct regpair *)pbl_tbl->va;
+ pbe_cnt = 0;
}
+
+ fw_pg_cnt++;
}
}
}
@@ -736,11 +699,10 @@ static inline int qedr_align_cq_entries(int entries)
return aligned_size / QEDR_CQE_SIZE;
}
-static inline int qedr_init_user_queue(struct ib_ucontext *ib_ctx,
+static inline int qedr_init_user_queue(struct ib_udata *udata,
struct qedr_dev *dev,
- struct qedr_userq *q,
- u64 buf_addr, size_t buf_len,
- int access, int dmasync,
+ struct qedr_userq *q, u64 buf_addr,
+ size_t buf_len, int access, int dmasync,
int alloc_and_init)
{
u32 fw_pages;
@@ -748,7 +710,7 @@ static inline int qedr_init_user_queue(struct ib_ucontext *ib_ctx,
q->buf_addr = buf_addr;
q->buf_len = buf_len;
- q->umem = ib_umem_get(ib_ctx, q->buf_addr, q->buf_len, access, dmasync);
+ q->umem = ib_umem_get(udata, q->buf_addr, q->buf_len, access, dmasync);
if (IS_ERR(q->umem)) {
DP_ERR(dev, "create user queue: failed ib_umem_get, got %ld\n",
PTR_ERR(q->umem));
@@ -756,7 +718,7 @@ static inline int qedr_init_user_queue(struct ib_ucontext *ib_ctx,
}
fw_pages = ib_umem_page_count(q->umem) <<
- (q->umem->page_shift - FW_PAGE_SHIFT);
+ (PAGE_SHIFT - FW_PAGE_SHIFT);
rc = qedr_prepare_pbl_tbl(dev, &q->pbl_info, fw_pages, 0);
if (rc)
@@ -905,9 +867,9 @@ struct ib_cq *qedr_create_cq(struct ib_device *ibdev,
cq->cq_type = QEDR_CQ_TYPE_USER;
- rc = qedr_init_user_queue(ib_ctx, dev, &cq->q, ureq.addr,
- ureq.len, IB_ACCESS_LOCAL_WRITE,
- 1, 1);
+ rc = qedr_init_user_queue(udata, dev, &cq->q, ureq.addr,
+ ureq.len, IB_ACCESS_LOCAL_WRITE, 1,
+ 1);
if (rc)
goto err0;
@@ -1344,7 +1306,7 @@ static void qedr_free_srq_kernel_params(struct qedr_srq *srq)
hw_srq->phy_prod_pair_addr);
}
-static int qedr_init_srq_user_params(struct ib_ucontext *ib_ctx,
+static int qedr_init_srq_user_params(struct ib_udata *udata,
struct qedr_srq *srq,
struct qedr_create_srq_ureq *ureq,
int access, int dmasync)
@@ -1352,14 +1314,14 @@ static int qedr_init_srq_user_params(struct ib_ucontext *ib_ctx,
struct scatterlist *sg;
int rc;
- rc = qedr_init_user_queue(ib_ctx, srq->dev, &srq->usrq, ureq->srq_addr,
+ rc = qedr_init_user_queue(udata, srq->dev, &srq->usrq, ureq->srq_addr,
ureq->srq_len, access, dmasync, 1);
if (rc)
return rc;
- srq->prod_umem = ib_umem_get(ib_ctx, ureq->prod_pair_addr,
- sizeof(struct rdma_srq_producers),
- access, dmasync);
+ srq->prod_umem =
+ ib_umem_get(udata, ureq->prod_pair_addr,
+ sizeof(struct rdma_srq_producers), access, dmasync);
if (IS_ERR(srq->prod_umem)) {
qedr_free_pbl(srq->dev, &srq->usrq.pbl_info, srq->usrq.pbl_tbl);
ib_umem_release(srq->usrq.umem);
@@ -1434,7 +1396,6 @@ struct ib_srq *qedr_create_srq(struct ib_pd *ibpd,
struct qedr_pd *pd = get_qedr_pd(ibpd);
struct qedr_create_srq_ureq ureq = {};
u64 pbl_base_addr, phy_prod_pair_addr;
- struct ib_ucontext *ib_ctx = NULL;
struct qedr_srq_hwq_info *hw_srq;
u32 page_cnt, page_size;
struct qedr_srq *srq;
@@ -1459,23 +1420,21 @@ struct ib_srq *qedr_create_srq(struct ib_pd *ibpd,
hw_srq->max_wr = init_attr->attr.max_wr;
hw_srq->max_sges = init_attr->attr.max_sge;
- if (udata && ibpd->uobject && ibpd->uobject->context) {
- ib_ctx = ibpd->uobject->context;
-
+ if (udata) {
if (ib_copy_from_udata(&ureq, udata, sizeof(ureq))) {
DP_ERR(dev,
"create srq: problem copying data from user space\n");
goto err0;
}
- rc = qedr_init_srq_user_params(ib_ctx, srq, &ureq, 0, 0);
+ rc = qedr_init_srq_user_params(udata, srq, &ureq, 0, 0);
if (rc)
goto err0;
page_cnt = srq->usrq.pbl_info.num_pbes;
pbl_base_addr = srq->usrq.pbl_tbl->pa;
phy_prod_pair_addr = hw_srq->phy_prod_pair_addr;
- page_size = BIT(srq->usrq.umem->page_shift);
+ page_size = PAGE_SIZE;
} else {
struct qed_chain *pbl;
@@ -1699,13 +1658,10 @@ static int qedr_create_user_qp(struct qedr_dev *dev,
struct qed_rdma_create_qp_in_params in_params;
struct qed_rdma_create_qp_out_params out_params;
struct qedr_pd *pd = get_qedr_pd(ibpd);
- struct ib_ucontext *ib_ctx = NULL;
struct qedr_create_qp_ureq ureq;
int alloc_and_init = rdma_protocol_roce(&dev->ibdev, 1);
int rc = -EINVAL;
- ib_ctx = ibpd->uobject->context;
-
memset(&ureq, 0, sizeof(ureq));
rc = ib_copy_from_udata(&ureq, udata, sizeof(ureq));
if (rc) {
@@ -1714,14 +1670,14 @@ static int qedr_create_user_qp(struct qedr_dev *dev,
}
/* SQ - read access only (0), dma sync not required (0) */
- rc = qedr_init_user_queue(ib_ctx, dev, &qp->usq, ureq.sq_addr,
+ rc = qedr_init_user_queue(udata, dev, &qp->usq, ureq.sq_addr,
ureq.sq_len, 0, 0, alloc_and_init);
if (rc)
return rc;
if (!qp->srq) {
/* RQ - read access only (0), dma sync not required (0) */
- rc = qedr_init_user_queue(ib_ctx, dev, &qp->urq, ureq.rq_addr,
+ rc = qedr_init_user_queue(udata, dev, &qp->urq, ureq.rq_addr,
ureq.rq_len, 0, 0, alloc_and_init);
if (rc)
return rc;
@@ -2117,7 +2073,7 @@ static int qedr_update_qp_state(struct qedr_dev *dev,
default:
status = -EINVAL;
break;
- };
+ }
break;
case QED_ROCE_QP_STATE_INIT:
switch (new_state) {
@@ -2138,7 +2094,7 @@ static int qedr_update_qp_state(struct qedr_dev *dev,
/* Invalid state change. */
status = -EINVAL;
break;
- };
+ }
break;
case QED_ROCE_QP_STATE_RTR:
/* RTR->XXX */
@@ -2151,7 +2107,7 @@ static int qedr_update_qp_state(struct qedr_dev *dev,
/* Invalid state change. */
status = -EINVAL;
break;
- };
+ }
break;
case QED_ROCE_QP_STATE_RTS:
/* RTS->XXX */
@@ -2164,7 +2120,7 @@ static int qedr_update_qp_state(struct qedr_dev *dev,
/* Invalid state change. */
status = -EINVAL;
break;
- };
+ }
break;
case QED_ROCE_QP_STATE_SQD:
/* SQD->XXX */
@@ -2176,7 +2132,7 @@ static int qedr_update_qp_state(struct qedr_dev *dev,
/* Invalid state change. */
status = -EINVAL;
break;
- };
+ }
break;
case QED_ROCE_QP_STATE_ERR:
/* ERR->XXX */
@@ -2194,12 +2150,12 @@ static int qedr_update_qp_state(struct qedr_dev *dev,
default:
status = -EINVAL;
break;
- };
+ }
break;
default:
status = -EINVAL;
break;
- };
+ }
return status;
}
@@ -2719,7 +2675,7 @@ struct ib_mr *qedr_reg_user_mr(struct ib_pd *ibpd, u64 start, u64 len,
mr->type = QEDR_MR_USER;
- mr->umem = ib_umem_get(ibpd->uobject->context, start, len, acc, 0);
+ mr->umem = ib_umem_get(udata, start, len, acc, 0);
if (IS_ERR(mr->umem)) {
rc = -EFAULT;
goto err0;
@@ -2730,7 +2686,7 @@ struct ib_mr *qedr_reg_user_mr(struct ib_pd *ibpd, u64 start, u64 len,
goto err1;
qedr_populate_pbls(dev, mr->umem, mr->info.pbl_table,
- &mr->info.pbl_info, mr->umem->page_shift);
+ &mr->info.pbl_info, PAGE_SHIFT);
rc = dev->ops->rdma_alloc_tid(dev->rdma_ctx, &mr->hw_mr.itid);
if (rc) {
@@ -2751,7 +2707,7 @@ struct ib_mr *qedr_reg_user_mr(struct ib_pd *ibpd, u64 start, u64 len,
mr->hw_mr.pbl_ptr = mr->info.pbl_table[0].pa;
mr->hw_mr.pbl_two_level = mr->info.pbl_info.two_layered;
mr->hw_mr.pbl_page_size_log = ilog2(mr->info.pbl_info.pbl_size);
- mr->hw_mr.page_size_log = mr->umem->page_shift;
+ mr->hw_mr.page_size_log = PAGE_SHIFT;
mr->hw_mr.fbo = ib_umem_offset(mr->umem);
mr->hw_mr.length = len;
mr->hw_mr.vaddr = usr_addr;
diff --git a/drivers/infiniband/hw/qedr/verbs.h b/drivers/infiniband/hw/qedr/verbs.h
index 1852b7012bf4..f0c05f4771ac 100644
--- a/drivers/infiniband/hw/qedr/verbs.h
+++ b/drivers/infiniband/hw/qedr/verbs.h
@@ -43,13 +43,13 @@ int qedr_iw_query_gid(struct ib_device *ibdev, u8 port,
int qedr_query_pkey(struct ib_device *, u8 port, u16 index, u16 *pkey);
-struct ib_ucontext *qedr_alloc_ucontext(struct ib_device *, struct ib_udata *);
-int qedr_dealloc_ucontext(struct ib_ucontext *);
+int qedr_alloc_ucontext(struct ib_ucontext *uctx, struct ib_udata *udata);
+void qedr_dealloc_ucontext(struct ib_ucontext *uctx);
int qedr_mmap(struct ib_ucontext *, struct vm_area_struct *vma);
-struct ib_pd *qedr_alloc_pd(struct ib_device *,
- struct ib_ucontext *, struct ib_udata *);
-int qedr_dealloc_pd(struct ib_pd *pd);
+int qedr_alloc_pd(struct ib_pd *pd, struct ib_ucontext *uctx,
+ struct ib_udata *udata);
+void qedr_dealloc_pd(struct ib_pd *pd);
struct ib_cq *qedr_create_cq(struct ib_device *ibdev,
const struct ib_cq_init_attr *attr,
diff --git a/drivers/infiniband/hw/qib/qib_debugfs.c b/drivers/infiniband/hw/qib/qib_debugfs.c
index 5ed1ed93380f..caeb77d07a58 100644
--- a/drivers/infiniband/hw/qib/qib_debugfs.c
+++ b/drivers/infiniband/hw/qib/qib_debugfs.c
@@ -66,15 +66,6 @@ static const struct file_operations _##name##_file_ops = { \
.release = seq_release \
};
-#define DEBUGFS_FILE_CREATE(name) \
-do { \
- struct dentry *ent; \
- ent = debugfs_create_file(#name , 0400, ibd->qib_ibdev_dbg, \
- ibd, &_##name##_file_ops); \
- if (!ent) \
- pr_warn("create of " #name " failed\n"); \
-} while (0)
-
static void *_opcode_stats_seq_start(struct seq_file *s, loff_t *pos)
{
struct qib_opcode_stats_perctx *opstats;
@@ -249,17 +240,17 @@ DEBUGFS_FILE(qp_stats)
void qib_dbg_ibdev_init(struct qib_ibdev *ibd)
{
+ struct dentry *root;
char name[10];
snprintf(name, sizeof(name), "qib%d", dd_from_dev(ibd)->unit);
- ibd->qib_ibdev_dbg = debugfs_create_dir(name, qib_dbg_root);
- if (!ibd->qib_ibdev_dbg) {
- pr_warn("create of %s failed\n", name);
- return;
- }
- DEBUGFS_FILE_CREATE(opcode_stats);
- DEBUGFS_FILE_CREATE(ctx_stats);
- DEBUGFS_FILE_CREATE(qp_stats);
+ root = debugfs_create_dir(name, qib_dbg_root);
+ ibd->qib_ibdev_dbg = root;
+
+ debugfs_create_file("opcode_stats", 0400, root, ibd,
+ &_opcode_stats_file_ops);
+ debugfs_create_file("ctx_stats", 0400, root, ibd, &_ctx_stats_file_ops);
+ debugfs_create_file("qp_stats", 0400, root, ibd, &_qp_stats_file_ops);
}
void qib_dbg_ibdev_exit(struct qib_ibdev *ibd)
@@ -274,8 +265,6 @@ out:
void qib_dbg_init(void)
{
qib_dbg_root = debugfs_create_dir(QIB_DRV_NAME, NULL);
- if (!qib_dbg_root)
- pr_warn("init of debugfs failed\n");
}
void qib_dbg_exit(void)
diff --git a/drivers/infiniband/hw/qib/qib_rc.c b/drivers/infiniband/hw/qib/qib_rc.c
index 6fa002940451..50dd9811b088 100644
--- a/drivers/infiniband/hw/qib/qib_rc.c
+++ b/drivers/infiniband/hw/qib/qib_rc.c
@@ -45,12 +45,7 @@ static u32 restart_sge(struct rvt_sge_state *ss, struct rvt_swqe *wqe,
u32 len;
len = ((psn - wqe->psn) & QIB_PSN_MASK) * pmtu;
- ss->sge = wqe->sg_list[0];
- ss->sg_list = wqe->sg_list + 1;
- ss->num_sge = wqe->wr.num_sge;
- ss->total_len = wqe->length;
- rvt_skip_sge(ss, len, false);
- return wqe->length - len;
+ return rvt_restart_sge(ss, wqe, len);
}
/**
diff --git a/drivers/infiniband/hw/qib/qib_sdma.c b/drivers/infiniband/hw/qib/qib_sdma.c
index 3d64081c4819..99e11c347130 100644
--- a/drivers/infiniband/hw/qib/qib_sdma.c
+++ b/drivers/infiniband/hw/qib/qib_sdma.c
@@ -565,13 +565,8 @@ retry:
sge = &ss->sge;
while (dwords) {
u32 dw;
- u32 len;
+ u32 len = rvt_get_sge_length(sge, dwords << 2);
- len = dwords << 2;
- if (len > sge->length)
- len = sge->length;
- if (len > sge->sge_length)
- len = sge->sge_length;
dw = (len + 3) >> 2;
addr = dma_map_single(&ppd->dd->pcidev->dev, sge->vaddr,
dw << 2, DMA_TO_DEVICE);
@@ -594,24 +589,7 @@ retry:
descqp = &ppd->sdma_descq[0].qw[0];
++ppd->sdma_generation;
}
- sge->vaddr += len;
- sge->length -= len;
- sge->sge_length -= len;
- if (sge->sge_length == 0) {
- if (--ss->num_sge)
- *sge = *ss->sg_list++;
- } else if (sge->length == 0 && sge->mr->lkey) {
- if (++sge->n >= RVT_SEGSZ) {
- if (++sge->m >= sge->mr->mapsz)
- break;
- sge->n = 0;
- }
- sge->vaddr =
- sge->mr->map[sge->m]->segs[sge->n].vaddr;
- sge->length =
- sge->mr->map[sge->m]->segs[sge->n].length;
- }
-
+ rvt_update_sge(ss, len, false);
dwoffset += dw;
dwords -= dw;
}
diff --git a/drivers/infiniband/hw/qib/qib_sysfs.c b/drivers/infiniband/hw/qib/qib_sysfs.c
index 1cf4ca3f23e3..905206a0c2d5 100644
--- a/drivers/infiniband/hw/qib/qib_sysfs.c
+++ b/drivers/infiniband/hw/qib/qib_sysfs.c
@@ -555,7 +555,7 @@ static ssize_t hw_rev_show(struct device *device, struct device_attribute *attr,
char *buf)
{
struct qib_ibdev *dev =
- container_of(device, struct qib_ibdev, rdi.ibdev.dev);
+ rdma_device_to_drv_device(device, struct qib_ibdev, rdi.ibdev);
return sprintf(buf, "%x\n", dd_from_dev(dev)->minrev);
}
@@ -565,7 +565,7 @@ static ssize_t hca_type_show(struct device *device,
struct device_attribute *attr, char *buf)
{
struct qib_ibdev *dev =
- container_of(device, struct qib_ibdev, rdi.ibdev.dev);
+ rdma_device_to_drv_device(device, struct qib_ibdev, rdi.ibdev);
struct qib_devdata *dd = dd_from_dev(dev);
int ret;
@@ -590,7 +590,7 @@ static ssize_t boardversion_show(struct device *device,
struct device_attribute *attr, char *buf)
{
struct qib_ibdev *dev =
- container_of(device, struct qib_ibdev, rdi.ibdev.dev);
+ rdma_device_to_drv_device(device, struct qib_ibdev, rdi.ibdev);
struct qib_devdata *dd = dd_from_dev(dev);
/* The string printed here is already newline-terminated. */
@@ -602,7 +602,7 @@ static ssize_t localbus_info_show(struct device *device,
struct device_attribute *attr, char *buf)
{
struct qib_ibdev *dev =
- container_of(device, struct qib_ibdev, rdi.ibdev.dev);
+ rdma_device_to_drv_device(device, struct qib_ibdev, rdi.ibdev);
struct qib_devdata *dd = dd_from_dev(dev);
/* The string printed here is already newline-terminated. */
@@ -614,7 +614,7 @@ static ssize_t nctxts_show(struct device *device,
struct device_attribute *attr, char *buf)
{
struct qib_ibdev *dev =
- container_of(device, struct qib_ibdev, rdi.ibdev.dev);
+ rdma_device_to_drv_device(device, struct qib_ibdev, rdi.ibdev);
struct qib_devdata *dd = dd_from_dev(dev);
/* Return the number of user ports (contexts) available. */
@@ -630,7 +630,7 @@ static ssize_t nfreectxts_show(struct device *device,
struct device_attribute *attr, char *buf)
{
struct qib_ibdev *dev =
- container_of(device, struct qib_ibdev, rdi.ibdev.dev);
+ rdma_device_to_drv_device(device, struct qib_ibdev, rdi.ibdev);
struct qib_devdata *dd = dd_from_dev(dev);
/* Return the number of free user ports (contexts) available. */
@@ -642,7 +642,7 @@ static ssize_t serial_show(struct device *device,
struct device_attribute *attr, char *buf)
{
struct qib_ibdev *dev =
- container_of(device, struct qib_ibdev, rdi.ibdev.dev);
+ rdma_device_to_drv_device(device, struct qib_ibdev, rdi.ibdev);
struct qib_devdata *dd = dd_from_dev(dev);
buf[sizeof(dd->serial)] = '\0';
@@ -657,7 +657,7 @@ static ssize_t chip_reset_store(struct device *device,
size_t count)
{
struct qib_ibdev *dev =
- container_of(device, struct qib_ibdev, rdi.ibdev.dev);
+ rdma_device_to_drv_device(device, struct qib_ibdev, rdi.ibdev);
struct qib_devdata *dd = dd_from_dev(dev);
int ret;
@@ -679,7 +679,7 @@ static ssize_t tempsense_show(struct device *device,
struct device_attribute *attr, char *buf)
{
struct qib_ibdev *dev =
- container_of(device, struct qib_ibdev, rdi.ibdev.dev);
+ rdma_device_to_drv_device(device, struct qib_ibdev, rdi.ibdev);
struct qib_devdata *dd = dd_from_dev(dev);
int ret;
int idx;
diff --git a/drivers/infiniband/hw/qib/qib_ud.c b/drivers/infiniband/hw/qib/qib_ud.c
index 445ea19a2ec8..5cdedba2d164 100644
--- a/drivers/infiniband/hw/qib/qib_ud.c
+++ b/drivers/infiniband/hw/qib/qib_ud.c
@@ -172,12 +172,8 @@ static void qib_ud_loopback(struct rvt_qp *sqp, struct rvt_swqe *swqe)
ssge.num_sge = swqe->wr.num_sge;
sge = &ssge.sge;
while (length) {
- u32 len = sge->length;
+ u32 len = rvt_get_sge_length(sge, length);
- if (len > length)
- len = length;
- if (len > sge->sge_length)
- len = sge->sge_length;
rvt_copy_sge(qp, &qp->r_sge, sge->vaddr, len, true, false);
sge->vaddr += len;
sge->length -= len;
diff --git a/drivers/infiniband/hw/qib/qib_user_pages.c b/drivers/infiniband/hw/qib/qib_user_pages.c
index 16543d5e80c3..123ca8f64f75 100644
--- a/drivers/infiniband/hw/qib/qib_user_pages.c
+++ b/drivers/infiniband/hw/qib/qib_user_pages.c
@@ -49,43 +49,6 @@ static void __qib_release_user_pages(struct page **p, size_t num_pages,
}
}
-/*
- * Call with current->mm->mmap_sem held.
- */
-static int __qib_get_user_pages(unsigned long start_page, size_t num_pages,
- struct page **p)
-{
- unsigned long lock_limit;
- size_t got;
- int ret;
-
- lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT;
-
- if (num_pages > lock_limit && !capable(CAP_IPC_LOCK)) {
- ret = -ENOMEM;
- goto bail;
- }
-
- for (got = 0; got < num_pages; got += ret) {
- ret = get_user_pages(start_page + got * PAGE_SIZE,
- num_pages - got,
- FOLL_WRITE | FOLL_FORCE,
- p + got, NULL);
- if (ret < 0)
- goto bail_release;
- }
-
- current->mm->pinned_vm += num_pages;
-
- ret = 0;
- goto bail;
-
-bail_release:
- __qib_release_user_pages(p, got, 0);
-bail:
- return ret;
-}
-
/**
* qib_map_page - a safety wrapper around pci_map_page()
*
@@ -137,26 +100,44 @@ int qib_map_page(struct pci_dev *hwdev, struct page *page, dma_addr_t *daddr)
int qib_get_user_pages(unsigned long start_page, size_t num_pages,
struct page **p)
{
+ unsigned long locked, lock_limit;
+ size_t got;
int ret;
- down_write(&current->mm->mmap_sem);
+ lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT;
+ locked = atomic64_add_return(num_pages, &current->mm->pinned_vm);
- ret = __qib_get_user_pages(start_page, num_pages, p);
+ if (locked > lock_limit && !capable(CAP_IPC_LOCK)) {
+ ret = -ENOMEM;
+ goto bail;
+ }
- up_write(&current->mm->mmap_sem);
+ down_read(&current->mm->mmap_sem);
+ for (got = 0; got < num_pages; got += ret) {
+ ret = get_user_pages_longterm(start_page + got * PAGE_SIZE,
+ num_pages - got,
+ FOLL_WRITE | FOLL_FORCE,
+ p + got, NULL);
+ if (ret < 0) {
+ up_read(&current->mm->mmap_sem);
+ goto bail_release;
+ }
+ }
+ up_read(&current->mm->mmap_sem);
+ return 0;
+bail_release:
+ __qib_release_user_pages(p, got, 0);
+bail:
+ atomic64_sub(num_pages, &current->mm->pinned_vm);
return ret;
}
void qib_release_user_pages(struct page **p, size_t num_pages)
{
- if (current->mm) /* during close after signal, mm can be NULL */
- down_write(&current->mm->mmap_sem);
-
__qib_release_user_pages(p, num_pages, 1);
- if (current->mm) {
- current->mm->pinned_vm -= num_pages;
- up_write(&current->mm->mmap_sem);
- }
+ /* during close after signal, mm can be NULL */
+ if (current->mm)
+ atomic64_sub(num_pages, &current->mm->pinned_vm);
}
diff --git a/drivers/infiniband/hw/qib/qib_verbs.c b/drivers/infiniband/hw/qib/qib_verbs.c
index 276304f611ab..5ff32d32c61c 100644
--- a/drivers/infiniband/hw/qib/qib_verbs.c
+++ b/drivers/infiniband/hw/qib/qib_verbs.c
@@ -144,12 +144,8 @@ static u32 qib_count_sge(struct rvt_sge_state *ss, u32 length)
u32 ndesc = 1; /* count the header */
while (length) {
- u32 len = sge.length;
+ u32 len = rvt_get_sge_length(&sge, length);
- if (len > length)
- len = length;
- if (len > sge.sge_length)
- len = sge.sge_length;
if (((long) sge.vaddr & (sizeof(u32) - 1)) ||
(len != length && (len & (sizeof(u32) - 1)))) {
ndesc = 0;
@@ -186,12 +182,8 @@ static void qib_copy_from_sge(void *data, struct rvt_sge_state *ss, u32 length)
struct rvt_sge *sge = &ss->sge;
while (length) {
- u32 len = sge->length;
+ u32 len = rvt_get_sge_length(sge, length);
- if (len > length)
- len = length;
- if (len > sge->sge_length)
- len = sge->sge_length;
memcpy(data, sge->vaddr, len);
sge->vaddr += len;
sge->length -= len;
@@ -440,13 +432,9 @@ static void copy_io(u32 __iomem *piobuf, struct rvt_sge_state *ss,
u32 last;
while (1) {
- u32 len = ss->sge.length;
+ u32 len = rvt_get_sge_length(&ss->sge, length);
u32 off;
- if (len > length)
- len = length;
- if (len > ss->sge.sge_length)
- len = ss->sge.sge_length;
/* If the source address is not aligned, try to align it. */
off = (unsigned long)ss->sge.vaddr & (sizeof(u32) - 1);
if (off) {
@@ -1494,6 +1482,7 @@ static void qib_fill_device_attr(struct qib_devdata *dd)
}
static const struct ib_device_ops qib_dev_ops = {
+ .init_port = qib_create_port_files,
.modify_device = qib_modify_device,
.process_mad = qib_process_mad,
};
@@ -1567,7 +1556,6 @@ int qib_register_ib_device(struct qib_devdata *dd)
/*
* Fill in rvt info object.
*/
- dd->verbs_dev.rdi.driver_f.port_callback = qib_create_port_files;
dd->verbs_dev.rdi.driver_f.get_pci_dev = qib_get_pci_dev;
dd->verbs_dev.rdi.driver_f.check_ah = qib_check_ah;
dd->verbs_dev.rdi.driver_f.setup_wqe = qib_check_send_wqe;
diff --git a/drivers/infiniband/hw/usnic/Makefile b/drivers/infiniband/hw/usnic/Makefile
index 94ae7a1a6950..f12a4938ffd2 100644
--- a/drivers/infiniband/hw/usnic/Makefile
+++ b/drivers/infiniband/hw/usnic/Makefile
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
-ccflags-y := -Idrivers/net/ethernet/cisco/enic
+ccflags-y := -I $(srctree)/drivers/net/ethernet/cisco/enic
obj-$(CONFIG_INFINIBAND_USNIC)+= usnic_verbs.o
diff --git a/drivers/infiniband/hw/usnic/usnic_debugfs.c b/drivers/infiniband/hw/usnic/usnic_debugfs.c
index a3115709fb03..e5a3f02fb078 100644
--- a/drivers/infiniband/hw/usnic/usnic_debugfs.c
+++ b/drivers/infiniband/hw/usnic/usnic_debugfs.c
@@ -113,42 +113,21 @@ static const struct file_operations flowinfo_ops = {
void usnic_debugfs_init(void)
{
debugfs_root = debugfs_create_dir(DRV_NAME, NULL);
- if (IS_ERR(debugfs_root)) {
- usnic_err("Failed to create debugfs root dir, check if debugfs is enabled in kernel configuration\n");
- goto out_clear_root;
- }
flows_dentry = debugfs_create_dir("flows", debugfs_root);
- if (IS_ERR_OR_NULL(flows_dentry)) {
- usnic_err("Failed to create debugfs flow dir with err %ld\n",
- PTR_ERR(flows_dentry));
- goto out_free_root;
- }
debugfs_create_file("build-info", S_IRUGO, debugfs_root,
NULL, &usnic_debugfs_buildinfo_ops);
- return;
-
-out_free_root:
- debugfs_remove_recursive(debugfs_root);
-out_clear_root:
- debugfs_root = NULL;
}
void usnic_debugfs_exit(void)
{
- if (!debugfs_root)
- return;
-
debugfs_remove_recursive(debugfs_root);
debugfs_root = NULL;
}
void usnic_debugfs_flow_add(struct usnic_ib_qp_grp_flow *qp_flow)
{
- if (IS_ERR_OR_NULL(flows_dentry))
- return;
-
scnprintf(qp_flow->dentry_name, sizeof(qp_flow->dentry_name),
"%u", qp_flow->flow->flow_id);
qp_flow->dbgfs_dentry = debugfs_create_file(qp_flow->dentry_name,
@@ -156,11 +135,6 @@ void usnic_debugfs_flow_add(struct usnic_ib_qp_grp_flow *qp_flow)
flows_dentry,
qp_flow,
&flowinfo_ops);
- if (IS_ERR_OR_NULL(qp_flow->dbgfs_dentry)) {
- usnic_err("Failed to create dbg fs entry for flow %u with error %ld\n",
- qp_flow->flow->flow_id,
- PTR_ERR(qp_flow->dbgfs_dentry));
- }
}
void usnic_debugfs_flow_remove(struct usnic_ib_qp_grp_flow *qp_flow)
diff --git a/drivers/infiniband/hw/usnic/usnic_ib_main.c b/drivers/infiniband/hw/usnic/usnic_ib_main.c
index b2323a52a0dd..d88d9f8a7f9a 100644
--- a/drivers/infiniband/hw/usnic/usnic_ib_main.c
+++ b/drivers/infiniband/hw/usnic/usnic_ib_main.c
@@ -216,18 +216,17 @@ static int usnic_ib_netdevice_event(struct notifier_block *notifier,
unsigned long event, void *ptr)
{
struct usnic_ib_dev *us_ibdev;
+ struct ib_device *ibdev;
struct net_device *netdev = netdev_notifier_info_to_dev(ptr);
- mutex_lock(&usnic_ib_ibdev_list_lock);
- list_for_each_entry(us_ibdev, &usnic_ib_ibdev_list, ib_dev_link) {
- if (us_ibdev->netdev == netdev) {
- usnic_ib_handle_usdev_event(us_ibdev, event);
- break;
- }
- }
- mutex_unlock(&usnic_ib_ibdev_list_lock);
+ ibdev = ib_device_get_by_netdev(netdev, RDMA_DRIVER_USNIC);
+ if (!ibdev)
+ return NOTIFY_DONE;
+ us_ibdev = container_of(ibdev, struct usnic_ib_dev, ib_dev);
+ usnic_ib_handle_usdev_event(us_ibdev, event);
+ ib_device_put(ibdev);
return NOTIFY_DONE;
}
@@ -282,16 +281,15 @@ static int usnic_ib_inetaddr_event(struct notifier_block *notifier,
struct usnic_ib_dev *us_ibdev;
struct in_ifaddr *ifa = ptr;
struct net_device *netdev = ifa->ifa_dev->dev;
+ struct ib_device *ibdev;
- mutex_lock(&usnic_ib_ibdev_list_lock);
- list_for_each_entry(us_ibdev, &usnic_ib_ibdev_list, ib_dev_link) {
- if (us_ibdev->netdev == netdev) {
- usnic_ib_handle_inet_event(us_ibdev, event, ptr);
- break;
- }
- }
- mutex_unlock(&usnic_ib_ibdev_list_lock);
+ ibdev = ib_device_get_by_netdev(netdev, RDMA_DRIVER_USNIC);
+ if (!ibdev)
+ return NOTIFY_DONE;
+ us_ibdev = container_of(ibdev, struct usnic_ib_dev, ib_dev);
+ usnic_ib_handle_inet_event(us_ibdev, event, ptr);
+ ib_device_put(ibdev);
return NOTIFY_DONE;
}
static struct notifier_block usnic_ib_inetaddr_notifier = {
@@ -333,32 +331,26 @@ static void usnic_get_dev_fw_str(struct ib_device *device, char *str)
static const struct ib_device_ops usnic_dev_ops = {
.alloc_pd = usnic_ib_alloc_pd,
.alloc_ucontext = usnic_ib_alloc_ucontext,
- .create_ah = usnic_ib_create_ah,
.create_cq = usnic_ib_create_cq,
.create_qp = usnic_ib_create_qp,
.dealloc_pd = usnic_ib_dealloc_pd,
.dealloc_ucontext = usnic_ib_dealloc_ucontext,
.dereg_mr = usnic_ib_dereg_mr,
- .destroy_ah = usnic_ib_destroy_ah,
.destroy_cq = usnic_ib_destroy_cq,
.destroy_qp = usnic_ib_destroy_qp,
.get_dev_fw_str = usnic_get_dev_fw_str,
- .get_dma_mr = usnic_ib_get_dma_mr,
.get_link_layer = usnic_ib_port_link_layer,
- .get_netdev = usnic_get_netdev,
.get_port_immutable = usnic_port_immutable,
.mmap = usnic_ib_mmap,
.modify_qp = usnic_ib_modify_qp,
- .poll_cq = usnic_ib_poll_cq,
- .post_recv = usnic_ib_post_recv,
- .post_send = usnic_ib_post_send,
.query_device = usnic_ib_query_device,
.query_gid = usnic_ib_query_gid,
.query_pkey = usnic_ib_query_pkey,
.query_port = usnic_ib_query_port,
.query_qp = usnic_ib_query_qp,
.reg_user_mr = usnic_ib_reg_mr,
- .req_notify_cq = usnic_ib_req_notify_cq,
+ INIT_RDMA_OBJ_SIZE(ib_pd, usnic_ib_pd, ibpd),
+ INIT_RDMA_OBJ_SIZE(ib_ucontext, usnic_ib_ucontext, ibucontext),
};
/* Start of PF discovery section */
@@ -368,11 +360,12 @@ static void *usnic_ib_device_add(struct pci_dev *dev)
union ib_gid gid;
struct in_device *ind;
struct net_device *netdev;
+ int ret;
usnic_dbg("\n");
netdev = pci_get_drvdata(dev);
- us_ibdev = (struct usnic_ib_dev *)ib_alloc_device(sizeof(*us_ibdev));
+ us_ibdev = ib_alloc_device(usnic_ib_dev, ib_dev);
if (!us_ibdev) {
usnic_err("Device %s context alloc failed\n",
netdev_name(pci_get_drvdata(dev)));
@@ -422,7 +415,11 @@ static void *usnic_ib_device_add(struct pci_dev *dev)
us_ibdev->ib_dev.driver_id = RDMA_DRIVER_USNIC;
rdma_set_device_sysfs_group(&us_ibdev->ib_dev, &usnic_attr_group);
- if (ib_register_device(&us_ibdev->ib_dev, "usnic_%d", NULL))
+ ret = ib_device_set_netdev(&us_ibdev->ib_dev, us_ibdev->netdev, 1);
+ if (ret)
+ goto err_fwd_dealloc;
+
+ if (ib_register_device(&us_ibdev->ib_dev, "usnic_%d"))
goto err_fwd_dealloc;
usnic_fwd_set_mtu(us_ibdev->ufdev, us_ibdev->netdev->mtu);
@@ -477,15 +474,17 @@ static void usnic_ib_undiscover_pf(struct kref *kref)
&usnic_ib_ibdev_list, ib_dev_link) {
if (us_ibdev->pdev == dev) {
list_del(&us_ibdev->ib_dev_link);
- usnic_ib_device_remove(us_ibdev);
found = true;
break;
}
}
- WARN(!found, "Failed to remove PF %s\n", pci_name(dev));
mutex_unlock(&usnic_ib_ibdev_list_lock);
+ if (found)
+ usnic_ib_device_remove(us_ibdev);
+ else
+ WARN(1, "Failed to remove PF %s\n", pci_name(dev));
}
static struct usnic_ib_dev *usnic_ib_discover_pf(struct usnic_vnic *vnic)
@@ -691,7 +690,6 @@ out_unreg_netdev_notifier:
out_pci_unreg:
pci_unregister_driver(&usnic_ib_pci_driver);
out_umem_fini:
- usnic_uiom_fini();
return err;
}
@@ -704,7 +702,6 @@ static void __exit usnic_ib_destroy(void)
unregister_inetaddr_notifier(&usnic_ib_inetaddr_notifier);
unregister_netdevice_notifier(&usnic_ib_netdevice_notifier);
pci_unregister_driver(&usnic_ib_pci_driver);
- usnic_uiom_fini();
}
MODULE_DESCRIPTION("Cisco VIC (usNIC) Verbs Driver");
diff --git a/drivers/infiniband/hw/usnic/usnic_ib_sysfs.c b/drivers/infiniband/hw/usnic/usnic_ib_sysfs.c
index a7e4b2ccfaf8..c85d48ae7442 100644
--- a/drivers/infiniband/hw/usnic/usnic_ib_sysfs.c
+++ b/drivers/infiniband/hw/usnic/usnic_ib_sysfs.c
@@ -50,7 +50,7 @@ static ssize_t board_id_show(struct device *device,
struct device_attribute *attr, char *buf)
{
struct usnic_ib_dev *us_ibdev =
- container_of(device, struct usnic_ib_dev, ib_dev.dev);
+ rdma_device_to_drv_device(device, struct usnic_ib_dev, ib_dev);
unsigned short subsystem_device_id;
mutex_lock(&us_ibdev->usdev_lock);
@@ -67,14 +67,13 @@ static DEVICE_ATTR_RO(board_id);
static ssize_t
config_show(struct device *device, struct device_attribute *attr, char *buf)
{
- struct usnic_ib_dev *us_ibdev;
+ struct usnic_ib_dev *us_ibdev =
+ rdma_device_to_drv_device(device, struct usnic_ib_dev, ib_dev);
char *ptr;
unsigned left;
unsigned n;
enum usnic_vnic_res_type res_type;
- us_ibdev = container_of(device, struct usnic_ib_dev, ib_dev.dev);
-
/* Buffer space limit is 1 page */
ptr = buf;
left = PAGE_SIZE;
@@ -130,9 +129,8 @@ static DEVICE_ATTR_RO(config);
static ssize_t
iface_show(struct device *device, struct device_attribute *attr, char *buf)
{
- struct usnic_ib_dev *us_ibdev;
-
- us_ibdev = container_of(device, struct usnic_ib_dev, ib_dev.dev);
+ struct usnic_ib_dev *us_ibdev =
+ rdma_device_to_drv_device(device, struct usnic_ib_dev, ib_dev);
return scnprintf(buf, PAGE_SIZE, "%s\n",
netdev_name(us_ibdev->netdev));
@@ -142,9 +140,8 @@ static DEVICE_ATTR_RO(iface);
static ssize_t
max_vf_show(struct device *device, struct device_attribute *attr, char *buf)
{
- struct usnic_ib_dev *us_ibdev;
-
- us_ibdev = container_of(device, struct usnic_ib_dev, ib_dev.dev);
+ struct usnic_ib_dev *us_ibdev =
+ rdma_device_to_drv_device(device, struct usnic_ib_dev, ib_dev);
return scnprintf(buf, PAGE_SIZE, "%u\n",
kref_read(&us_ibdev->vf_cnt));
@@ -154,10 +151,10 @@ static DEVICE_ATTR_RO(max_vf);
static ssize_t
qp_per_vf_show(struct device *device, struct device_attribute *attr, char *buf)
{
- struct usnic_ib_dev *us_ibdev;
+ struct usnic_ib_dev *us_ibdev =
+ rdma_device_to_drv_device(device, struct usnic_ib_dev, ib_dev);
int qp_per_vf;
- us_ibdev = container_of(device, struct usnic_ib_dev, ib_dev.dev);
qp_per_vf = max(us_ibdev->vf_res_cnt[USNIC_VNIC_RES_TYPE_WQ],
us_ibdev->vf_res_cnt[USNIC_VNIC_RES_TYPE_RQ]);
@@ -169,9 +166,8 @@ static DEVICE_ATTR_RO(qp_per_vf);
static ssize_t
cq_per_vf_show(struct device *device, struct device_attribute *attr, char *buf)
{
- struct usnic_ib_dev *us_ibdev;
-
- us_ibdev = container_of(device, struct usnic_ib_dev, ib_dev.dev);
+ struct usnic_ib_dev *us_ibdev =
+ rdma_device_to_drv_device(device, struct usnic_ib_dev, ib_dev);
return scnprintf(buf, PAGE_SIZE, "%d\n",
us_ibdev->vf_res_cnt[USNIC_VNIC_RES_TYPE_CQ]);
diff --git a/drivers/infiniband/hw/usnic/usnic_ib_verbs.c b/drivers/infiniband/hw/usnic/usnic_ib_verbs.c
index 1d4abef17e38..bd4521b2cc5f 100644
--- a/drivers/infiniband/hw/usnic/usnic_ib_verbs.c
+++ b/drivers/infiniband/hw/usnic/usnic_ib_verbs.c
@@ -37,6 +37,7 @@
#include <rdma/ib_user_verbs.h>
#include <rdma/ib_addr.h>
+#include <rdma/uverbs_ioctl.h>
#include "usnic_abi.h"
#include "usnic_ib.h"
@@ -436,57 +437,33 @@ int usnic_ib_query_gid(struct ib_device *ibdev, u8 port, int index,
return 0;
}
-struct net_device *usnic_get_netdev(struct ib_device *device, u8 port_num)
-{
- struct usnic_ib_dev *us_ibdev = to_usdev(device);
-
- if (us_ibdev->netdev)
- dev_hold(us_ibdev->netdev);
-
- return us_ibdev->netdev;
-}
-
int usnic_ib_query_pkey(struct ib_device *ibdev, u8 port, u16 index,
u16 *pkey)
{
- if (index > 1)
+ if (index > 0)
return -EINVAL;
*pkey = 0xffff;
return 0;
}
-struct ib_pd *usnic_ib_alloc_pd(struct ib_device *ibdev,
- struct ib_ucontext *context,
- struct ib_udata *udata)
+int usnic_ib_alloc_pd(struct ib_pd *ibpd, struct ib_ucontext *context,
+ struct ib_udata *udata)
{
- struct usnic_ib_pd *pd;
+ struct usnic_ib_pd *pd = to_upd(ibpd);
void *umem_pd;
- usnic_dbg("\n");
-
- pd = kzalloc(sizeof(*pd), GFP_KERNEL);
- if (!pd)
- return ERR_PTR(-ENOMEM);
-
umem_pd = pd->umem_pd = usnic_uiom_alloc_pd();
if (IS_ERR_OR_NULL(umem_pd)) {
- kfree(pd);
- return ERR_PTR(umem_pd ? PTR_ERR(umem_pd) : -ENOMEM);
+ return umem_pd ? PTR_ERR(umem_pd) : -ENOMEM;
}
- usnic_info("domain 0x%p allocated for context 0x%p and device %s\n",
- pd, context, dev_name(&ibdev->dev));
- return &pd->ibpd;
+ return 0;
}
-int usnic_ib_dealloc_pd(struct ib_pd *pd)
+void usnic_ib_dealloc_pd(struct ib_pd *pd)
{
- usnic_info("freeing domain 0x%p\n", pd);
-
usnic_uiom_dealloc_pd((to_upd(pd))->umem_pd);
- kfree(pd);
- return 0;
}
struct ib_qp *usnic_ib_create_qp(struct ib_pd *pd,
@@ -496,7 +473,8 @@ struct ib_qp *usnic_ib_create_qp(struct ib_pd *pd,
int err;
struct usnic_ib_dev *us_ibdev;
struct usnic_ib_qp_grp *qp_grp;
- struct usnic_ib_ucontext *ucontext;
+ struct usnic_ib_ucontext *ucontext = rdma_udata_to_drv_context(
+ udata, struct usnic_ib_ucontext, ibucontext);
int cq_cnt;
struct usnic_vnic_res_spec res_spec;
struct usnic_ib_create_qp_cmd cmd;
@@ -504,7 +482,6 @@ struct ib_qp *usnic_ib_create_qp(struct ib_pd *pd,
usnic_dbg("\n");
- ucontext = to_uucontext(pd->uobject->context);
us_ibdev = to_usdev(pd->device);
if (init_attr->create_flags)
@@ -676,37 +653,31 @@ int usnic_ib_dereg_mr(struct ib_mr *ibmr)
return 0;
}
-struct ib_ucontext *usnic_ib_alloc_ucontext(struct ib_device *ibdev,
- struct ib_udata *udata)
+int usnic_ib_alloc_ucontext(struct ib_ucontext *uctx, struct ib_udata *udata)
{
- struct usnic_ib_ucontext *context;
+ struct ib_device *ibdev = uctx->device;
+ struct usnic_ib_ucontext *context = to_ucontext(uctx);
struct usnic_ib_dev *us_ibdev = to_usdev(ibdev);
usnic_dbg("\n");
- context = kmalloc(sizeof(*context), GFP_KERNEL);
- if (!context)
- return ERR_PTR(-ENOMEM);
-
INIT_LIST_HEAD(&context->qp_grp_list);
mutex_lock(&us_ibdev->usdev_lock);
list_add_tail(&context->link, &us_ibdev->ctx_list);
mutex_unlock(&us_ibdev->usdev_lock);
- return &context->ibucontext;
+ return 0;
}
-int usnic_ib_dealloc_ucontext(struct ib_ucontext *ibcontext)
+void usnic_ib_dealloc_ucontext(struct ib_ucontext *ibcontext)
{
struct usnic_ib_ucontext *context = to_uucontext(ibcontext);
struct usnic_ib_dev *us_ibdev = to_usdev(ibcontext->device);
usnic_dbg("\n");
mutex_lock(&us_ibdev->usdev_lock);
- BUG_ON(!list_empty(&context->qp_grp_list));
+ WARN_ON_ONCE(!list_empty(&context->qp_grp_list));
list_del(&context->link);
mutex_unlock(&us_ibdev->usdev_lock);
- kfree(context);
- return 0;
}
int usnic_ib_mmap(struct ib_ucontext *context,
@@ -760,57 +731,4 @@ int usnic_ib_mmap(struct ib_ucontext *context,
return -EINVAL;
}
-/* In ib callbacks section - Start of stub funcs */
-struct ib_ah *usnic_ib_create_ah(struct ib_pd *pd,
- struct rdma_ah_attr *ah_attr,
- u32 flags,
- struct ib_udata *udata)
-
-{
- usnic_dbg("\n");
- return ERR_PTR(-EPERM);
-}
-
-int usnic_ib_destroy_ah(struct ib_ah *ah, u32 flags)
-{
- usnic_dbg("\n");
- return -EINVAL;
-}
-
-int usnic_ib_post_send(struct ib_qp *ibqp, const struct ib_send_wr *wr,
- const struct ib_send_wr **bad_wr)
-{
- usnic_dbg("\n");
- return -EINVAL;
-}
-
-int usnic_ib_post_recv(struct ib_qp *ibqp, const struct ib_recv_wr *wr,
- const struct ib_recv_wr **bad_wr)
-{
- usnic_dbg("\n");
- return -EINVAL;
-}
-
-int usnic_ib_poll_cq(struct ib_cq *ibcq, int num_entries,
- struct ib_wc *wc)
-{
- usnic_dbg("\n");
- return -EINVAL;
-}
-
-int usnic_ib_req_notify_cq(struct ib_cq *cq,
- enum ib_cq_notify_flags flags)
-{
- usnic_dbg("\n");
- return -EINVAL;
-}
-
-struct ib_mr *usnic_ib_get_dma_mr(struct ib_pd *pd, int acc)
-{
- usnic_dbg("\n");
- return ERR_PTR(-ENOMEM);
-}
-
-
-/* In ib callbacks section - End of stub funcs */
/* End of ib callbacks section */
diff --git a/drivers/infiniband/hw/usnic/usnic_ib_verbs.h b/drivers/infiniband/hw/usnic/usnic_ib_verbs.h
index e33144261b9a..c40e89b6246f 100644
--- a/drivers/infiniband/hw/usnic/usnic_ib_verbs.h
+++ b/drivers/infiniband/hw/usnic/usnic_ib_verbs.h
@@ -48,13 +48,11 @@ int usnic_ib_query_qp(struct ib_qp *qp, struct ib_qp_attr *qp_attr,
struct ib_qp_init_attr *qp_init_attr);
int usnic_ib_query_gid(struct ib_device *ibdev, u8 port, int index,
union ib_gid *gid);
-struct net_device *usnic_get_netdev(struct ib_device *device, u8 port_num);
int usnic_ib_query_pkey(struct ib_device *ibdev, u8 port, u16 index,
u16 *pkey);
-struct ib_pd *usnic_ib_alloc_pd(struct ib_device *ibdev,
- struct ib_ucontext *context,
- struct ib_udata *udata);
-int usnic_ib_dealloc_pd(struct ib_pd *pd);
+int usnic_ib_alloc_pd(struct ib_pd *ibpd, struct ib_ucontext *context,
+ struct ib_udata *udata);
+void usnic_ib_dealloc_pd(struct ib_pd *pd);
struct ib_qp *usnic_ib_create_qp(struct ib_pd *pd,
struct ib_qp_init_attr *init_attr,
struct ib_udata *udata);
@@ -70,24 +68,8 @@ struct ib_mr *usnic_ib_reg_mr(struct ib_pd *pd, u64 start, u64 length,
u64 virt_addr, int access_flags,
struct ib_udata *udata);
int usnic_ib_dereg_mr(struct ib_mr *ibmr);
-struct ib_ucontext *usnic_ib_alloc_ucontext(struct ib_device *ibdev,
- struct ib_udata *udata);
-int usnic_ib_dealloc_ucontext(struct ib_ucontext *ibcontext);
+int usnic_ib_alloc_ucontext(struct ib_ucontext *uctx, struct ib_udata *udata);
+void usnic_ib_dealloc_ucontext(struct ib_ucontext *ibcontext);
int usnic_ib_mmap(struct ib_ucontext *context,
struct vm_area_struct *vma);
-struct ib_ah *usnic_ib_create_ah(struct ib_pd *pd,
- struct rdma_ah_attr *ah_attr,
- u32 flags,
- struct ib_udata *udata);
-
-int usnic_ib_destroy_ah(struct ib_ah *ah, u32 flags);
-int usnic_ib_post_send(struct ib_qp *ibqp, const struct ib_send_wr *wr,
- const struct ib_send_wr **bad_wr);
-int usnic_ib_post_recv(struct ib_qp *ibqp, const struct ib_recv_wr *wr,
- const struct ib_recv_wr **bad_wr);
-int usnic_ib_poll_cq(struct ib_cq *ibcq, int num_entries,
- struct ib_wc *wc);
-int usnic_ib_req_notify_cq(struct ib_cq *cq,
- enum ib_cq_notify_flags flags);
-struct ib_mr *usnic_ib_get_dma_mr(struct ib_pd *pd, int acc);
#endif /* !USNIC_IB_VERBS_H */
diff --git a/drivers/infiniband/hw/usnic/usnic_uiom.c b/drivers/infiniband/hw/usnic/usnic_uiom.c
index 49275a548751..06862a6af185 100644
--- a/drivers/infiniband/hw/usnic/usnic_uiom.c
+++ b/drivers/infiniband/hw/usnic/usnic_uiom.c
@@ -47,8 +47,6 @@
#include "usnic_uiom.h"
#include "usnic_uiom_interval_tree.h"
-static struct workqueue_struct *usnic_uiom_wq;
-
#define USNIC_UIOM_PAGE_CHUNK \
((PAGE_SIZE - offsetof(struct usnic_uiom_chunk, page_list)) /\
((void *) &((struct usnic_uiom_chunk *) 0)->page_list[1] - \
@@ -127,9 +125,9 @@ static int usnic_uiom_get_pages(unsigned long addr, size_t size, int writable,
npages = PAGE_ALIGN(size + (addr & ~PAGE_MASK)) >> PAGE_SHIFT;
uiomr->owning_mm = mm = current->mm;
- down_write(&mm->mmap_sem);
+ down_read(&mm->mmap_sem);
- locked = npages + current->mm->pinned_vm;
+ locked = atomic64_add_return(npages, &current->mm->pinned_vm);
lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT;
if ((locked > lock_limit) && !capable(CAP_IPC_LOCK)) {
@@ -157,9 +155,8 @@ static int usnic_uiom_get_pages(unsigned long addr, size_t size, int writable,
off = 0;
while (ret) {
- chunk = kmalloc(sizeof(*chunk) +
- sizeof(struct scatterlist) *
- min_t(int, ret, USNIC_UIOM_PAGE_CHUNK),
+ chunk = kmalloc(struct_size(chunk, page_list,
+ min_t(int, ret, USNIC_UIOM_PAGE_CHUNK)),
GFP_KERNEL);
if (!chunk) {
ret = -ENOMEM;
@@ -185,14 +182,13 @@ static int usnic_uiom_get_pages(unsigned long addr, size_t size, int writable,
}
out:
- if (ret < 0)
+ if (ret < 0) {
usnic_uiom_put_pages(chunk_list, 0);
- else {
- mm->pinned_vm = locked;
+ atomic64_sub(npages, &current->mm->pinned_vm);
+ } else
mmgrab(uiomr->owning_mm);
- }
- up_write(&mm->mmap_sem);
+ up_read(&mm->mmap_sem);
free_page((unsigned long) page_list);
return ret;
}
@@ -436,43 +432,12 @@ static inline size_t usnic_uiom_num_pages(struct usnic_uiom_reg *uiomr)
return PAGE_ALIGN(uiomr->length + uiomr->offset) >> PAGE_SHIFT;
}
-static void usnic_uiom_release_defer(struct work_struct *work)
-{
- struct usnic_uiom_reg *uiomr =
- container_of(work, struct usnic_uiom_reg, work);
-
- down_write(&uiomr->owning_mm->mmap_sem);
- uiomr->owning_mm->pinned_vm -= usnic_uiom_num_pages(uiomr);
- up_write(&uiomr->owning_mm->mmap_sem);
-
- __usnic_uiom_release_tail(uiomr);
-}
-
void usnic_uiom_reg_release(struct usnic_uiom_reg *uiomr,
struct ib_ucontext *context)
{
__usnic_uiom_reg_release(uiomr->pd, uiomr, 1);
- /*
- * We may be called with the mm's mmap_sem already held. This
- * can happen when a userspace munmap() is the call that drops
- * the last reference to our file and calls our release
- * method. If there are memory regions to destroy, we'll end
- * up here and not be able to take the mmap_sem. In that case
- * we defer the vm_locked accounting to a workqueue.
- */
- if (context->closing) {
- if (!down_write_trylock(&uiomr->owning_mm->mmap_sem)) {
- INIT_WORK(&uiomr->work, usnic_uiom_release_defer);
- queue_work(usnic_uiom_wq, &uiomr->work);
- return;
- }
- } else {
- down_write(&uiomr->owning_mm->mmap_sem);
- }
- uiomr->owning_mm->pinned_vm -= usnic_uiom_num_pages(uiomr);
- up_write(&uiomr->owning_mm->mmap_sem);
-
+ atomic64_sub(usnic_uiom_num_pages(uiomr), &uiomr->owning_mm->pinned_vm);
__usnic_uiom_release_tail(uiomr);
}
@@ -601,17 +566,5 @@ int usnic_uiom_init(char *drv_name)
return -EPERM;
}
- usnic_uiom_wq = create_workqueue(drv_name);
- if (!usnic_uiom_wq) {
- usnic_err("Unable to alloc wq for drv %s\n", drv_name);
- return -ENOMEM;
- }
-
return 0;
}
-
-void usnic_uiom_fini(void)
-{
- flush_workqueue(usnic_uiom_wq);
- destroy_workqueue(usnic_uiom_wq);
-}
diff --git a/drivers/infiniband/hw/usnic/usnic_uiom.h b/drivers/infiniband/hw/usnic/usnic_uiom.h
index b86a9731071b..c88cfa087e3a 100644
--- a/drivers/infiniband/hw/usnic/usnic_uiom.h
+++ b/drivers/infiniband/hw/usnic/usnic_uiom.h
@@ -93,5 +93,4 @@ struct usnic_uiom_reg *usnic_uiom_reg_get(struct usnic_uiom_pd *pd,
void usnic_uiom_reg_release(struct usnic_uiom_reg *uiomr,
struct ib_ucontext *ucontext);
int usnic_uiom_init(char *drv_name);
-void usnic_uiom_fini(void);
#endif /* USNIC_UIOM_H_ */
diff --git a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_cq.c b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_cq.c
index 0f004c737620..104c7db4704f 100644
--- a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_cq.c
+++ b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_cq.c
@@ -141,7 +141,7 @@ struct ib_cq *pvrdma_create_cq(struct ib_device *ibdev,
goto err_cq;
}
- cq->umem = ib_umem_get(context, ucmd.buf_addr, ucmd.buf_size,
+ cq->umem = ib_umem_get(udata, ucmd.buf_addr, ucmd.buf_size,
IB_ACCESS_LOCAL_WRITE, 1);
if (IS_ERR(cq->umem)) {
ret = PTR_ERR(cq->umem);
diff --git a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_dev_api.h b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_dev_api.h
index 6fd5a8f4e2f6..8f9749d54688 100644
--- a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_dev_api.h
+++ b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_dev_api.h
@@ -57,7 +57,8 @@
#define PVRDMA_ROCEV1_VERSION 17
#define PVRDMA_ROCEV2_VERSION 18
-#define PVRDMA_VERSION PVRDMA_ROCEV2_VERSION
+#define PVRDMA_PPN64_VERSION 19
+#define PVRDMA_VERSION PVRDMA_PPN64_VERSION
#define PVRDMA_BOARD_ID 1
#define PVRDMA_REV_ID 1
@@ -279,8 +280,10 @@ struct pvrdma_device_shared_region {
/* W: Async ring page info. */
struct pvrdma_ring_page_info cq_ring_pages;
/* W: CQ ring page info. */
- u32 uar_pfn; /* W: UAR pageframe. */
- u32 pad2; /* Pad to 8-byte align. */
+ union {
+ u32 uar_pfn; /* W: UAR pageframe. */
+ u64 uar_pfn64; /* W: 64-bit UAR page frame. */
+ };
struct pvrdma_device_caps caps; /* R: Device capabilities. */
};
@@ -411,8 +414,10 @@ struct pvrdma_cmd_query_pkey_resp {
struct pvrdma_cmd_create_uc {
struct pvrdma_cmd_hdr hdr;
- u32 pfn; /* UAR page frame number */
- u8 reserved[4];
+ union {
+ u32 pfn; /* UAR page frame number */
+ u64 pfn64; /* 64-bit UAR page frame number */
+ };
};
struct pvrdma_cmd_create_uc_resp {
diff --git a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_main.c b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_main.c
index 39c37b6fd715..6d8b3e0de57a 100644
--- a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_main.c
+++ b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_main.c
@@ -195,6 +195,8 @@ static const struct ib_device_ops pvrdma_dev_ops = {
.query_qp = pvrdma_query_qp,
.reg_user_mr = pvrdma_reg_user_mr,
.req_notify_cq = pvrdma_req_notify_cq,
+ INIT_RDMA_OBJ_SIZE(ib_pd, pvrdma_pd, ibpd),
+ INIT_RDMA_OBJ_SIZE(ib_ucontext, pvrdma_ucontext, ibucontext),
};
static const struct ib_device_ops pvrdma_dev_srq_ops = {
@@ -278,7 +280,7 @@ static int pvrdma_register_device(struct pvrdma_dev *dev)
spin_lock_init(&dev->srq_tbl_lock);
rdma_set_device_sysfs_group(&dev->ib_dev, &pvrdma_attr_group);
- ret = ib_register_device(&dev->ib_dev, "vmw_pvrdma%d", NULL);
+ ret = ib_register_device(&dev->ib_dev, "vmw_pvrdma%d");
if (ret)
goto err_srq_free;
@@ -795,7 +797,7 @@ static int pvrdma_pci_probe(struct pci_dev *pdev,
dev_dbg(&pdev->dev, "initializing driver %s\n", pci_name(pdev));
/* Allocate zero-out device */
- dev = (struct pvrdma_dev *)ib_alloc_device(sizeof(*dev));
+ dev = ib_alloc_device(pvrdma_dev, ib_dev);
if (!dev) {
dev_err(&pdev->dev, "failed to allocate IB device\n");
return -ENOMEM;
@@ -905,7 +907,11 @@ static int pvrdma_pci_probe(struct pci_dev *pdev,
PVRDMA_GOS_BITS_64;
dev->dsr->gos_info.gos_type = PVRDMA_GOS_TYPE_LINUX;
dev->dsr->gos_info.gos_ver = 1;
- dev->dsr->uar_pfn = dev->driver_uar.pfn;
+
+ if (dev->dsr_version < PVRDMA_PPN64_VERSION)
+ dev->dsr->uar_pfn = dev->driver_uar.pfn;
+ else
+ dev->dsr->uar_pfn64 = dev->driver_uar.pfn;
/* Command slot. */
dev->cmd_slot = dma_alloc_coherent(&pdev->dev, PAGE_SIZE,
diff --git a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_misc.c b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_misc.c
index fb0c5c0976b3..7944c58ded0e 100644
--- a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_misc.c
+++ b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_misc.c
@@ -183,25 +183,20 @@ int pvrdma_page_dir_insert_umem(struct pvrdma_page_dir *pdir,
struct ib_umem *umem, u64 offset)
{
u64 i = offset;
- int j, entry;
- int ret = 0, len = 0;
- struct scatterlist *sg;
+ int ret = 0;
+ struct sg_dma_page_iter sg_iter;
if (offset >= pdir->npages)
return -EINVAL;
- for_each_sg(umem->sg_head.sgl, sg, umem->nmap, entry) {
- len = sg_dma_len(sg) >> PAGE_SHIFT;
- for (j = 0; j < len; j++) {
- dma_addr_t addr = sg_dma_address(sg) +
- (j << umem->page_shift);
+ for_each_sg_dma_page(umem->sg_head.sgl, &sg_iter, umem->nmap, 0) {
+ dma_addr_t addr = sg_page_iter_dma_address(&sg_iter);
- ret = pvrdma_page_dir_insert_dma(pdir, i, addr);
- if (ret)
- goto exit;
+ ret = pvrdma_page_dir_insert_dma(pdir, i, addr);
+ if (ret)
+ goto exit;
- i++;
- }
+ i++;
}
exit:
diff --git a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_mr.c b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_mr.c
index fa96fa4fb829..a85884e90e84 100644
--- a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_mr.c
+++ b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_mr.c
@@ -126,8 +126,7 @@ struct ib_mr *pvrdma_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
return ERR_PTR(-EINVAL);
}
- umem = ib_umem_get(pd->uobject->context, start,
- length, access_flags, 0);
+ umem = ib_umem_get(udata, start, length, access_flags, 0);
if (IS_ERR(umem)) {
dev_warn(&dev->pdev->dev,
"could not get umem for mem region\n");
diff --git a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_qp.c b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_qp.c
index 1ec3646087ba..08f4257169bd 100644
--- a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_qp.c
+++ b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_qp.c
@@ -262,8 +262,7 @@ struct ib_qp *pvrdma_create_qp(struct ib_pd *pd,
if (!is_srq) {
/* set qp->sq.wqe_cnt, shift, buf_size.. */
- qp->rumem = ib_umem_get(pd->uobject->context,
- ucmd.rbuf_addr,
+ qp->rumem = ib_umem_get(udata, ucmd.rbuf_addr,
ucmd.rbuf_size, 0, 0);
if (IS_ERR(qp->rumem)) {
ret = PTR_ERR(qp->rumem);
@@ -275,8 +274,7 @@ struct ib_qp *pvrdma_create_qp(struct ib_pd *pd,
qp->srq = to_vsrq(init_attr->srq);
}
- qp->sumem = ib_umem_get(pd->uobject->context,
- ucmd.sbuf_addr,
+ qp->sumem = ib_umem_get(udata, ucmd.sbuf_addr,
ucmd.sbuf_size, 0, 0);
if (IS_ERR(qp->sumem)) {
if (!is_srq)
diff --git a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_srq.c b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_srq.c
index 06ba7c7a2235..951d9d68107a 100644
--- a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_srq.c
+++ b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_srq.c
@@ -153,9 +153,7 @@ struct ib_srq *pvrdma_create_srq(struct ib_pd *pd,
goto err_srq;
}
- srq->umem = ib_umem_get(pd->uobject->context,
- ucmd.buf_addr,
- ucmd.buf_size, 0, 0);
+ srq->umem = ib_umem_get(udata, ucmd.buf_addr, ucmd.buf_size, 0, 0);
if (IS_ERR(srq->umem)) {
ret = PTR_ERR(srq->umem);
goto err_srq;
diff --git a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.c b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.c
index 4d238d0e484b..42fe821f8d58 100644
--- a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.c
+++ b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.c
@@ -306,47 +306,42 @@ out:
/**
* pvrdma_alloc_ucontext - allocate ucontext
- * @ibdev: the IB device
+ * @uctx: the uverbs countext
* @udata: user data
*
- * @return: the ib_ucontext pointer on success, otherwise errno.
+ * @return: zero on success, otherwise errno.
*/
-struct ib_ucontext *pvrdma_alloc_ucontext(struct ib_device *ibdev,
- struct ib_udata *udata)
+int pvrdma_alloc_ucontext(struct ib_ucontext *uctx, struct ib_udata *udata)
{
+ struct ib_device *ibdev = uctx->device;
struct pvrdma_dev *vdev = to_vdev(ibdev);
- struct pvrdma_ucontext *context;
- union pvrdma_cmd_req req;
- union pvrdma_cmd_resp rsp;
+ struct pvrdma_ucontext *context = to_vucontext(uctx);
+ union pvrdma_cmd_req req = {};
+ union pvrdma_cmd_resp rsp = {};
struct pvrdma_cmd_create_uc *cmd = &req.create_uc;
struct pvrdma_cmd_create_uc_resp *resp = &rsp.create_uc_resp;
- struct pvrdma_alloc_ucontext_resp uresp = {0};
+ struct pvrdma_alloc_ucontext_resp uresp = {};
int ret;
- void *ptr;
if (!vdev->ib_active)
- return ERR_PTR(-EAGAIN);
-
- context = kmalloc(sizeof(*context), GFP_KERNEL);
- if (!context)
- return ERR_PTR(-ENOMEM);
+ return -EAGAIN;
context->dev = vdev;
ret = pvrdma_uar_alloc(vdev, &context->uar);
- if (ret) {
- kfree(context);
- return ERR_PTR(-ENOMEM);
- }
+ if (ret)
+ return -ENOMEM;
/* get ctx_handle from host */
- memset(cmd, 0, sizeof(*cmd));
- cmd->pfn = context->uar.pfn;
+ if (vdev->dsr_version < PVRDMA_PPN64_VERSION)
+ cmd->pfn = context->uar.pfn;
+ else
+ cmd->pfn64 = context->uar.pfn;
+
cmd->hdr.cmd = PVRDMA_CMD_CREATE_UC;
ret = pvrdma_cmd_post(vdev, &req, &rsp, PVRDMA_CMD_CREATE_UC_RESP);
if (ret < 0) {
dev_warn(&vdev->pdev->dev,
"could not create ucontext, error: %d\n", ret);
- ptr = ERR_PTR(ret);
goto err;
}
@@ -357,33 +352,28 @@ struct ib_ucontext *pvrdma_alloc_ucontext(struct ib_device *ibdev,
ret = ib_copy_to_udata(udata, &uresp, sizeof(uresp));
if (ret) {
pvrdma_uar_free(vdev, &context->uar);
- context->ibucontext.device = ibdev;
pvrdma_dealloc_ucontext(&context->ibucontext);
- return ERR_PTR(-EFAULT);
+ return -EFAULT;
}
- return &context->ibucontext;
+ return 0;
err:
pvrdma_uar_free(vdev, &context->uar);
- kfree(context);
- return ptr;
+ return ret;
}
/**
* pvrdma_dealloc_ucontext - deallocate ucontext
* @ibcontext: the ucontext
- *
- * @return: 0 on success, otherwise errno.
*/
-int pvrdma_dealloc_ucontext(struct ib_ucontext *ibcontext)
+void pvrdma_dealloc_ucontext(struct ib_ucontext *ibcontext)
{
struct pvrdma_ucontext *context = to_vucontext(ibcontext);
- union pvrdma_cmd_req req;
+ union pvrdma_cmd_req req = {};
struct pvrdma_cmd_destroy_uc *cmd = &req.destroy_uc;
int ret;
- memset(cmd, 0, sizeof(*cmd));
cmd->hdr.cmd = PVRDMA_CMD_DESTROY_UC;
cmd->ctx_handle = context->ctx_handle;
@@ -394,9 +384,6 @@ int pvrdma_dealloc_ucontext(struct ib_ucontext *ibcontext)
/* Free the UAR even if the device command failed */
pvrdma_uar_free(to_vdev(ibcontext->device), &context->uar);
- kfree(context);
-
- return ret;
}
/**
@@ -433,37 +420,29 @@ int pvrdma_mmap(struct ib_ucontext *ibcontext, struct vm_area_struct *vma)
/**
* pvrdma_alloc_pd - allocate protection domain
- * @ibdev: the IB device
+ * @ibpd: PD pointer
* @context: user context
* @udata: user data
*
* @return: the ib_pd protection domain pointer on success, otherwise errno.
*/
-struct ib_pd *pvrdma_alloc_pd(struct ib_device *ibdev,
- struct ib_ucontext *context,
- struct ib_udata *udata)
+int pvrdma_alloc_pd(struct ib_pd *ibpd, struct ib_ucontext *context,
+ struct ib_udata *udata)
{
- struct pvrdma_pd *pd;
+ struct ib_device *ibdev = ibpd->device;
+ struct pvrdma_pd *pd = to_vpd(ibpd);
struct pvrdma_dev *dev = to_vdev(ibdev);
- union pvrdma_cmd_req req;
- union pvrdma_cmd_resp rsp;
+ union pvrdma_cmd_req req = {};
+ union pvrdma_cmd_resp rsp = {};
struct pvrdma_cmd_create_pd *cmd = &req.create_pd;
struct pvrdma_cmd_create_pd_resp *resp = &rsp.create_pd_resp;
struct pvrdma_alloc_pd_resp pd_resp = {0};
int ret;
- void *ptr;
/* Check allowed max pds */
if (!atomic_add_unless(&dev->num_pds, 1, dev->dsr->caps.max_pd))
- return ERR_PTR(-ENOMEM);
-
- pd = kmalloc(sizeof(*pd), GFP_KERNEL);
- if (!pd) {
- ptr = ERR_PTR(-ENOMEM);
- goto err;
- }
+ return -ENOMEM;
- memset(cmd, 0, sizeof(*cmd));
cmd->hdr.cmd = PVRDMA_CMD_CREATE_PD;
cmd->ctx_handle = (context) ? to_vucontext(context)->ctx_handle : 0;
ret = pvrdma_cmd_post(dev, &req, &rsp, PVRDMA_CMD_CREATE_PD_RESP);
@@ -471,8 +450,7 @@ struct ib_pd *pvrdma_alloc_pd(struct ib_device *ibdev,
dev_warn(&dev->pdev->dev,
"failed to allocate protection domain, error: %d\n",
ret);
- ptr = ERR_PTR(ret);
- goto freepd;
+ goto err;
}
pd->privileged = !context;
@@ -485,18 +463,16 @@ struct ib_pd *pvrdma_alloc_pd(struct ib_device *ibdev,
dev_warn(&dev->pdev->dev,
"failed to copy back protection domain\n");
pvrdma_dealloc_pd(&pd->ibpd);
- return ERR_PTR(-EFAULT);
+ return -EFAULT;
}
}
/* u32 pd handle */
- return &pd->ibpd;
+ return 0;
-freepd:
- kfree(pd);
err:
atomic_dec(&dev->num_pds);
- return ptr;
+ return ret;
}
/**
@@ -505,14 +481,13 @@ err:
*
* @return: 0 on success, otherwise errno.
*/
-int pvrdma_dealloc_pd(struct ib_pd *pd)
+void pvrdma_dealloc_pd(struct ib_pd *pd)
{
struct pvrdma_dev *dev = to_vdev(pd->device);
- union pvrdma_cmd_req req;
+ union pvrdma_cmd_req req = {};
struct pvrdma_cmd_destroy_pd *cmd = &req.destroy_pd;
int ret;
- memset(cmd, 0, sizeof(*cmd));
cmd->hdr.cmd = PVRDMA_CMD_DESTROY_PD;
cmd->pd_handle = to_vpd(pd)->pd_handle;
@@ -522,10 +497,7 @@ int pvrdma_dealloc_pd(struct ib_pd *pd)
"could not dealloc protection domain, error: %d\n",
ret);
- kfree(to_vpd(pd));
atomic_dec(&dev->num_pds);
-
- return 0;
}
/**
diff --git a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.h b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.h
index f7f758d60110..607aa131d67c 100644
--- a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.h
+++ b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.h
@@ -396,13 +396,11 @@ int pvrdma_modify_device(struct ib_device *ibdev, int mask,
int pvrdma_modify_port(struct ib_device *ibdev, u8 port,
int mask, struct ib_port_modify *props);
int pvrdma_mmap(struct ib_ucontext *context, struct vm_area_struct *vma);
-struct ib_ucontext *pvrdma_alloc_ucontext(struct ib_device *ibdev,
- struct ib_udata *udata);
-int pvrdma_dealloc_ucontext(struct ib_ucontext *context);
-struct ib_pd *pvrdma_alloc_pd(struct ib_device *ibdev,
- struct ib_ucontext *context,
- struct ib_udata *udata);
-int pvrdma_dealloc_pd(struct ib_pd *ibpd);
+int pvrdma_alloc_ucontext(struct ib_ucontext *uctx, struct ib_udata *udata);
+void pvrdma_dealloc_ucontext(struct ib_ucontext *context);
+int pvrdma_alloc_pd(struct ib_pd *pd, struct ib_ucontext *context,
+ struct ib_udata *udata);
+void pvrdma_dealloc_pd(struct ib_pd *ibpd);
struct ib_mr *pvrdma_get_dma_mr(struct ib_pd *pd, int acc);
struct ib_mr *pvrdma_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
u64 virt_addr, int access_flags,
diff --git a/drivers/infiniband/sw/rdmavt/mr.c b/drivers/infiniband/sw/rdmavt/mr.c
index 49c9541050d4..728795043496 100644
--- a/drivers/infiniband/sw/rdmavt/mr.c
+++ b/drivers/infiniband/sw/rdmavt/mr.c
@@ -381,15 +381,14 @@ struct ib_mr *rvt_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
{
struct rvt_mr *mr;
struct ib_umem *umem;
- struct scatterlist *sg;
- int n, m, entry;
+ struct sg_page_iter sg_iter;
+ int n, m;
struct ib_mr *ret;
if (length == 0)
return ERR_PTR(-EINVAL);
- umem = ib_umem_get(pd->uobject->context, start, length,
- mr_access_flags, 0);
+ umem = ib_umem_get(udata, start, length, mr_access_flags, 0);
if (IS_ERR(umem))
return (void *)umem;
@@ -408,23 +407,21 @@ struct ib_mr *rvt_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
mr->mr.access_flags = mr_access_flags;
mr->umem = umem;
- mr->mr.page_shift = umem->page_shift;
+ mr->mr.page_shift = PAGE_SHIFT;
m = 0;
n = 0;
- for_each_sg(umem->sg_head.sgl, sg, umem->nmap, entry) {
+ for_each_sg_page (umem->sg_head.sgl, &sg_iter, umem->nmap, 0) {
void *vaddr;
- vaddr = page_address(sg_page(sg));
+ vaddr = page_address(sg_page_iter_page(&sg_iter));
if (!vaddr) {
ret = ERR_PTR(-EINVAL);
goto bail_inval;
}
mr->mr.map[m]->segs[n].vaddr = vaddr;
- mr->mr.map[m]->segs[n].length = BIT(umem->page_shift);
- trace_rvt_mr_user_seg(&mr->mr, m, n, vaddr,
- BIT(umem->page_shift));
- n++;
- if (n == RVT_SEGSZ) {
+ mr->mr.map[m]->segs[n].length = PAGE_SIZE;
+ trace_rvt_mr_user_seg(&mr->mr, m, n, vaddr, PAGE_SIZE);
+ if (++n == RVT_SEGSZ) {
m++;
n = 0;
}
diff --git a/drivers/infiniband/sw/rdmavt/pd.c b/drivers/infiniband/sw/rdmavt/pd.c
index 8a89afff3363..6033054b22fa 100644
--- a/drivers/infiniband/sw/rdmavt/pd.c
+++ b/drivers/infiniband/sw/rdmavt/pd.c
@@ -50,7 +50,7 @@
/**
* rvt_alloc_pd - allocate a protection domain
- * @ibdev: ib device
+ * @ibpd: PD
* @context: optional user context
* @udata: optional user data
*
@@ -58,19 +58,14 @@
*
* Return: 0 on success
*/
-struct ib_pd *rvt_alloc_pd(struct ib_device *ibdev,
- struct ib_ucontext *context,
- struct ib_udata *udata)
+int rvt_alloc_pd(struct ib_pd *ibpd, struct ib_ucontext *context,
+ struct ib_udata *udata)
{
+ struct ib_device *ibdev = ibpd->device;
struct rvt_dev_info *dev = ib_to_rvt(ibdev);
- struct rvt_pd *pd;
- struct ib_pd *ret;
+ struct rvt_pd *pd = ibpd_to_rvtpd(ibpd);
+ int ret = 0;
- pd = kmalloc(sizeof(*pd), GFP_KERNEL);
- if (!pd) {
- ret = ERR_PTR(-ENOMEM);
- goto bail;
- }
/*
* While we could continue allocating protecetion domains, being
* constrained only by system resources. The IBTA spec defines that
@@ -81,8 +76,7 @@ struct ib_pd *rvt_alloc_pd(struct ib_device *ibdev,
spin_lock(&dev->n_pds_lock);
if (dev->n_pds_allocated == dev->dparms.props.max_pd) {
spin_unlock(&dev->n_pds_lock);
- kfree(pd);
- ret = ERR_PTR(-ENOMEM);
+ ret = -ENOMEM;
goto bail;
}
@@ -92,8 +86,6 @@ struct ib_pd *rvt_alloc_pd(struct ib_device *ibdev,
/* ib_alloc_pd() will initialize pd->ibpd. */
pd->user = !!udata;
- ret = &pd->ibpd;
-
bail:
return ret;
}
@@ -104,16 +96,11 @@ bail:
*
* Return: always 0
*/
-int rvt_dealloc_pd(struct ib_pd *ibpd)
+void rvt_dealloc_pd(struct ib_pd *ibpd)
{
- struct rvt_pd *pd = ibpd_to_rvtpd(ibpd);
struct rvt_dev_info *dev = ib_to_rvt(ibpd->device);
spin_lock(&dev->n_pds_lock);
dev->n_pds_allocated--;
spin_unlock(&dev->n_pds_lock);
-
- kfree(pd);
-
- return 0;
}
diff --git a/drivers/infiniband/sw/rdmavt/pd.h b/drivers/infiniband/sw/rdmavt/pd.h
index 1892ca4a9746..7a887e4a45e7 100644
--- a/drivers/infiniband/sw/rdmavt/pd.h
+++ b/drivers/infiniband/sw/rdmavt/pd.h
@@ -50,9 +50,8 @@
#include <rdma/rdma_vt.h>
-struct ib_pd *rvt_alloc_pd(struct ib_device *ibdev,
- struct ib_ucontext *context,
- struct ib_udata *udata);
-int rvt_dealloc_pd(struct ib_pd *ibpd);
+int rvt_alloc_pd(struct ib_pd *pd, struct ib_ucontext *context,
+ struct ib_udata *udata);
+void rvt_dealloc_pd(struct ib_pd *ibpd);
#endif /* DEF_RDMAVTPD_H */
diff --git a/drivers/infiniband/sw/rdmavt/qp.c b/drivers/infiniband/sw/rdmavt/qp.c
index c6cc3e4ab71d..a34b9a2a32b6 100644
--- a/drivers/infiniband/sw/rdmavt/qp.c
+++ b/drivers/infiniband/sw/rdmavt/qp.c
@@ -53,6 +53,7 @@
#include <rdma/ib_verbs.h>
#include <rdma/ib_hdrs.h>
#include <rdma/opa_addr.h>
+#include <rdma/uverbs_ioctl.h>
#include "qp.h"
#include "vt.h"
#include "trace.h"
@@ -854,6 +855,7 @@ static void rvt_init_qp(struct rvt_dev_info *rdi, struct rvt_qp *qp,
qp->s_mig_state = IB_MIG_MIGRATED;
qp->r_head_ack_queue = 0;
qp->s_tail_ack_queue = 0;
+ qp->s_acked_ack_queue = 0;
qp->s_num_rd_atomic = 0;
if (qp->r_rq.wq) {
qp->r_rq.wq->head = 0;
@@ -955,6 +957,8 @@ struct ib_qp *rvt_create_qp(struct ib_pd *ibpd,
size_t sg_list_sz;
struct ib_qp *ret = ERR_PTR(-ENOMEM);
struct rvt_dev_info *rdi = ib_to_rvt(ibpd->device);
+ struct rvt_ucontext *ucontext = rdma_udata_to_drv_context(
+ udata, struct rvt_ucontext, ibucontext);
void *priv = NULL;
size_t sqsize;
@@ -1128,7 +1132,7 @@ struct ib_qp *rvt_create_qp(struct ib_pd *ibpd,
u32 s = sizeof(struct rvt_rwq) + qp->r_rq.size * sz;
qp->ip = rvt_create_mmap_info(rdi, s,
- ibpd->uobject->context,
+ &ucontext->ibucontext,
qp->r_rq.wq);
if (!qp->ip) {
ret = ERR_PTR(-ENOMEM);
@@ -1642,11 +1646,11 @@ int rvt_destroy_qp(struct ib_qp *ibqp)
kref_put(&qp->ip->ref, rvt_release_mmap_info);
else
vfree(qp->r_rq.wq);
- vfree(qp->s_wq);
rdi->driver_f.qp_priv_free(rdi, qp);
kfree(qp->s_ack_queue);
rdma_destroy_ah_attr(&qp->remote_ah_attr);
rdma_destroy_ah_attr(&qp->alt_ah_attr);
+ vfree(qp->s_wq);
kfree(qp);
return 0;
}
@@ -2393,11 +2397,12 @@ static inline unsigned long rvt_aeth_to_usec(u32 aeth)
}
/*
- * rvt_add_retry_timer - add/start a retry timer
+ * rvt_add_retry_timer_ext - add/start a retry timer
* @qp - the QP
+ * @shift - timeout shift to wait for multiple packets
* add a retry timer on the QP
*/
-void rvt_add_retry_timer(struct rvt_qp *qp)
+void rvt_add_retry_timer_ext(struct rvt_qp *qp, u8 shift)
{
struct ib_qp *ibqp = &qp->ibqp;
struct rvt_dev_info *rdi = ib_to_rvt(ibqp->device);
@@ -2405,11 +2410,11 @@ void rvt_add_retry_timer(struct rvt_qp *qp)
lockdep_assert_held(&qp->s_lock);
qp->s_flags |= RVT_S_TIMER;
/* 4.096 usec. * (1 << qp->timeout) */
- qp->s_timer.expires = jiffies + qp->timeout_jiffies +
- rdi->busy_jiffies;
+ qp->s_timer.expires = jiffies + rdi->busy_jiffies +
+ (qp->timeout_jiffies << shift);
add_timer(&qp->s_timer);
}
-EXPORT_SYMBOL(rvt_add_retry_timer);
+EXPORT_SYMBOL(rvt_add_retry_timer_ext);
/**
* rvt_add_rnr_timer - add/start an rnr timer
@@ -2785,6 +2790,18 @@ again:
}
EXPORT_SYMBOL(rvt_copy_sge);
+static enum ib_wc_status loopback_qp_drop(struct rvt_ibport *rvp,
+ struct rvt_qp *sqp)
+{
+ rvp->n_pkt_drops++;
+ /*
+ * For RC, the requester would timeout and retry so
+ * shortcut the timeouts and just signal too many retries.
+ */
+ return sqp->ibqp.qp_type == IB_QPT_RC ?
+ IB_WC_RETRY_EXC_ERR : IB_WC_SUCCESS;
+}
+
/**
* ruc_loopback - handle UC and RC loopback requests
* @sqp: the sending QP
@@ -2857,17 +2874,14 @@ again:
}
spin_unlock_irqrestore(&sqp->s_lock, flags);
- if (!qp || !(ib_rvt_state_ops[qp->state] & RVT_PROCESS_RECV_OK) ||
+ if (!qp) {
+ send_status = loopback_qp_drop(rvp, sqp);
+ goto serr_no_r_lock;
+ }
+ spin_lock_irqsave(&qp->r_lock, flags);
+ if (!(ib_rvt_state_ops[qp->state] & RVT_PROCESS_RECV_OK) ||
qp->ibqp.qp_type != sqp->ibqp.qp_type) {
- rvp->n_pkt_drops++;
- /*
- * For RC, the requester would timeout and retry so
- * shortcut the timeouts and just signal too many retries.
- */
- if (sqp->ibqp.qp_type == IB_QPT_RC)
- send_status = IB_WC_RETRY_EXC_ERR;
- else
- send_status = IB_WC_SUCCESS;
+ send_status = loopback_qp_drop(rvp, sqp);
goto serr;
}
@@ -2893,18 +2907,8 @@ again:
goto send_comp;
case IB_WR_SEND_WITH_INV:
- if (!rvt_invalidate_rkey(qp, wqe->wr.ex.invalidate_rkey)) {
- wc.wc_flags = IB_WC_WITH_INVALIDATE;
- wc.ex.invalidate_rkey = wqe->wr.ex.invalidate_rkey;
- }
- goto send;
-
case IB_WR_SEND_WITH_IMM:
- wc.wc_flags = IB_WC_WITH_IMM;
- wc.ex.imm_data = wqe->wr.ex.imm_data;
- /* FALLTHROUGH */
case IB_WR_SEND:
-send:
ret = rvt_get_rwqe(qp, false);
if (ret < 0)
goto op_err;
@@ -2912,6 +2916,22 @@ send:
goto rnr_nak;
if (wqe->length > qp->r_len)
goto inv_err;
+ switch (wqe->wr.opcode) {
+ case IB_WR_SEND_WITH_INV:
+ if (!rvt_invalidate_rkey(qp,
+ wqe->wr.ex.invalidate_rkey)) {
+ wc.wc_flags = IB_WC_WITH_INVALIDATE;
+ wc.ex.invalidate_rkey =
+ wqe->wr.ex.invalidate_rkey;
+ }
+ break;
+ case IB_WR_SEND_WITH_IMM:
+ wc.wc_flags = IB_WC_WITH_IMM;
+ wc.ex.imm_data = wqe->wr.ex.imm_data;
+ break;
+ default:
+ break;
+ }
break;
case IB_WR_RDMA_WRITE_WITH_IMM:
@@ -2988,34 +3008,12 @@ do_write:
sge = &sqp->s_sge.sge;
while (sqp->s_len) {
- u32 len = sqp->s_len;
+ u32 len = rvt_get_sge_length(sge, sqp->s_len);
- if (len > sge->length)
- len = sge->length;
- if (len > sge->sge_length)
- len = sge->sge_length;
WARN_ON_ONCE(len == 0);
rvt_copy_sge(qp, &qp->r_sge, sge->vaddr,
len, release, copy_last);
- sge->vaddr += len;
- sge->length -= len;
- sge->sge_length -= len;
- if (sge->sge_length == 0) {
- if (!release)
- rvt_put_mr(sge->mr);
- if (--sqp->s_sge.num_sge)
- *sge = *sqp->s_sge.sg_list++;
- } else if (sge->length == 0 && sge->mr->lkey) {
- if (++sge->n >= RVT_SEGSZ) {
- if (++sge->m >= sge->mr->mapsz)
- break;
- sge->n = 0;
- }
- sge->vaddr =
- sge->mr->map[sge->m]->segs[sge->n].vaddr;
- sge->length =
- sge->mr->map[sge->m]->segs[sge->n].length;
- }
+ rvt_update_sge(&sqp->s_sge, len, !release);
sqp->s_len -= len;
}
if (release)
@@ -3041,6 +3039,7 @@ do_write:
wqe->wr.send_flags & IB_SEND_SOLICITED);
send_comp:
+ spin_unlock_irqrestore(&qp->r_lock, flags);
spin_lock_irqsave(&sqp->s_lock, flags);
rvp->n_loop_pkts++;
flush_send:
@@ -3067,6 +3066,7 @@ rnr_nak:
}
if (sqp->s_rnr_retry_cnt < 7)
sqp->s_rnr_retry--;
+ spin_unlock_irqrestore(&qp->r_lock, flags);
spin_lock_irqsave(&sqp->s_lock, flags);
if (!(ib_rvt_state_ops[sqp->state] & RVT_PROCESS_RECV_OK))
goto clr_busy;
@@ -3095,6 +3095,8 @@ err:
rvt_rc_error(qp, wc.status);
serr:
+ spin_unlock_irqrestore(&qp->r_lock, flags);
+serr_no_r_lock:
spin_lock_irqsave(&sqp->s_lock, flags);
rvt_send_complete(sqp, wqe, send_status);
if (sqp->ibqp.qp_type == IB_QPT_RC) {
diff --git a/drivers/infiniband/sw/rdmavt/rc.c b/drivers/infiniband/sw/rdmavt/rc.c
index 6131cc558bdb..8d71647820a8 100644
--- a/drivers/infiniband/sw/rdmavt/rc.c
+++ b/drivers/infiniband/sw/rdmavt/rc.c
@@ -187,3 +187,16 @@ void rvt_get_credit(struct rvt_qp *qp, u32 aeth)
}
}
EXPORT_SYMBOL(rvt_get_credit);
+
+/* rvt_restart_sge - rewind the sge state for a wqe */
+u32 rvt_restart_sge(struct rvt_sge_state *ss, struct rvt_swqe *wqe, u32 len)
+{
+ ss->sge = wqe->sg_list[0];
+ ss->sg_list = wqe->sg_list + 1;
+ ss->num_sge = wqe->wr.num_sge;
+ ss->total_len = wqe->length;
+ rvt_skip_sge(ss, len, false);
+ return wqe->length - len;
+}
+EXPORT_SYMBOL(rvt_restart_sge);
+
diff --git a/drivers/infiniband/sw/rdmavt/srq.c b/drivers/infiniband/sw/rdmavt/srq.c
index 78e06fc456c5..895b3fabd0bf 100644
--- a/drivers/infiniband/sw/rdmavt/srq.c
+++ b/drivers/infiniband/sw/rdmavt/srq.c
@@ -48,6 +48,7 @@
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
+#include <rdma/uverbs_ioctl.h>
#include "srq.h"
#include "vt.h"
@@ -77,6 +78,8 @@ struct ib_srq *rvt_create_srq(struct ib_pd *ibpd,
struct ib_udata *udata)
{
struct rvt_dev_info *dev = ib_to_rvt(ibpd->device);
+ struct rvt_ucontext *ucontext = rdma_udata_to_drv_context(
+ udata, struct rvt_ucontext, ibucontext);
struct rvt_srq *srq;
u32 sz;
struct ib_srq *ret;
@@ -119,7 +122,7 @@ struct ib_srq *rvt_create_srq(struct ib_pd *ibpd,
u32 s = sizeof(struct rvt_rwq) + srq->rq.size * sz;
srq->ip =
- rvt_create_mmap_info(dev, s, ibpd->uobject->context,
+ rvt_create_mmap_info(dev, s, &ucontext->ibucontext,
srq->rq.wq);
if (!srq->ip) {
ret = ERR_PTR(-ENOMEM);
diff --git a/drivers/infiniband/sw/rdmavt/trace_cq.h b/drivers/infiniband/sw/rdmavt/trace_cq.h
index df8e1adbef9d..e3c416c6f900 100644
--- a/drivers/infiniband/sw/rdmavt/trace_cq.h
+++ b/drivers/infiniband/sw/rdmavt/trace_cq.h
@@ -105,7 +105,7 @@ DEFINE_EVENT(rvt_cq_template, rvt_create_cq,
TP_ARGS(cq, attr));
#define CQ_PRN \
-"[%s] idx %u wr_id %llx status %u opcode %u,%s length %u qpn %x"
+"[%s] idx %u wr_id %llx status %u opcode %u,%s length %u qpn %x flags %x imm %x"
DECLARE_EVENT_CLASS(
rvt_cq_entry_template,
@@ -119,6 +119,8 @@ DECLARE_EVENT_CLASS(
__field(u32, qpn)
__field(u32, length)
__field(u32, idx)
+ __field(u32, flags)
+ __field(u32, imm)
),
TP_fast_assign(
RDI_DEV_ASSIGN(cq->rdi)
@@ -128,6 +130,8 @@ DECLARE_EVENT_CLASS(
__entry->length = wc->byte_len;
__entry->qpn = wc->qp->qp_num;
__entry->idx = idx;
+ __entry->flags = wc->wc_flags;
+ __entry->imm = be32_to_cpu(wc->ex.imm_data);
),
TP_printk(
CQ_PRN,
@@ -137,7 +141,9 @@ DECLARE_EVENT_CLASS(
__entry->status,
__entry->opcode, show_wc_opcode(__entry->opcode),
__entry->length,
- __entry->qpn
+ __entry->qpn,
+ __entry->flags,
+ __entry->imm
)
);
diff --git a/drivers/infiniband/sw/rdmavt/vt.c b/drivers/infiniband/sw/rdmavt/vt.c
index aef3aa3fe667..42c9d35f832d 100644
--- a/drivers/infiniband/sw/rdmavt/vt.c
+++ b/drivers/infiniband/sw/rdmavt/vt.c
@@ -91,7 +91,7 @@ struct rvt_dev_info *rvt_alloc_device(size_t size, int nports)
{
struct rvt_dev_info *rdi;
- rdi = (struct rvt_dev_info *)ib_alloc_device(size);
+ rdi = container_of(_ib_alloc_device(size), struct rvt_dev_info, ibdev);
if (!rdi)
return rdi;
@@ -284,10 +284,6 @@ static int rvt_query_gid(struct ib_device *ibdev, u8 port_num,
&gid->global.interface_id);
}
-struct rvt_ucontext {
- struct ib_ucontext ibucontext;
-};
-
static inline struct rvt_ucontext *to_iucontext(struct ib_ucontext
*ibucontext)
{
@@ -296,28 +292,21 @@ static inline struct rvt_ucontext *to_iucontext(struct ib_ucontext
/**
* rvt_alloc_ucontext - Allocate a user context
- * @ibdev: Verbs IB dev
+ * @uctx: Verbs context
* @udata: User data allocated
*/
-static struct ib_ucontext *rvt_alloc_ucontext(struct ib_device *ibdev,
- struct ib_udata *udata)
+static int rvt_alloc_ucontext(struct ib_ucontext *uctx, struct ib_udata *udata)
{
- struct rvt_ucontext *context;
-
- context = kmalloc(sizeof(*context), GFP_KERNEL);
- if (!context)
- return ERR_PTR(-ENOMEM);
- return &context->ibucontext;
+ return 0;
}
/**
- *rvt_dealloc_ucontext - Free a user context
- *@context - Free this
+ * rvt_dealloc_ucontext - Free a user context
+ * @context - Free this
*/
-static int rvt_dealloc_ucontext(struct ib_ucontext *context)
+static void rvt_dealloc_ucontext(struct ib_ucontext *context)
{
- kfree(to_iucontext(context));
- return 0;
+ return;
}
static int rvt_get_port_immutable(struct ib_device *ibdev, u8 port_num,
@@ -436,6 +425,8 @@ static const struct ib_device_ops rvt_dev_ops = {
.req_notify_cq = rvt_req_notify_cq,
.resize_cq = rvt_resize_cq,
.unmap_fmr = rvt_unmap_fmr,
+ INIT_RDMA_OBJ_SIZE(ib_pd, rvt_pd, ibpd),
+ INIT_RDMA_OBJ_SIZE(ib_ucontext, rvt_ucontext, ibucontext),
};
static noinline int check_support(struct rvt_dev_info *rdi, int verb)
@@ -446,7 +437,7 @@ static noinline int check_support(struct rvt_dev_info *rdi, int verb)
* These functions are not part of verbs specifically but are
* required for rdmavt to function.
*/
- if ((!rdi->driver_f.port_callback) ||
+ if ((!rdi->ibdev.ops.init_port) ||
(!rdi->driver_f.get_pci_dev))
return -EINVAL;
break;
@@ -644,8 +635,7 @@ int rvt_register_device(struct rvt_dev_info *rdi, u32 driver_id)
rdi->ibdev.driver_id = driver_id;
/* We are now good to announce we exist */
- ret = ib_register_device(&rdi->ibdev, dev_name(&rdi->ibdev.dev),
- rdi->driver_f.port_callback);
+ ret = ib_register_device(&rdi->ibdev, dev_name(&rdi->ibdev.dev));
if (ret) {
rvt_pr_err(rdi, "Failed to register driver with ib core.\n");
goto bail_wss;
diff --git a/drivers/infiniband/sw/rxe/rxe.c b/drivers/infiniband/sw/rxe/rxe.c
index 383e65c7bbc0..a8c11b5e1e94 100644
--- a/drivers/infiniband/sw/rxe/rxe.c
+++ b/drivers/infiniband/sw/rxe/rxe.c
@@ -31,6 +31,7 @@
* SOFTWARE.
*/
+#include <rdma/rdma_netlink.h>
#include <net/addrconf.h>
#include "rxe.h"
#include "rxe_loc.h"
@@ -50,8 +51,10 @@ static void rxe_cleanup_ports(struct rxe_dev *rxe)
/* free resources for a rxe device all objects created for this device must
* have been destroyed
*/
-static void rxe_cleanup(struct rxe_dev *rxe)
+void rxe_dealloc(struct ib_device *ib_dev)
{
+ struct rxe_dev *rxe = container_of(ib_dev, struct rxe_dev, ib_dev);
+
rxe_pool_cleanup(&rxe->uc_pool);
rxe_pool_cleanup(&rxe->pd_pool);
rxe_pool_cleanup(&rxe->ah_pool);
@@ -65,16 +68,8 @@ static void rxe_cleanup(struct rxe_dev *rxe)
rxe_cleanup_ports(rxe);
- crypto_free_shash(rxe->tfm);
-}
-
-/* called when all references have been dropped */
-void rxe_release(struct kref *kref)
-{
- struct rxe_dev *rxe = container_of(kref, struct rxe_dev, ref_cnt);
-
- rxe_cleanup(rxe);
- ib_dealloc_device(&rxe->ib_dev);
+ if (rxe->tfm)
+ crypto_free_shash(rxe->tfm);
}
/* initialize rxe device parameters */
@@ -279,7 +274,6 @@ static int rxe_init(struct rxe_dev *rxe)
spin_lock_init(&rxe->mmap_offset_lock);
spin_lock_init(&rxe->pending_lock);
INIT_LIST_HEAD(&rxe->pending_mmaps);
- INIT_LIST_HEAD(&rxe->list);
mutex_init(&rxe->usdev_lock);
@@ -308,37 +302,46 @@ void rxe_set_mtu(struct rxe_dev *rxe, unsigned int ndev_mtu)
/* called by ifc layer to create new rxe device.
* The caller should allocate memory for rxe by calling ib_alloc_device.
*/
-int rxe_add(struct rxe_dev *rxe, unsigned int mtu)
+int rxe_add(struct rxe_dev *rxe, unsigned int mtu, const char *ibdev_name)
{
int err;
- kref_init(&rxe->ref_cnt);
-
err = rxe_init(rxe);
if (err)
- goto err1;
+ return err;
rxe_set_mtu(rxe, mtu);
- err = rxe_register_device(rxe);
- if (err)
- goto err1;
-
- return 0;
-
-err1:
- rxe_dev_put(rxe);
- return err;
+ return rxe_register_device(rxe, ibdev_name);
}
-/* called by the ifc layer to remove a device */
-void rxe_remove(struct rxe_dev *rxe)
+static int rxe_newlink(const char *ibdev_name, struct net_device *ndev)
{
- rxe_unregister_device(rxe);
+ struct rxe_dev *exists;
+ int err = 0;
+
+ exists = rxe_get_dev_from_net(ndev);
+ if (exists) {
+ ib_device_put(&exists->ib_dev);
+ pr_err("already configured on %s\n", ndev->name);
+ err = -EEXIST;
+ goto err;
+ }
- rxe_dev_put(rxe);
+ err = rxe_net_add(ibdev_name, ndev);
+ if (err) {
+ pr_err("failed to add %s\n", ndev->name);
+ goto err;
+ }
+err:
+ return err;
}
+static struct rdma_link_ops rxe_link_ops = {
+ .type = "rxe",
+ .newlink = rxe_newlink,
+};
+
static int __init rxe_module_init(void)
{
int err;
@@ -354,13 +357,15 @@ static int __init rxe_module_init(void)
if (err)
return err;
+ rdma_link_register(&rxe_link_ops);
pr_info("loaded\n");
return 0;
}
static void __exit rxe_module_exit(void)
{
- rxe_remove_all();
+ rdma_link_unregister(&rxe_link_ops);
+ ib_unregister_driver(RDMA_DRIVER_RXE);
rxe_net_exit();
rxe_cache_exit();
@@ -369,3 +374,5 @@ static void __exit rxe_module_exit(void)
late_initcall(rxe_module_init);
module_exit(rxe_module_exit);
+
+MODULE_ALIAS_RDMA_LINK("rxe");
diff --git a/drivers/infiniband/sw/rxe/rxe.h b/drivers/infiniband/sw/rxe/rxe.h
index 5bde2ad964d2..2e2dff478833 100644
--- a/drivers/infiniband/sw/rxe/rxe.h
+++ b/drivers/infiniband/sw/rxe/rxe.h
@@ -95,18 +95,20 @@ static inline u32 rxe_crc32(struct rxe_dev *rxe,
void rxe_set_mtu(struct rxe_dev *rxe, unsigned int dev_mtu);
-int rxe_add(struct rxe_dev *rxe, unsigned int mtu);
-void rxe_remove(struct rxe_dev *rxe);
-void rxe_remove_all(void);
+int rxe_add(struct rxe_dev *rxe, unsigned int mtu, const char *ibdev_name);
void rxe_rcv(struct sk_buff *skb);
-static inline void rxe_dev_put(struct rxe_dev *rxe)
+/* The caller must do a matching ib_device_put(&dev->ib_dev) */
+static inline struct rxe_dev *rxe_get_dev_from_net(struct net_device *ndev)
{
- kref_put(&rxe->ref_cnt, rxe_release);
+ struct ib_device *ibdev =
+ ib_device_get_by_netdev(ndev, RDMA_DRIVER_RXE);
+
+ if (!ibdev)
+ return NULL;
+ return container_of(ibdev, struct rxe_dev, ib_dev);
}
-struct rxe_dev *net_to_rxe(struct net_device *ndev);
-struct rxe_dev *get_rxe_by_name(const char *name);
void rxe_port_up(struct rxe_dev *rxe);
void rxe_port_down(struct rxe_dev *rxe);
diff --git a/drivers/infiniband/sw/rxe/rxe_av.c b/drivers/infiniband/sw/rxe/rxe_av.c
index 26fe8d7dbc55..81ee756c19b8 100644
--- a/drivers/infiniband/sw/rxe/rxe_av.c
+++ b/drivers/infiniband/sw/rxe/rxe_av.c
@@ -34,6 +34,13 @@
#include "rxe.h"
#include "rxe_loc.h"
+void rxe_init_av(struct rdma_ah_attr *attr, struct rxe_av *av)
+{
+ rxe_av_from_attr(rdma_ah_get_port_num(attr), av, attr);
+ rxe_av_fill_ip_info(av, attr);
+ memcpy(av->dmac, attr->roce.dmac, ETH_ALEN);
+}
+
int rxe_av_chk_attr(struct rxe_dev *rxe, struct rdma_ah_attr *attr)
{
struct rxe_port *port;
diff --git a/drivers/infiniband/sw/rxe/rxe_comp.c b/drivers/infiniband/sw/rxe/rxe_comp.c
index e996da67a851..00eb99d3df86 100644
--- a/drivers/infiniband/sw/rxe/rxe_comp.c
+++ b/drivers/infiniband/sw/rxe/rxe_comp.c
@@ -146,8 +146,7 @@ void retransmit_timer(struct timer_list *t)
}
}
-void rxe_comp_queue_pkt(struct rxe_dev *rxe, struct rxe_qp *qp,
- struct sk_buff *skb)
+void rxe_comp_queue_pkt(struct rxe_qp *qp, struct sk_buff *skb)
{
int must_sched;
@@ -155,7 +154,8 @@ void rxe_comp_queue_pkt(struct rxe_dev *rxe, struct rxe_qp *qp,
must_sched = skb_queue_len(&qp->resp_pkts) > 1;
if (must_sched != 0)
- rxe_counter_inc(rxe, RXE_CNT_COMPLETER_SCHED);
+ rxe_counter_inc(SKB_TO_PKT(skb)->rxe, RXE_CNT_COMPLETER_SCHED);
+
rxe_run_task(&qp->comp.task, must_sched);
}
diff --git a/drivers/infiniband/sw/rxe/rxe_loc.h b/drivers/infiniband/sw/rxe/rxe_loc.h
index 01b74597b36a..3d8cef836f0d 100644
--- a/drivers/infiniband/sw/rxe/rxe_loc.h
+++ b/drivers/infiniband/sw/rxe/rxe_loc.h
@@ -35,6 +35,7 @@
#define RXE_LOC_H
/* rxe_av.c */
+void rxe_init_av(struct rdma_ah_attr *attr, struct rxe_av *av);
int rxe_av_chk_attr(struct rxe_dev *rxe, struct rdma_ah_attr *attr);
@@ -231,7 +232,7 @@ int rxe_srq_from_attr(struct rxe_dev *rxe, struct rxe_srq *srq,
struct ib_srq_attr *attr, enum ib_srq_attr_mask mask,
struct rxe_modify_srq_cmd *ucmd);
-void rxe_release(struct kref *kref);
+void rxe_dealloc(struct ib_device *ib_dev);
int rxe_completer(void *arg);
int rxe_requester(void *arg);
@@ -239,11 +240,9 @@ int rxe_responder(void *arg);
u32 rxe_icrc_hdr(struct rxe_pkt_info *pkt, struct sk_buff *skb);
-void rxe_resp_queue_pkt(struct rxe_dev *rxe,
- struct rxe_qp *qp, struct sk_buff *skb);
+void rxe_resp_queue_pkt(struct rxe_qp *qp, struct sk_buff *skb);
-void rxe_comp_queue_pkt(struct rxe_dev *rxe,
- struct rxe_qp *qp, struct sk_buff *skb);
+void rxe_comp_queue_pkt(struct rxe_qp *qp, struct sk_buff *skb);
static inline unsigned int wr_opcode_mask(int opcode, struct rxe_qp *qp)
{
diff --git a/drivers/infiniband/sw/rxe/rxe_mr.c b/drivers/infiniband/sw/rxe/rxe_mr.c
index 9d3916b93f23..42f0f25e396c 100644
--- a/drivers/infiniband/sw/rxe/rxe_mr.c
+++ b/drivers/infiniband/sw/rxe/rxe_mr.c
@@ -162,16 +162,15 @@ int rxe_mem_init_user(struct rxe_pd *pd, u64 start,
u64 length, u64 iova, int access, struct ib_udata *udata,
struct rxe_mem *mem)
{
- int entry;
struct rxe_map **map;
struct rxe_phys_buf *buf = NULL;
struct ib_umem *umem;
- struct scatterlist *sg;
+ struct sg_page_iter sg_iter;
int num_buf;
void *vaddr;
int err;
- umem = ib_umem_get(pd->ibpd.uobject->context, start, length, access, 0);
+ umem = ib_umem_get(udata, start, length, access, 0);
if (IS_ERR(umem)) {
pr_warn("err %d from rxe_umem_get\n",
(int)PTR_ERR(umem));
@@ -191,16 +190,16 @@ int rxe_mem_init_user(struct rxe_pd *pd, u64 start,
goto err1;
}
- mem->page_shift = umem->page_shift;
- mem->page_mask = BIT(umem->page_shift) - 1;
+ mem->page_shift = PAGE_SHIFT;
+ mem->page_mask = PAGE_SIZE - 1;
num_buf = 0;
map = mem->map;
if (length > 0) {
buf = map[0]->buf;
- for_each_sg(umem->sg_head.sgl, sg, umem->nmap, entry) {
- vaddr = page_address(sg_page(sg));
+ for_each_sg_page(umem->sg_head.sgl, &sg_iter, umem->nmap, 0) {
+ vaddr = page_address(sg_page_iter_page(&sg_iter));
if (!vaddr) {
pr_warn("null vaddr\n");
err = -ENOMEM;
@@ -208,7 +207,7 @@ int rxe_mem_init_user(struct rxe_pd *pd, u64 start,
}
buf->addr = (uintptr_t)vaddr;
- buf->size = BIT(umem->page_shift);
+ buf->size = PAGE_SIZE;
num_buf++;
buf++;
diff --git a/drivers/infiniband/sw/rxe/rxe_net.c b/drivers/infiniband/sw/rxe/rxe_net.c
index 8fd03ae20efc..753cabcd441c 100644
--- a/drivers/infiniband/sw/rxe/rxe_net.c
+++ b/drivers/infiniband/sw/rxe/rxe_net.c
@@ -45,43 +45,6 @@
#include "rxe_net.h"
#include "rxe_loc.h"
-static LIST_HEAD(rxe_dev_list);
-static DEFINE_SPINLOCK(dev_list_lock); /* spinlock for device list */
-
-struct rxe_dev *net_to_rxe(struct net_device *ndev)
-{
- struct rxe_dev *rxe;
- struct rxe_dev *found = NULL;
-
- spin_lock_bh(&dev_list_lock);
- list_for_each_entry(rxe, &rxe_dev_list, list) {
- if (rxe->ndev == ndev) {
- found = rxe;
- break;
- }
- }
- spin_unlock_bh(&dev_list_lock);
-
- return found;
-}
-
-struct rxe_dev *get_rxe_by_name(const char *name)
-{
- struct rxe_dev *rxe;
- struct rxe_dev *found = NULL;
-
- spin_lock_bh(&dev_list_lock);
- list_for_each_entry(rxe, &rxe_dev_list, list) {
- if (!strcmp(name, dev_name(&rxe->ib_dev.dev))) {
- found = rxe;
- break;
- }
- }
- spin_unlock_bh(&dev_list_lock);
- return found;
-}
-
-
static struct rxe_recv_sockets recv_sockets;
struct device *rxe_dma_device(struct rxe_dev *rxe)
@@ -229,18 +192,19 @@ static int rxe_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
struct udphdr *udph;
struct net_device *ndev = skb->dev;
struct net_device *rdev = ndev;
- struct rxe_dev *rxe = net_to_rxe(ndev);
+ struct rxe_dev *rxe = rxe_get_dev_from_net(ndev);
struct rxe_pkt_info *pkt = SKB_TO_PKT(skb);
if (!rxe && is_vlan_dev(rdev)) {
rdev = vlan_dev_real_dev(ndev);
- rxe = net_to_rxe(rdev);
+ rxe = rxe_get_dev_from_net(rdev);
}
if (!rxe)
goto drop;
if (skb_linearize(skb)) {
pr_err("skb_linearize failed\n");
+ ib_device_put(&rxe->ib_dev);
goto drop;
}
@@ -253,6 +217,12 @@ static int rxe_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
rxe_rcv(skb);
+ /*
+ * FIXME: this is in the wrong place, it needs to be done when pkt is
+ * destroyed
+ */
+ ib_device_put(&rxe->ib_dev);
+
return 0;
drop:
kfree_skb(skb);
@@ -384,9 +354,6 @@ static int prepare4(struct rxe_pkt_info *pkt, struct sk_buff *skb,
return -EHOSTUNREACH;
}
- if (!memcmp(saddr, daddr, sizeof(*daddr)))
- pkt->mask |= RXE_LOOPBACK_MASK;
-
prepare_udp_hdr(skb, cpu_to_be16(qp->src_port),
cpu_to_be16(ROCE_V2_UDP_DPORT));
@@ -411,9 +378,6 @@ static int prepare6(struct rxe_pkt_info *pkt, struct sk_buff *skb,
return -EHOSTUNREACH;
}
- if (!memcmp(saddr, daddr, sizeof(*daddr)))
- pkt->mask |= RXE_LOOPBACK_MASK;
-
prepare_udp_hdr(skb, cpu_to_be16(qp->src_port),
cpu_to_be16(ROCE_V2_UDP_DPORT));
@@ -437,6 +401,9 @@ int rxe_prepare(struct rxe_pkt_info *pkt, struct sk_buff *skb, u32 *crc)
*crc = rxe_icrc_hdr(pkt, skb);
+ if (ether_addr_equal(skb->dev->dev_addr, av->dmac))
+ pkt->mask |= RXE_LOOPBACK_MASK;
+
return err;
}
@@ -550,42 +517,24 @@ enum rdma_link_layer rxe_link_layer(struct rxe_dev *rxe, unsigned int port_num)
return IB_LINK_LAYER_ETHERNET;
}
-struct rxe_dev *rxe_net_add(struct net_device *ndev)
+int rxe_net_add(const char *ibdev_name, struct net_device *ndev)
{
int err;
struct rxe_dev *rxe = NULL;
- rxe = (struct rxe_dev *)ib_alloc_device(sizeof(*rxe));
+ rxe = ib_alloc_device(rxe_dev, ib_dev);
if (!rxe)
- return NULL;
+ return -ENOMEM;
rxe->ndev = ndev;
- err = rxe_add(rxe, ndev->mtu);
+ err = rxe_add(rxe, ndev->mtu, ibdev_name);
if (err) {
ib_dealloc_device(&rxe->ib_dev);
- return NULL;
+ return err;
}
- spin_lock_bh(&dev_list_lock);
- list_add_tail(&rxe->list, &rxe_dev_list);
- spin_unlock_bh(&dev_list_lock);
- return rxe;
-}
-
-void rxe_remove_all(void)
-{
- spin_lock_bh(&dev_list_lock);
- while (!list_empty(&rxe_dev_list)) {
- struct rxe_dev *rxe =
- list_first_entry(&rxe_dev_list, struct rxe_dev, list);
-
- list_del(&rxe->list);
- spin_unlock_bh(&dev_list_lock);
- rxe_remove(rxe);
- spin_lock_bh(&dev_list_lock);
- }
- spin_unlock_bh(&dev_list_lock);
+ return 0;
}
static void rxe_port_event(struct rxe_dev *rxe,
@@ -638,15 +587,14 @@ static int rxe_notify(struct notifier_block *not_blk,
void *arg)
{
struct net_device *ndev = netdev_notifier_info_to_dev(arg);
- struct rxe_dev *rxe = net_to_rxe(ndev);
+ struct rxe_dev *rxe = rxe_get_dev_from_net(ndev);
if (!rxe)
- goto out;
+ return NOTIFY_OK;
switch (event) {
case NETDEV_UNREGISTER:
- list_del(&rxe->list);
- rxe_remove(rxe);
+ ib_unregister_device_queued(&rxe->ib_dev);
break;
case NETDEV_UP:
rxe_port_up(rxe);
@@ -671,7 +619,8 @@ static int rxe_notify(struct notifier_block *not_blk,
event, ndev->name);
break;
}
-out:
+
+ ib_device_put(&rxe->ib_dev);
return NOTIFY_OK;
}
diff --git a/drivers/infiniband/sw/rxe/rxe_net.h b/drivers/infiniband/sw/rxe/rxe_net.h
index 106c586dbb26..2ca71d3d245c 100644
--- a/drivers/infiniband/sw/rxe/rxe_net.h
+++ b/drivers/infiniband/sw/rxe/rxe_net.h
@@ -43,7 +43,7 @@ struct rxe_recv_sockets {
struct socket *sk6;
};
-struct rxe_dev *rxe_net_add(struct net_device *ndev);
+int rxe_net_add(const char *ibdev_name, struct net_device *ndev);
int rxe_net_init(void);
void rxe_net_exit(void);
diff --git a/drivers/infiniband/sw/rxe/rxe_param.h b/drivers/infiniband/sw/rxe/rxe_param.h
index bdea899a58ac..1abed47ca221 100644
--- a/drivers/infiniband/sw/rxe/rxe_param.h
+++ b/drivers/infiniband/sw/rxe/rxe_param.h
@@ -78,7 +78,8 @@ enum rxe_device_param {
| IB_DEVICE_SYS_IMAGE_GUID
| IB_DEVICE_RC_RNR_NAK_GEN
| IB_DEVICE_SRQ_RESIZE
- | IB_DEVICE_MEM_MGT_EXTENSIONS,
+ | IB_DEVICE_MEM_MGT_EXTENSIONS
+ | IB_DEVICE_ALLOW_USER_UNREG,
RXE_MAX_SGE = 32,
RXE_MAX_SGE_RD = 32,
RXE_MAX_CQ = 16384,
diff --git a/drivers/infiniband/sw/rxe/rxe_pool.c b/drivers/infiniband/sw/rxe/rxe_pool.c
index b5c91df22047..120fa9005954 100644
--- a/drivers/infiniband/sw/rxe/rxe_pool.c
+++ b/drivers/infiniband/sw/rxe/rxe_pool.c
@@ -42,10 +42,12 @@ struct rxe_type_info rxe_type_info[RXE_NUM_TYPES] = {
[RXE_TYPE_UC] = {
.name = "rxe-uc",
.size = sizeof(struct rxe_ucontext),
+ .flags = RXE_POOL_NO_ALLOC,
},
[RXE_TYPE_PD] = {
.name = "rxe-pd",
.size = sizeof(struct rxe_pd),
+ .flags = RXE_POOL_NO_ALLOC,
},
[RXE_TYPE_AH] = {
.name = "rxe-ah",
@@ -119,8 +121,10 @@ static void rxe_cache_clean(size_t cnt)
for (i = 0; i < cnt; i++) {
type = &rxe_type_info[i];
- kmem_cache_destroy(type->cache);
- type->cache = NULL;
+ if (!(type->flags & RXE_POOL_NO_ALLOC)) {
+ kmem_cache_destroy(type->cache);
+ type->cache = NULL;
+ }
}
}
@@ -134,14 +138,17 @@ int rxe_cache_init(void)
for (i = 0; i < RXE_NUM_TYPES; i++) {
type = &rxe_type_info[i];
size = ALIGN(type->size, RXE_POOL_ALIGN);
- type->cache = kmem_cache_create(type->name, size,
- RXE_POOL_ALIGN,
- RXE_POOL_CACHE_FLAGS, NULL);
- if (!type->cache) {
- pr_err("Unable to init kmem cache for %s\n",
- type->name);
- err = -ENOMEM;
- goto err1;
+ if (!(type->flags & RXE_POOL_NO_ALLOC)) {
+ type->cache =
+ kmem_cache_create(type->name, size,
+ RXE_POOL_ALIGN,
+ RXE_POOL_CACHE_FLAGS, NULL);
+ if (!type->cache) {
+ pr_err("Unable to init kmem cache for %s\n",
+ type->name);
+ err = -ENOMEM;
+ goto err1;
+ }
}
}
@@ -392,29 +399,64 @@ void *rxe_alloc(struct rxe_pool *pool)
kref_get(&pool->ref_cnt);
read_unlock_irqrestore(&pool->pool_lock, flags);
- kref_get(&pool->rxe->ref_cnt);
+ if (!ib_device_try_get(&pool->rxe->ib_dev))
+ goto out_put_pool;
if (atomic_inc_return(&pool->num_elem) > pool->max_elem)
- goto out_put_pool;
+ goto out_cnt;
elem = kmem_cache_zalloc(pool_cache(pool),
(pool->flags & RXE_POOL_ATOMIC) ?
GFP_ATOMIC : GFP_KERNEL);
if (!elem)
- goto out_put_pool;
+ goto out_cnt;
elem->pool = pool;
kref_init(&elem->ref_cnt);
return elem;
-out_put_pool:
+out_cnt:
atomic_dec(&pool->num_elem);
- rxe_dev_put(pool->rxe);
+ ib_device_put(&pool->rxe->ib_dev);
+out_put_pool:
rxe_pool_put(pool);
return NULL;
}
+int rxe_add_to_pool(struct rxe_pool *pool, struct rxe_pool_entry *elem)
+{
+ unsigned long flags;
+
+ might_sleep_if(!(pool->flags & RXE_POOL_ATOMIC));
+
+ read_lock_irqsave(&pool->pool_lock, flags);
+ if (pool->state != RXE_POOL_STATE_VALID) {
+ read_unlock_irqrestore(&pool->pool_lock, flags);
+ return -EINVAL;
+ }
+ kref_get(&pool->ref_cnt);
+ read_unlock_irqrestore(&pool->pool_lock, flags);
+
+ if (!ib_device_try_get(&pool->rxe->ib_dev))
+ goto out_put_pool;
+
+ if (atomic_inc_return(&pool->num_elem) > pool->max_elem)
+ goto out_cnt;
+
+ elem->pool = pool;
+ kref_init(&elem->ref_cnt);
+
+ return 0;
+
+out_cnt:
+ atomic_dec(&pool->num_elem);
+ ib_device_put(&pool->rxe->ib_dev);
+out_put_pool:
+ rxe_pool_put(pool);
+ return -EINVAL;
+}
+
void rxe_elem_release(struct kref *kref)
{
struct rxe_pool_entry *elem =
@@ -424,9 +466,10 @@ void rxe_elem_release(struct kref *kref)
if (pool->cleanup)
pool->cleanup(elem);
- kmem_cache_free(pool_cache(pool), elem);
+ if (!(pool->flags & RXE_POOL_NO_ALLOC))
+ kmem_cache_free(pool_cache(pool), elem);
atomic_dec(&pool->num_elem);
- rxe_dev_put(pool->rxe);
+ ib_device_put(&pool->rxe->ib_dev);
rxe_pool_put(pool);
}
diff --git a/drivers/infiniband/sw/rxe/rxe_pool.h b/drivers/infiniband/sw/rxe/rxe_pool.h
index 72968c29e01f..2f2cff1cbe43 100644
--- a/drivers/infiniband/sw/rxe/rxe_pool.h
+++ b/drivers/infiniband/sw/rxe/rxe_pool.h
@@ -41,6 +41,7 @@ enum rxe_pool_flags {
RXE_POOL_ATOMIC = BIT(0),
RXE_POOL_INDEX = BIT(1),
RXE_POOL_KEY = BIT(2),
+ RXE_POOL_NO_ALLOC = BIT(4),
};
enum rxe_elem_type {
@@ -131,6 +132,9 @@ void rxe_pool_cleanup(struct rxe_pool *pool);
/* allocate an object from pool */
void *rxe_alloc(struct rxe_pool *pool);
+/* connect already allocated object to pool */
+int rxe_add_to_pool(struct rxe_pool *pool, struct rxe_pool_entry *elem);
+
/* assign an index to an indexed object and insert object into
* pool's rb tree
*/
diff --git a/drivers/infiniband/sw/rxe/rxe_qp.c b/drivers/infiniband/sw/rxe/rxe_qp.c
index fd86fd2fbb26..09ede70dc1e8 100644
--- a/drivers/infiniband/sw/rxe/rxe_qp.c
+++ b/drivers/infiniband/sw/rxe/rxe_qp.c
@@ -35,6 +35,7 @@
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/vmalloc.h>
+#include <rdma/uverbs_ioctl.h>
#include "rxe.h"
#include "rxe_loc.h"
@@ -343,7 +344,8 @@ int rxe_qp_from_init(struct rxe_dev *rxe, struct rxe_qp *qp, struct rxe_pd *pd,
struct rxe_cq *rcq = to_rcq(init->recv_cq);
struct rxe_cq *scq = to_rcq(init->send_cq);
struct rxe_srq *srq = init->srq ? to_rsrq(init->srq) : NULL;
- struct ib_ucontext *context = udata ? ibpd->uobject->context : NULL;
+ struct rxe_ucontext *ucontext =
+ rdma_udata_to_drv_context(udata, struct rxe_ucontext, ibuc);
rxe_add_ref(pd);
rxe_add_ref(rcq);
@@ -358,11 +360,11 @@ int rxe_qp_from_init(struct rxe_dev *rxe, struct rxe_qp *qp, struct rxe_pd *pd,
rxe_qp_init_misc(rxe, qp, init);
- err = rxe_qp_init_req(rxe, qp, init, context, uresp);
+ err = rxe_qp_init_req(rxe, qp, init, &ucontext->ibuc, uresp);
if (err)
goto err1;
- err = rxe_qp_init_resp(rxe, qp, init, context, uresp);
+ err = rxe_qp_init_resp(rxe, qp, init, &ucontext->ibuc, uresp);
if (err)
goto err2;
@@ -631,14 +633,11 @@ int rxe_qp_from_attr(struct rxe_qp *qp, struct ib_qp_attr *attr, int mask,
qp->attr.qkey = attr->qkey;
if (mask & IB_QP_AV) {
- rxe_av_from_attr(attr->port_num, &qp->pri_av, &attr->ah_attr);
- rxe_av_fill_ip_info(&qp->pri_av, &attr->ah_attr);
+ rxe_init_av(&attr->ah_attr, &qp->pri_av);
}
if (mask & IB_QP_ALT_PATH) {
- rxe_av_from_attr(attr->alt_port_num, &qp->alt_av,
- &attr->alt_ah_attr);
- rxe_av_fill_ip_info(&qp->alt_av, &attr->alt_ah_attr);
+ rxe_init_av(&attr->alt_ah_attr, &qp->alt_av);
qp->attr.alt_port_num = attr->alt_port_num;
qp->attr.alt_pkey_index = attr->alt_pkey_index;
qp->attr.alt_timeout = attr->alt_timeout;
diff --git a/drivers/infiniband/sw/rxe/rxe_recv.c b/drivers/infiniband/sw/rxe/rxe_recv.c
index 5c29a1bb575a..f9a492ed900b 100644
--- a/drivers/infiniband/sw/rxe/rxe_recv.c
+++ b/drivers/infiniband/sw/rxe/rxe_recv.c
@@ -266,14 +266,12 @@ err1:
return -EINVAL;
}
-static inline void rxe_rcv_pkt(struct rxe_dev *rxe,
- struct rxe_pkt_info *pkt,
- struct sk_buff *skb)
+static inline void rxe_rcv_pkt(struct rxe_pkt_info *pkt, struct sk_buff *skb)
{
if (pkt->mask & RXE_REQ_MASK)
- rxe_resp_queue_pkt(rxe, pkt->qp, skb);
+ rxe_resp_queue_pkt(pkt->qp, skb);
else
- rxe_comp_queue_pkt(rxe, pkt->qp, skb);
+ rxe_comp_queue_pkt(pkt->qp, skb);
}
static void rxe_rcv_mcast_pkt(struct rxe_dev *rxe, struct sk_buff *skb)
@@ -319,7 +317,7 @@ static void rxe_rcv_mcast_pkt(struct rxe_dev *rxe, struct sk_buff *skb)
pkt->qp = qp;
rxe_add_ref(qp);
- rxe_rcv_pkt(rxe, pkt, skb);
+ rxe_rcv_pkt(pkt, skb);
}
spin_unlock_bh(&mcg->mcg_lock);
@@ -411,7 +409,7 @@ void rxe_rcv(struct sk_buff *skb)
if (unlikely(bth_qpn(pkt) == IB_MULTICAST_QPN))
rxe_rcv_mcast_pkt(rxe, skb);
else
- rxe_rcv_pkt(rxe, pkt, skb);
+ rxe_rcv_pkt(pkt, skb);
return;
diff --git a/drivers/infiniband/sw/rxe/rxe_resp.c b/drivers/infiniband/sw/rxe/rxe_resp.c
index 231528188250..aca9f60f9b21 100644
--- a/drivers/infiniband/sw/rxe/rxe_resp.c
+++ b/drivers/infiniband/sw/rxe/rxe_resp.c
@@ -104,8 +104,7 @@ static char *resp_state_name[] = {
};
/* rxe_recv calls here to add a request packet to the input queue */
-void rxe_resp_queue_pkt(struct rxe_dev *rxe, struct rxe_qp *qp,
- struct sk_buff *skb)
+void rxe_resp_queue_pkt(struct rxe_qp *qp, struct sk_buff *skb)
{
int must_sched;
struct rxe_pkt_info *pkt = SKB_TO_PKT(skb);
diff --git a/drivers/infiniband/sw/rxe/rxe_sysfs.c b/drivers/infiniband/sw/rxe/rxe_sysfs.c
index 95a15892f7e6..ccda5f5a3bc0 100644
--- a/drivers/infiniband/sw/rxe/rxe_sysfs.c
+++ b/drivers/infiniband/sw/rxe/rxe_sysfs.c
@@ -58,41 +58,37 @@ static int rxe_param_set_add(const char *val, const struct kernel_param *kp)
int len;
int err = 0;
char intf[32];
- struct net_device *ndev = NULL;
- struct rxe_dev *rxe;
+ struct net_device *ndev;
+ struct rxe_dev *exists;
len = sanitize_arg(val, intf, sizeof(intf));
if (!len) {
pr_err("add: invalid interface name\n");
- err = -EINVAL;
- goto err;
+ return -EINVAL;
}
ndev = dev_get_by_name(&init_net, intf);
if (!ndev) {
pr_err("interface %s not found\n", intf);
- err = -EINVAL;
- goto err;
+ return -EINVAL;
}
- if (net_to_rxe(ndev)) {
+ exists = rxe_get_dev_from_net(ndev);
+ if (exists) {
+ ib_device_put(&exists->ib_dev);
pr_err("already configured on %s\n", intf);
err = -EINVAL;
goto err;
}
- rxe = rxe_net_add(ndev);
- if (!rxe) {
+ err = rxe_net_add("rxe%d", ndev);
+ if (err) {
pr_err("failed to add %s\n", intf);
- err = -EINVAL;
goto err;
}
- rxe_set_port_state(rxe);
- dev_info(&rxe->ib_dev.dev, "added %s\n", intf);
err:
- if (ndev)
- dev_put(ndev);
+ dev_put(ndev);
return err;
}
@@ -100,7 +96,7 @@ static int rxe_param_set_remove(const char *val, const struct kernel_param *kp)
{
int len;
char intf[32];
- struct rxe_dev *rxe;
+ struct ib_device *ib_dev;
len = sanitize_arg(val, intf, sizeof(intf));
if (!len) {
@@ -110,19 +106,17 @@ static int rxe_param_set_remove(const char *val, const struct kernel_param *kp)
if (strncmp("all", intf, len) == 0) {
pr_info("rxe_sys: remove all");
- rxe_remove_all();
+ ib_unregister_driver(RDMA_DRIVER_RXE);
return 0;
}
- rxe = get_rxe_by_name(intf);
-
- if (!rxe) {
+ ib_dev = ib_device_get_by_name(intf, RDMA_DRIVER_RXE);
+ if (!ib_dev) {
pr_err("not configured on %s\n", intf);
return -EINVAL;
}
- list_del(&rxe->list);
- rxe_remove(rxe);
+ ib_unregister_device_and_put(ib_dev);
return 0;
}
@@ -136,6 +130,6 @@ static const struct kernel_param_ops rxe_remove_ops = {
};
module_param_cb(add, &rxe_add_ops, NULL, 0200);
-MODULE_PARM_DESC(add, "Create RXE device over network interface");
+MODULE_PARM_DESC(add, "DEPRECATED. Create RXE device over network interface");
module_param_cb(remove, &rxe_remove_ops, NULL, 0200);
-MODULE_PARM_DESC(remove, "Remove RXE device over network interface");
+MODULE_PARM_DESC(remove, "DEPRECATED. Remove RXE device over network interface");
diff --git a/drivers/infiniband/sw/rxe/rxe_verbs.c b/drivers/infiniband/sw/rxe/rxe_verbs.c
index b20e6e0415f5..6ecf28570ff0 100644
--- a/drivers/infiniband/sw/rxe/rxe_verbs.c
+++ b/drivers/infiniband/sw/rxe/rxe_verbs.c
@@ -33,6 +33,7 @@
#include <linux/dma-mapping.h>
#include <net/addrconf.h>
+#include <rdma/uverbs_ioctl.h>
#include "rxe.h"
#include "rxe_loc.h"
#include "rxe_queue.h"
@@ -79,19 +80,6 @@ static int rxe_query_port(struct ib_device *dev,
return rc;
}
-static struct net_device *rxe_get_netdev(struct ib_device *device,
- u8 port_num)
-{
- struct rxe_dev *rxe = to_rdev(device);
-
- if (rxe->ndev) {
- dev_hold(rxe->ndev);
- return rxe->ndev;
- }
-
- return NULL;
-}
-
static int rxe_query_pkey(struct ib_device *device,
u8 port_num, u16 index, u16 *pkey)
{
@@ -154,22 +142,19 @@ static enum rdma_link_layer rxe_get_link_layer(struct ib_device *dev,
return rxe_link_layer(rxe, port_num);
}
-static struct ib_ucontext *rxe_alloc_ucontext(struct ib_device *dev,
- struct ib_udata *udata)
+static int rxe_alloc_ucontext(struct ib_ucontext *uctx, struct ib_udata *udata)
{
- struct rxe_dev *rxe = to_rdev(dev);
- struct rxe_ucontext *uc;
+ struct rxe_dev *rxe = to_rdev(uctx->device);
+ struct rxe_ucontext *uc = to_ruc(uctx);
- uc = rxe_alloc(&rxe->uc_pool);
- return uc ? &uc->ibuc : ERR_PTR(-ENOMEM);
+ return rxe_add_to_pool(&rxe->uc_pool, &uc->pelem);
}
-static int rxe_dealloc_ucontext(struct ib_ucontext *ibuc)
+static void rxe_dealloc_ucontext(struct ib_ucontext *ibuc)
{
struct rxe_ucontext *uc = to_ruc(ibuc);
rxe_drop_ref(uc);
- return 0;
}
static int rxe_port_immutable(struct ib_device *dev, u8 port_num,
@@ -191,30 +176,20 @@ static int rxe_port_immutable(struct ib_device *dev, u8 port_num,
return 0;
}
-static struct ib_pd *rxe_alloc_pd(struct ib_device *dev,
- struct ib_ucontext *context,
- struct ib_udata *udata)
+static int rxe_alloc_pd(struct ib_pd *ibpd, struct ib_ucontext *context,
+ struct ib_udata *udata)
{
- struct rxe_dev *rxe = to_rdev(dev);
- struct rxe_pd *pd;
+ struct rxe_dev *rxe = to_rdev(ibpd->device);
+ struct rxe_pd *pd = to_rpd(ibpd);
- pd = rxe_alloc(&rxe->pd_pool);
- return pd ? &pd->ibpd : ERR_PTR(-ENOMEM);
+ return rxe_add_to_pool(&rxe->pd_pool, &pd->pelem);
}
-static int rxe_dealloc_pd(struct ib_pd *ibpd)
+static void rxe_dealloc_pd(struct ib_pd *ibpd)
{
struct rxe_pd *pd = to_rpd(ibpd);
rxe_drop_ref(pd);
- return 0;
-}
-
-static void rxe_init_av(struct rxe_dev *rxe, struct rdma_ah_attr *attr,
- struct rxe_av *av)
-{
- rxe_av_from_attr(rdma_ah_get_port_num(attr), av, attr);
- rxe_av_fill_ip_info(av, attr);
}
static struct ib_ah *rxe_create_ah(struct ib_pd *ibpd,
@@ -239,7 +214,7 @@ static struct ib_ah *rxe_create_ah(struct ib_pd *ibpd,
rxe_add_ref(pd);
ah->pd = pd;
- rxe_init_av(rxe, attr, &ah->av);
+ rxe_init_av(attr, &ah->av);
return &ah->ibah;
}
@@ -253,7 +228,7 @@ static int rxe_modify_ah(struct ib_ah *ibah, struct rdma_ah_attr *attr)
if (err)
return err;
- rxe_init_av(rxe, attr, &ah->av);
+ rxe_init_av(attr, &ah->av);
return 0;
}
@@ -330,8 +305,9 @@ static struct ib_srq *rxe_create_srq(struct ib_pd *ibpd,
int err;
struct rxe_dev *rxe = to_rdev(ibpd->device);
struct rxe_pd *pd = to_rpd(ibpd);
+ struct rxe_ucontext *ucontext =
+ rdma_udata_to_drv_context(udata, struct rxe_ucontext, ibuc);
struct rxe_srq *srq;
- struct ib_ucontext *context = udata ? ibpd->uobject->context : NULL;
struct rxe_create_srq_resp __user *uresp = NULL;
if (udata) {
@@ -354,7 +330,7 @@ static struct ib_srq *rxe_create_srq(struct ib_pd *ibpd,
rxe_add_ref(pd);
srq->pd = pd;
- err = rxe_srq_from_init(rxe, srq, init, context, uresp);
+ err = rxe_srq_from_init(rxe, srq, init, &ucontext->ibuc, uresp);
if (err)
goto err2;
@@ -1129,8 +1105,8 @@ static int rxe_detach_mcast(struct ib_qp *ibqp, union ib_gid *mgid, u16 mlid)
static ssize_t parent_show(struct device *device,
struct device_attribute *attr, char *buf)
{
- struct rxe_dev *rxe = container_of(device, struct rxe_dev,
- ib_dev.dev);
+ struct rxe_dev *rxe =
+ rdma_device_to_drv_device(device, struct rxe_dev, ib_dev);
return snprintf(buf, 16, "%s\n", rxe_parent_name(rxe, 1));
}
@@ -1146,6 +1122,15 @@ static const struct attribute_group rxe_attr_group = {
.attrs = rxe_dev_attributes,
};
+static int rxe_enable_driver(struct ib_device *ib_dev)
+{
+ struct rxe_dev *rxe = container_of(ib_dev, struct rxe_dev, ib_dev);
+
+ rxe_set_port_state(rxe);
+ dev_info(&rxe->ib_dev.dev, "added %s\n", netdev_name(rxe->ndev));
+ return 0;
+}
+
static const struct ib_device_ops rxe_dev_ops = {
.alloc_hw_stats = rxe_ib_alloc_hw_stats,
.alloc_mr = rxe_alloc_mr,
@@ -1156,6 +1141,7 @@ static const struct ib_device_ops rxe_dev_ops = {
.create_cq = rxe_create_cq,
.create_qp = rxe_create_qp,
.create_srq = rxe_create_srq,
+ .dealloc_driver = rxe_dealloc,
.dealloc_pd = rxe_dealloc_pd,
.dealloc_ucontext = rxe_dealloc_ucontext,
.dereg_mr = rxe_dereg_mr,
@@ -1164,10 +1150,10 @@ static const struct ib_device_ops rxe_dev_ops = {
.destroy_qp = rxe_destroy_qp,
.destroy_srq = rxe_destroy_srq,
.detach_mcast = rxe_detach_mcast,
+ .enable_driver = rxe_enable_driver,
.get_dma_mr = rxe_get_dma_mr,
.get_hw_stats = rxe_ib_get_hw_stats,
.get_link_layer = rxe_get_link_layer,
- .get_netdev = rxe_get_netdev,
.get_port_immutable = rxe_port_immutable,
.map_mr_sg = rxe_map_mr_sg,
.mmap = rxe_mmap,
@@ -1190,9 +1176,11 @@ static const struct ib_device_ops rxe_dev_ops = {
.reg_user_mr = rxe_reg_user_mr,
.req_notify_cq = rxe_req_notify_cq,
.resize_cq = rxe_resize_cq,
+ INIT_RDMA_OBJ_SIZE(ib_pd, rxe_pd, ibpd),
+ INIT_RDMA_OBJ_SIZE(ib_ucontext, rxe_ucontext, ibuc),
};
-int rxe_register_device(struct rxe_dev *rxe)
+int rxe_register_device(struct rxe_dev *rxe, const char *ibdev_name)
{
int err;
struct ib_device *dev = &rxe->ib_dev;
@@ -1247,6 +1235,9 @@ int rxe_register_device(struct rxe_dev *rxe)
;
ib_set_device_ops(dev, &rxe_dev_ops);
+ err = ib_device_set_netdev(&rxe->ib_dev, rxe->ndev, 1);
+ if (err)
+ return err;
tfm = crypto_alloc_shash("crc32", 0, 0);
if (IS_ERR(tfm)) {
@@ -1258,23 +1249,13 @@ int rxe_register_device(struct rxe_dev *rxe)
rdma_set_device_sysfs_group(dev, &rxe_attr_group);
dev->driver_id = RDMA_DRIVER_RXE;
- err = ib_register_device(dev, "rxe%d", NULL);
- if (err) {
+ err = ib_register_device(dev, ibdev_name);
+ if (err)
pr_warn("%s failed with error %d\n", __func__, err);
- goto err1;
- }
-
- return 0;
-
-err1:
- crypto_free_shash(rxe->tfm);
+ /*
+ * Note that rxe may be invalid at this point if another thread
+ * unregistered it.
+ */
return err;
}
-
-void rxe_unregister_device(struct rxe_dev *rxe)
-{
- struct ib_device *dev = &rxe->ib_dev;
-
- ib_unregister_device(dev);
-}
diff --git a/drivers/infiniband/sw/rxe/rxe_verbs.h b/drivers/infiniband/sw/rxe/rxe_verbs.h
index 74e04801d34d..157e51aeb1e1 100644
--- a/drivers/infiniband/sw/rxe/rxe_verbs.h
+++ b/drivers/infiniband/sw/rxe/rxe_verbs.h
@@ -61,13 +61,13 @@ static inline int psn_compare(u32 psn_a, u32 psn_b)
}
struct rxe_ucontext {
+ struct ib_ucontext ibuc;
struct rxe_pool_entry pelem;
- struct ib_ucontext ibuc;
};
struct rxe_pd {
+ struct ib_pd ibpd;
struct rxe_pool_entry pelem;
- struct ib_pd ibpd;
};
struct rxe_ah {
@@ -385,7 +385,6 @@ struct rxe_dev {
struct ib_device_attr attr;
int max_ucontext;
int max_inline_data;
- struct kref ref_cnt;
struct mutex usdev_lock;
struct net_device *ndev;
@@ -412,7 +411,6 @@ struct rxe_dev {
atomic64_t stats_counters[RXE_NUM_OF_COUNTERS];
struct rxe_port port;
- struct list_head list;
struct crypto_shash *tfm;
};
@@ -466,8 +464,7 @@ static inline struct rxe_mem *to_rmw(struct ib_mw *mw)
return mw ? container_of(mw, struct rxe_mem, ibmw) : NULL;
}
-int rxe_register_device(struct rxe_dev *rxe);
-void rxe_unregister_device(struct rxe_dev *rxe);
+int rxe_register_device(struct rxe_dev *rxe, const char *ibdev_name);
void rxe_mc_cleanup(struct rxe_pool_entry *arg);
diff --git a/drivers/infiniband/ulp/ipoib/ipoib.h b/drivers/infiniband/ulp/ipoib/ipoib.h
index 73e808c1e6ad..2aa3457a30ce 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib.h
+++ b/drivers/infiniband/ulp/ipoib/ipoib.h
@@ -780,12 +780,12 @@ static inline void ipoib_cm_handle_tx_wc(struct net_device *dev, struct ib_wc *w
#ifdef CONFIG_INFINIBAND_IPOIB_DEBUG
void ipoib_create_debug_files(struct net_device *dev);
void ipoib_delete_debug_files(struct net_device *dev);
-int ipoib_register_debugfs(void);
+void ipoib_register_debugfs(void);
void ipoib_unregister_debugfs(void);
#else
static inline void ipoib_create_debug_files(struct net_device *dev) { }
static inline void ipoib_delete_debug_files(struct net_device *dev) { }
-static inline int ipoib_register_debugfs(void) { return 0; }
+static inline void ipoib_register_debugfs(void) { }
static inline void ipoib_unregister_debugfs(void) { }
#endif
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_fs.c b/drivers/infiniband/ulp/ipoib/ipoib_fs.c
index 178488028734..64c19f6fa931 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_fs.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_fs.c
@@ -267,14 +267,10 @@ void ipoib_create_debug_files(struct net_device *dev)
snprintf(name, sizeof(name), "%s_mcg", dev->name);
priv->mcg_dentry = debugfs_create_file(name, S_IFREG | S_IRUGO,
ipoib_root, dev, &ipoib_mcg_fops);
- if (!priv->mcg_dentry)
- ipoib_warn(priv, "failed to create mcg debug file\n");
snprintf(name, sizeof(name), "%s_path", dev->name);
priv->path_dentry = debugfs_create_file(name, S_IFREG | S_IRUGO,
ipoib_root, dev, &ipoib_path_fops);
- if (!priv->path_dentry)
- ipoib_warn(priv, "failed to create path debug file\n");
}
void ipoib_delete_debug_files(struct net_device *dev)
@@ -286,10 +282,9 @@ void ipoib_delete_debug_files(struct net_device *dev)
priv->mcg_dentry = priv->path_dentry = NULL;
}
-int ipoib_register_debugfs(void)
+void ipoib_register_debugfs(void)
{
ipoib_root = debugfs_create_dir("ipoib", NULL);
- return ipoib_root ? 0 : -ENOMEM;
}
void ipoib_unregister_debugfs(void)
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c
index d932f99201d1..48eda16db1a7 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_main.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c
@@ -613,7 +613,7 @@ static void path_free(struct net_device *dev, struct ipoib_path *path)
while ((skb = __skb_dequeue(&path->queue)))
dev_kfree_skb_irq(skb);
- ipoib_dbg(ipoib_priv(dev), "path_free\n");
+ ipoib_dbg(ipoib_priv(dev), "%s\n", __func__);
/* remove all neigh connected to this path */
ipoib_del_neighs_by_gid(dev, path->pathrec.dgid.raw);
@@ -1641,7 +1641,7 @@ static void ipoib_neigh_hash_uninit(struct net_device *dev)
{
struct ipoib_dev_priv *priv = ipoib_priv(dev);
- ipoib_dbg(priv, "ipoib_neigh_hash_uninit\n");
+ ipoib_dbg(priv, "%s\n", __func__);
init_completion(&priv->ntbl.deleted);
cancel_delayed_work_sync(&priv->neigh_reap_task);
@@ -2411,7 +2411,7 @@ static ssize_t dev_id_show(struct device *dev,
}
static DEVICE_ATTR_RO(dev_id);
-int ipoib_intercept_dev_id_attr(struct net_device *dev)
+static int ipoib_intercept_dev_id_attr(struct net_device *dev)
{
device_remove_file(&dev->dev, &dev_attr_dev_id);
return device_create_file(&dev->dev, &dev_attr_dev_id);
@@ -2495,7 +2495,7 @@ static void ipoib_add_one(struct ib_device *device)
struct list_head *dev_list;
struct net_device *dev;
struct ipoib_dev_priv *priv;
- int p;
+ unsigned int p;
int count = 0;
dev_list = kmalloc(sizeof(*dev_list), GFP_KERNEL);
@@ -2504,7 +2504,7 @@ static void ipoib_add_one(struct ib_device *device)
INIT_LIST_HEAD(dev_list);
- for (p = rdma_start_port(device); p <= rdma_end_port(device); ++p) {
+ rdma_for_each_port (device, p) {
if (!rdma_protocol_ib(device, p))
continue;
dev = ipoib_add_port("ib%d", device, p);
@@ -2577,9 +2577,7 @@ static int __init ipoib_init_module(void)
*/
BUILD_BUG_ON(IPOIB_CM_COPYBREAK > IPOIB_CM_HEAD_SIZE);
- ret = ipoib_register_debugfs();
- if (ret)
- return ret;
+ ipoib_register_debugfs();
/*
* We create a global workqueue here that is used for all flush
diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.h b/drivers/infiniband/ulp/iser/iscsi_iser.h
index 120b40829560..a7aeaa0c6fbc 100644
--- a/drivers/infiniband/ulp/iser/iscsi_iser.h
+++ b/drivers/infiniband/ulp/iser/iscsi_iser.h
@@ -197,7 +197,7 @@ struct iser_data_buf {
struct scatterlist *sg;
int size;
unsigned long data_len;
- unsigned int dma_nents;
+ int dma_nents;
};
/* fwd declarations */
diff --git a/drivers/infiniband/ulp/iser/iser_memory.c b/drivers/infiniband/ulp/iser/iser_memory.c
index e9b7efc302d0..2ba70729d7b0 100644
--- a/drivers/infiniband/ulp/iser/iser_memory.c
+++ b/drivers/infiniband/ulp/iser/iser_memory.c
@@ -145,9 +145,8 @@ static void iser_data_buf_dump(struct iser_data_buf *data,
for_each_sg(data->sg, sg, data->dma_nents, i)
iser_dbg("sg[%d] dma_addr:0x%lX page:0x%p "
"off:0x%x sz:0x%x dma_len:0x%x\n",
- i, (unsigned long)ib_sg_dma_address(ibdev, sg),
- sg_page(sg), sg->offset,
- sg->length, ib_sg_dma_len(ibdev, sg));
+ i, (unsigned long)sg_dma_address(sg),
+ sg_page(sg), sg->offset, sg->length, sg_dma_len(sg));
}
static void iser_dump_page_vec(struct iser_page_vec *page_vec)
@@ -204,8 +203,8 @@ iser_reg_dma(struct iser_device *device, struct iser_data_buf *mem,
reg->rkey = device->pd->unsafe_global_rkey;
else
reg->rkey = 0;
- reg->sge.addr = ib_sg_dma_address(device->ib_device, &sg[0]);
- reg->sge.length = ib_sg_dma_len(device->ib_device, &sg[0]);
+ reg->sge.addr = sg_dma_address(&sg[0]);
+ reg->sge.length = sg_dma_len(&sg[0]);
iser_dbg("Single DMA entry: lkey=0x%x, rkey=0x%x, addr=0x%llx,"
" length=0x%x\n", reg->sge.lkey, reg->rkey,
@@ -240,8 +239,8 @@ int iser_fast_reg_fmr(struct iscsi_iser_task *iser_task,
page_vec->npages = 0;
page_vec->fake_mr.page_size = SIZE_4K;
plen = ib_sg_to_pages(&page_vec->fake_mr, mem->sg,
- mem->size, NULL, iser_set_page);
- if (unlikely(plen < mem->size)) {
+ mem->dma_nents, NULL, iser_set_page);
+ if (unlikely(plen < mem->dma_nents)) {
iser_err("page vec too short to hold this SG\n");
iser_data_buf_dump(mem, device->ib_device);
iser_dump_page_vec(page_vec);
@@ -448,10 +447,10 @@ static int iser_fast_reg_mr(struct iscsi_iser_task *iser_task,
ib_update_fast_reg_key(mr, ib_inc_rkey(mr->rkey));
- n = ib_map_mr_sg(mr, mem->sg, mem->size, NULL, SIZE_4K);
- if (unlikely(n != mem->size)) {
+ n = ib_map_mr_sg(mr, mem->sg, mem->dma_nents, NULL, SIZE_4K);
+ if (unlikely(n != mem->dma_nents)) {
iser_err("failed to map sg (%d/%d)\n",
- n, mem->size);
+ n, mem->dma_nents);
return n < 0 ? n : -EINVAL;
}
diff --git a/drivers/infiniband/ulp/isert/Makefile b/drivers/infiniband/ulp/isert/Makefile
index c8bf2421f5bc..a4a4766e3e18 100644
--- a/drivers/infiniband/ulp/isert/Makefile
+++ b/drivers/infiniband/ulp/isert/Makefile
@@ -1,2 +1 @@
-ccflags-y := -Idrivers/target -Idrivers/target/iscsi
obj-$(CONFIG_INFINIBAND_ISERT) += ib_isert.o
diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c
index e3dd13798d79..989f1ac4245c 100644
--- a/drivers/infiniband/ulp/isert/ib_isert.c
+++ b/drivers/infiniband/ulp/isert/ib_isert.c
@@ -1186,7 +1186,7 @@ sequence_cmd:
rc = iscsit_sequence_cmd(conn, cmd, buf, hdr->cmdsn);
if (!rc && dump_payload == false && unsol_data)
- iscsit_set_unsoliticed_dataout(cmd);
+ iscsit_set_unsolicited_dataout(cmd);
else if (dump_payload && imm_data)
target_put_sess_cmd(&cmd->se_cmd);
diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c
index 694324b37480..be9ddcad8f28 100644
--- a/drivers/infiniband/ulp/srp/ib_srp.c
+++ b/drivers/infiniband/ulp/srp/ib_srp.c
@@ -443,8 +443,7 @@ static struct srp_fr_pool *srp_create_fr_pool(struct ib_device *device,
if (pool_size <= 0)
goto err;
ret = -ENOMEM;
- pool = kzalloc(sizeof(struct srp_fr_pool) +
- pool_size * sizeof(struct srp_fr_desc), GFP_KERNEL);
+ pool = kzalloc(struct_size(pool, desc, pool_size), GFP_KERNEL);
if (!pool)
goto err;
pool->size = pool_size;
@@ -1601,9 +1600,8 @@ static int srp_map_sg_entry(struct srp_map_state *state,
{
struct srp_target_port *target = ch->target;
struct srp_device *dev = target->srp_host->srp_dev;
- struct ib_device *ibdev = dev->dev;
- dma_addr_t dma_addr = ib_sg_dma_address(ibdev, sg);
- unsigned int dma_len = ib_sg_dma_len(ibdev, sg);
+ dma_addr_t dma_addr = sg_dma_address(sg);
+ unsigned int dma_len = sg_dma_len(sg);
unsigned int len = 0;
int ret;
@@ -1697,13 +1695,11 @@ static int srp_map_sg_dma(struct srp_map_state *state, struct srp_rdma_ch *ch,
int count)
{
struct srp_target_port *target = ch->target;
- struct srp_device *dev = target->srp_host->srp_dev;
struct scatterlist *sg;
int i;
for_each_sg(scat, sg, count, i) {
- srp_map_desc(state, ib_sg_dma_address(dev->dev, sg),
- ib_sg_dma_len(dev->dev, sg),
+ srp_map_desc(state, sg_dma_address(sg), sg_dma_len(sg),
target->global_rkey);
}
@@ -1853,8 +1849,8 @@ static int srp_map_data(struct scsi_cmnd *scmnd, struct srp_rdma_ch *ch,
buf->len = cpu_to_be32(data_len);
WARN_ON_ONCE((void *)(buf + 1) > (void *)cmd + len);
for_each_sg(scat, sg, count, i) {
- sge[i].addr = ib_sg_dma_address(ibdev, sg);
- sge[i].length = ib_sg_dma_len(ibdev, sg);
+ sge[i].addr = sg_dma_address(sg);
+ sge[i].length = sg_dma_len(sg);
sge[i].lkey = target->lkey;
}
req->cmd->num_sge += count;
@@ -1875,9 +1871,9 @@ static int srp_map_data(struct scsi_cmnd *scmnd, struct srp_rdma_ch *ch,
struct srp_direct_buf *buf;
buf = (void *)cmd->add_data + cmd->add_cdb_len;
- buf->va = cpu_to_be64(ib_sg_dma_address(ibdev, scat));
+ buf->va = cpu_to_be64(sg_dma_address(scat));
buf->key = cpu_to_be32(target->global_rkey);
- buf->len = cpu_to_be32(ib_sg_dma_len(ibdev, scat));
+ buf->len = cpu_to_be32(sg_dma_len(scat));
req->nmdesc = 0;
goto map_complete;
@@ -3814,6 +3810,7 @@ static ssize_t srp_create_target(struct device *dev,
target_host->max_id = 1;
target_host->max_lun = -1LL;
target_host->max_cmd_len = sizeof ((struct srp_cmd *) (void *) 0L)->cdb;
+ target_host->max_segment_size = ib_dma_max_seg_size(ibdev);
target = host_to_target(target_host);
@@ -4120,7 +4117,8 @@ static void srp_add_one(struct ib_device *device)
struct srp_device *srp_dev;
struct ib_device_attr *attr = &device->attrs;
struct srp_host *host;
- int mr_page_shift, p;
+ int mr_page_shift;
+ unsigned int p;
u64 max_pages_per_mr;
unsigned int flags = 0;
@@ -4187,7 +4185,7 @@ static void srp_add_one(struct ib_device *device)
WARN_ON_ONCE(srp_dev->global_rkey == 0);
}
- for (p = rdma_start_port(device); p <= rdma_end_port(device); ++p) {
+ rdma_for_each_port (device, p) {
host = srp_add_port(srp_dev, p);
if (host)
list_add_tail(&host->list, &srp_dev->dev_list);
diff --git a/drivers/infiniband/ulp/srpt/Makefile b/drivers/infiniband/ulp/srpt/Makefile
index e3ee4bdfffa5..43fbde42c58b 100644
--- a/drivers/infiniband/ulp/srpt/Makefile
+++ b/drivers/infiniband/ulp/srpt/Makefile
@@ -1,2 +1 @@
-ccflags-y := -Idrivers/target
obj-$(CONFIG_INFINIBAND_SRPT) += ib_srpt.o
diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.c b/drivers/infiniband/ulp/srpt/ib_srpt.c
index e9c336cff8f5..1a039f16d315 100644
--- a/drivers/infiniband/ulp/srpt/ib_srpt.c
+++ b/drivers/infiniband/ulp/srpt/ib_srpt.c
@@ -1217,22 +1217,15 @@ static int srpt_ch_qp_err(struct srpt_rdma_ch *ch)
static struct srpt_send_ioctx *srpt_get_send_ioctx(struct srpt_rdma_ch *ch)
{
struct srpt_send_ioctx *ioctx;
- unsigned long flags;
+ int tag, cpu;
BUG_ON(!ch);
- ioctx = NULL;
- spin_lock_irqsave(&ch->spinlock, flags);
- if (!list_empty(&ch->free_list)) {
- ioctx = list_first_entry(&ch->free_list,
- struct srpt_send_ioctx, free_list);
- list_del(&ioctx->free_list);
- }
- spin_unlock_irqrestore(&ch->spinlock, flags);
-
- if (!ioctx)
- return ioctx;
+ tag = sbitmap_queue_get(&ch->sess->sess_tag_pool, &cpu);
+ if (tag < 0)
+ return NULL;
+ ioctx = ch->ioctx_ring[tag];
BUG_ON(ioctx->ch != ch);
ioctx->state = SRPT_STATE_NEW;
WARN_ON_ONCE(ioctx->recv_ioctx);
@@ -1245,6 +1238,8 @@ static struct srpt_send_ioctx *srpt_get_send_ioctx(struct srpt_rdma_ch *ch)
*/
memset(&ioctx->cmd, 0, sizeof(ioctx->cmd));
memset(&ioctx->sense_data, 0, sizeof(ioctx->sense_data));
+ ioctx->cmd.map_tag = tag;
+ ioctx->cmd.map_cpu = cpu;
return ioctx;
}
@@ -1505,7 +1500,7 @@ static void srpt_handle_cmd(struct srpt_rdma_ch *ch,
pr_err("0x%llx: parsing SRP descriptor table failed.\n",
srp_cmd->tag);
}
- goto release_ioctx;
+ goto busy;
}
rc = target_submit_cmd_map_sgls(cmd, ch->sess, srp_cmd->cdb,
@@ -1516,13 +1511,12 @@ static void srpt_handle_cmd(struct srpt_rdma_ch *ch,
if (rc != 0) {
pr_debug("target_submit_cmd() returned %d for tag %#llx\n", rc,
srp_cmd->tag);
- goto release_ioctx;
+ goto busy;
}
return;
-release_ioctx:
- send_ioctx->state = SRPT_STATE_DONE;
- srpt_release_cmd(cmd);
+busy:
+ target_send_busy(cmd);
}
static int srp_tmr_to_tcm(int fn)
@@ -1582,11 +1576,9 @@ static void srpt_handle_tsk_mgmt(struct srpt_rdma_ch *ch,
TARGET_SCF_ACK_KREF);
if (rc != 0) {
send_ioctx->cmd.se_tmr_req->response = TMR_FUNCTION_REJECTED;
- goto fail;
+ cmd->se_tfo->queue_tm_rsp(cmd);
}
return;
-fail:
- transport_send_check_condition_and_sense(cmd, 0, 0); // XXX:
}
/**
@@ -2151,7 +2143,7 @@ static int srpt_cm_req_recv(struct srpt_device *const sdev,
struct srpt_rdma_ch *ch = NULL;
char i_port_id[36];
u32 it_iu_len;
- int i, ret;
+ int i, tag_num, tag_size, ret;
WARN_ON_ONCE(irqs_disabled());
@@ -2251,11 +2243,8 @@ static int srpt_cm_req_recv(struct srpt_device *const sdev,
goto free_rsp_cache;
}
- INIT_LIST_HEAD(&ch->free_list);
- for (i = 0; i < ch->rq_size; i++) {
+ for (i = 0; i < ch->rq_size; i++)
ch->ioctx_ring[i]->ch = ch;
- list_add_tail(&ch->ioctx_ring[i]->free_list, &ch->free_list);
- }
if (!sdev->use_srq) {
u16 imm_data_offset = req->req_flags & SRP_IMMED_REQUESTED ?
be16_to_cpu(req->imm_data_offset) : 0;
@@ -2309,18 +2298,20 @@ static int srpt_cm_req_recv(struct srpt_device *const sdev,
pr_debug("registering session %s\n", ch->sess_name);
+ tag_num = ch->rq_size;
+ tag_size = 1; /* ib_srpt does not use se_sess->sess_cmd_map */
if (sport->port_guid_tpg.se_tpg_wwn)
- ch->sess = target_setup_session(&sport->port_guid_tpg, 0, 0,
- TARGET_PROT_NORMAL,
+ ch->sess = target_setup_session(&sport->port_guid_tpg, tag_num,
+ tag_size, TARGET_PROT_NORMAL,
ch->sess_name, ch, NULL);
if (sport->port_gid_tpg.se_tpg_wwn && IS_ERR_OR_NULL(ch->sess))
- ch->sess = target_setup_session(&sport->port_gid_tpg, 0, 0,
- TARGET_PROT_NORMAL, i_port_id, ch,
- NULL);
+ ch->sess = target_setup_session(&sport->port_gid_tpg, tag_num,
+ tag_size, TARGET_PROT_NORMAL, i_port_id,
+ ch, NULL);
/* Retry without leading "0x" */
if (sport->port_gid_tpg.se_tpg_wwn && IS_ERR_OR_NULL(ch->sess))
- ch->sess = target_setup_session(&sport->port_gid_tpg, 0, 0,
- TARGET_PROT_NORMAL,
+ ch->sess = target_setup_session(&sport->port_gid_tpg, tag_num,
+ tag_size, TARGET_PROT_NORMAL,
i_port_id + 2, ch, NULL);
if (IS_ERR_OR_NULL(ch->sess)) {
WARN_ON_ONCE(ch->sess == NULL);
@@ -2703,14 +2694,6 @@ static int srpt_rdma_cm_handler(struct rdma_cm_id *cm_id,
return ret;
}
-static int srpt_write_pending_status(struct se_cmd *se_cmd)
-{
- struct srpt_send_ioctx *ioctx;
-
- ioctx = container_of(se_cmd, struct srpt_send_ioctx, cmd);
- return ioctx->state == SRPT_STATE_NEED_DATA;
-}
-
/*
* srpt_write_pending - Start data transfer from initiator to target (write).
*/
@@ -2887,8 +2870,19 @@ static void srpt_queue_tm_rsp(struct se_cmd *cmd)
srpt_queue_response(cmd);
}
+/*
+ * This function is called for aborted commands if no response is sent to the
+ * initiator. Make sure that the credits freed by aborting a command are
+ * returned to the initiator the next time a response is sent by incrementing
+ * ch->req_lim_delta.
+ */
static void srpt_aborted_task(struct se_cmd *cmd)
{
+ struct srpt_send_ioctx *ioctx = container_of(cmd,
+ struct srpt_send_ioctx, cmd);
+ struct srpt_rdma_ch *ch = ioctx->ch;
+
+ atomic_inc(&ch->req_lim_delta);
}
static int srpt_queue_status(struct se_cmd *cmd)
@@ -3290,7 +3284,6 @@ static void srpt_release_cmd(struct se_cmd *se_cmd)
struct srpt_send_ioctx, cmd);
struct srpt_rdma_ch *ch = ioctx->ch;
struct srpt_recv_ioctx *recv_ioctx = ioctx->recv_ioctx;
- unsigned long flags;
WARN_ON_ONCE(ioctx->state != SRPT_STATE_DONE &&
!(ioctx->cmd.transport_state & CMD_T_ABORTED));
@@ -3306,9 +3299,7 @@ static void srpt_release_cmd(struct se_cmd *se_cmd)
ioctx->n_rw_ctx = 0;
}
- spin_lock_irqsave(&ch->spinlock, flags);
- list_add(&ioctx->free_list, &ch->free_list);
- spin_unlock_irqrestore(&ch->spinlock, flags);
+ target_free_tag(se_cmd->se_sess, se_cmd);
}
/**
@@ -3806,7 +3797,6 @@ static const struct target_core_fabric_ops srpt_template = {
.sess_get_index = srpt_sess_get_index,
.sess_get_initiator_sid = NULL,
.write_pending = srpt_write_pending,
- .write_pending_status = srpt_write_pending_status,
.set_default_node_attributes = srpt_set_default_node_attrs,
.get_cmd_state = srpt_get_tcm_cmd_state,
.queue_data_in = srpt_queue_data_in,
diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.h b/drivers/infiniband/ulp/srpt/ib_srpt.h
index 39b3e50baf3d..ee9f20e9177a 100644
--- a/drivers/infiniband/ulp/srpt/ib_srpt.h
+++ b/drivers/infiniband/ulp/srpt/ib_srpt.h
@@ -207,7 +207,6 @@ struct srpt_rw_ctx {
* @rw_ctxs: RDMA read/write contexts.
* @imm_sg: Scatterlist for immediate data.
* @rdma_cqe: RDMA completion queue element.
- * @free_list: Node in srpt_rdma_ch.free_list.
* @state: I/O context state.
* @cmd: Target core command data structure.
* @sense_data: SCSI sense data.
@@ -227,7 +226,6 @@ struct srpt_send_ioctx {
struct scatterlist imm_sg;
struct ib_cqe rdma_cqe;
- struct list_head free_list;
enum srpt_command_state state;
struct se_cmd cmd;
u8 n_rdma;
@@ -277,7 +275,6 @@ enum rdma_ch_state {
* @req_lim_delta: Number of credits not yet sent back to the initiator.
* @imm_data_offset: Offset from start of SRP_CMD for immediate data.
* @spinlock: Protects free_list and state.
- * @free_list: Head of list with free send I/O contexts.
* @state: channel state. See also enum rdma_ch_state.
* @using_rdma_cm: Whether the RDMA/CM or IB/CM is used for this channel.
* @processing_wait_list: Whether or not cmd_wait_list is being processed.
@@ -318,7 +315,6 @@ struct srpt_rdma_ch {
atomic_t req_lim_delta;
u16 imm_data_offset;
spinlock_t spinlock;
- struct list_head free_list;
enum rdma_ch_state state;
struct kmem_cache *rsp_buf_cache;
struct srpt_send_ioctx **ioctx_ring;
diff --git a/drivers/input/joystick/db9.c b/drivers/input/joystick/db9.c
index 804b1b80a8be..5a52b65bef9a 100644
--- a/drivers/input/joystick/db9.c
+++ b/drivers/input/joystick/db9.c
@@ -259,7 +259,7 @@ static unsigned char db9_saturn_read_packet(struct parport *port, unsigned char
db9_saturn_write_sub(port, type, 3, powered, 0);
return data[0] = 0xe3;
}
- /* else: fall through */
+ /* fall through */
default:
return data[0];
}
diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c
index 492a971b95b5..6cd199e8a370 100644
--- a/drivers/input/keyboard/gpio_keys.c
+++ b/drivers/input/keyboard/gpio_keys.c
@@ -1015,8 +1015,18 @@ static int __maybe_unused gpio_keys_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(gpio_keys_pm_ops, gpio_keys_suspend, gpio_keys_resume);
+static void gpio_keys_shutdown(struct platform_device *pdev)
+{
+ int ret;
+
+ ret = gpio_keys_suspend(&pdev->dev);
+ if (ret)
+ dev_err(&pdev->dev, "failed to shutdown\n");
+}
+
static struct platform_driver gpio_keys_device_driver = {
.probe = gpio_keys_probe,
+ .shutdown = gpio_keys_shutdown,
.driver = {
.name = "gpio-keys",
.pm = &gpio_keys_pm_ops,
diff --git a/drivers/input/keyboard/mcs_touchkey.c b/drivers/input/keyboard/mcs_touchkey.c
index be56d4f262a7..b132662201a4 100644
--- a/drivers/input/keyboard/mcs_touchkey.c
+++ b/drivers/input/keyboard/mcs_touchkey.c
@@ -113,9 +113,8 @@ static int mcs_touchkey_probe(struct i2c_client *client,
return -EINVAL;
}
- data = kzalloc(sizeof(struct mcs_touchkey_data) +
- sizeof(data->keycodes[0]) * (pdata->key_maxval + 1),
- GFP_KERNEL);
+ data = kzalloc(struct_size(data, keycodes, pdata->key_maxval + 1),
+ GFP_KERNEL);
input_dev = input_allocate_device();
if (!data || !input_dev) {
dev_err(&client->dev, "Failed to allocate memory\n");
diff --git a/drivers/input/keyboard/mtk-pmic-keys.c b/drivers/input/keyboard/mtk-pmic-keys.c
index 02c67a1749fc..8e6ebab05ab4 100644
--- a/drivers/input/keyboard/mtk-pmic-keys.c
+++ b/drivers/input/keyboard/mtk-pmic-keys.c
@@ -14,18 +14,17 @@
*
*/
-#include <linux/module.h>
-#include <linux/kernel.h>
#include <linux/input.h>
#include <linux/interrupt.h>
-#include <linux/platform_device.h>
#include <linux/kernel.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-#include <linux/regmap.h>
#include <linux/mfd/mt6323/registers.h>
-#include <linux/mfd/mt6397/registers.h>
#include <linux/mfd/mt6397/core.h>
+#include <linux/mfd/mt6397/registers.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
#define MTK_PMIC_PWRKEY_RST_EN_MASK 0x1
#define MTK_PMIC_PWRKEY_RST_EN_SHIFT 6
diff --git a/drivers/input/keyboard/qt2160.c b/drivers/input/keyboard/qt2160.c
index d466bc07aebb..6a43895b28e7 100644
--- a/drivers/input/keyboard/qt2160.c
+++ b/drivers/input/keyboard/qt2160.c
@@ -68,7 +68,6 @@ struct qt2160_data {
struct i2c_client *client;
struct input_dev *input;
struct delayed_work dwork;
- spinlock_t lock; /* Protects canceling/rescheduling of dwork */
unsigned short keycodes[ARRAY_SIZE(qt2160_key2code)];
u16 key_matrix;
#ifdef CONFIG_LEDS_CLASS
@@ -212,22 +211,15 @@ static int qt2160_get_key_matrix(struct qt2160_data *qt2160)
static irqreturn_t qt2160_irq(int irq, void *_qt2160)
{
struct qt2160_data *qt2160 = _qt2160;
- unsigned long flags;
-
- spin_lock_irqsave(&qt2160->lock, flags);
mod_delayed_work(system_wq, &qt2160->dwork, 0);
- spin_unlock_irqrestore(&qt2160->lock, flags);
-
return IRQ_HANDLED;
}
static void qt2160_schedule_read(struct qt2160_data *qt2160)
{
- spin_lock_irq(&qt2160->lock);
schedule_delayed_work(&qt2160->dwork, QT2160_CYCLE_INTERVAL);
- spin_unlock_irq(&qt2160->lock);
}
static void qt2160_worker(struct work_struct *work)
@@ -391,7 +383,6 @@ static int qt2160_probe(struct i2c_client *client,
qt2160->client = client;
qt2160->input = input;
INIT_DELAYED_WORK(&qt2160->dwork, qt2160_worker);
- spin_lock_init(&qt2160->lock);
input->name = "AT42QT2160 Touch Sense Keyboard";
input->id.bustype = BUS_I2C;
diff --git a/drivers/input/keyboard/tca6416-keypad.c b/drivers/input/keyboard/tca6416-keypad.c
index dc983ab6c0ad..cdeef180aead 100644
--- a/drivers/input/keyboard/tca6416-keypad.c
+++ b/drivers/input/keyboard/tca6416-keypad.c
@@ -219,9 +219,7 @@ static int tca6416_keypad_probe(struct i2c_client *client,
return -EINVAL;
}
- chip = kzalloc(sizeof(struct tca6416_keypad_chip) +
- pdata->nbuttons * sizeof(struct tca6416_button),
- GFP_KERNEL);
+ chip = kzalloc(struct_size(chip, buttons, pdata->nbuttons), GFP_KERNEL);
input = input_allocate_device();
if (!chip || !input) {
error = -ENOMEM;
diff --git a/drivers/input/keyboard/tm2-touchkey.c b/drivers/input/keyboard/tm2-touchkey.c
index abc266e40e17..d4455f3a5cf1 100644
--- a/drivers/input/keyboard/tm2-touchkey.c
+++ b/drivers/input/keyboard/tm2-touchkey.c
@@ -22,12 +22,14 @@
#include <linux/leds.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/pm.h>
#include <linux/regulator/consumer.h>
#define TM2_TOUCHKEY_DEV_NAME "tm2-touchkey"
-#define TM2_TOUCHKEY_KEYCODE_REG 0x03
-#define TM2_TOUCHKEY_BASE_REG 0x00
+
+#define ARIES_TOUCHKEY_CMD_LED_ON 0x1
+#define ARIES_TOUCHKEY_CMD_LED_OFF 0x2
#define TM2_TOUCHKEY_CMD_LED_ON 0x10
#define TM2_TOUCHKEY_CMD_LED_OFF 0x20
#define TM2_TOUCHKEY_BIT_PRESS_EV BIT(3)
@@ -35,9 +37,13 @@
#define TM2_TOUCHKEY_LED_VOLTAGE_MIN 2500000
#define TM2_TOUCHKEY_LED_VOLTAGE_MAX 3300000
-enum {
- TM2_TOUCHKEY_KEY_MENU = 0x1,
- TM2_TOUCHKEY_KEY_BACK,
+struct touchkey_variant {
+ u8 keycode_reg;
+ u8 base_reg;
+ u8 cmd_led_on;
+ u8 cmd_led_off;
+ bool no_reg;
+ bool fixed_regulator;
};
struct tm2_touchkey_data {
@@ -46,9 +52,33 @@ struct tm2_touchkey_data {
struct led_classdev led_dev;
struct regulator *vdd;
struct regulator_bulk_data regulators[2];
+ const struct touchkey_variant *variant;
+ u32 keycodes[4];
+ int num_keycodes;
+};
+
+static const struct touchkey_variant tm2_touchkey_variant = {
+ .keycode_reg = 0x03,
+ .base_reg = 0x00,
+ .cmd_led_on = TM2_TOUCHKEY_CMD_LED_ON,
+ .cmd_led_off = TM2_TOUCHKEY_CMD_LED_OFF,
+};
+
+static const struct touchkey_variant midas_touchkey_variant = {
+ .keycode_reg = 0x00,
+ .base_reg = 0x00,
+ .cmd_led_on = TM2_TOUCHKEY_CMD_LED_ON,
+ .cmd_led_off = TM2_TOUCHKEY_CMD_LED_OFF,
};
-static void tm2_touchkey_led_brightness_set(struct led_classdev *led_dev,
+static struct touchkey_variant aries_touchkey_variant = {
+ .no_reg = true,
+ .fixed_regulator = true,
+ .cmd_led_on = ARIES_TOUCHKEY_CMD_LED_ON,
+ .cmd_led_off = ARIES_TOUCHKEY_CMD_LED_OFF,
+};
+
+static int tm2_touchkey_led_brightness_set(struct led_classdev *led_dev,
enum led_brightness brightness)
{
struct tm2_touchkey_data *touchkey =
@@ -58,15 +88,19 @@ static void tm2_touchkey_led_brightness_set(struct led_classdev *led_dev,
if (brightness == LED_OFF) {
volt = TM2_TOUCHKEY_LED_VOLTAGE_MIN;
- data = TM2_TOUCHKEY_CMD_LED_OFF;
+ data = touchkey->variant->cmd_led_off;
} else {
volt = TM2_TOUCHKEY_LED_VOLTAGE_MAX;
- data = TM2_TOUCHKEY_CMD_LED_ON;
+ data = touchkey->variant->cmd_led_on;
}
- regulator_set_voltage(touchkey->vdd, volt, volt);
- i2c_smbus_write_byte_data(touchkey->client,
- TM2_TOUCHKEY_BASE_REG, data);
+ if (!touchkey->variant->fixed_regulator)
+ regulator_set_voltage(touchkey->vdd, volt, volt);
+
+ return touchkey->variant->no_reg ?
+ i2c_smbus_write_byte(touchkey->client, data) :
+ i2c_smbus_write_byte_data(touchkey->client,
+ touchkey->variant->base_reg, data);
}
static int tm2_touchkey_power_enable(struct tm2_touchkey_data *touchkey)
@@ -96,49 +130,57 @@ static irqreturn_t tm2_touchkey_irq_handler(int irq, void *devid)
{
struct tm2_touchkey_data *touchkey = devid;
int data;
- int key;
-
- data = i2c_smbus_read_byte_data(touchkey->client,
- TM2_TOUCHKEY_KEYCODE_REG);
+ int index;
+ int i;
+
+ if (touchkey->variant->no_reg)
+ data = i2c_smbus_read_byte(touchkey->client);
+ else
+ data = i2c_smbus_read_byte_data(touchkey->client,
+ touchkey->variant->keycode_reg);
if (data < 0) {
dev_err(&touchkey->client->dev,
"failed to read i2c data: %d\n", data);
goto out;
}
- switch (data & TM2_TOUCHKEY_BIT_KEYCODE) {
- case TM2_TOUCHKEY_KEY_MENU:
- key = KEY_PHONE;
- break;
-
- case TM2_TOUCHKEY_KEY_BACK:
- key = KEY_BACK;
- break;
-
- default:
+ index = (data & TM2_TOUCHKEY_BIT_KEYCODE) - 1;
+ if (index < 0 || index >= touchkey->num_keycodes) {
dev_warn(&touchkey->client->dev,
- "unhandled keycode, data %#02x\n", data);
+ "invalid keycode index %d\n", index);
goto out;
}
if (data & TM2_TOUCHKEY_BIT_PRESS_EV) {
- input_report_key(touchkey->input_dev, KEY_PHONE, 0);
- input_report_key(touchkey->input_dev, KEY_BACK, 0);
+ for (i = 0; i < touchkey->num_keycodes; i++)
+ input_report_key(touchkey->input_dev,
+ touchkey->keycodes[i], 0);
} else {
- input_report_key(touchkey->input_dev, key, 1);
+ input_report_key(touchkey->input_dev,
+ touchkey->keycodes[index], 1);
}
input_sync(touchkey->input_dev);
out:
+ if (touchkey->variant->fixed_regulator &&
+ data & TM2_TOUCHKEY_BIT_PRESS_EV) {
+ /* touch turns backlight on, so make sure we're in sync */
+ if (touchkey->led_dev.brightness == LED_OFF)
+ tm2_touchkey_led_brightness_set(&touchkey->led_dev,
+ LED_OFF);
+ }
+
return IRQ_HANDLED;
}
static int tm2_touchkey_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
+ struct device_node *np = client->dev.of_node;
struct tm2_touchkey_data *touchkey;
int error;
+ int i;
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA)) {
@@ -153,6 +195,8 @@ static int tm2_touchkey_probe(struct i2c_client *client,
touchkey->client = client;
i2c_set_clientdata(client, touchkey);
+ touchkey->variant = of_device_get_match_data(&client->dev);
+
touchkey->regulators[0].supply = "vcc";
touchkey->regulators[1].supply = "vdd";
error = devm_regulator_bulk_get(&client->dev,
@@ -166,6 +210,16 @@ static int tm2_touchkey_probe(struct i2c_client *client,
/* Save VDD for easy access */
touchkey->vdd = touchkey->regulators[1].consumer;
+ touchkey->num_keycodes = of_property_read_variable_u32_array(np,
+ "linux,keycodes", touchkey->keycodes, 0,
+ ARRAY_SIZE(touchkey->keycodes));
+ if (touchkey->num_keycodes <= 0) {
+ /* default keycodes */
+ touchkey->keycodes[0] = KEY_PHONE;
+ touchkey->keycodes[1] = KEY_BACK;
+ touchkey->num_keycodes = 2;
+ }
+
error = tm2_touchkey_power_enable(touchkey);
if (error) {
dev_err(&client->dev, "failed to power up device: %d\n", error);
@@ -190,8 +244,9 @@ static int tm2_touchkey_probe(struct i2c_client *client,
touchkey->input_dev->name = TM2_TOUCHKEY_DEV_NAME;
touchkey->input_dev->id.bustype = BUS_I2C;
- input_set_capability(touchkey->input_dev, EV_KEY, KEY_PHONE);
- input_set_capability(touchkey->input_dev, EV_KEY, KEY_BACK);
+ for (i = 0; i < touchkey->num_keycodes; i++)
+ input_set_capability(touchkey->input_dev, EV_KEY,
+ touchkey->keycodes[i]);
error = input_register_device(touchkey->input_dev);
if (error) {
@@ -212,9 +267,10 @@ static int tm2_touchkey_probe(struct i2c_client *client,
/* led device */
touchkey->led_dev.name = TM2_TOUCHKEY_DEV_NAME;
- touchkey->led_dev.brightness = LED_FULL;
+ touchkey->led_dev.brightness = LED_ON;
touchkey->led_dev.max_brightness = LED_ON;
- touchkey->led_dev.brightness_set = tm2_touchkey_led_brightness_set;
+ touchkey->led_dev.brightness_set_blocking =
+ tm2_touchkey_led_brightness_set;
error = devm_led_classdev_register(&client->dev, &touchkey->led_dev);
if (error) {
@@ -223,6 +279,9 @@ static int tm2_touchkey_probe(struct i2c_client *client,
return error;
}
+ if (touchkey->variant->fixed_regulator)
+ tm2_touchkey_led_brightness_set(&touchkey->led_dev, LED_ON);
+
return 0;
}
@@ -262,7 +321,16 @@ static const struct i2c_device_id tm2_touchkey_id_table[] = {
MODULE_DEVICE_TABLE(i2c, tm2_touchkey_id_table);
static const struct of_device_id tm2_touchkey_of_match[] = {
- { .compatible = "cypress,tm2-touchkey", },
+ {
+ .compatible = "cypress,tm2-touchkey",
+ .data = &tm2_touchkey_variant,
+ }, {
+ .compatible = "cypress,midas-touchkey",
+ .data = &midas_touchkey_variant,
+ }, {
+ .compatible = "cypress,aries-touchkey",
+ .data = &aries_touchkey_variant,
+ },
{ },
};
MODULE_DEVICE_TABLE(of, tm2_touchkey_of_match);
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index ca59a2be9bc5..e15ed1bb8558 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -117,6 +117,16 @@ config INPUT_E3X0_BUTTON
To compile this driver as a module, choose M here: the
module will be called e3x0_button.
+config INPUT_MSM_VIBRATOR
+ tristate "Qualcomm MSM vibrator driver"
+ select INPUT_FF_MEMLESS
+ help
+ Support for the vibrator that is found on various Qualcomm MSM
+ SOCs.
+
+ To compile this driver as a module, choose M here: the module
+ will be called msm_vibrator.
+
config INPUT_PCSPKR
tristate "PC Speaker support"
depends on PCSPKR_PLATFORM
@@ -851,4 +861,15 @@ config INPUT_SC27XX_VIBRA
To compile this driver as a module, choose M here. The module will
be called sc27xx_vibra.
+config INPUT_STPMIC1_ONKEY
+ tristate "STPMIC1 PMIC Onkey support"
+ depends on MFD_STPMIC1
+ help
+ Say Y to enable support of onkey embedded into STPMIC1 PMIC. onkey
+ can be used to wakeup from low power modes and force a shut-down on
+ long press.
+
+ To compile this driver as a module, choose M here: the
+ module will be called stpmic1_onkey.
+
endif
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 9d0f9d1ff68f..b936c5b1d4ac 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -48,6 +48,7 @@ obj-$(CONFIG_INPUT_MAX8925_ONKEY) += max8925_onkey.o
obj-$(CONFIG_INPUT_MAX8997_HAPTIC) += max8997_haptic.o
obj-$(CONFIG_INPUT_MC13783_PWRBUTTON) += mc13783-pwrbutton.o
obj-$(CONFIG_INPUT_MMA8450) += mma8450.o
+obj-$(CONFIG_INPUT_MSM_VIBRATOR) += msm-vibrator.o
obj-$(CONFIG_INPUT_PALMAS_PWRBUTTON) += palmas-pwrbutton.o
obj-$(CONFIG_INPUT_PCAP) += pcap_keys.o
obj-$(CONFIG_INPUT_PCF50633_PMU) += pcf50633-input.o
@@ -71,6 +72,7 @@ obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o
obj-$(CONFIG_INPUT_SIRFSOC_ONKEY) += sirfsoc-onkey.o
obj-$(CONFIG_INPUT_SOC_BUTTON_ARRAY) += soc_button_array.o
obj-$(CONFIG_INPUT_SPARCSPKR) += sparcspkr.o
+obj-$(CONFIG_INPUT_STPMIC1_ONKEY) += stpmic1_onkey.o
obj-$(CONFIG_INPUT_TPS65218_PWRBUTTON) += tps65218-pwrbutton.o
obj-$(CONFIG_INPUT_TWL4030_PWRBUTTON) += twl4030-pwrbutton.o
obj-$(CONFIG_INPUT_TWL4030_VIBRA) += twl4030-vibra.o
@@ -81,3 +83,4 @@ obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o
obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o
obj-$(CONFIG_INPUT_YEALINK) += yealink.o
obj-$(CONFIG_INPUT_IDEAPAD_SLIDEBAR) += ideapad_slidebar.o
+
diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c
index 3d51175c4d72..74cf3b612f05 100644
--- a/drivers/input/misc/ims-pcu.c
+++ b/drivers/input/misc/ims-pcu.c
@@ -39,8 +39,6 @@ struct ims_pcu_gamepad {
struct ims_pcu_backlight {
struct led_classdev cdev;
- struct work_struct work;
- enum led_brightness desired_brightness;
char name[32];
};
@@ -949,14 +947,14 @@ out:
#define IMS_PCU_MAX_BRIGHTNESS 31998
-static void ims_pcu_backlight_work(struct work_struct *work)
+static int ims_pcu_backlight_set_brightness(struct led_classdev *cdev,
+ enum led_brightness value)
{
struct ims_pcu_backlight *backlight =
- container_of(work, struct ims_pcu_backlight, work);
+ container_of(cdev, struct ims_pcu_backlight, cdev);
struct ims_pcu *pcu =
container_of(backlight, struct ims_pcu, backlight);
- int desired_brightness = backlight->desired_brightness;
- __le16 br_val = cpu_to_le16(desired_brightness);
+ __le16 br_val = cpu_to_le16(value);
int error;
mutex_lock(&pcu->cmd_mutex);
@@ -966,19 +964,11 @@ static void ims_pcu_backlight_work(struct work_struct *work)
if (error && error != -ENODEV)
dev_warn(pcu->dev,
"Failed to set desired brightness %u, error: %d\n",
- desired_brightness, error);
+ value, error);
mutex_unlock(&pcu->cmd_mutex);
-}
-static void ims_pcu_backlight_set_brightness(struct led_classdev *cdev,
- enum led_brightness value)
-{
- struct ims_pcu_backlight *backlight =
- container_of(cdev, struct ims_pcu_backlight, cdev);
-
- backlight->desired_brightness = value;
- schedule_work(&backlight->work);
+ return error;
}
static enum led_brightness
@@ -1015,14 +1005,14 @@ static int ims_pcu_setup_backlight(struct ims_pcu *pcu)
struct ims_pcu_backlight *backlight = &pcu->backlight;
int error;
- INIT_WORK(&backlight->work, ims_pcu_backlight_work);
snprintf(backlight->name, sizeof(backlight->name),
"pcu%d::kbd_backlight", pcu->device_no);
backlight->cdev.name = backlight->name;
backlight->cdev.max_brightness = IMS_PCU_MAX_BRIGHTNESS;
backlight->cdev.brightness_get = ims_pcu_backlight_get_brightness;
- backlight->cdev.brightness_set = ims_pcu_backlight_set_brightness;
+ backlight->cdev.brightness_set_blocking =
+ ims_pcu_backlight_set_brightness;
error = led_classdev_register(pcu->dev, &backlight->cdev);
if (error) {
@@ -1040,7 +1030,6 @@ static void ims_pcu_destroy_backlight(struct ims_pcu *pcu)
struct ims_pcu_backlight *backlight = &pcu->backlight;
led_classdev_unregister(&backlight->cdev);
- cancel_work_sync(&backlight->work);
}
diff --git a/drivers/input/misc/msm-vibrator.c b/drivers/input/misc/msm-vibrator.c
new file mode 100644
index 000000000000..b60f1aaee705
--- /dev/null
+++ b/drivers/input/misc/msm-vibrator.c
@@ -0,0 +1,281 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Qualcomm MSM vibrator driver
+ *
+ * Copyright (c) 2018 Brian Masney <masneyb@onstation.org>
+ *
+ * Based on qcom,pwm-vibrator.c from:
+ * Copyright (c) 2018 Jonathan Marek <jonathan@marek.ca>
+ *
+ * Based on msm_pwm_vibrator.c from downstream Android sources:
+ * Copyright (C) 2009-2014 LGE, Inc.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/input.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+
+#define REG_CMD_RCGR 0x00
+#define REG_CFG_RCGR 0x04
+#define REG_M 0x08
+#define REG_N 0x0C
+#define REG_D 0x10
+#define REG_CBCR 0x24
+#define MMSS_CC_M_DEFAULT 1
+
+struct msm_vibrator {
+ struct input_dev *input;
+ struct mutex mutex;
+ struct work_struct worker;
+ void __iomem *base;
+ struct regulator *vcc;
+ struct clk *clk;
+ struct gpio_desc *enable_gpio;
+ u16 magnitude;
+ bool enabled;
+};
+
+static void msm_vibrator_write(struct msm_vibrator *vibrator, int offset,
+ u32 value)
+{
+ writel(value, vibrator->base + offset);
+}
+
+static int msm_vibrator_start(struct msm_vibrator *vibrator)
+{
+ int d_reg_val, ret = 0;
+
+ mutex_lock(&vibrator->mutex);
+
+ if (!vibrator->enabled) {
+ ret = clk_set_rate(vibrator->clk, 24000);
+ if (ret) {
+ dev_err(&vibrator->input->dev,
+ "Failed to set clock rate: %d\n", ret);
+ goto unlock;
+ }
+
+ ret = clk_prepare_enable(vibrator->clk);
+ if (ret) {
+ dev_err(&vibrator->input->dev,
+ "Failed to enable clock: %d\n", ret);
+ goto unlock;
+ }
+
+ ret = regulator_enable(vibrator->vcc);
+ if (ret) {
+ dev_err(&vibrator->input->dev,
+ "Failed to enable regulator: %d\n", ret);
+ clk_disable(vibrator->clk);
+ goto unlock;
+ }
+
+ gpiod_set_value_cansleep(vibrator->enable_gpio, 1);
+
+ vibrator->enabled = true;
+ }
+
+ d_reg_val = 127 - ((126 * vibrator->magnitude) / 0xffff);
+ msm_vibrator_write(vibrator, REG_CFG_RCGR,
+ (2 << 12) | /* dual edge mode */
+ (0 << 8) | /* cxo */
+ (7 << 0));
+ msm_vibrator_write(vibrator, REG_M, 1);
+ msm_vibrator_write(vibrator, REG_N, 128);
+ msm_vibrator_write(vibrator, REG_D, d_reg_val);
+ msm_vibrator_write(vibrator, REG_CMD_RCGR, 1);
+ msm_vibrator_write(vibrator, REG_CBCR, 1);
+
+unlock:
+ mutex_unlock(&vibrator->mutex);
+
+ return ret;
+}
+
+static void msm_vibrator_stop(struct msm_vibrator *vibrator)
+{
+ mutex_lock(&vibrator->mutex);
+
+ if (vibrator->enabled) {
+ gpiod_set_value_cansleep(vibrator->enable_gpio, 0);
+ regulator_disable(vibrator->vcc);
+ clk_disable(vibrator->clk);
+ vibrator->enabled = false;
+ }
+
+ mutex_unlock(&vibrator->mutex);
+}
+
+static void msm_vibrator_worker(struct work_struct *work)
+{
+ struct msm_vibrator *vibrator = container_of(work,
+ struct msm_vibrator,
+ worker);
+
+ if (vibrator->magnitude)
+ msm_vibrator_start(vibrator);
+ else
+ msm_vibrator_stop(vibrator);
+}
+
+static int msm_vibrator_play_effect(struct input_dev *dev, void *data,
+ struct ff_effect *effect)
+{
+ struct msm_vibrator *vibrator = input_get_drvdata(dev);
+
+ mutex_lock(&vibrator->mutex);
+
+ if (effect->u.rumble.strong_magnitude > 0)
+ vibrator->magnitude = effect->u.rumble.strong_magnitude;
+ else
+ vibrator->magnitude = effect->u.rumble.weak_magnitude;
+
+ mutex_unlock(&vibrator->mutex);
+
+ schedule_work(&vibrator->worker);
+
+ return 0;
+}
+
+static void msm_vibrator_close(struct input_dev *input)
+{
+ struct msm_vibrator *vibrator = input_get_drvdata(input);
+
+ cancel_work_sync(&vibrator->worker);
+ msm_vibrator_stop(vibrator);
+}
+
+static int msm_vibrator_probe(struct platform_device *pdev)
+{
+ struct msm_vibrator *vibrator;
+ struct resource *res;
+ int ret;
+
+ vibrator = devm_kzalloc(&pdev->dev, sizeof(*vibrator), GFP_KERNEL);
+ if (!vibrator)
+ return -ENOMEM;
+
+ vibrator->input = devm_input_allocate_device(&pdev->dev);
+ if (!vibrator->input)
+ return -ENOMEM;
+
+ vibrator->vcc = devm_regulator_get(&pdev->dev, "vcc");
+ if (IS_ERR(vibrator->vcc)) {
+ if (PTR_ERR(vibrator->vcc) != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "Failed to get regulator: %ld\n",
+ PTR_ERR(vibrator->vcc));
+ return PTR_ERR(vibrator->vcc);
+ }
+
+ vibrator->enable_gpio = devm_gpiod_get(&pdev->dev, "enable",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(vibrator->enable_gpio)) {
+ if (PTR_ERR(vibrator->enable_gpio) != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "Failed to get enable gpio: %ld\n",
+ PTR_ERR(vibrator->enable_gpio));
+ return PTR_ERR(vibrator->enable_gpio);
+ }
+
+ vibrator->clk = devm_clk_get(&pdev->dev, "pwm");
+ if (IS_ERR(vibrator->clk)) {
+ if (PTR_ERR(vibrator->clk) != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "Failed to lookup pwm clock: %ld\n",
+ PTR_ERR(vibrator->clk));
+ return PTR_ERR(vibrator->clk);
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "Failed to get platform resource\n");
+ return -ENODEV;
+ }
+
+ vibrator->base = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!vibrator->base) {
+ dev_err(&pdev->dev, "Failed to iomap resource.\n");
+ return -ENOMEM;
+ }
+
+ vibrator->enabled = false;
+ mutex_init(&vibrator->mutex);
+ INIT_WORK(&vibrator->worker, msm_vibrator_worker);
+
+ vibrator->input->name = "msm-vibrator";
+ vibrator->input->id.bustype = BUS_HOST;
+ vibrator->input->close = msm_vibrator_close;
+
+ input_set_drvdata(vibrator->input, vibrator);
+ input_set_capability(vibrator->input, EV_FF, FF_RUMBLE);
+
+ ret = input_ff_create_memless(vibrator->input, NULL,
+ msm_vibrator_play_effect);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to create ff memless: %d", ret);
+ return ret;
+ }
+
+ ret = input_register_device(vibrator->input);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register input device: %d", ret);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, vibrator);
+
+ return 0;
+}
+
+static int __maybe_unused msm_vibrator_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct msm_vibrator *vibrator = platform_get_drvdata(pdev);
+
+ cancel_work_sync(&vibrator->worker);
+
+ if (vibrator->enabled)
+ msm_vibrator_stop(vibrator);
+
+ return 0;
+}
+
+static int __maybe_unused msm_vibrator_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct msm_vibrator *vibrator = platform_get_drvdata(pdev);
+
+ if (vibrator->enabled)
+ msm_vibrator_start(vibrator);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(msm_vibrator_pm_ops, msm_vibrator_suspend,
+ msm_vibrator_resume);
+
+static const struct of_device_id msm_vibrator_of_match[] = {
+ { .compatible = "qcom,msm8226-vibrator" },
+ { .compatible = "qcom,msm8974-vibrator" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, msm_vibrator_of_match);
+
+static struct platform_driver msm_vibrator_driver = {
+ .probe = msm_vibrator_probe,
+ .driver = {
+ .name = "msm-vibrator",
+ .pm = &msm_vibrator_pm_ops,
+ .of_match_table = of_match_ptr(msm_vibrator_of_match),
+ },
+};
+module_platform_driver(msm_vibrator_driver);
+
+MODULE_AUTHOR("Brian Masney <masneyb@onstation.org>");
+MODULE_DESCRIPTION("Qualcomm MSM vibrator driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/soc_button_array.c b/drivers/input/misc/soc_button_array.c
index 23520df7650f..bb458beecb43 100644
--- a/drivers/input/misc/soc_button_array.c
+++ b/drivers/input/misc/soc_button_array.c
@@ -185,6 +185,10 @@ static int soc_button_parse_btn_desc(struct device *dev,
info->name = "power";
info->event_code = KEY_POWER;
info->wakeup = true;
+ } else if (upage == 0x01 && usage == 0xca) {
+ info->name = "rotation lock switch";
+ info->event_type = EV_SW;
+ info->event_code = SW_ROTATE_LOCK;
} else if (upage == 0x07 && usage == 0xe3) {
info->name = "home";
info->event_code = KEY_LEFTMETA;
@@ -373,7 +377,7 @@ static struct soc_button_info soc_button_PNP0C40[] = {
{ "home", 1, EV_KEY, KEY_LEFTMETA, false, true },
{ "volume_up", 2, EV_KEY, KEY_VOLUMEUP, true, false },
{ "volume_down", 3, EV_KEY, KEY_VOLUMEDOWN, true, false },
- { "rotation_lock", 4, EV_SW, SW_ROTATE_LOCK, false, false },
+ { "rotation_lock", 4, EV_KEY, KEY_ROTATE_LOCK_TOGGLE, false, false },
{ }
};
diff --git a/drivers/input/misc/stpmic1_onkey.c b/drivers/input/misc/stpmic1_onkey.c
new file mode 100644
index 000000000000..7b49c9997df7
--- /dev/null
+++ b/drivers/input/misc/stpmic1_onkey.c
@@ -0,0 +1,198 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) STMicroelectronics 2018
+// Author: Pascal Paillet <p.paillet@st.com> for STMicroelectronics.
+
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/stpmic1.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+
+/**
+ * struct stpmic1_onkey - OnKey data
+ * @input_dev: pointer to input device
+ * @irq_falling: irq that we are hooked on to
+ * @irq_rising: irq that we are hooked on to
+ */
+struct stpmic1_onkey {
+ struct input_dev *input_dev;
+ int irq_falling;
+ int irq_rising;
+};
+
+static irqreturn_t onkey_falling_irq(int irq, void *ponkey)
+{
+ struct stpmic1_onkey *onkey = ponkey;
+ struct input_dev *input_dev = onkey->input_dev;
+
+ input_report_key(input_dev, KEY_POWER, 1);
+ pm_wakeup_event(input_dev->dev.parent, 0);
+ input_sync(input_dev);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t onkey_rising_irq(int irq, void *ponkey)
+{
+ struct stpmic1_onkey *onkey = ponkey;
+ struct input_dev *input_dev = onkey->input_dev;
+
+ input_report_key(input_dev, KEY_POWER, 0);
+ pm_wakeup_event(input_dev->dev.parent, 0);
+ input_sync(input_dev);
+
+ return IRQ_HANDLED;
+}
+
+static int stpmic1_onkey_probe(struct platform_device *pdev)
+{
+ struct stpmic1 *pmic = dev_get_drvdata(pdev->dev.parent);
+ struct device *dev = &pdev->dev;
+ struct input_dev *input_dev;
+ struct stpmic1_onkey *onkey;
+ unsigned int val, reg = 0;
+ int error;
+
+ onkey = devm_kzalloc(dev, sizeof(*onkey), GFP_KERNEL);
+ if (!onkey)
+ return -ENOMEM;
+
+ onkey->irq_falling = platform_get_irq_byname(pdev, "onkey-falling");
+ if (onkey->irq_falling < 0) {
+ dev_err(dev, "failed: request IRQ onkey-falling %d\n",
+ onkey->irq_falling);
+ return onkey->irq_falling;
+ }
+
+ onkey->irq_rising = platform_get_irq_byname(pdev, "onkey-rising");
+ if (onkey->irq_rising < 0) {
+ dev_err(dev, "failed: request IRQ onkey-rising %d\n",
+ onkey->irq_rising);
+ return onkey->irq_rising;
+ }
+
+ if (!device_property_read_u32(dev, "power-off-time-sec", &val)) {
+ if (val > 0 && val <= 16) {
+ dev_dbg(dev, "power-off-time=%d seconds\n", val);
+ reg |= PONKEY_PWR_OFF;
+ reg |= ((16 - val) & PONKEY_TURNOFF_TIMER_MASK);
+ } else {
+ dev_err(dev, "power-off-time-sec out of range\n");
+ return -EINVAL;
+ }
+ }
+
+ if (device_property_present(dev, "st,onkey-clear-cc-flag"))
+ reg |= PONKEY_CC_FLAG_CLEAR;
+
+ error = regmap_update_bits(pmic->regmap, PKEY_TURNOFF_CR,
+ PONKEY_TURNOFF_MASK, reg);
+ if (error) {
+ dev_err(dev, "PKEY_TURNOFF_CR write failed: %d\n", error);
+ return error;
+ }
+
+ if (device_property_present(dev, "st,onkey-pu-inactive")) {
+ error = regmap_update_bits(pmic->regmap, PADS_PULL_CR,
+ PONKEY_PU_INACTIVE,
+ PONKEY_PU_INACTIVE);
+ if (error) {
+ dev_err(dev, "ONKEY Pads configuration failed: %d\n",
+ error);
+ return error;
+ }
+ }
+
+ input_dev = devm_input_allocate_device(dev);
+ if (!input_dev) {
+ dev_err(dev, "Can't allocate Pwr Onkey Input Device\n");
+ return -ENOMEM;
+ }
+
+ input_dev->name = "pmic_onkey";
+ input_dev->phys = "pmic_onkey/input0";
+
+ input_set_capability(input_dev, EV_KEY, KEY_POWER);
+
+ onkey->input_dev = input_dev;
+
+ /* interrupt is nested in a thread */
+ error = devm_request_threaded_irq(dev, onkey->irq_falling, NULL,
+ onkey_falling_irq, IRQF_ONESHOT,
+ dev_name(dev), onkey);
+ if (error) {
+ dev_err(dev, "Can't get IRQ Onkey Falling: %d\n", error);
+ return error;
+ }
+
+ error = devm_request_threaded_irq(dev, onkey->irq_rising, NULL,
+ onkey_rising_irq, IRQF_ONESHOT,
+ dev_name(dev), onkey);
+ if (error) {
+ dev_err(dev, "Can't get IRQ Onkey Rising: %d\n", error);
+ return error;
+ }
+
+ error = input_register_device(input_dev);
+ if (error) {
+ dev_err(dev, "Can't register power button: %d\n", error);
+ return error;
+ }
+
+ platform_set_drvdata(pdev, onkey);
+ device_init_wakeup(dev, true);
+
+ return 0;
+}
+
+static int __maybe_unused stpmic1_onkey_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct stpmic1_onkey *onkey = platform_get_drvdata(pdev);
+
+ if (device_may_wakeup(dev)) {
+ enable_irq_wake(onkey->irq_falling);
+ enable_irq_wake(onkey->irq_rising);
+ }
+ return 0;
+}
+
+static int __maybe_unused stpmic1_onkey_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct stpmic1_onkey *onkey = platform_get_drvdata(pdev);
+
+ if (device_may_wakeup(dev)) {
+ disable_irq_wake(onkey->irq_falling);
+ disable_irq_wake(onkey->irq_rising);
+ }
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(stpmic1_onkey_pm,
+ stpmic1_onkey_suspend,
+ stpmic1_onkey_resume);
+
+static const struct of_device_id of_stpmic1_onkey_match[] = {
+ { .compatible = "st,stpmic1-onkey" },
+ { },
+};
+
+MODULE_DEVICE_TABLE(of, of_stpmic1_onkey_match);
+
+static struct platform_driver stpmic1_onkey_driver = {
+ .probe = stpmic1_onkey_probe,
+ .driver = {
+ .name = "stpmic1_onkey",
+ .of_match_table = of_match_ptr(of_stpmic1_onkey_match),
+ .pm = &stpmic1_onkey_pm,
+ },
+};
+module_platform_driver(stpmic1_onkey_driver);
+
+MODULE_DESCRIPTION("Onkey driver for STPMIC1");
+MODULE_AUTHOR("Pascal Paillet <p.paillet@st.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c
index 225ae6980182..628ef617bb2f 100644
--- a/drivers/input/mouse/elan_i2c_core.c
+++ b/drivers/input/mouse/elan_i2c_core.c
@@ -1337,6 +1337,7 @@ static const struct acpi_device_id elan_acpi_id[] = {
{ "ELAN0000", 0 },
{ "ELAN0100", 0 },
{ "ELAN0600", 0 },
+ { "ELAN0601", 0 },
{ "ELAN0602", 0 },
{ "ELAN0605", 0 },
{ "ELAN0608", 0 },
diff --git a/drivers/input/mouse/synaptics_i2c.c b/drivers/input/mouse/synaptics_i2c.c
index 8538318d332c..fa304648d611 100644
--- a/drivers/input/mouse/synaptics_i2c.c
+++ b/drivers/input/mouse/synaptics_i2c.c
@@ -219,7 +219,6 @@ struct synaptics_i2c {
struct i2c_client *client;
struct input_dev *input;
struct delayed_work dwork;
- spinlock_t lock;
int no_data_count;
int no_decel_param;
int reduce_report_param;
@@ -369,23 +368,11 @@ static bool synaptics_i2c_get_input(struct synaptics_i2c *touch)
return xy_delta || gesture;
}
-static void synaptics_i2c_reschedule_work(struct synaptics_i2c *touch,
- unsigned long delay)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&touch->lock, flags);
-
- mod_delayed_work(system_wq, &touch->dwork, delay);
-
- spin_unlock_irqrestore(&touch->lock, flags);
-}
-
static irqreturn_t synaptics_i2c_irq(int irq, void *dev_id)
{
struct synaptics_i2c *touch = dev_id;
- synaptics_i2c_reschedule_work(touch, 0);
+ mod_delayed_work(system_wq, &touch->dwork, 0);
return IRQ_HANDLED;
}
@@ -461,7 +448,7 @@ static void synaptics_i2c_work_handler(struct work_struct *work)
* We poll the device once in THREAD_IRQ_SLEEP_SECS and
* if error is detected, we try to reset and reconfigure the touchpad.
*/
- synaptics_i2c_reschedule_work(touch, delay);
+ mod_delayed_work(system_wq, &touch->dwork, delay);
}
static int synaptics_i2c_open(struct input_dev *input)
@@ -474,7 +461,7 @@ static int synaptics_i2c_open(struct input_dev *input)
return ret;
if (polling_req)
- synaptics_i2c_reschedule_work(touch,
+ mod_delayed_work(system_wq, &touch->dwork,
msecs_to_jiffies(NO_DATA_SLEEP_MSECS));
return 0;
@@ -530,7 +517,6 @@ static struct synaptics_i2c *synaptics_i2c_touch_create(struct i2c_client *clien
touch->scan_rate_param = scan_rate;
set_scan_rate(touch, scan_rate);
INIT_DELAYED_WORK(&touch->dwork, synaptics_i2c_work_handler);
- spin_lock_init(&touch->lock);
return touch;
}
@@ -637,7 +623,7 @@ static int __maybe_unused synaptics_i2c_resume(struct device *dev)
if (ret)
return ret;
- synaptics_i2c_reschedule_work(touch,
+ mod_delayed_work(system_wq, &touch->dwork,
msecs_to_jiffies(NO_DATA_SLEEP_MSECS));
return 0;
diff --git a/drivers/input/serio/i8042-sparcio.h b/drivers/input/serio/i8042-sparcio.h
index 796289846204..fce76812843b 100644
--- a/drivers/input/serio/i8042-sparcio.h
+++ b/drivers/input/serio/i8042-sparcio.h
@@ -53,12 +53,11 @@ static struct resource *kbd_res;
static int sparc_i8042_probe(struct platform_device *op)
{
- struct device_node *dp = op->dev.of_node;
+ struct device_node *dp;
- dp = dp->child;
- while (dp) {
- if (!strcmp(dp->name, OBP_PS2KBD_NAME1) ||
- !strcmp(dp->name, OBP_PS2KBD_NAME2)) {
+ for_each_child_of_node(op->dev.of_node, dp) {
+ if (of_node_name_eq(dp, OBP_PS2KBD_NAME1) ||
+ of_node_name_eq(dp, OBP_PS2KBD_NAME2)) {
struct platform_device *kbd = of_find_device_by_node(dp);
unsigned int irq = kbd->archdata.irqs[0];
if (irq == 0xffffffff)
@@ -67,16 +66,14 @@ static int sparc_i8042_probe(struct platform_device *op)
kbd_iobase = of_ioremap(&kbd->resource[0],
0, 8, "kbd");
kbd_res = &kbd->resource[0];
- } else if (!strcmp(dp->name, OBP_PS2MS_NAME1) ||
- !strcmp(dp->name, OBP_PS2MS_NAME2)) {
+ } else if (of_node_name_eq(dp, OBP_PS2MS_NAME1) ||
+ of_node_name_eq(dp, OBP_PS2MS_NAME2)) {
struct platform_device *ms = of_find_device_by_node(dp);
unsigned int irq = ms->archdata.irqs[0];
if (irq == 0xffffffff)
irq = op->archdata.irqs[0];
i8042_aux_irq = irq;
}
-
- dp = dp->sibling;
}
return 0;
@@ -109,8 +106,9 @@ static struct platform_driver sparc_i8042_driver = {
static int __init i8042_platform_init(void)
{
struct device_node *root = of_find_node_by_path("/");
+ const char *name = of_get_property(root, "name", NULL);
- if (!strcmp(root->name, "SUNW,JavaStation-1")) {
+ if (name && !strcmp(name, "SUNW,JavaStation-1")) {
/* Hardcoded values for MrCoffee. */
i8042_kbd_irq = i8042_aux_irq = 13 | 0x20;
kbd_iobase = ioremap(0x71300060, 8);
@@ -139,8 +137,9 @@ static int __init i8042_platform_init(void)
static inline void i8042_platform_exit(void)
{
struct device_node *root = of_find_node_by_path("/");
+ const char *name = of_get_property(root, "name", NULL);
- if (strcmp(root->name, "SUNW,JavaStation-1"))
+ if (!name || strcmp(name, "SUNW,JavaStation-1"))
platform_driver_unregister(&sparc_i8042_driver);
}
diff --git a/drivers/input/tablet/wacom_serial4.c b/drivers/input/tablet/wacom_serial4.c
index 38bfaca48eab..150f9eecaca7 100644
--- a/drivers/input/tablet/wacom_serial4.c
+++ b/drivers/input/tablet/wacom_serial4.c
@@ -187,6 +187,7 @@ enum {
MODEL_DIGITIZER_II = 0x5544, /* UD */
MODEL_GRAPHIRE = 0x4554, /* ET */
MODEL_PENPARTNER = 0x4354, /* CT */
+ MODEL_ARTPAD_II = 0x4B54, /* KT */
};
static void wacom_handle_model_response(struct wacom *wacom)
@@ -245,6 +246,7 @@ static void wacom_handle_model_response(struct wacom *wacom)
wacom->flags = F_HAS_STYLUS2 | F_HAS_SCROLLWHEEL;
break;
+ case MODEL_ARTPAD_II:
case MODEL_DIGITIZER_II:
wacom->dev->name = "Wacom Digitizer II";
wacom->dev->id.version = MODEL_DIGITIZER_II;
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 068dbbc610fc..7a4884ad198b 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -699,6 +699,7 @@ config TOUCHSCREEN_EDT_FT5X06
config TOUCHSCREEN_RASPBERRYPI_FW
tristate "Raspberry Pi's firmware base touch screen support"
depends on RASPBERRYPI_FIRMWARE || (RASPBERRYPI_FIRMWARE=n && COMPILE_TEST)
+ select INPUT_POLLDEV
help
Say Y here if you have the official Raspberry Pi 7 inch screen on
your system.
@@ -1168,11 +1169,11 @@ config TOUCHSCREEN_SIS_I2C
module will be called sis_i2c.
config TOUCHSCREEN_ST1232
- tristate "Sitronix ST1232 touchscreen controllers"
+ tristate "Sitronix ST1232 or ST1633 touchscreen controllers"
depends on I2C
help
- Say Y here if you want to support Sitronix ST1232
- touchscreen controller.
+ Say Y here if you want to support the Sitronix ST1232
+ or ST1633 touchscreen controller.
If unsure, say N.
diff --git a/drivers/input/touchscreen/ad7879.c b/drivers/input/touchscreen/ad7879.c
index 6fa714c587b4..3a016f43fb85 100644
--- a/drivers/input/touchscreen/ad7879.c
+++ b/drivers/input/touchscreen/ad7879.c
@@ -246,11 +246,14 @@ static void ad7879_timer(struct timer_list *t)
static irqreturn_t ad7879_irq(int irq, void *handle)
{
struct ad7879 *ts = handle;
+ int error;
- regmap_bulk_read(ts->regmap, AD7879_REG_XPLUS,
- ts->conversion_data, AD7879_NR_SENSE);
-
- if (!ad7879_report(ts))
+ error = regmap_bulk_read(ts->regmap, AD7879_REG_XPLUS,
+ ts->conversion_data, AD7879_NR_SENSE);
+ if (error)
+ dev_err_ratelimited(ts->dev, "failed to read %#02x: %d\n",
+ AD7879_REG_XPLUS, error);
+ else if (!ad7879_report(ts))
mod_timer(&ts->timer, jiffies + TS_PEN_UP_TIMEOUT);
return IRQ_HANDLED;
diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c
index 1e18ca0d1b4e..702bfda7ee77 100644
--- a/drivers/input/touchscreen/edt-ft5x06.c
+++ b/drivers/input/touchscreen/edt-ft5x06.c
@@ -31,6 +31,7 @@
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/i2c.h>
+#include <linux/kernel.h>
#include <linux/uaccess.h>
#include <linux/delay.h>
#include <linux/debugfs.h>
@@ -53,6 +54,11 @@
#define M09_REGISTER_NUM_X 0x94
#define M09_REGISTER_NUM_Y 0x95
+#define EV_REGISTER_THRESHOLD 0x40
+#define EV_REGISTER_GAIN 0x41
+#define EV_REGISTER_OFFSET_Y 0x45
+#define EV_REGISTER_OFFSET_X 0x46
+
#define NO_REGISTER 0xff
#define WORK_REGISTER_OPMODE 0x3c
@@ -73,6 +79,7 @@ enum edt_ver {
EDT_M06,
EDT_M09,
EDT_M12,
+ EV_FT,
GENERIC_FT,
};
@@ -81,6 +88,8 @@ struct edt_reg_addr {
int reg_report_rate;
int reg_gain;
int reg_offset;
+ int reg_offset_x;
+ int reg_offset_y;
int reg_num_x;
int reg_num_y;
};
@@ -106,6 +115,8 @@ struct edt_ft5x06_ts_data {
int threshold;
int gain;
int offset;
+ int offset_x;
+ int offset_y;
int report_rate;
int max_support_points;
@@ -190,6 +201,7 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id)
case EDT_M09:
case EDT_M12:
+ case EV_FT:
case GENERIC_FT:
cmd = 0x0;
offset = 3;
@@ -242,6 +254,10 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id)
x = ((buf[0] << 8) | buf[1]) & 0x0fff;
y = ((buf[2] << 8) | buf[3]) & 0x0fff;
+ /* The FT5x26 send the y coordinate first */
+ if (tsdata->version == EV_FT)
+ swap(x, y);
+
id = (buf[2] >> 4) & 0x0f;
down = type != TOUCH_EVENT_UP;
@@ -275,8 +291,10 @@ static int edt_ft5x06_register_write(struct edt_ft5x06_ts_data *tsdata,
wrbuf[3] = wrbuf[0] ^ wrbuf[1] ^ wrbuf[2];
return edt_ft5x06_ts_readwrite(tsdata->client, 4,
wrbuf, 0, NULL);
+ /* fallthrough */
case EDT_M09:
case EDT_M12:
+ case EV_FT:
case GENERIC_FT:
wrbuf[0] = addr;
wrbuf[1] = value;
@@ -315,8 +333,10 @@ static int edt_ft5x06_register_read(struct edt_ft5x06_ts_data *tsdata,
}
break;
+ /* fallthrough */
case EDT_M09:
case EDT_M12:
+ case EV_FT:
case GENERIC_FT:
wrbuf[0] = addr;
error = edt_ft5x06_ts_readwrite(tsdata->client, 1,
@@ -339,9 +359,10 @@ struct edt_ft5x06_attribute {
u8 limit_high;
u8 addr_m06;
u8 addr_m09;
+ u8 addr_ev;
};
-#define EDT_ATTR(_field, _mode, _addr_m06, _addr_m09, \
+#define EDT_ATTR(_field, _mode, _addr_m06, _addr_m09, _addr_ev, \
_limit_low, _limit_high) \
struct edt_ft5x06_attribute edt_ft5x06_attr_##_field = { \
.dattr = __ATTR(_field, _mode, \
@@ -350,6 +371,7 @@ struct edt_ft5x06_attribute {
.field_offset = offsetof(struct edt_ft5x06_ts_data, _field), \
.addr_m06 = _addr_m06, \
.addr_m09 = _addr_m09, \
+ .addr_ev = _addr_ev, \
.limit_low = _limit_low, \
.limit_high = _limit_high, \
}
@@ -386,6 +408,10 @@ static ssize_t edt_ft5x06_setting_show(struct device *dev,
addr = attr->addr_m09;
break;
+ case EV_FT:
+ addr = attr->addr_ev;
+ break;
+
default:
error = -ENODEV;
goto out;
@@ -457,6 +483,10 @@ static ssize_t edt_ft5x06_setting_store(struct device *dev,
addr = attr->addr_m09;
break;
+ case EV_FT:
+ addr = attr->addr_ev;
+ break;
+
default:
error = -ENODEV;
goto out;
@@ -480,20 +510,28 @@ out:
/* m06, m09: range 0-31, m12: range 0-5 */
static EDT_ATTR(gain, S_IWUSR | S_IRUGO, WORK_REGISTER_GAIN,
- M09_REGISTER_GAIN, 0, 31);
+ M09_REGISTER_GAIN, EV_REGISTER_GAIN, 0, 31);
/* m06, m09: range 0-31, m12: range 0-16 */
static EDT_ATTR(offset, S_IWUSR | S_IRUGO, WORK_REGISTER_OFFSET,
- M09_REGISTER_OFFSET, 0, 31);
+ M09_REGISTER_OFFSET, NO_REGISTER, 0, 31);
+/* m06, m09, m12: no supported, ev_ft: range 0-80 */
+static EDT_ATTR(offset_x, S_IWUSR | S_IRUGO, NO_REGISTER, NO_REGISTER,
+ EV_REGISTER_OFFSET_X, 0, 80);
+/* m06, m09, m12: no supported, ev_ft: range 0-80 */
+static EDT_ATTR(offset_y, S_IWUSR | S_IRUGO, NO_REGISTER, NO_REGISTER,
+ EV_REGISTER_OFFSET_Y, 0, 80);
/* m06: range 20 to 80, m09: range 0 to 30, m12: range 1 to 255... */
static EDT_ATTR(threshold, S_IWUSR | S_IRUGO, WORK_REGISTER_THRESHOLD,
- M09_REGISTER_THRESHOLD, 0, 255);
+ M09_REGISTER_THRESHOLD, EV_REGISTER_THRESHOLD, 0, 255);
/* m06: range 3 to 14, m12: (0x64: 100Hz) */
static EDT_ATTR(report_rate, S_IWUSR | S_IRUGO, WORK_REGISTER_REPORT_RATE,
- NO_REGISTER, 0, 255);
+ NO_REGISTER, NO_REGISTER, 0, 255);
static struct attribute *edt_ft5x06_attrs[] = {
&edt_ft5x06_attr_gain.dattr.attr,
&edt_ft5x06_attr_offset.dattr.attr,
+ &edt_ft5x06_attr_offset_x.dattr.attr,
+ &edt_ft5x06_attr_offset_y.dattr.attr,
&edt_ft5x06_attr_threshold.dattr.attr,
&edt_ft5x06_attr_report_rate.dattr.attr,
NULL
@@ -605,8 +643,15 @@ static int edt_ft5x06_work_mode(struct edt_ft5x06_ts_data *tsdata)
tsdata->threshold);
edt_ft5x06_register_write(tsdata, reg_addr->reg_gain,
tsdata->gain);
- edt_ft5x06_register_write(tsdata, reg_addr->reg_offset,
- tsdata->offset);
+ if (reg_addr->reg_offset != NO_REGISTER)
+ edt_ft5x06_register_write(tsdata, reg_addr->reg_offset,
+ tsdata->offset);
+ if (reg_addr->reg_offset_x != NO_REGISTER)
+ edt_ft5x06_register_write(tsdata, reg_addr->reg_offset_x,
+ tsdata->offset_x);
+ if (reg_addr->reg_offset_y != NO_REGISTER)
+ edt_ft5x06_register_write(tsdata, reg_addr->reg_offset_y,
+ tsdata->offset_y);
if (reg_addr->reg_report_rate != NO_REGISTER)
edt_ft5x06_register_write(tsdata, reg_addr->reg_report_rate,
tsdata->report_rate);
@@ -867,6 +912,16 @@ static int edt_ft5x06_ts_identify(struct i2c_client *client,
case 0x5a: /* Solomon Goldentek Display */
snprintf(model_name, EDT_NAME_LEN, "GKTW50SCED1R0");
break;
+ case 0x59: /* Evervision Display with FT5xx6 TS */
+ tsdata->version = EV_FT;
+ error = edt_ft5x06_ts_readwrite(client, 1, "\x53",
+ 1, rdbuf);
+ if (error)
+ return error;
+ strlcpy(fw_version, rdbuf, 1);
+ snprintf(model_name, EDT_NAME_LEN,
+ "EVERVISION-FT5726NEi");
+ break;
default:
snprintf(model_name, EDT_NAME_LEN,
"generic ft5x06 (%02x)",
@@ -902,6 +957,18 @@ static void edt_ft5x06_ts_get_defaults(struct device *dev,
edt_ft5x06_register_write(tsdata, reg_addr->reg_offset, val);
tsdata->offset = val;
}
+
+ error = device_property_read_u32(dev, "offset-x", &val);
+ if (!error) {
+ edt_ft5x06_register_write(tsdata, reg_addr->reg_offset_x, val);
+ tsdata->offset_x = val;
+ }
+
+ error = device_property_read_u32(dev, "offset-y", &val);
+ if (!error) {
+ edt_ft5x06_register_write(tsdata, reg_addr->reg_offset_y, val);
+ tsdata->offset_y = val;
+ }
}
static void
@@ -912,7 +979,15 @@ edt_ft5x06_ts_get_parameters(struct edt_ft5x06_ts_data *tsdata)
tsdata->threshold = edt_ft5x06_register_read(tsdata,
reg_addr->reg_threshold);
tsdata->gain = edt_ft5x06_register_read(tsdata, reg_addr->reg_gain);
- tsdata->offset = edt_ft5x06_register_read(tsdata, reg_addr->reg_offset);
+ if (reg_addr->reg_offset != NO_REGISTER)
+ tsdata->offset =
+ edt_ft5x06_register_read(tsdata, reg_addr->reg_offset);
+ if (reg_addr->reg_offset_x != NO_REGISTER)
+ tsdata->offset_x = edt_ft5x06_register_read(tsdata,
+ reg_addr->reg_offset_x);
+ if (reg_addr->reg_offset_y != NO_REGISTER)
+ tsdata->offset_y = edt_ft5x06_register_read(tsdata,
+ reg_addr->reg_offset_y);
if (reg_addr->reg_report_rate != NO_REGISTER)
tsdata->report_rate = edt_ft5x06_register_read(tsdata,
reg_addr->reg_report_rate);
@@ -940,6 +1015,8 @@ edt_ft5x06_ts_set_regs(struct edt_ft5x06_ts_data *tsdata)
reg_addr->reg_report_rate = WORK_REGISTER_REPORT_RATE;
reg_addr->reg_gain = WORK_REGISTER_GAIN;
reg_addr->reg_offset = WORK_REGISTER_OFFSET;
+ reg_addr->reg_offset_x = NO_REGISTER;
+ reg_addr->reg_offset_y = NO_REGISTER;
reg_addr->reg_num_x = WORK_REGISTER_NUM_X;
reg_addr->reg_num_y = WORK_REGISTER_NUM_Y;
break;
@@ -950,15 +1027,30 @@ edt_ft5x06_ts_set_regs(struct edt_ft5x06_ts_data *tsdata)
reg_addr->reg_report_rate = NO_REGISTER;
reg_addr->reg_gain = M09_REGISTER_GAIN;
reg_addr->reg_offset = M09_REGISTER_OFFSET;
+ reg_addr->reg_offset_x = NO_REGISTER;
+ reg_addr->reg_offset_y = NO_REGISTER;
reg_addr->reg_num_x = M09_REGISTER_NUM_X;
reg_addr->reg_num_y = M09_REGISTER_NUM_Y;
break;
+ case EV_FT:
+ reg_addr->reg_threshold = EV_REGISTER_THRESHOLD;
+ reg_addr->reg_gain = EV_REGISTER_GAIN;
+ reg_addr->reg_offset = NO_REGISTER;
+ reg_addr->reg_offset_x = EV_REGISTER_OFFSET_X;
+ reg_addr->reg_offset_y = EV_REGISTER_OFFSET_Y;
+ reg_addr->reg_num_x = NO_REGISTER;
+ reg_addr->reg_num_y = NO_REGISTER;
+ reg_addr->reg_report_rate = NO_REGISTER;
+ break;
+
case GENERIC_FT:
/* this is a guesswork */
reg_addr->reg_threshold = M09_REGISTER_THRESHOLD;
reg_addr->reg_gain = M09_REGISTER_GAIN;
reg_addr->reg_offset = M09_REGISTER_OFFSET;
+ reg_addr->reg_offset_x = NO_REGISTER;
+ reg_addr->reg_offset_y = NO_REGISTER;
break;
}
}
@@ -1155,6 +1247,7 @@ static const struct edt_i2c_chip_data edt_ft6236_data = {
static const struct i2c_device_id edt_ft5x06_ts_id[] = {
{ .name = "edt-ft5x06", .driver_data = (long)&edt_ft5x06_data },
{ .name = "edt-ft5506", .driver_data = (long)&edt_ft5506_data },
+ { .name = "ev-ft5726", .driver_data = (long)&edt_ft5506_data },
/* Note no edt- prefix for compatibility with the ft6236.c driver */
{ .name = "ft6236", .driver_data = (long)&edt_ft6236_data },
{ /* sentinel */ }
@@ -1167,6 +1260,7 @@ static const struct of_device_id edt_ft5x06_of_match[] = {
{ .compatible = "edt,edt-ft5306", .data = &edt_ft5x06_data },
{ .compatible = "edt,edt-ft5406", .data = &edt_ft5x06_data },
{ .compatible = "edt,edt-ft5506", .data = &edt_ft5506_data },
+ { .compatible = "evervision,ev-ft5726", .data = &edt_ft5506_data },
/* Note focaltech vendor prefix for compatibility with ft6236.c */
{ .compatible = "focaltech,ft6236", .data = &edt_ft6236_data },
{ /* sentinel */ }
diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c
index f2d9c2c41885..f57d82220a88 100644
--- a/drivers/input/touchscreen/goodix.c
+++ b/drivers/input/touchscreen/goodix.c
@@ -216,6 +216,7 @@ static const struct goodix_chip_data *goodix_get_chip_data(u16 id)
{
switch (id) {
case 1151:
+ case 5688:
return &gt1x_chip_data;
case 911:
@@ -692,7 +693,9 @@ static int goodix_configure_dev(struct goodix_ts_data *ts)
touchscreen_parse_properties(ts->input_dev, true, &ts->prop);
if (!ts->prop.max_x || !ts->prop.max_y || !ts->max_touch_num) {
- dev_err(&ts->client->dev, "Invalid config, using defaults\n");
+ dev_err(&ts->client->dev,
+ "Invalid config (%d, %d, %d), using defaults\n",
+ ts->prop.max_x, ts->prop.max_y, ts->max_touch_num);
ts->prop.max_x = GOODIX_MAX_WIDTH - 1;
ts->prop.max_y = GOODIX_MAX_HEIGHT - 1;
ts->max_touch_num = GOODIX_MAX_CONTACTS;
@@ -942,6 +945,7 @@ MODULE_DEVICE_TABLE(acpi, goodix_acpi_match);
#ifdef CONFIG_OF
static const struct of_device_id goodix_of_match[] = {
{ .compatible = "goodix,gt1151" },
+ { .compatible = "goodix,gt5688" },
{ .compatible = "goodix,gt911" },
{ .compatible = "goodix,gt9110" },
{ .compatible = "goodix,gt912" },
diff --git a/drivers/input/touchscreen/ili210x.c b/drivers/input/touchscreen/ili210x.c
index 6f76eeedf465..9169aa03958a 100644
--- a/drivers/input/touchscreen/ili210x.c
+++ b/drivers/input/touchscreen/ili210x.c
@@ -4,11 +4,15 @@
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/input/mt.h>
+#include <linux/input/touchscreen.h>
#include <linux/delay.h>
#include <linux/workqueue.h>
-#include <linux/input/ili210x.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of_device.h>
+#include <asm/unaligned.h>
-#define MAX_TOUCHES 2
+#define ILI210X_TOUCHES 2
+#define ILI251X_TOUCHES 10
#define DEFAULT_POLL_PERIOD 20
/* Touchscreen commands */
@@ -17,41 +21,32 @@
#define REG_FIRMWARE_VERSION 0x40
#define REG_CALIBRATE 0xcc
-struct finger {
- u8 x_low;
- u8 x_high;
- u8 y_low;
- u8 y_high;
-} __packed;
-
-struct touchdata {
- u8 status;
- struct finger finger[MAX_TOUCHES];
-} __packed;
-
-struct panel_info {
- struct finger finger_max;
- u8 xchannel_num;
- u8 ychannel_num;
-} __packed;
-
struct firmware_version {
u8 id;
u8 major;
u8 minor;
} __packed;
+enum ili2xxx_model {
+ MODEL_ILI210X,
+ MODEL_ILI251X,
+};
+
struct ili210x {
struct i2c_client *client;
struct input_dev *input;
- bool (*get_pendown_state)(void);
unsigned int poll_period;
struct delayed_work dwork;
+ struct gpio_desc *reset_gpio;
+ struct touchscreen_properties prop;
+ enum ili2xxx_model model;
+ unsigned int max_touches;
};
static int ili210x_read_reg(struct i2c_client *client, u8 reg, void *buf,
size_t len)
{
+ struct ili210x *priv = i2c_get_clientdata(client);
struct i2c_msg msg[2] = {
{
.addr = client->addr,
@@ -67,7 +62,38 @@ static int ili210x_read_reg(struct i2c_client *client, u8 reg, void *buf,
}
};
- if (i2c_transfer(client->adapter, msg, 2) != 2) {
+ if (priv->model == MODEL_ILI251X) {
+ if (i2c_transfer(client->adapter, msg, 1) != 1) {
+ dev_err(&client->dev, "i2c transfer failed\n");
+ return -EIO;
+ }
+
+ usleep_range(5000, 5500);
+
+ if (i2c_transfer(client->adapter, msg + 1, 1) != 1) {
+ dev_err(&client->dev, "i2c transfer failed\n");
+ return -EIO;
+ }
+ } else {
+ if (i2c_transfer(client->adapter, msg, 2) != 2) {
+ dev_err(&client->dev, "i2c transfer failed\n");
+ return -EIO;
+ }
+ }
+
+ return 0;
+}
+
+static int ili210x_read(struct i2c_client *client, void *buf, size_t len)
+{
+ struct i2c_msg msg = {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = len,
+ .buf = buf,
+ };
+
+ if (i2c_transfer(client->adapter, &msg, 1) != 1) {
dev_err(&client->dev, "i2c transfer failed\n");
return -EIO;
}
@@ -75,42 +101,72 @@ static int ili210x_read_reg(struct i2c_client *client, u8 reg, void *buf,
return 0;
}
-static void ili210x_report_events(struct input_dev *input,
- const struct touchdata *touchdata)
+static bool ili210x_touchdata_to_coords(struct ili210x *priv, u8 *touchdata,
+ unsigned int finger,
+ unsigned int *x, unsigned int *y)
{
- int i;
- bool touch;
- unsigned int x, y;
- const struct finger *finger;
+ if (finger >= ILI210X_TOUCHES)
+ return false;
- for (i = 0; i < MAX_TOUCHES; i++) {
- input_mt_slot(input, i);
+ if (touchdata[0] & BIT(finger))
+ return false;
- finger = &touchdata->finger[i];
+ *x = get_unaligned_be16(touchdata + 1 + (finger * 4) + 0);
+ *y = get_unaligned_be16(touchdata + 1 + (finger * 4) + 2);
- touch = touchdata->status & (1 << i);
- input_mt_report_slot_state(input, MT_TOOL_FINGER, touch);
- if (touch) {
- x = finger->x_low | (finger->x_high << 8);
- y = finger->y_low | (finger->y_high << 8);
+ return true;
+}
+
+static bool ili251x_touchdata_to_coords(struct ili210x *priv, u8 *touchdata,
+ unsigned int finger,
+ unsigned int *x, unsigned int *y)
+{
+ if (finger >= ILI251X_TOUCHES)
+ return false;
+
+ *x = get_unaligned_be16(touchdata + 1 + (finger * 5) + 0);
+ if (!(*x & BIT(15))) /* Touch indication */
+ return false;
- input_report_abs(input, ABS_MT_POSITION_X, x);
- input_report_abs(input, ABS_MT_POSITION_Y, y);
+ *x &= 0x3fff;
+ *y = get_unaligned_be16(touchdata + 1 + (finger * 5) + 2);
+
+ return true;
+}
+
+static bool ili210x_report_events(struct ili210x *priv, u8 *touchdata)
+{
+ struct input_dev *input = priv->input;
+ int i;
+ bool contact = false, touch = false;
+ unsigned int x = 0, y = 0;
+
+ for (i = 0; i < priv->max_touches; i++) {
+ if (priv->model == MODEL_ILI210X) {
+ touch = ili210x_touchdata_to_coords(priv, touchdata,
+ i, &x, &y);
+ } else if (priv->model == MODEL_ILI251X) {
+ touch = ili251x_touchdata_to_coords(priv, touchdata,
+ i, &x, &y);
+ if (touch)
+ contact = true;
}
+
+ input_mt_slot(input, i);
+ input_mt_report_slot_state(input, MT_TOOL_FINGER, touch);
+ if (!touch)
+ continue;
+ touchscreen_report_pos(input, &priv->prop, x, y,
+ true);
}
input_mt_report_pointer_emulation(input, false);
input_sync(input);
-}
-static bool get_pendown_state(const struct ili210x *priv)
-{
- bool state = false;
-
- if (priv->get_pendown_state)
- state = priv->get_pendown_state();
+ if (priv->model == MODEL_ILI210X)
+ contact = touchdata[0] & 0xf3;
- return state;
+ return contact;
}
static void ili210x_work(struct work_struct *work)
@@ -118,20 +174,29 @@ static void ili210x_work(struct work_struct *work)
struct ili210x *priv = container_of(work, struct ili210x,
dwork.work);
struct i2c_client *client = priv->client;
- struct touchdata touchdata;
- int error;
+ u8 touchdata[64] = { 0 };
+ bool touch;
+ int error = -EINVAL;
+
+ if (priv->model == MODEL_ILI210X) {
+ error = ili210x_read_reg(client, REG_TOUCHDATA,
+ touchdata, sizeof(touchdata));
+ } else if (priv->model == MODEL_ILI251X) {
+ error = ili210x_read_reg(client, REG_TOUCHDATA,
+ touchdata, 31);
+ if (!error && touchdata[0] == 2)
+ error = ili210x_read(client, &touchdata[31], 20);
+ }
- error = ili210x_read_reg(client, REG_TOUCHDATA,
- &touchdata, sizeof(touchdata));
if (error) {
dev_err(&client->dev,
"Unable to get touchdata, err = %d\n", error);
return;
}
- ili210x_report_events(priv->input, &touchdata);
+ touch = ili210x_report_events(priv, touchdata);
- if ((touchdata.status & 0xf3) || get_pendown_state(priv))
+ if (touch)
schedule_delayed_work(&priv->dwork,
msecs_to_jiffies(priv->poll_period));
}
@@ -180,103 +245,119 @@ static const struct attribute_group ili210x_attr_group = {
.attrs = ili210x_attributes,
};
+static void ili210x_power_down(void *data)
+{
+ struct gpio_desc *reset_gpio = data;
+
+ gpiod_set_value_cansleep(reset_gpio, 1);
+}
+
+static void ili210x_cancel_work(void *data)
+{
+ struct ili210x *priv = data;
+
+ cancel_delayed_work_sync(&priv->dwork);
+}
+
static int ili210x_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
- const struct ili210x_platform_data *pdata = dev_get_platdata(dev);
struct ili210x *priv;
+ struct gpio_desc *reset_gpio;
struct input_dev *input;
- struct panel_info panel;
struct firmware_version firmware;
- int xmax, ymax;
+ enum ili2xxx_model model;
int error;
- dev_dbg(dev, "Probing for ILI210X I2C Touschreen driver");
+ model = (enum ili2xxx_model)id->driver_data;
- if (!pdata) {
- dev_err(dev, "No platform data!\n");
- return -EINVAL;
- }
+ dev_dbg(dev, "Probing for ILI210X I2C Touschreen driver");
if (client->irq <= 0) {
dev_err(dev, "No IRQ!\n");
return -EINVAL;
}
- /* Get firmware version */
- error = ili210x_read_reg(client, REG_FIRMWARE_VERSION,
- &firmware, sizeof(firmware));
- if (error) {
- dev_err(dev, "Failed to get firmware version, err: %d\n",
- error);
- return error;
- }
+ reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(reset_gpio))
+ return PTR_ERR(reset_gpio);
- /* get panel info */
- error = ili210x_read_reg(client, REG_PANEL_INFO, &panel, sizeof(panel));
- if (error) {
- dev_err(dev, "Failed to get panel information, err: %d\n",
- error);
- return error;
+ if (reset_gpio) {
+ error = devm_add_action_or_reset(dev, ili210x_power_down,
+ reset_gpio);
+ if (error)
+ return error;
+
+ usleep_range(50, 100);
+ gpiod_set_value_cansleep(reset_gpio, 0);
+ msleep(100);
}
- xmax = panel.finger_max.x_low | (panel.finger_max.x_high << 8);
- ymax = panel.finger_max.y_low | (panel.finger_max.y_high << 8);
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
- priv = kzalloc(sizeof(*priv), GFP_KERNEL);
- input = input_allocate_device();
- if (!priv || !input) {
- error = -ENOMEM;
- goto err_free_mem;
- }
+ input = devm_input_allocate_device(dev);
+ if (!input)
+ return -ENOMEM;
priv->client = client;
priv->input = input;
- priv->get_pendown_state = pdata->get_pendown_state;
- priv->poll_period = pdata->poll_period ? : DEFAULT_POLL_PERIOD;
+ priv->poll_period = DEFAULT_POLL_PERIOD;
INIT_DELAYED_WORK(&priv->dwork, ili210x_work);
+ priv->reset_gpio = reset_gpio;
+ priv->model = model;
+ if (model == MODEL_ILI210X)
+ priv->max_touches = ILI210X_TOUCHES;
+ if (model == MODEL_ILI251X)
+ priv->max_touches = ILI251X_TOUCHES;
+
+ i2c_set_clientdata(client, priv);
+
+ /* Get firmware version */
+ error = ili210x_read_reg(client, REG_FIRMWARE_VERSION,
+ &firmware, sizeof(firmware));
+ if (error) {
+ dev_err(dev, "Failed to get firmware version, err: %d\n",
+ error);
+ return error;
+ }
/* Setup input device */
input->name = "ILI210x Touchscreen";
input->id.bustype = BUS_I2C;
input->dev.parent = dev;
- __set_bit(EV_SYN, input->evbit);
- __set_bit(EV_KEY, input->evbit);
- __set_bit(EV_ABS, input->evbit);
- __set_bit(BTN_TOUCH, input->keybit);
-
- /* Single touch */
- input_set_abs_params(input, ABS_X, 0, xmax, 0, 0);
- input_set_abs_params(input, ABS_Y, 0, ymax, 0, 0);
-
/* Multi touch */
- input_mt_init_slots(input, MAX_TOUCHES, 0);
- input_set_abs_params(input, ABS_MT_POSITION_X, 0, xmax, 0, 0);
- input_set_abs_params(input, ABS_MT_POSITION_Y, 0, ymax, 0, 0);
+ input_set_abs_params(input, ABS_MT_POSITION_X, 0, 0xffff, 0, 0);
+ input_set_abs_params(input, ABS_MT_POSITION_Y, 0, 0xffff, 0, 0);
+ touchscreen_parse_properties(input, true, &priv->prop);
+ input_mt_init_slots(input, priv->max_touches, INPUT_MT_DIRECT);
- i2c_set_clientdata(client, priv);
+ error = devm_add_action(dev, ili210x_cancel_work, priv);
+ if (error)
+ return error;
- error = request_irq(client->irq, ili210x_irq, pdata->irq_flags,
- client->name, priv);
+ error = devm_request_irq(dev, client->irq, ili210x_irq, 0,
+ client->name, priv);
if (error) {
dev_err(dev, "Unable to request touchscreen IRQ, err: %d\n",
error);
- goto err_free_mem;
+ return error;
}
- error = sysfs_create_group(&dev->kobj, &ili210x_attr_group);
+ error = devm_device_add_group(dev, &ili210x_attr_group);
if (error) {
dev_err(dev, "Unable to create sysfs attributes, err: %d\n",
error);
- goto err_free_irq;
+ return error;
}
error = input_register_device(priv->input);
if (error) {
dev_err(dev, "Cannot register input device, err: %d\n", error);
- goto err_remove_sysfs;
+ return error;
}
device_init_wakeup(dev, 1);
@@ -286,28 +367,6 @@ static int ili210x_i2c_probe(struct i2c_client *client,
client->irq, firmware.id, firmware.major, firmware.minor);
return 0;
-
-err_remove_sysfs:
- sysfs_remove_group(&dev->kobj, &ili210x_attr_group);
-err_free_irq:
- free_irq(client->irq, priv);
-err_free_mem:
- input_free_device(input);
- kfree(priv);
- return error;
-}
-
-static int ili210x_i2c_remove(struct i2c_client *client)
-{
- struct ili210x *priv = i2c_get_clientdata(client);
-
- sysfs_remove_group(&client->dev.kobj, &ili210x_attr_group);
- free_irq(priv->client->irq, priv);
- cancel_delayed_work_sync(&priv->dwork);
- input_unregister_device(priv->input);
- kfree(priv);
-
- return 0;
}
static int __maybe_unused ili210x_i2c_suspend(struct device *dev)
@@ -334,19 +393,27 @@ static SIMPLE_DEV_PM_OPS(ili210x_i2c_pm,
ili210x_i2c_suspend, ili210x_i2c_resume);
static const struct i2c_device_id ili210x_i2c_id[] = {
- { "ili210x", 0 },
+ { "ili210x", MODEL_ILI210X },
+ { "ili251x", MODEL_ILI251X },
{ }
};
MODULE_DEVICE_TABLE(i2c, ili210x_i2c_id);
+static const struct of_device_id ili210x_dt_ids[] = {
+ { .compatible = "ilitek,ili210x", .data = (void *)MODEL_ILI210X },
+ { .compatible = "ilitek,ili251x", .data = (void *)MODEL_ILI251X },
+ { },
+};
+MODULE_DEVICE_TABLE(of, ili210x_dt_ids);
+
static struct i2c_driver ili210x_ts_driver = {
.driver = {
.name = "ili210x_i2c",
.pm = &ili210x_i2c_pm,
+ .of_match_table = ili210x_dt_ids,
},
.id_table = ili210x_i2c_id,
.probe = ili210x_i2c_probe,
- .remove = ili210x_i2c_remove,
};
module_i2c_driver(ili210x_ts_driver);
diff --git a/drivers/input/touchscreen/st1232.c b/drivers/input/touchscreen/st1232.c
index 11ff32c68025..34923399ece4 100644
--- a/drivers/input/touchscreen/st1232.c
+++ b/drivers/input/touchscreen/st1232.c
@@ -11,25 +11,19 @@
*/
#include <linux/delay.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of.h>
-#include <linux/of_gpio.h>
#include <linux/pm_qos.h>
#include <linux/slab.h>
#include <linux/types.h>
+#include <linux/input/touchscreen.h>
#define ST1232_TS_NAME "st1232-ts"
-
-#define MIN_X 0x00
-#define MIN_Y 0x00
-#define MAX_X 0x31f /* (800 - 1) */
-#define MAX_Y 0x1df /* (480 - 1) */
-#define MAX_AREA 0xff
-#define MAX_FINGERS 2
+#define ST1633_TS_NAME "st1633-ts"
struct st1232_ts_finger {
u16 x;
@@ -38,12 +32,25 @@ struct st1232_ts_finger {
bool is_valid;
};
+struct st_chip_info {
+ bool have_z;
+ u16 max_x;
+ u16 max_y;
+ u16 max_area;
+ u16 max_fingers;
+ u8 start_reg;
+};
+
struct st1232_ts_data {
struct i2c_client *client;
struct input_dev *input_dev;
- struct st1232_ts_finger finger[MAX_FINGERS];
+ struct touchscreen_properties prop;
struct dev_pm_qos_request low_latency_req;
- int reset_gpio;
+ struct gpio_desc *reset_gpio;
+ const struct st_chip_info *chip_info;
+ int read_buf_len;
+ u8 *read_buf;
+ struct st1232_ts_finger *finger;
};
static int st1232_ts_read_data(struct st1232_ts_data *ts)
@@ -52,40 +59,35 @@ static int st1232_ts_read_data(struct st1232_ts_data *ts)
struct i2c_client *client = ts->client;
struct i2c_msg msg[2];
int error;
- u8 start_reg;
- u8 buf[10];
+ int i, y;
+ u8 start_reg = ts->chip_info->start_reg;
+ u8 *buf = ts->read_buf;
- /* read touchscreen data from ST1232 */
+ /* read touchscreen data */
msg[0].addr = client->addr;
msg[0].flags = 0;
msg[0].len = 1;
msg[0].buf = &start_reg;
- start_reg = 0x10;
msg[1].addr = ts->client->addr;
msg[1].flags = I2C_M_RD;
- msg[1].len = sizeof(buf);
+ msg[1].len = ts->read_buf_len;
msg[1].buf = buf;
error = i2c_transfer(client->adapter, msg, 2);
if (error < 0)
return error;
- /* get "valid" bits */
- finger[0].is_valid = buf[2] >> 7;
- finger[1].is_valid = buf[5] >> 7;
+ for (i = 0, y = 0; i < ts->chip_info->max_fingers; i++, y += 3) {
+ finger[i].is_valid = buf[i + y] >> 7;
+ if (finger[i].is_valid) {
+ finger[i].x = ((buf[i + y] & 0x0070) << 4) | buf[i + 1];
+ finger[i].y = ((buf[i + y] & 0x0007) << 8) | buf[i + 2];
- /* get xy coordinate */
- if (finger[0].is_valid) {
- finger[0].x = ((buf[2] & 0x0070) << 4) | buf[3];
- finger[0].y = ((buf[2] & 0x0007) << 8) | buf[4];
- finger[0].t = buf[8];
- }
-
- if (finger[1].is_valid) {
- finger[1].x = ((buf[5] & 0x0070) << 4) | buf[6];
- finger[1].y = ((buf[5] & 0x0007) << 8) | buf[7];
- finger[1].t = buf[9];
+ /* st1232 includes a z-axis / touch strength */
+ if (ts->chip_info->have_z)
+ finger[i].t = buf[i + 6];
+ }
}
return 0;
@@ -104,13 +106,16 @@ static irqreturn_t st1232_ts_irq_handler(int irq, void *dev_id)
goto end;
/* multi touch protocol */
- for (i = 0; i < MAX_FINGERS; i++) {
+ for (i = 0; i < ts->chip_info->max_fingers; i++) {
if (!finger[i].is_valid)
continue;
- input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, finger[i].t);
- input_report_abs(input_dev, ABS_MT_POSITION_X, finger[i].x);
- input_report_abs(input_dev, ABS_MT_POSITION_Y, finger[i].y);
+ if (ts->chip_info->have_z)
+ input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR,
+ finger[i].t);
+
+ touchscreen_report_pos(input_dev, &ts->prop,
+ finger[i].x, finger[i].y, true);
input_mt_sync(input_dev);
count++;
}
@@ -138,17 +143,45 @@ end:
static void st1232_ts_power(struct st1232_ts_data *ts, bool poweron)
{
- if (gpio_is_valid(ts->reset_gpio))
- gpio_direction_output(ts->reset_gpio, poweron);
+ if (ts->reset_gpio)
+ gpiod_set_value_cansleep(ts->reset_gpio, !poweron);
}
+static const struct st_chip_info st1232_chip_info = {
+ .have_z = true,
+ .max_x = 0x31f, /* 800 - 1 */
+ .max_y = 0x1df, /* 480 -1 */
+ .max_area = 0xff,
+ .max_fingers = 2,
+ .start_reg = 0x12,
+};
+
+static const struct st_chip_info st1633_chip_info = {
+ .have_z = false,
+ .max_x = 0x13f, /* 320 - 1 */
+ .max_y = 0x1df, /* 480 -1 */
+ .max_area = 0x00,
+ .max_fingers = 5,
+ .start_reg = 0x12,
+};
+
static int st1232_ts_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
+ const struct st_chip_info *match;
struct st1232_ts_data *ts;
+ struct st1232_ts_finger *finger;
struct input_dev *input_dev;
int error;
+ match = device_get_match_data(&client->dev);
+ if (!match && id)
+ match = (const void *)id->driver_data;
+ if (!match) {
+ dev_err(&client->dev, "unknown device model\n");
+ return -ENODEV;
+ }
+
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
dev_err(&client->dev, "need I2C_FUNC_I2C\n");
return -EIO;
@@ -163,6 +196,19 @@ static int st1232_ts_probe(struct i2c_client *client,
if (!ts)
return -ENOMEM;
+ ts->chip_info = match;
+ ts->finger = devm_kcalloc(&client->dev,
+ ts->chip_info->max_fingers, sizeof(*finger),
+ GFP_KERNEL);
+ if (!ts->finger)
+ return -ENOMEM;
+
+ /* allocate a buffer according to the number of registers to read */
+ ts->read_buf_len = ts->chip_info->max_fingers * 4;
+ ts->read_buf = devm_kzalloc(&client->dev, ts->read_buf_len, GFP_KERNEL);
+ if (!ts->read_buf)
+ return -ENOMEM;
+
input_dev = devm_input_allocate_device(&client->dev);
if (!input_dev)
return -ENOMEM;
@@ -170,15 +216,13 @@ static int st1232_ts_probe(struct i2c_client *client,
ts->client = client;
ts->input_dev = input_dev;
- ts->reset_gpio = of_get_gpio(client->dev.of_node, 0);
- if (gpio_is_valid(ts->reset_gpio)) {
- error = devm_gpio_request(&client->dev, ts->reset_gpio, NULL);
- if (error) {
- dev_err(&client->dev,
- "Unable to request GPIO pin %d.\n",
- ts->reset_gpio);
- return error;
- }
+ ts->reset_gpio = devm_gpiod_get_optional(&client->dev, NULL,
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(ts->reset_gpio)) {
+ error = PTR_ERR(ts->reset_gpio);
+ dev_err(&client->dev, "Unable to request GPIO pin: %d.\n",
+ error);
+ return error;
}
st1232_ts_power(ts, true);
@@ -192,9 +236,16 @@ static int st1232_ts_probe(struct i2c_client *client,
__set_bit(EV_KEY, input_dev->evbit);
__set_bit(EV_ABS, input_dev->evbit);
- input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, MAX_AREA, 0, 0);
- input_set_abs_params(input_dev, ABS_MT_POSITION_X, MIN_X, MAX_X, 0, 0);
- input_set_abs_params(input_dev, ABS_MT_POSITION_Y, MIN_Y, MAX_Y, 0, 0);
+ if (ts->chip_info->have_z)
+ input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0,
+ ts->chip_info->max_area, 0, 0);
+
+ input_set_abs_params(input_dev, ABS_MT_POSITION_X,
+ 0, ts->chip_info->max_x, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
+ 0, ts->chip_info->max_y, 0, 0);
+
+ touchscreen_parse_properties(input_dev, true, &ts->prop);
error = devm_request_threaded_irq(&client->dev, client->irq,
NULL, st1232_ts_irq_handler,
@@ -261,13 +312,15 @@ static SIMPLE_DEV_PM_OPS(st1232_ts_pm_ops,
st1232_ts_suspend, st1232_ts_resume);
static const struct i2c_device_id st1232_ts_id[] = {
- { ST1232_TS_NAME, 0 },
+ { ST1232_TS_NAME, (unsigned long)&st1232_chip_info },
+ { ST1633_TS_NAME, (unsigned long)&st1633_chip_info },
{ }
};
MODULE_DEVICE_TABLE(i2c, st1232_ts_id);
static const struct of_device_id st1232_ts_dt_ids[] = {
- { .compatible = "sitronix,st1232", },
+ { .compatible = "sitronix,st1232", .data = &st1232_chip_info },
+ { .compatible = "sitronix,st1633", .data = &st1633_chip_info },
{ }
};
MODULE_DEVICE_TABLE(of, st1232_ts_dt_ids);
@@ -286,5 +339,6 @@ static struct i2c_driver st1232_ts_driver = {
module_i2c_driver(st1232_ts_driver);
MODULE_AUTHOR("Tony SIM <chinyeow.sim.xt@renesas.com>");
+MODULE_AUTHOR("Martin Kepplinger <martin.kepplinger@ginzinger.com>");
MODULE_DESCRIPTION("SITRONIX ST1232 Touchscreen Controller Driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/stmfts.c b/drivers/input/touchscreen/stmfts.c
index 704e99046916..b6f95f20f924 100644
--- a/drivers/input/touchscreen/stmfts.c
+++ b/drivers/input/touchscreen/stmfts.c
@@ -106,27 +106,29 @@ struct stmfts_data {
bool running;
};
-static void stmfts_brightness_set(struct led_classdev *led_cdev,
+static int stmfts_brightness_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
struct stmfts_data *sdata = container_of(led_cdev,
struct stmfts_data, led_cdev);
int err;
- if (value == sdata->led_status || !sdata->ledvdd)
- return;
-
- if (!value) {
- regulator_disable(sdata->ledvdd);
- } else {
- err = regulator_enable(sdata->ledvdd);
- if (err)
- dev_warn(&sdata->client->dev,
- "failed to disable ledvdd regulator: %d\n",
- err);
+ if (value != sdata->led_status && sdata->ledvdd) {
+ if (!value) {
+ regulator_disable(sdata->ledvdd);
+ } else {
+ err = regulator_enable(sdata->ledvdd);
+ if (err) {
+ dev_warn(&sdata->client->dev,
+ "failed to disable ledvdd regulator: %d\n",
+ err);
+ return err;
+ }
+ }
+ sdata->led_status = value;
}
- sdata->led_status = value;
+ return 0;
}
static enum led_brightness stmfts_brightness_get(struct led_classdev *led_cdev)
@@ -608,7 +610,7 @@ static int stmfts_enable_led(struct stmfts_data *sdata)
sdata->led_cdev.name = STMFTS_DEV_NAME;
sdata->led_cdev.max_brightness = LED_ON;
sdata->led_cdev.brightness = LED_OFF;
- sdata->led_cdev.brightness_set = stmfts_brightness_set;
+ sdata->led_cdev.brightness_set_blocking = stmfts_brightness_set;
sdata->led_cdev.brightness_get = stmfts_brightness_get;
err = devm_led_classdev_register(&sdata->client->dev, &sdata->led_cdev);
diff --git a/drivers/input/touchscreen/stmpe-ts.c b/drivers/input/touchscreen/stmpe-ts.c
index 2a78e27b4495..cf9c9aa39f6e 100644
--- a/drivers/input/touchscreen/stmpe-ts.c
+++ b/drivers/input/touchscreen/stmpe-ts.c
@@ -30,8 +30,6 @@
* with touchscreen controller
*/
#define STMPE_REG_INT_STA 0x0B
-#define STMPE_REG_ADC_CTRL1 0x20
-#define STMPE_REG_ADC_CTRL2 0x21
#define STMPE_REG_TSC_CTRL 0x40
#define STMPE_REG_TSC_CFG 0x41
#define STMPE_REG_FIFO_TH 0x4A
@@ -49,17 +47,6 @@
#define STMPE_IRQ_TOUCH_DET 0
-#define SAMPLE_TIME(x) ((x & 0xf) << 4)
-#define MOD_12B(x) ((x & 0x1) << 3)
-#define REF_SEL(x) ((x & 0x1) << 1)
-#define ADC_FREQ(x) (x & 0x3)
-#define AVE_CTRL(x) ((x & 0x3) << 6)
-#define DET_DELAY(x) ((x & 0x7) << 3)
-#define SETTLING(x) (x & 0x7)
-#define FRACTION_Z(x) (x & 0x7)
-#define I_DRIVE(x) (x & 0x1)
-#define OP_MODE(x) ((x & 0x7) << 1)
-
#define STMPE_TS_NAME "stmpe-ts"
#define XY_MASK 0xfff
@@ -69,15 +56,6 @@
* @idev: registered input device
* @work: a work item used to scan the device
* @dev: a pointer back to the MFD cell struct device*
- * @sample_time: ADC converstion time in number of clock.
- * (0 -> 36 clocks, 1 -> 44 clocks, 2 -> 56 clocks, 3 -> 64 clocks,
- * 4 -> 80 clocks, 5 -> 96 clocks, 6 -> 144 clocks),
- * recommended is 4.
- * @mod_12b: ADC Bit mode (0 -> 10bit ADC, 1 -> 12bit ADC)
- * @ref_sel: ADC reference source
- * (0 -> internal reference, 1 -> external reference)
- * @adc_freq: ADC Clock speed
- * (0 -> 1.625 MHz, 1 -> 3.25 MHz, 2 || 3 -> 6.5 MHz)
* @ave_ctrl: Sample average control
* (0 -> 1 sample, 1 -> 2 samples, 2 -> 4 samples, 3 -> 8 samples)
* @touch_det_delay: Touch detect interrupt delay
@@ -99,10 +77,6 @@ struct stmpe_touch {
struct input_dev *idev;
struct delayed_work work;
struct device *dev;
- u8 sample_time;
- u8 mod_12b;
- u8 ref_sel;
- u8 adc_freq;
u8 ave_ctrl;
u8 touch_det_delay;
u8 settling;
@@ -203,7 +177,7 @@ static irqreturn_t stmpe_ts_handler(int irq, void *data)
static int stmpe_init_hw(struct stmpe_touch *ts)
{
int ret;
- u8 adc_ctrl1, adc_ctrl1_mask, tsc_cfg, tsc_cfg_mask;
+ u8 tsc_cfg, tsc_cfg_mask;
struct stmpe *stmpe = ts->stmpe;
struct device *dev = ts->dev;
@@ -213,27 +187,17 @@ static int stmpe_init_hw(struct stmpe_touch *ts)
return ret;
}
- adc_ctrl1 = SAMPLE_TIME(ts->sample_time) | MOD_12B(ts->mod_12b) |
- REF_SEL(ts->ref_sel);
- adc_ctrl1_mask = SAMPLE_TIME(0xff) | MOD_12B(0xff) | REF_SEL(0xff);
-
- ret = stmpe_set_bits(stmpe, STMPE_REG_ADC_CTRL1,
- adc_ctrl1_mask, adc_ctrl1);
- if (ret) {
- dev_err(dev, "Could not setup ADC\n");
- return ret;
- }
-
- ret = stmpe_set_bits(stmpe, STMPE_REG_ADC_CTRL2,
- ADC_FREQ(0xff), ADC_FREQ(ts->adc_freq));
+ ret = stmpe811_adc_common_init(stmpe);
if (ret) {
- dev_err(dev, "Could not setup ADC\n");
+ stmpe_disable(stmpe, STMPE_BLOCK_TOUCHSCREEN | STMPE_BLOCK_ADC);
return ret;
}
- tsc_cfg = AVE_CTRL(ts->ave_ctrl) | DET_DELAY(ts->touch_det_delay) |
- SETTLING(ts->settling);
- tsc_cfg_mask = AVE_CTRL(0xff) | DET_DELAY(0xff) | SETTLING(0xff);
+ tsc_cfg = STMPE_AVE_CTRL(ts->ave_ctrl) |
+ STMPE_DET_DELAY(ts->touch_det_delay) |
+ STMPE_SETTLING(ts->settling);
+ tsc_cfg_mask = STMPE_AVE_CTRL(0xff) | STMPE_DET_DELAY(0xff) |
+ STMPE_SETTLING(0xff);
ret = stmpe_set_bits(stmpe, STMPE_REG_TSC_CFG, tsc_cfg_mask, tsc_cfg);
if (ret) {
@@ -242,14 +206,14 @@ static int stmpe_init_hw(struct stmpe_touch *ts)
}
ret = stmpe_set_bits(stmpe, STMPE_REG_TSC_FRACTION_Z,
- FRACTION_Z(0xff), FRACTION_Z(ts->fraction_z));
+ STMPE_FRACTION_Z(0xff), STMPE_FRACTION_Z(ts->fraction_z));
if (ret) {
dev_err(dev, "Could not config touch\n");
return ret;
}
ret = stmpe_set_bits(stmpe, STMPE_REG_TSC_I_DRIVE,
- I_DRIVE(0xff), I_DRIVE(ts->i_drive));
+ STMPE_I_DRIVE(0xff), STMPE_I_DRIVE(ts->i_drive));
if (ret) {
dev_err(dev, "Could not config touch\n");
return ret;
@@ -263,7 +227,7 @@ static int stmpe_init_hw(struct stmpe_touch *ts)
}
ret = stmpe_set_bits(stmpe, STMPE_REG_TSC_CTRL,
- OP_MODE(0xff), OP_MODE(OP_MOD_XYZ));
+ STMPE_OP_MODE(0xff), STMPE_OP_MODE(OP_MOD_XYZ));
if (ret) {
dev_err(dev, "Could not set mode\n");
return ret;
@@ -303,13 +267,13 @@ static void stmpe_ts_get_platform_info(struct platform_device *pdev,
if (np) {
if (!of_property_read_u32(np, "st,sample-time", &val))
- ts->sample_time = val;
+ ts->stmpe->sample_time = val;
if (!of_property_read_u32(np, "st,mod-12b", &val))
- ts->mod_12b = val;
+ ts->stmpe->mod_12b = val;
if (!of_property_read_u32(np, "st,ref-sel", &val))
- ts->ref_sel = val;
+ ts->stmpe->ref_sel = val;
if (!of_property_read_u32(np, "st,adc-freq", &val))
- ts->adc_freq = val;
+ ts->stmpe->adc_freq = val;
if (!of_property_read_u32(np, "st,ave-ctrl", &val))
ts->ave_ctrl = val;
if (!of_property_read_u32(np, "st,touch-det-delay", &val))
diff --git a/drivers/input/touchscreen/sx8654.c b/drivers/input/touchscreen/sx8654.c
index ed29db3ec731..dbdf4898aa17 100644
--- a/drivers/input/touchscreen/sx8654.c
+++ b/drivers/input/touchscreen/sx8654.c
@@ -27,12 +27,16 @@
* published by the Free Software Foundation.
*/
-#include <linux/input.h>
-#include <linux/module.h>
-#include <linux/of.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/input/touchscreen.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/of.h>
/* register addresses */
#define I2C_REG_TOUCH0 0x00
@@ -42,25 +46,28 @@
#define I2C_REG_IRQSRC 0x23
#define I2C_REG_SOFTRESET 0x3f
+#define I2C_REG_SX8650_STAT 0x05
+#define SX8650_STAT_CONVIRQ BIT(7)
+
/* commands */
#define CMD_READ_REGISTER 0x40
-#define CMD_MANUAL 0xc0
#define CMD_PENTRG 0xe0
/* value for I2C_REG_SOFTRESET */
#define SOFTRESET_VALUE 0xde
/* bits for I2C_REG_IRQSRC */
-#define IRQ_PENTOUCH_TOUCHCONVDONE 0x08
-#define IRQ_PENRELEASE 0x04
+#define IRQ_PENTOUCH_TOUCHCONVDONE BIT(3)
+#define IRQ_PENRELEASE BIT(2)
/* bits for RegTouch1 */
#define CONDIRQ 0x20
+#define RPDNT_100K 0x00
#define FILT_7SA 0x03
/* bits for I2C_REG_CHANMASK */
-#define CONV_X 0x80
-#define CONV_Y 0x40
+#define CONV_X BIT(7)
+#define CONV_Y BIT(6)
/* coordinates rate: higher nibble of CTRL0 register */
#define RATE_MANUAL 0x00
@@ -69,13 +76,122 @@
/* power delay: lower nibble of CTRL0 register */
#define POWDLY_1_1MS 0x0b
+/* for sx8650, as we have no pen release IRQ there: timeout in ns following the
+ * last PENIRQ after which we assume the pen is lifted.
+ */
+#define SX8650_PENIRQ_TIMEOUT msecs_to_jiffies(10)
+
#define MAX_12BIT ((1 << 12) - 1)
+#define MAX_I2C_READ_LEN 10 /* see datasheet section 5.1.5 */
+
+/* channel definition */
+#define CH_X 0x00
+#define CH_Y 0x01
+
+struct sx865x_data {
+ u8 cmd_manual;
+ u8 chan_mask;
+ bool has_irq_penrelease;
+ bool has_reg_irqmask;
+ irq_handler_t irqh;
+};
struct sx8654 {
struct input_dev *input;
struct i2c_client *client;
+ struct gpio_desc *gpio_reset;
+
+ spinlock_t lock; /* for input reporting from irq/timer */
+ struct timer_list timer;
+
+ struct touchscreen_properties props;
+
+ const struct sx865x_data *data;
};
+static inline void sx865x_penrelease(struct sx8654 *ts)
+{
+ struct input_dev *input_dev = ts->input;
+
+ input_report_key(input_dev, BTN_TOUCH, 0);
+ input_sync(input_dev);
+}
+
+static void sx865x_penrelease_timer_handler(struct timer_list *t)
+{
+ struct sx8654 *ts = from_timer(ts, t, timer);
+ unsigned long flags;
+
+ spin_lock_irqsave(&ts->lock, flags);
+ sx865x_penrelease(ts);
+ spin_unlock_irqrestore(&ts->lock, flags);
+ dev_dbg(&ts->client->dev, "penrelease by timer\n");
+}
+
+static irqreturn_t sx8650_irq(int irq, void *handle)
+{
+ struct sx8654 *ts = handle;
+ struct device *dev = &ts->client->dev;
+ int len, i;
+ unsigned long flags;
+ u8 stat;
+ u16 x, y;
+ u16 ch;
+ u16 chdata;
+ __be16 data[MAX_I2C_READ_LEN / sizeof(__be16)];
+ u8 nchan = hweight32(ts->data->chan_mask);
+ u8 readlen = nchan * sizeof(*data);
+
+ stat = i2c_smbus_read_byte_data(ts->client, CMD_READ_REGISTER
+ | I2C_REG_SX8650_STAT);
+
+ if (!(stat & SX8650_STAT_CONVIRQ)) {
+ dev_dbg(dev, "%s ignore stat [0x%02x]", __func__, stat);
+ return IRQ_HANDLED;
+ }
+
+ len = i2c_master_recv(ts->client, (u8 *)data, readlen);
+ if (len != readlen) {
+ dev_dbg(dev, "ignore short recv (%d)\n", len);
+ return IRQ_HANDLED;
+ }
+
+ spin_lock_irqsave(&ts->lock, flags);
+
+ x = 0;
+ y = 0;
+ for (i = 0; i < nchan; i++) {
+ chdata = be16_to_cpu(data[i]);
+
+ if (unlikely(chdata == 0xFFFF)) {
+ dev_dbg(dev, "invalid qualified data @ %d\n", i);
+ continue;
+ } else if (unlikely(chdata & 0x8000)) {
+ dev_warn(dev, "hibit @ %d [0x%04x]\n", i, chdata);
+ continue;
+ }
+
+ ch = chdata >> 12;
+ if (ch == CH_X)
+ x = chdata & MAX_12BIT;
+ else if (ch == CH_Y)
+ y = chdata & MAX_12BIT;
+ else
+ dev_warn(dev, "unknown channel %d [0x%04x]\n", ch,
+ chdata);
+ }
+
+ touchscreen_report_pos(ts->input, &ts->props, x, y, false);
+ input_report_key(ts->input, BTN_TOUCH, 1);
+ input_sync(ts->input);
+ dev_dbg(dev, "point(%4d,%4d)\n", x, y);
+
+ mod_timer(&ts->timer, jiffies + SX8650_PENIRQ_TIMEOUT);
+ spin_unlock_irqrestore(&ts->lock, flags);
+
+ return IRQ_HANDLED;
+}
+
static irqreturn_t sx8654_irq(int irq, void *handle)
{
struct sx8654 *sx8654 = handle;
@@ -112,8 +228,8 @@ static irqreturn_t sx8654_irq(int irq, void *handle)
x = ((data[0] & 0xf) << 8) | (data[1]);
y = ((data[2] & 0xf) << 8) | (data[3]);
- input_report_abs(sx8654->input, ABS_X, x);
- input_report_abs(sx8654->input, ABS_Y, y);
+ touchscreen_report_pos(sx8654->input, &sx8654->props, x, y,
+ false);
input_report_key(sx8654->input, BTN_TOUCH, 1);
input_sync(sx8654->input);
@@ -124,6 +240,25 @@ out:
return IRQ_HANDLED;
}
+static int sx8654_reset(struct sx8654 *ts)
+{
+ int err;
+
+ if (ts->gpio_reset) {
+ gpiod_set_value_cansleep(ts->gpio_reset, 1);
+ udelay(2); /* Tpulse > 1µs */
+ gpiod_set_value_cansleep(ts->gpio_reset, 0);
+ } else {
+ dev_dbg(&ts->client->dev, "NRST unavailable, try softreset\n");
+ err = i2c_smbus_write_byte_data(ts->client, I2C_REG_SOFTRESET,
+ SOFTRESET_VALUE);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
static int sx8654_open(struct input_dev *dev)
{
struct sx8654 *sx8654 = input_get_drvdata(dev);
@@ -157,14 +292,17 @@ static void sx8654_close(struct input_dev *dev)
disable_irq(client->irq);
+ if (!sx8654->data->has_irq_penrelease)
+ del_timer_sync(&sx8654->timer);
+
/* enable manual mode mode */
- error = i2c_smbus_write_byte(client, CMD_MANUAL);
+ error = i2c_smbus_write_byte(client, sx8654->data->cmd_manual);
if (error) {
dev_err(&client->dev, "writing command CMD_MANUAL failed");
return;
}
- error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH0, 0);
+ error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH0, RATE_MANUAL);
if (error) {
dev_err(&client->dev, "writing to I2C_REG_TOUCH0 failed");
return;
@@ -186,6 +324,31 @@ static int sx8654_probe(struct i2c_client *client,
if (!sx8654)
return -ENOMEM;
+ sx8654->gpio_reset = devm_gpiod_get_optional(&client->dev, "reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(sx8654->gpio_reset)) {
+ error = PTR_ERR(sx8654->gpio_reset);
+ if (error != -EPROBE_DEFER)
+ dev_err(&client->dev, "unable to get reset-gpio: %d\n",
+ error);
+ return error;
+ }
+ dev_dbg(&client->dev, "got GPIO reset pin\n");
+
+ sx8654->data = device_get_match_data(&client->dev);
+ if (!sx8654->data)
+ sx8654->data = (const struct sx865x_data *)id->driver_data;
+ if (!sx8654->data) {
+ dev_err(&client->dev, "invalid or missing device data\n");
+ return -EINVAL;
+ }
+
+ if (!sx8654->data->has_irq_penrelease) {
+ dev_dbg(&client->dev, "use timer for penrelease\n");
+ timer_setup(&sx8654->timer, sx865x_penrelease_timer_handler, 0);
+ spin_lock_init(&sx8654->lock);
+ }
+
input = devm_input_allocate_device(&client->dev);
if (!input)
return -ENOMEM;
@@ -201,43 +364,46 @@ static int sx8654_probe(struct i2c_client *client,
input_set_abs_params(input, ABS_X, 0, MAX_12BIT, 0, 0);
input_set_abs_params(input, ABS_Y, 0, MAX_12BIT, 0, 0);
+ touchscreen_parse_properties(input, false, &sx8654->props);
+
sx8654->client = client;
sx8654->input = input;
input_set_drvdata(sx8654->input, sx8654);
- error = i2c_smbus_write_byte_data(client, I2C_REG_SOFTRESET,
- SOFTRESET_VALUE);
+ error = sx8654_reset(sx8654);
if (error) {
- dev_err(&client->dev, "writing softreset value failed");
+ dev_err(&client->dev, "reset failed");
return error;
}
error = i2c_smbus_write_byte_data(client, I2C_REG_CHANMASK,
- CONV_X | CONV_Y);
+ sx8654->data->chan_mask);
if (error) {
dev_err(&client->dev, "writing to I2C_REG_CHANMASK failed");
return error;
}
- error = i2c_smbus_write_byte_data(client, I2C_REG_IRQMASK,
- IRQ_PENTOUCH_TOUCHCONVDONE |
- IRQ_PENRELEASE);
- if (error) {
- dev_err(&client->dev, "writing to I2C_REG_IRQMASK failed");
- return error;
+ if (sx8654->data->has_reg_irqmask) {
+ error = i2c_smbus_write_byte_data(client, I2C_REG_IRQMASK,
+ IRQ_PENTOUCH_TOUCHCONVDONE |
+ IRQ_PENRELEASE);
+ if (error) {
+ dev_err(&client->dev, "writing I2C_REG_IRQMASK failed");
+ return error;
+ }
}
error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH1,
- CONDIRQ | FILT_7SA);
+ CONDIRQ | RPDNT_100K | FILT_7SA);
if (error) {
dev_err(&client->dev, "writing to I2C_REG_TOUCH1 failed");
return error;
}
error = devm_request_threaded_irq(&client->dev, client->irq,
- NULL, sx8654_irq,
- IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ NULL, sx8654->data->irqh,
+ IRQF_ONESHOT,
client->name, sx8654);
if (error) {
dev_err(&client->dev,
@@ -256,17 +422,48 @@ static int sx8654_probe(struct i2c_client *client,
return 0;
}
+static const struct sx865x_data sx8650_data = {
+ .cmd_manual = 0xb0,
+ .has_irq_penrelease = false,
+ .has_reg_irqmask = false,
+ .chan_mask = (CONV_X | CONV_Y),
+ .irqh = sx8650_irq,
+};
+
+static const struct sx865x_data sx8654_data = {
+ .cmd_manual = 0xc0,
+ .has_irq_penrelease = true,
+ .has_reg_irqmask = true,
+ .chan_mask = (CONV_X | CONV_Y),
+ .irqh = sx8654_irq,
+};
+
#ifdef CONFIG_OF
static const struct of_device_id sx8654_of_match[] = {
- { .compatible = "semtech,sx8654", },
- { },
+ {
+ .compatible = "semtech,sx8650",
+ .data = &sx8650_data,
+ }, {
+ .compatible = "semtech,sx8654",
+ .data = &sx8654_data,
+ }, {
+ .compatible = "semtech,sx8655",
+ .data = &sx8654_data,
+ }, {
+ .compatible = "semtech,sx8656",
+ .data = &sx8654_data,
+ },
+ { }
};
MODULE_DEVICE_TABLE(of, sx8654_of_match);
#endif
static const struct i2c_device_id sx8654_id_table[] = {
- { "semtech_sx8654", 0 },
- { },
+ { .name = "semtech_sx8650", .driver_data = (long)&sx8650_data },
+ { .name = "semtech_sx8654", .driver_data = (long)&sx8654_data },
+ { .name = "semtech_sx8655", .driver_data = (long)&sx8654_data },
+ { .name = "semtech_sx8656", .driver_data = (long)&sx8654_data },
+ { }
};
MODULE_DEVICE_TABLE(i2c, sx8654_id_table);
diff --git a/drivers/input/touchscreen/ti_am335x_tsc.c b/drivers/input/touchscreen/ti_am335x_tsc.c
index 9e8684ab48f4..83e685557a19 100644
--- a/drivers/input/touchscreen/ti_am335x_tsc.c
+++ b/drivers/input/touchscreen/ti_am335x_tsc.c
@@ -507,10 +507,8 @@ static int titsc_remove(struct platform_device *pdev)
static int __maybe_unused titsc_suspend(struct device *dev)
{
struct titsc *ts_dev = dev_get_drvdata(dev);
- struct ti_tscadc_dev *tscadc_dev;
unsigned int idle;
- tscadc_dev = ti_tscadc_dev_get(to_platform_device(dev));
if (device_may_wakeup(dev)) {
titsc_writel(ts_dev, REG_IRQSTATUS, TSC_IRQENB_MASK);
idle = titsc_readl(ts_dev, REG_IRQENABLE);
@@ -524,9 +522,7 @@ static int __maybe_unused titsc_suspend(struct device *dev)
static int __maybe_unused titsc_resume(struct device *dev)
{
struct titsc *ts_dev = dev_get_drvdata(dev);
- struct ti_tscadc_dev *tscadc_dev;
- tscadc_dev = ti_tscadc_dev_get(to_platform_device(dev));
if (device_may_wakeup(dev)) {
titsc_writel(ts_dev, REG_IRQWAKEUP,
0x00);
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index d9a25715650e..6f07f3b21816 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -1,3 +1,7 @@
+# The IOVA library may also be used by non-IOMMU_API users
+config IOMMU_IOVA
+ tristate
+
# IOMMU_API always gets selected by whoever wants it.
config IOMMU_API
bool
@@ -81,9 +85,6 @@ config IOMMU_DEFAULT_PASSTHROUGH
If unsure, say N here.
-config IOMMU_IOVA
- tristate
-
config OF_IOMMU
def_bool y
depends on OF && IOMMU_API
@@ -282,6 +283,7 @@ config ROCKCHIP_IOMMU
config TEGRA_IOMMU_GART
bool "Tegra GART IOMMU Support"
depends on ARCH_TEGRA_2x_SOC
+ depends on TEGRA_MC
select IOMMU_API
help
Enables support for remapping discontiguous physical memory
@@ -435,4 +437,13 @@ config QCOM_IOMMU
help
Support for IOMMU on certain Qualcomm SoCs.
+config HYPERV_IOMMU
+ bool "Hyper-V x2APIC IRQ Handling"
+ depends on HYPERV
+ select IOMMU_API
+ default HYPERV
+ help
+ Stub IOMMU driver to handle IRQs as to allow Hyper-V Linux
+ guests to run with x2APIC mode enabled.
+
endif # IOMMU_SUPPORT
diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile
index a158a68c8ea8..8c71a15e986b 100644
--- a/drivers/iommu/Makefile
+++ b/drivers/iommu/Makefile
@@ -32,3 +32,4 @@ obj-$(CONFIG_EXYNOS_IOMMU) += exynos-iommu.o
obj-$(CONFIG_FSL_PAMU) += fsl_pamu.o fsl_pamu_domain.o
obj-$(CONFIG_S390_IOMMU) += s390-iommu.o
obj-$(CONFIG_QCOM_IOMMU) += qcom_iommu.o
+obj-$(CONFIG_HYPERV_IOMMU) += hyperv-iommu.o
diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index 2a7b78bb98b4..f7cdd2ab7f11 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -18,6 +18,7 @@
*/
#define pr_fmt(fmt) "AMD-Vi: " fmt
+#define dev_fmt(fmt) pr_fmt(fmt)
#include <linux/ratelimit.h>
#include <linux/pci.h>
@@ -139,10 +140,14 @@ static struct lock_class_key reserved_rbtree_key;
static inline int match_hid_uid(struct device *dev,
struct acpihid_map_entry *entry)
{
+ struct acpi_device *adev = ACPI_COMPANION(dev);
const char *hid, *uid;
- hid = acpi_device_hid(ACPI_COMPANION(dev));
- uid = acpi_device_uid(ACPI_COMPANION(dev));
+ if (!adev)
+ return -ENODEV;
+
+ hid = acpi_device_hid(adev);
+ uid = acpi_device_uid(adev);
if (!hid || !(*hid))
return -ENODEV;
@@ -279,10 +284,10 @@ static u16 get_alias(struct device *dev)
return pci_alias;
}
- pr_info("Using IVRS reported alias %02x:%02x.%d "
- "for device %s[%04x:%04x], kernel reported alias "
+ pci_info(pdev, "Using IVRS reported alias %02x:%02x.%d "
+ "for device [%04x:%04x], kernel reported alias "
"%02x:%02x.%d\n", PCI_BUS_NUM(ivrs_alias), PCI_SLOT(ivrs_alias),
- PCI_FUNC(ivrs_alias), dev_name(dev), pdev->vendor, pdev->device,
+ PCI_FUNC(ivrs_alias), pdev->vendor, pdev->device,
PCI_BUS_NUM(pci_alias), PCI_SLOT(pci_alias),
PCI_FUNC(pci_alias));
@@ -293,9 +298,8 @@ static u16 get_alias(struct device *dev)
if (pci_alias == devid &&
PCI_BUS_NUM(ivrs_alias) == pdev->bus->number) {
pci_add_dma_alias(pdev, ivrs_alias & 0xff);
- pr_info("Added PCI DMA alias %02x.%d for %s\n",
- PCI_SLOT(ivrs_alias), PCI_FUNC(ivrs_alias),
- dev_name(dev));
+ pci_info(pdev, "Added PCI DMA alias %02x.%d\n",
+ PCI_SLOT(ivrs_alias), PCI_FUNC(ivrs_alias));
}
return ivrs_alias;
@@ -545,7 +549,7 @@ static void amd_iommu_report_page_fault(u16 devid, u16 domain_id,
dev_data = get_dev_data(&pdev->dev);
if (dev_data && __ratelimit(&dev_data->rs)) {
- dev_err(&pdev->dev, "Event logged [IO_PAGE_FAULT domain=0x%04x address=0x%llx flags=0x%04x]\n",
+ pci_err(pdev, "Event logged [IO_PAGE_FAULT domain=0x%04x address=0x%llx flags=0x%04x]\n",
domain_id, address, flags);
} else if (printk_ratelimit()) {
pr_err("Event logged [IO_PAGE_FAULT device=%02x:%02x.%x domain=0x%04x address=0x%llx flags=0x%04x]\n",
@@ -2258,8 +2262,7 @@ static int amd_iommu_add_device(struct device *dev)
ret = iommu_init_device(dev);
if (ret) {
if (ret != -ENOTSUPP)
- pr_err("Failed to initialize device %s - trying to proceed anyway\n",
- dev_name(dev));
+ dev_err(dev, "Failed to initialize - trying to proceed anyway\n");
iommu_ignore_device(dev);
dev->dma_ops = NULL;
@@ -2569,6 +2572,7 @@ static int map_sg(struct device *dev, struct scatterlist *sglist,
struct scatterlist *s;
unsigned long address;
u64 dma_mask;
+ int ret;
domain = get_domain(dev);
if (IS_ERR(domain))
@@ -2591,7 +2595,6 @@ static int map_sg(struct device *dev, struct scatterlist *sglist,
for (j = 0; j < pages; ++j) {
unsigned long bus_addr, phys_addr;
- int ret;
bus_addr = address + s->dma_address + (j << PAGE_SHIFT);
phys_addr = (sg_phys(s) & PAGE_MASK) + (j << PAGE_SHIFT);
@@ -2605,15 +2608,20 @@ static int map_sg(struct device *dev, struct scatterlist *sglist,
/* Everything is mapped - write the right values into s->dma_address */
for_each_sg(sglist, s, nelems, i) {
- s->dma_address += address + s->offset;
+ /*
+ * Add in the remaining piece of the scatter-gather offset that
+ * was masked out when we were determining the physical address
+ * via (sg_phys(s) & PAGE_MASK) earlier.
+ */
+ s->dma_address += address + (s->offset & ~PAGE_MASK);
s->dma_length = s->length;
}
return nelems;
out_unmap:
- pr_err("%s: IOMMU mapping error in map_sg (io-pages: %d)\n",
- dev_name(dev), npages);
+ dev_err(dev, "IOMMU mapping error in map_sg (io-pages: %d reason: %d)\n",
+ npages, ret);
for_each_sg(sglist, s, nelems, i) {
int j, pages = iommu_num_pages(sg_phys(s), s->length, PAGE_SIZE);
@@ -2807,7 +2815,7 @@ static int init_reserved_iova_ranges(void)
IOVA_PFN(r->start),
IOVA_PFN(r->end));
if (!val) {
- pr_err("Reserve pci-resource range failed\n");
+ pci_err(pdev, "Reserve pci-resource range %pR failed\n", r);
return -ENOMEM;
}
}
@@ -3161,24 +3169,26 @@ static void amd_iommu_get_resv_regions(struct device *dev,
return;
list_for_each_entry(entry, &amd_iommu_unity_map, list) {
+ int type, prot = 0;
size_t length;
- int prot = 0;
if (devid < entry->devid_start || devid > entry->devid_end)
continue;
+ type = IOMMU_RESV_DIRECT;
length = entry->address_end - entry->address_start;
if (entry->prot & IOMMU_PROT_IR)
prot |= IOMMU_READ;
if (entry->prot & IOMMU_PROT_IW)
prot |= IOMMU_WRITE;
+ if (entry->prot & IOMMU_UNITY_MAP_FLAG_EXCL_RANGE)
+ /* Exclusion range */
+ type = IOMMU_RESV_RESERVED;
region = iommu_alloc_resv_region(entry->address_start,
- length, prot,
- IOMMU_RESV_DIRECT);
+ length, prot, type);
if (!region) {
- pr_err("Out of memory allocating dm-regions for %s\n",
- dev_name(dev));
+ dev_err(dev, "Out of memory allocating dm-regions\n");
return;
}
list_add_tail(&region->list, head);
diff --git a/drivers/iommu/amd_iommu_init.c b/drivers/iommu/amd_iommu_init.c
index 66123b911ec8..1b1378619fc9 100644
--- a/drivers/iommu/amd_iommu_init.c
+++ b/drivers/iommu/amd_iommu_init.c
@@ -18,6 +18,7 @@
*/
#define pr_fmt(fmt) "AMD-Vi: " fmt
+#define dev_fmt(fmt) pr_fmt(fmt)
#include <linux/pci.h>
#include <linux/acpi.h>
@@ -1457,8 +1458,7 @@ static void amd_iommu_erratum_746_workaround(struct amd_iommu *iommu)
pci_write_config_dword(iommu->dev, 0xf0, 0x90 | (1 << 8));
pci_write_config_dword(iommu->dev, 0xf4, value | 0x4);
- pr_info("Applying erratum 746 workaround for IOMMU at %s\n",
- dev_name(&iommu->dev->dev));
+ pci_info(iommu->dev, "Applying erratum 746 workaround\n");
/* Clear the enable writing bit */
pci_write_config_dword(iommu->dev, 0xf0, 0x90);
@@ -1488,8 +1488,7 @@ static void amd_iommu_ats_write_check_workaround(struct amd_iommu *iommu)
/* Set L2_DEBUG_3[AtsIgnoreIWDis] = 1 */
iommu_write_l2(iommu, 0x47, value | BIT(0));
- pr_info("Applying ATS write check workaround for IOMMU at %s\n",
- dev_name(&iommu->dev->dev));
+ pci_info(iommu->dev, "Applying ATS write check workaround\n");
}
/*
@@ -1665,6 +1664,7 @@ static int iommu_pc_get_set_reg(struct amd_iommu *iommu, u8 bank, u8 cntr,
static void init_iommu_perf_ctr(struct amd_iommu *iommu)
{
+ struct pci_dev *pdev = iommu->dev;
u64 val = 0xabcd, val2 = 0;
if (!iommu_feature(iommu, FEATURE_PC))
@@ -1676,12 +1676,12 @@ static void init_iommu_perf_ctr(struct amd_iommu *iommu)
if ((iommu_pc_get_set_reg(iommu, 0, 0, 0, &val, true)) ||
(iommu_pc_get_set_reg(iommu, 0, 0, 0, &val2, false)) ||
(val != val2)) {
- pr_err("Unable to write to IOMMU perf counter.\n");
+ pci_err(pdev, "Unable to write to IOMMU perf counter.\n");
amd_iommu_pc_present = false;
return;
}
- pr_info("IOMMU performance counters supported\n");
+ pci_info(pdev, "IOMMU performance counters supported\n");
val = readl(iommu->mmio_base + MMIO_CNTR_CONF_OFFSET);
iommu->max_banks = (u8) ((val >> 12) & 0x3f);
@@ -1840,14 +1840,14 @@ static void print_iommu_info(void)
struct amd_iommu *iommu;
for_each_iommu(iommu) {
+ struct pci_dev *pdev = iommu->dev;
int i;
- pr_info("Found IOMMU at %s cap 0x%hx\n",
- dev_name(&iommu->dev->dev), iommu->cap_ptr);
+ pci_info(pdev, "Found IOMMU cap 0x%hx\n", iommu->cap_ptr);
if (iommu->cap & (1 << IOMMU_CAP_EFR)) {
- pr_info("Extended features (%#llx):\n",
- iommu->features);
+ pci_info(pdev, "Extended features (%#llx):\n",
+ iommu->features);
for (i = 0; i < ARRAY_SIZE(feat_str); ++i) {
if (iommu_feature(iommu, (1ULL << i)))
pr_cont(" %s", feat_str[i]);
@@ -2013,6 +2013,9 @@ static int __init init_unity_map_range(struct ivmd_header *m)
if (e == NULL)
return -ENOMEM;
+ if (m->flags & IVMD_FLAG_EXCL_RANGE)
+ init_exclusion_range(m);
+
switch (m->type) {
default:
kfree(e);
@@ -2059,9 +2062,7 @@ static int __init init_memory_definitions(struct acpi_table_header *table)
while (p < end) {
m = (struct ivmd_header *)p;
- if (m->flags & IVMD_FLAG_EXCL_RANGE)
- init_exclusion_range(m);
- else if (m->flags & IVMD_FLAG_UNITY_MAP)
+ if (m->flags & (IVMD_FLAG_UNITY_MAP | IVMD_FLAG_EXCL_RANGE))
init_unity_map_range(m);
p += m->length;
diff --git a/drivers/iommu/amd_iommu_types.h b/drivers/iommu/amd_iommu_types.h
index eae0741f72dc..87965e4d9647 100644
--- a/drivers/iommu/amd_iommu_types.h
+++ b/drivers/iommu/amd_iommu_types.h
@@ -374,6 +374,8 @@
#define IOMMU_PROT_IR 0x01
#define IOMMU_PROT_IW 0x02
+#define IOMMU_UNITY_MAP_FLAG_EXCL_RANGE (1 << 2)
+
/* IOMMU capabilities */
#define IOMMU_CAP_IOTLB 24
#define IOMMU_CAP_NPCACHE 26
diff --git a/drivers/iommu/amd_iommu_v2.c b/drivers/iommu/amd_iommu_v2.c
index 23dae9348ace..5d7ef750e4a0 100644
--- a/drivers/iommu/amd_iommu_v2.c
+++ b/drivers/iommu/amd_iommu_v2.c
@@ -370,29 +370,6 @@ static struct pasid_state *mn_to_state(struct mmu_notifier *mn)
return container_of(mn, struct pasid_state, mn);
}
-static void __mn_flush_page(struct mmu_notifier *mn,
- unsigned long address)
-{
- struct pasid_state *pasid_state;
- struct device_state *dev_state;
-
- pasid_state = mn_to_state(mn);
- dev_state = pasid_state->device_state;
-
- amd_iommu_flush_page(dev_state->domain, pasid_state->pasid, address);
-}
-
-static int mn_clear_flush_young(struct mmu_notifier *mn,
- struct mm_struct *mm,
- unsigned long start,
- unsigned long end)
-{
- for (; start < end; start += PAGE_SIZE)
- __mn_flush_page(mn, start);
-
- return 0;
-}
-
static void mn_invalidate_range(struct mmu_notifier *mn,
struct mm_struct *mm,
unsigned long start, unsigned long end)
@@ -430,7 +407,6 @@ static void mn_release(struct mmu_notifier *mn, struct mm_struct *mm)
static const struct mmu_notifier_ops iommu_mn = {
.release = mn_release,
- .clear_flush_young = mn_clear_flush_young,
.invalidate_range = mn_invalidate_range,
};
diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
index 0d284029dc73..d3880010c6cf 100644
--- a/drivers/iommu/arm-smmu-v3.c
+++ b/drivers/iommu/arm-smmu-v3.c
@@ -18,6 +18,7 @@
#include <linux/dma-iommu.h>
#include <linux/err.h>
#include <linux/interrupt.h>
+#include <linux/io-pgtable.h>
#include <linux/iommu.h>
#include <linux/iopoll.h>
#include <linux/init.h>
@@ -32,8 +33,6 @@
#include <linux/amba/bus.h>
-#include "io-pgtable.h"
-
/* MMIO registers */
#define ARM_SMMU_IDR0 0x0
#define IDR0_ST_LVL GENMASK(28, 27)
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index af18a7e7f917..045d93884164 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -39,6 +39,7 @@
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/io-64-nonatomic-hi-lo.h>
+#include <linux/io-pgtable.h>
#include <linux/iommu.h>
#include <linux/iopoll.h>
#include <linux/init.h>
@@ -56,7 +57,6 @@
#include <linux/amba/bus.h>
#include <linux/fsl/mc.h>
-#include "io-pgtable.h"
#include "arm-smmu-regs.h"
#define ARM_MMU500_ACTLR_CPRE (1 << 1)
diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index d19f3d6b43c1..77aabe637a60 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -289,7 +289,7 @@ int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t base,
{
struct iommu_dma_cookie *cookie = domain->iova_cookie;
struct iova_domain *iovad = &cookie->iovad;
- unsigned long order, base_pfn, end_pfn;
+ unsigned long order, base_pfn;
int attr;
if (!cookie || cookie->type != IOMMU_DMA_IOVA_COOKIE)
@@ -298,7 +298,6 @@ int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t base,
/* Use the smallest supported page size for IOVA granularity */
order = __ffs(domain->pgsize_bitmap);
base_pfn = max_t(unsigned long, 1, base >> order);
- end_pfn = (base + size - 1) >> order;
/* Check the domain allows at least some access to the device... */
if (domain->geometry.force_aperture) {
diff --git a/drivers/iommu/hyperv-iommu.c b/drivers/iommu/hyperv-iommu.c
new file mode 100644
index 000000000000..a386b83e0e34
--- /dev/null
+++ b/drivers/iommu/hyperv-iommu.c
@@ -0,0 +1,196 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Hyper-V stub IOMMU driver.
+ *
+ * Copyright (C) 2019, Microsoft, Inc.
+ *
+ * Author : Lan Tianyu <Tianyu.Lan@microsoft.com>
+ */
+
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/iommu.h>
+#include <linux/module.h>
+
+#include <asm/apic.h>
+#include <asm/cpu.h>
+#include <asm/hw_irq.h>
+#include <asm/io_apic.h>
+#include <asm/irq_remapping.h>
+#include <asm/hypervisor.h>
+
+#include "irq_remapping.h"
+
+#ifdef CONFIG_IRQ_REMAP
+
+/*
+ * According 82093AA IO-APIC spec , IO APIC has a 24-entry Interrupt
+ * Redirection Table. Hyper-V exposes one single IO-APIC and so define
+ * 24 IO APIC remmapping entries.
+ */
+#define IOAPIC_REMAPPING_ENTRY 24
+
+static cpumask_t ioapic_max_cpumask = { CPU_BITS_NONE };
+static struct irq_domain *ioapic_ir_domain;
+
+static int hyperv_ir_set_affinity(struct irq_data *data,
+ const struct cpumask *mask, bool force)
+{
+ struct irq_data *parent = data->parent_data;
+ struct irq_cfg *cfg = irqd_cfg(data);
+ struct IO_APIC_route_entry *entry;
+ int ret;
+
+ /* Return error If new irq affinity is out of ioapic_max_cpumask. */
+ if (!cpumask_subset(mask, &ioapic_max_cpumask))
+ return -EINVAL;
+
+ ret = parent->chip->irq_set_affinity(parent, mask, force);
+ if (ret < 0 || ret == IRQ_SET_MASK_OK_DONE)
+ return ret;
+
+ entry = data->chip_data;
+ entry->dest = cfg->dest_apicid;
+ entry->vector = cfg->vector;
+ send_cleanup_vector(cfg);
+
+ return 0;
+}
+
+static struct irq_chip hyperv_ir_chip = {
+ .name = "HYPERV-IR",
+ .irq_ack = apic_ack_irq,
+ .irq_set_affinity = hyperv_ir_set_affinity,
+};
+
+static int hyperv_irq_remapping_alloc(struct irq_domain *domain,
+ unsigned int virq, unsigned int nr_irqs,
+ void *arg)
+{
+ struct irq_alloc_info *info = arg;
+ struct irq_data *irq_data;
+ struct irq_desc *desc;
+ int ret = 0;
+
+ if (!info || info->type != X86_IRQ_ALLOC_TYPE_IOAPIC || nr_irqs > 1)
+ return -EINVAL;
+
+ ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, arg);
+ if (ret < 0)
+ return ret;
+
+ irq_data = irq_domain_get_irq_data(domain, virq);
+ if (!irq_data) {
+ irq_domain_free_irqs_common(domain, virq, nr_irqs);
+ return -EINVAL;
+ }
+
+ irq_data->chip = &hyperv_ir_chip;
+
+ /*
+ * If there is interrupt remapping function of IOMMU, setting irq
+ * affinity only needs to change IRTE of IOMMU. But Hyper-V doesn't
+ * support interrupt remapping function, setting irq affinity of IO-APIC
+ * interrupts still needs to change IO-APIC registers. But ioapic_
+ * configure_entry() will ignore value of cfg->vector and cfg->
+ * dest_apicid when IO-APIC's parent irq domain is not the vector
+ * domain.(See ioapic_configure_entry()) In order to setting vector
+ * and dest_apicid to IO-APIC register, IO-APIC entry pointer is saved
+ * in the chip_data and hyperv_irq_remapping_activate()/hyperv_ir_set_
+ * affinity() set vector and dest_apicid directly into IO-APIC entry.
+ */
+ irq_data->chip_data = info->ioapic_entry;
+
+ /*
+ * Hypver-V IO APIC irq affinity should be in the scope of
+ * ioapic_max_cpumask because no irq remapping support.
+ */
+ desc = irq_data_to_desc(irq_data);
+ cpumask_copy(desc->irq_common_data.affinity, &ioapic_max_cpumask);
+
+ return 0;
+}
+
+static void hyperv_irq_remapping_free(struct irq_domain *domain,
+ unsigned int virq, unsigned int nr_irqs)
+{
+ irq_domain_free_irqs_common(domain, virq, nr_irqs);
+}
+
+static int hyperv_irq_remapping_activate(struct irq_domain *domain,
+ struct irq_data *irq_data, bool reserve)
+{
+ struct irq_cfg *cfg = irqd_cfg(irq_data);
+ struct IO_APIC_route_entry *entry = irq_data->chip_data;
+
+ entry->dest = cfg->dest_apicid;
+ entry->vector = cfg->vector;
+
+ return 0;
+}
+
+static struct irq_domain_ops hyperv_ir_domain_ops = {
+ .alloc = hyperv_irq_remapping_alloc,
+ .free = hyperv_irq_remapping_free,
+ .activate = hyperv_irq_remapping_activate,
+};
+
+static int __init hyperv_prepare_irq_remapping(void)
+{
+ struct fwnode_handle *fn;
+ int i;
+
+ if (!hypervisor_is_type(X86_HYPER_MS_HYPERV) ||
+ !x2apic_supported())
+ return -ENODEV;
+
+ fn = irq_domain_alloc_named_id_fwnode("HYPERV-IR", 0);
+ if (!fn)
+ return -ENOMEM;
+
+ ioapic_ir_domain =
+ irq_domain_create_hierarchy(arch_get_ir_parent_domain(),
+ 0, IOAPIC_REMAPPING_ENTRY, fn,
+ &hyperv_ir_domain_ops, NULL);
+
+ irq_domain_free_fwnode(fn);
+
+ /*
+ * Hyper-V doesn't provide irq remapping function for
+ * IO-APIC and so IO-APIC only accepts 8-bit APIC ID.
+ * Cpu's APIC ID is read from ACPI MADT table and APIC IDs
+ * in the MADT table on Hyper-v are sorted monotonic increasingly.
+ * APIC ID reflects cpu topology. There maybe some APIC ID
+ * gaps when cpu number in a socket is not power of two. Prepare
+ * max cpu affinity for IOAPIC irqs. Scan cpu 0-255 and set cpu
+ * into ioapic_max_cpumask if its APIC ID is less than 256.
+ */
+ for (i = min_t(unsigned int, num_possible_cpus() - 1, 255); i >= 0; i--)
+ if (cpu_physical_id(i) < 256)
+ cpumask_set_cpu(i, &ioapic_max_cpumask);
+
+ return 0;
+}
+
+static int __init hyperv_enable_irq_remapping(void)
+{
+ return IRQ_REMAP_X2APIC_MODE;
+}
+
+static struct irq_domain *hyperv_get_ir_irq_domain(struct irq_alloc_info *info)
+{
+ if (info->type == X86_IRQ_ALLOC_TYPE_IOAPIC)
+ return ioapic_ir_domain;
+ else
+ return NULL;
+}
+
+struct irq_remap_ops hyperv_irq_remap_ops = {
+ .prepare = hyperv_prepare_irq_remapping,
+ .enable = hyperv_enable_irq_remapping,
+ .get_ir_irq_domain = hyperv_get_ir_irq_domain,
+};
+
+#endif
diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
index 39a33dec4d0b..28cb713d728c 100644
--- a/drivers/iommu/intel-iommu.c
+++ b/drivers/iommu/intel-iommu.c
@@ -19,6 +19,7 @@
*/
#define pr_fmt(fmt) "DMAR: " fmt
+#define dev_fmt(fmt) pr_fmt(fmt)
#include <linux/init.h>
#include <linux/bitmap.h>
@@ -343,8 +344,7 @@ static int g_num_of_iommus;
static void domain_exit(struct dmar_domain *domain);
static void domain_remove_dev_info(struct dmar_domain *domain);
-static void dmar_remove_one_dev_info(struct dmar_domain *domain,
- struct device *dev);
+static void dmar_remove_one_dev_info(struct device *dev);
static void __dmar_remove_one_dev_info(struct device_domain_info *info);
static void domain_context_clear(struct intel_iommu *iommu,
struct device *dev);
@@ -865,7 +865,7 @@ out:
static struct dma_pte *pfn_to_dma_pte(struct dmar_domain *domain,
unsigned long pfn, int *target_level)
{
- struct dma_pte *parent, *pte = NULL;
+ struct dma_pte *parent, *pte;
int level = agaw_to_level(domain->agaw);
int offset;
@@ -922,7 +922,7 @@ 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 = NULL;
+ struct dma_pte *parent, *pte;
int total = agaw_to_level(domain->agaw);
int offset;
@@ -954,7 +954,7 @@ static void dma_pte_clear_range(struct dmar_domain *domain,
unsigned long start_pfn,
unsigned long last_pfn)
{
- unsigned int large_page = 1;
+ unsigned int large_page;
struct dma_pte *first_pte, *pte;
BUG_ON(!domain_pfn_supported(domain, start_pfn));
@@ -1132,7 +1132,7 @@ static struct page *domain_unmap(struct dmar_domain *domain,
unsigned long start_pfn,
unsigned long last_pfn)
{
- struct page *freelist = NULL;
+ struct page *freelist;
BUG_ON(!domain_pfn_supported(domain, start_pfn));
BUG_ON(!domain_pfn_supported(domain, last_pfn));
@@ -1403,10 +1403,13 @@ static void iommu_enable_dev_iotlb(struct device_domain_info *info)
if (info->pasid_supported && !pci_enable_pasid(pdev, info->pasid_supported & ~1))
info->pasid_enabled = 1;
- if (info->pri_supported && !pci_reset_pri(pdev) && !pci_enable_pri(pdev, 32))
+ if (info->pri_supported &&
+ (info->pasid_enabled ? pci_prg_resp_pasid_required(pdev) : 1) &&
+ !pci_reset_pri(pdev) && !pci_enable_pri(pdev, 32))
info->pri_enabled = 1;
#endif
if (!pdev->untrusted && info->ats_supported &&
+ pci_ats_page_aligned(pdev) &&
!pci_enable_ats(pdev, VTD_PAGE_SHIFT)) {
info->ats_enabled = 1;
domain_update_iotlb(info->domain);
@@ -1535,6 +1538,9 @@ static void iommu_disable_protect_mem_regions(struct intel_iommu *iommu)
u32 pmen;
unsigned long flags;
+ if (!cap_plmr(iommu->cap) && !cap_phmr(iommu->cap))
+ return;
+
raw_spin_lock_irqsave(&iommu->register_lock, flags);
pmen = readl(iommu->reg + DMAR_PMEN_REG);
pmen &= ~DMA_PMEN_EPM;
@@ -1763,7 +1769,7 @@ static int domain_attach_iommu(struct dmar_domain *domain,
static int domain_detach_iommu(struct dmar_domain *domain,
struct intel_iommu *iommu)
{
- int num, count = INT_MAX;
+ int num, count;
assert_spin_locked(&device_domain_lock);
assert_spin_locked(&iommu->lock);
@@ -1816,7 +1822,7 @@ static int dmar_init_reserved_ranges(void)
IOVA_PFN(r->start),
IOVA_PFN(r->end));
if (!iova) {
- pr_err("Reserve iova failed\n");
+ pci_err(pdev, "Reserve iova for %pR failed\n", r);
return -ENODEV;
}
}
@@ -1902,11 +1908,7 @@ static int domain_init(struct dmar_domain *domain, struct intel_iommu *iommu,
static void domain_exit(struct dmar_domain *domain)
{
- struct page *freelist = NULL;
-
- /* Domain 0 is reserved, so dont process it */
- if (!domain)
- return;
+ struct page *freelist;
/* Remove associated devices and clear attached or cached domains */
rcu_read_lock();
@@ -2058,7 +2060,6 @@ static int domain_context_mapping_one(struct dmar_domain *domain,
int agaw;
context_set_domain_id(context, did);
- context_set_translation_type(context, translation);
if (translation != CONTEXT_TT_PASS_THROUGH) {
/*
@@ -2088,6 +2089,8 @@ static int domain_context_mapping_one(struct dmar_domain *domain,
*/
context_set_address_width(context, iommu->msagaw);
}
+
+ context_set_translation_type(context, translation);
}
context_set_fault_enable(context);
@@ -2486,7 +2489,8 @@ static struct dmar_domain *dmar_insert_one_dev_info(struct intel_iommu *iommu,
if (dev && dev_is_pci(dev)) {
struct pci_dev *pdev = to_pci_dev(info->dev);
- if (!pci_ats_disabled() &&
+ if (!pdev->untrusted &&
+ !pci_ats_disabled() &&
ecap_dev_iotlb_support(iommu->ecap) &&
pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ATS) &&
dmar_find_matched_atsr_unit(pdev))
@@ -2545,9 +2549,8 @@ static struct dmar_domain *dmar_insert_one_dev_info(struct intel_iommu *iommu,
if (dev && dev_is_pci(dev) && sm_supported(iommu)) {
ret = intel_pasid_alloc_table(dev);
if (ret) {
- pr_err("PASID table allocation for %s failed\n",
- dev_name(dev));
- dmar_remove_one_dev_info(domain, dev);
+ dev_err(dev, "PASID table allocation failed\n");
+ dmar_remove_one_dev_info(dev);
return NULL;
}
@@ -2561,16 +2564,15 @@ static struct dmar_domain *dmar_insert_one_dev_info(struct intel_iommu *iommu,
dev, PASID_RID2PASID);
spin_unlock(&iommu->lock);
if (ret) {
- pr_err("Setup RID2PASID for %s failed\n",
- dev_name(dev));
- dmar_remove_one_dev_info(domain, dev);
+ dev_err(dev, "Setup RID2PASID failed\n");
+ dmar_remove_one_dev_info(dev);
return NULL;
}
}
if (dev && domain_context_mapping(domain, dev)) {
- pr_err("Domain context map for %s failed\n", dev_name(dev));
- dmar_remove_one_dev_info(domain, dev);
+ dev_err(dev, "Domain context map failed\n");
+ dmar_remove_one_dev_info(dev);
return NULL;
}
@@ -2585,7 +2587,7 @@ static int get_last_alias(struct pci_dev *pdev, u16 alias, void *opaque)
static struct dmar_domain *find_or_alloc_domain(struct device *dev, int gaw)
{
- struct device_domain_info *info = NULL;
+ struct device_domain_info *info;
struct dmar_domain *domain = NULL;
struct intel_iommu *iommu;
u16 dma_alias;
@@ -2724,13 +2726,12 @@ static int domain_prepare_identity_map(struct device *dev,
range which is reserved in E820, so which didn't get set
up to start with in si_domain */
if (domain == si_domain && hw_pass_through) {
- pr_warn("Ignoring identity map for HW passthrough device %s [0x%Lx - 0x%Lx]\n",
- dev_name(dev), start, end);
+ dev_warn(dev, "Ignoring identity map for HW passthrough [0x%Lx - 0x%Lx]\n",
+ start, end);
return 0;
}
- pr_info("Setting identity map for device %s [0x%Lx - 0x%Lx]\n",
- dev_name(dev), start, end);
+ dev_info(dev, "Setting identity map [0x%Lx - 0x%Lx]\n", start, end);
if (end < start) {
WARN(1, "Your BIOS is broken; RMRR ends before it starts!\n"
@@ -2810,7 +2811,7 @@ static int md_domain_init(struct dmar_domain *domain, int guest_width);
static int __init si_domain_init(int hw)
{
- int nid, ret = 0;
+ int nid, ret;
si_domain = alloc_domain(DOMAIN_FLAG_STATIC_IDENTITY);
if (!si_domain)
@@ -2934,7 +2935,6 @@ static bool device_is_rmrr_locked(struct device *dev)
static int iommu_should_identity_map(struct device *dev, int startup)
{
-
if (dev_is_pci(dev)) {
struct pci_dev *pdev = to_pci_dev(dev);
@@ -3017,8 +3017,8 @@ static int __init dev_prepare_static_identity_mapping(struct device *dev, int hw
ret = domain_add_dev_info(si_domain, dev);
if (!ret)
- pr_info("%s identity mapping for device %s\n",
- hw ? "Hardware" : "Software", dev_name(dev));
+ dev_info(dev, "%s identity mapping\n",
+ hw ? "Hardware" : "Software");
else if (ret == -ENODEV)
/* device not associated with an iommu */
ret = 0;
@@ -3530,7 +3530,7 @@ static unsigned long intel_alloc_iova(struct device *dev,
struct dmar_domain *domain,
unsigned long nrpages, uint64_t dma_mask)
{
- unsigned long iova_pfn = 0;
+ unsigned long iova_pfn;
/* Restrict dma_mask to the width that the iommu can handle */
dma_mask = min_t(uint64_t, DOMAIN_MAX_ADDR(domain->gaw), dma_mask);
@@ -3551,8 +3551,7 @@ static unsigned long intel_alloc_iova(struct device *dev,
iova_pfn = alloc_iova_fast(&domain->iovad, nrpages,
IOVA_PFN(dma_mask), true);
if (unlikely(!iova_pfn)) {
- pr_err("Allocating %ld-page iova for %s failed",
- nrpages, dev_name(dev));
+ dev_err(dev, "Allocating %ld-page iova failed", nrpages);
return 0;
}
@@ -3600,7 +3599,7 @@ struct dmar_domain *get_valid_domain_for_dev(struct device *dev)
out:
if (!domain)
- pr_err("Allocating domain for %s failed\n", dev_name(dev));
+ dev_err(dev, "Allocating domain failed\n");
return domain;
@@ -3626,9 +3625,8 @@ static int iommu_no_mapping(struct device *dev)
* 32 bit DMA is removed from si_domain and fall back
* to non-identity mapping.
*/
- dmar_remove_one_dev_info(si_domain, dev);
- pr_info("32bit %s uses non-identity mapping\n",
- dev_name(dev));
+ dmar_remove_one_dev_info(dev);
+ dev_info(dev, "32bit DMA uses non-identity mapping\n");
return 0;
}
} else {
@@ -3640,8 +3638,7 @@ static int iommu_no_mapping(struct device *dev)
int ret;
ret = domain_add_dev_info(si_domain, dev);
if (!ret) {
- pr_info("64bit %s uses identity mapping\n",
- dev_name(dev));
+ dev_info(dev, "64bit DMA uses identity mapping\n");
return 1;
}
}
@@ -3650,11 +3647,9 @@ static int iommu_no_mapping(struct device *dev)
return 0;
}
-static dma_addr_t __intel_map_page(struct device *dev, struct page *page,
- unsigned long offset, size_t size, int dir,
- u64 dma_mask)
+static dma_addr_t __intel_map_single(struct device *dev, phys_addr_t paddr,
+ size_t size, int dir, u64 dma_mask)
{
- phys_addr_t paddr = page_to_phys(page) + offset;
struct dmar_domain *domain;
phys_addr_t start_paddr;
unsigned long iova_pfn;
@@ -3706,8 +3701,8 @@ static dma_addr_t __intel_map_page(struct device *dev, struct page *page,
error:
if (iova_pfn)
free_iova_fast(&domain->iovad, iova_pfn, dma_to_mm_pfn(size));
- pr_err("Device %s request: %zx@%llx dir %d --- failed\n",
- dev_name(dev), size, (unsigned long long)paddr, dir);
+ dev_err(dev, "Device request: %zx@%llx dir %d --- failed\n",
+ size, (unsigned long long)paddr, dir);
return DMA_MAPPING_ERROR;
}
@@ -3716,7 +3711,15 @@ static dma_addr_t intel_map_page(struct device *dev, struct page *page,
enum dma_data_direction dir,
unsigned long attrs)
{
- return __intel_map_page(dev, page, offset, size, dir, *dev->dma_mask);
+ return __intel_map_single(dev, page_to_phys(page) + offset, size,
+ dir, *dev->dma_mask);
+}
+
+static dma_addr_t intel_map_resource(struct device *dev, phys_addr_t phys_addr,
+ size_t size, enum dma_data_direction dir,
+ unsigned long attrs)
+{
+ return __intel_map_single(dev, phys_addr, size, dir, *dev->dma_mask);
}
static void intel_unmap(struct device *dev, dma_addr_t dev_addr, size_t size)
@@ -3742,8 +3745,7 @@ static void intel_unmap(struct device *dev, dma_addr_t dev_addr, size_t size)
start_pfn = mm_to_dma_pfn(iova_pfn);
last_pfn = start_pfn + nrpages - 1;
- pr_debug("Device %s unmapping: pfn %lx-%lx\n",
- dev_name(dev), start_pfn, last_pfn);
+ dev_dbg(dev, "Device unmapping: pfn %lx-%lx\n", start_pfn, last_pfn);
freelist = domain_unmap(domain, start_pfn, last_pfn);
@@ -3807,8 +3809,9 @@ static void *intel_alloc_coherent(struct device *dev, size_t size,
return NULL;
memset(page_address(page), 0, size);
- *dma_handle = __intel_map_page(dev, page, 0, size, DMA_BIDIRECTIONAL,
- dev->coherent_dma_mask);
+ *dma_handle = __intel_map_single(dev, page_to_phys(page), size,
+ DMA_BIDIRECTIONAL,
+ dev->coherent_dma_mask);
if (*dma_handle != DMA_MAPPING_ERROR)
return page_address(page);
if (!dma_release_from_contiguous(dev, page, size >> PAGE_SHIFT))
@@ -3925,6 +3928,8 @@ static const struct dma_map_ops intel_dma_ops = {
.unmap_sg = intel_unmap_sg,
.map_page = intel_map_page,
.unmap_page = intel_unmap_page,
+ .map_resource = intel_map_resource,
+ .unmap_resource = intel_unmap_page,
.dma_supported = dma_direct_supported,
};
@@ -4340,7 +4345,7 @@ int dmar_check_one_atsr(struct acpi_dmar_header *hdr, void *arg)
static int intel_iommu_add(struct dmar_drhd_unit *dmaru)
{
- int sp, ret = 0;
+ int sp, ret;
struct intel_iommu *iommu = dmaru->iommu;
if (g_iommus[iommu->seq_id])
@@ -4504,7 +4509,7 @@ out:
int dmar_iommu_notify_scope_dev(struct dmar_pci_notify_info *info)
{
- int ret = 0;
+ int ret;
struct dmar_rmrr_unit *rmrru;
struct dmar_atsr_unit *atsru;
struct acpi_dmar_atsr *atsr;
@@ -4521,7 +4526,7 @@ int dmar_iommu_notify_scope_dev(struct dmar_pci_notify_info *info)
((void *)rmrr) + rmrr->header.length,
rmrr->segment, rmrru->devices,
rmrru->devices_cnt);
- if(ret < 0)
+ if (ret < 0)
return ret;
} else if (info->event == BUS_NOTIFY_REMOVED_DEVICE) {
dmar_remove_dev_scope(info, rmrr->segment,
@@ -4541,7 +4546,7 @@ int dmar_iommu_notify_scope_dev(struct dmar_pci_notify_info *info)
atsru->devices_cnt);
if (ret > 0)
break;
- else if(ret < 0)
+ else if (ret < 0)
return ret;
} else if (info->event == BUS_NOTIFY_REMOVED_DEVICE) {
if (dmar_remove_dev_scope(info, atsr->segment,
@@ -4568,16 +4573,19 @@ static int device_notifier(struct notifier_block *nb,
if (iommu_dummy(dev))
return 0;
- if (action != BUS_NOTIFY_REMOVED_DEVICE)
- return 0;
-
- domain = find_domain(dev);
- if (!domain)
- return 0;
+ if (action == BUS_NOTIFY_REMOVED_DEVICE) {
+ domain = find_domain(dev);
+ if (!domain)
+ return 0;
- dmar_remove_one_dev_info(domain, dev);
- if (!domain_type_is_vm_or_si(domain) && list_empty(&domain->devices))
- domain_exit(domain);
+ dmar_remove_one_dev_info(dev);
+ if (!domain_type_is_vm_or_si(domain) &&
+ list_empty(&domain->devices))
+ domain_exit(domain);
+ } else if (action == BUS_NOTIFY_ADD_DEVICE) {
+ if (iommu_should_identity_map(dev, 1))
+ domain_add_dev_info(si_domain, dev);
+ }
return 0;
}
@@ -4988,8 +4996,7 @@ static void __dmar_remove_one_dev_info(struct device_domain_info *info)
free_devinfo_mem(info);
}
-static void dmar_remove_one_dev_info(struct dmar_domain *domain,
- struct device *dev)
+static void dmar_remove_one_dev_info(struct device *dev)
{
struct device_domain_info *info;
unsigned long flags;
@@ -5078,7 +5085,7 @@ static int intel_iommu_attach_device(struct iommu_domain *domain,
old_domain = find_domain(dev);
if (old_domain) {
rcu_read_lock();
- dmar_remove_one_dev_info(old_domain, dev);
+ dmar_remove_one_dev_info(dev);
rcu_read_unlock();
if (!domain_type_is_vm_or_si(old_domain) &&
@@ -5097,9 +5104,9 @@ static int intel_iommu_attach_device(struct iommu_domain *domain,
addr_width = cap_mgaw(iommu->cap);
if (dmar_domain->max_addr > (1LL << addr_width)) {
- pr_err("%s: iommu width (%d) is not "
- "sufficient for the mapped address (%llx)\n",
- __func__, addr_width, dmar_domain->max_addr);
+ dev_err(dev, "%s: iommu width (%d) is not "
+ "sufficient for the mapped address (%llx)\n",
+ __func__, addr_width, dmar_domain->max_addr);
return -EFAULT;
}
dmar_domain->gaw = addr_width;
@@ -5125,7 +5132,7 @@ static int intel_iommu_attach_device(struct iommu_domain *domain,
static void intel_iommu_detach_device(struct iommu_domain *domain,
struct device *dev)
{
- dmar_remove_one_dev_info(to_dmar_domain(domain), dev);
+ dmar_remove_one_dev_info(dev);
}
static int intel_iommu_map(struct iommu_domain *domain,
@@ -5328,7 +5335,7 @@ int intel_iommu_enable_pasid(struct intel_iommu *iommu, struct intel_svm_dev *sd
ctx_lo = context[0].lo;
- sdev->did = domain->iommu_did[iommu->seq_id];
+ sdev->did = FLPT_DEFAULT_DID;
sdev->sid = PCI_DEVID(info->bus, info->devfn);
if (!(ctx_lo & CONTEXT_PASIDE)) {
@@ -5400,7 +5407,7 @@ const struct iommu_ops intel_iommu_ops = {
static void quirk_iommu_g4x_gfx(struct pci_dev *dev)
{
/* G4x/GM45 integrated gfx dmar support is totally busted. */
- pr_info("Disabling IOMMU for graphics on this chipset\n");
+ pci_info(dev, "Disabling IOMMU for graphics on this chipset\n");
dmar_map_gfx = 0;
}
@@ -5418,7 +5425,7 @@ static void quirk_iommu_rwbf(struct pci_dev *dev)
* Mobile 4 Series Chipset neglects to set RWBF capability,
* but needs it. Same seems to hold for the desktop versions.
*/
- pr_info("Forcing write-buffer flush capability\n");
+ pci_info(dev, "Forcing write-buffer flush capability\n");
rwbf_quirk = 1;
}
@@ -5448,11 +5455,11 @@ static void quirk_calpella_no_shadow_gtt(struct pci_dev *dev)
return;
if (!(ggc & GGC_MEMORY_VT_ENABLED)) {
- pr_info("BIOS has allocated no shadow GTT; disabling IOMMU for graphics\n");
+ pci_info(dev, "BIOS has allocated no shadow GTT; disabling IOMMU for graphics\n");
dmar_map_gfx = 0;
} else if (dmar_map_gfx) {
/* we have to ensure the gfx device is idle before we flush */
- pr_info("Disabling batched IOTLB flush on Ironlake\n");
+ pci_info(dev, "Disabling batched IOTLB flush on Ironlake\n");
intel_iommu_strict = 1;
}
}
diff --git a/drivers/iommu/intel-pasid.c b/drivers/iommu/intel-pasid.c
index 53fe5248d8f1..03b12d2ee213 100644
--- a/drivers/iommu/intel-pasid.c
+++ b/drivers/iommu/intel-pasid.c
@@ -466,8 +466,8 @@ void intel_pasid_tear_down_entry(struct intel_iommu *iommu,
if (WARN_ON(!pte))
return;
- intel_pasid_clear_entry(dev, pasid);
did = pasid_get_domain_id(pte);
+ intel_pasid_clear_entry(dev, pasid);
if (!ecap_coherent(iommu->ecap))
clflush_cache_range(pte, sizeof(*pte));
diff --git a/drivers/iommu/intel-svm.c b/drivers/iommu/intel-svm.c
index a2a2aa4439aa..3a4b09ae8561 100644
--- a/drivers/iommu/intel-svm.c
+++ b/drivers/iommu/intel-svm.c
@@ -180,14 +180,6 @@ static void intel_flush_svm_range(struct intel_svm *svm, unsigned long address,
rcu_read_unlock();
}
-static void intel_change_pte(struct mmu_notifier *mn, struct mm_struct *mm,
- unsigned long address, pte_t pte)
-{
- struct intel_svm *svm = container_of(mn, struct intel_svm, notifier);
-
- intel_flush_svm_range(svm, address, 1, 1, 0);
-}
-
/* Pages have been freed at this point */
static void intel_invalidate_range(struct mmu_notifier *mn,
struct mm_struct *mm,
@@ -227,7 +219,6 @@ static void intel_mm_release(struct mmu_notifier *mn, struct mm_struct *mm)
static const struct mmu_notifier_ops intel_mmuops = {
.release = intel_mm_release,
- .change_pte = intel_change_pte,
.invalidate_range = intel_invalidate_range,
};
@@ -243,7 +234,7 @@ int intel_svm_bind_mm(struct device *dev, int *pasid, int flags, struct svm_dev_
int pasid_max;
int ret;
- if (!iommu)
+ if (!iommu || dmar_disabled)
return -EINVAL;
if (dev_is_pci(dev)) {
@@ -470,20 +461,31 @@ EXPORT_SYMBOL_GPL(intel_svm_is_pasid_valid);
/* Page request queue descriptor */
struct page_req_dsc {
- u64 srr:1;
- u64 bof:1;
- u64 pasid_present:1;
- u64 lpig:1;
- u64 pasid:20;
- u64 bus:8;
- u64 private:23;
- u64 prg_index:9;
- u64 rd_req:1;
- u64 wr_req:1;
- u64 exe_req:1;
- u64 priv_req:1;
- u64 devfn:8;
- u64 addr:52;
+ union {
+ struct {
+ u64 type:8;
+ u64 pasid_present:1;
+ u64 priv_data_present:1;
+ u64 rsvd:6;
+ u64 rid:16;
+ u64 pasid:20;
+ u64 exe_req:1;
+ u64 pm_req:1;
+ u64 rsvd2:10;
+ };
+ u64 qw_0;
+ };
+ union {
+ struct {
+ u64 rd_req:1;
+ u64 wr_req:1;
+ u64 lpig:1;
+ u64 prg_index:9;
+ u64 addr:52;
+ };
+ u64 qw_1;
+ };
+ u64 priv_data[2];
};
#define PRQ_RING_MASK ((0x1000 << PRQ_ORDER) - 0x10)
@@ -596,7 +598,7 @@ static irqreturn_t prq_event_thread(int irq, void *d)
/* Accounting for major/minor faults? */
rcu_read_lock();
list_for_each_entry_rcu(sdev, &svm->devs, list) {
- if (sdev->sid == PCI_DEVID(req->bus, req->devfn))
+ if (sdev->sid == req->rid)
break;
}
/* Other devices can go away, but the drivers are not permitted
@@ -609,33 +611,35 @@ static irqreturn_t prq_event_thread(int irq, void *d)
if (sdev && sdev->ops && sdev->ops->fault_cb) {
int rwxp = (req->rd_req << 3) | (req->wr_req << 2) |
- (req->exe_req << 1) | (req->priv_req);
- sdev->ops->fault_cb(sdev->dev, req->pasid, req->addr, req->private, rwxp, result);
+ (req->exe_req << 1) | (req->pm_req);
+ sdev->ops->fault_cb(sdev->dev, req->pasid, req->addr,
+ req->priv_data, rwxp, result);
}
/* We get here in the error case where the PASID lookup failed,
and these can be NULL. Do not use them below this point! */
sdev = NULL;
svm = NULL;
no_pasid:
- if (req->lpig) {
- /* Page Group Response */
+ if (req->lpig || req->priv_data_present) {
+ /*
+ * Per VT-d spec. v3.0 ch7.7, system software must
+ * respond with page group response if private data
+ * is present (PDP) or last page in group (LPIG) bit
+ * is set. This is an additional VT-d feature beyond
+ * PCI ATS spec.
+ */
resp.qw0 = QI_PGRP_PASID(req->pasid) |
- QI_PGRP_DID((req->bus << 8) | req->devfn) |
+ QI_PGRP_DID(req->rid) |
QI_PGRP_PASID_P(req->pasid_present) |
+ QI_PGRP_PDP(req->pasid_present) |
+ QI_PGRP_RESP_CODE(result) |
QI_PGRP_RESP_TYPE;
resp.qw1 = QI_PGRP_IDX(req->prg_index) |
- QI_PGRP_PRIV(req->private) |
- QI_PGRP_RESP_CODE(result);
- } else if (req->srr) {
- /* Page Stream Response */
- resp.qw0 = QI_PSTRM_IDX(req->prg_index) |
- QI_PSTRM_PRIV(req->private) |
- QI_PSTRM_BUS(req->bus) |
- QI_PSTRM_PASID(req->pasid) |
- QI_PSTRM_RESP_TYPE;
- resp.qw1 = QI_PSTRM_ADDR(address) |
- QI_PSTRM_DEVFN(req->devfn) |
- QI_PSTRM_RESP_CODE(result);
+ QI_PGRP_LPIG(req->lpig);
+
+ if (req->priv_data_present)
+ memcpy(&resp.qw2, req->priv_data,
+ sizeof(req->priv_data));
}
resp.qw2 = 0;
resp.qw3 = 0;
diff --git a/drivers/iommu/intel_irq_remapping.c b/drivers/iommu/intel_irq_remapping.c
index 24d45b07f425..2d74641b7f7b 100644
--- a/drivers/iommu/intel_irq_remapping.c
+++ b/drivers/iommu/intel_irq_remapping.c
@@ -294,6 +294,18 @@ static void set_irte_sid(struct irte *irte, unsigned int svt,
irte->sid = sid;
}
+/*
+ * Set an IRTE to match only the bus number. Interrupt requests that reference
+ * this IRTE must have a requester-id whose bus number is between or equal
+ * to the start_bus and end_bus arguments.
+ */
+static void set_irte_verify_bus(struct irte *irte, unsigned int start_bus,
+ unsigned int end_bus)
+{
+ set_irte_sid(irte, SVT_VERIFY_BUS, SQ_ALL_16,
+ (start_bus << 8) | end_bus);
+}
+
static int set_ioapic_sid(struct irte *irte, int apic)
{
int i;
@@ -356,6 +368,8 @@ static int set_hpet_sid(struct irte *irte, u8 id)
struct set_msi_sid_data {
struct pci_dev *pdev;
u16 alias;
+ int count;
+ int busmatch_count;
};
static int set_msi_sid_cb(struct pci_dev *pdev, u16 alias, void *opaque)
@@ -364,6 +378,10 @@ static int set_msi_sid_cb(struct pci_dev *pdev, u16 alias, void *opaque)
data->pdev = pdev;
data->alias = alias;
+ data->count++;
+
+ if (PCI_BUS_NUM(alias) == pdev->bus->number)
+ data->busmatch_count++;
return 0;
}
@@ -375,6 +393,8 @@ static int set_msi_sid(struct irte *irte, struct pci_dev *dev)
if (!irte || !dev)
return -1;
+ data.count = 0;
+ data.busmatch_count = 0;
pci_for_each_dma_alias(dev, set_msi_sid_cb, &data);
/*
@@ -383,6 +403,11 @@ static int set_msi_sid(struct irte *irte, struct pci_dev *dev)
* device is the case of a PCIe-to-PCI bridge, where the alias is for
* the subordinate bus. In this case we can only verify the bus.
*
+ * If there are multiple aliases, all with the same bus number,
+ * then all we can do is verify the bus. This is typical in NTB
+ * hardware which use proxy IDs where the device will generate traffic
+ * from multiple devfn numbers on the same bus.
+ *
* If the alias device is on a different bus than our source device
* then we have a topology based alias, use it.
*
@@ -391,9 +416,10 @@ static int set_msi_sid(struct irte *irte, struct pci_dev *dev)
* original device.
*/
if (PCI_BUS_NUM(data.alias) != data.pdev->bus->number)
- set_irte_sid(irte, SVT_VERIFY_BUS, SQ_ALL_16,
- PCI_DEVID(PCI_BUS_NUM(data.alias),
- dev->bus->number));
+ set_irte_verify_bus(irte, PCI_BUS_NUM(data.alias),
+ dev->bus->number);
+ else if (data.count >= 2 && data.busmatch_count == data.count)
+ set_irte_verify_bus(irte, dev->bus->number, dev->bus->number);
else if (data.pdev->bus->number != dev->bus->number)
set_irte_sid(irte, SVT_VERIFY_SID_SQ, SQ_ALL_16, data.alias);
else
diff --git a/drivers/iommu/io-pgtable-arm-v7s.c b/drivers/iommu/io-pgtable-arm-v7s.c
index cec29bf45c9b..9a8a8870e267 100644
--- a/drivers/iommu/io-pgtable-arm-v7s.c
+++ b/drivers/iommu/io-pgtable-arm-v7s.c
@@ -35,6 +35,7 @@
#include <linux/atomic.h>
#include <linux/dma-mapping.h>
#include <linux/gfp.h>
+#include <linux/io-pgtable.h>
#include <linux/iommu.h>
#include <linux/kernel.h>
#include <linux/kmemleak.h>
@@ -45,8 +46,6 @@
#include <asm/barrier.h>
-#include "io-pgtable.h"
-
/* Struct accessors */
#define io_pgtable_to_data(x) \
container_of((x), struct arm_v7s_io_pgtable, iop)
@@ -161,6 +160,14 @@
#define ARM_V7S_TCR_PD1 BIT(5)
+#ifdef CONFIG_ZONE_DMA32
+#define ARM_V7S_TABLE_GFP_DMA GFP_DMA32
+#define ARM_V7S_TABLE_SLAB_FLAGS SLAB_CACHE_DMA32
+#else
+#define ARM_V7S_TABLE_GFP_DMA GFP_DMA
+#define ARM_V7S_TABLE_SLAB_FLAGS SLAB_CACHE_DMA
+#endif
+
typedef u32 arm_v7s_iopte;
static bool selftest_running;
@@ -198,13 +205,16 @@ static void *__arm_v7s_alloc_table(int lvl, gfp_t gfp,
void *table = NULL;
if (lvl == 1)
- table = (void *)__get_dma_pages(__GFP_ZERO, get_order(size));
+ table = (void *)__get_free_pages(
+ __GFP_ZERO | ARM_V7S_TABLE_GFP_DMA, get_order(size));
else if (lvl == 2)
- table = kmem_cache_zalloc(data->l2_tables, gfp | GFP_DMA);
+ table = kmem_cache_zalloc(data->l2_tables, gfp);
phys = virt_to_phys(table);
- if (phys != (arm_v7s_iopte)phys)
+ if (phys != (arm_v7s_iopte)phys) {
/* Doesn't fit in PTE */
+ dev_err(dev, "Page table does not fit in PTE: %pa", &phys);
goto out_free;
+ }
if (table && !(cfg->quirks & IO_PGTABLE_QUIRK_NO_DMA)) {
dma = dma_map_single(dev, table, size, DMA_TO_DEVICE);
if (dma_mapping_error(dev, dma))
@@ -217,7 +227,8 @@ static void *__arm_v7s_alloc_table(int lvl, gfp_t gfp,
if (dma != phys)
goto out_unmap;
}
- kmemleak_ignore(table);
+ if (lvl == 2)
+ kmemleak_ignore(table);
return table;
out_unmap:
@@ -733,7 +744,7 @@ static struct io_pgtable *arm_v7s_alloc_pgtable(struct io_pgtable_cfg *cfg,
data->l2_tables = kmem_cache_create("io-pgtable_armv7s_l2",
ARM_V7S_TABLE_SIZE(2),
ARM_V7S_TABLE_SIZE(2),
- SLAB_CACHE_DMA, NULL);
+ ARM_V7S_TABLE_SLAB_FLAGS, NULL);
if (!data->l2_tables)
goto out_free_data;
diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c
index 237cacd4a62b..d3700ec15cbd 100644
--- a/drivers/iommu/io-pgtable-arm.c
+++ b/drivers/iommu/io-pgtable-arm.c
@@ -22,6 +22,7 @@
#include <linux/atomic.h>
#include <linux/bitops.h>
+#include <linux/io-pgtable.h>
#include <linux/iommu.h>
#include <linux/kernel.h>
#include <linux/sizes.h>
@@ -31,8 +32,6 @@
#include <asm/barrier.h>
-#include "io-pgtable.h"
-
#define ARM_LPAE_MAX_ADDR_BITS 52
#define ARM_LPAE_S2_MAX_CONCAT_PAGES 16
#define ARM_LPAE_MAX_LEVELS 4
diff --git a/drivers/iommu/io-pgtable.c b/drivers/iommu/io-pgtable.c
index 127558d83667..93f2880be6c6 100644
--- a/drivers/iommu/io-pgtable.c
+++ b/drivers/iommu/io-pgtable.c
@@ -19,11 +19,10 @@
*/
#include <linux/bug.h>
+#include <linux/io-pgtable.h>
#include <linux/kernel.h>
#include <linux/types.h>
-#include "io-pgtable.h"
-
static const struct io_pgtable_init_fns *
io_pgtable_init_table[IO_PGTABLE_NUM_FMTS] = {
#ifdef CONFIG_IOMMU_IO_PGTABLE_LPAE
@@ -61,6 +60,7 @@ struct io_pgtable_ops *alloc_io_pgtable_ops(enum io_pgtable_fmt fmt,
return &iop->ops;
}
+EXPORT_SYMBOL_GPL(alloc_io_pgtable_ops);
/*
* It is the IOMMU driver's responsibility to ensure that the page table
@@ -77,3 +77,4 @@ void free_io_pgtable_ops(struct io_pgtable_ops *ops)
io_pgtable_tlb_flush_all(iop);
io_pgtable_init_table[iop->fmt]->free(iop);
}
+EXPORT_SYMBOL_GPL(free_io_pgtable_ops);
diff --git a/drivers/iommu/io-pgtable.h b/drivers/iommu/io-pgtable.h
deleted file mode 100644
index 47d5ae559329..000000000000
--- a/drivers/iommu/io-pgtable.h
+++ /dev/null
@@ -1,213 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef __IO_PGTABLE_H
-#define __IO_PGTABLE_H
-#include <linux/bitops.h>
-
-/*
- * Public API for use by IOMMU drivers
- */
-enum io_pgtable_fmt {
- ARM_32_LPAE_S1,
- ARM_32_LPAE_S2,
- ARM_64_LPAE_S1,
- ARM_64_LPAE_S2,
- ARM_V7S,
- IO_PGTABLE_NUM_FMTS,
-};
-
-/**
- * struct iommu_gather_ops - IOMMU callbacks for TLB and page table management.
- *
- * @tlb_flush_all: Synchronously invalidate the entire TLB context.
- * @tlb_add_flush: Queue up a TLB invalidation for a virtual address range.
- * @tlb_sync: Ensure any queued TLB invalidation has taken effect, and
- * any corresponding page table updates are visible to the
- * IOMMU.
- *
- * Note that these can all be called in atomic context and must therefore
- * not block.
- */
-struct iommu_gather_ops {
- void (*tlb_flush_all)(void *cookie);
- void (*tlb_add_flush)(unsigned long iova, size_t size, size_t granule,
- bool leaf, void *cookie);
- void (*tlb_sync)(void *cookie);
-};
-
-/**
- * struct io_pgtable_cfg - Configuration data for a set of page tables.
- *
- * @quirks: A bitmap of hardware quirks that require some special
- * action by the low-level page table allocator.
- * @pgsize_bitmap: A bitmap of page sizes supported by this set of page
- * tables.
- * @ias: Input address (iova) size, in bits.
- * @oas: Output address (paddr) size, in bits.
- * @tlb: TLB management callbacks for this set of tables.
- * @iommu_dev: The device representing the DMA configuration for the
- * page table walker.
- */
-struct io_pgtable_cfg {
- /*
- * IO_PGTABLE_QUIRK_ARM_NS: (ARM formats) Set NS and NSTABLE bits in
- * stage 1 PTEs, for hardware which insists on validating them
- * even in non-secure state where they should normally be ignored.
- *
- * IO_PGTABLE_QUIRK_NO_PERMS: Ignore the IOMMU_READ, IOMMU_WRITE and
- * IOMMU_NOEXEC flags and map everything with full access, for
- * hardware which does not implement the permissions of a given
- * format, and/or requires some format-specific default value.
- *
- * IO_PGTABLE_QUIRK_TLBI_ON_MAP: If the format forbids caching invalid
- * (unmapped) entries but the hardware might do so anyway, perform
- * TLB maintenance when mapping as well as when unmapping.
- *
- * IO_PGTABLE_QUIRK_ARM_MTK_4GB: (ARM v7s format) Set bit 9 in all
- * PTEs, for Mediatek IOMMUs which treat it as a 33rd address bit
- * when the SoC is in "4GB mode" and they can only access the high
- * remap of DRAM (0x1_00000000 to 0x1_ffffffff).
- *
- * IO_PGTABLE_QUIRK_NO_DMA: Guarantees that the tables will only ever
- * be accessed by a fully cache-coherent IOMMU or CPU (e.g. for a
- * software-emulated IOMMU), such that pagetable updates need not
- * be treated as explicit DMA data.
- *
- * IO_PGTABLE_QUIRK_NON_STRICT: Skip issuing synchronous leaf TLBIs
- * on unmap, for DMA domains using the flush queue mechanism for
- * delayed invalidation.
- */
- #define IO_PGTABLE_QUIRK_ARM_NS BIT(0)
- #define IO_PGTABLE_QUIRK_NO_PERMS BIT(1)
- #define IO_PGTABLE_QUIRK_TLBI_ON_MAP BIT(2)
- #define IO_PGTABLE_QUIRK_ARM_MTK_4GB BIT(3)
- #define IO_PGTABLE_QUIRK_NO_DMA BIT(4)
- #define IO_PGTABLE_QUIRK_NON_STRICT BIT(5)
- unsigned long quirks;
- unsigned long pgsize_bitmap;
- unsigned int ias;
- unsigned int oas;
- const struct iommu_gather_ops *tlb;
- struct device *iommu_dev;
-
- /* Low-level data specific to the table format */
- union {
- struct {
- u64 ttbr[2];
- u64 tcr;
- u64 mair[2];
- } arm_lpae_s1_cfg;
-
- struct {
- u64 vttbr;
- u64 vtcr;
- } arm_lpae_s2_cfg;
-
- struct {
- u32 ttbr[2];
- u32 tcr;
- u32 nmrr;
- u32 prrr;
- } arm_v7s_cfg;
- };
-};
-
-/**
- * struct io_pgtable_ops - Page table manipulation API for IOMMU drivers.
- *
- * @map: Map a physically contiguous memory region.
- * @unmap: Unmap a physically contiguous memory region.
- * @iova_to_phys: Translate iova to physical address.
- *
- * These functions map directly onto the iommu_ops member functions with
- * the same names.
- */
-struct io_pgtable_ops {
- int (*map)(struct io_pgtable_ops *ops, unsigned long iova,
- phys_addr_t paddr, size_t size, int prot);
- size_t (*unmap)(struct io_pgtable_ops *ops, unsigned long iova,
- size_t size);
- phys_addr_t (*iova_to_phys)(struct io_pgtable_ops *ops,
- unsigned long iova);
-};
-
-/**
- * alloc_io_pgtable_ops() - Allocate a page table allocator for use by an IOMMU.
- *
- * @fmt: The page table format.
- * @cfg: The page table configuration. This will be modified to represent
- * the configuration actually provided by the allocator (e.g. the
- * pgsize_bitmap may be restricted).
- * @cookie: An opaque token provided by the IOMMU driver and passed back to
- * the callback routines in cfg->tlb.
- */
-struct io_pgtable_ops *alloc_io_pgtable_ops(enum io_pgtable_fmt fmt,
- struct io_pgtable_cfg *cfg,
- void *cookie);
-
-/**
- * free_io_pgtable_ops() - Free an io_pgtable_ops structure. The caller
- * *must* ensure that the page table is no longer
- * live, but the TLB can be dirty.
- *
- * @ops: The ops returned from alloc_io_pgtable_ops.
- */
-void free_io_pgtable_ops(struct io_pgtable_ops *ops);
-
-
-/*
- * Internal structures for page table allocator implementations.
- */
-
-/**
- * struct io_pgtable - Internal structure describing a set of page tables.
- *
- * @fmt: The page table format.
- * @cookie: An opaque token provided by the IOMMU driver and passed back to
- * any callback routines.
- * @cfg: A copy of the page table configuration.
- * @ops: The page table operations in use for this set of page tables.
- */
-struct io_pgtable {
- enum io_pgtable_fmt fmt;
- void *cookie;
- struct io_pgtable_cfg cfg;
- struct io_pgtable_ops ops;
-};
-
-#define io_pgtable_ops_to_pgtable(x) container_of((x), struct io_pgtable, ops)
-
-static inline void io_pgtable_tlb_flush_all(struct io_pgtable *iop)
-{
- iop->cfg.tlb->tlb_flush_all(iop->cookie);
-}
-
-static inline void io_pgtable_tlb_add_flush(struct io_pgtable *iop,
- unsigned long iova, size_t size, size_t granule, bool leaf)
-{
- iop->cfg.tlb->tlb_add_flush(iova, size, granule, leaf, iop->cookie);
-}
-
-static inline void io_pgtable_tlb_sync(struct io_pgtable *iop)
-{
- iop->cfg.tlb->tlb_sync(iop->cookie);
-}
-
-/**
- * struct io_pgtable_init_fns - Alloc/free a set of page tables for a
- * particular format.
- *
- * @alloc: Allocate a set of page tables described by cfg.
- * @free: Free the page tables associated with iop.
- */
-struct io_pgtable_init_fns {
- struct io_pgtable *(*alloc)(struct io_pgtable_cfg *cfg, void *cookie);
- void (*free)(struct io_pgtable *iop);
-};
-
-extern struct io_pgtable_init_fns io_pgtable_arm_32_lpae_s1_init_fns;
-extern struct io_pgtable_init_fns io_pgtable_arm_32_lpae_s2_init_fns;
-extern struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s1_init_fns;
-extern struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s2_init_fns;
-extern struct io_pgtable_init_fns io_pgtable_arm_v7s_init_fns;
-
-#endif /* __IO_PGTABLE_H */
diff --git a/drivers/iommu/iommu-debugfs.c b/drivers/iommu/iommu-debugfs.c
index 3b1bf88fd1b0..f03548942096 100644
--- a/drivers/iommu/iommu-debugfs.c
+++ b/drivers/iommu/iommu-debugfs.c
@@ -12,6 +12,7 @@
#include <linux/debugfs.h>
struct dentry *iommu_debugfs_dir;
+EXPORT_SYMBOL_GPL(iommu_debugfs_dir);
/**
* iommu_debugfs_setup - create the top-level iommu directory in debugfs
@@ -23,9 +24,9 @@ struct dentry *iommu_debugfs_dir;
* Emit a strong warning at boot time to indicate that this feature is
* enabled.
*
- * This function is called from iommu_init; drivers may then call
- * iommu_debugfs_new_driver_dir() to instantiate a vendor-specific
- * directory to be used to expose internal data.
+ * This function is called from iommu_init; drivers may then use
+ * iommu_debugfs_dir to instantiate a vendor-specific directory to be used
+ * to expose internal data.
*/
void iommu_debugfs_setup(void)
{
@@ -48,19 +49,3 @@ void iommu_debugfs_setup(void)
pr_warn("*************************************************************\n");
}
}
-
-/**
- * iommu_debugfs_new_driver_dir - create a vendor directory under debugfs/iommu
- * @vendor: name of the vendor-specific subdirectory to create
- *
- * This function is called by an IOMMU driver to create the top-level debugfs
- * directory for that driver.
- *
- * Return: upon success, a pointer to the dentry for the new directory.
- * NULL in case of failure.
- */
-struct dentry *iommu_debugfs_new_driver_dir(const char *vendor)
-{
- return debugfs_create_dir(vendor, iommu_debugfs_dir);
-}
-EXPORT_SYMBOL_GPL(iommu_debugfs_new_driver_dir);
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index 3ed4db334341..109de67d5d72 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -668,7 +668,7 @@ rename:
trace_add_device_to_group(group->id, dev);
- pr_info("Adding device %s to group %d\n", dev_name(dev), group->id);
+ dev_info(dev, "Adding to iommu group %d\n", group->id);
return 0;
@@ -684,7 +684,7 @@ err_remove_link:
sysfs_remove_link(&dev->kobj, "iommu_group");
err_free_device:
kfree(device);
- pr_err("Failed to add device %s to group %d: %d\n", dev_name(dev), group->id, ret);
+ dev_err(dev, "Failed to add to iommu group %d: %d\n", group->id, ret);
return ret;
}
EXPORT_SYMBOL_GPL(iommu_group_add_device);
@@ -701,7 +701,7 @@ void iommu_group_remove_device(struct device *dev)
struct iommu_group *group = dev->iommu_group;
struct group_device *tmp_device, *device = NULL;
- pr_info("Removing device %s from group %d\n", dev_name(dev), group->id);
+ dev_info(dev, "Removing from iommu group %d\n", group->id);
/* Pre-notify listeners that a device is being removed. */
blocking_notifier_call_chain(&group->notifier,
@@ -1105,10 +1105,12 @@ struct iommu_group *iommu_group_get_for_dev(struct device *dev)
dom = __iommu_domain_alloc(dev->bus, iommu_def_domain_type);
if (!dom && iommu_def_domain_type != IOMMU_DOMAIN_DMA) {
- dev_warn(dev,
- "failed to allocate default IOMMU domain of type %u; falling back to IOMMU_DOMAIN_DMA",
- iommu_def_domain_type);
dom = __iommu_domain_alloc(dev->bus, IOMMU_DOMAIN_DMA);
+ if (dom) {
+ dev_warn(dev,
+ "failed to allocate default IOMMU domain of type %u; falling back to IOMMU_DOMAIN_DMA",
+ iommu_def_domain_type);
+ }
}
group->default_domain = dom;
@@ -1585,13 +1587,14 @@ static size_t iommu_pgsize(struct iommu_domain *domain,
int iommu_map(struct iommu_domain *domain, unsigned long iova,
phys_addr_t paddr, size_t size, int prot)
{
+ const struct iommu_ops *ops = domain->ops;
unsigned long orig_iova = iova;
unsigned int min_pagesz;
size_t orig_size = size;
phys_addr_t orig_paddr = paddr;
int ret = 0;
- if (unlikely(domain->ops->map == NULL ||
+ if (unlikely(ops->map == NULL ||
domain->pgsize_bitmap == 0UL))
return -ENODEV;
@@ -1620,7 +1623,7 @@ int iommu_map(struct iommu_domain *domain, unsigned long iova,
pr_debug("mapping: iova 0x%lx pa %pa pgsize 0x%zx\n",
iova, &paddr, pgsize);
- ret = domain->ops->map(domain, iova, paddr, pgsize, prot);
+ ret = ops->map(domain, iova, paddr, pgsize, prot);
if (ret)
break;
@@ -1629,6 +1632,9 @@ int iommu_map(struct iommu_domain *domain, unsigned long iova,
size -= pgsize;
}
+ if (ops->iotlb_sync_map)
+ ops->iotlb_sync_map(domain);
+
/* unroll mapping in case something went wrong */
if (ret)
iommu_unmap(domain, orig_iova, orig_size - size);
@@ -1951,7 +1957,7 @@ int iommu_request_dm_for_dev(struct device *dev)
iommu_domain_free(group->default_domain);
group->default_domain = dm_domain;
- pr_info("Using direct mapping for device %s\n", dev_name(dev));
+ dev_info(dev, "Using iommu direct mapping\n");
ret = 0;
out:
diff --git a/drivers/iommu/iova.c b/drivers/iommu/iova.c
index f8d3ba247523..2de8122e218f 100644
--- a/drivers/iommu/iova.c
+++ b/drivers/iommu/iova.c
@@ -207,8 +207,10 @@ static int __alloc_and_insert_iova_range(struct iova_domain *iovad,
curr_iova = rb_entry(curr, struct iova, node);
} while (curr && new_pfn <= curr_iova->pfn_hi);
- if (limit_pfn < size || new_pfn < iovad->start_pfn)
+ if (limit_pfn < size || new_pfn < iovad->start_pfn) {
+ iovad->max32_alloc_size = size;
goto iova32_full;
+ }
/* pfn_lo will point to size aligned address if size_aligned is set */
new->pfn_lo = new_pfn;
@@ -222,7 +224,6 @@ static int __alloc_and_insert_iova_range(struct iova_domain *iovad,
return 0;
iova32_full:
- iovad->max32_alloc_size = size;
spin_unlock_irqrestore(&iovad->iova_rbtree_lock, flags);
return -ENOMEM;
}
diff --git a/drivers/iommu/ipmmu-vmsa.c b/drivers/iommu/ipmmu-vmsa.c
index 7a4529c61c19..9a380c10655e 100644
--- a/drivers/iommu/ipmmu-vmsa.c
+++ b/drivers/iommu/ipmmu-vmsa.c
@@ -15,6 +15,7 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
+#include <linux/io-pgtable.h>
#include <linux/iommu.h>
#include <linux/of.h>
#include <linux/of_device.h>
@@ -35,8 +36,6 @@
#define arm_iommu_detach_device(...) do {} while (0)
#endif
-#include "io-pgtable.h"
-
#define IPMMU_CTX_MAX 8
struct ipmmu_features {
diff --git a/drivers/iommu/irq_remapping.c b/drivers/iommu/irq_remapping.c
index b94ebd42edd8..81cf2908c531 100644
--- a/drivers/iommu/irq_remapping.c
+++ b/drivers/iommu/irq_remapping.c
@@ -103,6 +103,9 @@ int __init irq_remapping_prepare(void)
else if (IS_ENABLED(CONFIG_AMD_IOMMU) &&
amd_iommu_irq_ops.prepare() == 0)
remap_ops = &amd_iommu_irq_ops;
+ else if (IS_ENABLED(CONFIG_HYPERV_IOMMU) &&
+ hyperv_irq_remap_ops.prepare() == 0)
+ remap_ops = &hyperv_irq_remap_ops;
else
return -ENOSYS;
diff --git a/drivers/iommu/irq_remapping.h b/drivers/iommu/irq_remapping.h
index 0afef6e43be4..f8609e9f1f5d 100644
--- a/drivers/iommu/irq_remapping.h
+++ b/drivers/iommu/irq_remapping.h
@@ -64,6 +64,7 @@ struct irq_remap_ops {
extern struct irq_remap_ops intel_irq_remap_ops;
extern struct irq_remap_ops amd_iommu_irq_ops;
+extern struct irq_remap_ops hyperv_irq_remap_ops;
#else /* CONFIG_IRQ_REMAP */
diff --git a/drivers/iommu/msm_iommu.c b/drivers/iommu/msm_iommu.c
index fc4270733f11..9fb0eb7a4d02 100644
--- a/drivers/iommu/msm_iommu.c
+++ b/drivers/iommu/msm_iommu.c
@@ -23,6 +23,7 @@
#include <linux/platform_device.h>
#include <linux/errno.h>
#include <linux/io.h>
+#include <linux/io-pgtable.h>
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/spinlock.h>
@@ -37,7 +38,6 @@
#include "msm_iommu_hw-8xxx.h"
#include "msm_iommu.h"
-#include "io-pgtable.h"
#define MRC(reg, processor, op1, crn, crm, op2) \
__asm__ __volatile__ ( \
@@ -461,10 +461,10 @@ static int msm_iommu_attach_dev(struct iommu_domain *domain, struct device *dev)
master->num =
msm_iommu_alloc_ctx(iommu->context_map,
0, iommu->ncb);
- if (IS_ERR_VALUE(master->num)) {
- ret = -ENODEV;
- goto fail;
- }
+ if (IS_ERR_VALUE(master->num)) {
+ ret = -ENODEV;
+ goto fail;
+ }
config_mids(iommu, master);
__program_context(iommu->base, master->num,
priv);
diff --git a/drivers/iommu/mtk_iommu.h b/drivers/iommu/mtk_iommu.h
index 778498b8633f..62c2c3e8c5df 100644
--- a/drivers/iommu/mtk_iommu.h
+++ b/drivers/iommu/mtk_iommu.h
@@ -19,13 +19,12 @@
#include <linux/component.h>
#include <linux/device.h>
#include <linux/io.h>
+#include <linux/io-pgtable.h>
#include <linux/iommu.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#include <soc/mediatek/smi.h>
-#include "io-pgtable.h"
-
struct mtk_iommu_suspend_reg {
u32 standard_axi_mode;
u32 dcm_dis;
diff --git a/drivers/iommu/mtk_iommu_v1.c b/drivers/iommu/mtk_iommu_v1.c
index 7e0df67bd3e9..52b01e3a49df 100644
--- a/drivers/iommu/mtk_iommu_v1.c
+++ b/drivers/iommu/mtk_iommu_v1.c
@@ -474,7 +474,7 @@ static int mtk_iommu_add_device(struct device *dev)
return err;
}
- return iommu_device_link(&data->iommu, dev);;
+ return iommu_device_link(&data->iommu, dev);
}
static void mtk_iommu_remove_device(struct device *dev)
diff --git a/drivers/iommu/qcom_iommu.c b/drivers/iommu/qcom_iommu.c
index d8595f0a987d..8cdd3f059513 100644
--- a/drivers/iommu/qcom_iommu.c
+++ b/drivers/iommu/qcom_iommu.c
@@ -26,6 +26,7 @@
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/io-64-nonatomic-hi-lo.h>
+#include <linux/io-pgtable.h>
#include <linux/iommu.h>
#include <linux/iopoll.h>
#include <linux/kconfig.h>
@@ -42,7 +43,6 @@
#include <linux/slab.h>
#include <linux/spinlock.h>
-#include "io-pgtable.h"
#include "arm-smmu-regs.h"
#define SMMU_INTR_SEL_NS 0x2000
diff --git a/drivers/iommu/tegra-gart.c b/drivers/iommu/tegra-gart.c
index da6a4e357b2b..4d8057916552 100644
--- a/drivers/iommu/tegra-gart.c
+++ b/drivers/iommu/tegra-gart.c
@@ -1,5 +1,5 @@
/*
- * IOMMU API for GART in Tegra20
+ * IOMMU API for Graphics Address Relocation Table on Tegra20
*
* Copyright (c) 2010-2012, NVIDIA CORPORATION. All rights reserved.
*
@@ -19,101 +19,75 @@
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#define pr_fmt(fmt) "%s(): " fmt, __func__
+#define dev_fmt(fmt) "gart: " fmt
-#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/iommu.h>
#include <linux/moduleparam.h>
#include <linux/platform_device.h>
-#include <linux/spinlock.h>
#include <linux/slab.h>
+#include <linux/spinlock.h>
#include <linux/vmalloc.h>
-#include <linux/mm.h>
-#include <linux/list.h>
-#include <linux/device.h>
-#include <linux/io.h>
-#include <linux/iommu.h>
-#include <linux/of.h>
-
-#include <asm/cacheflush.h>
-/* bitmap of the page sizes currently supported */
-#define GART_IOMMU_PGSIZES (SZ_4K)
+#include <soc/tegra/mc.h>
#define GART_REG_BASE 0x24
#define GART_CONFIG (0x24 - GART_REG_BASE)
#define GART_ENTRY_ADDR (0x28 - GART_REG_BASE)
#define GART_ENTRY_DATA (0x2c - GART_REG_BASE)
-#define GART_ENTRY_PHYS_ADDR_VALID (1 << 31)
+
+#define GART_ENTRY_PHYS_ADDR_VALID BIT(31)
#define GART_PAGE_SHIFT 12
#define GART_PAGE_SIZE (1 << GART_PAGE_SHIFT)
-#define GART_PAGE_MASK \
- (~(GART_PAGE_SIZE - 1) & ~GART_ENTRY_PHYS_ADDR_VALID)
+#define GART_PAGE_MASK GENMASK(30, GART_PAGE_SHIFT)
-struct gart_client {
- struct device *dev;
- struct list_head list;
-};
+/* bitmap of the page sizes currently supported */
+#define GART_IOMMU_PGSIZES (GART_PAGE_SIZE)
struct gart_device {
void __iomem *regs;
u32 *savedata;
- u32 page_count; /* total remappable size */
- dma_addr_t iovmm_base; /* offset to vmm_area */
+ unsigned long iovmm_base; /* offset to vmm_area start */
+ unsigned long iovmm_end; /* offset to vmm_area end */
spinlock_t pte_lock; /* for pagetable */
- struct list_head client;
- spinlock_t client_lock; /* for client list */
- struct device *dev;
-
+ spinlock_t dom_lock; /* for active domain */
+ unsigned int active_devices; /* number of active devices */
+ struct iommu_domain *active_domain; /* current active domain */
struct iommu_device iommu; /* IOMMU Core handle */
-};
-
-struct gart_domain {
- struct iommu_domain domain; /* generic domain handle */
- struct gart_device *gart; /* link to gart device */
+ struct device *dev;
};
static struct gart_device *gart_handle; /* unique for a system */
static bool gart_debug;
-#define GART_PTE(_pfn) \
- (GART_ENTRY_PHYS_ADDR_VALID | ((_pfn) << PAGE_SHIFT))
-
-static struct gart_domain *to_gart_domain(struct iommu_domain *dom)
-{
- return container_of(dom, struct gart_domain, domain);
-}
-
/*
* Any interaction between any block on PPSB and a block on APB or AHB
* must have these read-back to ensure the APB/AHB bus transaction is
* complete before initiating activity on the PPSB block.
*/
-#define FLUSH_GART_REGS(gart) ((void)readl((gart)->regs + GART_CONFIG))
+#define FLUSH_GART_REGS(gart) readl_relaxed((gart)->regs + GART_CONFIG)
#define for_each_gart_pte(gart, iova) \
for (iova = gart->iovmm_base; \
- iova < gart->iovmm_base + GART_PAGE_SIZE * gart->page_count; \
+ iova < gart->iovmm_end; \
iova += GART_PAGE_SIZE)
static inline void gart_set_pte(struct gart_device *gart,
- unsigned long offs, u32 pte)
+ unsigned long iova, unsigned long pte)
{
- writel(offs, gart->regs + GART_ENTRY_ADDR);
- writel(pte, gart->regs + GART_ENTRY_DATA);
-
- dev_dbg(gart->dev, "%s %08lx:%08x\n",
- pte ? "map" : "unmap", offs, pte & GART_PAGE_MASK);
+ writel_relaxed(iova, gart->regs + GART_ENTRY_ADDR);
+ writel_relaxed(pte, gart->regs + GART_ENTRY_DATA);
}
static inline unsigned long gart_read_pte(struct gart_device *gart,
- unsigned long offs)
+ unsigned long iova)
{
unsigned long pte;
- writel(offs, gart->regs + GART_ENTRY_ADDR);
- pte = readl(gart->regs + GART_ENTRY_DATA);
+ writel_relaxed(iova, gart->regs + GART_ENTRY_ADDR);
+ pte = readl_relaxed(gart->regs + GART_ENTRY_DATA);
return pte;
}
@@ -125,224 +99,155 @@ static void do_gart_setup(struct gart_device *gart, const u32 *data)
for_each_gart_pte(gart, iova)
gart_set_pte(gart, iova, data ? *(data++) : 0);
- writel(1, gart->regs + GART_CONFIG);
+ writel_relaxed(1, gart->regs + GART_CONFIG);
FLUSH_GART_REGS(gart);
}
-#ifdef DEBUG
-static void gart_dump_table(struct gart_device *gart)
-{
- unsigned long iova;
- unsigned long flags;
-
- spin_lock_irqsave(&gart->pte_lock, flags);
- for_each_gart_pte(gart, iova) {
- unsigned long pte;
-
- pte = gart_read_pte(gart, iova);
-
- dev_dbg(gart->dev, "%s %08lx:%08lx\n",
- (GART_ENTRY_PHYS_ADDR_VALID & pte) ? "v" : " ",
- iova, pte & GART_PAGE_MASK);
- }
- spin_unlock_irqrestore(&gart->pte_lock, flags);
-}
-#else
-static inline void gart_dump_table(struct gart_device *gart)
+static inline bool gart_iova_range_invalid(struct gart_device *gart,
+ unsigned long iova, size_t bytes)
{
+ return unlikely(iova < gart->iovmm_base || bytes != GART_PAGE_SIZE ||
+ iova + bytes > gart->iovmm_end);
}
-#endif
-static inline bool gart_iova_range_valid(struct gart_device *gart,
- unsigned long iova, size_t bytes)
+static inline bool gart_pte_valid(struct gart_device *gart, unsigned long iova)
{
- unsigned long iova_start, iova_end, gart_start, gart_end;
-
- iova_start = iova;
- iova_end = iova_start + bytes - 1;
- gart_start = gart->iovmm_base;
- gart_end = gart_start + gart->page_count * GART_PAGE_SIZE - 1;
-
- if (iova_start < gart_start)
- return false;
- if (iova_end > gart_end)
- return false;
- return true;
+ return !!(gart_read_pte(gart, iova) & GART_ENTRY_PHYS_ADDR_VALID);
}
static int gart_iommu_attach_dev(struct iommu_domain *domain,
struct device *dev)
{
- struct gart_domain *gart_domain = to_gart_domain(domain);
- struct gart_device *gart = gart_domain->gart;
- struct gart_client *client, *c;
- int err = 0;
-
- client = devm_kzalloc(gart->dev, sizeof(*c), GFP_KERNEL);
- if (!client)
- return -ENOMEM;
- client->dev = dev;
-
- spin_lock(&gart->client_lock);
- list_for_each_entry(c, &gart->client, list) {
- if (c->dev == dev) {
- dev_err(gart->dev,
- "%s is already attached\n", dev_name(dev));
- err = -EINVAL;
- goto fail;
- }
+ struct gart_device *gart = gart_handle;
+ int ret = 0;
+
+ spin_lock(&gart->dom_lock);
+
+ if (gart->active_domain && gart->active_domain != domain) {
+ ret = -EBUSY;
+ } else if (dev->archdata.iommu != domain) {
+ dev->archdata.iommu = domain;
+ gart->active_domain = domain;
+ gart->active_devices++;
}
- list_add(&client->list, &gart->client);
- spin_unlock(&gart->client_lock);
- dev_dbg(gart->dev, "Attached %s\n", dev_name(dev));
- return 0;
-fail:
- devm_kfree(gart->dev, client);
- spin_unlock(&gart->client_lock);
- return err;
+ spin_unlock(&gart->dom_lock);
+
+ return ret;
}
static void gart_iommu_detach_dev(struct iommu_domain *domain,
struct device *dev)
{
- struct gart_domain *gart_domain = to_gart_domain(domain);
- struct gart_device *gart = gart_domain->gart;
- struct gart_client *c;
-
- spin_lock(&gart->client_lock);
-
- list_for_each_entry(c, &gart->client, list) {
- if (c->dev == dev) {
- list_del(&c->list);
- devm_kfree(gart->dev, c);
- dev_dbg(gart->dev, "Detached %s\n", dev_name(dev));
- goto out;
- }
+ struct gart_device *gart = gart_handle;
+
+ spin_lock(&gart->dom_lock);
+
+ if (dev->archdata.iommu == domain) {
+ dev->archdata.iommu = NULL;
+
+ if (--gart->active_devices == 0)
+ gart->active_domain = NULL;
}
- dev_err(gart->dev, "Couldn't find\n");
-out:
- spin_unlock(&gart->client_lock);
+
+ spin_unlock(&gart->dom_lock);
}
static struct iommu_domain *gart_iommu_domain_alloc(unsigned type)
{
- struct gart_domain *gart_domain;
- struct gart_device *gart;
+ struct iommu_domain *domain;
if (type != IOMMU_DOMAIN_UNMANAGED)
return NULL;
- gart = gart_handle;
- if (!gart)
- return NULL;
-
- gart_domain = kzalloc(sizeof(*gart_domain), GFP_KERNEL);
- if (!gart_domain)
- return NULL;
-
- gart_domain->gart = gart;
- gart_domain->domain.geometry.aperture_start = gart->iovmm_base;
- gart_domain->domain.geometry.aperture_end = gart->iovmm_base +
- gart->page_count * GART_PAGE_SIZE - 1;
- gart_domain->domain.geometry.force_aperture = true;
+ domain = kzalloc(sizeof(*domain), GFP_KERNEL);
+ if (domain) {
+ domain->geometry.aperture_start = gart_handle->iovmm_base;
+ domain->geometry.aperture_end = gart_handle->iovmm_end - 1;
+ domain->geometry.force_aperture = true;
+ }
- return &gart_domain->domain;
+ return domain;
}
static void gart_iommu_domain_free(struct iommu_domain *domain)
{
- struct gart_domain *gart_domain = to_gart_domain(domain);
- struct gart_device *gart = gart_domain->gart;
-
- if (gart) {
- spin_lock(&gart->client_lock);
- if (!list_empty(&gart->client)) {
- struct gart_client *c;
-
- list_for_each_entry(c, &gart->client, list)
- gart_iommu_detach_dev(domain, c->dev);
- }
- spin_unlock(&gart->client_lock);
+ WARN_ON(gart_handle->active_domain == domain);
+ kfree(domain);
+}
+
+static inline int __gart_iommu_map(struct gart_device *gart, unsigned long iova,
+ unsigned long pa)
+{
+ if (unlikely(gart_debug && gart_pte_valid(gart, iova))) {
+ dev_err(gart->dev, "Page entry is in-use\n");
+ return -EINVAL;
}
- kfree(gart_domain);
+ gart_set_pte(gart, iova, GART_ENTRY_PHYS_ADDR_VALID | pa);
+
+ return 0;
}
static int gart_iommu_map(struct iommu_domain *domain, unsigned long iova,
phys_addr_t pa, size_t bytes, int prot)
{
- struct gart_domain *gart_domain = to_gart_domain(domain);
- struct gart_device *gart = gart_domain->gart;
- unsigned long flags;
- unsigned long pfn;
- unsigned long pte;
+ struct gart_device *gart = gart_handle;
+ int ret;
- if (!gart_iova_range_valid(gart, iova, bytes))
+ if (gart_iova_range_invalid(gart, iova, bytes))
return -EINVAL;
- spin_lock_irqsave(&gart->pte_lock, flags);
- pfn = __phys_to_pfn(pa);
- if (!pfn_valid(pfn)) {
- dev_err(gart->dev, "Invalid page: %pa\n", &pa);
- spin_unlock_irqrestore(&gart->pte_lock, flags);
+ spin_lock(&gart->pte_lock);
+ ret = __gart_iommu_map(gart, iova, (unsigned long)pa);
+ spin_unlock(&gart->pte_lock);
+
+ return ret;
+}
+
+static inline int __gart_iommu_unmap(struct gart_device *gart,
+ unsigned long iova)
+{
+ if (unlikely(gart_debug && !gart_pte_valid(gart, iova))) {
+ dev_err(gart->dev, "Page entry is invalid\n");
return -EINVAL;
}
- if (gart_debug) {
- pte = gart_read_pte(gart, iova);
- if (pte & GART_ENTRY_PHYS_ADDR_VALID) {
- spin_unlock_irqrestore(&gart->pte_lock, flags);
- dev_err(gart->dev, "Page entry is in-use\n");
- return -EBUSY;
- }
- }
- gart_set_pte(gart, iova, GART_PTE(pfn));
- FLUSH_GART_REGS(gart);
- spin_unlock_irqrestore(&gart->pte_lock, flags);
+
+ gart_set_pte(gart, iova, 0);
+
return 0;
}
static size_t gart_iommu_unmap(struct iommu_domain *domain, unsigned long iova,
size_t bytes)
{
- struct gart_domain *gart_domain = to_gart_domain(domain);
- struct gart_device *gart = gart_domain->gart;
- unsigned long flags;
+ struct gart_device *gart = gart_handle;
+ int err;
- if (!gart_iova_range_valid(gart, iova, bytes))
+ if (gart_iova_range_invalid(gart, iova, bytes))
return 0;
- spin_lock_irqsave(&gart->pte_lock, flags);
- gart_set_pte(gart, iova, 0);
- FLUSH_GART_REGS(gart);
- spin_unlock_irqrestore(&gart->pte_lock, flags);
- return bytes;
+ spin_lock(&gart->pte_lock);
+ err = __gart_iommu_unmap(gart, iova);
+ spin_unlock(&gart->pte_lock);
+
+ return err ? 0 : bytes;
}
static phys_addr_t gart_iommu_iova_to_phys(struct iommu_domain *domain,
dma_addr_t iova)
{
- struct gart_domain *gart_domain = to_gart_domain(domain);
- struct gart_device *gart = gart_domain->gart;
+ struct gart_device *gart = gart_handle;
unsigned long pte;
- phys_addr_t pa;
- unsigned long flags;
- if (!gart_iova_range_valid(gart, iova, 0))
+ if (gart_iova_range_invalid(gart, iova, GART_PAGE_SIZE))
return -EINVAL;
- spin_lock_irqsave(&gart->pte_lock, flags);
+ spin_lock(&gart->pte_lock);
pte = gart_read_pte(gart, iova);
- spin_unlock_irqrestore(&gart->pte_lock, flags);
+ spin_unlock(&gart->pte_lock);
- pa = (pte & GART_PAGE_MASK);
- if (!pfn_valid(__phys_to_pfn(pa))) {
- dev_err(gart->dev, "No entry for %08llx:%pa\n",
- (unsigned long long)iova, &pa);
- gart_dump_table(gart);
- return -EINVAL;
- }
- return pa;
+ return pte & GART_PAGE_MASK;
}
static bool gart_iommu_capable(enum iommu_cap cap)
@@ -352,8 +257,12 @@ static bool gart_iommu_capable(enum iommu_cap cap)
static int gart_iommu_add_device(struct device *dev)
{
- struct iommu_group *group = iommu_group_get_for_dev(dev);
+ struct iommu_group *group;
+
+ if (!dev->iommu_fwspec)
+ return -ENODEV;
+ group = iommu_group_get_for_dev(dev);
if (IS_ERR(group))
return PTR_ERR(group);
@@ -370,6 +279,17 @@ static void gart_iommu_remove_device(struct device *dev)
iommu_device_unlink(&gart_handle->iommu, dev);
}
+static int gart_iommu_of_xlate(struct device *dev,
+ struct of_phandle_args *args)
+{
+ return 0;
+}
+
+static void gart_iommu_sync(struct iommu_domain *domain)
+{
+ FLUSH_GART_REGS(gart_handle);
+}
+
static const struct iommu_ops gart_iommu_ops = {
.capable = gart_iommu_capable,
.domain_alloc = gart_iommu_domain_alloc,
@@ -383,129 +303,96 @@ static const struct iommu_ops gart_iommu_ops = {
.unmap = gart_iommu_unmap,
.iova_to_phys = gart_iommu_iova_to_phys,
.pgsize_bitmap = GART_IOMMU_PGSIZES,
+ .of_xlate = gart_iommu_of_xlate,
+ .iotlb_sync_map = gart_iommu_sync,
+ .iotlb_sync = gart_iommu_sync,
};
-static int tegra_gart_suspend(struct device *dev)
+int tegra_gart_suspend(struct gart_device *gart)
{
- struct gart_device *gart = dev_get_drvdata(dev);
- unsigned long iova;
u32 *data = gart->savedata;
- unsigned long flags;
+ unsigned long iova;
+
+ /*
+ * All GART users shall be suspended at this point. Disable
+ * address translation to trap all GART accesses as invalid
+ * memory accesses.
+ */
+ writel_relaxed(0, gart->regs + GART_CONFIG);
+ FLUSH_GART_REGS(gart);
- spin_lock_irqsave(&gart->pte_lock, flags);
for_each_gart_pte(gart, iova)
*(data++) = gart_read_pte(gart, iova);
- spin_unlock_irqrestore(&gart->pte_lock, flags);
+
return 0;
}
-static int tegra_gart_resume(struct device *dev)
+int tegra_gart_resume(struct gart_device *gart)
{
- struct gart_device *gart = dev_get_drvdata(dev);
- unsigned long flags;
-
- spin_lock_irqsave(&gart->pte_lock, flags);
do_gart_setup(gart, gart->savedata);
- spin_unlock_irqrestore(&gart->pte_lock, flags);
+
return 0;
}
-static int tegra_gart_probe(struct platform_device *pdev)
+struct gart_device *tegra_gart_probe(struct device *dev, struct tegra_mc *mc)
{
struct gart_device *gart;
- struct resource *res, *res_remap;
- void __iomem *gart_regs;
- struct device *dev = &pdev->dev;
- int ret;
-
- if (gart_handle)
- return -EIO;
+ struct resource *res;
+ int err;
BUILD_BUG_ON(PAGE_SHIFT != GART_PAGE_SHIFT);
/* the GART memory aperture is required */
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- res_remap = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- if (!res || !res_remap) {
- dev_err(dev, "GART memory aperture expected\n");
- return -ENXIO;
- }
-
- gart = devm_kzalloc(dev, sizeof(*gart), GFP_KERNEL);
- if (!gart) {
- dev_err(dev, "failed to allocate gart_device\n");
- return -ENOMEM;
+ res = platform_get_resource(to_platform_device(dev), IORESOURCE_MEM, 1);
+ if (!res) {
+ dev_err(dev, "Memory aperture resource unavailable\n");
+ return ERR_PTR(-ENXIO);
}
- gart_regs = devm_ioremap(dev, res->start, resource_size(res));
- if (!gart_regs) {
- dev_err(dev, "failed to remap GART registers\n");
- return -ENXIO;
- }
-
- ret = iommu_device_sysfs_add(&gart->iommu, &pdev->dev, NULL,
- dev_name(&pdev->dev));
- if (ret) {
- dev_err(dev, "Failed to register IOMMU in sysfs\n");
- return ret;
- }
-
- iommu_device_set_ops(&gart->iommu, &gart_iommu_ops);
+ gart = kzalloc(sizeof(*gart), GFP_KERNEL);
+ if (!gart)
+ return ERR_PTR(-ENOMEM);
- ret = iommu_device_register(&gart->iommu);
- if (ret) {
- dev_err(dev, "Failed to register IOMMU\n");
- iommu_device_sysfs_remove(&gart->iommu);
- return ret;
- }
+ gart_handle = gart;
- gart->dev = &pdev->dev;
+ gart->dev = dev;
+ gart->regs = mc->regs + GART_REG_BASE;
+ gart->iovmm_base = res->start;
+ gart->iovmm_end = res->end + 1;
spin_lock_init(&gart->pte_lock);
- spin_lock_init(&gart->client_lock);
- INIT_LIST_HEAD(&gart->client);
- gart->regs = gart_regs;
- gart->iovmm_base = (dma_addr_t)res_remap->start;
- gart->page_count = (resource_size(res_remap) >> GART_PAGE_SHIFT);
-
- gart->savedata = vmalloc(array_size(sizeof(u32), gart->page_count));
- if (!gart->savedata) {
- dev_err(dev, "failed to allocate context save area\n");
- return -ENOMEM;
- }
+ spin_lock_init(&gart->dom_lock);
- platform_set_drvdata(pdev, gart);
do_gart_setup(gart, NULL);
- gart_handle = gart;
+ err = iommu_device_sysfs_add(&gart->iommu, dev, NULL, "gart");
+ if (err)
+ goto free_gart;
- return 0;
-}
+ iommu_device_set_ops(&gart->iommu, &gart_iommu_ops);
+ iommu_device_set_fwnode(&gart->iommu, dev->fwnode);
-static const struct dev_pm_ops tegra_gart_pm_ops = {
- .suspend = tegra_gart_suspend,
- .resume = tegra_gart_resume,
-};
+ err = iommu_device_register(&gart->iommu);
+ if (err)
+ goto remove_sysfs;
-static const struct of_device_id tegra_gart_of_match[] = {
- { .compatible = "nvidia,tegra20-gart", },
- { },
-};
+ gart->savedata = vmalloc(resource_size(res) / GART_PAGE_SIZE *
+ sizeof(u32));
+ if (!gart->savedata) {
+ err = -ENOMEM;
+ goto unregister_iommu;
+ }
-static struct platform_driver tegra_gart_driver = {
- .probe = tegra_gart_probe,
- .driver = {
- .name = "tegra-gart",
- .pm = &tegra_gart_pm_ops,
- .of_match_table = tegra_gart_of_match,
- .suppress_bind_attrs = true,
- },
-};
+ return gart;
-static int __init tegra_gart_init(void)
-{
- return platform_driver_register(&tegra_gart_driver);
+unregister_iommu:
+ iommu_device_unregister(&gart->iommu);
+remove_sysfs:
+ iommu_device_sysfs_remove(&gart->iommu);
+free_gart:
+ kfree(gart);
+
+ return ERR_PTR(err);
}
-subsys_initcall(tegra_gart_init);
module_param(gart_debug, bool, 0644);
MODULE_PARM_DESC(gart_debug, "Enable GART debugging");
diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c
index 3a5c7dc6dc57..5182c7d6171e 100644
--- a/drivers/iommu/tegra-smmu.c
+++ b/drivers/iommu/tegra-smmu.c
@@ -982,10 +982,6 @@ struct tegra_smmu *tegra_smmu_probe(struct device *dev,
u32 value;
int err;
- /* This can happen on Tegra20 which doesn't have an SMMU */
- if (!soc)
- return NULL;
-
smmu = devm_kzalloc(dev, sizeof(*smmu), GFP_KERNEL);
if (!smmu)
return ERR_PTR(-ENOMEM);
diff --git a/drivers/irqchip/irq-brcmstb-l2.c b/drivers/irqchip/irq-brcmstb-l2.c
index 83364fedbf0a..5e4ca139e4ea 100644
--- a/drivers/irqchip/irq-brcmstb-l2.c
+++ b/drivers/irqchip/irq-brcmstb-l2.c
@@ -275,14 +275,14 @@ out_free:
return ret;
}
-int __init brcmstb_l2_edge_intc_of_init(struct device_node *np,
+static int __init brcmstb_l2_edge_intc_of_init(struct device_node *np,
struct device_node *parent)
{
return brcmstb_l2_intc_of_init(np, parent, &l2_edge_intc_init);
}
IRQCHIP_DECLARE(brcmstb_l2_intc, "brcm,l2-intc", brcmstb_l2_edge_intc_of_init);
-int __init brcmstb_l2_lvl_intc_of_init(struct device_node *np,
+static int __init brcmstb_l2_lvl_intc_of_init(struct device_node *np,
struct device_node *parent)
{
return brcmstb_l2_intc_of_init(np, parent, &l2_lvl_intc_init);
diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c
index 2dd1ff0cf558..7577755bdcf4 100644
--- a/drivers/irqchip/irq-gic-v3-its.c
+++ b/drivers/irqchip/irq-gic-v3-its.c
@@ -1482,7 +1482,7 @@ static int lpi_range_cmp(void *priv, struct list_head *a, struct list_head *b)
ra = container_of(a, struct lpi_range, entry);
rb = container_of(b, struct lpi_range, entry);
- return rb->base_id - ra->base_id;
+ return ra->base_id - rb->base_id;
}
static void merge_lpi_ranges(void)
diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c
index 0868a9d81c3c..15e55d327505 100644
--- a/drivers/irqchip/irq-gic-v3.c
+++ b/drivers/irqchip/irq-gic-v3.c
@@ -27,6 +27,7 @@
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/percpu.h>
+#include <linux/refcount.h>
#include <linux/slab.h>
#include <linux/irqchip.h>
@@ -41,6 +42,8 @@
#include "irq-gic-common.h"
+#define GICD_INT_NMI_PRI (GICD_INT_DEF_PRI & ~0x80)
+
#define FLAGS_WORKAROUND_GICR_WAKER_MSM8996 (1ULL << 0)
struct redist_region {
@@ -66,6 +69,34 @@ struct gic_chip_data {
static struct gic_chip_data gic_data __read_mostly;
static DEFINE_STATIC_KEY_TRUE(supports_deactivate_key);
+/*
+ * The behaviours of RPR and PMR registers differ depending on the value of
+ * SCR_EL3.FIQ, and the behaviour of non-secure priority registers of the
+ * distributor and redistributors depends on whether security is enabled in the
+ * GIC.
+ *
+ * When security is enabled, non-secure priority values from the (re)distributor
+ * are presented to the GIC CPUIF as follow:
+ * (GIC_(R)DIST_PRI[irq] >> 1) | 0x80;
+ *
+ * If SCR_EL3.FIQ == 1, the values writen to/read from PMR and RPR at non-secure
+ * EL1 are subject to a similar operation thus matching the priorities presented
+ * from the (re)distributor when security is enabled.
+ *
+ * see GICv3/GICv4 Architecture Specification (IHI0069D):
+ * - section 4.8.1 Non-secure accesses to register fields for Secure interrupt
+ * priorities.
+ * - Figure 4-7 Secure read of the priority field for a Non-secure Group 1
+ * interrupt.
+ *
+ * For now, we only support pseudo-NMIs if we have non-secure view of
+ * priorities.
+ */
+static DEFINE_STATIC_KEY_FALSE(supports_pseudo_nmis);
+
+/* ppi_nmi_refs[n] == number of cpus having ppi[n + 16] set as NMI */
+static refcount_t ppi_nmi_refs[16];
+
static struct gic_kvm_info gic_v3_kvm_info;
static DEFINE_PER_CPU(bool, has_rss);
@@ -232,6 +263,12 @@ static void gic_unmask_irq(struct irq_data *d)
gic_poke_irq(d, GICD_ISENABLER);
}
+static inline bool gic_supports_nmi(void)
+{
+ return IS_ENABLED(CONFIG_ARM64_PSEUDO_NMI) &&
+ static_branch_likely(&supports_pseudo_nmis);
+}
+
static int gic_irq_set_irqchip_state(struct irq_data *d,
enum irqchip_irq_state which, bool val)
{
@@ -287,6 +324,79 @@ static int gic_irq_get_irqchip_state(struct irq_data *d,
return 0;
}
+static void gic_irq_set_prio(struct irq_data *d, u8 prio)
+{
+ void __iomem *base = gic_dist_base(d);
+
+ writeb_relaxed(prio, base + GICD_IPRIORITYR + gic_irq(d));
+}
+
+static int gic_irq_nmi_setup(struct irq_data *d)
+{
+ struct irq_desc *desc = irq_to_desc(d->irq);
+
+ if (!gic_supports_nmi())
+ return -EINVAL;
+
+ if (gic_peek_irq(d, GICD_ISENABLER)) {
+ pr_err("Cannot set NMI property of enabled IRQ %u\n", d->irq);
+ return -EINVAL;
+ }
+
+ /*
+ * A secondary irq_chip should be in charge of LPI request,
+ * it should not be possible to get there
+ */
+ if (WARN_ON(gic_irq(d) >= 8192))
+ return -EINVAL;
+
+ /* desc lock should already be held */
+ if (gic_irq(d) < 32) {
+ /* Setting up PPI as NMI, only switch handler for first NMI */
+ if (!refcount_inc_not_zero(&ppi_nmi_refs[gic_irq(d) - 16])) {
+ refcount_set(&ppi_nmi_refs[gic_irq(d) - 16], 1);
+ desc->handle_irq = handle_percpu_devid_fasteoi_nmi;
+ }
+ } else {
+ desc->handle_irq = handle_fasteoi_nmi;
+ }
+
+ gic_irq_set_prio(d, GICD_INT_NMI_PRI);
+
+ return 0;
+}
+
+static void gic_irq_nmi_teardown(struct irq_data *d)
+{
+ struct irq_desc *desc = irq_to_desc(d->irq);
+
+ if (WARN_ON(!gic_supports_nmi()))
+ return;
+
+ if (gic_peek_irq(d, GICD_ISENABLER)) {
+ pr_err("Cannot set NMI property of enabled IRQ %u\n", d->irq);
+ return;
+ }
+
+ /*
+ * A secondary irq_chip should be in charge of LPI request,
+ * it should not be possible to get there
+ */
+ if (WARN_ON(gic_irq(d) >= 8192))
+ return;
+
+ /* desc lock should already be held */
+ if (gic_irq(d) < 32) {
+ /* Tearing down NMI, only switch handler for last NMI */
+ if (refcount_dec_and_test(&ppi_nmi_refs[gic_irq(d) - 16]))
+ desc->handle_irq = handle_percpu_devid_irq;
+ } else {
+ desc->handle_irq = handle_fasteoi_irq;
+ }
+
+ gic_irq_set_prio(d, GICD_INT_DEF_PRI);
+}
+
static void gic_eoi_irq(struct irq_data *d)
{
gic_write_eoir(gic_irq(d));
@@ -350,12 +460,50 @@ static u64 gic_mpidr_to_affinity(unsigned long mpidr)
return aff;
}
+static void gic_deactivate_unhandled(u32 irqnr)
+{
+ if (static_branch_likely(&supports_deactivate_key)) {
+ if (irqnr < 8192)
+ gic_write_dir(irqnr);
+ } else {
+ gic_write_eoir(irqnr);
+ }
+}
+
+static inline void gic_handle_nmi(u32 irqnr, struct pt_regs *regs)
+{
+ int err;
+
+ if (static_branch_likely(&supports_deactivate_key))
+ gic_write_eoir(irqnr);
+ /*
+ * Leave the PSR.I bit set to prevent other NMIs to be
+ * received while handling this one.
+ * PSR.I will be restored when we ERET to the
+ * interrupted context.
+ */
+ err = handle_domain_nmi(gic_data.domain, irqnr, regs);
+ if (err)
+ gic_deactivate_unhandled(irqnr);
+}
+
static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
{
u32 irqnr;
irqnr = gic_read_iar();
+ if (gic_supports_nmi() &&
+ unlikely(gic_read_rpr() == GICD_INT_NMI_PRI)) {
+ gic_handle_nmi(irqnr, regs);
+ return;
+ }
+
+ if (gic_prio_masking_enabled()) {
+ gic_pmr_mask_irqs();
+ gic_arch_enable_irqs();
+ }
+
if (likely(irqnr > 15 && irqnr < 1020) || irqnr >= 8192) {
int err;
@@ -367,12 +515,7 @@ static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs
err = handle_domain_irq(gic_data.domain, irqnr, regs);
if (err) {
WARN_ONCE(true, "Unexpected interrupt received!\n");
- if (static_branch_likely(&supports_deactivate_key)) {
- if (irqnr < 8192)
- gic_write_dir(irqnr);
- } else {
- gic_write_eoir(irqnr);
- }
+ gic_deactivate_unhandled(irqnr);
}
return;
}
@@ -395,6 +538,44 @@ static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs
}
}
+static u32 gic_get_pribits(void)
+{
+ u32 pribits;
+
+ pribits = gic_read_ctlr();
+ pribits &= ICC_CTLR_EL1_PRI_BITS_MASK;
+ pribits >>= ICC_CTLR_EL1_PRI_BITS_SHIFT;
+ pribits++;
+
+ return pribits;
+}
+
+static bool gic_has_group0(void)
+{
+ u32 val;
+ u32 old_pmr;
+
+ old_pmr = gic_read_pmr();
+
+ /*
+ * Let's find out if Group0 is under control of EL3 or not by
+ * setting the highest possible, non-zero priority in PMR.
+ *
+ * If SCR_EL3.FIQ is set, the priority gets shifted down in
+ * order for the CPU interface to set bit 7, and keep the
+ * actual priority in the non-secure range. In the process, it
+ * looses the least significant bit and the actual priority
+ * becomes 0x80. Reading it back returns 0, indicating that
+ * we're don't have access to Group0.
+ */
+ gic_write_pmr(BIT(8 - gic_get_pribits()));
+ val = gic_read_pmr();
+
+ gic_write_pmr(old_pmr);
+
+ return val != 0;
+}
+
static void __init gic_dist_init(void)
{
unsigned int i;
@@ -530,13 +711,19 @@ static void gic_update_vlpi_properties(void)
!gic_data.rdists.has_direct_lpi ? "no " : "");
}
+/* Check whether it's single security state view */
+static inline bool gic_dist_security_disabled(void)
+{
+ return readl_relaxed(gic_data.dist_base + GICD_CTLR) & GICD_CTLR_DS;
+}
+
static void gic_cpu_sys_reg_init(void)
{
int i, cpu = smp_processor_id();
u64 mpidr = cpu_logical_map(cpu);
u64 need_rss = MPIDR_RS(mpidr);
bool group0;
- u32 val, pribits;
+ u32 pribits;
/*
* Need to check that the SRE bit has actually been set. If
@@ -548,28 +735,22 @@ static void gic_cpu_sys_reg_init(void)
if (!gic_enable_sre())
pr_err("GIC: unable to set SRE (disabled at EL2), panic ahead\n");
- pribits = gic_read_ctlr();
- pribits &= ICC_CTLR_EL1_PRI_BITS_MASK;
- pribits >>= ICC_CTLR_EL1_PRI_BITS_SHIFT;
- pribits++;
+ pribits = gic_get_pribits();
- /*
- * Let's find out if Group0 is under control of EL3 or not by
- * setting the highest possible, non-zero priority in PMR.
- *
- * If SCR_EL3.FIQ is set, the priority gets shifted down in
- * order for the CPU interface to set bit 7, and keep the
- * actual priority in the non-secure range. In the process, it
- * looses the least significant bit and the actual priority
- * becomes 0x80. Reading it back returns 0, indicating that
- * we're don't have access to Group0.
- */
- write_gicreg(BIT(8 - pribits), ICC_PMR_EL1);
- val = read_gicreg(ICC_PMR_EL1);
- group0 = val != 0;
+ group0 = gic_has_group0();
/* Set priority mask register */
- write_gicreg(DEFAULT_PMR_VALUE, ICC_PMR_EL1);
+ if (!gic_prio_masking_enabled()) {
+ write_gicreg(DEFAULT_PMR_VALUE, ICC_PMR_EL1);
+ } else {
+ /*
+ * Mismatch configuration with boot CPU, the system is likely
+ * to die as interrupt masking will not work properly on all
+ * CPUs
+ */
+ WARN_ON(gic_supports_nmi() && group0 &&
+ !gic_dist_security_disabled());
+ }
/*
* Some firmwares hand over to the kernel with the BPR changed from
@@ -824,12 +1005,6 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val,
#endif
#ifdef CONFIG_CPU_PM
-/* Check whether it's single security state view */
-static bool gic_dist_security_disabled(void)
-{
- return readl_relaxed(gic_data.dist_base + GICD_CTLR) & GICD_CTLR_DS;
-}
-
static int gic_cpu_pm_notifier(struct notifier_block *self,
unsigned long cmd, void *v)
{
@@ -866,6 +1041,8 @@ static struct irq_chip gic_chip = {
.irq_set_affinity = gic_set_affinity,
.irq_get_irqchip_state = gic_irq_get_irqchip_state,
.irq_set_irqchip_state = gic_irq_set_irqchip_state,
+ .irq_nmi_setup = gic_irq_nmi_setup,
+ .irq_nmi_teardown = gic_irq_nmi_teardown,
.flags = IRQCHIP_SET_TYPE_MASKED |
IRQCHIP_SKIP_SET_WAKE |
IRQCHIP_MASK_ON_SUSPEND,
@@ -881,6 +1058,8 @@ static struct irq_chip gic_eoimode1_chip = {
.irq_get_irqchip_state = gic_irq_get_irqchip_state,
.irq_set_irqchip_state = gic_irq_set_irqchip_state,
.irq_set_vcpu_affinity = gic_irq_set_vcpu_affinity,
+ .irq_nmi_setup = gic_irq_nmi_setup,
+ .irq_nmi_teardown = gic_irq_nmi_teardown,
.flags = IRQCHIP_SET_TYPE_MASKED |
IRQCHIP_SKIP_SET_WAKE |
IRQCHIP_MASK_ON_SUSPEND,
@@ -1082,6 +1261,21 @@ static bool gic_enable_quirk_msm8996(void *data)
return true;
}
+static void gic_enable_nmi_support(void)
+{
+ int i;
+
+ for (i = 0; i < 16; i++)
+ refcount_set(&ppi_nmi_refs[i], 0);
+
+ static_branch_enable(&supports_pseudo_nmis);
+
+ if (static_branch_likely(&supports_deactivate_key))
+ gic_eoimode1_chip.flags |= IRQCHIP_SUPPORTS_NMI;
+ else
+ gic_chip.flags |= IRQCHIP_SUPPORTS_NMI;
+}
+
static int __init gic_init_bases(void __iomem *dist_base,
struct redist_region *rdist_regs,
u32 nr_redist_regions,
@@ -1151,6 +1345,13 @@ static int __init gic_init_bases(void __iomem *dist_base,
its_cpu_init();
}
+ if (gic_prio_masking_enabled()) {
+ if (!gic_has_group0() || gic_dist_security_disabled())
+ gic_enable_nmi_support();
+ else
+ pr_warn("SCR_EL3.FIQ is cleared, cannot enable use of pseudo-NMIs\n");
+ }
+
return 0;
out_free:
diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
index ba2a37a27a54..fd3110c171ba 100644
--- a/drivers/irqchip/irq-gic.c
+++ b/drivers/irqchip/irq-gic.c
@@ -1089,11 +1089,10 @@ static void gic_init_chip(struct gic_chip_data *gic, struct device *dev,
#endif
}
-static int gic_init_bases(struct gic_chip_data *gic, int irq_start,
+static int gic_init_bases(struct gic_chip_data *gic,
struct fwnode_handle *handle)
{
- irq_hw_number_t hwirq_base;
- int gic_irqs, irq_base, ret;
+ int gic_irqs, ret;
if (IS_ENABLED(CONFIG_GIC_NON_BANKED) && gic->percpu_offset) {
/* Frankein-GIC without banked registers... */
@@ -1145,28 +1144,21 @@ static int gic_init_bases(struct gic_chip_data *gic, int irq_start,
} else { /* Legacy support */
/*
* For primary GICs, skip over SGIs.
- * For secondary GICs, skip over PPIs, too.
+ * No secondary GIC support whatsoever.
*/
- if (gic == &gic_data[0] && (irq_start & 31) > 0) {
- hwirq_base = 16;
- if (irq_start != -1)
- irq_start = (irq_start & ~31) + 16;
- } else {
- hwirq_base = 32;
- }
+ int irq_base;
- gic_irqs -= hwirq_base; /* calculate # of irqs to allocate */
+ gic_irqs -= 16; /* calculate # of irqs to allocate */
- irq_base = irq_alloc_descs(irq_start, 16, gic_irqs,
+ irq_base = irq_alloc_descs(16, 16, gic_irqs,
numa_node_id());
if (irq_base < 0) {
- WARN(1, "Cannot allocate irq_descs @ IRQ%d, assuming pre-allocated\n",
- irq_start);
- irq_base = irq_start;
+ WARN(1, "Cannot allocate irq_descs @ IRQ16, assuming pre-allocated\n");
+ irq_base = 16;
}
gic->domain = irq_domain_add_legacy(NULL, gic_irqs, irq_base,
- hwirq_base, &gic_irq_domain_ops, gic);
+ 16, &gic_irq_domain_ops, gic);
}
if (WARN_ON(!gic->domain)) {
@@ -1195,7 +1187,6 @@ error:
}
static int __init __gic_init_bases(struct gic_chip_data *gic,
- int irq_start,
struct fwnode_handle *handle)
{
char *name;
@@ -1231,32 +1222,28 @@ static int __init __gic_init_bases(struct gic_chip_data *gic,
gic_init_chip(gic, NULL, name, false);
}
- ret = gic_init_bases(gic, irq_start, handle);
+ ret = gic_init_bases(gic, handle);
if (ret)
kfree(name);
return ret;
}
-void __init gic_init(unsigned int gic_nr, int irq_start,
- void __iomem *dist_base, void __iomem *cpu_base)
+void __init gic_init(void __iomem *dist_base, void __iomem *cpu_base)
{
struct gic_chip_data *gic;
- if (WARN_ON(gic_nr >= CONFIG_ARM_GIC_MAX_NR))
- return;
-
/*
* Non-DT/ACPI systems won't run a hypervisor, so let's not
* bother with these...
*/
static_branch_disable(&supports_deactivate_key);
- gic = &gic_data[gic_nr];
+ gic = &gic_data[0];
gic->raw_dist_base = dist_base;
gic->raw_cpu_base = cpu_base;
- __gic_init_bases(gic, irq_start, NULL);
+ __gic_init_bases(gic, NULL);
}
static void gic_teardown(struct gic_chip_data *gic)
@@ -1399,7 +1386,7 @@ int gic_of_init_child(struct device *dev, struct gic_chip_data **gic, int irq)
if (ret)
return ret;
- ret = gic_init_bases(*gic, -1, &dev->of_node->fwnode);
+ ret = gic_init_bases(*gic, &dev->of_node->fwnode);
if (ret) {
gic_teardown(*gic);
return ret;
@@ -1459,7 +1446,7 @@ gic_of_init(struct device_node *node, struct device_node *parent)
if (gic_cnt == 0 && !gic_check_eoimode(node, &gic->raw_cpu_base))
static_branch_disable(&supports_deactivate_key);
- ret = __gic_init_bases(gic, -1, &node->fwnode);
+ ret = __gic_init_bases(gic, &node->fwnode);
if (ret) {
gic_teardown(gic);
return ret;
@@ -1650,7 +1637,7 @@ static int __init gic_v2_acpi_init(struct acpi_subtable_header *header,
return -ENOMEM;
}
- ret = __gic_init_bases(gic, -1, domain_handle);
+ ret = __gic_init_bases(gic, domain_handle);
if (ret) {
pr_err("Failed to initialise GIC\n");
irq_domain_free_fwnode(domain_handle);
diff --git a/drivers/irqchip/irq-imx-irqsteer.c b/drivers/irqchip/irq-imx-irqsteer.c
index d1098f4da6a4..88df3d00052c 100644
--- a/drivers/irqchip/irq-imx-irqsteer.c
+++ b/drivers/irqchip/irq-imx-irqsteer.c
@@ -169,8 +169,12 @@ static int imx_irqsteer_probe(struct platform_device *pdev)
raw_spin_lock_init(&data->lock);
- of_property_read_u32(np, "fsl,num-irqs", &irqs_num);
- of_property_read_u32(np, "fsl,channel", &data->channel);
+ ret = of_property_read_u32(np, "fsl,num-irqs", &irqs_num);
+ if (ret)
+ return ret;
+ ret = of_property_read_u32(np, "fsl,channel", &data->channel);
+ if (ret)
+ return ret;
/*
* There is one output irq for each group of 64 inputs.
diff --git a/drivers/irqchip/irq-mbigen.c b/drivers/irqchip/irq-mbigen.c
index 567b29c47608..98b6e1d4b1a6 100644
--- a/drivers/irqchip/irq-mbigen.c
+++ b/drivers/irqchip/irq-mbigen.c
@@ -161,6 +161,9 @@ static void mbigen_write_msg(struct msi_desc *desc, struct msi_msg *msg)
void __iomem *base = d->chip_data;
u32 val;
+ if (!msg->address_lo && !msg->address_hi)
+ return;
+
base += get_mbigen_vec_reg(d->hwirq);
val = readl_relaxed(base);
diff --git a/drivers/irqchip/irq-mmp.c b/drivers/irqchip/irq-mmp.c
index 3496b61a312a..8eed478f3b7e 100644
--- a/drivers/irqchip/irq-mmp.c
+++ b/drivers/irqchip/irq-mmp.c
@@ -179,7 +179,7 @@ static int mmp_irq_domain_xlate(struct irq_domain *d, struct device_node *node,
return 0;
}
-const struct irq_domain_ops mmp_irq_domain_ops = {
+static const struct irq_domain_ops mmp_irq_domain_ops = {
.map = mmp_irq_domain_map,
.xlate = mmp_irq_domain_xlate,
};
diff --git a/drivers/irqchip/irq-mvebu-sei.c b/drivers/irqchip/irq-mvebu-sei.c
index add4c9c934c8..18832ccc8ff8 100644
--- a/drivers/irqchip/irq-mvebu-sei.c
+++ b/drivers/irqchip/irq-mvebu-sei.c
@@ -478,7 +478,7 @@ dispose_irq:
return ret;
}
-struct mvebu_sei_caps mvebu_sei_ap806_caps = {
+static struct mvebu_sei_caps mvebu_sei_ap806_caps = {
.ap_range = {
.first = 0,
.size = 21,
diff --git a/drivers/irqchip/irq-stm32-exti.c b/drivers/irqchip/irq-stm32-exti.c
index a93296b9b45d..7bd1d4cb2e19 100644
--- a/drivers/irqchip/irq-stm32-exti.c
+++ b/drivers/irqchip/irq-stm32-exti.c
@@ -716,7 +716,6 @@ stm32_exti_chip_data *stm32_exti_chip_init(struct stm32_exti_host_data *h_data,
const struct stm32_exti_bank *stm32_bank;
struct stm32_exti_chip_data *chip_data;
void __iomem *base = h_data->base;
- u32 irqs_mask;
stm32_bank = h_data->drv_data->exti_banks[bank_idx];
chip_data = &h_data->chips_data[bank_idx];
@@ -725,21 +724,12 @@ stm32_exti_chip_data *stm32_exti_chip_init(struct stm32_exti_host_data *h_data,
raw_spin_lock_init(&chip_data->rlock);
- /* Determine number of irqs supported */
- writel_relaxed(~0UL, base + stm32_bank->rtsr_ofst);
- irqs_mask = readl_relaxed(base + stm32_bank->rtsr_ofst);
-
/*
* This IP has no reset, so after hot reboot we should
* clear registers to avoid residue
*/
writel_relaxed(0, base + stm32_bank->imr_ofst);
writel_relaxed(0, base + stm32_bank->emr_ofst);
- writel_relaxed(0, base + stm32_bank->rtsr_ofst);
- writel_relaxed(0, base + stm32_bank->ftsr_ofst);
- writel_relaxed(~0UL, base + stm32_bank->rpr_ofst);
- if (stm32_bank->fpr_ofst != UNDEF_REG)
- writel_relaxed(~0UL, base + stm32_bank->fpr_ofst);
pr_info("%pOF: bank%d\n", h_data->node, bank_idx);
diff --git a/drivers/isdn/hardware/mISDN/hfcmulti.c b/drivers/isdn/hardware/mISDN/hfcmulti.c
index 4d85645c87f7..0928fd1f0e0c 100644
--- a/drivers/isdn/hardware/mISDN/hfcmulti.c
+++ b/drivers/isdn/hardware/mISDN/hfcmulti.c
@@ -4365,7 +4365,8 @@ setup_pci(struct hfc_multi *hc, struct pci_dev *pdev,
if (m->clock2)
test_and_set_bit(HFC_CHIP_CLOCK2, &hc->chip);
- if (ent->device == 0xB410) {
+ if (ent->vendor == PCI_VENDOR_ID_DIGIUM &&
+ ent->device == PCI_DEVICE_ID_DIGIUM_HFC4S) {
test_and_set_bit(HFC_CHIP_B410P, &hc->chip);
test_and_set_bit(HFC_CHIP_PCM_MASTER, &hc->chip);
test_and_clear_bit(HFC_CHIP_PCM_SLAVE, &hc->chip);
diff --git a/drivers/isdn/hardware/mISDN/hfcpci.c b/drivers/isdn/hardware/mISDN/hfcpci.c
index ebb3fa2e1d00..362aa5450a5e 100644
--- a/drivers/isdn/hardware/mISDN/hfcpci.c
+++ b/drivers/isdn/hardware/mISDN/hfcpci.c
@@ -2032,10 +2032,19 @@ setup_hw(struct hfc_pci *hc)
hc->hw.fifos = buffer;
pci_write_config_dword(hc->pdev, 0x80, hc->hw.dmahandle);
hc->hw.pci_io = ioremap((ulong) hc->hw.pci_io, 256);
+ if (unlikely(!hc->hw.pci_io)) {
+ printk(KERN_WARNING
+ "HFC-PCI: Error in ioremap for PCI!\n");
+ pci_free_consistent(hc->pdev, 0x8000, hc->hw.fifos,
+ hc->hw.dmahandle);
+ return 1;
+ }
+
printk(KERN_INFO
"HFC-PCI: defined at mem %#lx fifo %#lx(%#lx) IRQ %d HZ %d\n",
(u_long) hc->hw.pci_io, (u_long) hc->hw.fifos,
(u_long) hc->hw.dmahandle, hc->irq, HZ);
+
/* enable memory mapped ports, disable busmaster */
pci_write_config_word(hc->pdev, PCI_COMMAND, PCI_ENA_MEMIO);
hc->hw.int_m2 = 0;
diff --git a/drivers/isdn/hardware/mISDN/mISDNinfineon.c b/drivers/isdn/hardware/mISDN/mISDNinfineon.c
index 3e01012be4ab..0fe6ddcb3fdc 100644
--- a/drivers/isdn/hardware/mISDN/mISDNinfineon.c
+++ b/drivers/isdn/hardware/mISDN/mISDNinfineon.c
@@ -712,8 +712,11 @@ setup_io(struct inf_hw *hw)
(ulong)hw->addr.start, (ulong)hw->addr.size);
return err;
}
- if (hw->ci->addr_mode == AM_MEMIO)
+ if (hw->ci->addr_mode == AM_MEMIO) {
hw->addr.p = ioremap(hw->addr.start, hw->addr.size);
+ if (unlikely(!hw->addr.p))
+ return -ENOMEM;
+ }
hw->addr.mode = hw->ci->addr_mode;
if (debug & DEBUG_HW)
pr_notice("%s: IO addr %lx (%lu bytes) mode%d\n",
diff --git a/drivers/isdn/isdnloop/isdnloop.c b/drivers/isdn/isdnloop/isdnloop.c
index f4253d468ae1..755c6bbc9553 100644
--- a/drivers/isdn/isdnloop/isdnloop.c
+++ b/drivers/isdn/isdnloop/isdnloop.c
@@ -570,7 +570,7 @@ isdnloop_atimeout(isdnloop_card *card, int ch)
char buf[60];
spin_lock_irqsave(&card->isdnloop_lock, flags);
- if (card->rcard) {
+ if (card->rcard[ch]) {
isdnloop_fake(card->rcard[ch], "DDIS_I", card->rch[ch] + 1);
card->rcard[ch]->rcard[card->rch[ch]] = NULL;
card->rcard[ch] = NULL;
diff --git a/drivers/leds/leds-mlxreg.c b/drivers/leds/leds-mlxreg.c
index 1ee48cb21df9..cabe379071a7 100644
--- a/drivers/leds/leds-mlxreg.c
+++ b/drivers/leds/leds-mlxreg.c
@@ -22,6 +22,7 @@
#define MLXREG_LED_AMBER_SOLID 0x09 /* Solid amber */
#define MLXREG_LED_BLINK_3HZ 167 /* ~167 msec off/on - HW support */
#define MLXREG_LED_BLINK_6HZ 83 /* ~83 msec off/on - HW support */
+#define MLXREG_LED_CAPABILITY_CLEAR GENMASK(31, 8) /* Clear mask */
/**
* struct mlxreg_led_data - led control data:
@@ -187,6 +188,7 @@ static int mlxreg_led_config(struct mlxreg_led_priv_data *priv)
struct mlxreg_led_data *led_data;
struct led_classdev *led_cdev;
enum led_brightness brightness;
+ u32 regval;
int i;
int err;
@@ -196,6 +198,23 @@ static int mlxreg_led_config(struct mlxreg_led_priv_data *priv)
if (!led_data)
return -ENOMEM;
+ if (data->capability) {
+ err = regmap_read(led_pdata->regmap, data->capability,
+ &regval);
+ if (err) {
+ dev_err(&priv->pdev->dev, "Failed to query capability register\n");
+ return err;
+ }
+ if (!(regval & data->bit))
+ continue;
+ /*
+ * Field "bit" can contain one capability bit in 0 byte
+ * and offset bit in 1-3 bytes. Clear capability bit and
+ * keep only offset bit.
+ */
+ data->bit &= MLXREG_LED_CAPABILITY_CLEAR;
+ }
+
led_cdev = &led_data->led_cdev;
led_data->data_parent = priv;
if (strstr(data->label, "red") ||
diff --git a/drivers/leds/leds-pca9532.c b/drivers/leds/leds-pca9532.c
index 7fea18b0c15d..7cb4d685a1f1 100644
--- a/drivers/leds/leds-pca9532.c
+++ b/drivers/leds/leds-pca9532.c
@@ -513,6 +513,7 @@ static int pca9532_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int devid;
+ const struct of_device_id *of_id;
struct pca9532_data *data = i2c_get_clientdata(client);
struct pca9532_platform_data *pca9532_pdata =
dev_get_platdata(&client->dev);
@@ -528,8 +529,11 @@ static int pca9532_probe(struct i2c_client *client,
dev_err(&client->dev, "no platform data\n");
return -EINVAL;
}
- devid = (int)(uintptr_t)of_match_device(
- of_pca9532_leds_match, &client->dev)->data;
+ of_id = of_match_device(of_pca9532_leds_match,
+ &client->dev);
+ if (unlikely(!of_id))
+ return -EINVAL;
+ devid = (int)(uintptr_t) of_id->data;
} else {
devid = id->driver_data;
}
diff --git a/drivers/leds/trigger/ledtrig-netdev.c b/drivers/leds/trigger/ledtrig-netdev.c
index 3dd3ed46d473..136f86a1627d 100644
--- a/drivers/leds/trigger/ledtrig-netdev.c
+++ b/drivers/leds/trigger/ledtrig-netdev.c
@@ -122,7 +122,8 @@ static ssize_t device_name_store(struct device *dev,
trigger_data->net_dev = NULL;
}
- strncpy(trigger_data->device_name, buf, size);
+ memcpy(trigger_data->device_name, buf, size);
+ trigger_data->device_name[size] = 0;
if (size > 0 && trigger_data->device_name[size - 1] == '\n')
trigger_data->device_name[size - 1] = 0;
@@ -301,11 +302,11 @@ static int netdev_trig_notify(struct notifier_block *nb,
container_of(nb, struct led_netdev_data, notifier);
if (evt != NETDEV_UP && evt != NETDEV_DOWN && evt != NETDEV_CHANGE
- && evt != NETDEV_REGISTER && evt != NETDEV_UNREGISTER
- && evt != NETDEV_CHANGENAME)
+ && evt != NETDEV_REGISTER && evt != NETDEV_UNREGISTER)
return NOTIFY_DONE;
- if (strcmp(dev->name, trigger_data->device_name))
+ if (!(dev == trigger_data->net_dev ||
+ (evt == NETDEV_REGISTER && !strcmp(dev->name, trigger_data->device_name))))
return NOTIFY_DONE;
cancel_delayed_work_sync(&trigger_data->work);
@@ -320,12 +321,9 @@ static int netdev_trig_notify(struct notifier_block *nb,
dev_hold(dev);
trigger_data->net_dev = dev;
break;
- case NETDEV_CHANGENAME:
case NETDEV_UNREGISTER:
- if (trigger_data->net_dev) {
- dev_put(trigger_data->net_dev);
- trigger_data->net_dev = NULL;
- }
+ dev_put(trigger_data->net_dev);
+ trigger_data->net_dev = NULL;
break;
case NETDEV_UP:
case NETDEV_CHANGE:
diff --git a/drivers/lightnvm/pblk-core.c b/drivers/lightnvm/pblk-core.c
index 1ff165351180..6ca868868fee 100644
--- a/drivers/lightnvm/pblk-core.c
+++ b/drivers/lightnvm/pblk-core.c
@@ -141,7 +141,7 @@ struct nvm_chk_meta *pblk_get_chunk_meta(struct pblk *pblk)
ret = nvm_get_chunk_meta(dev, ppa, geo->all_chunks, meta);
if (ret) {
- kfree(meta);
+ vfree(meta);
return ERR_PTR(-EIO);
}
@@ -1065,7 +1065,7 @@ static int pblk_line_init_metadata(struct pblk *pblk, struct pblk_line *line,
bitmap_set(line->lun_bitmap, 0, lm->lun_bitmap_len);
smeta_buf->header.identifier = cpu_to_le32(PBLK_MAGIC);
- memcpy(smeta_buf->header.uuid, pblk->instance_uuid, 16);
+ guid_copy((guid_t *)&smeta_buf->header.uuid, &pblk->instance_uuid);
smeta_buf->header.id = cpu_to_le32(line->id);
smeta_buf->header.type = cpu_to_le16(line->type);
smeta_buf->header.version_major = SMETA_VERSION_MAJOR;
@@ -1278,6 +1278,7 @@ static int pblk_line_prepare(struct pblk *pblk, struct pblk_line *line)
spin_unlock(&line->lock);
kref_init(&line->ref);
+ atomic_set(&line->sec_to_update, 0);
return 0;
}
@@ -1874,7 +1875,8 @@ void pblk_line_close_meta(struct pblk *pblk, struct pblk_line *line)
if (le32_to_cpu(emeta_buf->header.identifier) != PBLK_MAGIC) {
emeta_buf->header.identifier = cpu_to_le32(PBLK_MAGIC);
- memcpy(emeta_buf->header.uuid, pblk->instance_uuid, 16);
+ guid_copy((guid_t *)&emeta_buf->header.uuid,
+ &pblk->instance_uuid);
emeta_buf->header.id = cpu_to_le32(line->id);
emeta_buf->header.type = cpu_to_le16(line->type);
emeta_buf->header.version_major = EMETA_VERSION_MAJOR;
diff --git a/drivers/lightnvm/pblk-gc.c b/drivers/lightnvm/pblk-gc.c
index 2fa118c8eb71..26a52ea7ec45 100644
--- a/drivers/lightnvm/pblk-gc.c
+++ b/drivers/lightnvm/pblk-gc.c
@@ -365,16 +365,22 @@ static struct pblk_line *pblk_gc_get_victim_line(struct pblk *pblk,
struct list_head *group_list)
{
struct pblk_line *line, *victim;
- int line_vsc, victim_vsc;
+ unsigned int line_vsc = ~0x0L, victim_vsc = ~0x0L;
victim = list_first_entry(group_list, struct pblk_line, list);
+
list_for_each_entry(line, group_list, list) {
- line_vsc = le32_to_cpu(*line->vsc);
- victim_vsc = le32_to_cpu(*victim->vsc);
- if (line_vsc < victim_vsc)
+ if (!atomic_read(&line->sec_to_update))
+ line_vsc = le32_to_cpu(*line->vsc);
+ if (line_vsc < victim_vsc) {
victim = line;
+ victim_vsc = le32_to_cpu(*victim->vsc);
+ }
}
+ if (victim_vsc == ~0x0)
+ return NULL;
+
return victim;
}
@@ -448,13 +454,13 @@ next_gc_group:
do {
spin_lock(&l_mg->gc_lock);
- if (list_empty(group_list)) {
+
+ line = pblk_gc_get_victim_line(pblk, group_list);
+ if (!line) {
spin_unlock(&l_mg->gc_lock);
break;
}
- line = pblk_gc_get_victim_line(pblk, group_list);
-
spin_lock(&line->lock);
WARN_ON(line->state != PBLK_LINESTATE_CLOSED);
line->state = PBLK_LINESTATE_GC;
diff --git a/drivers/lightnvm/pblk-init.c b/drivers/lightnvm/pblk-init.c
index f9a3e47b6a93..8b643d0bffae 100644
--- a/drivers/lightnvm/pblk-init.c
+++ b/drivers/lightnvm/pblk-init.c
@@ -130,7 +130,7 @@ static int pblk_l2p_recover(struct pblk *pblk, bool factory_init)
struct pblk_line *line = NULL;
if (factory_init) {
- pblk_setup_uuid(pblk);
+ guid_gen(&pblk->instance_uuid);
} else {
line = pblk_recov_l2p(pblk);
if (IS_ERR(line)) {
@@ -584,14 +584,12 @@ static void pblk_lines_free(struct pblk *pblk)
struct pblk_line *line;
int i;
- spin_lock(&l_mg->free_lock);
for (i = 0; i < l_mg->nr_lines; i++) {
line = &pblk->lines[i];
pblk_line_free(line);
pblk_line_meta_free(l_mg, line);
}
- spin_unlock(&l_mg->free_lock);
pblk_line_mg_free(pblk);
diff --git a/drivers/lightnvm/pblk-map.c b/drivers/lightnvm/pblk-map.c
index 79df583ea709..7fbc99b60cac 100644
--- a/drivers/lightnvm/pblk-map.c
+++ b/drivers/lightnvm/pblk-map.c
@@ -73,6 +73,7 @@ static int pblk_map_page_data(struct pblk *pblk, unsigned int sentry,
*/
if (i < valid_secs) {
kref_get(&line->ref);
+ atomic_inc(&line->sec_to_update);
w_ctx = pblk_rb_w_ctx(&pblk->rwb, sentry + i);
w_ctx->ppa = ppa_list[i];
meta->lba = cpu_to_le64(w_ctx->lba);
diff --git a/drivers/lightnvm/pblk-rb.c b/drivers/lightnvm/pblk-rb.c
index d4ca8c64ee0f..03c241b340ea 100644
--- a/drivers/lightnvm/pblk-rb.c
+++ b/drivers/lightnvm/pblk-rb.c
@@ -45,10 +45,23 @@ void pblk_rb_free(struct pblk_rb *rb)
/*
* pblk_rb_calculate_size -- calculate the size of the write buffer
*/
-static unsigned int pblk_rb_calculate_size(unsigned int nr_entries)
+static unsigned int pblk_rb_calculate_size(unsigned int nr_entries,
+ unsigned int threshold)
{
- /* Alloc a write buffer that can at least fit 128 entries */
- return (1 << max(get_count_order(nr_entries), 7));
+ unsigned int thr_sz = 1 << (get_count_order(threshold + NVM_MAX_VLBA));
+ unsigned int max_sz = max(thr_sz, nr_entries);
+ unsigned int max_io;
+
+ /* Alloc a write buffer that can (i) fit at least two split bios
+ * (considering max I/O size NVM_MAX_VLBA, and (ii) guarantee that the
+ * threshold will be respected
+ */
+ max_io = (1 << max((int)(get_count_order(max_sz)),
+ (int)(get_count_order(NVM_MAX_VLBA << 1))));
+ if ((threshold + NVM_MAX_VLBA) >= max_io)
+ max_io <<= 1;
+
+ return max_io;
}
/*
@@ -67,12 +80,12 @@ int pblk_rb_init(struct pblk_rb *rb, unsigned int size, unsigned int threshold,
unsigned int alloc_order, order, iter;
unsigned int nr_entries;
- nr_entries = pblk_rb_calculate_size(size);
+ nr_entries = pblk_rb_calculate_size(size, threshold);
entries = vzalloc(array_size(nr_entries, sizeof(struct pblk_rb_entry)));
if (!entries)
return -ENOMEM;
- power_size = get_count_order(size);
+ power_size = get_count_order(nr_entries);
power_seg_sz = get_count_order(seg_size);
down_write(&pblk_rb_lock);
@@ -149,7 +162,7 @@ int pblk_rb_init(struct pblk_rb *rb, unsigned int size, unsigned int threshold,
* Initialize rate-limiter, which controls access to the write buffer
* by user and GC I/O
*/
- pblk_rl_init(&pblk->rl, rb->nr_entries);
+ pblk_rl_init(&pblk->rl, rb->nr_entries, threshold);
return 0;
}
@@ -247,6 +260,7 @@ static int __pblk_rb_update_l2p(struct pblk_rb *rb, unsigned int to_update)
entry->cacheline);
line = pblk_ppa_to_line(pblk, w_ctx->ppa);
+ atomic_dec(&line->sec_to_update);
kref_put(&line->ref, pblk_line_put);
clean_wctx(w_ctx);
rb->l2p_update = pblk_rb_ptr_wrap(rb, rb->l2p_update, 1);
diff --git a/drivers/lightnvm/pblk-recovery.c b/drivers/lightnvm/pblk-recovery.c
index 5ee20da7bdb3..d86f580036d3 100644
--- a/drivers/lightnvm/pblk-recovery.c
+++ b/drivers/lightnvm/pblk-recovery.c
@@ -302,35 +302,55 @@ static int pblk_pad_distance(struct pblk *pblk, struct pblk_line *line)
return (distance > line->left_msecs) ? line->left_msecs : distance;
}
-static int pblk_line_wp_is_unbalanced(struct pblk *pblk,
- struct pblk_line *line)
+/* Return a chunk belonging to a line by stripe(write order) index */
+static struct nvm_chk_meta *pblk_get_stripe_chunk(struct pblk *pblk,
+ struct pblk_line *line,
+ int index)
{
struct nvm_tgt_dev *dev = pblk->dev;
struct nvm_geo *geo = &dev->geo;
- struct pblk_line_meta *lm = &pblk->lm;
struct pblk_lun *rlun;
- struct nvm_chk_meta *chunk;
struct ppa_addr ppa;
- u64 line_wp;
- int pos, i;
+ int pos;
- rlun = &pblk->luns[0];
+ rlun = &pblk->luns[index];
ppa = rlun->bppa;
pos = pblk_ppa_to_pos(geo, ppa);
- chunk = &line->chks[pos];
- line_wp = chunk->wp;
+ return &line->chks[pos];
+}
- for (i = 1; i < lm->blk_per_line; i++) {
- rlun = &pblk->luns[i];
- ppa = rlun->bppa;
- pos = pblk_ppa_to_pos(geo, ppa);
- chunk = &line->chks[pos];
+static int pblk_line_wps_are_unbalanced(struct pblk *pblk,
+ struct pblk_line *line)
+{
+ struct pblk_line_meta *lm = &pblk->lm;
+ int blk_in_line = lm->blk_per_line;
+ struct nvm_chk_meta *chunk;
+ u64 max_wp, min_wp;
+ int i;
+
+ i = find_first_zero_bit(line->blk_bitmap, blk_in_line);
- if (chunk->wp > line_wp)
+ /* If there is one or zero good chunks in the line,
+ * the write pointers can't be unbalanced.
+ */
+ if (i >= (blk_in_line - 1))
+ return 0;
+
+ chunk = pblk_get_stripe_chunk(pblk, line, i);
+ max_wp = chunk->wp;
+ if (max_wp > pblk->max_write_pgs)
+ min_wp = max_wp - pblk->max_write_pgs;
+ else
+ min_wp = 0;
+
+ i = find_next_zero_bit(line->blk_bitmap, blk_in_line, i + 1);
+ while (i < blk_in_line) {
+ chunk = pblk_get_stripe_chunk(pblk, line, i);
+ if (chunk->wp > max_wp || chunk->wp < min_wp)
return 1;
- else if (chunk->wp < line_wp)
- line_wp = chunk->wp;
+
+ i = find_next_zero_bit(line->blk_bitmap, blk_in_line, i + 1);
}
return 0;
@@ -356,7 +376,7 @@ static int pblk_recov_scan_oob(struct pblk *pblk, struct pblk_line *line,
int ret;
u64 left_ppas = pblk_sec_in_open_line(pblk, line) - lm->smeta_sec;
- if (pblk_line_wp_is_unbalanced(pblk, line))
+ if (pblk_line_wps_are_unbalanced(pblk, line))
pblk_warn(pblk, "recovering unbalanced line (%d)\n", line->id);
ppa_list = p.ppa_list;
@@ -703,11 +723,13 @@ struct pblk_line *pblk_recov_l2p(struct pblk *pblk)
/* The first valid instance uuid is used for initialization */
if (!valid_uuid) {
- memcpy(pblk->instance_uuid, smeta_buf->header.uuid, 16);
+ guid_copy(&pblk->instance_uuid,
+ (guid_t *)&smeta_buf->header.uuid);
valid_uuid = 1;
}
- if (memcmp(pblk->instance_uuid, smeta_buf->header.uuid, 16)) {
+ if (!guid_equal(&pblk->instance_uuid,
+ (guid_t *)&smeta_buf->header.uuid)) {
pblk_debug(pblk, "ignore line %u due to uuid mismatch\n",
i);
continue;
@@ -737,7 +759,7 @@ struct pblk_line *pblk_recov_l2p(struct pblk *pblk)
}
if (!found_lines) {
- pblk_setup_uuid(pblk);
+ guid_gen(&pblk->instance_uuid);
spin_lock(&l_mg->free_lock);
WARN_ON_ONCE(!test_and_clear_bit(meta_line,
diff --git a/drivers/lightnvm/pblk-rl.c b/drivers/lightnvm/pblk-rl.c
index 76116d5f78e4..a5f8bc2defbc 100644
--- a/drivers/lightnvm/pblk-rl.c
+++ b/drivers/lightnvm/pblk-rl.c
@@ -207,7 +207,7 @@ void pblk_rl_free(struct pblk_rl *rl)
del_timer(&rl->u_timer);
}
-void pblk_rl_init(struct pblk_rl *rl, int budget)
+void pblk_rl_init(struct pblk_rl *rl, int budget, int threshold)
{
struct pblk *pblk = container_of(rl, struct pblk, rl);
struct nvm_tgt_dev *dev = pblk->dev;
@@ -217,7 +217,6 @@ void pblk_rl_init(struct pblk_rl *rl, int budget)
int sec_meta, blk_meta;
unsigned int rb_windows;
-
/* Consider sectors used for metadata */
sec_meta = (lm->smeta_sec + lm->emeta_sec[0]) * l_mg->nr_free_lines;
blk_meta = DIV_ROUND_UP(sec_meta, geo->clba);
@@ -234,10 +233,15 @@ void pblk_rl_init(struct pblk_rl *rl, int budget)
/* To start with, all buffer is available to user I/O writers */
rl->rb_budget = budget;
rl->rb_user_max = budget;
- rl->rb_max_io = budget >> 1;
rl->rb_gc_max = 0;
rl->rb_state = PBLK_RL_HIGH;
+ /* Maximize I/O size and ansure that back threshold is respected */
+ if (threshold)
+ rl->rb_max_io = budget - pblk->min_write_pgs_data - threshold;
+ else
+ rl->rb_max_io = budget - pblk->min_write_pgs_data - 1;
+
atomic_set(&rl->rb_user_cnt, 0);
atomic_set(&rl->rb_gc_cnt, 0);
atomic_set(&rl->rb_space, -1);
diff --git a/drivers/lightnvm/pblk-trace.h b/drivers/lightnvm/pblk-trace.h
index 679e5c458ca6..9534503b69d9 100644
--- a/drivers/lightnvm/pblk-trace.h
+++ b/drivers/lightnvm/pblk-trace.h
@@ -139,7 +139,7 @@ TRACE_EVENT(pblk_state,
/* This part must be outside protection */
#undef TRACE_INCLUDE_PATH
-#define TRACE_INCLUDE_PATH ../../../drivers/lightnvm
+#define TRACE_INCLUDE_PATH ../../drivers/lightnvm
#undef TRACE_INCLUDE_FILE
#define TRACE_INCLUDE_FILE pblk-trace
#include <trace/define_trace.h>
diff --git a/drivers/lightnvm/pblk-write.c b/drivers/lightnvm/pblk-write.c
index 06d56deb645d..6593deab52da 100644
--- a/drivers/lightnvm/pblk-write.c
+++ b/drivers/lightnvm/pblk-write.c
@@ -177,6 +177,7 @@ static void pblk_prepare_resubmit(struct pblk *pblk, unsigned int sentry,
* re-map these entries
*/
line = pblk_ppa_to_line(pblk, w_ctx->ppa);
+ atomic_dec(&line->sec_to_update);
kref_put(&line->ref, pblk_line_put);
}
spin_unlock(&pblk->trans_lock);
diff --git a/drivers/lightnvm/pblk.h b/drivers/lightnvm/pblk.h
index 85e38ed62f85..ac3ab778e976 100644
--- a/drivers/lightnvm/pblk.h
+++ b/drivers/lightnvm/pblk.h
@@ -131,8 +131,8 @@ struct pblk_pr_ctx {
unsigned int bio_init_idx;
void *ppa_ptr;
dma_addr_t dma_ppa_list;
- __le64 lba_list_mem[NVM_MAX_VLBA];
- __le64 lba_list_media[NVM_MAX_VLBA];
+ u64 lba_list_mem[NVM_MAX_VLBA];
+ u64 lba_list_media[NVM_MAX_VLBA];
};
/* Pad context */
@@ -487,6 +487,7 @@ struct pblk_line {
__le32 *vsc; /* Valid sector count in line */
struct kref ref; /* Write buffer L2P references */
+ atomic_t sec_to_update; /* Outstanding L2P updates to ppa */
struct pblk_w_err_gc *w_err_gc; /* Write error gc recovery metadata */
@@ -646,7 +647,7 @@ struct pblk {
int sec_per_write;
- unsigned char instance_uuid[16];
+ guid_t instance_uuid;
/* Persistent write amplification counters, 4kb sector I/Os */
atomic64_t user_wa; /* Sectors written by user */
@@ -924,7 +925,7 @@ int pblk_gc_sysfs_force(struct pblk *pblk, int force);
/*
* pblk rate limiter
*/
-void pblk_rl_init(struct pblk_rl *rl, int budget);
+void pblk_rl_init(struct pblk_rl *rl, int budget, int threshold);
void pblk_rl_free(struct pblk_rl *rl);
void pblk_rl_update_rates(struct pblk_rl *rl);
int pblk_rl_high_thrs(struct pblk_rl *rl);
@@ -1360,14 +1361,6 @@ static inline unsigned int pblk_get_secs(struct bio *bio)
return bio->bi_iter.bi_size / PBLK_EXPOSED_PAGE_SIZE;
}
-static inline void pblk_setup_uuid(struct pblk *pblk)
-{
- uuid_le uuid;
-
- uuid_le_gen(&uuid);
- memcpy(pblk->instance_uuid, uuid.b, 16);
-}
-
static inline char *pblk_disk_name(struct pblk *pblk)
{
struct gendisk *disk = pblk->disk;
diff --git a/drivers/macintosh/smu.c b/drivers/macintosh/smu.c
index 0a0b8e1f4236..6a844125cf2d 100644
--- a/drivers/macintosh/smu.c
+++ b/drivers/macintosh/smu.c
@@ -485,7 +485,7 @@ int __init smu_init (void)
* SMU based G5s need some memory below 2Gb. Thankfully this is
* called at a time where memblock is still available.
*/
- smu_cmdbuf_abs = memblock_alloc_base(4096, 4096, 0x80000000UL);
+ smu_cmdbuf_abs = memblock_phys_alloc_range(4096, 4096, 0, 0x80000000UL);
if (smu_cmdbuf_abs == 0) {
printk(KERN_ERR "SMU: Command buffer allocation failed !\n");
ret = -EINVAL;
@@ -493,6 +493,9 @@ int __init smu_init (void)
}
smu = memblock_alloc(sizeof(struct smu_device), SMP_CACHE_BYTES);
+ if (!smu)
+ panic("%s: Failed to allocate %zu bytes\n", __func__,
+ sizeof(struct smu_device));
spin_lock_init(&smu->lock);
INIT_LIST_HEAD(&smu->cmd_list);
diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig
index 3eeb12e93e98..d86e7a4ac04d 100644
--- a/drivers/mailbox/Kconfig
+++ b/drivers/mailbox/Kconfig
@@ -205,4 +205,15 @@ config MTK_CMDQ_MBOX
mailbox driver. The CMDQ is used to help read/write registers with
critical time limitation, such as updating display configuration
during the vblank.
+
+config ZYNQMP_IPI_MBOX
+ bool "Xilinx ZynqMP IPI Mailbox"
+ depends on ARCH_ZYNQMP && OF
+ help
+ Say yes here to add support for Xilinx IPI mailbox driver.
+ This mailbox driver is used to send notification or short message
+ between processors with Xilinx ZynqMP IPI. It will place the
+ message to the IPI buffer and will access the IPI control
+ registers to kick the other processor or enquire status.
+
endif
diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile
index c818b5d011ae..8be3bcbcf882 100644
--- a/drivers/mailbox/Makefile
+++ b/drivers/mailbox/Makefile
@@ -44,3 +44,5 @@ obj-$(CONFIG_TEGRA_HSP_MBOX) += tegra-hsp.o
obj-$(CONFIG_STM32_IPCC) += stm32-ipcc.o
obj-$(CONFIG_MTK_CMDQ_MBOX) += mtk-cmdq-mailbox.o
+
+obj-$(CONFIG_ZYNQMP_IPI_MBOX) += zynqmp-ipi-mailbox.o
diff --git a/drivers/mailbox/imx-mailbox.c b/drivers/mailbox/imx-mailbox.c
index 774362a05159..85fc5b56f99b 100644
--- a/drivers/mailbox/imx-mailbox.c
+++ b/drivers/mailbox/imx-mailbox.c
@@ -187,8 +187,8 @@ static int imx_mu_startup(struct mbox_chan *chan)
return 0;
}
- ret = request_irq(priv->irq, imx_mu_isr, IRQF_SHARED, cp->irq_desc,
- chan);
+ ret = request_irq(priv->irq, imx_mu_isr, IRQF_SHARED |
+ IRQF_NO_SUSPEND, cp->irq_desc, chan);
if (ret) {
dev_err(priv->dev,
"Unable to acquire IRQ %d\n", priv->irq);
diff --git a/drivers/mailbox/mailbox-test.c b/drivers/mailbox/mailbox-test.c
index 58bfafc34bc4..4e4ac4be6423 100644
--- a/drivers/mailbox/mailbox-test.c
+++ b/drivers/mailbox/mailbox-test.c
@@ -31,7 +31,6 @@
(MBOX_MAX_MSG_LEN / MBOX_BYTES_PER_LINE))
static bool mbox_data_ready;
-static struct dentry *root_debugfs_dir;
struct mbox_test_device {
struct device *dev;
@@ -45,6 +44,7 @@ struct mbox_test_device {
spinlock_t lock;
wait_queue_head_t waitq;
struct fasync_struct *async_queue;
+ struct dentry *root_debugfs_dir;
};
static ssize_t mbox_test_signal_write(struct file *filp,
@@ -262,16 +262,16 @@ static int mbox_test_add_debugfs(struct platform_device *pdev,
if (!debugfs_initialized())
return 0;
- root_debugfs_dir = debugfs_create_dir("mailbox", NULL);
- if (!root_debugfs_dir) {
+ tdev->root_debugfs_dir = debugfs_create_dir(dev_name(&pdev->dev), NULL);
+ if (!tdev->root_debugfs_dir) {
dev_err(&pdev->dev, "Failed to create Mailbox debugfs\n");
return -EINVAL;
}
- debugfs_create_file("message", 0600, root_debugfs_dir,
+ debugfs_create_file("message", 0600, tdev->root_debugfs_dir,
tdev, &mbox_test_message_ops);
- debugfs_create_file("signal", 0200, root_debugfs_dir,
+ debugfs_create_file("signal", 0200, tdev->root_debugfs_dir,
tdev, &mbox_test_signal_ops);
return 0;
@@ -363,22 +363,24 @@ static int mbox_test_probe(struct platform_device *pdev)
/* It's okay for MMIO to be NULL */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- size = resource_size(res);
tdev->tx_mmio = devm_ioremap_resource(&pdev->dev, res);
- if (PTR_ERR(tdev->tx_mmio) == -EBUSY)
+ if (PTR_ERR(tdev->tx_mmio) == -EBUSY) {
/* if reserved area in SRAM, try just ioremap */
+ size = resource_size(res);
tdev->tx_mmio = devm_ioremap(&pdev->dev, res->start, size);
- else if (IS_ERR(tdev->tx_mmio))
+ } else if (IS_ERR(tdev->tx_mmio)) {
tdev->tx_mmio = NULL;
+ }
/* If specified, second reg entry is Rx MMIO */
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- size = resource_size(res);
tdev->rx_mmio = devm_ioremap_resource(&pdev->dev, res);
- if (PTR_ERR(tdev->rx_mmio) == -EBUSY)
+ if (PTR_ERR(tdev->rx_mmio) == -EBUSY) {
+ size = resource_size(res);
tdev->rx_mmio = devm_ioremap(&pdev->dev, res->start, size);
- else if (IS_ERR(tdev->rx_mmio))
+ } else if (IS_ERR(tdev->rx_mmio)) {
tdev->rx_mmio = tdev->tx_mmio;
+ }
tdev->tx_channel = mbox_test_request_channel(pdev, "tx");
tdev->rx_channel = mbox_test_request_channel(pdev, "rx");
@@ -416,7 +418,7 @@ static int mbox_test_remove(struct platform_device *pdev)
{
struct mbox_test_device *tdev = platform_get_drvdata(pdev);
- debugfs_remove_recursive(root_debugfs_dir);
+ debugfs_remove_recursive(tdev->root_debugfs_dir);
if (tdev->tx_channel)
mbox_free_channel(tdev->tx_channel);
diff --git a/drivers/mailbox/stm32-ipcc.c b/drivers/mailbox/stm32-ipcc.c
index a338bd4cd7db..210fe504f5ae 100644
--- a/drivers/mailbox/stm32-ipcc.c
+++ b/drivers/mailbox/stm32-ipcc.c
@@ -270,14 +270,12 @@ static int stm32_ipcc_probe(struct platform_device *pdev)
goto err_clk;
}
- device_init_wakeup(dev, true);
+ device_set_wakeup_capable(dev, true);
ret = dev_pm_set_dedicated_wake_irq(dev, ipcc->wkp);
if (ret) {
dev_err(dev, "Failed to set wake up irq\n");
goto err_init_wkp;
}
- } else {
- device_init_wakeup(dev, false);
}
/* mailbox controller */
diff --git a/drivers/mailbox/tegra-hsp.c b/drivers/mailbox/tegra-hsp.c
index e443f6a2ec4b..11fc9fd6a94a 100644
--- a/drivers/mailbox/tegra-hsp.c
+++ b/drivers/mailbox/tegra-hsp.c
@@ -779,7 +779,7 @@ static int tegra_hsp_probe(struct platform_device *pdev)
return 0;
}
-static int tegra_hsp_resume(struct device *dev)
+static int __maybe_unused tegra_hsp_resume(struct device *dev)
{
struct tegra_hsp *hsp = dev_get_drvdata(dev);
unsigned int i;
diff --git a/drivers/mailbox/zynqmp-ipi-mailbox.c b/drivers/mailbox/zynqmp-ipi-mailbox.c
new file mode 100644
index 000000000000..86887c9a349a
--- /dev/null
+++ b/drivers/mailbox/zynqmp-ipi-mailbox.c
@@ -0,0 +1,725 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Xilinx Inter Processor Interrupt(IPI) Mailbox Driver
+ *
+ * Copyright (C) 2018 Xilinx, Inc.
+ */
+
+#include <linux/arm-smccc.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mailbox_controller.h>
+#include <linux/mailbox/zynqmp-ipi-message.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+
+/* IPI agent ID any */
+#define IPI_ID_ANY 0xFFUL
+
+/* indicate if ZynqMP IPI mailbox driver uses SMC calls or HVC calls */
+#define USE_SMC 0
+#define USE_HVC 1
+
+/* Default IPI SMC function IDs */
+#define SMC_IPI_MAILBOX_OPEN 0x82001000U
+#define SMC_IPI_MAILBOX_RELEASE 0x82001001U
+#define SMC_IPI_MAILBOX_STATUS_ENQUIRY 0x82001002U
+#define SMC_IPI_MAILBOX_NOTIFY 0x82001003U
+#define SMC_IPI_MAILBOX_ACK 0x82001004U
+#define SMC_IPI_MAILBOX_ENABLE_IRQ 0x82001005U
+#define SMC_IPI_MAILBOX_DISABLE_IRQ 0x82001006U
+
+/* IPI SMC Macros */
+#define IPI_SMC_ENQUIRY_DIRQ_MASK 0x00000001UL /* Flag to indicate if
+ * notification interrupt
+ * to be disabled.
+ */
+#define IPI_SMC_ACK_EIRQ_MASK 0x00000001UL /* Flag to indicate if
+ * notification interrupt
+ * to be enabled.
+ */
+
+/* IPI mailbox status */
+#define IPI_MB_STATUS_IDLE 0
+#define IPI_MB_STATUS_SEND_PENDING 1
+#define IPI_MB_STATUS_RECV_PENDING 2
+
+#define IPI_MB_CHNL_TX 0 /* IPI mailbox TX channel */
+#define IPI_MB_CHNL_RX 1 /* IPI mailbox RX channel */
+
+/**
+ * struct zynqmp_ipi_mchan - Description of a Xilinx ZynqMP IPI mailbox channel
+ * @is_opened: indicate if the IPI channel is opened
+ * @req_buf: local to remote request buffer start address
+ * @resp_buf: local to remote response buffer start address
+ * @req_buf_size: request buffer size
+ * @resp_buf_size: response buffer size
+ * @rx_buf: receive buffer to pass received message to client
+ * @chan_type: channel type
+ */
+struct zynqmp_ipi_mchan {
+ int is_opened;
+ void __iomem *req_buf;
+ void __iomem *resp_buf;
+ void *rx_buf;
+ size_t req_buf_size;
+ size_t resp_buf_size;
+ unsigned int chan_type;
+};
+
+/**
+ * struct zynqmp_ipi_mbox - Description of a ZynqMP IPI mailbox
+ * platform data.
+ * @pdata: pointer to the IPI private data
+ * @dev: device pointer corresponding to the Xilinx ZynqMP
+ * IPI mailbox
+ * @remote_id: remote IPI agent ID
+ * @mbox: mailbox Controller
+ * @mchans: array for channels, tx channel and rx channel.
+ * @irq: IPI agent interrupt ID
+ */
+struct zynqmp_ipi_mbox {
+ struct zynqmp_ipi_pdata *pdata;
+ struct device dev;
+ u32 remote_id;
+ struct mbox_controller mbox;
+ struct zynqmp_ipi_mchan mchans[2];
+};
+
+/**
+ * struct zynqmp_ipi_pdata - Description of z ZynqMP IPI agent platform data.
+ *
+ * @dev: device pointer corresponding to the Xilinx ZynqMP
+ * IPI agent
+ * @irq: IPI agent interrupt ID
+ * @method: IPI SMC or HVC is going to be used
+ * @local_id: local IPI agent ID
+ * @num_mboxes: number of mailboxes of this IPI agent
+ * @ipi_mboxes: IPI mailboxes of this IPI agent
+ */
+struct zynqmp_ipi_pdata {
+ struct device *dev;
+ int irq;
+ unsigned int method;
+ u32 local_id;
+ int num_mboxes;
+ struct zynqmp_ipi_mbox *ipi_mboxes;
+};
+
+static struct device_driver zynqmp_ipi_mbox_driver = {
+ .owner = THIS_MODULE,
+ .name = "zynqmp-ipi-mbox",
+};
+
+static void zynqmp_ipi_fw_call(struct zynqmp_ipi_mbox *ipi_mbox,
+ unsigned long a0, unsigned long a3,
+ struct arm_smccc_res *res)
+{
+ struct zynqmp_ipi_pdata *pdata = ipi_mbox->pdata;
+ unsigned long a1, a2;
+
+ a1 = pdata->local_id;
+ a2 = ipi_mbox->remote_id;
+ if (pdata->method == USE_SMC)
+ arm_smccc_smc(a0, a1, a2, a3, 0, 0, 0, 0, res);
+ else
+ arm_smccc_hvc(a0, a1, a2, a3, 0, 0, 0, 0, res);
+}
+
+/**
+ * zynqmp_ipi_interrupt - Interrupt handler for IPI notification
+ *
+ * @irq: Interrupt number
+ * @data: ZynqMP IPI mailbox platform data.
+ *
+ * Return: -EINVAL if there is no instance
+ * IRQ_NONE if the interrupt is not ours.
+ * IRQ_HANDLED if the rx interrupt was successfully handled.
+ */
+static irqreturn_t zynqmp_ipi_interrupt(int irq, void *data)
+{
+ struct zynqmp_ipi_pdata *pdata = data;
+ struct mbox_chan *chan;
+ struct zynqmp_ipi_mbox *ipi_mbox;
+ struct zynqmp_ipi_mchan *mchan;
+ struct zynqmp_ipi_message *msg;
+ u64 arg0, arg3;
+ struct arm_smccc_res res;
+ int ret, i;
+
+ (void)irq;
+ arg0 = SMC_IPI_MAILBOX_STATUS_ENQUIRY;
+ arg3 = IPI_SMC_ENQUIRY_DIRQ_MASK;
+ for (i = 0; i < pdata->num_mboxes; i++) {
+ ipi_mbox = &pdata->ipi_mboxes[i];
+ mchan = &ipi_mbox->mchans[IPI_MB_CHNL_RX];
+ chan = &ipi_mbox->mbox.chans[IPI_MB_CHNL_RX];
+ zynqmp_ipi_fw_call(ipi_mbox, arg0, arg3, &res);
+ ret = (int)(res.a0 & 0xFFFFFFFF);
+ if (ret > 0 && ret & IPI_MB_STATUS_RECV_PENDING) {
+ if (mchan->is_opened) {
+ msg = mchan->rx_buf;
+ msg->len = mchan->req_buf_size;
+ memcpy_fromio(msg->data, mchan->req_buf,
+ msg->len);
+ mbox_chan_received_data(chan, (void *)msg);
+ return IRQ_HANDLED;
+ }
+ }
+ }
+ return IRQ_NONE;
+}
+
+/**
+ * zynqmp_ipi_peek_data - Peek to see if there are any rx messages.
+ *
+ * @chan: Channel Pointer
+ *
+ * Return: 'true' if there is pending rx data, 'false' if there is none.
+ */
+static bool zynqmp_ipi_peek_data(struct mbox_chan *chan)
+{
+ struct device *dev = chan->mbox->dev;
+ struct zynqmp_ipi_mbox *ipi_mbox = dev_get_drvdata(dev);
+ struct zynqmp_ipi_mchan *mchan = chan->con_priv;
+ int ret;
+ u64 arg0;
+ struct arm_smccc_res res;
+
+ if (WARN_ON(!ipi_mbox)) {
+ dev_err(dev, "no platform drv data??\n");
+ return false;
+ }
+
+ arg0 = SMC_IPI_MAILBOX_STATUS_ENQUIRY;
+ zynqmp_ipi_fw_call(ipi_mbox, arg0, 0, &res);
+ ret = (int)(res.a0 & 0xFFFFFFFF);
+
+ if (mchan->chan_type == IPI_MB_CHNL_TX) {
+ /* TX channel, check if the message has been acked
+ * by the remote, if yes, response is available.
+ */
+ if (ret < 0 || ret & IPI_MB_STATUS_SEND_PENDING)
+ return false;
+ else
+ return true;
+ } else if (ret > 0 && ret & IPI_MB_STATUS_RECV_PENDING) {
+ /* RX channel, check if there is message arrived. */
+ return true;
+ }
+ return false;
+}
+
+/**
+ * zynqmp_ipi_last_tx_done - See if the last tx message is sent
+ *
+ * @chan: Channel pointer
+ *
+ * Return: 'true' is no pending tx data, 'false' if there are any.
+ */
+static bool zynqmp_ipi_last_tx_done(struct mbox_chan *chan)
+{
+ struct device *dev = chan->mbox->dev;
+ struct zynqmp_ipi_mbox *ipi_mbox = dev_get_drvdata(dev);
+ struct zynqmp_ipi_mchan *mchan = chan->con_priv;
+ int ret;
+ u64 arg0;
+ struct arm_smccc_res res;
+
+ if (WARN_ON(!ipi_mbox)) {
+ dev_err(dev, "no platform drv data??\n");
+ return false;
+ }
+
+ if (mchan->chan_type == IPI_MB_CHNL_TX) {
+ /* We only need to check if the message been taken
+ * by the remote in the TX channel
+ */
+ arg0 = SMC_IPI_MAILBOX_STATUS_ENQUIRY;
+ zynqmp_ipi_fw_call(ipi_mbox, arg0, 0, &res);
+ /* Check the SMC call status, a0 of the result */
+ ret = (int)(res.a0 & 0xFFFFFFFF);
+ if (ret < 0 || ret & IPI_MB_STATUS_SEND_PENDING)
+ return false;
+ return true;
+ }
+ /* Always true for the response message in RX channel */
+ return true;
+}
+
+/**
+ * zynqmp_ipi_send_data - Send data
+ *
+ * @chan: Channel Pointer
+ * @data: Message Pointer
+ *
+ * Return: 0 if all goes good, else appropriate error messages.
+ */
+static int zynqmp_ipi_send_data(struct mbox_chan *chan, void *data)
+{
+ struct device *dev = chan->mbox->dev;
+ struct zynqmp_ipi_mbox *ipi_mbox = dev_get_drvdata(dev);
+ struct zynqmp_ipi_mchan *mchan = chan->con_priv;
+ struct zynqmp_ipi_message *msg = data;
+ u64 arg0;
+ struct arm_smccc_res res;
+
+ if (WARN_ON(!ipi_mbox)) {
+ dev_err(dev, "no platform drv data??\n");
+ return -EINVAL;
+ }
+
+ if (mchan->chan_type == IPI_MB_CHNL_TX) {
+ /* Send request message */
+ if (msg && msg->len > mchan->req_buf_size) {
+ dev_err(dev, "channel %d message length %u > max %lu\n",
+ mchan->chan_type, (unsigned int)msg->len,
+ mchan->req_buf_size);
+ return -EINVAL;
+ }
+ if (msg && msg->len)
+ memcpy_toio(mchan->req_buf, msg->data, msg->len);
+ /* Kick IPI mailbox to send message */
+ arg0 = SMC_IPI_MAILBOX_NOTIFY;
+ zynqmp_ipi_fw_call(ipi_mbox, arg0, 0, &res);
+ } else {
+ /* Send response message */
+ if (msg && msg->len > mchan->resp_buf_size) {
+ dev_err(dev, "channel %d message length %u > max %lu\n",
+ mchan->chan_type, (unsigned int)msg->len,
+ mchan->resp_buf_size);
+ return -EINVAL;
+ }
+ if (msg && msg->len)
+ memcpy_toio(mchan->resp_buf, msg->data, msg->len);
+ arg0 = SMC_IPI_MAILBOX_ACK;
+ zynqmp_ipi_fw_call(ipi_mbox, arg0, IPI_SMC_ACK_EIRQ_MASK,
+ &res);
+ }
+ return 0;
+}
+
+/**
+ * zynqmp_ipi_startup - Startup the IPI channel
+ *
+ * @chan: Channel pointer
+ *
+ * Return: 0 if all goes good, else return corresponding error message
+ */
+static int zynqmp_ipi_startup(struct mbox_chan *chan)
+{
+ struct device *dev = chan->mbox->dev;
+ struct zynqmp_ipi_mbox *ipi_mbox = dev_get_drvdata(dev);
+ struct zynqmp_ipi_mchan *mchan = chan->con_priv;
+ u64 arg0;
+ struct arm_smccc_res res;
+ int ret = 0;
+ unsigned int nchan_type;
+
+ if (mchan->is_opened)
+ return 0;
+
+ /* If no channel has been opened, open the IPI mailbox */
+ nchan_type = (mchan->chan_type + 1) % 2;
+ if (!ipi_mbox->mchans[nchan_type].is_opened) {
+ arg0 = SMC_IPI_MAILBOX_OPEN;
+ zynqmp_ipi_fw_call(ipi_mbox, arg0, 0, &res);
+ /* Check the SMC call status, a0 of the result */
+ ret = (int)(res.a0 & 0xFFFFFFFF);
+ if (ret < 0) {
+ dev_err(dev, "SMC to open the IPI channel failed.\n");
+ return ret;
+ }
+ ret = 0;
+ }
+
+ /* If it is RX channel, enable the IPI notification interrupt */
+ if (mchan->chan_type == IPI_MB_CHNL_RX) {
+ arg0 = SMC_IPI_MAILBOX_ENABLE_IRQ;
+ zynqmp_ipi_fw_call(ipi_mbox, arg0, 0, &res);
+ }
+ mchan->is_opened = 1;
+
+ return ret;
+}
+
+/**
+ * zynqmp_ipi_shutdown - Shutdown the IPI channel
+ *
+ * @chan: Channel pointer
+ */
+static void zynqmp_ipi_shutdown(struct mbox_chan *chan)
+{
+ struct device *dev = chan->mbox->dev;
+ struct zynqmp_ipi_mbox *ipi_mbox = dev_get_drvdata(dev);
+ struct zynqmp_ipi_mchan *mchan = chan->con_priv;
+ u64 arg0;
+ struct arm_smccc_res res;
+ unsigned int chan_type;
+
+ if (!mchan->is_opened)
+ return;
+
+ /* If it is RX channel, disable notification interrupt */
+ chan_type = mchan->chan_type;
+ if (chan_type == IPI_MB_CHNL_RX) {
+ arg0 = SMC_IPI_MAILBOX_DISABLE_IRQ;
+ zynqmp_ipi_fw_call(ipi_mbox, arg0, 0, &res);
+ }
+ /* Release IPI mailbox if no other channel is opened */
+ chan_type = (chan_type + 1) % 2;
+ if (!ipi_mbox->mchans[chan_type].is_opened) {
+ arg0 = SMC_IPI_MAILBOX_RELEASE;
+ zynqmp_ipi_fw_call(ipi_mbox, arg0, 0, &res);
+ }
+
+ mchan->is_opened = 0;
+}
+
+/* ZynqMP IPI mailbox operations */
+static const struct mbox_chan_ops zynqmp_ipi_chan_ops = {
+ .startup = zynqmp_ipi_startup,
+ .shutdown = zynqmp_ipi_shutdown,
+ .peek_data = zynqmp_ipi_peek_data,
+ .last_tx_done = zynqmp_ipi_last_tx_done,
+ .send_data = zynqmp_ipi_send_data,
+};
+
+/**
+ * zynqmp_ipi_of_xlate - Translate of phandle to IPI mailbox channel
+ *
+ * @mbox: mailbox controller pointer
+ * @p: phandle pointer
+ *
+ * Return: Mailbox channel, else return error pointer.
+ */
+static struct mbox_chan *zynqmp_ipi_of_xlate(struct mbox_controller *mbox,
+ const struct of_phandle_args *p)
+{
+ struct mbox_chan *chan;
+ struct device *dev = mbox->dev;
+ unsigned int chan_type;
+
+ /* Only supports TX and RX channels */
+ chan_type = p->args[0];
+ if (chan_type != IPI_MB_CHNL_TX && chan_type != IPI_MB_CHNL_RX) {
+ dev_err(dev, "req chnl failure: invalid chnl type %u.\n",
+ chan_type);
+ return ERR_PTR(-EINVAL);
+ }
+ chan = &mbox->chans[chan_type];
+ return chan;
+}
+
+static const struct of_device_id zynqmp_ipi_of_match[] = {
+ { .compatible = "xlnx,zynqmp-ipi-mailbox" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, zynqmp_ipi_of_match);
+
+/**
+ * zynqmp_ipi_mbox_get_buf_res - Get buffer resource from the IPI dev node
+ *
+ * @node: IPI mbox device child node
+ * @name: name of the IPI buffer
+ * @res: pointer to where the resource information will be stored.
+ *
+ * Return: 0 for success, negative value for failure
+ */
+static int zynqmp_ipi_mbox_get_buf_res(struct device_node *node,
+ const char *name,
+ struct resource *res)
+{
+ int ret, index;
+
+ index = of_property_match_string(node, "reg-names", name);
+ if (index >= 0) {
+ ret = of_address_to_resource(node, index, res);
+ if (ret < 0)
+ return -EINVAL;
+ return 0;
+ }
+ return -ENODEV;
+}
+
+/**
+ * zynqmp_ipi_mbox_dev_release() - release the existence of a ipi mbox dev
+ *
+ * @dev: the ipi mailbox device
+ *
+ * This is to avoid the no device release() function kernel warning.
+ *
+ */
+static void zynqmp_ipi_mbox_dev_release(struct device *dev)
+{
+ (void)dev;
+}
+
+/**
+ * zynqmp_ipi_mbox_probe - probe IPI mailbox resource from device node
+ *
+ * @ipi_mbox: pointer to IPI mailbox private data structure
+ * @node: IPI mailbox device node
+ *
+ * Return: 0 for success, negative value for failure
+ */
+static int zynqmp_ipi_mbox_probe(struct zynqmp_ipi_mbox *ipi_mbox,
+ struct device_node *node)
+{
+ struct zynqmp_ipi_mchan *mchan;
+ struct mbox_chan *chans;
+ struct mbox_controller *mbox;
+ struct resource res;
+ struct device *dev, *mdev;
+ const char *name;
+ int ret;
+
+ dev = ipi_mbox->pdata->dev;
+ /* Initialize dev for IPI mailbox */
+ ipi_mbox->dev.parent = dev;
+ ipi_mbox->dev.release = NULL;
+ ipi_mbox->dev.of_node = node;
+ dev_set_name(&ipi_mbox->dev, "%s", of_node_full_name(node));
+ dev_set_drvdata(&ipi_mbox->dev, ipi_mbox);
+ ipi_mbox->dev.release = zynqmp_ipi_mbox_dev_release;
+ ipi_mbox->dev.driver = &zynqmp_ipi_mbox_driver;
+ ret = device_register(&ipi_mbox->dev);
+ if (ret) {
+ dev_err(dev, "Failed to register ipi mbox dev.\n");
+ return ret;
+ }
+ mdev = &ipi_mbox->dev;
+
+ mchan = &ipi_mbox->mchans[IPI_MB_CHNL_TX];
+ name = "local_request_region";
+ ret = zynqmp_ipi_mbox_get_buf_res(node, name, &res);
+ if (!ret) {
+ mchan->req_buf_size = resource_size(&res);
+ mchan->req_buf = devm_ioremap(mdev, res.start,
+ mchan->req_buf_size);
+ if (IS_ERR(mchan->req_buf)) {
+ dev_err(mdev, "Unable to map IPI buffer I/O memory\n");
+ ret = PTR_ERR(mchan->req_buf);
+ return ret;
+ }
+ } else if (ret != -ENODEV) {
+ dev_err(mdev, "Unmatched resource %s, %d.\n", name, ret);
+ return ret;
+ }
+
+ name = "remote_response_region";
+ ret = zynqmp_ipi_mbox_get_buf_res(node, name, &res);
+ if (!ret) {
+ mchan->resp_buf_size = resource_size(&res);
+ mchan->resp_buf = devm_ioremap(mdev, res.start,
+ mchan->resp_buf_size);
+ if (IS_ERR(mchan->resp_buf)) {
+ dev_err(mdev, "Unable to map IPI buffer I/O memory\n");
+ ret = PTR_ERR(mchan->resp_buf);
+ return ret;
+ }
+ } else if (ret != -ENODEV) {
+ dev_err(mdev, "Unmatched resource %s.\n", name);
+ return ret;
+ }
+ mchan->rx_buf = devm_kzalloc(mdev,
+ mchan->resp_buf_size +
+ sizeof(struct zynqmp_ipi_message),
+ GFP_KERNEL);
+ if (!mchan->rx_buf)
+ return -ENOMEM;
+
+ mchan = &ipi_mbox->mchans[IPI_MB_CHNL_RX];
+ name = "remote_request_region";
+ ret = zynqmp_ipi_mbox_get_buf_res(node, name, &res);
+ if (!ret) {
+ mchan->req_buf_size = resource_size(&res);
+ mchan->req_buf = devm_ioremap(mdev, res.start,
+ mchan->req_buf_size);
+ if (IS_ERR(mchan->req_buf)) {
+ dev_err(mdev, "Unable to map IPI buffer I/O memory\n");
+ ret = PTR_ERR(mchan->req_buf);
+ return ret;
+ }
+ } else if (ret != -ENODEV) {
+ dev_err(mdev, "Unmatched resource %s.\n", name);
+ return ret;
+ }
+
+ name = "local_response_region";
+ ret = zynqmp_ipi_mbox_get_buf_res(node, name, &res);
+ if (!ret) {
+ mchan->resp_buf_size = resource_size(&res);
+ mchan->resp_buf = devm_ioremap(mdev, res.start,
+ mchan->resp_buf_size);
+ if (IS_ERR(mchan->resp_buf)) {
+ dev_err(mdev, "Unable to map IPI buffer I/O memory\n");
+ ret = PTR_ERR(mchan->resp_buf);
+ return ret;
+ }
+ } else if (ret != -ENODEV) {
+ dev_err(mdev, "Unmatched resource %s.\n", name);
+ return ret;
+ }
+ mchan->rx_buf = devm_kzalloc(mdev,
+ mchan->resp_buf_size +
+ sizeof(struct zynqmp_ipi_message),
+ GFP_KERNEL);
+ if (!mchan->rx_buf)
+ return -ENOMEM;
+
+ /* Get the IPI remote agent ID */
+ ret = of_property_read_u32(node, "xlnx,ipi-id", &ipi_mbox->remote_id);
+ if (ret < 0) {
+ dev_err(dev, "No IPI remote ID is specified.\n");
+ return ret;
+ }
+
+ mbox = &ipi_mbox->mbox;
+ mbox->dev = mdev;
+ mbox->ops = &zynqmp_ipi_chan_ops;
+ mbox->num_chans = 2;
+ mbox->txdone_irq = false;
+ mbox->txdone_poll = true;
+ mbox->txpoll_period = 5;
+ mbox->of_xlate = zynqmp_ipi_of_xlate;
+ chans = devm_kzalloc(mdev, 2 * sizeof(*chans), GFP_KERNEL);
+ if (!chans)
+ return -ENOMEM;
+ mbox->chans = chans;
+ chans[IPI_MB_CHNL_TX].con_priv = &ipi_mbox->mchans[IPI_MB_CHNL_TX];
+ chans[IPI_MB_CHNL_RX].con_priv = &ipi_mbox->mchans[IPI_MB_CHNL_RX];
+ ipi_mbox->mchans[IPI_MB_CHNL_TX].chan_type = IPI_MB_CHNL_TX;
+ ipi_mbox->mchans[IPI_MB_CHNL_RX].chan_type = IPI_MB_CHNL_RX;
+ ret = devm_mbox_controller_register(mdev, mbox);
+ if (ret)
+ dev_err(mdev,
+ "Failed to register mbox_controller(%d)\n", ret);
+ else
+ dev_info(mdev,
+ "Registered ZynqMP IPI mbox with TX/RX channels.\n");
+ return ret;
+}
+
+/**
+ * zynqmp_ipi_free_mboxes - Free IPI mailboxes devices
+ *
+ * @pdata: IPI private data
+ */
+static void zynqmp_ipi_free_mboxes(struct zynqmp_ipi_pdata *pdata)
+{
+ struct zynqmp_ipi_mbox *ipi_mbox;
+ int i;
+
+ i = pdata->num_mboxes;
+ for (; i >= 0; i--) {
+ ipi_mbox = &pdata->ipi_mboxes[i];
+ if (ipi_mbox->dev.parent) {
+ mbox_controller_unregister(&ipi_mbox->mbox);
+ device_unregister(&ipi_mbox->dev);
+ }
+ }
+}
+
+static int zynqmp_ipi_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *nc, *np = pdev->dev.of_node;
+ struct zynqmp_ipi_pdata *pdata;
+ struct zynqmp_ipi_mbox *mbox;
+ int num_mboxes, ret = -EINVAL;
+
+ num_mboxes = of_get_child_count(np);
+ pdata = devm_kzalloc(dev, sizeof(*pdata) + (num_mboxes * sizeof(*mbox)),
+ GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+ pdata->dev = dev;
+
+ /* Get the IPI local agents ID */
+ ret = of_property_read_u32(np, "xlnx,ipi-id", &pdata->local_id);
+ if (ret < 0) {
+ dev_err(dev, "No IPI local ID is specified.\n");
+ return ret;
+ }
+
+ pdata->num_mboxes = num_mboxes;
+ pdata->ipi_mboxes = (struct zynqmp_ipi_mbox *)
+ ((char *)pdata + sizeof(*pdata));
+
+ mbox = pdata->ipi_mboxes;
+ for_each_available_child_of_node(np, nc) {
+ mbox->pdata = pdata;
+ ret = zynqmp_ipi_mbox_probe(mbox, nc);
+ if (ret) {
+ dev_err(dev, "failed to probe subdev.\n");
+ ret = -EINVAL;
+ goto free_mbox_dev;
+ }
+ mbox++;
+ }
+
+ /* IPI IRQ */
+ ret = platform_get_irq(pdev, 0);
+ if (ret < 0) {
+ dev_err(dev, "unable to find IPI IRQ.\n");
+ goto free_mbox_dev;
+ }
+ pdata->irq = ret;
+ ret = devm_request_irq(dev, pdata->irq, zynqmp_ipi_interrupt,
+ IRQF_SHARED, dev_name(dev), pdata);
+ if (ret) {
+ dev_err(dev, "IRQ %d is not requested successfully.\n",
+ pdata->irq);
+ goto free_mbox_dev;
+ }
+
+ platform_set_drvdata(pdev, pdata);
+ return ret;
+
+free_mbox_dev:
+ zynqmp_ipi_free_mboxes(pdata);
+ return ret;
+}
+
+static int zynqmp_ipi_remove(struct platform_device *pdev)
+{
+ struct zynqmp_ipi_pdata *pdata;
+
+ pdata = platform_get_drvdata(pdev);
+ zynqmp_ipi_free_mboxes(pdata);
+
+ return 0;
+}
+
+static struct platform_driver zynqmp_ipi_driver = {
+ .probe = zynqmp_ipi_probe,
+ .remove = zynqmp_ipi_remove,
+ .driver = {
+ .name = "zynqmp-ipi",
+ .of_match_table = of_match_ptr(zynqmp_ipi_of_match),
+ },
+};
+
+static int __init zynqmp_ipi_init(void)
+{
+ return platform_driver_register(&zynqmp_ipi_driver);
+}
+subsys_initcall(zynqmp_ipi_init);
+
+static void __exit zynqmp_ipi_exit(void)
+{
+ platform_driver_unregister(&zynqmp_ipi_driver);
+}
+module_exit(zynqmp_ipi_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Xilinx ZynqMP IPI Mailbox driver");
+MODULE_AUTHOR("Xilinx Inc.");
diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig
index 3db222509e44..2557f198e175 100644
--- a/drivers/md/Kconfig
+++ b/drivers/md/Kconfig
@@ -436,6 +436,18 @@ config DM_DELAY
If unsure, say N.
+config DM_INIT
+ bool "DM \"dm-mod.create=\" parameter support"
+ depends on BLK_DEV_DM=y
+ ---help---
+ Enable "dm-mod.create=" parameter to create mapped devices at init time.
+ This option is useful to allow mounting rootfs without requiring an
+ initramfs.
+ See Documentation/device-mapper/dm-init.txt for dm-mod.create="..."
+ format.
+
+ If unsure, say N.
+
config DM_UEVENT
bool "DM uevents"
depends on BLK_DEV_DM
diff --git a/drivers/md/Makefile b/drivers/md/Makefile
index 822f4e8753bc..a52b703e588e 100644
--- a/drivers/md/Makefile
+++ b/drivers/md/Makefile
@@ -69,6 +69,10 @@ obj-$(CONFIG_DM_INTEGRITY) += dm-integrity.o
obj-$(CONFIG_DM_ZONED) += dm-zoned.o
obj-$(CONFIG_DM_WRITECACHE) += dm-writecache.o
+ifeq ($(CONFIG_DM_INIT),y)
+dm-mod-objs += dm-init.o
+endif
+
ifeq ($(CONFIG_DM_UEVENT),y)
dm-mod-objs += dm-uevent.o
endif
diff --git a/drivers/md/bcache/btree.c b/drivers/md/bcache/btree.c
index 23cb1dc7296b..64def336f053 100644
--- a/drivers/md/bcache/btree.c
+++ b/drivers/md/bcache/btree.c
@@ -432,8 +432,9 @@ static void do_btree_node_write(struct btree *b)
int j;
struct bio_vec *bv;
void *base = (void *) ((unsigned long) i & ~(PAGE_SIZE - 1));
+ struct bvec_iter_all iter_all;
- bio_for_each_segment_all(bv, b->bio, j)
+ bio_for_each_segment_all(bv, b->bio, j, iter_all)
memcpy(page_address(bv->bv_page),
base + j * PAGE_SIZE, PAGE_SIZE);
diff --git a/drivers/md/bcache/extents.c b/drivers/md/bcache/extents.c
index 956004366699..886710043025 100644
--- a/drivers/md/bcache/extents.c
+++ b/drivers/md/bcache/extents.c
@@ -538,6 +538,7 @@ static bool bch_extent_bad(struct btree_keys *bk, const struct bkey *k)
{
struct btree *b = container_of(bk, struct btree, keys);
unsigned int i, stale;
+ char buf[80];
if (!KEY_PTRS(k) ||
bch_extent_invalid(bk, k))
@@ -547,19 +548,19 @@ static bool bch_extent_bad(struct btree_keys *bk, const struct bkey *k)
if (!ptr_available(b->c, k, i))
return true;
- if (!expensive_debug_checks(b->c) && KEY_DIRTY(k))
- return false;
-
for (i = 0; i < KEY_PTRS(k); i++) {
stale = ptr_stale(b->c, k, i);
+ if (stale && KEY_DIRTY(k)) {
+ bch_extent_to_text(buf, sizeof(buf), k);
+ pr_info("stale dirty pointer, stale %u, key: %s",
+ stale, buf);
+ }
+
btree_bug_on(stale > BUCKET_GC_GEN_MAX, b,
"key too stale: %i, need_gc %u",
stale, b->c->need_gc);
- btree_bug_on(stale && KEY_DIRTY(k) && KEY_SIZE(k),
- b, "stale dirty pointer");
-
if (stale)
return true;
diff --git a/drivers/md/bcache/request.c b/drivers/md/bcache/request.c
index 15070412a32e..f101bfe8657a 100644
--- a/drivers/md/bcache/request.c
+++ b/drivers/md/bcache/request.c
@@ -392,10 +392,11 @@ static bool check_should_bypass(struct cached_dev *dc, struct bio *bio)
/*
* Flag for bypass if the IO is for read-ahead or background,
- * unless the read-ahead request is for metadata (eg, for gfs2).
+ * unless the read-ahead request is for metadata
+ * (eg, for gfs2 or xfs).
*/
if (bio->bi_opf & (REQ_RAHEAD|REQ_BACKGROUND) &&
- !(bio->bi_opf & REQ_PRIO))
+ !(bio->bi_opf & (REQ_META|REQ_PRIO)))
goto skip;
if (bio->bi_iter.bi_sector & (c->sb.block_size - 1) ||
@@ -877,7 +878,7 @@ static int cached_dev_cache_miss(struct btree *b, struct search *s,
}
if (!(bio->bi_opf & REQ_RAHEAD) &&
- !(bio->bi_opf & REQ_PRIO) &&
+ !(bio->bi_opf & (REQ_META|REQ_PRIO)) &&
s->iop.c->gc_stats.in_use < CUTOFF_CACHE_READA)
reada = min_t(sector_t, dc->readahead >> 9,
get_capacity(bio->bi_disk) - bio_end_sector(bio));
diff --git a/drivers/md/bcache/stats.c b/drivers/md/bcache/stats.c
index 894410f3f829..ba1c93791d8d 100644
--- a/drivers/md/bcache/stats.c
+++ b/drivers/md/bcache/stats.c
@@ -111,7 +111,7 @@ void bch_cache_accounting_clear(struct cache_accounting *acc)
{
memset(&acc->total.cache_hits,
0,
- sizeof(unsigned long) * 7);
+ sizeof(struct cache_stats));
}
void bch_cache_accounting_destroy(struct cache_accounting *acc)
diff --git a/drivers/md/bcache/super.c b/drivers/md/bcache/super.c
index 4dee119c3664..a697a3a923cd 100644
--- a/drivers/md/bcache/super.c
+++ b/drivers/md/bcache/super.c
@@ -1615,21 +1615,21 @@ static void conditional_stop_bcache_device(struct cache_set *c,
*/
pr_warn("stop_when_cache_set_failed of %s is \"auto\" and cache is dirty, stop it to avoid potential data corruption.",
d->disk->disk_name);
- /*
- * There might be a small time gap that cache set is
- * released but bcache device is not. Inside this time
- * gap, regular I/O requests will directly go into
- * backing device as no cache set attached to. This
- * behavior may also introduce potential inconsistence
- * data in writeback mode while cache is dirty.
- * Therefore before calling bcache_device_stop() due
- * to a broken cache device, dc->io_disable should be
- * explicitly set to true.
- */
- dc->io_disable = true;
- /* make others know io_disable is true earlier */
- smp_mb();
- bcache_device_stop(d);
+ /*
+ * There might be a small time gap that cache set is
+ * released but bcache device is not. Inside this time
+ * gap, regular I/O requests will directly go into
+ * backing device as no cache set attached to. This
+ * behavior may also introduce potential inconsistence
+ * data in writeback mode while cache is dirty.
+ * Therefore before calling bcache_device_stop() due
+ * to a broken cache device, dc->io_disable should be
+ * explicitly set to true.
+ */
+ dc->io_disable = true;
+ /* make others know io_disable is true earlier */
+ smp_mb();
+ bcache_device_stop(d);
} else {
/*
* dc->stop_when_cache_set_failed == BCH_CACHED_STOP_AUTO
diff --git a/drivers/md/bcache/sysfs.c b/drivers/md/bcache/sysfs.c
index 557a8a3270a1..17bae9c14ca0 100644
--- a/drivers/md/bcache/sysfs.c
+++ b/drivers/md/bcache/sysfs.c
@@ -67,6 +67,8 @@ read_attribute(written);
read_attribute(btree_written);
read_attribute(metadata_written);
read_attribute(active_journal_entries);
+read_attribute(backing_dev_name);
+read_attribute(backing_dev_uuid);
sysfs_time_stats_attribute(btree_gc, sec, ms);
sysfs_time_stats_attribute(btree_split, sec, us);
@@ -243,6 +245,19 @@ SHOW(__bch_cached_dev)
return strlen(buf);
}
+ if (attr == &sysfs_backing_dev_name) {
+ snprintf(buf, BDEVNAME_SIZE + 1, "%s", dc->backing_dev_name);
+ strcat(buf, "\n");
+ return strlen(buf);
+ }
+
+ if (attr == &sysfs_backing_dev_uuid) {
+ /* convert binary uuid into 36-byte string plus '\0' */
+ snprintf(buf, 36+1, "%pU", dc->sb.uuid);
+ strcat(buf, "\n");
+ return strlen(buf);
+ }
+
#undef var
return 0;
}
@@ -262,10 +277,10 @@ STORE(__cached_dev)
sysfs_strtoul(data_csum, dc->disk.data_csum);
d_strtoul(verify);
- d_strtoul(bypass_torture_test);
- d_strtoul(writeback_metadata);
- d_strtoul(writeback_running);
- d_strtoul(writeback_delay);
+ sysfs_strtoul_bool(bypass_torture_test, dc->bypass_torture_test);
+ sysfs_strtoul_bool(writeback_metadata, dc->writeback_metadata);
+ sysfs_strtoul_bool(writeback_running, dc->writeback_running);
+ sysfs_strtoul_clamp(writeback_delay, dc->writeback_delay, 0, UINT_MAX);
sysfs_strtoul_clamp(writeback_percent, dc->writeback_percent,
0, bch_cutoff_writeback);
@@ -287,9 +302,15 @@ STORE(__cached_dev)
sysfs_strtoul_clamp(writeback_rate_update_seconds,
dc->writeback_rate_update_seconds,
1, WRITEBACK_RATE_UPDATE_SECS_MAX);
- d_strtoul(writeback_rate_i_term_inverse);
- d_strtoul_nonzero(writeback_rate_p_term_inverse);
- d_strtoul_nonzero(writeback_rate_minimum);
+ sysfs_strtoul_clamp(writeback_rate_i_term_inverse,
+ dc->writeback_rate_i_term_inverse,
+ 1, UINT_MAX);
+ sysfs_strtoul_clamp(writeback_rate_p_term_inverse,
+ dc->writeback_rate_p_term_inverse,
+ 1, UINT_MAX);
+ sysfs_strtoul_clamp(writeback_rate_minimum,
+ dc->writeback_rate_minimum,
+ 1, UINT_MAX);
sysfs_strtoul_clamp(io_error_limit, dc->error_limit, 0, INT_MAX);
@@ -299,7 +320,9 @@ STORE(__cached_dev)
dc->io_disable = v ? 1 : 0;
}
- d_strtoi_h(sequential_cutoff);
+ sysfs_strtoul_clamp(sequential_cutoff,
+ dc->sequential_cutoff,
+ 0, UINT_MAX);
d_strtoi_h(readahead);
if (attr == &sysfs_clear_stats)
@@ -452,6 +475,8 @@ static struct attribute *bch_cached_dev_files[] = {
&sysfs_verify,
&sysfs_bypass_torture_test,
#endif
+ &sysfs_backing_dev_name,
+ &sysfs_backing_dev_uuid,
NULL
};
KTYPE(bch_cached_dev);
@@ -761,10 +786,12 @@ STORE(__bch_cache_set)
c->shrink.scan_objects(&c->shrink, &sc);
}
- sysfs_strtoul(congested_read_threshold_us,
- c->congested_read_threshold_us);
- sysfs_strtoul(congested_write_threshold_us,
- c->congested_write_threshold_us);
+ sysfs_strtoul_clamp(congested_read_threshold_us,
+ c->congested_read_threshold_us,
+ 0, UINT_MAX);
+ sysfs_strtoul_clamp(congested_write_threshold_us,
+ c->congested_write_threshold_us,
+ 0, UINT_MAX);
if (attr == &sysfs_errors) {
v = __sysfs_match_string(error_actions, -1, buf);
@@ -774,12 +801,20 @@ STORE(__bch_cache_set)
c->on_error = v;
}
- if (attr == &sysfs_io_error_limit)
- c->error_limit = strtoul_or_return(buf);
+ sysfs_strtoul_clamp(io_error_limit, c->error_limit, 0, UINT_MAX);
/* See count_io_errors() for why 88 */
- if (attr == &sysfs_io_error_halflife)
- c->error_decay = strtoul_or_return(buf) / 88;
+ if (attr == &sysfs_io_error_halflife) {
+ unsigned long v = 0;
+ ssize_t ret;
+
+ ret = strtoul_safe_clamp(buf, v, 0, UINT_MAX);
+ if (!ret) {
+ c->error_decay = v / 88;
+ return size;
+ }
+ return ret;
+ }
if (attr == &sysfs_io_disable) {
v = strtoul_or_return(buf);
@@ -794,13 +829,15 @@ STORE(__bch_cache_set)
}
}
- sysfs_strtoul(journal_delay_ms, c->journal_delay_ms);
- sysfs_strtoul(verify, c->verify);
- sysfs_strtoul(key_merging_disabled, c->key_merging_disabled);
+ sysfs_strtoul_clamp(journal_delay_ms,
+ c->journal_delay_ms,
+ 0, USHRT_MAX);
+ sysfs_strtoul_bool(verify, c->verify);
+ sysfs_strtoul_bool(key_merging_disabled, c->key_merging_disabled);
sysfs_strtoul(expensive_debug_checks, c->expensive_debug_checks);
- sysfs_strtoul(gc_always_rewrite, c->gc_always_rewrite);
- sysfs_strtoul(btree_shrinker_disabled, c->shrinker_disabled);
- sysfs_strtoul(copy_gc_enabled, c->copy_gc_enabled);
+ sysfs_strtoul_bool(gc_always_rewrite, c->gc_always_rewrite);
+ sysfs_strtoul_bool(btree_shrinker_disabled, c->shrinker_disabled);
+ sysfs_strtoul_bool(copy_gc_enabled, c->copy_gc_enabled);
/*
* write gc_after_writeback here may overwrite an already set
* BCH_DO_AUTO_GC, it doesn't matter because this flag will be
diff --git a/drivers/md/bcache/sysfs.h b/drivers/md/bcache/sysfs.h
index 3fe82425859c..215df32f567b 100644
--- a/drivers/md/bcache/sysfs.h
+++ b/drivers/md/bcache/sysfs.h
@@ -79,11 +79,28 @@ do { \
return strtoul_safe(buf, var) ?: (ssize_t) size; \
} while (0)
+#define sysfs_strtoul_bool(file, var) \
+do { \
+ if (attr == &sysfs_ ## file) { \
+ unsigned long v = strtoul_or_return(buf); \
+ \
+ var = v ? 1 : 0; \
+ return size; \
+ } \
+} while (0)
+
#define sysfs_strtoul_clamp(file, var, min, max) \
do { \
- if (attr == &sysfs_ ## file) \
- return strtoul_safe_clamp(buf, var, min, max) \
- ?: (ssize_t) size; \
+ if (attr == &sysfs_ ## file) { \
+ unsigned long v = 0; \
+ ssize_t ret; \
+ ret = strtoul_safe_clamp(buf, v, min, max); \
+ if (!ret) { \
+ var = v; \
+ return size; \
+ } \
+ return ret; \
+ } \
} while (0)
#define strtoul_or_return(cp) \
diff --git a/drivers/md/bcache/util.c b/drivers/md/bcache/util.c
index 20eddeac1531..62fb917f7a4f 100644
--- a/drivers/md/bcache/util.c
+++ b/drivers/md/bcache/util.c
@@ -270,7 +270,11 @@ int bch_bio_alloc_pages(struct bio *bio, gfp_t gfp_mask)
int i;
struct bio_vec *bv;
- bio_for_each_segment_all(bv, bio, i) {
+ /*
+ * This is called on freshly new bio, so it is safe to access the
+ * bvec table directly.
+ */
+ for (i = 0, bv = bio->bi_io_vec; i < bio->bi_vcnt; bv++, i++) {
bv->bv_page = alloc_page(gfp_mask);
if (!bv->bv_page) {
while (--bv >= bio->bi_io_vec)
diff --git a/drivers/md/bcache/writeback.h b/drivers/md/bcache/writeback.h
index 6a743d3bb338..4e4c6810dc3c 100644
--- a/drivers/md/bcache/writeback.h
+++ b/drivers/md/bcache/writeback.h
@@ -71,6 +71,9 @@ static inline bool should_writeback(struct cached_dev *dc, struct bio *bio,
in_use > bch_cutoff_writeback_sync)
return false;
+ if (bio_op(bio) == REQ_OP_DISCARD)
+ return false;
+
if (dc->partial_stripes_expensive &&
bcache_dev_stripe_dirty(dc, bio->bi_iter.bi_sector,
bio_sectors(bio)))
diff --git a/drivers/md/dm-cache-target.c b/drivers/md/dm-cache-target.c
index b29a8327eed1..d249cf8ac277 100644
--- a/drivers/md/dm-cache-target.c
+++ b/drivers/md/dm-cache-target.c
@@ -353,6 +353,7 @@ struct cache_features {
enum cache_metadata_mode mode;
enum cache_io_mode io_mode;
unsigned metadata_version;
+ bool discard_passdown:1;
};
struct cache_stats {
@@ -1899,7 +1900,11 @@ static bool process_discard_bio(struct cache *cache, struct bio *bio)
b = to_dblock(from_dblock(b) + 1);
}
- bio_endio(bio);
+ if (cache->features.discard_passdown) {
+ remap_to_origin(cache, bio);
+ generic_make_request(bio);
+ } else
+ bio_endio(bio);
return false;
}
@@ -2233,13 +2238,14 @@ static void init_features(struct cache_features *cf)
cf->mode = CM_WRITE;
cf->io_mode = CM_IO_WRITEBACK;
cf->metadata_version = 1;
+ cf->discard_passdown = true;
}
static int parse_features(struct cache_args *ca, struct dm_arg_set *as,
char **error)
{
static const struct dm_arg _args[] = {
- {0, 2, "Invalid number of cache feature arguments"},
+ {0, 3, "Invalid number of cache feature arguments"},
};
int r, mode_ctr = 0;
@@ -2274,6 +2280,9 @@ static int parse_features(struct cache_args *ca, struct dm_arg_set *as,
else if (!strcasecmp(arg, "metadata2"))
cf->metadata_version = 2;
+ else if (!strcasecmp(arg, "no_discard_passdown"))
+ cf->discard_passdown = false;
+
else {
*error = "Unrecognised cache feature requested";
return -EINVAL;
@@ -2496,7 +2505,6 @@ static int cache_create(struct cache_args *ca, struct cache **result)
ti->num_discard_bios = 1;
ti->discards_supported = true;
- ti->split_discard_bios = false;
ti->per_io_data_size = sizeof(struct per_bio_data);
@@ -3120,6 +3128,39 @@ static void cache_resume(struct dm_target *ti)
do_waker(&cache->waker.work);
}
+static void emit_flags(struct cache *cache, char *result,
+ unsigned maxlen, ssize_t *sz_ptr)
+{
+ ssize_t sz = *sz_ptr;
+ struct cache_features *cf = &cache->features;
+ unsigned count = (cf->metadata_version == 2) + !cf->discard_passdown + 1;
+
+ DMEMIT("%u ", count);
+
+ if (cf->metadata_version == 2)
+ DMEMIT("metadata2 ");
+
+ if (writethrough_mode(cache))
+ DMEMIT("writethrough ");
+
+ else if (passthrough_mode(cache))
+ DMEMIT("passthrough ");
+
+ else if (writeback_mode(cache))
+ DMEMIT("writeback ");
+
+ else {
+ DMEMIT("unknown ");
+ DMERR("%s: internal error: unknown io mode: %d",
+ cache_device_name(cache), (int) cf->io_mode);
+ }
+
+ if (!cf->discard_passdown)
+ DMEMIT("no_discard_passdown ");
+
+ *sz_ptr = sz;
+}
+
/*
* Status format:
*
@@ -3186,25 +3227,7 @@ static void cache_status(struct dm_target *ti, status_type_t type,
(unsigned) atomic_read(&cache->stats.promotion),
(unsigned long) atomic_read(&cache->nr_dirty));
- if (cache->features.metadata_version == 2)
- DMEMIT("2 metadata2 ");
- else
- DMEMIT("1 ");
-
- if (writethrough_mode(cache))
- DMEMIT("writethrough ");
-
- else if (passthrough_mode(cache))
- DMEMIT("passthrough ");
-
- else if (writeback_mode(cache))
- DMEMIT("writeback ");
-
- else {
- DMERR("%s: internal error: unknown io mode: %d",
- cache_device_name(cache), (int) cache->features.io_mode);
- goto err;
- }
+ emit_flags(cache, result, maxlen, &sz);
DMEMIT("2 migration_threshold %llu ", (unsigned long long) cache->migration_threshold);
@@ -3433,14 +3456,62 @@ static int cache_iterate_devices(struct dm_target *ti,
return r;
}
+static bool origin_dev_supports_discard(struct block_device *origin_bdev)
+{
+ struct request_queue *q = bdev_get_queue(origin_bdev);
+
+ return q && blk_queue_discard(q);
+}
+
+/*
+ * If discard_passdown was enabled verify that the origin device
+ * supports discards. Disable discard_passdown if not.
+ */
+static void disable_passdown_if_not_supported(struct cache *cache)
+{
+ struct block_device *origin_bdev = cache->origin_dev->bdev;
+ struct queue_limits *origin_limits = &bdev_get_queue(origin_bdev)->limits;
+ const char *reason = NULL;
+ char buf[BDEVNAME_SIZE];
+
+ if (!cache->features.discard_passdown)
+ return;
+
+ if (!origin_dev_supports_discard(origin_bdev))
+ reason = "discard unsupported";
+
+ else if (origin_limits->max_discard_sectors < cache->sectors_per_block)
+ reason = "max discard sectors smaller than a block";
+
+ if (reason) {
+ DMWARN("Origin device (%s) %s: Disabling discard passdown.",
+ bdevname(origin_bdev, buf), reason);
+ cache->features.discard_passdown = false;
+ }
+}
+
static void set_discard_limits(struct cache *cache, struct queue_limits *limits)
{
+ struct block_device *origin_bdev = cache->origin_dev->bdev;
+ struct queue_limits *origin_limits = &bdev_get_queue(origin_bdev)->limits;
+
+ if (!cache->features.discard_passdown) {
+ /* No passdown is done so setting own virtual limits */
+ limits->max_discard_sectors = min_t(sector_t, cache->discard_block_size * 1024,
+ cache->origin_sectors);
+ limits->discard_granularity = cache->discard_block_size << SECTOR_SHIFT;
+ return;
+ }
+
/*
- * FIXME: these limits may be incompatible with the cache device
+ * cache_iterate_devices() is stacking both origin and fast device limits
+ * but discards aren't passed to fast device, so inherit origin's limits.
*/
- limits->max_discard_sectors = min_t(sector_t, cache->discard_block_size * 1024,
- cache->origin_sectors);
- limits->discard_granularity = cache->discard_block_size << SECTOR_SHIFT;
+ limits->max_discard_sectors = origin_limits->max_discard_sectors;
+ limits->max_hw_discard_sectors = origin_limits->max_hw_discard_sectors;
+ limits->discard_granularity = origin_limits->discard_granularity;
+ limits->discard_alignment = origin_limits->discard_alignment;
+ limits->discard_misaligned = origin_limits->discard_misaligned;
}
static void cache_io_hints(struct dm_target *ti, struct queue_limits *limits)
@@ -3457,6 +3528,8 @@ static void cache_io_hints(struct dm_target *ti, struct queue_limits *limits)
blk_limits_io_min(limits, cache->sectors_per_block << SECTOR_SHIFT);
blk_limits_io_opt(limits, cache->sectors_per_block << SECTOR_SHIFT);
}
+
+ disable_passdown_if_not_supported(cache);
set_discard_limits(cache, limits);
}
@@ -3464,7 +3537,7 @@ static void cache_io_hints(struct dm_target *ti, struct queue_limits *limits)
static struct target_type cache_target = {
.name = "cache",
- .version = {2, 0, 0},
+ .version = {2, 1, 0},
.module = THIS_MODULE,
.ctr = cache_ctr,
.dtr = cache_dtr,
diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c
index dd538e6b2748..dd6565798778 100644
--- a/drivers/md/dm-crypt.c
+++ b/drivers/md/dm-crypt.c
@@ -1447,8 +1447,9 @@ static void crypt_free_buffer_pages(struct crypt_config *cc, struct bio *clone)
{
unsigned int i;
struct bio_vec *bv;
+ struct bvec_iter_all iter_all;
- bio_for_each_segment_all(bv, clone, i) {
+ bio_for_each_segment_all(bv, clone, i, iter_all) {
BUG_ON(!bv->bv_page);
mempool_free(bv->bv_page, &cc->page_pool);
}
diff --git a/drivers/md/dm-init.c b/drivers/md/dm-init.c
new file mode 100644
index 000000000000..b53f30f16b4d
--- /dev/null
+++ b/drivers/md/dm-init.c
@@ -0,0 +1,303 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * dm-init.c
+ * Copyright (C) 2017 The Chromium OS Authors <chromium-os-dev@chromium.org>
+ *
+ * This file is released under the GPLv2.
+ */
+
+#include <linux/ctype.h>
+#include <linux/device.h>
+#include <linux/device-mapper.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/moduleparam.h>
+
+#define DM_MSG_PREFIX "init"
+#define DM_MAX_DEVICES 256
+#define DM_MAX_TARGETS 256
+#define DM_MAX_STR_SIZE 4096
+
+static char *create;
+
+/*
+ * Format: dm-mod.create=<name>,<uuid>,<minor>,<flags>,<table>[,<table>+][;<name>,<uuid>,<minor>,<flags>,<table>[,<table>+]+]
+ * Table format: <start_sector> <num_sectors> <target_type> <target_args>
+ *
+ * See Documentation/device-mapper/dm-init.txt for dm-mod.create="..." format
+ * details.
+ */
+
+struct dm_device {
+ struct dm_ioctl dmi;
+ struct dm_target_spec *table[DM_MAX_TARGETS];
+ char *target_args_array[DM_MAX_TARGETS];
+ struct list_head list;
+};
+
+const char *dm_allowed_targets[] __initconst = {
+ "crypt",
+ "delay",
+ "linear",
+ "snapshot-origin",
+ "striped",
+ "verity",
+};
+
+static int __init dm_verify_target_type(const char *target)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(dm_allowed_targets); i++) {
+ if (!strcmp(dm_allowed_targets[i], target))
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static void __init dm_setup_cleanup(struct list_head *devices)
+{
+ struct dm_device *dev, *tmp;
+ unsigned int i;
+
+ list_for_each_entry_safe(dev, tmp, devices, list) {
+ list_del(&dev->list);
+ for (i = 0; i < dev->dmi.target_count; i++) {
+ kfree(dev->table[i]);
+ kfree(dev->target_args_array[i]);
+ }
+ kfree(dev);
+ }
+}
+
+/**
+ * str_field_delimit - delimit a string based on a separator char.
+ * @str: the pointer to the string to delimit.
+ * @separator: char that delimits the field
+ *
+ * Find a @separator and replace it by '\0'.
+ * Remove leading and trailing spaces.
+ * Return the remainder string after the @separator.
+ */
+static char __init *str_field_delimit(char **str, char separator)
+{
+ char *s;
+
+ /* TODO: add support for escaped characters */
+ *str = skip_spaces(*str);
+ s = strchr(*str, separator);
+ /* Delimit the field and remove trailing spaces */
+ if (s)
+ *s = '\0';
+ *str = strim(*str);
+ return s ? ++s : NULL;
+}
+
+/**
+ * dm_parse_table_entry - parse a table entry
+ * @dev: device to store the parsed information.
+ * @str: the pointer to a string with the format:
+ * <start_sector> <num_sectors> <target_type> <target_args>[, ...]
+ *
+ * Return the remainder string after the table entry, i.e, after the comma which
+ * delimits the entry or NULL if reached the end of the string.
+ */
+static char __init *dm_parse_table_entry(struct dm_device *dev, char *str)
+{
+ const unsigned int n = dev->dmi.target_count - 1;
+ struct dm_target_spec *sp;
+ unsigned int i;
+ /* fields: */
+ char *field[4];
+ char *next;
+
+ field[0] = str;
+ /* Delimit first 3 fields that are separated by space */
+ for (i = 0; i < ARRAY_SIZE(field) - 1; i++) {
+ field[i + 1] = str_field_delimit(&field[i], ' ');
+ if (!field[i + 1])
+ return ERR_PTR(-EINVAL);
+ }
+ /* Delimit last field that can be terminated by comma */
+ next = str_field_delimit(&field[i], ',');
+
+ sp = kzalloc(sizeof(*sp), GFP_KERNEL);
+ if (!sp)
+ return ERR_PTR(-ENOMEM);
+ dev->table[n] = sp;
+
+ /* start_sector */
+ if (kstrtoull(field[0], 0, &sp->sector_start))
+ return ERR_PTR(-EINVAL);
+ /* num_sector */
+ if (kstrtoull(field[1], 0, &sp->length))
+ return ERR_PTR(-EINVAL);
+ /* target_type */
+ strscpy(sp->target_type, field[2], sizeof(sp->target_type));
+ if (dm_verify_target_type(sp->target_type)) {
+ DMERR("invalid type \"%s\"", sp->target_type);
+ return ERR_PTR(-EINVAL);
+ }
+ /* target_args */
+ dev->target_args_array[n] = kstrndup(field[3], GFP_KERNEL,
+ DM_MAX_STR_SIZE);
+ if (!dev->target_args_array[n])
+ return ERR_PTR(-ENOMEM);
+
+ return next;
+}
+
+/**
+ * dm_parse_table - parse "dm-mod.create=" table field
+ * @dev: device to store the parsed information.
+ * @str: the pointer to a string with the format:
+ * <table>[,<table>+]
+ */
+static int __init dm_parse_table(struct dm_device *dev, char *str)
+{
+ char *table_entry = str;
+
+ while (table_entry) {
+ DMDEBUG("parsing table \"%s\"", str);
+ if (++dev->dmi.target_count >= DM_MAX_TARGETS) {
+ DMERR("too many targets %u > %d",
+ dev->dmi.target_count, DM_MAX_TARGETS);
+ return -EINVAL;
+ }
+ table_entry = dm_parse_table_entry(dev, table_entry);
+ if (IS_ERR(table_entry)) {
+ DMERR("couldn't parse table");
+ return PTR_ERR(table_entry);
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * dm_parse_device_entry - parse a device entry
+ * @dev: device to store the parsed information.
+ * @str: the pointer to a string with the format:
+ * name,uuid,minor,flags,table[; ...]
+ *
+ * Return the remainder string after the table entry, i.e, after the semi-colon
+ * which delimits the entry or NULL if reached the end of the string.
+ */
+static char __init *dm_parse_device_entry(struct dm_device *dev, char *str)
+{
+ /* There are 5 fields: name,uuid,minor,flags,table; */
+ char *field[5];
+ unsigned int i;
+ char *next;
+
+ field[0] = str;
+ /* Delimit first 4 fields that are separated by comma */
+ for (i = 0; i < ARRAY_SIZE(field) - 1; i++) {
+ field[i+1] = str_field_delimit(&field[i], ',');
+ if (!field[i+1])
+ return ERR_PTR(-EINVAL);
+ }
+ /* Delimit last field that can be delimited by semi-colon */
+ next = str_field_delimit(&field[i], ';');
+
+ /* name */
+ strscpy(dev->dmi.name, field[0], sizeof(dev->dmi.name));
+ /* uuid */
+ strscpy(dev->dmi.uuid, field[1], sizeof(dev->dmi.uuid));
+ /* minor */
+ if (strlen(field[2])) {
+ if (kstrtoull(field[2], 0, &dev->dmi.dev))
+ return ERR_PTR(-EINVAL);
+ dev->dmi.flags |= DM_PERSISTENT_DEV_FLAG;
+ }
+ /* flags */
+ if (!strcmp(field[3], "ro"))
+ dev->dmi.flags |= DM_READONLY_FLAG;
+ else if (strcmp(field[3], "rw"))
+ return ERR_PTR(-EINVAL);
+ /* table */
+ if (dm_parse_table(dev, field[4]))
+ return ERR_PTR(-EINVAL);
+
+ return next;
+}
+
+/**
+ * dm_parse_devices - parse "dm-mod.create=" argument
+ * @devices: list of struct dm_device to store the parsed information.
+ * @str: the pointer to a string with the format:
+ * <device>[;<device>+]
+ */
+static int __init dm_parse_devices(struct list_head *devices, char *str)
+{
+ unsigned long ndev = 0;
+ struct dm_device *dev;
+ char *device = str;
+
+ DMDEBUG("parsing \"%s\"", str);
+ while (device) {
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+ list_add_tail(&dev->list, devices);
+
+ if (++ndev >= DM_MAX_DEVICES) {
+ DMERR("too many targets %u > %d",
+ dev->dmi.target_count, DM_MAX_TARGETS);
+ return -EINVAL;
+ }
+
+ device = dm_parse_device_entry(dev, device);
+ if (IS_ERR(device)) {
+ DMERR("couldn't parse device");
+ return PTR_ERR(device);
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * dm_init_init - parse "dm-mod.create=" argument and configure drivers
+ */
+static int __init dm_init_init(void)
+{
+ struct dm_device *dev;
+ LIST_HEAD(devices);
+ char *str;
+ int r;
+
+ if (!create)
+ return 0;
+
+ if (strlen(create) >= DM_MAX_STR_SIZE) {
+ DMERR("Argument is too big. Limit is %d\n", DM_MAX_STR_SIZE);
+ return -EINVAL;
+ }
+ str = kstrndup(create, GFP_KERNEL, DM_MAX_STR_SIZE);
+ if (!str)
+ return -ENOMEM;
+
+ r = dm_parse_devices(&devices, str);
+ if (r)
+ goto out;
+
+ DMINFO("waiting for all devices to be available before creating mapped devices\n");
+ wait_for_device_probe();
+
+ list_for_each_entry(dev, &devices, list) {
+ if (dm_early_create(&dev->dmi, dev->table,
+ dev->target_args_array))
+ break;
+ }
+out:
+ kfree(str);
+ dm_setup_cleanup(&devices);
+ return r;
+}
+
+late_initcall(dm_init_init);
+
+module_param(create, charp, 0);
+MODULE_PARM_DESC(create, "Create a mapped device in early boot");
diff --git a/drivers/md/dm-integrity.c b/drivers/md/dm-integrity.c
index 457200ca6287..d57d997a52c8 100644
--- a/drivers/md/dm-integrity.c
+++ b/drivers/md/dm-integrity.c
@@ -1122,7 +1122,7 @@ static int dm_integrity_rw_tag(struct dm_integrity_c *ic, unsigned char *tag, se
return r;
data = dm_bufio_read(ic->bufio, *metadata_block, &b);
- if (unlikely(IS_ERR(data)))
+ if (IS_ERR(data))
return PTR_ERR(data);
to_copy = min((1U << SECTOR_SHIFT << ic->log2_buffer_sectors) - *metadata_offset, total_size);
@@ -1368,8 +1368,8 @@ again:
checksums_ptr - checksums, !dio->write ? TAG_CMP : TAG_WRITE);
if (unlikely(r)) {
if (r > 0) {
- DMERR("Checksum failed at sector 0x%llx",
- (unsigned long long)(sector - ((r + ic->tag_size - 1) / ic->tag_size)));
+ DMERR_LIMIT("Checksum failed at sector 0x%llx",
+ (unsigned long long)(sector - ((r + ic->tag_size - 1) / ic->tag_size)));
r = -EILSEQ;
atomic64_inc(&ic->number_of_mismatches);
}
@@ -1561,8 +1561,8 @@ retry_kmap:
integrity_sector_checksum(ic, logical_sector, mem + bv.bv_offset, checksums_onstack);
if (unlikely(memcmp(checksums_onstack, journal_entry_tag(ic, je), ic->tag_size))) {
- DMERR("Checksum failed when reading from journal, at sector 0x%llx",
- (unsigned long long)logical_sector);
+ DMERR_LIMIT("Checksum failed when reading from journal, at sector 0x%llx",
+ (unsigned long long)logical_sector);
}
}
#endif
diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c
index f666778ad237..c740153b4e52 100644
--- a/drivers/md/dm-ioctl.c
+++ b/drivers/md/dm-ioctl.c
@@ -2018,3 +2018,106 @@ out:
return r;
}
+
+
+/**
+ * dm_early_create - create a mapped device in early boot.
+ *
+ * @dmi: Contains main information of the device mapping to be created.
+ * @spec_array: array of pointers to struct dm_target_spec. Describes the
+ * mapping table of the device.
+ * @target_params_array: array of strings with the parameters to a specific
+ * target.
+ *
+ * Instead of having the struct dm_target_spec and the parameters for every
+ * target embedded at the end of struct dm_ioctl (as performed in a normal
+ * ioctl), pass them as arguments, so the caller doesn't need to serialize them.
+ * The size of the spec_array and target_params_array is given by
+ * @dmi->target_count.
+ * This function is supposed to be called in early boot, so locking mechanisms
+ * to protect against concurrent loads are not required.
+ */
+int __init dm_early_create(struct dm_ioctl *dmi,
+ struct dm_target_spec **spec_array,
+ char **target_params_array)
+{
+ int r, m = DM_ANY_MINOR;
+ struct dm_table *t, *old_map;
+ struct mapped_device *md;
+ unsigned int i;
+
+ if (!dmi->target_count)
+ return -EINVAL;
+
+ r = check_name(dmi->name);
+ if (r)
+ return r;
+
+ if (dmi->flags & DM_PERSISTENT_DEV_FLAG)
+ m = MINOR(huge_decode_dev(dmi->dev));
+
+ /* alloc dm device */
+ r = dm_create(m, &md);
+ if (r)
+ return r;
+
+ /* hash insert */
+ r = dm_hash_insert(dmi->name, *dmi->uuid ? dmi->uuid : NULL, md);
+ if (r)
+ goto err_destroy_dm;
+
+ /* alloc table */
+ r = dm_table_create(&t, get_mode(dmi), dmi->target_count, md);
+ if (r)
+ goto err_destroy_dm;
+
+ /* add targets */
+ for (i = 0; i < dmi->target_count; i++) {
+ r = dm_table_add_target(t, spec_array[i]->target_type,
+ (sector_t) spec_array[i]->sector_start,
+ (sector_t) spec_array[i]->length,
+ target_params_array[i]);
+ if (r) {
+ DMWARN("error adding target to table");
+ goto err_destroy_table;
+ }
+ }
+
+ /* finish table */
+ r = dm_table_complete(t);
+ if (r)
+ goto err_destroy_table;
+
+ md->type = dm_table_get_type(t);
+ /* setup md->queue to reflect md's type (may block) */
+ r = dm_setup_md_queue(md, t);
+ if (r) {
+ DMWARN("unable to set up device queue for new table.");
+ goto err_destroy_table;
+ }
+
+ /* Set new map */
+ dm_suspend(md, 0);
+ old_map = dm_swap_table(md, t);
+ if (IS_ERR(old_map)) {
+ r = PTR_ERR(old_map);
+ goto err_destroy_table;
+ }
+ set_disk_ro(dm_disk(md), !!(dmi->flags & DM_READONLY_FLAG));
+
+ /* resume device */
+ r = dm_resume(md);
+ if (r)
+ goto err_destroy_table;
+
+ DMINFO("%s (%s) is ready", md->disk->disk_name, dmi->name);
+ dm_put(md);
+ return 0;
+
+err_destroy_table:
+ dm_table_destroy(t);
+err_destroy_dm:
+ dm_put(md);
+ dm_destroy(md);
+ return r;
+}
diff --git a/drivers/md/dm-raid.c b/drivers/md/dm-raid.c
index adcfe8ae10aa..9fdef6897316 100644
--- a/drivers/md/dm-raid.c
+++ b/drivers/md/dm-raid.c
@@ -2986,11 +2986,6 @@ static void configure_discard_support(struct raid_set *rs)
}
}
- /*
- * RAID1 and RAID10 personalities require bio splitting,
- * RAID0/4/5/6 don't and process large discard bios properly.
- */
- ti->split_discard_bios = !!(rs_is_raid1(rs) || rs_is_raid10(rs));
ti->num_discard_bios = 1;
}
@@ -3747,6 +3742,15 @@ static void raid_io_hints(struct dm_target *ti, struct queue_limits *limits)
blk_limits_io_min(limits, chunk_size);
blk_limits_io_opt(limits, chunk_size * mddev_data_stripes(rs));
+
+ /*
+ * RAID1 and RAID10 personalities require bio splitting,
+ * RAID0/4/5/6 don't and process large discard bios properly.
+ */
+ if (rs_is_raid1(rs) || rs_is_raid10(rs)) {
+ limits->discard_granularity = chunk_size;
+ limits->max_discard_sectors = chunk_size;
+ }
}
static void raid_postsuspend(struct dm_target *ti)
diff --git a/drivers/md/dm-rq.c b/drivers/md/dm-rq.c
index a20531e5f3b4..09773636602d 100644
--- a/drivers/md/dm-rq.c
+++ b/drivers/md/dm-rq.c
@@ -12,6 +12,22 @@
#define DM_MSG_PREFIX "core-rq"
+/*
+ * One of these is allocated per request.
+ */
+struct dm_rq_target_io {
+ struct mapped_device *md;
+ struct dm_target *ti;
+ struct request *orig, *clone;
+ struct kthread_work work;
+ blk_status_t error;
+ union map_info info;
+ struct dm_stats_aux stats_aux;
+ unsigned long duration_jiffies;
+ unsigned n_sectors;
+ unsigned completed;
+};
+
#define DM_MQ_NR_HW_QUEUES 1
#define DM_MQ_QUEUE_DEPTH 2048
static unsigned dm_mq_nr_hw_queues = DM_MQ_NR_HW_QUEUES;
@@ -527,7 +543,7 @@ int dm_mq_init_request_queue(struct mapped_device *md, struct dm_table *t)
md->tag_set->ops = &dm_mq_ops;
md->tag_set->queue_depth = dm_get_blk_mq_queue_depth();
md->tag_set->numa_node = md->numa_node_id;
- md->tag_set->flags = BLK_MQ_F_SHOULD_MERGE | BLK_MQ_F_SG_MERGE;
+ md->tag_set->flags = BLK_MQ_F_SHOULD_MERGE;
md->tag_set->nr_hw_queues = dm_get_blk_mq_nr_hw_queues();
md->tag_set->driver_data = md;
diff --git a/drivers/md/dm-rq.h b/drivers/md/dm-rq.h
index b39245545229..1eea0da641db 100644
--- a/drivers/md/dm-rq.h
+++ b/drivers/md/dm-rq.h
@@ -17,22 +17,6 @@
struct mapped_device;
/*
- * One of these is allocated per request.
- */
-struct dm_rq_target_io {
- struct mapped_device *md;
- struct dm_target *ti;
- struct request *orig, *clone;
- struct kthread_work work;
- blk_status_t error;
- union map_info info;
- struct dm_stats_aux stats_aux;
- unsigned long duration_jiffies;
- unsigned n_sectors;
- unsigned completed;
-};
-
-/*
* For request-based dm - the bio clones we allocate are embedded in these
* structs.
*
diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c
index 36805b12661e..a168963b757d 100644
--- a/drivers/md/dm-snap.c
+++ b/drivers/md/dm-snap.c
@@ -2338,13 +2338,6 @@ static int origin_map(struct dm_target *ti, struct bio *bio)
return do_origin(o->dev, bio);
}
-static long origin_dax_direct_access(struct dm_target *ti, pgoff_t pgoff,
- long nr_pages, void **kaddr, pfn_t *pfn)
-{
- DMWARN("device does not support dax.");
- return -EIO;
-}
-
/*
* Set the target "max_io_len" field to the minimum of all the snapshots'
* chunk sizes.
@@ -2404,7 +2397,6 @@ static struct target_type origin_target = {
.postsuspend = origin_postsuspend,
.status = origin_status,
.iterate_devices = origin_iterate_devices,
- .direct_access = origin_dax_direct_access,
};
static struct target_type snapshot_target = {
diff --git a/drivers/md/dm-switch.c b/drivers/md/dm-switch.c
index fae35caf3672..8a0f057b8122 100644
--- a/drivers/md/dm-switch.c
+++ b/drivers/md/dm-switch.c
@@ -61,8 +61,7 @@ static struct switch_ctx *alloc_switch_ctx(struct dm_target *ti, unsigned nr_pat
{
struct switch_ctx *sctx;
- sctx = kzalloc(sizeof(struct switch_ctx) + nr_paths * sizeof(struct switch_path),
- GFP_KERNEL);
+ sctx = kzalloc(struct_size(sctx, path_list, nr_paths), GFP_KERNEL);
if (!sctx)
return NULL;
diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c
index 4b1be754cc41..ba9481f1bf3c 100644
--- a/drivers/md/dm-table.c
+++ b/drivers/md/dm-table.c
@@ -1698,14 +1698,6 @@ static int device_is_not_random(struct dm_target *ti, struct dm_dev *dev,
return q && !blk_queue_add_random(q);
}
-static int queue_supports_sg_merge(struct dm_target *ti, struct dm_dev *dev,
- sector_t start, sector_t len, void *data)
-{
- struct request_queue *q = bdev_get_queue(dev->bdev);
-
- return q && !test_bit(QUEUE_FLAG_NO_SG_MERGE, &q->queue_flags);
-}
-
static bool dm_table_all_devices_attribute(struct dm_table *t,
iterate_devices_callout_fn func)
{
@@ -1902,11 +1894,6 @@ void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q,
if (!dm_table_supports_write_zeroes(t))
q->limits.max_write_zeroes_sectors = 0;
- if (dm_table_all_devices_attribute(t, queue_supports_sg_merge))
- blk_queue_flag_clear(QUEUE_FLAG_NO_SG_MERGE, q);
- else
- blk_queue_flag_set(QUEUE_FLAG_NO_SG_MERGE, q);
-
dm_table_verify_integrity(t);
/*
diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c
index e83b63608262..fcd887703f95 100644
--- a/drivers/md/dm-thin.c
+++ b/drivers/md/dm-thin.c
@@ -3283,6 +3283,13 @@ static int pool_ctr(struct dm_target *ti, unsigned argc, char **argv)
as.argc = argc;
as.argv = argv;
+ /* make sure metadata and data are different devices */
+ if (!strcmp(argv[0], argv[1])) {
+ ti->error = "Error setting metadata or data device";
+ r = -EINVAL;
+ goto out_unlock;
+ }
+
/*
* Set default pool features.
*/
@@ -4167,6 +4174,12 @@ static int thin_ctr(struct dm_target *ti, unsigned argc, char **argv)
tc->sort_bio_list = RB_ROOT;
if (argc == 3) {
+ if (!strcmp(argv[0], argv[2])) {
+ ti->error = "Error setting origin device";
+ r = -EINVAL;
+ goto bad_origin_dev;
+ }
+
r = dm_get_device(ti, argv[2], FMODE_READ, &origin_dev);
if (r) {
ti->error = "Error opening origin device";
@@ -4227,7 +4240,6 @@ static int thin_ctr(struct dm_target *ti, unsigned argc, char **argv)
if (tc->pool->pf.discard_enabled) {
ti->discards_supported = true;
ti->num_discard_bios = 1;
- ti->split_discard_bios = false;
}
mutex_unlock(&dm_thin_pool_table.mutex);
diff --git a/drivers/md/dm-verity-fec.c b/drivers/md/dm-verity-fec.c
index 0ce04e5b4afb..b634fa23f4c4 100644
--- a/drivers/md/dm-verity-fec.c
+++ b/drivers/md/dm-verity-fec.c
@@ -73,7 +73,7 @@ static u8 *fec_read_parity(struct dm_verity *v, u64 rsb, int index,
*offset = (unsigned)(position - (block << v->data_dev_block_bits));
res = dm_bufio_read(v->fec->bufio, v->fec->start + block, buf);
- if (unlikely(IS_ERR(res))) {
+ if (IS_ERR(res)) {
DMERR("%s: FEC %llu: parity read failed (block %llu): %ld",
v->data_dev->name, (unsigned long long)rsb,
(unsigned long long)(v->fec->start + block),
@@ -163,7 +163,7 @@ static int fec_decode_bufs(struct dm_verity *v, struct dm_verity_fec_io *fio,
dm_bufio_release(buf);
par = fec_read_parity(v, rsb, block_offset, &offset, &buf);
- if (unlikely(IS_ERR(par)))
+ if (IS_ERR(par))
return PTR_ERR(par);
}
}
@@ -253,7 +253,7 @@ static int fec_read_bufs(struct dm_verity *v, struct dm_verity_io *io,
}
bbuf = dm_bufio_read(bufio, block, &buf);
- if (unlikely(IS_ERR(bbuf))) {
+ if (IS_ERR(bbuf)) {
DMWARN_LIMIT("%s: FEC %llu: read failed (%llu): %ld",
v->data_dev->name,
(unsigned long long)rsb,
diff --git a/drivers/md/dm-writecache.c b/drivers/md/dm-writecache.c
index 2b8cee35e4d5..f7822875589e 100644
--- a/drivers/md/dm-writecache.c
+++ b/drivers/md/dm-writecache.c
@@ -1859,7 +1859,7 @@ static int writecache_ctr(struct dm_target *ti, unsigned argc, char **argv)
goto bad;
}
- wc->writeback_wq = alloc_workqueue("writecache-writeabck", WQ_MEM_RECLAIM, 1);
+ wc->writeback_wq = alloc_workqueue("writecache-writeback", WQ_MEM_RECLAIM, 1);
if (!wc->writeback_wq) {
r = -ENOMEM;
ti->error = "Could not allocate writeback workqueue";
diff --git a/drivers/md/dm-zoned-target.c b/drivers/md/dm-zoned-target.c
index 6af5babe6837..8865c1709e16 100644
--- a/drivers/md/dm-zoned-target.c
+++ b/drivers/md/dm-zoned-target.c
@@ -727,7 +727,6 @@ static int dmz_ctr(struct dm_target *ti, unsigned int argc, char **argv)
ti->per_io_data_size = sizeof(struct dmz_bioctx);
ti->flush_supported = true;
ti->discards_supported = true;
- ti->split_discard_bios = true;
/* The exposed capacity is the number of chunks that can be mapped */
ti->len = (sector_t)dmz_nr_chunks(dmz->metadata) << dev->zone_nr_sectors_shift;
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index 515e6af9bed2..68d24056d0b1 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -158,9 +158,6 @@ struct table_device {
struct dm_dev dm_dev;
};
-static struct kmem_cache *_rq_tio_cache;
-static struct kmem_cache *_rq_cache;
-
/*
* Bio-based DM's mempools' reserved IOs set by the user.
*/
@@ -222,20 +219,11 @@ static unsigned dm_get_numa_node(void)
static int __init local_init(void)
{
- int r = -ENOMEM;
-
- _rq_tio_cache = KMEM_CACHE(dm_rq_target_io, 0);
- if (!_rq_tio_cache)
- return r;
-
- _rq_cache = kmem_cache_create("dm_old_clone_request", sizeof(struct request),
- __alignof__(struct request), 0, NULL);
- if (!_rq_cache)
- goto out_free_rq_tio_cache;
+ int r;
r = dm_uevent_init();
if (r)
- goto out_free_rq_cache;
+ return r;
deferred_remove_workqueue = alloc_workqueue("kdmremove", WQ_UNBOUND, 1);
if (!deferred_remove_workqueue) {
@@ -257,10 +245,6 @@ out_free_workqueue:
destroy_workqueue(deferred_remove_workqueue);
out_uevent_exit:
dm_uevent_exit();
-out_free_rq_cache:
- kmem_cache_destroy(_rq_cache);
-out_free_rq_tio_cache:
- kmem_cache_destroy(_rq_tio_cache);
return r;
}
@@ -270,8 +254,6 @@ static void local_exit(void)
flush_scheduled_work();
destroy_workqueue(deferred_remove_workqueue);
- kmem_cache_destroy(_rq_cache);
- kmem_cache_destroy(_rq_tio_cache);
unregister_blkdev(_major, _name);
dm_uevent_exit();
@@ -1478,17 +1460,10 @@ static unsigned get_num_write_zeroes_bios(struct dm_target *ti)
return ti->num_write_zeroes_bios;
}
-typedef bool (*is_split_required_fn)(struct dm_target *ti);
-
-static bool is_split_required_for_discard(struct dm_target *ti)
-{
- return ti->split_discard_bios;
-}
-
static int __send_changing_extent_only(struct clone_info *ci, struct dm_target *ti,
- unsigned num_bios, bool is_split_required)
+ unsigned num_bios)
{
- unsigned len;
+ unsigned len = ci->sector_count;
/*
* Even though the device advertised support for this type of
@@ -1499,11 +1474,6 @@ static int __send_changing_extent_only(struct clone_info *ci, struct dm_target *
if (!num_bios)
return -EOPNOTSUPP;
- if (!is_split_required)
- len = min((sector_t)ci->sector_count, max_io_len_target_boundary(ci->sector, ti));
- else
- len = min((sector_t)ci->sector_count, max_io_len(ci->sector, ti));
-
__send_duplicate_bios(ci, ti, num_bios, &len);
ci->sector += len;
@@ -1514,23 +1484,38 @@ static int __send_changing_extent_only(struct clone_info *ci, struct dm_target *
static int __send_discard(struct clone_info *ci, struct dm_target *ti)
{
- return __send_changing_extent_only(ci, ti, get_num_discard_bios(ti),
- is_split_required_for_discard(ti));
+ return __send_changing_extent_only(ci, ti, get_num_discard_bios(ti));
}
static int __send_secure_erase(struct clone_info *ci, struct dm_target *ti)
{
- return __send_changing_extent_only(ci, ti, get_num_secure_erase_bios(ti), false);
+ return __send_changing_extent_only(ci, ti, get_num_secure_erase_bios(ti));
}
static int __send_write_same(struct clone_info *ci, struct dm_target *ti)
{
- return __send_changing_extent_only(ci, ti, get_num_write_same_bios(ti), false);
+ return __send_changing_extent_only(ci, ti, get_num_write_same_bios(ti));
}
static int __send_write_zeroes(struct clone_info *ci, struct dm_target *ti)
{
- return __send_changing_extent_only(ci, ti, get_num_write_zeroes_bios(ti), false);
+ return __send_changing_extent_only(ci, ti, get_num_write_zeroes_bios(ti));
+}
+
+static bool is_abnormal_io(struct bio *bio)
+{
+ bool r = false;
+
+ switch (bio_op(bio)) {
+ case REQ_OP_DISCARD:
+ case REQ_OP_SECURE_ERASE:
+ case REQ_OP_WRITE_SAME:
+ case REQ_OP_WRITE_ZEROES:
+ r = true;
+ break;
+ }
+
+ return r;
}
static bool __process_abnormal_io(struct clone_info *ci, struct dm_target *ti,
@@ -1565,7 +1550,7 @@ static int __split_and_process_non_flush(struct clone_info *ci)
if (!dm_target_is_valid(ti))
return -EIO;
- if (unlikely(__process_abnormal_io(ci, ti, &r)))
+ if (__process_abnormal_io(ci, ti, &r))
return r;
len = min_t(sector_t, max_io_len(ci->sector, ti), ci->sector_count);
@@ -1601,13 +1586,6 @@ static blk_qc_t __split_and_process_bio(struct mapped_device *md,
blk_qc_t ret = BLK_QC_T_NONE;
int error = 0;
- if (unlikely(!map)) {
- bio_io_error(bio);
- return ret;
- }
-
- blk_queue_split(md->queue, &bio);
-
init_clone_info(&ci, md, map, bio);
if (bio->bi_opf & REQ_PREFLUSH) {
@@ -1675,18 +1653,13 @@ static blk_qc_t __split_and_process_bio(struct mapped_device *md,
* Optimized variant of __split_and_process_bio that leverages the
* fact that targets that use it do _not_ have a need to split bios.
*/
-static blk_qc_t __process_bio(struct mapped_device *md,
- struct dm_table *map, struct bio *bio)
+static blk_qc_t __process_bio(struct mapped_device *md, struct dm_table *map,
+ struct bio *bio, struct dm_target *ti)
{
struct clone_info ci;
blk_qc_t ret = BLK_QC_T_NONE;
int error = 0;
- if (unlikely(!map)) {
- bio_io_error(bio);
- return ret;
- }
-
init_clone_info(&ci, md, map, bio);
if (bio->bi_opf & REQ_PREFLUSH) {
@@ -1704,21 +1677,11 @@ static blk_qc_t __process_bio(struct mapped_device *md,
error = __send_empty_flush(&ci);
/* dec_pending submits any data associated with flush */
} else {
- struct dm_target *ti = md->immutable_target;
struct dm_target_io *tio;
- /*
- * Defend against IO still getting in during teardown
- * - as was seen for a time with nvme-fcloop
- */
- if (WARN_ON_ONCE(!ti || !dm_target_is_valid(ti))) {
- error = -EIO;
- goto out;
- }
-
ci.bio = bio;
ci.sector_count = bio_sectors(bio);
- if (unlikely(__process_abnormal_io(&ci, ti, &error)))
+ if (__process_abnormal_io(&ci, ti, &error))
goto out;
tio = alloc_tio(&ci, ti, 0, GFP_NOIO);
@@ -1730,11 +1693,55 @@ out:
return ret;
}
+static void dm_queue_split(struct mapped_device *md, struct dm_target *ti, struct bio **bio)
+{
+ unsigned len, sector_count;
+
+ sector_count = bio_sectors(*bio);
+ len = min_t(sector_t, max_io_len((*bio)->bi_iter.bi_sector, ti), sector_count);
+
+ if (sector_count > len) {
+ struct bio *split = bio_split(*bio, len, GFP_NOIO, &md->queue->bio_split);
+
+ bio_chain(split, *bio);
+ trace_block_split(md->queue, split, (*bio)->bi_iter.bi_sector);
+ generic_make_request(*bio);
+ *bio = split;
+ }
+}
+
static blk_qc_t dm_process_bio(struct mapped_device *md,
struct dm_table *map, struct bio *bio)
{
+ blk_qc_t ret = BLK_QC_T_NONE;
+ struct dm_target *ti = md->immutable_target;
+
+ if (unlikely(!map)) {
+ bio_io_error(bio);
+ return ret;
+ }
+
+ if (!ti) {
+ ti = dm_table_find_target(map, bio->bi_iter.bi_sector);
+ if (unlikely(!ti || !dm_target_is_valid(ti))) {
+ bio_io_error(bio);
+ return ret;
+ }
+ }
+
+ /*
+ * If in ->make_request_fn we need to use blk_queue_split(), otherwise
+ * queue_limits for abnormal requests (e.g. discard, writesame, etc)
+ * won't be imposed.
+ */
+ if (current->bio_list) {
+ blk_queue_split(md->queue, &bio);
+ if (!is_abnormal_io(bio))
+ dm_queue_split(md, ti, &bio);
+ }
+
if (dm_get_md_type(md) == DM_TYPE_NVME_BIO_BASED)
- return __process_bio(md, map, bio);
+ return __process_bio(md, map, bio, ti);
else
return __split_and_process_bio(md, map, bio);
}
diff --git a/drivers/md/md-linear.c b/drivers/md/md-linear.c
index d45c697c0ebe..5998d78aa189 100644
--- a/drivers/md/md-linear.c
+++ b/drivers/md/md-linear.c
@@ -96,8 +96,7 @@ static struct linear_conf *linear_conf(struct mddev *mddev, int raid_disks)
int i, cnt;
bool discard_supported = false;
- conf = kzalloc (sizeof (*conf) + raid_disks*sizeof(struct dev_info),
- GFP_KERNEL);
+ conf = kzalloc(struct_size(conf, disks, raid_disks), GFP_KERNEL);
if (!conf)
return NULL;
diff --git a/drivers/md/persistent-data/dm-block-manager.c b/drivers/md/persistent-data/dm-block-manager.c
index 492a3f8ac119..3972232b8037 100644
--- a/drivers/md/persistent-data/dm-block-manager.c
+++ b/drivers/md/persistent-data/dm-block-manager.c
@@ -462,7 +462,7 @@ int dm_bm_read_lock(struct dm_block_manager *bm, dm_block_t b,
int r;
p = dm_bufio_read(bm->bufio, b, (struct dm_buffer **) result);
- if (unlikely(IS_ERR(p)))
+ if (IS_ERR(p))
return PTR_ERR(p);
aux = dm_bufio_get_aux_data(to_buffer(*result));
@@ -498,7 +498,7 @@ int dm_bm_write_lock(struct dm_block_manager *bm,
return -EPERM;
p = dm_bufio_read(bm->bufio, b, (struct dm_buffer **) result);
- if (unlikely(IS_ERR(p)))
+ if (IS_ERR(p))
return PTR_ERR(p);
aux = dm_bufio_get_aux_data(to_buffer(*result));
@@ -531,7 +531,7 @@ int dm_bm_read_try_lock(struct dm_block_manager *bm,
int r;
p = dm_bufio_get(bm->bufio, b, (struct dm_buffer **) result);
- if (unlikely(IS_ERR(p)))
+ if (IS_ERR(p))
return PTR_ERR(p);
if (unlikely(!p))
return -EWOULDBLOCK;
@@ -567,7 +567,7 @@ int dm_bm_write_lock_zero(struct dm_block_manager *bm,
return -EPERM;
p = dm_bufio_new(bm->bufio, b, (struct dm_buffer **) result);
- if (unlikely(IS_ERR(p)))
+ if (IS_ERR(p))
return PTR_ERR(p);
memset(p, 0, dm_bm_block_size(bm));
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index fa47249fa3e4..fdf451aac369 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -1603,11 +1603,9 @@ static void raid1_error(struct mddev *mddev, struct md_rdev *rdev)
return;
}
set_bit(Blocked, &rdev->flags);
- if (test_and_clear_bit(In_sync, &rdev->flags)) {
+ if (test_and_clear_bit(In_sync, &rdev->flags))
mddev->degraded++;
- set_bit(Faulty, &rdev->flags);
- } else
- set_bit(Faulty, &rdev->flags);
+ set_bit(Faulty, &rdev->flags);
spin_unlock_irqrestore(&conf->device_lock, flags);
/*
* if recovery is running, make sure it aborts.
@@ -2120,13 +2118,14 @@ static void process_checks(struct r1bio *r1_bio)
struct page **spages = get_resync_pages(sbio)->pages;
struct bio_vec *bi;
int page_len[RESYNC_PAGES] = { 0 };
+ struct bvec_iter_all iter_all;
if (sbio->bi_end_io != end_sync_read)
continue;
/* Now we can 'fixup' the error value */
sbio->bi_status = 0;
- bio_for_each_segment_all(bi, sbio, j)
+ bio_for_each_segment_all(bi, sbio, j, iter_all)
page_len[j] = bi->bv_len;
if (!status) {
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index abb5d382f64d..3b6880dd648d 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -3939,6 +3939,8 @@ static int raid10_run(struct mddev *mddev)
set_bit(MD_RECOVERY_RUNNING, &mddev->recovery);
mddev->sync_thread = md_register_thread(md_do_sync, mddev,
"reshape");
+ if (!mddev->sync_thread)
+ goto out_free_conf;
}
return 0;
@@ -4670,7 +4672,6 @@ read_more:
atomic_inc(&r10_bio->remaining);
read_bio->bi_next = NULL;
generic_make_request(read_bio);
- sector_nr += nr_sectors;
sectors_done += nr_sectors;
if (sector_nr <= last)
goto read_more;
diff --git a/drivers/md/raid5-log.h b/drivers/md/raid5-log.h
index bfb811407061..43c714a8798c 100644
--- a/drivers/md/raid5-log.h
+++ b/drivers/md/raid5-log.h
@@ -45,6 +45,7 @@ extern void ppl_stripe_write_finished(struct stripe_head *sh);
extern int ppl_modify_log(struct r5conf *conf, struct md_rdev *rdev, bool add);
extern void ppl_quiesce(struct r5conf *conf, int quiesce);
extern int ppl_handle_flush_request(struct r5l_log *log, struct bio *bio);
+extern struct md_sysfs_entry ppl_write_hint;
static inline bool raid5_has_log(struct r5conf *conf)
{
diff --git a/drivers/md/raid5-ppl.c b/drivers/md/raid5-ppl.c
index 3a7c36326589..17e9e7d51097 100644
--- a/drivers/md/raid5-ppl.c
+++ b/drivers/md/raid5-ppl.c
@@ -16,11 +16,11 @@
#include <linux/blkdev.h>
#include <linux/slab.h>
#include <linux/crc32c.h>
-#include <linux/flex_array.h>
#include <linux/async_tx.h>
#include <linux/raid/md_p.h>
#include "md.h"
#include "raid5.h"
+#include "raid5-log.h"
/*
* PPL consists of a 4KB header (struct ppl_header) and at least 128KB for
@@ -116,6 +116,8 @@ struct ppl_conf {
/* stripes to retry if failed to allocate io_unit */
struct list_head no_mem_stripes;
spinlock_t no_mem_stripes_lock;
+
+ unsigned short write_hint;
};
struct ppl_log {
@@ -165,7 +167,7 @@ ops_run_partial_parity(struct stripe_head *sh, struct raid5_percpu *percpu,
struct dma_async_tx_descriptor *tx)
{
int disks = sh->disks;
- struct page **srcs = flex_array_get(percpu->scribble, 0);
+ struct page **srcs = percpu->scribble;
int count = 0, pd_idx = sh->pd_idx, i;
struct async_submit_ctl submit;
@@ -196,8 +198,7 @@ ops_run_partial_parity(struct stripe_head *sh, struct raid5_percpu *percpu,
}
init_async_submit(&submit, ASYNC_TX_FENCE|ASYNC_TX_XOR_ZERO_DST, tx,
- NULL, sh, flex_array_get(percpu->scribble, 0)
- + sizeof(struct page *) * (sh->disks + 2));
+ NULL, sh, (void *) (srcs + sh->disks + 2));
if (count == 1)
tx = async_memcpy(sh->ppl_page, srcs[0], 0, 0, PAGE_SIZE,
@@ -476,6 +477,7 @@ static void ppl_submit_iounit(struct ppl_io_unit *io)
bio_set_dev(bio, log->rdev->bdev);
bio->bi_iter.bi_sector = log->next_io_sector;
bio_add_page(bio, io->header_page, PAGE_SIZE, 0);
+ bio->bi_write_hint = ppl_conf->write_hint;
pr_debug("%s: log->current_io_sector: %llu\n", __func__,
(unsigned long long)log->next_io_sector);
@@ -505,6 +507,7 @@ static void ppl_submit_iounit(struct ppl_io_unit *io)
bio = bio_alloc_bioset(GFP_NOIO, BIO_MAX_PAGES,
&ppl_conf->bs);
bio->bi_opf = prev->bi_opf;
+ bio->bi_write_hint = prev->bi_write_hint;
bio_copy_dev(bio, prev);
bio->bi_iter.bi_sector = bio_end_sector(prev);
bio_add_page(bio, sh->ppl_page, PAGE_SIZE, 0);
@@ -1409,6 +1412,7 @@ int ppl_init_log(struct r5conf *conf)
atomic64_set(&ppl_conf->seq, 0);
INIT_LIST_HEAD(&ppl_conf->no_mem_stripes);
spin_lock_init(&ppl_conf->no_mem_stripes_lock);
+ ppl_conf->write_hint = RWF_WRITE_LIFE_NOT_SET;
if (!mddev->external) {
ppl_conf->signature = ~crc32c_le(~0, mddev->uuid, sizeof(mddev->uuid));
@@ -1503,3 +1507,60 @@ int ppl_modify_log(struct r5conf *conf, struct md_rdev *rdev, bool add)
return ret;
}
+
+static ssize_t
+ppl_write_hint_show(struct mddev *mddev, char *buf)
+{
+ size_t ret = 0;
+ struct r5conf *conf;
+ struct ppl_conf *ppl_conf = NULL;
+
+ spin_lock(&mddev->lock);
+ conf = mddev->private;
+ if (conf && raid5_has_ppl(conf))
+ ppl_conf = conf->log_private;
+ ret = sprintf(buf, "%d\n", ppl_conf ? ppl_conf->write_hint : 0);
+ spin_unlock(&mddev->lock);
+
+ return ret;
+}
+
+static ssize_t
+ppl_write_hint_store(struct mddev *mddev, const char *page, size_t len)
+{
+ struct r5conf *conf;
+ struct ppl_conf *ppl_conf;
+ int err = 0;
+ unsigned short new;
+
+ if (len >= PAGE_SIZE)
+ return -EINVAL;
+ if (kstrtou16(page, 10, &new))
+ return -EINVAL;
+
+ err = mddev_lock(mddev);
+ if (err)
+ return err;
+
+ conf = mddev->private;
+ if (!conf) {
+ err = -ENODEV;
+ } else if (raid5_has_ppl(conf)) {
+ ppl_conf = conf->log_private;
+ if (!ppl_conf)
+ err = -EINVAL;
+ else
+ ppl_conf->write_hint = new;
+ } else {
+ err = -EINVAL;
+ }
+
+ mddev_unlock(mddev);
+
+ return err ?: len;
+}
+
+struct md_sysfs_entry
+ppl_write_hint = __ATTR(ppl_write_hint, S_IRUGO | S_IWUSR,
+ ppl_write_hint_show,
+ ppl_write_hint_store);
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index cecea901ab8c..c033bfcb209e 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -54,7 +54,6 @@
#include <linux/slab.h>
#include <linux/ratelimit.h>
#include <linux/nodemask.h>
-#include <linux/flex_array.h>
#include <trace/events/block.h>
#include <linux/list_sort.h>
@@ -1394,22 +1393,16 @@ static void ops_complete_compute(void *stripe_head_ref)
}
/* return a pointer to the address conversion region of the scribble buffer */
-static addr_conv_t *to_addr_conv(struct stripe_head *sh,
- struct raid5_percpu *percpu, int i)
+static struct page **to_addr_page(struct raid5_percpu *percpu, int i)
{
- void *addr;
-
- addr = flex_array_get(percpu->scribble, i);
- return addr + sizeof(struct page *) * (sh->disks + 2);
+ return percpu->scribble + i * percpu->scribble_obj_size;
}
/* return a pointer to the address conversion region of the scribble buffer */
-static struct page **to_addr_page(struct raid5_percpu *percpu, int i)
+static addr_conv_t *to_addr_conv(struct stripe_head *sh,
+ struct raid5_percpu *percpu, int i)
{
- void *addr;
-
- addr = flex_array_get(percpu->scribble, i);
- return addr;
+ return (void *) (to_addr_page(percpu, i) + sh->disks + 2);
}
static struct dma_async_tx_descriptor *
@@ -2238,21 +2231,23 @@ static int grow_stripes(struct r5conf *conf, int num)
* calculate over all devices (not just the data blocks), using zeros in place
* of the P and Q blocks.
*/
-static struct flex_array *scribble_alloc(int num, int cnt, gfp_t flags)
+static int scribble_alloc(struct raid5_percpu *percpu,
+ int num, int cnt, gfp_t flags)
{
- struct flex_array *ret;
- size_t len;
+ size_t obj_size =
+ sizeof(struct page *) * (num+2) +
+ sizeof(addr_conv_t) * (num+2);
+ void *scribble;
- len = sizeof(struct page *) * (num+2) + sizeof(addr_conv_t) * (num+2);
- ret = flex_array_alloc(len, cnt, flags);
- if (!ret)
- return NULL;
- /* always prealloc all elements, so no locking is required */
- if (flex_array_prealloc(ret, 0, cnt, flags)) {
- flex_array_free(ret);
- return NULL;
- }
- return ret;
+ scribble = kvmalloc_array(cnt, obj_size, flags);
+ if (!scribble)
+ return -ENOMEM;
+
+ kvfree(percpu->scribble);
+
+ percpu->scribble = scribble;
+ percpu->scribble_obj_size = obj_size;
+ return 0;
}
static int resize_chunks(struct r5conf *conf, int new_disks, int new_sectors)
@@ -2270,23 +2265,18 @@ static int resize_chunks(struct r5conf *conf, int new_disks, int new_sectors)
return 0;
mddev_suspend(conf->mddev);
get_online_cpus();
+
for_each_present_cpu(cpu) {
struct raid5_percpu *percpu;
- struct flex_array *scribble;
percpu = per_cpu_ptr(conf->percpu, cpu);
- scribble = scribble_alloc(new_disks,
- new_sectors / STRIPE_SECTORS,
- GFP_NOIO);
-
- if (scribble) {
- flex_array_free(percpu->scribble);
- percpu->scribble = scribble;
- } else {
- err = -ENOMEM;
+ err = scribble_alloc(percpu, new_disks,
+ new_sectors / STRIPE_SECTORS,
+ GFP_NOIO);
+ if (err)
break;
- }
}
+
put_online_cpus();
mddev_resume(conf->mddev);
if (!err) {
@@ -6660,6 +6650,7 @@ static struct attribute *raid5_attrs[] = {
&raid5_skip_copy.attr,
&raid5_rmw_level.attr,
&r5c_journal_mode.attr,
+ &ppl_write_hint.attr,
NULL,
};
static struct attribute_group raid5_attrs_group = {
@@ -6742,25 +6733,26 @@ raid5_size(struct mddev *mddev, sector_t sectors, int raid_disks)
static void free_scratch_buffer(struct r5conf *conf, struct raid5_percpu *percpu)
{
safe_put_page(percpu->spare_page);
- if (percpu->scribble)
- flex_array_free(percpu->scribble);
percpu->spare_page = NULL;
+ kvfree(percpu->scribble);
percpu->scribble = NULL;
}
static int alloc_scratch_buffer(struct r5conf *conf, struct raid5_percpu *percpu)
{
- if (conf->level == 6 && !percpu->spare_page)
+ if (conf->level == 6 && !percpu->spare_page) {
percpu->spare_page = alloc_page(GFP_KERNEL);
- if (!percpu->scribble)
- percpu->scribble = scribble_alloc(max(conf->raid_disks,
- conf->previous_raid_disks),
- max(conf->chunk_sectors,
- conf->prev_chunk_sectors)
- / STRIPE_SECTORS,
- GFP_KERNEL);
-
- if (!percpu->scribble || (conf->level == 6 && !percpu->spare_page)) {
+ if (!percpu->spare_page)
+ return -ENOMEM;
+ }
+
+ if (scribble_alloc(percpu,
+ max(conf->raid_disks,
+ conf->previous_raid_disks),
+ max(conf->chunk_sectors,
+ conf->prev_chunk_sectors)
+ / STRIPE_SECTORS,
+ GFP_KERNEL)) {
free_scratch_buffer(conf, percpu);
return -ENOMEM;
}
@@ -7402,6 +7394,8 @@ static int raid5_run(struct mddev *mddev)
set_bit(MD_RECOVERY_RUNNING, &mddev->recovery);
mddev->sync_thread = md_register_thread(md_do_sync, mddev,
"reshape");
+ if (!mddev->sync_thread)
+ goto abort;
}
/* Ok, everything is just fine now */
diff --git a/drivers/md/raid5.h b/drivers/md/raid5.h
index 8474c224127b..cf991f13403e 100644
--- a/drivers/md/raid5.h
+++ b/drivers/md/raid5.h
@@ -638,10 +638,11 @@ struct r5conf {
/* per cpu variables */
struct raid5_percpu {
struct page *spare_page; /* Used when checking P/Q in raid6 */
- struct flex_array *scribble; /* space for constructing buffer
- * lists and performing address
- * conversions
- */
+ void *scribble; /* space for constructing buffer
+ * lists and performing address
+ * conversions
+ */
+ int scribble_obj_size;
} __percpu *percpu;
int scribble_disks;
int scribble_sectors;
diff --git a/drivers/media/cec/cec-api.c b/drivers/media/cec/cec-api.c
index 391b6fd483e1..156a0d76ab2a 100644
--- a/drivers/media/cec/cec-api.c
+++ b/drivers/media/cec/cec-api.c
@@ -38,6 +38,7 @@ static __poll_t cec_poll(struct file *filp,
struct cec_adapter *adap = fh->adap;
__poll_t res = 0;
+ poll_wait(filp, &fh->wait, poll);
if (!cec_is_registered(adap))
return EPOLLERR | EPOLLHUP;
mutex_lock(&adap->lock);
@@ -48,7 +49,6 @@ static __poll_t cec_poll(struct file *filp,
res |= EPOLLIN | EPOLLRDNORM;
if (fh->total_queued_events)
res |= EPOLLPRI;
- poll_wait(filp, &fh->wait, poll);
mutex_unlock(&adap->lock);
return res;
}
diff --git a/drivers/media/common/saa7146/saa7146_fops.c b/drivers/media/common/saa7146/saa7146_fops.c
index c790ae264464..be4355a4c126 100644
--- a/drivers/media/common/saa7146/saa7146_fops.c
+++ b/drivers/media/common/saa7146/saa7146_fops.c
@@ -105,7 +105,7 @@ void saa7146_buffer_finish(struct saa7146_dev *dev,
}
q->curr->vb.state = state;
- v4l2_get_timestamp(&q->curr->vb.ts);
+ q->curr->vb.ts = ktime_get_ns();
wake_up(&q->curr->vb.done);
q->curr = NULL;
diff --git a/drivers/media/common/saa7146/saa7146_i2c.c b/drivers/media/common/saa7146/saa7146_i2c.c
index 3feddc52c446..df9ebe2a168c 100644
--- a/drivers/media/common/saa7146/saa7146_i2c.c
+++ b/drivers/media/common/saa7146/saa7146_i2c.c
@@ -54,10 +54,7 @@ static int saa7146_i2c_msg_prepare(const struct i2c_msg *m, int num, __le32 *op)
/* loop through all messages */
for(i = 0; i < num; i++) {
- /* insert the address of the i2c-slave.
- note: we get 7 bit i2c-addresses,
- so we have to perform a translation */
- addr = (m[i].addr*2) + ( (0 != (m[i].flags & I2C_M_RD)) ? 1 : 0);
+ addr = i2c_8bit_addr_from_msg(&m[i]);
h1 = op_count/3; h2 = op_count%3;
op[h1] |= cpu_to_le32( (u8)addr << ((3-h2)*8));
op[h1] |= cpu_to_le32(SAA7146_I2C_START << ((3-h2)*2));
diff --git a/drivers/media/common/saa7146/saa7146_video.c b/drivers/media/common/saa7146/saa7146_video.c
index f90aa8109663..a0f0b5eef0bd 100644
--- a/drivers/media/common/saa7146/saa7146_video.c
+++ b/drivers/media/common/saa7146/saa7146_video.c
@@ -796,7 +796,7 @@ static int vidioc_s_fmt_vid_overlay(struct file *file, void *__fh, struct v4l2_f
return -EFAULT;
}
- /* vv->ov.fh is used to indicate that we have valid overlay informations, too */
+ /* vv->ov.fh is used to indicate that we have valid overlay information, too */
vv->ov.fh = fh;
/* check if our current overlay is active */
diff --git a/drivers/media/common/siano/sms-cards.c b/drivers/media/common/siano/sms-cards.c
index af6b2268db61..e238c9bc17d3 100644
--- a/drivers/media/common/siano/sms-cards.c
+++ b/drivers/media/common/siano/sms-cards.c
@@ -311,7 +311,7 @@ int sms_board_led_feedback(struct smscore_device_t *coredev, int led)
int board_id = smscore_get_board_id(coredev);
struct sms_board *board = sms_get_board(board_id);
- /* dont touch GPIO if LEDs are already set */
+ /* don't touch GPIO if LEDs are already set */
if (smscore_led_state(coredev, -1) == led)
return 0;
diff --git a/drivers/media/common/siano/smscoreapi.h b/drivers/media/common/siano/smscoreapi.h
index eb58853008c9..476fa7a8b152 100644
--- a/drivers/media/common/siano/smscoreapi.h
+++ b/drivers/media/common/siano/smscoreapi.h
@@ -750,7 +750,7 @@ struct sms_stats {
u32 num_of_corrected_mpe_tlbs;/* Number of MPE tables which were
corrected by MPE RS decoding */
/* Common params */
- u32 ber_error_count; /* Number of errornous SYNC bits. */
+ u32 ber_error_count; /* Number of erroneous SYNC bits. */
u32 ber_bit_count; /* Total number of SYNC bits. */
/* Interface information */
diff --git a/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c b/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c
index d9a590ae7545..07e0629af8ed 100644
--- a/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c
+++ b/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c
@@ -246,6 +246,10 @@ bool tpg_s_fourcc(struct tpg_data *tpg, u32 fourcc)
case V4L2_PIX_FMT_YUV555:
case V4L2_PIX_FMT_YUV565:
case V4L2_PIX_FMT_YUV32:
+ case V4L2_PIX_FMT_AYUV32:
+ case V4L2_PIX_FMT_XYUV32:
+ case V4L2_PIX_FMT_VUYA32:
+ case V4L2_PIX_FMT_VUYX32:
tpg->color_enc = TGP_COLOR_ENC_YCBCR;
break;
case V4L2_PIX_FMT_YUV420M:
@@ -372,6 +376,10 @@ bool tpg_s_fourcc(struct tpg_data *tpg, u32 fourcc)
case V4L2_PIX_FMT_ARGB32:
case V4L2_PIX_FMT_ABGR32:
case V4L2_PIX_FMT_YUV32:
+ case V4L2_PIX_FMT_AYUV32:
+ case V4L2_PIX_FMT_XYUV32:
+ case V4L2_PIX_FMT_VUYA32:
+ case V4L2_PIX_FMT_VUYX32:
case V4L2_PIX_FMT_HSV32:
tpg->twopixelsize[0] = 2 * 4;
break;
@@ -1267,10 +1275,12 @@ static void gen_twopix(struct tpg_data *tpg,
case V4L2_PIX_FMT_RGB32:
case V4L2_PIX_FMT_XRGB32:
case V4L2_PIX_FMT_HSV32:
+ case V4L2_PIX_FMT_XYUV32:
alpha = 0;
/* fall through */
case V4L2_PIX_FMT_YUV32:
case V4L2_PIX_FMT_ARGB32:
+ case V4L2_PIX_FMT_AYUV32:
buf[0][offset] = alpha;
buf[0][offset + 1] = r_y_h;
buf[0][offset + 2] = g_u_s;
@@ -1278,9 +1288,11 @@ static void gen_twopix(struct tpg_data *tpg,
break;
case V4L2_PIX_FMT_BGR32:
case V4L2_PIX_FMT_XBGR32:
+ case V4L2_PIX_FMT_VUYX32:
alpha = 0;
/* fall through */
case V4L2_PIX_FMT_ABGR32:
+ case V4L2_PIX_FMT_VUYA32:
buf[0][offset] = b_v;
buf[0][offset + 1] = g_u_s;
buf[0][offset + 2] = r_y_h;
diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c
index 70e8c3366f9c..15b6b9c0a2e4 100644
--- a/drivers/media/common/videobuf2/videobuf2-core.c
+++ b/drivers/media/common/videobuf2/videobuf2-core.c
@@ -499,9 +499,9 @@ static int __vb2_queue_free(struct vb2_queue *q, unsigned int buffers)
pr_info(" buf_init: %u buf_cleanup: %u buf_prepare: %u buf_finish: %u\n",
vb->cnt_buf_init, vb->cnt_buf_cleanup,
vb->cnt_buf_prepare, vb->cnt_buf_finish);
- pr_info(" buf_queue: %u buf_done: %u buf_request_complete: %u\n",
- vb->cnt_buf_queue, vb->cnt_buf_done,
- vb->cnt_buf_request_complete);
+ pr_info(" buf_out_validate: %u buf_queue: %u buf_done: %u buf_request_complete: %u\n",
+ vb->cnt_buf_out_validate, vb->cnt_buf_queue,
+ vb->cnt_buf_done, vb->cnt_buf_request_complete);
pr_info(" alloc: %u put: %u prepare: %u finish: %u mmap: %u\n",
vb->cnt_mem_alloc, vb->cnt_mem_put,
vb->cnt_mem_prepare, vb->cnt_mem_finish,
@@ -934,7 +934,7 @@ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state)
/* sync buffers */
for (plane = 0; plane < vb->num_planes; ++plane)
call_void_memop(vb, finish, vb->planes[plane].mem_priv);
- vb->synced = false;
+ vb->synced = 0;
}
spin_lock_irqsave(&q->done_lock, flags);
@@ -1041,6 +1041,7 @@ static int __prepare_userptr(struct vb2_buffer *vb)
if (vb->planes[plane].mem_priv) {
if (!reacquired) {
reacquired = true;
+ vb->copied_timestamp = 0;
call_void_vb_qop(vb, buf_cleanup, vb);
}
call_void_memop(vb, put_userptr, vb->planes[plane].mem_priv);
@@ -1165,6 +1166,7 @@ static int __prepare_dmabuf(struct vb2_buffer *vb)
if (!reacquired) {
reacquired = true;
+ vb->copied_timestamp = 0;
call_void_vb_qop(vb, buf_cleanup, vb);
}
@@ -1196,6 +1198,9 @@ static int __prepare_dmabuf(struct vb2_buffer *vb)
* userspace knows sooner rather than later if the dma-buf map fails.
*/
for (plane = 0; plane < vb->num_planes; ++plane) {
+ if (vb->planes[plane].dbuf_mapped)
+ continue;
+
ret = call_memop(vb, map_dmabuf, vb->planes[plane].mem_priv);
if (ret) {
dprintk(1, "failed to map dmabuf for plane %d\n",
@@ -1274,6 +1279,14 @@ static int __buf_prepare(struct vb2_buffer *vb)
return 0;
WARN_ON(vb->synced);
+ if (q->is_output) {
+ ret = call_vb_qop(vb, buf_out_validate, vb);
+ if (ret) {
+ dprintk(1, "buffer validation failed\n");
+ return ret;
+ }
+ }
+
vb->state = VB2_BUF_STATE_PREPARING;
switch (q->memory) {
@@ -1302,8 +1315,8 @@ static int __buf_prepare(struct vb2_buffer *vb)
for (plane = 0; plane < vb->num_planes; ++plane)
call_void_memop(vb, prepare, vb->planes[plane].mem_priv);
- vb->synced = true;
- vb->prepared = true;
+ vb->synced = 1;
+ vb->prepared = 1;
vb->state = orig_state;
return 0;
@@ -1520,6 +1533,14 @@ int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb,
return -EINVAL;
}
+ if (q->is_output && !vb->prepared) {
+ ret = call_vb_qop(vb, buf_out_validate, vb);
+ if (ret) {
+ dprintk(1, "buffer validation failed\n");
+ return ret;
+ }
+ }
+
media_request_object_init(&vb->req_obj);
/* Make sure the request is in a safe state for updating. */
@@ -1750,7 +1771,6 @@ EXPORT_SYMBOL_GPL(vb2_wait_for_all_buffers);
static void __vb2_dqbuf(struct vb2_buffer *vb)
{
struct vb2_queue *q = vb->vb2_queue;
- unsigned int i;
/* nothing to do if the buffer is already dequeued */
if (vb->state == VB2_BUF_STATE_DEQUEUED)
@@ -1758,14 +1778,6 @@ static void __vb2_dqbuf(struct vb2_buffer *vb)
vb->state = VB2_BUF_STATE_DEQUEUED;
- /* unmap DMABUF buffer */
- if (q->memory == VB2_MEMORY_DMABUF)
- for (i = 0; i < vb->num_planes; ++i) {
- if (!vb->planes[i].dbuf_mapped)
- continue;
- call_void_memop(vb, unmap_dmabuf, vb->planes[i].mem_priv);
- vb->planes[i].dbuf_mapped = 0;
- }
call_void_bufop(q, init_buffer, vb);
}
@@ -1792,7 +1804,7 @@ int vb2_core_dqbuf(struct vb2_queue *q, unsigned int *pindex, void *pb,
}
call_void_vb_qop(vb, buf_finish, vb);
- vb->prepared = false;
+ vb->prepared = 0;
if (pindex)
*pindex = vb->index;
@@ -1916,12 +1928,12 @@ static void __vb2_queue_cancel(struct vb2_queue *q)
for (plane = 0; plane < vb->num_planes; ++plane)
call_void_memop(vb, finish,
vb->planes[plane].mem_priv);
- vb->synced = false;
+ vb->synced = 0;
}
if (vb->prepared) {
call_void_vb_qop(vb, buf_finish, vb);
- vb->prepared = false;
+ vb->prepared = 0;
}
__vb2_dqbuf(vb);
@@ -1932,6 +1944,7 @@ static void __vb2_queue_cancel(struct vb2_queue *q)
if (vb->request)
media_request_put(vb->request);
vb->request = NULL;
+ vb->copied_timestamp = 0;
}
}
@@ -2278,6 +2291,8 @@ __poll_t vb2_core_poll(struct vb2_queue *q, struct file *file,
if (q->is_output && !(req_events & (EPOLLOUT | EPOLLWRNORM)))
return 0;
+ poll_wait(file, &q->done_wq, wait);
+
/*
* Start file I/O emulator only if streaming API has not been used yet.
*/
@@ -2329,8 +2344,6 @@ __poll_t vb2_core_poll(struct vb2_queue *q, struct file *file,
*/
if (q->last_buffer_dequeued)
return EPOLLIN | EPOLLRDNORM;
-
- poll_wait(file, &q->done_wq, wait);
}
/*
diff --git a/drivers/media/common/videobuf2/videobuf2-dma-contig.c b/drivers/media/common/videobuf2/videobuf2-dma-contig.c
index aff0ab7bf83d..82389aead6ed 100644
--- a/drivers/media/common/videobuf2/videobuf2-dma-contig.c
+++ b/drivers/media/common/videobuf2/videobuf2-dma-contig.c
@@ -439,42 +439,14 @@ static void vb2_dc_put_userptr(void *buf_priv)
set_page_dirty_lock(pages[i]);
sg_free_table(sgt);
kfree(sgt);
+ } else {
+ dma_unmap_resource(buf->dev, buf->dma_addr, buf->size,
+ buf->dma_dir, 0);
}
vb2_destroy_framevec(buf->vec);
kfree(buf);
}
-/*
- * For some kind of reserved memory there might be no struct page available,
- * so all that can be done to support such 'pages' is to try to convert
- * pfn to dma address or at the last resort just assume that
- * dma address == physical address (like it has been assumed in earlier version
- * of videobuf2-dma-contig
- */
-
-#ifdef __arch_pfn_to_dma
-static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev, unsigned long pfn)
-{
- return (dma_addr_t)__arch_pfn_to_dma(dev, pfn);
-}
-#elif defined(__pfn_to_bus)
-static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev, unsigned long pfn)
-{
- return (dma_addr_t)__pfn_to_bus(pfn);
-}
-#elif defined(__pfn_to_phys)
-static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev, unsigned long pfn)
-{
- return (dma_addr_t)__pfn_to_phys(pfn);
-}
-#else
-static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev, unsigned long pfn)
-{
- /* really, we cannot do anything better at this point */
- return (dma_addr_t)(pfn) << PAGE_SHIFT;
-}
-#endif
-
static void *vb2_dc_get_userptr(struct device *dev, unsigned long vaddr,
unsigned long size, enum dma_data_direction dma_dir)
{
@@ -528,7 +500,12 @@ static void *vb2_dc_get_userptr(struct device *dev, unsigned long vaddr,
for (i = 1; i < n_pages; i++)
if (nums[i-1] + 1 != nums[i])
goto fail_pfnvec;
- buf->dma_addr = vb2_dc_pfn_to_dma(buf->dev, nums[0]);
+ buf->dma_addr = dma_map_resource(buf->dev,
+ __pfn_to_phys(nums[0]), size, buf->dma_dir, 0);
+ if (dma_mapping_error(buf->dev, buf->dma_addr)) {
+ ret = -ENOMEM;
+ goto fail_pfnvec;
+ }
goto out;
}
diff --git a/drivers/media/common/videobuf2/videobuf2-dma-sg.c b/drivers/media/common/videobuf2/videobuf2-dma-sg.c
index 015e737095cd..270c3162fdcb 100644
--- a/drivers/media/common/videobuf2/videobuf2-dma-sg.c
+++ b/drivers/media/common/videobuf2/videobuf2-dma-sg.c
@@ -3,7 +3,7 @@
*
* Copyright (C) 2010 Samsung Electronics
*
- * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ * Author: Andrzej Pietrasiewicz <andrzejtp2010@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -67,7 +67,7 @@ static int vb2_dma_sg_alloc_compacted(struct vb2_dma_sg_buf *buf,
int i;
order = get_order(size);
- /* Dont over allocate*/
+ /* Don't over allocate*/
if ((PAGE_SIZE << order) > size)
order--;
diff --git a/drivers/media/common/videobuf2/videobuf2-memops.c b/drivers/media/common/videobuf2/videobuf2-memops.c
index 89e51989332b..c4a85be48ac2 100644
--- a/drivers/media/common/videobuf2/videobuf2-memops.c
+++ b/drivers/media/common/videobuf2/videobuf2-memops.c
@@ -121,7 +121,7 @@ static void vb2_common_vm_close(struct vm_area_struct *vma)
}
/*
- * vb2_common_vm_ops - common vm_ops used for tracking refcount of mmaped
+ * vb2_common_vm_ops - common vm_ops used for tracking refcount of mmapped
* video buffers
*/
const struct vm_operations_struct vb2_common_vm_ops = {
diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c
index 3a0ca2f9854f..d09dee20e421 100644
--- a/drivers/media/common/videobuf2/videobuf2-v4l2.c
+++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c
@@ -143,7 +143,7 @@ static void __copy_timestamp(struct vb2_buffer *vb, const void *pb)
* and the timecode field and flag if needed.
*/
if (q->copy_timestamp)
- vb->timestamp = timeval_to_ns(&b->timestamp);
+ vb->timestamp = v4l2_timeval_to_ns(&b->timestamp);
vbuf->flags |= b->flags & V4L2_BUF_FLAG_TIMECODE;
if (b->flags & V4L2_BUF_FLAG_TIMECODE)
vbuf->timecode = b->timecode;
@@ -409,6 +409,15 @@ static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct media_device *md
*/
if (WARN_ON(!q->ops->buf_request_complete))
return -EINVAL;
+ /*
+ * Make sure this op is implemented by the driver for the output queue.
+ * It's easy to forget this callback, but is it important to correctly
+ * validate the 'field' value at QBUF time.
+ */
+ if (WARN_ON((q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT ||
+ q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) &&
+ !q->ops->buf_out_validate))
+ return -EINVAL;
if (vb->state != VB2_BUF_STATE_DEQUEUED) {
dprintk(1, "%s: buffer is not in dequeued state\n", opname);
@@ -567,7 +576,7 @@ static int __fill_vb2_buffer(struct vb2_buffer *vb, struct vb2_plane *planes)
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
unsigned int plane;
- if (!vb->vb2_queue->is_output || !vb->vb2_queue->copy_timestamp)
+ if (!vb->vb2_queue->copy_timestamp)
vb->timestamp = 0;
for (plane = 0; plane < vb->num_planes; ++plane) {
@@ -589,6 +598,19 @@ static const struct vb2_buf_ops v4l2_buf_ops = {
.copy_timestamp = __copy_timestamp,
};
+int vb2_find_timestamp(const struct vb2_queue *q, u64 timestamp,
+ unsigned int start_idx)
+{
+ unsigned int i;
+
+ for (i = start_idx; i < q->num_buffers; i++)
+ if (q->bufs[i]->copied_timestamp &&
+ q->bufs[i]->timestamp == timestamp)
+ return i;
+ return -1;
+}
+EXPORT_SYMBOL_GPL(vb2_find_timestamp);
+
/*
* vb2_querybuf() - query video buffer information
* @q: videobuf queue
@@ -846,16 +868,14 @@ EXPORT_SYMBOL_GPL(vb2_queue_release);
__poll_t vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait)
{
struct video_device *vfd = video_devdata(file);
- __poll_t req_events = poll_requested_events(wait);
__poll_t res = 0;
if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) {
struct v4l2_fh *fh = file->private_data;
+ poll_wait(file, &fh->wait, wait);
if (v4l2_event_pending(fh))
res = EPOLLPRI;
- else if (req_events & EPOLLPRI)
- poll_wait(file, &fh->wait, wait);
}
return res | vb2_core_poll(q, file, wait);
diff --git a/drivers/media/dvb-core/dmxdev.c b/drivers/media/dvb-core/dmxdev.c
index 1544e8cef564..f14a872d1268 100644
--- a/drivers/media/dvb-core/dmxdev.c
+++ b/drivers/media/dvb-core/dmxdev.c
@@ -1195,13 +1195,13 @@ static __poll_t dvb_demux_poll(struct file *file, poll_table *wait)
struct dmxdev_filter *dmxdevfilter = file->private_data;
__poll_t mask = 0;
+ poll_wait(file, &dmxdevfilter->buffer.queue, wait);
+
if ((!dmxdevfilter) || dmxdevfilter->dev->exit)
return EPOLLERR;
if (dvb_vb2_is_streaming(&dmxdevfilter->vb2_ctx))
return dvb_vb2_poll(&dmxdevfilter->vb2_ctx, file, wait);
- poll_wait(file, &dmxdevfilter->buffer.queue, wait);
-
if (dmxdevfilter->state != DMXDEV_STATE_GO &&
dmxdevfilter->state != DMXDEV_STATE_DONE &&
dmxdevfilter->state != DMXDEV_STATE_TIMEDOUT)
@@ -1346,13 +1346,13 @@ static __poll_t dvb_dvr_poll(struct file *file, poll_table *wait)
dprintk("%s\n", __func__);
+ poll_wait(file, &dmxdev->dvr_buffer.queue, wait);
+
if (dmxdev->exit)
return EPOLLERR;
if (dvb_vb2_is_streaming(&dmxdev->dvr_vb2_ctx))
return dvb_vb2_poll(&dmxdev->dvr_vb2_ctx, file, wait);
- poll_wait(file, &dmxdev->dvr_buffer.queue, wait);
-
if (((file->f_flags & O_ACCMODE) == O_RDONLY) ||
dmxdev->may_do_mmap) {
if (dmxdev->dvr_buffer.error)
diff --git a/drivers/media/dvb-core/dvb_ca_en50221.c b/drivers/media/dvb-core/dvb_ca_en50221.c
index 4d371cea0d5d..ebf1e3b03819 100644
--- a/drivers/media/dvb-core/dvb_ca_en50221.c
+++ b/drivers/media/dvb-core/dvb_ca_en50221.c
@@ -1797,6 +1797,8 @@ static __poll_t dvb_ca_en50221_io_poll(struct file *file, poll_table *wait)
dprintk("%s\n", __func__);
+ poll_wait(file, &ca->wait_queue, wait);
+
if (dvb_ca_en50221_io_read_condition(ca, &result, &slot) == 1)
mask |= EPOLLIN;
@@ -1804,9 +1806,6 @@ static __poll_t dvb_ca_en50221_io_poll(struct file *file, poll_table *wait)
if (mask)
return mask;
- /* wait for something to happen */
- poll_wait(file, &ca->wait_queue, wait);
-
if (dvb_ca_en50221_io_read_condition(ca, &result, &slot) == 1)
mask |= EPOLLIN;
diff --git a/drivers/media/dvb-core/dvb_frontend.c b/drivers/media/dvb-core/dvb_frontend.c
index 27a1d4a98d73..fbdb4ecc7c50 100644
--- a/drivers/media/dvb-core/dvb_frontend.c
+++ b/drivers/media/dvb-core/dvb_frontend.c
@@ -1596,7 +1596,7 @@ static bool is_dvbv3_delsys(u32 delsys)
*
* Provides emulation for delivery systems that are compatible with the old
* DVBv3 call. Among its usages, it provices support for ISDB-T, and allows
- * using a DVB-S2 only frontend just like it were a DVB-S, if the frontent
+ * using a DVB-S2 only frontend just like it were a DVB-S, if the frontend
* parameters are compatible with DVB-S spec.
*/
static int emulate_delivery_system(struct dvb_frontend *fe, u32 delsys)
diff --git a/drivers/media/dvb-core/dvbdev.c b/drivers/media/dvb-core/dvbdev.c
index b7171bf094fb..4a5834a1c3b7 100644
--- a/drivers/media/dvb-core/dvbdev.c
+++ b/drivers/media/dvb-core/dvbdev.c
@@ -898,7 +898,7 @@ EXPORT_SYMBOL(dvb_unregister_adapter);
/* if the miracle happens and "generic_usercopy()" is included into
the kernel, then this can vanish. please don't make the mistake and
- define this as video_usercopy(). this will introduce a dependecy
+ define this as video_usercopy(). this will introduce a dependency
to the v4l "videodev.o" module, which is unnecessary for some
cards (ie. the budget dvb-cards don't need the v4l module...) */
int dvb_usercopy(struct file *file,
diff --git a/drivers/media/dvb-frontends/cxd2841er.c b/drivers/media/dvb-frontends/cxd2841er.c
index c98093ed3dd7..8acf0b91b437 100644
--- a/drivers/media/dvb-frontends/cxd2841er.c
+++ b/drivers/media/dvb-frontends/cxd2841er.c
@@ -2947,7 +2947,7 @@ static int cxd2841er_sleep_tc_to_active_t(struct cxd2841er_priv *priv,
((priv->flags & CXD2841ER_ASCOT) ? 0x01 : 0x00), 0x01);
/* Set SLV-T Bank : 0x18 */
cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x18);
- /* Pre-RS BER moniter setting */
+ /* Pre-RS BER monitor setting */
cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x36, 0x40, 0x07);
/* FEC Auto Recovery setting */
cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x30, 0x01, 0x01);
diff --git a/drivers/media/dvb-frontends/dib0090.c b/drivers/media/dvb-frontends/dib0090.c
index 4813a88eb9f7..18c41cfef8d6 100644
--- a/drivers/media/dvb-frontends/dib0090.c
+++ b/drivers/media/dvb-frontends/dib0090.c
@@ -2459,7 +2459,7 @@ static int dib0090_tune(struct dvb_frontend *fe)
state->current_standard = state->fe->dtv_property_cache.delivery_system;
ret = 20;
- state->calibrate = CAPTRIM_CAL; /* captrim serach now */
+ state->calibrate = CAPTRIM_CAL; /* captrim search now */
}
else if (*tune_state == CT_TUNER_STEP_0) { /* Warning : because of captrim cal, if you change this step, change it also in _cal.c file because it is the step following captrim cal state machine */
diff --git a/drivers/media/dvb-frontends/dib7000m.c b/drivers/media/dvb-frontends/dib7000m.c
index b79358d09de6..389db9077ad5 100644
--- a/drivers/media/dvb-frontends/dib7000m.c
+++ b/drivers/media/dvb-frontends/dib7000m.c
@@ -369,7 +369,7 @@ static int dib7000m_sad_calib(struct dib7000m_state *state)
{
/* internal */
-// dib7000m_write_word(state, 928, (3 << 14) | (1 << 12) | (524 << 0)); // sampling clock of the SAD is writting in set_bandwidth
+// dib7000m_write_word(state, 928, (3 << 14) | (1 << 12) | (524 << 0)); // sampling clock of the SAD is writing in set_bandwidth
dib7000m_write_word(state, 929, (0 << 1) | (0 << 0));
dib7000m_write_word(state, 930, 776); // 0.625*3.3 / 4096
@@ -928,7 +928,7 @@ static void dib7000m_set_channel(struct dib7000m_state *state, struct dtv_fronte
}
state->div_sync_wait = (value * 3) / 2 + 32; // add 50% SFN margin + compensate for one DVSY-fifo TODO
- /* deactive the possibility of diversity reception if extended interleave - not for 7000MC */
+ /* deactivate the possibility of diversity reception if extended interleave - not for 7000MC */
/* P_dvsy_sync_mode = 0, P_dvsy_sync_enable=1, P_dvcb_comb_mode=2 */
if (1 == 1 || state->revision > 0x4000)
state->div_force_off = 0;
diff --git a/drivers/media/dvb-frontends/dib7000p.c b/drivers/media/dvb-frontends/dib7000p.c
index 2818e8def1b3..f8040f6def62 100644
--- a/drivers/media/dvb-frontends/dib7000p.c
+++ b/drivers/media/dvb-frontends/dib7000p.c
@@ -94,7 +94,7 @@ enum dib7000p_power_mode {
DIB7000P_POWER_INTERFACE_ONLY,
};
-/* dib7090 specific fonctions */
+/* dib7090 specific functions */
static int dib7090_set_output_mode(struct dvb_frontend *fe, int mode);
static int dib7090_set_diversity_in(struct dvb_frontend *fe, int onoff);
static void dib7090_setDibTxMux(struct dib7000p_state *state, int mode);
@@ -319,7 +319,7 @@ static void dib7000p_set_adc_state(struct dib7000p_state *state, enum dibx000_ad
dib7000p_write_word(state, 1925, reg | (1 << 4) | (1 << 2)); /* en_slowAdc = 1 & reset_sladc = 1 */
- reg = dib7000p_read_word(state, 1925); /* read acces to make it works... strange ... */
+ reg = dib7000p_read_word(state, 1925); /* read access to make it works... strange ... */
msleep(200);
dib7000p_write_word(state, 1925, reg & ~(1 << 4)); /* en_slowAdc = 1 & reset_sladc = 0 */
@@ -1101,7 +1101,7 @@ static void dib7000p_set_channel(struct dib7000p_state *state,
else
state->div_sync_wait = (value * 3) / 2 + state->cfg.diversity_delay;
- /* deactive the possibility of diversity reception if extended interleaver */
+ /* deactivate the possibility of diversity reception if extended interleaver */
state->div_force_off = !1 && ch->transmission_mode != TRANSMISSION_MODE_8K;
dib7000p_set_diversity_in(&state->demod, state->div_state);
@@ -2378,7 +2378,7 @@ static int dib7090_tuner_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msg[]
}
}
- if (apb_address != 0) /* R/W acces via APB */
+ if (apb_address != 0) /* R/W access via APB */
return dib7090p_rw_on_apb(i2c_adap, msg, num, apb_address);
else /* R/W access via SERPAR */
return w7090p_tuner_rw_serpar(i2c_adap, msg, num);
diff --git a/drivers/media/dvb-frontends/dib8000.c b/drivers/media/dvb-frontends/dib8000.c
index 3c3f8cb14845..85c429cce23e 100644
--- a/drivers/media/dvb-frontends/dib8000.c
+++ b/drivers/media/dvb-frontends/dib8000.c
@@ -564,7 +564,7 @@ static int dib8000_set_adc_state(struct dib8000_state *state, enum dibx000_adc_s
dib8000_write_word(state, 1925, reg |
(1<<4) | (1<<2));
- /* read acces to make it works... strange ... */
+ /* read access to make it works... strange ... */
reg = dib8000_read_word(state, 1925);
msleep(20);
/* en_slowAdc = 1 & reset_sladc = 0 */
@@ -1091,7 +1091,7 @@ static int dib8000_reset(struct dvb_frontend *fe)
if ((state->revision != 0x8090) &&
(dib8000_set_output_mode(fe, OUTMODE_HIGH_Z) != 0))
- dprintk("OUTPUT_MODE could not be resetted.\n");
+ dprintk("OUTPUT_MODE could not be reset.\n");
state->current_agc = NULL;
@@ -1867,7 +1867,7 @@ static int dib8096p_tuner_xfer(struct i2c_adapter *i2c_adap,
}
}
- if (apb_address != 0) /* R/W acces via APB */
+ if (apb_address != 0) /* R/W access via APB */
return dib8096p_rw_on_apb(i2c_adap, msg, num, apb_address);
else /* R/W access via SERPAR */
return dib8096p_tuner_rw_serpar(i2c_adap, msg, num);
@@ -3082,7 +3082,7 @@ static int dib8000_tune(struct dvb_frontend *fe)
state->autosearch_state = AS_DONE;
*tune_state = CT_DEMOD_STOP; /* else we are done here */
break;
- case 2: /* Succes */
+ case 2: /* Success */
state->status = FE_STATUS_FFT_SUCCESS; /* signal to the upper layer, that there was a channel found and the parameters can be read */
*tune_state = CT_DEMOD_STEP_3;
if (state->autosearch_state == AS_SEARCHING_GUARD)
@@ -3193,10 +3193,10 @@ static int dib8000_tune(struct dvb_frontend *fe)
case CT_DEMOD_STEP_6: /* (36) if there is an input (diversity) */
if ((state->fe[1] != NULL) && (state->output_mode != OUTMODE_DIVERSITY)) {
- /* if there is a diversity fe in input and this fe is has not already failled : wait here until this this fe has succedeed or failled */
+ /* if there is a diversity fe in input and this fe is has not already failed : wait here until this this fe has succedeed or failed */
if (dib8000_get_status(state->fe[1]) <= FE_STATUS_STD_SUCCESS) /* Something is locked on the input fe */
*tune_state = CT_DEMOD_STEP_8; /* go for mpeg */
- else if (dib8000_get_status(state->fe[1]) >= FE_STATUS_TUNE_TIME_TOO_SHORT) { /* fe in input failled also, break the current one */
+ else if (dib8000_get_status(state->fe[1]) >= FE_STATUS_TUNE_TIME_TOO_SHORT) { /* fe in input failed also, break the current one */
*tune_state = CT_DEMOD_STOP; /* else we are done here ; step 8 will close the loops and exit */
dib8000_viterbi_state(state, 1); /* start viterbi chandec */
dib8000_set_isdbt_loop_params(state, LOOP_TUNE_2);
diff --git a/drivers/media/dvb-frontends/dib9000.c b/drivers/media/dvb-frontends/dib9000.c
index 0183fb1346ef..1875da07c150 100644
--- a/drivers/media/dvb-frontends/dib9000.c
+++ b/drivers/media/dvb-frontends/dib9000.c
@@ -1020,7 +1020,7 @@ static int dib9000_risc_apb_access_read(struct dib9000_state *state, u32 address
if (address >= 1024 || !state->platform.risc.fw_is_running)
return -EINVAL;
- /* dprintk( "APB access thru rd fw %d %x\n", address, attribute); */
+ /* dprintk( "APB access through rd fw %d %x\n", address, attribute); */
mb[0] = (u16) address;
mb[1] = len / 2;
@@ -1050,7 +1050,7 @@ static int dib9000_risc_apb_access_write(struct dib9000_state *state, u32 addres
if (len > 18)
return -EINVAL;
- /* dprintk( "APB access thru wr fw %d %x\n", address, attribute); */
+ /* dprintk( "APB access through wr fw %d %x\n", address, attribute); */
mb[0] = (u16)address;
for (i = 0; i + 1 < len; i += 2)
diff --git a/drivers/media/dvb-frontends/drx39xyj/drx_dap_fasi.h b/drivers/media/dvb-frontends/drx39xyj/drx_dap_fasi.h
index 23ae72468025..739dc5590fa4 100644
--- a/drivers/media/dvb-frontends/drx39xyj/drx_dap_fasi.h
+++ b/drivers/media/dvb-frontends/drx39xyj/drx_dap_fasi.h
@@ -67,7 +67,7 @@
* (2 bytes). The DAP can operate in 3 modes:
* (1) only short
* (2) only long
-* (3) both long and short but short preferred and long only when necesarry
+* (3) both long and short but short preferred and long only when necessary
*
* These modes must be selected compile time via compile switches.
* Compile switch settings for the different modes:
@@ -112,14 +112,14 @@
* + single master mode means no use of repeated starts
* + multi master mode means use of repeated starts
* Default is single master.
-* Default can be overriden by setting the compile switch DRXDAP_SINGLE_MASTER.
+* Default can be overridden by setting the compile switch DRXDAP_SINGLE_MASTER.
*
* Slave:
* Single/multi master selected via the flags in the FASI protocol.
* + single master means remember memory address between i2c packets
* + multimaster means flush memory address between i2c packets
* Default is single master, DAP FASI changes multi-master setting silently
-* into single master setting. This cannot be overrriden.
+* into single master setting. This cannot be overridden.
*
*/
/* set default */
@@ -139,7 +139,7 @@
* In single master mode, data can be written by sending the register address
* first, then two or four bytes of data in the next packet.
* Because the device address plus a register address equals five bytes,
-* the mimimum chunk size must be five.
+* the minimum chunk size must be five.
* If ten-bit I2C device addresses are used, the minimum chunk size must be six,
* because the I2C device address will then occupy two bytes when writing.
*
diff --git a/drivers/media/dvb-frontends/drx39xyj/drx_driver.h b/drivers/media/dvb-frontends/drx39xyj/drx_driver.h
index 1ec20eecc433..15f7e58c5a30 100644
--- a/drivers/media/dvb-frontends/drx39xyj/drx_driver.h
+++ b/drivers/media/dvb-frontends/drx39xyj/drx_driver.h
@@ -94,7 +94,7 @@ int drxbsp_i2c_term(void);
* \param r_count The number of bytes to read
* \param r_data The array to read the data from
* \return int Return status.
-* \retval 0 Succes.
+* \retval 0 Success.
* \retval -EIO Failure.
* \retval -EINVAL Parameter 'wcount' is not zero but parameter
* 'wdata' contains NULL.
@@ -986,7 +986,7 @@ struct drx_filter_info {
* \struct struct drx_channel * \brief The set of parameters describing a single channel.
*
* Used by DRX_CTRL_SET_CHANNEL and DRX_CTRL_GET_CHANNEL.
-* Only certain fields need to be used for a specfic standard.
+* Only certain fields need to be used for a specific standard.
*
*/
struct drx_channel {
@@ -1606,7 +1606,7 @@ struct drx_version_list {
DRX_AUD_I2S_MATRIX_B_MONO,
/*< B sound only, stereo or mono */
DRX_AUD_I2S_MATRIX_STEREO,
- /*< A+B sound, transparant */
+ /*< A+B sound, transparent */
DRX_AUD_I2S_MATRIX_MONO /*< A+B mixed to mono sum, (L+R)/2 */};
/*
@@ -1870,7 +1870,7 @@ struct drx_reg_dump {
/*< current power management mode */
/* Tuner */
- u8 tuner_port_nr; /*< nr of I2C port to wich tuner is */
+ u8 tuner_port_nr; /*< nr of I2C port to which tuner is */
s32 tuner_min_freq_rf;
/*< minimum RF input frequency, in kHz */
s32 tuner_max_freq_rf;
diff --git a/drivers/media/dvb-frontends/drx39xyj/drxj.c b/drivers/media/dvb-frontends/drx39xyj/drxj.c
index 551b7d65fa66..a6876fa48753 100644
--- a/drivers/media/dvb-frontends/drx39xyj/drxj.c
+++ b/drivers/media/dvb-frontends/drx39xyj/drxj.c
@@ -380,10 +380,10 @@ DEFINES
*/
/*****************************************************************************/
-/* Audio block 0x103 is write only. To avoid shadowing in driver accessing */
-/* RAM adresses directly. This must be READ ONLY to avoid problems. */
-/* Writing to the interface adresses is more than only writing the RAM */
-/* locations */
+/* Audio block 0x103 is write only. To avoid shadowing in driver accessing */
+/* RAM addresses directly. This must be READ ONLY to avoid problems. */
+/* Writing to the interface addresses are more than only writing the RAM */
+/* locations */
/*****************************************************************************/
/*
* \brief RAM location of MODUS registers
@@ -656,8 +656,8 @@ static struct drxj_data drxj_data_g = {
false, /* flag: true=bypass */
ATV_TOP_VID_PEAK__PRE, /* shadow of ATV_TOP_VID_PEAK__A */
ATV_TOP_NOISE_TH__PRE, /* shadow of ATV_TOP_NOISE_TH__A */
- true, /* flag CVBS ouput enable */
- false, /* flag SIF ouput enable */
+ true, /* flag CVBS output enable */
+ false, /* flag SIF output enable */
DRXJ_SIF_ATTENUATION_0DB, /* current SIF att setting */
{ /* qam_rf_agc_cfg */
DRX_STANDARD_ITU_B, /* standard */
@@ -832,7 +832,7 @@ static struct drx_common_attr drxj_default_comm_attr_g = {
false, /* If true mirror frequency spectrum */
{
/* MPEG output configuration */
- true, /* If true, enable MPEG ouput */
+ true, /* If true, enable MPEG output */
false, /* If true, insert RS byte */
false, /* If true, parallel out otherwise serial */
false, /* If true, invert DATA signals */
@@ -848,7 +848,7 @@ static struct drx_common_attr drxj_default_comm_attr_g = {
DRX_MPEG_STR_WIDTH_1 /* MPEG Start width in clock cycles */
},
/* Initilisations below can be omitted, they require no user input and
- are initialy 0, NULL or false. The compiler will initialize them to these
+ are initially 0, NULL or false. The compiler will initialize them to these
values when omitted. */
false, /* is_opened */
@@ -869,7 +869,7 @@ static struct drx_common_attr drxj_default_comm_attr_g = {
DRX_POWER_UP,
/* Tuner */
- 1, /* nr of I2C port to wich tuner is */
+ 1, /* nr of I2C port to which tuner is */
0L, /* minimum RF input frequency, in kHz */
0L, /* maximum RF input frequency, in kHz */
false, /* Rf Agc Polarity */
@@ -1656,7 +1656,7 @@ static int drxdap_fasi_write_block(struct i2c_device_addr *dev_addr,
sequense will be visible: (1) write address {i2c addr,
4 bytes chip address} (2) write data {i2c addr, 4 bytes data }
(3) write address (4) write data etc...
- Address must be rewriten because HI is reset after data transport and
+ Address must be rewritten because HI is reset after data transport and
expects an address.
*/
todo = (block_size < datasize ? block_size : datasize);
@@ -1820,7 +1820,7 @@ static int drxdap_fasi_write_reg32(struct i2c_device_addr *dev_addr,
* \param wdata Data to write
* \param rdata Buffer for data to read
* \return int
-* \retval 0 Succes
+* \retval 0 Success
* \retval -EIO Timeout, I2C error, illegal bank
*
* 16 bits register read modify write access using short addressing format only.
@@ -1897,7 +1897,7 @@ static int drxj_dap_read_modify_write_reg16(struct i2c_device_addr *dev_addr,
* \param addr
* \param data
* \return int
-* \retval 0 Succes
+* \retval 0 Success
* \retval -EIO Timeout, I2C error, illegal bank
*
* 16 bits register read access via audio token ring interface.
@@ -2004,7 +2004,7 @@ static int drxj_dap_read_reg16(struct i2c_device_addr *dev_addr,
* \param addr
* \param data
* \return int
-* \retval 0 Succes
+* \retval 0 Success
* \retval -EIO Timeout, I2C error, illegal bank
*
* 16 bits register write access via audio token ring interface.
@@ -2094,7 +2094,7 @@ static int drxj_dap_write_reg16(struct i2c_device_addr *dev_addr,
* \param datasize size of data buffer in bytes
* \param data pointer to data buffer
* \return int
-* \retval 0 Succes
+* \retval 0 Success
* \retval -EIO Timeout, I2C error, illegal bank
*
*/
@@ -2338,7 +2338,7 @@ hi_command(struct i2c_device_addr *dev_addr, const struct drxj_hi_cmd *cmd, u16
if ((cmd->cmd) == SIO_HI_RA_RAM_CMD_RESET)
msleep(1);
- /* Detect power down to ommit reading result */
+ /* Detect power down to omit reading result */
powerdown_cmd = (bool) ((cmd->cmd == SIO_HI_RA_RAM_CMD_CONFIG) &&
(((cmd->
param5) & SIO_HI_RA_RAM_PAR_5_CFG_SLEEP__M)
@@ -2754,7 +2754,7 @@ ctrl_set_cfg_mpeg_output(struct drx_demod_instance *demod, struct drx_cfg_mpeg_o
common_attr = (struct drx_common_attr *) demod->my_common_attr;
if (cfg_data->enable_mpeg_output == true) {
- /* quick and dirty patch to set MPEG incase current std is not
+ /* quick and dirty patch to set MPEG in case current std is not
producing MPEG */
switch (ext_attr->standard) {
case DRX_STANDARD_8VSB:
@@ -2894,7 +2894,7 @@ ctrl_set_cfg_mpeg_output(struct drx_demod_instance *demod, struct drx_cfg_mpeg_o
break;
default:
break;
- } /* swtich (standard) */
+ } /* switch (standard) */
/* Check insertion of the Reed-Solomon parity bytes */
rc = drxj_dap_read_reg16(dev_addr, FEC_OC_MODE__A, &fec_oc_reg_mode, 0);
@@ -4127,7 +4127,7 @@ rw_error:
* \param datasize size of data buffer in bytes
* \param data pointer to data buffer
* \return int
-* \retval 0 Succes
+* \retval 0 Success
* \retval -EIO Timeout, I2C error, illegal bank
*
*/
@@ -8989,7 +8989,7 @@ qam64auto(struct drx_demod_instance *demod,
((jiffies_to_msecs(jiffies) - start_time) <
(DRXJ_QAM_MAX_WAITTIME + timeout_ofs))
);
- /* Returning control to apllication ... */
+ /* Returning control to application ... */
return 0;
rw_error:
@@ -9309,7 +9309,7 @@ get_qamrs_err_count(struct i2c_device_addr *dev_addr,
return -EINVAL;
/* all reported errors are received in the */
- /* most recently finished measurment period */
+ /* most recently finished measurement period */
/* no of pre RS bit errors */
rc = drxj_dap_read_reg16(dev_addr, FEC_RS_NR_BIT_ERRORS__A, &nr_bit_errors, 0);
if (rc != 0) {
@@ -9689,7 +9689,7 @@ rw_error:
(3) SIF AGC (used to amplify the output signal in case input to low)
The SIF AGC is now coupled to the RF/IF AGCs.
- The SIF AGC is needed for both SIF ouput and the internal SIF signal to
+ The SIF AGC is needed for both SIF output and the internal SIF signal to
the AUD block.
RF and IF AGCs DACs are part of AFE, Video and SIF AGC DACs are part of
@@ -9702,11 +9702,11 @@ rw_error:
later on because of the schedule)
Several HW/SCU "settings" can be used for ATV. The standard selection
- will reset most of these settings. To avoid that the end user apllication
+ will reset most of these settings. To avoid that the end user application
has to perform these settings each time the ATV or FM standards is
selected the driver will shadow these settings. This enables the end user
to perform the settings only once after a drx_open(). The driver must
- write the shadow settings to HW/SCU incase:
+ write the shadow settings to HW/SCU in case:
( setstandard FM/ATV) ||
( settings have changed && FM/ATV standard is active)
The shadow settings will be stored in the device specific data container.
@@ -9908,7 +9908,7 @@ rw_error:
#define IMPULSE_COSINE_ALPHA_0_5 { 2, 0, -2, -2, 2, 5, 2, -10, -20, -14, 20, 74, 125, 145} /*sqrt raised-cosine filter with alpha=0.5 */
#define IMPULSE_COSINE_ALPHA_RO_0_5 { 0, 0, 1, 2, 3, 0, -7, -15, -16, 0, 34, 77, 114, 128} /*full raised-cosine filter with alpha=0.5 (receiver only) */
-/* Coefficients for the nyquist fitler (total: 27 taps) */
+/* Coefficients for the nyquist filter (total: 27 taps) */
#define NYQFILTERLEN 27
static int ctrl_set_oob(struct drx_demod_instance *demod, struct drxoob *oob_param)
diff --git a/drivers/media/dvb-frontends/drx39xyj/drxj.h b/drivers/media/dvb-frontends/drx39xyj/drxj.h
index d3ee1c23bb2f..d62412f71c88 100644
--- a/drivers/media/dvb-frontends/drx39xyj/drxj.h
+++ b/drivers/media/dvb-frontends/drx39xyj/drxj.h
@@ -49,7 +49,7 @@ INCLUDES
#if ((DRXDAP_SINGLE_MASTER == 0) && (DRXDAPFASI_LONG_ADDR_ALLOWED == 0))
#error "Multi master mode and short addressing only is an illegal combination"
*; /* Generate a fatal compiler error to make sure it stops here,
- this is necesarry because not all compilers stop after a #error. */
+ this is necessary because not all compilers stop after a #error. */
#endif
/*-------------------------------------------------------------------------
@@ -203,7 +203,7 @@ struct drxj_agc_status {
* /struct drxjrs_errors
* Available failure information in DRXJ_FEC_RS.
*
-* Container for errors that are received in the most recently finished measurment period
+* Container for errors that are received in the most recently finished measurement period
*
*/
struct drxjrs_errors {
@@ -405,7 +405,7 @@ struct drxj_cfg_atv_output {
*
*/
struct drxj_data {
- /* device capabilties (determined during drx_open()) */
+ /* device capabilities (determined during drx_open()) */
bool has_lna; /*< true if LNA (aka PGA) present */
bool has_oob; /*< true if OOB supported */
bool has_ntsc; /*< true if NTSC supported */
@@ -455,7 +455,7 @@ struct drxj_cfg_atv_output {
/* IQM fs frequecy shift and inversion */
u32 iqm_fs_rate_ofs; /*< frequency shifter setting after setchannel */
- bool pos_image; /*< Ture: positive image */
+ bool pos_image; /*< True: positive image */
/* IQM RC frequecy shift */
u32 iqm_rc_rate_ofs; /*< frequency shifter setting after setchannel */
@@ -468,8 +468,8 @@ struct drxj_cfg_atv_output {
bool phase_correction_bypass;/*< flag: true=bypass */
s16 atv_top_vid_peak; /*< shadow of ATV_TOP_VID_PEAK__A */
u16 atv_top_noise_th; /*< shadow of ATV_TOP_NOISE_TH__A */
- bool enable_cvbs_output; /*< flag CVBS ouput enable */
- bool enable_sif_output; /*< flag SIF ouput enable */
+ bool enable_cvbs_output; /*< flag CVBS output enable */
+ bool enable_sif_output; /*< flag SIF output enable */
enum drxjsif_attenuation sif_attenuation;
/*< current SIF att setting */
/* Agc configuration for QAM and VSB */
diff --git a/drivers/media/dvb-frontends/drxd_firm.c b/drivers/media/dvb-frontends/drxd_firm.c
index 4e1d8905e06a..412871d6636b 100644
--- a/drivers/media/dvb-frontends/drxd_firm.c
+++ b/drivers/media/dvb-frontends/drxd_firm.c
@@ -890,7 +890,7 @@ u8 DRXD_StartDiversityEnd[] = {
/* End demod, combining RF in and diversity in, MPEG TS out */
WR16(B_FE_CF_REG_IMP_VAL__A, 0x0), /* disable impulse noise cruncher */
WR16(B_FE_AD_REG_INVEXT__A, 0x0), /* clock inversion (for sohard board) */
- WR16(B_CP_REG_BR_STR_DEL__A, 10), /* apperently no mb delay matching is best */
+ WR16(B_CP_REG_BR_STR_DEL__A, 10), /* apparently no mb delay matching is best */
WR16(B_EQ_REG_RC_SEL_CAR__A, B_EQ_REG_RC_SEL_CAR_DIV_ON | /* org = 0x81 combining enabled */
B_EQ_REG_RC_SEL_CAR_MEAS_A_CC |
diff --git a/drivers/media/dvb-frontends/drxd_hard.c b/drivers/media/dvb-frontends/drxd_hard.c
index 684d428efb0d..0a5b15bee1d7 100644
--- a/drivers/media/dvb-frontends/drxd_hard.c
+++ b/drivers/media/dvb-frontends/drxd_hard.c
@@ -1144,6 +1144,8 @@ static int EnableAndResetMB(struct drxd_state *state)
static int InitCC(struct drxd_state *state)
{
+ int status = 0;
+
if (state->osc_clock_freq == 0 ||
state->osc_clock_freq > 20000 ||
(state->osc_clock_freq % 4000) != 0) {
@@ -1151,14 +1153,17 @@ static int InitCC(struct drxd_state *state)
return -1;
}
- Write16(state, CC_REG_OSC_MODE__A, CC_REG_OSC_MODE_M20, 0);
- Write16(state, CC_REG_PLL_MODE__A, CC_REG_PLL_MODE_BYPASS_PLL |
- CC_REG_PLL_MODE_PUMP_CUR_12, 0);
- Write16(state, CC_REG_REF_DIVIDE__A, state->osc_clock_freq / 4000, 0);
- Write16(state, CC_REG_PWD_MODE__A, CC_REG_PWD_MODE_DOWN_PLL, 0);
- Write16(state, CC_REG_UPDATE__A, CC_REG_UPDATE_KEY, 0);
+ status |= Write16(state, CC_REG_OSC_MODE__A, CC_REG_OSC_MODE_M20, 0);
+ status |= Write16(state, CC_REG_PLL_MODE__A,
+ CC_REG_PLL_MODE_BYPASS_PLL |
+ CC_REG_PLL_MODE_PUMP_CUR_12, 0);
+ status |= Write16(state, CC_REG_REF_DIVIDE__A,
+ state->osc_clock_freq / 4000, 0);
+ status |= Write16(state, CC_REG_PWD_MODE__A, CC_REG_PWD_MODE_DOWN_PLL,
+ 0);
+ status |= Write16(state, CC_REG_UPDATE__A, CC_REG_UPDATE_KEY, 0);
- return 0;
+ return status;
}
static int ResetECOD(struct drxd_state *state)
@@ -1312,7 +1317,10 @@ static int SC_SendCommand(struct drxd_state *state, u16 cmd)
int status = 0, ret;
u16 errCode;
- Write16(state, SC_RA_RAM_CMD__A, cmd, 0);
+ status = Write16(state, SC_RA_RAM_CMD__A, cmd, 0);
+ if (status < 0)
+ return status;
+
SC_WaitForReady(state);
ret = Read16(state, SC_RA_RAM_CMD_ADDR__A, &errCode, 0);
@@ -1339,9 +1347,9 @@ static int SC_ProcStartCommand(struct drxd_state *state,
break;
}
SC_WaitForReady(state);
- Write16(state, SC_RA_RAM_CMD_ADDR__A, subCmd, 0);
- Write16(state, SC_RA_RAM_PARAM1__A, param1, 0);
- Write16(state, SC_RA_RAM_PARAM0__A, param0, 0);
+ status |= Write16(state, SC_RA_RAM_CMD_ADDR__A, subCmd, 0);
+ status |= Write16(state, SC_RA_RAM_PARAM1__A, param1, 0);
+ status |= Write16(state, SC_RA_RAM_PARAM0__A, param0, 0);
SC_SendCommand(state, SC_RA_RAM_CMD_PROC_START);
} while (0);
diff --git a/drivers/media/dvb-frontends/drxk.h b/drivers/media/dvb-frontends/drxk.h
index 76466f7ec3a0..ee06e89187e4 100644
--- a/drivers/media/dvb-frontends/drxk.h
+++ b/drivers/media/dvb-frontends/drxk.h
@@ -24,7 +24,7 @@
* @microcode_name: Name of the firmware file with the microcode
* @qam_demod_parameter_count: The number of parameters used for the command
* to set the demodulator parameters. All
- * firmwares are using the 2-parameter commmand.
+ * firmwares are using the 2-parameter command.
* An exception is the ``drxk_a3.mc`` firmware,
* which uses the 4-parameter command.
* A value of 0 (default) or lower indicates that
diff --git a/drivers/media/dvb-frontends/drxk_hard.c b/drivers/media/dvb-frontends/drxk_hard.c
index 8ea1e45be710..86652a4ef9ce 100644
--- a/drivers/media/dvb-frontends/drxk_hard.c
+++ b/drivers/media/dvb-frontends/drxk_hard.c
@@ -723,7 +723,7 @@ static int init_state(struct drxk_state *state)
state->m_drxk_state = DRXK_UNINITIALIZED;
/* MPEG output configuration */
- state->m_enable_mpeg_output = true; /* If TRUE; enable MPEG ouput */
+ state->m_enable_mpeg_output = true; /* If TRUE; enable MPEG output */
state->m_insert_rs_byte = false; /* If TRUE; insert RS byte */
state->m_invert_data = false; /* If TRUE; invert DATA signals */
state->m_invert_err = false; /* If TRUE; invert ERR signal */
@@ -3870,7 +3870,7 @@ static int set_dvbt(struct drxk_state *state, u16 intermediate_freqk_hz,
goto error;
}
#else
- /* Set Priorty high */
+ /* Set Priority high */
transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_PRIO_HI;
status = write16(state, OFDM_EC_SB_PRIOR__A, OFDM_EC_SB_PRIOR_HI);
if (status < 0)
@@ -3901,7 +3901,7 @@ static int set_dvbt(struct drxk_state *state, u16 intermediate_freqk_hz,
}
/*
- * SAW filter selection: normaly not necesarry, but if wanted
+ * SAW filter selection: normally not necessary, but if wanted
* the application can select a SAW filter via the driver by
* using UIOs
*/
@@ -5423,7 +5423,7 @@ static int qam_demodulator_command(struct drxk_state *state,
set_param_parameters[3] |= (QAM_MIRROR_AUTO_ON);
/* Env parameters */
- /* check for LOCKRANGE Extented */
+ /* check for LOCKRANGE Extended */
/* set_param_parameters[3] |= QAM_LOCKRANGE_NORMAL; */
status = scu_command(state,
diff --git a/drivers/media/dvb-frontends/ds3000.c b/drivers/media/dvb-frontends/ds3000.c
index 46a55146cb07..2b422d3ac5fa 100644
--- a/drivers/media/dvb-frontends/ds3000.c
+++ b/drivers/media/dvb-frontends/ds3000.c
@@ -914,7 +914,7 @@ static int ds3000_set_frontend(struct dvb_frontend *fe)
/* ds3000 global reset */
ds3000_writereg(state, 0x07, 0x80);
ds3000_writereg(state, 0x07, 0x00);
- /* ds3000 build-in uC reset */
+ /* ds3000 built-in uC reset */
ds3000_writereg(state, 0xb2, 0x01);
/* ds3000 software reset */
ds3000_writereg(state, 0x00, 0x01);
@@ -1023,7 +1023,7 @@ static int ds3000_set_frontend(struct dvb_frontend *fe)
/* ds3000 out of software reset */
ds3000_writereg(state, 0x00, 0x00);
- /* start ds3000 build-in uC */
+ /* start ds3000 built-in uC */
ds3000_writereg(state, 0xb2, 0x00);
if (fe->ops.tuner_ops.get_frequency) {
diff --git a/drivers/media/dvb-frontends/isl6421.c b/drivers/media/dvb-frontends/isl6421.c
index ae8ec59b665c..7de11d5062c2 100644
--- a/drivers/media/dvb-frontends/isl6421.c
+++ b/drivers/media/dvb-frontends/isl6421.c
@@ -98,7 +98,7 @@ static int isl6421_set_voltage(struct dvb_frontend *fe,
if (ret != 2)
return -EIO;
- /* Store off status now incase future commands fail */
+ /* Store off status now in case future commands fail */
isl6421->is_off = is_off;
/* On overflow, the device will try again after 900 ms (typically) */
diff --git a/drivers/media/dvb-frontends/lgdt3306a.c b/drivers/media/dvb-frontends/lgdt3306a.c
index cee9c83e48de..99c6289ae585 100644
--- a/drivers/media/dvb-frontends/lgdt3306a.c
+++ b/drivers/media/dvb-frontends/lgdt3306a.c
@@ -1685,7 +1685,10 @@ static int lgdt3306a_read_signal_strength(struct dvb_frontend *fe,
case QAM_256:
case QAM_AUTO:
/* need to know actual modulation to set proper SNR baseline */
- lgdt3306a_read_reg(state, 0x00a6, &val);
+ ret = lgdt3306a_read_reg(state, 0x00a6, &val);
+ if (lg_chkerr(ret))
+ goto fail;
+
if(val & 0x04)
ref_snr = 2800; /* QAM-256 28dB */
else
diff --git a/drivers/media/dvb-frontends/lgdt330x.c b/drivers/media/dvb-frontends/lgdt330x.c
index 96807e134886..8abb1a510a81 100644
--- a/drivers/media/dvb-frontends/lgdt330x.c
+++ b/drivers/media/dvb-frontends/lgdt330x.c
@@ -783,7 +783,7 @@ static int lgdt3303_read_status(struct dvb_frontend *fe,
if ((buf[0] & 0x02) == 0x00)
*status |= FE_HAS_SYNC;
- if ((buf[0] & 0xfd) == 0x01)
+ if ((buf[0] & 0x01) == 0x01)
*status |= FE_HAS_VITERBI | FE_HAS_LOCK;
break;
default:
diff --git a/drivers/media/dvb-frontends/m88rs2000.c b/drivers/media/dvb-frontends/m88rs2000.c
index d5bc85501f9e..13888732951c 100644
--- a/drivers/media/dvb-frontends/m88rs2000.c
+++ b/drivers/media/dvb-frontends/m88rs2000.c
@@ -701,7 +701,7 @@ static int m88rs2000_set_frontend(struct dvb_frontend *fe)
if (status & FE_HAS_LOCK) {
state->fec_inner = m88rs2000_get_fec(state);
- /* Uknown suspect SNR level */
+ /* Unknown suspect SNR level */
reg = m88rs2000_readreg(state, 0x65);
}
diff --git a/drivers/media/dvb-frontends/mt312.c b/drivers/media/dvb-frontends/mt312.c
index 03e74a729168..bfbb879469f2 100644
--- a/drivers/media/dvb-frontends/mt312.c
+++ b/drivers/media/dvb-frontends/mt312.c
@@ -645,7 +645,9 @@ static int mt312_set_frontend(struct dvb_frontend *fe)
if (ret < 0)
return ret;
- mt312_reset(state, 0);
+ ret = mt312_reset(state, 0);
+ if (ret < 0)
+ return ret;
return 0;
}
diff --git a/drivers/media/dvb-frontends/nxt200x.c b/drivers/media/dvb-frontends/nxt200x.c
index 0961e686ff68..0ef72d6c6f8b 100644
--- a/drivers/media/dvb-frontends/nxt200x.c
+++ b/drivers/media/dvb-frontends/nxt200x.c
@@ -153,7 +153,7 @@ static int nxt200x_writereg_multibyte (struct nxt200x_state* state, u8 reg, u8*
u8 attr, len2, buf;
dprintk("%s\n", __func__);
- /* set mutli register register */
+ /* set multi register register */
nxt200x_writebytes(state, 0x35, &reg, 1);
/* send the actual data */
@@ -214,7 +214,7 @@ static int nxt200x_readreg_multibyte (struct nxt200x_state* state, u8 reg, u8* d
u8 buf, len2, attr;
dprintk("%s\n", __func__);
- /* set mutli register register */
+ /* set multi register register */
nxt200x_writebytes(state, 0x35, &reg, 1);
switch (state->demod_chip) {
diff --git a/drivers/media/dvb-frontends/or51211.c b/drivers/media/dvb-frontends/or51211.c
index a39bbd8ff1f0..7343da11a1d8 100644
--- a/drivers/media/dvb-frontends/or51211.c
+++ b/drivers/media/dvb-frontends/or51211.c
@@ -59,7 +59,7 @@ struct or51211_state {
/* Demodulator private data */
u8 initialized:1;
- u32 snr; /* Result of last SNR claculation */
+ u32 snr; /* Result of last SNR calculation */
/* Tuner private data */
u32 current_frequency;
diff --git a/drivers/media/dvb-frontends/rtl2832_sdr.c b/drivers/media/dvb-frontends/rtl2832_sdr.c
index d6673f4fb47b..57fb05bb7e96 100644
--- a/drivers/media/dvb-frontends/rtl2832_sdr.c
+++ b/drivers/media/dvb-frontends/rtl2832_sdr.c
@@ -471,7 +471,7 @@ static int rtl2832_sdr_buf_prepare(struct vb2_buffer *vb)
{
struct rtl2832_sdr_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
- /* Don't allow queing new buffers after device disconnection */
+ /* Don't allow queueing new buffers after device disconnection */
if (!dev->udev)
return -ENODEV;
diff --git a/drivers/media/dvb-frontends/s5h1409.c b/drivers/media/dvb-frontends/s5h1409.c
index ceeb0c3551ce..a2907d035fe2 100644
--- a/drivers/media/dvb-frontends/s5h1409.c
+++ b/drivers/media/dvb-frontends/s5h1409.c
@@ -490,7 +490,7 @@ static void s5h1409_set_qam_amhum_mode(struct dvb_frontend *fe)
if (state->qam_state == QAM_STATE_QAM_OPTIMIZED_L3) {
/* We've already reached the maximum optimization level, so
- dont bother banging on the status registers */
+ don't bother banging on the status registers */
return;
}
diff --git a/drivers/media/dvb-frontends/sp8870.c b/drivers/media/dvb-frontends/sp8870.c
index 8d31cf3f4f07..270a3c559e08 100644
--- a/drivers/media/dvb-frontends/sp8870.c
+++ b/drivers/media/dvb-frontends/sp8870.c
@@ -293,7 +293,9 @@ static int sp8870_set_frontend_parameters(struct dvb_frontend *fe)
sp8870_writereg(state, 0xc05, reg0xc05);
// read status reg in order to clear pending irqs
- sp8870_readreg(state, 0x200);
+ err = sp8870_readreg(state, 0x200);
+ if (err)
+ return err;
// system controller start
sp8870_microcontroller_start(state);
diff --git a/drivers/media/dvb-frontends/stb0899_algo.c b/drivers/media/dvb-frontends/stb0899_algo.c
index bd2defde7a77..b5debb61bca5 100644
--- a/drivers/media/dvb-frontends/stb0899_algo.c
+++ b/drivers/media/dvb-frontends/stb0899_algo.c
@@ -835,8 +835,8 @@ static u32 stb0899_dvbs2_calc_dev(struct stb0899_state *state)
dec_ratio = (internal->master_clk * 2) / (5 * internal->srate);
dec_ratio = (dec_ratio == 0) ? 1 : dec_ratio;
- master_clk = internal->master_clk / 1000; /* for integer Caculation*/
- srate = internal->srate / 1000; /* for integer Caculation*/
+ master_clk = internal->master_clk / 1000; /* for integer Calculation*/
+ srate = internal->srate / 1000; /* for integer Calculation*/
correction = (512 * master_clk) / (2 * dec_ratio * srate);
return correction;
@@ -864,7 +864,7 @@ static void stb0899_dvbs2_set_srate(struct stb0899_state *state)
win_sel = dec_rate - 4;
decim = (1 << dec_rate);
- /* (FSamp/Fsymbol *100) for integer Caculation */
+ /* (FSamp/Fsymbol *100) for integer Calculation */
f_sym = internal->master_clk / ((decim * internal->srate) / 1000);
if (f_sym <= 2250) /* don't band limit signal going into btr block*/
diff --git a/drivers/media/dvb-frontends/stv0367_defs.h b/drivers/media/dvb-frontends/stv0367_defs.h
index 277d2971ed3f..4afe8248a667 100644
--- a/drivers/media/dvb-frontends/stv0367_defs.h
+++ b/drivers/media/dvb-frontends/stv0367_defs.h
@@ -1096,7 +1096,7 @@ static const struct st_register def0367dd_ofdm[] = {
};
static const struct st_register def0367dd_qam[] = {
- {R367CAB_CTRL_1, 0x06}, /* Orginal 0x04 */
+ {R367CAB_CTRL_1, 0x06}, /* Original 0x04 */
{R367CAB_CTRL_2, 0x03},
{R367CAB_IT_STATUS1, 0x2b},
{R367CAB_IT_STATUS2, 0x08},
diff --git a/drivers/media/dvb-frontends/stv0900_core.c b/drivers/media/dvb-frontends/stv0900_core.c
index 254618a06140..fa1a0fb577ad 100644
--- a/drivers/media/dvb-frontends/stv0900_core.c
+++ b/drivers/media/dvb-frontends/stv0900_core.c
@@ -744,12 +744,12 @@ static int stv0900_read_ucblocks(struct dvb_frontend *fe, u32 * ucblocks)
if (stv0900_get_standard(fe, demod) == STV0900_DVBS2_STANDARD) {
/* DVB-S2 delineator errors count */
- /* retreiving number for errnous headers */
+ /* retrieving number for errnous headers */
err_val1 = stv0900_read_reg(intp, BBFCRCKO1);
err_val0 = stv0900_read_reg(intp, BBFCRCKO0);
header_err_val = (err_val1 << 8) | err_val0;
- /* retreiving number for errnous packets */
+ /* retrieving number for errnous packets */
err_val1 = stv0900_read_reg(intp, UPCRCKO1);
err_val0 = stv0900_read_reg(intp, UPCRCKO0);
*ucblocks = (err_val1 << 8) | err_val0;
diff --git a/drivers/media/dvb-frontends/stv0910.c b/drivers/media/dvb-frontends/stv0910.c
index fc2440d8af36..68d7c7b41071 100644
--- a/drivers/media/dvb-frontends/stv0910.c
+++ b/drivers/media/dvb-frontends/stv0910.c
@@ -1238,7 +1238,7 @@ static int gate_ctrl(struct dvb_frontend *fe, int enable)
* mutex_lock note: Concurrent I2C gate bus accesses must be
* prevented (STV0910 = dual demod on a single IC with a single I2C
* gate/bus, and two tuners attached), similar to most (if not all)
- * other I2C host interfaces/busses.
+ * other I2C host interfaces/buses.
*
* enable=1 (open I2C gate) will grab the lock
* enable=0 (close I2C gate) releases the lock
@@ -1500,7 +1500,7 @@ static int read_status(struct dvb_frontend *fe, enum fe_status *status)
RSTV0910_P2_FBERCPT4 + state->regoff, 0x00);
/*
* Reset the packet Error counter2 (and Set it to
- * infinit error count mode)
+ * infinite error count mode)
*/
write_reg(state,
RSTV0910_P2_ERRCTRL2 + state->regoff, 0xc1);
diff --git a/drivers/media/dvb-frontends/stv6110.c b/drivers/media/dvb-frontends/stv6110.c
index 7db9a5bceccc..e54708eb4fb0 100644
--- a/drivers/media/dvb-frontends/stv6110.c
+++ b/drivers/media/dvb-frontends/stv6110.c
@@ -202,7 +202,7 @@ static int stv6110_set_bandwidth(struct dvb_frontend *fe, u32 bandwidth)
i++;
}
- /* RCCLKOFF = 1 calibration done, desactivate the calibration Clock */
+ /* RCCLKOFF = 1 calibration done, deactivate the calibration Clock */
priv->regs[RSTV6110_CTRL3] |= (1 << 6);
stv6110_write_regs(fe, &priv->regs[RSTV6110_CTRL3], RSTV6110_CTRL3, 1);
return 0;
diff --git a/drivers/media/dvb-frontends/tda1004x.h b/drivers/media/dvb-frontends/tda1004x.h
index efd7659dace9..26f504a830e3 100644
--- a/drivers/media/dvb-frontends/tda1004x.h
+++ b/drivers/media/dvb-frontends/tda1004x.h
@@ -33,7 +33,7 @@ enum tda10046_xtal {
enum tda10046_agc {
TDA10046_AGC_DEFAULT, /* original configuration */
- TDA10046_AGC_IFO_AUTO_NEG, /* IF AGC only, automatic, negtive */
+ TDA10046_AGC_IFO_AUTO_NEG, /* IF AGC only, automatic, negative */
TDA10046_AGC_IFO_AUTO_POS, /* IF AGC only, automatic, positive */
TDA10046_AGC_TDA827X, /* IF AGC only, special setup for tda827x */
};
diff --git a/drivers/media/dvb-frontends/tda10086.c b/drivers/media/dvb-frontends/tda10086.c
index 8323e4e53d66..85dddfce8ef4 100644
--- a/drivers/media/dvb-frontends/tda10086.c
+++ b/drivers/media/dvb-frontends/tda10086.c
@@ -437,7 +437,7 @@ static int tda10086_set_frontend(struct dvb_frontend *fe)
fe->ops.i2c_gate_ctrl(fe, 0);
}
- /* calcluate the frequency offset (in *Hz* not kHz) */
+ /* calculate the frequency offset (in *Hz* not kHz) */
freqoff = fe_params->frequency - freq;
freqoff = ((1<<16) * freqoff) / (SACLK/1000);
tda10086_write_byte(state, 0x3d, 0x80 | ((freqoff >> 8) & 0x7f));
diff --git a/drivers/media/dvb-frontends/tda18271c2dd.c b/drivers/media/dvb-frontends/tda18271c2dd.c
index eeb2318c102f..e064e2b22d9d 100644
--- a/drivers/media/dvb-frontends/tda18271c2dd.c
+++ b/drivers/media/dvb-frontends/tda18271c2dd.c
@@ -105,7 +105,7 @@ struct tda_state {
s32 m_RF_B2[7];
u32 m_RF3[7];
- u8 m_TMValue_RFCal; /* Calibration temperatur */
+ u8 m_TMValue_RFCal; /* Calibration temperature */
bool m_bFMInput; /* true to use Pin 8 for FM Radio */
@@ -400,7 +400,7 @@ static int CalibrateRF(struct tda_state *state,
break;
/* Switching off LT (as datasheet says) causes calibration on C1 to fail */
- /* (Readout of Cprog is allways 255) */
+ /* (Readout of Cprog is always 255) */
if (state->m_Regs[ID] != 0x83) /* C1: ID == 83, C2: ID == 84 */
state->m_Regs[EP3] |= 0x40; /* SM_LT = 1 */
@@ -644,7 +644,7 @@ static int PowerScan(struct tda_state *state,
if (status < 0)
break;
CID_Gain = Regs[EB10] & 0x3F;
- state->m_Regs[ID] = Regs[ID]; /* Chip version, (needed for C1 workarround in CalibrateRF) */
+ state->m_Regs[ID] = Regs[ID]; /* Chip version, (needed for C1 workaround in CalibrateRF) */
*pRF_Out = RF_in;
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 4c936e129500..6d32f8dcf83b 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -820,6 +820,25 @@ config VIDEO_OV7740
This is a Video4Linux2 sensor driver for the OmniVision
OV7740 VGA camera sensor.
+config VIDEO_OV8856
+ tristate "OmniVision OV8856 sensor support"
+ depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+ depends on MEDIA_CAMERA_SUPPORT
+ select V4L2_FWNODE
+ help
+ This is a Video4Linux2 sensor driver for the OmniVision
+ OV8856 camera sensor.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ov8856.
+
+config VIDEO_OV9640
+ tristate "OmniVision OV9640 sensor support"
+ depends on I2C && VIDEO_V4L2
+ help
+ This is a Video4Linux2 sensor driver for the OmniVision
+ OV9640 camera sensor.
+
config VIDEO_OV9650
tristate "OmniVision OV9650/OV9652 sensor support"
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
@@ -848,6 +867,14 @@ config VIDEO_VS6624
To compile this driver as a module, choose M here: the
module will be called vs6624.
+config VIDEO_MT9M001
+ tristate "mt9m001 support"
+ depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+ depends on MEDIA_CAMERA_SUPPORT
+ help
+ This driver supports MT9M001 cameras from Micron, monochrome
+ and colour models.
+
config VIDEO_MT9M032
tristate "MT9M032 camera sensor support"
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
@@ -1100,18 +1127,11 @@ config VIDEO_I2C
Enable the I2C transport video support which supports the
following:
* Panasonic AMG88xx Grid-Eye Sensors
+ * Melexis MLX90640 Thermal Cameras
To compile this driver as a module, choose M here: the
module will be called video-i2c
endmenu
-menu "Sensors used on soc_camera driver"
-
-if SOC_CAMERA
- source "drivers/media/i2c/soc_camera/Kconfig"
-endif
-
-endmenu
-
endif
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index 65fae7732de0..a64fca82e0c4 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -6,7 +6,6 @@ obj-$(CONFIG_VIDEO_SMIAPP) += smiapp/
obj-$(CONFIG_VIDEO_ET8EK8) += et8ek8/
obj-$(CONFIG_VIDEO_CX25840) += cx25840/
obj-$(CONFIG_VIDEO_M5MOLS) += m5mols/
-obj-y += soc_camera/
obj-$(CONFIG_VIDEO_APTINA_PLL) += aptina-pll.o
obj-$(CONFIG_VIDEO_TVAUDIO) += tvaudio.o
@@ -78,8 +77,11 @@ obj-$(CONFIG_VIDEO_OV7640) += ov7640.o
obj-$(CONFIG_VIDEO_OV7670) += ov7670.o
obj-$(CONFIG_VIDEO_OV772X) += ov772x.o
obj-$(CONFIG_VIDEO_OV7740) += ov7740.o
+obj-$(CONFIG_VIDEO_OV8856) += ov8856.o
+obj-$(CONFIG_VIDEO_OV9640) += ov9640.o
obj-$(CONFIG_VIDEO_OV9650) += ov9650.o
obj-$(CONFIG_VIDEO_OV13858) += ov13858.o
+obj-$(CONFIG_VIDEO_MT9M001) += mt9m001.o
obj-$(CONFIG_VIDEO_MT9M032) += mt9m032.o
obj-$(CONFIG_VIDEO_MT9M111) += mt9m111.o
obj-$(CONFIG_VIDEO_MT9P031) += mt9p031.o
diff --git a/drivers/media/i2c/adv7175.c b/drivers/media/i2c/adv7175.c
index e31e8d909bb9..419b98117133 100644
--- a/drivers/media/i2c/adv7175.c
+++ b/drivers/media/i2c/adv7175.c
@@ -219,7 +219,7 @@ static int adv7175_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std)
* SECAM->PAL (typically it does not work
* due to genlock: when decoder is in SECAM
* and encoder in in PAL the subcarrier can
- * not be syncronized with horizontal
+ * not be synchronized with horizontal
* quency) */
adv7175_write_block(sd, init_pal, sizeof(init_pal));
if (encoder->input == 0)
diff --git a/drivers/media/i2c/adv748x/adv748x-afe.c b/drivers/media/i2c/adv748x/adv748x-afe.c
index 71714634efb0..dbbb1e4d6363 100644
--- a/drivers/media/i2c/adv748x/adv748x-afe.c
+++ b/drivers/media/i2c/adv748x/adv748x-afe.c
@@ -282,7 +282,7 @@ static int adv748x_afe_s_stream(struct v4l2_subdev *sd, int enable)
goto unlock;
}
- ret = adv748x_tx_power(&state->txb, enable);
+ ret = adv748x_tx_power(afe->tx, enable);
if (ret)
goto unlock;
diff --git a/drivers/media/i2c/adv748x/adv748x-core.c b/drivers/media/i2c/adv748x/adv748x-core.c
index 6854d898fdd1..f57cd77a32fa 100644
--- a/drivers/media/i2c/adv748x/adv748x-core.c
+++ b/drivers/media/i2c/adv748x/adv748x-core.c
@@ -23,6 +23,7 @@
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-dv-timings.h>
+#include <media/v4l2-fwnode.h>
#include <media/v4l2-ioctl.h>
#include "adv748x.h"
@@ -124,6 +125,16 @@ int adv748x_write(struct adv748x_state *state, u8 page, u8 reg, u8 value)
return regmap_write(state->regmap[page], reg, value);
}
+static int adv748x_write_check(struct adv748x_state *state, u8 page, u8 reg,
+ u8 value, int *error)
+{
+ if (*error)
+ return *error;
+
+ *error = adv748x_write(state, page, reg, value);
+ return *error;
+}
+
/* adv748x_write_block(): Write raw data with a maximum of I2C_SMBUS_BLOCK_MAX
* size to one or more registers.
*
@@ -207,20 +218,13 @@ static int adv748x_write_regs(struct adv748x_state *state,
{
int ret;
- while (regs->page != ADV748X_PAGE_EOR) {
- if (regs->page == ADV748X_PAGE_WAIT) {
- msleep(regs->value);
- } else {
- ret = adv748x_write(state, regs->page, regs->reg,
- regs->value);
- if (ret < 0) {
- adv_err(state,
- "Error regs page: 0x%02x reg: 0x%02x\n",
- regs->page, regs->reg);
- return ret;
- }
+ for (; regs->page != ADV748X_PAGE_EOR; regs++) {
+ ret = adv748x_write(state, regs->page, regs->reg, regs->value);
+ if (ret < 0) {
+ adv_err(state, "Error regs page: 0x%02x reg: 0x%02x\n",
+ regs->page, regs->reg);
+ return ret;
}
- regs++;
}
return 0;
@@ -230,68 +234,77 @@ static int adv748x_write_regs(struct adv748x_state *state,
* TXA and TXB
*/
-static const struct adv748x_reg_value adv748x_power_up_txa_4lane[] = {
+static int adv748x_power_up_tx(struct adv748x_csi2 *tx)
+{
+ struct adv748x_state *state = tx->state;
+ u8 page = is_txa(tx) ? ADV748X_PAGE_TXA : ADV748X_PAGE_TXB;
+ int ret = 0;
- {ADV748X_PAGE_TXA, 0x00, 0x84}, /* Enable 4-lane MIPI */
- {ADV748X_PAGE_TXA, 0x00, 0xa4}, /* Set Auto DPHY Timing */
+ /* Enable n-lane MIPI */
+ adv748x_write_check(state, page, 0x00, 0x80 | tx->num_lanes, &ret);
- {ADV748X_PAGE_TXA, 0x31, 0x82}, /* ADI Required Write */
- {ADV748X_PAGE_TXA, 0x1e, 0x40}, /* ADI Required Write */
- {ADV748X_PAGE_TXA, 0xda, 0x01}, /* i2c_mipi_pll_en - 1'b1 */
- {ADV748X_PAGE_WAIT, 0x00, 0x02},/* delay 2 */
- {ADV748X_PAGE_TXA, 0x00, 0x24 },/* Power-up CSI-TX */
- {ADV748X_PAGE_WAIT, 0x00, 0x01},/* delay 1 */
- {ADV748X_PAGE_TXA, 0xc1, 0x2b}, /* ADI Required Write */
- {ADV748X_PAGE_WAIT, 0x00, 0x01},/* delay 1 */
- {ADV748X_PAGE_TXA, 0x31, 0x80}, /* ADI Required Write */
+ /* Set Auto DPHY Timing */
+ adv748x_write_check(state, page, 0x00, 0xa0 | tx->num_lanes, &ret);
- {ADV748X_PAGE_EOR, 0xff, 0xff} /* End of register table */
-};
+ /* ADI Required Write */
+ if (tx->src == &state->hdmi.sd) {
+ adv748x_write_check(state, page, 0xdb, 0x10, &ret);
+ adv748x_write_check(state, page, 0xd6, 0x07, &ret);
+ } else {
+ adv748x_write_check(state, page, 0xd2, 0x40, &ret);
+ }
-static const struct adv748x_reg_value adv748x_power_down_txa_4lane[] = {
+ adv748x_write_check(state, page, 0xc4, 0x0a, &ret);
+ adv748x_write_check(state, page, 0x71, 0x33, &ret);
+ adv748x_write_check(state, page, 0x72, 0x11, &ret);
- {ADV748X_PAGE_TXA, 0x31, 0x82}, /* ADI Required Write */
- {ADV748X_PAGE_TXA, 0x1e, 0x00}, /* ADI Required Write */
- {ADV748X_PAGE_TXA, 0x00, 0x84}, /* Enable 4-lane MIPI */
- {ADV748X_PAGE_TXA, 0xda, 0x01}, /* i2c_mipi_pll_en - 1'b1 */
- {ADV748X_PAGE_TXA, 0xc1, 0x3b}, /* ADI Required Write */
+ /* i2c_dphy_pwdn - 1'b0 */
+ adv748x_write_check(state, page, 0xf0, 0x00, &ret);
- {ADV748X_PAGE_EOR, 0xff, 0xff} /* End of register table */
-};
+ /* ADI Required Writes*/
+ adv748x_write_check(state, page, 0x31, 0x82, &ret);
+ adv748x_write_check(state, page, 0x1e, 0x40, &ret);
-static const struct adv748x_reg_value adv748x_power_up_txb_1lane[] = {
+ /* i2c_mipi_pll_en - 1'b1 */
+ adv748x_write_check(state, page, 0xda, 0x01, &ret);
+ usleep_range(2000, 2500);
- {ADV748X_PAGE_TXB, 0x00, 0x81}, /* Enable 1-lane MIPI */
- {ADV748X_PAGE_TXB, 0x00, 0xa1}, /* Set Auto DPHY Timing */
+ /* Power-up CSI-TX */
+ adv748x_write_check(state, page, 0x00, 0x20 | tx->num_lanes, &ret);
+ usleep_range(1000, 1500);
- {ADV748X_PAGE_TXB, 0x31, 0x82}, /* ADI Required Write */
- {ADV748X_PAGE_TXB, 0x1e, 0x40}, /* ADI Required Write */
- {ADV748X_PAGE_TXB, 0xda, 0x01}, /* i2c_mipi_pll_en - 1'b1 */
- {ADV748X_PAGE_WAIT, 0x00, 0x02},/* delay 2 */
- {ADV748X_PAGE_TXB, 0x00, 0x21 },/* Power-up CSI-TX */
- {ADV748X_PAGE_WAIT, 0x00, 0x01},/* delay 1 */
- {ADV748X_PAGE_TXB, 0xc1, 0x2b}, /* ADI Required Write */
- {ADV748X_PAGE_WAIT, 0x00, 0x01},/* delay 1 */
- {ADV748X_PAGE_TXB, 0x31, 0x80}, /* ADI Required Write */
+ /* ADI Required Writes */
+ adv748x_write_check(state, page, 0xc1, 0x2b, &ret);
+ usleep_range(1000, 1500);
+ adv748x_write_check(state, page, 0x31, 0x80, &ret);
- {ADV748X_PAGE_EOR, 0xff, 0xff} /* End of register table */
-};
+ return ret;
+}
-static const struct adv748x_reg_value adv748x_power_down_txb_1lane[] = {
+static int adv748x_power_down_tx(struct adv748x_csi2 *tx)
+{
+ struct adv748x_state *state = tx->state;
+ u8 page = is_txa(tx) ? ADV748X_PAGE_TXA : ADV748X_PAGE_TXB;
+ int ret = 0;
- {ADV748X_PAGE_TXB, 0x31, 0x82}, /* ADI Required Write */
- {ADV748X_PAGE_TXB, 0x1e, 0x00}, /* ADI Required Write */
- {ADV748X_PAGE_TXB, 0x00, 0x81}, /* Enable 1-lane MIPI */
- {ADV748X_PAGE_TXB, 0xda, 0x01}, /* i2c_mipi_pll_en - 1'b1 */
- {ADV748X_PAGE_TXB, 0xc1, 0x3b}, /* ADI Required Write */
+ /* ADI Required Writes */
+ adv748x_write_check(state, page, 0x31, 0x82, &ret);
+ adv748x_write_check(state, page, 0x1e, 0x00, &ret);
- {ADV748X_PAGE_EOR, 0xff, 0xff} /* End of register table */
-};
+ /* Enable n-lane MIPI */
+ adv748x_write_check(state, page, 0x00, 0x80 | tx->num_lanes, &ret);
+
+ /* i2c_mipi_pll_en - 1'b1 */
+ adv748x_write_check(state, page, 0xda, 0x01, &ret);
+
+ /* ADI Required Write */
+ adv748x_write_check(state, page, 0xc1, 0x3b, &ret);
+
+ return ret;
+}
int adv748x_tx_power(struct adv748x_csi2 *tx, bool on)
{
- struct adv748x_state *state = tx->state;
- const struct adv748x_reg_value *reglist;
int val;
if (!is_tx_enabled(tx))
@@ -309,19 +322,57 @@ int adv748x_tx_power(struct adv748x_csi2 *tx, bool on)
WARN_ONCE((on && val & ADV748X_CSI_FS_AS_LS_UNKNOWN),
"Enabling with unknown bit set");
- if (on)
- reglist = is_txa(tx) ? adv748x_power_up_txa_4lane :
- adv748x_power_up_txb_1lane;
- else
- reglist = is_txa(tx) ? adv748x_power_down_txa_4lane :
- adv748x_power_down_txb_1lane;
-
- return adv748x_write_regs(state, reglist);
+ return on ? adv748x_power_up_tx(tx) : adv748x_power_down_tx(tx);
}
/* -----------------------------------------------------------------------------
* Media Operations
*/
+static int adv748x_link_setup(struct media_entity *entity,
+ const struct media_pad *local,
+ const struct media_pad *remote, u32 flags)
+{
+ struct v4l2_subdev *rsd = media_entity_to_v4l2_subdev(remote->entity);
+ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
+ struct adv748x_state *state = v4l2_get_subdevdata(sd);
+ struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd);
+ bool enable = flags & MEDIA_LNK_FL_ENABLED;
+ u8 io10_mask = ADV748X_IO_10_CSI1_EN |
+ ADV748X_IO_10_CSI4_EN |
+ ADV748X_IO_10_CSI4_IN_SEL_AFE;
+ u8 io10 = 0;
+
+ /* Refuse to enable multiple links to the same TX at the same time. */
+ if (enable && tx->src)
+ return -EINVAL;
+
+ /* Set or clear the source (HDMI or AFE) and the current TX. */
+ if (rsd == &state->afe.sd)
+ state->afe.tx = enable ? tx : NULL;
+ else
+ state->hdmi.tx = enable ? tx : NULL;
+
+ tx->src = enable ? rsd : NULL;
+
+ if (state->afe.tx) {
+ /* AFE Requires TXA enabled, even when output to TXB */
+ io10 |= ADV748X_IO_10_CSI4_EN;
+ if (is_txa(tx))
+ io10 |= ADV748X_IO_10_CSI4_IN_SEL_AFE;
+ else
+ io10 |= ADV748X_IO_10_CSI1_EN;
+ }
+
+ if (state->hdmi.tx)
+ io10 |= ADV748X_IO_10_CSI4_EN;
+
+ return io_clrset(state, ADV748X_IO_10, io10_mask, io10);
+}
+
+static const struct media_entity_operations adv748x_tx_media_ops = {
+ .link_setup = adv748x_link_setup,
+ .link_validate = v4l2_subdev_link_validate,
+};
static const struct media_entity_operations adv748x_media_ops = {
.link_validate = v4l2_subdev_link_validate,
@@ -331,18 +382,8 @@ static const struct media_entity_operations adv748x_media_ops = {
* HW setup
*/
-static const struct adv748x_reg_value adv748x_sw_reset[] = {
-
- {ADV748X_PAGE_IO, 0xff, 0xff}, /* SW reset */
- {ADV748X_PAGE_WAIT, 0x00, 0x05},/* delay 5 */
- {ADV748X_PAGE_IO, 0x01, 0x76}, /* ADI Required Write */
- {ADV748X_PAGE_IO, 0xf2, 0x01}, /* Enable I2C Read Auto-Increment */
- {ADV748X_PAGE_EOR, 0xff, 0xff} /* End of register table */
-};
-
-/* Supported Formats For Script Below */
-/* - 01-29 HDMI to MIPI TxA CSI 4-Lane - RGB888: */
-static const struct adv748x_reg_value adv748x_init_txa_4lane[] = {
+/* Initialize CP Core with RGB888 format. */
+static const struct adv748x_reg_value adv748x_init_hdmi[] = {
/* Disable chip powerdown & Enable HDMI Rx block */
{ADV748X_PAGE_IO, 0x00, 0x40},
@@ -383,32 +424,11 @@ static const struct adv748x_reg_value adv748x_init_txa_4lane[] = {
{ADV748X_PAGE_IO, 0x0c, 0xe0}, /* Enable LLC_DLL & Double LLC Timing */
{ADV748X_PAGE_IO, 0x0e, 0xdd}, /* LLC/PIX/SPI PINS TRISTATED AUD */
- {ADV748X_PAGE_TXA, 0x00, 0x84}, /* Enable 4-lane MIPI */
- {ADV748X_PAGE_TXA, 0x00, 0xa4}, /* Set Auto DPHY Timing */
- {ADV748X_PAGE_TXA, 0xdb, 0x10}, /* ADI Required Write */
- {ADV748X_PAGE_TXA, 0xd6, 0x07}, /* ADI Required Write */
- {ADV748X_PAGE_TXA, 0xc4, 0x0a}, /* ADI Required Write */
- {ADV748X_PAGE_TXA, 0x71, 0x33}, /* ADI Required Write */
- {ADV748X_PAGE_TXA, 0x72, 0x11}, /* ADI Required Write */
- {ADV748X_PAGE_TXA, 0xf0, 0x00}, /* i2c_dphy_pwdn - 1'b0 */
-
- {ADV748X_PAGE_TXA, 0x31, 0x82}, /* ADI Required Write */
- {ADV748X_PAGE_TXA, 0x1e, 0x40}, /* ADI Required Write */
- {ADV748X_PAGE_TXA, 0xda, 0x01}, /* i2c_mipi_pll_en - 1'b1 */
- {ADV748X_PAGE_WAIT, 0x00, 0x02},/* delay 2 */
- {ADV748X_PAGE_TXA, 0x00, 0x24 },/* Power-up CSI-TX */
- {ADV748X_PAGE_WAIT, 0x00, 0x01},/* delay 1 */
- {ADV748X_PAGE_TXA, 0xc1, 0x2b}, /* ADI Required Write */
- {ADV748X_PAGE_WAIT, 0x00, 0x01},/* delay 1 */
- {ADV748X_PAGE_TXA, 0x31, 0x80}, /* ADI Required Write */
-
{ADV748X_PAGE_EOR, 0xff, 0xff} /* End of register table */
};
-/* 02-01 Analog CVBS to MIPI TX-B CSI 1-Lane - */
-/* Autodetect CVBS Single Ended In Ain 1 - MIPI Out */
-static const struct adv748x_reg_value adv748x_init_txb_1lane[] = {
-
+/* Initialize AFE core with YUV8 format. */
+static const struct adv748x_reg_value adv748x_init_afe[] = {
{ADV748X_PAGE_IO, 0x00, 0x30}, /* Disable chip powerdown Rx */
{ADV748X_PAGE_IO, 0xf2, 0x01}, /* Enable I2C Read Auto-Increment */
@@ -435,33 +455,36 @@ static const struct adv748x_reg_value adv748x_init_txb_1lane[] = {
{ADV748X_PAGE_SDP, 0x31, 0x12}, /* ADI Required Write */
{ADV748X_PAGE_SDP, 0xe6, 0x4f}, /* V bit end pos manually in NTSC */
- {ADV748X_PAGE_TXB, 0x00, 0x81}, /* Enable 1-lane MIPI */
- {ADV748X_PAGE_TXB, 0x00, 0xa1}, /* Set Auto DPHY Timing */
- {ADV748X_PAGE_TXB, 0xd2, 0x40}, /* ADI Required Write */
- {ADV748X_PAGE_TXB, 0xc4, 0x0a}, /* ADI Required Write */
- {ADV748X_PAGE_TXB, 0x71, 0x33}, /* ADI Required Write */
- {ADV748X_PAGE_TXB, 0x72, 0x11}, /* ADI Required Write */
- {ADV748X_PAGE_TXB, 0xf0, 0x00}, /* i2c_dphy_pwdn - 1'b0 */
- {ADV748X_PAGE_TXB, 0x31, 0x82}, /* ADI Required Write */
- {ADV748X_PAGE_TXB, 0x1e, 0x40}, /* ADI Required Write */
- {ADV748X_PAGE_TXB, 0xda, 0x01}, /* i2c_mipi_pll_en - 1'b1 */
-
- {ADV748X_PAGE_WAIT, 0x00, 0x02},/* delay 2 */
- {ADV748X_PAGE_TXB, 0x00, 0x21 },/* Power-up CSI-TX */
- {ADV748X_PAGE_WAIT, 0x00, 0x01},/* delay 1 */
- {ADV748X_PAGE_TXB, 0xc1, 0x2b}, /* ADI Required Write */
- {ADV748X_PAGE_WAIT, 0x00, 0x01},/* delay 1 */
- {ADV748X_PAGE_TXB, 0x31, 0x80}, /* ADI Required Write */
-
{ADV748X_PAGE_EOR, 0xff, 0xff} /* End of register table */
};
+static int adv748x_sw_reset(struct adv748x_state *state)
+{
+ int ret;
+
+ ret = io_write(state, ADV748X_IO_REG_FF, ADV748X_IO_REG_FF_MAIN_RESET);
+ if (ret)
+ return ret;
+
+ usleep_range(5000, 6000);
+
+ /* Disable CEC Wakeup from power-down mode */
+ ret = io_clrset(state, ADV748X_IO_REG_01, ADV748X_IO_REG_01_PWRDN_MASK,
+ ADV748X_IO_REG_01_PWRDNB);
+ if (ret)
+ return ret;
+
+ /* Enable I2C Read Auto-Increment for consecutive reads */
+ return io_write(state, ADV748X_IO_REG_F2,
+ ADV748X_IO_REG_F2_READ_AUTO_INC);
+}
+
static int adv748x_reset(struct adv748x_state *state)
{
int ret;
u8 regval = 0;
- ret = adv748x_write_regs(state, adv748x_sw_reset);
+ ret = adv748x_sw_reset(state);
if (ret < 0)
return ret;
@@ -469,18 +492,19 @@ static int adv748x_reset(struct adv748x_state *state)
if (ret < 0)
return ret;
- /* Init and power down TXA */
- ret = adv748x_write_regs(state, adv748x_init_txa_4lane);
+ /* Initialize CP and AFE cores. */
+ ret = adv748x_write_regs(state, adv748x_init_hdmi);
if (ret)
return ret;
- adv748x_tx_power(&state->txa, 0);
-
- /* Init and power down TXB */
- ret = adv748x_write_regs(state, adv748x_init_txb_1lane);
+ ret = adv748x_write_regs(state, adv748x_init_afe);
if (ret)
return ret;
+ /* Reset TXA and TXB */
+ adv748x_tx_power(&state->txa, 1);
+ adv748x_tx_power(&state->txa, 0);
+ adv748x_tx_power(&state->txb, 1);
adv748x_tx_power(&state->txb, 0);
/* Disable chip powerdown & Enable HDMI Rx block */
@@ -542,7 +566,51 @@ void adv748x_subdev_init(struct v4l2_subdev *sd, struct adv748x_state *state,
state->client->addr, ident);
sd->entity.function = function;
- sd->entity.ops = &adv748x_media_ops;
+ sd->entity.ops = is_tx(adv748x_sd_to_csi2(sd)) ?
+ &adv748x_tx_media_ops : &adv748x_media_ops;
+}
+
+static int adv748x_parse_csi2_lanes(struct adv748x_state *state,
+ unsigned int port,
+ struct device_node *ep)
+{
+ struct v4l2_fwnode_endpoint vep;
+ unsigned int num_lanes;
+ int ret;
+
+ if (port != ADV748X_PORT_TXA && port != ADV748X_PORT_TXB)
+ return 0;
+
+ vep.bus_type = V4L2_MBUS_CSI2_DPHY;
+ ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &vep);
+ if (ret)
+ return ret;
+
+ num_lanes = vep.bus.mipi_csi2.num_data_lanes;
+
+ if (vep.base.port == ADV748X_PORT_TXA) {
+ if (num_lanes != 1 && num_lanes != 2 && num_lanes != 4) {
+ adv_err(state, "TXA: Invalid number (%u) of lanes\n",
+ num_lanes);
+ return -EINVAL;
+ }
+
+ state->txa.num_lanes = num_lanes;
+ adv_dbg(state, "TXA: using %u lanes\n", state->txa.num_lanes);
+ }
+
+ if (vep.base.port == ADV748X_PORT_TXB) {
+ if (num_lanes != 1) {
+ adv_err(state, "TXB: Invalid number (%u) of lanes\n",
+ num_lanes);
+ return -EINVAL;
+ }
+
+ state->txb.num_lanes = num_lanes;
+ adv_dbg(state, "TXB: using %u lanes\n", state->txb.num_lanes);
+ }
+
+ return 0;
}
static int adv748x_parse_dt(struct adv748x_state *state)
@@ -551,6 +619,7 @@ static int adv748x_parse_dt(struct adv748x_state *state)
struct of_endpoint ep;
bool out_found = false;
bool in_found = false;
+ int ret;
for_each_endpoint_of_node(state->dev->of_node, ep_np) {
of_graph_parse_endpoint(ep_np, &ep);
@@ -581,6 +650,11 @@ static int adv748x_parse_dt(struct adv748x_state *state)
in_found = true;
else
out_found = true;
+
+ /* Store number of CSI-2 lanes used for TXA and TXB. */
+ ret = adv748x_parse_csi2_lanes(state, ep.port, ep_np);
+ if (ret)
+ return ret;
}
return in_found && out_found ? 0 : -ENODEV;
@@ -604,7 +678,7 @@ static int adv748x_probe(struct i2c_client *client,
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -EIO;
- state = kzalloc(sizeof(struct adv748x_state), GFP_KERNEL);
+ state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL);
if (!state)
return -ENOMEM;
@@ -702,7 +776,6 @@ err_cleanup_dt:
adv748x_dt_cleanup(state);
err_free_mutex:
mutex_destroy(&state->mutex);
- kfree(state);
return ret;
}
@@ -721,8 +794,6 @@ static int adv748x_remove(struct i2c_client *client)
adv748x_dt_cleanup(state);
mutex_destroy(&state->mutex);
- kfree(state);
-
return 0;
}
diff --git a/drivers/media/i2c/adv748x/adv748x-csi2.c b/drivers/media/i2c/adv748x/adv748x-csi2.c
index 6ce21542ed48..2091cda50935 100644
--- a/drivers/media/i2c/adv748x/adv748x-csi2.c
+++ b/drivers/media/i2c/adv748x/adv748x-csi2.c
@@ -27,6 +27,7 @@ static int adv748x_csi2_set_virtual_channel(struct adv748x_csi2 *tx,
* @v4l2_dev: Video registration device
* @src: Source subdevice to establish link
* @src_pad: Pad number of source to link to this @tx
+ * @enable: Link enabled flag
*
* Ensure that the subdevice is registered against the v4l2_device, and link the
* source pad to the sink pad of the CSI2 bus entity.
@@ -34,26 +35,27 @@ static int adv748x_csi2_set_virtual_channel(struct adv748x_csi2 *tx,
static int adv748x_csi2_register_link(struct adv748x_csi2 *tx,
struct v4l2_device *v4l2_dev,
struct v4l2_subdev *src,
- unsigned int src_pad)
+ unsigned int src_pad,
+ bool enable)
{
- int enabled = MEDIA_LNK_FL_ENABLED;
int ret;
- /*
- * Dynamic linking of the AFE is not supported.
- * Register the links as immutable.
- */
- enabled |= MEDIA_LNK_FL_IMMUTABLE;
-
if (!src->v4l2_dev) {
ret = v4l2_device_register_subdev(v4l2_dev, src);
if (ret)
return ret;
}
- return media_create_pad_link(&src->entity, src_pad,
- &tx->sd.entity, ADV748X_CSI2_SINK,
- enabled);
+ ret = media_create_pad_link(&src->entity, src_pad,
+ &tx->sd.entity, ADV748X_CSI2_SINK,
+ enable ? MEDIA_LNK_FL_ENABLED : 0);
+ if (ret)
+ return ret;
+
+ if (enable)
+ tx->src = src;
+
+ return 0;
}
/* -----------------------------------------------------------------------------
@@ -68,24 +70,42 @@ static int adv748x_csi2_registered(struct v4l2_subdev *sd)
{
struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd);
struct adv748x_state *state = tx->state;
+ int ret;
adv_dbg(state, "Registered %s (%s)", is_txa(tx) ? "TXA":"TXB",
sd->name);
/*
- * The adv748x hardware allows the AFE to route through the TXA, however
- * this is not currently supported in this driver.
+ * Link TXA to AFE and HDMI, and TXB to AFE only as TXB cannot output
+ * HDMI.
*
- * Link HDMI->TXA, and AFE->TXB directly.
+ * The HDMI->TXA link is enabled by default, as is the AFE->TXB one.
*/
- if (is_txa(tx) && is_hdmi_enabled(state))
- return adv748x_csi2_register_link(tx, sd->v4l2_dev,
- &state->hdmi.sd,
- ADV748X_HDMI_SOURCE);
- if (!is_txa(tx) && is_afe_enabled(state))
- return adv748x_csi2_register_link(tx, sd->v4l2_dev,
- &state->afe.sd,
- ADV748X_AFE_SOURCE);
+ if (is_afe_enabled(state)) {
+ ret = adv748x_csi2_register_link(tx, sd->v4l2_dev,
+ &state->afe.sd,
+ ADV748X_AFE_SOURCE,
+ is_txb(tx));
+ if (ret)
+ return ret;
+
+ /* TXB can output AFE signals only. */
+ if (is_txb(tx))
+ state->afe.tx = tx;
+ }
+
+ /* Register link to HDMI for TXA only. */
+ if (is_txb(tx) || !is_hdmi_enabled(state))
+ return 0;
+
+ ret = adv748x_csi2_register_link(tx, sd->v4l2_dev, &state->hdmi.sd,
+ ADV748X_HDMI_SOURCE, true);
+ if (ret)
+ return ret;
+
+ /* The default HDMI output is TXA. */
+ state->hdmi.tx = tx;
+
return 0;
}
diff --git a/drivers/media/i2c/adv748x/adv748x-hdmi.c b/drivers/media/i2c/adv748x/adv748x-hdmi.c
index 35d027941482..c557f8fdf11a 100644
--- a/drivers/media/i2c/adv748x/adv748x-hdmi.c
+++ b/drivers/media/i2c/adv748x/adv748x-hdmi.c
@@ -358,7 +358,7 @@ static int adv748x_hdmi_s_stream(struct v4l2_subdev *sd, int enable)
mutex_lock(&state->mutex);
- ret = adv748x_tx_power(&state->txa, enable);
+ ret = adv748x_tx_power(hdmi->tx, enable);
if (ret)
goto done;
diff --git a/drivers/media/i2c/adv748x/adv748x.h b/drivers/media/i2c/adv748x/adv748x.h
index 39c2fdc3b416..5042f9e94aee 100644
--- a/drivers/media/i2c/adv748x/adv748x.h
+++ b/drivers/media/i2c/adv748x/adv748x.h
@@ -39,7 +39,6 @@ enum adv748x_page {
ADV748X_PAGE_MAX,
/* Fake pages for register sequences */
- ADV748X_PAGE_WAIT, /* Wait x msec */
ADV748X_PAGE_EOR, /* End Mark */
};
@@ -79,17 +78,23 @@ struct adv748x_csi2 {
struct v4l2_mbus_framefmt format;
unsigned int page;
unsigned int port;
+ unsigned int num_lanes;
struct media_pad pads[ADV748X_CSI2_NR_PADS];
struct v4l2_ctrl_handler ctrl_hdl;
struct v4l2_ctrl *pixel_rate;
+ struct v4l2_subdev *src;
struct v4l2_subdev sd;
};
#define notifier_to_csi2(n) container_of(n, struct adv748x_csi2, notifier)
#define adv748x_sd_to_csi2(sd) container_of(sd, struct adv748x_csi2, sd)
+
#define is_tx_enabled(_tx) ((_tx)->state->endpoints[(_tx)->port] != NULL)
#define is_txa(_tx) ((_tx) == &(_tx)->state->txa)
+#define is_txb(_tx) ((_tx) == &(_tx)->state->txb)
+#define is_tx(_tx) (is_txa(_tx) || is_txb(_tx))
+
#define is_afe_enabled(_state) \
((_state)->endpoints[ADV748X_PORT_AIN0] != NULL || \
(_state)->endpoints[ADV748X_PORT_AIN1] != NULL || \
@@ -116,6 +121,8 @@ struct adv748x_hdmi {
struct v4l2_dv_timings timings;
struct v4l2_fract aspect_ratio;
+ struct adv748x_csi2 *tx;
+
struct {
u8 edid[512];
u32 present;
@@ -146,6 +153,8 @@ struct adv748x_afe {
struct v4l2_subdev sd;
struct v4l2_mbus_framefmt format;
+ struct adv748x_csi2 *tx;
+
bool streaming;
v4l2_std_id curr_norm;
unsigned int input;
@@ -201,6 +210,11 @@ struct adv748x_state {
#define ADV748X_IO_PD 0x00 /* power down controls */
#define ADV748X_IO_PD_RX_EN BIT(6)
+#define ADV748X_IO_REG_01 0x01 /* pwrdn{2}b, prog_xtal_freq */
+#define ADV748X_IO_REG_01_PWRDN_MASK (BIT(7) | BIT(6))
+#define ADV748X_IO_REG_01_PWRDN2B BIT(7) /* CEC Wakeup Support */
+#define ADV748X_IO_REG_01_PWRDNB BIT(6) /* CEC Wakeup Support */
+
#define ADV748X_IO_REG_04 0x04
#define ADV748X_IO_REG_04_FORCE_FR BIT(0) /* Force CP free-run */
@@ -214,12 +228,24 @@ struct adv748x_state {
#define ADV748X_IO_10_CSI4_EN BIT(7)
#define ADV748X_IO_10_CSI1_EN BIT(6)
#define ADV748X_IO_10_PIX_OUT_EN BIT(5)
+#define ADV748X_IO_10_CSI4_IN_SEL_AFE BIT(3)
#define ADV748X_IO_CHIP_REV_ID_1 0xdf
#define ADV748X_IO_CHIP_REV_ID_2 0xe0
+#define ADV748X_IO_REG_F2 0xf2
+#define ADV748X_IO_REG_F2_READ_AUTO_INC BIT(0)
+
+/* For PAGE slave address offsets */
#define ADV748X_IO_SLAVE_ADDR_BASE 0xf2
+/*
+ * The ADV748x_Recommended_Settings_PrA_2014-08-20.pdf details both 0x80 and
+ * 0xff as examples for performing a software reset.
+ */
+#define ADV748X_IO_REG_FF 0xff
+#define ADV748X_IO_REG_FF_MAIN_RESET 0xff
+
/* HDMI RX Map */
#define ADV748X_HDMI_LW1 0x07 /* line width_1 */
#define ADV748X_HDMI_LW1_VERT_FILTER BIT(7)
diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c
index 989259488e3d..11ab2df02dc7 100644
--- a/drivers/media/i2c/adv7842.c
+++ b/drivers/media/i2c/adv7842.c
@@ -3102,11 +3102,11 @@ static int adv7842_ddr_ram_test(struct v4l2_subdev *sd)
io_write(sd, 0x00, 0x01); /* Program SDP 4x1 */
io_write(sd, 0x01, 0x00); /* Program SDP mode */
- afe_write(sd, 0x80, 0x92); /* SDP Recommeneded Write */
- afe_write(sd, 0x9B, 0x01); /* SDP Recommeneded Write ADV7844ES1 */
- afe_write(sd, 0x9C, 0x60); /* SDP Recommeneded Write ADV7844ES1 */
- afe_write(sd, 0x9E, 0x02); /* SDP Recommeneded Write ADV7844ES1 */
- afe_write(sd, 0xA0, 0x0B); /* SDP Recommeneded Write ADV7844ES1 */
+ afe_write(sd, 0x80, 0x92); /* SDP Recommended Write */
+ afe_write(sd, 0x9B, 0x01); /* SDP Recommended Write ADV7844ES1 */
+ afe_write(sd, 0x9C, 0x60); /* SDP Recommended Write ADV7844ES1 */
+ afe_write(sd, 0x9E, 0x02); /* SDP Recommended Write ADV7844ES1 */
+ afe_write(sd, 0xA0, 0x0B); /* SDP Recommended Write ADV7844ES1 */
afe_write(sd, 0xC3, 0x02); /* Memory BIST Initialisation */
io_write(sd, 0x0C, 0x40); /* Power up ADV7844 */
io_write(sd, 0x15, 0xBA); /* Enable outputs */
diff --git a/drivers/media/i2c/bt819.c b/drivers/media/i2c/bt819.c
index 472e37637c8d..e6d3fe7790bc 100644
--- a/drivers/media/i2c/bt819.c
+++ b/drivers/media/i2c/bt819.c
@@ -164,12 +164,12 @@ static int bt819_init(struct v4l2_subdev *sd)
0x0e, 0xb4, /* 0x0e Chroma Gain (V) msb */
0x0f, 0x00, /* 0x0f Hue control */
0x12, 0x04, /* 0x12 Output Format */
- 0x13, 0x20, /* 0x13 Vertial Scaling msb 0x00
+ 0x13, 0x20, /* 0x13 Vertical Scaling msb 0x00
chroma comb OFF, line drop scaling, interlace scaling
BUG? Why does turning the chroma comb on fuck up color?
Bug in the bt819 stepping on my board?
*/
- 0x14, 0x00, /* 0x14 Vertial Scaling lsb */
+ 0x14, 0x00, /* 0x14 Vertical Scaling lsb */
0x16, 0x07, /* 0x16 Video Timing Polarity
ACTIVE=active low
FIELD: high=odd,
diff --git a/drivers/media/i2c/cx25840/cx25840-core.c b/drivers/media/i2c/cx25840/cx25840-core.c
index b168bf3635b6..8b0b8b5aa531 100644
--- a/drivers/media/i2c/cx25840/cx25840-core.c
+++ b/drivers/media/i2c/cx25840/cx25840-core.c
@@ -5216,8 +5216,9 @@ static int cx25840_probe(struct i2c_client *client,
* those extra inputs. So, let's add it only when needed.
*/
state->pads[CX25840_PAD_INPUT].flags = MEDIA_PAD_FL_SINK;
+ state->pads[CX25840_PAD_INPUT].sig_type = PAD_SIGNAL_ANALOG;
state->pads[CX25840_PAD_VID_OUT].flags = MEDIA_PAD_FL_SOURCE;
- state->pads[CX25840_PAD_VBI_OUT].flags = MEDIA_PAD_FL_SOURCE;
+ state->pads[CX25840_PAD_VID_OUT].sig_type = PAD_SIGNAL_DV;
sd->entity.function = MEDIA_ENT_F_ATV_DECODER;
ret = media_entity_pads_init(&sd->entity, ARRAY_SIZE(state->pads),
diff --git a/drivers/media/i2c/cx25840/cx25840-core.h b/drivers/media/i2c/cx25840/cx25840-core.h
index c323b1af1f83..e3ff1d7ec770 100644
--- a/drivers/media/i2c/cx25840/cx25840-core.h
+++ b/drivers/media/i2c/cx25840/cx25840-core.h
@@ -40,7 +40,6 @@ enum cx25840_model {
enum cx25840_media_pads {
CX25840_PAD_INPUT,
CX25840_PAD_VID_OUT,
- CX25840_PAD_VBI_OUT,
CX25840_NUM_PADS
};
@@ -67,7 +66,7 @@ enum cx25840_media_pads {
* @is_initialized: whether we have already loaded firmware into the chip
* and initialized it
* @vbi_regs_offset: offset of vbi regs
- * @fw_wait: wait queue to wake an initalization function up when
+ * @fw_wait: wait queue to wake an initialization function up when
* firmware loading (on a separate workqueue) finishes
* @fw_work: a work that actually loads the firmware on a separate
* workqueue
diff --git a/drivers/media/i2c/cx25840/cx25840-ir.c b/drivers/media/i2c/cx25840/cx25840-ir.c
index 69cdc09981af..a266118cd7ca 100644
--- a/drivers/media/i2c/cx25840/cx25840-ir.c
+++ b/drivers/media/i2c/cx25840/cx25840-ir.c
@@ -549,7 +549,7 @@ int cx25840_ir_irq_handler(struct v4l2_subdev *sd, u32 status, bool *handled)
ror = stats & STATS_ROR; /* Rx FIFO Over Run */
tse = irqen & IRQEN_TSE; /* Tx FIFO Service Request IRQ Enable */
- rse = irqen & IRQEN_RSE; /* Rx FIFO Service Reuqest IRQ Enable */
+ rse = irqen & IRQEN_RSE; /* Rx FIFO Service Request IRQ Enable */
rte = irqen & IRQEN_RTE; /* Rx Pulse Width Timer Time Out IRQ Enable */
roe = irqen & IRQEN_ROE; /* Rx FIFO Over Run IRQ Enable */
@@ -638,7 +638,7 @@ int cx25840_ir_irq_handler(struct v4l2_subdev *sd, u32 status, bool *handled)
events |= V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED;
}
if (v) {
- /* Clear STATS_ROR & STATS_RTO as needed by reseting hardware */
+ /* Clear STATS_ROR & STATS_RTO as needed by resetting hardware */
cx25840_write4(c, CX25840_IR_CNTRL_REG, cntrl & ~v);
cx25840_write4(c, CX25840_IR_CNTRL_REG, cntrl);
*handled = true;
diff --git a/drivers/media/i2c/dw9714.c b/drivers/media/i2c/dw9714.c
index 26d83693a681..3f0b082f863f 100644
--- a/drivers/media/i2c/dw9714.c
+++ b/drivers/media/i2c/dw9714.c
@@ -267,7 +267,7 @@ static struct i2c_driver dw9714_i2c_driver = {
module_i2c_driver(dw9714_i2c_driver);
MODULE_AUTHOR("Tianshu Qiu <tian.shu.qiu@intel.com>");
-MODULE_AUTHOR("Jian Xu Zheng <jian.xu.zheng@intel.com>");
+MODULE_AUTHOR("Jian Xu Zheng");
MODULE_AUTHOR("Yuning Pu <yuning.pu@intel.com>");
MODULE_AUTHOR("Jouni Ukkonen <jouni.ukkonen@intel.com>");
MODULE_AUTHOR("Tommi Franttila <tommi.franttila@intel.com>");
diff --git a/drivers/media/i2c/et8ek8/et8ek8_mode.c b/drivers/media/i2c/et8ek8/et8ek8_mode.c
index a79882a83885..f503303cb8bc 100644
--- a/drivers/media/i2c/et8ek8/et8ek8_mode.c
+++ b/drivers/media/i2c/et8ek8/et8ek8_mode.c
@@ -79,7 +79,7 @@ static struct et8ek8_reglist mode1_poweron_mode2_16vga_2592x1968_12_07fps = {
{ ET8EK8_REG_8BIT, 0x1258, 0x00 },
/* From parallel out to serial out */
{ ET8EK8_REG_8BIT, 0x125D, 0x88 },
- /* From w/ embeded data to w/o embeded data */
+ /* From w/ embedded data to w/o embedded data */
{ ET8EK8_REG_8BIT, 0x125E, 0xC0 },
/* CCP2 out is from STOP to ACTIVE */
{ ET8EK8_REG_8BIT, 0x1263, 0x98 },
diff --git a/drivers/media/i2c/imx214.c b/drivers/media/i2c/imx214.c
index ec3d1b855f62..9857e151db46 100644
--- a/drivers/media/i2c/imx214.c
+++ b/drivers/media/i2c/imx214.c
@@ -377,7 +377,7 @@ static const struct reg_8 mode_table_common[] = {
/* Moire reduction */
{0x6957, 0x01},
- /* image enhancment */
+ /* image enhancement */
{0x6987, 0x17},
{0x698A, 0x03},
{0x698B, 0x03},
diff --git a/drivers/media/i2c/imx274.c b/drivers/media/i2c/imx274.c
index 5fac7fd32634..f3ff1af209f9 100644
--- a/drivers/media/i2c/imx274.c
+++ b/drivers/media/i2c/imx274.c
@@ -207,8 +207,8 @@ static const char * const tp_qmenu[] = {
"Vertical Stripe (555h / 000h)",
"Vertical Stripe (000h / FFFh)",
"Vertical Stripe (FFFh / 000h)",
- "Horizontal Color Bars",
"Vertical Color Bars",
+ "Horizontal Color Bars",
};
/*
@@ -405,12 +405,12 @@ static const struct reg_8 imx274_start_2[] = {
*/
static const struct reg_8 imx274_start_3[] = {
{0x30F4, 0x00},
- {0x3018, 0xA2}, /* XHS VHS OUTUPT */
+ {0x3018, 0xA2}, /* XHS VHS OUTPUT */
{IMX274_TABLE_END, 0x00}
};
/*
- * imx274 register configuration for stoping stream
+ * imx274 register configuration for stopping stream
*/
static const struct reg_8 imx274_stop[] = {
{IMX274_STANDBY_REG, 0x01},
@@ -617,24 +617,6 @@ static int imx274_write_table(struct stimx274 *priv, const struct reg_8 table[])
return 0;
}
-static inline int imx274_read_reg(struct stimx274 *priv, u16 addr, u8 *val)
-{
- unsigned int uint_val;
- int err;
-
- err = regmap_read(priv->regmap, addr, &uint_val);
- if (err)
- dev_err(&priv->client->dev,
- "%s : i2c read failed, addr = %x\n", __func__, addr);
- else
- dev_dbg(&priv->client->dev,
- "%s : addr 0x%x, val=0x%x\n", __func__,
- addr, uint_val);
-
- *val = uint_val;
- return err;
-}
-
static inline int imx274_write_reg(struct stimx274 *priv, u16 addr, u8 val)
{
int err;
diff --git a/drivers/media/i2c/lm3560.c b/drivers/media/i2c/lm3560.c
index f122f03bd6b7..70c3294c21d3 100644
--- a/drivers/media/i2c/lm3560.c
+++ b/drivers/media/i2c/lm3560.c
@@ -55,7 +55,7 @@ enum led_enable {
* @regmap: reg. map for i2c
* @lock: muxtex for serial access.
* @led_mode: V4L2 LED mode
- * @ctrls_led: V4L2 contols
+ * @ctrls_led: V4L2 controls
* @subdev_led: V4L2 subdev
*/
struct lm3560_flash {
diff --git a/drivers/media/i2c/lm3646.c b/drivers/media/i2c/lm3646.c
index 12ef2653987b..73fbe3c37fc9 100644
--- a/drivers/media/i2c/lm3646.c
+++ b/drivers/media/i2c/lm3646.c
@@ -62,7 +62,7 @@ enum led_mode {
* @regmap: reg. map for i2c
* @lock: muxtex for serial access.
* @led_mode: V4L2 LED mode
- * @ctrls_led: V4L2 contols
+ * @ctrls_led: V4L2 controls
* @subdev_led: V4L2 subdev
* @mode_reg : mode register value
*/
diff --git a/drivers/media/i2c/m5mols/m5mols.h b/drivers/media/i2c/m5mols/m5mols.h
index 90a6c520f115..aef5b4f8904e 100644
--- a/drivers/media/i2c/m5mols/m5mols.h
+++ b/drivers/media/i2c/m5mols/m5mols.h
@@ -253,7 +253,7 @@ struct m5mols_info {
*
* The I2C read operation of the M-5MOLS requires 2 messages. The first
* message sends the information about the command, command category, and total
- * message size. The second message is used to retrieve the data specifed in
+ * message size. The second message is used to retrieve the data specified in
* the first message
*
* 1st message 2nd message
diff --git a/drivers/media/i2c/m5mols/m5mols_core.c b/drivers/media/i2c/m5mols/m5mols_core.c
index b8b2bf4cbfb2..454a336be336 100644
--- a/drivers/media/i2c/m5mols/m5mols_core.c
+++ b/drivers/media/i2c/m5mols/m5mols_core.c
@@ -291,7 +291,7 @@ int m5mols_write(struct v4l2_subdev *sd, u32 reg, u32 val)
* @reg: the I2C_REG() address of an 8-bit status register to check
* @value: expected status register value
* @mask: bit mask for the read status register value
- * @timeout: timeout in miliseconds, or -1 for default timeout
+ * @timeout: timeout in milliseconds, or -1 for default timeout
*
* The @reg register value is ORed with @mask before comparing with @value.
*
diff --git a/drivers/media/i2c/msp3400-driver.c b/drivers/media/i2c/msp3400-driver.c
index c63be01059b2..522fb1d561e7 100644
--- a/drivers/media/i2c/msp3400-driver.c
+++ b/drivers/media/i2c/msp3400-driver.c
@@ -11,7 +11,7 @@
*
* FM-Mono
* should work. The stereo modes are backward compatible to FM-mono,
- * therefore FM-Mono should be allways available.
+ * therefore FM-Mono should be always available.
*
* FM-Stereo (B/G, used in germany)
* should work, with autodetect
diff --git a/drivers/media/i2c/soc_camera/soc_mt9m001.c b/drivers/media/i2c/mt9m001.c
index a1a85ff838c5..4b23fde937b3 100644
--- a/drivers/media/i2c/soc_camera/soc_mt9m001.c
+++ b/drivers/media/i2c/mt9m001.c
@@ -1,29 +1,27 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Driver for MT9M001 CMOS Image Sensor from Micron
*
* Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
- *
- * 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 <linux/videodev2.h>
-#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/log2.h>
#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
-#include <media/soc_camera.h>
-#include <media/drv-intf/soc_mediabus.h>
-#include <media/v4l2-clk.h>
-#include <media/v4l2-subdev.h>
#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-subdev.h>
/*
* mt9m001 i2c address 0x5d
- * The platform has to define struct i2c_board_info objects and link to them
- * from struct soc_camera_host_desc
*/
/* mt9m001 selected register addresses */
@@ -50,6 +48,8 @@
#define MT9M001_MIN_HEIGHT 32
#define MT9M001_COLUMN_SKIP 20
#define MT9M001_ROW_SKIP 12
+#define MT9M001_DEFAULT_HBLANK 9
+#define MT9M001_DEFAULT_VBLANK 25
/* MT9M001 has only one fixed colorspace per pixelcode */
struct mt9m001_datafmt {
@@ -93,13 +93,18 @@ struct mt9m001 {
struct v4l2_ctrl *autoexposure;
struct v4l2_ctrl *exposure;
};
+ bool streaming;
+ struct mutex mutex;
struct v4l2_rect rect; /* Sensor window */
- struct v4l2_clk *clk;
+ struct clk *clk;
+ struct gpio_desc *standby_gpio;
+ struct gpio_desc *reset_gpio;
const struct mt9m001_datafmt *fmt;
const struct mt9m001_datafmt *fmts;
int num_fmts;
unsigned int total_h;
unsigned short y_skip_top; /* Lines to skip at the top */
+ struct media_pad pad;
};
static struct mt9m001 *to_mt9m001(const struct i2c_client *client)
@@ -140,35 +145,111 @@ static int reg_clear(struct i2c_client *client, const u8 reg,
return reg_write(client, reg, ret & ~data);
}
+struct mt9m001_reg {
+ u8 reg;
+ u16 data;
+};
+
+static int multi_reg_write(struct i2c_client *client,
+ const struct mt9m001_reg *regs, int num)
+{
+ int i;
+
+ for (i = 0; i < num; i++) {
+ int ret = reg_write(client, regs[i].reg, regs[i].data);
+
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
static int mt9m001_init(struct i2c_client *client)
{
- int ret;
+ const struct mt9m001_reg init_regs[] = {
+ /*
+ * Issue a soft reset. This returns all registers to their
+ * default values.
+ */
+ { MT9M001_RESET, 1 },
+ { MT9M001_RESET, 0 },
+ /* Disable chip, synchronous option update */
+ { MT9M001_OUTPUT_CONTROL, 0 }
+ };
dev_dbg(&client->dev, "%s\n", __func__);
- /*
- * We don't know, whether platform provides reset, issue a soft reset
- * too. This returns all registers to their default values.
- */
- ret = reg_write(client, MT9M001_RESET, 1);
- if (!ret)
- ret = reg_write(client, MT9M001_RESET, 0);
+ return multi_reg_write(client, init_regs, ARRAY_SIZE(init_regs));
+}
- /* Disable chip, synchronous option update */
- if (!ret)
- ret = reg_write(client, MT9M001_OUTPUT_CONTROL, 0);
+static int mt9m001_apply_selection(struct v4l2_subdev *sd)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9m001 *mt9m001 = to_mt9m001(client);
+ const struct mt9m001_reg regs[] = {
+ /* Blanking and start values - default... */
+ { MT9M001_HORIZONTAL_BLANKING, MT9M001_DEFAULT_HBLANK },
+ { MT9M001_VERTICAL_BLANKING, MT9M001_DEFAULT_VBLANK },
+ /*
+ * The caller provides a supported format, as verified per
+ * call to .set_fmt(FORMAT_TRY).
+ */
+ { MT9M001_COLUMN_START, mt9m001->rect.left },
+ { MT9M001_ROW_START, mt9m001->rect.top },
+ { MT9M001_WINDOW_WIDTH, mt9m001->rect.width - 1 },
+ { MT9M001_WINDOW_HEIGHT,
+ mt9m001->rect.height + mt9m001->y_skip_top - 1 },
+ };
- return ret;
+ return multi_reg_write(client, regs, ARRAY_SIZE(regs));
}
static int mt9m001_s_stream(struct v4l2_subdev *sd, int enable)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9m001 *mt9m001 = to_mt9m001(client);
+ int ret = 0;
+
+ mutex_lock(&mt9m001->mutex);
+
+ if (mt9m001->streaming == enable)
+ goto done;
+
+ if (enable) {
+ ret = pm_runtime_get_sync(&client->dev);
+ if (ret < 0)
+ goto put_unlock;
+
+ ret = mt9m001_apply_selection(sd);
+ if (ret)
+ goto put_unlock;
+
+ ret = __v4l2_ctrl_handler_setup(&mt9m001->hdl);
+ if (ret)
+ goto put_unlock;
+
+ /* Switch to master "normal" mode */
+ ret = reg_write(client, MT9M001_OUTPUT_CONTROL, 2);
+ if (ret < 0)
+ goto put_unlock;
+ } else {
+ /* Switch to master stop sensor readout */
+ reg_write(client, MT9M001_OUTPUT_CONTROL, 0);
+ pm_runtime_put(&client->dev);
+ }
+
+ mt9m001->streaming = enable;
+done:
+ mutex_unlock(&mt9m001->mutex);
- /* Switch to master "normal" mode or stop sensor readout */
- if (reg_write(client, MT9M001_OUTPUT_CONTROL, enable ? 2 : 0) < 0)
- return -EIO;
return 0;
+
+put_unlock:
+ pm_runtime_put(&client->dev);
+ mutex_unlock(&mt9m001->mutex);
+
+ return ret;
}
static int mt9m001_set_selection(struct v4l2_subdev *sd,
@@ -178,8 +259,6 @@ static int mt9m001_set_selection(struct v4l2_subdev *sd,
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct mt9m001 *mt9m001 = to_mt9m001(client);
struct v4l2_rect rect = sel->r;
- const u16 hblank = 9, vblank = 25;
- int ret;
if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE ||
sel->target != V4L2_SEL_TGT_CROP)
@@ -196,39 +275,22 @@ static int mt9m001_set_selection(struct v4l2_subdev *sd,
rect.width = ALIGN(rect.width, 2);
rect.left = ALIGN(rect.left, 2);
- soc_camera_limit_side(&rect.left, &rect.width,
- MT9M001_COLUMN_SKIP, MT9M001_MIN_WIDTH, MT9M001_MAX_WIDTH);
+ rect.width = clamp_t(u32, rect.width, MT9M001_MIN_WIDTH,
+ MT9M001_MAX_WIDTH);
+ rect.left = clamp_t(u32, rect.left, MT9M001_COLUMN_SKIP,
+ MT9M001_COLUMN_SKIP + MT9M001_MAX_WIDTH - rect.width);
- soc_camera_limit_side(&rect.top, &rect.height,
- MT9M001_ROW_SKIP, MT9M001_MIN_HEIGHT, MT9M001_MAX_HEIGHT);
+ rect.height = clamp_t(u32, rect.height, MT9M001_MIN_HEIGHT,
+ MT9M001_MAX_HEIGHT);
+ rect.top = clamp_t(u32, rect.top, MT9M001_ROW_SKIP,
+ MT9M001_ROW_SKIP + MT9M001_MAX_HEIGHT - rect.height);
- mt9m001->total_h = rect.height + mt9m001->y_skip_top + vblank;
+ mt9m001->total_h = rect.height + mt9m001->y_skip_top +
+ MT9M001_DEFAULT_VBLANK;
- /* Blanking and start values - default... */
- ret = reg_write(client, MT9M001_HORIZONTAL_BLANKING, hblank);
- if (!ret)
- ret = reg_write(client, MT9M001_VERTICAL_BLANKING, vblank);
+ mt9m001->rect = rect;
- /*
- * The caller provides a supported format, as verified per
- * call to .set_fmt(FORMAT_TRY).
- */
- if (!ret)
- ret = reg_write(client, MT9M001_COLUMN_START, rect.left);
- if (!ret)
- ret = reg_write(client, MT9M001_ROW_START, rect.top);
- if (!ret)
- ret = reg_write(client, MT9M001_WINDOW_WIDTH, rect.width - 1);
- if (!ret)
- ret = reg_write(client, MT9M001_WINDOW_HEIGHT,
- rect.height + mt9m001->y_skip_top - 1);
- if (!ret && v4l2_ctrl_g_ctrl(mt9m001->autoexposure) == V4L2_EXPOSURE_AUTO)
- ret = reg_write(client, MT9M001_SHUTTER_WIDTH, mt9m001->total_h);
-
- if (!ret)
- mt9m001->rect = rect;
-
- return ret;
+ return 0;
}
static int mt9m001_get_selection(struct v4l2_subdev *sd,
@@ -267,11 +329,20 @@ static int mt9m001_get_fmt(struct v4l2_subdev *sd,
if (format->pad)
return -EINVAL;
+ if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
+ mf = v4l2_subdev_get_try_format(sd, cfg, 0);
+ format->format = *mf;
+ return 0;
+ }
+
mf->width = mt9m001->rect.width;
mf->height = mt9m001->rect.height;
mf->code = mt9m001->fmt->code;
mf->colorspace = mt9m001->fmt->colorspace;
mf->field = V4L2_FIELD_NONE;
+ mf->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ mf->quantization = V4L2_QUANTIZATION_DEFAULT;
+ mf->xfer_func = V4L2_XFER_FUNC_DEFAULT;
return 0;
}
@@ -332,6 +403,10 @@ static int mt9m001_set_fmt(struct v4l2_subdev *sd,
}
mf->colorspace = fmt->colorspace;
+ mf->field = V4L2_FIELD_NONE;
+ mf->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ mf->quantization = V4L2_QUANTIZATION_DEFAULT;
+ mf->xfer_func = V4L2_XFER_FUNC_DEFAULT;
if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE)
return mt9m001_s_fmt(sd, fmt, mf);
@@ -372,13 +447,40 @@ static int mt9m001_s_register(struct v4l2_subdev *sd,
}
#endif
-static int mt9m001_s_power(struct v4l2_subdev *sd, int on)
+static int mt9m001_power_on(struct device *dev)
{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
+ struct i2c_client *client = to_i2c_client(dev);
struct mt9m001 *mt9m001 = to_mt9m001(client);
+ int ret;
+
+ ret = clk_prepare_enable(mt9m001->clk);
+ if (ret)
+ return ret;
+
+ if (mt9m001->standby_gpio) {
+ gpiod_set_value_cansleep(mt9m001->standby_gpio, 0);
+ usleep_range(1000, 2000);
+ }
+
+ if (mt9m001->reset_gpio) {
+ gpiod_set_value_cansleep(mt9m001->reset_gpio, 1);
+ usleep_range(1000, 2000);
+ gpiod_set_value_cansleep(mt9m001->reset_gpio, 0);
+ usleep_range(1000, 2000);
+ }
- return soc_camera_set_power(&client->dev, ssdd, mt9m001->clk, on);
+ return 0;
+}
+
+static int mt9m001_power_off(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct mt9m001 *mt9m001 = to_mt9m001(client);
+
+ gpiod_set_value_cansleep(mt9m001->standby_gpio, 1);
+ clk_disable_unprepare(mt9m001->clk);
+
+ return 0;
}
static int mt9m001_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
@@ -406,16 +508,18 @@ static int mt9m001_s_ctrl(struct v4l2_ctrl *ctrl)
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct v4l2_ctrl *exp = mt9m001->exposure;
int data;
+ int ret;
+
+ if (!pm_runtime_get_if_in_use(&client->dev))
+ return 0;
switch (ctrl->id) {
case V4L2_CID_VFLIP:
if (ctrl->val)
- data = reg_set(client, MT9M001_READ_OPTIONS2, 0x8000);
+ ret = reg_set(client, MT9M001_READ_OPTIONS2, 0x8000);
else
- data = reg_clear(client, MT9M001_READ_OPTIONS2, 0x8000);
- if (data < 0)
- return -EIO;
- return 0;
+ ret = reg_clear(client, MT9M001_READ_OPTIONS2, 0x8000);
+ break;
case V4L2_CID_GAIN:
/* See Datasheet Table 7, Gain settings. */
@@ -425,9 +529,7 @@ static int mt9m001_s_ctrl(struct v4l2_ctrl *ctrl)
data = ((ctrl->val - (s32)ctrl->minimum) * 8 + range / 2) / range;
dev_dbg(&client->dev, "Setting gain %d\n", data);
- data = reg_write(client, MT9M001_GLOBAL_GAIN, data);
- if (data < 0)
- return -EIO;
+ ret = reg_write(client, MT9M001_GLOBAL_GAIN, data);
} else {
/* Pack it into 1.125..15 variable step, register values 9..67 */
/* We assume qctrl->maximum - qctrl->default_value - 1 > 0 */
@@ -444,11 +546,9 @@ static int mt9m001_s_ctrl(struct v4l2_ctrl *ctrl)
dev_dbg(&client->dev, "Setting gain from %d to %d\n",
reg_read(client, MT9M001_GLOBAL_GAIN), data);
- data = reg_write(client, MT9M001_GLOBAL_GAIN, data);
- if (data < 0)
- return -EIO;
+ ret = reg_write(client, MT9M001_GLOBAL_GAIN, data);
}
- return 0;
+ break;
case V4L2_CID_EXPOSURE_AUTO:
if (ctrl->val == V4L2_EXPOSURE_MANUAL) {
@@ -459,37 +559,34 @@ static int mt9m001_s_ctrl(struct v4l2_ctrl *ctrl)
dev_dbg(&client->dev,
"Setting shutter width from %d to %lu\n",
reg_read(client, MT9M001_SHUTTER_WIDTH), shutter);
- if (reg_write(client, MT9M001_SHUTTER_WIDTH, shutter) < 0)
- return -EIO;
+ ret = reg_write(client, MT9M001_SHUTTER_WIDTH, shutter);
} else {
- const u16 vblank = 25;
-
mt9m001->total_h = mt9m001->rect.height +
- mt9m001->y_skip_top + vblank;
- if (reg_write(client, MT9M001_SHUTTER_WIDTH, mt9m001->total_h) < 0)
- return -EIO;
+ mt9m001->y_skip_top + MT9M001_DEFAULT_VBLANK;
+ ret = reg_write(client, MT9M001_SHUTTER_WIDTH,
+ mt9m001->total_h);
}
- return 0;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
}
- return -EINVAL;
+
+ pm_runtime_put(&client->dev);
+
+ return ret;
}
/*
* Interface active, can use i2c. If it fails, it can indeed mean, that
* this wasn't our capture interface, so, we wait for the right one
*/
-static int mt9m001_video_probe(struct soc_camera_subdev_desc *ssdd,
- struct i2c_client *client)
+static int mt9m001_video_probe(struct i2c_client *client)
{
struct mt9m001 *mt9m001 = to_mt9m001(client);
s32 data;
- unsigned long flags;
int ret;
- ret = mt9m001_s_power(&mt9m001->subdev, 1);
- if (ret < 0)
- return ret;
-
/* Enable the chip */
data = reg_write(client, MT9M001_CHIP_ENABLE, 1);
dev_dbg(&client->dev, "write: %d\n", data);
@@ -502,9 +599,11 @@ static int mt9m001_video_probe(struct soc_camera_subdev_desc *ssdd,
case 0x8411:
case 0x8421:
mt9m001->fmts = mt9m001_colour_fmts;
+ mt9m001->num_fmts = ARRAY_SIZE(mt9m001_colour_fmts);
break;
case 0x8431:
mt9m001->fmts = mt9m001_monochrome_fmts;
+ mt9m001->num_fmts = ARRAY_SIZE(mt9m001_monochrome_fmts);
break;
default:
dev_err(&client->dev,
@@ -513,26 +612,6 @@ static int mt9m001_video_probe(struct soc_camera_subdev_desc *ssdd,
goto done;
}
- mt9m001->num_fmts = 0;
-
- /*
- * This is a 10bit sensor, so by default we only allow 10bit.
- * The platform may support different bus widths due to
- * different routing of the data lines.
- */
- if (ssdd->query_bus_param)
- flags = ssdd->query_bus_param(ssdd);
- else
- flags = SOCAM_DATAWIDTH_10;
-
- if (flags & SOCAM_DATAWIDTH_10)
- mt9m001->num_fmts++;
- else
- mt9m001->fmts++;
-
- if (flags & SOCAM_DATAWIDTH_8)
- mt9m001->num_fmts++;
-
mt9m001->fmt = &mt9m001->fmts[0];
dev_info(&client->dev, "Detected a MT9M001 chip ID %x (%s)\n", data,
@@ -548,16 +627,9 @@ static int mt9m001_video_probe(struct soc_camera_subdev_desc *ssdd,
ret = v4l2_ctrl_handler_setup(&mt9m001->hdl);
done:
- mt9m001_s_power(&mt9m001->subdev, 0);
return ret;
}
-static void mt9m001_video_remove(struct soc_camera_subdev_desc *ssdd)
-{
- if (ssdd->free_bus)
- ssdd->free_bus(ssdd);
-}
-
static int mt9m001_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
@@ -574,13 +646,35 @@ static const struct v4l2_ctrl_ops mt9m001_ctrl_ops = {
};
static const struct v4l2_subdev_core_ops mt9m001_subdev_core_ops = {
+ .log_status = v4l2_ctrl_subdev_log_status,
+ .subscribe_event = v4l2_ctrl_subdev_subscribe_event,
+ .unsubscribe_event = v4l2_event_subdev_unsubscribe,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = mt9m001_g_register,
.s_register = mt9m001_s_register,
#endif
- .s_power = mt9m001_s_power,
};
+static int mt9m001_init_cfg(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9m001 *mt9m001 = to_mt9m001(client);
+ struct v4l2_mbus_framefmt *try_fmt =
+ v4l2_subdev_get_try_format(sd, cfg, 0);
+
+ try_fmt->width = MT9M001_MAX_WIDTH;
+ try_fmt->height = MT9M001_MAX_HEIGHT;
+ try_fmt->code = mt9m001->fmts[0].code;
+ try_fmt->colorspace = mt9m001->fmts[0].colorspace;
+ try_fmt->field = V4L2_FIELD_NONE;
+ try_fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ try_fmt->quantization = V4L2_QUANTIZATION_DEFAULT;
+ try_fmt->xfer_func = V4L2_XFER_FUNC_DEFAULT;
+
+ return 0;
+}
+
static int mt9m001_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_mbus_code_enum *code)
@@ -598,41 +692,18 @@ static int mt9m001_enum_mbus_code(struct v4l2_subdev *sd,
static int mt9m001_g_mbus_config(struct v4l2_subdev *sd,
struct v4l2_mbus_config *cfg)
{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
-
/* MT9M001 has all capture_format parameters fixed */
cfg->flags = V4L2_MBUS_PCLK_SAMPLE_FALLING |
V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH |
V4L2_MBUS_DATA_ACTIVE_HIGH | V4L2_MBUS_MASTER;
cfg->type = V4L2_MBUS_PARALLEL;
- cfg->flags = soc_camera_apply_board_flags(ssdd, cfg);
return 0;
}
-static int mt9m001_s_mbus_config(struct v4l2_subdev *sd,
- const struct v4l2_mbus_config *cfg)
-{
- const struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
- struct mt9m001 *mt9m001 = to_mt9m001(client);
- unsigned int bps = soc_mbus_get_fmtdesc(mt9m001->fmt->code)->bits_per_sample;
-
- if (ssdd->set_bus_param)
- return ssdd->set_bus_param(ssdd, 1 << (bps - 1));
-
- /*
- * Without board specific bus width settings we only support the
- * sensors native bus width
- */
- return bps == 10 ? 0 : -EINVAL;
-}
-
static const struct v4l2_subdev_video_ops mt9m001_subdev_video_ops = {
.s_stream = mt9m001_s_stream,
.g_mbus_config = mt9m001_g_mbus_config,
- .s_mbus_config = mt9m001_s_mbus_config,
};
static const struct v4l2_subdev_sensor_ops mt9m001_subdev_sensor_ops = {
@@ -640,6 +711,7 @@ static const struct v4l2_subdev_sensor_ops mt9m001_subdev_sensor_ops = {
};
static const struct v4l2_subdev_pad_ops mt9m001_subdev_pad_ops = {
+ .init_cfg = mt9m001_init_cfg,
.enum_mbus_code = mt9m001_enum_mbus_code,
.get_selection = mt9m001_get_selection,
.set_selection = mt9m001_set_selection,
@@ -659,25 +731,35 @@ static int mt9m001_probe(struct i2c_client *client,
{
struct mt9m001 *mt9m001;
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
- struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
int ret;
- if (!ssdd) {
- dev_err(&client->dev, "MT9M001 driver needs platform data\n");
- return -EINVAL;
- }
-
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
dev_warn(&adapter->dev,
"I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n");
return -EIO;
}
- mt9m001 = devm_kzalloc(&client->dev, sizeof(struct mt9m001), GFP_KERNEL);
+ mt9m001 = devm_kzalloc(&client->dev, sizeof(*mt9m001), GFP_KERNEL);
if (!mt9m001)
return -ENOMEM;
+ mt9m001->clk = devm_clk_get(&client->dev, NULL);
+ if (IS_ERR(mt9m001->clk))
+ return PTR_ERR(mt9m001->clk);
+
+ mt9m001->standby_gpio = devm_gpiod_get_optional(&client->dev, "standby",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(mt9m001->standby_gpio))
+ return PTR_ERR(mt9m001->standby_gpio);
+
+ mt9m001->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(mt9m001->reset_gpio))
+ return PTR_ERR(mt9m001->reset_gpio);
+
v4l2_i2c_subdev_init(&mt9m001->subdev, client, &mt9m001_subdev_ops);
+ mt9m001->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
+ V4L2_SUBDEV_FL_HAS_EVENTS;
v4l2_ctrl_handler_init(&mt9m001->hdl, 4);
v4l2_ctrl_new_std(&mt9m001->hdl, &mt9m001_ctrl_ops,
V4L2_CID_VFLIP, 0, 1, 1, 0);
@@ -699,6 +781,9 @@ static int mt9m001_probe(struct i2c_client *client,
v4l2_ctrl_auto_cluster(2, &mt9m001->autoexposure,
V4L2_EXPOSURE_MANUAL, true);
+ mutex_init(&mt9m001->mutex);
+ mt9m001->hdl.lock = &mt9m001->mutex;
+
/* Second stage probe - when a capture adapter is there */
mt9m001->y_skip_top = 0;
mt9m001->rect.left = MT9M001_COLUMN_SKIP;
@@ -706,18 +791,41 @@ static int mt9m001_probe(struct i2c_client *client,
mt9m001->rect.width = MT9M001_MAX_WIDTH;
mt9m001->rect.height = MT9M001_MAX_HEIGHT;
- mt9m001->clk = v4l2_clk_get(&client->dev, "mclk");
- if (IS_ERR(mt9m001->clk)) {
- ret = PTR_ERR(mt9m001->clk);
- goto eclkget;
- }
+ ret = mt9m001_power_on(&client->dev);
+ if (ret)
+ goto error_hdl_free;
- ret = mt9m001_video_probe(ssdd, client);
- if (ret) {
- v4l2_clk_put(mt9m001->clk);
-eclkget:
- v4l2_ctrl_handler_free(&mt9m001->hdl);
- }
+ pm_runtime_set_active(&client->dev);
+ pm_runtime_enable(&client->dev);
+
+ ret = mt9m001_video_probe(client);
+ if (ret)
+ goto error_power_off;
+
+ mt9m001->pad.flags = MEDIA_PAD_FL_SOURCE;
+ mt9m001->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+ ret = media_entity_pads_init(&mt9m001->subdev.entity, 1, &mt9m001->pad);
+ if (ret)
+ goto error_power_off;
+
+ ret = v4l2_async_register_subdev(&mt9m001->subdev);
+ if (ret)
+ goto error_entity_cleanup;
+
+ pm_runtime_idle(&client->dev);
+
+ return 0;
+
+error_entity_cleanup:
+ media_entity_cleanup(&mt9m001->subdev.entity);
+error_power_off:
+ pm_runtime_disable(&client->dev);
+ pm_runtime_set_suspended(&client->dev);
+ mt9m001_power_off(&client->dev);
+
+error_hdl_free:
+ v4l2_ctrl_handler_free(&mt9m001->hdl);
+ mutex_destroy(&mt9m001->mutex);
return ret;
}
@@ -725,12 +833,19 @@ eclkget:
static int mt9m001_remove(struct i2c_client *client)
{
struct mt9m001 *mt9m001 = to_mt9m001(client);
- struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
- v4l2_clk_put(mt9m001->clk);
- v4l2_device_unregister_subdev(&mt9m001->subdev);
+ pm_runtime_get_sync(&client->dev);
+
+ v4l2_async_unregister_subdev(&mt9m001->subdev);
+ media_entity_cleanup(&mt9m001->subdev.entity);
+
+ pm_runtime_disable(&client->dev);
+ pm_runtime_set_suspended(&client->dev);
+ pm_runtime_put_noidle(&client->dev);
+ mt9m001_power_off(&client->dev);
+
v4l2_ctrl_handler_free(&mt9m001->hdl);
- mt9m001_video_remove(ssdd);
+ mutex_destroy(&mt9m001->mutex);
return 0;
}
@@ -741,9 +856,21 @@ static const struct i2c_device_id mt9m001_id[] = {
};
MODULE_DEVICE_TABLE(i2c, mt9m001_id);
+static const struct dev_pm_ops mt9m001_pm_ops = {
+ SET_RUNTIME_PM_OPS(mt9m001_power_off, mt9m001_power_on, NULL)
+};
+
+static const struct of_device_id mt9m001_of_match[] = {
+ { .compatible = "onnn,mt9m001", },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, mt9m001_of_match);
+
static struct i2c_driver mt9m001_i2c_driver = {
.driver = {
.name = "mt9m001",
+ .pm = &mt9m001_pm_ops,
+ .of_match_table = mt9m001_of_match,
},
.probe = mt9m001_probe,
.remove = mt9m001_remove,
@@ -754,4 +881,4 @@ module_i2c_driver(mt9m001_i2c_driver);
MODULE_DESCRIPTION("Micron MT9M001 Camera driver");
MODULE_AUTHOR("Guennadi Liakhovetski <kernel@pengutronix.de>");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/i2c/mt9m111.c b/drivers/media/i2c/mt9m111.c
index d639b9bcf64a..5168bb5880c4 100644
--- a/drivers/media/i2c/mt9m111.c
+++ b/drivers/media/i2c/mt9m111.c
@@ -528,11 +528,24 @@ static int mt9m111_get_fmt(struct v4l2_subdev *sd,
if (format->pad)
return -EINVAL;
+ if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+ mf = v4l2_subdev_get_try_format(sd, cfg, format->pad);
+ format->format = *mf;
+ return 0;
+#else
+ return -ENOTTY;
+#endif
+ }
+
mf->width = mt9m111->width;
mf->height = mt9m111->height;
mf->code = mt9m111->fmt->code;
mf->colorspace = mt9m111->fmt->colorspace;
mf->field = V4L2_FIELD_NONE;
+ mf->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ mf->quantization = V4L2_QUANTIZATION_DEFAULT;
+ mf->xfer_func = V4L2_XFER_FUNC_DEFAULT;
return 0;
}
@@ -660,6 +673,10 @@ static int mt9m111_set_fmt(struct v4l2_subdev *sd,
mf->code = fmt->code;
mf->colorspace = fmt->colorspace;
+ mf->field = V4L2_FIELD_NONE;
+ mf->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ mf->quantization = V4L2_QUANTIZATION_DEFAULT;
+ mf->xfer_func = V4L2_XFER_FUNC_DEFAULT;
if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
cfg->try_fmt = *mf;
@@ -1089,6 +1106,25 @@ static int mt9m111_s_stream(struct v4l2_subdev *sd, int enable)
return 0;
}
+static int mt9m111_init_cfg(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg)
+{
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+ struct v4l2_mbus_framefmt *format =
+ v4l2_subdev_get_try_format(sd, cfg, 0);
+
+ format->width = MT9M111_MAX_WIDTH;
+ format->height = MT9M111_MAX_HEIGHT;
+ format->code = mt9m111_colour_fmts[0].code;
+ format->colorspace = mt9m111_colour_fmts[0].colorspace;
+ format->field = V4L2_FIELD_NONE;
+ format->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ format->quantization = V4L2_QUANTIZATION_DEFAULT;
+ format->xfer_func = V4L2_XFER_FUNC_DEFAULT;
+#endif
+ return 0;
+}
+
static int mt9m111_g_mbus_config(struct v4l2_subdev *sd,
struct v4l2_mbus_config *cfg)
{
@@ -1114,6 +1150,7 @@ static const struct v4l2_subdev_video_ops mt9m111_subdev_video_ops = {
};
static const struct v4l2_subdev_pad_ops mt9m111_subdev_pad_ops = {
+ .init_cfg = mt9m111_init_cfg,
.enum_mbus_code = mt9m111_enum_mbus_code,
.get_selection = mt9m111_get_selection,
.set_selection = mt9m111_set_selection,
@@ -1273,6 +1310,8 @@ static int mt9m111_probe(struct i2c_client *client,
mt9m111->rect.top = MT9M111_MIN_DARK_ROWS;
mt9m111->rect.width = MT9M111_MAX_WIDTH;
mt9m111->rect.height = MT9M111_MAX_HEIGHT;
+ mt9m111->width = mt9m111->rect.width;
+ mt9m111->height = mt9m111->rect.height;
mt9m111->fmt = &mt9m111_colour_fmts[0];
mt9m111->lastpage = -1;
mutex_init(&mt9m111->power_lock);
diff --git a/drivers/media/i2c/mt9t112.c b/drivers/media/i2c/mt9t112.c
index ef353a244e33..ae3c336eadf5 100644
--- a/drivers/media/i2c/mt9t112.c
+++ b/drivers/media/i2c/mt9t112.c
@@ -541,7 +541,7 @@ static int mt9t112_init_setting(const struct i2c_client *client)
mt9t112_mcu_write(ret, client, VAR(18, 109), 0x0AF0);
/*
- * Flicker Dectection registers.
+ * Flicker Detection registers.
* This section should be replaced whenever new timing file is
* generated. All the following registers need to be replaced.
* Following registers are generated from Register Wizard but user can
diff --git a/drivers/media/i2c/ov2640.c b/drivers/media/i2c/ov2640.c
index 5d2d6735cc78..83031cfc7914 100644
--- a/drivers/media/i2c/ov2640.c
+++ b/drivers/media/i2c/ov2640.c
@@ -842,9 +842,6 @@ static int ov2640_set_params(struct i2c_client *client,
u8 val;
int ret;
- if (!win)
- return -EINVAL;
-
switch (code) {
case MEDIA_BUS_FMT_RGB565_2X8_BE:
dev_dbg(&client->dev, "%s: Selected cfmt RGB565 BE", __func__);
@@ -929,9 +926,14 @@ static int ov2640_get_fmt(struct v4l2_subdev *sd,
if (format->pad)
return -EINVAL;
- if (!priv->win) {
- priv->win = ov2640_select_win(SVGA_WIDTH, SVGA_HEIGHT);
- priv->cfmt_code = MEDIA_BUS_FMT_UYVY8_2X8;
+ if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+ mf = v4l2_subdev_get_try_format(sd, cfg, 0);
+ format->format = *mf;
+ return 0;
+#else
+ return -ENOTTY;
+#endif
}
mf->width = priv->win->width;
@@ -939,6 +941,9 @@ static int ov2640_get_fmt(struct v4l2_subdev *sd,
mf->code = priv->cfmt_code;
mf->colorspace = V4L2_COLORSPACE_SRGB;
mf->field = V4L2_FIELD_NONE;
+ mf->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ mf->quantization = V4L2_QUANTIZATION_DEFAULT;
+ mf->xfer_func = V4L2_XFER_FUNC_DEFAULT;
return 0;
}
@@ -965,6 +970,9 @@ static int ov2640_set_fmt(struct v4l2_subdev *sd,
mf->field = V4L2_FIELD_NONE;
mf->colorspace = V4L2_COLORSPACE_SRGB;
+ mf->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ mf->quantization = V4L2_QUANTIZATION_DEFAULT;
+ mf->xfer_func = V4L2_XFER_FUNC_DEFAULT;
switch (mf->code) {
case MEDIA_BUS_FMT_RGB565_2X8_BE:
@@ -999,6 +1007,27 @@ out:
return ret;
}
+static int ov2640_init_cfg(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg)
+{
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+ struct v4l2_mbus_framefmt *try_fmt =
+ v4l2_subdev_get_try_format(sd, cfg, 0);
+ const struct ov2640_win_size *win =
+ ov2640_select_win(SVGA_WIDTH, SVGA_HEIGHT);
+
+ try_fmt->width = win->width;
+ try_fmt->height = win->height;
+ try_fmt->code = MEDIA_BUS_FMT_UYVY8_2X8;
+ try_fmt->colorspace = V4L2_COLORSPACE_SRGB;
+ try_fmt->field = V4L2_FIELD_NONE;
+ try_fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ try_fmt->quantization = V4L2_QUANTIZATION_DEFAULT;
+ try_fmt->xfer_func = V4L2_XFER_FUNC_DEFAULT;
+#endif
+ return 0;
+}
+
static int ov2640_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_mbus_code_enum *code)
@@ -1108,6 +1137,7 @@ static const struct v4l2_subdev_core_ops ov2640_subdev_core_ops = {
};
static const struct v4l2_subdev_pad_ops ov2640_subdev_pad_ops = {
+ .init_cfg = ov2640_init_cfg,
.enum_mbus_code = ov2640_enum_mbus_code,
.get_selection = ov2640_get_selection,
.get_fmt = ov2640_get_fmt,
@@ -1193,6 +1223,9 @@ static int ov2640_probe(struct i2c_client *client,
if (ret)
goto err_clk;
+ priv->win = ov2640_select_win(SVGA_WIDTH, SVGA_HEIGHT);
+ priv->cfmt_code = MEDIA_BUS_FMT_UYVY8_2X8;
+
v4l2_i2c_subdev_init(&priv->subdev, client, &ov2640_subdev_ops);
priv->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
V4L2_SUBDEV_FL_HAS_EVENTS;
diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index bef3f3aae0ed..82d4ce93312c 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -83,6 +83,9 @@
#define OV5640_REG_SIGMADELTA_CTRL0C 0x3c0c
#define OV5640_REG_FRAME_CTRL01 0x4202
#define OV5640_REG_FORMAT_CONTROL00 0x4300
+#define OV5640_REG_VFIFO_HSIZE 0x4602
+#define OV5640_REG_VFIFO_VSIZE 0x4604
+#define OV5640_REG_JPG_MODE_SELECT 0x4713
#define OV5640_REG_POLARITY_CTRL00 0x4740
#define OV5640_REG_MIPI_CTRL00 0x4800
#define OV5640_REG_DEBUG_MODE 0x4814
@@ -115,6 +118,15 @@ enum ov5640_frame_rate {
OV5640_NUM_FRAMERATES,
};
+enum ov5640_format_mux {
+ OV5640_FMT_MUX_YUV422 = 0,
+ OV5640_FMT_MUX_RGB,
+ OV5640_FMT_MUX_DITHER,
+ OV5640_FMT_MUX_RAW_DPC,
+ OV5640_FMT_MUX_SNR_RAW,
+ OV5640_FMT_MUX_RAW_CIP,
+};
+
struct ov5640_pixfmt {
u32 code;
u32 colorspace;
@@ -126,6 +138,10 @@ static const struct ov5640_pixfmt ov5640_formats[] = {
{ MEDIA_BUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_SRGB, },
{ MEDIA_BUS_FMT_RGB565_2X8_LE, V4L2_COLORSPACE_SRGB, },
{ MEDIA_BUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_SRGB, },
+ { MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB, },
+ { MEDIA_BUS_FMT_SGBRG8_1X8, V4L2_COLORSPACE_SRGB, },
+ { MEDIA_BUS_FMT_SGRBG8_1X8, V4L2_COLORSPACE_SRGB, },
+ { MEDIA_BUS_FMT_SRGGB8_1X8, V4L2_COLORSPACE_SRGB, },
};
/*
@@ -288,7 +304,7 @@ static const struct reg_value ov5640_init_setting_30fps_VGA[] = {
{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x3000, 0x00, 0, 0},
{0x3002, 0x1c, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3006, 0xc3, 0, 0},
{0x302e, 0x08, 0, 0}, {0x4300, 0x3f, 0, 0},
- {0x501f, 0x00, 0, 0}, {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0},
+ {0x501f, 0x00, 0, 0}, {0x4407, 0x04, 0, 0},
{0x440e, 0x00, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
{0x4837, 0x0a, 0, 0}, {0x3824, 0x02, 0, 0},
{0x5000, 0xa7, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x5180, 0xff, 0, 0},
@@ -357,7 +373,7 @@ static const struct reg_value ov5640_setting_VGA_640_480[] = {
{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
- {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
+ {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
};
@@ -376,7 +392,7 @@ static const struct reg_value ov5640_setting_XGA_1024_768[] = {
{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
- {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
+ {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
};
@@ -395,7 +411,7 @@ static const struct reg_value ov5640_setting_QVGA_320_240[] = {
{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
- {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
+ {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
};
@@ -414,7 +430,7 @@ static const struct reg_value ov5640_setting_QCIF_176_144[] = {
{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
- {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
+ {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
};
@@ -433,7 +449,7 @@ static const struct reg_value ov5640_setting_NTSC_720_480[] = {
{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
- {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
+ {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
};
@@ -452,7 +468,7 @@ static const struct reg_value ov5640_setting_PAL_720_576[] = {
{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
- {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
+ {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
};
@@ -471,7 +487,7 @@ static const struct reg_value ov5640_setting_720P_1280_720[] = {
{0x3a03, 0xe4, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0xbc, 0, 0},
{0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x72, 0, 0}, {0x3a0e, 0x01, 0, 0},
{0x3a0d, 0x02, 0, 0}, {0x3a14, 0x02, 0, 0}, {0x3a15, 0xe4, 0, 0},
- {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
+ {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
{0x4407, 0x04, 0, 0}, {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0},
{0x3824, 0x04, 0, 0}, {0x5001, 0x83, 0, 0},
};
@@ -491,7 +507,7 @@ static const struct reg_value ov5640_setting_1080P_1920_1080[] = {
{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
- {0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0}, {0x4713, 0x03, 0, 0},
+ {0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0},
{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
{0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 0},
{0x3c07, 0x07, 0, 0}, {0x3c08, 0x00, 0, 0},
@@ -503,7 +519,7 @@ static const struct reg_value ov5640_setting_1080P_1920_1080[] = {
{0x3a02, 0x04, 0, 0}, {0x3a03, 0x60, 0, 0}, {0x3a08, 0x01, 0, 0},
{0x3a09, 0x50, 0, 0}, {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x18, 0, 0},
{0x3a0e, 0x03, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x04, 0, 0},
- {0x3a15, 0x60, 0, 0}, {0x4713, 0x02, 0, 0}, {0x4407, 0x04, 0, 0},
+ {0x3a15, 0x60, 0, 0}, {0x4407, 0x04, 0, 0},
{0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3824, 0x04, 0, 0},
{0x4005, 0x1a, 0, 0}, {0x3008, 0x02, 0, 0},
};
@@ -522,7 +538,7 @@ static const struct reg_value ov5640_setting_QSXGA_2592_1944[] = {
{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
- {0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0}, {0x4713, 0x03, 0, 0},
+ {0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0},
{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
{0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 70},
};
@@ -705,7 +721,7 @@ static int ov5640_mod_reg(struct ov5640_dev *sensor, u16 reg,
/*
* After trying the various combinations, reading various
- * documentations spreaded around the net, and from the various
+ * documentations spread around the net, and from the various
* feedback, the clock tree is probably as follows:
*
* +--------------+
@@ -1030,12 +1046,42 @@ static int ov5640_set_dvp_pclk(struct ov5640_dev *sensor, unsigned long rate)
(ilog2(pclk_div) << 4));
}
+/* set JPEG framing sizes */
+static int ov5640_set_jpeg_timings(struct ov5640_dev *sensor,
+ const struct ov5640_mode_info *mode)
+{
+ int ret;
+
+ /*
+ * compression mode 3 timing
+ *
+ * Data is transmitted with programmable width (VFIFO_HSIZE).
+ * No padding done. Last line may have less data. Varying
+ * number of lines per frame, depending on amount of data.
+ */
+ ret = ov5640_mod_reg(sensor, OV5640_REG_JPG_MODE_SELECT, 0x7, 0x3);
+ if (ret < 0)
+ return ret;
+
+ ret = ov5640_write_reg16(sensor, OV5640_REG_VFIFO_HSIZE, mode->hact);
+ if (ret < 0)
+ return ret;
+
+ return ov5640_write_reg16(sensor, OV5640_REG_VFIFO_VSIZE, mode->vact);
+}
+
/* download ov5640 settings to sensor through i2c */
static int ov5640_set_timings(struct ov5640_dev *sensor,
const struct ov5640_mode_info *mode)
{
int ret;
+ if (sensor->fmt.code == MEDIA_BUS_FMT_JPEG_1X8) {
+ ret = ov5640_set_jpeg_timings(sensor, mode);
+ if (ret < 0)
+ return ret;
+ }
+
ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_DVPHO, mode->hact);
if (ret < 0)
return ret;
@@ -1893,7 +1939,7 @@ static void ov5640_reset(struct ov5640_dev *sensor)
usleep_range(1000, 2000);
gpiod_set_value_cansleep(sensor->reset_gpio, 0);
- usleep_range(5000, 10000);
+ usleep_range(20000, 25000);
}
static int ov5640_set_power_on(struct ov5640_dev *sensor)
@@ -2059,7 +2105,7 @@ static int ov5640_try_frame_interval(struct ov5640_dev *sensor,
u32 width, u32 height)
{
const struct ov5640_mode_info *mode;
- enum ov5640_frame_rate rate = OV5640_30_FPS;
+ enum ov5640_frame_rate rate = OV5640_15_FPS;
int minfps, maxfps, best_fps, fps;
int i;
@@ -2200,46 +2246,67 @@ static int ov5640_set_framefmt(struct ov5640_dev *sensor,
struct v4l2_mbus_framefmt *format)
{
int ret = 0;
- bool is_rgb = false;
bool is_jpeg = false;
- u8 val;
+ u8 fmt, mux;
switch (format->code) {
case MEDIA_BUS_FMT_UYVY8_2X8:
/* YUV422, UYVY */
- val = 0x3f;
+ fmt = 0x3f;
+ mux = OV5640_FMT_MUX_YUV422;
break;
case MEDIA_BUS_FMT_YUYV8_2X8:
/* YUV422, YUYV */
- val = 0x30;
+ fmt = 0x30;
+ mux = OV5640_FMT_MUX_YUV422;
break;
case MEDIA_BUS_FMT_RGB565_2X8_LE:
/* RGB565 {g[2:0],b[4:0]},{r[4:0],g[5:3]} */
- val = 0x6F;
- is_rgb = true;
+ fmt = 0x6F;
+ mux = OV5640_FMT_MUX_RGB;
break;
case MEDIA_BUS_FMT_RGB565_2X8_BE:
/* RGB565 {r[4:0],g[5:3]},{g[2:0],b[4:0]} */
- val = 0x61;
- is_rgb = true;
+ fmt = 0x61;
+ mux = OV5640_FMT_MUX_RGB;
break;
case MEDIA_BUS_FMT_JPEG_1X8:
/* YUV422, YUYV */
- val = 0x30;
+ fmt = 0x30;
+ mux = OV5640_FMT_MUX_YUV422;
is_jpeg = true;
break;
+ case MEDIA_BUS_FMT_SBGGR8_1X8:
+ /* Raw, BGBG... / GRGR... */
+ fmt = 0x00;
+ mux = OV5640_FMT_MUX_RAW_DPC;
+ break;
+ case MEDIA_BUS_FMT_SGBRG8_1X8:
+ /* Raw bayer, GBGB... / RGRG... */
+ fmt = 0x01;
+ mux = OV5640_FMT_MUX_RAW_DPC;
+ break;
+ case MEDIA_BUS_FMT_SGRBG8_1X8:
+ /* Raw bayer, GRGR... / BGBG... */
+ fmt = 0x02;
+ mux = OV5640_FMT_MUX_RAW_DPC;
+ break;
+ case MEDIA_BUS_FMT_SRGGB8_1X8:
+ /* Raw bayer, RGRG... / GBGB... */
+ fmt = 0x03;
+ mux = OV5640_FMT_MUX_RAW_DPC;
+ break;
default:
return -EINVAL;
}
/* FORMAT CONTROL00: YUV and RGB formatting */
- ret = ov5640_write_reg(sensor, OV5640_REG_FORMAT_CONTROL00, val);
+ ret = ov5640_write_reg(sensor, OV5640_REG_FORMAT_CONTROL00, fmt);
if (ret)
return ret;
/* FORMAT MUX CONTROL: ISP YUV or RGB */
- ret = ov5640_write_reg(sensor, OV5640_REG_ISP_FORMAT_MUX_CTRL,
- is_rgb ? 0x01 : 0x00);
+ ret = ov5640_write_reg(sensor, OV5640_REG_ISP_FORMAT_MUX_CTRL, mux);
if (ret)
return ret;
@@ -2407,10 +2474,41 @@ static int ov5640_set_ctrl_gain(struct ov5640_dev *sensor, bool auto_gain)
return ret;
}
+static const char * const test_pattern_menu[] = {
+ "Disabled",
+ "Color bars",
+ "Color bars w/ rolling bar",
+ "Color squares",
+ "Color squares w/ rolling bar",
+};
+
+#define OV5640_TEST_ENABLE BIT(7)
+#define OV5640_TEST_ROLLING BIT(6) /* rolling horizontal bar */
+#define OV5640_TEST_TRANSPARENT BIT(5)
+#define OV5640_TEST_SQUARE_BW BIT(4) /* black & white squares */
+#define OV5640_TEST_BAR_STANDARD (0 << 2)
+#define OV5640_TEST_BAR_VERT_CHANGE_1 (1 << 2)
+#define OV5640_TEST_BAR_HOR_CHANGE (2 << 2)
+#define OV5640_TEST_BAR_VERT_CHANGE_2 (3 << 2)
+#define OV5640_TEST_BAR (0 << 0)
+#define OV5640_TEST_RANDOM (1 << 0)
+#define OV5640_TEST_SQUARE (2 << 0)
+#define OV5640_TEST_BLACK (3 << 0)
+
+static const u8 test_pattern_val[] = {
+ 0,
+ OV5640_TEST_ENABLE | OV5640_TEST_BAR_VERT_CHANGE_1 |
+ OV5640_TEST_BAR,
+ OV5640_TEST_ENABLE | OV5640_TEST_ROLLING |
+ OV5640_TEST_BAR_VERT_CHANGE_1 | OV5640_TEST_BAR,
+ OV5640_TEST_ENABLE | OV5640_TEST_SQUARE,
+ OV5640_TEST_ENABLE | OV5640_TEST_ROLLING | OV5640_TEST_SQUARE,
+};
+
static int ov5640_set_ctrl_test_pattern(struct ov5640_dev *sensor, int value)
{
- return ov5640_mod_reg(sensor, OV5640_REG_PRE_ISP_TEST_SET1,
- 0xa4, value ? 0xa4 : 0);
+ return ov5640_write_reg(sensor, OV5640_REG_PRE_ISP_TEST_SET1,
+ test_pattern_val[value]);
}
static int ov5640_set_ctrl_light_freq(struct ov5640_dev *sensor, int value)
@@ -2551,11 +2649,6 @@ static const struct v4l2_ctrl_ops ov5640_ctrl_ops = {
.s_ctrl = ov5640_s_ctrl,
};
-static const char * const test_pattern_menu[] = {
- "Disabled",
- "Color bars",
-};
-
static int ov5640_init_controls(struct ov5640_dev *sensor)
{
const struct v4l2_ctrl_ops *ops = &ov5640_ctrl_ops;
diff --git a/drivers/media/i2c/ov6650.c b/drivers/media/i2c/ov6650.c
index 5d1b218bb7f0..c33fd584cb44 100644
--- a/drivers/media/i2c/ov6650.c
+++ b/drivers/media/i2c/ov6650.c
@@ -15,7 +15,7 @@
* Copyright (C) 2008 Magnus Damm
* Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
*
- * Hardware specific bits initialy based on former work by Matt Callow
+ * Hardware specific bits initially based on former work by Matt Callow
* drivers/media/video/omap/sensor_ov6650.c
* Copyright (C) 2006 Matt Callow
*
@@ -759,7 +759,7 @@ static int ov6650_s_frame_interval(struct v4l2_subdev *sd,
/*
* Keep result to be used as tpf limit
- * for subseqent clock divider calculations
+ * for subsequent clock divider calculations
*/
priv->tpf.numerator = div;
priv->tpf.denominator = FRAME_RATE_MAX;
diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c
index a70a6ff7b36e..a7d26b294eb5 100644
--- a/drivers/media/i2c/ov7670.c
+++ b/drivers/media/i2c/ov7670.c
@@ -160,10 +160,10 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)");
#define REG_GFIX 0x69 /* Fix gain control */
#define REG_DBLV 0x6b /* PLL control an debugging */
-#define DBLV_BYPASS 0x00 /* Bypass PLL */
-#define DBLV_X4 0x01 /* clock x4 */
-#define DBLV_X6 0x10 /* clock x6 */
-#define DBLV_X8 0x11 /* clock x8 */
+#define DBLV_BYPASS 0x0a /* Bypass PLL */
+#define DBLV_X4 0x4a /* clock x4 */
+#define DBLV_X6 0x8a /* clock x6 */
+#define DBLV_X8 0xca /* clock x8 */
#define REG_SCALING_XSC 0x70 /* Test pattern and horizontal scale factor */
#define TEST_PATTTERN_0 0x80
@@ -241,7 +241,9 @@ struct ov7670_info {
};
struct v4l2_mbus_framefmt format;
struct ov7670_format_struct *fmt; /* Current format */
+ struct ov7670_win_size *wsize;
struct clk *clk;
+ int on;
struct gpio_desc *resetb_gpio;
struct gpio_desc *pwdn_gpio;
unsigned int mbus_config; /* Media bus configuration flags */
@@ -810,13 +812,25 @@ static void ov7675_get_framerate(struct v4l2_subdev *sd,
(4 * clkrc);
}
+static int ov7675_apply_framerate(struct v4l2_subdev *sd)
+{
+ struct ov7670_info *info = to_state(sd);
+ int ret;
+
+ ret = ov7670_write(sd, REG_CLKRC, info->clkrc);
+ if (ret < 0)
+ return ret;
+
+ return ov7670_write(sd, REG_DBLV,
+ info->pll_bypass ? DBLV_BYPASS : DBLV_X4);
+}
+
static int ov7675_set_framerate(struct v4l2_subdev *sd,
struct v4l2_fract *tpf)
{
struct ov7670_info *info = to_state(sd);
u32 clkrc;
int pll_factor;
- int ret;
/*
* The formula is fps = 5/4*pixclk for YUV/RGB and
@@ -825,19 +839,10 @@ static int ov7675_set_framerate(struct v4l2_subdev *sd,
* pixclk = clock_speed / (clkrc + 1) * PLLfactor
*
*/
- if (info->pll_bypass) {
- pll_factor = 1;
- ret = ov7670_write(sd, REG_DBLV, DBLV_BYPASS);
- } else {
- pll_factor = PLL_FACTOR;
- ret = ov7670_write(sd, REG_DBLV, DBLV_X4);
- }
- if (ret < 0)
- return ret;
-
if (tpf->numerator == 0 || tpf->denominator == 0) {
clkrc = 0;
} else {
+ pll_factor = info->pll_bypass ? 1 : PLL_FACTOR;
clkrc = (5 * pll_factor * info->clock_speed * tpf->numerator) /
(4 * tpf->denominator);
if (info->fmt->mbus_code == MEDIA_BUS_FMT_SBGGR8_1X8)
@@ -859,11 +864,7 @@ static int ov7675_set_framerate(struct v4l2_subdev *sd,
/* Recalculate frame rate */
ov7675_get_framerate(sd, tpf);
- ret = ov7670_write(sd, REG_CLKRC, info->clkrc);
- if (ret < 0)
- return ret;
-
- return ov7670_write(sd, REG_DBLV, DBLV_X4);
+ return ov7675_apply_framerate(sd);
}
static void ov7670_get_framerate_legacy(struct v4l2_subdev *sd,
@@ -1004,48 +1005,20 @@ static int ov7670_try_fmt_internal(struct v4l2_subdev *sd,
return 0;
}
-/*
- * Set a format.
- */
-static int ov7670_set_fmt(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_format *format)
+static int ov7670_apply_fmt(struct v4l2_subdev *sd)
{
- struct ov7670_format_struct *ovfmt;
- struct ov7670_win_size *wsize;
struct ov7670_info *info = to_state(sd);
-#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
- struct v4l2_mbus_framefmt *mbus_fmt;
-#endif
+ struct ov7670_win_size *wsize = info->wsize;
unsigned char com7, com10 = 0;
int ret;
- if (format->pad)
- return -EINVAL;
-
- if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
- ret = ov7670_try_fmt_internal(sd, &format->format, NULL, NULL);
- if (ret)
- return ret;
-#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
- mbus_fmt = v4l2_subdev_get_try_format(sd, cfg, format->pad);
- *mbus_fmt = format->format;
- return 0;
-#else
- return -ENOTTY;
-#endif
- }
-
- ret = ov7670_try_fmt_internal(sd, &format->format, &ovfmt, &wsize);
- if (ret)
- return ret;
/*
* COM7 is a pain in the ass, it doesn't like to be read then
* quickly written afterward. But we have everything we need
* to set it absolutely here, as long as the format-specific
* register sets list it first.
*/
- com7 = ovfmt->regs[0].value;
+ com7 = info->fmt->regs[0].value;
com7 |= wsize->com7_bit;
ret = ov7670_write(sd, REG_COM7, com7);
if (ret)
@@ -1067,7 +1040,7 @@ static int ov7670_set_fmt(struct v4l2_subdev *sd,
/*
* Now write the rest of the array. Also store start/stops
*/
- ret = ov7670_write_array(sd, ovfmt->regs + 1);
+ ret = ov7670_write_array(sd, info->fmt->regs + 1);
if (ret)
return ret;
@@ -1082,8 +1055,6 @@ static int ov7670_set_fmt(struct v4l2_subdev *sd,
return ret;
}
- info->fmt = ovfmt;
-
/*
* If we're running RGB565, we must rewrite clkrc after setting
* the other parameters or the image looks poor. If we're *not*
@@ -1101,6 +1072,46 @@ static int ov7670_set_fmt(struct v4l2_subdev *sd,
return 0;
}
+/*
+ * Set a format.
+ */
+static int ov7670_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *format)
+{
+ struct ov7670_info *info = to_state(sd);
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+ struct v4l2_mbus_framefmt *mbus_fmt;
+#endif
+ int ret;
+
+ if (format->pad)
+ return -EINVAL;
+
+ if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
+ ret = ov7670_try_fmt_internal(sd, &format->format, NULL, NULL);
+ if (ret)
+ return ret;
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+ mbus_fmt = v4l2_subdev_get_try_format(sd, cfg, format->pad);
+ *mbus_fmt = format->format;
+ return 0;
+#else
+ return -ENOTTY;
+#endif
+ }
+
+ ret = ov7670_try_fmt_internal(sd, &format->format, &info->fmt, &info->wsize);
+ if (ret)
+ return ret;
+
+ ret = ov7670_apply_fmt(sd);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
static int ov7670_get_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *format)
@@ -1607,17 +1618,57 @@ static int ov7670_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_regis
}
#endif
-static int ov7670_s_power(struct v4l2_subdev *sd, int on)
+static void ov7670_power_on(struct v4l2_subdev *sd)
{
struct ov7670_info *info = to_state(sd);
+ if (info->on)
+ return;
+
+ clk_prepare_enable(info->clk);
+
if (info->pwdn_gpio)
- gpiod_set_value(info->pwdn_gpio, !on);
- if (on && info->resetb_gpio) {
+ gpiod_set_value(info->pwdn_gpio, 0);
+ if (info->resetb_gpio) {
gpiod_set_value(info->resetb_gpio, 1);
usleep_range(500, 1000);
gpiod_set_value(info->resetb_gpio, 0);
+ }
+ if (info->pwdn_gpio || info->resetb_gpio || info->clk)
usleep_range(3000, 5000);
+
+ info->on = true;
+}
+
+static void ov7670_power_off(struct v4l2_subdev *sd)
+{
+ struct ov7670_info *info = to_state(sd);
+
+ if (!info->on)
+ return;
+
+ clk_disable_unprepare(info->clk);
+
+ if (info->pwdn_gpio)
+ gpiod_set_value(info->pwdn_gpio, 1);
+
+ info->on = false;
+}
+
+static int ov7670_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct ov7670_info *info = to_state(sd);
+
+ if (info->on == on)
+ return 0;
+
+ if (on) {
+ ov7670_power_on (sd);
+ ov7670_apply_fmt(sd);
+ ov7675_apply_framerate(sd);
+ v4l2_ctrl_handler_setup(&info->hdl);
+ } else {
+ ov7670_power_off (sd);
}
return 0;
@@ -1652,6 +1703,7 @@ static int ov7670_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
static const struct v4l2_subdev_core_ops ov7670_core_ops = {
.reset = ov7670_reset,
.init = ov7670_init,
+ .s_power = ov7670_s_power,
.log_status = v4l2_ctrl_subdev_log_status,
.subscribe_event = v4l2_ctrl_subdev_subscribe_event,
.unsubscribe_event = v4l2_event_subdev_unsubscribe,
@@ -1801,11 +1853,7 @@ static int ov7670_probe(struct i2c_client *client,
if (config->clock_speed)
info->clock_speed = config->clock_speed;
- /*
- * It should be allowed for ov7670 too when it is migrated to
- * the new frame rate formula.
- */
- if (config->pll_bypass && id->driver_data != MODEL_OV7670)
+ if (config->pll_bypass)
info->pll_bypass = true;
if (config->pclk_hb_disable)
@@ -1820,24 +1868,21 @@ static int ov7670_probe(struct i2c_client *client,
else
return ret;
}
- if (info->clk) {
- ret = clk_prepare_enable(info->clk);
- if (ret)
- return ret;
+ ret = ov7670_init_gpio(client, info);
+ if (ret)
+ return ret;
+
+ ov7670_power_on(sd);
+
+ if (info->clk) {
info->clock_speed = clk_get_rate(info->clk) / 1000000;
if (info->clock_speed < 10 || info->clock_speed > 48) {
ret = -EINVAL;
- goto clk_disable;
+ goto power_off;
}
}
- ret = ov7670_init_gpio(client, info);
- if (ret)
- goto clk_disable;
-
- ov7670_s_power(sd, 1);
-
/* Make sure it's an ov7670 */
ret = ov7670_detect(sd);
if (ret) {
@@ -1851,6 +1896,7 @@ static int ov7670_probe(struct i2c_client *client,
info->devtype = &ov7670_devdata[id->driver_data];
info->fmt = &ov7670_formats[0];
+ info->wsize = &info->devtype->win_sizes[0];
ov7670_get_default_format(sd, &info->format);
@@ -1916,6 +1962,7 @@ static int ov7670_probe(struct i2c_client *client,
if (ret < 0)
goto entity_cleanup;
+ ov7670_power_off(sd);
return 0;
entity_cleanup:
@@ -1923,13 +1970,10 @@ entity_cleanup:
hdl_free:
v4l2_ctrl_handler_free(&info->hdl);
power_off:
- ov7670_s_power(sd, 0);
-clk_disable:
- clk_disable_unprepare(info->clk);
+ ov7670_power_off(sd);
return ret;
}
-
static int ov7670_remove(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
@@ -1937,9 +1981,8 @@ static int ov7670_remove(struct i2c_client *client)
v4l2_async_unregister_subdev(sd);
v4l2_ctrl_handler_free(&info->hdl);
- clk_disable_unprepare(info->clk);
media_entity_cleanup(&info->sd.entity);
- ov7670_s_power(sd, 0);
+ ov7670_power_off(sd);
return 0;
}
diff --git a/drivers/media/i2c/ov7740.c b/drivers/media/i2c/ov7740.c
index 177688afd9a6..dfece91ce96b 100644
--- a/drivers/media/i2c/ov7740.c
+++ b/drivers/media/i2c/ov7740.c
@@ -20,7 +20,7 @@
#define REG_BGAIN 0x01 /* blue gain */
#define REG_RGAIN 0x02 /* red gain */
#define REG_GGAIN 0x03 /* green gain */
-#define REG_REG04 0x04 /* analog setting, dont change*/
+#define REG_REG04 0x04 /* analog setting, don't change*/
#define REG_BAVG 0x05 /* b channel average */
#define REG_GAVG 0x06 /* g channel average */
#define REG_RAVG 0x07 /* r channel average */
@@ -1101,6 +1101,9 @@ static int ov7740_probe(struct i2c_client *client,
if (ret)
return ret;
+ pm_runtime_set_active(&client->dev);
+ pm_runtime_enable(&client->dev);
+
ret = ov7740_detect(ov7740);
if (ret)
goto error_detect;
@@ -1123,8 +1126,6 @@ static int ov7740_probe(struct i2c_client *client,
if (ret)
goto error_async_register;
- pm_runtime_set_active(&client->dev);
- pm_runtime_enable(&client->dev);
pm_runtime_idle(&client->dev);
return 0;
@@ -1134,6 +1135,8 @@ error_async_register:
error_init_controls:
ov7740_free_controls(ov7740);
error_detect:
+ pm_runtime_disable(&client->dev);
+ pm_runtime_set_suspended(&client->dev);
ov7740_set_power(ov7740, 0);
media_entity_cleanup(&ov7740->subdev.entity);
diff --git a/drivers/media/i2c/ov8856.c b/drivers/media/i2c/ov8856.c
new file mode 100644
index 000000000000..dbf1095b9440
--- /dev/null
+++ b/drivers/media/i2c/ov8856.c
@@ -0,0 +1,1268 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2019 Intel Corporation.
+
+#include <asm/unaligned.h>
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
+
+#define OV8856_REG_VALUE_08BIT 1
+#define OV8856_REG_VALUE_16BIT 2
+#define OV8856_REG_VALUE_24BIT 3
+
+#define OV8856_LINK_FREQ_360MHZ 360000000ULL
+#define OV8856_LINK_FREQ_180MHZ 180000000ULL
+#define OV8856_SCLK 144000000ULL
+#define OV8856_MCLK 19200000
+#define OV8856_DATA_LANES 4
+#define OV8856_RGB_DEPTH 10
+
+#define OV8856_REG_CHIP_ID 0x300a
+#define OV8856_CHIP_ID 0x00885a
+
+#define OV8856_REG_MODE_SELECT 0x0100
+#define OV8856_MODE_STANDBY 0x00
+#define OV8856_MODE_STREAMING 0x01
+
+/* vertical-timings from sensor */
+#define OV8856_REG_VTS 0x380e
+#define OV8856_VTS_MAX 0x7fff
+
+/* horizontal-timings from sensor */
+#define OV8856_REG_HTS 0x380c
+
+/* Exposure controls from sensor */
+#define OV8856_REG_EXPOSURE 0x3500
+#define OV8856_EXPOSURE_MIN 6
+#define OV8856_EXPOSURE_MAX_MARGIN 6
+#define OV8856_EXPOSURE_STEP 1
+
+/* Analog gain controls from sensor */
+#define OV8856_REG_ANALOG_GAIN 0x3508
+#define OV8856_ANAL_GAIN_MIN 128
+#define OV8856_ANAL_GAIN_MAX 2047
+#define OV8856_ANAL_GAIN_STEP 1
+
+/* Digital gain controls from sensor */
+#define OV8856_REG_MWB_R_GAIN 0x5019
+#define OV8856_REG_MWB_G_GAIN 0x501b
+#define OV8856_REG_MWB_B_GAIN 0x501d
+#define OV8856_DGTL_GAIN_MIN 0
+#define OV8856_DGTL_GAIN_MAX 4095
+#define OV8856_DGTL_GAIN_STEP 1
+#define OV8856_DGTL_GAIN_DEFAULT 1024
+
+/* Test Pattern Control */
+#define OV8856_REG_TEST_PATTERN 0x5e00
+#define OV8856_TEST_PATTERN_ENABLE BIT(7)
+#define OV8856_TEST_PATTERN_BAR_SHIFT 2
+
+#define to_ov8856(_sd) container_of(_sd, struct ov8856, sd)
+
+enum {
+ OV8856_LINK_FREQ_720MBPS,
+ OV8856_LINK_FREQ_360MBPS,
+};
+
+struct ov8856_reg {
+ u16 address;
+ u8 val;
+};
+
+struct ov8856_reg_list {
+ u32 num_of_regs;
+ const struct ov8856_reg *regs;
+};
+
+struct ov8856_link_freq_config {
+ const struct ov8856_reg_list reg_list;
+};
+
+struct ov8856_mode {
+ /* Frame width in pixels */
+ u32 width;
+
+ /* Frame height in pixels */
+ u32 height;
+
+ /* Horizontal timining size */
+ u32 hts;
+
+ /* Default vertical timining size */
+ u32 vts_def;
+
+ /* Min vertical timining size */
+ u32 vts_min;
+
+ /* Link frequency needed for this resolution */
+ u32 link_freq_index;
+
+ /* Sensor register settings for this resolution */
+ const struct ov8856_reg_list reg_list;
+};
+
+static const struct ov8856_reg mipi_data_rate_720mbps[] = {
+ {0x0103, 0x01},
+ {0x0100, 0x00},
+ {0x0302, 0x4b},
+ {0x0303, 0x01},
+ {0x030b, 0x02},
+ {0x030d, 0x4b},
+ {0x031e, 0x0c},
+};
+
+static const struct ov8856_reg mipi_data_rate_360mbps[] = {
+ {0x0103, 0x01},
+ {0x0100, 0x00},
+ {0x0302, 0x4b},
+ {0x0303, 0x03},
+ {0x030b, 0x02},
+ {0x030d, 0x4b},
+ {0x031e, 0x0c},
+};
+
+static const struct ov8856_reg mode_3280x2464_regs[] = {
+ {0x3000, 0x20},
+ {0x3003, 0x08},
+ {0x300e, 0x20},
+ {0x3010, 0x00},
+ {0x3015, 0x84},
+ {0x3018, 0x72},
+ {0x3021, 0x23},
+ {0x3033, 0x24},
+ {0x3500, 0x00},
+ {0x3501, 0x9a},
+ {0x3502, 0x20},
+ {0x3503, 0x08},
+ {0x3505, 0x83},
+ {0x3508, 0x01},
+ {0x3509, 0x80},
+ {0x350c, 0x00},
+ {0x350d, 0x80},
+ {0x350e, 0x04},
+ {0x350f, 0x00},
+ {0x3510, 0x00},
+ {0x3511, 0x02},
+ {0x3512, 0x00},
+ {0x3600, 0x72},
+ {0x3601, 0x40},
+ {0x3602, 0x30},
+ {0x3610, 0xc5},
+ {0x3611, 0x58},
+ {0x3612, 0x5c},
+ {0x3613, 0xca},
+ {0x3614, 0x20},
+ {0x3628, 0xff},
+ {0x3629, 0xff},
+ {0x362a, 0xff},
+ {0x3633, 0x10},
+ {0x3634, 0x10},
+ {0x3635, 0x10},
+ {0x3636, 0x10},
+ {0x3663, 0x08},
+ {0x3669, 0x34},
+ {0x366e, 0x10},
+ {0x3706, 0x86},
+ {0x370b, 0x7e},
+ {0x3714, 0x23},
+ {0x3730, 0x12},
+ {0x3733, 0x10},
+ {0x3764, 0x00},
+ {0x3765, 0x00},
+ {0x3769, 0x62},
+ {0x376a, 0x2a},
+ {0x376b, 0x30},
+ {0x3780, 0x00},
+ {0x3781, 0x24},
+ {0x3782, 0x00},
+ {0x3783, 0x23},
+ {0x3798, 0x2f},
+ {0x37a1, 0x60},
+ {0x37a8, 0x6a},
+ {0x37ab, 0x3f},
+ {0x37c2, 0x04},
+ {0x37c3, 0xf1},
+ {0x37c9, 0x80},
+ {0x37cb, 0x16},
+ {0x37cc, 0x16},
+ {0x37cd, 0x16},
+ {0x37ce, 0x16},
+ {0x3800, 0x00},
+ {0x3801, 0x00},
+ {0x3802, 0x00},
+ {0x3803, 0x07},
+ {0x3804, 0x0c},
+ {0x3805, 0xdf},
+ {0x3806, 0x09},
+ {0x3807, 0xa6},
+ {0x3808, 0x0c},
+ {0x3809, 0xd0},
+ {0x380a, 0x09},
+ {0x380b, 0xa0},
+ {0x380c, 0x07},
+ {0x380d, 0x88},
+ {0x380e, 0x09},
+ {0x380f, 0xb8},
+ {0x3810, 0x00},
+ {0x3811, 0x00},
+ {0x3812, 0x00},
+ {0x3813, 0x00},
+ {0x3814, 0x01},
+ {0x3815, 0x01},
+ {0x3816, 0x00},
+ {0x3817, 0x00},
+ {0x3818, 0x00},
+ {0x3819, 0x10},
+ {0x3820, 0x80},
+ {0x3821, 0x46},
+ {0x382a, 0x01},
+ {0x382b, 0x01},
+ {0x3830, 0x06},
+ {0x3836, 0x02},
+ {0x3862, 0x04},
+ {0x3863, 0x08},
+ {0x3cc0, 0x33},
+ {0x3d85, 0x17},
+ {0x3d8c, 0x73},
+ {0x3d8d, 0xde},
+ {0x4001, 0xe0},
+ {0x4003, 0x40},
+ {0x4008, 0x00},
+ {0x4009, 0x0b},
+ {0x400a, 0x00},
+ {0x400b, 0x84},
+ {0x400f, 0x80},
+ {0x4010, 0xf0},
+ {0x4011, 0xff},
+ {0x4012, 0x02},
+ {0x4013, 0x01},
+ {0x4014, 0x01},
+ {0x4015, 0x01},
+ {0x4042, 0x00},
+ {0x4043, 0x80},
+ {0x4044, 0x00},
+ {0x4045, 0x80},
+ {0x4046, 0x00},
+ {0x4047, 0x80},
+ {0x4048, 0x00},
+ {0x4049, 0x80},
+ {0x4041, 0x03},
+ {0x404c, 0x20},
+ {0x404d, 0x00},
+ {0x404e, 0x20},
+ {0x4203, 0x80},
+ {0x4307, 0x30},
+ {0x4317, 0x00},
+ {0x4503, 0x08},
+ {0x4601, 0x80},
+ {0x4800, 0x44},
+ {0x4816, 0x53},
+ {0x481b, 0x58},
+ {0x481f, 0x27},
+ {0x4837, 0x16},
+ {0x483c, 0x0f},
+ {0x484b, 0x05},
+ {0x5000, 0x57},
+ {0x5001, 0x0a},
+ {0x5004, 0x04},
+ {0x502e, 0x03},
+ {0x5030, 0x41},
+ {0x5780, 0x14},
+ {0x5781, 0x0f},
+ {0x5782, 0x44},
+ {0x5783, 0x02},
+ {0x5784, 0x01},
+ {0x5785, 0x01},
+ {0x5786, 0x00},
+ {0x5787, 0x04},
+ {0x5788, 0x02},
+ {0x5789, 0x0f},
+ {0x578a, 0xfd},
+ {0x578b, 0xf5},
+ {0x578c, 0xf5},
+ {0x578d, 0x03},
+ {0x578e, 0x08},
+ {0x578f, 0x0c},
+ {0x5790, 0x08},
+ {0x5791, 0x04},
+ {0x5792, 0x00},
+ {0x5793, 0x52},
+ {0x5794, 0xa3},
+ {0x5795, 0x02},
+ {0x5796, 0x20},
+ {0x5797, 0x20},
+ {0x5798, 0xd5},
+ {0x5799, 0xd5},
+ {0x579a, 0x00},
+ {0x579b, 0x50},
+ {0x579c, 0x00},
+ {0x579d, 0x2c},
+ {0x579e, 0x0c},
+ {0x579f, 0x40},
+ {0x57a0, 0x09},
+ {0x57a1, 0x40},
+ {0x59f8, 0x3d},
+ {0x5a08, 0x02},
+ {0x5b00, 0x02},
+ {0x5b01, 0x10},
+ {0x5b02, 0x03},
+ {0x5b03, 0xcf},
+ {0x5b05, 0x6c},
+ {0x5e00, 0x00}
+};
+
+static const struct ov8856_reg mode_1640x1232_regs[] = {
+ {0x3000, 0x20},
+ {0x3003, 0x08},
+ {0x300e, 0x20},
+ {0x3010, 0x00},
+ {0x3015, 0x84},
+ {0x3018, 0x72},
+ {0x3021, 0x23},
+ {0x3033, 0x24},
+ {0x3500, 0x00},
+ {0x3501, 0x4c},
+ {0x3502, 0xe0},
+ {0x3503, 0x08},
+ {0x3505, 0x83},
+ {0x3508, 0x01},
+ {0x3509, 0x80},
+ {0x350c, 0x00},
+ {0x350d, 0x80},
+ {0x350e, 0x04},
+ {0x350f, 0x00},
+ {0x3510, 0x00},
+ {0x3511, 0x02},
+ {0x3512, 0x00},
+ {0x3600, 0x72},
+ {0x3601, 0x40},
+ {0x3602, 0x30},
+ {0x3610, 0xc5},
+ {0x3611, 0x58},
+ {0x3612, 0x5c},
+ {0x3613, 0xca},
+ {0x3614, 0x20},
+ {0x3628, 0xff},
+ {0x3629, 0xff},
+ {0x362a, 0xff},
+ {0x3633, 0x10},
+ {0x3634, 0x10},
+ {0x3635, 0x10},
+ {0x3636, 0x10},
+ {0x3663, 0x08},
+ {0x3669, 0x34},
+ {0x366e, 0x08},
+ {0x3706, 0x86},
+ {0x370b, 0x7e},
+ {0x3714, 0x27},
+ {0x3730, 0x12},
+ {0x3733, 0x10},
+ {0x3764, 0x00},
+ {0x3765, 0x00},
+ {0x3769, 0x62},
+ {0x376a, 0x2a},
+ {0x376b, 0x30},
+ {0x3780, 0x00},
+ {0x3781, 0x24},
+ {0x3782, 0x00},
+ {0x3783, 0x23},
+ {0x3798, 0x2f},
+ {0x37a1, 0x60},
+ {0x37a8, 0x6a},
+ {0x37ab, 0x3f},
+ {0x37c2, 0x14},
+ {0x37c3, 0xf1},
+ {0x37c9, 0x80},
+ {0x37cb, 0x16},
+ {0x37cc, 0x16},
+ {0x37cd, 0x16},
+ {0x37ce, 0x16},
+ {0x3800, 0x00},
+ {0x3801, 0x00},
+ {0x3802, 0x00},
+ {0x3803, 0x07},
+ {0x3804, 0x0c},
+ {0x3805, 0xdf},
+ {0x3806, 0x09},
+ {0x3807, 0xa6},
+ {0x3808, 0x06},
+ {0x3809, 0x68},
+ {0x380a, 0x04},
+ {0x380b, 0xd0},
+ {0x380c, 0x0e},
+ {0x380d, 0xec},
+ {0x380e, 0x04},
+ {0x380f, 0xe8},
+ {0x3810, 0x00},
+ {0x3811, 0x00},
+ {0x3812, 0x00},
+ {0x3813, 0x00},
+ {0x3814, 0x03},
+ {0x3815, 0x01},
+ {0x3816, 0x00},
+ {0x3817, 0x00},
+ {0x3818, 0x00},
+ {0x3819, 0x10},
+ {0x3820, 0x90},
+ {0x3821, 0x67},
+ {0x382a, 0x03},
+ {0x382b, 0x01},
+ {0x3830, 0x06},
+ {0x3836, 0x02},
+ {0x3862, 0x04},
+ {0x3863, 0x08},
+ {0x3cc0, 0x33},
+ {0x3d85, 0x17},
+ {0x3d8c, 0x73},
+ {0x3d8d, 0xde},
+ {0x4001, 0xe0},
+ {0x4003, 0x40},
+ {0x4008, 0x00},
+ {0x4009, 0x05},
+ {0x400a, 0x00},
+ {0x400b, 0x84},
+ {0x400f, 0x80},
+ {0x4010, 0xf0},
+ {0x4011, 0xff},
+ {0x4012, 0x02},
+ {0x4013, 0x01},
+ {0x4014, 0x01},
+ {0x4015, 0x01},
+ {0x4042, 0x00},
+ {0x4043, 0x80},
+ {0x4044, 0x00},
+ {0x4045, 0x80},
+ {0x4046, 0x00},
+ {0x4047, 0x80},
+ {0x4048, 0x00},
+ {0x4049, 0x80},
+ {0x4041, 0x03},
+ {0x404c, 0x20},
+ {0x404d, 0x00},
+ {0x404e, 0x20},
+ {0x4203, 0x80},
+ {0x4307, 0x30},
+ {0x4317, 0x00},
+ {0x4503, 0x08},
+ {0x4601, 0x80},
+ {0x4800, 0x44},
+ {0x4816, 0x53},
+ {0x481b, 0x58},
+ {0x481f, 0x27},
+ {0x4837, 0x16},
+ {0x483c, 0x0f},
+ {0x484b, 0x05},
+ {0x5000, 0x57},
+ {0x5001, 0x0a},
+ {0x5004, 0x04},
+ {0x502e, 0x03},
+ {0x5030, 0x41},
+ {0x5780, 0x14},
+ {0x5781, 0x0f},
+ {0x5782, 0x44},
+ {0x5783, 0x02},
+ {0x5784, 0x01},
+ {0x5785, 0x01},
+ {0x5786, 0x00},
+ {0x5787, 0x04},
+ {0x5788, 0x02},
+ {0x5789, 0x0f},
+ {0x578a, 0xfd},
+ {0x578b, 0xf5},
+ {0x578c, 0xf5},
+ {0x578d, 0x03},
+ {0x578e, 0x08},
+ {0x578f, 0x0c},
+ {0x5790, 0x08},
+ {0x5791, 0x04},
+ {0x5792, 0x00},
+ {0x5793, 0x52},
+ {0x5794, 0xa3},
+ {0x5795, 0x00},
+ {0x5796, 0x10},
+ {0x5797, 0x10},
+ {0x5798, 0x73},
+ {0x5799, 0x73},
+ {0x579a, 0x00},
+ {0x579b, 0x28},
+ {0x579c, 0x00},
+ {0x579d, 0x16},
+ {0x579e, 0x06},
+ {0x579f, 0x20},
+ {0x57a0, 0x04},
+ {0x57a1, 0xa0},
+ {0x59f8, 0x3d},
+ {0x5a08, 0x02},
+ {0x5b00, 0x02},
+ {0x5b01, 0x10},
+ {0x5b02, 0x03},
+ {0x5b03, 0xcf},
+ {0x5b05, 0x6c},
+ {0x5e00, 0x00}
+};
+
+static const char * const ov8856_test_pattern_menu[] = {
+ "Disabled",
+ "Standard Color Bar",
+ "Top-Bottom Darker Color Bar",
+ "Right-Left Darker Color Bar",
+ "Bottom-Top Darker Color Bar"
+};
+
+static const s64 link_freq_menu_items[] = {
+ OV8856_LINK_FREQ_360MHZ,
+ OV8856_LINK_FREQ_180MHZ
+};
+
+static const struct ov8856_link_freq_config link_freq_configs[] = {
+ [OV8856_LINK_FREQ_720MBPS] = {
+ .reg_list = {
+ .num_of_regs = ARRAY_SIZE(mipi_data_rate_720mbps),
+ .regs = mipi_data_rate_720mbps,
+ }
+ },
+ [OV8856_LINK_FREQ_360MBPS] = {
+ .reg_list = {
+ .num_of_regs = ARRAY_SIZE(mipi_data_rate_360mbps),
+ .regs = mipi_data_rate_360mbps,
+ }
+ }
+};
+
+static const struct ov8856_mode supported_modes[] = {
+ {
+ .width = 3280,
+ .height = 2464,
+ .hts = 1928,
+ .vts_def = 2488,
+ .vts_min = 2488,
+ .reg_list = {
+ .num_of_regs = ARRAY_SIZE(mode_3280x2464_regs),
+ .regs = mode_3280x2464_regs,
+ },
+ .link_freq_index = OV8856_LINK_FREQ_720MBPS,
+ },
+ {
+ .width = 1640,
+ .height = 1232,
+ .hts = 3820,
+ .vts_def = 1256,
+ .vts_min = 1256,
+ .reg_list = {
+ .num_of_regs = ARRAY_SIZE(mode_1640x1232_regs),
+ .regs = mode_1640x1232_regs,
+ },
+ .link_freq_index = OV8856_LINK_FREQ_360MBPS,
+ }
+};
+
+struct ov8856 {
+ struct v4l2_subdev sd;
+ struct media_pad pad;
+ struct v4l2_ctrl_handler ctrl_handler;
+
+ /* V4L2 Controls */
+ struct v4l2_ctrl *link_freq;
+ struct v4l2_ctrl *pixel_rate;
+ struct v4l2_ctrl *vblank;
+ struct v4l2_ctrl *hblank;
+ struct v4l2_ctrl *exposure;
+
+ /* Current mode */
+ const struct ov8856_mode *cur_mode;
+
+ /* To serialize asynchronus callbacks */
+ struct mutex mutex;
+
+ /* Streaming on/off */
+ bool streaming;
+};
+
+static u64 to_pixel_rate(u32 f_index)
+{
+ u64 pixel_rate = link_freq_menu_items[f_index] * 2 * OV8856_DATA_LANES;
+
+ do_div(pixel_rate, OV8856_RGB_DEPTH);
+
+ return pixel_rate;
+}
+
+static u64 to_pixels_per_line(u32 hts, u32 f_index)
+{
+ u64 ppl = hts * to_pixel_rate(f_index);
+
+ do_div(ppl, OV8856_SCLK);
+
+ return ppl;
+}
+
+static int ov8856_read_reg(struct ov8856 *ov8856, u16 reg, u16 len, u32 *val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&ov8856->sd);
+ struct i2c_msg msgs[2];
+ u8 addr_buf[2];
+ u8 data_buf[4] = {0};
+ int ret;
+
+ if (len > 4)
+ return -EINVAL;
+
+ put_unaligned_be16(reg, addr_buf);
+ msgs[0].addr = client->addr;
+ msgs[0].flags = 0;
+ msgs[0].len = sizeof(addr_buf);
+ msgs[0].buf = addr_buf;
+ msgs[1].addr = client->addr;
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].len = len;
+ msgs[1].buf = &data_buf[4 - len];
+
+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret != ARRAY_SIZE(msgs))
+ return -EIO;
+
+ *val = get_unaligned_be32(data_buf);
+
+ return 0;
+}
+
+static int ov8856_write_reg(struct ov8856 *ov8856, u16 reg, u16 len, u32 val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&ov8856->sd);
+ u8 buf[6];
+
+ if (len > 4)
+ return -EINVAL;
+
+ put_unaligned_be16(reg, buf);
+ put_unaligned_be32(val << 8 * (4 - len), buf + 2);
+ if (i2c_master_send(client, buf, len + 2) != len + 2)
+ return -EIO;
+
+ return 0;
+}
+
+static int ov8856_write_reg_list(struct ov8856 *ov8856,
+ const struct ov8856_reg_list *r_list)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&ov8856->sd);
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < r_list->num_of_regs; i++) {
+ ret = ov8856_write_reg(ov8856, r_list->regs[i].address, 1,
+ r_list->regs[i].val);
+ if (ret) {
+ dev_err_ratelimited(&client->dev,
+ "failed to write reg 0x%4.4x. error = %d",
+ r_list->regs[i].address, ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int ov8856_update_digital_gain(struct ov8856 *ov8856, u32 d_gain)
+{
+ int ret;
+
+ ret = ov8856_write_reg(ov8856, OV8856_REG_MWB_R_GAIN,
+ OV8856_REG_VALUE_16BIT, d_gain);
+ if (ret)
+ return ret;
+
+ ret = ov8856_write_reg(ov8856, OV8856_REG_MWB_G_GAIN,
+ OV8856_REG_VALUE_16BIT, d_gain);
+ if (ret)
+ return ret;
+
+ return ov8856_write_reg(ov8856, OV8856_REG_MWB_B_GAIN,
+ OV8856_REG_VALUE_16BIT, d_gain);
+}
+
+static int ov8856_test_pattern(struct ov8856 *ov8856, u32 pattern)
+{
+ if (pattern)
+ pattern = (pattern - 1) << OV8856_TEST_PATTERN_BAR_SHIFT |
+ OV8856_TEST_PATTERN_ENABLE;
+
+ return ov8856_write_reg(ov8856, OV8856_REG_TEST_PATTERN,
+ OV8856_REG_VALUE_08BIT, pattern);
+}
+
+static int ov8856_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct ov8856 *ov8856 = container_of(ctrl->handler,
+ struct ov8856, ctrl_handler);
+ struct i2c_client *client = v4l2_get_subdevdata(&ov8856->sd);
+ s64 exposure_max;
+ int ret = 0;
+
+ /* Propagate change of current control to all related controls */
+ if (ctrl->id == V4L2_CID_VBLANK) {
+ /* Update max exposure while meeting expected vblanking */
+ exposure_max = ov8856->cur_mode->height + ctrl->val -
+ OV8856_EXPOSURE_MAX_MARGIN;
+ __v4l2_ctrl_modify_range(ov8856->exposure,
+ ov8856->exposure->minimum,
+ exposure_max, ov8856->exposure->step,
+ exposure_max);
+ }
+
+ /* V4L2 controls values will be applied only when power is already up */
+ if (!pm_runtime_get_if_in_use(&client->dev))
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_ANALOGUE_GAIN:
+ ret = ov8856_write_reg(ov8856, OV8856_REG_ANALOG_GAIN,
+ OV8856_REG_VALUE_16BIT, ctrl->val);
+ break;
+
+ case V4L2_CID_DIGITAL_GAIN:
+ ret = ov8856_update_digital_gain(ov8856, ctrl->val);
+ break;
+
+ case V4L2_CID_EXPOSURE:
+ /* 4 least significant bits of expsoure are fractional part */
+ ret = ov8856_write_reg(ov8856, OV8856_REG_EXPOSURE,
+ OV8856_REG_VALUE_24BIT, ctrl->val << 4);
+ break;
+
+ case V4L2_CID_VBLANK:
+ ret = ov8856_write_reg(ov8856, OV8856_REG_VTS,
+ OV8856_REG_VALUE_16BIT,
+ ov8856->cur_mode->height + ctrl->val);
+ break;
+
+ case V4L2_CID_TEST_PATTERN:
+ ret = ov8856_test_pattern(ov8856, ctrl->val);
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ pm_runtime_put(&client->dev);
+
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops ov8856_ctrl_ops = {
+ .s_ctrl = ov8856_set_ctrl,
+};
+
+static int ov8856_init_controls(struct ov8856 *ov8856)
+{
+ struct v4l2_ctrl_handler *ctrl_hdlr;
+ s64 exposure_max, h_blank;
+ int ret;
+
+ ctrl_hdlr = &ov8856->ctrl_handler;
+ ret = v4l2_ctrl_handler_init(ctrl_hdlr, 8);
+ if (ret)
+ return ret;
+
+ ctrl_hdlr->lock = &ov8856->mutex;
+ ov8856->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, &ov8856_ctrl_ops,
+ V4L2_CID_LINK_FREQ,
+ ARRAY_SIZE(link_freq_menu_items) - 1,
+ 0, link_freq_menu_items);
+ if (ov8856->link_freq)
+ ov8856->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ ov8856->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &ov8856_ctrl_ops,
+ V4L2_CID_PIXEL_RATE, 0,
+ to_pixel_rate(OV8856_LINK_FREQ_720MBPS),
+ 1,
+ to_pixel_rate(OV8856_LINK_FREQ_720MBPS));
+ ov8856->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov8856_ctrl_ops,
+ V4L2_CID_VBLANK,
+ ov8856->cur_mode->vts_min - ov8856->cur_mode->height,
+ OV8856_VTS_MAX - ov8856->cur_mode->height, 1,
+ ov8856->cur_mode->vts_def - ov8856->cur_mode->height);
+ h_blank = to_pixels_per_line(ov8856->cur_mode->hts,
+ ov8856->cur_mode->link_freq_index) - ov8856->cur_mode->width;
+ ov8856->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov8856_ctrl_ops,
+ V4L2_CID_HBLANK, h_blank, h_blank, 1,
+ h_blank);
+ if (ov8856->hblank)
+ ov8856->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ v4l2_ctrl_new_std(ctrl_hdlr, &ov8856_ctrl_ops, V4L2_CID_ANALOGUE_GAIN,
+ OV8856_ANAL_GAIN_MIN, OV8856_ANAL_GAIN_MAX,
+ OV8856_ANAL_GAIN_STEP, OV8856_ANAL_GAIN_MIN);
+ v4l2_ctrl_new_std(ctrl_hdlr, &ov8856_ctrl_ops, V4L2_CID_DIGITAL_GAIN,
+ OV8856_DGTL_GAIN_MIN, OV8856_DGTL_GAIN_MAX,
+ OV8856_DGTL_GAIN_STEP, OV8856_DGTL_GAIN_DEFAULT);
+ exposure_max = ov8856->cur_mode->vts_def - OV8856_EXPOSURE_MAX_MARGIN;
+ ov8856->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &ov8856_ctrl_ops,
+ V4L2_CID_EXPOSURE,
+ OV8856_EXPOSURE_MIN, exposure_max,
+ OV8856_EXPOSURE_STEP,
+ exposure_max);
+ v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &ov8856_ctrl_ops,
+ V4L2_CID_TEST_PATTERN,
+ ARRAY_SIZE(ov8856_test_pattern_menu) - 1,
+ 0, 0, ov8856_test_pattern_menu);
+ if (ctrl_hdlr->error)
+ return ctrl_hdlr->error;
+
+ ov8856->sd.ctrl_handler = ctrl_hdlr;
+
+ return 0;
+}
+
+static void ov8856_update_pad_format(const struct ov8856_mode *mode,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ fmt->width = mode->width;
+ fmt->height = mode->height;
+ fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10;
+ fmt->field = V4L2_FIELD_NONE;
+}
+
+static int ov8856_start_streaming(struct ov8856 *ov8856)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&ov8856->sd);
+ const struct ov8856_reg_list *reg_list;
+ int link_freq_index, ret;
+
+ link_freq_index = ov8856->cur_mode->link_freq_index;
+ reg_list = &link_freq_configs[link_freq_index].reg_list;
+ ret = ov8856_write_reg_list(ov8856, reg_list);
+ if (ret) {
+ dev_err(&client->dev, "failed to set plls");
+ return ret;
+ }
+
+ reg_list = &ov8856->cur_mode->reg_list;
+ ret = ov8856_write_reg_list(ov8856, reg_list);
+ if (ret) {
+ dev_err(&client->dev, "failed to set mode");
+ return ret;
+ }
+
+ ret = __v4l2_ctrl_handler_setup(ov8856->sd.ctrl_handler);
+ if (ret)
+ return ret;
+
+ ret = ov8856_write_reg(ov8856, OV8856_REG_MODE_SELECT,
+ OV8856_REG_VALUE_08BIT, OV8856_MODE_STREAMING);
+ if (ret) {
+ dev_err(&client->dev, "failed to set stream");
+ return ret;
+ }
+
+ return 0;
+}
+
+static void ov8856_stop_streaming(struct ov8856 *ov8856)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&ov8856->sd);
+
+ if (ov8856_write_reg(ov8856, OV8856_REG_MODE_SELECT,
+ OV8856_REG_VALUE_08BIT, OV8856_MODE_STANDBY))
+ dev_err(&client->dev, "failed to set stream");
+}
+
+static int ov8856_set_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct ov8856 *ov8856 = to_ov8856(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret = 0;
+
+ if (ov8856->streaming == enable)
+ return 0;
+
+ mutex_lock(&ov8856->mutex);
+ if (enable) {
+ ret = pm_runtime_get_sync(&client->dev);
+ if (ret < 0) {
+ pm_runtime_put_noidle(&client->dev);
+ mutex_unlock(&ov8856->mutex);
+ return ret;
+ }
+
+ ret = ov8856_start_streaming(ov8856);
+ if (ret) {
+ enable = 0;
+ ov8856_stop_streaming(ov8856);
+ pm_runtime_put(&client->dev);
+ }
+ } else {
+ ov8856_stop_streaming(ov8856);
+ pm_runtime_put(&client->dev);
+ }
+
+ ov8856->streaming = enable;
+ mutex_unlock(&ov8856->mutex);
+
+ return ret;
+}
+
+static int __maybe_unused ov8856_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ov8856 *ov8856 = to_ov8856(sd);
+
+ mutex_lock(&ov8856->mutex);
+ if (ov8856->streaming)
+ ov8856_stop_streaming(ov8856);
+
+ mutex_unlock(&ov8856->mutex);
+
+ return 0;
+}
+
+static int __maybe_unused ov8856_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ov8856 *ov8856 = to_ov8856(sd);
+ int ret;
+
+ mutex_lock(&ov8856->mutex);
+ if (ov8856->streaming) {
+ ret = ov8856_start_streaming(ov8856);
+ if (ret) {
+ ov8856->streaming = false;
+ ov8856_stop_streaming(ov8856);
+ mutex_unlock(&ov8856->mutex);
+ return ret;
+ }
+ }
+
+ mutex_unlock(&ov8856->mutex);
+
+ return 0;
+}
+
+static int ov8856_set_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ struct ov8856 *ov8856 = to_ov8856(sd);
+ const struct ov8856_mode *mode;
+ s32 vblank_def, h_blank;
+
+ mode = v4l2_find_nearest_size(supported_modes,
+ ARRAY_SIZE(supported_modes), width,
+ height, fmt->format.width,
+ fmt->format.height);
+
+ mutex_lock(&ov8856->mutex);
+ ov8856_update_pad_format(mode, &fmt->format);
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+ *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format;
+ } else {
+ ov8856->cur_mode = mode;
+ __v4l2_ctrl_s_ctrl(ov8856->link_freq, mode->link_freq_index);
+ __v4l2_ctrl_s_ctrl_int64(ov8856->pixel_rate,
+ to_pixel_rate(mode->link_freq_index));
+
+ /* Update limits and set FPS to default */
+ vblank_def = mode->vts_def - mode->height;
+ __v4l2_ctrl_modify_range(ov8856->vblank,
+ mode->vts_min - mode->height,
+ OV8856_VTS_MAX - mode->height, 1,
+ vblank_def);
+ __v4l2_ctrl_s_ctrl(ov8856->vblank, vblank_def);
+ h_blank = to_pixels_per_line(mode->hts, mode->link_freq_index) -
+ mode->width;
+ __v4l2_ctrl_modify_range(ov8856->hblank, h_blank, h_blank, 1,
+ h_blank);
+ }
+
+ mutex_unlock(&ov8856->mutex);
+
+ return 0;
+}
+
+static int ov8856_get_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ struct ov8856 *ov8856 = to_ov8856(sd);
+
+ mutex_lock(&ov8856->mutex);
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY)
+ fmt->format = *v4l2_subdev_get_try_format(&ov8856->sd, cfg,
+ fmt->pad);
+ else
+ ov8856_update_pad_format(ov8856->cur_mode, &fmt->format);
+
+ mutex_unlock(&ov8856->mutex);
+
+ return 0;
+}
+
+static int ov8856_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ /* Only one bayer order GRBG is supported */
+ if (code->index > 0)
+ return -EINVAL;
+
+ code->code = MEDIA_BUS_FMT_SGRBG10_1X10;
+
+ return 0;
+}
+
+static int ov8856_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ if (fse->index >= ARRAY_SIZE(supported_modes))
+ return -EINVAL;
+
+ if (fse->code != MEDIA_BUS_FMT_SGRBG10_1X10)
+ return -EINVAL;
+
+ fse->min_width = supported_modes[fse->index].width;
+ fse->max_width = fse->min_width;
+ fse->min_height = supported_modes[fse->index].height;
+ fse->max_height = fse->min_height;
+
+ return 0;
+}
+
+static int ov8856_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ struct ov8856 *ov8856 = to_ov8856(sd);
+
+ mutex_lock(&ov8856->mutex);
+ ov8856_update_pad_format(&supported_modes[0],
+ v4l2_subdev_get_try_format(sd, fh->pad, 0));
+ mutex_unlock(&ov8856->mutex);
+
+ return 0;
+}
+
+static const struct v4l2_subdev_video_ops ov8856_video_ops = {
+ .s_stream = ov8856_set_stream,
+};
+
+static const struct v4l2_subdev_pad_ops ov8856_pad_ops = {
+ .set_fmt = ov8856_set_format,
+ .get_fmt = ov8856_get_format,
+ .enum_mbus_code = ov8856_enum_mbus_code,
+ .enum_frame_size = ov8856_enum_frame_size,
+};
+
+static const struct v4l2_subdev_ops ov8856_subdev_ops = {
+ .video = &ov8856_video_ops,
+ .pad = &ov8856_pad_ops,
+};
+
+static const struct media_entity_operations ov8856_subdev_entity_ops = {
+ .link_validate = v4l2_subdev_link_validate,
+};
+
+static const struct v4l2_subdev_internal_ops ov8856_internal_ops = {
+ .open = ov8856_open,
+};
+
+static int ov8856_identify_module(struct ov8856 *ov8856)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&ov8856->sd);
+ int ret;
+ u32 val;
+
+ ret = ov8856_read_reg(ov8856, OV8856_REG_CHIP_ID,
+ OV8856_REG_VALUE_24BIT, &val);
+ if (ret)
+ return ret;
+
+ if (val != OV8856_CHIP_ID) {
+ dev_err(&client->dev, "chip id mismatch: %x!=%x",
+ OV8856_CHIP_ID, val);
+ return -ENXIO;
+ }
+
+ return 0;
+}
+
+static int ov8856_check_hwcfg(struct device *dev)
+{
+ struct fwnode_handle *ep;
+ struct fwnode_handle *fwnode = dev_fwnode(dev);
+ struct v4l2_fwnode_endpoint bus_cfg = {
+ .bus_type = V4L2_MBUS_CSI2_DPHY
+ };
+ u32 mclk;
+ int ret;
+ unsigned int i, j;
+
+ if (!fwnode)
+ return -ENXIO;
+
+ fwnode_property_read_u32(fwnode, "clock-frequency", &mclk);
+ if (mclk != OV8856_MCLK) {
+ dev_err(dev, "external clock %d is not supported", mclk);
+ return -EINVAL;
+ }
+
+ ep = fwnode_graph_get_next_endpoint(fwnode, NULL);
+ if (!ep)
+ return -ENXIO;
+
+ ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg);
+ fwnode_handle_put(ep);
+ if (ret)
+ return ret;
+
+ if (bus_cfg.bus.mipi_csi2.num_data_lanes != OV8856_DATA_LANES) {
+ dev_err(dev, "number of CSI2 data lanes %d is not supported",
+ bus_cfg.bus.mipi_csi2.num_data_lanes);
+ ret = -EINVAL;
+ goto check_hwcfg_error;
+ }
+
+ if (!bus_cfg.nr_of_link_frequencies) {
+ dev_err(dev, "no link frequencies defined");
+ ret = -EINVAL;
+ goto check_hwcfg_error;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(link_freq_menu_items); i++) {
+ for (j = 0; j < bus_cfg.nr_of_link_frequencies; j++) {
+ if (link_freq_menu_items[i] ==
+ bus_cfg.link_frequencies[j])
+ break;
+ }
+
+ if (j == bus_cfg.nr_of_link_frequencies) {
+ dev_err(dev, "no link frequency %lld supported",
+ link_freq_menu_items[i]);
+ ret = -EINVAL;
+ goto check_hwcfg_error;
+ }
+ }
+
+check_hwcfg_error:
+ v4l2_fwnode_endpoint_free(&bus_cfg);
+
+ return ret;
+}
+
+static int ov8856_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ov8856 *ov8856 = to_ov8856(sd);
+
+ v4l2_async_unregister_subdev(sd);
+ media_entity_cleanup(&sd->entity);
+ v4l2_ctrl_handler_free(sd->ctrl_handler);
+ pm_runtime_disable(&client->dev);
+ mutex_destroy(&ov8856->mutex);
+
+ return 0;
+}
+
+static int ov8856_probe(struct i2c_client *client)
+{
+ struct ov8856 *ov8856;
+ int ret;
+
+ ret = ov8856_check_hwcfg(&client->dev);
+ if (ret) {
+ dev_err(&client->dev, "failed to check HW configuration: %d",
+ ret);
+ return ret;
+ }
+
+ ov8856 = devm_kzalloc(&client->dev, sizeof(*ov8856), GFP_KERNEL);
+ if (!ov8856)
+ return -ENOMEM;
+
+ v4l2_i2c_subdev_init(&ov8856->sd, client, &ov8856_subdev_ops);
+ ret = ov8856_identify_module(ov8856);
+ if (ret) {
+ dev_err(&client->dev, "failed to find sensor: %d", ret);
+ return ret;
+ }
+
+ mutex_init(&ov8856->mutex);
+ ov8856->cur_mode = &supported_modes[0];
+ ret = ov8856_init_controls(ov8856);
+ if (ret) {
+ dev_err(&client->dev, "failed to init controls: %d", ret);
+ goto probe_error_v4l2_ctrl_handler_free;
+ }
+
+ ov8856->sd.internal_ops = &ov8856_internal_ops;
+ ov8856->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ ov8856->sd.entity.ops = &ov8856_subdev_entity_ops;
+ ov8856->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+ ov8856->pad.flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_pads_init(&ov8856->sd.entity, 1, &ov8856->pad);
+ if (ret) {
+ dev_err(&client->dev, "failed to init entity pads: %d", ret);
+ goto probe_error_v4l2_ctrl_handler_free;
+ }
+
+ ret = v4l2_async_register_subdev_sensor_common(&ov8856->sd);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed to register V4L2 subdev: %d",
+ ret);
+ goto probe_error_media_entity_cleanup;
+ }
+
+ /*
+ * Device is already turned on by i2c-core with ACPI domain PM.
+ * Enable runtime PM and turn off the device.
+ */
+ pm_runtime_set_active(&client->dev);
+ pm_runtime_enable(&client->dev);
+ pm_runtime_idle(&client->dev);
+
+ return 0;
+
+probe_error_media_entity_cleanup:
+ media_entity_cleanup(&ov8856->sd.entity);
+
+probe_error_v4l2_ctrl_handler_free:
+ v4l2_ctrl_handler_free(ov8856->sd.ctrl_handler);
+ mutex_destroy(&ov8856->mutex);
+
+ return ret;
+}
+
+static const struct dev_pm_ops ov8856_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(ov8856_suspend, ov8856_resume)
+};
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id ov8856_acpi_ids[] = {
+ {"OVTI8856"},
+ {}
+};
+
+MODULE_DEVICE_TABLE(acpi, ov8856_acpi_ids);
+#endif
+
+static struct i2c_driver ov8856_i2c_driver = {
+ .driver = {
+ .name = "ov8856",
+ .pm = &ov8856_pm_ops,
+ .acpi_match_table = ACPI_PTR(ov8856_acpi_ids),
+ },
+ .probe_new = ov8856_probe,
+ .remove = ov8856_remove,
+};
+
+module_i2c_driver(ov8856_i2c_driver);
+
+MODULE_AUTHOR("Ben Kao <ben.kao@intel.com>");
+MODULE_DESCRIPTION("OmniVision OV8856 sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/i2c/soc_camera/soc_ov9640.c b/drivers/media/i2c/ov9640.c
index eb91b8240083..d6831f28378b 100644
--- a/drivers/media/i2c/soc_camera/soc_ov9640.c
+++ b/drivers/media/i2c/ov9640.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* OmniVision OV96xx Camera Driver
*
@@ -9,14 +10,11 @@
* Kuninori Morimoto <morimoto.kuninori@renesas.com>
*
* Based on ov7670 and soc_camera_platform driver,
+ * transition from soc_camera to pxa_camera based on mt9m111
*
* Copyright 2006-7 Jonathan Corbet <corbet@lwn.net>
* Copyright (C) 2008 Magnus Damm
* Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
- *
- * 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 <linux/init.h>
@@ -27,10 +25,14 @@
#include <linux/v4l2-mediabus.h>
#include <linux/videodev2.h>
-#include <media/soc_camera.h>
+#include <media/v4l2-async.h>
#include <media/v4l2-clk.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+
+#include <linux/gpio/consumer.h>
#include "ov9640.h"
@@ -159,7 +161,7 @@ static const struct ov9640_reg ov9640_regs_rgb[] = {
{ OV9640_MTXS, 0x65 },
};
-static u32 ov9640_codes[] = {
+static const u32 ov9640_codes[] = {
MEDIA_BUS_FMT_UYVY8_2X8,
MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE,
MEDIA_BUS_FMT_RGB565_2X8_LE,
@@ -269,21 +271,23 @@ static int ov9640_s_stream(struct v4l2_subdev *sd, int enable)
/* Set status of additional camera capabilities */
static int ov9640_s_ctrl(struct v4l2_ctrl *ctrl)
{
- struct ov9640_priv *priv = container_of(ctrl->handler, struct ov9640_priv, hdl);
+ struct ov9640_priv *priv = container_of(ctrl->handler,
+ struct ov9640_priv, hdl);
struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev);
switch (ctrl->id) {
case V4L2_CID_VFLIP:
if (ctrl->val)
return ov9640_reg_rmw(client, OV9640_MVFP,
- OV9640_MVFP_V, 0);
+ OV9640_MVFP_V, 0);
return ov9640_reg_rmw(client, OV9640_MVFP, 0, OV9640_MVFP_V);
case V4L2_CID_HFLIP:
if (ctrl->val)
return ov9640_reg_rmw(client, OV9640_MVFP,
- OV9640_MVFP_H, 0);
+ OV9640_MVFP_H, 0);
return ov9640_reg_rmw(client, OV9640_MVFP, 0, OV9640_MVFP_H);
}
+
return -EINVAL;
}
@@ -323,20 +327,33 @@ static int ov9640_set_register(struct v4l2_subdev *sd,
static int ov9640_s_power(struct v4l2_subdev *sd, int on)
{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
struct ov9640_priv *priv = to_ov9640_sensor(sd);
+ int ret = 0;
+
+ if (on) {
+ gpiod_set_value(priv->gpio_power, 1);
+ usleep_range(1000, 2000);
+ ret = v4l2_clk_enable(priv->clk);
+ usleep_range(1000, 2000);
+ gpiod_set_value(priv->gpio_reset, 0);
+ } else {
+ gpiod_set_value(priv->gpio_reset, 1);
+ usleep_range(1000, 2000);
+ v4l2_clk_disable(priv->clk);
+ usleep_range(1000, 2000);
+ gpiod_set_value(priv->gpio_power, 0);
+ }
- return soc_camera_set_power(&client->dev, ssdd, priv->clk, on);
+ return ret;
}
/* select nearest higher resolution for capture */
static void ov9640_res_roundup(u32 *width, u32 *height)
{
- int i;
+ unsigned int i;
enum { QQCIF, QQVGA, QCIF, QVGA, CIF, VGA, SXGA };
- static const int res_x[] = { 88, 160, 176, 320, 352, 640, 1280 };
- static const int res_y[] = { 72, 120, 144, 240, 288, 480, 960 };
+ static const u32 res_x[] = { 88, 160, 176, 320, 352, 640, 1280 };
+ static const u32 res_y[] = { 72, 120, 144, 240, 288, 480, 960 };
for (i = 0; i < ARRAY_SIZE(res_x); i++) {
if (res_x[i] >= *width && res_y[i] >= *height) {
@@ -379,8 +396,9 @@ static int ov9640_write_regs(struct i2c_client *client, u32 width,
u32 code, struct ov9640_reg_alt *alts)
{
const struct ov9640_reg *ov9640_regs, *matrix_regs;
- int ov9640_regs_len, matrix_regs_len;
- int i, ret;
+ unsigned int ov9640_regs_len, matrix_regs_len;
+ unsigned int i;
+ int ret;
u8 val;
/* select register configuration for given resolution */
@@ -454,7 +472,7 @@ static int ov9640_write_regs(struct i2c_client *client, u32 width,
/* write color matrix configuration into the module */
for (i = 0; i < matrix_regs_len; i++) {
ret = ov9640_reg_write(client, matrix_regs[i].reg,
- matrix_regs[i].val);
+ matrix_regs[i].val);
if (ret)
return ret;
}
@@ -465,17 +483,18 @@ static int ov9640_write_regs(struct i2c_client *client, u32 width,
/* program default register values */
static int ov9640_prog_dflt(struct i2c_client *client)
{
- int i, ret;
+ unsigned int i;
+ int ret;
for (i = 0; i < ARRAY_SIZE(ov9640_regs_dflt); i++) {
ret = ov9640_reg_write(client, ov9640_regs_dflt[i].reg,
- ov9640_regs_dflt[i].val);
+ ov9640_regs_dflt[i].val);
if (ret)
return ret;
}
/* wait for the changes to actually happen, 140ms are not enough yet */
- mdelay(150);
+ msleep(150);
return 0;
}
@@ -529,6 +548,7 @@ static int ov9640_set_fmt(struct v4l2_subdev *sd,
return ov9640_s_fmt(sd, mf);
cfg->try_fmt = *mf;
+
return 0;
}
@@ -540,6 +560,7 @@ static int ov9640_enum_mbus_code(struct v4l2_subdev *sd,
return -EINVAL;
code->code = ov9640_codes[code->index];
+
return 0;
}
@@ -630,14 +651,10 @@ static const struct v4l2_subdev_core_ops ov9640_core_ops = {
static int ov9640_g_mbus_config(struct v4l2_subdev *sd,
struct v4l2_mbus_config *cfg)
{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
-
cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER |
V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH |
V4L2_MBUS_DATA_ACTIVE_HIGH;
cfg->type = V4L2_MBUS_PARALLEL;
- cfg->flags = soc_camera_apply_board_flags(ssdd, cfg);
return 0;
}
@@ -666,41 +683,62 @@ static int ov9640_probe(struct i2c_client *client,
const struct i2c_device_id *did)
{
struct ov9640_priv *priv;
- struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
int ret;
- if (!ssdd) {
- dev_err(&client->dev, "Missing platform_data for driver\n");
- return -EINVAL;
- }
-
priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
+ priv->gpio_power = devm_gpiod_get(&client->dev, "Camera power",
+ GPIOD_OUT_LOW);
+ if (IS_ERR_OR_NULL(priv->gpio_power)) {
+ ret = PTR_ERR(priv->gpio_power);
+ return ret;
+ }
+
+ priv->gpio_reset = devm_gpiod_get(&client->dev, "Camera reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR_OR_NULL(priv->gpio_reset)) {
+ ret = PTR_ERR(priv->gpio_reset);
+ return ret;
+ }
+
v4l2_i2c_subdev_init(&priv->subdev, client, &ov9640_subdev_ops);
v4l2_ctrl_handler_init(&priv->hdl, 2);
v4l2_ctrl_new_std(&priv->hdl, &ov9640_ctrl_ops,
- V4L2_CID_VFLIP, 0, 1, 1, 0);
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
v4l2_ctrl_new_std(&priv->hdl, &ov9640_ctrl_ops,
- V4L2_CID_HFLIP, 0, 1, 1, 0);
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+
+ if (priv->hdl.error) {
+ ret = priv->hdl.error;
+ goto ectrlinit;
+ }
+
priv->subdev.ctrl_handler = &priv->hdl;
- if (priv->hdl.error)
- return priv->hdl.error;
priv->clk = v4l2_clk_get(&client->dev, "mclk");
if (IS_ERR(priv->clk)) {
ret = PTR_ERR(priv->clk);
- goto eclkget;
+ goto ectrlinit;
}
ret = ov9640_video_probe(client);
- if (ret) {
- v4l2_clk_put(priv->clk);
-eclkget:
- v4l2_ctrl_handler_free(&priv->hdl);
- }
+ if (ret)
+ goto eprobe;
+
+ priv->subdev.dev = &client->dev;
+ ret = v4l2_async_register_subdev(&priv->subdev);
+ if (ret)
+ goto eprobe;
+
+ return 0;
+
+eprobe:
+ v4l2_clk_put(priv->clk);
+ectrlinit:
+ v4l2_ctrl_handler_free(&priv->hdl);
return ret;
}
@@ -711,8 +749,9 @@ static int ov9640_remove(struct i2c_client *client)
struct ov9640_priv *priv = to_ov9640_sensor(sd);
v4l2_clk_put(priv->clk);
- v4l2_device_unregister_subdev(&priv->subdev);
+ v4l2_async_unregister_subdev(&priv->subdev);
v4l2_ctrl_handler_free(&priv->hdl);
+
return 0;
}
diff --git a/drivers/media/i2c/soc_camera/ov9640.h b/drivers/media/i2c/ov9640.h
index 65d13ff17536..a8ed6992c1a8 100644
--- a/drivers/media/i2c/soc_camera/ov9640.h
+++ b/drivers/media/i2c/ov9640.h
@@ -1,11 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* OmniVision OV96xx Camera Header File
*
* Copyright (C) 2009 Marek Vasut <marek.vasut@gmail.com>
- *
- * 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.
*/
#ifndef __DRIVERS_MEDIA_VIDEO_OV9640_H__
@@ -200,6 +197,8 @@ struct ov9640_priv {
struct v4l2_subdev subdev;
struct v4l2_ctrl_handler hdl;
struct v4l2_clk *clk;
+ struct gpio_desc *gpio_power;
+ struct gpio_desc *gpio_reset;
int model;
int revision;
diff --git a/drivers/media/i2c/ov9650.c b/drivers/media/i2c/ov9650.c
index f0587c0c0a72..eefd57ec2a73 100644
--- a/drivers/media/i2c/ov9650.c
+++ b/drivers/media/i2c/ov9650.c
@@ -45,8 +45,8 @@ MODULE_PARM_DESC(debug, "Debug level (0-2)");
* OV9650/OV9652 register definitions
*/
#define REG_GAIN 0x00 /* Gain control, AGC[7:0] */
-#define REG_BLUE 0x01 /* AWB - Blue chanel gain */
-#define REG_RED 0x02 /* AWB - Red chanel gain */
+#define REG_BLUE 0x01 /* AWB - Blue channel gain */
+#define REG_RED 0x02 /* AWB - Red channel gain */
#define REG_VREF 0x03 /* [7:6] - AGC[9:8], [5:3]/[2:0] */
#define VREF_GAIN_MASK 0xc0 /* - VREF end/start low 3 bits */
#define REG_COM1 0x04
diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-core.c b/drivers/media/i2c/s5c73m3/s5c73m3-core.c
index c461847ddae8..b52fe250f75f 100644
--- a/drivers/media/i2c/s5c73m3/s5c73m3-core.c
+++ b/drivers/media/i2c/s5c73m3/s5c73m3-core.c
@@ -1431,7 +1431,7 @@ err:
for (++i; i < S5C73M3_MAX_SUPPLIES; i++) {
int r = regulator_enable(state->supplies[i].consumer);
if (r < 0)
- v4l2_err(&state->oif_sd, "Failed to reenable %s: %d\n",
+ v4l2_err(&state->oif_sd, "Failed to re-enable %s: %d\n",
state->supplies[i].supply, r);
}
diff --git a/drivers/media/i2c/s5k4ecgx.c b/drivers/media/i2c/s5k4ecgx.c
index 79aa2740edc4..79c1894c2c83 100644
--- a/drivers/media/i2c/s5k4ecgx.c
+++ b/drivers/media/i2c/s5k4ecgx.c
@@ -263,8 +263,6 @@ static int s5k4ecgx_read(struct i2c_client *client, u32 addr, u16 *val)
ret = s5k4ecgx_i2c_write(client, REG_CMDRD_ADDRL, low);
if (!ret)
ret = s5k4ecgx_i2c_read(client, REG_CMDBUF0_ADDR, val);
- if (!ret)
- dev_err(&client->dev, "Failed to execute read command\n");
return ret;
}
diff --git a/drivers/media/i2c/s5k6aa.c b/drivers/media/i2c/s5k6aa.c
index ab26f549d716..f8630c4c2ef0 100644
--- a/drivers/media/i2c/s5k6aa.c
+++ b/drivers/media/i2c/s5k6aa.c
@@ -729,7 +729,7 @@ static int s5k6aa_new_config_sync(struct i2c_client *client, int timeout,
* @s5k6aa: pointer to &struct s5k6aa describing the device
* @preset: s5kaa preset to be applied
*
- * Configure output resolution and color fromat, pixel clock
+ * Configure output resolution and color format, pixel clock
* frequency range, device frame rate type and frame period range.
*/
static int s5k6aa_set_prev_config(struct s5k6aa *s5k6aa,
diff --git a/drivers/media/i2c/saa7115.c b/drivers/media/i2c/saa7115.c
index 6bc278aa31fc..88dc6baac639 100644
--- a/drivers/media/i2c/saa7115.c
+++ b/drivers/media/i2c/saa7115.c
@@ -1766,7 +1766,7 @@ static int saa711x_detect_chip(struct i2c_client *client,
* exists. However, tests on a device labeled as:
* "GM7113C 1145" returned "10" on all 16 chip
* version (reg 0x00) reads. So, we need to also
- * accept at least verion 0. For now, let's just
+ * accept at least version 0. For now, let's just
* assume that a device that returns "0000" for
* the lower nibble is a gm7113c.
*/
diff --git a/drivers/media/i2c/saa717x.c b/drivers/media/i2c/saa717x.c
index 668c39cc29e8..86b8b65ea683 100644
--- a/drivers/media/i2c/saa717x.c
+++ b/drivers/media/i2c/saa717x.c
@@ -844,7 +844,7 @@ static void set_h_prescale(struct v4l2_subdev *sd,
if (i == count)
return;
- /* horizonal prescaling */
+ /* horizontal prescaling */
saa717x_write(sd, 0x60 + task_shift, vals[i].xpsc);
/* accumulation length */
saa717x_write(sd, 0x61 + task_shift, vals[i].xacl);
diff --git a/drivers/media/i2c/soc_camera/Makefile b/drivers/media/i2c/soc_camera/Makefile
deleted file mode 100644
index 09ae483b96ef..000000000000
--- a/drivers/media/i2c/soc_camera/Makefile
+++ /dev/null
@@ -1,10 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0
-obj-$(CONFIG_SOC_CAMERA_MT9M001) += soc_mt9m001.o
-obj-$(CONFIG_SOC_CAMERA_MT9T112) += soc_mt9t112.o
-obj-$(CONFIG_SOC_CAMERA_MT9V022) += soc_mt9v022.o
-obj-$(CONFIG_SOC_CAMERA_OV5642) += soc_ov5642.o
-obj-$(CONFIG_SOC_CAMERA_OV772X) += soc_ov772x.o
-obj-$(CONFIG_SOC_CAMERA_OV9640) += soc_ov9640.o
-obj-$(CONFIG_SOC_CAMERA_OV9740) += soc_ov9740.o
-obj-$(CONFIG_SOC_CAMERA_RJ54N1) += soc_rj54n1cb0c.o
-obj-$(CONFIG_SOC_CAMERA_TW9910) += soc_tw9910.o
diff --git a/drivers/media/i2c/soc_camera/soc_mt9t112.c b/drivers/media/i2c/soc_camera/soc_mt9t112.c
deleted file mode 100644
index ea1ff270bc2d..000000000000
--- a/drivers/media/i2c/soc_camera/soc_mt9t112.c
+++ /dev/null
@@ -1,1157 +0,0 @@
-/*
- * mt9t112 Camera Driver
- *
- * Copyright (C) 2009 Renesas Solutions Corp.
- * Kuninori Morimoto <morimoto.kuninori@renesas.com>
- *
- * Based on ov772x driver, mt9m111 driver,
- *
- * Copyright (C) 2008 Kuninori Morimoto <morimoto.kuninori@renesas.com>
- * Copyright (C) 2008, Robert Jarzmik <robert.jarzmik@free.fr>
- * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net>
- * Copyright (C) 2008 Magnus Damm
- * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
- *
- * 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 <linux/delay.h>
-#include <linux/i2c.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/v4l2-mediabus.h>
-#include <linux/videodev2.h>
-
-#include <media/i2c/mt9t112.h>
-#include <media/soc_camera.h>
-#include <media/v4l2-clk.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-image-sizes.h>
-
-/* you can check PLL/clock info */
-/* #define EXT_CLOCK 24000000 */
-
-/************************************************************************
- macro
-************************************************************************/
-/*
- * frame size
- */
-#define MAX_WIDTH 2048
-#define MAX_HEIGHT 1536
-
-/*
- * macro of read/write
- */
-#define ECHECKER(ret, x) \
- do { \
- (ret) = (x); \
- if ((ret) < 0) \
- return (ret); \
- } while (0)
-
-#define mt9t112_reg_write(ret, client, a, b) \
- ECHECKER(ret, __mt9t112_reg_write(client, a, b))
-#define mt9t112_mcu_write(ret, client, a, b) \
- ECHECKER(ret, __mt9t112_mcu_write(client, a, b))
-
-#define mt9t112_reg_mask_set(ret, client, a, b, c) \
- ECHECKER(ret, __mt9t112_reg_mask_set(client, a, b, c))
-#define mt9t112_mcu_mask_set(ret, client, a, b, c) \
- ECHECKER(ret, __mt9t112_mcu_mask_set(client, a, b, c))
-
-#define mt9t112_reg_read(ret, client, a) \
- ECHECKER(ret, __mt9t112_reg_read(client, a))
-
-/*
- * Logical address
- */
-#define _VAR(id, offset, base) (base | (id & 0x1f) << 10 | (offset & 0x3ff))
-#define VAR(id, offset) _VAR(id, offset, 0x0000)
-#define VAR8(id, offset) _VAR(id, offset, 0x8000)
-
-/************************************************************************
- struct
-************************************************************************/
-struct mt9t112_format {
- u32 code;
- enum v4l2_colorspace colorspace;
- u16 fmt;
- u16 order;
-};
-
-struct mt9t112_priv {
- struct v4l2_subdev subdev;
- struct mt9t112_platform_data *info;
- struct i2c_client *client;
- struct v4l2_rect frame;
- struct v4l2_clk *clk;
- const struct mt9t112_format *format;
- int num_formats;
- u32 flags;
-/* for flags */
-#define INIT_DONE (1 << 0)
-#define PCLK_RISING (1 << 1)
-};
-
-/************************************************************************
- supported format
-************************************************************************/
-
-static const struct mt9t112_format mt9t112_cfmts[] = {
- {
- .code = MEDIA_BUS_FMT_UYVY8_2X8,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .fmt = 1,
- .order = 0,
- }, {
- .code = MEDIA_BUS_FMT_VYUY8_2X8,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .fmt = 1,
- .order = 1,
- }, {
- .code = MEDIA_BUS_FMT_YUYV8_2X8,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .fmt = 1,
- .order = 2,
- }, {
- .code = MEDIA_BUS_FMT_YVYU8_2X8,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .fmt = 1,
- .order = 3,
- }, {
- .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .fmt = 8,
- .order = 2,
- }, {
- .code = MEDIA_BUS_FMT_RGB565_2X8_LE,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .fmt = 4,
- .order = 2,
- },
-};
-
-/************************************************************************
- general function
-************************************************************************/
-static struct mt9t112_priv *to_mt9t112(const struct i2c_client *client)
-{
- return container_of(i2c_get_clientdata(client),
- struct mt9t112_priv,
- subdev);
-}
-
-static int __mt9t112_reg_read(const struct i2c_client *client, u16 command)
-{
- struct i2c_msg msg[2];
- u8 buf[2];
- int ret;
-
- command = swab16(command);
-
- msg[0].addr = client->addr;
- msg[0].flags = 0;
- msg[0].len = 2;
- msg[0].buf = (u8 *)&command;
-
- msg[1].addr = client->addr;
- msg[1].flags = I2C_M_RD;
- msg[1].len = 2;
- msg[1].buf = buf;
-
- /*
- * if return value of this function is < 0,
- * it mean error.
- * else, under 16bit is valid data.
- */
- ret = i2c_transfer(client->adapter, msg, 2);
- if (ret < 0)
- return ret;
-
- memcpy(&ret, buf, 2);
- return swab16(ret);
-}
-
-static int __mt9t112_reg_write(const struct i2c_client *client,
- u16 command, u16 data)
-{
- struct i2c_msg msg;
- u8 buf[4];
- int ret;
-
- command = swab16(command);
- data = swab16(data);
-
- memcpy(buf + 0, &command, 2);
- memcpy(buf + 2, &data, 2);
-
- msg.addr = client->addr;
- msg.flags = 0;
- msg.len = 4;
- msg.buf = buf;
-
- /*
- * i2c_transfer return message length,
- * but this function should return 0 if correct case
- */
- ret = i2c_transfer(client->adapter, &msg, 1);
- if (ret >= 0)
- ret = 0;
-
- return ret;
-}
-
-static int __mt9t112_reg_mask_set(const struct i2c_client *client,
- u16 command,
- u16 mask,
- u16 set)
-{
- int val = __mt9t112_reg_read(client, command);
- if (val < 0)
- return val;
-
- val &= ~mask;
- val |= set & mask;
-
- return __mt9t112_reg_write(client, command, val);
-}
-
-/* mcu access */
-static int __mt9t112_mcu_read(const struct i2c_client *client, u16 command)
-{
- int ret;
-
- ret = __mt9t112_reg_write(client, 0x098E, command);
- if (ret < 0)
- return ret;
-
- return __mt9t112_reg_read(client, 0x0990);
-}
-
-static int __mt9t112_mcu_write(const struct i2c_client *client,
- u16 command, u16 data)
-{
- int ret;
-
- ret = __mt9t112_reg_write(client, 0x098E, command);
- if (ret < 0)
- return ret;
-
- return __mt9t112_reg_write(client, 0x0990, data);
-}
-
-static int __mt9t112_mcu_mask_set(const struct i2c_client *client,
- u16 command,
- u16 mask,
- u16 set)
-{
- int val = __mt9t112_mcu_read(client, command);
- if (val < 0)
- return val;
-
- val &= ~mask;
- val |= set & mask;
-
- return __mt9t112_mcu_write(client, command, val);
-}
-
-static int mt9t112_reset(const struct i2c_client *client)
-{
- int ret;
-
- mt9t112_reg_mask_set(ret, client, 0x001a, 0x0001, 0x0001);
- msleep(1);
- mt9t112_reg_mask_set(ret, client, 0x001a, 0x0001, 0x0000);
-
- return ret;
-}
-
-#ifndef EXT_CLOCK
-#define CLOCK_INFO(a, b)
-#else
-#define CLOCK_INFO(a, b) mt9t112_clock_info(a, b)
-static int mt9t112_clock_info(const struct i2c_client *client, u32 ext)
-{
- int m, n, p1, p2, p3, p4, p5, p6, p7;
- u32 vco, clk;
- char *enable;
-
- ext /= 1000; /* kbyte order */
-
- mt9t112_reg_read(n, client, 0x0012);
- p1 = n & 0x000f;
- n = n >> 4;
- p2 = n & 0x000f;
- n = n >> 4;
- p3 = n & 0x000f;
-
- mt9t112_reg_read(n, client, 0x002a);
- p4 = n & 0x000f;
- n = n >> 4;
- p5 = n & 0x000f;
- n = n >> 4;
- p6 = n & 0x000f;
-
- mt9t112_reg_read(n, client, 0x002c);
- p7 = n & 0x000f;
-
- mt9t112_reg_read(n, client, 0x0010);
- m = n & 0x00ff;
- n = (n >> 8) & 0x003f;
-
- enable = ((6000 > ext) || (54000 < ext)) ? "X" : "";
- dev_dbg(&client->dev, "EXTCLK : %10u K %s\n", ext, enable);
-
- vco = 2 * m * ext / (n+1);
- enable = ((384000 > vco) || (768000 < vco)) ? "X" : "";
- dev_dbg(&client->dev, "VCO : %10u K %s\n", vco, enable);
-
- clk = vco / (p1+1) / (p2+1);
- enable = (96000 < clk) ? "X" : "";
- dev_dbg(&client->dev, "PIXCLK : %10u K %s\n", clk, enable);
-
- clk = vco / (p3+1);
- enable = (768000 < clk) ? "X" : "";
- dev_dbg(&client->dev, "MIPICLK : %10u K %s\n", clk, enable);
-
- clk = vco / (p6+1);
- enable = (96000 < clk) ? "X" : "";
- dev_dbg(&client->dev, "MCU CLK : %10u K %s\n", clk, enable);
-
- clk = vco / (p5+1);
- enable = (54000 < clk) ? "X" : "";
- dev_dbg(&client->dev, "SOC CLK : %10u K %s\n", clk, enable);
-
- clk = vco / (p4+1);
- enable = (70000 < clk) ? "X" : "";
- dev_dbg(&client->dev, "Sensor CLK : %10u K %s\n", clk, enable);
-
- clk = vco / (p7+1);
- dev_dbg(&client->dev, "External sensor : %10u K\n", clk);
-
- clk = ext / (n+1);
- enable = ((2000 > clk) || (24000 < clk)) ? "X" : "";
- dev_dbg(&client->dev, "PFD : %10u K %s\n", clk, enable);
-
- return 0;
-}
-#endif
-
-static void mt9t112_frame_check(u32 *width, u32 *height, u32 *left, u32 *top)
-{
- soc_camera_limit_side(left, width, 0, 0, MAX_WIDTH);
- soc_camera_limit_side(top, height, 0, 0, MAX_HEIGHT);
-}
-
-static int mt9t112_set_a_frame_size(const struct i2c_client *client,
- u16 width,
- u16 height)
-{
- int ret;
- u16 wstart = (MAX_WIDTH - width) / 2;
- u16 hstart = (MAX_HEIGHT - height) / 2;
-
- /* (Context A) Image Width/Height */
- mt9t112_mcu_write(ret, client, VAR(26, 0), width);
- mt9t112_mcu_write(ret, client, VAR(26, 2), height);
-
- /* (Context A) Output Width/Height */
- mt9t112_mcu_write(ret, client, VAR(18, 43), 8 + width);
- mt9t112_mcu_write(ret, client, VAR(18, 45), 8 + height);
-
- /* (Context A) Start Row/Column */
- mt9t112_mcu_write(ret, client, VAR(18, 2), 4 + hstart);
- mt9t112_mcu_write(ret, client, VAR(18, 4), 4 + wstart);
-
- /* (Context A) End Row/Column */
- mt9t112_mcu_write(ret, client, VAR(18, 6), 11 + height + hstart);
- mt9t112_mcu_write(ret, client, VAR(18, 8), 11 + width + wstart);
-
- mt9t112_mcu_write(ret, client, VAR8(1, 0), 0x06);
-
- return ret;
-}
-
-static int mt9t112_set_pll_dividers(const struct i2c_client *client,
- u8 m, u8 n,
- u8 p1, u8 p2, u8 p3,
- u8 p4, u8 p5, u8 p6,
- u8 p7)
-{
- int ret;
- u16 val;
-
- /* N/M */
- val = (n << 8) |
- (m << 0);
- mt9t112_reg_mask_set(ret, client, 0x0010, 0x3fff, val);
-
- /* P1/P2/P3 */
- val = ((p3 & 0x0F) << 8) |
- ((p2 & 0x0F) << 4) |
- ((p1 & 0x0F) << 0);
- mt9t112_reg_mask_set(ret, client, 0x0012, 0x0fff, val);
-
- /* P4/P5/P6 */
- val = (0x7 << 12) |
- ((p6 & 0x0F) << 8) |
- ((p5 & 0x0F) << 4) |
- ((p4 & 0x0F) << 0);
- mt9t112_reg_mask_set(ret, client, 0x002A, 0x7fff, val);
-
- /* P7 */
- val = (0x1 << 12) |
- ((p7 & 0x0F) << 0);
- mt9t112_reg_mask_set(ret, client, 0x002C, 0x100f, val);
-
- return ret;
-}
-
-static int mt9t112_init_pll(const struct i2c_client *client)
-{
- struct mt9t112_priv *priv = to_mt9t112(client);
- int data, i, ret;
-
- mt9t112_reg_mask_set(ret, client, 0x0014, 0x003, 0x0001);
-
- /* PLL control: BYPASS PLL = 8517 */
- mt9t112_reg_write(ret, client, 0x0014, 0x2145);
-
- /* Replace these registers when new timing parameters are generated */
- mt9t112_set_pll_dividers(client,
- priv->info->divider.m,
- priv->info->divider.n,
- priv->info->divider.p1,
- priv->info->divider.p2,
- priv->info->divider.p3,
- priv->info->divider.p4,
- priv->info->divider.p5,
- priv->info->divider.p6,
- priv->info->divider.p7);
-
- /*
- * TEST_BYPASS on
- * PLL_ENABLE on
- * SEL_LOCK_DET on
- * TEST_BYPASS off
- */
- mt9t112_reg_write(ret, client, 0x0014, 0x2525);
- mt9t112_reg_write(ret, client, 0x0014, 0x2527);
- mt9t112_reg_write(ret, client, 0x0014, 0x3427);
- mt9t112_reg_write(ret, client, 0x0014, 0x3027);
-
- mdelay(10);
-
- /*
- * PLL_BYPASS off
- * Reference clock count
- * I2C Master Clock Divider
- */
- mt9t112_reg_write(ret, client, 0x0014, 0x3046);
- mt9t112_reg_write(ret, client, 0x0016, 0x0400); /* JPEG initialization workaround */
- mt9t112_reg_write(ret, client, 0x0022, 0x0190);
- mt9t112_reg_write(ret, client, 0x3B84, 0x0212);
-
- /* External sensor clock is PLL bypass */
- mt9t112_reg_write(ret, client, 0x002E, 0x0500);
-
- mt9t112_reg_mask_set(ret, client, 0x0018, 0x0002, 0x0002);
- mt9t112_reg_mask_set(ret, client, 0x3B82, 0x0004, 0x0004);
-
- /* MCU disabled */
- mt9t112_reg_mask_set(ret, client, 0x0018, 0x0004, 0x0004);
-
- /* out of standby */
- mt9t112_reg_mask_set(ret, client, 0x0018, 0x0001, 0);
-
- mdelay(50);
-
- /*
- * Standby Workaround
- * Disable Secondary I2C Pads
- */
- mt9t112_reg_write(ret, client, 0x0614, 0x0001);
- mdelay(1);
- mt9t112_reg_write(ret, client, 0x0614, 0x0001);
- mdelay(1);
- mt9t112_reg_write(ret, client, 0x0614, 0x0001);
- mdelay(1);
- mt9t112_reg_write(ret, client, 0x0614, 0x0001);
- mdelay(1);
- mt9t112_reg_write(ret, client, 0x0614, 0x0001);
- mdelay(1);
- mt9t112_reg_write(ret, client, 0x0614, 0x0001);
- mdelay(1);
-
- /* poll to verify out of standby. Must Poll this bit */
- for (i = 0; i < 100; i++) {
- mt9t112_reg_read(data, client, 0x0018);
- if (!(0x4000 & data))
- break;
-
- mdelay(10);
- }
-
- return ret;
-}
-
-static int mt9t112_init_setting(const struct i2c_client *client)
-{
-
- int ret;
-
- /* Adaptive Output Clock (A) */
- mt9t112_mcu_mask_set(ret, client, VAR(26, 160), 0x0040, 0x0000);
-
- /* Read Mode (A) */
- mt9t112_mcu_write(ret, client, VAR(18, 12), 0x0024);
-
- /* Fine Correction (A) */
- mt9t112_mcu_write(ret, client, VAR(18, 15), 0x00CC);
-
- /* Fine IT Min (A) */
- mt9t112_mcu_write(ret, client, VAR(18, 17), 0x01f1);
-
- /* Fine IT Max Margin (A) */
- mt9t112_mcu_write(ret, client, VAR(18, 19), 0x00fF);
-
- /* Base Frame Lines (A) */
- mt9t112_mcu_write(ret, client, VAR(18, 29), 0x032D);
-
- /* Min Line Length (A) */
- mt9t112_mcu_write(ret, client, VAR(18, 31), 0x073a);
-
- /* Line Length (A) */
- mt9t112_mcu_write(ret, client, VAR(18, 37), 0x07d0);
-
- /* Adaptive Output Clock (B) */
- mt9t112_mcu_mask_set(ret, client, VAR(27, 160), 0x0040, 0x0000);
-
- /* Row Start (B) */
- mt9t112_mcu_write(ret, client, VAR(18, 74), 0x004);
-
- /* Column Start (B) */
- mt9t112_mcu_write(ret, client, VAR(18, 76), 0x004);
-
- /* Row End (B) */
- mt9t112_mcu_write(ret, client, VAR(18, 78), 0x60B);
-
- /* Column End (B) */
- mt9t112_mcu_write(ret, client, VAR(18, 80), 0x80B);
-
- /* Fine Correction (B) */
- mt9t112_mcu_write(ret, client, VAR(18, 87), 0x008C);
-
- /* Fine IT Min (B) */
- mt9t112_mcu_write(ret, client, VAR(18, 89), 0x01F1);
-
- /* Fine IT Max Margin (B) */
- mt9t112_mcu_write(ret, client, VAR(18, 91), 0x00FF);
-
- /* Base Frame Lines (B) */
- mt9t112_mcu_write(ret, client, VAR(18, 101), 0x0668);
-
- /* Min Line Length (B) */
- mt9t112_mcu_write(ret, client, VAR(18, 103), 0x0AF0);
-
- /* Line Length (B) */
- mt9t112_mcu_write(ret, client, VAR(18, 109), 0x0AF0);
-
- /*
- * Flicker Dectection registers
- * This section should be replaced whenever new Timing file is generated
- * All the following registers need to be replaced
- * Following registers are generated from Register Wizard but user can
- * modify them. For detail see auto flicker detection tuning
- */
-
- /* FD_FDPERIOD_SELECT */
- mt9t112_mcu_write(ret, client, VAR8(8, 5), 0x01);
-
- /* PRI_B_CONFIG_FD_ALGO_RUN */
- mt9t112_mcu_write(ret, client, VAR(27, 17), 0x0003);
-
- /* PRI_A_CONFIG_FD_ALGO_RUN */
- mt9t112_mcu_write(ret, client, VAR(26, 17), 0x0003);
-
- /*
- * AFD range detection tuning registers
- */
-
- /* search_f1_50 */
- mt9t112_mcu_write(ret, client, VAR8(18, 165), 0x25);
-
- /* search_f2_50 */
- mt9t112_mcu_write(ret, client, VAR8(18, 166), 0x28);
-
- /* search_f1_60 */
- mt9t112_mcu_write(ret, client, VAR8(18, 167), 0x2C);
-
- /* search_f2_60 */
- mt9t112_mcu_write(ret, client, VAR8(18, 168), 0x2F);
-
- /* period_50Hz (A) */
- mt9t112_mcu_write(ret, client, VAR8(18, 68), 0xBA);
-
- /* secret register by aptina */
- /* period_50Hz (A MSB) */
- mt9t112_mcu_write(ret, client, VAR8(18, 303), 0x00);
-
- /* period_60Hz (A) */
- mt9t112_mcu_write(ret, client, VAR8(18, 69), 0x9B);
-
- /* secret register by aptina */
- /* period_60Hz (A MSB) */
- mt9t112_mcu_write(ret, client, VAR8(18, 301), 0x00);
-
- /* period_50Hz (B) */
- mt9t112_mcu_write(ret, client, VAR8(18, 140), 0x82);
-
- /* secret register by aptina */
- /* period_50Hz (B) MSB */
- mt9t112_mcu_write(ret, client, VAR8(18, 304), 0x00);
-
- /* period_60Hz (B) */
- mt9t112_mcu_write(ret, client, VAR8(18, 141), 0x6D);
-
- /* secret register by aptina */
- /* period_60Hz (B) MSB */
- mt9t112_mcu_write(ret, client, VAR8(18, 302), 0x00);
-
- /* FD Mode */
- mt9t112_mcu_write(ret, client, VAR8(8, 2), 0x10);
-
- /* Stat_min */
- mt9t112_mcu_write(ret, client, VAR8(8, 9), 0x02);
-
- /* Stat_max */
- mt9t112_mcu_write(ret, client, VAR8(8, 10), 0x03);
-
- /* Min_amplitude */
- mt9t112_mcu_write(ret, client, VAR8(8, 12), 0x0A);
-
- /* RX FIFO Watermark (A) */
- mt9t112_mcu_write(ret, client, VAR(18, 70), 0x0014);
-
- /* RX FIFO Watermark (B) */
- mt9t112_mcu_write(ret, client, VAR(18, 142), 0x0014);
-
- /* MCLK: 16MHz
- * PCLK: 73MHz
- * CorePixCLK: 36.5 MHz
- */
- mt9t112_mcu_write(ret, client, VAR8(18, 0x0044), 133);
- mt9t112_mcu_write(ret, client, VAR8(18, 0x0045), 110);
- mt9t112_mcu_write(ret, client, VAR8(18, 0x008c), 130);
- mt9t112_mcu_write(ret, client, VAR8(18, 0x008d), 108);
-
- mt9t112_mcu_write(ret, client, VAR8(18, 0x00A5), 27);
- mt9t112_mcu_write(ret, client, VAR8(18, 0x00a6), 30);
- mt9t112_mcu_write(ret, client, VAR8(18, 0x00a7), 32);
- mt9t112_mcu_write(ret, client, VAR8(18, 0x00a8), 35);
-
- return ret;
-}
-
-static int mt9t112_auto_focus_setting(const struct i2c_client *client)
-{
- int ret;
-
- mt9t112_mcu_write(ret, client, VAR(12, 13), 0x000F);
- mt9t112_mcu_write(ret, client, VAR(12, 23), 0x0F0F);
- mt9t112_mcu_write(ret, client, VAR8(1, 0), 0x06);
-
- mt9t112_reg_write(ret, client, 0x0614, 0x0000);
-
- mt9t112_mcu_write(ret, client, VAR8(1, 0), 0x05);
- mt9t112_mcu_write(ret, client, VAR8(12, 2), 0x02);
- mt9t112_mcu_write(ret, client, VAR(12, 3), 0x0002);
- mt9t112_mcu_write(ret, client, VAR(17, 3), 0x8001);
- mt9t112_mcu_write(ret, client, VAR(17, 11), 0x0025);
- mt9t112_mcu_write(ret, client, VAR(17, 13), 0x0193);
- mt9t112_mcu_write(ret, client, VAR8(17, 33), 0x18);
- mt9t112_mcu_write(ret, client, VAR8(1, 0), 0x05);
-
- return ret;
-}
-
-static int mt9t112_auto_focus_trigger(const struct i2c_client *client)
-{
- int ret;
-
- mt9t112_mcu_write(ret, client, VAR8(12, 25), 0x01);
-
- return ret;
-}
-
-static int mt9t112_init_camera(const struct i2c_client *client)
-{
- int ret;
-
- ECHECKER(ret, mt9t112_reset(client));
-
- ECHECKER(ret, mt9t112_init_pll(client));
-
- ECHECKER(ret, mt9t112_init_setting(client));
-
- ECHECKER(ret, mt9t112_auto_focus_setting(client));
-
- mt9t112_reg_mask_set(ret, client, 0x0018, 0x0004, 0);
-
- /* Analog setting B */
- mt9t112_reg_write(ret, client, 0x3084, 0x2409);
- mt9t112_reg_write(ret, client, 0x3092, 0x0A49);
- mt9t112_reg_write(ret, client, 0x3094, 0x4949);
- mt9t112_reg_write(ret, client, 0x3096, 0x4950);
-
- /*
- * Disable adaptive clock
- * PRI_A_CONFIG_JPEG_OB_TX_CONTROL_VAR
- * PRI_B_CONFIG_JPEG_OB_TX_CONTROL_VAR
- */
- mt9t112_mcu_write(ret, client, VAR(26, 160), 0x0A2E);
- mt9t112_mcu_write(ret, client, VAR(27, 160), 0x0A2E);
-
- /* Configure STatus in Status_before_length Format and enable header */
- /* PRI_B_CONFIG_JPEG_OB_TX_CONTROL_VAR */
- mt9t112_mcu_write(ret, client, VAR(27, 144), 0x0CB4);
-
- /* Enable JPEG in context B */
- /* PRI_B_CONFIG_JPEG_OB_TX_CONTROL_VAR */
- mt9t112_mcu_write(ret, client, VAR8(27, 142), 0x01);
-
- /* Disable Dac_TXLO */
- mt9t112_reg_write(ret, client, 0x316C, 0x350F);
-
- /* Set max slew rates */
- mt9t112_reg_write(ret, client, 0x1E, 0x777);
-
- return ret;
-}
-
-/************************************************************************
- v4l2_subdev_core_ops
-************************************************************************/
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int mt9t112_g_register(struct v4l2_subdev *sd,
- struct v4l2_dbg_register *reg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- int ret;
-
- reg->size = 2;
- mt9t112_reg_read(ret, client, reg->reg);
-
- reg->val = (__u64)ret;
-
- return 0;
-}
-
-static int mt9t112_s_register(struct v4l2_subdev *sd,
- const struct v4l2_dbg_register *reg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- int ret;
-
- mt9t112_reg_write(ret, client, reg->reg, reg->val);
-
- return ret;
-}
-#endif
-
-static int mt9t112_s_power(struct v4l2_subdev *sd, int on)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
- struct mt9t112_priv *priv = to_mt9t112(client);
-
- return soc_camera_set_power(&client->dev, ssdd, priv->clk, on);
-}
-
-static const struct v4l2_subdev_core_ops mt9t112_subdev_core_ops = {
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .g_register = mt9t112_g_register,
- .s_register = mt9t112_s_register,
-#endif
- .s_power = mt9t112_s_power,
-};
-
-
-/************************************************************************
- v4l2_subdev_video_ops
-************************************************************************/
-static int mt9t112_s_stream(struct v4l2_subdev *sd, int enable)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9t112_priv *priv = to_mt9t112(client);
- int ret = 0;
-
- if (!enable) {
- /* FIXME
- *
- * If user selected large output size,
- * and used it long time,
- * mt9t112 camera will be very warm.
- *
- * But current driver can not stop mt9t112 camera.
- * So, set small size here to solve this problem.
- */
- mt9t112_set_a_frame_size(client, VGA_WIDTH, VGA_HEIGHT);
- return ret;
- }
-
- if (!(priv->flags & INIT_DONE)) {
- u16 param = PCLK_RISING & priv->flags ? 0x0001 : 0x0000;
-
- ECHECKER(ret, mt9t112_init_camera(client));
-
- /* Invert PCLK (Data sampled on falling edge of pixclk) */
- mt9t112_reg_write(ret, client, 0x3C20, param);
-
- mdelay(5);
-
- priv->flags |= INIT_DONE;
- }
-
- mt9t112_mcu_write(ret, client, VAR(26, 7), priv->format->fmt);
- mt9t112_mcu_write(ret, client, VAR(26, 9), priv->format->order);
- mt9t112_mcu_write(ret, client, VAR8(1, 0), 0x06);
-
- mt9t112_set_a_frame_size(client,
- priv->frame.width,
- priv->frame.height);
-
- ECHECKER(ret, mt9t112_auto_focus_trigger(client));
-
- dev_dbg(&client->dev, "format : %d\n", priv->format->code);
- dev_dbg(&client->dev, "size : %d x %d\n",
- priv->frame.width,
- priv->frame.height);
-
- CLOCK_INFO(client, EXT_CLOCK);
-
- return ret;
-}
-
-static int mt9t112_set_params(struct mt9t112_priv *priv,
- const struct v4l2_rect *rect,
- u32 code)
-{
- int i;
-
- /*
- * get color format
- */
- for (i = 0; i < priv->num_formats; i++)
- if (mt9t112_cfmts[i].code == code)
- break;
-
- if (i == priv->num_formats)
- return -EINVAL;
-
- priv->frame = *rect;
-
- /*
- * frame size check
- */
- mt9t112_frame_check(&priv->frame.width, &priv->frame.height,
- &priv->frame.left, &priv->frame.top);
-
- priv->format = mt9t112_cfmts + i;
-
- return 0;
-}
-
-static int mt9t112_get_selection(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_selection *sel)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9t112_priv *priv = to_mt9t112(client);
-
- if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
- return -EINVAL;
-
- switch (sel->target) {
- case V4L2_SEL_TGT_CROP_BOUNDS:
- sel->r.left = 0;
- sel->r.top = 0;
- sel->r.width = MAX_WIDTH;
- sel->r.height = MAX_HEIGHT;
- return 0;
- case V4L2_SEL_TGT_CROP:
- sel->r = priv->frame;
- return 0;
- default:
- return -EINVAL;
- }
-}
-
-static int mt9t112_set_selection(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_selection *sel)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9t112_priv *priv = to_mt9t112(client);
- const struct v4l2_rect *rect = &sel->r;
-
- if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE ||
- sel->target != V4L2_SEL_TGT_CROP)
- return -EINVAL;
-
- return mt9t112_set_params(priv, rect, priv->format->code);
-}
-
-static int mt9t112_get_fmt(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_format *format)
-{
- struct v4l2_mbus_framefmt *mf = &format->format;
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9t112_priv *priv = to_mt9t112(client);
-
- if (format->pad)
- return -EINVAL;
-
- mf->width = priv->frame.width;
- mf->height = priv->frame.height;
- mf->colorspace = priv->format->colorspace;
- mf->code = priv->format->code;
- mf->field = V4L2_FIELD_NONE;
-
- return 0;
-}
-
-static int mt9t112_s_fmt(struct v4l2_subdev *sd,
- struct v4l2_mbus_framefmt *mf)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9t112_priv *priv = to_mt9t112(client);
- struct v4l2_rect rect = {
- .width = mf->width,
- .height = mf->height,
- .left = priv->frame.left,
- .top = priv->frame.top,
- };
- int ret;
-
- ret = mt9t112_set_params(priv, &rect, mf->code);
-
- if (!ret)
- mf->colorspace = priv->format->colorspace;
-
- return ret;
-}
-
-static int mt9t112_set_fmt(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_format *format)
-{
- struct v4l2_mbus_framefmt *mf = &format->format;
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9t112_priv *priv = to_mt9t112(client);
- unsigned int top, left;
- int i;
-
- if (format->pad)
- return -EINVAL;
-
- for (i = 0; i < priv->num_formats; i++)
- if (mt9t112_cfmts[i].code == mf->code)
- break;
-
- if (i == priv->num_formats) {
- mf->code = MEDIA_BUS_FMT_UYVY8_2X8;
- mf->colorspace = V4L2_COLORSPACE_JPEG;
- } else {
- mf->colorspace = mt9t112_cfmts[i].colorspace;
- }
-
- mt9t112_frame_check(&mf->width, &mf->height, &left, &top);
-
- mf->field = V4L2_FIELD_NONE;
-
- if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE)
- return mt9t112_s_fmt(sd, mf);
- cfg->try_fmt = *mf;
- return 0;
-}
-
-static int mt9t112_enum_mbus_code(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_mbus_code_enum *code)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9t112_priv *priv = to_mt9t112(client);
-
- if (code->pad || code->index >= priv->num_formats)
- return -EINVAL;
-
- code->code = mt9t112_cfmts[code->index].code;
-
- return 0;
-}
-
-static int mt9t112_g_mbus_config(struct v4l2_subdev *sd,
- struct v4l2_mbus_config *cfg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
-
- cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_VSYNC_ACTIVE_HIGH |
- V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_HIGH |
- V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING;
- cfg->type = V4L2_MBUS_PARALLEL;
- cfg->flags = soc_camera_apply_board_flags(ssdd, cfg);
-
- return 0;
-}
-
-static int mt9t112_s_mbus_config(struct v4l2_subdev *sd,
- const struct v4l2_mbus_config *cfg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
- struct mt9t112_priv *priv = to_mt9t112(client);
-
- if (soc_camera_apply_board_flags(ssdd, cfg) & V4L2_MBUS_PCLK_SAMPLE_RISING)
- priv->flags |= PCLK_RISING;
-
- return 0;
-}
-
-static const struct v4l2_subdev_video_ops mt9t112_subdev_video_ops = {
- .s_stream = mt9t112_s_stream,
- .g_mbus_config = mt9t112_g_mbus_config,
- .s_mbus_config = mt9t112_s_mbus_config,
-};
-
-static const struct v4l2_subdev_pad_ops mt9t112_subdev_pad_ops = {
- .enum_mbus_code = mt9t112_enum_mbus_code,
- .get_selection = mt9t112_get_selection,
- .set_selection = mt9t112_set_selection,
- .get_fmt = mt9t112_get_fmt,
- .set_fmt = mt9t112_set_fmt,
-};
-
-/************************************************************************
- i2c driver
-************************************************************************/
-static const struct v4l2_subdev_ops mt9t112_subdev_ops = {
- .core = &mt9t112_subdev_core_ops,
- .video = &mt9t112_subdev_video_ops,
- .pad = &mt9t112_subdev_pad_ops,
-};
-
-static int mt9t112_camera_probe(struct i2c_client *client)
-{
- struct mt9t112_priv *priv = to_mt9t112(client);
- const char *devname;
- int chipid;
- int ret;
-
- ret = mt9t112_s_power(&priv->subdev, 1);
- if (ret < 0)
- return ret;
-
- /*
- * check and show chip ID
- */
- mt9t112_reg_read(chipid, client, 0x0000);
-
- switch (chipid) {
- case 0x2680:
- devname = "mt9t111";
- priv->num_formats = 1;
- break;
- case 0x2682:
- devname = "mt9t112";
- priv->num_formats = ARRAY_SIZE(mt9t112_cfmts);
- break;
- default:
- dev_err(&client->dev, "Product ID error %04x\n", chipid);
- ret = -ENODEV;
- goto done;
- }
-
- dev_info(&client->dev, "%s chip ID %04x\n", devname, chipid);
-
-done:
- mt9t112_s_power(&priv->subdev, 0);
- return ret;
-}
-
-static int mt9t112_probe(struct i2c_client *client,
- const struct i2c_device_id *did)
-{
- struct mt9t112_priv *priv;
- struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
- struct v4l2_rect rect = {
- .width = VGA_WIDTH,
- .height = VGA_HEIGHT,
- .left = (MAX_WIDTH - VGA_WIDTH) / 2,
- .top = (MAX_HEIGHT - VGA_HEIGHT) / 2,
- };
- int ret;
-
- if (!ssdd || !ssdd->drv_priv) {
- dev_err(&client->dev, "mt9t112: missing platform data!\n");
- return -EINVAL;
- }
-
- priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
-
- priv->info = ssdd->drv_priv;
-
- v4l2_i2c_subdev_init(&priv->subdev, client, &mt9t112_subdev_ops);
-
- priv->clk = v4l2_clk_get(&client->dev, "mclk");
- if (IS_ERR(priv->clk))
- return PTR_ERR(priv->clk);
-
- ret = mt9t112_camera_probe(client);
-
- /* Cannot fail: using the default supported pixel code */
- if (!ret)
- mt9t112_set_params(priv, &rect, MEDIA_BUS_FMT_UYVY8_2X8);
- else
- v4l2_clk_put(priv->clk);
-
- return ret;
-}
-
-static int mt9t112_remove(struct i2c_client *client)
-{
- struct mt9t112_priv *priv = to_mt9t112(client);
-
- v4l2_clk_put(priv->clk);
- return 0;
-}
-
-static const struct i2c_device_id mt9t112_id[] = {
- { "mt9t112", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, mt9t112_id);
-
-static struct i2c_driver mt9t112_i2c_driver = {
- .driver = {
- .name = "mt9t112",
- },
- .probe = mt9t112_probe,
- .remove = mt9t112_remove,
- .id_table = mt9t112_id,
-};
-
-module_i2c_driver(mt9t112_i2c_driver);
-
-MODULE_DESCRIPTION("SoC Camera driver for mt9t112");
-MODULE_AUTHOR("Kuninori Morimoto");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/i2c/soc_camera/soc_ov772x.c b/drivers/media/i2c/soc_camera/soc_ov772x.c
deleted file mode 100644
index fafd372527b2..000000000000
--- a/drivers/media/i2c/soc_camera/soc_ov772x.c
+++ /dev/null
@@ -1,1123 +0,0 @@
-/*
- * ov772x Camera Driver
- *
- * Copyright (C) 2008 Renesas Solutions Corp.
- * Kuninori Morimoto <morimoto.kuninori@renesas.com>
- *
- * Based on ov7670 and soc_camera_platform driver,
- *
- * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net>
- * Copyright (C) 2008 Magnus Damm
- * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
- *
- * 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 <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/i2c.h>
-#include <linux/slab.h>
-#include <linux/delay.h>
-#include <linux/v4l2-mediabus.h>
-#include <linux/videodev2.h>
-
-#include <media/i2c/ov772x.h>
-#include <media/soc_camera.h>
-#include <media/v4l2-clk.h>
-#include <media/v4l2-ctrls.h>
-#include <media/v4l2-subdev.h>
-#include <media/v4l2-image-sizes.h>
-
-/*
- * register offset
- */
-#define GAIN 0x00 /* AGC - Gain control gain setting */
-#define BLUE 0x01 /* AWB - Blue channel gain setting */
-#define RED 0x02 /* AWB - Red channel gain setting */
-#define GREEN 0x03 /* AWB - Green channel gain setting */
-#define COM1 0x04 /* Common control 1 */
-#define BAVG 0x05 /* U/B Average Level */
-#define GAVG 0x06 /* Y/Gb Average Level */
-#define RAVG 0x07 /* V/R Average Level */
-#define AECH 0x08 /* Exposure Value - AEC MSBs */
-#define COM2 0x09 /* Common control 2 */
-#define PID 0x0A /* Product ID Number MSB */
-#define VER 0x0B /* Product ID Number LSB */
-#define COM3 0x0C /* Common control 3 */
-#define COM4 0x0D /* Common control 4 */
-#define COM5 0x0E /* Common control 5 */
-#define COM6 0x0F /* Common control 6 */
-#define AEC 0x10 /* Exposure Value */
-#define CLKRC 0x11 /* Internal clock */
-#define COM7 0x12 /* Common control 7 */
-#define COM8 0x13 /* Common control 8 */
-#define COM9 0x14 /* Common control 9 */
-#define COM10 0x15 /* Common control 10 */
-#define REG16 0x16 /* Register 16 */
-#define HSTART 0x17 /* Horizontal sensor size */
-#define HSIZE 0x18 /* Horizontal frame (HREF column) end high 8-bit */
-#define VSTART 0x19 /* Vertical frame (row) start high 8-bit */
-#define VSIZE 0x1A /* Vertical sensor size */
-#define PSHFT 0x1B /* Data format - pixel delay select */
-#define MIDH 0x1C /* Manufacturer ID byte - high */
-#define MIDL 0x1D /* Manufacturer ID byte - low */
-#define LAEC 0x1F /* Fine AEC value */
-#define COM11 0x20 /* Common control 11 */
-#define BDBASE 0x22 /* Banding filter Minimum AEC value */
-#define DBSTEP 0x23 /* Banding filter Maximum Setp */
-#define AEW 0x24 /* AGC/AEC - Stable operating region (upper limit) */
-#define AEB 0x25 /* AGC/AEC - Stable operating region (lower limit) */
-#define VPT 0x26 /* AGC/AEC Fast mode operating region */
-#define REG28 0x28 /* Register 28 */
-#define HOUTSIZE 0x29 /* Horizontal data output size MSBs */
-#define EXHCH 0x2A /* Dummy pixel insert MSB */
-#define EXHCL 0x2B /* Dummy pixel insert LSB */
-#define VOUTSIZE 0x2C /* Vertical data output size MSBs */
-#define ADVFL 0x2D /* LSB of insert dummy lines in Vertical direction */
-#define ADVFH 0x2E /* MSG of insert dummy lines in Vertical direction */
-#define YAVE 0x2F /* Y/G Channel Average value */
-#define LUMHTH 0x30 /* Histogram AEC/AGC Luminance high level threshold */
-#define LUMLTH 0x31 /* Histogram AEC/AGC Luminance low level threshold */
-#define HREF 0x32 /* Image start and size control */
-#define DM_LNL 0x33 /* Dummy line low 8 bits */
-#define DM_LNH 0x34 /* Dummy line high 8 bits */
-#define ADOFF_B 0x35 /* AD offset compensation value for B channel */
-#define ADOFF_R 0x36 /* AD offset compensation value for R channel */
-#define ADOFF_GB 0x37 /* AD offset compensation value for Gb channel */
-#define ADOFF_GR 0x38 /* AD offset compensation value for Gr channel */
-#define OFF_B 0x39 /* Analog process B channel offset value */
-#define OFF_R 0x3A /* Analog process R channel offset value */
-#define OFF_GB 0x3B /* Analog process Gb channel offset value */
-#define OFF_GR 0x3C /* Analog process Gr channel offset value */
-#define COM12 0x3D /* Common control 12 */
-#define COM13 0x3E /* Common control 13 */
-#define COM14 0x3F /* Common control 14 */
-#define COM15 0x40 /* Common control 15*/
-#define COM16 0x41 /* Common control 16 */
-#define TGT_B 0x42 /* BLC blue channel target value */
-#define TGT_R 0x43 /* BLC red channel target value */
-#define TGT_GB 0x44 /* BLC Gb channel target value */
-#define TGT_GR 0x45 /* BLC Gr channel target value */
-/* for ov7720 */
-#define LCC0 0x46 /* Lens correction control 0 */
-#define LCC1 0x47 /* Lens correction option 1 - X coordinate */
-#define LCC2 0x48 /* Lens correction option 2 - Y coordinate */
-#define LCC3 0x49 /* Lens correction option 3 */
-#define LCC4 0x4A /* Lens correction option 4 - radius of the circular */
-#define LCC5 0x4B /* Lens correction option 5 */
-#define LCC6 0x4C /* Lens correction option 6 */
-/* for ov7725 */
-#define LC_CTR 0x46 /* Lens correction control */
-#define LC_XC 0x47 /* X coordinate of lens correction center relative */
-#define LC_YC 0x48 /* Y coordinate of lens correction center relative */
-#define LC_COEF 0x49 /* Lens correction coefficient */
-#define LC_RADI 0x4A /* Lens correction radius */
-#define LC_COEFB 0x4B /* Lens B channel compensation coefficient */
-#define LC_COEFR 0x4C /* Lens R channel compensation coefficient */
-
-#define FIXGAIN 0x4D /* Analog fix gain amplifer */
-#define AREF0 0x4E /* Sensor reference control */
-#define AREF1 0x4F /* Sensor reference current control */
-#define AREF2 0x50 /* Analog reference control */
-#define AREF3 0x51 /* ADC reference control */
-#define AREF4 0x52 /* ADC reference control */
-#define AREF5 0x53 /* ADC reference control */
-#define AREF6 0x54 /* Analog reference control */
-#define AREF7 0x55 /* Analog reference control */
-#define UFIX 0x60 /* U channel fixed value output */
-#define VFIX 0x61 /* V channel fixed value output */
-#define AWBB_BLK 0x62 /* AWB option for advanced AWB */
-#define AWB_CTRL0 0x63 /* AWB control byte 0 */
-#define DSP_CTRL1 0x64 /* DSP control byte 1 */
-#define DSP_CTRL2 0x65 /* DSP control byte 2 */
-#define DSP_CTRL3 0x66 /* DSP control byte 3 */
-#define DSP_CTRL4 0x67 /* DSP control byte 4 */
-#define AWB_BIAS 0x68 /* AWB BLC level clip */
-#define AWB_CTRL1 0x69 /* AWB control 1 */
-#define AWB_CTRL2 0x6A /* AWB control 2 */
-#define AWB_CTRL3 0x6B /* AWB control 3 */
-#define AWB_CTRL4 0x6C /* AWB control 4 */
-#define AWB_CTRL5 0x6D /* AWB control 5 */
-#define AWB_CTRL6 0x6E /* AWB control 6 */
-#define AWB_CTRL7 0x6F /* AWB control 7 */
-#define AWB_CTRL8 0x70 /* AWB control 8 */
-#define AWB_CTRL9 0x71 /* AWB control 9 */
-#define AWB_CTRL10 0x72 /* AWB control 10 */
-#define AWB_CTRL11 0x73 /* AWB control 11 */
-#define AWB_CTRL12 0x74 /* AWB control 12 */
-#define AWB_CTRL13 0x75 /* AWB control 13 */
-#define AWB_CTRL14 0x76 /* AWB control 14 */
-#define AWB_CTRL15 0x77 /* AWB control 15 */
-#define AWB_CTRL16 0x78 /* AWB control 16 */
-#define AWB_CTRL17 0x79 /* AWB control 17 */
-#define AWB_CTRL18 0x7A /* AWB control 18 */
-#define AWB_CTRL19 0x7B /* AWB control 19 */
-#define AWB_CTRL20 0x7C /* AWB control 20 */
-#define AWB_CTRL21 0x7D /* AWB control 21 */
-#define GAM1 0x7E /* Gamma Curve 1st segment input end point */
-#define GAM2 0x7F /* Gamma Curve 2nd segment input end point */
-#define GAM3 0x80 /* Gamma Curve 3rd segment input end point */
-#define GAM4 0x81 /* Gamma Curve 4th segment input end point */
-#define GAM5 0x82 /* Gamma Curve 5th segment input end point */
-#define GAM6 0x83 /* Gamma Curve 6th segment input end point */
-#define GAM7 0x84 /* Gamma Curve 7th segment input end point */
-#define GAM8 0x85 /* Gamma Curve 8th segment input end point */
-#define GAM9 0x86 /* Gamma Curve 9th segment input end point */
-#define GAM10 0x87 /* Gamma Curve 10th segment input end point */
-#define GAM11 0x88 /* Gamma Curve 11th segment input end point */
-#define GAM12 0x89 /* Gamma Curve 12th segment input end point */
-#define GAM13 0x8A /* Gamma Curve 13th segment input end point */
-#define GAM14 0x8B /* Gamma Curve 14th segment input end point */
-#define GAM15 0x8C /* Gamma Curve 15th segment input end point */
-#define SLOP 0x8D /* Gamma curve highest segment slope */
-#define DNSTH 0x8E /* De-noise threshold */
-#define EDGE_STRNGT 0x8F /* Edge strength control when manual mode */
-#define EDGE_TRSHLD 0x90 /* Edge threshold control when manual mode */
-#define DNSOFF 0x91 /* Auto De-noise threshold control */
-#define EDGE_UPPER 0x92 /* Edge strength upper limit when Auto mode */
-#define EDGE_LOWER 0x93 /* Edge strength lower limit when Auto mode */
-#define MTX1 0x94 /* Matrix coefficient 1 */
-#define MTX2 0x95 /* Matrix coefficient 2 */
-#define MTX3 0x96 /* Matrix coefficient 3 */
-#define MTX4 0x97 /* Matrix coefficient 4 */
-#define MTX5 0x98 /* Matrix coefficient 5 */
-#define MTX6 0x99 /* Matrix coefficient 6 */
-#define MTX_CTRL 0x9A /* Matrix control */
-#define BRIGHT 0x9B /* Brightness control */
-#define CNTRST 0x9C /* Contrast contrast */
-#define CNTRST_CTRL 0x9D /* Contrast contrast center */
-#define UVAD_J0 0x9E /* Auto UV adjust contrast 0 */
-#define UVAD_J1 0x9F /* Auto UV adjust contrast 1 */
-#define SCAL0 0xA0 /* Scaling control 0 */
-#define SCAL1 0xA1 /* Scaling control 1 */
-#define SCAL2 0xA2 /* Scaling control 2 */
-#define FIFODLYM 0xA3 /* FIFO manual mode delay control */
-#define FIFODLYA 0xA4 /* FIFO auto mode delay control */
-#define SDE 0xA6 /* Special digital effect control */
-#define USAT 0xA7 /* U component saturation control */
-#define VSAT 0xA8 /* V component saturation control */
-/* for ov7720 */
-#define HUE0 0xA9 /* Hue control 0 */
-#define HUE1 0xAA /* Hue control 1 */
-/* for ov7725 */
-#define HUECOS 0xA9 /* Cosine value */
-#define HUESIN 0xAA /* Sine value */
-
-#define SIGN 0xAB /* Sign bit for Hue and contrast */
-#define DSPAUTO 0xAC /* DSP auto function ON/OFF control */
-
-/*
- * register detail
- */
-
-/* COM2 */
-#define SOFT_SLEEP_MODE 0x10 /* Soft sleep mode */
- /* Output drive capability */
-#define OCAP_1x 0x00 /* 1x */
-#define OCAP_2x 0x01 /* 2x */
-#define OCAP_3x 0x02 /* 3x */
-#define OCAP_4x 0x03 /* 4x */
-
-/* COM3 */
-#define SWAP_MASK (SWAP_RGB | SWAP_YUV | SWAP_ML)
-#define IMG_MASK (VFLIP_IMG | HFLIP_IMG)
-
-#define VFLIP_IMG 0x80 /* Vertical flip image ON/OFF selection */
-#define HFLIP_IMG 0x40 /* Horizontal mirror image ON/OFF selection */
-#define SWAP_RGB 0x20 /* Swap B/R output sequence in RGB mode */
-#define SWAP_YUV 0x10 /* Swap Y/UV output sequence in YUV mode */
-#define SWAP_ML 0x08 /* Swap output MSB/LSB */
- /* Tri-state option for output clock */
-#define NOTRI_CLOCK 0x04 /* 0: Tri-state at this period */
- /* 1: No tri-state at this period */
- /* Tri-state option for output data */
-#define NOTRI_DATA 0x02 /* 0: Tri-state at this period */
- /* 1: No tri-state at this period */
-#define SCOLOR_TEST 0x01 /* Sensor color bar test pattern */
-
-/* COM4 */
- /* PLL frequency control */
-#define PLL_BYPASS 0x00 /* 00: Bypass PLL */
-#define PLL_4x 0x40 /* 01: PLL 4x */
-#define PLL_6x 0x80 /* 10: PLL 6x */
-#define PLL_8x 0xc0 /* 11: PLL 8x */
- /* AEC evaluate window */
-#define AEC_FULL 0x00 /* 00: Full window */
-#define AEC_1p2 0x10 /* 01: 1/2 window */
-#define AEC_1p4 0x20 /* 10: 1/4 window */
-#define AEC_2p3 0x30 /* 11: Low 2/3 window */
-
-/* COM5 */
-#define AFR_ON_OFF 0x80 /* Auto frame rate control ON/OFF selection */
-#define AFR_SPPED 0x40 /* Auto frame rate control speed selection */
- /* Auto frame rate max rate control */
-#define AFR_NO_RATE 0x00 /* No reduction of frame rate */
-#define AFR_1p2 0x10 /* Max reduction to 1/2 frame rate */
-#define AFR_1p4 0x20 /* Max reduction to 1/4 frame rate */
-#define AFR_1p8 0x30 /* Max reduction to 1/8 frame rate */
- /* Auto frame rate active point control */
-#define AF_2x 0x00 /* Add frame when AGC reaches 2x gain */
-#define AF_4x 0x04 /* Add frame when AGC reaches 4x gain */
-#define AF_8x 0x08 /* Add frame when AGC reaches 8x gain */
-#define AF_16x 0x0c /* Add frame when AGC reaches 16x gain */
- /* AEC max step control */
-#define AEC_NO_LIMIT 0x01 /* 0 : AEC incease step has limit */
- /* 1 : No limit to AEC increase step */
-
-/* COM7 */
- /* SCCB Register Reset */
-#define SCCB_RESET 0x80 /* 0 : No change */
- /* 1 : Resets all registers to default */
- /* Resolution selection */
-#define SLCT_MASK 0x40 /* Mask of VGA or QVGA */
-#define SLCT_VGA 0x00 /* 0 : VGA */
-#define SLCT_QVGA 0x40 /* 1 : QVGA */
-#define ITU656_ON_OFF 0x20 /* ITU656 protocol ON/OFF selection */
-#define SENSOR_RAW 0x10 /* Sensor RAW */
- /* RGB output format control */
-#define FMT_MASK 0x0c /* Mask of color format */
-#define FMT_GBR422 0x00 /* 00 : GBR 4:2:2 */
-#define FMT_RGB565 0x04 /* 01 : RGB 565 */
-#define FMT_RGB555 0x08 /* 10 : RGB 555 */
-#define FMT_RGB444 0x0c /* 11 : RGB 444 */
- /* Output format control */
-#define OFMT_MASK 0x03 /* Mask of output format */
-#define OFMT_YUV 0x00 /* 00 : YUV */
-#define OFMT_P_BRAW 0x01 /* 01 : Processed Bayer RAW */
-#define OFMT_RGB 0x02 /* 10 : RGB */
-#define OFMT_BRAW 0x03 /* 11 : Bayer RAW */
-
-/* COM8 */
-#define FAST_ALGO 0x80 /* Enable fast AGC/AEC algorithm */
- /* AEC Setp size limit */
-#define UNLMT_STEP 0x40 /* 0 : Step size is limited */
- /* 1 : Unlimited step size */
-#define BNDF_ON_OFF 0x20 /* Banding filter ON/OFF */
-#define AEC_BND 0x10 /* Enable AEC below banding value */
-#define AEC_ON_OFF 0x08 /* Fine AEC ON/OFF control */
-#define AGC_ON 0x04 /* AGC Enable */
-#define AWB_ON 0x02 /* AWB Enable */
-#define AEC_ON 0x01 /* AEC Enable */
-
-/* COM9 */
-#define BASE_AECAGC 0x80 /* Histogram or average based AEC/AGC */
- /* Automatic gain ceiling - maximum AGC value */
-#define GAIN_2x 0x00 /* 000 : 2x */
-#define GAIN_4x 0x10 /* 001 : 4x */
-#define GAIN_8x 0x20 /* 010 : 8x */
-#define GAIN_16x 0x30 /* 011 : 16x */
-#define GAIN_32x 0x40 /* 100 : 32x */
-#define GAIN_64x 0x50 /* 101 : 64x */
-#define GAIN_128x 0x60 /* 110 : 128x */
-#define DROP_VSYNC 0x04 /* Drop VSYNC output of corrupt frame */
-#define DROP_HREF 0x02 /* Drop HREF output of corrupt frame */
-
-/* COM11 */
-#define SGLF_ON_OFF 0x02 /* Single frame ON/OFF selection */
-#define SGLF_TRIG 0x01 /* Single frame transfer trigger */
-
-/* HREF */
-#define HREF_VSTART_SHIFT 6 /* VSTART LSB */
-#define HREF_HSTART_SHIFT 4 /* HSTART 2 LSBs */
-#define HREF_VSIZE_SHIFT 2 /* VSIZE LSB */
-#define HREF_HSIZE_SHIFT 0 /* HSIZE 2 LSBs */
-
-/* EXHCH */
-#define EXHCH_VSIZE_SHIFT 2 /* VOUTSIZE LSB */
-#define EXHCH_HSIZE_SHIFT 0 /* HOUTSIZE 2 LSBs */
-
-/* DSP_CTRL1 */
-#define FIFO_ON 0x80 /* FIFO enable/disable selection */
-#define UV_ON_OFF 0x40 /* UV adjust function ON/OFF selection */
-#define YUV444_2_422 0x20 /* YUV444 to 422 UV channel option selection */
-#define CLR_MTRX_ON_OFF 0x10 /* Color matrix ON/OFF selection */
-#define INTPLT_ON_OFF 0x08 /* Interpolation ON/OFF selection */
-#define GMM_ON_OFF 0x04 /* Gamma function ON/OFF selection */
-#define AUTO_BLK_ON_OFF 0x02 /* Black defect auto correction ON/OFF */
-#define AUTO_WHT_ON_OFF 0x01 /* White define auto correction ON/OFF */
-
-/* DSP_CTRL3 */
-#define UV_MASK 0x80 /* UV output sequence option */
-#define UV_ON 0x80 /* ON */
-#define UV_OFF 0x00 /* OFF */
-#define CBAR_MASK 0x20 /* DSP Color bar mask */
-#define CBAR_ON 0x20 /* ON */
-#define CBAR_OFF 0x00 /* OFF */
-
-/* DSP_CTRL4 */
-#define DSP_OFMT_YUV 0x00
-#define DSP_OFMT_RGB 0x00
-#define DSP_OFMT_RAW8 0x02
-#define DSP_OFMT_RAW10 0x03
-
-/* DSPAUTO (DSP Auto Function ON/OFF Control) */
-#define AWB_ACTRL 0x80 /* AWB auto threshold control */
-#define DENOISE_ACTRL 0x40 /* De-noise auto threshold control */
-#define EDGE_ACTRL 0x20 /* Edge enhancement auto strength control */
-#define UV_ACTRL 0x10 /* UV adjust auto slope control */
-#define SCAL0_ACTRL 0x08 /* Auto scaling factor control */
-#define SCAL1_2_ACTRL 0x04 /* Auto scaling factor control */
-
-#define OV772X_MAX_WIDTH VGA_WIDTH
-#define OV772X_MAX_HEIGHT VGA_HEIGHT
-
-/*
- * ID
- */
-#define OV7720 0x7720
-#define OV7725 0x7721
-#define VERSION(pid, ver) ((pid<<8)|(ver&0xFF))
-
-/*
- * struct
- */
-
-struct ov772x_color_format {
- u32 code;
- enum v4l2_colorspace colorspace;
- u8 dsp3;
- u8 dsp4;
- u8 com3;
- u8 com7;
-};
-
-struct ov772x_win_size {
- char *name;
- unsigned char com7_bit;
- struct v4l2_rect rect;
-};
-
-struct ov772x_priv {
- struct v4l2_subdev subdev;
- struct v4l2_ctrl_handler hdl;
- struct v4l2_clk *clk;
- struct ov772x_camera_info *info;
- const struct ov772x_color_format *cfmt;
- const struct ov772x_win_size *win;
- unsigned short flag_vflip:1;
- unsigned short flag_hflip:1;
- /* band_filter = COM8[5] ? 256 - BDBASE : 0 */
- unsigned short band_filter;
-};
-
-/*
- * supported color format list
- */
-static const struct ov772x_color_format ov772x_cfmts[] = {
- {
- .code = MEDIA_BUS_FMT_YUYV8_2X8,
- .colorspace = V4L2_COLORSPACE_JPEG,
- .dsp3 = 0x0,
- .dsp4 = DSP_OFMT_YUV,
- .com3 = SWAP_YUV,
- .com7 = OFMT_YUV,
- },
- {
- .code = MEDIA_BUS_FMT_YVYU8_2X8,
- .colorspace = V4L2_COLORSPACE_JPEG,
- .dsp3 = UV_ON,
- .dsp4 = DSP_OFMT_YUV,
- .com3 = SWAP_YUV,
- .com7 = OFMT_YUV,
- },
- {
- .code = MEDIA_BUS_FMT_UYVY8_2X8,
- .colorspace = V4L2_COLORSPACE_JPEG,
- .dsp3 = 0x0,
- .dsp4 = DSP_OFMT_YUV,
- .com3 = 0x0,
- .com7 = OFMT_YUV,
- },
- {
- .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .dsp3 = 0x0,
- .dsp4 = DSP_OFMT_YUV,
- .com3 = SWAP_RGB,
- .com7 = FMT_RGB555 | OFMT_RGB,
- },
- {
- .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .dsp3 = 0x0,
- .dsp4 = DSP_OFMT_YUV,
- .com3 = 0x0,
- .com7 = FMT_RGB555 | OFMT_RGB,
- },
- {
- .code = MEDIA_BUS_FMT_RGB565_2X8_LE,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .dsp3 = 0x0,
- .dsp4 = DSP_OFMT_YUV,
- .com3 = SWAP_RGB,
- .com7 = FMT_RGB565 | OFMT_RGB,
- },
- {
- .code = MEDIA_BUS_FMT_RGB565_2X8_BE,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .dsp3 = 0x0,
- .dsp4 = DSP_OFMT_YUV,
- .com3 = 0x0,
- .com7 = FMT_RGB565 | OFMT_RGB,
- },
- {
- /* Setting DSP4 to DSP_OFMT_RAW8 still gives 10-bit output,
- * regardless of the COM7 value. We can thus only support 10-bit
- * Bayer until someone figures it out.
- */
- .code = MEDIA_BUS_FMT_SBGGR10_1X10,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .dsp3 = 0x0,
- .dsp4 = DSP_OFMT_RAW10,
- .com3 = 0x0,
- .com7 = SENSOR_RAW | OFMT_BRAW,
- },
-};
-
-
-/*
- * window size list
- */
-
-static const struct ov772x_win_size ov772x_win_sizes[] = {
- {
- .name = "VGA",
- .com7_bit = SLCT_VGA,
- .rect = {
- .left = 140,
- .top = 14,
- .width = VGA_WIDTH,
- .height = VGA_HEIGHT,
- },
- }, {
- .name = "QVGA",
- .com7_bit = SLCT_QVGA,
- .rect = {
- .left = 252,
- .top = 6,
- .width = QVGA_WIDTH,
- .height = QVGA_HEIGHT,
- },
- },
-};
-
-/*
- * general function
- */
-
-static struct ov772x_priv *to_ov772x(struct v4l2_subdev *sd)
-{
- return container_of(sd, struct ov772x_priv, subdev);
-}
-
-static inline int ov772x_read(struct i2c_client *client, u8 addr)
-{
- return i2c_smbus_read_byte_data(client, addr);
-}
-
-static inline int ov772x_write(struct i2c_client *client, u8 addr, u8 value)
-{
- return i2c_smbus_write_byte_data(client, addr, value);
-}
-
-static int ov772x_mask_set(struct i2c_client *client, u8 command, u8 mask,
- u8 set)
-{
- s32 val = ov772x_read(client, command);
- if (val < 0)
- return val;
-
- val &= ~mask;
- val |= set & mask;
-
- return ov772x_write(client, command, val);
-}
-
-static int ov772x_reset(struct i2c_client *client)
-{
- int ret;
-
- ret = ov772x_write(client, COM7, SCCB_RESET);
- if (ret < 0)
- return ret;
-
- msleep(1);
-
- return ov772x_mask_set(client, COM2, SOFT_SLEEP_MODE, SOFT_SLEEP_MODE);
-}
-
-/*
- * soc_camera_ops function
- */
-
-static int ov772x_s_stream(struct v4l2_subdev *sd, int enable)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ov772x_priv *priv = to_ov772x(sd);
-
- if (!enable) {
- ov772x_mask_set(client, COM2, SOFT_SLEEP_MODE, SOFT_SLEEP_MODE);
- return 0;
- }
-
- ov772x_mask_set(client, COM2, SOFT_SLEEP_MODE, 0);
-
- dev_dbg(&client->dev, "format %d, win %s\n",
- priv->cfmt->code, priv->win->name);
-
- return 0;
-}
-
-static int ov772x_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct ov772x_priv *priv = container_of(ctrl->handler,
- struct ov772x_priv, hdl);
- struct v4l2_subdev *sd = &priv->subdev;
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- int ret = 0;
- u8 val;
-
- switch (ctrl->id) {
- case V4L2_CID_VFLIP:
- val = ctrl->val ? VFLIP_IMG : 0x00;
- priv->flag_vflip = ctrl->val;
- if (priv->info->flags & OV772X_FLAG_VFLIP)
- val ^= VFLIP_IMG;
- return ov772x_mask_set(client, COM3, VFLIP_IMG, val);
- case V4L2_CID_HFLIP:
- val = ctrl->val ? HFLIP_IMG : 0x00;
- priv->flag_hflip = ctrl->val;
- if (priv->info->flags & OV772X_FLAG_HFLIP)
- val ^= HFLIP_IMG;
- return ov772x_mask_set(client, COM3, HFLIP_IMG, val);
- case V4L2_CID_BAND_STOP_FILTER:
- if (!ctrl->val) {
- /* Switch the filter off, it is on now */
- ret = ov772x_mask_set(client, BDBASE, 0xff, 0xff);
- if (!ret)
- ret = ov772x_mask_set(client, COM8,
- BNDF_ON_OFF, 0);
- } else {
- /* Switch the filter on, set AEC low limit */
- val = 256 - ctrl->val;
- ret = ov772x_mask_set(client, COM8,
- BNDF_ON_OFF, BNDF_ON_OFF);
- if (!ret)
- ret = ov772x_mask_set(client, BDBASE,
- 0xff, val);
- }
- if (!ret)
- priv->band_filter = ctrl->val;
- return ret;
- }
-
- return -EINVAL;
-}
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int ov772x_g_register(struct v4l2_subdev *sd,
- struct v4l2_dbg_register *reg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- int ret;
-
- reg->size = 1;
- if (reg->reg > 0xff)
- return -EINVAL;
-
- ret = ov772x_read(client, reg->reg);
- if (ret < 0)
- return ret;
-
- reg->val = (__u64)ret;
-
- return 0;
-}
-
-static int ov772x_s_register(struct v4l2_subdev *sd,
- const struct v4l2_dbg_register *reg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (reg->reg > 0xff ||
- reg->val > 0xff)
- return -EINVAL;
-
- return ov772x_write(client, reg->reg, reg->val);
-}
-#endif
-
-static int ov772x_s_power(struct v4l2_subdev *sd, int on)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
- struct ov772x_priv *priv = to_ov772x(sd);
-
- return soc_camera_set_power(&client->dev, ssdd, priv->clk, on);
-}
-
-static const struct ov772x_win_size *ov772x_select_win(u32 width, u32 height)
-{
- const struct ov772x_win_size *win = &ov772x_win_sizes[0];
- u32 best_diff = UINT_MAX;
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(ov772x_win_sizes); ++i) {
- u32 diff = abs(width - ov772x_win_sizes[i].rect.width)
- + abs(height - ov772x_win_sizes[i].rect.height);
- if (diff < best_diff) {
- best_diff = diff;
- win = &ov772x_win_sizes[i];
- }
- }
-
- return win;
-}
-
-static void ov772x_select_params(const struct v4l2_mbus_framefmt *mf,
- const struct ov772x_color_format **cfmt,
- const struct ov772x_win_size **win)
-{
- unsigned int i;
-
- /* Select a format. */
- *cfmt = &ov772x_cfmts[0];
-
- for (i = 0; i < ARRAY_SIZE(ov772x_cfmts); i++) {
- if (mf->code == ov772x_cfmts[i].code) {
- *cfmt = &ov772x_cfmts[i];
- break;
- }
- }
-
- /* Select a window size. */
- *win = ov772x_select_win(mf->width, mf->height);
-}
-
-static int ov772x_set_params(struct ov772x_priv *priv,
- const struct ov772x_color_format *cfmt,
- const struct ov772x_win_size *win)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev);
- int ret;
- u8 val;
-
- /*
- * reset hardware
- */
- ov772x_reset(client);
-
- /*
- * Edge Ctrl
- */
- if (priv->info->edgectrl.strength & OV772X_MANUAL_EDGE_CTRL) {
-
- /*
- * Manual Edge Control Mode
- *
- * Edge auto strength bit is set by default.
- * Remove it when manual mode.
- */
-
- ret = ov772x_mask_set(client, DSPAUTO, EDGE_ACTRL, 0x00);
- if (ret < 0)
- goto ov772x_set_fmt_error;
-
- ret = ov772x_mask_set(client,
- EDGE_TRSHLD, OV772X_EDGE_THRESHOLD_MASK,
- priv->info->edgectrl.threshold);
- if (ret < 0)
- goto ov772x_set_fmt_error;
-
- ret = ov772x_mask_set(client,
- EDGE_STRNGT, OV772X_EDGE_STRENGTH_MASK,
- priv->info->edgectrl.strength);
- if (ret < 0)
- goto ov772x_set_fmt_error;
-
- } else if (priv->info->edgectrl.upper > priv->info->edgectrl.lower) {
- /*
- * Auto Edge Control Mode
- *
- * set upper and lower limit
- */
- ret = ov772x_mask_set(client,
- EDGE_UPPER, OV772X_EDGE_UPPER_MASK,
- priv->info->edgectrl.upper);
- if (ret < 0)
- goto ov772x_set_fmt_error;
-
- ret = ov772x_mask_set(client,
- EDGE_LOWER, OV772X_EDGE_LOWER_MASK,
- priv->info->edgectrl.lower);
- if (ret < 0)
- goto ov772x_set_fmt_error;
- }
-
- /* Format and window size */
- ret = ov772x_write(client, HSTART, win->rect.left >> 2);
- if (ret < 0)
- goto ov772x_set_fmt_error;
- ret = ov772x_write(client, HSIZE, win->rect.width >> 2);
- if (ret < 0)
- goto ov772x_set_fmt_error;
- ret = ov772x_write(client, VSTART, win->rect.top >> 1);
- if (ret < 0)
- goto ov772x_set_fmt_error;
- ret = ov772x_write(client, VSIZE, win->rect.height >> 1);
- if (ret < 0)
- goto ov772x_set_fmt_error;
- ret = ov772x_write(client, HOUTSIZE, win->rect.width >> 2);
- if (ret < 0)
- goto ov772x_set_fmt_error;
- ret = ov772x_write(client, VOUTSIZE, win->rect.height >> 1);
- if (ret < 0)
- goto ov772x_set_fmt_error;
- ret = ov772x_write(client, HREF,
- ((win->rect.top & 1) << HREF_VSTART_SHIFT) |
- ((win->rect.left & 3) << HREF_HSTART_SHIFT) |
- ((win->rect.height & 1) << HREF_VSIZE_SHIFT) |
- ((win->rect.width & 3) << HREF_HSIZE_SHIFT));
- if (ret < 0)
- goto ov772x_set_fmt_error;
- ret = ov772x_write(client, EXHCH,
- ((win->rect.height & 1) << EXHCH_VSIZE_SHIFT) |
- ((win->rect.width & 3) << EXHCH_HSIZE_SHIFT));
- if (ret < 0)
- goto ov772x_set_fmt_error;
-
- /*
- * set DSP_CTRL3
- */
- val = cfmt->dsp3;
- if (val) {
- ret = ov772x_mask_set(client,
- DSP_CTRL3, UV_MASK, val);
- if (ret < 0)
- goto ov772x_set_fmt_error;
- }
-
- /* DSP_CTRL4: AEC reference point and DSP output format. */
- if (cfmt->dsp4) {
- ret = ov772x_write(client, DSP_CTRL4, cfmt->dsp4);
- if (ret < 0)
- goto ov772x_set_fmt_error;
- }
-
- /*
- * set COM3
- */
- val = cfmt->com3;
- if (priv->info->flags & OV772X_FLAG_VFLIP)
- val |= VFLIP_IMG;
- if (priv->info->flags & OV772X_FLAG_HFLIP)
- val |= HFLIP_IMG;
- if (priv->flag_vflip)
- val ^= VFLIP_IMG;
- if (priv->flag_hflip)
- val ^= HFLIP_IMG;
-
- ret = ov772x_mask_set(client,
- COM3, SWAP_MASK | IMG_MASK, val);
- if (ret < 0)
- goto ov772x_set_fmt_error;
-
- /* COM7: Sensor resolution and output format control. */
- ret = ov772x_write(client, COM7, win->com7_bit | cfmt->com7);
- if (ret < 0)
- goto ov772x_set_fmt_error;
-
- /*
- * set COM8
- */
- if (priv->band_filter) {
- ret = ov772x_mask_set(client, COM8, BNDF_ON_OFF, BNDF_ON_OFF);
- if (!ret)
- ret = ov772x_mask_set(client, BDBASE,
- 0xff, 256 - priv->band_filter);
- if (ret < 0)
- goto ov772x_set_fmt_error;
- }
-
- return ret;
-
-ov772x_set_fmt_error:
-
- ov772x_reset(client);
-
- return ret;
-}
-
-static int ov772x_get_selection(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_selection *sel)
-{
- if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
- return -EINVAL;
-
- sel->r.left = 0;
- sel->r.top = 0;
- switch (sel->target) {
- case V4L2_SEL_TGT_CROP_BOUNDS:
- sel->r.width = OV772X_MAX_WIDTH;
- sel->r.height = OV772X_MAX_HEIGHT;
- return 0;
- case V4L2_SEL_TGT_CROP:
- sel->r.width = VGA_WIDTH;
- sel->r.height = VGA_HEIGHT;
- return 0;
- default:
- return -EINVAL;
- }
-}
-
-static int ov772x_get_fmt(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_format *format)
-{
- struct v4l2_mbus_framefmt *mf = &format->format;
- struct ov772x_priv *priv = to_ov772x(sd);
-
- if (format->pad)
- return -EINVAL;
-
- mf->width = priv->win->rect.width;
- mf->height = priv->win->rect.height;
- mf->code = priv->cfmt->code;
- mf->colorspace = priv->cfmt->colorspace;
- mf->field = V4L2_FIELD_NONE;
-
- return 0;
-}
-
-static int ov772x_set_fmt(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_format *format)
-{
- struct ov772x_priv *priv = to_ov772x(sd);
- struct v4l2_mbus_framefmt *mf = &format->format;
- const struct ov772x_color_format *cfmt;
- const struct ov772x_win_size *win;
- int ret;
-
- if (format->pad)
- return -EINVAL;
-
- ov772x_select_params(mf, &cfmt, &win);
-
- mf->code = cfmt->code;
- mf->width = win->rect.width;
- mf->height = win->rect.height;
- mf->field = V4L2_FIELD_NONE;
- mf->colorspace = cfmt->colorspace;
-
- if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
- cfg->try_fmt = *mf;
- return 0;
- }
-
- ret = ov772x_set_params(priv, cfmt, win);
- if (ret < 0)
- return ret;
-
- priv->win = win;
- priv->cfmt = cfmt;
- return 0;
-}
-
-static int ov772x_video_probe(struct ov772x_priv *priv)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev);
- u8 pid, ver;
- const char *devname;
- int ret;
-
- ret = ov772x_s_power(&priv->subdev, 1);
- if (ret < 0)
- return ret;
-
- /*
- * check and show product ID and manufacturer ID
- */
- pid = ov772x_read(client, PID);
- ver = ov772x_read(client, VER);
-
- switch (VERSION(pid, ver)) {
- case OV7720:
- devname = "ov7720";
- break;
- case OV7725:
- devname = "ov7725";
- break;
- default:
- dev_err(&client->dev,
- "Product ID error %x:%x\n", pid, ver);
- ret = -ENODEV;
- goto done;
- }
-
- dev_info(&client->dev,
- "%s Product ID %0x:%0x Manufacturer ID %x:%x\n",
- devname,
- pid,
- ver,
- ov772x_read(client, MIDH),
- ov772x_read(client, MIDL));
- ret = v4l2_ctrl_handler_setup(&priv->hdl);
-
-done:
- ov772x_s_power(&priv->subdev, 0);
- return ret;
-}
-
-static const struct v4l2_ctrl_ops ov772x_ctrl_ops = {
- .s_ctrl = ov772x_s_ctrl,
-};
-
-static const struct v4l2_subdev_core_ops ov772x_subdev_core_ops = {
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .g_register = ov772x_g_register,
- .s_register = ov772x_s_register,
-#endif
- .s_power = ov772x_s_power,
-};
-
-static int ov772x_enum_mbus_code(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_mbus_code_enum *code)
-{
- if (code->pad || code->index >= ARRAY_SIZE(ov772x_cfmts))
- return -EINVAL;
-
- code->code = ov772x_cfmts[code->index].code;
- return 0;
-}
-
-static int ov772x_g_mbus_config(struct v4l2_subdev *sd,
- struct v4l2_mbus_config *cfg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
-
- cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER |
- V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH |
- V4L2_MBUS_DATA_ACTIVE_HIGH;
- cfg->type = V4L2_MBUS_PARALLEL;
- cfg->flags = soc_camera_apply_board_flags(ssdd, cfg);
-
- return 0;
-}
-
-static const struct v4l2_subdev_video_ops ov772x_subdev_video_ops = {
- .s_stream = ov772x_s_stream,
- .g_mbus_config = ov772x_g_mbus_config,
-};
-
-static const struct v4l2_subdev_pad_ops ov772x_subdev_pad_ops = {
- .enum_mbus_code = ov772x_enum_mbus_code,
- .get_selection = ov772x_get_selection,
- .get_fmt = ov772x_get_fmt,
- .set_fmt = ov772x_set_fmt,
-};
-
-static const struct v4l2_subdev_ops ov772x_subdev_ops = {
- .core = &ov772x_subdev_core_ops,
- .video = &ov772x_subdev_video_ops,
- .pad = &ov772x_subdev_pad_ops,
-};
-
-/*
- * i2c_driver function
- */
-
-static int ov772x_probe(struct i2c_client *client,
- const struct i2c_device_id *did)
-{
- struct ov772x_priv *priv;
- struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
- struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
- int ret;
-
- if (!ssdd || !ssdd->drv_priv) {
- dev_err(&client->dev, "OV772X: missing platform data!\n");
- return -EINVAL;
- }
-
- if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
- I2C_FUNC_PROTOCOL_MANGLING)) {
- dev_err(&adapter->dev,
- "I2C-Adapter doesn't support SMBUS_BYTE_DATA or PROTOCOL_MANGLING\n");
- return -EIO;
- }
- client->flags |= I2C_CLIENT_SCCB;
-
- priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
-
- priv->info = ssdd->drv_priv;
-
- v4l2_i2c_subdev_init(&priv->subdev, client, &ov772x_subdev_ops);
- v4l2_ctrl_handler_init(&priv->hdl, 3);
- v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops,
- V4L2_CID_VFLIP, 0, 1, 1, 0);
- v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops,
- V4L2_CID_HFLIP, 0, 1, 1, 0);
- v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops,
- V4L2_CID_BAND_STOP_FILTER, 0, 256, 1, 0);
- priv->subdev.ctrl_handler = &priv->hdl;
- if (priv->hdl.error)
- return priv->hdl.error;
-
- priv->clk = v4l2_clk_get(&client->dev, "mclk");
- if (IS_ERR(priv->clk)) {
- ret = PTR_ERR(priv->clk);
- goto eclkget;
- }
-
- ret = ov772x_video_probe(priv);
- if (ret < 0) {
- v4l2_clk_put(priv->clk);
-eclkget:
- v4l2_ctrl_handler_free(&priv->hdl);
- } else {
- priv->cfmt = &ov772x_cfmts[0];
- priv->win = &ov772x_win_sizes[0];
- }
-
- return ret;
-}
-
-static int ov772x_remove(struct i2c_client *client)
-{
- struct ov772x_priv *priv = to_ov772x(i2c_get_clientdata(client));
-
- v4l2_clk_put(priv->clk);
- v4l2_device_unregister_subdev(&priv->subdev);
- v4l2_ctrl_handler_free(&priv->hdl);
- return 0;
-}
-
-static const struct i2c_device_id ov772x_id[] = {
- { "ov772x", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, ov772x_id);
-
-static struct i2c_driver ov772x_i2c_driver = {
- .driver = {
- .name = "ov772x",
- },
- .probe = ov772x_probe,
- .remove = ov772x_remove,
- .id_table = ov772x_id,
-};
-
-module_i2c_driver(ov772x_i2c_driver);
-
-MODULE_DESCRIPTION("SoC Camera driver for ov772x");
-MODULE_AUTHOR("Kuninori Morimoto");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/i2c/soc_camera/soc_rj54n1cb0c.c b/drivers/media/i2c/soc_camera/soc_rj54n1cb0c.c
deleted file mode 100644
index f0cb49a6167b..000000000000
--- a/drivers/media/i2c/soc_camera/soc_rj54n1cb0c.c
+++ /dev/null
@@ -1,1415 +0,0 @@
-/*
- * Driver for RJ54N1CB0C CMOS Image Sensor from Sharp
- *
- * Copyright (C) 2009, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
- *
- * 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 <linux/delay.h>
-#include <linux/i2c.h>
-#include <linux/slab.h>
-#include <linux/v4l2-mediabus.h>
-#include <linux/videodev2.h>
-#include <linux/module.h>
-
-#include <media/i2c/rj54n1cb0c.h>
-#include <media/soc_camera.h>
-#include <media/v4l2-clk.h>
-#include <media/v4l2-subdev.h>
-#include <media/v4l2-ctrls.h>
-
-#define RJ54N1_DEV_CODE 0x0400
-#define RJ54N1_DEV_CODE2 0x0401
-#define RJ54N1_OUT_SEL 0x0403
-#define RJ54N1_XY_OUTPUT_SIZE_S_H 0x0404
-#define RJ54N1_X_OUTPUT_SIZE_S_L 0x0405
-#define RJ54N1_Y_OUTPUT_SIZE_S_L 0x0406
-#define RJ54N1_XY_OUTPUT_SIZE_P_H 0x0407
-#define RJ54N1_X_OUTPUT_SIZE_P_L 0x0408
-#define RJ54N1_Y_OUTPUT_SIZE_P_L 0x0409
-#define RJ54N1_LINE_LENGTH_PCK_S_H 0x040a
-#define RJ54N1_LINE_LENGTH_PCK_S_L 0x040b
-#define RJ54N1_LINE_LENGTH_PCK_P_H 0x040c
-#define RJ54N1_LINE_LENGTH_PCK_P_L 0x040d
-#define RJ54N1_RESIZE_N 0x040e
-#define RJ54N1_RESIZE_N_STEP 0x040f
-#define RJ54N1_RESIZE_STEP 0x0410
-#define RJ54N1_RESIZE_HOLD_H 0x0411
-#define RJ54N1_RESIZE_HOLD_L 0x0412
-#define RJ54N1_H_OBEN_OFS 0x0413
-#define RJ54N1_V_OBEN_OFS 0x0414
-#define RJ54N1_RESIZE_CONTROL 0x0415
-#define RJ54N1_STILL_CONTROL 0x0417
-#define RJ54N1_INC_USE_SEL_H 0x0425
-#define RJ54N1_INC_USE_SEL_L 0x0426
-#define RJ54N1_MIRROR_STILL_MODE 0x0427
-#define RJ54N1_INIT_START 0x0428
-#define RJ54N1_SCALE_1_2_LEV 0x0429
-#define RJ54N1_SCALE_4_LEV 0x042a
-#define RJ54N1_Y_GAIN 0x04d8
-#define RJ54N1_APT_GAIN_UP 0x04fa
-#define RJ54N1_RA_SEL_UL 0x0530
-#define RJ54N1_BYTE_SWAP 0x0531
-#define RJ54N1_OUT_SIGPO 0x053b
-#define RJ54N1_WB_SEL_WEIGHT_I 0x054e
-#define RJ54N1_BIT8_WB 0x0569
-#define RJ54N1_HCAPS_WB 0x056a
-#define RJ54N1_VCAPS_WB 0x056b
-#define RJ54N1_HCAPE_WB 0x056c
-#define RJ54N1_VCAPE_WB 0x056d
-#define RJ54N1_EXPOSURE_CONTROL 0x058c
-#define RJ54N1_FRAME_LENGTH_S_H 0x0595
-#define RJ54N1_FRAME_LENGTH_S_L 0x0596
-#define RJ54N1_FRAME_LENGTH_P_H 0x0597
-#define RJ54N1_FRAME_LENGTH_P_L 0x0598
-#define RJ54N1_PEAK_H 0x05b7
-#define RJ54N1_PEAK_50 0x05b8
-#define RJ54N1_PEAK_60 0x05b9
-#define RJ54N1_PEAK_DIFF 0x05ba
-#define RJ54N1_IOC 0x05ef
-#define RJ54N1_TG_BYPASS 0x0700
-#define RJ54N1_PLL_L 0x0701
-#define RJ54N1_PLL_N 0x0702
-#define RJ54N1_PLL_EN 0x0704
-#define RJ54N1_RATIO_TG 0x0706
-#define RJ54N1_RATIO_T 0x0707
-#define RJ54N1_RATIO_R 0x0708
-#define RJ54N1_RAMP_TGCLK_EN 0x0709
-#define RJ54N1_OCLK_DSP 0x0710
-#define RJ54N1_RATIO_OP 0x0711
-#define RJ54N1_RATIO_O 0x0712
-#define RJ54N1_OCLK_SEL_EN 0x0713
-#define RJ54N1_CLK_RST 0x0717
-#define RJ54N1_RESET_STANDBY 0x0718
-#define RJ54N1_FWFLG 0x07fe
-
-#define E_EXCLK (1 << 7)
-#define SOFT_STDBY (1 << 4)
-#define SEN_RSTX (1 << 2)
-#define TG_RSTX (1 << 1)
-#define DSP_RSTX (1 << 0)
-
-#define RESIZE_HOLD_SEL (1 << 2)
-#define RESIZE_GO (1 << 1)
-
-/*
- * When cropping, the camera automatically centers the cropped region, there
- * doesn't seem to be a way to specify an explicit location of the rectangle.
- */
-#define RJ54N1_COLUMN_SKIP 0
-#define RJ54N1_ROW_SKIP 0
-#define RJ54N1_MAX_WIDTH 1600
-#define RJ54N1_MAX_HEIGHT 1200
-
-#define PLL_L 2
-#define PLL_N 0x31
-
-/* I2C addresses: 0x50, 0x51, 0x60, 0x61 */
-
-/* RJ54N1CB0C has only one fixed colorspace per pixelcode */
-struct rj54n1_datafmt {
- u32 code;
- enum v4l2_colorspace colorspace;
-};
-
-/* Find a data format by a pixel code in an array */
-static const struct rj54n1_datafmt *rj54n1_find_datafmt(
- u32 code, const struct rj54n1_datafmt *fmt,
- int n)
-{
- int i;
- for (i = 0; i < n; i++)
- if (fmt[i].code == code)
- return fmt + i;
-
- return NULL;
-}
-
-static const struct rj54n1_datafmt rj54n1_colour_fmts[] = {
- {MEDIA_BUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_JPEG},
- {MEDIA_BUS_FMT_YVYU8_2X8, V4L2_COLORSPACE_JPEG},
- {MEDIA_BUS_FMT_RGB565_2X8_LE, V4L2_COLORSPACE_SRGB},
- {MEDIA_BUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_SRGB},
- {MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, V4L2_COLORSPACE_SRGB},
- {MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_LE, V4L2_COLORSPACE_SRGB},
- {MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_BE, V4L2_COLORSPACE_SRGB},
- {MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_BE, V4L2_COLORSPACE_SRGB},
- {MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_COLORSPACE_SRGB},
-};
-
-struct rj54n1_clock_div {
- u8 ratio_tg; /* can be 0 or an odd number */
- u8 ratio_t;
- u8 ratio_r;
- u8 ratio_op;
- u8 ratio_o;
-};
-
-struct rj54n1 {
- struct v4l2_subdev subdev;
- struct v4l2_ctrl_handler hdl;
- struct v4l2_clk *clk;
- struct rj54n1_clock_div clk_div;
- const struct rj54n1_datafmt *fmt;
- struct v4l2_rect rect; /* Sensor window */
- unsigned int tgclk_mhz;
- bool auto_wb;
- unsigned short width; /* Output window */
- unsigned short height;
- unsigned short resize; /* Sensor * 1024 / resize = Output */
- unsigned short scale;
- u8 bank;
-};
-
-struct rj54n1_reg_val {
- u16 reg;
- u8 val;
-};
-
-static const struct rj54n1_reg_val bank_4[] = {
- {0x417, 0},
- {0x42c, 0},
- {0x42d, 0xf0},
- {0x42e, 0},
- {0x42f, 0x50},
- {0x430, 0xf5},
- {0x431, 0x16},
- {0x432, 0x20},
- {0x433, 0},
- {0x434, 0xc8},
- {0x43c, 8},
- {0x43e, 0x90},
- {0x445, 0x83},
- {0x4ba, 0x58},
- {0x4bb, 4},
- {0x4bc, 0x20},
- {0x4db, 4},
- {0x4fe, 2},
-};
-
-static const struct rj54n1_reg_val bank_5[] = {
- {0x514, 0},
- {0x516, 0},
- {0x518, 0},
- {0x51a, 0},
- {0x51d, 0xff},
- {0x56f, 0x28},
- {0x575, 0x40},
- {0x5bc, 0x48},
- {0x5c1, 6},
- {0x5e5, 0x11},
- {0x5e6, 0x43},
- {0x5e7, 0x33},
- {0x5e8, 0x21},
- {0x5e9, 0x30},
- {0x5ea, 0x0},
- {0x5eb, 0xa5},
- {0x5ec, 0xff},
- {0x5fe, 2},
-};
-
-static const struct rj54n1_reg_val bank_7[] = {
- {0x70a, 0},
- {0x714, 0xff},
- {0x715, 0xff},
- {0x716, 0x1f},
- {0x7FE, 2},
-};
-
-static const struct rj54n1_reg_val bank_8[] = {
- {0x800, 0x00},
- {0x801, 0x01},
- {0x802, 0x61},
- {0x805, 0x00},
- {0x806, 0x00},
- {0x807, 0x00},
- {0x808, 0x00},
- {0x809, 0x01},
- {0x80A, 0x61},
- {0x80B, 0x00},
- {0x80C, 0x01},
- {0x80D, 0x00},
- {0x80E, 0x00},
- {0x80F, 0x00},
- {0x810, 0x00},
- {0x811, 0x01},
- {0x812, 0x61},
- {0x813, 0x00},
- {0x814, 0x11},
- {0x815, 0x00},
- {0x816, 0x41},
- {0x817, 0x00},
- {0x818, 0x51},
- {0x819, 0x01},
- {0x81A, 0x1F},
- {0x81B, 0x00},
- {0x81C, 0x01},
- {0x81D, 0x00},
- {0x81E, 0x11},
- {0x81F, 0x00},
- {0x820, 0x41},
- {0x821, 0x00},
- {0x822, 0x51},
- {0x823, 0x00},
- {0x824, 0x00},
- {0x825, 0x00},
- {0x826, 0x47},
- {0x827, 0x01},
- {0x828, 0x4F},
- {0x829, 0x00},
- {0x82A, 0x00},
- {0x82B, 0x00},
- {0x82C, 0x30},
- {0x82D, 0x00},
- {0x82E, 0x40},
- {0x82F, 0x00},
- {0x830, 0xB3},
- {0x831, 0x00},
- {0x832, 0xE3},
- {0x833, 0x00},
- {0x834, 0x00},
- {0x835, 0x00},
- {0x836, 0x00},
- {0x837, 0x00},
- {0x838, 0x00},
- {0x839, 0x01},
- {0x83A, 0x61},
- {0x83B, 0x00},
- {0x83C, 0x01},
- {0x83D, 0x00},
- {0x83E, 0x00},
- {0x83F, 0x00},
- {0x840, 0x00},
- {0x841, 0x01},
- {0x842, 0x61},
- {0x843, 0x00},
- {0x844, 0x1D},
- {0x845, 0x00},
- {0x846, 0x00},
- {0x847, 0x00},
- {0x848, 0x00},
- {0x849, 0x01},
- {0x84A, 0x1F},
- {0x84B, 0x00},
- {0x84C, 0x05},
- {0x84D, 0x00},
- {0x84E, 0x19},
- {0x84F, 0x01},
- {0x850, 0x21},
- {0x851, 0x01},
- {0x852, 0x5D},
- {0x853, 0x00},
- {0x854, 0x00},
- {0x855, 0x00},
- {0x856, 0x19},
- {0x857, 0x01},
- {0x858, 0x21},
- {0x859, 0x00},
- {0x85A, 0x00},
- {0x85B, 0x00},
- {0x85C, 0x00},
- {0x85D, 0x00},
- {0x85E, 0x00},
- {0x85F, 0x00},
- {0x860, 0xB3},
- {0x861, 0x00},
- {0x862, 0xE3},
- {0x863, 0x00},
- {0x864, 0x00},
- {0x865, 0x00},
- {0x866, 0x00},
- {0x867, 0x00},
- {0x868, 0x00},
- {0x869, 0xE2},
- {0x86A, 0x00},
- {0x86B, 0x01},
- {0x86C, 0x06},
- {0x86D, 0x00},
- {0x86E, 0x00},
- {0x86F, 0x00},
- {0x870, 0x60},
- {0x871, 0x8C},
- {0x872, 0x10},
- {0x873, 0x00},
- {0x874, 0xE0},
- {0x875, 0x00},
- {0x876, 0x27},
- {0x877, 0x01},
- {0x878, 0x00},
- {0x879, 0x00},
- {0x87A, 0x00},
- {0x87B, 0x03},
- {0x87C, 0x00},
- {0x87D, 0x00},
- {0x87E, 0x00},
- {0x87F, 0x00},
- {0x880, 0x00},
- {0x881, 0x00},
- {0x882, 0x00},
- {0x883, 0x00},
- {0x884, 0x00},
- {0x885, 0x00},
- {0x886, 0xF8},
- {0x887, 0x00},
- {0x888, 0x03},
- {0x889, 0x00},
- {0x88A, 0x64},
- {0x88B, 0x00},
- {0x88C, 0x03},
- {0x88D, 0x00},
- {0x88E, 0xB1},
- {0x88F, 0x00},
- {0x890, 0x03},
- {0x891, 0x01},
- {0x892, 0x1D},
- {0x893, 0x00},
- {0x894, 0x03},
- {0x895, 0x01},
- {0x896, 0x4B},
- {0x897, 0x00},
- {0x898, 0xE5},
- {0x899, 0x00},
- {0x89A, 0x01},
- {0x89B, 0x00},
- {0x89C, 0x01},
- {0x89D, 0x04},
- {0x89E, 0xC8},
- {0x89F, 0x00},
- {0x8A0, 0x01},
- {0x8A1, 0x01},
- {0x8A2, 0x61},
- {0x8A3, 0x00},
- {0x8A4, 0x01},
- {0x8A5, 0x00},
- {0x8A6, 0x00},
- {0x8A7, 0x00},
- {0x8A8, 0x00},
- {0x8A9, 0x00},
- {0x8AA, 0x7F},
- {0x8AB, 0x03},
- {0x8AC, 0x00},
- {0x8AD, 0x00},
- {0x8AE, 0x00},
- {0x8AF, 0x00},
- {0x8B0, 0x00},
- {0x8B1, 0x00},
- {0x8B6, 0x00},
- {0x8B7, 0x01},
- {0x8B8, 0x00},
- {0x8B9, 0x00},
- {0x8BA, 0x02},
- {0x8BB, 0x00},
- {0x8BC, 0xFF},
- {0x8BD, 0x00},
- {0x8FE, 2},
-};
-
-static const struct rj54n1_reg_val bank_10[] = {
- {0x10bf, 0x69}
-};
-
-/* Clock dividers - these are default register values, divider = register + 1 */
-static const struct rj54n1_clock_div clk_div = {
- .ratio_tg = 3 /* default: 5 */,
- .ratio_t = 4 /* default: 1 */,
- .ratio_r = 4 /* default: 0 */,
- .ratio_op = 1 /* default: 5 */,
- .ratio_o = 9 /* default: 0 */,
-};
-
-static struct rj54n1 *to_rj54n1(const struct i2c_client *client)
-{
- return container_of(i2c_get_clientdata(client), struct rj54n1, subdev);
-}
-
-static int reg_read(struct i2c_client *client, const u16 reg)
-{
- struct rj54n1 *rj54n1 = to_rj54n1(client);
- int ret;
-
- /* set bank */
- if (rj54n1->bank != reg >> 8) {
- dev_dbg(&client->dev, "[0x%x] = 0x%x\n", 0xff, reg >> 8);
- ret = i2c_smbus_write_byte_data(client, 0xff, reg >> 8);
- if (ret < 0)
- return ret;
- rj54n1->bank = reg >> 8;
- }
- return i2c_smbus_read_byte_data(client, reg & 0xff);
-}
-
-static int reg_write(struct i2c_client *client, const u16 reg,
- const u8 data)
-{
- struct rj54n1 *rj54n1 = to_rj54n1(client);
- int ret;
-
- /* set bank */
- if (rj54n1->bank != reg >> 8) {
- dev_dbg(&client->dev, "[0x%x] = 0x%x\n", 0xff, reg >> 8);
- ret = i2c_smbus_write_byte_data(client, 0xff, reg >> 8);
- if (ret < 0)
- return ret;
- rj54n1->bank = reg >> 8;
- }
- dev_dbg(&client->dev, "[0x%x] = 0x%x\n", reg & 0xff, data);
- return i2c_smbus_write_byte_data(client, reg & 0xff, data);
-}
-
-static int reg_set(struct i2c_client *client, const u16 reg,
- const u8 data, const u8 mask)
-{
- int ret;
-
- ret = reg_read(client, reg);
- if (ret < 0)
- return ret;
- return reg_write(client, reg, (ret & ~mask) | (data & mask));
-}
-
-static int reg_write_multiple(struct i2c_client *client,
- const struct rj54n1_reg_val *rv, const int n)
-{
- int i, ret;
-
- for (i = 0; i < n; i++) {
- ret = reg_write(client, rv->reg, rv->val);
- if (ret < 0)
- return ret;
- rv++;
- }
-
- return 0;
-}
-
-static int rj54n1_enum_mbus_code(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_mbus_code_enum *code)
-{
- if (code->pad || code->index >= ARRAY_SIZE(rj54n1_colour_fmts))
- return -EINVAL;
-
- code->code = rj54n1_colour_fmts[code->index].code;
- return 0;
-}
-
-static int rj54n1_s_stream(struct v4l2_subdev *sd, int enable)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- /* Switch between preview and still shot modes */
- return reg_set(client, RJ54N1_STILL_CONTROL, (!enable) << 7, 0x80);
-}
-
-static int rj54n1_set_rect(struct i2c_client *client,
- u16 reg_x, u16 reg_y, u16 reg_xy,
- u32 width, u32 height)
-{
- int ret;
-
- ret = reg_write(client, reg_xy,
- ((width >> 4) & 0x70) |
- ((height >> 8) & 7));
-
- if (!ret)
- ret = reg_write(client, reg_x, width & 0xff);
- if (!ret)
- ret = reg_write(client, reg_y, height & 0xff);
-
- return ret;
-}
-
-/*
- * Some commands, specifically certain initialisation sequences, require
- * a commit operation.
- */
-static int rj54n1_commit(struct i2c_client *client)
-{
- int ret = reg_write(client, RJ54N1_INIT_START, 1);
- msleep(10);
- if (!ret)
- ret = reg_write(client, RJ54N1_INIT_START, 0);
- return ret;
-}
-
-static int rj54n1_sensor_scale(struct v4l2_subdev *sd, s32 *in_w, s32 *in_h,
- s32 *out_w, s32 *out_h);
-
-static int rj54n1_set_selection(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_selection *sel)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct rj54n1 *rj54n1 = to_rj54n1(client);
- const struct v4l2_rect *rect = &sel->r;
- int dummy = 0, output_w, output_h,
- input_w = rect->width, input_h = rect->height;
- int ret;
-
- if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE ||
- sel->target != V4L2_SEL_TGT_CROP)
- return -EINVAL;
-
- /* arbitrary minimum width and height, edges unimportant */
- soc_camera_limit_side(&dummy, &input_w,
- RJ54N1_COLUMN_SKIP, 8, RJ54N1_MAX_WIDTH);
-
- soc_camera_limit_side(&dummy, &input_h,
- RJ54N1_ROW_SKIP, 8, RJ54N1_MAX_HEIGHT);
-
- output_w = (input_w * 1024 + rj54n1->resize / 2) / rj54n1->resize;
- output_h = (input_h * 1024 + rj54n1->resize / 2) / rj54n1->resize;
-
- dev_dbg(&client->dev, "Scaling for %dx%d : %u = %dx%d\n",
- input_w, input_h, rj54n1->resize, output_w, output_h);
-
- ret = rj54n1_sensor_scale(sd, &input_w, &input_h, &output_w, &output_h);
- if (ret < 0)
- return ret;
-
- rj54n1->width = output_w;
- rj54n1->height = output_h;
- rj54n1->resize = ret;
- rj54n1->rect.width = input_w;
- rj54n1->rect.height = input_h;
-
- return 0;
-}
-
-static int rj54n1_get_selection(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_selection *sel)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct rj54n1 *rj54n1 = to_rj54n1(client);
-
- if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
- return -EINVAL;
-
- switch (sel->target) {
- case V4L2_SEL_TGT_CROP_BOUNDS:
- sel->r.left = RJ54N1_COLUMN_SKIP;
- sel->r.top = RJ54N1_ROW_SKIP;
- sel->r.width = RJ54N1_MAX_WIDTH;
- sel->r.height = RJ54N1_MAX_HEIGHT;
- return 0;
- case V4L2_SEL_TGT_CROP:
- sel->r = rj54n1->rect;
- return 0;
- default:
- return -EINVAL;
- }
-}
-
-static int rj54n1_get_fmt(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_format *format)
-{
- struct v4l2_mbus_framefmt *mf = &format->format;
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct rj54n1 *rj54n1 = to_rj54n1(client);
-
- if (format->pad)
- return -EINVAL;
-
- mf->code = rj54n1->fmt->code;
- mf->colorspace = rj54n1->fmt->colorspace;
- mf->field = V4L2_FIELD_NONE;
- mf->width = rj54n1->width;
- mf->height = rj54n1->height;
-
- return 0;
-}
-
-/*
- * The actual geometry configuration routine. It scales the input window into
- * the output one, updates the window sizes and returns an error or the resize
- * coefficient on success. Note: we only use the "Fixed Scaling" on this camera.
- */
-static int rj54n1_sensor_scale(struct v4l2_subdev *sd, s32 *in_w, s32 *in_h,
- s32 *out_w, s32 *out_h)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct rj54n1 *rj54n1 = to_rj54n1(client);
- unsigned int skip, resize, input_w = *in_w, input_h = *in_h,
- output_w = *out_w, output_h = *out_h;
- u16 inc_sel, wb_bit8, wb_left, wb_right, wb_top, wb_bottom;
- unsigned int peak, peak_50, peak_60;
- int ret;
-
- /*
- * We have a problem with crops, where the window is larger than 512x384
- * and output window is larger than a half of the input one. In this
- * case we have to either reduce the input window to equal or below
- * 512x384 or the output window to equal or below 1/2 of the input.
- */
- if (output_w > max(512U, input_w / 2)) {
- if (2 * output_w > RJ54N1_MAX_WIDTH) {
- input_w = RJ54N1_MAX_WIDTH;
- output_w = RJ54N1_MAX_WIDTH / 2;
- } else {
- input_w = output_w * 2;
- }
-
- dev_dbg(&client->dev, "Adjusted output width: in %u, out %u\n",
- input_w, output_w);
- }
-
- if (output_h > max(384U, input_h / 2)) {
- if (2 * output_h > RJ54N1_MAX_HEIGHT) {
- input_h = RJ54N1_MAX_HEIGHT;
- output_h = RJ54N1_MAX_HEIGHT / 2;
- } else {
- input_h = output_h * 2;
- }
-
- dev_dbg(&client->dev, "Adjusted output height: in %u, out %u\n",
- input_h, output_h);
- }
-
- /* Idea: use the read mode for snapshots, handle separate geometries */
- ret = rj54n1_set_rect(client, RJ54N1_X_OUTPUT_SIZE_S_L,
- RJ54N1_Y_OUTPUT_SIZE_S_L,
- RJ54N1_XY_OUTPUT_SIZE_S_H, output_w, output_h);
- if (!ret)
- ret = rj54n1_set_rect(client, RJ54N1_X_OUTPUT_SIZE_P_L,
- RJ54N1_Y_OUTPUT_SIZE_P_L,
- RJ54N1_XY_OUTPUT_SIZE_P_H, output_w, output_h);
-
- if (ret < 0)
- return ret;
-
- if (output_w > input_w && output_h > input_h) {
- input_w = output_w;
- input_h = output_h;
-
- resize = 1024;
- } else {
- unsigned int resize_x, resize_y;
- resize_x = (input_w * 1024 + output_w / 2) / output_w;
- resize_y = (input_h * 1024 + output_h / 2) / output_h;
-
- /* We want max(resize_x, resize_y), check if it still fits */
- if (resize_x > resize_y &&
- (output_h * resize_x + 512) / 1024 > RJ54N1_MAX_HEIGHT)
- resize = (RJ54N1_MAX_HEIGHT * 1024 + output_h / 2) /
- output_h;
- else if (resize_y > resize_x &&
- (output_w * resize_y + 512) / 1024 > RJ54N1_MAX_WIDTH)
- resize = (RJ54N1_MAX_WIDTH * 1024 + output_w / 2) /
- output_w;
- else
- resize = max(resize_x, resize_y);
-
- /* Prohibited value ranges */
- switch (resize) {
- case 2040 ... 2047:
- resize = 2039;
- break;
- case 4080 ... 4095:
- resize = 4079;
- break;
- case 8160 ... 8191:
- resize = 8159;
- break;
- case 16320 ... 16384:
- resize = 16319;
- }
- }
-
- /* Set scaling */
- ret = reg_write(client, RJ54N1_RESIZE_HOLD_L, resize & 0xff);
- if (!ret)
- ret = reg_write(client, RJ54N1_RESIZE_HOLD_H, resize >> 8);
-
- if (ret < 0)
- return ret;
-
- /*
- * Configure a skipping bitmask. The sensor will select a skipping value
- * among set bits automatically. This is very unclear in the datasheet
- * too. I was told, in this register one enables all skipping values,
- * that are required for a specific resize, and the camera selects
- * automatically, which ones to use. But it is unclear how to identify,
- * which cropping values are needed. Secondly, why don't we just set all
- * bits and let the camera choose? Would it increase processing time and
- * reduce the framerate? Using 0xfffc for INC_USE_SEL doesn't seem to
- * improve the image quality or stability for larger frames (see comment
- * above), but I didn't check the framerate.
- */
- skip = min(resize / 1024, 15U);
-
- inc_sel = 1 << skip;
-
- if (inc_sel <= 2)
- inc_sel = 0xc;
- else if (resize & 1023 && skip < 15)
- inc_sel |= 1 << (skip + 1);
-
- ret = reg_write(client, RJ54N1_INC_USE_SEL_L, inc_sel & 0xfc);
- if (!ret)
- ret = reg_write(client, RJ54N1_INC_USE_SEL_H, inc_sel >> 8);
-
- if (!rj54n1->auto_wb) {
- /* Auto white balance window */
- wb_left = output_w / 16;
- wb_right = (3 * output_w / 4 - 3) / 4;
- wb_top = output_h / 16;
- wb_bottom = (3 * output_h / 4 - 3) / 4;
- wb_bit8 = ((wb_left >> 2) & 0x40) | ((wb_top >> 4) & 0x10) |
- ((wb_right >> 6) & 4) | ((wb_bottom >> 8) & 1);
-
- if (!ret)
- ret = reg_write(client, RJ54N1_BIT8_WB, wb_bit8);
- if (!ret)
- ret = reg_write(client, RJ54N1_HCAPS_WB, wb_left);
- if (!ret)
- ret = reg_write(client, RJ54N1_VCAPS_WB, wb_top);
- if (!ret)
- ret = reg_write(client, RJ54N1_HCAPE_WB, wb_right);
- if (!ret)
- ret = reg_write(client, RJ54N1_VCAPE_WB, wb_bottom);
- }
-
- /* Antiflicker */
- peak = 12 * RJ54N1_MAX_WIDTH * (1 << 14) * resize / rj54n1->tgclk_mhz /
- 10000;
- peak_50 = peak / 6;
- peak_60 = peak / 5;
-
- if (!ret)
- ret = reg_write(client, RJ54N1_PEAK_H,
- ((peak_50 >> 4) & 0xf0) | (peak_60 >> 8));
- if (!ret)
- ret = reg_write(client, RJ54N1_PEAK_50, peak_50);
- if (!ret)
- ret = reg_write(client, RJ54N1_PEAK_60, peak_60);
- if (!ret)
- ret = reg_write(client, RJ54N1_PEAK_DIFF, peak / 150);
-
- /* Start resizing */
- if (!ret)
- ret = reg_write(client, RJ54N1_RESIZE_CONTROL,
- RESIZE_HOLD_SEL | RESIZE_GO | 1);
-
- if (ret < 0)
- return ret;
-
- /* Constant taken from manufacturer's example */
- msleep(230);
-
- ret = reg_write(client, RJ54N1_RESIZE_CONTROL, RESIZE_HOLD_SEL | 1);
- if (ret < 0)
- return ret;
-
- *in_w = (output_w * resize + 512) / 1024;
- *in_h = (output_h * resize + 512) / 1024;
- *out_w = output_w;
- *out_h = output_h;
-
- dev_dbg(&client->dev, "Scaled for %dx%d : %u = %ux%u, skip %u\n",
- *in_w, *in_h, resize, output_w, output_h, skip);
-
- return resize;
-}
-
-static int rj54n1_set_clock(struct i2c_client *client)
-{
- struct rj54n1 *rj54n1 = to_rj54n1(client);
- int ret;
-
- /* Enable external clock */
- ret = reg_write(client, RJ54N1_RESET_STANDBY, E_EXCLK | SOFT_STDBY);
- /* Leave stand-by. Note: use this when implementing suspend / resume */
- if (!ret)
- ret = reg_write(client, RJ54N1_RESET_STANDBY, E_EXCLK);
-
- if (!ret)
- ret = reg_write(client, RJ54N1_PLL_L, PLL_L);
- if (!ret)
- ret = reg_write(client, RJ54N1_PLL_N, PLL_N);
-
- /* TGCLK dividers */
- if (!ret)
- ret = reg_write(client, RJ54N1_RATIO_TG,
- rj54n1->clk_div.ratio_tg);
- if (!ret)
- ret = reg_write(client, RJ54N1_RATIO_T,
- rj54n1->clk_div.ratio_t);
- if (!ret)
- ret = reg_write(client, RJ54N1_RATIO_R,
- rj54n1->clk_div.ratio_r);
-
- /* Enable TGCLK & RAMP */
- if (!ret)
- ret = reg_write(client, RJ54N1_RAMP_TGCLK_EN, 3);
-
- /* Disable clock output */
- if (!ret)
- ret = reg_write(client, RJ54N1_OCLK_DSP, 0);
-
- /* Set divisors */
- if (!ret)
- ret = reg_write(client, RJ54N1_RATIO_OP,
- rj54n1->clk_div.ratio_op);
- if (!ret)
- ret = reg_write(client, RJ54N1_RATIO_O,
- rj54n1->clk_div.ratio_o);
-
- /* Enable OCLK */
- if (!ret)
- ret = reg_write(client, RJ54N1_OCLK_SEL_EN, 1);
-
- /* Use PLL for Timing Generator, write 2 to reserved bits */
- if (!ret)
- ret = reg_write(client, RJ54N1_TG_BYPASS, 2);
-
- /* Take sensor out of reset */
- if (!ret)
- ret = reg_write(client, RJ54N1_RESET_STANDBY,
- E_EXCLK | SEN_RSTX);
- /* Enable PLL */
- if (!ret)
- ret = reg_write(client, RJ54N1_PLL_EN, 1);
-
- /* Wait for PLL to stabilise */
- msleep(10);
-
- /* Enable clock to frequency divider */
- if (!ret)
- ret = reg_write(client, RJ54N1_CLK_RST, 1);
-
- if (!ret)
- ret = reg_read(client, RJ54N1_CLK_RST);
- if (ret != 1) {
- dev_err(&client->dev,
- "Resetting RJ54N1CB0C clock failed: %d!\n", ret);
- return -EIO;
- }
-
- /* Start the PLL */
- ret = reg_set(client, RJ54N1_OCLK_DSP, 1, 1);
-
- /* Enable OCLK */
- if (!ret)
- ret = reg_write(client, RJ54N1_OCLK_SEL_EN, 1);
-
- return ret;
-}
-
-static int rj54n1_reg_init(struct i2c_client *client)
-{
- struct rj54n1 *rj54n1 = to_rj54n1(client);
- int ret = rj54n1_set_clock(client);
-
- if (!ret)
- ret = reg_write_multiple(client, bank_7, ARRAY_SIZE(bank_7));
- if (!ret)
- ret = reg_write_multiple(client, bank_10, ARRAY_SIZE(bank_10));
-
- /* Set binning divisors */
- if (!ret)
- ret = reg_write(client, RJ54N1_SCALE_1_2_LEV, 3 | (7 << 4));
- if (!ret)
- ret = reg_write(client, RJ54N1_SCALE_4_LEV, 0xf);
-
- /* Switch to fixed resize mode */
- if (!ret)
- ret = reg_write(client, RJ54N1_RESIZE_CONTROL,
- RESIZE_HOLD_SEL | 1);
-
- /* Set gain */
- if (!ret)
- ret = reg_write(client, RJ54N1_Y_GAIN, 0x84);
-
- /*
- * Mirror the image back: default is upside down and left-to-right...
- * Set manual preview / still shot switching
- */
- if (!ret)
- ret = reg_write(client, RJ54N1_MIRROR_STILL_MODE, 0x27);
-
- if (!ret)
- ret = reg_write_multiple(client, bank_4, ARRAY_SIZE(bank_4));
-
- /* Auto exposure area */
- if (!ret)
- ret = reg_write(client, RJ54N1_EXPOSURE_CONTROL, 0x80);
- /* Check current auto WB config */
- if (!ret)
- ret = reg_read(client, RJ54N1_WB_SEL_WEIGHT_I);
- if (ret >= 0) {
- rj54n1->auto_wb = ret & 0x80;
- ret = reg_write_multiple(client, bank_5, ARRAY_SIZE(bank_5));
- }
- if (!ret)
- ret = reg_write_multiple(client, bank_8, ARRAY_SIZE(bank_8));
-
- if (!ret)
- ret = reg_write(client, RJ54N1_RESET_STANDBY,
- E_EXCLK | DSP_RSTX | SEN_RSTX);
-
- /* Commit init */
- if (!ret)
- ret = rj54n1_commit(client);
-
- /* Take DSP, TG, sensor out of reset */
- if (!ret)
- ret = reg_write(client, RJ54N1_RESET_STANDBY,
- E_EXCLK | DSP_RSTX | TG_RSTX | SEN_RSTX);
-
- /* Start register update? Same register as 0x?FE in many bank_* sets */
- if (!ret)
- ret = reg_write(client, RJ54N1_FWFLG, 2);
-
- /* Constant taken from manufacturer's example */
- msleep(700);
-
- return ret;
-}
-
-static int rj54n1_set_fmt(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_format *format)
-{
- struct v4l2_mbus_framefmt *mf = &format->format;
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct rj54n1 *rj54n1 = to_rj54n1(client);
- const struct rj54n1_datafmt *fmt;
- int output_w, output_h, max_w, max_h,
- input_w = rj54n1->rect.width, input_h = rj54n1->rect.height;
- int align = mf->code == MEDIA_BUS_FMT_SBGGR10_1X10 ||
- mf->code == MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_BE ||
- mf->code == MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_BE ||
- mf->code == MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE ||
- mf->code == MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_LE;
- int ret;
-
- if (format->pad)
- return -EINVAL;
-
- dev_dbg(&client->dev, "%s: code = %d, width = %u, height = %u\n",
- __func__, mf->code, mf->width, mf->height);
-
- fmt = rj54n1_find_datafmt(mf->code, rj54n1_colour_fmts,
- ARRAY_SIZE(rj54n1_colour_fmts));
- if (!fmt) {
- fmt = rj54n1->fmt;
- mf->code = fmt->code;
- }
-
- mf->field = V4L2_FIELD_NONE;
- mf->colorspace = fmt->colorspace;
-
- v4l_bound_align_image(&mf->width, 112, RJ54N1_MAX_WIDTH, align,
- &mf->height, 84, RJ54N1_MAX_HEIGHT, align, 0);
-
- if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
- cfg->try_fmt = *mf;
- return 0;
- }
-
- /*
- * Verify if the sensor has just been powered on. TODO: replace this
- * with proper PM, when a suitable API is available.
- */
- ret = reg_read(client, RJ54N1_RESET_STANDBY);
- if (ret < 0)
- return ret;
-
- if (!(ret & E_EXCLK)) {
- ret = rj54n1_reg_init(client);
- if (ret < 0)
- return ret;
- }
-
- /* RA_SEL_UL is only relevant for raw modes, ignored otherwise. */
- switch (mf->code) {
- case MEDIA_BUS_FMT_YUYV8_2X8:
- ret = reg_write(client, RJ54N1_OUT_SEL, 0);
- if (!ret)
- ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8);
- break;
- case MEDIA_BUS_FMT_YVYU8_2X8:
- ret = reg_write(client, RJ54N1_OUT_SEL, 0);
- if (!ret)
- ret = reg_set(client, RJ54N1_BYTE_SWAP, 0, 8);
- break;
- case MEDIA_BUS_FMT_RGB565_2X8_LE:
- ret = reg_write(client, RJ54N1_OUT_SEL, 0x11);
- if (!ret)
- ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8);
- break;
- case MEDIA_BUS_FMT_RGB565_2X8_BE:
- ret = reg_write(client, RJ54N1_OUT_SEL, 0x11);
- if (!ret)
- ret = reg_set(client, RJ54N1_BYTE_SWAP, 0, 8);
- break;
- case MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_LE:
- ret = reg_write(client, RJ54N1_OUT_SEL, 4);
- if (!ret)
- ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8);
- if (!ret)
- ret = reg_write(client, RJ54N1_RA_SEL_UL, 0);
- break;
- case MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE:
- ret = reg_write(client, RJ54N1_OUT_SEL, 4);
- if (!ret)
- ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8);
- if (!ret)
- ret = reg_write(client, RJ54N1_RA_SEL_UL, 8);
- break;
- case MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_BE:
- ret = reg_write(client, RJ54N1_OUT_SEL, 4);
- if (!ret)
- ret = reg_set(client, RJ54N1_BYTE_SWAP, 0, 8);
- if (!ret)
- ret = reg_write(client, RJ54N1_RA_SEL_UL, 0);
- break;
- case MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_BE:
- ret = reg_write(client, RJ54N1_OUT_SEL, 4);
- if (!ret)
- ret = reg_set(client, RJ54N1_BYTE_SWAP, 0, 8);
- if (!ret)
- ret = reg_write(client, RJ54N1_RA_SEL_UL, 8);
- break;
- case MEDIA_BUS_FMT_SBGGR10_1X10:
- ret = reg_write(client, RJ54N1_OUT_SEL, 5);
- break;
- default:
- ret = -EINVAL;
- }
-
- /* Special case: a raw mode with 10 bits of data per clock tick */
- if (!ret)
- ret = reg_set(client, RJ54N1_OCLK_SEL_EN,
- (mf->code == MEDIA_BUS_FMT_SBGGR10_1X10) << 1, 2);
-
- if (ret < 0)
- return ret;
-
- /* Supported scales 1:1 >= scale > 1:16 */
- max_w = mf->width * (16 * 1024 - 1) / 1024;
- if (input_w > max_w)
- input_w = max_w;
- max_h = mf->height * (16 * 1024 - 1) / 1024;
- if (input_h > max_h)
- input_h = max_h;
-
- output_w = mf->width;
- output_h = mf->height;
-
- ret = rj54n1_sensor_scale(sd, &input_w, &input_h, &output_w, &output_h);
- if (ret < 0)
- return ret;
-
- fmt = rj54n1_find_datafmt(mf->code, rj54n1_colour_fmts,
- ARRAY_SIZE(rj54n1_colour_fmts));
-
- rj54n1->fmt = fmt;
- rj54n1->resize = ret;
- rj54n1->rect.width = input_w;
- rj54n1->rect.height = input_h;
- rj54n1->width = output_w;
- rj54n1->height = output_h;
-
- mf->width = output_w;
- mf->height = output_h;
- mf->field = V4L2_FIELD_NONE;
- mf->colorspace = fmt->colorspace;
-
- return 0;
-}
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int rj54n1_g_register(struct v4l2_subdev *sd,
- struct v4l2_dbg_register *reg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (reg->reg < 0x400 || reg->reg > 0x1fff)
- /* Registers > 0x0800 are only available from Sharp support */
- return -EINVAL;
-
- reg->size = 1;
- reg->val = reg_read(client, reg->reg);
-
- if (reg->val > 0xff)
- return -EIO;
-
- return 0;
-}
-
-static int rj54n1_s_register(struct v4l2_subdev *sd,
- const struct v4l2_dbg_register *reg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (reg->reg < 0x400 || reg->reg > 0x1fff)
- /* Registers >= 0x0800 are only available from Sharp support */
- return -EINVAL;
-
- if (reg_write(client, reg->reg, reg->val) < 0)
- return -EIO;
-
- return 0;
-}
-#endif
-
-static int rj54n1_s_power(struct v4l2_subdev *sd, int on)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
- struct rj54n1 *rj54n1 = to_rj54n1(client);
-
- return soc_camera_set_power(&client->dev, ssdd, rj54n1->clk, on);
-}
-
-static int rj54n1_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct rj54n1 *rj54n1 = container_of(ctrl->handler, struct rj54n1, hdl);
- struct v4l2_subdev *sd = &rj54n1->subdev;
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- int data;
-
- switch (ctrl->id) {
- case V4L2_CID_VFLIP:
- if (ctrl->val)
- data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 0, 1);
- else
- data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 1, 1);
- if (data < 0)
- return -EIO;
- return 0;
- case V4L2_CID_HFLIP:
- if (ctrl->val)
- data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 0, 2);
- else
- data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 2, 2);
- if (data < 0)
- return -EIO;
- return 0;
- case V4L2_CID_GAIN:
- if (reg_write(client, RJ54N1_Y_GAIN, ctrl->val * 2) < 0)
- return -EIO;
- return 0;
- case V4L2_CID_AUTO_WHITE_BALANCE:
- /* Auto WB area - whole image */
- if (reg_set(client, RJ54N1_WB_SEL_WEIGHT_I, ctrl->val << 7,
- 0x80) < 0)
- return -EIO;
- rj54n1->auto_wb = ctrl->val;
- return 0;
- }
-
- return -EINVAL;
-}
-
-static const struct v4l2_ctrl_ops rj54n1_ctrl_ops = {
- .s_ctrl = rj54n1_s_ctrl,
-};
-
-static const struct v4l2_subdev_core_ops rj54n1_subdev_core_ops = {
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .g_register = rj54n1_g_register,
- .s_register = rj54n1_s_register,
-#endif
- .s_power = rj54n1_s_power,
-};
-
-static int rj54n1_g_mbus_config(struct v4l2_subdev *sd,
- struct v4l2_mbus_config *cfg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
-
- cfg->flags =
- V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING |
- V4L2_MBUS_MASTER | V4L2_MBUS_DATA_ACTIVE_HIGH |
- V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH;
- cfg->type = V4L2_MBUS_PARALLEL;
- cfg->flags = soc_camera_apply_board_flags(ssdd, cfg);
-
- return 0;
-}
-
-static int rj54n1_s_mbus_config(struct v4l2_subdev *sd,
- const struct v4l2_mbus_config *cfg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
-
- /* Figures 2.5-1 to 2.5-3 - default falling pixclk edge */
- if (soc_camera_apply_board_flags(ssdd, cfg) &
- V4L2_MBUS_PCLK_SAMPLE_RISING)
- return reg_write(client, RJ54N1_OUT_SIGPO, 1 << 4);
- else
- return reg_write(client, RJ54N1_OUT_SIGPO, 0);
-}
-
-static const struct v4l2_subdev_video_ops rj54n1_subdev_video_ops = {
- .s_stream = rj54n1_s_stream,
- .g_mbus_config = rj54n1_g_mbus_config,
- .s_mbus_config = rj54n1_s_mbus_config,
-};
-
-static const struct v4l2_subdev_pad_ops rj54n1_subdev_pad_ops = {
- .enum_mbus_code = rj54n1_enum_mbus_code,
- .get_selection = rj54n1_get_selection,
- .set_selection = rj54n1_set_selection,
- .get_fmt = rj54n1_get_fmt,
- .set_fmt = rj54n1_set_fmt,
-};
-
-static const struct v4l2_subdev_ops rj54n1_subdev_ops = {
- .core = &rj54n1_subdev_core_ops,
- .video = &rj54n1_subdev_video_ops,
- .pad = &rj54n1_subdev_pad_ops,
-};
-
-/*
- * Interface active, can use i2c. If it fails, it can indeed mean, that
- * this wasn't our capture interface, so, we wait for the right one
- */
-static int rj54n1_video_probe(struct i2c_client *client,
- struct rj54n1_pdata *priv)
-{
- struct rj54n1 *rj54n1 = to_rj54n1(client);
- int data1, data2;
- int ret;
-
- ret = rj54n1_s_power(&rj54n1->subdev, 1);
- if (ret < 0)
- return ret;
-
- /* Read out the chip version register */
- data1 = reg_read(client, RJ54N1_DEV_CODE);
- data2 = reg_read(client, RJ54N1_DEV_CODE2);
-
- if (data1 != 0x51 || data2 != 0x10) {
- ret = -ENODEV;
- dev_info(&client->dev, "No RJ54N1CB0C found, read 0x%x:0x%x\n",
- data1, data2);
- goto done;
- }
-
- /* Configure IOCTL polarity from the platform data: 0 or 1 << 7. */
- ret = reg_write(client, RJ54N1_IOC, priv->ioctl_high << 7);
- if (ret < 0)
- goto done;
-
- dev_info(&client->dev, "Detected a RJ54N1CB0C chip ID 0x%x:0x%x\n",
- data1, data2);
-
- ret = v4l2_ctrl_handler_setup(&rj54n1->hdl);
-
-done:
- rj54n1_s_power(&rj54n1->subdev, 0);
- return ret;
-}
-
-static int rj54n1_probe(struct i2c_client *client,
- const struct i2c_device_id *did)
-{
- struct rj54n1 *rj54n1;
- struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
- struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
- struct rj54n1_pdata *rj54n1_priv;
- int ret;
-
- if (!ssdd || !ssdd->drv_priv) {
- dev_err(&client->dev, "RJ54N1CB0C: missing platform data!\n");
- return -EINVAL;
- }
-
- rj54n1_priv = ssdd->drv_priv;
-
- if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
- dev_warn(&adapter->dev,
- "I2C-Adapter doesn't support I2C_FUNC_SMBUS_BYTE\n");
- return -EIO;
- }
-
- rj54n1 = devm_kzalloc(&client->dev, sizeof(struct rj54n1), GFP_KERNEL);
- if (!rj54n1)
- return -ENOMEM;
-
- v4l2_i2c_subdev_init(&rj54n1->subdev, client, &rj54n1_subdev_ops);
- v4l2_ctrl_handler_init(&rj54n1->hdl, 4);
- v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops,
- V4L2_CID_VFLIP, 0, 1, 1, 0);
- v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops,
- V4L2_CID_HFLIP, 0, 1, 1, 0);
- v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops,
- V4L2_CID_GAIN, 0, 127, 1, 66);
- v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops,
- V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1);
- rj54n1->subdev.ctrl_handler = &rj54n1->hdl;
- if (rj54n1->hdl.error)
- return rj54n1->hdl.error;
-
- rj54n1->clk_div = clk_div;
- rj54n1->rect.left = RJ54N1_COLUMN_SKIP;
- rj54n1->rect.top = RJ54N1_ROW_SKIP;
- rj54n1->rect.width = RJ54N1_MAX_WIDTH;
- rj54n1->rect.height = RJ54N1_MAX_HEIGHT;
- rj54n1->width = RJ54N1_MAX_WIDTH;
- rj54n1->height = RJ54N1_MAX_HEIGHT;
- rj54n1->fmt = &rj54n1_colour_fmts[0];
- rj54n1->resize = 1024;
- rj54n1->tgclk_mhz = (rj54n1_priv->mclk_freq / PLL_L * PLL_N) /
- (clk_div.ratio_tg + 1) / (clk_div.ratio_t + 1);
-
- rj54n1->clk = v4l2_clk_get(&client->dev, "mclk");
- if (IS_ERR(rj54n1->clk)) {
- ret = PTR_ERR(rj54n1->clk);
- goto eclkget;
- }
-
- ret = rj54n1_video_probe(client, rj54n1_priv);
- if (ret < 0) {
- v4l2_clk_put(rj54n1->clk);
-eclkget:
- v4l2_ctrl_handler_free(&rj54n1->hdl);
- }
-
- return ret;
-}
-
-static int rj54n1_remove(struct i2c_client *client)
-{
- struct rj54n1 *rj54n1 = to_rj54n1(client);
- struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
-
- v4l2_clk_put(rj54n1->clk);
- v4l2_device_unregister_subdev(&rj54n1->subdev);
- if (ssdd->free_bus)
- ssdd->free_bus(ssdd);
- v4l2_ctrl_handler_free(&rj54n1->hdl);
-
- return 0;
-}
-
-static const struct i2c_device_id rj54n1_id[] = {
- { "rj54n1cb0c", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, rj54n1_id);
-
-static struct i2c_driver rj54n1_i2c_driver = {
- .driver = {
- .name = "rj54n1cb0c",
- },
- .probe = rj54n1_probe,
- .remove = rj54n1_remove,
- .id_table = rj54n1_id,
-};
-
-module_i2c_driver(rj54n1_i2c_driver);
-
-MODULE_DESCRIPTION("Sharp RJ54N1CB0C Camera driver");
-MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/i2c/soc_camera/soc_tw9910.c b/drivers/media/i2c/soc_camera/soc_tw9910.c
deleted file mode 100644
index bdb5e0a431e9..000000000000
--- a/drivers/media/i2c/soc_camera/soc_tw9910.c
+++ /dev/null
@@ -1,999 +0,0 @@
-/*
- * tw9910 Video Driver
- *
- * Copyright (C) 2008 Renesas Solutions Corp.
- * Kuninori Morimoto <morimoto.kuninori@renesas.com>
- *
- * Based on ov772x driver,
- *
- * Copyright (C) 2008 Kuninori Morimoto <morimoto.kuninori@renesas.com>
- * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net>
- * Copyright (C) 2008 Magnus Damm
- * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
- *
- * 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 <linux/init.h>
-#include <linux/module.h>
-#include <linux/i2c.h>
-#include <linux/slab.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/v4l2-mediabus.h>
-#include <linux/videodev2.h>
-
-#include <media/soc_camera.h>
-#include <media/i2c/tw9910.h>
-#include <media/v4l2-clk.h>
-#include <media/v4l2-subdev.h>
-
-#define GET_ID(val) ((val & 0xF8) >> 3)
-#define GET_REV(val) (val & 0x07)
-
-/*
- * register offset
- */
-#define ID 0x00 /* Product ID Code Register */
-#define STATUS1 0x01 /* Chip Status Register I */
-#define INFORM 0x02 /* Input Format */
-#define OPFORM 0x03 /* Output Format Control Register */
-#define DLYCTR 0x04 /* Hysteresis and HSYNC Delay Control */
-#define OUTCTR1 0x05 /* Output Control I */
-#define ACNTL1 0x06 /* Analog Control Register 1 */
-#define CROP_HI 0x07 /* Cropping Register, High */
-#define VDELAY_LO 0x08 /* Vertical Delay Register, Low */
-#define VACTIVE_LO 0x09 /* Vertical Active Register, Low */
-#define HDELAY_LO 0x0A /* Horizontal Delay Register, Low */
-#define HACTIVE_LO 0x0B /* Horizontal Active Register, Low */
-#define CNTRL1 0x0C /* Control Register I */
-#define VSCALE_LO 0x0D /* Vertical Scaling Register, Low */
-#define SCALE_HI 0x0E /* Scaling Register, High */
-#define HSCALE_LO 0x0F /* Horizontal Scaling Register, Low */
-#define BRIGHT 0x10 /* BRIGHTNESS Control Register */
-#define CONTRAST 0x11 /* CONTRAST Control Register */
-#define SHARPNESS 0x12 /* SHARPNESS Control Register I */
-#define SAT_U 0x13 /* Chroma (U) Gain Register */
-#define SAT_V 0x14 /* Chroma (V) Gain Register */
-#define HUE 0x15 /* Hue Control Register */
-#define CORING1 0x17
-#define CORING2 0x18 /* Coring and IF compensation */
-#define VBICNTL 0x19 /* VBI Control Register */
-#define ACNTL2 0x1A /* Analog Control 2 */
-#define OUTCTR2 0x1B /* Output Control 2 */
-#define SDT 0x1C /* Standard Selection */
-#define SDTR 0x1D /* Standard Recognition */
-#define TEST 0x1F /* Test Control Register */
-#define CLMPG 0x20 /* Clamping Gain */
-#define IAGC 0x21 /* Individual AGC Gain */
-#define AGCGAIN 0x22 /* AGC Gain */
-#define PEAKWT 0x23 /* White Peak Threshold */
-#define CLMPL 0x24 /* Clamp level */
-#define SYNCT 0x25 /* Sync Amplitude */
-#define MISSCNT 0x26 /* Sync Miss Count Register */
-#define PCLAMP 0x27 /* Clamp Position Register */
-#define VCNTL1 0x28 /* Vertical Control I */
-#define VCNTL2 0x29 /* Vertical Control II */
-#define CKILL 0x2A /* Color Killer Level Control */
-#define COMB 0x2B /* Comb Filter Control */
-#define LDLY 0x2C /* Luma Delay and H Filter Control */
-#define MISC1 0x2D /* Miscellaneous Control I */
-#define LOOP 0x2E /* LOOP Control Register */
-#define MISC2 0x2F /* Miscellaneous Control II */
-#define MVSN 0x30 /* Macrovision Detection */
-#define STATUS2 0x31 /* Chip STATUS II */
-#define HFREF 0x32 /* H monitor */
-#define CLMD 0x33 /* CLAMP MODE */
-#define IDCNTL 0x34 /* ID Detection Control */
-#define CLCNTL1 0x35 /* Clamp Control I */
-#define ANAPLLCTL 0x4C
-#define VBIMIN 0x4D
-#define HSLOWCTL 0x4E
-#define WSS3 0x4F
-#define FILLDATA 0x50
-#define SDID 0x51
-#define DID 0x52
-#define WSS1 0x53
-#define WSS2 0x54
-#define VVBI 0x55
-#define LCTL6 0x56
-#define LCTL7 0x57
-#define LCTL8 0x58
-#define LCTL9 0x59
-#define LCTL10 0x5A
-#define LCTL11 0x5B
-#define LCTL12 0x5C
-#define LCTL13 0x5D
-#define LCTL14 0x5E
-#define LCTL15 0x5F
-#define LCTL16 0x60
-#define LCTL17 0x61
-#define LCTL18 0x62
-#define LCTL19 0x63
-#define LCTL20 0x64
-#define LCTL21 0x65
-#define LCTL22 0x66
-#define LCTL23 0x67
-#define LCTL24 0x68
-#define LCTL25 0x69
-#define LCTL26 0x6A
-#define HSBEGIN 0x6B
-#define HSEND 0x6C
-#define OVSDLY 0x6D
-#define OVSEND 0x6E
-#define VBIDELAY 0x6F
-
-/*
- * register detail
- */
-
-/* INFORM */
-#define FC27_ON 0x40 /* 1 : Input crystal clock frequency is 27MHz */
-#define FC27_FF 0x00 /* 0 : Square pixel mode. */
- /* Must use 24.54MHz for 60Hz field rate */
- /* source or 29.5MHz for 50Hz field rate */
-#define IFSEL_S 0x10 /* 01 : S-video decoding */
-#define IFSEL_C 0x00 /* 00 : Composite video decoding */
- /* Y input video selection */
-#define YSEL_M0 0x00 /* 00 : Mux0 selected */
-#define YSEL_M1 0x04 /* 01 : Mux1 selected */
-#define YSEL_M2 0x08 /* 10 : Mux2 selected */
-#define YSEL_M3 0x10 /* 11 : Mux3 selected */
-
-/* OPFORM */
-#define MODE 0x80 /* 0 : CCIR601 compatible YCrCb 4:2:2 format */
- /* 1 : ITU-R-656 compatible data sequence format */
-#define LEN 0x40 /* 0 : 8-bit YCrCb 4:2:2 output format */
- /* 1 : 16-bit YCrCb 4:2:2 output format.*/
-#define LLCMODE 0x20 /* 1 : LLC output mode. */
- /* 0 : free-run output mode */
-#define AINC 0x10 /* Serial interface auto-indexing control */
- /* 0 : auto-increment */
- /* 1 : non-auto */
-#define VSCTL 0x08 /* 1 : Vertical out ctrl by DVALID */
- /* 0 : Vertical out ctrl by HACTIVE and DVALID */
-#define OEN_TRI_SEL_MASK 0x07
-#define OEN_TRI_SEL_ALL_ON 0x00 /* Enable output for Rev0/Rev1 */
-#define OEN_TRI_SEL_ALL_OFF_r0 0x06 /* All tri-stated for Rev0 */
-#define OEN_TRI_SEL_ALL_OFF_r1 0x07 /* All tri-stated for Rev1 */
-
-/* OUTCTR1 */
-#define VSP_LO 0x00 /* 0 : VS pin output polarity is active low */
-#define VSP_HI 0x80 /* 1 : VS pin output polarity is active high. */
- /* VS pin output control */
-#define VSSL_VSYNC 0x00 /* 0 : VSYNC */
-#define VSSL_VACT 0x10 /* 1 : VACT */
-#define VSSL_FIELD 0x20 /* 2 : FIELD */
-#define VSSL_VVALID 0x30 /* 3 : VVALID */
-#define VSSL_ZERO 0x70 /* 7 : 0 */
-#define HSP_LOW 0x00 /* 0 : HS pin output polarity is active low */
-#define HSP_HI 0x08 /* 1 : HS pin output polarity is active high.*/
- /* HS pin output control */
-#define HSSL_HACT 0x00 /* 0 : HACT */
-#define HSSL_HSYNC 0x01 /* 1 : HSYNC */
-#define HSSL_DVALID 0x02 /* 2 : DVALID */
-#define HSSL_HLOCK 0x03 /* 3 : HLOCK */
-#define HSSL_ASYNCW 0x04 /* 4 : ASYNCW */
-#define HSSL_ZERO 0x07 /* 7 : 0 */
-
-/* ACNTL1 */
-#define SRESET 0x80 /* resets the device to its default state
- * but all register content remain unchanged.
- * This bit is self-resetting.
- */
-#define ACNTL1_PDN_MASK 0x0e
-#define CLK_PDN 0x08 /* system clock power down */
-#define Y_PDN 0x04 /* Luma ADC power down */
-#define C_PDN 0x02 /* Chroma ADC power down */
-
-/* ACNTL2 */
-#define ACNTL2_PDN_MASK 0x40
-#define PLL_PDN 0x40 /* PLL power down */
-
-/* VBICNTL */
-
-/* RTSEL : control the real time signal output from the MPOUT pin */
-#define RTSEL_MASK 0x07
-#define RTSEL_VLOSS 0x00 /* 0000 = Video loss */
-#define RTSEL_HLOCK 0x01 /* 0001 = H-lock */
-#define RTSEL_SLOCK 0x02 /* 0010 = S-lock */
-#define RTSEL_VLOCK 0x03 /* 0011 = V-lock */
-#define RTSEL_MONO 0x04 /* 0100 = MONO */
-#define RTSEL_DET50 0x05 /* 0101 = DET50 */
-#define RTSEL_FIELD 0x06 /* 0110 = FIELD */
-#define RTSEL_RTCO 0x07 /* 0111 = RTCO ( Real Time Control ) */
-
-/* HSYNC start and end are constant for now */
-#define HSYNC_START 0x0260
-#define HSYNC_END 0x0300
-
-/*
- * structure
- */
-
-struct regval_list {
- unsigned char reg_num;
- unsigned char value;
-};
-
-struct tw9910_scale_ctrl {
- char *name;
- unsigned short width;
- unsigned short height;
- u16 hscale;
- u16 vscale;
-};
-
-struct tw9910_priv {
- struct v4l2_subdev subdev;
- struct v4l2_clk *clk;
- struct tw9910_video_info *info;
- const struct tw9910_scale_ctrl *scale;
- v4l2_std_id norm;
- u32 revision;
-};
-
-static const struct tw9910_scale_ctrl tw9910_ntsc_scales[] = {
- {
- .name = "NTSC SQ",
- .width = 640,
- .height = 480,
- .hscale = 0x0100,
- .vscale = 0x0100,
- },
- {
- .name = "NTSC CCIR601",
- .width = 720,
- .height = 480,
- .hscale = 0x0100,
- .vscale = 0x0100,
- },
- {
- .name = "NTSC SQ (CIF)",
- .width = 320,
- .height = 240,
- .hscale = 0x0200,
- .vscale = 0x0200,
- },
- {
- .name = "NTSC CCIR601 (CIF)",
- .width = 360,
- .height = 240,
- .hscale = 0x0200,
- .vscale = 0x0200,
- },
- {
- .name = "NTSC SQ (QCIF)",
- .width = 160,
- .height = 120,
- .hscale = 0x0400,
- .vscale = 0x0400,
- },
- {
- .name = "NTSC CCIR601 (QCIF)",
- .width = 180,
- .height = 120,
- .hscale = 0x0400,
- .vscale = 0x0400,
- },
-};
-
-static const struct tw9910_scale_ctrl tw9910_pal_scales[] = {
- {
- .name = "PAL SQ",
- .width = 768,
- .height = 576,
- .hscale = 0x0100,
- .vscale = 0x0100,
- },
- {
- .name = "PAL CCIR601",
- .width = 720,
- .height = 576,
- .hscale = 0x0100,
- .vscale = 0x0100,
- },
- {
- .name = "PAL SQ (CIF)",
- .width = 384,
- .height = 288,
- .hscale = 0x0200,
- .vscale = 0x0200,
- },
- {
- .name = "PAL CCIR601 (CIF)",
- .width = 360,
- .height = 288,
- .hscale = 0x0200,
- .vscale = 0x0200,
- },
- {
- .name = "PAL SQ (QCIF)",
- .width = 192,
- .height = 144,
- .hscale = 0x0400,
- .vscale = 0x0400,
- },
- {
- .name = "PAL CCIR601 (QCIF)",
- .width = 180,
- .height = 144,
- .hscale = 0x0400,
- .vscale = 0x0400,
- },
-};
-
-/*
- * general function
- */
-static struct tw9910_priv *to_tw9910(const struct i2c_client *client)
-{
- return container_of(i2c_get_clientdata(client), struct tw9910_priv,
- subdev);
-}
-
-static int tw9910_mask_set(struct i2c_client *client, u8 command,
- u8 mask, u8 set)
-{
- s32 val = i2c_smbus_read_byte_data(client, command);
- if (val < 0)
- return val;
-
- val &= ~mask;
- val |= set & mask;
-
- return i2c_smbus_write_byte_data(client, command, val);
-}
-
-static int tw9910_set_scale(struct i2c_client *client,
- const struct tw9910_scale_ctrl *scale)
-{
- int ret;
-
- ret = i2c_smbus_write_byte_data(client, SCALE_HI,
- (scale->vscale & 0x0F00) >> 4 |
- (scale->hscale & 0x0F00) >> 8);
- if (ret < 0)
- return ret;
-
- ret = i2c_smbus_write_byte_data(client, HSCALE_LO,
- scale->hscale & 0x00FF);
- if (ret < 0)
- return ret;
-
- ret = i2c_smbus_write_byte_data(client, VSCALE_LO,
- scale->vscale & 0x00FF);
-
- return ret;
-}
-
-static int tw9910_set_hsync(struct i2c_client *client)
-{
- struct tw9910_priv *priv = to_tw9910(client);
- int ret;
-
- /* bit 10 - 3 */
- ret = i2c_smbus_write_byte_data(client, HSBEGIN,
- (HSYNC_START & 0x07F8) >> 3);
- if (ret < 0)
- return ret;
-
- /* bit 10 - 3 */
- ret = i2c_smbus_write_byte_data(client, HSEND,
- (HSYNC_END & 0x07F8) >> 3);
- if (ret < 0)
- return ret;
-
- /* So far only revisions 0 and 1 have been seen */
- /* bit 2 - 0 */
- if (1 == priv->revision)
- ret = tw9910_mask_set(client, HSLOWCTL, 0x77,
- (HSYNC_START & 0x0007) << 4 |
- (HSYNC_END & 0x0007));
-
- return ret;
-}
-
-static void tw9910_reset(struct i2c_client *client)
-{
- tw9910_mask_set(client, ACNTL1, SRESET, SRESET);
- msleep(1);
-}
-
-static int tw9910_power(struct i2c_client *client, int enable)
-{
- int ret;
- u8 acntl1;
- u8 acntl2;
-
- if (enable) {
- acntl1 = 0;
- acntl2 = 0;
- } else {
- acntl1 = CLK_PDN | Y_PDN | C_PDN;
- acntl2 = PLL_PDN;
- }
-
- ret = tw9910_mask_set(client, ACNTL1, ACNTL1_PDN_MASK, acntl1);
- if (ret < 0)
- return ret;
-
- return tw9910_mask_set(client, ACNTL2, ACNTL2_PDN_MASK, acntl2);
-}
-
-static const struct tw9910_scale_ctrl *tw9910_select_norm(v4l2_std_id norm,
- u32 width, u32 height)
-{
- const struct tw9910_scale_ctrl *scale;
- const struct tw9910_scale_ctrl *ret = NULL;
- __u32 diff = 0xffffffff, tmp;
- int size, i;
-
- if (norm & V4L2_STD_NTSC) {
- scale = tw9910_ntsc_scales;
- size = ARRAY_SIZE(tw9910_ntsc_scales);
- } else if (norm & V4L2_STD_PAL) {
- scale = tw9910_pal_scales;
- size = ARRAY_SIZE(tw9910_pal_scales);
- } else {
- return NULL;
- }
-
- for (i = 0; i < size; i++) {
- tmp = abs(width - scale[i].width) +
- abs(height - scale[i].height);
- if (tmp < diff) {
- diff = tmp;
- ret = scale + i;
- }
- }
-
- return ret;
-}
-
-/*
- * subdevice operations
- */
-static int tw9910_s_stream(struct v4l2_subdev *sd, int enable)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct tw9910_priv *priv = to_tw9910(client);
- u8 val;
- int ret;
-
- if (!enable) {
- switch (priv->revision) {
- case 0:
- val = OEN_TRI_SEL_ALL_OFF_r0;
- break;
- case 1:
- val = OEN_TRI_SEL_ALL_OFF_r1;
- break;
- default:
- dev_err(&client->dev, "un-supported revision\n");
- return -EINVAL;
- }
- } else {
- val = OEN_TRI_SEL_ALL_ON;
-
- if (!priv->scale) {
- dev_err(&client->dev, "norm select error\n");
- return -EPERM;
- }
-
- dev_dbg(&client->dev, "%s %dx%d\n",
- priv->scale->name,
- priv->scale->width,
- priv->scale->height);
- }
-
- ret = tw9910_mask_set(client, OPFORM, OEN_TRI_SEL_MASK, val);
- if (ret < 0)
- return ret;
-
- return tw9910_power(client, enable);
-}
-
-static int tw9910_g_std(struct v4l2_subdev *sd, v4l2_std_id *norm)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct tw9910_priv *priv = to_tw9910(client);
-
- *norm = priv->norm;
-
- return 0;
-}
-
-static int tw9910_s_std(struct v4l2_subdev *sd, v4l2_std_id norm)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct tw9910_priv *priv = to_tw9910(client);
- const unsigned hact = 720;
- const unsigned hdelay = 15;
- unsigned vact;
- unsigned vdelay;
- int ret;
-
- if (!(norm & (V4L2_STD_NTSC | V4L2_STD_PAL)))
- return -EINVAL;
-
- priv->norm = norm;
- if (norm & V4L2_STD_525_60) {
- vact = 240;
- vdelay = 18;
- ret = tw9910_mask_set(client, VVBI, 0x10, 0x10);
- } else {
- vact = 288;
- vdelay = 24;
- ret = tw9910_mask_set(client, VVBI, 0x10, 0x00);
- }
- if (!ret)
- ret = i2c_smbus_write_byte_data(client, CROP_HI,
- ((vdelay >> 2) & 0xc0) |
- ((vact >> 4) & 0x30) |
- ((hdelay >> 6) & 0x0c) |
- ((hact >> 8) & 0x03));
- if (!ret)
- ret = i2c_smbus_write_byte_data(client, VDELAY_LO,
- vdelay & 0xff);
- if (!ret)
- ret = i2c_smbus_write_byte_data(client, VACTIVE_LO,
- vact & 0xff);
-
- return ret;
-}
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int tw9910_g_register(struct v4l2_subdev *sd,
- struct v4l2_dbg_register *reg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- int ret;
-
- if (reg->reg > 0xff)
- return -EINVAL;
-
- reg->size = 1;
- ret = i2c_smbus_read_byte_data(client, reg->reg);
- if (ret < 0)
- return ret;
-
- /*
- * ret = int
- * reg->val = __u64
- */
- reg->val = (__u64)ret;
-
- return 0;
-}
-
-static int tw9910_s_register(struct v4l2_subdev *sd,
- const struct v4l2_dbg_register *reg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (reg->reg > 0xff ||
- reg->val > 0xff)
- return -EINVAL;
-
- return i2c_smbus_write_byte_data(client, reg->reg, reg->val);
-}
-#endif
-
-static int tw9910_s_power(struct v4l2_subdev *sd, int on)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
- struct tw9910_priv *priv = to_tw9910(client);
-
- return soc_camera_set_power(&client->dev, ssdd, priv->clk, on);
-}
-
-static int tw9910_set_frame(struct v4l2_subdev *sd, u32 *width, u32 *height)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct tw9910_priv *priv = to_tw9910(client);
- int ret = -EINVAL;
- u8 val;
-
- /*
- * select suitable norm
- */
- priv->scale = tw9910_select_norm(priv->norm, *width, *height);
- if (!priv->scale)
- goto tw9910_set_fmt_error;
-
- /*
- * reset hardware
- */
- tw9910_reset(client);
-
- /*
- * set bus width
- */
- val = 0x00;
- if (SOCAM_DATAWIDTH_16 == priv->info->buswidth)
- val = LEN;
-
- ret = tw9910_mask_set(client, OPFORM, LEN, val);
- if (ret < 0)
- goto tw9910_set_fmt_error;
-
- /*
- * select MPOUT behavior
- */
- switch (priv->info->mpout) {
- case TW9910_MPO_VLOSS:
- val = RTSEL_VLOSS; break;
- case TW9910_MPO_HLOCK:
- val = RTSEL_HLOCK; break;
- case TW9910_MPO_SLOCK:
- val = RTSEL_SLOCK; break;
- case TW9910_MPO_VLOCK:
- val = RTSEL_VLOCK; break;
- case TW9910_MPO_MONO:
- val = RTSEL_MONO; break;
- case TW9910_MPO_DET50:
- val = RTSEL_DET50; break;
- case TW9910_MPO_FIELD:
- val = RTSEL_FIELD; break;
- case TW9910_MPO_RTCO:
- val = RTSEL_RTCO; break;
- default:
- val = 0;
- }
-
- ret = tw9910_mask_set(client, VBICNTL, RTSEL_MASK, val);
- if (ret < 0)
- goto tw9910_set_fmt_error;
-
- /*
- * set scale
- */
- ret = tw9910_set_scale(client, priv->scale);
- if (ret < 0)
- goto tw9910_set_fmt_error;
-
- /*
- * set hsync
- */
- ret = tw9910_set_hsync(client);
- if (ret < 0)
- goto tw9910_set_fmt_error;
-
- *width = priv->scale->width;
- *height = priv->scale->height;
-
- return ret;
-
-tw9910_set_fmt_error:
-
- tw9910_reset(client);
- priv->scale = NULL;
-
- return ret;
-}
-
-static int tw9910_get_selection(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_selection *sel)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct tw9910_priv *priv = to_tw9910(client);
-
- if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
- return -EINVAL;
- /* Only CROP, CROP_DEFAULT and CROP_BOUNDS are supported */
- if (sel->target > V4L2_SEL_TGT_CROP_BOUNDS)
- return -EINVAL;
-
- sel->r.left = 0;
- sel->r.top = 0;
- if (priv->norm & V4L2_STD_NTSC) {
- sel->r.width = 640;
- sel->r.height = 480;
- } else {
- sel->r.width = 768;
- sel->r.height = 576;
- }
- return 0;
-}
-
-static int tw9910_get_fmt(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_format *format)
-{
- struct v4l2_mbus_framefmt *mf = &format->format;
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct tw9910_priv *priv = to_tw9910(client);
-
- if (format->pad)
- return -EINVAL;
-
- if (!priv->scale) {
- priv->scale = tw9910_select_norm(priv->norm, 640, 480);
- if (!priv->scale)
- return -EINVAL;
- }
-
- mf->width = priv->scale->width;
- mf->height = priv->scale->height;
- mf->code = MEDIA_BUS_FMT_UYVY8_2X8;
- mf->colorspace = V4L2_COLORSPACE_SMPTE170M;
- mf->field = V4L2_FIELD_INTERLACED_BT;
-
- return 0;
-}
-
-static int tw9910_s_fmt(struct v4l2_subdev *sd,
- struct v4l2_mbus_framefmt *mf)
-{
- u32 width = mf->width, height = mf->height;
- int ret;
-
- WARN_ON(mf->field != V4L2_FIELD_ANY &&
- mf->field != V4L2_FIELD_INTERLACED_BT);
-
- /*
- * check color format
- */
- if (mf->code != MEDIA_BUS_FMT_UYVY8_2X8)
- return -EINVAL;
-
- mf->colorspace = V4L2_COLORSPACE_SMPTE170M;
-
- ret = tw9910_set_frame(sd, &width, &height);
- if (!ret) {
- mf->width = width;
- mf->height = height;
- }
- return ret;
-}
-
-static int tw9910_set_fmt(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_format *format)
-{
- struct v4l2_mbus_framefmt *mf = &format->format;
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct tw9910_priv *priv = to_tw9910(client);
- const struct tw9910_scale_ctrl *scale;
-
- if (format->pad)
- return -EINVAL;
-
- if (V4L2_FIELD_ANY == mf->field) {
- mf->field = V4L2_FIELD_INTERLACED_BT;
- } else if (V4L2_FIELD_INTERLACED_BT != mf->field) {
- dev_err(&client->dev, "Field type %d invalid.\n", mf->field);
- return -EINVAL;
- }
-
- mf->code = MEDIA_BUS_FMT_UYVY8_2X8;
- mf->colorspace = V4L2_COLORSPACE_SMPTE170M;
-
- /*
- * select suitable norm
- */
- scale = tw9910_select_norm(priv->norm, mf->width, mf->height);
- if (!scale)
- return -EINVAL;
-
- mf->width = scale->width;
- mf->height = scale->height;
-
- if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE)
- return tw9910_s_fmt(sd, mf);
- cfg->try_fmt = *mf;
- return 0;
-}
-
-static int tw9910_video_probe(struct i2c_client *client)
-{
- struct tw9910_priv *priv = to_tw9910(client);
- s32 id;
- int ret;
-
- /*
- * tw9910 only use 8 or 16 bit bus width
- */
- if (SOCAM_DATAWIDTH_16 != priv->info->buswidth &&
- SOCAM_DATAWIDTH_8 != priv->info->buswidth) {
- dev_err(&client->dev, "bus width error\n");
- return -ENODEV;
- }
-
- ret = tw9910_s_power(&priv->subdev, 1);
- if (ret < 0)
- return ret;
-
- /*
- * check and show Product ID
- * So far only revisions 0 and 1 have been seen
- */
- id = i2c_smbus_read_byte_data(client, ID);
- priv->revision = GET_REV(id);
- id = GET_ID(id);
-
- if (0x0B != id ||
- 0x01 < priv->revision) {
- dev_err(&client->dev,
- "Product ID error %x:%x\n",
- id, priv->revision);
- ret = -ENODEV;
- goto done;
- }
-
- dev_info(&client->dev,
- "tw9910 Product ID %0x:%0x\n", id, priv->revision);
-
- priv->norm = V4L2_STD_NTSC;
- priv->scale = &tw9910_ntsc_scales[0];
-
-done:
- tw9910_s_power(&priv->subdev, 0);
- return ret;
-}
-
-static const struct v4l2_subdev_core_ops tw9910_subdev_core_ops = {
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .g_register = tw9910_g_register,
- .s_register = tw9910_s_register,
-#endif
- .s_power = tw9910_s_power,
-};
-
-static int tw9910_enum_mbus_code(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_mbus_code_enum *code)
-{
- if (code->pad || code->index)
- return -EINVAL;
-
- code->code = MEDIA_BUS_FMT_UYVY8_2X8;
- return 0;
-}
-
-static int tw9910_g_mbus_config(struct v4l2_subdev *sd,
- struct v4l2_mbus_config *cfg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
-
- cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER |
- V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW |
- V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW |
- V4L2_MBUS_DATA_ACTIVE_HIGH;
- cfg->type = V4L2_MBUS_PARALLEL;
- cfg->flags = soc_camera_apply_board_flags(ssdd, cfg);
-
- return 0;
-}
-
-static int tw9910_s_mbus_config(struct v4l2_subdev *sd,
- const struct v4l2_mbus_config *cfg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
- u8 val = VSSL_VVALID | HSSL_DVALID;
- unsigned long flags = soc_camera_apply_board_flags(ssdd, cfg);
-
- /*
- * set OUTCTR1
- *
- * We use VVALID and DVALID signals to control VSYNC and HSYNC
- * outputs, in this mode their polarity is inverted.
- */
- if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
- val |= HSP_HI;
-
- if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
- val |= VSP_HI;
-
- return i2c_smbus_write_byte_data(client, OUTCTR1, val);
-}
-
-static int tw9910_g_tvnorms(struct v4l2_subdev *sd, v4l2_std_id *norm)
-{
- *norm = V4L2_STD_NTSC | V4L2_STD_PAL;
- return 0;
-}
-
-static const struct v4l2_subdev_video_ops tw9910_subdev_video_ops = {
- .s_std = tw9910_s_std,
- .g_std = tw9910_g_std,
- .s_stream = tw9910_s_stream,
- .g_mbus_config = tw9910_g_mbus_config,
- .s_mbus_config = tw9910_s_mbus_config,
- .g_tvnorms = tw9910_g_tvnorms,
-};
-
-static const struct v4l2_subdev_pad_ops tw9910_subdev_pad_ops = {
- .enum_mbus_code = tw9910_enum_mbus_code,
- .get_selection = tw9910_get_selection,
- .get_fmt = tw9910_get_fmt,
- .set_fmt = tw9910_set_fmt,
-};
-
-static const struct v4l2_subdev_ops tw9910_subdev_ops = {
- .core = &tw9910_subdev_core_ops,
- .video = &tw9910_subdev_video_ops,
- .pad = &tw9910_subdev_pad_ops,
-};
-
-/*
- * i2c_driver function
- */
-
-static int tw9910_probe(struct i2c_client *client,
- const struct i2c_device_id *did)
-
-{
- struct tw9910_priv *priv;
- struct tw9910_video_info *info;
- struct i2c_adapter *adapter =
- to_i2c_adapter(client->dev.parent);
- struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
- int ret;
-
- if (!ssdd || !ssdd->drv_priv) {
- dev_err(&client->dev, "TW9910: missing platform data!\n");
- return -EINVAL;
- }
-
- info = ssdd->drv_priv;
-
- if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
- dev_err(&client->dev,
- "I2C-Adapter doesn't support I2C_FUNC_SMBUS_BYTE_DATA\n");
- return -EIO;
- }
-
- priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
-
- priv->info = info;
-
- v4l2_i2c_subdev_init(&priv->subdev, client, &tw9910_subdev_ops);
-
- priv->clk = v4l2_clk_get(&client->dev, "mclk");
- if (IS_ERR(priv->clk))
- return PTR_ERR(priv->clk);
-
- ret = tw9910_video_probe(client);
- if (ret < 0)
- v4l2_clk_put(priv->clk);
-
- return ret;
-}
-
-static int tw9910_remove(struct i2c_client *client)
-{
- struct tw9910_priv *priv = to_tw9910(client);
- v4l2_clk_put(priv->clk);
- return 0;
-}
-
-static const struct i2c_device_id tw9910_id[] = {
- { "tw9910", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, tw9910_id);
-
-static struct i2c_driver tw9910_i2c_driver = {
- .driver = {
- .name = "tw9910",
- },
- .probe = tw9910_probe,
- .remove = tw9910_remove,
- .id_table = tw9910_id,
-};
-
-module_i2c_driver(tw9910_i2c_driver);
-
-MODULE_DESCRIPTION("SoC Camera driver for tw9910");
-MODULE_AUTHOR("Kuninori Morimoto");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/i2c/tda1997x.c b/drivers/media/i2c/tda1997x.c
index e8613e364403..a62ede096636 100644
--- a/drivers/media/i2c/tda1997x.c
+++ b/drivers/media/i2c/tda1997x.c
@@ -1884,6 +1884,10 @@ static int tda1997x_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
for (i = 0; i < 128; i++)
io_write(sd, REG_EDID_IN_BYTE128 + i, edid->edid[i+128]);
+ /* store state */
+ memcpy(state->edid.edid, edid->edid, 256);
+ state->edid.blocks = edid->blocks;
+
tda1997x_enable_edid(sd);
return 0;
diff --git a/drivers/media/i2c/tda1997x_regs.h b/drivers/media/i2c/tda1997x_regs.h
index f55dfc423a86..ecf87534613b 100644
--- a/drivers/media/i2c/tda1997x_regs.h
+++ b/drivers/media/i2c/tda1997x_regs.h
@@ -596,7 +596,7 @@
#define RESET_AUDIO BIT(0) /* Reset Audio FIFO control */
/* HDCP_BCAPS bits */
-#define HDCP_HDMI BIT(7) /* HDCP suports HDMI (vs DVI only) */
+#define HDCP_HDMI BIT(7) /* HDCP supports HDMI (vs DVI only) */
#define HDCP_REPEATER BIT(6) /* HDCP supports repeater function */
#define HDCP_READY BIT(5) /* set by repeater function */
#define HDCP_FAST BIT(4) /* Up to 400kHz */
diff --git a/drivers/media/i2c/tda9840.c b/drivers/media/i2c/tda9840.c
index 0dd6ff3e6201..6ba53f3a6dd2 100644
--- a/drivers/media/i2c/tda9840.c
+++ b/drivers/media/i2c/tda9840.c
@@ -7,7 +7,7 @@
The tda9840 is a stereo/dual sound processor with digital
identification. It can be found at address 0x84 on the i2c-bus.
- For detailed informations download the specifications directly
+ For detailed information download the specifications directly
from SGS Thomson at http://www.st.com
This program is free software; you can redistribute it and/or modify
diff --git a/drivers/media/i2c/tea6415c.c b/drivers/media/i2c/tea6415c.c
index 084bd75bb32c..965c6ccc4fee 100644
--- a/drivers/media/i2c/tea6415c.c
+++ b/drivers/media/i2c/tea6415c.c
@@ -9,7 +9,7 @@
It is cascadable, i.e. it can be found at the addresses
0x86 and 0x06 on the i2c-bus.
- For detailed informations download the specifications directly
+ For detailed information download the specifications directly
from SGS Thomson at http://www.st.com
This program is free software; you can redistribute it and/or modify
diff --git a/drivers/media/i2c/tea6420.c b/drivers/media/i2c/tea6420.c
index b7f4e58f3624..2701a4c9734d 100644
--- a/drivers/media/i2c/tea6420.c
+++ b/drivers/media/i2c/tea6420.c
@@ -9,7 +9,7 @@
It is cascadable, i.e. it can be found at the addresses 0x98
and 0x9a on the i2c-bus.
- For detailed informations download the specifications directly
+ For detailed information download the specifications directly
from SGS Thomson at http://www.st.com
This program is free software; you can redistribute it and/or modify
diff --git a/drivers/media/i2c/tvaudio.c b/drivers/media/i2c/tvaudio.c
index af2da977a685..e6796e94dadf 100644
--- a/drivers/media/i2c/tvaudio.c
+++ b/drivers/media/i2c/tvaudio.c
@@ -538,7 +538,7 @@ static int tda9840_checkit(struct CHIPSTATE *chip)
#define TDA9855_INT 0 /* Selects inputs LOR and LOL. (internal) */
/* Unique to TDA9850: */
-/* lower 4 bits contol SAP noise threshold, over which SAP turns off
+/* lower 4 bits control SAP noise threshold, over which SAP turns off
* set to values of 0x00 through 0x0f for SAP1 through SAP16 */
@@ -546,7 +546,7 @@ static int tda9840_checkit(struct CHIPSTATE *chip)
/* Common to TDA9855 and TDA9850: */
#define TDA985x_SAP 3<<6 /* Selects SAP output, mute if not received */
#define TDA985x_MONOSAP 2<<6 /* Selects Mono on left, SAP on right */
-#define TDA985x_STEREO 1<<6 /* Selects Stereo ouput, mono if not received */
+#define TDA985x_STEREO 1<<6 /* Selects Stereo output, mono if not received */
#define TDA985x_MONO 0 /* Forces Mono output */
#define TDA985x_LMU 1<<3 /* Mute (LOR/LOL for 9855, OUTL/OUTR for 9850) */
diff --git a/drivers/media/i2c/tvp514x.c b/drivers/media/i2c/tvp514x.c
index 1cc83cb934e2..3ada3bb27402 100644
--- a/drivers/media/i2c/tvp514x.c
+++ b/drivers/media/i2c/tvp514x.c
@@ -67,7 +67,7 @@ enum tvp514x_std {
};
/**
- * struct tvp514x_std_info - Structure to store standard informations
+ * struct tvp514x_std_info - Structure to store standard information
* @width: Line width in pixels
* @height:Number of active lines
* @video_std: Value to write in REG_VIDEO_STD register
diff --git a/drivers/media/i2c/tw9910.c b/drivers/media/i2c/tw9910.c
index a54548cc4285..4d7cd736b930 100644
--- a/drivers/media/i2c/tw9910.c
+++ b/drivers/media/i2c/tw9910.c
@@ -584,6 +584,14 @@ static int tw9910_s_register(struct v4l2_subdev *sd,
}
#endif
+static void tw9910_set_gpio_value(struct gpio_desc *desc, int value)
+{
+ if (desc) {
+ gpiod_set_value(desc, value);
+ usleep_range(500, 1000);
+ }
+}
+
static int tw9910_power_on(struct tw9910_priv *priv)
{
struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev);
@@ -595,10 +603,7 @@ static int tw9910_power_on(struct tw9910_priv *priv)
return ret;
}
- if (priv->pdn_gpio) {
- gpiod_set_value(priv->pdn_gpio, 0);
- usleep_range(500, 1000);
- }
+ tw9910_set_gpio_value(priv->pdn_gpio, 0);
/*
* FIXME: The reset signal is connected to a shared GPIO on some
@@ -610,14 +615,14 @@ static int tw9910_power_on(struct tw9910_priv *priv)
GPIOD_OUT_LOW);
if (IS_ERR(priv->rstb_gpio)) {
dev_info(&client->dev, "Unable to get GPIO \"rstb\"");
+ clk_disable_unprepare(priv->clk);
+ tw9910_set_gpio_value(priv->pdn_gpio, 1);
return PTR_ERR(priv->rstb_gpio);
}
if (priv->rstb_gpio) {
- gpiod_set_value(priv->rstb_gpio, 1);
- usleep_range(500, 1000);
- gpiod_set_value(priv->rstb_gpio, 0);
- usleep_range(500, 1000);
+ tw9910_set_gpio_value(priv->rstb_gpio, 1);
+ tw9910_set_gpio_value(priv->rstb_gpio, 0);
gpiod_put(priv->rstb_gpio);
}
@@ -628,11 +633,7 @@ static int tw9910_power_on(struct tw9910_priv *priv)
static int tw9910_power_off(struct tw9910_priv *priv)
{
clk_disable_unprepare(priv->clk);
-
- if (priv->pdn_gpio) {
- gpiod_set_value(priv->pdn_gpio, 1);
- usleep_range(500, 1000);
- }
+ tw9910_set_gpio_value(priv->pdn_gpio, 1);
return 0;
}
@@ -1000,7 +1001,7 @@ static int tw9910_remove(struct i2c_client *client)
if (priv->pdn_gpio)
gpiod_put(priv->pdn_gpio);
clk_put(priv->clk);
- v4l2_device_unregister_subdev(&priv->subdev);
+ v4l2_async_unregister_subdev(&priv->subdev);
return 0;
}
diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c
index 01dcf179f203..abd3152df7d0 100644
--- a/drivers/media/i2c/video-i2c.c
+++ b/drivers/media/i2c/video-i2c.c
@@ -6,6 +6,7 @@
*
* Supported:
* - Panasonic AMG88xx Grid-Eye Sensors
+ * - Melexis MLX90640 Thermal Cameras
*/
#include <linux/delay.h>
@@ -18,6 +19,7 @@
#include <linux/mutex.h>
#include <linux/of_device.h>
#include <linux/pm_runtime.h>
+#include <linux/nvmem-provider.h>
#include <linux/regmap.h>
#include <linux/sched.h>
#include <linux/slab.h>
@@ -66,12 +68,26 @@ static const struct v4l2_frmsize_discrete amg88xx_size = {
.height = 8,
};
+static const struct v4l2_fmtdesc mlx90640_format = {
+ .pixelformat = V4L2_PIX_FMT_Y16_BE,
+};
+
+static const struct v4l2_frmsize_discrete mlx90640_size = {
+ .width = 32,
+ .height = 26, /* 24 lines of pixel data + 2 lines of processing data */
+};
+
static const struct regmap_config amg88xx_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = 0xff
};
+static const struct regmap_config mlx90640_regmap_config = {
+ .reg_bits = 16,
+ .val_bits = 16,
+};
+
struct video_i2c_chip {
/* video dimensions */
const struct v4l2_fmtdesc *format;
@@ -88,6 +104,7 @@ struct video_i2c_chip {
unsigned int bpp;
const struct regmap_config *regmap_config;
+ struct nvmem_config *nvmem_config;
/* setup function */
int (*setup)(struct video_i2c_data *data);
@@ -102,6 +119,22 @@ struct video_i2c_chip {
int (*hwmon_init)(struct video_i2c_data *data);
};
+static int mlx90640_nvram_read(void *priv, unsigned int offset, void *val,
+ size_t bytes)
+{
+ struct video_i2c_data *data = priv;
+
+ return regmap_bulk_read(data->regmap, 0x2400 + offset, val, bytes);
+}
+
+static struct nvmem_config mlx90640_nvram_config = {
+ .name = "mlx90640_nvram",
+ .word_size = 2,
+ .stride = 1,
+ .size = 1664,
+ .reg_read = mlx90640_nvram_read,
+};
+
/* Power control register */
#define AMG88XX_REG_PCTL 0x00
#define AMG88XX_PCTL_NORMAL 0x00
@@ -122,12 +155,23 @@ struct video_i2c_chip {
/* Temperature register */
#define AMG88XX_REG_T01L 0x80
+/* Control register */
+#define MLX90640_REG_CTL1 0x800d
+#define MLX90640_REG_CTL1_MASK 0x0380
+#define MLX90640_REG_CTL1_MASK_SHIFT 7
+
static int amg88xx_xfer(struct video_i2c_data *data, char *buf)
{
return regmap_bulk_read(data->regmap, AMG88XX_REG_T01L, buf,
data->chip->buffer_size);
}
+static int mlx90640_xfer(struct video_i2c_data *data, char *buf)
+{
+ return regmap_bulk_read(data->regmap, 0x400, buf,
+ data->chip->buffer_size);
+}
+
static int amg88xx_setup(struct video_i2c_data *data)
{
unsigned int mask = AMG88XX_FPSC_1FPS;
@@ -141,6 +185,27 @@ static int amg88xx_setup(struct video_i2c_data *data)
return regmap_update_bits(data->regmap, AMG88XX_REG_FPSC, mask, val);
}
+static int mlx90640_setup(struct video_i2c_data *data)
+{
+ unsigned int n, idx;
+
+ for (n = 0; n < data->chip->num_frame_intervals - 1; n++) {
+ if (data->frame_interval.numerator
+ != data->chip->frame_intervals[n].numerator)
+ continue;
+
+ if (data->frame_interval.denominator
+ == data->chip->frame_intervals[n].denominator)
+ break;
+ }
+
+ idx = data->chip->num_frame_intervals - n - 1;
+
+ return regmap_update_bits(data->regmap, MLX90640_REG_CTL1,
+ MLX90640_REG_CTL1_MASK,
+ idx << MLX90640_REG_CTL1_MASK_SHIFT);
+}
+
static int amg88xx_set_power_on(struct video_i2c_data *data)
{
int ret;
@@ -274,13 +339,27 @@ static int amg88xx_hwmon_init(struct video_i2c_data *data)
#define amg88xx_hwmon_init NULL
#endif
-#define AMG88XX 0
+enum {
+ AMG88XX,
+ MLX90640,
+};
static const struct v4l2_fract amg88xx_frame_intervals[] = {
{ 1, 10 },
{ 1, 1 },
};
+static const struct v4l2_fract mlx90640_frame_intervals[] = {
+ { 1, 64 },
+ { 1, 32 },
+ { 1, 16 },
+ { 1, 8 },
+ { 1, 4 },
+ { 1, 2 },
+ { 1, 1 },
+ { 2, 1 },
+};
+
static const struct video_i2c_chip video_i2c_chip[] = {
[AMG88XX] = {
.size = &amg88xx_size,
@@ -295,6 +374,18 @@ static const struct video_i2c_chip video_i2c_chip[] = {
.set_power = amg88xx_set_power,
.hwmon_init = amg88xx_hwmon_init,
},
+ [MLX90640] = {
+ .size = &mlx90640_size,
+ .format = &mlx90640_format,
+ .frame_intervals = mlx90640_frame_intervals,
+ .num_frame_intervals = ARRAY_SIZE(mlx90640_frame_intervals),
+ .buffer_size = 1664,
+ .bpp = 16,
+ .regmap_config = &mlx90640_regmap_config,
+ .nvmem_config = &mlx90640_nvram_config,
+ .setup = mlx90640_setup,
+ .xfer = mlx90640_xfer,
+ },
};
static const struct v4l2_file_operations video_i2c_fops = {
@@ -756,6 +847,21 @@ static int video_i2c_probe(struct i2c_client *client,
}
}
+ if (data->chip->nvmem_config) {
+ struct nvmem_config *config = data->chip->nvmem_config;
+ struct nvmem_device *device;
+
+ config->priv = data;
+ config->dev = &client->dev;
+
+ device = devm_nvmem_register(&client->dev, config);
+
+ if (IS_ERR(device)) {
+ dev_warn(&client->dev,
+ "failed to register nvmem device\n");
+ }
+ }
+
ret = video_register_device(&data->vdev, VFL_TYPE_GRABBER, -1);
if (ret < 0)
goto error_pm_disable;
@@ -835,12 +941,14 @@ static const struct dev_pm_ops video_i2c_pm_ops = {
static const struct i2c_device_id video_i2c_id_table[] = {
{ "amg88xx", AMG88XX },
+ { "mlx90640", MLX90640 },
{}
};
MODULE_DEVICE_TABLE(i2c, video_i2c_id_table);
static const struct of_device_id video_i2c_of_match[] = {
{ .compatible = "panasonic,amg88xx", .data = &video_i2c_chip[AMG88XX] },
+ { .compatible = "melexis,mlx90640", .data = &video_i2c_chip[MLX90640] },
{}
};
MODULE_DEVICE_TABLE(of, video_i2c_of_match);
diff --git a/drivers/media/media-request.c b/drivers/media/media-request.c
index c71a34ae6383..eec2e2b2f6ec 100644
--- a/drivers/media/media-request.c
+++ b/drivers/media/media-request.c
@@ -100,6 +100,7 @@ static __poll_t media_request_poll(struct file *filp,
if (!(poll_requested_events(wait) & EPOLLPRI))
return 0;
+ poll_wait(filp, &req->poll_wait, wait);
spin_lock_irqsave(&req->lock, flags);
if (req->state == MEDIA_REQUEST_STATE_COMPLETE) {
ret = EPOLLPRI;
@@ -110,8 +111,6 @@ static __poll_t media_request_poll(struct file *filp,
goto unlock;
}
- poll_wait(filp, &req->poll_wait, wait);
-
unlock:
spin_unlock_irqrestore(&req->lock, flags);
return ret;
diff --git a/drivers/media/pci/bt8xx/bttv-audio-hook.c b/drivers/media/pci/bt8xx/bttv-audio-hook.c
index 346fc7f58839..8febe7358a8f 100644
--- a/drivers/media/pci/bt8xx/bttv-audio-hook.c
+++ b/drivers/media/pci/bt8xx/bttv-audio-hook.c
@@ -1,5 +1,5 @@
/*
- * Handlers for board audio hooks, splitted from bttv-cards
+ * Handlers for board audio hooks, split from bttv-cards
*
* Copyright (c) 2006 Mauro Carvalho Chehab <mchehab@kernel.org>
* This code is placed under the terms of the GNU General Public License
diff --git a/drivers/media/pci/bt8xx/bttv-audio-hook.h b/drivers/media/pci/bt8xx/bttv-audio-hook.h
index be16a537a03a..c61b9ac4f4e3 100644
--- a/drivers/media/pci/bt8xx/bttv-audio-hook.h
+++ b/drivers/media/pci/bt8xx/bttv-audio-hook.h
@@ -1,5 +1,5 @@
/*
- * Handlers for board audio hooks, splitted from bttv-cards
+ * Handlers for board audio hooks, split from bttv-cards
*
* Copyright (c) 2006 Mauro Carvalho Chehab <mchehab@kernel.org>
* This code is placed under the terms of the GNU General Public License
diff --git a/drivers/media/pci/bt8xx/bttv-cards.c b/drivers/media/pci/bt8xx/bttv-cards.c
index 2616243b2c49..b1c6f3e9c3d0 100644
--- a/drivers/media/pci/bt8xx/bttv-cards.c
+++ b/drivers/media/pci/bt8xx/bttv-cards.c
@@ -2,7 +2,7 @@
bttv-cards.c
- this file has configuration informations - card-specific stuff
+ this file has configuration information - card-specific stuff
like the big tvcards array for the most part
Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de)
@@ -1391,7 +1391,7 @@ struct tvcard bttv_tvcards[] = {
.gpiomux = {0x947fff, 0x987fff,0x947fff,0x947fff },
.gpiomute = 0x947fff,
/* tvtuner, radio, external,internal, mute, stereo
- * tuner, Composit, SVid, Composit-on-Svid-adapter */
+ * tuner, Composite, SVid, Composite-on-Svid-adapter */
.muxsel = MUXSEL(2, 3, 0, 1),
.tuner_type = TUNER_MT2032,
.tuner_addr = ADDR_UNSET,
@@ -1411,7 +1411,7 @@ struct tvcard bttv_tvcards[] = {
.gpiomux = {0x947fff, 0x987fff,0x947fff,0x947fff },
.gpiomute = 0x947fff,
/* tvtuner, radio, external,internal, mute, stereo
- * tuner, Composit, SVid, Composit-on-Svid-adapter */
+ * tuner, Composite, SVid, Composite-on-Svid-adapter */
.muxsel = MUXSEL(2, 3, 0, 1),
.tuner_type = TUNER_MT2032,
.tuner_addr = ADDR_UNSET,
@@ -4180,7 +4180,7 @@ static void init_PXC200(struct bttv *btv)
bttv_I2CWrite(btv,0x5E,0,0x80,1);
/* Initialise 12C508 PIC */
- /* The I2CWrite and I2CRead commmands are actually to the
+ /* The I2CWrite and I2CRead commands are actually to the
* same chips - but the R/W bit is included in the address
* argument so the numbers are different */
@@ -4289,7 +4289,7 @@ init_RTV24 (struct bttv *btv)
/* ----------------------------------------------------------------------- */
/*
* The PCI-8604PW contains a CPLD, probably an ispMACH 4A, that filters
- * the PCI REQ signals comming from the four BT878 chips. After power
+ * the PCI REQ signals coming from the four BT878 chips. After power
* up, the CPLD does not forward requests to the bus, which prevents
* the BT878 from fetching RISC instructions from memory. While the
* CPLD is connected to most of the GPIOs of PCI device 0xD, only
@@ -4405,7 +4405,7 @@ static void rv605_muxsel(struct bttv *btv, unsigned int input)
gpio_bits(0x07f, muxgpio[input]);
- /* reset all conections */
+ /* reset all connections */
gpio_bits(0x200,0x200);
mdelay(1);
gpio_bits(0x200,0x000);
diff --git a/drivers/media/pci/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c
index d09785fd37a8..b7150648081d 100644
--- a/drivers/media/pci/bt8xx/bttv-driver.c
+++ b/drivers/media/pci/bt8xx/bttv-driver.c
@@ -2435,7 +2435,7 @@ static int bttv_s_fmt_vid_cap(struct file *file, void *priv,
f->fmt.pix.field = field;
- /* update our state informations */
+ /* update our state information */
fh->fmt = fmt;
fh->cap.field = f->fmt.pix.field;
fh->cap.last = V4L2_FIELD_NONE;
@@ -3064,7 +3064,7 @@ static int bttv_open(struct file *file)
V4L2 apps we now reset the cropping parameters as seen through
this fh, which is to say VIDIOC_G_SELECTION and scaling limit checks
will use btv->crop[0], the default cropping parameters for the
- current video standard, and VIDIOC_S_FMT will not implicitely
+ current video standard, and VIDIOC_S_FMT will not implicitly
change the cropping parameters until VIDIOC_S_SELECTION has been
called. */
fh->do_crop = !reset_crop; /* module parameter */
@@ -3600,9 +3600,7 @@ static void
bttv_irq_wakeup_video(struct bttv *btv, struct bttv_buffer_set *wakeup,
struct bttv_buffer_set *curr, unsigned int state)
{
- struct timeval ts;
-
- v4l2_get_timestamp(&ts);
+ u64 ts = ktime_get_ns();
if (wakeup->top == wakeup->bottom) {
if (NULL != wakeup->top && curr->top != wakeup->top) {
@@ -3643,7 +3641,7 @@ bttv_irq_wakeup_vbi(struct bttv *btv, struct bttv_buffer *wakeup,
if (NULL == wakeup)
return;
- v4l2_get_timestamp(&wakeup->vb.ts);
+ wakeup->vb.ts = ktime_get_ns();
wakeup->vb.field_count = btv->field_count;
wakeup->vb.state = state;
wake_up(&wakeup->vb.done);
@@ -3713,7 +3711,7 @@ bttv_irq_wakeup_top(struct bttv *btv)
btv->curr.top = NULL;
bttv_risc_hook(btv, RISC_SLOT_O_FIELD, NULL, 0);
- v4l2_get_timestamp(&wakeup->vb.ts);
+ wakeup->vb.ts = ktime_get_ns();
wakeup->vb.field_count = btv->field_count;
wakeup->vb.state = VIDEOBUF_DONE;
wake_up(&wakeup->vb.done);
diff --git a/drivers/media/pci/bt8xx/bttv-risc.c b/drivers/media/pci/bt8xx/bttv-risc.c
index 74aff6877d9c..6b3c73674a3c 100644
--- a/drivers/media/pci/bt8xx/bttv-risc.c
+++ b/drivers/media/pci/bt8xx/bttv-risc.c
@@ -93,7 +93,7 @@ bttv_risc_packed(struct bttv *btv, struct btcx_riscmem *risc,
*(rp++)=cpu_to_le32(sg_dma_address(sg)+offset);
offset+=bpl;
} else {
- /* scanline needs to be splitted */
+ /* scanline needs to be split */
todo = bpl;
*(rp++)=cpu_to_le32(BT848_RISC_WRITE|BT848_RISC_SOL|
(sg_dma_len(sg)-offset));
diff --git a/drivers/media/pci/bt8xx/bttv.h b/drivers/media/pci/bt8xx/bttv.h
index a27384adadd2..d24b9ef9f59f 100644
--- a/drivers/media/pci/bt8xx/bttv.h
+++ b/drivers/media/pci/bt8xx/bttv.h
@@ -288,7 +288,7 @@ extern void bttv_init_card1(struct bttv *btv);
extern void bttv_init_card2(struct bttv *btv);
extern void bttv_init_tuner(struct bttv *btv);
-/* card-specific funtions */
+/* card-specific functions */
extern void tea5757_set_freq(struct bttv *btv, unsigned short freq);
extern u32 bttv_tda9880_setnorm(struct bttv *btv, u32 gpiobits);
diff --git a/drivers/media/pci/bt8xx/dst.c b/drivers/media/pci/bt8xx/dst.c
index b98de2a22f78..c94318c71a0c 100644
--- a/drivers/media/pci/bt8xx/dst.c
+++ b/drivers/media/pci/bt8xx/dst.c
@@ -1295,15 +1295,15 @@ static int dst_get_signal(struct dst_state *state)
static int dst_tone_power_cmd(struct dst_state *state)
{
- u8 paket[8] = { 0x00, 0x09, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00 };
+ u8 packet[8] = { 0x00, 0x09, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00 };
if (state->dst_type != DST_TYPE_IS_SAT)
return -EOPNOTSUPP;
- paket[4] = state->tx_tuna[4];
- paket[2] = state->tx_tuna[2];
- paket[3] = state->tx_tuna[3];
- paket[7] = dst_check_sum (paket, 7);
- return dst_command(state, paket, 8);
+ packet[4] = state->tx_tuna[4];
+ packet[2] = state->tx_tuna[2];
+ packet[3] = state->tx_tuna[3];
+ packet[7] = dst_check_sum (packet, 7);
+ return dst_command(state, packet, 8);
}
static int dst_get_tuna(struct dst_state *state)
@@ -1429,18 +1429,18 @@ error:
static int dst_set_diseqc(struct dvb_frontend *fe, struct dvb_diseqc_master_cmd *cmd)
{
struct dst_state *state = fe->demodulator_priv;
- u8 paket[8] = { 0x00, 0x08, 0x04, 0xe0, 0x10, 0x38, 0xf0, 0xec };
+ u8 packet[8] = { 0x00, 0x08, 0x04, 0xe0, 0x10, 0x38, 0xf0, 0xec };
if (state->dst_type != DST_TYPE_IS_SAT)
return -EOPNOTSUPP;
if (cmd->msg_len > 0 && cmd->msg_len < 5)
- memcpy(&paket[3], cmd->msg, cmd->msg_len);
+ memcpy(&packet[3], cmd->msg, cmd->msg_len);
else if (cmd->msg_len == 5 && state->dst_hw_cap & DST_TYPE_HAS_DISEQC5)
- memcpy(&paket[2], cmd->msg, cmd->msg_len);
+ memcpy(&packet[2], cmd->msg, cmd->msg_len);
else
return -EINVAL;
- paket[7] = dst_check_sum(&paket[0], 7);
- return dst_command(state, paket, 8);
+ packet[7] = dst_check_sum(&packet[0], 7);
+ return dst_command(state, packet, 8);
}
static int dst_set_voltage(struct dvb_frontend *fe, enum fe_sec_voltage voltage)
diff --git a/drivers/media/pci/cobalt/cobalt-v4l2.c b/drivers/media/pci/cobalt/cobalt-v4l2.c
index c088de551081..f9fa3a7c3b8f 100644
--- a/drivers/media/pci/cobalt/cobalt-v4l2.c
+++ b/drivers/media/pci/cobalt/cobalt-v4l2.c
@@ -375,7 +375,7 @@ static void cobalt_dma_stop_streaming(struct cobalt_stream *s)
}
spin_unlock_irqrestore(&s->irqlock, flags);
- /* Wait 100 milisecond for DMA to finish, abort on timeout. */
+ /* Wait 100 millisecond for DMA to finish, abort on timeout. */
if (!wait_event_timeout(s->q.done_wq, is_dma_done(s),
msecs_to_jiffies(timeout_msec))) {
omni_sg_dma_abort_channel(s);
diff --git a/drivers/media/pci/cx18/cx18-cards.h b/drivers/media/pci/cx18/cx18-cards.h
index 02d0fb703a41..c563cc2358ba 100644
--- a/drivers/media/pci/cx18/cx18-cards.h
+++ b/drivers/media/pci/cx18/cx18-cards.h
@@ -83,7 +83,7 @@ struct cx18_gpio_i2c_slave_reset {
u32 active_hi_mask; /* GPIO outputs that reset i2c chips when high */
int msecs_asserted; /* time period reset must remain asserted */
int msecs_recovery; /* time after deassert for chips to be ready */
- u32 ir_reset_mask; /* GPIO to reset the Zilog Z8F0811 IR contoller */
+ u32 ir_reset_mask; /* GPIO to reset the Zilog Z8F0811 IR controller */
};
struct cx18_gpio_audio_input { /* select tuner/line in input */
diff --git a/drivers/media/pci/cx18/cx18-dvb.c b/drivers/media/pci/cx18/cx18-dvb.c
index a3a7f7065349..61452c50a9c3 100644
--- a/drivers/media/pci/cx18/cx18-dvb.c
+++ b/drivers/media/pci/cx18/cx18-dvb.c
@@ -120,8 +120,8 @@ static struct zl10353_config leadtek_dvr3100h_demod = {
/*
* Due to
*
- * 1. an absence of information on how to prgram the MT352
- * 2. the Linux mt352 module pushing MT352 initialzation off onto us here
+ * 1. an absence of information on how to program the MT352
+ * 2. the Linux mt352 module pushing MT352 initialization off onto us here
*
* We have to use an init sequence that *you* must extract from the Windows
* driver (yuanrap.sys) and which we load as a firmware.
@@ -458,7 +458,7 @@ void cx18_dvb_unregister(struct cx18_stream *stream)
dvb_unregister_adapter(dvb_adapter);
}
-/* All the DVB attach calls go here, this function get's modified
+/* All the DVB attach calls go here, this function gets modified
* for each new card. cx18_dvb_start_feed() will also need changes.
*/
static int dvb_register(struct cx18_stream *stream)
diff --git a/drivers/media/pci/cx18/cx18-fileops.c b/drivers/media/pci/cx18/cx18-fileops.c
index a3f44e30f821..f778965a2eb8 100644
--- a/drivers/media/pci/cx18/cx18-fileops.c
+++ b/drivers/media/pci/cx18/cx18-fileops.c
@@ -403,7 +403,7 @@ static size_t cx18_copy_mdl_to_user(struct cx18_stream *s,
tot_written += rc;
if (stop || /* Forced stopping point for VBI insertion */
- tot_written >= ucount || /* Reader request statisfied */
+ tot_written >= ucount || /* Reader request satisfied */
mdl->curr_buf->readpos < mdl->curr_buf->bytesused ||
mdl->readpos >= mdl->bytesused) /* MDL buffers drained */
break;
diff --git a/drivers/media/pci/cx18/cx18-io.h b/drivers/media/pci/cx18/cx18-io.h
index a3c96fb5d28d..da7871fdb56d 100644
--- a/drivers/media/pci/cx18/cx18-io.h
+++ b/drivers/media/pci/cx18/cx18-io.h
@@ -23,7 +23,7 @@
/*
* Readback and retry of MMIO access for reliability:
* The concept was suggested by Steve Toth <stoth@linuxtv.org>.
- * The implmentation is the fault of Andy Walls <awalls@md.metrocast.net>.
+ * The implementation is the fault of Andy Walls <awalls@md.metrocast.net>.
*
* *write* functions are implied to retry the mmio unless suffixed with _noretry
* *read* functions never retry the mmio (it never helps to do so)
diff --git a/drivers/media/pci/cx18/cx18-mailbox.c b/drivers/media/pci/cx18/cx18-mailbox.c
index f66dd63e1994..0ffd2196a980 100644
--- a/drivers/media/pci/cx18/cx18-mailbox.c
+++ b/drivers/media/pci/cx18/cx18-mailbox.c
@@ -197,7 +197,7 @@ static void cx18_mdl_send_to_videobuf(struct cx18_stream *s,
}
if (dispatch) {
- v4l2_get_timestamp(&vb_buf->vb.ts);
+ vb_buf->vb.ts = ktime_get_ns();
list_del(&vb_buf->vb.queue);
vb_buf->vb.state = VIDEOBUF_DONE;
wake_up(&vb_buf->vb.done);
diff --git a/drivers/media/pci/cx18/cx18-vbi.c b/drivers/media/pci/cx18/cx18-vbi.c
index 81f1e27436fd..48cb96f953a0 100644
--- a/drivers/media/pci/cx18/cx18-vbi.c
+++ b/drivers/media/pci/cx18/cx18-vbi.c
@@ -24,7 +24,7 @@
/*
* Raster Reference/Protection (RP) bytes, used in Start/End Active
* Video codes emitted from the digitzer in VIP 1.x mode, that flag the start
- * of VBI sample or VBI ancillary data regions in the digitial ratser line.
+ * of VBI sample or VBI ancillary data regions in the digital ratser line.
*
* Task FieldEven VerticalBlank HorizontalBlank 0 0 0 0
*/
diff --git a/drivers/media/pci/cx18/cx23418.h b/drivers/media/pci/cx18/cx23418.h
index 15205b662952..e9f6e50ca5b4 100644
--- a/drivers/media/pci/cx18/cx23418.h
+++ b/drivers/media/pci/cx18/cx23418.h
@@ -439,7 +439,7 @@
/* Error in I2C data xfer (but I2C device is present) */
#define CXERR_I2CDEV_XFERERR 0x000011
-/* Chanel changing component not ready */
+/* Channel changing component not ready */
#define CXERR_CHANNELNOTREADY 0x000012
/* PPU (Presensation/Decoder) mail box is corrupted */
diff --git a/drivers/media/pci/cx23885/cx23885-417.c b/drivers/media/pci/cx23885/cx23885-417.c
index a00b77d80ed9..4863bd4ea5d0 100644
--- a/drivers/media/pci/cx23885/cx23885-417.c
+++ b/drivers/media/pci/cx23885/cx23885-417.c
@@ -1561,7 +1561,7 @@ int cx23885_417_register(struct cx23885_dev *dev)
pr_info("%s: registered device %s [mpeg]\n",
dev->name, video_device_node_name(dev->v4l_device));
- /* ST: Configure the encoder paramaters, but don't begin
+ /* ST: Configure the encoder parameters, but don't begin
* encoding, this resolves an issue where the first time the
* encoder is started video can be choppy.
*/
diff --git a/drivers/media/pci/cx23885/cx23885-alsa.c b/drivers/media/pci/cx23885/cx23885-alsa.c
index ee9d329c4038..c9c1385208f9 100644
--- a/drivers/media/pci/cx23885/cx23885-alsa.c
+++ b/drivers/media/pci/cx23885/cx23885-alsa.c
@@ -59,7 +59,7 @@ module_param(audio_debug, int, 0644);
MODULE_PARM_DESC(audio_debug, "enable debug messages [analog audio]");
/****************************************************************************
- Board specific funtions
+ Board specific functions
****************************************************************************/
/* Constants taken from cx88-reg.h */
diff --git a/drivers/media/pci/cx23885/cx23885-core.c b/drivers/media/pci/cx23885/cx23885-core.c
index fd5c52b21436..cec3cbca8d32 100644
--- a/drivers/media/pci/cx23885/cx23885-core.c
+++ b/drivers/media/pci/cx23885/cx23885-core.c
@@ -1996,9 +1996,9 @@ static inline int encoder_on_portc(struct cx23885_dev *dev)
* and report errors if we think we're tampering with a GPIo that might
* be assigned to the encoder (and used for the host bus).
*
- * GPIO 2 thru 0 - On the cx23885 bridge
- * GPIO 18 thru 3 - On the cx23417 host bus interface
- * GPIO 23 thru 19 - On the cx25840 a/v core
+ * GPIO 2 through 0 - On the cx23885 bridge
+ * GPIO 18 through 3 - On the cx23417 host bus interface
+ * GPIO 23 through 19 - On the cx25840 a/v core
*/
void cx23885_gpio_set(struct cx23885_dev *dev, u32 mask)
{
diff --git a/drivers/media/pci/cx23885/cx23885.h b/drivers/media/pci/cx23885/cx23885.h
index cf965efabe66..4d34799431dc 100644
--- a/drivers/media/pci/cx23885/cx23885.h
+++ b/drivers/media/pci/cx23885/cx23885.h
@@ -246,7 +246,7 @@ struct cx23885_i2c {
struct i2c_client i2c_client;
u32 i2c_rc;
- /* 885 registers used for raw addess */
+ /* 885 registers used for raw address */
u32 i2c_period;
u32 reg_ctrl;
u32 reg_stat;
diff --git a/drivers/media/pci/cx23885/cx23888-ir.c b/drivers/media/pci/cx23885/cx23888-ir.c
index 1d775c90df51..a4d66141c4bb 100644
--- a/drivers/media/pci/cx23885/cx23888-ir.c
+++ b/drivers/media/pci/cx23885/cx23888-ir.c
@@ -548,7 +548,7 @@ static int cx23888_ir_irq_handler(struct v4l2_subdev *sd, u32 status,
ror = stats & STATS_ROR; /* Rx FIFO Over Run */
tse = irqen & IRQEN_TSE; /* Tx FIFO Service Request IRQ Enable */
- rse = irqen & IRQEN_RSE; /* Rx FIFO Service Reuqest IRQ Enable */
+ rse = irqen & IRQEN_RSE; /* Rx FIFO Service Request IRQ Enable */
rte = irqen & IRQEN_RTE; /* Rx Pulse Width Timer Time Out IRQ Enable */
roe = irqen & IRQEN_ROE; /* Rx FIFO Over Run IRQ Enable */
@@ -638,7 +638,7 @@ static int cx23888_ir_irq_handler(struct v4l2_subdev *sd, u32 status,
events |= V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED;
}
if (v) {
- /* Clear STATS_ROR & STATS_RTO as needed by reseting hardware */
+ /* Clear STATS_ROR & STATS_RTO as needed by resetting hardware */
cx23888_ir_write4(dev, CX23888_IR_CNTRL_REG, cntrl & ~v);
cx23888_ir_write4(dev, CX23888_IR_CNTRL_REG, cntrl);
*handled = true;
diff --git a/drivers/media/pci/cx25821/cx25821-alsa.c b/drivers/media/pci/cx25821/cx25821-alsa.c
index 0a6c90e92557..926e12a1554a 100644
--- a/drivers/media/pci/cx25821/cx25821-alsa.c
+++ b/drivers/media/pci/cx25821/cx25821-alsa.c
@@ -120,7 +120,7 @@ module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "enable debug messages");
/****************************************************************************
- Module specific funtions
+ Module specific functions
****************************************************************************/
/* Constants taken from cx88-reg.h */
#define AUD_INT_DN_RISCI1 (1 << 0)
diff --git a/drivers/media/pci/cx25821/cx25821-sram.h b/drivers/media/pci/cx25821/cx25821-sram.h
index b94e0d4df664..a907662dbb1c 100644
--- a/drivers/media/pci/cx25821/cx25821-sram.h
+++ b/drivers/media/pci/cx25821/cx25821-sram.h
@@ -24,7 +24,7 @@
#define AUDIO_CMDS_SIZE 80 /* AUDIO CMDS size in bytes */
#define MBIF_CMDS_SIZE 80 /* MBIF CMDS size in bytes */
-/* #define RX_SRAM_POOL_START_SIZE = 0; // Start of useable RX SRAM for buffers */
+/* #define RX_SRAM_POOL_START_SIZE = 0; // Start of usable RX SRAM for buffers */
#define VID_IQ_SIZE 64 /* VID instruction queue size in bytes */
#define MBIF_IQ_SIZE 64
#define AUDIO_IQ_SIZE 64 /* AUD instruction queue size in bytes */
diff --git a/drivers/media/pci/cx25821/cx25821.h b/drivers/media/pci/cx25821/cx25821.h
index 25eba4ac4499..b422275ff4f1 100644
--- a/drivers/media/pci/cx25821/cx25821.h
+++ b/drivers/media/pci/cx25821/cx25821.h
@@ -156,7 +156,7 @@ struct cx25821_i2c {
struct i2c_client i2c_client;
u32 i2c_rc;
- /* cx25821 registers used for raw addess */
+ /* cx25821 registers used for raw address */
u32 i2c_period;
u32 reg_ctrl;
u32 reg_stat;
diff --git a/drivers/media/pci/dm1105/dm1105.c b/drivers/media/pci/dm1105/dm1105.c
index a84c8270ea13..60138ca3372b 100644
--- a/drivers/media/pci/dm1105/dm1105.c
+++ b/drivers/media/pci/dm1105/dm1105.c
@@ -517,7 +517,7 @@ static int dm1105_i2c_xfer(struct i2c_adapter *i2c_adap,
msgs[i].buf[byte] = rc;
}
} else if ((msgs[i].buf[0] == 0xf7) && (msgs[i].addr == 0x55)) {
- /* prepaired for cx24116 firmware */
+ /* prepared for cx24116 firmware */
/* Write in small blocks */
len = msgs[i].len - 1;
k = 1;
diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.c b/drivers/media/pci/intel/ipu3/ipu3-cio2.c
index cdb79ae2d8dc..f8020ebe9f05 100644
--- a/drivers/media/pci/intel/ipu3/ipu3-cio2.c
+++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.c
@@ -264,7 +264,7 @@ static void cio2_fbpt_exit(struct cio2_queue *q, struct device *dev)
*/
/*
- * shift for keeping value range suitable for 32-bit integer arithmetics
+ * shift for keeping value range suitable for 32-bit integer arithmetic
*/
#define LIMIT_SHIFT 8
@@ -846,7 +846,7 @@ static int cio2_vb2_buf_init(struct vb2_buffer *vb)
unsigned int pages = DIV_ROUND_UP(vb->planes[0].length, CIO2_PAGE_SIZE);
unsigned int lops = DIV_ROUND_UP(pages + 1, entries_per_page);
struct sg_table *sg;
- struct sg_page_iter sg_iter;
+ struct sg_dma_page_iter sg_iter;
int i, j;
if (lops <= 0 || lops > CIO2_MAX_LOPS) {
@@ -873,7 +873,7 @@ static int cio2_vb2_buf_init(struct vb2_buffer *vb)
b->offset = sg->sgl->offset;
i = j = 0;
- for_each_sg_page(sg->sgl, &sg_iter, sg->nents, 0) {
+ for_each_sg_dma_page (sg->sgl, &sg_iter, sg->nents, 0) {
if (!pages--)
break;
b->lop[i][j] = sg_page_iter_dma_address(&sg_iter) >> PAGE_SHIFT;
@@ -1810,7 +1810,8 @@ static int cio2_pci_probe(struct pci_dev *pci_dev,
/* Register notifier for subdevices we care */
r = cio2_notifier_init(cio2);
- if (r)
+ /* Proceed without sensors connected to allow the device to suspend. */
+ if (r && r != -ENODEV)
goto fail_cio2_queue_exit;
r = devm_request_irq(&pci_dev->dev, pci_dev->irq, cio2_irq,
@@ -2047,7 +2048,7 @@ module_pci_driver(cio2_pci_driver);
MODULE_AUTHOR("Tuukka Toivonen <tuukka.toivonen@intel.com>");
MODULE_AUTHOR("Tianshu Qiu <tian.shu.qiu@intel.com>");
-MODULE_AUTHOR("Jian Xu Zheng <jian.xu.zheng@intel.com>");
+MODULE_AUTHOR("Jian Xu Zheng");
MODULE_AUTHOR("Yuning Pu <yuning.pu@intel.com>");
MODULE_AUTHOR("Yong Zhi <yong.zhi@intel.com>");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/pci/ivtv/Kconfig b/drivers/media/pci/ivtv/Kconfig
index c72cbbd2d40c..06ca4e23f9fb 100644
--- a/drivers/media/pci/ivtv/Kconfig
+++ b/drivers/media/pci/ivtv/Kconfig
@@ -70,8 +70,25 @@ config VIDEO_FB_IVTV
This is used in the Hauppauge PVR-350 card. There is a driver
homepage at <http://www.ivtvdriver.org>.
- In order to use this module, you will need to boot with PAT disabled
- on x86 systems, using the nopat kernel parameter.
-
To compile this driver as a module, choose M here: the
module will be called ivtvfb.
+
+config VIDEO_FB_IVTV_FORCE_PAT
+ bool "force cx23415 framebuffer init with x86 PAT enabled"
+ depends on VIDEO_FB_IVTV && X86_PAT
+ default n
+ ---help---
+ With PAT enabled, the cx23415 framebuffer driver does not
+ utilize write-combined caching on the framebuffer memory.
+ For this reason, the driver will by default disable itself
+ when initializied on a kernel with PAT enabled (i.e. not
+ using the nopat kernel parameter).
+
+ The driver is not easily upgradable to the PAT-aware
+ ioremap_wc() API since the firmware hides the address
+ ranges that should be marked write-combined from the driver.
+
+ With this setting enabled, the framebuffer will initialize on
+ PAT-enabled systems but the framebuffer memory will be uncached.
+
+ If unsure, say N.
diff --git a/drivers/media/pci/ivtv/ivtv-yuv.c b/drivers/media/pci/ivtv/ivtv-yuv.c
index 1380474519f2..c3c2c79585f5 100644
--- a/drivers/media/pci/ivtv/ivtv-yuv.c
+++ b/drivers/media/pci/ivtv/ivtv-yuv.c
@@ -110,7 +110,7 @@ static int ivtv_yuv_prep_user_dma(struct ivtv *itv, struct ivtv_user_dma *dma,
/*
* Inherit the -EFAULT from rc's
* initialization, but allow it to be
- * overriden by uv_pages above if it was an
+ * overridden by uv_pages above if it was an
* actual errno.
*/
} else {
diff --git a/drivers/media/pci/ivtv/ivtvfb.c b/drivers/media/pci/ivtv/ivtvfb.c
index 8ec2525d8ef5..cfd21040d0e3 100644
--- a/drivers/media/pci/ivtv/ivtvfb.c
+++ b/drivers/media/pci/ivtv/ivtvfb.c
@@ -55,6 +55,7 @@
/* card parameters */
static int ivtvfb_card_id = -1;
static int ivtvfb_debug = 0;
+static bool ivtvfb_force_pat = IS_ENABLED(CONFIG_VIDEO_FB_IVTV_FORCE_PAT);
static bool osd_laced;
static int osd_depth;
static int osd_upper;
@@ -64,6 +65,7 @@ static int osd_xres;
module_param(ivtvfb_card_id, int, 0444);
module_param_named(debug,ivtvfb_debug, int, 0644);
+module_param_named(force_pat, ivtvfb_force_pat, bool, 0644);
module_param(osd_laced, bool, 0444);
module_param(osd_depth, int, 0444);
module_param(osd_upper, int, 0444);
@@ -79,6 +81,9 @@ MODULE_PARM_DESC(debug,
"Debug level (bitmask). Default: errors only\n"
"\t\t\t(debug = 3 gives full debugging)");
+MODULE_PARM_DESC(force_pat,
+ "Force initialization on x86 PAT-enabled systems (bool).\n");
+
/* Why upper, left, xres, yres, depth, laced ? To match terminology used
by fbset.
Why start at 1 for left & upper coordinate ? Because X doesn't allow 0 */
@@ -1167,8 +1172,15 @@ static int ivtvfb_init_card(struct ivtv *itv)
#ifdef CONFIG_X86_64
if (pat_enabled()) {
- pr_warn("ivtvfb needs PAT disabled, boot with nopat kernel parameter\n");
- return -ENODEV;
+ if (ivtvfb_force_pat) {
+ pr_info("PAT is enabled. Write-combined framebuffer caching will be disabled.\n");
+ pr_info("To enable caching, boot with nopat kernel parameter\n");
+ } else {
+ pr_warn("ivtvfb needs PAT disabled for write-combined framebuffer caching.\n");
+ pr_warn("Boot with nopat kernel parameter to use caching, or use the\n");
+ pr_warn("force_pat module parameter to run with caching disabled\n");
+ return -ENODEV;
+ }
}
#endif
diff --git a/drivers/media/pci/meye/meye.c b/drivers/media/pci/meye/meye.c
index bd870e60c32b..896d2d856795 100644
--- a/drivers/media/pci/meye/meye.c
+++ b/drivers/media/pci/meye/meye.c
@@ -805,7 +805,7 @@ again:
mchip_hsize() * mchip_vsize() * 2);
meye.grab_buffer[reqnr].size = mchip_hsize() * mchip_vsize() * 2;
meye.grab_buffer[reqnr].state = MEYE_BUF_DONE;
- v4l2_get_timestamp(&meye.grab_buffer[reqnr].timestamp);
+ meye.grab_buffer[reqnr].ts = ktime_get_ns();
meye.grab_buffer[reqnr].sequence = sequence++;
kfifo_in_locked(&meye.doneq, (unsigned char *)&reqnr,
sizeof(int), &meye.doneq_lock);
@@ -826,7 +826,7 @@ again:
size);
meye.grab_buffer[reqnr].size = size;
meye.grab_buffer[reqnr].state = MEYE_BUF_DONE;
- v4l2_get_timestamp(&meye.grab_buffer[reqnr].timestamp);
+ meye.grab_buffer[reqnr].ts = ktime_get_ns();
meye.grab_buffer[reqnr].sequence = sequence++;
kfifo_in_locked(&meye.doneq, (unsigned char *)&reqnr,
sizeof(int), &meye.doneq_lock);
@@ -1283,7 +1283,7 @@ static int vidioc_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf)
buf->flags |= V4L2_BUF_FLAG_DONE;
buf->field = V4L2_FIELD_NONE;
- buf->timestamp = meye.grab_buffer[index].timestamp;
+ buf->timestamp = ns_to_timeval(meye.grab_buffer[index].ts);
buf->sequence = meye.grab_buffer[index].sequence;
buf->memory = V4L2_MEMORY_MMAP;
buf->m.offset = index * gbufsize;
@@ -1349,7 +1349,7 @@ static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
buf->bytesused = meye.grab_buffer[reqnr].size;
buf->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
buf->field = V4L2_FIELD_NONE;
- buf->timestamp = meye.grab_buffer[reqnr].timestamp;
+ buf->timestamp = ns_to_timeval(meye.grab_buffer[reqnr].ts);
buf->sequence = meye.grab_buffer[reqnr].sequence;
buf->memory = V4L2_MEMORY_MMAP;
buf->m.offset = reqnr * gbufsize;
diff --git a/drivers/media/pci/meye/meye.h b/drivers/media/pci/meye/meye.h
index c4a8a5fe040c..aff6631535be 100644
--- a/drivers/media/pci/meye/meye.h
+++ b/drivers/media/pci/meye/meye.h
@@ -277,11 +277,11 @@
struct meye_grab_buffer {
int state; /* state of buffer */
unsigned long size; /* size of jpg frame */
- struct timeval timestamp; /* timestamp */
+ u64 ts; /* timestamp */
unsigned long sequence; /* sequence number */
};
-/* size of kfifos containings buffer indices */
+/* size of kfifos containing buffer indices */
#define MEYE_QUEUE_SIZE MEYE_MAX_BUFNBRS
/* Motion Eye device structure */
diff --git a/drivers/media/pci/ngene/ngene-core.c b/drivers/media/pci/ngene/ngene-core.c
index 25f16833a475..27953b3610a3 100644
--- a/drivers/media/pci/ngene/ngene-core.c
+++ b/drivers/media/pci/ngene/ngene-core.c
@@ -1014,7 +1014,7 @@ static int FillTSIdleBuffer(struct SRingBufferDescriptor *pIdleBuffer,
/* Point to first buffer entry */
struct SBufferHeader *Cur = pRingBuffer->Head;
int i;
- /* Loop thru all buffer and set Buffer 2 pointers to TSIdlebuffer */
+ /* Loop through all buffer and set Buffer 2 pointers to TSIdlebuffer */
for (i = 0; i < n; i++) {
Cur->Buffer2 = pIdleBuffer->Head->Buffer1;
Cur->scList2 = pIdleBuffer->Head->scList1;
diff --git a/drivers/media/pci/pt1/pt1.c b/drivers/media/pci/pt1/pt1.c
index f4b8030e2369..393f4c596819 100644
--- a/drivers/media/pci/pt1/pt1.c
+++ b/drivers/media/pci/pt1/pt1.c
@@ -200,16 +200,10 @@ static const u8 va1j5jf8007t_25mhz_configs[][2] = {
static int config_demod(struct i2c_client *cl, enum pt1_fe_clk clk)
{
int ret;
- u8 buf[2] = {0x01, 0x80};
bool is_sat;
const u8 (*cfg_data)[2];
int i, len;
- ret = i2c_master_send(cl, buf, 2);
- if (ret < 0)
- return ret;
- usleep_range(30000, 50000);
-
is_sat = !strncmp(cl->name, TC90522_I2C_DEV_SAT,
strlen(TC90522_I2C_DEV_SAT));
if (is_sat) {
@@ -260,6 +254,46 @@ static int config_demod(struct i2c_client *cl, enum pt1_fe_clk clk)
return 0;
}
+/*
+ * Init registers for (each pair of) terrestrial/satellite block in demod.
+ * Note that resetting terr. block also resets its peer sat. block as well.
+ * This function must be called before configuring any demod block
+ * (before pt1_wakeup(), fe->ops.init()).
+ */
+static int pt1_demod_block_init(struct pt1 *pt1)
+{
+ struct i2c_client *cl;
+ u8 buf[2] = {0x01, 0x80};
+ int ret;
+ int i;
+
+ /* reset all terr. & sat. pairs first */
+ for (i = 0; i < PT1_NR_ADAPS; i++) {
+ cl = pt1->adaps[i]->demod_i2c_client;
+ if (strncmp(cl->name, TC90522_I2C_DEV_TER,
+ strlen(TC90522_I2C_DEV_TER)))
+ continue;
+
+ ret = i2c_master_send(cl, buf, 2);
+ if (ret < 0)
+ return ret;
+ usleep_range(30000, 50000);
+ }
+
+ for (i = 0; i < PT1_NR_ADAPS; i++) {
+ cl = pt1->adaps[i]->demod_i2c_client;
+ if (strncmp(cl->name, TC90522_I2C_DEV_SAT,
+ strlen(TC90522_I2C_DEV_SAT)))
+ continue;
+
+ ret = i2c_master_send(cl, buf, 2);
+ if (ret < 0)
+ return ret;
+ usleep_range(30000, 50000);
+ }
+ return 0;
+}
+
static void pt1_write_reg(struct pt1 *pt1, int reg, u32 data)
{
writel(data, pt1->regs + reg * 4);
@@ -987,6 +1021,10 @@ static int pt1_init_frontends(struct pt1 *pt1)
goto tuner_release;
}
+ ret = pt1_demod_block_init(pt1);
+ if (ret < 0)
+ goto fe_unregister;
+
return 0;
tuner_release:
@@ -1245,6 +1283,10 @@ static int pt1_resume(struct device *dev)
pt1_update_power(pt1);
usleep_range(1000, 2000);
+ ret = pt1_demod_block_init(pt1);
+ if (ret < 0)
+ goto resume_err;
+
for (i = 0; i < PT1_NR_ADAPS; i++)
dvb_frontend_reinitialise(pt1->adaps[i]->fe);
diff --git a/drivers/media/pci/pt3/pt3.h b/drivers/media/pci/pt3/pt3.h
index 495891a4ee17..a53124438f51 100644
--- a/drivers/media/pci/pt3/pt3.h
+++ b/drivers/media/pci/pt3/pt3.h
@@ -76,7 +76,7 @@ struct xfer_desc {
u32 addr_l; /* bus address of target data buffer */
u32 addr_h;
u32 size;
- u32 next_l; /* bus adddress of the next xfer_desc */
+ u32 next_l; /* bus address of the next xfer_desc */
u32 next_h;
};
diff --git a/drivers/media/pci/saa7134/saa7134-cards.c b/drivers/media/pci/saa7134/saa7134-cards.c
index 40ce033cb884..94d6484a3afe 100644
--- a/drivers/media/pci/saa7134/saa7134-cards.c
+++ b/drivers/media/pci/saa7134/saa7134-cards.c
@@ -6423,7 +6423,7 @@ struct pci_device_id saa7134_pci_tbl[] = {
.vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7133,
.subvendor = 0x5168,
- .subdevice = 0x3502, /* whats the difference to 0x3306 ?*/
+ .subdevice = 0x3502, /* what's the difference to 0x3306 ?*/
.driver_data = SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS,
},{
.vendor = PCI_VENDOR_ID_PHILIPS,
diff --git a/drivers/media/pci/saa7146/mxb.c b/drivers/media/pci/saa7146/mxb.c
index 44440c6208df..e94324b1de68 100644
--- a/drivers/media/pci/saa7146/mxb.c
+++ b/drivers/media/pci/saa7146/mxb.c
@@ -399,7 +399,7 @@ static int mxb_init_done(struct saa7146_dev* dev)
/* check if the saa7740 (aka 'sound arena module') is present
on the mxb. if so, we must initialize it. due to lack of
- informations about the saa7740, the values were reverse
+ information about the saa7740, the values were reverse
engineered. */
msg.addr = 0x1b;
msg.flags = 0;
@@ -495,7 +495,7 @@ static int vidioc_s_input(struct file *file, void *fh, unsigned int input)
input_port_selection[input].hps_sync);
/* prepare switching of tea6415c and saa7111a;
- have a look at the 'background'-file for further informations */
+ have a look at the 'background'-file for further information */
switch (input) {
case TUNER:
i = SAA7115_COMPOSITE0;
diff --git a/drivers/media/pci/saa7164/saa7164-api.c b/drivers/media/pci/saa7164/saa7164-api.c
index e318ccf81277..d6c996f39cf2 100644
--- a/drivers/media/pci/saa7164/saa7164-api.c
+++ b/drivers/media/pci/saa7164/saa7164-api.c
@@ -749,7 +749,7 @@ int saa7164_api_initialize_dif(struct saa7164_port *port)
if (port->type == SAA7164_MPEG_ENCODER) {
/* Pick any analog standard to init the diff.
* we'll come back during encoder_init'
- * and set the correct standard if requried.
+ * and set the correct standard if required.
*/
std = V4L2_STD_NTSC;
} else
diff --git a/drivers/media/pci/saa7164/saa7164-cards.c b/drivers/media/pci/saa7164/saa7164-cards.c
index 3af16062e79d..9a6fe7cd4d59 100644
--- a/drivers/media/pci/saa7164/saa7164-cards.c
+++ b/drivers/media/pci/saa7164/saa7164-cards.c
@@ -685,7 +685,7 @@ struct saa7164_subid saa7164_subids[] = {
.subvendor = 0x0070,
.subdevice = 0xf111,
.card = SAA7164_BOARD_HAUPPAUGE_HVR2255,
- /* Prototype card left here for documenation purposes.
+ /* Prototype card left here for documentation purposes.
.card = SAA7164_BOARD_HAUPPAUGE_HVR2255proto,
*/
}, {
@@ -866,7 +866,7 @@ void saa7164_card_setup(struct saa7164_dev *dev)
* access to I2C. Instead we have to communicate through the device f/w for
* register access to 'processing units'. Each unit has a unique
* id, regardless of how the physical implementation occurs across
- * the three physical i2c busses. The being said if we want leverge of
+ * the three physical i2c buses. The being said if we want leverge of
* the existing kernel drivers for tuners and demods we have to 'speak i2c',
* to this bridge implements 3 virtual i2c buses. This is a helper function
* for those.
diff --git a/drivers/media/pci/saa7164/saa7164-core.c b/drivers/media/pci/saa7164/saa7164-core.c
index f33349e16359..05f25c9bb308 100644
--- a/drivers/media/pci/saa7164/saa7164-core.c
+++ b/drivers/media/pci/saa7164/saa7164-core.c
@@ -157,7 +157,7 @@ static void saa7164_ts_verifier(struct saa7164_buffer *buf)
}
- /* Only report errors if we've been through this function atleast
+ /* Only report errors if we've been through this function at least
* once already and the cached cc values are primed. First time through
* always generates errors.
*/
@@ -1020,7 +1020,7 @@ static int saa7164_dev_setup(struct saa7164_dev *dev)
dev->bmmio = (u8 __iomem *)dev->lmmio;
dev->bmmio2 = (u8 __iomem *)dev->lmmio2;
- /* Inerrupt and ack register locations offset of bmmio */
+ /* Interrupt and ack register locations offset of bmmio */
dev->int_status = 0x183000 + 0xf80;
dev->int_ack = 0x183000 + 0xf90;
diff --git a/drivers/media/pci/saa7164/saa7164-dvb.c b/drivers/media/pci/saa7164/saa7164-dvb.c
index dfb118d7d1ec..3e73cb3c7e88 100644
--- a/drivers/media/pci/saa7164/saa7164-dvb.c
+++ b/drivers/media/pci/saa7164/saa7164-dvb.c
@@ -529,7 +529,7 @@ int saa7164_dvb_unregister(struct saa7164_port *port)
return 0;
}
-/* All the DVB attach calls go here, this function get's modified
+/* All the DVB attach calls go here, this function gets modified
* for each new card.
*/
int saa7164_dvb_register(struct saa7164_port *port)
diff --git a/drivers/media/pci/saa7164/saa7164-fw.c b/drivers/media/pci/saa7164/saa7164-fw.c
index a50461861133..ed27b3ce5e8e 100644
--- a/drivers/media/pci/saa7164/saa7164-fw.c
+++ b/drivers/media/pci/saa7164/saa7164-fw.c
@@ -409,7 +409,7 @@ int saa7164_downloadfirmware(struct saa7164_dev *dev)
(version & 0x0000001f),
(version & 0xffff0000) >> 16);
- /* Load the firmwware from the disk if required */
+ /* Load the firmware from the disk if required */
if (version == 0) {
printk(KERN_INFO "%s() Waiting for firmware upload (%s)\n",
diff --git a/drivers/media/pci/smipcie/smipcie-ir.c b/drivers/media/pci/smipcie/smipcie-ir.c
index c5595af6b976..d292cdfb3671 100644
--- a/drivers/media/pci/smipcie/smipcie-ir.c
+++ b/drivers/media/pci/smipcie/smipcie-ir.c
@@ -16,6 +16,9 @@
#include "smipcie.h"
+#define SMI_SAMPLE_PERIOD 83
+#define SMI_SAMPLE_IDLEMIN (10000 / SMI_SAMPLE_PERIOD)
+
static void smi_ir_enableInterrupt(struct smi_rc *ir)
{
struct smi_dev *dev = ir->dev;
@@ -42,114 +45,64 @@ static void smi_ir_stop(struct smi_rc *ir)
struct smi_dev *dev = ir->dev;
smi_ir_disableInterrupt(ir);
- smi_clear(IR_Init_Reg, 0x80);
+ smi_clear(IR_Init_Reg, rbIRen);
}
-#define BITS_PER_COMMAND 14
-#define GROUPS_PER_BIT 2
-#define IR_RC5_MIN_BIT 36
-#define IR_RC5_MAX_BIT 52
-static u32 smi_decode_rc5(u8 *pData, u8 size)
+static void smi_raw_process(struct rc_dev *rc_dev, const u8 *buffer,
+ const u8 length)
{
- u8 index, current_bit, bit_count;
- u8 group_array[BITS_PER_COMMAND * GROUPS_PER_BIT + 4];
- u8 group_index = 0;
- u32 command = 0xFFFFFFFF;
-
- group_array[group_index++] = 1;
-
- for (index = 0; index < size; index++) {
-
- current_bit = (pData[index] & 0x80) ? 1 : 0;
- bit_count = pData[index] & 0x7f;
-
- if ((current_bit == 1) && (bit_count >= 2*IR_RC5_MAX_BIT + 1)) {
- goto process_code;
- } else if ((bit_count >= IR_RC5_MIN_BIT) &&
- (bit_count <= IR_RC5_MAX_BIT)) {
- group_array[group_index++] = current_bit;
- } else if ((bit_count > IR_RC5_MAX_BIT) &&
- (bit_count <= 2*IR_RC5_MAX_BIT)) {
- group_array[group_index++] = current_bit;
- group_array[group_index++] = current_bit;
- } else {
- goto invalid_timing;
- }
- if (group_index >= BITS_PER_COMMAND*GROUPS_PER_BIT)
- goto process_code;
-
- if ((group_index == BITS_PER_COMMAND*GROUPS_PER_BIT - 1)
- && (group_array[group_index-1] == 0)) {
- group_array[group_index++] = 1;
- goto process_code;
- }
- }
-
-process_code:
- if (group_index == (BITS_PER_COMMAND*GROUPS_PER_BIT-1))
- group_array[group_index++] = 1;
-
- if (group_index == BITS_PER_COMMAND*GROUPS_PER_BIT) {
- command = 0;
- for (index = 0; index < (BITS_PER_COMMAND*GROUPS_PER_BIT);
- index = index + 2) {
- if ((group_array[index] == 1) &&
- (group_array[index+1] == 0)) {
- command |= (1 << (BITS_PER_COMMAND -
- (index/2) - 1));
- } else if ((group_array[index] == 0) &&
- (group_array[index+1] == 1)) {
- /* */
- } else {
- command = 0xFFFFFFFF;
- goto invalid_timing;
- }
+ struct ir_raw_event rawir = {};
+ int cnt;
+
+ for (cnt = 0; cnt < length; cnt++) {
+ if (buffer[cnt] & 0x7f) {
+ rawir.pulse = (buffer[cnt] & 0x80) == 0;
+ rawir.duration = ((buffer[cnt] & 0x7f) +
+ (rawir.pulse ? 0 : -1)) *
+ rc_dev->rx_resolution;
+ ir_raw_event_store_with_filter(rc_dev, &rawir);
}
}
-
-invalid_timing:
- return command;
}
-static void smi_ir_decode(struct work_struct *work)
+static void smi_ir_decode(struct smi_rc *ir)
{
- struct smi_rc *ir = container_of(work, struct smi_rc, work);
struct smi_dev *dev = ir->dev;
struct rc_dev *rc_dev = ir->rc_dev;
- u32 dwIRControl, dwIRData, dwIRCode, scancode;
- u8 index, ucIRCount, readLoop, rc5_command, rc5_system, toggle;
+ u32 dwIRControl, dwIRData;
+ u8 index, ucIRCount, readLoop;
dwIRControl = smi_read(IR_Init_Reg);
+
if (dwIRControl & rbIRVld) {
ucIRCount = (u8) smi_read(IR_Data_Cnt);
- if (ucIRCount < 4)
- goto end_ir_decode;
-
readLoop = ucIRCount/4;
if (ucIRCount % 4)
readLoop += 1;
for (index = 0; index < readLoop; index++) {
- dwIRData = smi_read(IR_DATA_BUFFER_BASE + (index*4));
+ dwIRData = smi_read(IR_DATA_BUFFER_BASE + (index * 4));
ir->irData[index*4 + 0] = (u8)(dwIRData);
ir->irData[index*4 + 1] = (u8)(dwIRData >> 8);
ir->irData[index*4 + 2] = (u8)(dwIRData >> 16);
ir->irData[index*4 + 3] = (u8)(dwIRData >> 24);
}
- dwIRCode = smi_decode_rc5(ir->irData, ucIRCount);
-
- if (dwIRCode != 0xFFFFFFFF) {
- rc5_command = dwIRCode & 0x3F;
- rc5_system = (dwIRCode & 0x7C0) >> 6;
- toggle = (dwIRCode & 0x800) ? 1 : 0;
- scancode = rc5_system << 8 | rc5_command;
- rc_keydown(rc_dev, RC_PROTO_RC5, scancode, toggle);
- }
+ smi_raw_process(rc_dev, ir->irData, ucIRCount);
+ smi_set(IR_Init_Reg, rbIRVld);
}
-end_ir_decode:
- smi_set(IR_Init_Reg, 0x04);
- smi_ir_enableInterrupt(ir);
+
+ if (dwIRControl & rbIRhighidle) {
+ struct ir_raw_event rawir = {};
+
+ rawir.pulse = 0;
+ rawir.duration = US_TO_NS(SMI_SAMPLE_PERIOD *
+ SMI_SAMPLE_IDLEMIN);
+ ir_raw_event_store_with_filter(rc_dev, &rawir);
+ smi_set(IR_Init_Reg, rbIRhighidle);
+ }
+
+ ir_raw_event_handle(rc_dev);
}
/* ir functions call by main driver.*/
@@ -160,7 +113,8 @@ int smi_ir_irq(struct smi_rc *ir, u32 int_status)
if (int_status & IR_X_INT) {
smi_ir_disableInterrupt(ir);
smi_ir_clearInterrupt(ir);
- schedule_work(&ir->work);
+ smi_ir_decode(ir);
+ smi_ir_enableInterrupt(ir);
handled = 1;
}
return handled;
@@ -170,9 +124,11 @@ void smi_ir_start(struct smi_rc *ir)
{
struct smi_dev *dev = ir->dev;
- smi_write(IR_Idle_Cnt_Low, 0x00140070);
+ smi_write(IR_Idle_Cnt_Low,
+ (((SMI_SAMPLE_PERIOD - 1) & 0xFFFF) << 16) |
+ (SMI_SAMPLE_IDLEMIN & 0xFFFF));
msleep(20);
- smi_set(IR_Init_Reg, 0x90);
+ smi_set(IR_Init_Reg, rbIRen | rbIRhighidle);
smi_ir_enableInterrupt(ir);
}
@@ -183,7 +139,7 @@ int smi_ir_init(struct smi_dev *dev)
struct rc_dev *rc_dev;
struct smi_rc *ir = &dev->ir;
- rc_dev = rc_allocate_device(RC_DRIVER_SCANCODE);
+ rc_dev = rc_allocate_device(RC_DRIVER_IR_RAW);
if (!rc_dev)
return -ENOMEM;
@@ -193,6 +149,7 @@ int smi_ir_init(struct smi_dev *dev)
snprintf(ir->input_phys, sizeof(ir->input_phys), "pci-%s/ir0",
pci_name(dev->pci_dev));
+ rc_dev->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER;
rc_dev->driver_name = "SMI_PCIe";
rc_dev->input_phys = ir->input_phys;
rc_dev->device_name = ir->device_name;
@@ -203,11 +160,12 @@ int smi_ir_init(struct smi_dev *dev)
rc_dev->dev.parent = &dev->pci_dev->dev;
rc_dev->map_name = dev->info->rc_map;
+ rc_dev->timeout = MS_TO_NS(100);
+ rc_dev->rx_resolution = US_TO_NS(SMI_SAMPLE_PERIOD);
ir->rc_dev = rc_dev;
ir->dev = dev;
- INIT_WORK(&ir->work, smi_ir_decode);
smi_ir_disableInterrupt(ir);
ret = rc_register_device(rc_dev);
diff --git a/drivers/media/pci/smipcie/smipcie.h b/drivers/media/pci/smipcie/smipcie.h
index a6c5b1bd7edb..e52229a87b84 100644
--- a/drivers/media/pci/smipcie/smipcie.h
+++ b/drivers/media/pci/smipcie/smipcie.h
@@ -241,7 +241,6 @@ struct smi_rc {
struct rc_dev *rc_dev;
char input_phys[64];
char device_name[64];
- struct work_struct work;
u8 irData[256];
int users;
diff --git a/drivers/media/pci/solo6x10/solo6x10-disp.c b/drivers/media/pci/solo6x10/solo6x10-disp.c
index 11c98f0625e4..f61007022471 100644
--- a/drivers/media/pci/solo6x10/solo6x10-disp.c
+++ b/drivers/media/pci/solo6x10/solo6x10-disp.c
@@ -71,7 +71,7 @@ static void solo_vin_config(struct solo_dev *solo_dev)
solo_reg_write(solo_dev, SOLO_VI_CH_FORMAT,
SOLO_VI_FD_SEL_MASK(0) | SOLO_VI_PROG_MASK(0));
- /* On 6110, initialize mozaic darkness stength */
+ /* On 6110, initialize mozaic darkness strength */
if (solo_dev->type == SOLO_DEV_6010)
solo_reg_write(solo_dev, SOLO_VI_FMT_CFG, 0);
else
@@ -230,7 +230,7 @@ int solo_set_motion_block(struct solo_dev *solo_dev, u8 ch,
}
/* First 8k is motion flag (512 bytes * 16). Following that is an 8k+8k
- * threshold and working table for each channel. Atleast that's what the
+ * threshold and working table for each channel. At least that's what the
* spec says. However, this code (taken from rdk) has some mystery 8k
* block right after the flag area, before the first thresh table. */
static void solo_motion_config(struct solo_dev *solo_dev)
diff --git a/drivers/media/pci/sta2x11/sta2x11_vip.c b/drivers/media/pci/sta2x11/sta2x11_vip.c
index 411177ec4d72..2452d8f59cb0 100644
--- a/drivers/media/pci/sta2x11/sta2x11_vip.c
+++ b/drivers/media/pci/sta2x11/sta2x11_vip.c
@@ -110,7 +110,7 @@ static inline struct vip_buffer *to_vip_buffer(struct vb2_v4l2_buffer *vb2)
* @std: video standard (e.g. PAL/NTSC)
* @input: input line for video signal ( 0 or 1 )
* @disabled: Device is in power down state
- * @slock: for excluse acces of registers
+ * @slock: for excluse access of registers
* @vb_vidq: queue maintained by videobuf2 layer
* @buffer_list: list of buffer in use
* @sequence: sequence number of acquired buffer
diff --git a/drivers/media/pci/ttpci/av7110.c b/drivers/media/pci/ttpci/av7110.c
index 409defc75c05..9345287ad963 100644
--- a/drivers/media/pci/ttpci/av7110.c
+++ b/drivers/media/pci/ttpci/av7110.c
@@ -2313,7 +2313,7 @@ static int frontend_init(struct av7110 *av7110)
* (n in defined in the RPS_THRESH1 counter threshold)
* I think HS is raised high on the beginning of the n-th line
* and remains high until this n-th line that triggered
- * it is completely received. When the receiption of n-th line
+ * it is completely received. When the reception of n-th line
* ends, HS is lowered.
*
* To transmit data over DMA, 7146 needs changing state at
@@ -2347,7 +2347,7 @@ static int frontend_init(struct av7110 *av7110)
* hardware debug note: a working budget card (including budget patch)
* with vpeirq() interrupt setup in mode "0x90" (every 64K) will
* generate 3 interrupts per 25-Hz DMA frame of 2*188*512 bytes
- * and that means 3*25=75 Hz of interrupt freqency, as seen by
+ * and that means 3*25=75 Hz of interrupt frequency, as seen by
* watch cat /proc/interrupts
*
* If this frequency is 3x lower (and data received in the DMA
@@ -2356,7 +2356,7 @@ static int frontend_init(struct av7110 *av7110)
* this means VSYNC line is not connected in the hardware.
* (check soldering pcb and pins)
* The same behaviour of missing VSYNC can be duplicated on budget
- * cards, by seting DD1_INIT trigger mode 7 in 3rd nibble.
+ * cards, by setting DD1_INIT trigger mode 7 in 3rd nibble.
*/
static int av7110_attach(struct saa7146_dev* dev,
struct saa7146_pci_extension_data *pci_ext)
diff --git a/drivers/media/pci/tw68/tw68-video.c b/drivers/media/pci/tw68/tw68-video.c
index d3f727045ae8..4f74b14c3b4f 100644
--- a/drivers/media/pci/tw68/tw68-video.c
+++ b/drivers/media/pci/tw68/tw68-video.c
@@ -446,7 +446,7 @@ static void tw68_buf_queue(struct vb2_buffer *vb)
/*
* buffer_prepare
*
- * Set the ancilliary information into the buffer structure. This
+ * Set the ancillary information into the buffer structure. This
* includes generating the necessary risc program if it hasn't already
* been done for the current buffer format.
* The structure fh contains the details of the format requested by the
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index a505e9f5a1e2..4acbed189644 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -141,7 +141,6 @@ config VIDEO_RENESAS_CEU
---help---
This is a v4l2 driver for the Renesas CEU Interface
-source "drivers/media/platform/soc_camera/Kconfig"
source "drivers/media/platform/exynos4-is/Kconfig"
source "drivers/media/platform/am437x/Kconfig"
source "drivers/media/platform/xilinx/Kconfig"
@@ -650,7 +649,7 @@ config VIDEO_SECO_CEC
config VIDEO_SECO_RC
bool "SECO Boards IR RC5 support"
depends on VIDEO_SECO_CEC
- select RC_CORE
+ depends on RC_CORE
help
If you say yes here you will get support for the
SECO Boards Consumer-IR in seco-cec driver.
@@ -669,7 +668,7 @@ menuconfig SDR_PLATFORM_DRIVERS
if SDR_PLATFORM_DRIVERS
config VIDEO_RCAR_DRIF
- tristate "Renesas Digitial Radio Interface (DRIF)"
+ tristate "Renesas Digital Radio Interface (DRIF)"
depends on VIDEO_V4L2
depends on ARCH_RENESAS || COMPILE_TEST
select VIDEOBUF2_VMALLOC
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index e6deb2597738..7cbbd925124c 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -62,8 +62,6 @@ obj-y += davinci/
obj-$(CONFIG_VIDEO_SH_VOU) += sh_vou.o
-obj-$(CONFIG_SOC_CAMERA) += soc_camera/
-
obj-$(CONFIG_VIDEO_RCAR_DRIF) += rcar_drif.o
obj-$(CONFIG_VIDEO_RENESAS_CEU) += renesas-ceu.o
obj-$(CONFIG_VIDEO_RENESAS_FCP) += rcar-fcp.o
diff --git a/drivers/media/platform/aspeed-video.c b/drivers/media/platform/aspeed-video.c
index dfec813f50a9..692e08ef38c0 100644
--- a/drivers/media/platform/aspeed-video.c
+++ b/drivers/media/platform/aspeed-video.c
@@ -1661,6 +1661,7 @@ static int aspeed_video_probe(struct platform_device *pdev)
video->frame_rate = 30;
video->dev = &pdev->dev;
+ spin_lock_init(&video->lock);
mutex_init(&video->video_lock);
init_waitqueue_head(&video->wait);
INIT_DELAYED_WORK(&video->res_work, aspeed_video_resolution_work);
diff --git a/drivers/media/platform/atmel/atmel-isi.c b/drivers/media/platform/atmel/atmel-isi.c
index fdb255e4a956..08b8d5583080 100644
--- a/drivers/media/platform/atmel/atmel-isi.c
+++ b/drivers/media/platform/atmel/atmel-isi.c
@@ -110,7 +110,7 @@ struct atmel_isi {
bool enable_preview_path;
struct completion complete;
- /* ISI peripherial clock */
+ /* ISI peripheral clock */
struct clk *pclk;
unsigned int irq;
@@ -1078,7 +1078,7 @@ static void isi_graph_notify_unbind(struct v4l2_async_notifier *notifier,
dev_dbg(isi->dev, "Removing %s\n", video_device_node_name(isi->vdev));
- /* Checks internaly if vdev have been init or not */
+ /* Checks internally if vdev have been init or not */
video_unregister_device(isi->vdev);
}
diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c
index 8e0194993a52..b4f396c2e72c 100644
--- a/drivers/media/platform/coda/coda-bit.c
+++ b/drivers/media/platform/coda/coda-bit.c
@@ -1010,7 +1010,11 @@ static int coda_start_encoding(struct coda_ctx *ctx)
CODA_264PARAM_DEBLKFILTEROFFSETALPHA_OFFSET) |
((ctx->params.h264_slice_beta_offset_div2 &
CODA_264PARAM_DEBLKFILTEROFFSETBETA_MASK) <<
- CODA_264PARAM_DEBLKFILTEROFFSETBETA_OFFSET);
+ CODA_264PARAM_DEBLKFILTEROFFSETBETA_OFFSET) |
+ (ctx->params.h264_constrained_intra_pred_flag <<
+ CODA_264PARAM_CONSTRAINEDINTRAPREDFLAG_OFFSET) |
+ (ctx->params.h264_chroma_qp_index_offset &
+ CODA_264PARAM_CHROMAQPOFFSET_MASK);
coda_write(dev, value, CODA_CMD_ENC_SEQ_264_PARA);
break;
case V4L2_PIX_FMT_JPEG:
@@ -2020,7 +2024,6 @@ static void coda_finish_decode(struct coda_ctx *ctx)
struct coda_q_data *q_data_dst;
struct vb2_v4l2_buffer *dst_buf;
struct coda_buffer_meta *meta;
- unsigned long payload;
int width, height;
int decoded_idx;
int display_idx;
@@ -2226,21 +2229,8 @@ static void coda_finish_decode(struct coda_ctx *ctx)
trace_coda_dec_rot_done(ctx, dst_buf, meta);
- switch (q_data_dst->fourcc) {
- case V4L2_PIX_FMT_YUYV:
- payload = width * height * 2;
- break;
- case V4L2_PIX_FMT_YUV420:
- case V4L2_PIX_FMT_YVU420:
- case V4L2_PIX_FMT_NV12:
- default:
- payload = width * height * 3 / 2;
- break;
- case V4L2_PIX_FMT_YUV422P:
- payload = width * height * 2;
- break;
- }
- vb2_set_plane_payload(&dst_buf->vb2_buf, 0, payload);
+ vb2_set_plane_payload(&dst_buf->vb2_buf, 0,
+ q_data_dst->sizeimage);
if (ctx->frame_errors[ctx->display_idx] || err_vdoa)
coda_m2m_buf_done(ctx, dst_buf, VB2_BUF_STATE_ERROR);
diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c
index 7518f01c48f7..fa0b22fb7991 100644
--- a/drivers/media/platform/coda/coda-common.c
+++ b/drivers/media/platform/coda/coda-common.c
@@ -728,7 +728,7 @@ static int coda_s_fmt(struct coda_ctx *ctx, struct v4l2_format *f,
ctx->tiled_map_type = GDI_TILED_FRAME_MB_RASTER_MAP;
break;
case V4L2_PIX_FMT_NV12:
- if (!disable_tiling) {
+ if (!disable_tiling && ctx->dev->devtype->product == CODA_960) {
ctx->tiled_map_type = GDI_TILED_FRAME_MB_RASTER_MAP;
break;
}
@@ -1839,6 +1839,12 @@ static int coda_s_ctrl(struct v4l2_ctrl *ctrl)
case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE:
ctx->params.h264_disable_deblocking_filter_idc = ctrl->val;
break;
+ case V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION:
+ ctx->params.h264_constrained_intra_pred_flag = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_CHROMA_QP_INDEX_OFFSET:
+ ctx->params.h264_chroma_qp_index_offset = ctrl->val;
+ break;
case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
/* TODO: switch between baseline and constrained baseline */
if (ctx->inst_type == CODA_INST_ENCODER)
@@ -1925,6 +1931,11 @@ static void coda_encode_ctrls(struct coda_ctx *ctx)
V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE,
V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY,
0x0, V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED);
+ v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION, 0, 1, 1,
+ 0);
+ v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_CHROMA_QP_INDEX_OFFSET, -12, 12, 1, 0);
v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops,
V4L2_CID_MPEG_VIDEO_H264_PROFILE,
V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE, 0x0,
diff --git a/drivers/media/platform/coda/coda-jpeg.c b/drivers/media/platform/coda/coda-jpeg.c
index 9f899a6cefed..39a2351c1e48 100644
--- a/drivers/media/platform/coda/coda-jpeg.c
+++ b/drivers/media/platform/coda/coda-jpeg.c
@@ -103,7 +103,7 @@ static const unsigned char chroma_ac_value[162 + 2] = {
/*
* Quantization tables for luminance and chrominance components in
- * zig-zag scan order from the Freescale i.MX VPU libaries
+ * zig-zag scan order from the Freescale i.MX VPU libraries
*/
static unsigned char luma_q[64] = {
diff --git a/drivers/media/platform/coda/coda.h b/drivers/media/platform/coda/coda.h
index 31cea72f5b2a..31c80bda2c0b 100644
--- a/drivers/media/platform/coda/coda.h
+++ b/drivers/media/platform/coda/coda.h
@@ -118,6 +118,8 @@ struct coda_params {
u8 h264_disable_deblocking_filter_idc;
s8 h264_slice_alpha_c0_offset_div2;
s8 h264_slice_beta_offset_div2;
+ bool h264_constrained_intra_pred_flag;
+ s8 h264_chroma_qp_index_offset;
u8 h264_profile_idc;
u8 h264_level_idc;
u8 mpeg4_intra_qp;
diff --git a/drivers/media/platform/davinci/isif.c b/drivers/media/platform/davinci/isif.c
index 340f8218f54d..47cecd10eb9f 100644
--- a/drivers/media/platform/davinci/isif.c
+++ b/drivers/media/platform/davinci/isif.c
@@ -328,7 +328,7 @@ static void isif_config_bclamp(struct isif_black_clamp *bc)
if (bc->en) {
val = bc->bc_mode_color << ISIF_BC_MODE_COLOR_SHIFT;
- /* Enable BC and horizontal clamp caculation paramaters */
+ /* Enable BC and horizontal clamp calculation parameters */
val = val | 1 | (bc->horz.mode << ISIF_HORZ_BC_MODE_SHIFT);
regw(val, CLAMPCFG);
@@ -358,7 +358,7 @@ static void isif_config_bclamp(struct isif_black_clamp *bc)
regw(bc->horz.win_start_v_calc, CLHWIN2);
}
- /* vertical clamp caculation paramaters */
+ /* vertical clamp calculation parameters */
/* Reset clamp value sel for previous line */
val |=
diff --git a/drivers/media/platform/davinci/vpbe.c b/drivers/media/platform/davinci/vpbe.c
index 4766a7a23d16..8339163a5231 100644
--- a/drivers/media/platform/davinci/vpbe.c
+++ b/drivers/media/platform/davinci/vpbe.c
@@ -242,7 +242,7 @@ static int vpbe_set_output(struct vpbe_device *vpbe_dev, int index)
goto unlock;
/*
- * It is assumed that venc or extenal encoder will set a default
+ * It is assumed that venc or external encoder will set a default
* mode in the sub device. For external encoder or LCD pannel output,
* we also need to set up the lcd port for the required mode. So setup
* the lcd port for the default mode that is configured in the board
diff --git a/drivers/media/platform/davinci/vpfe_capture.c b/drivers/media/platform/davinci/vpfe_capture.c
index 9996bab98fe3..26dadbba930f 100644
--- a/drivers/media/platform/davinci/vpfe_capture.c
+++ b/drivers/media/platform/davinci/vpfe_capture.c
@@ -518,7 +518,7 @@ static void vpfe_schedule_bottom_field(struct vpfe_device *vpfe_dev)
static void vpfe_process_buffer_complete(struct vpfe_device *vpfe_dev)
{
- v4l2_get_timestamp(&vpfe_dev->cur_frm->ts);
+ vpfe_dev->cur_frm->ts = ktime_get_ns();
vpfe_dev->cur_frm->state = VIDEOBUF_DONE;
vpfe_dev->cur_frm->size = vpfe_dev->fmt.fmt.pix.sizeimage;
wake_up_interruptible(&vpfe_dev->cur_frm->done);
diff --git a/drivers/media/platform/davinci/vpif.c b/drivers/media/platform/davinci/vpif.c
index 16352e2263d2..df66461f5d4f 100644
--- a/drivers/media/platform/davinci/vpif.c
+++ b/drivers/media/platform/davinci/vpif.c
@@ -1,7 +1,7 @@
/*
* vpif - Video Port Interface driver
* VPIF is a receiver and transmitter for video data. It has two channels(0, 1)
- * that receiveing video byte stream and two channels(2, 3) for video output.
+ * that receiving video byte stream and two channels(2, 3) for video output.
* The hardware supports SDTV, HDTV formats, raw data capture.
* Currently, the driver supports NTSC and PAL standards.
*
diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c
index 3517487d9760..f4b4f2a1dfc0 100644
--- a/drivers/media/platform/davinci/vpif_display.c
+++ b/drivers/media/platform/davinci/vpif_display.c
@@ -138,7 +138,7 @@ static int vpif_buffer_queue_setup(struct vb2_queue *vq,
* vpif_buffer_queue : Callback function to add buffer to DMA queue
* @vb: ptr to vb2_buffer
*
- * This callback fucntion queues the buffer to DMA engine
+ * This callback function queues the buffer to DMA engine
*/
static void vpif_buffer_queue(struct vb2_buffer *vb)
{
@@ -635,7 +635,7 @@ static int vpif_try_fmt_vid_out(struct file *file, void *priv,
struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
/*
- * to supress v4l-compliance warnings silently correct
+ * to suppress v4l-compliance warnings silently correct
* the pixelformat
*/
if (pixfmt->pixelformat != V4L2_PIX_FMT_YUV422P)
diff --git a/drivers/media/platform/exynos4-is/fimc-is-command.h b/drivers/media/platform/exynos4-is/fimc-is-command.h
index 0d1f52e394b1..b06b56b890d5 100644
--- a/drivers/media/platform/exynos4-is/fimc-is-command.h
+++ b/drivers/media/platform/exynos4-is/fimc-is-command.h
@@ -18,7 +18,7 @@
#define FIMC_IS_COMMAND_VER 110 /* FIMC-IS command set version 1.10 */
-/* Enumeration of commands beetween the FIMC-IS and the host processor. */
+/* Enumeration of commands between the FIMC-IS and the host processor. */
/* HOST to FIMC-IS */
#define HIC_PREVIEW_STILL 0x0001
diff --git a/drivers/media/platform/exynos4-is/fimc-is-param.h b/drivers/media/platform/exynos4-is/fimc-is-param.h
index 8e31f7642776..22923a3d405e 100644
--- a/drivers/media/platform/exynos4-is/fimc-is-param.h
+++ b/drivers/media/platform/exynos4-is/fimc-is-param.h
@@ -298,7 +298,7 @@ enum isp_af_mode {
#define ISP_FLASH_COMMAND_AUTO 2
#define ISP_FLASH_COMMAND_TORCH 3 /* 3 sec */
-/* Flash red-eye commads */
+/* Flash red-eye commands */
#define ISP_FLASH_REDEYE_DISABLE 0
#define ISP_FLASH_REDEYE_ENABLE 1
diff --git a/drivers/media/platform/exynos4-is/fimc-is.c b/drivers/media/platform/exynos4-is/fimc-is.c
index f5fc54de19da..02da0b06e56a 100644
--- a/drivers/media/platform/exynos4-is/fimc-is.c
+++ b/drivers/media/platform/exynos4-is/fimc-is.c
@@ -738,7 +738,7 @@ int fimc_is_hw_initialize(struct fimc_is *is)
return 0;
}
-static int fimc_is_log_show(struct seq_file *s, void *data)
+static int fimc_is_show(struct seq_file *s, void *data)
{
struct fimc_is *is = s->private;
const u8 *buf = is->memory.vaddr + FIMC_IS_DEBUG_REGION_OFFSET;
@@ -752,17 +752,7 @@ static int fimc_is_log_show(struct seq_file *s, void *data)
return 0;
}
-static int fimc_is_debugfs_open(struct inode *inode, struct file *file)
-{
- return single_open(file, fimc_is_log_show, inode->i_private);
-}
-
-static const struct file_operations fimc_is_debugfs_fops = {
- .open = fimc_is_debugfs_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(fimc_is);
static void fimc_is_debugfs_remove(struct fimc_is *is)
{
@@ -777,7 +767,7 @@ static int fimc_is_debugfs_create(struct fimc_is *is)
is->debugfs_entry = debugfs_create_dir("fimc_is", NULL);
dentry = debugfs_create_file("fw_log", S_IRUGO, is->debugfs_entry,
- is, &fimc_is_debugfs_fops);
+ is, &fimc_is_fops);
if (!dentry)
fimc_is_debugfs_remove(is);
diff --git a/drivers/media/platform/exynos4-is/fimc-isp-video.c b/drivers/media/platform/exynos4-is/fimc-isp-video.c
index de6bd28f7e31..bb35a2017f21 100644
--- a/drivers/media/platform/exynos4-is/fimc-isp-video.c
+++ b/drivers/media/platform/exynos4-is/fimc-isp-video.c
@@ -606,9 +606,7 @@ int fimc_isp_video_device_register(struct fimc_isp *isp,
vdev = &iv->ve.vdev;
memset(vdev, 0, sizeof(*vdev));
- snprintf(vdev->name, sizeof(vdev->name), "fimc-is-isp.%s",
- type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ?
- "capture" : "output");
+ strscpy(vdev->name, "fimc-is-isp.capture", sizeof(vdev->name));
vdev->queue = q;
vdev->fops = &isp_video_fops;
vdev->ioctl_ops = &isp_video_ioctl_ops;
diff --git a/drivers/media/platform/exynos4-is/media-dev.h b/drivers/media/platform/exynos4-is/media-dev.h
index 9f527670395a..a7c9490bbcb4 100644
--- a/drivers/media/platform/exynos4-is/media-dev.h
+++ b/drivers/media/platform/exynos4-is/media-dev.h
@@ -79,7 +79,7 @@ struct fimc_camclk_info {
/**
* struct fimc_sensor_info - image data source subdev information
- * @pdata: sensor's atrributes passed as media device's platform data
+ * @pdata: sensor's attributes passed as media device's platform data
* @asd: asynchronous subdev registration data structure
* @subdev: image sensor v4l2 subdev
* @host: fimc device the sensor is currently linked to
diff --git a/drivers/media/platform/exynos4-is/mipi-csis.c b/drivers/media/platform/exynos4-is/mipi-csis.c
index 35cb0162085b..234e047e3e8f 100644
--- a/drivers/media/platform/exynos4-is/mipi-csis.c
+++ b/drivers/media/platform/exynos4-is/mipi-csis.c
@@ -183,7 +183,7 @@ struct csis_drvdata {
* @index: the hardware instance index
* @pdev: CSIS platform device
* @phy: pointer to the CSIS generic PHY
- * @regs: mmaped I/O registers memory
+ * @regs: mmapped I/O registers memory
* @supplies: CSIS regulator supplies
* @clock: CSIS clocks
* @irq: requested s5p-mipi-csis irq number
@@ -745,7 +745,7 @@ static int s5pcsis_parse_dt(struct platform_device *pdev,
goto err;
}
- /* Get MIPI CSI-2 bus configration from the endpoint node. */
+ /* Get MIPI CSI-2 bus configuration from the endpoint node. */
of_property_read_u32(node, "samsung,csis-hs-settle",
&state->hs_settle);
state->wclk_ext = of_property_read_bool(node,
diff --git a/drivers/media/platform/fsl-viu.c b/drivers/media/platform/fsl-viu.c
index ca6d0317ab42..cffebcaacb90 100644
--- a/drivers/media/platform/fsl-viu.c
+++ b/drivers/media/platform/fsl-viu.c
@@ -1090,7 +1090,7 @@ static void viu_capture_intr(struct viu_dev *dev, u32 status)
if (waitqueue_active(&buf->vb.done)) {
list_del(&buf->vb.queue);
- v4l2_get_timestamp(&buf->vb.ts);
+ buf->vb.ts = ktime_get_ns();
buf->vb.state = VIDEOBUF_DONE;
buf->vb.field_count++;
wake_up(&buf->vb.done);
diff --git a/drivers/media/platform/imx-pxp.c b/drivers/media/platform/imx-pxp.c
index c1c255408d16..0bcfc5aa8f3d 100644
--- a/drivers/media/platform/imx-pxp.c
+++ b/drivers/media/platform/imx-pxp.c
@@ -90,7 +90,11 @@ static struct pxp_fmt formats[] = {
.depth = 16,
.types = MEM2MEM_CAPTURE | MEM2MEM_OUTPUT,
}, {
- .fourcc = V4L2_PIX_FMT_YUV32,
+ .fourcc = V4L2_PIX_FMT_VUYA32,
+ .depth = 32,
+ .types = MEM2MEM_CAPTURE,
+ }, {
+ .fourcc = V4L2_PIX_FMT_VUYX32,
.depth = 32,
.types = MEM2MEM_CAPTURE | MEM2MEM_OUTPUT,
}, {
@@ -236,7 +240,7 @@ static u32 pxp_v4l2_pix_fmt_to_ps_format(u32 v4l2_pix_fmt)
case V4L2_PIX_FMT_RGB555: return BV_PXP_PS_CTRL_FORMAT__RGB555;
case V4L2_PIX_FMT_RGB444: return BV_PXP_PS_CTRL_FORMAT__RGB444;
case V4L2_PIX_FMT_RGB565: return BV_PXP_PS_CTRL_FORMAT__RGB565;
- case V4L2_PIX_FMT_YUV32: return BV_PXP_PS_CTRL_FORMAT__YUV1P444;
+ case V4L2_PIX_FMT_VUYX32: return BV_PXP_PS_CTRL_FORMAT__YUV1P444;
case V4L2_PIX_FMT_UYVY: return BV_PXP_PS_CTRL_FORMAT__UYVY1P422;
case V4L2_PIX_FMT_YUYV: return BM_PXP_PS_CTRL_WB_SWAP |
BV_PXP_PS_CTRL_FORMAT__UYVY1P422;
@@ -265,7 +269,8 @@ static u32 pxp_v4l2_pix_fmt_to_out_format(u32 v4l2_pix_fmt)
case V4L2_PIX_FMT_RGB555: return BV_PXP_OUT_CTRL_FORMAT__RGB555;
case V4L2_PIX_FMT_RGB444: return BV_PXP_OUT_CTRL_FORMAT__RGB444;
case V4L2_PIX_FMT_RGB565: return BV_PXP_OUT_CTRL_FORMAT__RGB565;
- case V4L2_PIX_FMT_YUV32: return BV_PXP_OUT_CTRL_FORMAT__YUV1P444;
+ case V4L2_PIX_FMT_VUYA32:
+ case V4L2_PIX_FMT_VUYX32: return BV_PXP_OUT_CTRL_FORMAT__YUV1P444;
case V4L2_PIX_FMT_UYVY: return BV_PXP_OUT_CTRL_FORMAT__UYVY1P422;
case V4L2_PIX_FMT_VYUY: return BV_PXP_OUT_CTRL_FORMAT__VYUY1P422;
case V4L2_PIX_FMT_GREY: return BV_PXP_OUT_CTRL_FORMAT__Y8;
@@ -281,7 +286,8 @@ static u32 pxp_v4l2_pix_fmt_to_out_format(u32 v4l2_pix_fmt)
static bool pxp_v4l2_pix_fmt_is_yuv(u32 v4l2_pix_fmt)
{
switch (v4l2_pix_fmt) {
- case V4L2_PIX_FMT_YUV32:
+ case V4L2_PIX_FMT_VUYA32:
+ case V4L2_PIX_FMT_VUYX32:
case V4L2_PIX_FMT_UYVY:
case V4L2_PIX_FMT_YUYV:
case V4L2_PIX_FMT_VYUY:
@@ -680,7 +686,7 @@ static void pxp_setup_csc(struct pxp_ctx *ctx)
csc2_coef = csc2_coef_rec709_full;
else
csc2_coef = csc2_coef_rec709_lim;
- } else if (ycbcr_enc == V4L2_YCBCR_ENC_709) {
+ } else if (ycbcr_enc == V4L2_YCBCR_ENC_BT2020) {
if (quantization == V4L2_QUANTIZATION_FULL_RANGE)
csc2_coef = csc2_coef_bt2020_full;
else
diff --git a/drivers/media/platform/marvell-ccic/mmp-driver.c b/drivers/media/platform/marvell-ccic/mmp-driver.c
index 70a2833db0d1..af76eb637773 100644
--- a/drivers/media/platform/marvell-ccic/mmp-driver.c
+++ b/drivers/media/platform/marvell-ccic/mmp-driver.c
@@ -240,8 +240,8 @@ static void mmpcam_calc_dphy(struct mcam_camera *mcam)
* bit 8 ~ bit 15: HS_SETTLE
* Time interval during which the HS
* receiver shall ignore any Data Lane
- * HS transistions.
- * The vaule has been calibrated on
+ * HS transitions.
+ * The value has been calibrated on
* different boards. It seems to work well.
*
* More detail please refer
diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c b/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c
index 2a5d5002c27e..f761e4d8bf2a 100644
--- a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c
+++ b/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c
@@ -702,7 +702,7 @@ end:
v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, to_vb2_v4l2_buffer(vb));
}
-static void *mtk_jpeg_buf_remove(struct mtk_jpeg_ctx *ctx,
+static struct vb2_v4l2_buffer *mtk_jpeg_buf_remove(struct mtk_jpeg_ctx *ctx,
enum v4l2_buf_type type)
{
if (V4L2_TYPE_IS_OUTPUT(type))
@@ -714,7 +714,7 @@ static void *mtk_jpeg_buf_remove(struct mtk_jpeg_ctx *ctx,
static int mtk_jpeg_start_streaming(struct vb2_queue *q, unsigned int count)
{
struct mtk_jpeg_ctx *ctx = vb2_get_drv_priv(q);
- struct vb2_buffer *vb;
+ struct vb2_v4l2_buffer *vb;
int ret = 0;
ret = pm_runtime_get_sync(ctx->jpeg->dev);
@@ -724,14 +724,14 @@ static int mtk_jpeg_start_streaming(struct vb2_queue *q, unsigned int count)
return 0;
err:
while ((vb = mtk_jpeg_buf_remove(ctx, q->type)))
- v4l2_m2m_buf_done(to_vb2_v4l2_buffer(vb), VB2_BUF_STATE_QUEUED);
+ v4l2_m2m_buf_done(vb, VB2_BUF_STATE_QUEUED);
return ret;
}
static void mtk_jpeg_stop_streaming(struct vb2_queue *q)
{
struct mtk_jpeg_ctx *ctx = vb2_get_drv_priv(q);
- struct vb2_buffer *vb;
+ struct vb2_v4l2_buffer *vb;
/*
* STREAMOFF is an acknowledgment for source change event.
@@ -743,7 +743,7 @@ static void mtk_jpeg_stop_streaming(struct vb2_queue *q)
struct mtk_jpeg_src_buf *src_buf;
vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
- src_buf = mtk_jpeg_vb2_to_srcbuf(vb);
+ src_buf = mtk_jpeg_vb2_to_srcbuf(&vb->vb2_buf);
mtk_jpeg_set_queue_data(ctx, &src_buf->dec_param);
ctx->state = MTK_JPEG_RUNNING;
} else if (V4L2_TYPE_IS_OUTPUT(q->type)) {
@@ -751,7 +751,7 @@ static void mtk_jpeg_stop_streaming(struct vb2_queue *q)
}
while ((vb = mtk_jpeg_buf_remove(ctx, q->type)))
- v4l2_m2m_buf_done(to_vb2_v4l2_buffer(vb), VB2_BUF_STATE_ERROR);
+ v4l2_m2m_buf_done(vb, VB2_BUF_STATE_ERROR);
pm_runtime_put_sync(ctx->jpeg->dev);
}
@@ -807,7 +807,7 @@ static void mtk_jpeg_device_run(void *priv)
{
struct mtk_jpeg_ctx *ctx = priv;
struct mtk_jpeg_dev *jpeg = ctx->jpeg;
- struct vb2_buffer *src_buf, *dst_buf;
+ struct vb2_v4l2_buffer *src_buf, *dst_buf;
enum vb2_buffer_state buf_state = VB2_BUF_STATE_ERROR;
unsigned long flags;
struct mtk_jpeg_src_buf *jpeg_src_buf;
@@ -817,11 +817,11 @@ static void mtk_jpeg_device_run(void *priv)
src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
- jpeg_src_buf = mtk_jpeg_vb2_to_srcbuf(src_buf);
+ jpeg_src_buf = mtk_jpeg_vb2_to_srcbuf(&src_buf->vb2_buf);
if (jpeg_src_buf->flags & MTK_JPEG_BUF_FLAGS_LAST_FRAME) {
- for (i = 0; i < dst_buf->num_planes; i++)
- vb2_set_plane_payload(dst_buf, i, 0);
+ for (i = 0; i < dst_buf->vb2_buf.num_planes; i++)
+ vb2_set_plane_payload(&dst_buf->vb2_buf, i, 0);
buf_state = VB2_BUF_STATE_DONE;
goto dec_end;
}
@@ -833,8 +833,8 @@ static void mtk_jpeg_device_run(void *priv)
return;
}
- mtk_jpeg_set_dec_src(ctx, src_buf, &bs);
- if (mtk_jpeg_set_dec_dst(ctx, &jpeg_src_buf->dec_param, dst_buf, &fb))
+ mtk_jpeg_set_dec_src(ctx, &src_buf->vb2_buf, &bs);
+ if (mtk_jpeg_set_dec_dst(ctx, &jpeg_src_buf->dec_param, &dst_buf->vb2_buf, &fb))
goto dec_end;
spin_lock_irqsave(&jpeg->hw_lock, flags);
@@ -849,8 +849,8 @@ static void mtk_jpeg_device_run(void *priv)
dec_end:
v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
- v4l2_m2m_buf_done(to_vb2_v4l2_buffer(src_buf), buf_state);
- v4l2_m2m_buf_done(to_vb2_v4l2_buffer(dst_buf), buf_state);
+ v4l2_m2m_buf_done(src_buf, buf_state);
+ v4l2_m2m_buf_done(dst_buf, buf_state);
v4l2_m2m_job_finish(jpeg->m2m_dev, ctx->fh.m2m_ctx);
}
@@ -921,7 +921,7 @@ static irqreturn_t mtk_jpeg_dec_irq(int irq, void *priv)
{
struct mtk_jpeg_dev *jpeg = priv;
struct mtk_jpeg_ctx *ctx;
- struct vb2_buffer *src_buf, *dst_buf;
+ struct vb2_v4l2_buffer *src_buf, *dst_buf;
struct mtk_jpeg_src_buf *jpeg_src_buf;
enum vb2_buffer_state buf_state = VB2_BUF_STATE_ERROR;
u32 dec_irq_ret;
@@ -938,7 +938,7 @@ static irqreturn_t mtk_jpeg_dec_irq(int irq, void *priv)
src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
- jpeg_src_buf = mtk_jpeg_vb2_to_srcbuf(src_buf);
+ jpeg_src_buf = mtk_jpeg_vb2_to_srcbuf(&src_buf->vb2_buf);
if (dec_irq_ret >= MTK_JPEG_DEC_RESULT_UNDERFLOW)
mtk_jpeg_dec_reset(jpeg->dec_reg_base);
@@ -948,15 +948,15 @@ static irqreturn_t mtk_jpeg_dec_irq(int irq, void *priv)
goto dec_end;
}
- for (i = 0; i < dst_buf->num_planes; i++)
- vb2_set_plane_payload(dst_buf, i,
+ for (i = 0; i < dst_buf->vb2_buf.num_planes; i++)
+ vb2_set_plane_payload(&dst_buf->vb2_buf, i,
jpeg_src_buf->dec_param.comp_size[i]);
buf_state = VB2_BUF_STATE_DONE;
dec_end:
- v4l2_m2m_buf_done(to_vb2_v4l2_buffer(src_buf), buf_state);
- v4l2_m2m_buf_done(to_vb2_v4l2_buffer(dst_buf), buf_state);
+ v4l2_m2m_buf_done(src_buf, buf_state);
+ v4l2_m2m_buf_done(dst_buf, buf_state);
v4l2_m2m_job_finish(jpeg->m2m_dev, ctx->fh.m2m_ctx);
return IRQ_HANDLED;
}
diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_core.h b/drivers/media/platform/mtk-mdp/mtk_mdp_core.h
index ad1cff306efd..e5abb1abb3a3 100644
--- a/drivers/media/platform/mtk-mdp/mtk_mdp_core.h
+++ b/drivers/media/platform/mtk-mdp/mtk_mdp_core.h
@@ -41,7 +41,7 @@
#define MTK_MDP_CTX_ERROR BIT(5)
/**
- * struct mtk_mdp_pix_align - alignement of image
+ * struct mtk_mdp_pix_align - alignment of image
* @org_w: source alignment of width
* @org_h: source alignment of height
* @target_w: dst alignment of width
@@ -122,8 +122,8 @@ struct mtk_mdp_frame {
/**
* struct mtk_mdp_variant - image processor variant information
* @pix_max: maximum limit of image size
- * @pix_min: minimun limit of image size
- * @pix_align: alignement of image
+ * @pix_min: minimum limit of image size
+ * @pix_align: alignment of image
* @h_scale_up_max: maximum scale-up in horizontal
* @v_scale_up_max: maximum scale-up in vertical
* @h_scale_down_max: maximum scale-down in horizontal
diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c b/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c
index 51a13466261e..7d15c06e9db9 100644
--- a/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c
+++ b/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c
@@ -473,20 +473,17 @@ static void mtk_mdp_prepare_addr(struct mtk_mdp_ctx *ctx,
static void mtk_mdp_m2m_get_bufs(struct mtk_mdp_ctx *ctx)
{
struct mtk_mdp_frame *s_frame, *d_frame;
- struct vb2_buffer *src_vb, *dst_vb;
struct vb2_v4l2_buffer *src_vbuf, *dst_vbuf;
s_frame = &ctx->s_frame;
d_frame = &ctx->d_frame;
- src_vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
- mtk_mdp_prepare_addr(ctx, src_vb, s_frame, &s_frame->addr);
+ src_vbuf = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
+ mtk_mdp_prepare_addr(ctx, &src_vbuf->vb2_buf, s_frame, &s_frame->addr);
- dst_vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
- mtk_mdp_prepare_addr(ctx, dst_vb, d_frame, &d_frame->addr);
+ dst_vbuf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
+ mtk_mdp_prepare_addr(ctx, &dst_vbuf->vb2_buf, d_frame, &d_frame->addr);
- src_vbuf = to_vb2_v4l2_buffer(src_vb);
- dst_vbuf = to_vb2_v4l2_buffer(dst_vb);
dst_vbuf->vb2_buf.timestamp = src_vbuf->vb2_buf.timestamp;
}
@@ -494,17 +491,14 @@ static void mtk_mdp_process_done(void *priv, int vb_state)
{
struct mtk_mdp_dev *mdp = priv;
struct mtk_mdp_ctx *ctx;
- struct vb2_buffer *src_vb, *dst_vb;
- struct vb2_v4l2_buffer *src_vbuf = NULL, *dst_vbuf = NULL;
+ struct vb2_v4l2_buffer *src_vbuf, *dst_vbuf;
ctx = v4l2_m2m_get_curr_priv(mdp->m2m_dev);
if (!ctx)
return;
- src_vb = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
- src_vbuf = to_vb2_v4l2_buffer(src_vb);
- dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
- dst_vbuf = to_vb2_v4l2_buffer(dst_vb);
+ src_vbuf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
+ dst_vbuf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
dst_vbuf->vb2_buf.timestamp = src_vbuf->vb2_buf.timestamp;
dst_vbuf->timecode = src_vbuf->timecode;
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c
index ba619647bc10..d022c65bb34c 100644
--- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c
+++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c
@@ -325,13 +325,12 @@ static void mtk_vdec_worker(struct work_struct *work)
struct mtk_vcodec_ctx *ctx = container_of(work, struct mtk_vcodec_ctx,
decode_work);
struct mtk_vcodec_dev *dev = ctx->dev;
- struct vb2_buffer *src_buf, *dst_buf;
+ struct vb2_v4l2_buffer *src_buf, *dst_buf;
struct mtk_vcodec_mem buf;
struct vdec_fb *pfb;
bool res_chg = false;
int ret;
struct mtk_video_dec_buf *dst_buf_info, *src_buf_info;
- struct vb2_v4l2_buffer *dst_vb2_v4l2, *src_vb2_v4l2;
src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
if (src_buf == NULL) {
@@ -347,26 +346,23 @@ static void mtk_vdec_worker(struct work_struct *work)
return;
}
- src_vb2_v4l2 = container_of(src_buf, struct vb2_v4l2_buffer, vb2_buf);
- src_buf_info = container_of(src_vb2_v4l2, struct mtk_video_dec_buf, vb);
-
- dst_vb2_v4l2 = container_of(dst_buf, struct vb2_v4l2_buffer, vb2_buf);
- dst_buf_info = container_of(dst_vb2_v4l2, struct mtk_video_dec_buf, vb);
+ src_buf_info = container_of(src_buf, struct mtk_video_dec_buf, vb);
+ dst_buf_info = container_of(dst_buf, struct mtk_video_dec_buf, vb);
pfb = &dst_buf_info->frame_buffer;
- pfb->base_y.va = vb2_plane_vaddr(dst_buf, 0);
- pfb->base_y.dma_addr = vb2_dma_contig_plane_dma_addr(dst_buf, 0);
+ pfb->base_y.va = vb2_plane_vaddr(&dst_buf->vb2_buf, 0);
+ pfb->base_y.dma_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0);
pfb->base_y.size = ctx->picinfo.y_bs_sz + ctx->picinfo.y_len_sz;
- pfb->base_c.va = vb2_plane_vaddr(dst_buf, 1);
- pfb->base_c.dma_addr = vb2_dma_contig_plane_dma_addr(dst_buf, 1);
+ pfb->base_c.va = vb2_plane_vaddr(&dst_buf->vb2_buf, 1);
+ pfb->base_c.dma_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 1);
pfb->base_c.size = ctx->picinfo.c_bs_sz + ctx->picinfo.c_len_sz;
pfb->status = 0;
mtk_v4l2_debug(3, "===>[%d] vdec_if_decode() ===>", ctx->id);
mtk_v4l2_debug(3,
"id=%d Framebuf pfb=%p VA=%p Y_DMA=%pad C_DMA=%pad Size=%zx",
- dst_buf->index, pfb,
+ dst_buf->vb2_buf.index, pfb,
pfb->base_y.va, &pfb->base_y.dma_addr,
&pfb->base_c.dma_addr, pfb->base_y.size);
@@ -384,19 +380,19 @@ static void mtk_vdec_worker(struct work_struct *work)
clean_display_buffer(ctx);
vb2_set_plane_payload(&dst_buf_info->vb.vb2_buf, 0, 0);
vb2_set_plane_payload(&dst_buf_info->vb.vb2_buf, 1, 0);
- dst_vb2_v4l2->flags |= V4L2_BUF_FLAG_LAST;
+ dst_buf->flags |= V4L2_BUF_FLAG_LAST;
v4l2_m2m_buf_done(&dst_buf_info->vb, VB2_BUF_STATE_DONE);
clean_free_buffer(ctx);
v4l2_m2m_job_finish(dev->m2m_dev_dec, ctx->m2m_ctx);
return;
}
- buf.va = vb2_plane_vaddr(src_buf, 0);
- buf.dma_addr = vb2_dma_contig_plane_dma_addr(src_buf, 0);
+ buf.va = vb2_plane_vaddr(&src_buf->vb2_buf, 0);
+ buf.dma_addr = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0);
buf.size = (size_t)src_buf->planes[0].bytesused;
if (!buf.va) {
v4l2_m2m_job_finish(dev->m2m_dev_dec, ctx->m2m_ctx);
mtk_v4l2_err("[%d] id=%d src_addr is NULL!!",
- ctx->id, src_buf->index);
+ ctx->id, src_buf->vb2_buf.index);
return;
}
mtk_v4l2_debug(3, "[%d] Bitstream VA=%p DMA=%pad Size=%zx vb=%p",
@@ -416,10 +412,10 @@ static void mtk_vdec_worker(struct work_struct *work)
mtk_v4l2_err(
" <===[%d], src_buf[%d] sz=0x%zx pts=%llu dst_buf[%d] vdec_if_decode() ret=%d res_chg=%d===>",
ctx->id,
- src_buf->index,
+ src_buf->vb2_buf.index,
buf.size,
src_buf_info->vb.vb2_buf.timestamp,
- dst_buf->index,
+ dst_buf->vb2_buf.index,
ret, res_chg);
src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
if (ret == -EIO) {
@@ -1103,7 +1099,7 @@ static int vb2ops_vdec_buf_prepare(struct vb2_buffer *vb)
static void vb2ops_vdec_buf_queue(struct vb2_buffer *vb)
{
- struct vb2_buffer *src_buf;
+ struct vb2_v4l2_buffer *src_buf;
struct mtk_vcodec_mem src_mem;
bool res_chg = false;
int ret = 0;
@@ -1149,8 +1145,7 @@ static void vb2ops_vdec_buf_queue(struct vb2_buffer *vb)
mtk_v4l2_err("No src buffer");
return;
}
- vb2_v4l2 = to_vb2_v4l2_buffer(src_buf);
- buf = container_of(vb2_v4l2, struct mtk_video_dec_buf, vb);
+ buf = container_of(src_buf, struct mtk_video_dec_buf, vb);
if (buf->lastframe) {
/* This shouldn't happen. Just in case. */
mtk_v4l2_err("Invalid flush buffer.");
@@ -1158,8 +1153,8 @@ static void vb2ops_vdec_buf_queue(struct vb2_buffer *vb)
return;
}
- src_mem.va = vb2_plane_vaddr(src_buf, 0);
- src_mem.dma_addr = vb2_dma_contig_plane_dma_addr(src_buf, 0);
+ src_mem.va = vb2_plane_vaddr(&src_buf->vb2_buf, 0);
+ src_mem.dma_addr = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0);
src_mem.size = (size_t)src_buf->planes[0].bytesused;
mtk_v4l2_debug(2,
"[%d] buf id=%d va=%p dma=%pad size=%zx",
@@ -1170,7 +1165,7 @@ static void vb2ops_vdec_buf_queue(struct vb2_buffer *vb)
ret = vdec_if_decode(ctx, &src_mem, NULL, &res_chg);
if (ret || !res_chg) {
/*
- * fb == NULL menas to parse SPS/PPS header or
+ * fb == NULL means to parse SPS/PPS header or
* resolution info in src_mem. Decode can fail
* if there is no SPS header or picture info
* in bs
@@ -1181,11 +1176,9 @@ static void vb2ops_vdec_buf_queue(struct vb2_buffer *vb)
mtk_v4l2_err("[%d] Unrecoverable error in vdec_if_decode.",
ctx->id);
ctx->state = MTK_STATE_ABORT;
- v4l2_m2m_buf_done(to_vb2_v4l2_buffer(src_buf),
- VB2_BUF_STATE_ERROR);
+ v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR);
} else {
- v4l2_m2m_buf_done(to_vb2_v4l2_buffer(src_buf),
- VB2_BUF_STATE_DONE);
+ v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE);
}
mtk_v4l2_debug(ret ? 0 : 1,
"[%d] vdec_if_decode() src_buf=%d, size=%zu, fail=%d, res_chg=%d",
@@ -1281,7 +1274,7 @@ static int vb2ops_vdec_start_streaming(struct vb2_queue *q, unsigned int count)
static void vb2ops_vdec_stop_streaming(struct vb2_queue *q)
{
- struct vb2_buffer *src_buf = NULL, *dst_buf = NULL;
+ struct vb2_v4l2_buffer *src_buf = NULL, *dst_buf = NULL;
struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(q);
mtk_v4l2_debug(3, "[%d] (%d) state=(%x) ctx->decoded_frame_cnt=%d",
@@ -1289,12 +1282,10 @@ static void vb2ops_vdec_stop_streaming(struct vb2_queue *q)
if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
while ((src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx))) {
- struct vb2_v4l2_buffer *vb2_v4l2 =
- to_vb2_v4l2_buffer(src_buf);
struct mtk_video_dec_buf *buf_info = container_of(
- vb2_v4l2, struct mtk_video_dec_buf, vb);
+ src_buf, struct mtk_video_dec_buf, vb);
if (!buf_info->lastframe)
- v4l2_m2m_buf_done(vb2_v4l2,
+ v4l2_m2m_buf_done(src_buf,
VB2_BUF_STATE_ERROR);
}
return;
@@ -1323,10 +1314,9 @@ static void vb2ops_vdec_stop_streaming(struct vb2_queue *q)
ctx->state = MTK_STATE_FLUSH;
while ((dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx))) {
- vb2_set_plane_payload(dst_buf, 0, 0);
- vb2_set_plane_payload(dst_buf, 1, 0);
- v4l2_m2m_buf_done(to_vb2_v4l2_buffer(dst_buf),
- VB2_BUF_STATE_ERROR);
+ vb2_set_plane_payload(&dst_buf->vb2_buf, 0, 0);
+ vb2_set_plane_payload(&dst_buf->vb2_buf, 1, 0);
+ v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR);
}
}
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.c
index 79ca03ac449c..7884465afcd2 100644
--- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.c
+++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.c
@@ -27,11 +27,14 @@ int mtk_vcodec_init_dec_pm(struct mtk_vcodec_dev *mtkdev)
struct device_node *node;
struct platform_device *pdev;
struct mtk_vcodec_pm *pm;
- int ret = 0;
+ struct mtk_vcodec_clk *dec_clk;
+ struct mtk_vcodec_clk_info *clk_info;
+ int i = 0, ret = 0;
pdev = mtkdev->plat_dev;
pm = &mtkdev->pm;
pm->mtkdev = mtkdev;
+ dec_clk = &pm->vdec_clk;
node = of_parse_phandle(pdev->dev.of_node, "mediatek,larb", 0);
if (!node) {
mtk_v4l2_err("of_parse_phandle mediatek,larb fail!");
@@ -47,52 +50,34 @@ int mtk_vcodec_init_dec_pm(struct mtk_vcodec_dev *mtkdev)
pdev = mtkdev->plat_dev;
pm->dev = &pdev->dev;
- pm->vcodecpll = devm_clk_get(&pdev->dev, "vcodecpll");
- if (IS_ERR(pm->vcodecpll)) {
- mtk_v4l2_err("devm_clk_get vcodecpll fail");
- ret = PTR_ERR(pm->vcodecpll);
+ dec_clk->clk_num =
+ of_property_count_strings(pdev->dev.of_node, "clock-names");
+ if (dec_clk->clk_num > 0) {
+ dec_clk->clk_info = devm_kcalloc(&pdev->dev,
+ dec_clk->clk_num, sizeof(*clk_info),
+ GFP_KERNEL);
+ if (!dec_clk->clk_info)
+ return -ENOMEM;
+ } else {
+ mtk_v4l2_err("Failed to get vdec clock count");
+ return -EINVAL;
}
- pm->univpll_d2 = devm_clk_get(&pdev->dev, "univpll_d2");
- if (IS_ERR(pm->univpll_d2)) {
- mtk_v4l2_err("devm_clk_get univpll_d2 fail");
- ret = PTR_ERR(pm->univpll_d2);
- }
-
- pm->clk_cci400_sel = devm_clk_get(&pdev->dev, "clk_cci400_sel");
- if (IS_ERR(pm->clk_cci400_sel)) {
- mtk_v4l2_err("devm_clk_get clk_cci400_sel fail");
- ret = PTR_ERR(pm->clk_cci400_sel);
- }
-
- pm->vdec_sel = devm_clk_get(&pdev->dev, "vdec_sel");
- if (IS_ERR(pm->vdec_sel)) {
- mtk_v4l2_err("devm_clk_get vdec_sel fail");
- ret = PTR_ERR(pm->vdec_sel);
- }
-
- pm->vdecpll = devm_clk_get(&pdev->dev, "vdecpll");
- if (IS_ERR(pm->vdecpll)) {
- mtk_v4l2_err("devm_clk_get vdecpll fail");
- ret = PTR_ERR(pm->vdecpll);
- }
-
- pm->vencpll = devm_clk_get(&pdev->dev, "vencpll");
- if (IS_ERR(pm->vencpll)) {
- mtk_v4l2_err("devm_clk_get vencpll fail");
- ret = PTR_ERR(pm->vencpll);
- }
-
- pm->venc_lt_sel = devm_clk_get(&pdev->dev, "venc_lt_sel");
- if (IS_ERR(pm->venc_lt_sel)) {
- mtk_v4l2_err("devm_clk_get venc_lt_sel fail");
- ret = PTR_ERR(pm->venc_lt_sel);
- }
-
- pm->vdec_bus_clk_src = devm_clk_get(&pdev->dev, "vdec_bus_clk_src");
- if (IS_ERR(pm->vdec_bus_clk_src)) {
- mtk_v4l2_err("devm_clk_get vdec_bus_clk_src");
- ret = PTR_ERR(pm->vdec_bus_clk_src);
+ for (i = 0; i < dec_clk->clk_num; i++) {
+ clk_info = &dec_clk->clk_info[i];
+ ret = of_property_read_string_index(pdev->dev.of_node,
+ "clock-names", i, &clk_info->clk_name);
+ if (ret) {
+ mtk_v4l2_err("Failed to get clock name id = %d", i);
+ return ret;
+ }
+ clk_info->vcodec_clk = devm_clk_get(&pdev->dev,
+ clk_info->clk_name);
+ if (IS_ERR(clk_info->vcodec_clk)) {
+ mtk_v4l2_err("devm_clk_get (%d)%s fail", i,
+ clk_info->clk_name);
+ return PTR_ERR(clk_info->vcodec_clk);
+ }
}
pm_runtime_enable(&pdev->dev);
@@ -125,78 +110,36 @@ void mtk_vcodec_dec_pw_off(struct mtk_vcodec_pm *pm)
void mtk_vcodec_dec_clock_on(struct mtk_vcodec_pm *pm)
{
- int ret;
-
- ret = clk_set_rate(pm->vcodecpll, 1482 * 1000000);
- if (ret)
- mtk_v4l2_err("clk_set_rate vcodecpll fail %d", ret);
-
- ret = clk_set_rate(pm->vencpll, 800 * 1000000);
- if (ret)
- mtk_v4l2_err("clk_set_rate vencpll fail %d", ret);
-
- ret = clk_prepare_enable(pm->vcodecpll);
- if (ret)
- mtk_v4l2_err("clk_prepare_enable vcodecpll fail %d", ret);
-
- ret = clk_prepare_enable(pm->vencpll);
- if (ret)
- mtk_v4l2_err("clk_prepare_enable vencpll fail %d", ret);
-
- ret = clk_prepare_enable(pm->vdec_bus_clk_src);
- if (ret)
- mtk_v4l2_err("clk_prepare_enable vdec_bus_clk_src fail %d",
- ret);
-
- ret = clk_prepare_enable(pm->venc_lt_sel);
- if (ret)
- mtk_v4l2_err("clk_prepare_enable venc_lt_sel fail %d", ret);
-
- ret = clk_set_parent(pm->venc_lt_sel, pm->vdec_bus_clk_src);
- if (ret)
- mtk_v4l2_err("clk_set_parent venc_lt_sel vdec_bus_clk_src fail %d",
- ret);
-
- ret = clk_prepare_enable(pm->univpll_d2);
- if (ret)
- mtk_v4l2_err("clk_prepare_enable univpll_d2 fail %d", ret);
-
- ret = clk_prepare_enable(pm->clk_cci400_sel);
- if (ret)
- mtk_v4l2_err("clk_prepare_enable clk_cci400_sel fail %d", ret);
-
- ret = clk_set_parent(pm->clk_cci400_sel, pm->univpll_d2);
- if (ret)
- mtk_v4l2_err("clk_set_parent clk_cci400_sel univpll_d2 fail %d",
- ret);
-
- ret = clk_prepare_enable(pm->vdecpll);
- if (ret)
- mtk_v4l2_err("clk_prepare_enable vdecpll fail %d", ret);
-
- ret = clk_prepare_enable(pm->vdec_sel);
- if (ret)
- mtk_v4l2_err("clk_prepare_enable vdec_sel fail %d", ret);
-
- ret = clk_set_parent(pm->vdec_sel, pm->vdecpll);
- if (ret)
- mtk_v4l2_err("clk_set_parent vdec_sel vdecpll fail %d", ret);
+ struct mtk_vcodec_clk *dec_clk = &pm->vdec_clk;
+ int ret, i = 0;
+
+ for (i = 0; i < dec_clk->clk_num; i++) {
+ ret = clk_prepare_enable(dec_clk->clk_info[i].vcodec_clk);
+ if (ret) {
+ mtk_v4l2_err("clk_prepare_enable %d %s fail %d", i,
+ dec_clk->clk_info[i].clk_name, ret);
+ goto error;
+ }
+ }
ret = mtk_smi_larb_get(pm->larbvdec);
- if (ret)
+ if (ret) {
mtk_v4l2_err("mtk_smi_larb_get larbvdec fail %d", ret);
+ goto error;
+ }
+ return;
+error:
+ for (i -= 1; i >= 0; i--)
+ clk_disable_unprepare(dec_clk->clk_info[i].vcodec_clk);
}
void mtk_vcodec_dec_clock_off(struct mtk_vcodec_pm *pm)
{
+ struct mtk_vcodec_clk *dec_clk = &pm->vdec_clk;
+ int i = 0;
+
mtk_smi_larb_put(pm->larbvdec);
- clk_disable_unprepare(pm->vdec_sel);
- clk_disable_unprepare(pm->vdecpll);
- clk_disable_unprepare(pm->univpll_d2);
- clk_disable_unprepare(pm->clk_cci400_sel);
- clk_disable_unprepare(pm->venc_lt_sel);
- clk_disable_unprepare(pm->vdec_bus_clk_src);
- clk_disable_unprepare(pm->vencpll);
- clk_disable_unprepare(pm->vcodecpll);
+ for (i = dec_clk->clk_num - 1; i >= 0; i--)
+ clk_disable_unprepare(dec_clk->clk_info[i].vcodec_clk);
}
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h b/drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h
index 3cffb381ac8e..e7e2a108def9 100644
--- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h
+++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h
@@ -151,9 +151,9 @@ struct mtk_q_data {
* @intra_period: I frame period
* @gop_size: group of picture size, it's used as the intra frame period
* @framerate_num: frame rate numerator. ex: framerate_num=30 and
- * framerate_denom=1 menas FPS is 30
+ * framerate_denom=1 means FPS is 30
* @framerate_denom: frame rate denominator. ex: framerate_num=30 and
- * framerate_denom=1 menas FPS is 30
+ * framerate_denom=1 means FPS is 30
* @h264_max_qp: Max value for H.264 quantization parameter
* @h264_profile: V4L2 defined H.264 profile
* @h264_level: V4L2 defined H.264 level
@@ -176,22 +176,29 @@ struct mtk_enc_params {
};
/**
+ * struct mtk_vcodec_clk_info - Structure used to store clock name
+ */
+struct mtk_vcodec_clk_info {
+ const char *clk_name;
+ struct clk *vcodec_clk;
+};
+
+/**
+ * struct mtk_vcodec_clk - Structure used to store vcodec clock information
+ */
+struct mtk_vcodec_clk {
+ struct mtk_vcodec_clk_info *clk_info;
+ int clk_num;
+};
+
+/**
* struct mtk_vcodec_pm - Power management data structure
*/
struct mtk_vcodec_pm {
- struct clk *vdec_bus_clk_src;
- struct clk *vencpll;
-
- struct clk *vcodecpll;
- struct clk *univpll_d2;
- struct clk *clk_cci400_sel;
- struct clk *vdecpll;
- struct clk *vdec_sel;
- struct clk *vencpll_d2;
- struct clk *venc_sel;
- struct clk *univpll1_d2;
- struct clk *venc_lt_sel;
+ struct mtk_vcodec_clk vdec_clk;
struct device *larbvdec;
+
+ struct mtk_vcodec_clk venc_clk;
struct device *larbvenc;
struct device *larbvenclt;
struct device *dev;
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c
index d1f12257bf66..c6b48b5925fb 100644
--- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c
+++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c
@@ -393,7 +393,7 @@ static void mtk_venc_set_param(struct mtk_vcodec_ctx *ctx,
param->input_yuv_fmt = VENC_YUV_FORMAT_NV21;
break;
default:
- mtk_v4l2_err("Unsupport fourcc =%d", q_data_src->fmt->fourcc);
+ mtk_v4l2_err("Unsupported fourcc =%d", q_data_src->fmt->fourcc);
break;
}
param->h264_profile = enc_params->h264_profile;
@@ -887,7 +887,7 @@ err_set_param:
static void vb2ops_venc_stop_streaming(struct vb2_queue *q)
{
struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(q);
- struct vb2_buffer *src_buf, *dst_buf;
+ struct vb2_v4l2_buffer *src_buf, *dst_buf;
int ret;
mtk_v4l2_debug(2, "[%d]-> type=%d", ctx->id, q->type);
@@ -895,13 +895,11 @@ static void vb2ops_venc_stop_streaming(struct vb2_queue *q)
if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
while ((dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx))) {
dst_buf->planes[0].bytesused = 0;
- v4l2_m2m_buf_done(to_vb2_v4l2_buffer(dst_buf),
- VB2_BUF_STATE_ERROR);
+ v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR);
}
} else {
while ((src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx)))
- v4l2_m2m_buf_done(to_vb2_v4l2_buffer(src_buf),
- VB2_BUF_STATE_ERROR);
+ v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR);
}
if ((q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
@@ -937,8 +935,7 @@ static int mtk_venc_encode_header(void *priv)
{
struct mtk_vcodec_ctx *ctx = priv;
int ret;
- struct vb2_buffer *src_buf, *dst_buf;
- struct vb2_v4l2_buffer *dst_vb2_v4l2, *src_vb2_v4l2;
+ struct vb2_v4l2_buffer *src_buf, *dst_buf;
struct mtk_vcodec_mem bs_buf;
struct venc_done_result enc_result;
@@ -948,14 +945,14 @@ static int mtk_venc_encode_header(void *priv)
return -EINVAL;
}
- bs_buf.va = vb2_plane_vaddr(dst_buf, 0);
- bs_buf.dma_addr = vb2_dma_contig_plane_dma_addr(dst_buf, 0);
+ bs_buf.va = vb2_plane_vaddr(&dst_buf->vb2_buf, 0);
+ bs_buf.dma_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0);
bs_buf.size = (size_t)dst_buf->planes[0].length;
mtk_v4l2_debug(1,
"[%d] buf id=%d va=0x%p dma_addr=0x%llx size=%zu",
ctx->id,
- dst_buf->index, bs_buf.va,
+ dst_buf->vb2_buf.index, bs_buf.va,
(u64)bs_buf.dma_addr,
bs_buf.size);
@@ -964,26 +961,23 @@ static int mtk_venc_encode_header(void *priv)
NULL, &bs_buf, &enc_result);
if (ret) {
- dst_buf->planes[0].bytesused = 0;
+ dst_buf->vb2_buf.planes[0].bytesused = 0;
ctx->state = MTK_STATE_ABORT;
- v4l2_m2m_buf_done(to_vb2_v4l2_buffer(dst_buf),
- VB2_BUF_STATE_ERROR);
+ v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR);
mtk_v4l2_err("venc_if_encode failed=%d", ret);
return -EINVAL;
}
src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
if (src_buf) {
- src_vb2_v4l2 = to_vb2_v4l2_buffer(src_buf);
- dst_vb2_v4l2 = to_vb2_v4l2_buffer(dst_buf);
- dst_buf->timestamp = src_buf->timestamp;
- dst_vb2_v4l2->timecode = src_vb2_v4l2->timecode;
+ dst_buf->vb2_buf.timestamp = src_buf->vb2_buf.timestamp;
+ dst_buf->timecode = src_buf->timecode;
} else {
mtk_v4l2_err("No timestamp for the header buffer.");
}
ctx->state = MTK_STATE_HEADER;
dst_buf->planes[0].bytesused = enc_result.bs_size;
- v4l2_m2m_buf_done(to_vb2_v4l2_buffer(dst_buf), VB2_BUF_STATE_DONE);
+ v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE);
return 0;
}
@@ -991,9 +985,7 @@ static int mtk_venc_encode_header(void *priv)
static int mtk_venc_param_change(struct mtk_vcodec_ctx *ctx)
{
struct venc_enc_param enc_prm;
- struct vb2_buffer *vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
- struct vb2_v4l2_buffer *vb2_v4l2 =
- container_of(vb, struct vb2_v4l2_buffer, vb2_buf);
+ struct vb2_v4l2_buffer *vb2_v4l2 = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
struct mtk_video_enc_buf *mtk_buf =
container_of(vb2_v4l2, struct mtk_video_enc_buf, vb);
@@ -1067,12 +1059,11 @@ static void mtk_venc_worker(struct work_struct *work)
{
struct mtk_vcodec_ctx *ctx = container_of(work, struct mtk_vcodec_ctx,
encode_work);
- struct vb2_buffer *src_buf, *dst_buf;
+ struct vb2_v4l2_buffer *src_buf, *dst_buf;
struct venc_frm_buf frm_buf;
struct mtk_vcodec_mem bs_buf;
struct venc_done_result enc_result;
int ret, i;
- struct vb2_v4l2_buffer *dst_vb2_v4l2, *src_vb2_v4l2;
/* check dst_buf, dst_buf may be removed in device_run
* to stored encdoe header so we need check dst_buf and
@@ -1086,15 +1077,15 @@ static void mtk_venc_worker(struct work_struct *work)
src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
memset(&frm_buf, 0, sizeof(frm_buf));
- for (i = 0; i < src_buf->num_planes ; i++) {
+ for (i = 0; i < src_buf->vb2_buf.num_planes ; i++) {
frm_buf.fb_addr[i].dma_addr =
- vb2_dma_contig_plane_dma_addr(src_buf, i);
+ vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, i);
frm_buf.fb_addr[i].size =
- (size_t)src_buf->planes[i].length;
+ (size_t)src_buf->vb2_buf.planes[i].length;
}
- bs_buf.va = vb2_plane_vaddr(dst_buf, 0);
- bs_buf.dma_addr = vb2_dma_contig_plane_dma_addr(dst_buf, 0);
- bs_buf.size = (size_t)dst_buf->planes[0].length;
+ bs_buf.va = vb2_plane_vaddr(&dst_buf->vb2_buf, 0);
+ bs_buf.dma_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0);
+ bs_buf.size = (size_t)dst_buf->vb2_buf.planes[0].length;
mtk_v4l2_debug(2,
"Framebuf PA=%llx Size=0x%zx;PA=0x%llx Size=0x%zx;PA=0x%llx Size=%zu",
@@ -1108,28 +1099,21 @@ static void mtk_venc_worker(struct work_struct *work)
ret = venc_if_encode(ctx, VENC_START_OPT_ENCODE_FRAME,
&frm_buf, &bs_buf, &enc_result);
- src_vb2_v4l2 = to_vb2_v4l2_buffer(src_buf);
- dst_vb2_v4l2 = to_vb2_v4l2_buffer(dst_buf);
-
- dst_buf->timestamp = src_buf->timestamp;
- dst_vb2_v4l2->timecode = src_vb2_v4l2->timecode;
+ dst_buf->vb2_buf.timestamp = src_buf->vb2_buf.timestamp;
+ dst_buf->timecode = src_buf->timecode;
if (enc_result.is_key_frm)
- dst_vb2_v4l2->flags |= V4L2_BUF_FLAG_KEYFRAME;
+ dst_buf->flags |= V4L2_BUF_FLAG_KEYFRAME;
if (ret) {
- v4l2_m2m_buf_done(to_vb2_v4l2_buffer(src_buf),
- VB2_BUF_STATE_ERROR);
+ v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR);
dst_buf->planes[0].bytesused = 0;
- v4l2_m2m_buf_done(to_vb2_v4l2_buffer(dst_buf),
- VB2_BUF_STATE_ERROR);
+ v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR);
mtk_v4l2_err("venc_if_encode failed=%d", ret);
} else {
- v4l2_m2m_buf_done(to_vb2_v4l2_buffer(src_buf),
- VB2_BUF_STATE_DONE);
+ v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE);
dst_buf->planes[0].bytesused = enc_result.bs_size;
- v4l2_m2m_buf_done(to_vb2_v4l2_buffer(dst_buf),
- VB2_BUF_STATE_DONE);
+ v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE);
mtk_v4l2_debug(2, "venc_if_encode bs size=%d",
enc_result.bs_size);
}
@@ -1137,7 +1121,7 @@ static void mtk_venc_worker(struct work_struct *work)
v4l2_m2m_job_finish(ctx->dev->m2m_dev_enc, ctx->m2m_ctx);
mtk_v4l2_debug(1, "<=== src_buf[%d] dst_buf[%d] venc_if_encode ret=%d Size=%u===>",
- src_buf->index, dst_buf->index, ret,
+ src_buf->vb2_buf.index, dst_buf->vb2_buf.index, ret,
enc_result.bs_size);
}
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c
index 7c025045ea90..39375b8ea27c 100644
--- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c
+++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c
@@ -27,9 +27,11 @@ int mtk_vcodec_init_enc_pm(struct mtk_vcodec_dev *mtkdev)
{
struct device_node *node;
struct platform_device *pdev;
- struct device *dev;
struct mtk_vcodec_pm *pm;
- int ret = 0;
+ struct mtk_vcodec_clk *enc_clk;
+ struct mtk_vcodec_clk_info *clk_info;
+ int ret = 0, i = 0;
+ struct device *dev;
pdev = mtkdev->plat_dev;
pm = &mtkdev->pm;
@@ -37,6 +39,7 @@ int mtk_vcodec_init_enc_pm(struct mtk_vcodec_dev *mtkdev)
pm->mtkdev = mtkdev;
pm->dev = &pdev->dev;
dev = &pdev->dev;
+ enc_clk = &pm->venc_clk;
node = of_parse_phandle(dev->of_node, "mediatek,larb", 0);
if (!node) {
@@ -68,28 +71,34 @@ int mtk_vcodec_init_enc_pm(struct mtk_vcodec_dev *mtkdev)
pdev = mtkdev->plat_dev;
pm->dev = &pdev->dev;
- pm->vencpll_d2 = devm_clk_get(&pdev->dev, "venc_sel_src");
- if (IS_ERR(pm->vencpll_d2)) {
- mtk_v4l2_err("devm_clk_get vencpll_d2 fail");
- ret = PTR_ERR(pm->vencpll_d2);
- }
-
- pm->venc_sel = devm_clk_get(&pdev->dev, "venc_sel");
- if (IS_ERR(pm->venc_sel)) {
- mtk_v4l2_err("devm_clk_get venc_sel fail");
- ret = PTR_ERR(pm->venc_sel);
+ enc_clk->clk_num = of_property_count_strings(pdev->dev.of_node,
+ "clock-names");
+ if (enc_clk->clk_num > 0) {
+ enc_clk->clk_info = devm_kcalloc(&pdev->dev,
+ enc_clk->clk_num, sizeof(*clk_info),
+ GFP_KERNEL);
+ if (!enc_clk->clk_info)
+ return -ENOMEM;
+ } else {
+ mtk_v4l2_err("Failed to get venc clock count");
+ return -EINVAL;
}
- pm->univpll1_d2 = devm_clk_get(&pdev->dev, "venc_lt_sel_src");
- if (IS_ERR(pm->univpll1_d2)) {
- mtk_v4l2_err("devm_clk_get univpll1_d2 fail");
- ret = PTR_ERR(pm->univpll1_d2);
- }
-
- pm->venc_lt_sel = devm_clk_get(&pdev->dev, "venc_lt_sel");
- if (IS_ERR(pm->venc_lt_sel)) {
- mtk_v4l2_err("devm_clk_get venc_lt_sel fail");
- ret = PTR_ERR(pm->venc_lt_sel);
+ for (i = 0; i < enc_clk->clk_num; i++) {
+ clk_info = &enc_clk->clk_info[i];
+ ret = of_property_read_string_index(pdev->dev.of_node,
+ "clock-names", i, &clk_info->clk_name);
+ if (ret) {
+ mtk_v4l2_err("venc failed to get clk name %d", i);
+ return ret;
+ }
+ clk_info->vcodec_clk = devm_clk_get(&pdev->dev,
+ clk_info->clk_name);
+ if (IS_ERR(clk_info->vcodec_clk)) {
+ mtk_v4l2_err("venc devm_clk_get (%d)%s fail", i,
+ clk_info->clk_name);
+ return PTR_ERR(clk_info->vcodec_clk);
+ }
}
return ret;
@@ -102,38 +111,45 @@ void mtk_vcodec_release_enc_pm(struct mtk_vcodec_dev *mtkdev)
void mtk_vcodec_enc_clock_on(struct mtk_vcodec_pm *pm)
{
- int ret;
-
- ret = clk_prepare_enable(pm->venc_sel);
- if (ret)
- mtk_v4l2_err("clk_prepare_enable fail %d", ret);
-
- ret = clk_set_parent(pm->venc_sel, pm->vencpll_d2);
- if (ret)
- mtk_v4l2_err("clk_set_parent fail %d", ret);
-
- ret = clk_prepare_enable(pm->venc_lt_sel);
- if (ret)
- mtk_v4l2_err("clk_prepare_enable fail %d", ret);
-
- ret = clk_set_parent(pm->venc_lt_sel, pm->univpll1_d2);
- if (ret)
- mtk_v4l2_err("clk_set_parent fail %d", ret);
+ struct mtk_vcodec_clk *enc_clk = &pm->venc_clk;
+ int ret, i = 0;
+
+ for (i = 0; i < enc_clk->clk_num; i++) {
+ ret = clk_prepare_enable(enc_clk->clk_info[i].vcodec_clk);
+ if (ret) {
+ mtk_v4l2_err("venc clk_prepare_enable %d %s fail %d", i,
+ enc_clk->clk_info[i].clk_name, ret);
+ goto clkerr;
+ }
+ }
ret = mtk_smi_larb_get(pm->larbvenc);
- if (ret)
+ if (ret) {
mtk_v4l2_err("mtk_smi_larb_get larb3 fail %d", ret);
-
+ goto larbvencerr;
+ }
ret = mtk_smi_larb_get(pm->larbvenclt);
- if (ret)
+ if (ret) {
mtk_v4l2_err("mtk_smi_larb_get larb4 fail %d", ret);
+ goto larbvenclterr;
+ }
+ return;
+larbvenclterr:
+ mtk_smi_larb_put(pm->larbvenc);
+larbvencerr:
+clkerr:
+ for (i -= 1; i >= 0; i--)
+ clk_disable_unprepare(enc_clk->clk_info[i].vcodec_clk);
}
void mtk_vcodec_enc_clock_off(struct mtk_vcodec_pm *pm)
{
+ struct mtk_vcodec_clk *enc_clk = &pm->venc_clk;
+ int i = 0;
+
mtk_smi_larb_put(pm->larbvenc);
mtk_smi_larb_put(pm->larbvenclt);
- clk_disable_unprepare(pm->venc_lt_sel);
- clk_disable_unprepare(pm->venc_sel);
+ for (i = enc_clk->clk_num - 1; i >= 0; i--)
+ clk_disable_unprepare(enc_clk->clk_info[i].vcodec_clk);
}
diff --git a/drivers/media/platform/mtk-vcodec/vdec/vdec_h264_if.c b/drivers/media/platform/mtk-vcodec/vdec/vdec_h264_if.c
index aa3ce41898bc..02c960c63ac0 100644
--- a/drivers/media/platform/mtk-vcodec/vdec/vdec_h264_if.c
+++ b/drivers/media/platform/mtk-vcodec/vdec/vdec_h264_if.c
@@ -55,7 +55,7 @@ struct h264_fb {
/**
* struct h264_ring_fb_list - ring frame buffer list
- * @fb_list : frame buffer arrary
+ * @fb_list : frame buffer array
* @read_idx : read index
* @write_idx : write index
* @count : buffer count in list
@@ -72,7 +72,7 @@ struct h264_ring_fb_list {
/**
* struct vdec_h264_dec_info - decode information
* @dpb_sz : decoding picture buffer size
- * @resolution_changed : resoltion change happen
+ * @resolution_changed : resolution change happen
* @realloc_mv_buf : flag to notify driver to re-allocate mv buffer
* @reserved : for 8 bytes alignment
* @bs_dma : Input bit-stream buffer dma address
diff --git a/drivers/media/platform/mtk-vcodec/vdec/vdec_vp8_if.c b/drivers/media/platform/mtk-vcodec/vdec/vdec_vp8_if.c
index 3e84a761db3a..bac3723038de 100644
--- a/drivers/media/platform/mtk-vcodec/vdec/vdec_vp8_if.c
+++ b/drivers/media/platform/mtk-vcodec/vdec/vdec_vp8_if.c
@@ -120,7 +120,7 @@ struct vdec_vp8_hw_reg_base {
/**
* struct vdec_vp8_vpu_inst - VPU instance for VP8 decode
* @wq_hd : Wait queue to wait VPU message ack
- * @signaled : 1 - Host has received ack message from VPU, 0 - not recevie
+ * @signaled : 1 - Host has received ack message from VPU, 0 - not receive
* @failure : VPU execution result status 0 - success, others - fail
* @inst_addr : VPU decoder instance address
*/
diff --git a/drivers/media/platform/mtk-vcodec/vdec_drv_if.h b/drivers/media/platform/mtk-vcodec/vdec_drv_if.h
index ded1154481cd..9a21591f3818 100644
--- a/drivers/media/platform/mtk-vcodec/vdec_drv_if.h
+++ b/drivers/media/platform/mtk-vcodec/vdec_drv_if.h
@@ -80,7 +80,7 @@ void vdec_if_deinit(struct mtk_vcodec_ctx *ctx);
* vdec_if_decode() - trigger decode
* @ctx : [in] v4l2 context
* @bs : [in] input bitstream
- * @fb : [in] frame buffer to store decoded frame, when null menas parse
+ * @fb : [in] frame buffer to store decoded frame, when null means parse
* header only
* @res_chg : [out] resolution change happens if current bs have different
* picture width/height
diff --git a/drivers/media/platform/mtk-vcodec/vdec_vpu_if.h b/drivers/media/platform/mtk-vcodec/vdec_vpu_if.h
index cd37bb2a610f..79d8eac7f5e2 100644
--- a/drivers/media/platform/mtk-vcodec/vdec_vpu_if.h
+++ b/drivers/media/platform/mtk-vcodec/vdec_vpu_if.h
@@ -62,7 +62,7 @@ int vpu_dec_start(struct vdec_vpu_inst *vpu, uint32_t *data, unsigned int len);
/**
* vpu_dec_end - end decoding, basically the function will be invoked once
* when HW decoding done interrupt received successfully. The
- * decoder in VPU will continute to do referene frame management
+ * decoder in VPU will continue to do reference frame management
* and check if there is a new decoded frame available to display.
*
* @vpu : instance for vdec_vpu_inst
diff --git a/drivers/media/platform/mx2_emmaprp.c b/drivers/media/platform/mx2_emmaprp.c
index 27b078cf98e3..f60f499c596b 100644
--- a/drivers/media/platform/mx2_emmaprp.c
+++ b/drivers/media/platform/mx2_emmaprp.c
@@ -274,7 +274,7 @@ static void emmaprp_device_run(void *priv)
{
struct emmaprp_ctx *ctx = priv;
struct emmaprp_q_data *s_q_data, *d_q_data;
- struct vb2_buffer *src_buf, *dst_buf;
+ struct vb2_v4l2_buffer *src_buf, *dst_buf;
struct emmaprp_dev *pcdev = ctx->dev;
unsigned int s_width, s_height;
unsigned int d_width, d_height;
@@ -294,8 +294,8 @@ static void emmaprp_device_run(void *priv)
d_height = d_q_data->height;
d_size = d_width * d_height;
- p_in = vb2_dma_contig_plane_dma_addr(src_buf, 0);
- p_out = vb2_dma_contig_plane_dma_addr(dst_buf, 0);
+ p_in = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0);
+ p_out = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0);
if (!p_in || !p_out) {
v4l2_err(&pcdev->v4l2_dev,
"Acquiring kernel pointers to buffers failed\n");
diff --git a/drivers/media/platform/omap/omap_vout.c b/drivers/media/platform/omap/omap_vout.c
index f447ae3bb465..37f0d7146dfa 100644
--- a/drivers/media/platform/omap/omap_vout.c
+++ b/drivers/media/platform/omap/omap_vout.c
@@ -513,7 +513,7 @@ static int omapvid_apply_changes(struct omap_vout_device *vout)
}
static int omapvid_handle_interlace_display(struct omap_vout_device *vout,
- unsigned int irqstatus, struct timeval timevalue)
+ unsigned int irqstatus, u64 ts)
{
u32 fid;
@@ -537,7 +537,7 @@ static int omapvid_handle_interlace_display(struct omap_vout_device *vout,
if (vout->cur_frm == vout->next_frm)
goto err;
- vout->cur_frm->ts = timevalue;
+ vout->cur_frm->ts = ts;
vout->cur_frm->state = VIDEOBUF_DONE;
wake_up_interruptible(&vout->cur_frm->done);
vout->cur_frm = vout->next_frm;
@@ -557,7 +557,7 @@ static void omap_vout_isr(void *arg, unsigned int irqstatus)
int ret, fid, mgr_id;
u32 addr, irq;
struct omap_overlay *ovl;
- struct timeval timevalue;
+ u64 ts;
struct omapvideo_info *ovid;
struct omap_dss_device *cur_display;
struct omap_vout_device *vout = (struct omap_vout_device *)arg;
@@ -577,7 +577,7 @@ static void omap_vout_isr(void *arg, unsigned int irqstatus)
return;
spin_lock(&vout->vbq_lock);
- v4l2_get_timestamp(&timevalue);
+ ts = ktime_get_ns();
switch (cur_display->type) {
case OMAP_DISPLAY_TYPE_DSI:
@@ -595,7 +595,7 @@ static void omap_vout_isr(void *arg, unsigned int irqstatus)
break;
case OMAP_DISPLAY_TYPE_VENC:
fid = omapvid_handle_interlace_display(vout, irqstatus,
- timevalue);
+ ts);
if (!fid)
goto vout_isr_err;
break;
@@ -608,7 +608,7 @@ static void omap_vout_isr(void *arg, unsigned int irqstatus)
}
if (!vout->first_int && (vout->cur_frm != vout->next_frm)) {
- vout->cur_frm->ts = timevalue;
+ vout->cur_frm->ts = ts;
vout->cur_frm->state = VIDEOBUF_DONE;
wake_up_interruptible(&vout->cur_frm->done);
vout->cur_frm = vout->next_frm;
@@ -1129,7 +1129,7 @@ static int vidioc_s_fmt_vid_out(struct file *file, void *fh,
}
timing = &dssdev->panel.timings;
- /* We dont support RGB24-packed mode if vrfb rotation
+ /* We don't support RGB24-packed mode if vrfb rotation
* is enabled*/
if ((is_rotation_enabled(vout)) &&
f->fmt.pix.pixelformat == V4L2_PIX_FMT_RGB24) {
@@ -1147,7 +1147,7 @@ static int vidioc_s_fmt_vid_out(struct file *file, void *fh,
vout->fbuf.fmt.width = timing->x_res;
}
- /* change to samller size is OK */
+ /* change to smaller size is OK */
bpp = omap_vout_try_format(&f->fmt.pix);
f->fmt.pix.sizeimage = f->fmt.pix.width * f->fmt.pix.height * bpp;
diff --git a/drivers/media/platform/omap/omap_voutdef.h b/drivers/media/platform/omap/omap_voutdef.h
index 56b630b1c8b4..c740393c8509 100644
--- a/drivers/media/platform/omap/omap_voutdef.h
+++ b/drivers/media/platform/omap/omap_voutdef.h
@@ -37,7 +37,7 @@
#define VID_MAX_WIDTH 1280 /* Largest width */
#define VID_MAX_HEIGHT 720 /* Largest height */
-/* Mimimum requirement is 2x2 for DSS */
+/* Minimum requirement is 2x2 for DSS */
#define VID_MIN_WIDTH 2
#define VID_MIN_HEIGHT 2
@@ -135,7 +135,7 @@ struct omap_vout_device {
enum omap_color_mode dss_mode;
/* we don't allow to request new buffer when old buffers are
- * still mmaped
+ * still mmapped
*/
int mmap_count;
diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c
index 13f2828d880d..bd57174d81a7 100644
--- a/drivers/media/platform/omap3isp/isp.c
+++ b/drivers/media/platform/omap3isp/isp.c
@@ -1517,7 +1517,7 @@ void omap3isp_print_status(struct isp_device *isp)
*
* To solve this problem power management support is split into prepare/complete
* and suspend/resume operations. The pipelines are stopped in prepare() and the
- * ISP clocks get disabled in suspend(). Similarly, the clocks are reenabled in
+ * ISP clocks get disabled in suspend(). Similarly, the clocks are re-enabled in
* resume(), and the the pipelines are restarted in complete().
*
* TODO: PM dependencies between the ISP and sensors are not modelled explicitly
diff --git a/drivers/media/platform/omap3isp/ispccdc.c b/drivers/media/platform/omap3isp/ispccdc.c
index 14a1c24037c4..261ad1175f98 100644
--- a/drivers/media/platform/omap3isp/ispccdc.c
+++ b/drivers/media/platform/omap3isp/ispccdc.c
@@ -1594,7 +1594,7 @@ static int ccdc_isr_buffer(struct isp_ccdc_device *ccdc)
return 0;
/* We're in continuous mode, and memory writes were disabled due to a
- * buffer underrun. Reenable them now that we have a buffer. The buffer
+ * buffer underrun. Re-enable them now that we have a buffer. The buffer
* address has been set in ccdc_video_queue.
*/
if (ccdc->state == ISP_PIPELINE_STREAM_CONTINUOUS && ccdc->underrun) {
@@ -1712,7 +1712,7 @@ static void ccdc_vd1_isr(struct isp_ccdc_device *ccdc)
* data to memory the CCDC and LSC are stopped immediately but
* without change the CCDC stopping state machine. The CCDC
* stopping state machine should be used only when user request
- * for stopping is received (SINGLESHOT is an exeption).
+ * for stopping is received (SINGLESHOT is an exception).
*/
switch (ccdc->state) {
case ISP_PIPELINE_STREAM_SINGLESHOT:
diff --git a/drivers/media/platform/omap3isp/ispcsi2.c b/drivers/media/platform/omap3isp/ispcsi2.c
index 9c180f607bcb..da66ea65be5d 100644
--- a/drivers/media/platform/omap3isp/ispcsi2.c
+++ b/drivers/media/platform/omap3isp/ispcsi2.c
@@ -710,7 +710,7 @@ static void csi2_isr_ctx(struct isp_csi2_device *csi2,
/* Skip interrupts until we reach the frame skip count. The CSI2 will be
* automatically disabled, as the frame skip count has been programmed
- * in the CSI2_CTx_CTRL1::COUNT field, so reenable it.
+ * in the CSI2_CTx_CTRL1::COUNT field, so re-enable it.
*
* It would have been nice to rely on the FRAME_NUMBER interrupt instead
* but it turned out that the interrupt is only generated when the CSI2
diff --git a/drivers/media/platform/pxa_camera.c b/drivers/media/platform/pxa_camera.c
index 5f930560eb30..4fe228752a43 100644
--- a/drivers/media/platform/pxa_camera.c
+++ b/drivers/media/platform/pxa_camera.c
@@ -1631,7 +1631,7 @@ static int pxa_camera_set_bus_param(struct pxa_camera_dev *pcdev)
pcdev->channels = 1;
- /* Make choises, based on platform preferences */
+ /* Make choices, based on platform preferences */
if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) &&
(common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) {
if (pcdev->platform_flags & PXA_CAMERA_HSP)
@@ -2394,15 +2394,17 @@ static int pxa_camera_probe(struct platform_device *pdev)
pcdev->res = res;
pcdev->pdata = pdev->dev.platform_data;
- if (pdev->dev.of_node && !pcdev->pdata) {
- err = pxa_camera_pdata_from_dt(&pdev->dev, pcdev, &pcdev->asd);
- } else {
+ if (pcdev->pdata) {
pcdev->platform_flags = pcdev->pdata->flags;
pcdev->mclk = pcdev->pdata->mclk_10khz * 10000;
pcdev->asd.match_type = V4L2_ASYNC_MATCH_I2C;
pcdev->asd.match.i2c.adapter_id =
pcdev->pdata->sensor_i2c_adapter_id;
pcdev->asd.match.i2c.address = pcdev->pdata->sensor_i2c_address;
+ } else if (pdev->dev.of_node) {
+ err = pxa_camera_pdata_from_dt(&pdev->dev, pcdev, &pcdev->asd);
+ } else {
+ return -ENODEV;
}
if (err < 0)
return err;
diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c
index cb411eb85ee4..739366744e0f 100644
--- a/drivers/media/platform/qcom/venus/core.c
+++ b/drivers/media/platform/qcom/venus/core.c
@@ -455,7 +455,7 @@ static const struct venus_resources msm8996_res = {
.reg_tbl_size = ARRAY_SIZE(msm8996_reg_preset),
.clks = {"core", "iface", "bus", "mbus" },
.clks_num = 4,
- .max_load = 2563200,
+ .max_load = 3110400, /* 4096x2160@90 */
.hfi_version = HFI_VERSION_3XX,
.vmem_id = VIDC_RESOURCE_NONE,
.vmem_size = 0,
@@ -465,10 +465,12 @@ static const struct venus_resources msm8996_res = {
};
static const struct freq_tbl sdm845_freq_table[] = {
- { 1944000, 380000000 }, /* 4k UHD @ 60 */
- { 972000, 320000000 }, /* 4k UHD @ 30 */
- { 489600, 200000000 }, /* 1080p @ 60 */
- { 244800, 100000000 }, /* 1080p @ 30 */
+ { 3110400, 533000000 }, /* 4096x2160@90 */
+ { 2073600, 444000000 }, /* 4096x2160@60 */
+ { 1944000, 404000000 }, /* 3840x2160@60 */
+ { 972000, 330000000 }, /* 3840x2160@30 */
+ { 489600, 200000000 }, /* 1920x1080@60 */
+ { 244800, 100000000 }, /* 1920x1080@30 */
};
static const struct venus_resources sdm845_res = {
diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h
index 6382cea29185..7a3feb5cee00 100644
--- a/drivers/media/platform/qcom/venus/core.h
+++ b/drivers/media/platform/qcom/venus/core.h
@@ -134,6 +134,7 @@ struct venus_core {
struct video_firmware {
struct device *dev;
struct iommu_domain *iommu_domain;
+ size_t mapped_mem_size;
} fw;
struct mutex lock;
struct list_head instances;
@@ -218,7 +219,7 @@ struct venus_buffer {
#define to_venus_buffer(ptr) container_of(ptr, struct venus_buffer, vb)
/**
- * struct venus_inst - holds per instance paramerters
+ * struct venus_inst - holds per instance parameters
*
* @list: used for attach an instance to the core
* @lock: instance lock
diff --git a/drivers/media/platform/qcom/venus/firmware.c b/drivers/media/platform/qcom/venus/firmware.c
index c29acfd70c1b..6cfa8021721e 100644
--- a/drivers/media/platform/qcom/venus/firmware.c
+++ b/drivers/media/platform/qcom/venus/firmware.c
@@ -35,14 +35,15 @@
static void venus_reset_cpu(struct venus_core *core)
{
+ u32 fw_size = core->fw.mapped_mem_size;
void __iomem *base = core->base;
writel(0, base + WRAPPER_FW_START_ADDR);
- writel(VENUS_FW_MEM_SIZE, base + WRAPPER_FW_END_ADDR);
+ writel(fw_size, base + WRAPPER_FW_END_ADDR);
writel(0, base + WRAPPER_CPA_START_ADDR);
- writel(VENUS_FW_MEM_SIZE, base + WRAPPER_CPA_END_ADDR);
- writel(VENUS_FW_MEM_SIZE, base + WRAPPER_NONPIX_START_ADDR);
- writel(VENUS_FW_MEM_SIZE, base + WRAPPER_NONPIX_END_ADDR);
+ writel(fw_size, base + WRAPPER_CPA_END_ADDR);
+ writel(fw_size, base + WRAPPER_NONPIX_START_ADDR);
+ writel(fw_size, base + WRAPPER_NONPIX_END_ADDR);
writel(0x0, base + WRAPPER_CPU_CGC_DIS);
writel(0x0, base + WRAPPER_CPU_CLOCK_CONFIG);
@@ -74,6 +75,9 @@ static int venus_load_fw(struct venus_core *core, const char *fwname,
void *mem_va;
int ret;
+ *mem_phys = 0;
+ *mem_size = 0;
+
dev = core->dev;
node = of_parse_phandle(dev->of_node, "memory-region", 0);
if (!node) {
@@ -85,28 +89,30 @@ static int venus_load_fw(struct venus_core *core, const char *fwname,
if (ret)
return ret;
+ ret = request_firmware(&mdt, fwname, dev);
+ if (ret < 0)
+ return ret;
+
+ fw_size = qcom_mdt_get_size(mdt);
+ if (fw_size < 0) {
+ ret = fw_size;
+ goto err_release_fw;
+ }
+
*mem_phys = r.start;
*mem_size = resource_size(&r);
- if (*mem_size < VENUS_FW_MEM_SIZE)
- return -EINVAL;
+ if (*mem_size < fw_size || fw_size > VENUS_FW_MEM_SIZE) {
+ ret = -EINVAL;
+ goto err_release_fw;
+ }
mem_va = memremap(r.start, *mem_size, MEMREMAP_WC);
if (!mem_va) {
dev_err(dev, "unable to map memory region: %pa+%zx\n",
&r.start, *mem_size);
- return -ENOMEM;
- }
-
- ret = request_firmware(&mdt, fwname, dev);
- if (ret < 0)
- goto err_unmap;
-
- fw_size = qcom_mdt_get_size(mdt);
- if (fw_size < 0) {
- ret = fw_size;
- release_firmware(mdt);
- goto err_unmap;
+ ret = -ENOMEM;
+ goto err_release_fw;
}
if (core->use_tz)
@@ -116,10 +122,9 @@ static int venus_load_fw(struct venus_core *core, const char *fwname,
ret = qcom_mdt_load_no_init(dev, mdt, fwname, VENUS_PAS_ID,
mem_va, *mem_phys, *mem_size, NULL);
- release_firmware(mdt);
-
-err_unmap:
memunmap(mem_va);
+err_release_fw:
+ release_firmware(mdt);
return ret;
}
@@ -135,6 +140,7 @@ static int venus_boot_no_tz(struct venus_core *core, phys_addr_t mem_phys,
return -EPROBE_DEFER;
iommu = core->fw.iommu_domain;
+ core->fw.mapped_mem_size = mem_size;
ret = iommu_map(iommu, VENUS_FW_START_ADDR, mem_phys, mem_size,
IOMMU_READ | IOMMU_WRITE | IOMMU_PRIV);
@@ -150,6 +156,7 @@ static int venus_boot_no_tz(struct venus_core *core, phys_addr_t mem_phys,
static int venus_shutdown_no_tz(struct venus_core *core)
{
+ const size_t mapped = core->fw.mapped_mem_size;
struct iommu_domain *iommu;
size_t unmapped;
u32 reg;
@@ -166,8 +173,8 @@ static int venus_shutdown_no_tz(struct venus_core *core)
iommu = core->fw.iommu_domain;
- unmapped = iommu_unmap(iommu, VENUS_FW_START_ADDR, VENUS_FW_MEM_SIZE);
- if (unmapped != VENUS_FW_MEM_SIZE)
+ unmapped = iommu_unmap(iommu, VENUS_FW_START_ADDR, mapped);
+ if (unmapped != mapped)
dev_err(dev, "failed to unmap firmware\n");
return 0;
diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c
index e436385bc5ab..5cad601d4c57 100644
--- a/drivers/media/platform/qcom/venus/helpers.c
+++ b/drivers/media/platform/qcom/venus/helpers.c
@@ -439,9 +439,6 @@ session_process_buf(struct venus_inst *inst, struct vb2_v4l2_buffer *vbuf)
fdata.flags = 0;
fdata.clnt_data = vbuf->vb2_buf.index;
- if (!fdata.timestamp)
- fdata.flags |= HFI_BUFFERFLAG_TIMESTAMPINVALID;
-
if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
fdata.buffer_type = HFI_BUFFER_INPUT;
fdata.filled_len = vb2_get_plane_payload(vb, 0);
diff --git a/drivers/media/platform/rcar-vin/rcar-core.c b/drivers/media/platform/rcar-vin/rcar-core.c
index f0719ce24b97..594d80434004 100644
--- a/drivers/media/platform/rcar-vin/rcar-core.c
+++ b/drivers/media/platform/rcar-vin/rcar-core.c
@@ -131,9 +131,13 @@ static int rvin_group_link_notify(struct media_link *link, u32 flags,
!is_media_entity_v4l2_video_device(link->sink->entity))
return 0;
- /* If any entity is in use don't allow link changes. */
+ /*
+ * Don't allow link changes if any entity in the graph is
+ * streaming, modifying the CHSEL register fields can disrupt
+ * running streams.
+ */
media_device_for_each_entity(entity, &group->mdev)
- if (entity->use_count)
+ if (entity->stream_count)
return -EBUSY;
mutex_lock(&group->lock);
@@ -542,9 +546,7 @@ static void rvin_parallel_notify_unbind(struct v4l2_async_notifier *notifier,
vin_dbg(vin, "unbind parallel subdev %s\n", subdev->name);
- mutex_lock(&vin->lock);
rvin_parallel_subdevice_detach(vin);
- mutex_unlock(&vin->lock);
}
static int rvin_parallel_notify_bound(struct v4l2_async_notifier *notifier,
@@ -554,9 +556,7 @@ static int rvin_parallel_notify_bound(struct v4l2_async_notifier *notifier,
struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev);
int ret;
- mutex_lock(&vin->lock);
ret = rvin_parallel_subdevice_attach(vin, subdev);
- mutex_unlock(&vin->lock);
if (ret)
return ret;
@@ -664,7 +664,6 @@ static int rvin_group_notify_complete(struct v4l2_async_notifier *notifier)
}
/* Create all media device links between VINs and CSI-2's. */
- mutex_lock(&vin->group->lock);
for (route = vin->info->routes; route->mask; route++) {
struct media_pad *source_pad, *sink_pad;
struct media_entity *source, *sink;
@@ -700,7 +699,6 @@ static int rvin_group_notify_complete(struct v4l2_async_notifier *notifier)
break;
}
}
- mutex_unlock(&vin->group->lock);
return ret;
}
@@ -716,8 +714,6 @@ static void rvin_group_notify_unbind(struct v4l2_async_notifier *notifier,
if (vin->group->vin[i])
rvin_v4l2_unregister(vin->group->vin[i]);
- mutex_lock(&vin->group->lock);
-
for (i = 0; i < RVIN_CSI_MAX; i++) {
if (vin->group->csi[i].fwnode != asd->match.fwnode)
continue;
@@ -725,8 +721,6 @@ static void rvin_group_notify_unbind(struct v4l2_async_notifier *notifier,
vin_dbg(vin, "Unbind CSI-2 %s from slot %u\n", subdev->name, i);
break;
}
-
- mutex_unlock(&vin->group->lock);
}
static int rvin_group_notify_bound(struct v4l2_async_notifier *notifier,
@@ -736,8 +730,6 @@ static int rvin_group_notify_bound(struct v4l2_async_notifier *notifier,
struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev);
unsigned int i;
- mutex_lock(&vin->group->lock);
-
for (i = 0; i < RVIN_CSI_MAX; i++) {
if (vin->group->csi[i].fwnode != asd->match.fwnode)
continue;
@@ -746,8 +738,6 @@ static int rvin_group_notify_bound(struct v4l2_async_notifier *notifier,
break;
}
- mutex_unlock(&vin->group->lock);
-
return 0;
}
@@ -1146,6 +1136,10 @@ static const struct rvin_info rcar_info_r8a77995 = {
static const struct of_device_id rvin_of_id_table[] = {
{
+ .compatible = "renesas,vin-r8a774c0",
+ .data = &rcar_info_r8a77990,
+ },
+ {
.compatible = "renesas,vin-r8a7778",
.data = &rcar_info_m1,
},
diff --git a/drivers/media/platform/rcar-vin/rcar-csi2.c b/drivers/media/platform/rcar-vin/rcar-csi2.c
index 6d356f5a9456..f64528d2be3c 100644
--- a/drivers/media/platform/rcar-vin/rcar-csi2.c
+++ b/drivers/media/platform/rcar-vin/rcar-csi2.c
@@ -152,37 +152,37 @@ static const struct rcsi2_mbps_reg phtw_mbps_h3_v3h_m3n[] = {
};
static const struct rcsi2_mbps_reg phtw_mbps_v3m_e3[] = {
- { .mbps = 89, .reg = 0x00 },
- { .mbps = 99, .reg = 0x20 },
- { .mbps = 109, .reg = 0x40 },
- { .mbps = 129, .reg = 0x02 },
- { .mbps = 139, .reg = 0x22 },
- { .mbps = 149, .reg = 0x42 },
- { .mbps = 169, .reg = 0x04 },
- { .mbps = 179, .reg = 0x24 },
- { .mbps = 199, .reg = 0x44 },
- { .mbps = 219, .reg = 0x06 },
- { .mbps = 239, .reg = 0x26 },
- { .mbps = 249, .reg = 0x46 },
- { .mbps = 269, .reg = 0x08 },
- { .mbps = 299, .reg = 0x28 },
- { .mbps = 329, .reg = 0x0a },
- { .mbps = 359, .reg = 0x2a },
- { .mbps = 399, .reg = 0x4a },
- { .mbps = 449, .reg = 0x0c },
- { .mbps = 499, .reg = 0x2c },
- { .mbps = 549, .reg = 0x0e },
- { .mbps = 599, .reg = 0x2e },
- { .mbps = 649, .reg = 0x10 },
- { .mbps = 699, .reg = 0x30 },
- { .mbps = 749, .reg = 0x12 },
- { .mbps = 799, .reg = 0x32 },
- { .mbps = 849, .reg = 0x52 },
- { .mbps = 899, .reg = 0x72 },
- { .mbps = 949, .reg = 0x14 },
- { .mbps = 999, .reg = 0x34 },
- { .mbps = 1049, .reg = 0x54 },
- { .mbps = 1099, .reg = 0x74 },
+ { .mbps = 80, .reg = 0x00 },
+ { .mbps = 90, .reg = 0x20 },
+ { .mbps = 100, .reg = 0x40 },
+ { .mbps = 110, .reg = 0x02 },
+ { .mbps = 130, .reg = 0x22 },
+ { .mbps = 140, .reg = 0x42 },
+ { .mbps = 150, .reg = 0x04 },
+ { .mbps = 170, .reg = 0x24 },
+ { .mbps = 180, .reg = 0x44 },
+ { .mbps = 200, .reg = 0x06 },
+ { .mbps = 220, .reg = 0x26 },
+ { .mbps = 240, .reg = 0x46 },
+ { .mbps = 250, .reg = 0x08 },
+ { .mbps = 270, .reg = 0x28 },
+ { .mbps = 300, .reg = 0x0a },
+ { .mbps = 330, .reg = 0x2a },
+ { .mbps = 360, .reg = 0x4a },
+ { .mbps = 400, .reg = 0x0c },
+ { .mbps = 450, .reg = 0x2c },
+ { .mbps = 500, .reg = 0x0e },
+ { .mbps = 550, .reg = 0x2e },
+ { .mbps = 600, .reg = 0x10 },
+ { .mbps = 650, .reg = 0x30 },
+ { .mbps = 700, .reg = 0x12 },
+ { .mbps = 750, .reg = 0x32 },
+ { .mbps = 800, .reg = 0x52 },
+ { .mbps = 850, .reg = 0x72 },
+ { .mbps = 900, .reg = 0x14 },
+ { .mbps = 950, .reg = 0x34 },
+ { .mbps = 1000, .reg = 0x54 },
+ { .mbps = 1050, .reg = 0x74 },
{ .mbps = 1125, .reg = 0x16 },
{ /* sentinel */ },
};
@@ -986,6 +986,10 @@ static const struct rcar_csi2_info rcar_csi2_info_r8a77990 = {
static const struct of_device_id rcar_csi2_of_table[] = {
{
+ .compatible = "renesas,r8a774c0-csi2",
+ .data = &rcar_csi2_info_r8a77990,
+ },
+ {
.compatible = "renesas,r8a7795-csi2",
.data = &rcar_csi2_info_r8a7795,
},
diff --git a/drivers/media/platform/rcar-vin/rcar-dma.c b/drivers/media/platform/rcar-vin/rcar-dma.c
index 92323310f735..2207a31d355e 100644
--- a/drivers/media/platform/rcar-vin/rcar-dma.c
+++ b/drivers/media/platform/rcar-vin/rcar-dma.c
@@ -681,7 +681,7 @@ static int rvin_setup(struct rvin_dev *vin)
break;
}
- /* Enable VSYNC Field Toogle mode after one VSYNC input */
+ /* Enable VSYNC Field Toggle mode after one VSYNC input */
if (vin->info->model == RCAR_GEN3)
dmr2 = VNDMR2_FTEV;
else
@@ -1341,5 +1341,5 @@ int rvin_set_channel_routing(struct rvin_dev *vin, u8 chsel)
pm_runtime_put(vin->dev);
- return ret;
+ return 0;
}
diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c
index 7a2851790b91..7cbdcbf9b090 100644
--- a/drivers/media/platform/rcar-vin/rcar-v4l2.c
+++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c
@@ -665,7 +665,7 @@ static void rvin_mc_try_format(struct rvin_dev *vin,
* The V4L2 specification clearly documents the colorspace fields
* as being set by drivers for capture devices. Using the values
* supplied by userspace thus wouldn't comply with the API. Until
- * the API is updated force fixed vaules.
+ * the API is updated force fixed values.
*/
pix->colorspace = RVIN_DEFAULT_COLORSPACE;
pix->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(pix->colorspace);
@@ -964,7 +964,7 @@ void rvin_v4l2_unregister(struct rvin_dev *vin)
v4l2_info(&vin->v4l2_dev, "Removing %s\n",
video_device_node_name(&vin->vdev));
- /* Checks internaly if vdev have been init or not */
+ /* Checks internally if vdev have been init or not */
video_unregister_device(&vin->vdev);
}
diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c
index 96d1b1b3fe8e..843e50d17a72 100644
--- a/drivers/media/platform/rockchip/rga/rga-hw.c
+++ b/drivers/media/platform/rockchip/rga/rga-hw.c
@@ -256,7 +256,7 @@ static void rga_cmd_set_trans_info(struct rga_ctx *ctx)
}
/*
- * Cacluate the up/down scaling mode/factor.
+ * Calculate the up/down scaling mode/factor.
*
* RGA used to scale the picture first, and then rotate second,
* so we need to swap the w/h when rotate degree is 90/270.
@@ -304,7 +304,7 @@ static void rga_cmd_set_trans_info(struct rga_ctx *ctx)
}
/*
- * Cacluate the framebuffer virtual strides and active size,
+ * Calculate the framebuffer virtual strides and active size,
* note that the step of vir_stride / vir_width is 4 byte words
*/
src_vir_info.data.vir_stride = ctx->in.stride >> 2;
@@ -318,7 +318,7 @@ static void rga_cmd_set_trans_info(struct rga_ctx *ctx)
dst_act_info.data.act_width = dst_w - 1;
/*
- * Cacluate the source framebuffer base address with offset pixel.
+ * Calculate the source framebuffer base address with offset pixel.
*/
src_offsets = rga_get_addr_offset(&ctx->in, src_x, src_y,
src_w, src_h);
diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c
index 5c653287185f..b096227a9722 100644
--- a/drivers/media/platform/rockchip/rga/rga.c
+++ b/drivers/media/platform/rockchip/rga/rga.c
@@ -43,7 +43,7 @@ static void device_run(void *prv)
{
struct rga_ctx *ctx = prv;
struct rockchip_rga *rga = ctx->rga;
- struct vb2_buffer *src, *dst;
+ struct vb2_v4l2_buffer *src, *dst;
unsigned long flags;
spin_lock_irqsave(&rga->ctrl_lock, flags);
@@ -53,8 +53,8 @@ static void device_run(void *prv)
src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
- rga_buf_map(src);
- rga_buf_map(dst);
+ rga_buf_map(&src->vb2_buf);
+ rga_buf_map(&dst->vb2_buf);
rga_hw_start(rga);
diff --git a/drivers/media/platform/s3c-camif/camif-core.h b/drivers/media/platform/s3c-camif/camif-core.h
index 1f5c8c94ce89..be5e7357dffc 100644
--- a/drivers/media/platform/s3c-camif/camif-core.h
+++ b/drivers/media/platform/s3c-camif/camif-core.h
@@ -260,7 +260,7 @@ struct camif_vp {
* @clock: clocks required for the CAMIF operation
* @lock: mutex protecting this data structure
* @slock: spinlock protecting CAMIF registers
- * @io_base: start address of the mmaped CAMIF registers
+ * @io_base: start address of the mmapped CAMIF registers
*/
struct camif_dev {
struct media_device media_dev;
diff --git a/drivers/media/platform/s5p-g2d/g2d.c b/drivers/media/platform/s5p-g2d/g2d.c
index 57ab1d1085d1..971c47165010 100644
--- a/drivers/media/platform/s5p-g2d/g2d.c
+++ b/drivers/media/platform/s5p-g2d/g2d.c
@@ -513,7 +513,7 @@ static void device_run(void *prv)
{
struct g2d_ctx *ctx = prv;
struct g2d_dev *dev = ctx->dev;
- struct vb2_buffer *src, *dst;
+ struct vb2_v4l2_buffer *src, *dst;
unsigned long flags;
u32 cmd = 0;
@@ -528,10 +528,10 @@ static void device_run(void *prv)
spin_lock_irqsave(&dev->ctrl_lock, flags);
g2d_set_src_size(dev, &ctx->in);
- g2d_set_src_addr(dev, vb2_dma_contig_plane_dma_addr(src, 0));
+ g2d_set_src_addr(dev, vb2_dma_contig_plane_dma_addr(&src->vb2_buf, 0));
g2d_set_dst_size(dev, &ctx->out);
- g2d_set_dst_addr(dev, vb2_dma_contig_plane_dma_addr(dst, 0));
+ g2d_set_dst_addr(dev, vb2_dma_contig_plane_dma_addr(&dst->vb2_buf, 0));
g2d_set_rop4(dev, ctx->rop);
g2d_set_flip(dev, ctx->flip);
diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c
index 3f9000b70385..8cc730eccb6c 100644
--- a/drivers/media/platform/s5p-jpeg/jpeg-core.c
+++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c
@@ -3,7 +3,7 @@
* Copyright (c) 2011-2014 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
- * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ * Author: Andrzej Pietrasiewicz <andrzejtp2010@gmail.com>
* Author: Jacek Anaszewski <j.anaszewski@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
@@ -793,14 +793,14 @@ static void skip(struct s5p_jpeg_buffer *buf, long len);
static void exynos4_jpeg_parse_decode_h_tbl(struct s5p_jpeg_ctx *ctx)
{
struct s5p_jpeg *jpeg = ctx->jpeg;
- struct vb2_buffer *vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
+ struct vb2_v4l2_buffer *vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
struct s5p_jpeg_buffer jpeg_buffer;
unsigned int word;
int c, x, components;
jpeg_buffer.size = 2; /* Ls */
jpeg_buffer.data =
- (unsigned long)vb2_plane_vaddr(vb, 0) + ctx->out_q.sos + 2;
+ (unsigned long)vb2_plane_vaddr(&vb->vb2_buf, 0) + ctx->out_q.sos + 2;
jpeg_buffer.curr = 0;
word = 0;
@@ -830,14 +830,14 @@ static void exynos4_jpeg_parse_decode_h_tbl(struct s5p_jpeg_ctx *ctx)
static void exynos4_jpeg_parse_huff_tbl(struct s5p_jpeg_ctx *ctx)
{
struct s5p_jpeg *jpeg = ctx->jpeg;
- struct vb2_buffer *vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
+ struct vb2_v4l2_buffer *vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
struct s5p_jpeg_buffer jpeg_buffer;
unsigned int word;
int c, i, n, j;
for (j = 0; j < ctx->out_q.dht.n; ++j) {
jpeg_buffer.size = ctx->out_q.dht.len[j];
- jpeg_buffer.data = (unsigned long)vb2_plane_vaddr(vb, 0) +
+ jpeg_buffer.data = (unsigned long)vb2_plane_vaddr(&vb->vb2_buf, 0) +
ctx->out_q.dht.marker[j];
jpeg_buffer.curr = 0;
@@ -889,13 +889,13 @@ static void exynos4_jpeg_parse_huff_tbl(struct s5p_jpeg_ctx *ctx)
static void exynos4_jpeg_parse_decode_q_tbl(struct s5p_jpeg_ctx *ctx)
{
struct s5p_jpeg *jpeg = ctx->jpeg;
- struct vb2_buffer *vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
+ struct vb2_v4l2_buffer *vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
struct s5p_jpeg_buffer jpeg_buffer;
int c, x, components;
jpeg_buffer.size = ctx->out_q.sof_len;
jpeg_buffer.data =
- (unsigned long)vb2_plane_vaddr(vb, 0) + ctx->out_q.sof;
+ (unsigned long)vb2_plane_vaddr(&vb->vb2_buf, 0) + ctx->out_q.sof;
jpeg_buffer.curr = 0;
skip(&jpeg_buffer, 5); /* P, Y, X */
@@ -920,14 +920,14 @@ static void exynos4_jpeg_parse_decode_q_tbl(struct s5p_jpeg_ctx *ctx)
static void exynos4_jpeg_parse_q_tbl(struct s5p_jpeg_ctx *ctx)
{
struct s5p_jpeg *jpeg = ctx->jpeg;
- struct vb2_buffer *vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
+ struct vb2_v4l2_buffer *vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
struct s5p_jpeg_buffer jpeg_buffer;
unsigned int word;
int c, i, j;
for (j = 0; j < ctx->out_q.dqt.n; ++j) {
jpeg_buffer.size = ctx->out_q.dqt.len[j];
- jpeg_buffer.data = (unsigned long)vb2_plane_vaddr(vb, 0) +
+ jpeg_buffer.data = (unsigned long)vb2_plane_vaddr(&vb->vb2_buf, 0) +
ctx->out_q.dqt.marker[j];
jpeg_buffer.curr = 0;
@@ -1293,13 +1293,16 @@ static int s5p_jpeg_querycap(struct file *file, void *priv,
return 0;
}
-static int enum_fmt(struct s5p_jpeg_fmt *sjpeg_formats, int n,
+static int enum_fmt(struct s5p_jpeg_ctx *ctx,
+ struct s5p_jpeg_fmt *sjpeg_formats, int n,
struct v4l2_fmtdesc *f, u32 type)
{
int i, num = 0;
+ unsigned int fmt_ver_flag = ctx->jpeg->variant->fmt_ver_flag;
for (i = 0; i < n; ++i) {
- if (sjpeg_formats[i].flags & type) {
+ if (sjpeg_formats[i].flags & type &&
+ sjpeg_formats[i].flags & fmt_ver_flag) {
/* index-th format of type type found ? */
if (num == f->index)
break;
@@ -1326,11 +1329,11 @@ static int s5p_jpeg_enum_fmt_vid_cap(struct file *file, void *priv,
struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
if (ctx->mode == S5P_JPEG_ENCODE)
- return enum_fmt(sjpeg_formats, SJPEG_NUM_FORMATS, f,
+ return enum_fmt(ctx, sjpeg_formats, SJPEG_NUM_FORMATS, f,
SJPEG_FMT_FLAG_ENC_CAPTURE);
- return enum_fmt(sjpeg_formats, SJPEG_NUM_FORMATS, f,
- SJPEG_FMT_FLAG_DEC_CAPTURE);
+ return enum_fmt(ctx, sjpeg_formats, SJPEG_NUM_FORMATS, f,
+ SJPEG_FMT_FLAG_DEC_CAPTURE);
}
static int s5p_jpeg_enum_fmt_vid_out(struct file *file, void *priv,
@@ -1339,11 +1342,11 @@ static int s5p_jpeg_enum_fmt_vid_out(struct file *file, void *priv,
struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
if (ctx->mode == S5P_JPEG_ENCODE)
- return enum_fmt(sjpeg_formats, SJPEG_NUM_FORMATS, f,
+ return enum_fmt(ctx, sjpeg_formats, SJPEG_NUM_FORMATS, f,
SJPEG_FMT_FLAG_ENC_OUTPUT);
- return enum_fmt(sjpeg_formats, SJPEG_NUM_FORMATS, f,
- SJPEG_FMT_FLAG_DEC_OUTPUT);
+ return enum_fmt(ctx, sjpeg_formats, SJPEG_NUM_FORMATS, f,
+ SJPEG_FMT_FLAG_DEC_OUTPUT);
}
static struct s5p_jpeg_q_data *get_q_data(struct s5p_jpeg_ctx *ctx,
@@ -2002,7 +2005,7 @@ static int s5p_jpeg_controls_create(struct s5p_jpeg_ctx *ctx)
v4l2_ctrl_new_std(&ctx->ctrl_handler, &s5p_jpeg_ctrl_ops,
V4L2_CID_JPEG_RESTART_INTERVAL,
- 0, 3, 0xffff, 0);
+ 0, 0xffff, 1, 0);
if (ctx->jpeg->variant->version == SJPEG_S5P)
mask = ~0x06; /* 422, 420 */
}
@@ -2072,15 +2075,15 @@ static void s5p_jpeg_device_run(void *priv)
{
struct s5p_jpeg_ctx *ctx = priv;
struct s5p_jpeg *jpeg = ctx->jpeg;
- struct vb2_buffer *src_buf, *dst_buf;
+ struct vb2_v4l2_buffer *src_buf, *dst_buf;
unsigned long src_addr, dst_addr, flags;
spin_lock_irqsave(&ctx->jpeg->slock, flags);
src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
- src_addr = vb2_dma_contig_plane_dma_addr(src_buf, 0);
- dst_addr = vb2_dma_contig_plane_dma_addr(dst_buf, 0);
+ src_addr = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0);
+ dst_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0);
s5p_jpeg_reset(jpeg->regs);
s5p_jpeg_poweron(jpeg->regs);
@@ -2153,7 +2156,7 @@ static void exynos4_jpeg_set_img_addr(struct s5p_jpeg_ctx *ctx)
{
struct s5p_jpeg *jpeg = ctx->jpeg;
struct s5p_jpeg_fmt *fmt;
- struct vb2_buffer *vb;
+ struct vb2_v4l2_buffer *vb;
struct s5p_jpeg_addr jpeg_addr = {};
u32 pix_size, padding_bytes = 0;
@@ -2172,7 +2175,7 @@ static void exynos4_jpeg_set_img_addr(struct s5p_jpeg_ctx *ctx)
vb = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
}
- jpeg_addr.y = vb2_dma_contig_plane_dma_addr(vb, 0);
+ jpeg_addr.y = vb2_dma_contig_plane_dma_addr(&vb->vb2_buf, 0);
if (fmt->colplanes == 2) {
jpeg_addr.cb = jpeg_addr.y + pix_size - padding_bytes;
@@ -2190,7 +2193,7 @@ static void exynos4_jpeg_set_img_addr(struct s5p_jpeg_ctx *ctx)
static void exynos4_jpeg_set_jpeg_addr(struct s5p_jpeg_ctx *ctx)
{
struct s5p_jpeg *jpeg = ctx->jpeg;
- struct vb2_buffer *vb;
+ struct vb2_v4l2_buffer *vb;
unsigned int jpeg_addr = 0;
if (ctx->mode == S5P_JPEG_ENCODE)
@@ -2198,7 +2201,7 @@ static void exynos4_jpeg_set_jpeg_addr(struct s5p_jpeg_ctx *ctx)
else
vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
- jpeg_addr = vb2_dma_contig_plane_dma_addr(vb, 0);
+ jpeg_addr = vb2_dma_contig_plane_dma_addr(&vb->vb2_buf, 0);
if (jpeg->variant->version == SJPEG_EXYNOS5433 &&
ctx->mode == S5P_JPEG_DECODE)
jpeg_addr += ctx->out_q.sos;
@@ -2314,7 +2317,7 @@ static void exynos3250_jpeg_set_img_addr(struct s5p_jpeg_ctx *ctx)
{
struct s5p_jpeg *jpeg = ctx->jpeg;
struct s5p_jpeg_fmt *fmt;
- struct vb2_buffer *vb;
+ struct vb2_v4l2_buffer *vb;
struct s5p_jpeg_addr jpeg_addr = {};
u32 pix_size;
@@ -2328,7 +2331,7 @@ static void exynos3250_jpeg_set_img_addr(struct s5p_jpeg_ctx *ctx)
fmt = ctx->cap_q.fmt;
}
- jpeg_addr.y = vb2_dma_contig_plane_dma_addr(vb, 0);
+ jpeg_addr.y = vb2_dma_contig_plane_dma_addr(&vb->vb2_buf, 0);
if (fmt->colplanes == 2) {
jpeg_addr.cb = jpeg_addr.y + pix_size;
@@ -2346,7 +2349,7 @@ static void exynos3250_jpeg_set_img_addr(struct s5p_jpeg_ctx *ctx)
static void exynos3250_jpeg_set_jpeg_addr(struct s5p_jpeg_ctx *ctx)
{
struct s5p_jpeg *jpeg = ctx->jpeg;
- struct vb2_buffer *vb;
+ struct vb2_v4l2_buffer *vb;
unsigned int jpeg_addr = 0;
if (ctx->mode == S5P_JPEG_ENCODE)
@@ -2354,7 +2357,7 @@ static void exynos3250_jpeg_set_jpeg_addr(struct s5p_jpeg_ctx *ctx)
else
vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
- jpeg_addr = vb2_dma_contig_plane_dma_addr(vb, 0);
+ jpeg_addr = vb2_dma_contig_plane_dma_addr(&vb->vb2_buf, 0);
exynos3250_jpeg_jpgadr(jpeg->regs, jpeg_addr);
}
@@ -3220,7 +3223,7 @@ static struct platform_driver s5p_jpeg_driver = {
module_platform_driver(s5p_jpeg_driver);
-MODULE_AUTHOR("Andrzej Pietrasiewicz <andrzej.p@samsung.com>");
+MODULE_AUTHOR("Andrzej Pietrasiewicz <andrzejtp2010@gmail.com>");
MODULE_AUTHOR("Jacek Anaszewski <j.anaszewski@samsung.com>");
MODULE_DESCRIPTION("Samsung JPEG codec driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.h b/drivers/media/platform/s5p-jpeg/jpeg-core.h
index a46465e10351..144c102ff05f 100644
--- a/drivers/media/platform/s5p-jpeg/jpeg-core.h
+++ b/drivers/media/platform/s5p-jpeg/jpeg-core.h
@@ -3,7 +3,7 @@
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
- * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ * Author: Andrzej Pietrasiewicz <andrzejtp2010@gmail.com>
*
* 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
@@ -153,7 +153,7 @@ struct s5p_jpeg_variant {
/**
* struct jpeg_fmt - driver's internal color format data
- * @name: format descritpion
+ * @name: format description
* @fourcc: the fourcc code, 0 if not applicable
* @depth: number of bits per pixel
* @colplanes: number of color planes (1 for packed formats)
@@ -193,7 +193,7 @@ struct s5p_jpeg_marker {
* @sos: SOS marker's position relative to the buffer beginning
* @dht: DHT markers' positions relative to the buffer beginning
* @dqt: DQT markers' positions relative to the buffer beginning
- * @sof: SOF0 marker's postition relative to the buffer beginning
+ * @sof: SOF0 marker's position relative to the buffer beginning
* @sof_len: SOF0 marker's payload length (without length field itself)
* @components: number of image components
* @size: image buffer size in bytes
diff --git a/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.c b/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.c
index b5f20e722b63..59c6263a71bf 100644
--- a/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.c
+++ b/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.c
@@ -3,7 +3,7 @@
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
- * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ * Author: Andrzej Pietrasiewicz <andrzejtp2010@gmail.com>
*
* 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
diff --git a/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.h b/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.h
index f208fa3ed738..bfe746f8f750 100644
--- a/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.h
+++ b/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.h
@@ -3,7 +3,7 @@
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
- * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ * Author: Andrzej Pietrasiewicz <andrzejtp2010@gmail.com>
*
* 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
diff --git a/drivers/media/platform/s5p-jpeg/jpeg-regs.h b/drivers/media/platform/s5p-jpeg/jpeg-regs.h
index df790b10140c..574f0e8021e5 100644
--- a/drivers/media/platform/s5p-jpeg/jpeg-regs.h
+++ b/drivers/media/platform/s5p-jpeg/jpeg-regs.h
@@ -5,7 +5,7 @@
* Copyright (c) 2011-2014 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
- * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ * Author: Andrzej Pietrasiewicz <andrzejtp2010@gmail.com>
* Author: Jacek Anaszewski <j.anaszewski@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c
index 8a5ba3bec3af..9a53d3908b52 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c
@@ -1089,11 +1089,17 @@ static struct device *s5p_mfc_alloc_memdev(struct device *dev,
device_initialize(child);
dev_set_name(child, "%s:%s", dev_name(dev), name);
child->parent = dev;
- child->bus = dev->bus;
child->coherent_dma_mask = dev->coherent_dma_mask;
child->dma_mask = dev->dma_mask;
child->release = s5p_mfc_memdev_release;
+ /*
+ * The memdevs are not proper OF platform devices, so in order for them
+ * to be treated as valid DMA masters we need a bit of a hack to force
+ * them to inherit the MFC node's DMA configuration.
+ */
+ of_dma_configure(child, dev->of_node, true);
+
if (device_add(child) == 0) {
ret = of_reserved_mem_device_init_by_idx(child, dev->of_node,
idx);
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h
index 20442a9b9f7a..24148020baa9 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h
@@ -268,7 +268,7 @@ struct s5p_mfc_priv_buf {
* @enc_ctrl_handler: control framework handler for encoding
* @pm: power management control
* @variant: MFC hardware variant information
- * @num_inst: couter of active MFC instances
+ * @num_inst: counter of active MFC instances
* @irqlock: lock for operations on videobuf2 queues
* @condlock: lock for changing/checking if a context is ready to be
* processed
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c
index ee7b15b335e0..9f832ba7bc8c 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c
@@ -51,7 +51,7 @@ int s5p_mfc_load_firmware(struct s5p_mfc_dev *dev)
struct firmware *fw_blob;
int i, err = -EINVAL;
- /* Firmare has to be present as a separate file or compiled
+ /* Firmware has to be present as a separate file or compiled
* into kernel. */
mfc_debug_enter();
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c
index f4c0e3a8f27d..e111f9c47179 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c
@@ -602,7 +602,7 @@ static int vidioc_querybuf(struct file *file, void *priv,
int i;
if (buf->memory != V4L2_MEMORY_MMAP) {
- mfc_err("Only mmaped buffers can be used\n");
+ mfc_err("Only mmapped buffers can be used\n");
return -EINVAL;
}
mfc_debug(2, "State: %d, buf->type: %d\n", ctx->state, buf->type);
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c
index 0913881219ff..6144e95f6425 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c
@@ -1293,7 +1293,7 @@ static int s5p_mfc_run_init_dec_buffers(struct s5p_mfc_ctx *ctx)
* First set the output frame buffers
*/
if (ctx->capture_state != QUEUE_BUFS_MMAPED) {
- mfc_err("It seems that not all destination buffers were mmaped\nMFC requires that all destination are mmaped before starting processing\n");
+ mfc_err("It seems that not all destination buffers were mmapped\nMFC requires that all destination are mmapped before starting processing\n");
return -EAGAIN;
}
if (list_empty(&ctx->src_queue)) {
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c
index 7c629be43205..281699ab7fe1 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c
@@ -53,7 +53,7 @@ static int s5p_mfc_alloc_dec_temp_buffers_v6(struct s5p_mfc_ctx *ctx)
return 0;
}
-/* Release temproary buffers for decoding */
+/* Release temporary buffers for decoding */
static void s5p_mfc_release_dec_desc_buffer_v6(struct s5p_mfc_ctx *ctx)
{
/* NOP */
@@ -1928,7 +1928,7 @@ static inline int s5p_mfc_run_init_dec_buffers(struct s5p_mfc_ctx *ctx)
if (ctx->capture_state != QUEUE_BUFS_MMAPED) {
mfc_err("It seems that not all destination buffers were\n"
- "mmaped.MFC requires that all destination are mmaped\n"
+ "mmapped.MFC requires that all destination are mmapped\n"
"before starting processing.\n");
return -EAGAIN;
}
diff --git a/drivers/media/platform/seco-cec/seco-cec.h b/drivers/media/platform/seco-cec/seco-cec.h
index e632c4a2a044..843de8c7dfd4 100644
--- a/drivers/media/platform/seco-cec/seco-cec.h
+++ b/drivers/media/platform/seco-cec/seco-cec.h
@@ -106,7 +106,7 @@
#define SECOCEC_IR_COMMAND_MASK 0x007F
#define SECOCEC_IR_COMMAND_SHL 0
#define SECOCEC_IR_ADDRESS_MASK 0x1F00
-#define SECOCEC_IR_ADDRESS_SHL 7
+#define SECOCEC_IR_ADDRESS_SHL 8
#define SECOCEC_IR_TOGGLE_MASK 0x8000
#define SECOCEC_IR_TOGGLE_SHL 15
diff --git a/drivers/media/platform/sh_veu.c b/drivers/media/platform/sh_veu.c
index 09ae64a0004c..d277cc674349 100644
--- a/drivers/media/platform/sh_veu.c
+++ b/drivers/media/platform/sh_veu.c
@@ -273,13 +273,13 @@ static void sh_veu_process(struct sh_veu_dev *veu,
static void sh_veu_device_run(void *priv)
{
struct sh_veu_dev *veu = priv;
- struct vb2_buffer *src_buf, *dst_buf;
+ struct vb2_v4l2_buffer *src_buf, *dst_buf;
src_buf = v4l2_m2m_next_src_buf(veu->m2m_ctx);
dst_buf = v4l2_m2m_next_dst_buf(veu->m2m_ctx);
if (src_buf && dst_buf)
- sh_veu_process(veu, src_buf, dst_buf);
+ sh_veu_process(veu, &src_buf->vb2_buf, &dst_buf->vb2_buf);
}
/* ========== video ioctls ========== */
diff --git a/drivers/media/platform/soc_camera/Kconfig b/drivers/media/platform/soc_camera/Kconfig
deleted file mode 100644
index 669d116b8f09..000000000000
--- a/drivers/media/platform/soc_camera/Kconfig
+++ /dev/null
@@ -1,26 +0,0 @@
-config SOC_CAMERA
- tristate "SoC camera support"
- depends on VIDEO_V4L2 && HAS_DMA && I2C
- select VIDEOBUF2_CORE
- help
- SoC Camera is a common API to several cameras, not connecting
- over a bus like PCI or USB. For example some i2c camera connected
- directly to the data bus of an SoC.
-
-config SOC_CAMERA_SCALE_CROP
- tristate
-
-config SOC_CAMERA_PLATFORM
- tristate "platform camera support"
- depends on SOC_CAMERA
- help
- This is a generic SoC camera platform driver, useful for testing
-
-config VIDEO_SH_MOBILE_CEU
- tristate "SuperH Mobile CEU Interface driver"
- depends on VIDEO_DEV && SOC_CAMERA && HAVE_CLK
- depends on ARCH_SHMOBILE || COMPILE_TEST
- select VIDEOBUF2_DMA_CONTIG
- select SOC_CAMERA_SCALE_CROP
- ---help---
- This is a v4l2 driver for the SuperH Mobile CEU Interface
diff --git a/drivers/media/platform/soc_camera/Makefile b/drivers/media/platform/soc_camera/Makefile
deleted file mode 100644
index 07a451e8b228..000000000000
--- a/drivers/media/platform/soc_camera/Makefile
+++ /dev/null
@@ -1,9 +0,0 @@
-obj-$(CONFIG_SOC_CAMERA) += soc_camera.o soc_mediabus.o
-obj-$(CONFIG_SOC_CAMERA_SCALE_CROP) += soc_scale_crop.o
-
-# a platform subdevice driver stub, allowing to support cameras by adding a
-# couple of callback functions to the board code
-obj-$(CONFIG_SOC_CAMERA_PLATFORM) += soc_camera_platform.o
-
-# soc-camera host drivers have to be linked after camera drivers
-obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o
diff --git a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
deleted file mode 100644
index 6803f744e307..000000000000
--- a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
+++ /dev/null
@@ -1,1810 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * V4L2 Driver for SuperH Mobile CEU interface
- *
- * Copyright (C) 2008 Magnus Damm
- *
- * Based on V4L2 Driver for PXA camera host - "pxa_camera.c",
- *
- * Copyright (C) 2006, Sascha Hauer, Pengutronix
- * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/io.h>
-#include <linux/completion.h>
-#include <linux/delay.h>
-#include <linux/dma-mapping.h>
-#include <linux/err.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/interrupt.h>
-#include <linux/kernel.h>
-#include <linux/mm.h>
-#include <linux/moduleparam.h>
-#include <linux/of.h>
-#include <linux/time.h>
-#include <linux/slab.h>
-#include <linux/device.h>
-#include <linux/platform_device.h>
-#include <linux/videodev2.h>
-#include <linux/pm_runtime.h>
-#include <linux/sched.h>
-
-#include <media/v4l2-async.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-dev.h>
-#include <media/soc_camera.h>
-#include <media/drv-intf/sh_mobile_ceu.h>
-#include <media/videobuf2-dma-contig.h>
-#include <media/v4l2-mediabus.h>
-#include <media/drv-intf/soc_mediabus.h>
-
-#include "soc_scale_crop.h"
-
-/* register offsets for sh7722 / sh7723 */
-
-#define CAPSR 0x00 /* Capture start register */
-#define CAPCR 0x04 /* Capture control register */
-#define CAMCR 0x08 /* Capture interface control register */
-#define CMCYR 0x0c /* Capture interface cycle register */
-#define CAMOR 0x10 /* Capture interface offset register */
-#define CAPWR 0x14 /* Capture interface width register */
-#define CAIFR 0x18 /* Capture interface input format register */
-#define CSTCR 0x20 /* Camera strobe control register (<= sh7722) */
-#define CSECR 0x24 /* Camera strobe emission count register (<= sh7722) */
-#define CRCNTR 0x28 /* CEU register control register */
-#define CRCMPR 0x2c /* CEU register forcible control register */
-#define CFLCR 0x30 /* Capture filter control register */
-#define CFSZR 0x34 /* Capture filter size clip register */
-#define CDWDR 0x38 /* Capture destination width register */
-#define CDAYR 0x3c /* Capture data address Y register */
-#define CDACR 0x40 /* Capture data address C register */
-#define CDBYR 0x44 /* Capture data bottom-field address Y register */
-#define CDBCR 0x48 /* Capture data bottom-field address C register */
-#define CBDSR 0x4c /* Capture bundle destination size register */
-#define CFWCR 0x5c /* Firewall operation control register */
-#define CLFCR 0x60 /* Capture low-pass filter control register */
-#define CDOCR 0x64 /* Capture data output control register */
-#define CDDCR 0x68 /* Capture data complexity level register */
-#define CDDAR 0x6c /* Capture data complexity level address register */
-#define CEIER 0x70 /* Capture event interrupt enable register */
-#define CETCR 0x74 /* Capture event flag clear register */
-#define CSTSR 0x7c /* Capture status register */
-#define CSRTR 0x80 /* Capture software reset register */
-#define CDSSR 0x84 /* Capture data size register */
-#define CDAYR2 0x90 /* Capture data address Y register 2 */
-#define CDACR2 0x94 /* Capture data address C register 2 */
-#define CDBYR2 0x98 /* Capture data bottom-field address Y register 2 */
-#define CDBCR2 0x9c /* Capture data bottom-field address C register 2 */
-
-#undef DEBUG_GEOMETRY
-#ifdef DEBUG_GEOMETRY
-#define dev_geo dev_info
-#else
-#define dev_geo dev_dbg
-#endif
-
-/* per video frame buffer */
-struct sh_mobile_ceu_buffer {
- struct vb2_v4l2_buffer vb; /* v4l buffer must be first */
- struct list_head queue;
-};
-
-struct sh_mobile_ceu_dev {
- struct soc_camera_host ici;
-
- unsigned int irq;
- void __iomem *base;
- size_t video_limit;
- size_t buf_total;
-
- spinlock_t lock; /* Protects video buffer lists */
- struct list_head capture;
- struct vb2_v4l2_buffer *active;
-
- struct sh_mobile_ceu_info *pdata;
- struct completion complete;
-
- u32 cflcr;
-
- /* static max sizes either from platform data or default */
- int max_width;
- int max_height;
-
- enum v4l2_field field;
- int sequence;
- unsigned long flags;
-
- unsigned int image_mode:1;
- unsigned int is_16bit:1;
- unsigned int frozen:1;
-};
-
-struct sh_mobile_ceu_cam {
- /* CEU offsets within the camera output, before the CEU scaler */
- unsigned int ceu_left;
- unsigned int ceu_top;
- /* Client output, as seen by the CEU */
- unsigned int width;
- unsigned int height;
- /*
- * User window from S_SELECTION / G_SELECTION, produced by client cropping and
- * scaling, CEU scaling and CEU cropping, mapped back onto the client
- * input window
- */
- struct v4l2_rect subrect;
- /* Camera cropping rectangle */
- struct v4l2_rect rect;
- const struct soc_mbus_pixelfmt *extra_fmt;
- u32 code;
-};
-
-static struct sh_mobile_ceu_buffer *to_ceu_vb(struct vb2_v4l2_buffer *vbuf)
-{
- return container_of(vbuf, struct sh_mobile_ceu_buffer, vb);
-}
-
-static void ceu_write(struct sh_mobile_ceu_dev *priv,
- unsigned long reg_offs, u32 data)
-{
- iowrite32(data, priv->base + reg_offs);
-}
-
-static u32 ceu_read(struct sh_mobile_ceu_dev *priv, unsigned long reg_offs)
-{
- return ioread32(priv->base + reg_offs);
-}
-
-static int sh_mobile_ceu_soft_reset(struct sh_mobile_ceu_dev *pcdev)
-{
- int i, success = 0;
-
- ceu_write(pcdev, CAPSR, 1 << 16); /* reset */
-
- /* wait CSTSR.CPTON bit */
- for (i = 0; i < 1000; i++) {
- if (!(ceu_read(pcdev, CSTSR) & 1)) {
- success++;
- break;
- }
- udelay(1);
- }
-
- /* wait CAPSR.CPKIL bit */
- for (i = 0; i < 1000; i++) {
- if (!(ceu_read(pcdev, CAPSR) & (1 << 16))) {
- success++;
- break;
- }
- udelay(1);
- }
-
- if (2 != success) {
- dev_warn(pcdev->ici.v4l2_dev.dev, "soft reset time out\n");
- return -EIO;
- }
-
- return 0;
-}
-
-/*
- * Videobuf operations
- */
-
-/*
- * .queue_setup() is called to check, whether the driver can accept the
- * requested number of buffers and to fill in plane sizes
- * for the current frame format if required
- */
-static int sh_mobile_ceu_videobuf_setup(struct vb2_queue *vq,
- unsigned int *count, unsigned int *num_planes,
- unsigned int sizes[], struct device *alloc_devs[])
-{
- struct soc_camera_device *icd = soc_camera_from_vb2q(vq);
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct sh_mobile_ceu_dev *pcdev = ici->priv;
-
- if (!vq->num_buffers)
- pcdev->sequence = 0;
-
- if (!*count)
- *count = 2;
-
- /* Called from VIDIOC_REQBUFS or in compatibility mode */
- if (!*num_planes)
- sizes[0] = icd->sizeimage;
- else if (sizes[0] < icd->sizeimage)
- return -EINVAL;
-
- /* If *num_planes != 0, we have already verified *count. */
- if (pcdev->video_limit) {
- size_t size = PAGE_ALIGN(sizes[0]) * *count;
-
- if (size + pcdev->buf_total > pcdev->video_limit)
- *count = (pcdev->video_limit - pcdev->buf_total) /
- PAGE_ALIGN(sizes[0]);
- }
-
- *num_planes = 1;
-
- dev_dbg(icd->parent, "count=%d, size=%u\n", *count, sizes[0]);
-
- return 0;
-}
-
-#define CEU_CETCR_MAGIC 0x0317f313 /* acknowledge magical interrupt sources */
-#define CEU_CETCR_IGRW (1 << 4) /* prohibited register access interrupt bit */
-#define CEU_CEIER_CPEIE (1 << 0) /* one-frame capture end interrupt */
-#define CEU_CEIER_VBP (1 << 20) /* vbp error */
-#define CEU_CAPCR_CTNCP (1 << 16) /* continuous capture mode (if set) */
-#define CEU_CEIER_MASK (CEU_CEIER_CPEIE | CEU_CEIER_VBP)
-
-
-/*
- * return value doesn't reflex the success/failure to queue the new buffer,
- * but rather the status of the previous buffer.
- */
-static int sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev)
-{
- struct soc_camera_device *icd = pcdev->ici.icd;
- dma_addr_t phys_addr_top, phys_addr_bottom;
- unsigned long top1, top2;
- unsigned long bottom1, bottom2;
- u32 status;
- bool planar;
- int ret = 0;
-
- /*
- * The hardware is _very_ picky about this sequence. Especially
- * the CEU_CETCR_MAGIC value. It seems like we need to acknowledge
- * several not-so-well documented interrupt sources in CETCR.
- */
- ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) & ~CEU_CEIER_MASK);
- status = ceu_read(pcdev, CETCR);
- ceu_write(pcdev, CETCR, ~status & CEU_CETCR_MAGIC);
- if (!pcdev->frozen)
- ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) | CEU_CEIER_MASK);
- ceu_write(pcdev, CAPCR, ceu_read(pcdev, CAPCR) & ~CEU_CAPCR_CTNCP);
- ceu_write(pcdev, CETCR, CEU_CETCR_MAGIC ^ CEU_CETCR_IGRW);
-
- /*
- * When a VBP interrupt occurs, a capture end interrupt does not occur
- * and the image of that frame is not captured correctly. So, soft reset
- * is needed here.
- */
- if (status & CEU_CEIER_VBP) {
- sh_mobile_ceu_soft_reset(pcdev);
- ret = -EIO;
- }
-
- if (pcdev->frozen) {
- complete(&pcdev->complete);
- return ret;
- }
-
- if (!pcdev->active)
- return ret;
-
- if (V4L2_FIELD_INTERLACED_BT == pcdev->field) {
- top1 = CDBYR;
- top2 = CDBCR;
- bottom1 = CDAYR;
- bottom2 = CDACR;
- } else {
- top1 = CDAYR;
- top2 = CDACR;
- bottom1 = CDBYR;
- bottom2 = CDBCR;
- }
-
- phys_addr_top =
- vb2_dma_contig_plane_dma_addr(&pcdev->active->vb2_buf, 0);
-
- switch (icd->current_fmt->host_fmt->fourcc) {
- case V4L2_PIX_FMT_NV12:
- case V4L2_PIX_FMT_NV21:
- case V4L2_PIX_FMT_NV16:
- case V4L2_PIX_FMT_NV61:
- planar = true;
- break;
- default:
- planar = false;
- }
-
- ceu_write(pcdev, top1, phys_addr_top);
- if (V4L2_FIELD_NONE != pcdev->field) {
- phys_addr_bottom = phys_addr_top + icd->bytesperline;
- ceu_write(pcdev, bottom1, phys_addr_bottom);
- }
-
- if (planar) {
- phys_addr_top += icd->bytesperline * icd->user_height;
- ceu_write(pcdev, top2, phys_addr_top);
- if (V4L2_FIELD_NONE != pcdev->field) {
- phys_addr_bottom = phys_addr_top + icd->bytesperline;
- ceu_write(pcdev, bottom2, phys_addr_bottom);
- }
- }
-
- ceu_write(pcdev, CAPSR, 0x1); /* start capture */
-
- return ret;
-}
-
-static int sh_mobile_ceu_videobuf_prepare(struct vb2_buffer *vb)
-{
- struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
- struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vbuf);
-
- /* Added list head initialization on alloc */
- WARN(!list_empty(&buf->queue), "Buffer %p on queue!\n", vb);
-
- return 0;
-}
-
-static void sh_mobile_ceu_videobuf_queue(struct vb2_buffer *vb)
-{
- struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
- struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct sh_mobile_ceu_dev *pcdev = ici->priv;
- struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vbuf);
- unsigned long size;
-
- size = icd->sizeimage;
-
- if (vb2_plane_size(vb, 0) < size) {
- dev_err(icd->parent, "Buffer #%d too small (%lu < %lu)\n",
- vb->index, vb2_plane_size(vb, 0), size);
- goto error;
- }
-
- vb2_set_plane_payload(vb, 0, size);
-
- dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__,
- vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0));
-
-#ifdef DEBUG
- /*
- * This can be useful if you want to see if we actually fill
- * the buffer with something
- */
- if (vb2_plane_vaddr(vb, 0))
- memset(vb2_plane_vaddr(vb, 0), 0xaa, vb2_get_plane_payload(vb, 0));
-#endif
-
- spin_lock_irq(&pcdev->lock);
- list_add_tail(&buf->queue, &pcdev->capture);
-
- if (!pcdev->active) {
- /*
- * Because there were no active buffer at this moment,
- * we are not interested in the return value of
- * sh_mobile_ceu_capture here.
- */
- pcdev->active = vbuf;
- sh_mobile_ceu_capture(pcdev);
- }
- spin_unlock_irq(&pcdev->lock);
-
- return;
-
-error:
- vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
-}
-
-static void sh_mobile_ceu_videobuf_release(struct vb2_buffer *vb)
-{
- struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
- struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vbuf);
- struct sh_mobile_ceu_dev *pcdev = ici->priv;
-
- spin_lock_irq(&pcdev->lock);
-
- if (pcdev->active == vbuf) {
- /* disable capture (release DMA buffer), reset */
- ceu_write(pcdev, CAPSR, 1 << 16);
- pcdev->active = NULL;
- }
-
- /*
- * Doesn't hurt also if the list is empty, but it hurts, if queuing the
- * buffer failed, and .buf_init() hasn't been called
- */
- if (buf->queue.next)
- list_del_init(&buf->queue);
-
- pcdev->buf_total -= PAGE_ALIGN(vb2_plane_size(vb, 0));
- dev_dbg(icd->parent, "%s() %zu bytes buffers\n", __func__,
- pcdev->buf_total);
-
- spin_unlock_irq(&pcdev->lock);
-}
-
-static int sh_mobile_ceu_videobuf_init(struct vb2_buffer *vb)
-{
- struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
- struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct sh_mobile_ceu_dev *pcdev = ici->priv;
-
- pcdev->buf_total += PAGE_ALIGN(vb2_plane_size(vb, 0));
- dev_dbg(icd->parent, "%s() %zu bytes buffers\n", __func__,
- pcdev->buf_total);
-
- /* This is for locking debugging only */
- INIT_LIST_HEAD(&to_ceu_vb(vbuf)->queue);
- return 0;
-}
-
-static void sh_mobile_ceu_stop_streaming(struct vb2_queue *q)
-{
- struct soc_camera_device *icd = soc_camera_from_vb2q(q);
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct sh_mobile_ceu_dev *pcdev = ici->priv;
- struct list_head *buf_head, *tmp;
- struct vb2_v4l2_buffer *vbuf;
-
- spin_lock_irq(&pcdev->lock);
-
- pcdev->active = NULL;
-
- list_for_each_safe(buf_head, tmp, &pcdev->capture) {
- vbuf = &list_entry(buf_head, struct sh_mobile_ceu_buffer,
- queue)->vb;
- vb2_buffer_done(&vbuf->vb2_buf, VB2_BUF_STATE_DONE);
- list_del_init(buf_head);
- }
-
- spin_unlock_irq(&pcdev->lock);
-
- sh_mobile_ceu_soft_reset(pcdev);
-}
-
-static const struct vb2_ops sh_mobile_ceu_videobuf_ops = {
- .queue_setup = sh_mobile_ceu_videobuf_setup,
- .buf_prepare = sh_mobile_ceu_videobuf_prepare,
- .buf_queue = sh_mobile_ceu_videobuf_queue,
- .buf_cleanup = sh_mobile_ceu_videobuf_release,
- .buf_init = sh_mobile_ceu_videobuf_init,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
- .stop_streaming = sh_mobile_ceu_stop_streaming,
-};
-
-static irqreturn_t sh_mobile_ceu_irq(int irq, void *data)
-{
- struct sh_mobile_ceu_dev *pcdev = data;
- struct vb2_v4l2_buffer *vbuf;
- int ret;
-
- spin_lock(&pcdev->lock);
-
- vbuf = pcdev->active;
- if (!vbuf)
- /* Stale interrupt from a released buffer */
- goto out;
-
- list_del_init(&to_ceu_vb(vbuf)->queue);
-
- if (!list_empty(&pcdev->capture))
- pcdev->active = &list_entry(pcdev->capture.next,
- struct sh_mobile_ceu_buffer, queue)->vb;
- else
- pcdev->active = NULL;
-
- ret = sh_mobile_ceu_capture(pcdev);
- vbuf->vb2_buf.timestamp = ktime_get_ns();
- if (!ret) {
- vbuf->field = pcdev->field;
- vbuf->sequence = pcdev->sequence++;
- }
- vb2_buffer_done(&vbuf->vb2_buf,
- ret < 0 ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
-
-out:
- spin_unlock(&pcdev->lock);
-
- return IRQ_HANDLED;
-}
-
-static int sh_mobile_ceu_add_device(struct soc_camera_device *icd)
-{
- dev_info(icd->parent,
- "SuperH Mobile CEU driver attached to camera %d\n",
- icd->devnum);
-
- return 0;
-}
-
-static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd)
-{
- dev_info(icd->parent,
- "SuperH Mobile CEU driver detached from camera %d\n",
- icd->devnum);
-}
-
-/* Called with .host_lock held */
-static int sh_mobile_ceu_clock_start(struct soc_camera_host *ici)
-{
- struct sh_mobile_ceu_dev *pcdev = ici->priv;
-
- pm_runtime_get_sync(ici->v4l2_dev.dev);
-
- pcdev->buf_total = 0;
-
- sh_mobile_ceu_soft_reset(pcdev);
-
- return 0;
-}
-
-/* Called with .host_lock held */
-static void sh_mobile_ceu_clock_stop(struct soc_camera_host *ici)
-{
- struct sh_mobile_ceu_dev *pcdev = ici->priv;
-
- /* disable capture, disable interrupts */
- ceu_write(pcdev, CEIER, 0);
- sh_mobile_ceu_soft_reset(pcdev);
-
- /* make sure active buffer is canceled */
- spin_lock_irq(&pcdev->lock);
- if (pcdev->active) {
- list_del_init(&to_ceu_vb(pcdev->active)->queue);
- vb2_buffer_done(&pcdev->active->vb2_buf, VB2_BUF_STATE_ERROR);
- pcdev->active = NULL;
- }
- spin_unlock_irq(&pcdev->lock);
-
- pm_runtime_put(ici->v4l2_dev.dev);
-}
-
-/*
- * See chapter 29.4.12 "Capture Filter Control Register (CFLCR)"
- * in SH7722 Hardware Manual
- */
-static unsigned int size_dst(unsigned int src, unsigned int scale)
-{
- unsigned int mant_pre = scale >> 12;
- if (!src || !scale)
- return src;
- return ((mant_pre + 2 * (src - 1)) / (2 * mant_pre) - 1) *
- mant_pre * 4096 / scale + 1;
-}
-
-static u16 calc_scale(unsigned int src, unsigned int *dst)
-{
- u16 scale;
-
- if (src == *dst)
- return 0;
-
- scale = (src * 4096 / *dst) & ~7;
-
- while (scale > 4096 && size_dst(src, scale) < *dst)
- scale -= 8;
-
- *dst = size_dst(src, scale);
-
- return scale;
-}
-
-/* rect is guaranteed to not exceed the scaled camera rectangle */
-static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd)
-{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct sh_mobile_ceu_cam *cam = icd->host_priv;
- struct sh_mobile_ceu_dev *pcdev = ici->priv;
- unsigned int height, width, cdwdr_width, in_width, in_height;
- unsigned int left_offset, top_offset;
- u32 camor;
-
- dev_geo(icd->parent, "Crop %ux%u@%u:%u\n",
- icd->user_width, icd->user_height, cam->ceu_left, cam->ceu_top);
-
- left_offset = cam->ceu_left;
- top_offset = cam->ceu_top;
-
- WARN_ON(icd->user_width & 3 || icd->user_height & 3);
-
- width = icd->user_width;
-
- if (pcdev->image_mode) {
- in_width = cam->width;
- if (!pcdev->is_16bit) {
- in_width *= 2;
- left_offset *= 2;
- }
- } else {
- unsigned int w_factor;
-
- switch (icd->current_fmt->host_fmt->packing) {
- case SOC_MBUS_PACKING_2X8_PADHI:
- w_factor = 2;
- break;
- default:
- w_factor = 1;
- }
-
- in_width = cam->width * w_factor;
- left_offset *= w_factor;
- }
-
- cdwdr_width = icd->bytesperline;
-
- height = icd->user_height;
- in_height = cam->height;
- if (V4L2_FIELD_NONE != pcdev->field) {
- height = (height / 2) & ~3;
- in_height /= 2;
- top_offset /= 2;
- cdwdr_width *= 2;
- }
-
- /* Set CAMOR, CAPWR, CFSZR, take care of CDWDR */
- camor = left_offset | (top_offset << 16);
-
- dev_geo(icd->parent,
- "CAMOR 0x%x, CAPWR 0x%x, CFSZR 0x%x, CDWDR 0x%x\n", camor,
- (in_height << 16) | in_width, (height << 16) | width,
- cdwdr_width);
-
- ceu_write(pcdev, CAMOR, camor);
- ceu_write(pcdev, CAPWR, (in_height << 16) | in_width);
- /* CFSZR clipping is applied _after_ the scaling filter (CFLCR) */
- ceu_write(pcdev, CFSZR, (height << 16) | width);
- ceu_write(pcdev, CDWDR, cdwdr_width);
-}
-
-static u32 capture_save_reset(struct sh_mobile_ceu_dev *pcdev)
-{
- u32 capsr = ceu_read(pcdev, CAPSR);
- ceu_write(pcdev, CAPSR, 1 << 16); /* reset, stop capture */
- return capsr;
-}
-
-static void capture_restore(struct sh_mobile_ceu_dev *pcdev, u32 capsr)
-{
- unsigned long timeout = jiffies + 10 * HZ;
-
- /*
- * Wait until the end of the current frame. It can take a long time,
- * but if it has been aborted by a CAPSR reset, it shoule exit sooner.
- */
- while ((ceu_read(pcdev, CSTSR) & 1) && time_before(jiffies, timeout))
- msleep(1);
-
- if (time_after(jiffies, timeout)) {
- dev_err(pcdev->ici.v4l2_dev.dev,
- "Timeout waiting for frame end! Interface problem?\n");
- return;
- }
-
- /* Wait until reset clears, this shall not hang... */
- while (ceu_read(pcdev, CAPSR) & (1 << 16))
- udelay(10);
-
- /* Anything to restore? */
- if (capsr & ~(1 << 16))
- ceu_write(pcdev, CAPSR, capsr);
-}
-
-#define CEU_BUS_FLAGS (V4L2_MBUS_MASTER | \
- V4L2_MBUS_PCLK_SAMPLE_RISING | \
- V4L2_MBUS_HSYNC_ACTIVE_HIGH | \
- V4L2_MBUS_HSYNC_ACTIVE_LOW | \
- V4L2_MBUS_VSYNC_ACTIVE_HIGH | \
- V4L2_MBUS_VSYNC_ACTIVE_LOW | \
- V4L2_MBUS_DATA_ACTIVE_HIGH)
-
-/* Capture is not running, no interrupts, no locking needed */
-static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd)
-{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct sh_mobile_ceu_dev *pcdev = ici->priv;
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- struct sh_mobile_ceu_cam *cam = icd->host_priv;
- struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
- unsigned long value, common_flags = CEU_BUS_FLAGS;
- u32 capsr = capture_save_reset(pcdev);
- unsigned int yuv_lineskip;
- int ret;
-
- /*
- * If the client doesn't implement g_mbus_config, we just use our
- * platform data
- */
- ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
- if (!ret) {
- common_flags = soc_mbus_config_compatible(&cfg,
- common_flags);
- if (!common_flags)
- return -EINVAL;
- } else if (ret != -ENOIOCTLCMD) {
- return ret;
- }
-
- /* Make choises, based on platform preferences */
- if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) &&
- (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) {
- if (pcdev->flags & SH_CEU_FLAG_HSYNC_LOW)
- common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH;
- else
- common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW;
- }
-
- if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) &&
- (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) {
- if (pcdev->flags & SH_CEU_FLAG_VSYNC_LOW)
- common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH;
- else
- common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW;
- }
-
- cfg.flags = common_flags;
- ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
- if (ret < 0 && ret != -ENOIOCTLCMD)
- return ret;
-
- if (icd->current_fmt->host_fmt->bits_per_sample > 8)
- pcdev->is_16bit = 1;
- else
- pcdev->is_16bit = 0;
-
- ceu_write(pcdev, CRCNTR, 0);
- ceu_write(pcdev, CRCMPR, 0);
-
- value = 0x00000010; /* data fetch by default */
- yuv_lineskip = 0x10;
-
- switch (icd->current_fmt->host_fmt->fourcc) {
- case V4L2_PIX_FMT_NV12:
- case V4L2_PIX_FMT_NV21:
- /* convert 4:2:2 -> 4:2:0 */
- yuv_lineskip = 0; /* skip for NV12/21, no skip for NV16/61 */
- /* fall-through */
- case V4L2_PIX_FMT_NV16:
- case V4L2_PIX_FMT_NV61:
- switch (cam->code) {
- case MEDIA_BUS_FMT_UYVY8_2X8:
- value = 0x00000000; /* Cb0, Y0, Cr0, Y1 */
- break;
- case MEDIA_BUS_FMT_VYUY8_2X8:
- value = 0x00000100; /* Cr0, Y0, Cb0, Y1 */
- break;
- case MEDIA_BUS_FMT_YUYV8_2X8:
- value = 0x00000200; /* Y0, Cb0, Y1, Cr0 */
- break;
- case MEDIA_BUS_FMT_YVYU8_2X8:
- value = 0x00000300; /* Y0, Cr0, Y1, Cb0 */
- break;
- default:
- BUG();
- }
- }
-
- if (icd->current_fmt->host_fmt->fourcc == V4L2_PIX_FMT_NV21 ||
- icd->current_fmt->host_fmt->fourcc == V4L2_PIX_FMT_NV61)
- value ^= 0x00000100; /* swap U, V to change from NV1x->NVx1 */
-
- value |= common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW ? 1 << 1 : 0;
- value |= common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW ? 1 << 0 : 0;
-
- if (pcdev->is_16bit)
- value |= 1 << 12;
- else if (pcdev->flags & SH_CEU_FLAG_LOWER_8BIT)
- value |= 2 << 12;
-
- ceu_write(pcdev, CAMCR, value);
-
- ceu_write(pcdev, CAPCR, 0x00300000);
-
- switch (pcdev->field) {
- case V4L2_FIELD_INTERLACED_TB:
- value = 0x101;
- break;
- case V4L2_FIELD_INTERLACED_BT:
- value = 0x102;
- break;
- default:
- value = 0;
- break;
- }
- ceu_write(pcdev, CAIFR, value);
-
- sh_mobile_ceu_set_rect(icd);
- mdelay(1);
-
- dev_geo(icd->parent, "CFLCR 0x%x\n", pcdev->cflcr);
- ceu_write(pcdev, CFLCR, pcdev->cflcr);
-
- /*
- * A few words about byte order (observed in Big Endian mode)
- *
- * In data fetch mode bytes are received in chunks of 8 bytes.
- * D0, D1, D2, D3, D4, D5, D6, D7 (D0 received first)
- *
- * The data is however by default written to memory in reverse order:
- * D7, D6, D5, D4, D3, D2, D1, D0 (D7 written to lowest byte)
- *
- * The lowest three bits of CDOCR allows us to do swapping,
- * using 7 we swap the data bytes to match the incoming order:
- * D0, D1, D2, D3, D4, D5, D6, D7
- */
- value = 0x00000007 | yuv_lineskip;
-
- ceu_write(pcdev, CDOCR, value);
- ceu_write(pcdev, CFWCR, 0); /* keep "datafetch firewall" disabled */
-
- capture_restore(pcdev, capsr);
-
- /* not in bundle mode: skip CBDSR, CDAYR2, CDACR2, CDBYR2, CDBCR2 */
- return 0;
-}
-
-static int sh_mobile_ceu_try_bus_param(struct soc_camera_device *icd,
- unsigned char buswidth)
-{
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- unsigned long common_flags = CEU_BUS_FLAGS;
- struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
- int ret;
-
- ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
- if (!ret)
- common_flags = soc_mbus_config_compatible(&cfg,
- common_flags);
- else if (ret != -ENOIOCTLCMD)
- return ret;
-
- if (!common_flags || buswidth > 16)
- return -EINVAL;
-
- return 0;
-}
-
-static const struct soc_mbus_pixelfmt sh_mobile_ceu_formats[] = {
- {
- .fourcc = V4L2_PIX_FMT_NV12,
- .name = "NV12",
- .bits_per_sample = 8,
- .packing = SOC_MBUS_PACKING_1_5X8,
- .order = SOC_MBUS_ORDER_LE,
- .layout = SOC_MBUS_LAYOUT_PLANAR_2Y_C,
- }, {
- .fourcc = V4L2_PIX_FMT_NV21,
- .name = "NV21",
- .bits_per_sample = 8,
- .packing = SOC_MBUS_PACKING_1_5X8,
- .order = SOC_MBUS_ORDER_LE,
- .layout = SOC_MBUS_LAYOUT_PLANAR_2Y_C,
- }, {
- .fourcc = V4L2_PIX_FMT_NV16,
- .name = "NV16",
- .bits_per_sample = 8,
- .packing = SOC_MBUS_PACKING_2X8_PADHI,
- .order = SOC_MBUS_ORDER_LE,
- .layout = SOC_MBUS_LAYOUT_PLANAR_Y_C,
- }, {
- .fourcc = V4L2_PIX_FMT_NV61,
- .name = "NV61",
- .bits_per_sample = 8,
- .packing = SOC_MBUS_PACKING_2X8_PADHI,
- .order = SOC_MBUS_ORDER_LE,
- .layout = SOC_MBUS_LAYOUT_PLANAR_Y_C,
- },
-};
-
-/* This will be corrected as we get more formats */
-static bool sh_mobile_ceu_packing_supported(const struct soc_mbus_pixelfmt *fmt)
-{
- return fmt->packing == SOC_MBUS_PACKING_NONE ||
- (fmt->bits_per_sample == 8 &&
- fmt->packing == SOC_MBUS_PACKING_1_5X8) ||
- (fmt->bits_per_sample == 8 &&
- fmt->packing == SOC_MBUS_PACKING_2X8_PADHI) ||
- (fmt->bits_per_sample > 8 &&
- fmt->packing == SOC_MBUS_PACKING_EXTEND16);
-}
-
-static struct soc_camera_device *ctrl_to_icd(struct v4l2_ctrl *ctrl)
-{
- return container_of(ctrl->handler, struct soc_camera_device,
- ctrl_handler);
-}
-
-static int sh_mobile_ceu_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct soc_camera_device *icd = ctrl_to_icd(ctrl);
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct sh_mobile_ceu_dev *pcdev = ici->priv;
-
- switch (ctrl->id) {
- case V4L2_CID_SHARPNESS:
- switch (icd->current_fmt->host_fmt->fourcc) {
- case V4L2_PIX_FMT_NV12:
- case V4L2_PIX_FMT_NV21:
- case V4L2_PIX_FMT_NV16:
- case V4L2_PIX_FMT_NV61:
- ceu_write(pcdev, CLFCR, !ctrl->val);
- return 0;
- }
- break;
- }
-
- return -EINVAL;
-}
-
-static const struct v4l2_ctrl_ops sh_mobile_ceu_ctrl_ops = {
- .s_ctrl = sh_mobile_ceu_s_ctrl,
-};
-
-static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int idx,
- struct soc_camera_format_xlate *xlate)
-{
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- struct device *dev = icd->parent;
- struct soc_camera_host *ici = to_soc_camera_host(dev);
- struct sh_mobile_ceu_dev *pcdev = ici->priv;
- int ret, k, n;
- int formats = 0;
- struct sh_mobile_ceu_cam *cam;
- struct v4l2_subdev_mbus_code_enum code = {
- .which = V4L2_SUBDEV_FORMAT_ACTIVE,
- .index = idx,
- };
- const struct soc_mbus_pixelfmt *fmt;
-
- ret = v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code);
- if (ret < 0)
- /* No more formats */
- return 0;
-
- fmt = soc_mbus_get_fmtdesc(code.code);
- if (!fmt) {
- dev_warn(dev, "unsupported format code #%u: %d\n", idx, code.code);
- return 0;
- }
-
- ret = sh_mobile_ceu_try_bus_param(icd, fmt->bits_per_sample);
- if (ret < 0)
- return 0;
-
- if (!icd->host_priv) {
- struct v4l2_subdev_format fmt = {
- .which = V4L2_SUBDEV_FORMAT_ACTIVE,
- };
- struct v4l2_mbus_framefmt *mf = &fmt.format;
- struct v4l2_rect rect;
- int shift = 0;
-
- /* Add our control */
- v4l2_ctrl_new_std(&icd->ctrl_handler, &sh_mobile_ceu_ctrl_ops,
- V4L2_CID_SHARPNESS, 0, 1, 1, 1);
- if (icd->ctrl_handler.error)
- return icd->ctrl_handler.error;
-
- /* FIXME: subwindow is lost between close / open */
-
- /* Cache current client geometry */
- ret = soc_camera_client_g_rect(sd, &rect);
- if (ret < 0)
- return ret;
-
- /* First time */
- ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt);
- if (ret < 0)
- return ret;
-
- /*
- * All currently existing CEU implementations support 2560x1920
- * or larger frames. If the sensor is proposing too big a frame,
- * don't bother with possibly supportred by the CEU larger
- * sizes, just try VGA multiples. If needed, this can be
- * adjusted in the future.
- */
- while ((mf->width > pcdev->max_width ||
- mf->height > pcdev->max_height) && shift < 4) {
- /* Try 2560x1920, 1280x960, 640x480, 320x240 */
- mf->width = 2560 >> shift;
- mf->height = 1920 >> shift;
- ret = v4l2_device_call_until_err(sd->v4l2_dev,
- soc_camera_grp_id(icd), pad,
- set_fmt, NULL, &fmt);
- if (ret < 0)
- return ret;
- shift++;
- }
-
- if (shift == 4) {
- dev_err(dev, "Failed to configure the client below %ux%x\n",
- mf->width, mf->height);
- return -EIO;
- }
-
- dev_geo(dev, "camera fmt %ux%u\n", mf->width, mf->height);
-
- cam = kzalloc(sizeof(*cam), GFP_KERNEL);
- if (!cam)
- return -ENOMEM;
-
- /* We are called with current camera crop, initialise subrect with it */
- cam->rect = rect;
- cam->subrect = rect;
-
- cam->width = mf->width;
- cam->height = mf->height;
-
- icd->host_priv = cam;
- } else {
- cam = icd->host_priv;
- }
-
- /* Beginning of a pass */
- if (!idx)
- cam->extra_fmt = NULL;
-
- switch (code.code) {
- case MEDIA_BUS_FMT_UYVY8_2X8:
- case MEDIA_BUS_FMT_VYUY8_2X8:
- case MEDIA_BUS_FMT_YUYV8_2X8:
- case MEDIA_BUS_FMT_YVYU8_2X8:
- if (cam->extra_fmt)
- break;
-
- /*
- * Our case is simple so far: for any of the above four camera
- * formats we add all our four synthesized NV* formats, so,
- * just marking the device with a single flag suffices. If
- * the format generation rules are more complex, you would have
- * to actually hang your already added / counted formats onto
- * the host_priv pointer and check whether the format you're
- * going to add now is already there.
- */
- cam->extra_fmt = sh_mobile_ceu_formats;
-
- n = ARRAY_SIZE(sh_mobile_ceu_formats);
- formats += n;
- for (k = 0; xlate && k < n; k++) {
- xlate->host_fmt = &sh_mobile_ceu_formats[k];
- xlate->code = code.code;
- xlate++;
- dev_dbg(dev, "Providing format %s using code %d\n",
- sh_mobile_ceu_formats[k].name, code.code);
- }
- break;
- default:
- if (!sh_mobile_ceu_packing_supported(fmt))
- return 0;
- }
-
- /* Generic pass-through */
- formats++;
- if (xlate) {
- xlate->host_fmt = fmt;
- xlate->code = code.code;
- xlate++;
- dev_dbg(dev, "Providing format %s in pass-through mode\n",
- fmt->name);
- }
-
- return formats;
-}
-
-static void sh_mobile_ceu_put_formats(struct soc_camera_device *icd)
-{
- kfree(icd->host_priv);
- icd->host_priv = NULL;
-}
-
-#define scale_down(size, scale) soc_camera_shift_scale(size, 12, scale)
-#define calc_generic_scale(in, out) soc_camera_calc_scale(in, 12, out)
-
-/*
- * CEU can scale and crop, but we don't want to waste bandwidth and kill the
- * framerate by always requesting the maximum image from the client. See
- * Documentation/media/v4l-drivers/sh_mobile_ceu_camera.rst for a description of
- * scaling and cropping algorithms and for the meaning of referenced here steps.
- */
-static int sh_mobile_ceu_set_selection(struct soc_camera_device *icd,
- struct v4l2_selection *sel)
-{
- struct v4l2_rect *rect = &sel->r;
- struct device *dev = icd->parent;
- struct soc_camera_host *ici = to_soc_camera_host(dev);
- struct sh_mobile_ceu_dev *pcdev = ici->priv;
- struct v4l2_selection cam_sel;
- struct sh_mobile_ceu_cam *cam = icd->host_priv;
- struct v4l2_rect *cam_rect = &cam_sel.r;
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- struct v4l2_subdev_format fmt = {
- .which = V4L2_SUBDEV_FORMAT_ACTIVE,
- };
- struct v4l2_mbus_framefmt *mf = &fmt.format;
- unsigned int scale_cam_h, scale_cam_v, scale_ceu_h, scale_ceu_v,
- out_width, out_height;
- int interm_width, interm_height;
- u32 capsr, cflcr;
- int ret;
-
- dev_geo(dev, "S_SELECTION(%ux%u@%u:%u)\n", rect->width, rect->height,
- rect->left, rect->top);
-
- /* During camera cropping its output window can change too, stop CEU */
- capsr = capture_save_reset(pcdev);
- dev_dbg(dev, "CAPSR 0x%x, CFLCR 0x%x\n", capsr, pcdev->cflcr);
-
- /*
- * 1. - 2. Apply iterative camera S_SELECTION for new input window, read back
- * actual camera rectangle.
- */
- ret = soc_camera_client_s_selection(sd, sel, &cam_sel,
- &cam->rect, &cam->subrect);
- if (ret < 0)
- return ret;
-
- dev_geo(dev, "1-2: camera cropped to %ux%u@%u:%u\n",
- cam_rect->width, cam_rect->height,
- cam_rect->left, cam_rect->top);
-
- /* On success cam_crop contains current camera crop */
-
- /* 3. Retrieve camera output window */
- ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt);
- if (ret < 0)
- return ret;
-
- if (mf->width > pcdev->max_width || mf->height > pcdev->max_height)
- return -EINVAL;
-
- /* 4. Calculate camera scales */
- scale_cam_h = calc_generic_scale(cam_rect->width, mf->width);
- scale_cam_v = calc_generic_scale(cam_rect->height, mf->height);
-
- /* Calculate intermediate window */
- interm_width = scale_down(rect->width, scale_cam_h);
- interm_height = scale_down(rect->height, scale_cam_v);
-
- if (interm_width < icd->user_width) {
- u32 new_scale_h;
-
- new_scale_h = calc_generic_scale(rect->width, icd->user_width);
-
- mf->width = scale_down(cam_rect->width, new_scale_h);
- }
-
- if (interm_height < icd->user_height) {
- u32 new_scale_v;
-
- new_scale_v = calc_generic_scale(rect->height, icd->user_height);
-
- mf->height = scale_down(cam_rect->height, new_scale_v);
- }
-
- if (interm_width < icd->user_width || interm_height < icd->user_height) {
- ret = v4l2_device_call_until_err(sd->v4l2_dev,
- soc_camera_grp_id(icd), pad,
- set_fmt, NULL, &fmt);
- if (ret < 0)
- return ret;
-
- dev_geo(dev, "New camera output %ux%u\n", mf->width, mf->height);
- scale_cam_h = calc_generic_scale(cam_rect->width, mf->width);
- scale_cam_v = calc_generic_scale(cam_rect->height, mf->height);
- interm_width = scale_down(rect->width, scale_cam_h);
- interm_height = scale_down(rect->height, scale_cam_v);
- }
-
- /* Cache camera output window */
- cam->width = mf->width;
- cam->height = mf->height;
-
- if (pcdev->image_mode) {
- out_width = min(interm_width, icd->user_width);
- out_height = min(interm_height, icd->user_height);
- } else {
- out_width = interm_width;
- out_height = interm_height;
- }
-
- /*
- * 5. Calculate CEU scales from camera scales from results of (5) and
- * the user window
- */
- scale_ceu_h = calc_scale(interm_width, &out_width);
- scale_ceu_v = calc_scale(interm_height, &out_height);
-
- dev_geo(dev, "5: CEU scales %u:%u\n", scale_ceu_h, scale_ceu_v);
-
- /* Apply CEU scales. */
- cflcr = scale_ceu_h | (scale_ceu_v << 16);
- if (cflcr != pcdev->cflcr) {
- pcdev->cflcr = cflcr;
- ceu_write(pcdev, CFLCR, cflcr);
- }
-
- icd->user_width = out_width & ~3;
- icd->user_height = out_height & ~3;
- /* Offsets are applied at the CEU scaling filter input */
- cam->ceu_left = scale_down(rect->left - cam_rect->left, scale_cam_h) & ~1;
- cam->ceu_top = scale_down(rect->top - cam_rect->top, scale_cam_v) & ~1;
-
- /* 6. Use CEU cropping to crop to the new window. */
- sh_mobile_ceu_set_rect(icd);
-
- cam->subrect = *rect;
-
- dev_geo(dev, "6: CEU cropped to %ux%u@%u:%u\n",
- icd->user_width, icd->user_height,
- cam->ceu_left, cam->ceu_top);
-
- /* Restore capture. The CE bit can be cleared by the hardware */
- if (pcdev->active)
- capsr |= 1;
- capture_restore(pcdev, capsr);
-
- /* Even if only camera cropping succeeded */
- return ret;
-}
-
-static int sh_mobile_ceu_get_selection(struct soc_camera_device *icd,
- struct v4l2_selection *sel)
-{
- struct sh_mobile_ceu_cam *cam = icd->host_priv;
-
- sel->r = cam->subrect;
-
- return 0;
-}
-
-/* Similar to set_crop multistage iterative algorithm */
-static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd,
- struct v4l2_format *f)
-{
- struct device *dev = icd->parent;
- struct soc_camera_host *ici = to_soc_camera_host(dev);
- struct sh_mobile_ceu_dev *pcdev = ici->priv;
- struct sh_mobile_ceu_cam *cam = icd->host_priv;
- struct v4l2_pix_format *pix = &f->fmt.pix;
- struct v4l2_mbus_framefmt mf;
- __u32 pixfmt = pix->pixelformat;
- const struct soc_camera_format_xlate *xlate;
- unsigned int ceu_sub_width = pcdev->max_width,
- ceu_sub_height = pcdev->max_height;
- u16 scale_v, scale_h;
- int ret;
- bool image_mode;
- enum v4l2_field field;
-
- switch (pix->field) {
- default:
- pix->field = V4L2_FIELD_NONE;
- /* fall-through */
- case V4L2_FIELD_INTERLACED_TB:
- case V4L2_FIELD_INTERLACED_BT:
- case V4L2_FIELD_NONE:
- field = pix->field;
- break;
- case V4L2_FIELD_INTERLACED:
- field = V4L2_FIELD_INTERLACED_TB;
- break;
- }
-
- xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
- if (!xlate) {
- dev_warn(dev, "Format %x not found\n", pixfmt);
- return -EINVAL;
- }
-
- /* 1.-4. Calculate desired client output geometry */
- soc_camera_calc_client_output(icd, &cam->rect, &cam->subrect, pix, &mf, 12);
- mf.field = pix->field;
- mf.colorspace = pix->colorspace;
- mf.code = xlate->code;
-
- switch (pixfmt) {
- case V4L2_PIX_FMT_NV12:
- case V4L2_PIX_FMT_NV21:
- case V4L2_PIX_FMT_NV16:
- case V4L2_PIX_FMT_NV61:
- image_mode = true;
- break;
- default:
- image_mode = false;
- }
-
- dev_geo(dev, "S_FMT(pix=0x%x, fld 0x%x, code 0x%x, %ux%u)\n", pixfmt, mf.field, mf.code,
- pix->width, pix->height);
-
- dev_geo(dev, "4: request camera output %ux%u\n", mf.width, mf.height);
-
- /* 5. - 9. */
- ret = soc_camera_client_scale(icd, &cam->rect, &cam->subrect,
- &mf, &ceu_sub_width, &ceu_sub_height,
- image_mode && V4L2_FIELD_NONE == field, 12);
-
- dev_geo(dev, "5-9: client scale return %d\n", ret);
-
- /* Done with the camera. Now see if we can improve the result */
-
- dev_geo(dev, "fmt %ux%u, requested %ux%u\n",
- mf.width, mf.height, pix->width, pix->height);
- if (ret < 0)
- return ret;
-
- if (mf.code != xlate->code)
- return -EINVAL;
-
- /* 9. Prepare CEU crop */
- cam->width = mf.width;
- cam->height = mf.height;
-
- /* 10. Use CEU scaling to scale to the requested user window. */
-
- /* We cannot scale up */
- if (pix->width > ceu_sub_width)
- ceu_sub_width = pix->width;
-
- if (pix->height > ceu_sub_height)
- ceu_sub_height = pix->height;
-
- pix->colorspace = mf.colorspace;
-
- if (image_mode) {
- /* Scale pix->{width x height} down to width x height */
- scale_h = calc_scale(ceu_sub_width, &pix->width);
- scale_v = calc_scale(ceu_sub_height, &pix->height);
- } else {
- pix->width = ceu_sub_width;
- pix->height = ceu_sub_height;
- scale_h = 0;
- scale_v = 0;
- }
-
- pcdev->cflcr = scale_h | (scale_v << 16);
-
- /*
- * We have calculated CFLCR, the actual configuration will be performed
- * in sh_mobile_ceu_set_bus_param()
- */
-
- dev_geo(dev, "10: W: %u : 0x%x = %u, H: %u : 0x%x = %u\n",
- ceu_sub_width, scale_h, pix->width,
- ceu_sub_height, scale_v, pix->height);
-
- cam->code = xlate->code;
- icd->current_fmt = xlate;
-
- pcdev->field = field;
- pcdev->image_mode = image_mode;
-
- /* CFSZR requirement */
- pix->width &= ~3;
- pix->height &= ~3;
-
- return 0;
-}
-
-#define CEU_CHDW_MAX 8188U /* Maximum line stride */
-
-static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd,
- struct v4l2_format *f)
-{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct sh_mobile_ceu_dev *pcdev = ici->priv;
- const struct soc_camera_format_xlate *xlate;
- struct v4l2_pix_format *pix = &f->fmt.pix;
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- struct v4l2_subdev_pad_config pad_cfg;
- struct v4l2_subdev_format format = {
- .which = V4L2_SUBDEV_FORMAT_TRY,
- };
- struct v4l2_mbus_framefmt *mf = &format.format;
- __u32 pixfmt = pix->pixelformat;
- int width, height;
- int ret;
-
- dev_geo(icd->parent, "TRY_FMT(pix=0x%x, %ux%u)\n",
- pixfmt, pix->width, pix->height);
-
- xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
- if (!xlate) {
- xlate = icd->current_fmt;
- dev_dbg(icd->parent, "Format %x not found, keeping %x\n",
- pixfmt, xlate->host_fmt->fourcc);
- pixfmt = xlate->host_fmt->fourcc;
- pix->pixelformat = pixfmt;
- pix->colorspace = icd->colorspace;
- }
-
- /* FIXME: calculate using depth and bus width */
-
- /* CFSZR requires height and width to be 4-pixel aligned */
- v4l_bound_align_image(&pix->width, 2, pcdev->max_width, 2,
- &pix->height, 4, pcdev->max_height, 2, 0);
-
- width = pix->width;
- height = pix->height;
-
- /* limit to sensor capabilities */
- mf->width = pix->width;
- mf->height = pix->height;
- mf->field = pix->field;
- mf->code = xlate->code;
- mf->colorspace = pix->colorspace;
-
- ret = v4l2_device_call_until_err(sd->v4l2_dev, soc_camera_grp_id(icd),
- pad, set_fmt, &pad_cfg, &format);
- if (ret < 0)
- return ret;
-
- pix->width = mf->width;
- pix->height = mf->height;
- pix->field = mf->field;
- pix->colorspace = mf->colorspace;
-
- switch (pixfmt) {
- case V4L2_PIX_FMT_NV12:
- case V4L2_PIX_FMT_NV21:
- case V4L2_PIX_FMT_NV16:
- case V4L2_PIX_FMT_NV61:
- /* FIXME: check against rect_max after converting soc-camera */
- /* We can scale precisely, need a bigger image from camera */
- if (pix->width < width || pix->height < height) {
- /*
- * We presume, the sensor behaves sanely, i.e., if
- * requested a bigger rectangle, it will not return a
- * smaller one.
- */
- mf->width = pcdev->max_width;
- mf->height = pcdev->max_height;
- ret = v4l2_device_call_until_err(sd->v4l2_dev,
- soc_camera_grp_id(icd), pad,
- set_fmt, &pad_cfg, &format);
- if (ret < 0) {
- /* Shouldn't actually happen... */
- dev_err(icd->parent,
- "FIXME: client try_fmt() = %d\n", ret);
- return ret;
- }
- }
- /* We will scale exactly */
- if (mf->width > width)
- pix->width = width;
- if (mf->height > height)
- pix->height = height;
-
- pix->bytesperline = max(pix->bytesperline, pix->width);
- pix->bytesperline = min(pix->bytesperline, CEU_CHDW_MAX);
- pix->bytesperline &= ~3;
- break;
-
- default:
- /* Configurable stride isn't supported in pass-through mode. */
- pix->bytesperline = 0;
- }
-
- pix->width &= ~3;
- pix->height &= ~3;
- pix->sizeimage = 0;
-
- dev_geo(icd->parent, "%s(): return %d, fmt 0x%x, %ux%u\n",
- __func__, ret, pix->pixelformat, pix->width, pix->height);
-
- return ret;
-}
-
-static int sh_mobile_ceu_set_liveselection(struct soc_camera_device *icd,
- struct v4l2_selection *sel)
-{
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct sh_mobile_ceu_dev *pcdev = ici->priv;
- u32 out_width = icd->user_width, out_height = icd->user_height;
- int ret;
-
- /* Freeze queue */
- pcdev->frozen = 1;
- /* Wait for frame */
- ret = wait_for_completion_interruptible(&pcdev->complete);
- /* Stop the client */
- ret = v4l2_subdev_call(sd, video, s_stream, 0);
- if (ret < 0)
- dev_warn(icd->parent,
- "Client failed to stop the stream: %d\n", ret);
- else
- /* Do the crop, if it fails, there's nothing more we can do */
- sh_mobile_ceu_set_selection(icd, sel);
-
- dev_geo(icd->parent, "Output after crop: %ux%u\n", icd->user_width, icd->user_height);
-
- if (icd->user_width != out_width || icd->user_height != out_height) {
- struct v4l2_format f = {
- .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
- .fmt.pix = {
- .width = out_width,
- .height = out_height,
- .pixelformat = icd->current_fmt->host_fmt->fourcc,
- .field = pcdev->field,
- .colorspace = icd->colorspace,
- },
- };
- ret = sh_mobile_ceu_set_fmt(icd, &f);
- if (!ret && (out_width != f.fmt.pix.width ||
- out_height != f.fmt.pix.height))
- ret = -EINVAL;
- if (!ret) {
- icd->user_width = out_width & ~3;
- icd->user_height = out_height & ~3;
- ret = sh_mobile_ceu_set_bus_param(icd);
- }
- }
-
- /* Thaw the queue */
- pcdev->frozen = 0;
- spin_lock_irq(&pcdev->lock);
- sh_mobile_ceu_capture(pcdev);
- spin_unlock_irq(&pcdev->lock);
- /* Start the client */
- ret = v4l2_subdev_call(sd, video, s_stream, 1);
- return ret;
-}
-
-static __poll_t sh_mobile_ceu_poll(struct file *file, poll_table *pt)
-{
- struct soc_camera_device *icd = file->private_data;
-
- return vb2_poll(&icd->vb2_vidq, file, pt);
-}
-
-static int sh_mobile_ceu_querycap(struct soc_camera_host *ici,
- struct v4l2_capability *cap)
-{
- strscpy(cap->card, "SuperH_Mobile_CEU", sizeof(cap->card));
- strscpy(cap->driver, "sh_mobile_ceu", sizeof(cap->driver));
- strscpy(cap->bus_info, "platform:sh_mobile_ceu", sizeof(cap->bus_info));
- cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
- cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
-
- return 0;
-}
-
-static int sh_mobile_ceu_init_videobuf(struct vb2_queue *q,
- struct soc_camera_device *icd)
-{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
-
- q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- q->io_modes = VB2_MMAP | VB2_USERPTR;
- q->drv_priv = icd;
- q->ops = &sh_mobile_ceu_videobuf_ops;
- q->mem_ops = &vb2_dma_contig_memops;
- q->buf_struct_size = sizeof(struct sh_mobile_ceu_buffer);
- q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
- q->lock = &ici->host_lock;
- q->dev = ici->v4l2_dev.dev;
-
- return vb2_queue_init(q);
-}
-
-static struct soc_camera_host_ops sh_mobile_ceu_host_ops = {
- .owner = THIS_MODULE,
- .add = sh_mobile_ceu_add_device,
- .remove = sh_mobile_ceu_remove_device,
- .clock_start = sh_mobile_ceu_clock_start,
- .clock_stop = sh_mobile_ceu_clock_stop,
- .get_formats = sh_mobile_ceu_get_formats,
- .put_formats = sh_mobile_ceu_put_formats,
- .get_selection = sh_mobile_ceu_get_selection,
- .set_selection = sh_mobile_ceu_set_selection,
- .set_liveselection = sh_mobile_ceu_set_liveselection,
- .set_fmt = sh_mobile_ceu_set_fmt,
- .try_fmt = sh_mobile_ceu_try_fmt,
- .poll = sh_mobile_ceu_poll,
- .querycap = sh_mobile_ceu_querycap,
- .set_bus_param = sh_mobile_ceu_set_bus_param,
- .init_videobuf2 = sh_mobile_ceu_init_videobuf,
-};
-
-struct bus_wait {
- struct notifier_block notifier;
- struct completion completion;
- struct device *dev;
-};
-
-static int bus_notify(struct notifier_block *nb,
- unsigned long action, void *data)
-{
- struct device *dev = data;
- struct bus_wait *wait = container_of(nb, struct bus_wait, notifier);
-
- if (wait->dev != dev)
- return NOTIFY_DONE;
-
- switch (action) {
- case BUS_NOTIFY_UNBOUND_DRIVER:
- /* Protect from module unloading */
- wait_for_completion(&wait->completion);
- return NOTIFY_OK;
- }
- return NOTIFY_DONE;
-}
-
-static int sh_mobile_ceu_probe(struct platform_device *pdev)
-{
- struct sh_mobile_ceu_dev *pcdev;
- struct resource *res;
- void __iomem *base;
- unsigned int irq;
- int err;
- struct bus_wait wait = {
- .completion = COMPLETION_INITIALIZER_ONSTACK(wait.completion),
- .notifier.notifier_call = bus_notify,
- };
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- irq = platform_get_irq(pdev, 0);
- if (!res || (int)irq <= 0) {
- dev_err(&pdev->dev, "Not enough CEU platform resources.\n");
- return -ENODEV;
- }
-
- pcdev = devm_kzalloc(&pdev->dev, sizeof(*pcdev), GFP_KERNEL);
- if (!pcdev) {
- dev_err(&pdev->dev, "Could not allocate pcdev\n");
- return -ENOMEM;
- }
-
- INIT_LIST_HEAD(&pcdev->capture);
- spin_lock_init(&pcdev->lock);
- init_completion(&pcdev->complete);
-
- pcdev->pdata = pdev->dev.platform_data;
- if (!pcdev->pdata && !pdev->dev.of_node) {
- dev_err(&pdev->dev, "CEU platform data not set.\n");
- return -EINVAL;
- }
-
- /* TODO: implement per-device bus flags */
- if (pcdev->pdata) {
- pcdev->max_width = pcdev->pdata->max_width;
- pcdev->max_height = pcdev->pdata->max_height;
- pcdev->flags = pcdev->pdata->flags;
- }
- pcdev->field = V4L2_FIELD_NONE;
-
- if (!pcdev->max_width) {
- unsigned int v;
- err = of_property_read_u32(pdev->dev.of_node, "renesas,max-width", &v);
- if (!err)
- pcdev->max_width = v;
-
- if (!pcdev->max_width)
- pcdev->max_width = 2560;
- }
- if (!pcdev->max_height) {
- unsigned int v;
- err = of_property_read_u32(pdev->dev.of_node, "renesas,max-height", &v);
- if (!err)
- pcdev->max_height = v;
-
- if (!pcdev->max_height)
- pcdev->max_height = 1920;
- }
-
- base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(base))
- return PTR_ERR(base);
-
- pcdev->irq = irq;
- pcdev->base = base;
- pcdev->video_limit = 0; /* only enabled if second resource exists */
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- if (res) {
- err = dma_declare_coherent_memory(&pdev->dev, res->start,
- res->start,
- resource_size(res),
- DMA_MEMORY_EXCLUSIVE);
- if (err) {
- dev_err(&pdev->dev, "Unable to declare CEU memory.\n");
- return err;
- }
-
- pcdev->video_limit = resource_size(res);
- }
-
- /* request irq */
- err = devm_request_irq(&pdev->dev, pcdev->irq, sh_mobile_ceu_irq,
- 0, dev_name(&pdev->dev), pcdev);
- if (err) {
- dev_err(&pdev->dev, "Unable to register CEU interrupt.\n");
- goto exit_release_mem;
- }
-
- pm_suspend_ignore_children(&pdev->dev, true);
- pm_runtime_enable(&pdev->dev);
- pm_runtime_resume(&pdev->dev);
-
- pcdev->ici.priv = pcdev;
- pcdev->ici.v4l2_dev.dev = &pdev->dev;
- pcdev->ici.nr = pdev->id;
- pcdev->ici.drv_name = dev_name(&pdev->dev);
- pcdev->ici.ops = &sh_mobile_ceu_host_ops;
- pcdev->ici.capabilities = SOCAM_HOST_CAP_STRIDE;
-
- if (pcdev->pdata && pcdev->pdata->asd_sizes) {
- pcdev->ici.asd = pcdev->pdata->asd;
- pcdev->ici.asd_sizes = pcdev->pdata->asd_sizes;
- }
-
- err = soc_camera_host_register(&pcdev->ici);
- if (err)
- goto exit_free_clk;
-
- return 0;
-
-exit_free_clk:
- pm_runtime_disable(&pdev->dev);
-exit_release_mem:
- if (platform_get_resource(pdev, IORESOURCE_MEM, 1))
- dma_release_declared_memory(&pdev->dev);
- return err;
-}
-
-static int sh_mobile_ceu_remove(struct platform_device *pdev)
-{
- struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev);
-
- soc_camera_host_unregister(soc_host);
- pm_runtime_disable(&pdev->dev);
- if (platform_get_resource(pdev, IORESOURCE_MEM, 1))
- dma_release_declared_memory(&pdev->dev);
-
- return 0;
-}
-
-static int sh_mobile_ceu_runtime_nop(struct device *dev)
-{
- /* Runtime PM callback shared between ->runtime_suspend()
- * and ->runtime_resume(). Simply returns success.
- *
- * This driver re-initializes all registers after
- * pm_runtime_get_sync() anyway so there is no need
- * to save and restore registers here.
- */
- return 0;
-}
-
-static const struct dev_pm_ops sh_mobile_ceu_dev_pm_ops = {
- .runtime_suspend = sh_mobile_ceu_runtime_nop,
- .runtime_resume = sh_mobile_ceu_runtime_nop,
-};
-
-static const struct of_device_id sh_mobile_ceu_of_match[] = {
- { .compatible = "renesas,sh-mobile-ceu" },
- { }
-};
-MODULE_DEVICE_TABLE(of, sh_mobile_ceu_of_match);
-
-static struct platform_driver sh_mobile_ceu_driver = {
- .driver = {
- .name = "sh_mobile_ceu",
- .pm = &sh_mobile_ceu_dev_pm_ops,
- .of_match_table = sh_mobile_ceu_of_match,
- },
- .probe = sh_mobile_ceu_probe,
- .remove = sh_mobile_ceu_remove,
-};
-
-module_platform_driver(sh_mobile_ceu_driver);
-
-MODULE_DESCRIPTION("SuperH Mobile CEU driver");
-MODULE_AUTHOR("Magnus Damm");
-MODULE_LICENSE("GPL");
-MODULE_VERSION("0.1.0");
-MODULE_ALIAS("platform:sh_mobile_ceu");
diff --git a/drivers/media/platform/soc_camera/soc_camera_platform.c b/drivers/media/platform/soc_camera/soc_camera_platform.c
deleted file mode 100644
index 79fbe1fea95f..000000000000
--- a/drivers/media/platform/soc_camera/soc_camera_platform.c
+++ /dev/null
@@ -1,188 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Generic Platform Camera Driver
- *
- * Copyright (C) 2008 Magnus Damm
- * Based on mt9m001 driver,
- * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/delay.h>
-#include <linux/platform_device.h>
-#include <linux/videodev2.h>
-#include <media/v4l2-subdev.h>
-#include <media/soc_camera.h>
-#include <linux/platform_data/media/soc_camera_platform.h>
-
-struct soc_camera_platform_priv {
- struct v4l2_subdev subdev;
-};
-
-static struct soc_camera_platform_priv *get_priv(struct platform_device *pdev)
-{
- struct v4l2_subdev *subdev = platform_get_drvdata(pdev);
- return container_of(subdev, struct soc_camera_platform_priv, subdev);
-}
-
-static int soc_camera_platform_s_stream(struct v4l2_subdev *sd, int enable)
-{
- struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd);
- return p->set_capture(p, enable);
-}
-
-static int soc_camera_platform_fill_fmt(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_format *format)
-{
- struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd);
- struct v4l2_mbus_framefmt *mf = &format->format;
-
- mf->width = p->format.width;
- mf->height = p->format.height;
- mf->code = p->format.code;
- mf->colorspace = p->format.colorspace;
- mf->field = p->format.field;
-
- return 0;
-}
-
-static int soc_camera_platform_s_power(struct v4l2_subdev *sd, int on)
-{
- struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd);
-
- return soc_camera_set_power(p->icd->control, &p->icd->sdesc->subdev_desc, NULL, on);
-}
-
-static const struct v4l2_subdev_core_ops platform_subdev_core_ops = {
- .s_power = soc_camera_platform_s_power,
-};
-
-static int soc_camera_platform_enum_mbus_code(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_mbus_code_enum *code)
-{
- struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd);
-
- if (code->pad || code->index)
- return -EINVAL;
-
- code->code = p->format.code;
- return 0;
-}
-
-static int soc_camera_platform_get_selection(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_selection *sel)
-{
- struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd);
-
- if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
- return -EINVAL;
-
- switch (sel->target) {
- case V4L2_SEL_TGT_CROP_BOUNDS:
- case V4L2_SEL_TGT_CROP_DEFAULT:
- case V4L2_SEL_TGT_CROP:
- sel->r.left = 0;
- sel->r.top = 0;
- sel->r.width = p->format.width;
- sel->r.height = p->format.height;
- return 0;
- default:
- return -EINVAL;
- }
-}
-
-static int soc_camera_platform_g_mbus_config(struct v4l2_subdev *sd,
- struct v4l2_mbus_config *cfg)
-{
- struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd);
-
- cfg->flags = p->mbus_param;
- cfg->type = p->mbus_type;
-
- return 0;
-}
-
-static const struct v4l2_subdev_video_ops platform_subdev_video_ops = {
- .s_stream = soc_camera_platform_s_stream,
- .g_mbus_config = soc_camera_platform_g_mbus_config,
-};
-
-static const struct v4l2_subdev_pad_ops platform_subdev_pad_ops = {
- .enum_mbus_code = soc_camera_platform_enum_mbus_code,
- .get_selection = soc_camera_platform_get_selection,
- .get_fmt = soc_camera_platform_fill_fmt,
- .set_fmt = soc_camera_platform_fill_fmt,
-};
-
-static const struct v4l2_subdev_ops platform_subdev_ops = {
- .core = &platform_subdev_core_ops,
- .video = &platform_subdev_video_ops,
- .pad = &platform_subdev_pad_ops,
-};
-
-static int soc_camera_platform_probe(struct platform_device *pdev)
-{
- struct soc_camera_host *ici;
- struct soc_camera_platform_priv *priv;
- struct soc_camera_platform_info *p = pdev->dev.platform_data;
- struct soc_camera_device *icd;
-
- if (!p)
- return -EINVAL;
-
- if (!p->icd) {
- dev_err(&pdev->dev,
- "Platform has not set soc_camera_device pointer!\n");
- return -EINVAL;
- }
-
- priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
-
- icd = p->icd;
-
- /* soc-camera convention: control's drvdata points to the subdev */
- platform_set_drvdata(pdev, &priv->subdev);
- /* Set the control device reference */
- icd->control = &pdev->dev;
-
- ici = to_soc_camera_host(icd->parent);
-
- v4l2_subdev_init(&priv->subdev, &platform_subdev_ops);
- v4l2_set_subdevdata(&priv->subdev, p);
- strscpy(priv->subdev.name, dev_name(&pdev->dev),
- sizeof(priv->subdev.name));
-
- return v4l2_device_register_subdev(&ici->v4l2_dev, &priv->subdev);
-}
-
-static int soc_camera_platform_remove(struct platform_device *pdev)
-{
- struct soc_camera_platform_priv *priv = get_priv(pdev);
- struct soc_camera_platform_info *p = v4l2_get_subdevdata(&priv->subdev);
-
- p->icd->control = NULL;
- v4l2_device_unregister_subdev(&priv->subdev);
- return 0;
-}
-
-static struct platform_driver soc_camera_platform_driver = {
- .driver = {
- .name = "soc_camera_platform",
- },
- .probe = soc_camera_platform_probe,
- .remove = soc_camera_platform_remove,
-};
-
-module_platform_driver(soc_camera_platform_driver);
-
-MODULE_DESCRIPTION("SoC Camera Platform driver");
-MODULE_AUTHOR("Magnus Damm");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:soc_camera_platform");
diff --git a/drivers/media/platform/soc_camera/soc_scale_crop.c b/drivers/media/platform/soc_camera/soc_scale_crop.c
deleted file mode 100644
index 8d25ca0490f7..000000000000
--- a/drivers/media/platform/soc_camera/soc_scale_crop.c
+++ /dev/null
@@ -1,426 +0,0 @@
-/*
- * soc-camera generic scaling-cropping manipulation functions
- *
- * Copyright (C) 2013 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-
-#include <linux/device.h>
-#include <linux/module.h>
-
-#include <media/soc_camera.h>
-#include <media/v4l2-common.h>
-
-#include "soc_scale_crop.h"
-
-#ifdef DEBUG_GEOMETRY
-#define dev_geo dev_info
-#else
-#define dev_geo dev_dbg
-#endif
-
-/* Check if any dimension of r1 is smaller than respective one of r2 */
-static bool is_smaller(const struct v4l2_rect *r1, const struct v4l2_rect *r2)
-{
- return r1->width < r2->width || r1->height < r2->height;
-}
-
-/* Check if r1 fails to cover r2 */
-static bool is_inside(const struct v4l2_rect *r1, const struct v4l2_rect *r2)
-{
- return r1->left > r2->left || r1->top > r2->top ||
- r1->left + r1->width < r2->left + r2->width ||
- r1->top + r1->height < r2->top + r2->height;
-}
-
-/* Get and store current client crop */
-int soc_camera_client_g_rect(struct v4l2_subdev *sd, struct v4l2_rect *rect)
-{
- struct v4l2_subdev_selection sdsel = {
- .which = V4L2_SUBDEV_FORMAT_ACTIVE,
- .target = V4L2_SEL_TGT_CROP,
- };
- int ret;
-
- ret = v4l2_subdev_call(sd, pad, get_selection, NULL, &sdsel);
- if (!ret) {
- *rect = sdsel.r;
- return ret;
- }
-
- sdsel.target = V4L2_SEL_TGT_CROP_BOUNDS;
- ret = v4l2_subdev_call(sd, pad, get_selection, NULL, &sdsel);
- if (!ret)
- *rect = sdsel.r;
-
- return ret;
-}
-EXPORT_SYMBOL(soc_camera_client_g_rect);
-
-/* Client crop has changed, update our sub-rectangle to remain within the area */
-static void move_and_crop_subrect(struct v4l2_rect *rect,
- struct v4l2_rect *subrect)
-{
- if (rect->width < subrect->width)
- subrect->width = rect->width;
-
- if (rect->height < subrect->height)
- subrect->height = rect->height;
-
- if (rect->left > subrect->left)
- subrect->left = rect->left;
- else if (rect->left + rect->width <
- subrect->left + subrect->width)
- subrect->left = rect->left + rect->width -
- subrect->width;
-
- if (rect->top > subrect->top)
- subrect->top = rect->top;
- else if (rect->top + rect->height <
- subrect->top + subrect->height)
- subrect->top = rect->top + rect->height -
- subrect->height;
-}
-
-/*
- * The common for both scaling and cropping iterative approach is:
- * 1. try if the client can produce exactly what requested by the user
- * 2. if (1) failed, try to double the client image until we get one big enough
- * 3. if (2) failed, try to request the maximum image
- */
-int soc_camera_client_s_selection(struct v4l2_subdev *sd,
- struct v4l2_selection *sel, struct v4l2_selection *cam_sel,
- struct v4l2_rect *target_rect, struct v4l2_rect *subrect)
-{
- struct v4l2_subdev_selection sdsel = {
- .which = V4L2_SUBDEV_FORMAT_ACTIVE,
- .target = sel->target,
- .flags = sel->flags,
- .r = sel->r,
- };
- struct v4l2_subdev_selection bounds = {
- .which = V4L2_SUBDEV_FORMAT_ACTIVE,
- .target = V4L2_SEL_TGT_CROP_BOUNDS,
- };
- struct v4l2_rect *rect = &sel->r, *cam_rect = &cam_sel->r;
- struct device *dev = sd->v4l2_dev->dev;
- int ret;
- unsigned int width, height;
-
- v4l2_subdev_call(sd, pad, set_selection, NULL, &sdsel);
- sel->r = sdsel.r;
- ret = soc_camera_client_g_rect(sd, cam_rect);
- if (ret < 0)
- return ret;
-
- /*
- * Now cam_crop contains the current camera input rectangle, and it must
- * be within camera cropcap bounds
- */
- if (!memcmp(rect, cam_rect, sizeof(*rect))) {
- /* Even if camera S_SELECTION failed, but camera rectangle matches */
- dev_dbg(dev, "Camera S_SELECTION successful for %dx%d@%d:%d\n",
- rect->width, rect->height, rect->left, rect->top);
- *target_rect = *cam_rect;
- return 0;
- }
-
- /* Try to fix cropping, that camera hasn't managed to set */
- dev_geo(dev, "Fix camera S_SELECTION for %dx%d@%d:%d to %dx%d@%d:%d\n",
- cam_rect->width, cam_rect->height,
- cam_rect->left, cam_rect->top,
- rect->width, rect->height, rect->left, rect->top);
-
- /* We need sensor maximum rectangle */
- ret = v4l2_subdev_call(sd, pad, get_selection, NULL, &bounds);
- if (ret < 0)
- return ret;
-
- /* Put user requested rectangle within sensor bounds */
- soc_camera_limit_side(&rect->left, &rect->width, sdsel.r.left, 2,
- bounds.r.width);
- soc_camera_limit_side(&rect->top, &rect->height, sdsel.r.top, 4,
- bounds.r.height);
-
- /*
- * Popular special case - some cameras can only handle fixed sizes like
- * QVGA, VGA,... Take care to avoid infinite loop.
- */
- width = max_t(unsigned int, cam_rect->width, 2);
- height = max_t(unsigned int, cam_rect->height, 2);
-
- /*
- * Loop as long as sensor is not covering the requested rectangle and
- * is still within its bounds
- */
- while (!ret && (is_smaller(cam_rect, rect) ||
- is_inside(cam_rect, rect)) &&
- (bounds.r.width > width || bounds.r.height > height)) {
-
- width *= 2;
- height *= 2;
-
- cam_rect->width = width;
- cam_rect->height = height;
-
- /*
- * We do not know what capabilities the camera has to set up
- * left and top borders. We could try to be smarter in iterating
- * them, e.g., if camera current left is to the right of the
- * target left, set it to the middle point between the current
- * left and minimum left. But that would add too much
- * complexity: we would have to iterate each border separately.
- * Instead we just drop to the left and top bounds.
- */
- if (cam_rect->left > rect->left)
- cam_rect->left = bounds.r.left;
-
- if (cam_rect->left + cam_rect->width < rect->left + rect->width)
- cam_rect->width = rect->left + rect->width -
- cam_rect->left;
-
- if (cam_rect->top > rect->top)
- cam_rect->top = bounds.r.top;
-
- if (cam_rect->top + cam_rect->height < rect->top + rect->height)
- cam_rect->height = rect->top + rect->height -
- cam_rect->top;
-
- sdsel.r = *cam_rect;
- v4l2_subdev_call(sd, pad, set_selection, NULL, &sdsel);
- *cam_rect = sdsel.r;
- ret = soc_camera_client_g_rect(sd, cam_rect);
- dev_geo(dev, "Camera S_SELECTION %d for %dx%d@%d:%d\n", ret,
- cam_rect->width, cam_rect->height,
- cam_rect->left, cam_rect->top);
- }
-
- /* S_SELECTION must not modify the rectangle */
- if (is_smaller(cam_rect, rect) || is_inside(cam_rect, rect)) {
- /*
- * The camera failed to configure a suitable cropping,
- * we cannot use the current rectangle, set to max
- */
- sdsel.r = bounds.r;
- v4l2_subdev_call(sd, pad, set_selection, NULL, &sdsel);
- *cam_rect = sdsel.r;
-
- ret = soc_camera_client_g_rect(sd, cam_rect);
- dev_geo(dev, "Camera S_SELECTION %d for max %dx%d@%d:%d\n", ret,
- cam_rect->width, cam_rect->height,
- cam_rect->left, cam_rect->top);
- }
-
- if (!ret) {
- *target_rect = *cam_rect;
- move_and_crop_subrect(target_rect, subrect);
- }
-
- return ret;
-}
-EXPORT_SYMBOL(soc_camera_client_s_selection);
-
-/* Iterative set_fmt, also updates cached client crop on success */
-static int client_set_fmt(struct soc_camera_device *icd,
- struct v4l2_rect *rect, struct v4l2_rect *subrect,
- unsigned int max_width, unsigned int max_height,
- struct v4l2_subdev_format *format, bool host_can_scale)
-{
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- struct device *dev = icd->parent;
- struct v4l2_mbus_framefmt *mf = &format->format;
- unsigned int width = mf->width, height = mf->height, tmp_w, tmp_h;
- struct v4l2_subdev_selection sdsel = {
- .which = V4L2_SUBDEV_FORMAT_ACTIVE,
- .target = V4L2_SEL_TGT_CROP_BOUNDS,
- };
- bool host_1to1;
- int ret;
-
- ret = v4l2_device_call_until_err(sd->v4l2_dev,
- soc_camera_grp_id(icd), pad,
- set_fmt, NULL, format);
- if (ret < 0)
- return ret;
-
- dev_geo(dev, "camera scaled to %ux%u\n", mf->width, mf->height);
-
- if (width == mf->width && height == mf->height) {
- /* Perfect! The client has done it all. */
- host_1to1 = true;
- goto update_cache;
- }
-
- host_1to1 = false;
- if (!host_can_scale)
- goto update_cache;
-
- ret = v4l2_subdev_call(sd, pad, get_selection, NULL, &sdsel);
- if (ret < 0)
- return ret;
-
- if (max_width > sdsel.r.width)
- max_width = sdsel.r.width;
- if (max_height > sdsel.r.height)
- max_height = sdsel.r.height;
-
- /* Camera set a format, but geometry is not precise, try to improve */
- tmp_w = mf->width;
- tmp_h = mf->height;
-
- /* width <= max_width && height <= max_height - guaranteed by try_fmt */
- while ((width > tmp_w || height > tmp_h) &&
- tmp_w < max_width && tmp_h < max_height) {
- tmp_w = min(2 * tmp_w, max_width);
- tmp_h = min(2 * tmp_h, max_height);
- mf->width = tmp_w;
- mf->height = tmp_h;
- ret = v4l2_device_call_until_err(sd->v4l2_dev,
- soc_camera_grp_id(icd), pad,
- set_fmt, NULL, format);
- dev_geo(dev, "Camera scaled to %ux%u\n",
- mf->width, mf->height);
- if (ret < 0) {
- /* This shouldn't happen */
- dev_err(dev, "Client failed to set format: %d\n", ret);
- return ret;
- }
- }
-
-update_cache:
- /* Update cache */
- ret = soc_camera_client_g_rect(sd, rect);
- if (ret < 0)
- return ret;
-
- if (host_1to1)
- *subrect = *rect;
- else
- move_and_crop_subrect(rect, subrect);
-
- return 0;
-}
-
-/**
- * soc_camera_client_scale
- * @icd: soc-camera device
- * @rect: camera cropping window
- * @subrect: part of rect, sent to the user
- * @mf: in- / output camera output window
- * @width: on input: max host input width;
- * on output: user width, mapped back to input
- * @height: on input: max host input height;
- * on output: user height, mapped back to input
- * @host_can_scale: host can scale this pixel format
- * @shift: shift, used for scaling
- */
-int soc_camera_client_scale(struct soc_camera_device *icd,
- struct v4l2_rect *rect, struct v4l2_rect *subrect,
- struct v4l2_mbus_framefmt *mf,
- unsigned int *width, unsigned int *height,
- bool host_can_scale, unsigned int shift)
-{
- struct device *dev = icd->parent;
- struct v4l2_subdev_format fmt_tmp = {
- .which = V4L2_SUBDEV_FORMAT_ACTIVE,
- .format = *mf,
- };
- struct v4l2_mbus_framefmt *mf_tmp = &fmt_tmp.format;
- unsigned int scale_h, scale_v;
- int ret;
-
- /*
- * 5. Apply iterative camera S_FMT for camera user window (also updates
- * client crop cache and the imaginary sub-rectangle).
- */
- ret = client_set_fmt(icd, rect, subrect, *width, *height,
- &fmt_tmp, host_can_scale);
- if (ret < 0)
- return ret;
-
- dev_geo(dev, "5: camera scaled to %ux%u\n",
- mf_tmp->width, mf_tmp->height);
-
- /* 6. Retrieve camera output window (g_fmt) */
-
- /* unneeded - it is already in "mf_tmp" */
-
- /* 7. Calculate new client scales. */
- scale_h = soc_camera_calc_scale(rect->width, shift, mf_tmp->width);
- scale_v = soc_camera_calc_scale(rect->height, shift, mf_tmp->height);
-
- mf->width = mf_tmp->width;
- mf->height = mf_tmp->height;
- mf->colorspace = mf_tmp->colorspace;
-
- /*
- * 8. Calculate new host crop - apply camera scales to previously
- * updated "effective" crop.
- */
- *width = soc_camera_shift_scale(subrect->width, shift, scale_h);
- *height = soc_camera_shift_scale(subrect->height, shift, scale_v);
-
- dev_geo(dev, "8: new client sub-window %ux%u\n", *width, *height);
-
- return 0;
-}
-EXPORT_SYMBOL(soc_camera_client_scale);
-
-/*
- * Calculate real client output window by applying new scales to the current
- * client crop. New scales are calculated from the requested output format and
- * host crop, mapped backed onto the client input (subrect).
- */
-void soc_camera_calc_client_output(struct soc_camera_device *icd,
- struct v4l2_rect *rect, struct v4l2_rect *subrect,
- const struct v4l2_pix_format *pix, struct v4l2_mbus_framefmt *mf,
- unsigned int shift)
-{
- struct device *dev = icd->parent;
- unsigned int scale_v, scale_h;
-
- if (subrect->width == rect->width &&
- subrect->height == rect->height) {
- /* No sub-cropping */
- mf->width = pix->width;
- mf->height = pix->height;
- return;
- }
-
- /* 1.-2. Current camera scales and subwin - cached. */
-
- dev_geo(dev, "2: subwin %ux%u@%u:%u\n",
- subrect->width, subrect->height,
- subrect->left, subrect->top);
-
- /*
- * 3. Calculate new combined scales from input sub-window to requested
- * user window.
- */
-
- /*
- * TODO: CEU cannot scale images larger than VGA to smaller than SubQCIF
- * (128x96) or larger than VGA. This and similar limitations have to be
- * taken into account here.
- */
- scale_h = soc_camera_calc_scale(subrect->width, shift, pix->width);
- scale_v = soc_camera_calc_scale(subrect->height, shift, pix->height);
-
- dev_geo(dev, "3: scales %u:%u\n", scale_h, scale_v);
-
- /*
- * 4. Calculate desired client output window by applying combined scales
- * to client (real) input window.
- */
- mf->width = soc_camera_shift_scale(rect->width, shift, scale_h);
- mf->height = soc_camera_shift_scale(rect->height, shift, scale_v);
-}
-EXPORT_SYMBOL(soc_camera_calc_client_output);
-
-MODULE_DESCRIPTION("soc-camera scaling-cropping functions");
-MODULE_AUTHOR("Guennadi Liakhovetski <kernel@pengutronix.de>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/soc_camera/soc_scale_crop.h b/drivers/media/platform/soc_camera/soc_scale_crop.h
deleted file mode 100644
index 9ca469312a1f..000000000000
--- a/drivers/media/platform/soc_camera/soc_scale_crop.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * soc-camera generic scaling-cropping manipulation functions
- *
- * Copyright (C) 2013 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-
-#ifndef SOC_SCALE_CROP_H
-#define SOC_SCALE_CROP_H
-
-#include <linux/kernel.h>
-
-struct soc_camera_device;
-
-struct v4l2_selection;
-struct v4l2_mbus_framefmt;
-struct v4l2_pix_format;
-struct v4l2_rect;
-struct v4l2_subdev;
-
-static inline unsigned int soc_camera_shift_scale(unsigned int size,
- unsigned int shift, unsigned int scale)
-{
- return DIV_ROUND_CLOSEST(size << shift, scale);
-}
-
-#define soc_camera_calc_scale(in, shift, out) soc_camera_shift_scale(in, shift, out)
-
-int soc_camera_client_g_rect(struct v4l2_subdev *sd, struct v4l2_rect *rect);
-int soc_camera_client_s_selection(struct v4l2_subdev *sd,
- struct v4l2_selection *sel, struct v4l2_selection *cam_sel,
- struct v4l2_rect *target_rect, struct v4l2_rect *subrect);
-int soc_camera_client_scale(struct soc_camera_device *icd,
- struct v4l2_rect *rect, struct v4l2_rect *subrect,
- struct v4l2_mbus_framefmt *mf,
- unsigned int *width, unsigned int *height,
- bool host_can_scale, unsigned int shift);
-void soc_camera_calc_client_output(struct soc_camera_device *icd,
- struct v4l2_rect *rect, struct v4l2_rect *subrect,
- const struct v4l2_pix_format *pix, struct v4l2_mbus_framefmt *mf,
- unsigned int shift);
-
-#endif
diff --git a/drivers/media/platform/sti/bdisp/bdisp-debug.c b/drivers/media/platform/sti/bdisp/bdisp-debug.c
index c6a4e2de5c0c..77ca7517fa3e 100644
--- a/drivers/media/platform/sti/bdisp/bdisp-debug.c
+++ b/drivers/media/platform/sti/bdisp/bdisp-debug.c
@@ -315,7 +315,7 @@ static void bdisp_dbg_dump_ivmx(struct seq_file *s,
seq_puts(s, "Unknown conversion\n");
}
-static int bdisp_dbg_last_nodes(struct seq_file *s, void *data)
+static int last_nodes_show(struct seq_file *s, void *data)
{
/* Not dumping all fields, focusing on significant ones */
struct bdisp_dev *bdisp = s->private;
@@ -388,7 +388,7 @@ static int bdisp_dbg_last_nodes(struct seq_file *s, void *data)
return 0;
}
-static int bdisp_dbg_last_nodes_raw(struct seq_file *s, void *data)
+static int last_nodes_raw_show(struct seq_file *s, void *data)
{
struct bdisp_dev *bdisp = s->private;
struct bdisp_node *node;
@@ -437,7 +437,7 @@ static const char *bdisp_fmt_to_str(struct bdisp_frame frame)
}
}
-static int bdisp_dbg_last_request(struct seq_file *s, void *data)
+static int last_request_show(struct seq_file *s, void *data)
{
struct bdisp_dev *bdisp = s->private;
struct bdisp_request *request = &bdisp->dbg.copy_request;
@@ -474,7 +474,7 @@ static int bdisp_dbg_last_request(struct seq_file *s, void *data)
#define DUMP(reg) seq_printf(s, #reg " \t0x%08X\n", readl(bdisp->regs + reg))
-static int bdisp_dbg_regs(struct seq_file *s, void *data)
+static int regs_show(struct seq_file *s, void *data)
{
struct bdisp_dev *bdisp = s->private;
int ret;
@@ -582,7 +582,7 @@ static int bdisp_dbg_regs(struct seq_file *s, void *data)
#define SECOND 1000000
-static int bdisp_dbg_perf(struct seq_file *s, void *data)
+static int perf_show(struct seq_file *s, void *data)
{
struct bdisp_dev *bdisp = s->private;
struct bdisp_request *request = &bdisp->dbg.copy_request;
@@ -627,27 +627,15 @@ static int bdisp_dbg_perf(struct seq_file *s, void *data)
return 0;
}
-#define bdisp_dbg_declare(name) \
- static int bdisp_dbg_##name##_open(struct inode *i, struct file *f) \
- { \
- return single_open(f, bdisp_dbg_##name, i->i_private); \
- } \
- static const struct file_operations bdisp_dbg_##name##_fops = { \
- .open = bdisp_dbg_##name##_open, \
- .read = seq_read, \
- .llseek = seq_lseek, \
- .release = single_release, \
- }
-
#define bdisp_dbg_create_entry(name) \
debugfs_create_file(#name, S_IRUGO, bdisp->dbg.debugfs_entry, bdisp, \
- &bdisp_dbg_##name##_fops)
+ &name##_fops)
-bdisp_dbg_declare(regs);
-bdisp_dbg_declare(last_nodes);
-bdisp_dbg_declare(last_nodes_raw);
-bdisp_dbg_declare(last_request);
-bdisp_dbg_declare(perf);
+DEFINE_SHOW_ATTRIBUTE(regs);
+DEFINE_SHOW_ATTRIBUTE(last_nodes);
+DEFINE_SHOW_ATTRIBUTE(last_nodes_raw);
+DEFINE_SHOW_ATTRIBUTE(last_request);
+DEFINE_SHOW_ATTRIBUTE(perf);
int bdisp_debugfs_create(struct bdisp_dev *bdisp)
{
diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.h b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.h
index 3dbb3a287cc0..c9d6021904cd 100644
--- a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.h
+++ b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.h
@@ -193,7 +193,7 @@ struct c8sectpfei {
#define C8SECTPFE_SYS_ENABLE BIT(0)
/*
- * Ponter record data structure required for each input block
+ * Pointer record data structure required for each input block
* see Table 82 on page 167 of functional specification.
*/
diff --git a/drivers/media/platform/sti/delta/delta.h b/drivers/media/platform/sti/delta/delta.h
index 2ba99922c05b..914556030e70 100644
--- a/drivers/media/platform/sti/delta/delta.h
+++ b/drivers/media/platform/sti/delta/delta.h
@@ -286,7 +286,7 @@ struct delta_dec {
* Header parsing must be done using decode(), giving
* explicitly header access unit or first access unit of bitstream.
* If no valid header is found, get_streaminfo will return -ENODATA,
- * in this case the next bistream access unit must be decoded till
+ * in this case the next bitstream access unit must be decoded till
* get_streaminfo becomes successful.
*/
int (*get_streaminfo)(struct delta_ctx *ctx,
diff --git a/drivers/media/platform/sti/hva/hva-debugfs.c b/drivers/media/platform/sti/hva/hva-debugfs.c
index 9f7e8ac875d1..7d12a5b5d914 100644
--- a/drivers/media/platform/sti/hva/hva-debugfs.c
+++ b/drivers/media/platform/sti/hva/hva-debugfs.c
@@ -271,7 +271,7 @@ static void hva_dbg_perf_compute(struct hva_ctx *ctx)
* device debug info
*/
-static int hva_dbg_device(struct seq_file *s, void *data)
+static int device_show(struct seq_file *s, void *data)
{
struct hva_dev *hva = s->private;
@@ -281,7 +281,7 @@ static int hva_dbg_device(struct seq_file *s, void *data)
return 0;
}
-static int hva_dbg_encoders(struct seq_file *s, void *data)
+static int encoders_show(struct seq_file *s, void *data)
{
struct hva_dev *hva = s->private;
unsigned int i = 0;
@@ -299,7 +299,7 @@ static int hva_dbg_encoders(struct seq_file *s, void *data)
return 0;
}
-static int hva_dbg_last(struct seq_file *s, void *data)
+static int last_show(struct seq_file *s, void *data)
{
struct hva_dev *hva = s->private;
struct hva_ctx *last_ctx = &hva->dbg.last_ctx;
@@ -316,7 +316,7 @@ static int hva_dbg_last(struct seq_file *s, void *data)
return 0;
}
-static int hva_dbg_regs(struct seq_file *s, void *data)
+static int regs_show(struct seq_file *s, void *data)
{
struct hva_dev *hva = s->private;
@@ -325,26 +325,14 @@ static int hva_dbg_regs(struct seq_file *s, void *data)
return 0;
}
-#define hva_dbg_declare(name) \
- static int hva_dbg_##name##_open(struct inode *i, struct file *f) \
- { \
- return single_open(f, hva_dbg_##name, i->i_private); \
- } \
- static const struct file_operations hva_dbg_##name##_fops = { \
- .open = hva_dbg_##name##_open, \
- .read = seq_read, \
- .llseek = seq_lseek, \
- .release = single_release, \
- }
-
#define hva_dbg_create_entry(name) \
debugfs_create_file(#name, 0444, hva->dbg.debugfs_entry, hva, \
- &hva_dbg_##name##_fops)
+ &name##_fops)
-hva_dbg_declare(device);
-hva_dbg_declare(encoders);
-hva_dbg_declare(last);
-hva_dbg_declare(regs);
+DEFINE_SHOW_ATTRIBUTE(device);
+DEFINE_SHOW_ATTRIBUTE(encoders);
+DEFINE_SHOW_ATTRIBUTE(last);
+DEFINE_SHOW_ATTRIBUTE(regs);
void hva_debugfs_create(struct hva_dev *hva)
{
@@ -380,7 +368,7 @@ void hva_debugfs_remove(struct hva_dev *hva)
* context (instance) debug info
*/
-static int hva_dbg_ctx(struct seq_file *s, void *data)
+static int ctx_show(struct seq_file *s, void *data)
{
struct hva_ctx *ctx = s->private;
@@ -392,7 +380,7 @@ static int hva_dbg_ctx(struct seq_file *s, void *data)
return 0;
}
-hva_dbg_declare(ctx);
+DEFINE_SHOW_ATTRIBUTE(ctx);
void hva_dbg_ctx_create(struct hva_ctx *ctx)
{
@@ -407,7 +395,7 @@ void hva_dbg_ctx_create(struct hva_ctx *ctx)
ctx->dbg.debugfs_entry = debugfs_create_file(name, 0444,
hva->dbg.debugfs_entry,
- ctx, &hva_dbg_ctx_fops);
+ ctx, &ctx_fops);
}
void hva_dbg_ctx_remove(struct hva_ctx *ctx)
diff --git a/drivers/media/platform/sti/hva/hva-h264.c b/drivers/media/platform/sti/hva/hva-h264.c
index b61a5d337d2a..c34f7cf5aed2 100644
--- a/drivers/media/platform/sti/hva/hva-h264.c
+++ b/drivers/media/platform/sti/hva/hva-h264.c
@@ -626,7 +626,7 @@ static int hva_h264_prepare_task(struct hva_ctx *pctx,
td->frame_width = frame_width;
td->frame_height = frame_height;
- /* set frame alignement */
+ /* set frame alignment */
td->window_width = frame_width;
td->window_height = frame_height;
td->window_horizontal_offset = 0;
diff --git a/drivers/media/platform/stm32/stm32-dcmi.c b/drivers/media/platform/stm32/stm32-dcmi.c
index 6732874114cf..5fe5b38fa901 100644
--- a/drivers/media/platform/stm32/stm32-dcmi.c
+++ b/drivers/media/platform/stm32/stm32-dcmi.c
@@ -1544,7 +1544,7 @@ static void dcmi_graph_notify_unbind(struct v4l2_async_notifier *notifier,
dev_dbg(dcmi->dev, "Removing %s\n", video_device_node_name(dcmi->vdev));
- /* Checks internaly if vdev has been init or not */
+ /* Checks internally if vdev has been init or not */
video_unregister_device(dcmi->vdev);
}
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c
index 6950585edb5a..4c79eb64a7a7 100644
--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c
@@ -15,6 +15,7 @@
#include <linux/ioctl.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
@@ -143,6 +144,15 @@ bool sun6i_csi_is_format_supported(struct sun6i_csi *csi,
break;
}
break;
+
+ case V4L2_PIX_FMT_RGB565:
+ return (mbus_code == MEDIA_BUS_FMT_RGB565_2X8_LE);
+ case V4L2_PIX_FMT_RGB565X:
+ return (mbus_code == MEDIA_BUS_FMT_RGB565_2X8_BE);
+
+ case V4L2_PIX_FMT_JPEG:
+ return (mbus_code == MEDIA_BUS_FMT_JPEG_1X8);
+
default:
dev_dbg(sdev->dev, "Unsupported pixformat: 0x%x\n", pixformat);
break;
@@ -154,6 +164,7 @@ bool sun6i_csi_is_format_supported(struct sun6i_csi *csi,
int sun6i_csi_set_power(struct sun6i_csi *csi, bool enable)
{
struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi);
+ struct device *dev = sdev->dev;
struct regmap *regmap = sdev->regmap;
int ret;
@@ -161,6 +172,9 @@ int sun6i_csi_set_power(struct sun6i_csi *csi, bool enable)
regmap_update_bits(regmap, CSI_EN_REG, CSI_EN_CSI_EN, 0);
clk_disable_unprepare(sdev->clk_ram);
+ if (of_device_is_compatible(dev->of_node,
+ "allwinner,sun50i-a64-csi"))
+ clk_rate_exclusive_put(sdev->clk_mod);
clk_disable_unprepare(sdev->clk_mod);
reset_control_assert(sdev->rstc_bus);
return 0;
@@ -172,6 +186,9 @@ int sun6i_csi_set_power(struct sun6i_csi *csi, bool enable)
return ret;
}
+ if (of_device_is_compatible(dev->of_node, "allwinner,sun50i-a64-csi"))
+ clk_set_rate_exclusive(sdev->clk_mod, 300000000);
+
ret = clk_prepare_enable(sdev->clk_ram);
if (ret) {
dev_err(sdev->dev, "Enable clk_dram_csi clk err %d\n", ret);
@@ -191,6 +208,8 @@ int sun6i_csi_set_power(struct sun6i_csi *csi, bool enable)
clk_ram_disable:
clk_disable_unprepare(sdev->clk_ram);
clk_mod_disable:
+ if (of_device_is_compatible(dev->of_node, "allwinner,sun50i-a64-csi"))
+ clk_rate_exclusive_put(sdev->clk_mod);
clk_disable_unprepare(sdev->clk_mod);
return ret;
}
@@ -198,8 +217,8 @@ clk_mod_disable:
static enum csi_input_fmt get_csi_input_format(struct sun6i_csi_dev *sdev,
u32 mbus_code, u32 pixformat)
{
- /* bayer */
- if ((mbus_code & 0xF000) == 0x3000)
+ /* non-YUV */
+ if ((mbus_code & 0xF000) != 0x2000)
return CSI_INPUT_FORMAT_RAW;
switch (pixformat) {
@@ -268,6 +287,14 @@ static enum csi_output_fmt get_csi_output_format(struct sun6i_csi_dev *sdev,
case V4L2_PIX_FMT_YUV422P:
return buf_interlaced ? CSI_FRAME_PLANAR_YUV422 :
CSI_FIELD_PLANAR_YUV422;
+
+ case V4L2_PIX_FMT_RGB565:
+ case V4L2_PIX_FMT_RGB565X:
+ return buf_interlaced ? CSI_FRAME_RGB565 : CSI_FIELD_RGB565;
+
+ case V4L2_PIX_FMT_JPEG:
+ return buf_interlaced ? CSI_FRAME_RAW_8 : CSI_FIELD_RAW_8;
+
default:
dev_warn(sdev->dev, "Unsupported pixformat: 0x%x\n", pixformat);
break;
@@ -279,6 +306,10 @@ static enum csi_output_fmt get_csi_output_format(struct sun6i_csi_dev *sdev,
static enum csi_input_seq get_csi_input_seq(struct sun6i_csi_dev *sdev,
u32 mbus_code, u32 pixformat)
{
+ /* Input sequence does not apply to non-YUV formats */
+ if ((mbus_code & 0xF000) != 0x2000)
+ return 0;
+
switch (pixformat) {
case V4L2_PIX_FMT_HM12:
case V4L2_PIX_FMT_NV12:
@@ -793,7 +824,7 @@ static const struct regmap_config sun6i_csi_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
- .max_register = 0x1000,
+ .max_register = 0x9c,
};
static int sun6i_csi_resource_request(struct sun6i_csi_dev *sdev,
@@ -893,7 +924,9 @@ static int sun6i_csi_remove(struct platform_device *pdev)
static const struct of_device_id sun6i_csi_of_match[] = {
{ .compatible = "allwinner,sun6i-a31-csi", },
+ { .compatible = "allwinner,sun8i-h3-csi", },
{ .compatible = "allwinner,sun8i-v3s-csi", },
+ { .compatible = "allwinner,sun50i-a64-csi", },
{},
};
MODULE_DEVICE_TABLE(of, sun6i_csi_of_match);
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h
index 0bb000712c33..c626821aaedb 100644
--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h
@@ -65,7 +65,7 @@ bool sun6i_csi_is_format_supported(struct sun6i_csi *csi, u32 pixformat,
int sun6i_csi_set_power(struct sun6i_csi *csi, bool enable);
/**
- * sun6i_csi_update_config() - update the csi register setttings
+ * sun6i_csi_update_config() - update the csi register settings
* @csi: pointer to the csi
* @config: see struct sun6i_csi_config
*/
@@ -94,6 +94,7 @@ static inline int sun6i_csi_get_bpp(unsigned int pixformat)
case V4L2_PIX_FMT_SGBRG8:
case V4L2_PIX_FMT_SGRBG8:
case V4L2_PIX_FMT_SRGGB8:
+ case V4L2_PIX_FMT_JPEG:
return 8;
case V4L2_PIX_FMT_SBGGR10:
case V4L2_PIX_FMT_SGBRG10:
@@ -117,6 +118,8 @@ static inline int sun6i_csi_get_bpp(unsigned int pixformat)
case V4L2_PIX_FMT_NV16:
case V4L2_PIX_FMT_NV61:
case V4L2_PIX_FMT_YUV422P:
+ case V4L2_PIX_FMT_RGB565:
+ case V4L2_PIX_FMT_RGB565X:
return 16;
case V4L2_PIX_FMT_RGB24:
case V4L2_PIX_FMT_BGR24:
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c
index b04300c3811f..1fd16861f111 100644
--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c
@@ -56,6 +56,9 @@ static const u32 supported_pixformats[] = {
V4L2_PIX_FMT_NV16,
V4L2_PIX_FMT_NV61,
V4L2_PIX_FMT_YUV422P,
+ V4L2_PIX_FMT_RGB565,
+ V4L2_PIX_FMT_RGB565X,
+ V4L2_PIX_FMT_JPEG,
};
static bool is_pixformat_valid(unsigned int pixformat)
diff --git a/drivers/media/platform/ti-vpe/vpdma.c b/drivers/media/platform/ti-vpe/vpdma.c
index e2cf2b90e500..78d716c93649 100644
--- a/drivers/media/platform/ti-vpe/vpdma.c
+++ b/drivers/media/platform/ti-vpe/vpdma.c
@@ -404,7 +404,7 @@ EXPORT_SYMBOL(vpdma_map_desc_buf);
/*
* unmap descriptor/payload DMA buffer, disabling DMA access and
- * allowing the main processor to acces the data
+ * allowing the main processor to access the data
*/
void vpdma_unmap_desc_buf(struct vpdma_data *vpdma, struct vpdma_buf *buf)
{
@@ -501,7 +501,7 @@ void vpdma_reset_desc_list(struct vpdma_desc_list *list)
EXPORT_SYMBOL(vpdma_reset_desc_list);
/*
- * free the buffer allocated fot the VPDMA descriptor list, this should be
+ * free the buffer allocated for the VPDMA descriptor list, this should be
* called when the user doesn't want to use VPDMA any more.
*/
void vpdma_free_desc_list(struct vpdma_desc_list *list)
@@ -790,7 +790,7 @@ static void dump_dtd(struct vpdma_dtd *dtd)
* append an outbound data transfer descriptor to the given descriptor list,
* this sets up a 'client to memory' VPDMA transfer for the given VPDMA channel
*
- * @list: vpdma desc list to which we add this decriptor
+ * @list: vpdma desc list to which we add this descriptor
* @width: width of the image in pixels in memory
* @c_rect: compose params of output image
* @fmt: vpdma data format of the buffer
@@ -798,7 +798,7 @@ static void dump_dtd(struct vpdma_dtd *dtd)
* max_width: enum for maximum width of data transfer
* max_height: enum for maximum height of data transfer
* chan: VPDMA channel
- * flags: VPDMA flags to configure some descriptor fileds
+ * flags: VPDMA flags to configure some descriptor fields
*/
void vpdma_add_out_dtd(struct vpdma_desc_list *list, int width,
int stride, const struct v4l2_rect *c_rect,
@@ -863,14 +863,14 @@ EXPORT_SYMBOL(vpdma_rawchan_add_out_dtd);
* append an inbound data transfer descriptor to the given descriptor list,
* this sets up a 'memory to client' VPDMA transfer for the given VPDMA channel
*
- * @list: vpdma desc list to which we add this decriptor
+ * @list: vpdma desc list to which we add this descriptor
* @width: width of the image in pixels in memory(not the cropped width)
* @c_rect: crop params of input image
* @fmt: vpdma data format of the buffer
* dma_addr: dma address as seen by VPDMA
* chan: VPDMA channel
* field: top or bottom field info of the input image
- * flags: VPDMA flags to configure some descriptor fileds
+ * flags: VPDMA flags to configure some descriptor fields
* frame_width/height: the complete width/height of the image presented to the
* client (this makes sense when multiple channels are
* connected to the same client, forming a larger frame)
@@ -1008,7 +1008,7 @@ unsigned int vpdma_get_list_mask(struct vpdma_data *vpdma, int irq_num)
}
EXPORT_SYMBOL(vpdma_get_list_mask);
-/* clear previosuly occured list intterupts in the LIST_STAT register */
+/* clear previously occurred list interrupts in the LIST_STAT register */
void vpdma_clear_list_stat(struct vpdma_data *vpdma, int irq_num,
int list_num)
{
diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c
index d70871d0ad2d..207e7e76c048 100644
--- a/drivers/media/platform/ti-vpe/vpe.c
+++ b/drivers/media/platform/ti-vpe/vpe.c
@@ -876,7 +876,7 @@ static int set_srcdst_params(struct vpe_ctx *ctx)
/*
* we make sure that the source image has a 16 byte aligned
* stride, we need to do the same for the motion vector buffer
- * by aligning it's stride to the next 16 byte boundry. this
+ * by aligning it's stride to the next 16 byte boundary. this
* extra space will not be used by the de-interlacer, but will
* ensure that vpdma operates correctly
*/
diff --git a/drivers/media/platform/vicodec/codec-fwht.c b/drivers/media/platform/vicodec/codec-fwht.c
index 5630f1dc45e6..d1d6085da9f1 100644
--- a/drivers/media/platform/vicodec/codec-fwht.c
+++ b/drivers/media/platform/vicodec/codec-fwht.c
@@ -10,8 +10,11 @@
*/
#include <linux/string.h>
+#include <linux/kernel.h>
#include "codec-fwht.h"
+#define OVERFLOW_BIT BIT(14)
+
/*
* Note: bit 0 of the header must always be 0. Otherwise it cannot
* be guaranteed that the magic 8 byte sequence (see below) can
@@ -103,16 +106,21 @@ static int rlc(const s16 *in, __be16 *output, int blocktype)
* This function will worst-case increase rlc_in by 65*2 bytes:
* one s16 value for the header and 8 * 8 coefficients of type s16.
*/
-static s16 derlc(const __be16 **rlc_in, s16 *dwht_out)
+static u16 derlc(const __be16 **rlc_in, s16 *dwht_out,
+ const __be16 *end_of_input)
{
/* header */
const __be16 *input = *rlc_in;
- s16 ret = ntohs(*input++);
+ u16 stat;
int dec_count = 0;
s16 block[8 * 8 + 16];
s16 *wp = block;
int i;
+ if (input > end_of_input)
+ return OVERFLOW_BIT;
+ stat = ntohs(*input++);
+
/*
* Now de-compress, it expands one byte to up to 15 bytes
* (or fills the remainder of the 64 bytes with zeroes if it
@@ -122,9 +130,15 @@ static s16 derlc(const __be16 **rlc_in, s16 *dwht_out)
* allow for overflow if the incoming data was malformed.
*/
while (dec_count < 8 * 8) {
- s16 in = ntohs(*input++);
- int length = in & 0xf;
- int coeff = in >> 4;
+ s16 in;
+ int length;
+ int coeff;
+
+ if (input > end_of_input)
+ return OVERFLOW_BIT;
+ in = ntohs(*input++);
+ length = in & 0xf;
+ coeff = in >> 4;
/* fill remainder with zeros */
if (length == 15) {
@@ -149,7 +163,7 @@ static s16 derlc(const __be16 **rlc_in, s16 *dwht_out)
dwht_out[x + y * 8] = *wp++;
}
*rlc_in = input;
- return ret;
+ return stat;
}
static const int quant_table[] = {
@@ -237,8 +251,6 @@ static void fwht(const u8 *block, s16 *output_block, unsigned int stride,
unsigned int i;
/* stage 1 */
- stride *= input_step;
-
for (i = 0; i < 8; i++, tmp += stride, out += 8) {
switch (input_step) {
case 1:
@@ -562,7 +574,7 @@ static void fill_encoder_block(const u8 *input, s16 *dst,
for (i = 0; i < 8; i++) {
for (j = 0; j < 8; j++, input += input_step)
*dst++ = *input;
- input += (stride - 8) * input_step;
+ input += stride - 8 * input_step;
}
}
@@ -660,7 +672,7 @@ static void add_deltas(s16 *deltas, const u8 *ref, int stride)
static u32 encode_plane(u8 *input, u8 *refp, __be16 **rlco, __be16 *rlco_max,
struct fwht_cframe *cf, u32 height, u32 width,
- unsigned int input_step,
+ u32 stride, unsigned int input_step,
bool is_intra, bool next_is_intra)
{
u8 *input_start = input;
@@ -671,7 +683,11 @@ static u32 encode_plane(u8 *input, u8 *refp, __be16 **rlco, __be16 *rlco_max,
unsigned int last_size = 0;
unsigned int i, j;
+ width = round_up(width, 8);
+ height = round_up(height, 8);
+
for (j = 0; j < height / 8; j++) {
+ input = input_start + j * 8 * stride;
for (i = 0; i < width / 8; i++) {
/* intra code, first frame is always intra coded. */
int blocktype = IBLOCK;
@@ -679,9 +695,9 @@ static u32 encode_plane(u8 *input, u8 *refp, __be16 **rlco, __be16 *rlco_max,
if (!is_intra)
blocktype = decide_blocktype(input, refp,
- deltablock, width, input_step);
+ deltablock, stride, input_step);
if (blocktype == IBLOCK) {
- fwht(input, cf->coeffs, width, input_step, 1);
+ fwht(input, cf->coeffs, stride, input_step, 1);
quantize_intra(cf->coeffs, cf->de_coeffs,
cf->i_frame_qp);
} else {
@@ -722,12 +738,12 @@ static u32 encode_plane(u8 *input, u8 *refp, __be16 **rlco, __be16 *rlco_max,
}
last_size = size;
}
- input += width * 7 * input_step;
}
exit_loop:
if (encoding & FWHT_FRAME_UNENCODED) {
u8 *out = (u8 *)rlco_start;
+ u8 *p;
input = input_start;
/*
@@ -736,8 +752,11 @@ exit_loop:
* by 0xfe. Since YUV is limited range such values
* shouldn't appear anyway.
*/
- for (i = 0; i < height * width; i++, input += input_step)
- *out++ = (*input == 0xff) ? 0xfe : *input;
+ for (j = 0; j < height; j++) {
+ for (i = 0, p = input; i < width; i++, p += input_step)
+ *out++ = (*p == 0xff) ? 0xfe : *p;
+ input += stride;
+ }
*rlco = (__be16 *)out;
encoding &= ~FWHT_FRAME_PCODED;
}
@@ -747,30 +766,32 @@ exit_loop:
u32 fwht_encode_frame(struct fwht_raw_frame *frm,
struct fwht_raw_frame *ref_frm,
struct fwht_cframe *cf,
- bool is_intra, bool next_is_intra)
+ bool is_intra, bool next_is_intra,
+ unsigned int width, unsigned int height,
+ unsigned int stride, unsigned int chroma_stride)
{
- unsigned int size = frm->height * frm->width;
+ unsigned int size = height * width;
__be16 *rlco = cf->rlc_data;
__be16 *rlco_max;
u32 encoding;
rlco_max = rlco + size / 2 - 256;
encoding = encode_plane(frm->luma, ref_frm->luma, &rlco, rlco_max, cf,
- frm->height, frm->width,
+ height, width, stride,
frm->luma_alpha_step, is_intra, next_is_intra);
if (encoding & FWHT_FRAME_UNENCODED)
encoding |= FWHT_LUMA_UNENCODED;
encoding &= ~FWHT_FRAME_UNENCODED;
if (frm->components_num >= 3) {
- u32 chroma_h = frm->height / frm->height_div;
- u32 chroma_w = frm->width / frm->width_div;
+ u32 chroma_h = height / frm->height_div;
+ u32 chroma_w = width / frm->width_div;
unsigned int chroma_size = chroma_h * chroma_w;
rlco_max = rlco + chroma_size / 2 - 256;
encoding |= encode_plane(frm->cb, ref_frm->cb, &rlco, rlco_max,
cf, chroma_h, chroma_w,
- frm->chroma_step,
+ chroma_stride, frm->chroma_step,
is_intra, next_is_intra);
if (encoding & FWHT_FRAME_UNENCODED)
encoding |= FWHT_CB_UNENCODED;
@@ -778,7 +799,7 @@ u32 fwht_encode_frame(struct fwht_raw_frame *frm,
rlco_max = rlco + chroma_size / 2 - 256;
encoding |= encode_plane(frm->cr, ref_frm->cr, &rlco, rlco_max,
cf, chroma_h, chroma_w,
- frm->chroma_step,
+ chroma_stride, frm->chroma_step,
is_intra, next_is_intra);
if (encoding & FWHT_FRAME_UNENCODED)
encoding |= FWHT_CR_UNENCODED;
@@ -787,10 +808,10 @@ u32 fwht_encode_frame(struct fwht_raw_frame *frm,
if (frm->components_num == 4) {
rlco_max = rlco + size / 2 - 256;
- encoding = encode_plane(frm->alpha, ref_frm->alpha, &rlco,
- rlco_max, cf, frm->height, frm->width,
- frm->luma_alpha_step,
- is_intra, next_is_intra);
+ encoding |= encode_plane(frm->alpha, ref_frm->alpha, &rlco,
+ rlco_max, cf, height, width,
+ stride, frm->luma_alpha_step,
+ is_intra, next_is_intra);
if (encoding & FWHT_FRAME_UNENCODED)
encoding |= FWHT_ALPHA_UNENCODED;
encoding &= ~FWHT_FRAME_UNENCODED;
@@ -800,18 +821,24 @@ u32 fwht_encode_frame(struct fwht_raw_frame *frm,
return encoding;
}
-static void decode_plane(struct fwht_cframe *cf, const __be16 **rlco, u8 *ref,
- u32 height, u32 width, bool uncompressed)
+static bool decode_plane(struct fwht_cframe *cf, const __be16 **rlco, u8 *ref,
+ u32 height, u32 width, u32 coded_width,
+ bool uncompressed, const __be16 *end_of_rlco_buf)
{
unsigned int copies = 0;
s16 copy[8 * 8];
- s16 stat;
+ u16 stat;
unsigned int i, j;
+ width = round_up(width, 8);
+ height = round_up(height, 8);
+
if (uncompressed) {
+ if (end_of_rlco_buf + 1 < *rlco + width * height / 2)
+ return false;
memcpy(ref, *rlco, width * height);
*rlco += width * height / 2;
- return;
+ return true;
}
/*
@@ -822,19 +849,22 @@ static void decode_plane(struct fwht_cframe *cf, const __be16 **rlco, u8 *ref,
*/
for (j = 0; j < height / 8; j++) {
for (i = 0; i < width / 8; i++) {
- u8 *refp = ref + j * 8 * width + i * 8;
+ u8 *refp = ref + j * 8 * coded_width + i * 8;
if (copies) {
memcpy(cf->de_fwht, copy, sizeof(copy));
if (stat & PFRAME_BIT)
- add_deltas(cf->de_fwht, refp, width);
- fill_decoder_block(refp, cf->de_fwht, width);
+ add_deltas(cf->de_fwht, refp,
+ coded_width);
+ fill_decoder_block(refp, cf->de_fwht,
+ coded_width);
copies--;
continue;
}
- stat = derlc(rlco, cf->coeffs);
-
+ stat = derlc(rlco, cf->coeffs, end_of_rlco_buf);
+ if (stat & OVERFLOW_BIT)
+ return false;
if (stat & PFRAME_BIT)
dequantize_inter(cf->coeffs);
else
@@ -847,35 +877,53 @@ static void decode_plane(struct fwht_cframe *cf, const __be16 **rlco, u8 *ref,
if (copies)
memcpy(copy, cf->de_fwht, sizeof(copy));
if (stat & PFRAME_BIT)
- add_deltas(cf->de_fwht, refp, width);
- fill_decoder_block(refp, cf->de_fwht, width);
+ add_deltas(cf->de_fwht, refp, coded_width);
+ fill_decoder_block(refp, cf->de_fwht, coded_width);
}
}
+ return true;
}
-void fwht_decode_frame(struct fwht_cframe *cf, struct fwht_raw_frame *ref,
- u32 hdr_flags, unsigned int components_num)
+bool fwht_decode_frame(struct fwht_cframe *cf, struct fwht_raw_frame *ref,
+ u32 hdr_flags, unsigned int components_num,
+ unsigned int width, unsigned int height,
+ unsigned int coded_width)
{
const __be16 *rlco = cf->rlc_data;
+ const __be16 *end_of_rlco_buf = cf->rlc_data +
+ (cf->size / sizeof(*rlco)) - 1;
- decode_plane(cf, &rlco, ref->luma, cf->height, cf->width,
- hdr_flags & FWHT_FL_LUMA_IS_UNCOMPRESSED);
+ if (!decode_plane(cf, &rlco, ref->luma, height, width, coded_width,
+ hdr_flags & FWHT_FL_LUMA_IS_UNCOMPRESSED,
+ end_of_rlco_buf))
+ return false;
if (components_num >= 3) {
- u32 h = cf->height;
- u32 w = cf->width;
+ u32 h = height;
+ u32 w = width;
+ u32 c = coded_width;
if (!(hdr_flags & FWHT_FL_CHROMA_FULL_HEIGHT))
h /= 2;
- if (!(hdr_flags & FWHT_FL_CHROMA_FULL_WIDTH))
+ if (!(hdr_flags & FWHT_FL_CHROMA_FULL_WIDTH)) {
w /= 2;
- decode_plane(cf, &rlco, ref->cb, h, w,
- hdr_flags & FWHT_FL_CB_IS_UNCOMPRESSED);
- decode_plane(cf, &rlco, ref->cr, h, w,
- hdr_flags & FWHT_FL_CR_IS_UNCOMPRESSED);
+ c /= 2;
+ }
+ if (!decode_plane(cf, &rlco, ref->cb, h, w, c,
+ hdr_flags & FWHT_FL_CB_IS_UNCOMPRESSED,
+ end_of_rlco_buf))
+ return false;
+ if (!decode_plane(cf, &rlco, ref->cr, h, w, c,
+ hdr_flags & FWHT_FL_CR_IS_UNCOMPRESSED,
+ end_of_rlco_buf))
+ return false;
}
if (components_num == 4)
- decode_plane(cf, &rlco, ref->alpha, cf->height, cf->width,
- hdr_flags & FWHT_FL_ALPHA_IS_UNCOMPRESSED);
+ if (!decode_plane(cf, &rlco, ref->alpha, height, width,
+ coded_width,
+ hdr_flags & FWHT_FL_ALPHA_IS_UNCOMPRESSED,
+ end_of_rlco_buf))
+ return false;
+ return true;
}
diff --git a/drivers/media/platform/vicodec/codec-fwht.h b/drivers/media/platform/vicodec/codec-fwht.h
index 90ff8962fca7..c410512d47c5 100644
--- a/drivers/media/platform/vicodec/codec-fwht.h
+++ b/drivers/media/platform/vicodec/codec-fwht.h
@@ -56,7 +56,7 @@
#define FWHT_MAGIC1 0x4f4f4f4f
#define FWHT_MAGIC2 0xffffffff
-#define FWHT_VERSION 2
+#define FWHT_VERSION 3
/* Set if this is an interlaced format */
#define FWHT_FL_IS_INTERLACED BIT(0)
@@ -76,11 +76,25 @@
#define FWHT_FL_CHROMA_FULL_HEIGHT BIT(7)
#define FWHT_FL_CHROMA_FULL_WIDTH BIT(8)
#define FWHT_FL_ALPHA_IS_UNCOMPRESSED BIT(9)
+#define FWHT_FL_I_FRAME BIT(10)
/* A 4-values flag - the number of components - 1 */
-#define FWHT_FL_COMPONENTS_NUM_MSK GENMASK(17, 16)
+#define FWHT_FL_COMPONENTS_NUM_MSK GENMASK(18, 16)
#define FWHT_FL_COMPONENTS_NUM_OFFSET 16
+#define FWHT_FL_PIXENC_MSK GENMASK(20, 19)
+#define FWHT_FL_PIXENC_OFFSET 19
+#define FWHT_FL_PIXENC_YUV (1 << FWHT_FL_PIXENC_OFFSET)
+#define FWHT_FL_PIXENC_RGB (2 << FWHT_FL_PIXENC_OFFSET)
+#define FWHT_FL_PIXENC_HSV (3 << FWHT_FL_PIXENC_OFFSET)
+
+/*
+ * A macro to calculate the needed padding in order to make sure
+ * both luma and chroma components resolutions are rounded up to
+ * a multiple of 8
+ */
+#define vic_round_dim(dim, div) (round_up((dim) / (div), 8) * (div))
+
struct fwht_cframe_hdr {
u32 magic1;
u32 magic2;
@@ -95,7 +109,6 @@ struct fwht_cframe_hdr {
};
struct fwht_cframe {
- unsigned int width, height;
u16 i_frame_qp;
u16 p_frame_qp;
__be16 *rlc_data;
@@ -106,7 +119,6 @@ struct fwht_cframe {
};
struct fwht_raw_frame {
- unsigned int width, height;
unsigned int width_div;
unsigned int height_div;
unsigned int luma_alpha_step;
@@ -125,8 +137,12 @@ struct fwht_raw_frame {
u32 fwht_encode_frame(struct fwht_raw_frame *frm,
struct fwht_raw_frame *ref_frm,
struct fwht_cframe *cf,
- bool is_intra, bool next_is_intra);
-void fwht_decode_frame(struct fwht_cframe *cf, struct fwht_raw_frame *ref,
- u32 hdr_flags, unsigned int components_num);
+ bool is_intra, bool next_is_intra,
+ unsigned int width, unsigned int height,
+ unsigned int stride, unsigned int chroma_stride);
+bool fwht_decode_frame(struct fwht_cframe *cf, struct fwht_raw_frame *ref,
+ u32 hdr_flags, unsigned int components_num,
+ unsigned int width, unsigned int height,
+ unsigned int coded_width);
#endif
diff --git a/drivers/media/platform/vicodec/codec-v4l2-fwht.c b/drivers/media/platform/vicodec/codec-v4l2-fwht.c
index 8cb0212df67f..6573a471c5ca 100644
--- a/drivers/media/platform/vicodec/codec-v4l2-fwht.c
+++ b/drivers/media/platform/vicodec/codec-v4l2-fwht.c
@@ -11,32 +11,53 @@
#include "codec-v4l2-fwht.h"
static const struct v4l2_fwht_pixfmt_info v4l2_fwht_pixfmts[] = {
- { V4L2_PIX_FMT_YUV420, 1, 3, 2, 1, 1, 2, 2, 3},
- { V4L2_PIX_FMT_YVU420, 1, 3, 2, 1, 1, 2, 2, 3},
- { V4L2_PIX_FMT_YUV422P, 1, 2, 1, 1, 1, 2, 1, 3},
- { V4L2_PIX_FMT_NV12, 1, 3, 2, 1, 2, 2, 2, 3},
- { V4L2_PIX_FMT_NV21, 1, 3, 2, 1, 2, 2, 2, 3},
- { V4L2_PIX_FMT_NV16, 1, 2, 1, 1, 2, 2, 1, 3},
- { V4L2_PIX_FMT_NV61, 1, 2, 1, 1, 2, 2, 1, 3},
- { V4L2_PIX_FMT_NV24, 1, 3, 1, 1, 2, 1, 1, 3},
- { V4L2_PIX_FMT_NV42, 1, 3, 1, 1, 2, 1, 1, 3},
- { V4L2_PIX_FMT_YUYV, 2, 2, 1, 2, 4, 2, 1, 3},
- { V4L2_PIX_FMT_YVYU, 2, 2, 1, 2, 4, 2, 1, 3},
- { V4L2_PIX_FMT_UYVY, 2, 2, 1, 2, 4, 2, 1, 3},
- { V4L2_PIX_FMT_VYUY, 2, 2, 1, 2, 4, 2, 1, 3},
- { V4L2_PIX_FMT_BGR24, 3, 3, 1, 3, 3, 1, 1, 3},
- { V4L2_PIX_FMT_RGB24, 3, 3, 1, 3, 3, 1, 1, 3},
- { V4L2_PIX_FMT_HSV24, 3, 3, 1, 3, 3, 1, 1, 3},
- { V4L2_PIX_FMT_BGR32, 4, 4, 1, 4, 4, 1, 1, 3},
- { V4L2_PIX_FMT_XBGR32, 4, 4, 1, 4, 4, 1, 1, 3},
- { V4L2_PIX_FMT_RGB32, 4, 4, 1, 4, 4, 1, 1, 3},
- { V4L2_PIX_FMT_XRGB32, 4, 4, 1, 4, 4, 1, 1, 3},
- { V4L2_PIX_FMT_HSV32, 4, 4, 1, 4, 4, 1, 1, 3},
- { V4L2_PIX_FMT_ARGB32, 4, 4, 1, 4, 4, 1, 1, 4},
- { V4L2_PIX_FMT_ABGR32, 4, 4, 1, 4, 4, 1, 1, 4},
- { V4L2_PIX_FMT_GREY, 1, 1, 1, 1, 0, 1, 1, 1},
+ { V4L2_PIX_FMT_YUV420, 1, 3, 2, 1, 1, 2, 2, 3, 3, FWHT_FL_PIXENC_YUV},
+ { V4L2_PIX_FMT_YVU420, 1, 3, 2, 1, 1, 2, 2, 3, 3, FWHT_FL_PIXENC_YUV},
+ { V4L2_PIX_FMT_YUV422P, 1, 2, 1, 1, 1, 2, 1, 3, 3, FWHT_FL_PIXENC_YUV},
+ { V4L2_PIX_FMT_NV12, 1, 3, 2, 1, 2, 2, 2, 3, 2, FWHT_FL_PIXENC_YUV},
+ { V4L2_PIX_FMT_NV21, 1, 3, 2, 1, 2, 2, 2, 3, 2, FWHT_FL_PIXENC_YUV},
+ { V4L2_PIX_FMT_NV16, 1, 2, 1, 1, 2, 2, 1, 3, 2, FWHT_FL_PIXENC_YUV},
+ { V4L2_PIX_FMT_NV61, 1, 2, 1, 1, 2, 2, 1, 3, 2, FWHT_FL_PIXENC_YUV},
+ { V4L2_PIX_FMT_NV24, 1, 3, 1, 1, 2, 1, 1, 3, 2, FWHT_FL_PIXENC_YUV},
+ { V4L2_PIX_FMT_NV42, 1, 3, 1, 1, 2, 1, 1, 3, 2, FWHT_FL_PIXENC_YUV},
+ { V4L2_PIX_FMT_YUYV, 2, 2, 1, 2, 4, 2, 1, 3, 1, FWHT_FL_PIXENC_YUV},
+ { V4L2_PIX_FMT_YVYU, 2, 2, 1, 2, 4, 2, 1, 3, 1, FWHT_FL_PIXENC_YUV},
+ { V4L2_PIX_FMT_UYVY, 2, 2, 1, 2, 4, 2, 1, 3, 1, FWHT_FL_PIXENC_YUV},
+ { V4L2_PIX_FMT_VYUY, 2, 2, 1, 2, 4, 2, 1, 3, 1, FWHT_FL_PIXENC_YUV},
+ { V4L2_PIX_FMT_BGR24, 3, 3, 1, 3, 3, 1, 1, 3, 1, FWHT_FL_PIXENC_RGB},
+ { V4L2_PIX_FMT_RGB24, 3, 3, 1, 3, 3, 1, 1, 3, 1, FWHT_FL_PIXENC_RGB},
+ { V4L2_PIX_FMT_HSV24, 3, 3, 1, 3, 3, 1, 1, 3, 1, FWHT_FL_PIXENC_HSV},
+ { V4L2_PIX_FMT_BGR32, 4, 4, 1, 4, 4, 1, 1, 3, 1, FWHT_FL_PIXENC_RGB},
+ { V4L2_PIX_FMT_XBGR32, 4, 4, 1, 4, 4, 1, 1, 3, 1, FWHT_FL_PIXENC_RGB},
+ { V4L2_PIX_FMT_RGB32, 4, 4, 1, 4, 4, 1, 1, 3, 1, FWHT_FL_PIXENC_RGB},
+ { V4L2_PIX_FMT_XRGB32, 4, 4, 1, 4, 4, 1, 1, 3, 1, FWHT_FL_PIXENC_RGB},
+ { V4L2_PIX_FMT_HSV32, 4, 4, 1, 4, 4, 1, 1, 3, 1, FWHT_FL_PIXENC_HSV},
+ { V4L2_PIX_FMT_ARGB32, 4, 4, 1, 4, 4, 1, 1, 4, 1, FWHT_FL_PIXENC_RGB},
+ { V4L2_PIX_FMT_ABGR32, 4, 4, 1, 4, 4, 1, 1, 4, 1, FWHT_FL_PIXENC_RGB},
+ { V4L2_PIX_FMT_GREY, 1, 1, 1, 1, 0, 1, 1, 1, 1, FWHT_FL_PIXENC_RGB},
};
+const struct v4l2_fwht_pixfmt_info *v4l2_fwht_default_fmt(u32 width_div,
+ u32 height_div,
+ u32 components_num,
+ u32 pixenc,
+ unsigned int start_idx)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(v4l2_fwht_pixfmts); i++) {
+ if (v4l2_fwht_pixfmts[i].width_div == width_div &&
+ v4l2_fwht_pixfmts[i].height_div == height_div &&
+ (!pixenc || v4l2_fwht_pixfmts[i].pixenc == pixenc) &&
+ v4l2_fwht_pixfmts[i].components_num == components_num) {
+ if (start_idx == 0)
+ return v4l2_fwht_pixfmts + i;
+ start_idx--;
+ }
+ }
+ return NULL;
+}
+
const struct v4l2_fwht_pixfmt_info *v4l2_fwht_find_pixfmt(u32 pixelformat)
{
unsigned int i;
@@ -56,7 +77,8 @@ const struct v4l2_fwht_pixfmt_info *v4l2_fwht_get_pixfmt(u32 idx)
int v4l2_fwht_encode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out)
{
- unsigned int size = state->width * state->height;
+ unsigned int size = state->stride * state->coded_height;
+ unsigned int chroma_stride = state->stride;
const struct v4l2_fwht_pixfmt_info *info = state->info;
struct fwht_cframe_hdr *p_hdr;
struct fwht_cframe cf;
@@ -66,8 +88,7 @@ int v4l2_fwht_encode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out)
if (!info)
return -EINVAL;
- rf.width = state->width;
- rf.height = state->height;
+
rf.luma = p_in;
rf.width_div = info->width_div;
rf.height_div = info->height_div;
@@ -84,14 +105,17 @@ int v4l2_fwht_encode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out)
case V4L2_PIX_FMT_YUV420:
rf.cb = rf.luma + size;
rf.cr = rf.cb + size / 4;
+ chroma_stride /= 2;
break;
case V4L2_PIX_FMT_YVU420:
rf.cr = rf.luma + size;
rf.cb = rf.cr + size / 4;
+ chroma_stride /= 2;
break;
case V4L2_PIX_FMT_YUV422P:
rf.cb = rf.luma + size;
rf.cr = rf.cb + size / 2;
+ chroma_stride /= 2;
break;
case V4L2_PIX_FMT_NV12:
case V4L2_PIX_FMT_NV16:
@@ -163,15 +187,16 @@ int v4l2_fwht_encode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out)
return -EINVAL;
}
- cf.width = state->width;
- cf.height = state->height;
cf.i_frame_qp = state->i_frame_qp;
cf.p_frame_qp = state->p_frame_qp;
cf.rlc_data = (__be16 *)(p_out + sizeof(*p_hdr));
encoding = fwht_encode_frame(&rf, &state->ref_frame, &cf,
!state->gop_cnt,
- state->gop_cnt == state->gop_size - 1);
+ state->gop_cnt == state->gop_size - 1,
+ state->visible_width,
+ state->visible_height,
+ state->stride, chroma_stride);
if (!(encoding & FWHT_FRAME_PCODED))
state->gop_cnt = 0;
if (++state->gop_cnt >= state->gop_size)
@@ -181,9 +206,10 @@ int v4l2_fwht_encode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out)
p_hdr->magic1 = FWHT_MAGIC1;
p_hdr->magic2 = FWHT_MAGIC2;
p_hdr->version = htonl(FWHT_VERSION);
- p_hdr->width = htonl(cf.width);
- p_hdr->height = htonl(cf.height);
+ p_hdr->width = htonl(state->visible_width);
+ p_hdr->height = htonl(state->visible_height);
flags |= (info->components_num - 1) << FWHT_FL_COMPONENTS_NUM_OFFSET;
+ flags |= info->pixenc;
if (encoding & FWHT_LUMA_UNENCODED)
flags |= FWHT_FL_LUMA_IS_UNCOMPRESSED;
if (encoding & FWHT_CB_UNENCODED)
@@ -192,6 +218,8 @@ int v4l2_fwht_encode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out)
flags |= FWHT_FL_CR_IS_UNCOMPRESSED;
if (encoding & FWHT_ALPHA_UNENCODED)
flags |= FWHT_FL_ALPHA_IS_UNCOMPRESSED;
+ if (!(encoding & FWHT_FRAME_PCODED))
+ flags |= FWHT_FL_I_FRAME;
if (rf.height_div == 1)
flags |= FWHT_FL_CHROMA_FULL_HEIGHT;
if (rf.width_div == 1)
@@ -202,65 +230,70 @@ int v4l2_fwht_encode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out)
p_hdr->ycbcr_enc = htonl(state->ycbcr_enc);
p_hdr->quantization = htonl(state->quantization);
p_hdr->size = htonl(cf.size);
- state->ref_frame.width = cf.width;
- state->ref_frame.height = cf.height;
return cf.size + sizeof(*p_hdr);
}
int v4l2_fwht_decode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out)
{
- unsigned int size = state->width * state->height;
- unsigned int chroma_size = size;
- unsigned int i;
+ unsigned int i, j, k;
u32 flags;
- struct fwht_cframe_hdr *p_hdr;
struct fwht_cframe cf;
- u8 *p;
+ u8 *p, *ref_p;
unsigned int components_num = 3;
unsigned int version;
+ const struct v4l2_fwht_pixfmt_info *info;
+ unsigned int hdr_width_div, hdr_height_div;
if (!state->info)
return -EINVAL;
- p_hdr = (struct fwht_cframe_hdr *)p_in;
- cf.width = ntohl(p_hdr->width);
- cf.height = ntohl(p_hdr->height);
+ info = state->info;
- version = ntohl(p_hdr->version);
+ version = ntohl(state->header.version);
if (!version || version > FWHT_VERSION) {
pr_err("version %d is not supported, current version is %d\n",
version, FWHT_VERSION);
return -EINVAL;
}
- if (p_hdr->magic1 != FWHT_MAGIC1 ||
- p_hdr->magic2 != FWHT_MAGIC2 ||
- (cf.width & 7) || (cf.height & 7))
+ if (state->header.magic1 != FWHT_MAGIC1 ||
+ state->header.magic2 != FWHT_MAGIC2)
return -EINVAL;
/* TODO: support resolution changes */
- if (cf.width != state->width || cf.height != state->height)
+ if (ntohl(state->header.width) != state->visible_width ||
+ ntohl(state->header.height) != state->visible_height)
return -EINVAL;
- flags = ntohl(p_hdr->flags);
+ flags = ntohl(state->header.flags);
- if (version == FWHT_VERSION) {
+ if (version >= 2) {
+ if ((flags & FWHT_FL_PIXENC_MSK) != info->pixenc)
+ return -EINVAL;
components_num = 1 + ((flags & FWHT_FL_COMPONENTS_NUM_MSK) >>
- FWHT_FL_COMPONENTS_NUM_OFFSET);
+ FWHT_FL_COMPONENTS_NUM_OFFSET);
}
- state->colorspace = ntohl(p_hdr->colorspace);
- state->xfer_func = ntohl(p_hdr->xfer_func);
- state->ycbcr_enc = ntohl(p_hdr->ycbcr_enc);
- state->quantization = ntohl(p_hdr->quantization);
- cf.rlc_data = (__be16 *)(p_in + sizeof(*p_hdr));
+ if (components_num != info->components_num)
+ return -EINVAL;
- if (!(flags & FWHT_FL_CHROMA_FULL_WIDTH))
- chroma_size /= 2;
- if (!(flags & FWHT_FL_CHROMA_FULL_HEIGHT))
- chroma_size /= 2;
+ state->colorspace = ntohl(state->header.colorspace);
+ state->xfer_func = ntohl(state->header.xfer_func);
+ state->ycbcr_enc = ntohl(state->header.ycbcr_enc);
+ state->quantization = ntohl(state->header.quantization);
+ cf.rlc_data = (__be16 *)p_in;
+ cf.size = ntohl(state->header.size);
- fwht_decode_frame(&cf, &state->ref_frame, flags, components_num);
+ hdr_width_div = (flags & FWHT_FL_CHROMA_FULL_WIDTH) ? 1 : 2;
+ hdr_height_div = (flags & FWHT_FL_CHROMA_FULL_HEIGHT) ? 1 : 2;
+ if (hdr_width_div != info->width_div ||
+ hdr_height_div != info->height_div)
+ return -EINVAL;
+
+ if (!fwht_decode_frame(&cf, &state->ref_frame, flags, components_num,
+ state->visible_width, state->visible_height,
+ state->coded_width))
+ return -EINVAL;
/*
* TODO - handle the case where the compressed stream encodes a
@@ -268,123 +301,226 @@ int v4l2_fwht_decode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out)
*/
switch (state->info->id) {
case V4L2_PIX_FMT_GREY:
- memcpy(p_out, state->ref_frame.luma, size);
+ ref_p = state->ref_frame.luma;
+ for (i = 0; i < state->coded_height; i++) {
+ memcpy(p_out, ref_p, state->visible_width);
+ p_out += state->stride;
+ ref_p += state->coded_width;
+ }
break;
case V4L2_PIX_FMT_YUV420:
case V4L2_PIX_FMT_YUV422P:
- memcpy(p_out, state->ref_frame.luma, size);
- p_out += size;
- memcpy(p_out, state->ref_frame.cb, chroma_size);
- p_out += chroma_size;
- memcpy(p_out, state->ref_frame.cr, chroma_size);
+ ref_p = state->ref_frame.luma;
+ for (i = 0; i < state->coded_height; i++) {
+ memcpy(p_out, ref_p, state->visible_width);
+ p_out += state->stride;
+ ref_p += state->coded_width;
+ }
+
+ ref_p = state->ref_frame.cb;
+ for (i = 0; i < state->coded_height / 2; i++) {
+ memcpy(p_out, ref_p, state->visible_width / 2);
+ p_out += state->stride / 2;
+ ref_p += state->coded_width / 2;
+ }
+ ref_p = state->ref_frame.cr;
+ for (i = 0; i < state->coded_height / 2; i++) {
+ memcpy(p_out, ref_p, state->visible_width / 2);
+ p_out += state->stride / 2;
+ ref_p += state->coded_width / 2;
+ }
break;
case V4L2_PIX_FMT_YVU420:
- memcpy(p_out, state->ref_frame.luma, size);
- p_out += size;
- memcpy(p_out, state->ref_frame.cr, chroma_size);
- p_out += chroma_size;
- memcpy(p_out, state->ref_frame.cb, chroma_size);
+ ref_p = state->ref_frame.luma;
+ for (i = 0; i < state->coded_height; i++) {
+ memcpy(p_out, ref_p, state->visible_width);
+ p_out += state->stride;
+ ref_p += state->coded_width;
+ }
+
+ ref_p = state->ref_frame.cr;
+ for (i = 0; i < state->coded_height / 2; i++) {
+ memcpy(p_out, ref_p, state->visible_width / 2);
+ p_out += state->stride / 2;
+ ref_p += state->coded_width / 2;
+ }
+ ref_p = state->ref_frame.cb;
+ for (i = 0; i < state->coded_height / 2; i++) {
+ memcpy(p_out, ref_p, state->visible_width / 2);
+ p_out += state->stride / 2;
+ ref_p += state->coded_width / 2;
+ }
break;
case V4L2_PIX_FMT_NV12:
case V4L2_PIX_FMT_NV16:
case V4L2_PIX_FMT_NV24:
- memcpy(p_out, state->ref_frame.luma, size);
- p_out += size;
- for (i = 0, p = p_out; i < chroma_size; i++) {
- *p++ = state->ref_frame.cb[i];
- *p++ = state->ref_frame.cr[i];
+ ref_p = state->ref_frame.luma;
+ for (i = 0; i < state->coded_height; i++) {
+ memcpy(p_out, ref_p, state->visible_width);
+ p_out += state->stride;
+ ref_p += state->coded_width;
+ }
+
+ k = 0;
+ for (i = 0; i < state->coded_height / 2; i++) {
+ for (j = 0, p = p_out; j < state->coded_width / 2; j++) {
+ *p++ = state->ref_frame.cb[k];
+ *p++ = state->ref_frame.cr[k];
+ k++;
+ }
+ p_out += state->stride;
}
break;
case V4L2_PIX_FMT_NV21:
case V4L2_PIX_FMT_NV61:
case V4L2_PIX_FMT_NV42:
- memcpy(p_out, state->ref_frame.luma, size);
- p_out += size;
- for (i = 0, p = p_out; i < chroma_size; i++) {
- *p++ = state->ref_frame.cr[i];
- *p++ = state->ref_frame.cb[i];
+ ref_p = state->ref_frame.luma;
+ for (i = 0; i < state->coded_height; i++) {
+ memcpy(p_out, ref_p, state->visible_width);
+ p_out += state->stride;
+ ref_p += state->coded_width;
+ }
+
+ k = 0;
+ for (i = 0; i < state->coded_height / 2; i++) {
+ for (j = 0, p = p_out; j < state->coded_width / 2; j++) {
+ *p++ = state->ref_frame.cr[k];
+ *p++ = state->ref_frame.cb[k];
+ k++;
+ }
+ p_out += state->stride;
}
break;
case V4L2_PIX_FMT_YUYV:
- for (i = 0, p = p_out; i < size; i += 2) {
- *p++ = state->ref_frame.luma[i];
- *p++ = state->ref_frame.cb[i / 2];
- *p++ = state->ref_frame.luma[i + 1];
- *p++ = state->ref_frame.cr[i / 2];
+ k = 0;
+ for (i = 0; i < state->coded_height; i++) {
+ for (j = 0, p = p_out; j < state->coded_width / 2; j++) {
+ *p++ = state->ref_frame.luma[k];
+ *p++ = state->ref_frame.cb[k / 2];
+ *p++ = state->ref_frame.luma[k + 1];
+ *p++ = state->ref_frame.cr[k / 2];
+ k += 2;
+ }
+ p_out += state->stride;
}
break;
case V4L2_PIX_FMT_YVYU:
- for (i = 0, p = p_out; i < size; i += 2) {
- *p++ = state->ref_frame.luma[i];
- *p++ = state->ref_frame.cr[i / 2];
- *p++ = state->ref_frame.luma[i + 1];
- *p++ = state->ref_frame.cb[i / 2];
+ k = 0;
+ for (i = 0; i < state->coded_height; i++) {
+ for (j = 0, p = p_out; j < state->coded_width / 2; j++) {
+ *p++ = state->ref_frame.luma[k];
+ *p++ = state->ref_frame.cr[k / 2];
+ *p++ = state->ref_frame.luma[k + 1];
+ *p++ = state->ref_frame.cb[k / 2];
+ k += 2;
+ }
+ p_out += state->stride;
}
break;
case V4L2_PIX_FMT_UYVY:
- for (i = 0, p = p_out; i < size; i += 2) {
- *p++ = state->ref_frame.cb[i / 2];
- *p++ = state->ref_frame.luma[i];
- *p++ = state->ref_frame.cr[i / 2];
- *p++ = state->ref_frame.luma[i + 1];
+ k = 0;
+ for (i = 0; i < state->coded_height; i++) {
+ for (j = 0, p = p_out; j < state->coded_width / 2; j++) {
+ *p++ = state->ref_frame.cb[k / 2];
+ *p++ = state->ref_frame.luma[k];
+ *p++ = state->ref_frame.cr[k / 2];
+ *p++ = state->ref_frame.luma[k + 1];
+ k += 2;
+ }
+ p_out += state->stride;
}
break;
case V4L2_PIX_FMT_VYUY:
- for (i = 0, p = p_out; i < size; i += 2) {
- *p++ = state->ref_frame.cr[i / 2];
- *p++ = state->ref_frame.luma[i];
- *p++ = state->ref_frame.cb[i / 2];
- *p++ = state->ref_frame.luma[i + 1];
+ k = 0;
+ for (i = 0; i < state->coded_height; i++) {
+ for (j = 0, p = p_out; j < state->coded_width / 2; j++) {
+ *p++ = state->ref_frame.cr[k / 2];
+ *p++ = state->ref_frame.luma[k];
+ *p++ = state->ref_frame.cb[k / 2];
+ *p++ = state->ref_frame.luma[k + 1];
+ k += 2;
+ }
+ p_out += state->stride;
}
break;
case V4L2_PIX_FMT_RGB24:
case V4L2_PIX_FMT_HSV24:
- for (i = 0, p = p_out; i < size; i++) {
- *p++ = state->ref_frame.cr[i];
- *p++ = state->ref_frame.luma[i];
- *p++ = state->ref_frame.cb[i];
+ k = 0;
+ for (i = 0; i < state->coded_height; i++) {
+ for (j = 0, p = p_out; j < state->coded_width; j++) {
+ *p++ = state->ref_frame.cr[k];
+ *p++ = state->ref_frame.luma[k];
+ *p++ = state->ref_frame.cb[k];
+ k++;
+ }
+ p_out += state->stride;
}
break;
case V4L2_PIX_FMT_BGR24:
- for (i = 0, p = p_out; i < size; i++) {
- *p++ = state->ref_frame.cb[i];
- *p++ = state->ref_frame.luma[i];
- *p++ = state->ref_frame.cr[i];
+ k = 0;
+ for (i = 0; i < state->coded_height; i++) {
+ for (j = 0, p = p_out; j < state->coded_width; j++) {
+ *p++ = state->ref_frame.cb[k];
+ *p++ = state->ref_frame.luma[k];
+ *p++ = state->ref_frame.cr[k];
+ k++;
+ }
+ p_out += state->stride;
}
break;
case V4L2_PIX_FMT_RGB32:
case V4L2_PIX_FMT_XRGB32:
case V4L2_PIX_FMT_HSV32:
- for (i = 0, p = p_out; i < size; i++) {
- *p++ = 0;
- *p++ = state->ref_frame.cr[i];
- *p++ = state->ref_frame.luma[i];
- *p++ = state->ref_frame.cb[i];
+ k = 0;
+ for (i = 0; i < state->coded_height; i++) {
+ for (j = 0, p = p_out; j < state->coded_width; j++) {
+ *p++ = 0;
+ *p++ = state->ref_frame.cr[k];
+ *p++ = state->ref_frame.luma[k];
+ *p++ = state->ref_frame.cb[k];
+ k++;
+ }
+ p_out += state->stride;
}
break;
case V4L2_PIX_FMT_BGR32:
case V4L2_PIX_FMT_XBGR32:
- for (i = 0, p = p_out; i < size; i++) {
- *p++ = state->ref_frame.cb[i];
- *p++ = state->ref_frame.luma[i];
- *p++ = state->ref_frame.cr[i];
- *p++ = 0;
+ k = 0;
+ for (i = 0; i < state->coded_height; i++) {
+ for (j = 0, p = p_out; j < state->coded_width; j++) {
+ *p++ = state->ref_frame.cb[k];
+ *p++ = state->ref_frame.luma[k];
+ *p++ = state->ref_frame.cr[k];
+ *p++ = 0;
+ k++;
+ }
+ p_out += state->stride;
}
break;
case V4L2_PIX_FMT_ARGB32:
- for (i = 0, p = p_out; i < size; i++) {
- *p++ = state->ref_frame.alpha[i];
- *p++ = state->ref_frame.cr[i];
- *p++ = state->ref_frame.luma[i];
- *p++ = state->ref_frame.cb[i];
+ k = 0;
+ for (i = 0; i < state->coded_height; i++) {
+ for (j = 0, p = p_out; j < state->coded_width; j++) {
+ *p++ = state->ref_frame.alpha[k];
+ *p++ = state->ref_frame.cr[k];
+ *p++ = state->ref_frame.luma[k];
+ *p++ = state->ref_frame.cb[k];
+ k++;
+ }
+ p_out += state->stride;
}
break;
case V4L2_PIX_FMT_ABGR32:
- for (i = 0, p = p_out; i < size; i++) {
- *p++ = state->ref_frame.cb[i];
- *p++ = state->ref_frame.luma[i];
- *p++ = state->ref_frame.cr[i];
- *p++ = state->ref_frame.alpha[i];
+ k = 0;
+ for (i = 0; i < state->coded_height; i++) {
+ for (j = 0, p = p_out; j < state->coded_width; j++) {
+ *p++ = state->ref_frame.cb[k];
+ *p++ = state->ref_frame.luma[k];
+ *p++ = state->ref_frame.cr[k];
+ *p++ = state->ref_frame.alpha[k];
+ k++;
+ }
+ p_out += state->stride;
}
break;
default:
diff --git a/drivers/media/platform/vicodec/codec-v4l2-fwht.h b/drivers/media/platform/vicodec/codec-v4l2-fwht.h
index ed53e28d4f9c..aa6fa90a48be 100644
--- a/drivers/media/platform/vicodec/codec-v4l2-fwht.h
+++ b/drivers/media/platform/vicodec/codec-v4l2-fwht.h
@@ -19,12 +19,17 @@ struct v4l2_fwht_pixfmt_info {
unsigned int width_div;
unsigned int height_div;
unsigned int components_num;
+ unsigned int planes_num;
+ unsigned int pixenc;
};
struct v4l2_fwht_state {
const struct v4l2_fwht_pixfmt_info *info;
- unsigned int width;
- unsigned int height;
+ unsigned int visible_width;
+ unsigned int visible_height;
+ unsigned int coded_width;
+ unsigned int coded_height;
+ unsigned int stride;
unsigned int gop_size;
unsigned int gop_cnt;
u16 i_frame_qp;
@@ -36,11 +41,17 @@ struct v4l2_fwht_state {
enum v4l2_quantization quantization;
struct fwht_raw_frame ref_frame;
+ struct fwht_cframe_hdr header;
u8 *compressed_frame;
};
const struct v4l2_fwht_pixfmt_info *v4l2_fwht_find_pixfmt(u32 pixelformat);
const struct v4l2_fwht_pixfmt_info *v4l2_fwht_get_pixfmt(u32 idx);
+const struct v4l2_fwht_pixfmt_info *v4l2_fwht_default_fmt(u32 width_div,
+ u32 height_div,
+ u32 components_num,
+ u32 pixenc,
+ unsigned int start_idx);
int v4l2_fwht_encode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out);
int v4l2_fwht_decode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out);
diff --git a/drivers/media/platform/vicodec/vicodec-core.c b/drivers/media/platform/vicodec/vicodec-core.c
index 0d7876f5acf0..d7636fe9e174 100644
--- a/drivers/media/platform/vicodec/vicodec-core.c
+++ b/drivers/media/platform/vicodec/vicodec-core.c
@@ -61,7 +61,7 @@ struct pixfmt_info {
};
static const struct v4l2_fwht_pixfmt_info pixfmt_fwht = {
- V4L2_PIX_FMT_FWHT, 0, 3, 1, 1, 1, 1, 1, 0
+ V4L2_PIX_FMT_FWHT, 0, 3, 1, 1, 1, 1, 1, 0, 1
};
static void vicodec_dev_release(struct device *dev)
@@ -75,8 +75,10 @@ static struct platform_device vicodec_pdev = {
/* Per-queue, driver-specific private data */
struct vicodec_q_data {
- unsigned int width;
- unsigned int height;
+ unsigned int coded_width;
+ unsigned int coded_height;
+ unsigned int visible_width;
+ unsigned int visible_height;
unsigned int sizeimage;
unsigned int sequence;
const struct v4l2_fwht_pixfmt_info *info;
@@ -122,10 +124,12 @@ struct vicodec_ctx {
u32 cur_buf_offset;
u32 comp_max_size;
u32 comp_size;
+ u32 header_size;
u32 comp_magic_cnt;
- u32 comp_frame_size;
bool comp_has_frame;
bool comp_has_next_frame;
+ bool first_source_change_sent;
+ bool source_changed;
};
static inline struct vicodec_ctx *file2ctx(struct file *file)
@@ -182,6 +186,10 @@ static int device_process(struct vicodec_ctx *ctx,
return ret;
vb2_set_plane_payload(&dst_vb->vb2_buf, 0, ret);
} else {
+ unsigned int comp_frame_size = ntohl(ctx->state.header.size);
+
+ if (comp_frame_size > ctx->comp_max_size)
+ return -EINVAL;
state->info = q_dst->info;
ret = v4l2_fwht_decode(state, p_src, p_dst);
if (ret < 0)
@@ -190,18 +198,8 @@ static int device_process(struct vicodec_ctx *ctx,
}
dst_vb->sequence = q_dst->sequence++;
- dst_vb->vb2_buf.timestamp = src_vb->vb2_buf.timestamp;
-
- if (src_vb->flags & V4L2_BUF_FLAG_TIMECODE)
- dst_vb->timecode = src_vb->timecode;
- dst_vb->field = src_vb->field;
dst_vb->flags &= ~V4L2_BUF_FLAG_LAST;
- dst_vb->flags |= src_vb->flags &
- (V4L2_BUF_FLAG_TIMECODE |
- V4L2_BUF_FLAG_KEYFRAME |
- V4L2_BUF_FLAG_PFRAME |
- V4L2_BUF_FLAG_BFRAME |
- V4L2_BUF_FLAG_TSTAMP_SRC_MASK);
+ v4l2_m2m_buf_copy_metadata(src_vb, dst_vb, !ctx->is_enc);
return 0;
}
@@ -209,6 +207,63 @@ static int device_process(struct vicodec_ctx *ctx,
/*
* mem2mem callbacks
*/
+static enum vb2_buffer_state get_next_header(struct vicodec_ctx *ctx,
+ u8 **pp, u32 sz)
+{
+ static const u8 magic[] = {
+ 0x4f, 0x4f, 0x4f, 0x4f, 0xff, 0xff, 0xff, 0xff
+ };
+ u8 *p = *pp;
+ u32 state;
+ u8 *header = (u8 *)&ctx->state.header;
+
+ state = VB2_BUF_STATE_DONE;
+
+ if (!ctx->header_size) {
+ state = VB2_BUF_STATE_ERROR;
+ for (; p < *pp + sz; p++) {
+ u32 copy;
+
+ p = memchr(p, magic[ctx->comp_magic_cnt],
+ *pp + sz - p);
+ if (!p) {
+ ctx->comp_magic_cnt = 0;
+ p = *pp + sz;
+ break;
+ }
+ copy = sizeof(magic) - ctx->comp_magic_cnt;
+ if (*pp + sz - p < copy)
+ copy = *pp + sz - p;
+
+ memcpy(header + ctx->comp_magic_cnt, p, copy);
+ ctx->comp_magic_cnt += copy;
+ if (!memcmp(header, magic, ctx->comp_magic_cnt)) {
+ p += copy;
+ state = VB2_BUF_STATE_DONE;
+ break;
+ }
+ ctx->comp_magic_cnt = 0;
+ }
+ if (ctx->comp_magic_cnt < sizeof(magic)) {
+ *pp = p;
+ return state;
+ }
+ ctx->header_size = sizeof(magic);
+ }
+
+ if (ctx->header_size < sizeof(struct fwht_cframe_hdr)) {
+ u32 copy = sizeof(struct fwht_cframe_hdr) - ctx->header_size;
+
+ if (*pp + sz - p < copy)
+ copy = *pp + sz - p;
+
+ memcpy(header + ctx->header_size, p, copy);
+ p += copy;
+ ctx->header_size += copy;
+ }
+ *pp = p;
+ return state;
+}
/* device_run() - prepares and starts the device */
static void device_run(void *priv)
@@ -249,6 +304,7 @@ static void device_run(void *priv)
}
v4l2_m2m_buf_done(dst_buf, state);
ctx->comp_size = 0;
+ ctx->header_size = 0;
ctx->comp_magic_cnt = 0;
ctx->comp_has_frame = false;
spin_unlock(ctx->lock);
@@ -273,6 +329,96 @@ static void job_remove_src_buf(struct vicodec_ctx *ctx, u32 state)
spin_unlock(ctx->lock);
}
+static const struct v4l2_fwht_pixfmt_info *
+info_from_header(const struct fwht_cframe_hdr *p_hdr)
+{
+ unsigned int flags = ntohl(p_hdr->flags);
+ unsigned int width_div = (flags & FWHT_FL_CHROMA_FULL_WIDTH) ? 1 : 2;
+ unsigned int height_div = (flags & FWHT_FL_CHROMA_FULL_HEIGHT) ? 1 : 2;
+ unsigned int components_num = 3;
+ unsigned int pixenc = 0;
+ unsigned int version = ntohl(p_hdr->version);
+
+ if (version >= 2) {
+ components_num = 1 + ((flags & FWHT_FL_COMPONENTS_NUM_MSK) >>
+ FWHT_FL_COMPONENTS_NUM_OFFSET);
+ pixenc = (flags & FWHT_FL_PIXENC_MSK);
+ }
+ return v4l2_fwht_default_fmt(width_div, height_div,
+ components_num, pixenc, 0);
+}
+
+static bool is_header_valid(const struct fwht_cframe_hdr *p_hdr)
+{
+ const struct v4l2_fwht_pixfmt_info *info;
+ unsigned int w = ntohl(p_hdr->width);
+ unsigned int h = ntohl(p_hdr->height);
+ unsigned int version = ntohl(p_hdr->version);
+ unsigned int flags = ntohl(p_hdr->flags);
+
+ if (!version || version > FWHT_VERSION)
+ return false;
+
+ if (w < MIN_WIDTH || w > MAX_WIDTH || h < MIN_HEIGHT || h > MAX_HEIGHT)
+ return false;
+
+ if (version >= 2) {
+ unsigned int components_num = 1 +
+ ((flags & FWHT_FL_COMPONENTS_NUM_MSK) >>
+ FWHT_FL_COMPONENTS_NUM_OFFSET);
+ unsigned int pixenc = flags & FWHT_FL_PIXENC_MSK;
+
+ if (components_num == 0 || components_num > 4 || !pixenc)
+ return false;
+ }
+
+ info = info_from_header(p_hdr);
+ if (!info)
+ return false;
+ return true;
+}
+
+static void update_capture_data_from_header(struct vicodec_ctx *ctx)
+{
+ struct vicodec_q_data *q_dst = get_q_data(ctx,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ const struct fwht_cframe_hdr *p_hdr = &ctx->state.header;
+ const struct v4l2_fwht_pixfmt_info *info = info_from_header(p_hdr);
+ unsigned int flags = ntohl(p_hdr->flags);
+ unsigned int hdr_width_div = (flags & FWHT_FL_CHROMA_FULL_WIDTH) ? 1 : 2;
+ unsigned int hdr_height_div = (flags & FWHT_FL_CHROMA_FULL_HEIGHT) ? 1 : 2;
+
+ q_dst->info = info;
+ q_dst->visible_width = ntohl(p_hdr->width);
+ q_dst->visible_height = ntohl(p_hdr->height);
+ q_dst->coded_width = vic_round_dim(q_dst->visible_width, hdr_width_div);
+ q_dst->coded_height = vic_round_dim(q_dst->visible_height,
+ hdr_height_div);
+
+ q_dst->sizeimage = q_dst->coded_width * q_dst->coded_height *
+ q_dst->info->sizeimage_mult / q_dst->info->sizeimage_div;
+ ctx->state.colorspace = ntohl(p_hdr->colorspace);
+
+ ctx->state.xfer_func = ntohl(p_hdr->xfer_func);
+ ctx->state.ycbcr_enc = ntohl(p_hdr->ycbcr_enc);
+ ctx->state.quantization = ntohl(p_hdr->quantization);
+}
+
+static void set_last_buffer(struct vb2_v4l2_buffer *dst_buf,
+ const struct vb2_v4l2_buffer *src_buf,
+ struct vicodec_ctx *ctx)
+{
+ struct vicodec_q_data *q_dst = get_q_data(ctx,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE);
+
+ 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);
+ dst_buf->flags |= V4L2_BUF_FLAG_LAST;
+ v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE);
+}
+
static int job_ready(void *priv)
{
static const u8 magic[] = {
@@ -284,7 +430,16 @@ static int job_ready(void *priv)
u8 *p;
u32 sz;
u32 state;
-
+ struct vicodec_q_data *q_dst = get_q_data(ctx,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ unsigned int flags;
+ unsigned int hdr_width_div;
+ unsigned int hdr_height_div;
+ unsigned int max_to_copy;
+ unsigned int comp_frame_size;
+
+ if (ctx->source_changed)
+ return 0;
if (ctx->is_enc || ctx->comp_has_frame)
return 1;
@@ -299,59 +454,27 @@ restart:
state = VB2_BUF_STATE_DONE;
- if (!ctx->comp_size) {
- state = VB2_BUF_STATE_ERROR;
- for (; p < p_src + sz; p++) {
- u32 copy;
-
- p = memchr(p, magic[ctx->comp_magic_cnt],
- p_src + sz - p);
- if (!p) {
- ctx->comp_magic_cnt = 0;
- break;
- }
- copy = sizeof(magic) - ctx->comp_magic_cnt;
- if (p_src + sz - p < copy)
- copy = p_src + sz - p;
-
- memcpy(ctx->state.compressed_frame + ctx->comp_magic_cnt,
- p, copy);
- ctx->comp_magic_cnt += copy;
- if (!memcmp(ctx->state.compressed_frame, magic,
- ctx->comp_magic_cnt)) {
- p += copy;
- state = VB2_BUF_STATE_DONE;
- break;
- }
- ctx->comp_magic_cnt = 0;
- }
- if (ctx->comp_magic_cnt < sizeof(magic)) {
+ if (ctx->header_size < sizeof(struct fwht_cframe_hdr)) {
+ state = get_next_header(ctx, &p, p_src + sz - p);
+ if (ctx->header_size < sizeof(struct fwht_cframe_hdr)) {
job_remove_src_buf(ctx, state);
goto restart;
}
- ctx->comp_size = sizeof(magic);
}
- if (ctx->comp_size < sizeof(struct fwht_cframe_hdr)) {
- struct fwht_cframe_hdr *p_hdr =
- (struct fwht_cframe_hdr *)ctx->state.compressed_frame;
- u32 copy = sizeof(struct fwht_cframe_hdr) - ctx->comp_size;
- if (copy > p_src + sz - p)
- copy = p_src + sz - p;
- memcpy(ctx->state.compressed_frame + ctx->comp_size,
- p, copy);
- p += copy;
- ctx->comp_size += copy;
- if (ctx->comp_size < sizeof(struct fwht_cframe_hdr)) {
- job_remove_src_buf(ctx, state);
- goto restart;
- }
- ctx->comp_frame_size = ntohl(p_hdr->size) + sizeof(*p_hdr);
- if (ctx->comp_frame_size > ctx->comp_max_size)
- ctx->comp_frame_size = ctx->comp_max_size;
- }
- if (ctx->comp_size < ctx->comp_frame_size) {
- u32 copy = ctx->comp_frame_size - ctx->comp_size;
+ comp_frame_size = ntohl(ctx->state.header.size);
+
+ /*
+ * The current scanned frame might be the first frame of a new
+ * resolution so its size might be larger than ctx->comp_max_size.
+ * In that case it is copied up to the current buffer capacity and
+ * the copy will continue after allocating new large enough buffer
+ * when restreaming
+ */
+ max_to_copy = min(comp_frame_size, ctx->comp_max_size);
+
+ if (ctx->comp_size < max_to_copy) {
+ u32 copy = max_to_copy - ctx->comp_size;
if (copy > p_src + sz - p)
copy = p_src + sz - p;
@@ -360,15 +483,17 @@ restart:
p, copy);
p += copy;
ctx->comp_size += copy;
- if (ctx->comp_size < ctx->comp_frame_size) {
+ if (ctx->comp_size < max_to_copy) {
job_remove_src_buf(ctx, state);
goto restart;
}
}
ctx->cur_buf_offset = p - p_src;
- ctx->comp_has_frame = true;
+ if (ctx->comp_size == comp_frame_size)
+ ctx->comp_has_frame = true;
ctx->comp_has_next_frame = false;
- if (sz - ctx->cur_buf_offset >= sizeof(struct fwht_cframe_hdr)) {
+ if (ctx->comp_has_frame && sz - ctx->cur_buf_offset >=
+ sizeof(struct fwht_cframe_hdr)) {
struct fwht_cframe_hdr *p_hdr = (struct fwht_cframe_hdr *)p;
u32 frame_size = ntohl(p_hdr->size);
u32 remaining = sz - ctx->cur_buf_offset - sizeof(*p_hdr);
@@ -376,6 +501,36 @@ restart:
if (!memcmp(p, magic, sizeof(magic)))
ctx->comp_has_next_frame = remaining >= frame_size;
}
+ /*
+ * if the header is invalid the device_run will just drop the frame
+ * with an error
+ */
+ if (!is_header_valid(&ctx->state.header) && ctx->comp_has_frame)
+ return 1;
+ flags = ntohl(ctx->state.header.flags);
+ hdr_width_div = (flags & FWHT_FL_CHROMA_FULL_WIDTH) ? 1 : 2;
+ hdr_height_div = (flags & FWHT_FL_CHROMA_FULL_HEIGHT) ? 1 : 2;
+
+ if (ntohl(ctx->state.header.width) != q_dst->visible_width ||
+ ntohl(ctx->state.header.height) != q_dst->visible_height ||
+ !q_dst->info ||
+ hdr_width_div != q_dst->info->width_div ||
+ hdr_height_div != q_dst->info->height_div) {
+ static const struct v4l2_event rs_event = {
+ .type = V4L2_EVENT_SOURCE_CHANGE,
+ .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION,
+ };
+
+ struct vb2_v4l2_buffer *dst_buf =
+ v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
+
+ update_capture_data_from_header(ctx);
+ ctx->first_source_change_sent = true;
+ v4l2_event_queue_fh(&ctx->fh, &rs_event);
+ set_last_buffer(dst_buf, src_buf, ctx);
+ ctx->source_changed = true;
+ return 0;
+ }
return 1;
}
@@ -403,9 +558,10 @@ static int vidioc_querycap(struct file *file, void *priv,
return 0;
}
-static int enum_fmt(struct v4l2_fmtdesc *f, bool is_enc, bool is_out)
+static int enum_fmt(struct v4l2_fmtdesc *f, struct vicodec_ctx *ctx,
+ bool is_out)
{
- bool is_uncomp = (is_enc && is_out) || (!is_enc && !is_out);
+ bool is_uncomp = (ctx->is_enc && is_out) || (!ctx->is_enc && !is_out);
if (V4L2_TYPE_IS_MULTIPLANAR(f->type) && !multiplanar)
return -EINVAL;
@@ -414,8 +570,16 @@ static int enum_fmt(struct v4l2_fmtdesc *f, bool is_enc, bool is_out)
if (is_uncomp) {
const struct v4l2_fwht_pixfmt_info *info =
- v4l2_fwht_get_pixfmt(f->index);
+ get_q_data(ctx, f->type)->info;
+ if (!info || ctx->is_enc)
+ info = v4l2_fwht_get_pixfmt(f->index);
+ else
+ info = v4l2_fwht_default_fmt(info->width_div,
+ info->height_div,
+ info->components_num,
+ info->pixenc,
+ f->index);
if (!info)
return -EINVAL;
f->pixelformat = info->id;
@@ -432,7 +596,7 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
{
struct vicodec_ctx *ctx = file2ctx(file);
- return enum_fmt(f, ctx->is_enc, false);
+ return enum_fmt(f, ctx, false);
}
static int vidioc_enum_fmt_vid_out(struct file *file, void *priv,
@@ -440,7 +604,7 @@ static int vidioc_enum_fmt_vid_out(struct file *file, void *priv,
{
struct vicodec_ctx *ctx = file2ctx(file);
- return enum_fmt(f, ctx->is_enc, true);
+ return enum_fmt(f, ctx, true);
}
static int vidioc_g_fmt(struct vicodec_ctx *ctx, struct v4l2_format *f)
@@ -458,17 +622,21 @@ static int vidioc_g_fmt(struct vicodec_ctx *ctx, struct v4l2_format *f)
q_data = get_q_data(ctx, f->type);
info = q_data->info;
+ if (!info)
+ info = v4l2_fwht_get_pixfmt(0);
+
switch (f->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
if (multiplanar)
return -EINVAL;
pix = &f->fmt.pix;
- pix->width = q_data->width;
- pix->height = q_data->height;
+ pix->width = q_data->coded_width;
+ pix->height = q_data->coded_height;
pix->field = V4L2_FIELD_NONE;
pix->pixelformat = info->id;
- pix->bytesperline = q_data->width * info->bytesperline_mult;
+ pix->bytesperline = q_data->coded_width *
+ info->bytesperline_mult;
pix->sizeimage = q_data->sizeimage;
pix->colorspace = ctx->state.colorspace;
pix->xfer_func = ctx->state.xfer_func;
@@ -481,13 +649,13 @@ static int vidioc_g_fmt(struct vicodec_ctx *ctx, struct v4l2_format *f)
if (!multiplanar)
return -EINVAL;
pix_mp = &f->fmt.pix_mp;
- pix_mp->width = q_data->width;
- pix_mp->height = q_data->height;
+ pix_mp->width = q_data->coded_width;
+ pix_mp->height = q_data->coded_height;
pix_mp->field = V4L2_FIELD_NONE;
pix_mp->pixelformat = info->id;
pix_mp->num_planes = 1;
pix_mp->plane_fmt[0].bytesperline =
- q_data->width * info->bytesperline_mult;
+ q_data->coded_width * info->bytesperline_mult;
pix_mp->plane_fmt[0].sizeimage = q_data->sizeimage;
pix_mp->colorspace = ctx->state.colorspace;
pix_mp->xfer_func = ctx->state.xfer_func;
@@ -528,8 +696,13 @@ static int vidioc_try_fmt(struct vicodec_ctx *ctx, struct v4l2_format *f)
pix = &f->fmt.pix;
if (pix->pixelformat != V4L2_PIX_FMT_FWHT)
info = find_fmt(pix->pixelformat);
- pix->width = clamp(pix->width, MIN_WIDTH, MAX_WIDTH) & ~7;
- pix->height = clamp(pix->height, MIN_HEIGHT, MAX_HEIGHT) & ~7;
+
+ pix->width = clamp(pix->width, MIN_WIDTH, MAX_WIDTH);
+ pix->width = vic_round_dim(pix->width, info->width_div);
+
+ pix->height = clamp(pix->height, MIN_HEIGHT, MAX_HEIGHT);
+ pix->height = vic_round_dim(pix->height, info->height_div);
+
pix->field = V4L2_FIELD_NONE;
pix->bytesperline =
pix->width * info->bytesperline_mult;
@@ -545,9 +718,14 @@ static int vidioc_try_fmt(struct vicodec_ctx *ctx, struct v4l2_format *f)
if (pix_mp->pixelformat != V4L2_PIX_FMT_FWHT)
info = find_fmt(pix_mp->pixelformat);
pix_mp->num_planes = 1;
- pix_mp->width = clamp(pix_mp->width, MIN_WIDTH, MAX_WIDTH) & ~7;
- pix_mp->height =
- clamp(pix_mp->height, MIN_HEIGHT, MAX_HEIGHT) & ~7;
+
+ pix_mp->width = clamp(pix_mp->width, MIN_WIDTH, MAX_WIDTH);
+ pix_mp->width = vic_round_dim(pix_mp->width, info->width_div);
+
+ pix_mp->height = clamp(pix_mp->height, MIN_HEIGHT, MAX_HEIGHT);
+ pix_mp->height = vic_round_dim(pix_mp->height,
+ info->height_div);
+
pix_mp->field = V4L2_FIELD_NONE;
plane->bytesperline =
pix_mp->width * info->bytesperline_mult;
@@ -657,9 +835,10 @@ static int vidioc_s_fmt(struct vicodec_ctx *ctx, struct v4l2_format *f)
pix = &f->fmt.pix;
if (ctx->is_enc && V4L2_TYPE_IS_OUTPUT(f->type))
fmt_changed =
+ !q_data->info ||
q_data->info->id != pix->pixelformat ||
- q_data->width != pix->width ||
- q_data->height != pix->height;
+ q_data->coded_width != pix->width ||
+ q_data->coded_height != pix->height;
if (vb2_is_busy(vq) && fmt_changed)
return -EBUSY;
@@ -668,8 +847,8 @@ static int vidioc_s_fmt(struct vicodec_ctx *ctx, struct v4l2_format *f)
q_data->info = &pixfmt_fwht;
else
q_data->info = find_fmt(pix->pixelformat);
- q_data->width = pix->width;
- q_data->height = pix->height;
+ q_data->coded_width = pix->width;
+ q_data->coded_height = pix->height;
q_data->sizeimage = pix->sizeimage;
break;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
@@ -677,9 +856,10 @@ static int vidioc_s_fmt(struct vicodec_ctx *ctx, struct v4l2_format *f)
pix_mp = &f->fmt.pix_mp;
if (ctx->is_enc && V4L2_TYPE_IS_OUTPUT(f->type))
fmt_changed =
+ !q_data->info ||
q_data->info->id != pix_mp->pixelformat ||
- q_data->width != pix_mp->width ||
- q_data->height != pix_mp->height;
+ q_data->coded_width != pix_mp->width ||
+ q_data->coded_height != pix_mp->height;
if (vb2_is_busy(vq) && fmt_changed)
return -EBUSY;
@@ -688,17 +868,24 @@ static int vidioc_s_fmt(struct vicodec_ctx *ctx, struct v4l2_format *f)
q_data->info = &pixfmt_fwht;
else
q_data->info = find_fmt(pix_mp->pixelformat);
- q_data->width = pix_mp->width;
- q_data->height = pix_mp->height;
+ q_data->coded_width = pix_mp->width;
+ q_data->coded_height = pix_mp->height;
q_data->sizeimage = pix_mp->plane_fmt[0].sizeimage;
break;
default:
return -EINVAL;
}
+ if (q_data->visible_width > q_data->coded_width)
+ q_data->visible_width = q_data->coded_width;
+ if (q_data->visible_height > q_data->coded_height)
+ q_data->visible_height = q_data->coded_height;
+
dprintk(ctx->dev,
- "Setting format for type %d, wxh: %dx%d, fourcc: %08x\n",
- f->type, q_data->width, q_data->height, q_data->info->id);
+ "Setting format for type %d, coded wxh: %dx%d, visible wxh: %dx%d, fourcc: %08x\n",
+ f->type, q_data->coded_width, q_data->coded_height,
+ q_data->visible_width, q_data->visible_height,
+ q_data->info->id);
return 0;
}
@@ -753,6 +940,84 @@ static int vidioc_s_fmt_vid_out(struct file *file, void *priv,
return ret;
}
+static int vidioc_g_selection(struct file *file, void *priv,
+ struct v4l2_selection *s)
+{
+ struct vicodec_ctx *ctx = file2ctx(file);
+ struct vicodec_q_data *q_data;
+ enum v4l2_buf_type valid_cap_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ enum v4l2_buf_type valid_out_type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+
+ if (multiplanar) {
+ valid_cap_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ valid_out_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ }
+
+ if (s->type != valid_cap_type && s->type != valid_out_type)
+ return -EINVAL;
+
+ q_data = get_q_data(ctx, s->type);
+ if (!q_data)
+ return -EINVAL;
+ /*
+ * encoder supports only cropping on the OUTPUT buffer
+ * decoder supports only composing on the CAPTURE buffer
+ */
+ if ((ctx->is_enc && s->type == valid_out_type) ||
+ (!ctx->is_enc && s->type == valid_cap_type)) {
+ switch (s->target) {
+ case V4L2_SEL_TGT_COMPOSE:
+ case V4L2_SEL_TGT_CROP:
+ s->r.left = 0;
+ s->r.top = 0;
+ s->r.width = q_data->visible_width;
+ s->r.height = q_data->visible_height;
+ return 0;
+ case V4L2_SEL_TGT_COMPOSE_DEFAULT:
+ case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ s->r.left = 0;
+ s->r.top = 0;
+ s->r.width = q_data->coded_width;
+ s->r.height = q_data->coded_height;
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+static int vidioc_s_selection(struct file *file, void *priv,
+ struct v4l2_selection *s)
+{
+ struct vicodec_ctx *ctx = file2ctx(file);
+ struct vicodec_q_data *q_data;
+ enum v4l2_buf_type out_type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+
+ if (multiplanar)
+ out_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+
+ if (s->type != out_type)
+ return -EINVAL;
+
+ q_data = get_q_data(ctx, s->type);
+ if (!q_data)
+ return -EINVAL;
+
+ if (!ctx->is_enc || s->target != V4L2_SEL_TGT_CROP)
+ return -EINVAL;
+
+ s->r.left = 0;
+ s->r.top = 0;
+ q_data->visible_width = clamp(s->r.width, MIN_WIDTH,
+ q_data->coded_width);
+ s->r.width = q_data->visible_width;
+ q_data->visible_height = clamp(s->r.height, MIN_HEIGHT,
+ q_data->coded_height);
+ s->r.height = q_data->visible_height;
+ return 0;
+}
+
static void vicodec_mark_last_buf(struct vicodec_ctx *ctx)
{
static const struct v4l2_event eos_event = {
@@ -853,7 +1118,13 @@ static int vicodec_enum_framesizes(struct file *file, void *fh,
static int vicodec_subscribe_event(struct v4l2_fh *fh,
const struct v4l2_event_subscription *sub)
{
+ struct vicodec_ctx *ctx = container_of(fh, struct vicodec_ctx, fh);
+
switch (sub->type) {
+ case V4L2_EVENT_SOURCE_CHANGE:
+ if (ctx->is_enc)
+ return -EINVAL;
+ /* fall through */
case V4L2_EVENT_EOS:
return v4l2_event_subscribe(fh, sub, 0, NULL);
default:
@@ -895,6 +1166,9 @@ static const struct v4l2_ioctl_ops vicodec_ioctl_ops = {
.vidioc_streamon = v4l2_m2m_ioctl_streamon,
.vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
+ .vidioc_g_selection = vidioc_g_selection,
+ .vidioc_s_selection = vidioc_s_selection,
+
.vidioc_try_encoder_cmd = vicodec_try_encoder_cmd,
.vidioc_encoder_cmd = vicodec_encoder_cmd,
.vidioc_try_decoder_cmd = vicodec_try_decoder_cmd,
@@ -960,7 +1234,71 @@ static void vicodec_buf_queue(struct vb2_buffer *vb)
{
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
struct vicodec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ unsigned int sz = vb2_get_plane_payload(&vbuf->vb2_buf, 0);
+ u8 *p_src = vb2_plane_vaddr(&vbuf->vb2_buf, 0);
+ u8 *p = p_src;
+ struct vb2_queue *vq_out = v4l2_m2m_get_vq(ctx->fh.m2m_ctx,
+ V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ struct vb2_queue *vq_cap = v4l2_m2m_get_vq(ctx->fh.m2m_ctx,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ bool header_valid = false;
+ static const struct v4l2_event rs_event = {
+ .type = V4L2_EVENT_SOURCE_CHANGE,
+ .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION,
+ };
+ /* buf_queue handles only the first source change event */
+ if (ctx->first_source_change_sent) {
+ v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
+ return;
+ }
+
+ /*
+ * if both queues are streaming, the source change event is
+ * handled in job_ready
+ */
+ if (vb2_is_streaming(vq_cap) && vb2_is_streaming(vq_out)) {
+ v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
+ return;
+ }
+
+ /*
+ * source change event is relevant only for the decoder
+ * in the compressed stream
+ */
+ if (ctx->is_enc || !V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) {
+ v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
+ return;
+ }
+
+ do {
+ enum vb2_buffer_state state =
+ get_next_header(ctx, &p, p_src + sz - p);
+
+ if (ctx->header_size < sizeof(struct fwht_cframe_hdr)) {
+ v4l2_m2m_buf_done(vbuf, state);
+ return;
+ }
+ header_valid = is_header_valid(&ctx->state.header);
+ /*
+ * p points right after the end of the header in the
+ * buffer. If the header is invalid we set p to point
+ * to the next byte after the start of the header
+ */
+ if (!header_valid) {
+ p = p - sizeof(struct fwht_cframe_hdr) + 1;
+ if (p < p_src)
+ p = p_src;
+ ctx->header_size = 0;
+ ctx->comp_magic_cnt = 0;
+ }
+
+ } while (!header_valid);
+
+ ctx->cur_buf_offset = p - p_src;
+ update_capture_data_from_header(ctx);
+ ctx->first_source_change_sent = true;
+ v4l2_event_queue_fh(&ctx->fh, &rs_event);
v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
}
@@ -988,47 +1326,70 @@ static int vicodec_start_streaming(struct vb2_queue *q,
struct vicodec_ctx *ctx = vb2_get_drv_priv(q);
struct vicodec_q_data *q_data = get_q_data(ctx, q->type);
struct v4l2_fwht_state *state = &ctx->state;
- unsigned int size = q_data->width * q_data->height;
const struct v4l2_fwht_pixfmt_info *info = q_data->info;
- unsigned int chroma_div = info->width_div * info->height_div;
+ unsigned int size = q_data->coded_width * q_data->coded_height;
+ unsigned int chroma_div;
unsigned int total_planes_size;
+ u8 *new_comp_frame;
- /*
- * we don't know ahead how many components are in the encoding type
- * V4L2_PIX_FMT_FWHT, so we will allocate space for 4 planes.
- */
- if (info->id == V4L2_PIX_FMT_FWHT || info->components_num == 4)
+ if (!info)
+ return -EINVAL;
+
+ chroma_div = info->width_div * info->height_div;
+ q_data->sequence = 0;
+
+ ctx->last_src_buf = NULL;
+ ctx->last_dst_buf = NULL;
+ state->gop_cnt = 0;
+
+ if ((V4L2_TYPE_IS_OUTPUT(q->type) && !ctx->is_enc) ||
+ (!V4L2_TYPE_IS_OUTPUT(q->type) && ctx->is_enc))
+ return 0;
+
+ if (info->id == V4L2_PIX_FMT_FWHT) {
+ vicodec_return_bufs(q, VB2_BUF_STATE_QUEUED);
+ return -EINVAL;
+ }
+ if (info->components_num == 4)
total_planes_size = 2 * size + 2 * (size / chroma_div);
else if (info->components_num == 3)
total_planes_size = size + 2 * (size / chroma_div);
else
total_planes_size = size;
- q_data->sequence = 0;
+ state->visible_width = q_data->visible_width;
+ state->visible_height = q_data->visible_height;
+ state->coded_width = q_data->coded_width;
+ state->coded_height = q_data->coded_height;
+ state->stride = q_data->coded_width *
+ info->bytesperline_mult;
- if (!V4L2_TYPE_IS_OUTPUT(q->type)) {
- if (!ctx->is_enc) {
- state->width = q_data->width;
- state->height = q_data->height;
- }
- return 0;
- }
-
- if (ctx->is_enc) {
- state->width = q_data->width;
- state->height = q_data->height;
- }
- state->ref_frame.width = state->ref_frame.height = 0;
state->ref_frame.luma = kvmalloc(total_planes_size, GFP_KERNEL);
- ctx->comp_max_size = total_planes_size + sizeof(struct fwht_cframe_hdr);
- state->compressed_frame = kvmalloc(ctx->comp_max_size, GFP_KERNEL);
- if (!state->ref_frame.luma || !state->compressed_frame) {
+ ctx->comp_max_size = total_planes_size;
+ new_comp_frame = kvmalloc(ctx->comp_max_size, GFP_KERNEL);
+
+ if (!state->ref_frame.luma || !new_comp_frame) {
kvfree(state->ref_frame.luma);
- kvfree(state->compressed_frame);
+ kvfree(new_comp_frame);
vicodec_return_bufs(q, VB2_BUF_STATE_QUEUED);
return -ENOMEM;
}
- if (info->id == V4L2_PIX_FMT_FWHT || info->components_num >= 3) {
+ /*
+ * if state->compressed_frame was already allocated then
+ * it contain data of the first frame of the new resolution
+ */
+ if (state->compressed_frame) {
+ if (ctx->comp_size > ctx->comp_max_size)
+ ctx->comp_size = ctx->comp_max_size;
+
+ memcpy(new_comp_frame,
+ state->compressed_frame, ctx->comp_size);
+ }
+
+ kvfree(state->compressed_frame);
+ state->compressed_frame = new_comp_frame;
+
+ if (info->components_num >= 3) {
state->ref_frame.cb = state->ref_frame.luma + size;
state->ref_frame.cr = state->ref_frame.cb + size / chroma_div;
} else {
@@ -1036,20 +1397,11 @@ static int vicodec_start_streaming(struct vb2_queue *q,
state->ref_frame.cr = NULL;
}
- if (info->id == V4L2_PIX_FMT_FWHT || info->components_num == 4)
+ if (info->components_num == 4)
state->ref_frame.alpha =
state->ref_frame.cr + size / chroma_div;
else
state->ref_frame.alpha = NULL;
-
- ctx->last_src_buf = NULL;
- ctx->last_dst_buf = NULL;
- state->gop_cnt = 0;
- ctx->cur_buf_offset = 0;
- ctx->comp_size = 0;
- ctx->comp_magic_cnt = 0;
- ctx->comp_has_frame = false;
-
return 0;
}
@@ -1059,11 +1411,20 @@ static void vicodec_stop_streaming(struct vb2_queue *q)
vicodec_return_bufs(q, VB2_BUF_STATE_ERROR);
- if (!V4L2_TYPE_IS_OUTPUT(q->type))
- return;
-
- kvfree(ctx->state.ref_frame.luma);
- kvfree(ctx->state.compressed_frame);
+ if ((!V4L2_TYPE_IS_OUTPUT(q->type) && !ctx->is_enc) ||
+ (V4L2_TYPE_IS_OUTPUT(q->type) && ctx->is_enc)) {
+ kvfree(ctx->state.ref_frame.luma);
+ ctx->comp_max_size = 0;
+ ctx->source_changed = false;
+ }
+ if (V4L2_TYPE_IS_OUTPUT(q->type) && !ctx->is_enc) {
+ ctx->cur_buf_offset = 0;
+ ctx->comp_size = 0;
+ ctx->header_size = 0;
+ ctx->comp_magic_cnt = 0;
+ ctx->comp_has_frame = 0;
+ ctx->comp_has_next_frame = 0;
+ }
}
static const struct vb2_ops vicodec_qops = {
@@ -1204,8 +1565,10 @@ static int vicodec_open(struct file *file)
ctx->q_data[V4L2_M2M_SRC].info =
ctx->is_enc ? v4l2_fwht_get_pixfmt(0) : &pixfmt_fwht;
- ctx->q_data[V4L2_M2M_SRC].width = 1280;
- ctx->q_data[V4L2_M2M_SRC].height = 720;
+ ctx->q_data[V4L2_M2M_SRC].coded_width = 1280;
+ ctx->q_data[V4L2_M2M_SRC].coded_height = 720;
+ ctx->q_data[V4L2_M2M_SRC].visible_width = 1280;
+ ctx->q_data[V4L2_M2M_SRC].visible_height = 720;
size = 1280 * 720 * ctx->q_data[V4L2_M2M_SRC].info->sizeimage_mult /
ctx->q_data[V4L2_M2M_SRC].info->sizeimage_div;
if (ctx->is_enc)
@@ -1213,16 +1576,17 @@ static int vicodec_open(struct file *file)
else
ctx->q_data[V4L2_M2M_SRC].sizeimage =
size + sizeof(struct fwht_cframe_hdr);
- ctx->q_data[V4L2_M2M_DST] = ctx->q_data[V4L2_M2M_SRC];
- ctx->q_data[V4L2_M2M_DST].info =
- ctx->is_enc ? &pixfmt_fwht : v4l2_fwht_get_pixfmt(0);
- size = 1280 * 720 * ctx->q_data[V4L2_M2M_DST].info->sizeimage_mult /
- ctx->q_data[V4L2_M2M_DST].info->sizeimage_div;
- if (ctx->is_enc)
- ctx->q_data[V4L2_M2M_DST].sizeimage =
- size + sizeof(struct fwht_cframe_hdr);
- else
- ctx->q_data[V4L2_M2M_DST].sizeimage = size;
+ if (ctx->is_enc) {
+ ctx->q_data[V4L2_M2M_DST] = ctx->q_data[V4L2_M2M_SRC];
+ ctx->q_data[V4L2_M2M_DST].info = &pixfmt_fwht;
+ ctx->q_data[V4L2_M2M_DST].sizeimage = 1280 * 720 *
+ ctx->q_data[V4L2_M2M_DST].info->sizeimage_mult /
+ ctx->q_data[V4L2_M2M_DST].info->sizeimage_div +
+ sizeof(struct fwht_cframe_hdr);
+ } else {
+ ctx->q_data[V4L2_M2M_DST].info = NULL;
+ }
+
ctx->state.colorspace = V4L2_COLORSPACE_REC709;
if (ctx->is_enc) {
@@ -1310,6 +1674,8 @@ static int vicodec_probe(struct platform_device *pdev)
#ifdef CONFIG_MEDIA_CONTROLLER
dev->mdev.dev = &pdev->dev;
strscpy(dev->mdev.model, "vicodec", sizeof(dev->mdev.model));
+ strscpy(dev->mdev.bus_info, "platform:vicodec",
+ sizeof(dev->mdev.bus_info));
media_device_init(&dev->mdev);
dev->v4l2_dev.mdev = &dev->mdev;
#endif
diff --git a/drivers/media/platform/video-mux.c b/drivers/media/platform/video-mux.c
index c33900e3c23e..0ba30756e1e4 100644
--- a/drivers/media/platform/video-mux.c
+++ b/drivers/media/platform/video-mux.c
@@ -263,6 +263,26 @@ static int video_mux_set_format(struct v4l2_subdev *sd,
case MEDIA_BUS_FMT_UYYVYY16_0_5X48:
case MEDIA_BUS_FMT_JPEG_1X8:
case MEDIA_BUS_FMT_AHSV8888_1X32:
+ 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_SBGGR14_1X14:
+ case MEDIA_BUS_FMT_SGBRG14_1X14:
+ case MEDIA_BUS_FMT_SGRBG14_1X14:
+ case MEDIA_BUS_FMT_SRGGB14_1X14:
+ case MEDIA_BUS_FMT_SBGGR16_1X16:
+ case MEDIA_BUS_FMT_SGBRG16_1X16:
+ case MEDIA_BUS_FMT_SGRBG16_1X16:
+ case MEDIA_BUS_FMT_SRGGB16_1X16:
break;
default:
sdformat->format.code = MEDIA_BUS_FMT_Y8_1X8;
diff --git a/drivers/media/platform/vim2m.c b/drivers/media/platform/vim2m.c
index 89d9c4c21037..34dcaca45d8b 100644
--- a/drivers/media/platform/vim2m.c
+++ b/drivers/media/platform/vim2m.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* A virtual v4l2-mem2mem example device.
*
@@ -34,22 +35,34 @@
MODULE_DESCRIPTION("Virtual device for mem2mem framework testing");
MODULE_AUTHOR("Pawel Osciak, <pawel@osciak.com>");
MODULE_LICENSE("GPL");
-MODULE_VERSION("0.1.1");
+MODULE_VERSION("0.2");
MODULE_ALIAS("mem2mem_testdev");
-static unsigned debug;
+static unsigned int debug;
module_param(debug, uint, 0644);
-MODULE_PARM_DESC(debug, "activates debug info");
+MODULE_PARM_DESC(debug, "debug level");
+
+/* Default transaction time in msec */
+static unsigned int default_transtime = 40; /* Max 25 fps */
+module_param(default_transtime, uint, 0644);
+MODULE_PARM_DESC(default_transtime, "default transaction time in ms");
#define MIN_W 32
#define MIN_H 32
#define MAX_W 640
#define MAX_H 480
-#define DIM_ALIGN_MASK 7 /* 8-byte alignment for line length */
+
+/* Pixel alignment for non-bayer formats */
+#define WIDTH_ALIGN 2
+#define HEIGHT_ALIGN 1
+
+/* Pixel alignment for bayer formats */
+#define BAYER_WIDTH_ALIGN 2
+#define BAYER_HEIGHT_ALIGN 2
/* Flags that indicate a format can be used for capture/output */
-#define MEM2MEM_CAPTURE (1 << 0)
-#define MEM2MEM_OUTPUT (1 << 1)
+#define MEM2MEM_CAPTURE BIT(0)
+#define MEM2MEM_OUTPUT BIT(1)
#define MEM2MEM_NAME "vim2m"
@@ -58,18 +71,12 @@ MODULE_PARM_DESC(debug, "activates debug info");
/* In bytes, per queue */
#define MEM2MEM_VID_MEM_LIMIT (16 * 1024 * 1024)
-/* Default transaction time in msec */
-#define MEM2MEM_DEF_TRANSTIME 40
-#define MEM2MEM_COLOR_STEP (0xff >> 4)
-#define MEM2MEM_NUM_TILES 8
-
/* Flags that indicate processing mode */
-#define MEM2MEM_HFLIP (1 << 0)
-#define MEM2MEM_VFLIP (1 << 1)
-
-#define dprintk(dev, fmt, arg...) \
- v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: " fmt, __func__, ## arg)
+#define MEM2MEM_HFLIP BIT(0)
+#define MEM2MEM_VFLIP BIT(1)
+#define dprintk(dev, lvl, fmt, arg...) \
+ v4l2_dbg(lvl, debug, &(dev)->v4l2_dev, "%s: " fmt, __func__, ## arg)
static void vim2m_dev_release(struct device *dev)
{}
@@ -83,21 +90,46 @@ struct vim2m_fmt {
u32 fourcc;
int depth;
/* Types the format can be used for */
- u32 types;
+ u32 types;
};
static struct vim2m_fmt formats[] = {
{
- .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */
+ .fourcc = V4L2_PIX_FMT_RGB565, /* rrrrrggg gggbbbbb */
.depth = 16,
- /* Both capture and output format */
- .types = MEM2MEM_CAPTURE | MEM2MEM_OUTPUT,
- },
- {
+ .types = MEM2MEM_CAPTURE | MEM2MEM_OUTPUT,
+ }, {
+ .fourcc = V4L2_PIX_FMT_RGB565X, /* gggbbbbb rrrrrggg */
+ .depth = 16,
+ .types = MEM2MEM_CAPTURE | MEM2MEM_OUTPUT,
+ }, {
+ .fourcc = V4L2_PIX_FMT_RGB24,
+ .depth = 24,
+ .types = MEM2MEM_CAPTURE | MEM2MEM_OUTPUT,
+ }, {
+ .fourcc = V4L2_PIX_FMT_BGR24,
+ .depth = 24,
+ .types = MEM2MEM_CAPTURE | MEM2MEM_OUTPUT,
+ }, {
.fourcc = V4L2_PIX_FMT_YUYV,
.depth = 16,
- /* Output-only format */
- .types = MEM2MEM_OUTPUT,
+ .types = MEM2MEM_CAPTURE,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SBGGR8,
+ .depth = 8,
+ .types = MEM2MEM_CAPTURE,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGBRG8,
+ .depth = 8,
+ .types = MEM2MEM_CAPTURE,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGRBG8,
+ .depth = 8,
+ .types = MEM2MEM_CAPTURE,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SRGGB8,
+ .depth = 8,
+ .types = MEM2MEM_CAPTURE,
},
};
@@ -120,14 +152,14 @@ enum {
#define V4L2_CID_TRANS_TIME_MSEC (V4L2_CID_USER_BASE + 0x1000)
#define V4L2_CID_TRANS_NUM_BUFS (V4L2_CID_USER_BASE + 0x1001)
-static struct vim2m_fmt *find_format(struct v4l2_format *f)
+static struct vim2m_fmt *find_format(u32 fourcc)
{
struct vim2m_fmt *fmt;
unsigned int k;
for (k = 0; k < NUM_FORMATS; k++) {
fmt = &formats[k];
- if (fmt->fourcc == f->fmt.pix.pixelformat)
+ if (fmt->fourcc == fourcc)
break;
}
@@ -137,6 +169,24 @@ static struct vim2m_fmt *find_format(struct v4l2_format *f)
return &formats[k];
}
+static void get_alignment(u32 fourcc,
+ unsigned int *walign, unsigned int *halign)
+{
+ switch (fourcc) {
+ case V4L2_PIX_FMT_SBGGR8:
+ case V4L2_PIX_FMT_SGBRG8:
+ case V4L2_PIX_FMT_SGRBG8:
+ case V4L2_PIX_FMT_SRGGB8:
+ *walign = BAYER_WIDTH_ALIGN;
+ *halign = BAYER_HEIGHT_ALIGN;
+ return;
+ default:
+ *walign = WIDTH_ALIGN;
+ *halign = HEIGHT_ALIGN;
+ return;
+ }
+}
+
struct vim2m_dev {
struct v4l2_device v4l2_dev;
struct video_device vfd;
@@ -146,9 +196,6 @@ struct vim2m_dev {
atomic_t num_inst;
struct mutex dev_mutex;
- spinlock_t irqlock;
-
- struct delayed_work work_run;
struct v4l2_m2m_dev *m2m_dev;
};
@@ -167,6 +214,10 @@ struct vim2m_ctx {
/* Transaction time (i.e. simulated processing time) in milliseconds */
u32 transtime;
+ struct mutex vb_mutex;
+ struct delayed_work work_run;
+ spinlock_t irqlock;
+
/* Abort requested by m2m */
int aborting;
@@ -188,7 +239,7 @@ static inline struct vim2m_ctx *file2ctx(struct file *file)
}
static struct vim2m_q_data *get_q_data(struct vim2m_ctx *ctx,
- enum v4l2_buf_type type)
+ enum v4l2_buf_type type)
{
switch (type) {
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
@@ -196,28 +247,221 @@ static struct vim2m_q_data *get_q_data(struct vim2m_ctx *ctx,
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
return &ctx->q_data[V4L2_M2M_DST];
default:
- BUG();
+ return NULL;
+ }
+}
+
+static const char *type_name(enum v4l2_buf_type type)
+{
+ switch (type) {
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ return "Output";
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ return "Capture";
+ default:
+ return "Invalid";
+ }
+}
+
+#define CLIP(__color) \
+ (u8)(((__color) > 0xff) ? 0xff : (((__color) < 0) ? 0 : (__color)))
+
+static void copy_line(struct vim2m_q_data *q_data_out,
+ u8 *src, u8 *dst, bool reverse)
+{
+ int x, depth = q_data_out->fmt->depth >> 3;
+
+ if (!reverse) {
+ memcpy(dst, src, q_data_out->width * depth);
+ } else {
+ for (x = 0; x < q_data_out->width >> 1; x++) {
+ memcpy(dst, src, depth);
+ memcpy(dst + depth, src - depth, depth);
+ src -= depth << 1;
+ dst += depth << 1;
+ }
+ return;
}
- return NULL;
}
+static void copy_two_pixels(struct vim2m_q_data *q_data_in,
+ struct vim2m_q_data *q_data_out,
+ u8 *src[2], u8 **dst, int ypos, bool reverse)
+{
+ struct vim2m_fmt *out = q_data_out->fmt;
+ struct vim2m_fmt *in = q_data_in->fmt;
+ u8 _r[2], _g[2], _b[2], *r, *g, *b;
+ int i;
+
+ /* Step 1: read two consecutive pixels from src pointer */
+
+ r = _r;
+ g = _g;
+ b = _b;
+
+ switch (in->fourcc) {
+ case V4L2_PIX_FMT_RGB565: /* rrrrrggg gggbbbbb */
+ for (i = 0; i < 2; i++) {
+ u16 pix = *(u16 *)(src[i]);
+
+ *r++ = (u8)(((pix & 0xf800) >> 11) << 3) | 0x07;
+ *g++ = (u8)((((pix & 0x07e0) >> 5)) << 2) | 0x03;
+ *b++ = (u8)((pix & 0x1f) << 3) | 0x07;
+ }
+ break;
+ case V4L2_PIX_FMT_RGB565X: /* gggbbbbb rrrrrggg */
+ for (i = 0; i < 2; i++) {
+ u16 pix = *(u16 *)(src[i]);
+
+ *r++ = (u8)(((0x00f8 & pix) >> 3) << 3) | 0x07;
+ *g++ = (u8)(((pix & 0x7) << 2) |
+ ((pix & 0xe000) >> 5)) | 0x03;
+ *b++ = (u8)(((pix & 0x1f00) >> 8) << 3) | 0x07;
+ }
+ break;
+ default:
+ case V4L2_PIX_FMT_RGB24:
+ for (i = 0; i < 2; i++) {
+ *r++ = src[i][0];
+ *g++ = src[i][1];
+ *b++ = src[i][2];
+ }
+ break;
+ case V4L2_PIX_FMT_BGR24:
+ for (i = 0; i < 2; i++) {
+ *b++ = src[i][0];
+ *g++ = src[i][1];
+ *r++ = src[i][2];
+ }
+ break;
+ }
+
+ /* Step 2: store two consecutive points, reversing them if needed */
+
+ r = _r;
+ g = _g;
+ b = _b;
+
+ switch (out->fourcc) {
+ case V4L2_PIX_FMT_RGB565: /* rrrrrggg gggbbbbb */
+ for (i = 0; i < 2; i++) {
+ u16 *pix = (u16 *)*dst;
+
+ *pix = ((*r << 8) & 0xf800) | ((*g << 3) & 0x07e0) |
+ (*b >> 3);
+
+ *dst += 2;
+ }
+ return;
+ case V4L2_PIX_FMT_RGB565X: /* gggbbbbb rrrrrggg */
+ for (i = 0; i < 2; i++) {
+ u16 *pix = (u16 *)*dst;
+ u8 green = *g++ >> 2;
+
+ *pix = ((green << 8) & 0xe000) | (green & 0x07) |
+ ((*b++ << 5) & 0x1f00) | ((*r++ & 0xf8));
+
+ *dst += 2;
+ }
+ return;
+ case V4L2_PIX_FMT_RGB24:
+ for (i = 0; i < 2; i++) {
+ *(*dst)++ = *r++;
+ *(*dst)++ = *g++;
+ *(*dst)++ = *b++;
+ }
+ return;
+ case V4L2_PIX_FMT_BGR24:
+ for (i = 0; i < 2; i++) {
+ *(*dst)++ = *b++;
+ *(*dst)++ = *g++;
+ *(*dst)++ = *r++;
+ }
+ return;
+ case V4L2_PIX_FMT_YUYV:
+ default:
+ {
+ u8 y, y1, u, v;
+
+ y = ((8453 * (*r) + 16594 * (*g) + 3223 * (*b)
+ + 524288) >> 15);
+ u = ((-4878 * (*r) - 9578 * (*g) + 14456 * (*b)
+ + 4210688) >> 15);
+ v = ((14456 * (*r++) - 12105 * (*g++) - 2351 * (*b++)
+ + 4210688) >> 15);
+ y1 = ((8453 * (*r) + 16594 * (*g) + 3223 * (*b)
+ + 524288) >> 15);
+
+ *(*dst)++ = y;
+ *(*dst)++ = u;
+
+ *(*dst)++ = y1;
+ *(*dst)++ = v;
+ return;
+ }
+ case V4L2_PIX_FMT_SBGGR8:
+ if (!(ypos & 1)) {
+ *(*dst)++ = *b;
+ *(*dst)++ = *++g;
+ } else {
+ *(*dst)++ = *g;
+ *(*dst)++ = *++r;
+ }
+ return;
+ case V4L2_PIX_FMT_SGBRG8:
+ if (!(ypos & 1)) {
+ *(*dst)++ = *g;
+ *(*dst)++ = *++b;
+ } else {
+ *(*dst)++ = *r;
+ *(*dst)++ = *++g;
+ }
+ return;
+ case V4L2_PIX_FMT_SGRBG8:
+ if (!(ypos & 1)) {
+ *(*dst)++ = *g;
+ *(*dst)++ = *++r;
+ } else {
+ *(*dst)++ = *b;
+ *(*dst)++ = *++g;
+ }
+ return;
+ case V4L2_PIX_FMT_SRGGB8:
+ if (!(ypos & 1)) {
+ *(*dst)++ = *r;
+ *(*dst)++ = *++g;
+ } else {
+ *(*dst)++ = *g;
+ *(*dst)++ = *++b;
+ }
+ return;
+ }
+}
static int device_process(struct vim2m_ctx *ctx,
struct vb2_v4l2_buffer *in_vb,
struct vb2_v4l2_buffer *out_vb)
{
struct vim2m_dev *dev = ctx->dev;
- struct vim2m_q_data *q_data;
- u8 *p_in, *p_out;
- int x, y, t, w;
- int tile_w, bytes_left;
- int width, height, bytesperline;
+ struct vim2m_q_data *q_data_in, *q_data_out;
+ u8 *p_in, *p_line, *p_in_x[2], *p, *p_out;
+ unsigned int width, height, bytesperline, bytes_per_pixel;
+ unsigned int x, y, y_in, y_out, x_int, x_fract, x_err, x_offset;
+ int start, end, step;
+
+ q_data_in = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ if (!q_data_in)
+ return 0;
+ bytesperline = (q_data_in->width * q_data_in->fmt->depth) >> 3;
+ bytes_per_pixel = q_data_in->fmt->depth >> 3;
- q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ q_data_out = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ if (!q_data_out)
+ return 0;
- width = q_data->width;
- height = q_data->height;
- bytesperline = (q_data->width * q_data->fmt->depth) >> 3;
+ /* As we're doing scaling, use the output dimensions here */
+ height = q_data_out->height;
+ width = q_data_out->width;
p_in = vb2_plane_vaddr(&in_vb->vb2_buf, 0);
p_out = vb2_plane_vaddr(&out_vb->vb2_buf, 0);
@@ -227,109 +471,86 @@ static int device_process(struct vim2m_ctx *ctx,
return -EFAULT;
}
- if (vb2_plane_size(&in_vb->vb2_buf, 0) >
- vb2_plane_size(&out_vb->vb2_buf, 0)) {
- v4l2_err(&dev->v4l2_dev, "Output buffer is too small\n");
- return -EINVAL;
- }
+ out_vb->sequence = q_data_out->sequence++;
+ in_vb->sequence = q_data_in->sequence++;
+ v4l2_m2m_buf_copy_metadata(in_vb, out_vb, true);
- tile_w = (width * (q_data[V4L2_M2M_DST].fmt->depth >> 3))
- / MEM2MEM_NUM_TILES;
- bytes_left = bytesperline - tile_w * MEM2MEM_NUM_TILES;
- w = 0;
-
- out_vb->sequence =
- get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE)->sequence++;
- in_vb->sequence = q_data->sequence++;
- out_vb->vb2_buf.timestamp = in_vb->vb2_buf.timestamp;
-
- if (in_vb->flags & V4L2_BUF_FLAG_TIMECODE)
- out_vb->timecode = in_vb->timecode;
- out_vb->field = in_vb->field;
- out_vb->flags = in_vb->flags &
- (V4L2_BUF_FLAG_TIMECODE |
- V4L2_BUF_FLAG_KEYFRAME |
- V4L2_BUF_FLAG_PFRAME |
- V4L2_BUF_FLAG_BFRAME |
- V4L2_BUF_FLAG_TSTAMP_SRC_MASK);
-
- switch (ctx->mode) {
- case MEM2MEM_HFLIP | MEM2MEM_VFLIP:
- p_out += bytesperline * height - bytes_left;
- for (y = 0; y < height; ++y) {
- for (t = 0; t < MEM2MEM_NUM_TILES; ++t) {
- if (w & 0x1) {
- for (x = 0; x < tile_w; ++x)
- *--p_out = *p_in++ +
- MEM2MEM_COLOR_STEP;
- } else {
- for (x = 0; x < tile_w; ++x)
- *--p_out = *p_in++ -
- MEM2MEM_COLOR_STEP;
- }
- ++w;
- }
- p_in += bytes_left;
- p_out -= bytes_left;
- }
- break;
+ if (ctx->mode & MEM2MEM_VFLIP) {
+ start = height - 1;
+ end = -1;
+ step = -1;
+ } else {
+ start = 0;
+ end = height;
+ step = 1;
+ }
+ y_out = 0;
+
+ /*
+ * When format and resolution are identical,
+ * we can use a faster copy logic
+ */
+ if (q_data_in->fmt->fourcc == q_data_out->fmt->fourcc &&
+ q_data_in->width == q_data_out->width &&
+ q_data_in->height == q_data_out->height) {
+ for (y = start; y != end; y += step, y_out++) {
+ p = p_in + (y * bytesperline);
+ if (ctx->mode & MEM2MEM_HFLIP)
+ p += bytesperline - (q_data_in->fmt->depth >> 3);
+
+ copy_line(q_data_out, p, p_out,
+ ctx->mode & MEM2MEM_HFLIP);
- case MEM2MEM_HFLIP:
- for (y = 0; y < height; ++y) {
- p_out += MEM2MEM_NUM_TILES * tile_w;
- for (t = 0; t < MEM2MEM_NUM_TILES; ++t) {
- if (w & 0x01) {
- for (x = 0; x < tile_w; ++x)
- *--p_out = *p_in++ +
- MEM2MEM_COLOR_STEP;
- } else {
- for (x = 0; x < tile_w; ++x)
- *--p_out = *p_in++ -
- MEM2MEM_COLOR_STEP;
- }
- ++w;
- }
- p_in += bytes_left;
p_out += bytesperline;
}
- break;
+ return 0;
+ }
+
+ /* Slower algorithm with format conversion, hflip, vflip and scaler */
+
+ /* To speed scaler up, use Bresenham for X dimension */
+ x_int = q_data_in->width / q_data_out->width;
+ x_fract = q_data_in->width % q_data_out->width;
+
+ for (y = start; y != end; y += step, y_out++) {
+ y_in = (y * q_data_in->height) / q_data_out->height;
+ x_offset = 0;
+ x_err = 0;
- case MEM2MEM_VFLIP:
- p_out += bytesperline * (height - 1);
- for (y = 0; y < height; ++y) {
- for (t = 0; t < MEM2MEM_NUM_TILES; ++t) {
- if (w & 0x1) {
- for (x = 0; x < tile_w; ++x)
- *p_out++ = *p_in++ +
- MEM2MEM_COLOR_STEP;
- } else {
- for (x = 0; x < tile_w; ++x)
- *p_out++ = *p_in++ -
- MEM2MEM_COLOR_STEP;
- }
- ++w;
+ p_line = p_in + (y_in * bytesperline);
+ if (ctx->mode & MEM2MEM_HFLIP)
+ p_line += bytesperline - (q_data_in->fmt->depth >> 3);
+ p_in_x[0] = p_line;
+
+ for (x = 0; x < width >> 1; x++) {
+ x_offset += x_int;
+ x_err += x_fract;
+ if (x_err > width) {
+ x_offset++;
+ x_err -= width;
}
- p_in += bytes_left;
- p_out += bytes_left - 2 * bytesperline;
- }
- break;
- default:
- for (y = 0; y < height; ++y) {
- for (t = 0; t < MEM2MEM_NUM_TILES; ++t) {
- if (w & 0x1) {
- for (x = 0; x < tile_w; ++x)
- *p_out++ = *p_in++ +
- MEM2MEM_COLOR_STEP;
- } else {
- for (x = 0; x < tile_w; ++x)
- *p_out++ = *p_in++ -
- MEM2MEM_COLOR_STEP;
- }
- ++w;
+ if (ctx->mode & MEM2MEM_HFLIP)
+ p_in_x[1] = p_line - x_offset * bytes_per_pixel;
+ else
+ p_in_x[1] = p_line + x_offset * bytes_per_pixel;
+
+ copy_two_pixels(q_data_in, q_data_out,
+ p_in_x, &p_out, y_out,
+ ctx->mode & MEM2MEM_HFLIP);
+
+ /* Calculate the next p_in_x0 */
+ x_offset += x_int;
+ x_err += x_fract;
+ if (x_err > width) {
+ x_offset++;
+ x_err -= width;
}
- p_in += bytes_left;
- p_out += bytes_left;
+
+ if (ctx->mode & MEM2MEM_HFLIP)
+ p_in_x[0] = p_line - x_offset * bytes_per_pixel;
+ else
+ p_in_x[0] = p_line + x_offset * bytes_per_pixel;
}
}
@@ -349,7 +570,7 @@ static int job_ready(void *priv)
if (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) < ctx->translen
|| v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx) < ctx->translen) {
- dprintk(ctx->dev, "Not enough buffers available\n");
+ dprintk(ctx->dev, 1, "Not enough buffers available\n");
return 0;
}
@@ -373,7 +594,6 @@ static void job_abort(void *priv)
static void device_run(void *priv)
{
struct vim2m_ctx *ctx = priv;
- struct vim2m_dev *dev = ctx->dev;
struct vb2_v4l2_buffer *src_buf, *dst_buf;
src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
@@ -390,37 +610,38 @@ static void device_run(void *priv)
&ctx->hdl);
/* Run delayed work, which simulates a hardware irq */
- schedule_delayed_work(&dev->work_run, msecs_to_jiffies(ctx->transtime));
+ schedule_delayed_work(&ctx->work_run, msecs_to_jiffies(ctx->transtime));
}
static void device_work(struct work_struct *w)
{
- struct vim2m_dev *vim2m_dev =
- container_of(w, struct vim2m_dev, work_run.work);
struct vim2m_ctx *curr_ctx;
+ struct vim2m_dev *vim2m_dev;
struct vb2_v4l2_buffer *src_vb, *dst_vb;
unsigned long flags;
- curr_ctx = v4l2_m2m_get_curr_priv(vim2m_dev->m2m_dev);
+ curr_ctx = container_of(w, struct vim2m_ctx, work_run.work);
- if (NULL == curr_ctx) {
+ if (!curr_ctx) {
pr_err("Instance released before the end of transaction\n");
return;
}
+ vim2m_dev = curr_ctx->dev;
+
src_vb = v4l2_m2m_src_buf_remove(curr_ctx->fh.m2m_ctx);
dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->fh.m2m_ctx);
curr_ctx->num_processed++;
- spin_lock_irqsave(&vim2m_dev->irqlock, flags);
+ spin_lock_irqsave(&curr_ctx->irqlock, flags);
v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_DONE);
v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_DONE);
- spin_unlock_irqrestore(&vim2m_dev->irqlock, flags);
+ spin_unlock_irqrestore(&curr_ctx->irqlock, flags);
if (curr_ctx->num_processed == curr_ctx->translen
|| curr_ctx->aborting) {
- dprintk(curr_ctx->dev, "Finishing transaction\n");
+ dprintk(curr_ctx->dev, 2, "Finishing capture buffer fill\n");
curr_ctx->num_processed = 0;
v4l2_m2m_job_finish(vim2m_dev->m2m_dev, curr_ctx->fh.m2m_ctx);
} else {
@@ -437,7 +658,7 @@ static int vidioc_querycap(struct file *file, void *priv,
strncpy(cap->driver, MEM2MEM_NAME, sizeof(cap->driver) - 1);
strncpy(cap->card, MEM2MEM_NAME, sizeof(cap->card) - 1);
snprintf(cap->bus_info, sizeof(cap->bus_info),
- "platform:%s", MEM2MEM_NAME);
+ "platform:%s", MEM2MEM_NAME);
return 0;
}
@@ -453,8 +674,10 @@ static int enum_fmt(struct v4l2_fmtdesc *f, u32 type)
/* index-th format of type type found ? */
if (num == f->index)
break;
- /* Correct type but haven't reached our index yet,
- * just increment per-type index */
+ /*
+ * Correct type but haven't reached our index yet,
+ * just increment per-type index
+ */
++num;
}
}
@@ -482,6 +705,27 @@ static int vidioc_enum_fmt_vid_out(struct file *file, void *priv,
return enum_fmt(f, MEM2MEM_OUTPUT);
}
+static int vidioc_enum_framesizes(struct file *file, void *priv,
+ struct v4l2_frmsizeenum *fsize)
+{
+ if (fsize->index != 0)
+ return -EINVAL;
+
+ if (!find_format(fsize->pixel_format))
+ return -EINVAL;
+
+ fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
+ fsize->stepwise.min_width = MIN_W;
+ fsize->stepwise.min_height = MIN_H;
+ fsize->stepwise.max_width = MAX_W;
+ fsize->stepwise.max_height = MAX_H;
+
+ get_alignment(fsize->pixel_format,
+ &fsize->stepwise.step_width,
+ &fsize->stepwise.step_height);
+ return 0;
+}
+
static int vidioc_g_fmt(struct vim2m_ctx *ctx, struct v4l2_format *f)
{
struct vb2_queue *vq;
@@ -492,6 +736,8 @@ static int vidioc_g_fmt(struct vim2m_ctx *ctx, struct v4l2_format *f)
return -EINVAL;
q_data = get_q_data(ctx, f->type);
+ if (!q_data)
+ return -EINVAL;
f->fmt.pix.width = q_data->width;
f->fmt.pix.height = q_data->height;
@@ -521,8 +767,11 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
static int vidioc_try_fmt(struct v4l2_format *f, struct vim2m_fmt *fmt)
{
- /* V4L2 specification suggests the driver corrects the format struct
- * if any of the dimensions is unsupported */
+ int walign, halign;
+ /*
+ * V4L2 specification specifies the driver corrects the
+ * format struct if any of the dimensions is unsupported
+ */
if (f->fmt.pix.height < MIN_H)
f->fmt.pix.height = MIN_H;
else if (f->fmt.pix.height > MAX_H)
@@ -533,7 +782,9 @@ static int vidioc_try_fmt(struct v4l2_format *f, struct vim2m_fmt *fmt)
else if (f->fmt.pix.width > MAX_W)
f->fmt.pix.width = MAX_W;
- f->fmt.pix.width &= ~DIM_ALIGN_MASK;
+ get_alignment(f->fmt.pix.pixelformat, &walign, &halign);
+ f->fmt.pix.width &= ~(walign - 1);
+ f->fmt.pix.height &= ~(halign - 1);
f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3;
f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
f->fmt.pix.field = V4L2_FIELD_NONE;
@@ -547,10 +798,10 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
struct vim2m_fmt *fmt;
struct vim2m_ctx *ctx = file2ctx(file);
- fmt = find_format(f);
+ fmt = find_format(f->fmt.pix.pixelformat);
if (!fmt) {
f->fmt.pix.pixelformat = formats[0].fourcc;
- fmt = find_format(f);
+ fmt = find_format(f->fmt.pix.pixelformat);
}
if (!(fmt->types & MEM2MEM_CAPTURE)) {
v4l2_err(&ctx->dev->v4l2_dev,
@@ -572,10 +823,10 @@ static int vidioc_try_fmt_vid_out(struct file *file, void *priv,
struct vim2m_fmt *fmt;
struct vim2m_ctx *ctx = file2ctx(file);
- fmt = find_format(f);
+ fmt = find_format(f->fmt.pix.pixelformat);
if (!fmt) {
f->fmt.pix.pixelformat = formats[0].fourcc;
- fmt = find_format(f);
+ fmt = find_format(f->fmt.pix.pixelformat);
}
if (!(fmt->types & MEM2MEM_OUTPUT)) {
v4l2_err(&ctx->dev->v4l2_dev,
@@ -607,15 +858,20 @@ static int vidioc_s_fmt(struct vim2m_ctx *ctx, struct v4l2_format *f)
return -EBUSY;
}
- q_data->fmt = find_format(f);
+ q_data->fmt = find_format(f->fmt.pix.pixelformat);
q_data->width = f->fmt.pix.width;
q_data->height = f->fmt.pix.height;
q_data->sizeimage = q_data->width * q_data->height
* q_data->fmt->depth >> 3;
- dprintk(ctx->dev,
- "Setting format for type %d, wxh: %dx%d, fmt: %d\n",
- f->type, q_data->width, q_data->height, q_data->fmt->fourcc);
+ dprintk(ctx->dev, 1,
+ "Format for type %s: %dx%d (%d bpp), fmt: %c%c%c%c\n",
+ type_name(f->type), q_data->width, q_data->height,
+ q_data->fmt->depth,
+ (q_data->fmt->fourcc & 0xff),
+ (q_data->fmt->fourcc >> 8) & 0xff,
+ (q_data->fmt->fourcc >> 16) & 0xff,
+ (q_data->fmt->fourcc >> 24) & 0xff);
return 0;
}
@@ -674,6 +930,8 @@ static int vim2m_s_ctrl(struct v4l2_ctrl *ctrl)
case V4L2_CID_TRANS_TIME_MSEC:
ctx->transtime = ctrl->val;
+ if (ctx->transtime < 1)
+ ctx->transtime = 1;
break;
case V4L2_CID_TRANS_NUM_BUFS:
@@ -692,11 +950,11 @@ static const struct v4l2_ctrl_ops vim2m_ctrl_ops = {
.s_ctrl = vim2m_s_ctrl,
};
-
static const struct v4l2_ioctl_ops vim2m_ioctl_ops = {
.vidioc_querycap = vidioc_querycap,
.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_enum_framesizes = vidioc_enum_framesizes,
.vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
@@ -721,20 +979,23 @@ static const struct v4l2_ioctl_ops vim2m_ioctl_ops = {
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};
-
/*
* Queue operations
*/
static int vim2m_queue_setup(struct vb2_queue *vq,
- unsigned int *nbuffers, unsigned int *nplanes,
- unsigned int sizes[], struct device *alloc_devs[])
+ unsigned int *nbuffers,
+ unsigned int *nplanes,
+ unsigned int sizes[],
+ struct device *alloc_devs[])
{
struct vim2m_ctx *ctx = vb2_get_drv_priv(vq);
struct vim2m_q_data *q_data;
unsigned int size, count = *nbuffers;
q_data = get_q_data(ctx, vq->type);
+ if (!q_data)
+ return -EINVAL;
size = q_data->width * q_data->height * q_data->fmt->depth >> 3;
@@ -748,33 +1009,42 @@ static int vim2m_queue_setup(struct vb2_queue *vq,
*nplanes = 1;
sizes[0] = size;
- dprintk(ctx->dev, "get %d buffer(s) of size %d each.\n", count, size);
+ dprintk(ctx->dev, 1, "%s: get %d buffer(s) of size %d each.\n",
+ type_name(vq->type), count, size);
return 0;
}
-static int vim2m_buf_prepare(struct vb2_buffer *vb)
+static int vim2m_buf_out_validate(struct vb2_buffer *vb)
{
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
struct vim2m_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+
+ if (vbuf->field == V4L2_FIELD_ANY)
+ vbuf->field = V4L2_FIELD_NONE;
+ if (vbuf->field != V4L2_FIELD_NONE) {
+ dprintk(ctx->dev, 1, "%s field isn't supported\n", __func__);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int vim2m_buf_prepare(struct vb2_buffer *vb)
+{
+ struct vim2m_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
struct vim2m_q_data *q_data;
- dprintk(ctx->dev, "type: %d\n", vb->vb2_queue->type);
+ dprintk(ctx->dev, 2, "type: %s\n", type_name(vb->vb2_queue->type));
q_data = get_q_data(ctx, vb->vb2_queue->type);
- if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) {
- if (vbuf->field == V4L2_FIELD_ANY)
- vbuf->field = V4L2_FIELD_NONE;
- if (vbuf->field != V4L2_FIELD_NONE) {
- dprintk(ctx->dev, "%s field isn't supported\n",
- __func__);
- return -EINVAL;
- }
- }
-
+ if (!q_data)
+ return -EINVAL;
if (vb2_plane_size(vb, 0) < q_data->sizeimage) {
- dprintk(ctx->dev, "%s data will not fit into plane (%lu < %lu)\n",
- __func__, vb2_plane_size(vb, 0), (long)q_data->sizeimage);
+ dprintk(ctx->dev, 1,
+ "%s data will not fit into plane (%lu < %lu)\n",
+ __func__, vb2_plane_size(vb, 0),
+ (long)q_data->sizeimage);
return -EINVAL;
}
@@ -791,11 +1061,14 @@ static void vim2m_buf_queue(struct vb2_buffer *vb)
v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
}
-static int vim2m_start_streaming(struct vb2_queue *q, unsigned count)
+static int vim2m_start_streaming(struct vb2_queue *q, unsigned int count)
{
struct vim2m_ctx *ctx = vb2_get_drv_priv(q);
struct vim2m_q_data *q_data = get_q_data(ctx, q->type);
+ if (!q_data)
+ return -EINVAL;
+
q_data->sequence = 0;
return 0;
}
@@ -803,25 +1076,23 @@ static int vim2m_start_streaming(struct vb2_queue *q, unsigned count)
static void vim2m_stop_streaming(struct vb2_queue *q)
{
struct vim2m_ctx *ctx = vb2_get_drv_priv(q);
- struct vim2m_dev *dev = ctx->dev;
struct vb2_v4l2_buffer *vbuf;
unsigned long flags;
- if (v4l2_m2m_get_curr_priv(dev->m2m_dev) == ctx)
- cancel_delayed_work_sync(&dev->work_run);
+ cancel_delayed_work_sync(&ctx->work_run);
for (;;) {
if (V4L2_TYPE_IS_OUTPUT(q->type))
vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
else
vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
- if (vbuf == NULL)
+ if (!vbuf)
return;
v4l2_ctrl_request_complete(vbuf->vb2_buf.req_obj.req,
&ctx->hdl);
- spin_lock_irqsave(&ctx->dev->irqlock, flags);
+ spin_lock_irqsave(&ctx->irqlock, flags);
v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
- spin_unlock_irqrestore(&ctx->dev->irqlock, flags);
+ spin_unlock_irqrestore(&ctx->irqlock, flags);
}
}
@@ -834,6 +1105,7 @@ static void vim2m_buf_request_complete(struct vb2_buffer *vb)
static const struct vb2_ops vim2m_qops = {
.queue_setup = vim2m_queue_setup,
+ .buf_out_validate = vim2m_buf_out_validate,
.buf_prepare = vim2m_buf_prepare,
.buf_queue = vim2m_buf_queue,
.start_streaming = vim2m_start_streaming,
@@ -843,7 +1115,8 @@ static const struct vb2_ops vim2m_qops = {
.buf_request_complete = vim2m_buf_request_complete,
};
-static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq)
+static int queue_init(void *priv, struct vb2_queue *src_vq,
+ struct vb2_queue *dst_vq)
{
struct vim2m_ctx *ctx = priv;
int ret;
@@ -855,7 +1128,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *ds
src_vq->ops = &vim2m_qops;
src_vq->mem_ops = &vb2_vmalloc_memops;
src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
- src_vq->lock = &ctx->dev->dev_mutex;
+ src_vq->lock = &ctx->vb_mutex;
src_vq->supports_requests = true;
ret = vb2_queue_init(src_vq);
@@ -869,17 +1142,16 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *ds
dst_vq->ops = &vim2m_qops;
dst_vq->mem_ops = &vb2_vmalloc_memops;
dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
- dst_vq->lock = &ctx->dev->dev_mutex;
+ dst_vq->lock = &ctx->vb_mutex;
return vb2_queue_init(dst_vq);
}
-static const struct v4l2_ctrl_config vim2m_ctrl_trans_time_msec = {
+static struct v4l2_ctrl_config vim2m_ctrl_trans_time_msec = {
.ops = &vim2m_ctrl_ops,
.id = V4L2_CID_TRANS_TIME_MSEC,
.name = "Transaction Time (msec)",
.type = V4L2_CTRL_TYPE_INTEGER,
- .def = MEM2MEM_DEF_TRANSTIME,
.min = 1,
.max = 10001,
.step = 1,
@@ -921,6 +1193,8 @@ static int vim2m_open(struct file *file)
v4l2_ctrl_handler_init(hdl, 4);
v4l2_ctrl_new_std(hdl, &vim2m_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0);
v4l2_ctrl_new_std(hdl, &vim2m_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0);
+
+ vim2m_ctrl_trans_time_msec.def = default_transtime;
v4l2_ctrl_new_custom(hdl, &vim2m_ctrl_trans_time_msec, NULL);
v4l2_ctrl_new_custom(hdl, &vim2m_ctrl_trans_num_bufs, NULL);
if (hdl->error) {
@@ -944,6 +1218,10 @@ static int vim2m_open(struct file *file)
ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init);
+ mutex_init(&ctx->vb_mutex);
+ spin_lock_init(&ctx->irqlock);
+ INIT_DELAYED_WORK(&ctx->work_run, device_work);
+
if (IS_ERR(ctx->fh.m2m_ctx)) {
rc = PTR_ERR(ctx->fh.m2m_ctx);
@@ -956,7 +1234,7 @@ static int vim2m_open(struct file *file)
v4l2_fh_add(&ctx->fh);
atomic_inc(&dev->num_inst);
- dprintk(dev, "Created instance: %p, m2m_ctx: %p\n",
+ dprintk(dev, 1, "Created instance: %p, m2m_ctx: %p\n",
ctx, ctx->fh.m2m_ctx);
open_unlock:
@@ -969,7 +1247,7 @@ static int vim2m_release(struct file *file)
struct vim2m_dev *dev = video_drvdata(file);
struct vim2m_ctx *ctx = file2ctx(file);
- dprintk(dev, "Releasing instance %p\n", ctx);
+ dprintk(dev, 1, "Releasing instance %p\n", ctx);
v4l2_fh_del(&ctx->fh);
v4l2_fh_exit(&ctx->fh);
@@ -1024,8 +1302,6 @@ static int vim2m_probe(struct platform_device *pdev)
if (!dev)
return -ENOMEM;
- spin_lock_init(&dev->irqlock);
-
ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
if (ret)
return ret;
@@ -1037,7 +1313,6 @@ static int vim2m_probe(struct platform_device *pdev)
vfd = &dev->vfd;
vfd->lock = &dev->dev_mutex;
vfd->v4l2_dev = &dev->v4l2_dev;
- INIT_DELAYED_WORK(&dev->work_run, device_work);
ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
if (ret) {
@@ -1047,7 +1322,7 @@ static int vim2m_probe(struct platform_device *pdev)
video_set_drvdata(vfd, dev);
v4l2_info(&dev->v4l2_dev,
- "Device registered as /dev/video%d\n", vfd->num);
+ "Device registered as /dev/video%d\n", vfd->num);
platform_set_drvdata(pdev, dev);
@@ -1061,12 +1336,14 @@ static int vim2m_probe(struct platform_device *pdev)
#ifdef CONFIG_MEDIA_CONTROLLER
dev->mdev.dev = &pdev->dev;
strscpy(dev->mdev.model, "vim2m", sizeof(dev->mdev.model));
+ strscpy(dev->mdev.bus_info, "platform:vim2m",
+ sizeof(dev->mdev.bus_info));
media_device_init(&dev->mdev);
dev->mdev.ops = &m2m_media_ops;
dev->v4l2_dev.mdev = &dev->mdev;
- ret = v4l2_m2m_register_media_controller(dev->m2m_dev,
- vfd, MEDIA_ENT_F_PROC_VIDEO_SCALER);
+ ret = v4l2_m2m_register_media_controller(dev->m2m_dev, vfd,
+ MEDIA_ENT_F_PROC_VIDEO_SCALER);
if (ret) {
v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem media controller\n");
goto unreg_m2m;
diff --git a/drivers/media/platform/vimc/Makefile b/drivers/media/platform/vimc/Makefile
index 4b2e3de7856e..c4fc8e7d365a 100644
--- a/drivers/media/platform/vimc/Makefile
+++ b/drivers/media/platform/vimc/Makefile
@@ -5,6 +5,7 @@ vimc_common-objs := vimc-common.o
vimc_debayer-objs := vimc-debayer.o
vimc_scaler-objs := vimc-scaler.o
vimc_sensor-objs := vimc-sensor.o
+vimc_streamer-objs := vimc-streamer.o
obj-$(CONFIG_VIDEO_VIMC) += vimc.o vimc_capture.o vimc_common.o vimc-debayer.o \
- vimc_scaler.o vimc_sensor.o
+ vimc_scaler.o vimc_sensor.o vimc_streamer.o
diff --git a/drivers/media/platform/vimc/vimc-capture.c b/drivers/media/platform/vimc/vimc-capture.c
index 3f7e9ed56633..ea869631a3f6 100644
--- a/drivers/media/platform/vimc/vimc-capture.c
+++ b/drivers/media/platform/vimc/vimc-capture.c
@@ -24,6 +24,7 @@
#include <media/videobuf2-vmalloc.h>
#include "vimc-common.h"
+#include "vimc-streamer.h"
#define VIMC_CAP_DRV_NAME "vimc-capture"
@@ -44,7 +45,7 @@ struct vimc_cap_device {
spinlock_t qlock;
struct mutex lock;
u32 sequence;
- struct media_pipeline pipe;
+ struct vimc_stream stream;
};
static const struct v4l2_pix_format fmt_default = {
@@ -69,12 +70,10 @@ struct vimc_cap_buffer {
static int vimc_cap_querycap(struct file *file, void *priv,
struct v4l2_capability *cap)
{
- struct vimc_cap_device *vcap = video_drvdata(file);
-
- strscpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
+ strscpy(cap->driver, VIMC_PDEV_NAME, sizeof(cap->driver));
strscpy(cap->card, KBUILD_MODNAME, sizeof(cap->card));
snprintf(cap->bus_info, sizeof(cap->bus_info),
- "platform:%s", vcap->vdev.v4l2_dev->name);
+ "platform:%s", VIMC_PDEV_NAME);
return 0;
}
@@ -248,14 +247,13 @@ static int vimc_cap_start_streaming(struct vb2_queue *vq, unsigned int count)
vcap->sequence = 0;
/* Start the media pipeline */
- ret = media_pipeline_start(entity, &vcap->pipe);
+ ret = media_pipeline_start(entity, &vcap->stream.pipe);
if (ret) {
vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_QUEUED);
return ret;
}
- /* Enable streaming from the pipe */
- ret = vimc_pipeline_s_stream(&vcap->vdev.entity, 1);
+ ret = vimc_streamer_s_stream(&vcap->stream, &vcap->ved, 1);
if (ret) {
media_pipeline_stop(entity);
vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_QUEUED);
@@ -273,8 +271,7 @@ static void vimc_cap_stop_streaming(struct vb2_queue *vq)
{
struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
- /* Disable streaming from the pipe */
- vimc_pipeline_s_stream(&vcap->vdev.entity, 0);
+ vimc_streamer_s_stream(&vcap->stream, &vcap->ved, 0);
/* Stop the media pipeline */
media_pipeline_stop(&vcap->vdev.entity);
@@ -355,8 +352,8 @@ static void vimc_cap_comp_unbind(struct device *comp, struct device *master,
kfree(vcap);
}
-static void vimc_cap_process_frame(struct vimc_ent_device *ved,
- struct media_pad *sink, const void *frame)
+static void *vimc_cap_process_frame(struct vimc_ent_device *ved,
+ const void *frame)
{
struct vimc_cap_device *vcap = container_of(ved, struct vimc_cap_device,
ved);
@@ -370,7 +367,7 @@ static void vimc_cap_process_frame(struct vimc_ent_device *ved,
typeof(*vimc_buf), list);
if (!vimc_buf) {
spin_unlock(&vcap->qlock);
- return;
+ return ERR_PTR(-EAGAIN);
}
/* Remove this entry from the list */
@@ -391,6 +388,7 @@ static void vimc_cap_process_frame(struct vimc_ent_device *ved,
vb2_set_plane_payload(&vimc_buf->vb2.vb2_buf, 0,
vcap->format.sizeimage);
vb2_buffer_done(&vimc_buf->vb2.vb2_buf, VB2_BUF_STATE_DONE);
+ return NULL;
}
static int vimc_cap_comp_bind(struct device *comp, struct device *master,
@@ -431,7 +429,7 @@ static int vimc_cap_comp_bind(struct device *comp, struct device *master,
/* Initialize the vb2 queue */
q = &vcap->queue;
q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- q->io_modes = VB2_MMAP | VB2_DMABUF;
+ q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_USERPTR;
q->drv_priv = vcap;
q->buf_struct_size = sizeof(struct vimc_cap_buffer);
q->ops = &vimc_cap_qops;
diff --git a/drivers/media/platform/vimc/vimc-common.c b/drivers/media/platform/vimc/vimc-common.c
index 867e24dbd6b5..c1a74bb2df58 100644
--- a/drivers/media/platform/vimc/vimc-common.c
+++ b/drivers/media/platform/vimc/vimc-common.c
@@ -207,41 +207,6 @@ const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat)
}
EXPORT_SYMBOL_GPL(vimc_pix_map_by_pixelformat);
-int vimc_propagate_frame(struct media_pad *src, const void *frame)
-{
- struct media_link *link;
-
- if (!(src->flags & MEDIA_PAD_FL_SOURCE))
- return -EINVAL;
-
- /* Send this frame to all sink pads that are direct linked */
- list_for_each_entry(link, &src->entity->links, list) {
- if (link->source == src &&
- (link->flags & MEDIA_LNK_FL_ENABLED)) {
- struct vimc_ent_device *ved = NULL;
- struct media_entity *entity = link->sink->entity;
-
- if (is_media_entity_v4l2_subdev(entity)) {
- struct v4l2_subdev *sd =
- container_of(entity, struct v4l2_subdev,
- entity);
- ved = v4l2_get_subdevdata(sd);
- } else if (is_media_entity_v4l2_video_device(entity)) {
- struct video_device *vdev =
- container_of(entity,
- struct video_device,
- entity);
- ved = video_get_drvdata(vdev);
- }
- if (ved && ved->process_frame)
- ved->process_frame(ved, link->sink, frame);
- }
- }
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(vimc_propagate_frame);
-
/* Helper function to allocate and initialize pads */
struct media_pad *vimc_pads_init(u16 num_pads, const unsigned long *pads_flag)
{
diff --git a/drivers/media/platform/vimc/vimc-common.h b/drivers/media/platform/vimc/vimc-common.h
index 2e9981b18166..84539430b5e7 100644
--- a/drivers/media/platform/vimc/vimc-common.h
+++ b/drivers/media/platform/vimc/vimc-common.h
@@ -22,6 +22,8 @@
#include <media/media-device.h>
#include <media/v4l2-device.h>
+#define VIMC_PDEV_NAME "vimc"
+
/* VIMC-specific controls */
#define VIMC_CID_VIMC_BASE (0x00f00000 | 0xf000)
#define VIMC_CID_VIMC_CLASS (0x00f00000 | 1)
@@ -113,24 +115,13 @@ struct vimc_pix_map {
struct vimc_ent_device {
struct media_entity *ent;
struct media_pad *pads;
- void (*process_frame)(struct vimc_ent_device *ved,
- struct media_pad *sink, const void *frame);
+ void * (*process_frame)(struct vimc_ent_device *ved,
+ const void *frame);
void (*vdev_get_format)(struct vimc_ent_device *ved,
struct v4l2_pix_format *fmt);
};
/**
- * vimc_propagate_frame - propagate a frame through the topology
- *
- * @src: the source pad where the frame is being originated
- * @frame: the frame to be propagated
- *
- * This function will call the process_frame callback from the vimc_ent_device
- * struct of the nodes directly connected to the @src pad
- */
-int vimc_propagate_frame(struct media_pad *src, const void *frame);
-
-/**
* vimc_pads_init - initialize pads
*
* @num_pads: number of pads to initialize
diff --git a/drivers/media/platform/vimc/vimc-core.c b/drivers/media/platform/vimc/vimc-core.c
index ce809d2e3d53..0fbb7914098f 100644
--- a/drivers/media/platform/vimc/vimc-core.c
+++ b/drivers/media/platform/vimc/vimc-core.c
@@ -24,7 +24,6 @@
#include "vimc-common.h"
-#define VIMC_PDEV_NAME "vimc"
#define VIMC_MDEV_MODEL_NAME "VIMC MDEV"
#define VIMC_ENT_LINK(src, srcpad, sink, sinkpad, link_flags) { \
@@ -221,6 +220,7 @@ static int vimc_comp_bind(struct device *master)
err_mdev_unregister:
media_device_unregister(&vimc->mdev);
+ media_device_cleanup(&vimc->mdev);
err_comp_unbind_all:
component_unbind_all(master, NULL);
err_v4l2_unregister:
@@ -237,6 +237,7 @@ static void vimc_comp_unbind(struct device *master)
dev_dbg(master, "unbind");
media_device_unregister(&vimc->mdev);
+ media_device_cleanup(&vimc->mdev);
component_unbind_all(master, NULL);
v4l2_device_unregister(&vimc->v4l2_dev);
}
@@ -319,6 +320,8 @@ static int vimc_probe(struct platform_device *pdev)
/* Initialize media device */
strscpy(vimc->mdev.model, VIMC_MDEV_MODEL_NAME,
sizeof(vimc->mdev.model));
+ snprintf(vimc->mdev.bus_info, sizeof(vimc->mdev.bus_info),
+ "platform:%s", VIMC_PDEV_NAME);
vimc->mdev.dev = &pdev->dev;
media_device_init(&vimc->mdev);
diff --git a/drivers/media/platform/vimc/vimc-debayer.c b/drivers/media/platform/vimc/vimc-debayer.c
index 77887f66f323..7d77c63b99d2 100644
--- a/drivers/media/platform/vimc/vimc-debayer.c
+++ b/drivers/media/platform/vimc/vimc-debayer.c
@@ -321,7 +321,6 @@ static void vimc_deb_set_rgb_mbus_fmt_rgb888_1x24(struct vimc_deb_device *vdeb,
static int vimc_deb_s_stream(struct v4l2_subdev *sd, int enable)
{
struct vimc_deb_device *vdeb = v4l2_get_subdevdata(sd);
- int ret;
if (enable) {
const struct vimc_pix_map *vpix;
@@ -351,22 +350,10 @@ static int vimc_deb_s_stream(struct v4l2_subdev *sd, int enable)
if (!vdeb->src_frame)
return -ENOMEM;
- /* Turn the stream on in the subdevices directly connected */
- ret = vimc_pipeline_s_stream(&vdeb->sd.entity, 1);
- if (ret) {
- vfree(vdeb->src_frame);
- vdeb->src_frame = NULL;
- return ret;
- }
} else {
if (!vdeb->src_frame)
return 0;
- /* Disable streaming from the pipe */
- ret = vimc_pipeline_s_stream(&vdeb->sd.entity, 0);
- if (ret)
- return ret;
-
vfree(vdeb->src_frame);
vdeb->src_frame = NULL;
}
@@ -480,9 +467,8 @@ static void vimc_deb_calc_rgb_sink(struct vimc_deb_device *vdeb,
}
}
-static void vimc_deb_process_frame(struct vimc_ent_device *ved,
- struct media_pad *sink,
- const void *sink_frame)
+static void *vimc_deb_process_frame(struct vimc_ent_device *ved,
+ const void *sink_frame)
{
struct vimc_deb_device *vdeb = container_of(ved, struct vimc_deb_device,
ved);
@@ -491,7 +477,7 @@ static void vimc_deb_process_frame(struct vimc_ent_device *ved,
/* If the stream in this node is not active, just return */
if (!vdeb->src_frame)
- return;
+ return ERR_PTR(-EINVAL);
for (i = 0; i < vdeb->sink_fmt.height; i++)
for (j = 0; j < vdeb->sink_fmt.width; j++) {
@@ -499,12 +485,8 @@ static void vimc_deb_process_frame(struct vimc_ent_device *ved,
vdeb->set_rgb_src(vdeb, i, j, rgb);
}
- /* Propagate the frame through all source pads */
- for (i = 1; i < vdeb->sd.entity.num_pads; i++) {
- struct media_pad *pad = &vdeb->sd.entity.pads[i];
+ return vdeb->src_frame;
- vimc_propagate_frame(pad, vdeb->src_frame);
- }
}
static void vimc_deb_comp_unbind(struct device *comp, struct device *master,
diff --git a/drivers/media/platform/vimc/vimc-scaler.c b/drivers/media/platform/vimc/vimc-scaler.c
index b0952ee86296..39b2a73dfcc1 100644
--- a/drivers/media/platform/vimc/vimc-scaler.c
+++ b/drivers/media/platform/vimc/vimc-scaler.c
@@ -217,7 +217,6 @@ static const struct v4l2_subdev_pad_ops vimc_sca_pad_ops = {
static int vimc_sca_s_stream(struct v4l2_subdev *sd, int enable)
{
struct vimc_sca_device *vsca = v4l2_get_subdevdata(sd);
- int ret;
if (enable) {
const struct vimc_pix_map *vpix;
@@ -245,22 +244,10 @@ static int vimc_sca_s_stream(struct v4l2_subdev *sd, int enable)
if (!vsca->src_frame)
return -ENOMEM;
- /* Turn the stream on in the subdevices directly connected */
- ret = vimc_pipeline_s_stream(&vsca->sd.entity, 1);
- if (ret) {
- vfree(vsca->src_frame);
- vsca->src_frame = NULL;
- return ret;
- }
} else {
if (!vsca->src_frame)
return 0;
- /* Disable streaming from the pipe */
- ret = vimc_pipeline_s_stream(&vsca->sd.entity, 0);
- if (ret)
- return ret;
-
vfree(vsca->src_frame);
vsca->src_frame = NULL;
}
@@ -346,26 +333,19 @@ static void vimc_sca_fill_src_frame(const struct vimc_sca_device *const vsca,
vimc_sca_scale_pix(vsca, i, j, sink_frame);
}
-static void vimc_sca_process_frame(struct vimc_ent_device *ved,
- struct media_pad *sink,
- const void *sink_frame)
+static void *vimc_sca_process_frame(struct vimc_ent_device *ved,
+ const void *sink_frame)
{
struct vimc_sca_device *vsca = container_of(ved, struct vimc_sca_device,
ved);
- unsigned int i;
/* If the stream in this node is not active, just return */
if (!vsca->src_frame)
- return;
+ return ERR_PTR(-EINVAL);
vimc_sca_fill_src_frame(vsca, sink_frame);
- /* Propagate the frame through all source pads */
- for (i = 1; i < vsca->sd.entity.num_pads; i++) {
- struct media_pad *pad = &vsca->sd.entity.pads[i];
-
- vimc_propagate_frame(pad, vsca->src_frame);
- }
+ return vsca->src_frame;
};
static void vimc_sca_comp_unbind(struct device *comp, struct device *master,
diff --git a/drivers/media/platform/vimc/vimc-sensor.c b/drivers/media/platform/vimc/vimc-sensor.c
index 32ca9c6172b1..59195f262623 100644
--- a/drivers/media/platform/vimc/vimc-sensor.c
+++ b/drivers/media/platform/vimc/vimc-sensor.c
@@ -16,8 +16,6 @@
*/
#include <linux/component.h>
-#include <linux/freezer.h>
-#include <linux/kthread.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
@@ -201,38 +199,20 @@ static const struct v4l2_subdev_pad_ops vimc_sen_pad_ops = {
.set_fmt = vimc_sen_set_fmt,
};
-static int vimc_sen_tpg_thread(void *data)
+static void *vimc_sen_process_frame(struct vimc_ent_device *ved,
+ const void *sink_frame)
{
- struct vimc_sen_device *vsen = data;
- unsigned int i;
-
- set_freezable();
- set_current_state(TASK_UNINTERRUPTIBLE);
-
- for (;;) {
- try_to_freeze();
- if (kthread_should_stop())
- break;
+ struct vimc_sen_device *vsen = container_of(ved, struct vimc_sen_device,
+ ved);
- tpg_fill_plane_buffer(&vsen->tpg, 0, 0, vsen->frame);
-
- /* Send the frame to all source pads */
- for (i = 0; i < vsen->sd.entity.num_pads; i++)
- vimc_propagate_frame(&vsen->sd.entity.pads[i],
- vsen->frame);
-
- /* 60 frames per second */
- schedule_timeout(HZ/60);
- }
-
- return 0;
+ tpg_fill_plane_buffer(&vsen->tpg, 0, 0, vsen->frame);
+ return vsen->frame;
}
static int vimc_sen_s_stream(struct v4l2_subdev *sd, int enable)
{
struct vimc_sen_device *vsen =
container_of(sd, struct vimc_sen_device, sd);
- int ret;
if (enable) {
const struct vimc_pix_map *vpix;
@@ -258,26 +238,8 @@ static int vimc_sen_s_stream(struct v4l2_subdev *sd, int enable)
/* configure the test pattern generator */
vimc_sen_tpg_s_format(vsen);
- /* Initialize the image generator thread */
- vsen->kthread_sen = kthread_run(vimc_sen_tpg_thread, vsen,
- "%s-sen", vsen->sd.v4l2_dev->name);
- if (IS_ERR(vsen->kthread_sen)) {
- dev_err(vsen->dev, "%s: kernel_thread() failed\n",
- vsen->sd.name);
- vfree(vsen->frame);
- vsen->frame = NULL;
- return PTR_ERR(vsen->kthread_sen);
- }
} else {
- if (!vsen->kthread_sen)
- return 0;
-
- /* Stop image generator */
- ret = kthread_stop(vsen->kthread_sen);
- if (ret)
- return ret;
- vsen->kthread_sen = NULL;
vfree(vsen->frame);
vsen->frame = NULL;
return 0;
@@ -413,6 +375,7 @@ static int vimc_sen_comp_bind(struct device *comp, struct device *master,
if (ret)
goto err_free_hdl;
+ vsen->ved.process_frame = vimc_sen_process_frame;
dev_set_drvdata(comp, &vsen->ved);
vsen->dev = comp;
diff --git a/drivers/media/platform/vimc/vimc-streamer.c b/drivers/media/platform/vimc/vimc-streamer.c
new file mode 100644
index 000000000000..fcc897fb247b
--- /dev/null
+++ b/drivers/media/platform/vimc/vimc-streamer.c
@@ -0,0 +1,188 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * vimc-streamer.c Virtual Media Controller Driver
+ *
+ * Copyright (C) 2018 Lucas A. M. MagalhĂŁes <lucmaga@gmail.com>
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/freezer.h>
+#include <linux/kthread.h>
+
+#include "vimc-streamer.h"
+
+/**
+ * vimc_get_source_entity - get the entity connected with the first sink pad
+ *
+ * @ent: reference media_entity
+ *
+ * Helper function that returns the media entity containing the source pad
+ * linked with the first sink pad from the given media entity pad list.
+ */
+static struct media_entity *vimc_get_source_entity(struct media_entity *ent)
+{
+ struct media_pad *pad;
+ int i;
+
+ for (i = 0; i < ent->num_pads; i++) {
+ if (ent->pads[i].flags & MEDIA_PAD_FL_SOURCE)
+ continue;
+ pad = media_entity_remote_pad(&ent->pads[i]);
+ return pad ? pad->entity : NULL;
+ }
+ return NULL;
+}
+
+/*
+ * vimc_streamer_pipeline_terminate - Disable stream in all ved in stream
+ *
+ * @stream: the pointer to the stream structure with the pipeline to be
+ * disabled.
+ *
+ * Calls s_stream to disable the stream in each entity of the pipeline
+ *
+ */
+static void vimc_streamer_pipeline_terminate(struct vimc_stream *stream)
+{
+ struct media_entity *entity;
+ struct v4l2_subdev *sd;
+
+ while (stream->pipe_size) {
+ stream->pipe_size--;
+ entity = stream->ved_pipeline[stream->pipe_size]->ent;
+ entity = vimc_get_source_entity(entity);
+ stream->ved_pipeline[stream->pipe_size] = NULL;
+
+ if (!is_media_entity_v4l2_subdev(entity))
+ continue;
+
+ sd = media_entity_to_v4l2_subdev(entity);
+ v4l2_subdev_call(sd, video, s_stream, 0);
+ }
+}
+
+/*
+ * vimc_streamer_pipeline_init - initializes the stream structure
+ *
+ * @stream: the pointer to the stream structure to be initialized
+ * @ved: the pointer to the vimc entity initializing the stream
+ *
+ * Initializes the stream structure. Walks through the entity graph to
+ * construct the pipeline used later on the streamer thread.
+ * Calls s_stream to enable stream in all entities of the pipeline.
+ */
+static int vimc_streamer_pipeline_init(struct vimc_stream *stream,
+ struct vimc_ent_device *ved)
+{
+ struct media_entity *entity;
+ struct video_device *vdev;
+ struct v4l2_subdev *sd;
+ int ret = 0;
+
+ stream->pipe_size = 0;
+ while (stream->pipe_size < VIMC_STREAMER_PIPELINE_MAX_SIZE) {
+ if (!ved) {
+ vimc_streamer_pipeline_terminate(stream);
+ return -EINVAL;
+ }
+ stream->ved_pipeline[stream->pipe_size++] = ved;
+
+ entity = vimc_get_source_entity(ved->ent);
+ /* Check if the end of the pipeline was reached*/
+ if (!entity)
+ return 0;
+
+ if (is_media_entity_v4l2_subdev(entity)) {
+ sd = media_entity_to_v4l2_subdev(entity);
+ ret = v4l2_subdev_call(sd, video, s_stream, 1);
+ if (ret && ret != -ENOIOCTLCMD) {
+ vimc_streamer_pipeline_terminate(stream);
+ return ret;
+ }
+ ved = v4l2_get_subdevdata(sd);
+ } else {
+ vdev = container_of(entity,
+ struct video_device,
+ entity);
+ ved = video_get_drvdata(vdev);
+ }
+ }
+
+ vimc_streamer_pipeline_terminate(stream);
+ return -EINVAL;
+}
+
+static int vimc_streamer_thread(void *data)
+{
+ struct vimc_stream *stream = data;
+ int i;
+
+ set_freezable();
+ set_current_state(TASK_UNINTERRUPTIBLE);
+
+ for (;;) {
+ try_to_freeze();
+ if (kthread_should_stop())
+ break;
+
+ for (i = stream->pipe_size - 1; i >= 0; i--) {
+ stream->frame = stream->ved_pipeline[i]->process_frame(
+ stream->ved_pipeline[i],
+ stream->frame);
+ if (!stream->frame)
+ break;
+ if (IS_ERR(stream->frame))
+ break;
+ }
+ //wait for 60hz
+ schedule_timeout(HZ / 60);
+ }
+
+ return 0;
+}
+
+int vimc_streamer_s_stream(struct vimc_stream *stream,
+ struct vimc_ent_device *ved,
+ int enable)
+{
+ int ret;
+
+ if (!stream || !ved)
+ return -EINVAL;
+
+ if (enable) {
+ if (stream->kthread)
+ return 0;
+
+ ret = vimc_streamer_pipeline_init(stream, ved);
+ if (ret)
+ return ret;
+
+ stream->kthread = kthread_run(vimc_streamer_thread, stream,
+ "vimc-streamer thread");
+
+ if (IS_ERR(stream->kthread))
+ return PTR_ERR(stream->kthread);
+
+ } else {
+ if (!stream->kthread)
+ return 0;
+
+ ret = kthread_stop(stream->kthread);
+ if (ret)
+ return ret;
+
+ stream->kthread = NULL;
+
+ vimc_streamer_pipeline_terminate(stream);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vimc_streamer_s_stream);
+
+MODULE_DESCRIPTION("Virtual Media Controller Driver (VIMC) Streamer");
+MODULE_AUTHOR("Lucas A. M. MagalhĂŁes <lucmaga@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/vimc/vimc-streamer.h b/drivers/media/platform/vimc/vimc-streamer.h
new file mode 100644
index 000000000000..752af2e2d5a2
--- /dev/null
+++ b/drivers/media/platform/vimc/vimc-streamer.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * vimc-streamer.h Virtual Media Controller Driver
+ *
+ * Copyright (C) 2018 Lucas A. M. MagalhĂŁes <lucmaga@gmail.com>
+ *
+ */
+
+#ifndef _VIMC_STREAMER_H_
+#define _VIMC_STREAMER_H_
+
+#include <media/media-device.h>
+
+#include "vimc-common.h"
+
+#define VIMC_STREAMER_PIPELINE_MAX_SIZE 16
+
+struct vimc_stream {
+ struct media_pipeline pipe;
+ struct vimc_ent_device *ved_pipeline[VIMC_STREAMER_PIPELINE_MAX_SIZE];
+ unsigned int pipe_size;
+ u8 *frame;
+ struct task_struct *kthread;
+};
+
+/**
+ * vimc_streamer_s_streamer - start/stop the stream
+ *
+ * @stream: the pointer to the stream to start or stop
+ * @ved: The last entity of the streamer pipeline
+ * @enable: any non-zero number start the stream, zero stop
+ *
+ */
+int vimc_streamer_s_stream(struct vimc_stream *stream,
+ struct vimc_ent_device *ved,
+ int enable);
+
+#endif //_VIMC_STREAMER_H_
diff --git a/drivers/media/platform/vivid/vivid-core.c b/drivers/media/platform/vivid/vivid-core.c
index c931f007e5b0..342e0e6c103b 100644
--- a/drivers/media/platform/vivid/vivid-core.c
+++ b/drivers/media/platform/vivid/vivid-core.c
@@ -371,7 +371,7 @@ static int vidioc_s_parm(struct file *file, void *fh,
if (vdev->vfl_dir == VFL_DIR_RX)
return vivid_vid_cap_s_parm(file, fh, parm);
- return vivid_vid_out_g_parm(file, fh, parm);
+ return -ENOTTY;
}
static int vidioc_log_status(struct file *file, void *fh)
@@ -1094,7 +1094,9 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
q = &dev->vb_vid_cap_q;
q->type = dev->multiplanar ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE :
V4L2_BUF_TYPE_VIDEO_CAPTURE;
- q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ;
+ q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ;
+ if (!allocator)
+ q->io_modes |= VB2_USERPTR;
q->drv_priv = dev;
q->buf_struct_size = sizeof(struct vivid_buffer);
q->ops = &vivid_vid_cap_qops;
@@ -1115,7 +1117,9 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
q = &dev->vb_vid_out_q;
q->type = dev->multiplanar ? V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE :
V4L2_BUF_TYPE_VIDEO_OUTPUT;
- q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_WRITE;
+ q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_WRITE;
+ if (!allocator)
+ q->io_modes |= VB2_USERPTR;
q->drv_priv = dev;
q->buf_struct_size = sizeof(struct vivid_buffer);
q->ops = &vivid_vid_out_qops;
@@ -1136,7 +1140,9 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
q = &dev->vb_vbi_cap_q;
q->type = dev->has_raw_vbi_cap ? V4L2_BUF_TYPE_VBI_CAPTURE :
V4L2_BUF_TYPE_SLICED_VBI_CAPTURE;
- q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ;
+ q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ;
+ if (!allocator)
+ q->io_modes |= VB2_USERPTR;
q->drv_priv = dev;
q->buf_struct_size = sizeof(struct vivid_buffer);
q->ops = &vivid_vbi_cap_qops;
@@ -1157,7 +1163,9 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
q = &dev->vb_vbi_out_q;
q->type = dev->has_raw_vbi_out ? V4L2_BUF_TYPE_VBI_OUTPUT :
V4L2_BUF_TYPE_SLICED_VBI_OUTPUT;
- q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_WRITE;
+ q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_WRITE;
+ if (!allocator)
+ q->io_modes |= VB2_USERPTR;
q->drv_priv = dev;
q->buf_struct_size = sizeof(struct vivid_buffer);
q->ops = &vivid_vbi_out_qops;
@@ -1177,7 +1185,9 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
/* initialize sdr_cap queue */
q = &dev->vb_sdr_cap_q;
q->type = V4L2_BUF_TYPE_SDR_CAPTURE;
- q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ;
+ q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ;
+ if (!allocator)
+ q->io_modes |= VB2_USERPTR;
q->drv_priv = dev;
q->buf_struct_size = sizeof(struct vivid_buffer);
q->ops = &vivid_sdr_cap_qops;
@@ -1468,9 +1478,6 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
return 0;
unreg_dev:
-#ifdef CONFIG_MEDIA_CONTROLLER
- media_device_unregister(&dev->mdev);
-#endif
video_unregister_device(&dev->radio_tx_dev);
video_unregister_device(&dev->radio_rx_dev);
video_unregister_device(&dev->sdr_cap_dev);
@@ -1543,6 +1550,7 @@ static int vivid_remove(struct platform_device *pdev)
#ifdef CONFIG_MEDIA_CONTROLLER
media_device_unregister(&dev->mdev);
+ media_device_cleanup(&dev->mdev);
#endif
if (dev->has_vid_cap) {
diff --git a/drivers/media/platform/vivid/vivid-vid-cap.c b/drivers/media/platform/vivid/vivid-vid-cap.c
index c059fc12668a..52eeda624d7e 100644
--- a/drivers/media/platform/vivid/vivid-vid-cap.c
+++ b/drivers/media/platform/vivid/vivid-vid-cap.c
@@ -124,7 +124,8 @@ static int vid_cap_queue_setup(struct vb2_queue *vq,
}
} else {
for (p = 0; p < buffers; p++)
- sizes[p] = tpg_g_line_width(&dev->tpg, p) * h +
+ sizes[p] = (tpg_g_line_width(&dev->tpg, p) * h) /
+ dev->fmt_cap->vdownsampling[p] +
dev->fmt_cap->data_offset[p];
}
@@ -161,7 +162,9 @@ static int vid_cap_buf_prepare(struct vb2_buffer *vb)
return -EINVAL;
}
for (p = 0; p < buffers; p++) {
- size = tpg_g_line_width(&dev->tpg, p) * dev->fmt_cap_rect.height +
+ size = (tpg_g_line_width(&dev->tpg, p) *
+ dev->fmt_cap_rect.height) /
+ dev->fmt_cap->vdownsampling[p] +
dev->fmt_cap->data_offset[p];
if (vb2_plane_size(vb, p) < size) {
@@ -545,7 +548,8 @@ int vivid_g_fmt_vid_cap(struct file *file, void *priv,
for (p = 0; p < mp->num_planes; p++) {
mp->plane_fmt[p].bytesperline = tpg_g_bytesperline(&dev->tpg, p);
mp->plane_fmt[p].sizeimage =
- tpg_g_line_width(&dev->tpg, p) * mp->height +
+ (tpg_g_line_width(&dev->tpg, p) * mp->height) /
+ dev->fmt_cap->vdownsampling[p] +
dev->fmt_cap->data_offset[p];
}
return 0;
diff --git a/drivers/media/platform/vivid/vivid-vid-common.c b/drivers/media/platform/vivid/vivid-vid-common.c
index 661f4015fba1..74b83bcc6119 100644
--- a/drivers/media/platform/vivid/vivid-vid-common.c
+++ b/drivers/media/platform/vivid/vivid-vid-common.c
@@ -169,6 +169,36 @@ struct vivid_fmt vivid_formats[] = {
.alpha_mask = 0x000000ff,
},
{
+ .fourcc = V4L2_PIX_FMT_AYUV32,
+ .vdownsampling = { 1 },
+ .bit_depth = { 32 },
+ .planes = 1,
+ .buffers = 1,
+ .alpha_mask = 0x000000ff,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_XYUV32,
+ .vdownsampling = { 1 },
+ .bit_depth = { 32 },
+ .planes = 1,
+ .buffers = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_VUYA32,
+ .vdownsampling = { 1 },
+ .bit_depth = { 32 },
+ .planes = 1,
+ .buffers = 1,
+ .alpha_mask = 0xff000000,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_VUYX32,
+ .vdownsampling = { 1 },
+ .bit_depth = { 32 },
+ .planes = 1,
+ .buffers = 1,
+ },
+ {
.fourcc = V4L2_PIX_FMT_GREY,
.vdownsampling = { 1 },
.bit_depth = { 8 },
diff --git a/drivers/media/platform/vivid/vivid-vid-out.c b/drivers/media/platform/vivid/vivid-vid-out.c
index ea250aee2b2e..e61b91b414f9 100644
--- a/drivers/media/platform/vivid/vivid-vid-out.c
+++ b/drivers/media/platform/vivid/vivid-vid-out.c
@@ -28,11 +28,12 @@ static int vid_out_queue_setup(struct vb2_queue *vq,
const struct vivid_fmt *vfmt = dev->fmt_out;
unsigned planes = vfmt->buffers;
unsigned h = dev->fmt_out_rect.height;
- unsigned size = dev->bytesperline_out[0] * h;
+ unsigned int size = dev->bytesperline_out[0] * h + vfmt->data_offset[0];
unsigned p;
for (p = vfmt->buffers; p < vfmt->planes; p++)
- size += dev->bytesperline_out[p] * h / vfmt->vdownsampling[p];
+ size += dev->bytesperline_out[p] * h / vfmt->vdownsampling[p] +
+ vfmt->data_offset[p];
if (dev->field_out == V4L2_FIELD_ALTERNATE) {
/*
@@ -62,12 +63,14 @@ static int vid_out_queue_setup(struct vb2_queue *vq,
if (sizes[0] < size)
return -EINVAL;
for (p = 1; p < planes; p++) {
- if (sizes[p] < dev->bytesperline_out[p] * h)
+ if (sizes[p] < dev->bytesperline_out[p] * h +
+ vfmt->data_offset[p])
return -EINVAL;
}
} else {
for (p = 0; p < planes; p++)
- sizes[p] = p ? dev->bytesperline_out[p] * h : size;
+ sizes[p] = p ? dev->bytesperline_out[p] * h +
+ vfmt->data_offset[p] : size;
}
if (vq->num_buffers + *nbuffers < 2)
@@ -81,21 +84,38 @@ static int vid_out_queue_setup(struct vb2_queue *vq,
return 0;
}
-static int vid_out_buf_prepare(struct vb2_buffer *vb)
+static int vid_out_buf_out_validate(struct vb2_buffer *vb)
{
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
- unsigned long size;
- unsigned planes;
+
+ dprintk(dev, 1, "%s\n", __func__);
+
+ if (dev->field_out != V4L2_FIELD_ALTERNATE)
+ vbuf->field = dev->field_out;
+ else if (vbuf->field != V4L2_FIELD_TOP &&
+ vbuf->field != V4L2_FIELD_BOTTOM)
+ return -EINVAL;
+ return 0;
+}
+
+static int vid_out_buf_prepare(struct vb2_buffer *vb)
+{
+ struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
+ const struct vivid_fmt *vfmt = dev->fmt_out;
+ unsigned int planes = vfmt->buffers;
+ unsigned int h = dev->fmt_out_rect.height;
+ unsigned int size = dev->bytesperline_out[0] * h;
unsigned p;
+ for (p = vfmt->buffers; p < vfmt->planes; p++)
+ size += dev->bytesperline_out[p] * h / vfmt->vdownsampling[p];
+
dprintk(dev, 1, "%s\n", __func__);
if (WARN_ON(NULL == dev->fmt_out))
return -EINVAL;
- planes = dev->fmt_out->planes;
-
if (dev->buf_prepare_error) {
/*
* Error injection: test what happens if buf_prepare() returns
@@ -105,18 +125,13 @@ static int vid_out_buf_prepare(struct vb2_buffer *vb)
return -EINVAL;
}
- if (dev->field_out != V4L2_FIELD_ALTERNATE)
- vbuf->field = dev->field_out;
- else if (vbuf->field != V4L2_FIELD_TOP &&
- vbuf->field != V4L2_FIELD_BOTTOM)
- return -EINVAL;
-
for (p = 0; p < planes; p++) {
- size = dev->bytesperline_out[p] * dev->fmt_out_rect.height +
- vb->planes[p].data_offset;
+ if (p)
+ size = dev->bytesperline_out[p] * h;
+ size += vb->planes[p].data_offset;
if (vb2_get_plane_payload(vb, p) < size) {
- dprintk(dev, 1, "%s the payload is too small for plane %u (%lu < %lu)\n",
+ dprintk(dev, 1, "%s the payload is too small for plane %u (%lu < %u)\n",
__func__, p, vb2_get_plane_payload(vb, p), size);
return -EINVAL;
}
@@ -188,6 +203,7 @@ static void vid_out_buf_request_complete(struct vb2_buffer *vb)
const struct vb2_ops vivid_vid_out_qops = {
.queue_setup = vid_out_queue_setup,
+ .buf_out_validate = vid_out_buf_out_validate,
.buf_prepare = vid_out_buf_prepare,
.buf_queue = vid_out_buf_queue,
.start_streaming = vid_out_start_streaming,
@@ -321,7 +337,8 @@ int vivid_g_fmt_vid_out(struct file *file, void *priv,
for (p = 0; p < mp->num_planes; p++) {
mp->plane_fmt[p].bytesperline = dev->bytesperline_out[p];
mp->plane_fmt[p].sizeimage =
- mp->plane_fmt[p].bytesperline * mp->height;
+ mp->plane_fmt[p].bytesperline * mp->height +
+ fmt->data_offset[p];
}
for (p = fmt->buffers; p < fmt->planes; p++) {
unsigned stride = dev->bytesperline_out[p];
@@ -399,7 +416,7 @@ int vivid_try_fmt_vid_out(struct file *file, void *priv,
pfmt[p].bytesperline = bytesperline;
pfmt[p].sizeimage = (pfmt[p].bytesperline * mp->height) /
- fmt->vdownsampling[p];
+ fmt->vdownsampling[p] + fmt->data_offset[p];
memset(pfmt[p].reserved, 0, sizeof(pfmt[p].reserved));
}
diff --git a/drivers/media/platform/vsp1/vsp1_brx.c b/drivers/media/platform/vsp1/vsp1_brx.c
index 5e50178b057d..58ad248cd7f7 100644
--- a/drivers/media/platform/vsp1/vsp1_brx.c
+++ b/drivers/media/platform/vsp1/vsp1_brx.c
@@ -296,7 +296,7 @@ static void brx_configure_stream(struct vsp1_entity *entity,
/*
* The hardware is extremely flexible but we have no userspace API to
* expose all the parameters, nor is it clear whether we would have use
- * cases for all the supported modes. Let's just harcode the parameters
+ * cases for all the supported modes. Let's just hardcode the parameters
* to sane default values for now.
*/
@@ -373,7 +373,7 @@ static void brx_configure_stream(struct vsp1_entity *entity,
vsp1_brx_write(brx, dlb, VI6_BRU_CTRL(i), ctrl);
/*
- * Harcode the blending formula to
+ * Hardcode the blending formula to
*
* DSTc = DSTc * (1 - SRCa) + SRCc * SRCa
* DSTa = DSTa * (1 - SRCa) + SRCa
diff --git a/drivers/media/platform/vsp1/vsp1_drm.c b/drivers/media/platform/vsp1/vsp1_drm.c
index 8d86f618ec77..84895385d2e5 100644
--- a/drivers/media/platform/vsp1/vsp1_drm.c
+++ b/drivers/media/platform/vsp1/vsp1_drm.c
@@ -333,19 +333,19 @@ static int vsp1_du_pipeline_setup_brx(struct vsp1_device *vsp1,
* on the BRx sink pad 0 and propagated inside the entity, not on the
* source pad.
*/
- format.pad = pipe->brx->source_pad;
+ format.pad = brx->source_pad;
format.format.width = drm_pipe->width;
format.format.height = drm_pipe->height;
format.format.field = V4L2_FIELD_NONE;
- ret = v4l2_subdev_call(&pipe->brx->subdev, pad, set_fmt, NULL,
+ ret = v4l2_subdev_call(&brx->subdev, pad, set_fmt, NULL,
&format);
if (ret < 0)
return ret;
dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on %s pad %u\n",
__func__, format.format.width, format.format.height,
- format.format.code, BRX_NAME(pipe->brx), pipe->brx->source_pad);
+ format.format.code, BRX_NAME(brx), brx->source_pad);
if (format.format.width != drm_pipe->width ||
format.format.height != drm_pipe->height) {
diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c
index 771dfe1f7c20..7ceaf3222145 100644
--- a/drivers/media/platform/vsp1/vsp1_video.c
+++ b/drivers/media/platform/vsp1/vsp1_video.c
@@ -223,7 +223,7 @@ static void vsp1_video_calculate_partition(struct vsp1_pipeline *pipe,
* If the modulus is less than half of the partition size,
* the penultimate partition is reduced to half, which is added
* to the final partition: |1234|1234|1234|12|341|
- * to prevents this: |1234|1234|1234|1234|1|.
+ * to prevent this: |1234|1234|1234|1234|1|.
*/
if (modulus) {
/*
diff --git a/drivers/media/platform/xilinx/xilinx-vip.c b/drivers/media/platform/xilinx/xilinx-vip.c
index 18f98838111b..08a825c3a3f6 100644
--- a/drivers/media/platform/xilinx/xilinx-vip.c
+++ b/drivers/media/platform/xilinx/xilinx-vip.c
@@ -166,7 +166,7 @@ EXPORT_SYMBOL_GPL(xvip_set_format_size);
* the register, otherwise the bitmask is cleared from the register
* when the flag @set is false.
*
- * Fox eample, this function can be used to set a control with a boolean value
+ * Fox example, this function can be used to set a control with a boolean value
* requested by users. If the caller knows whether to set or clear in the first
* place, the caller should call xvip_clr() or xvip_set() directly instead of
* using this function.
diff --git a/drivers/media/radio/radio-si476x.c b/drivers/media/radio/radio-si476x.c
index 269971145f88..0261f4d28f16 100644
--- a/drivers/media/radio/radio-si476x.c
+++ b/drivers/media/radio/radio-si476x.c
@@ -1550,7 +1550,7 @@ static int si476x_radio_probe(struct platform_device *pdev)
rval = si476x_radio_init_debugfs(radio);
if (rval < 0) {
- dev_err(&pdev->dev, "Could not creat debugfs interface\n");
+ dev_err(&pdev->dev, "Could not create debugfs interface\n");
goto exit;
}
diff --git a/drivers/media/radio/si470x/radio-si470x-i2c.c b/drivers/media/radio/si470x/radio-si470x-i2c.c
index 9751ea1d80be..15eea2b2c90f 100644
--- a/drivers/media/radio/si470x/radio-si470x-i2c.c
+++ b/drivers/media/radio/si470x/radio-si470x-i2c.c
@@ -28,6 +28,7 @@
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
#include "radio-si470x.h"
@@ -350,7 +351,7 @@ static int si470x_i2c_probe(struct i2c_client *client,
unsigned char version_warning = 0;
/* private data allocation and initialization */
- radio = kzalloc(sizeof(struct si470x_device), GFP_KERNEL);
+ radio = devm_kzalloc(&client->dev, sizeof(*radio), GFP_KERNEL);
if (!radio) {
retval = -ENOMEM;
goto err_initial;
@@ -370,7 +371,7 @@ static int si470x_i2c_probe(struct i2c_client *client,
retval = v4l2_device_register(&client->dev, &radio->v4l2_dev);
if (retval < 0) {
dev_err(&client->dev, "couldn't register v4l2_device\n");
- goto err_radio;
+ goto err_initial;
}
v4l2_ctrl_handler_init(&radio->hdl, 2);
@@ -392,18 +393,29 @@ static int si470x_i2c_probe(struct i2c_client *client,
radio->videodev.release = video_device_release_empty;
video_set_drvdata(&radio->videodev, radio);
+ radio->gpio_reset = devm_gpiod_get_optional(&client->dev, "reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(radio->gpio_reset)) {
+ retval = PTR_ERR(radio->gpio_reset);
+ dev_err(&client->dev, "Failed to request gpio: %d\n", retval);
+ goto err_all;
+ }
+
+ if (radio->gpio_reset)
+ gpiod_set_value(radio->gpio_reset, 1);
+
/* power up : need 110ms */
radio->registers[POWERCFG] = POWERCFG_ENABLE;
if (si470x_set_register(radio, POWERCFG) < 0) {
retval = -EIO;
- goto err_ctrl;
+ goto err_all;
}
msleep(110);
/* get device and chip versions */
if (si470x_get_all_registers(radio) < 0) {
retval = -EIO;
- goto err_ctrl;
+ goto err_all;
}
dev_info(&client->dev, "DeviceID=0x%4.4hx ChipID=0x%4.4hx\n",
radio->registers[DEVICEID], radio->registers[SI_CHIPID]);
@@ -430,10 +442,10 @@ static int si470x_i2c_probe(struct i2c_client *client,
/* rds buffer allocation */
radio->buf_size = rds_buf * 3;
- radio->buffer = kmalloc(radio->buf_size, GFP_KERNEL);
+ radio->buffer = devm_kmalloc(&client->dev, radio->buf_size, GFP_KERNEL);
if (!radio->buffer) {
retval = -EIO;
- goto err_ctrl;
+ goto err_all;
}
/* rds buffer configuration */
@@ -441,12 +453,13 @@ static int si470x_i2c_probe(struct i2c_client *client,
radio->rd_index = 0;
init_waitqueue_head(&radio->read_queue);
- retval = request_threaded_irq(client->irq, NULL, si470x_i2c_interrupt,
- IRQF_TRIGGER_FALLING | IRQF_ONESHOT, DRIVER_NAME,
- radio);
+ retval = devm_request_threaded_irq(&client->dev, client->irq, NULL,
+ si470x_i2c_interrupt,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ DRIVER_NAME, radio);
if (retval) {
dev_err(&client->dev, "Failed to register interrupt\n");
- goto err_rds;
+ goto err_all;
}
/* register video device */
@@ -460,15 +473,9 @@ static int si470x_i2c_probe(struct i2c_client *client,
return 0;
err_all:
- free_irq(client->irq, radio);
-err_rds:
- kfree(radio->buffer);
-err_ctrl:
v4l2_ctrl_handler_free(&radio->hdl);
err_dev:
v4l2_device_unregister(&radio->v4l2_dev);
-err_radio:
- kfree(radio);
err_initial:
return retval;
}
@@ -481,9 +488,10 @@ static int si470x_i2c_remove(struct i2c_client *client)
{
struct si470x_device *radio = i2c_get_clientdata(client);
- free_irq(client->irq, radio);
video_unregister_device(&radio->videodev);
- kfree(radio);
+
+ if (radio->gpio_reset)
+ gpiod_set_value(radio->gpio_reset, 0);
return 0;
}
@@ -527,6 +535,13 @@ static int si470x_i2c_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(si470x_i2c_pm, si470x_i2c_suspend, si470x_i2c_resume);
#endif
+#if IS_ENABLED(CONFIG_OF)
+static const struct of_device_id si470x_of_match[] = {
+ { .compatible = "silabs,si470x" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, si470x_of_match);
+#endif
/*
* si470x_i2c_driver - i2c driver interface
@@ -534,6 +549,7 @@ static SIMPLE_DEV_PM_OPS(si470x_i2c_pm, si470x_i2c_suspend, si470x_i2c_resume);
static struct i2c_driver si470x_i2c_driver = {
.driver = {
.name = "si470x",
+ .of_match_table = of_match_ptr(si470x_of_match),
#ifdef CONFIG_PM_SLEEP
.pm = &si470x_i2c_pm,
#endif
diff --git a/drivers/media/radio/si470x/radio-si470x.h b/drivers/media/radio/si470x/radio-si470x.h
index 35fa0f3bbdd2..6fd6a399cb77 100644
--- a/drivers/media/radio/si470x/radio-si470x.h
+++ b/drivers/media/radio/si470x/radio-si470x.h
@@ -189,6 +189,7 @@ struct si470x_device {
#if IS_ENABLED(CONFIG_I2C_SI470X)
struct i2c_client *client;
+ struct gpio_desc *gpio_reset;
#endif
};
diff --git a/drivers/media/radio/wl128x/fmdrv.h b/drivers/media/radio/wl128x/fmdrv.h
index 1ff2eec4ed52..4c0d13539988 100644
--- a/drivers/media/radio/wl128x/fmdrv.h
+++ b/drivers/media/radio/wl128x/fmdrv.h
@@ -133,7 +133,7 @@ struct fm_rds {
/*
* Current RX channel Alternate Frequency cache.
* This info is used to switch to other freq (AF)
- * when current channel signal strengh is below RSSI threshold.
+ * when current channel signal strength is below RSSI threshold.
*/
struct tuned_station_info {
u16 picode;
@@ -228,7 +228,7 @@ struct fmdev {
struct fm_rx rx; /* FM receiver info */
struct fmtx_data tx_data;
- /* V4L2 ctrl framwork handler*/
+ /* V4L2 ctrl framework handler*/
struct v4l2_ctrl_handler ctrl_handler;
/* For core assisted locking */
diff --git a/drivers/media/radio/wl128x/fmdrv_common.c b/drivers/media/radio/wl128x/fmdrv_common.c
index 800d69c3f80b..3c8987af3772 100644
--- a/drivers/media/radio/wl128x/fmdrv_common.c
+++ b/drivers/media/radio/wl128x/fmdrv_common.c
@@ -908,7 +908,7 @@ static void fm_irq_afjump_setfreq(struct fmdev *fmdev)
u16 frq_index;
u16 payload;
- fmdbg("Swtich to %d KHz\n", fmdev->rx.stat_info.af_cache[fmdev->rx.afjump_idx]);
+ fmdbg("Switch to %d KHz\n", fmdev->rx.stat_info.af_cache[fmdev->rx.afjump_idx]);
frq_index = (fmdev->rx.stat_info.af_cache[fmdev->rx.afjump_idx] -
fmdev->rx.region.bot_freq) / FM_FREQ_MUL;
@@ -1047,7 +1047,7 @@ static void fm_irq_handle_intmsk_cmd_resp(struct fmdev *fmdev)
clear_bit(FM_INTTASK_RUNNING, &fmdev->flag);
}
-/* Returns availability of RDS data in internel buffer */
+/* Returns availability of RDS data in internal buffer */
int fmc_is_rds_data_available(struct fmdev *fmdev, struct file *file,
struct poll_table_struct *pts)
{
diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig
index 8a216068a35a..96ce3e5524e0 100644
--- a/drivers/media/rc/Kconfig
+++ b/drivers/media/rc/Kconfig
@@ -133,6 +133,19 @@ config IR_IMON_DECODER
remote control and you would like to use it with a raw IR
receiver, or if you wish to use an encoder to transmit this IR.
+config IR_RCMM_DECODER
+ tristate "Enable IR raw decoder for the RC-MM protocol"
+ depends on RC_CORE
+ help
+ Enable this option when you have IR with RC-MM protocol, and
+ you need the software decoder. The driver supports 12,
+ 24 and 32 bits RC-MM variants. You can enable or disable the
+ different modes using the following RC protocol keywords:
+ 'rc-mm-12', 'rc-mm-24' and 'rc-mm-32'.
+
+ To compile this driver as a module, choose M here: the module
+ will be called ir-rcmm-decoder.
+
endif #RC_DECODERS
menuconfig RC_DEVICES
@@ -240,7 +253,7 @@ config IR_FINTEK
depends on RC_CORE
---help---
Say Y here to enable support for integrated infrared receiver
- /transciever made by Fintek. This chip is found on assorted
+ /transceiver made by Fintek. This chip is found on assorted
Jetway motherboards (and of course, possibly others).
To compile this driver as a module, choose M here: the
@@ -274,7 +287,7 @@ config IR_NUVOTON
depends on RC_CORE
---help---
Say Y here to enable support for integrated infrared receiver
- /transciever made by Nuvoton (formerly Winbond). This chip is
+ /transceiver made by Nuvoton (formerly Winbond). This chip is
found in the ASRock ION 330HT, as well as assorted Intel
DP55-series motherboards (and of course, possibly others).
diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile
index 92c163816849..48d23433b3c0 100644
--- a/drivers/media/rc/Makefile
+++ b/drivers/media/rc/Makefile
@@ -16,6 +16,7 @@ obj-$(CONFIG_IR_SHARP_DECODER) += ir-sharp-decoder.o
obj-$(CONFIG_IR_MCE_KBD_DECODER) += ir-mce_kbd-decoder.o
obj-$(CONFIG_IR_XMP_DECODER) += ir-xmp-decoder.o
obj-$(CONFIG_IR_IMON_DECODER) += ir-imon-decoder.o
+obj-$(CONFIG_IR_RCMM_DECODER) += ir-rcmm-decoder.o
# stand-alone IR receivers/transmitters
obj-$(CONFIG_RC_ATI_REMOTE) += ati_remote.o
diff --git a/drivers/media/rc/ati_remote.c b/drivers/media/rc/ati_remote.c
index 265e91a2a70d..bc2da64858c3 100644
--- a/drivers/media/rc/ati_remote.c
+++ b/drivers/media/rc/ati_remote.c
@@ -304,7 +304,7 @@ static const struct {
{KIND_LITERAL, 0x7c, BTN_RIGHT},/* right btn down */
{KIND_LITERAL, 0x7d, BTN_RIGHT},/* right btn up */
- /* Artificial "doubleclick" events are generated by the hardware.
+ /* Artificial "double-click" events are generated by the hardware.
* They are mapped to the "side" and "extra" mouse buttons here. */
{KIND_FILTERED, 0x7a, BTN_SIDE}, /* left dblclick */
{KIND_FILTERED, 0x7e, BTN_EXTRA},/* right dblclick */
diff --git a/drivers/media/rc/ene_ir.c b/drivers/media/rc/ene_ir.c
index dd2fd307ef85..293ccee2c05e 100644
--- a/drivers/media/rc/ene_ir.c
+++ b/drivers/media/rc/ene_ir.c
@@ -184,7 +184,7 @@ static int ene_hw_detect(struct ene_device *dev)
return 0;
}
-/* Read properities of hw sample buffer */
+/* Read properties of hw sample buffer */
static void ene_rx_setup_hw_buffer(struct ene_device *dev)
{
u16 tmp;
diff --git a/drivers/media/rc/ene_ir.h b/drivers/media/rc/ene_ir.h
index 494646b2a284..0a45352efe40 100644
--- a/drivers/media/rc/ene_ir.h
+++ b/drivers/media/rc/ene_ir.h
@@ -118,7 +118,7 @@
#define ENE_CIRDAT_IN 0xFEC7
-/* RLC configuration - sample period (1us resulution) + idle mode */
+/* RLC configuration - sample period (1us resolution) + idle mode */
#define ENE_CIRRLC_CFG 0xFEC8
#define ENE_CIRRLC_CFG_OVERFLOW 0x80 /* interrupt on overflows if set */
#define ENE_DEFAULT_SAMPLE_PERIOD 50
diff --git a/drivers/media/rc/fintek-cir.h b/drivers/media/rc/fintek-cir.h
index ac34a774d018..dffe0bbfc6eb 100644
--- a/drivers/media/rc/fintek-cir.h
+++ b/drivers/media/rc/fintek-cir.h
@@ -176,7 +176,7 @@ struct fintek_dev {
#define CIR_CR_IRCS 0x05 /* Before host writes command to IR, host
must set to 1. When host finshes write
command to IR, host must clear to 0. */
-#define CIR_CR_COMMAND_DATA 0x06 /* Host read or write comand data */
+#define CIR_CR_COMMAND_DATA 0x06 /* Host read or write command data */
#define CIR_CR_CLASS 0x07 /* 0xff = rx-only, 0x66 = rx + 2 tx,
0x33 = rx + 1 tx */
#define CIR_CR_DEV_EN 0x30 /* bit0 = 1 enables CIR */
diff --git a/drivers/media/rc/ir-rc6-decoder.c b/drivers/media/rc/ir-rc6-decoder.c
index d96aed1343e4..5cc302fa4daa 100644
--- a/drivers/media/rc/ir-rc6-decoder.c
+++ b/drivers/media/rc/ir-rc6-decoder.c
@@ -40,6 +40,7 @@
#define RC6_6A_MCE_TOGGLE_MASK 0x8000 /* for the body bits */
#define RC6_6A_LCC_MASK 0xffff0000 /* RC6-6A-32 long customer code mask */
#define RC6_6A_MCE_CC 0x800f0000 /* MCE customer code */
+#define RC6_6A_ZOTAC_CC 0x80340000 /* Zotac customer code */
#define RC6_6A_KATHREIN_CC 0x80460000 /* Kathrein RCU-676 customer code */
#ifndef CHAR_BIT
#define CHAR_BIT 8 /* Normally in <limits.h> */
@@ -246,6 +247,7 @@ again:
switch (scancode & RC6_6A_LCC_MASK) {
case RC6_6A_MCE_CC:
case RC6_6A_KATHREIN_CC:
+ case RC6_6A_ZOTAC_CC:
protocol = RC_PROTO_RC6_MCE;
toggle = !!(scancode & RC6_6A_MCE_TOGGLE_MASK);
scancode &= ~RC6_6A_MCE_TOGGLE_MASK;
diff --git a/drivers/media/rc/ir-rcmm-decoder.c b/drivers/media/rc/ir-rcmm-decoder.c
new file mode 100644
index 000000000000..f1096ac1e5c5
--- /dev/null
+++ b/drivers/media/rc/ir-rcmm-decoder.c
@@ -0,0 +1,254 @@
+// SPDX-License-Identifier: GPL-2.0+
+// ir-rcmm-decoder.c - A decoder for the RCMM IR protocol
+//
+// Copyright (C) 2018 by Patrick Lerda <patrick9876@free.fr>
+
+#include "rc-core-priv.h"
+#include <linux/module.h>
+#include <linux/version.h>
+
+#define RCMM_UNIT 166667 /* nanosecs */
+#define RCMM_PREFIX_PULSE 416666 /* 166666.666666666*2.5 */
+#define RCMM_PULSE_0 277777 /* 166666.666666666*(1+2/3) */
+#define RCMM_PULSE_1 444444 /* 166666.666666666*(2+2/3) */
+#define RCMM_PULSE_2 611111 /* 166666.666666666*(3+2/3) */
+#define RCMM_PULSE_3 777778 /* 166666.666666666*(4+2/3) */
+
+enum rcmm_state {
+ STATE_INACTIVE,
+ STATE_LOW,
+ STATE_BUMP,
+ STATE_VALUE,
+ STATE_FINISHED,
+};
+
+static bool rcmm_mode(const struct rcmm_dec *data)
+{
+ return !((0x000c0000 & data->bits) == 0x000c0000);
+}
+
+static int rcmm_miscmode(struct rc_dev *dev, struct rcmm_dec *data)
+{
+ switch (data->count) {
+ case 24:
+ if (dev->enabled_protocols & RC_PROTO_BIT_RCMM24) {
+ rc_keydown(dev, RC_PROTO_RCMM24, data->bits, 0);
+ data->state = STATE_INACTIVE;
+ return 0;
+ }
+ return -1;
+
+ case 12:
+ if (dev->enabled_protocols & RC_PROTO_BIT_RCMM12) {
+ rc_keydown(dev, RC_PROTO_RCMM12, data->bits, 0);
+ data->state = STATE_INACTIVE;
+ return 0;
+ }
+ return -1;
+ }
+
+ return -1;
+}
+
+/**
+ * ir_rcmm_decode() - Decode one RCMM pulse or space
+ * @dev: the struct rc_dev descriptor of the device
+ * @ev: the struct ir_raw_event descriptor of the pulse/space
+ *
+ * This function returns -EINVAL if the pulse violates the state machine
+ */
+static int ir_rcmm_decode(struct rc_dev *dev, struct ir_raw_event ev)
+{
+ struct rcmm_dec *data = &dev->raw->rcmm;
+ u32 scancode;
+ u8 toggle;
+ int value;
+
+ if (!(dev->enabled_protocols & (RC_PROTO_BIT_RCMM32 |
+ RC_PROTO_BIT_RCMM24 |
+ RC_PROTO_BIT_RCMM12)))
+ return 0;
+
+ if (!is_timing_event(ev)) {
+ if (ev.reset)
+ data->state = STATE_INACTIVE;
+ return 0;
+ }
+
+ switch (data->state) {
+ case STATE_INACTIVE:
+ if (!ev.pulse)
+ break;
+
+ if (!eq_margin(ev.duration, RCMM_PREFIX_PULSE, RCMM_UNIT / 2))
+ break;
+
+ data->state = STATE_LOW;
+ data->count = 0;
+ data->bits = 0;
+ return 0;
+
+ case STATE_LOW:
+ if (ev.pulse)
+ break;
+
+ if (!eq_margin(ev.duration, RCMM_PULSE_0, RCMM_UNIT / 2))
+ break;
+
+ data->state = STATE_BUMP;
+ return 0;
+
+ case STATE_BUMP:
+ if (!ev.pulse)
+ break;
+
+ if (!eq_margin(ev.duration, RCMM_UNIT, RCMM_UNIT / 2))
+ break;
+
+ data->state = STATE_VALUE;
+ return 0;
+
+ case STATE_VALUE:
+ if (ev.pulse)
+ break;
+
+ if (eq_margin(ev.duration, RCMM_PULSE_0, RCMM_UNIT / 2))
+ value = 0;
+ else if (eq_margin(ev.duration, RCMM_PULSE_1, RCMM_UNIT / 2))
+ value = 1;
+ else if (eq_margin(ev.duration, RCMM_PULSE_2, RCMM_UNIT / 2))
+ value = 2;
+ else if (eq_margin(ev.duration, RCMM_PULSE_3, RCMM_UNIT / 2))
+ value = 3;
+ else
+ value = -1;
+
+ if (value == -1) {
+ if (!rcmm_miscmode(dev, data))
+ return 0;
+ break;
+ }
+
+ data->bits <<= 2;
+ data->bits |= value;
+
+ data->count += 2;
+
+ if (data->count < 32)
+ data->state = STATE_BUMP;
+ else
+ data->state = STATE_FINISHED;
+
+ return 0;
+
+ case STATE_FINISHED:
+ if (!ev.pulse)
+ break;
+
+ if (!eq_margin(ev.duration, RCMM_UNIT, RCMM_UNIT / 2))
+ break;
+
+ if (rcmm_mode(data)) {
+ toggle = !!(0x8000 & data->bits);
+ scancode = data->bits & ~0x8000;
+ } else {
+ toggle = 0;
+ scancode = data->bits;
+ }
+
+ if (dev->enabled_protocols & RC_PROTO_BIT_RCMM32) {
+ rc_keydown(dev, RC_PROTO_RCMM32, scancode, toggle);
+ data->state = STATE_INACTIVE;
+ return 0;
+ }
+
+ break;
+ }
+
+ data->state = STATE_INACTIVE;
+ return -EINVAL;
+}
+
+static const int rcmmspace[] = {
+ RCMM_PULSE_0,
+ RCMM_PULSE_1,
+ RCMM_PULSE_2,
+ RCMM_PULSE_3,
+};
+
+static int ir_rcmm_rawencoder(struct ir_raw_event **ev, unsigned int max,
+ unsigned int n, u32 data)
+{
+ int i;
+ int ret;
+
+ ret = ir_raw_gen_pulse_space(ev, &max, RCMM_PREFIX_PULSE, RCMM_PULSE_0);
+ if (ret)
+ return ret;
+
+ for (i = n - 2; i >= 0; i -= 2) {
+ const unsigned int space = rcmmspace[(data >> i) & 3];
+
+ ret = ir_raw_gen_pulse_space(ev, &max, RCMM_UNIT, space);
+ if (ret)
+ return ret;
+ }
+
+ return ir_raw_gen_pulse_space(ev, &max, RCMM_UNIT, RCMM_PULSE_3 * 2);
+}
+
+static int ir_rcmm_encode(enum rc_proto protocol, u32 scancode,
+ struct ir_raw_event *events, unsigned int max)
+{
+ struct ir_raw_event *e = events;
+ int ret;
+
+ switch (protocol) {
+ case RC_PROTO_RCMM32:
+ ret = ir_rcmm_rawencoder(&e, max, 32, scancode);
+ break;
+ case RC_PROTO_RCMM24:
+ ret = ir_rcmm_rawencoder(&e, max, 24, scancode);
+ break;
+ case RC_PROTO_RCMM12:
+ ret = ir_rcmm_rawencoder(&e, max, 12, scancode);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ if (ret < 0)
+ return ret;
+
+ return e - events;
+}
+
+static struct ir_raw_handler rcmm_handler = {
+ .protocols = RC_PROTO_BIT_RCMM32 |
+ RC_PROTO_BIT_RCMM24 |
+ RC_PROTO_BIT_RCMM12,
+ .decode = ir_rcmm_decode,
+ .encode = ir_rcmm_encode,
+ .carrier = 36000,
+ .min_timeout = RCMM_PULSE_3 + RCMM_UNIT,
+};
+
+static int __init ir_rcmm_decode_init(void)
+{
+ ir_raw_handler_register(&rcmm_handler);
+
+ pr_info("IR RCMM protocol handler initialized\n");
+ return 0;
+}
+
+static void __exit ir_rcmm_decode_exit(void)
+{
+ ir_raw_handler_unregister(&rcmm_handler);
+}
+
+module_init(ir_rcmm_decode_init);
+module_exit(ir_rcmm_decode_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Patrick Lerda");
+MODULE_DESCRIPTION("RCMM IR protocol decoder");
diff --git a/drivers/media/rc/ir-xmp-decoder.c b/drivers/media/rc/ir-xmp-decoder.c
index c965f51df1c1..2639b0b6d2f8 100644
--- a/drivers/media/rc/ir-xmp-decoder.c
+++ b/drivers/media/rc/ir-xmp-decoder.c
@@ -94,7 +94,7 @@ static int ir_xmp_decode(struct rc_dev *dev, struct ir_raw_event ev)
n = data->durations;
/*
* the 4th nibble should be 15 so base the divider on this
- * to transform durations into nibbles. Substract 2000 from
+ * to transform durations into nibbles. Subtract 2000 from
* the divider to compensate for fluctuations in the signal
*/
divider = (n[3] - XMP_NIBBLE_PREFIX) / 15 - 2000;
diff --git a/drivers/media/rc/ite-cir.c b/drivers/media/rc/ite-cir.c
index cd3c60ba8519..1d48a9e59f93 100644
--- a/drivers/media/rc/ite-cir.c
+++ b/drivers/media/rc/ite-cir.c
@@ -515,7 +515,7 @@ static int ite_tx_ir(struct rc_dev *rcdev, unsigned *txbuf, unsigned n)
/* and set the carrier values for reception */
ite_set_carrier_params(dev);
- /* reenable the receiver */
+ /* re-enable the receiver */
if (dev->in_use)
dev->params.enable_rx(dev);
diff --git a/drivers/media/rc/keymaps/rc-behold-columbus.c b/drivers/media/rc/keymaps/rc-behold-columbus.c
index e73057945bd1..b68380a76010 100644
--- a/drivers/media/rc/keymaps/rc-behold-columbus.c
+++ b/drivers/media/rc/keymaps/rc-behold-columbus.c
@@ -14,7 +14,7 @@
* The "ascii-art picture" below (in comments, first row
* is the keycode in hex, and subsequent row(s) shows
* the button labels (several variants when appropriate)
- * helps to descide which keycodes to assign to the buttons.
+ * helps to decide which keycodes to assign to the buttons.
*/
static struct rc_map_table behold_columbus[] = {
@@ -68,7 +68,7 @@ static struct rc_map_table behold_columbus[] = {
{ 0x18, KEY_VOLUMEDOWN },
/* 0x0E 0x1E 0x0F 0x1A *
- * Stop Pause Previouse Next *
+ * Stop Pause Previous Next *
* */
{ 0x0E, KEY_STOP },
diff --git a/drivers/media/rc/keymaps/rc-behold.c b/drivers/media/rc/keymaps/rc-behold.c
index e1b2c8e26883..2b7cddb2f36d 100644
--- a/drivers/media/rc/keymaps/rc-behold.c
+++ b/drivers/media/rc/keymaps/rc-behold.c
@@ -17,7 +17,7 @@
* The "ascii-art picture" below (in comments, first row
* is the keycode in hex, and subsequent row(s) shows
* the button labels (several variants when appropriate)
- * helps to descide which keycodes to assign to the buttons.
+ * helps to decide which keycodes to assign to the buttons.
*/
static struct rc_map_table behold[] = {
diff --git a/drivers/media/rc/keymaps/rc-manli.c b/drivers/media/rc/keymaps/rc-manli.c
index 29c9feaf413b..5e9a49e2dd6a 100644
--- a/drivers/media/rc/keymaps/rc-manli.c
+++ b/drivers/media/rc/keymaps/rc-manli.c
@@ -14,7 +14,7 @@
The "ascii-art picture" below (in comments, first row
is the keycode in hex, and subsequent row(s) shows
the button labels (several variants when appropriate)
- helps to descide which keycodes to assign to the buttons.
+ helps to decide which keycodes to assign to the buttons.
*/
static struct rc_map_table manli[] = {
diff --git a/drivers/media/rc/keymaps/rc-powercolor-real-angel.c b/drivers/media/rc/keymaps/rc-powercolor-real-angel.c
index 4988e71c524c..cf98cf8dc13c 100644
--- a/drivers/media/rc/keymaps/rc-powercolor-real-angel.c
+++ b/drivers/media/rc/keymaps/rc-powercolor-real-angel.c
@@ -26,7 +26,7 @@ static struct rc_map_table powercolor_real_angel[] = {
{ 0x07, KEY_7 },
{ 0x08, KEY_8 },
{ 0x09, KEY_9 },
- { 0x0a, KEY_DIGITS }, /* single, double, tripple digit */
+ { 0x0a, KEY_DIGITS }, /* single, double, triple digit */
{ 0x29, KEY_PREVIOUS }, /* previous channel */
{ 0x12, KEY_BRIGHTNESSUP },
{ 0x13, KEY_BRIGHTNESSDOWN },
diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c
index 8d7d3ef88862..fa4840940486 100644
--- a/drivers/media/rc/mceusb.c
+++ b/drivers/media/rc/mceusb.c
@@ -79,7 +79,7 @@
#define MCE_CMD 0x1f
#define MCE_PORT_IR 0x4 /* (0x4 << 5) | MCE_CMD = 0x9f */
#define MCE_PORT_SYS 0x7 /* (0x7 << 5) | MCE_CMD = 0xff */
-#define MCE_PORT_SER 0x6 /* 0xc0 thru 0xdf flush & 0x1f bytes */
+#define MCE_PORT_SER 0x6 /* 0xc0 through 0xdf flush & 0x1f bytes */
#define MCE_PORT_MASK 0xe0 /* Mask out command bits */
/* Command port headers */
diff --git a/drivers/media/rc/rc-core-priv.h b/drivers/media/rc/rc-core-priv.h
index c2cbe7f6266c..9f21b3e8b377 100644
--- a/drivers/media/rc/rc-core-priv.h
+++ b/drivers/media/rc/rc-core-priv.h
@@ -131,6 +131,11 @@ struct ir_raw_event_ctrl {
unsigned int bits;
bool stick_keyboard;
} imon;
+ struct rcmm_dec {
+ int state;
+ unsigned int count;
+ u32 bits;
+ } rcmm;
};
/* Mutex for locking raw IR processing and handler change */
diff --git a/drivers/media/rc/rc-ir-raw.c b/drivers/media/rc/rc-ir-raw.c
index e10b4644a442..39dd46bbd0c1 100644
--- a/drivers/media/rc/rc-ir-raw.c
+++ b/drivers/media/rc/rc-ir-raw.c
@@ -186,7 +186,7 @@ int ir_raw_event_store_with_filter(struct rc_dev *dev, struct ir_raw_event *ev)
dev->raw->this_ev = *ev;
}
- /* Enter idle mode if nessesary */
+ /* Enter idle mode if necessary */
if (!ev->pulse && dev->timeout &&
dev->raw->this_ev.duration >= dev->timeout)
ir_raw_event_set_idle(dev, true);
diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c
index 66a174979b3c..e8fa28e20192 100644
--- a/drivers/media/rc/rc-main.c
+++ b/drivers/media/rc/rc-main.c
@@ -70,6 +70,12 @@ static const struct {
[RC_PROTO_CEC] = { .name = "cec", .repeat_period = 0 },
[RC_PROTO_IMON] = { .name = "imon",
.scancode_bits = 0x7fffffff, .repeat_period = 114 },
+ [RC_PROTO_RCMM12] = { .name = "rc-mm-12",
+ .scancode_bits = 0x00000fff, .repeat_period = 114 },
+ [RC_PROTO_RCMM24] = { .name = "rc-mm-24",
+ .scancode_bits = 0x00ffffff, .repeat_period = 114 },
+ [RC_PROTO_RCMM32] = { .name = "rc-mm-32",
+ .scancode_bits = 0xffffffff, .repeat_period = 114 },
};
/* Used to keep track of known keymaps */
@@ -274,6 +280,7 @@ static unsigned int ir_update_mapping(struct rc_dev *dev,
unsigned int new_keycode)
{
int old_keycode = rc_map->scan[index].keycode;
+ int i;
/* Did the user wish to remove the mapping? */
if (new_keycode == KEY_RESERVED || new_keycode == KEY_UNKNOWN) {
@@ -288,9 +295,20 @@ static unsigned int ir_update_mapping(struct rc_dev *dev,
old_keycode == KEY_RESERVED ? "New" : "Replacing",
rc_map->scan[index].scancode, new_keycode);
rc_map->scan[index].keycode = new_keycode;
+ __set_bit(new_keycode, dev->input_dev->keybit);
}
if (old_keycode != KEY_RESERVED) {
+ /* A previous mapping was updated... */
+ __clear_bit(old_keycode, dev->input_dev->keybit);
+ /* ... but another scancode might use the same keycode */
+ for (i = 0; i < rc_map->len; i++) {
+ if (rc_map->scan[i].keycode == old_keycode) {
+ __set_bit(old_keycode, dev->input_dev->keybit);
+ break;
+ }
+ }
+
/* Possibly shrink the keytable, failure is not a problem */
ir_resize_table(dev, rc_map, GFP_ATOMIC);
}
@@ -1006,6 +1024,9 @@ static const struct {
{ RC_PROTO_BIT_XMP, "xmp", "ir-xmp-decoder" },
{ RC_PROTO_BIT_CEC, "cec", NULL },
{ RC_PROTO_BIT_IMON, "imon", "ir-imon-decoder" },
+ { RC_PROTO_BIT_RCMM12 |
+ RC_PROTO_BIT_RCMM24 |
+ RC_PROTO_BIT_RCMM32, "rc-mm", "ir-rcmm-decoder" },
};
/**
@@ -1035,7 +1056,7 @@ struct rc_filter_attribute {
* @buf: a pointer to the output buffer
*
* This routine is a callback routine for input read the IR protocol type(s).
- * it is trigged by reading /sys/class/rc/rc?/protocols.
+ * it is triggered by reading /sys/class/rc/rc?/protocols.
* It returns the protocol names of supported protocols.
* Enabled protocols are printed in brackets.
*
@@ -1206,7 +1227,7 @@ void ir_raw_load_modules(u64 *protocols)
* @len: length of the input buffer
*
* This routine is for changing the IR protocol type.
- * It is trigged by writing to /sys/class/rc/rc?/[wakeup_]protocols.
+ * It is triggered by writing to /sys/class/rc/rc?/[wakeup_]protocols.
* See parse_protocol_change() for the valid commands.
* Returns @len on success or a negative error code.
*
@@ -1290,7 +1311,7 @@ out:
* @buf: a pointer to the output buffer
*
* This routine is a callback routine to read a scancode filter value or mask.
- * It is trigged by reading /sys/class/rc/rc?/[wakeup_]filter[_mask].
+ * It is triggered by reading /sys/class/rc/rc?/[wakeup_]filter[_mask].
* It prints the current scancode filter value or mask of the appropriate filter
* type in hexadecimal into @buf and returns the size of the buffer.
*
@@ -1333,7 +1354,7 @@ static ssize_t show_filter(struct device *device,
* @len: length of the input buffer
*
* This routine is for changing a scancode filter value or mask.
- * It is trigged by writing to /sys/class/rc/rc?/[wakeup_]filter[_mask].
+ * It is triggered by writing to /sys/class/rc/rc?/[wakeup_]filter[_mask].
* Returns -EINVAL if an invalid filter value for the current protocol was
* specified or if scancode filtering is not supported by the driver, otherwise
* returns @len.
@@ -1417,7 +1438,7 @@ unlock:
* @buf: a pointer to the output buffer
*
* This routine is a callback routine for input read the IR protocol type(s).
- * it is trigged by reading /sys/class/rc/rc?/wakeup_protocols.
+ * it is triggered by reading /sys/class/rc/rc?/wakeup_protocols.
* It returns the protocol names of supported protocols.
* The enabled protocols are printed in brackets.
*
@@ -1468,7 +1489,7 @@ static ssize_t show_wakeup_protocols(struct device *device,
* @len: length of the input buffer
*
* This routine is for changing the IR protocol type.
- * It is trigged by writing to /sys/class/rc/rc?/wakeup_protocols.
+ * It is triggered by writing to /sys/class/rc/rc?/wakeup_protocols.
* Returns @len on success or a negative error code.
*
* dev->lock is taken to guard against races between
@@ -1750,7 +1771,6 @@ static int rc_prepare_rx_device(struct rc_dev *dev)
set_bit(EV_REP, dev->input_dev->evbit);
set_bit(EV_MSC, dev->input_dev->evbit);
set_bit(MSC_SCAN, dev->input_dev->mscbit);
- bitmap_fill(dev->input_dev->keybit, KEY_CNT);
/* Pointer/mouse events */
set_bit(EV_REL, dev->input_dev->evbit);
diff --git a/drivers/media/rc/redrat3.c b/drivers/media/rc/redrat3.c
index 08c51ffd74a0..b82a5c9db12c 100644
--- a/drivers/media/rc/redrat3.c
+++ b/drivers/media/rc/redrat3.c
@@ -140,7 +140,7 @@ MODULE_PARM_DESC(length_fuzz, "Length Fuzz (0-127)");
* When receiving a continuous ir stream (for example when a user is
* holding a button down on a remote), this specifies the minimum size
* of a space when the redrat3 sends a irdata packet to the host. Specified
- * in miliseconds. Default value 18ms.
+ * in milliseconds. Default value 18ms.
* The value can be between 2 and 30 inclusive.
*/
static int minimum_pause = 18;
diff --git a/drivers/media/spi/cxd2880-spi.c b/drivers/media/spi/cxd2880-spi.c
index d5c433e20d4a..4077217777f9 100644
--- a/drivers/media/spi/cxd2880-spi.c
+++ b/drivers/media/spi/cxd2880-spi.c
@@ -522,13 +522,15 @@ cxd2880_spi_probe(struct spi_device *spi)
dvb_spi->vcc_supply = devm_regulator_get_optional(&spi->dev, "vcc");
if (IS_ERR(dvb_spi->vcc_supply)) {
- if (PTR_ERR(dvb_spi->vcc_supply) == -EPROBE_DEFER)
- return -EPROBE_DEFER;
+ if (PTR_ERR(dvb_spi->vcc_supply) == -EPROBE_DEFER) {
+ ret = -EPROBE_DEFER;
+ goto fail_adapter;
+ }
dvb_spi->vcc_supply = NULL;
} else {
ret = regulator_enable(dvb_spi->vcc_supply);
if (ret)
- return ret;
+ goto fail_adapter;
}
dvb_spi->spi = spi;
diff --git a/drivers/media/tuners/mxl5005s.c b/drivers/media/tuners/mxl5005s.c
index ec584316c812..1c07e2225fb3 100644
--- a/drivers/media/tuners/mxl5005s.c
+++ b/drivers/media/tuners/mxl5005s.c
@@ -3584,7 +3584,7 @@ static u32 MXL_Ceiling(u32 value, u32 resolution)
return value / resolution + (value % resolution > 0 ? 1 : 0);
}
-/* Retrieve the Initialzation Registers */
+/* Retrieve the Initialization Registers */
static u16 MXL_GetInitRegister(struct dvb_frontend *fe, u8 *RegNum,
u8 *RegVal, int *count)
{
diff --git a/drivers/media/tuners/qm1d1b0004.h b/drivers/media/tuners/qm1d1b0004.h
index 7734ed109a22..7950ecd56430 100644
--- a/drivers/media/tuners/qm1d1b0004.h
+++ b/drivers/media/tuners/qm1d1b0004.h
@@ -14,7 +14,7 @@ struct qm1d1b0004_config {
struct dvb_frontend *fe;
u32 lpf_freq; /* LPF frequency[kHz]. Default: symbol rate */
- bool half_step; /* use PLL frequency step of 500Hz istead of 1000Hz */
+ bool half_step; /* use PLL frequency step of 500Hz instead of 1000Hz */
};
/* special values indicating to use the default in qm1d1b0004_config */
diff --git a/drivers/media/tuners/r820t.c b/drivers/media/tuners/r820t.c
index ba4be08a8551..aed2f130ec74 100644
--- a/drivers/media/tuners/r820t.c
+++ b/drivers/media/tuners/r820t.c
@@ -1664,7 +1664,7 @@ static int r820t_iq_tree(struct r820t_priv *priv,
/*
* record IMC results by input gain/phase location then adjust
- * gain or phase positive 1 step and negtive 1 step,
+ * gain or phase positive 1 step and negative 1 step,
* both record results
*/
@@ -2066,7 +2066,7 @@ static int r820t_imr_callibrate(struct r820t_priv *priv)
}
/*
- * Disables IMR callibration. That emulates the same behaviour
+ * Disables IMR calibration. That emulates the same behaviour
* as what is done by rtl-sdr userspace library. Useful for testing
*/
if (no_imr_cal) {
diff --git a/drivers/media/tuners/tda18271-common.c b/drivers/media/tuners/tda18271-common.c
index 054b3b747dae..d46a2e775e82 100644
--- a/drivers/media/tuners/tda18271-common.c
+++ b/drivers/media/tuners/tda18271-common.c
@@ -528,14 +528,14 @@ int tda18271_init_regs(struct dvb_frontend *fe)
* Standby modes, EP3 [7:5]
*
* | SM || SM_LT || SM_XT || mode description
- * |=====\\=======\\=======\\===================================
+ * |=====\\=======\\=======\\====================================
* | 0 || 0 || 0 || normal mode
- * |-----||-------||-------||-----------------------------------
+ * |-----||-------||-------||------------------------------------
* | || || || standby mode w/ slave tuner output
- * | 1 || 0 || 0 || & loop thru & xtal oscillator on
- * |-----||-------||-------||-----------------------------------
+ * | 1 || 0 || 0 || & loop through & xtal oscillator on
+ * |-----||-------||-------||------------------------------------
* | 1 || 1 || 0 || standby mode w/ xtal oscillator on
- * |-----||-------||-------||-----------------------------------
+ * |-----||-------||-------||------------------------------------
* | 1 || 1 || 1 || power off
*
*/
diff --git a/drivers/media/tuners/tda18271-fe.c b/drivers/media/tuners/tda18271-fe.c
index 4d69029229e4..cac6b8e62b73 100644
--- a/drivers/media/tuners/tda18271-fe.c
+++ b/drivers/media/tuners/tda18271-fe.c
@@ -48,7 +48,7 @@ static int tda18271_toggle_output(struct dvb_frontend *fe, int standby)
if (tda_fail(ret))
goto fail;
- tda_dbg("%s mode: xtal oscillator %s, slave tuner loop thru %s\n",
+ tda_dbg("%s mode: xtal oscillator %s, slave tuner loop through %s\n",
standby ? "standby" : "active",
priv->output_opt & TDA18271_OUTPUT_XT_OFF ? "off" : "on",
priv->output_opt & TDA18271_OUTPUT_LT_OFF ? "off" : "on");
diff --git a/drivers/media/tuners/tda18271.h b/drivers/media/tuners/tda18271.h
index 7e07966c5ace..1a23532586ef 100644
--- a/drivers/media/tuners/tda18271.h
+++ b/drivers/media/tuners/tda18271.h
@@ -69,10 +69,10 @@ enum tda18271_i2c_gate {
};
enum tda18271_output_options {
- /* slave tuner output & loop thru & xtal oscillator always on */
+ /* slave tuner output & loop through & xtal oscillator always on */
TDA18271_OUTPUT_LT_XT_ON = 0,
- /* slave tuner output loop thru off */
+ /* slave tuner output loop through off */
TDA18271_OUTPUT_LT_OFF = 1,
/* xtal oscillator off */
diff --git a/drivers/media/tuners/xc4000.c b/drivers/media/tuners/xc4000.c
index eb6d65dae748..a351390ee744 100644
--- a/drivers/media/tuners/xc4000.c
+++ b/drivers/media/tuners/xc4000.c
@@ -1471,8 +1471,8 @@ static int xc4000_get_signal(struct dvb_frontend *fe, u16 *strength)
if (rc < 0)
goto ret;
- /* Informations from real testing of DVB-T and radio part,
- coeficient for one dB is 0xff.
+ /* Information from real testing of DVB-T and radio part,
+ coefficient for one dB is 0xff.
*/
tuner_dbg("Signal strength: -%ddB (%05d)\n", value >> 8, value);
diff --git a/drivers/media/usb/au0828/au0828-core.c b/drivers/media/usb/au0828/au0828-core.c
index 1fdb1601dc65..3f8c92a70116 100644
--- a/drivers/media/usb/au0828/au0828-core.c
+++ b/drivers/media/usb/au0828/au0828-core.c
@@ -234,7 +234,7 @@ static void au0828_media_graph_notify(struct media_entity *new,
if (!new) {
/*
* Called during au0828 probe time to connect
- * entites that were created prior to registering
+ * entities that were created prior to registering
* the notify handler. Find mixer and decoder.
*/
media_device_for_each_entity(entity, dev->media_dev) {
diff --git a/drivers/media/usb/au0828/au0828-dvb.c b/drivers/media/usb/au0828/au0828-dvb.c
index d9093a3c57c5..6e43028112d1 100644
--- a/drivers/media/usb/au0828/au0828-dvb.c
+++ b/drivers/media/usb/au0828/au0828-dvb.c
@@ -566,7 +566,7 @@ void au0828_dvb_unregister(struct au0828_dev *dev)
dvb->frontend = NULL;
}
-/* All the DVB attach calls go here, this function get's modified
+/* All the DVB attach calls go here, this function gets modified
* for each new card. No other function in this file needs
* to change.
*/
diff --git a/drivers/media/usb/au0828/au0828.h b/drivers/media/usb/au0828/au0828.h
index 004eadef55c7..425c35d16057 100644
--- a/drivers/media/usb/au0828/au0828.h
+++ b/drivers/media/usb/au0828/au0828.h
@@ -52,7 +52,7 @@
#define AU0828_INTERLACED_DEFAULT 1
-/* Defination for AU0828 USB transfer */
+/* Definition for AU0828 USB transfer */
#define AU0828_MAX_ISO_BUFS 12 /* maybe resize this value in the future */
#define AU0828_ISO_PACKETS_PER_URB 128
diff --git a/drivers/media/usb/cpia2/cpia2.h b/drivers/media/usb/cpia2/cpia2.h
index ab238ac8bfc0..d0a464882510 100644
--- a/drivers/media/usb/cpia2/cpia2.h
+++ b/drivers/media/usb/cpia2/cpia2.h
@@ -350,7 +350,7 @@ struct cpia2_sbuf {
};
struct framebuf {
- struct timeval timestamp;
+ u64 ts;
unsigned long seq;
int num;
int length;
diff --git a/drivers/media/usb/cpia2/cpia2_usb.c b/drivers/media/usb/cpia2/cpia2_usb.c
index a771e0a52610..e5d8dee38fe4 100644
--- a/drivers/media/usb/cpia2/cpia2_usb.c
+++ b/drivers/media/usb/cpia2/cpia2_usb.c
@@ -324,7 +324,7 @@ static void cpia2_usb_complete(struct urb *urb)
continue;
}
DBG("Start of frame pattern found\n");
- v4l2_get_timestamp(&cam->workbuff->timestamp);
+ cam->workbuff->ts = ktime_get_ns();
cam->workbuff->seq = cam->frame_count++;
cam->workbuff->data[0] = 0xFF;
cam->workbuff->data[1] = 0xD8;
diff --git a/drivers/media/usb/cpia2/cpia2_v4l.c b/drivers/media/usb/cpia2/cpia2_v4l.c
index 748739c2b8b2..95c0bd4a19dc 100644
--- a/drivers/media/usb/cpia2/cpia2_v4l.c
+++ b/drivers/media/usb/cpia2/cpia2_v4l.c
@@ -833,7 +833,7 @@ static int cpia2_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf)
break;
case FRAME_READY:
buf->bytesused = cam->buffers[buf->index].length;
- buf->timestamp = cam->buffers[buf->index].timestamp;
+ buf->timestamp = ns_to_timeval(cam->buffers[buf->index].ts);
buf->sequence = cam->buffers[buf->index].seq;
buf->flags = V4L2_BUF_FLAG_DONE;
break;
@@ -889,12 +889,7 @@ static int find_earliest_filled_buffer(struct camera_data *cam)
found = i;
} else {
/* find which buffer is earlier */
- struct timeval *tv1, *tv2;
- tv1 = &cam->buffers[i].timestamp;
- tv2 = &cam->buffers[found].timestamp;
- if(tv1->tv_sec < tv2->tv_sec ||
- (tv1->tv_sec == tv2->tv_sec &&
- tv1->tv_usec < tv2->tv_usec))
+ if (cam->buffers[i].ts < cam->buffers[found].ts)
found = i;
}
}
@@ -945,7 +940,7 @@ static int cpia2_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
buf->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_DONE
| V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
buf->field = V4L2_FIELD_NONE;
- buf->timestamp = cam->buffers[buf->index].timestamp;
+ buf->timestamp = ns_to_timeval(cam->buffers[buf->index].ts);
buf->sequence = cam->buffers[buf->index].seq;
buf->m.offset = cam->buffers[buf->index].data - cam->frame_buffer;
buf->length = cam->frame_size;
diff --git a/drivers/media/usb/cx231xx/cx231xx-417.c b/drivers/media/usb/cx231xx/cx231xx-417.c
index 1c48c497bd6a..0f8ae81f4820 100644
--- a/drivers/media/usb/cx231xx/cx231xx-417.c
+++ b/drivers/media/usb/cx231xx/cx231xx-417.c
@@ -1316,7 +1316,7 @@ static void buffer_copy(struct cx231xx *dev, char *data, int len, struct urb *ur
buf->vb.state = VIDEOBUF_DONE;
buf->vb.field_count++;
- v4l2_get_timestamp(&buf->vb.ts);
+ buf->vb.ts = ktime_get_ns();
list_del(&buf->vb.queue);
wake_up(&buf->vb.done);
dma_q->mpeg_buffer_completed = 0;
@@ -1347,7 +1347,7 @@ static void buffer_filled(char *data, int len, struct urb *urb,
memcpy(vbuf, data, len);
buf->vb.state = VIDEOBUF_DONE;
buf->vb.field_count++;
- v4l2_get_timestamp(&buf->vb.ts);
+ buf->vb.ts = ktime_get_ns();
list_del(&buf->vb.queue);
wake_up(&buf->vb.done);
}
diff --git a/drivers/media/usb/cx231xx/cx231xx-avcore.c b/drivers/media/usb/cx231xx/cx231xx-avcore.c
index fdd3c221fa0d..3374888b3021 100644
--- a/drivers/media/usb/cx231xx/cx231xx-avcore.c
+++ b/drivers/media/usb/cx231xx/cx231xx-avcore.c
@@ -2987,7 +2987,7 @@ int cx231xx_gpio_i2c_write_ack(struct cx231xx *dev)
{
int status = 0;
- /* set SDA to ouput */
+ /* set SDA to output */
dev->gpio_dir |= 1 << dev->board.tuner_sda_gpio;
status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, dev->gpio_val);
diff --git a/drivers/media/usb/cx231xx/cx231xx-pcb-cfg.h b/drivers/media/usb/cx231xx/cx231xx-pcb-cfg.h
index 8f00b1d38277..bb4f817be0c5 100644
--- a/drivers/media/usb/cx231xx/cx231xx-pcb-cfg.h
+++ b/drivers/media/usb/cx231xx/cx231xx-pcb-cfg.h
@@ -86,7 +86,7 @@ enum TS_PORT{
#define EAVP_MASK 0x8
enum EAV_PRESENT{
NO_EXTERNAL_AV = 0x0, /* 0: No External A/V inputs
- (no need for i2s blcok),
+ (no need for i2s block),
Analog Tuner must be present */
EXTERNAL_AV = 0x8 /* 1: External A/V inputs
present (requires i2s blk) */
diff --git a/drivers/media/usb/cx231xx/cx231xx-vbi.c b/drivers/media/usb/cx231xx/cx231xx-vbi.c
index 10b2eb7338ad..d16b73c04445 100644
--- a/drivers/media/usb/cx231xx/cx231xx-vbi.c
+++ b/drivers/media/usb/cx231xx/cx231xx-vbi.c
@@ -528,7 +528,7 @@ static inline void vbi_buffer_filled(struct cx231xx *dev,
buf->vb.state = VIDEOBUF_DONE;
buf->vb.field_count++;
- v4l2_get_timestamp(&buf->vb.ts);
+ buf->vb.ts = ktime_get_ns();
dev->vbi_mode.bulk_ctl.buf = NULL;
diff --git a/drivers/media/usb/cx231xx/cx231xx-video.c b/drivers/media/usb/cx231xx/cx231xx-video.c
index 0d451c4ea3b9..aebbaf9d92a6 100644
--- a/drivers/media/usb/cx231xx/cx231xx-video.c
+++ b/drivers/media/usb/cx231xx/cx231xx-video.c
@@ -182,7 +182,7 @@ static inline void buffer_filled(struct cx231xx *dev,
cx231xx_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i);
buf->vb.state = VIDEOBUF_DONE;
buf->vb.field_count++;
- v4l2_get_timestamp(&buf->vb.ts);
+ buf->vb.ts = ktime_get_ns();
if (dev->USE_ISO)
dev->video_mode.isoc_ctl.buf = NULL;
diff --git a/drivers/media/usb/cx231xx/cx231xx.h b/drivers/media/usb/cx231xx/cx231xx.h
index fa640bf20111..86b7f57492b1 100644
--- a/drivers/media/usb/cx231xx/cx231xx.h
+++ b/drivers/media/usb/cx231xx/cx231xx.h
@@ -646,7 +646,7 @@ struct cx231xx {
/* frame properties */
int width; /* current frame width */
int height; /* current frame height */
- int interlaced; /* 1=interlace fileds, 0=just top fileds */
+ int interlaced; /* 1=interlace fields, 0=just top fields */
struct cx231xx_audio adev;
diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb.h b/drivers/media/usb/dvb-usb-v2/dvb_usb.h
index 3fd6cc0d6340..728ef5f3ada2 100644
--- a/drivers/media/usb/dvb-usb-v2/dvb_usb.h
+++ b/drivers/media/usb/dvb-usb-v2/dvb_usb.h
@@ -146,7 +146,7 @@ struct dvb_usb_rc {
};
/**
- * usb streaming configration for adapter
+ * usb streaming configuration for adapter
* @type: urb type
* @count: count of used urbs
* @endpoint: stream usb endpoint number
diff --git a/drivers/media/usb/dvb-usb-v2/lmedm04.c b/drivers/media/usb/dvb-usb-v2/lmedm04.c
index 602013cf3e69..15944b95970f 100644
--- a/drivers/media/usb/dvb-usb-v2/lmedm04.c
+++ b/drivers/media/usb/dvb-usb-v2/lmedm04.c
@@ -308,7 +308,7 @@ static void lme2510_int_response(struct urb *lme_urb)
switch (ibuf[0]) {
case 0xaa:
- debug_data_snipet(1, "INT Remote data snipet", ibuf);
+ debug_data_snipet(1, "INT Remote data snippet", ibuf);
if (!adap_to_d(adap)->rc_dev)
break;
@@ -358,13 +358,13 @@ static void lme2510_int_response(struct urb *lme_urb)
lme2510_update_stats(adap);
- debug_data_snipet(5, "INT Remote data snipet in", ibuf);
+ debug_data_snipet(5, "INT Remote data snippet in", ibuf);
break;
case 0xcc:
- debug_data_snipet(1, "INT Control data snipet", ibuf);
+ debug_data_snipet(1, "INT Control data snippet", ibuf);
break;
default:
- debug_data_snipet(1, "INT Unknown data snipet", ibuf);
+ debug_data_snipet(1, "INT Unknown data snippet", ibuf);
break;
}
}
diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf.c b/drivers/media/usb/dvb-usb-v2/mxl111sf.c
index 85cdf593a9ad..5e2d53af68c7 100644
--- a/drivers/media/usb/dvb-usb-v2/mxl111sf.c
+++ b/drivers/media/usb/dvb-usb-v2/mxl111sf.c
@@ -140,7 +140,7 @@ int mxl111sf_write_reg_mask(struct mxl111sf_state *state,
if (mask != 0xff) {
ret = mxl111sf_read_reg(state, addr, &val);
#if 1
- /* dont know why this usually errors out on the first try */
+ /* don't know why this usually errors out on the first try */
if (mxl_fail(ret))
pr_err("error writing addr: 0x%02x, mask: 0x%02x, data: 0x%02x, retrying...",
addr, mask, data);
@@ -783,7 +783,7 @@ static int mxl111sf_attach_demod(struct dvb_usb_adapter *adap, u8 fe_id)
if (mxl_fail(ret))
goto fail;
- /* dont care if this fails */
+ /* don't care if this fails */
mxl111sf_init_port_expander(state);
adap->fe[fe_id] = dvb_attach(mxl111sf_demod_attach, state,
diff --git a/drivers/media/usb/dvb-usb/af9005.c b/drivers/media/usb/dvb-usb/af9005.c
index 16e946e01d2c..0638d907c73e 100644
--- a/drivers/media/usb/dvb-usb/af9005.c
+++ b/drivers/media/usb/dvb-usb/af9005.c
@@ -845,7 +845,7 @@ static int af9005_rc_query(struct dvb_usb_device *d, u32 * event, int *state)
/* deb_info("rc_query\n"); */
st->data[0] = 3; /* rest of packet length low */
- st->data[1] = 0; /* rest of packet lentgh high */
+ st->data[1] = 0; /* rest of packet length high */
st->data[2] = 0x40; /* read remote */
st->data[3] = 1; /* rest of packet length */
st->data[4] = seq = st->sequence++; /* sequence number */
diff --git a/drivers/media/usb/dvb-usb/cinergyT2-fe.c b/drivers/media/usb/dvb-usb/cinergyT2-fe.c
index df71df7ed524..4c9f83ba260d 100644
--- a/drivers/media/usb/dvb-usb/cinergyT2-fe.c
+++ b/drivers/media/usb/dvb-usb/cinergyT2-fe.c
@@ -33,7 +33,7 @@
* This function is probably reusable and may better get placed in a support
* library.
*
- * We replace errornous fields by default TPS fields (the ones with value 0).
+ * We replace erroneous fields by default TPS fields (the ones with value 0).
*/
static uint16_t compute_tps(struct dtv_frontend_properties *op)
diff --git a/drivers/media/usb/dvb-usb/cxusb.c b/drivers/media/usb/dvb-usb/cxusb.c
index a51a45c60233..9ddb2000249e 100644
--- a/drivers/media/usb/dvb-usb/cxusb.c
+++ b/drivers/media/usb/dvb-usb/cxusb.c
@@ -1016,7 +1016,7 @@ static int cxusb_dualdig4_rev2_tuner_attach(struct dvb_usb_adapter *adap)
/*
* No need to call dvb7000p_attach here, as it was called
* already, as frontend_attach method is called first, and
- * tuner_attach is only called on sucess.
+ * tuner_attach is only called on success.
*/
tun_i2c = st->dib7000p_ops.get_i2c_master(adap->fe_adap[0].fe,
DIBX000_I2C_INTERFACE_TUNER, 1);
diff --git a/drivers/media/usb/dvb-usb/dvb-usb-init.c b/drivers/media/usb/dvb-usb/dvb-usb-init.c
index 40ca4eafb137..99951e02a880 100644
--- a/drivers/media/usb/dvb-usb/dvb-usb-init.c
+++ b/drivers/media/usb/dvb-usb/dvb-usb-init.c
@@ -98,7 +98,7 @@ static int dvb_usb_adapter_init(struct dvb_usb_device *d, short *adapter_nrs)
/*
* when reloading the driver w/o replugging the device
- * sometimes a timeout occures, this helps
+ * sometimes a timeout occurs, this helps
*/
if (d->props.generic_bulk_ctrl_endpoint != 0) {
usb_clear_halt(d->udev, usb_sndbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint));
diff --git a/drivers/media/usb/dvb-usb/dvb-usb.h b/drivers/media/usb/dvb-usb/dvb-usb.h
index 317ed6a82d19..32829bdd5f22 100644
--- a/drivers/media/usb/dvb-usb/dvb-usb.h
+++ b/drivers/media/usb/dvb-usb/dvb-usb.h
@@ -336,7 +336,7 @@ struct usb_data_stream {
* struct dvb_usb_adapter - a DVB adapter on a USB device
* @id: index of this adapter (starting with 0).
*
- * @feedcount: number of reqested feeds (used for streaming-activation)
+ * @feedcount: number of requested feeds (used for streaming-activation)
* @pid_filtering: is hardware pid_filtering used or not.
*
* @pll_addr: I2C address of the tuner for programming
diff --git a/drivers/media/usb/dvb-usb/pctv452e.c b/drivers/media/usb/dvb-usb/pctv452e.c
index 0af74383083d..150081128196 100644
--- a/drivers/media/usb/dvb-usb/pctv452e.c
+++ b/drivers/media/usb/dvb-usb/pctv452e.c
@@ -528,13 +528,13 @@ static int pctv452e_power_ctrl(struct dvb_usb_device *d, int i)
rx = b0 + 5;
- /* hmm where shoud this should go? */
+ /* hmm where should this should go? */
ret = usb_set_interface(d->udev, 0, ISOC_INTERFACE_ALTERNATIVE);
if (ret != 0)
info("%s: Warning set interface returned: %d\n",
__func__, ret);
- /* this is a one-time initialization, dont know where to put */
+ /* this is a one-time initialization, don't know where to put */
b0[0] = 0xaa;
b0[1] = state->c++;
b0[2] = PCTV_CMD_RESET;
diff --git a/drivers/media/usb/em28xx/em28xx-i2c.c b/drivers/media/usb/em28xx/em28xx-i2c.c
index 02c13d71e6c1..a3155ec196cc 100644
--- a/drivers/media/usb/em28xx/em28xx-i2c.c
+++ b/drivers/media/usb/em28xx/em28xx-i2c.c
@@ -296,7 +296,7 @@ static int em28xx_i2c_recv_bytes(struct em28xx *dev, u16 addr, u8 *buf, u16 len)
return ret;
}
/*
- * NOTE: some devices with two i2c busses have the bad habit to return 0
+ * NOTE: some devices with two i2c buses have the bad habit to return 0
* bytes if we are on bus B AND there was no write attempt to the
* specified slave address before AND no device is present at the
* requested slave address.
@@ -427,7 +427,7 @@ static int em25xx_bus_B_recv_bytes(struct em28xx *dev, u16 addr, u8 *buf,
return ret;
}
/*
- * NOTE: some devices with two i2c busses have the bad habit to return 0
+ * NOTE: some devices with two i2c buses have the bad habit to return 0
* bytes if we are on bus B AND there was no write attempt to the
* specified slave address before AND no device is present at the
* requested slave address.
diff --git a/drivers/media/usb/em28xx/em28xx-reg.h b/drivers/media/usb/em28xx/em28xx-reg.h
index f53afe18e92d..d7c60862874a 100644
--- a/drivers/media/usb/em28xx/em28xx-reg.h
+++ b/drivers/media/usb/em28xx/em28xx-reg.h
@@ -67,7 +67,7 @@
#define EM28XX_I2C_CLK_WAIT_ENABLE 0x40
#define EM28XX_I2C_EEPROM_ON_BOARD 0x08
#define EM28XX_I2C_EEPROM_KEY_VALID 0x04
-#define EM2874_I2C_SECONDARY_BUS_SELECT 0x04 /* em2874 has two i2c busses */
+#define EM2874_I2C_SECONDARY_BUS_SELECT 0x04 /* em2874 has two i2c buses */
#define EM28XX_I2C_FREQ_1_5_MHZ 0x03 /* bus frequency (bits [1-0]) */
#define EM28XX_I2C_FREQ_25_KHZ 0x02
#define EM28XX_I2C_FREQ_400_KHZ 0x01
diff --git a/drivers/media/usb/gspca/Kconfig b/drivers/media/usb/gspca/Kconfig
index d3b6665c342d..088566e88467 100644
--- a/drivers/media/usb/gspca/Kconfig
+++ b/drivers/media/usb/gspca/Kconfig
@@ -46,7 +46,7 @@ config USB_GSPCA_CPIA1
depends on VIDEO_V4L2 && USB_GSPCA
help
Say Y here if you want support for USB cameras based on the cpia
- CPiA chip. Note that you need atleast version 0.6.4 of libv4l for
+ CPiA chip. Note that you need at least version 0.6.4 of libv4l for
applications to understand the videoformat generated by this driver.
To compile this driver as a module, choose M here: the
diff --git a/drivers/media/usb/gspca/autogain_functions.c b/drivers/media/usb/gspca/autogain_functions.c
index 6dfab2b077f7..f915cc7c0c63 100644
--- a/drivers/media/usb/gspca/autogain_functions.c
+++ b/drivers/media/usb/gspca/autogain_functions.c
@@ -98,7 +98,7 @@ EXPORT_SYMBOL(gspca_expo_autogain);
80 %) and if that does not help, only then changes exposure. This leads
to a much more stable image then using the knee algorithm which at
certain points of the knee graph will only try to adjust exposure,
- which leads to oscilating as one exposure step is huge.
+ which leads to oscillating as one exposure step is huge.
Returns 0 if no changes were made, 1 if the gain and or exposure settings
where changed. */
diff --git a/drivers/media/usb/gspca/benq.c b/drivers/media/usb/gspca/benq.c
index 8a8db5eb6d5f..1744591b8ba0 100644
--- a/drivers/media/usb/gspca/benq.c
+++ b/drivers/media/usb/gspca/benq.c
@@ -205,12 +205,12 @@ static void sd_isoc_irq(struct urb *urb)
* - 80 ba/bb 00 00 = start of image followed by 'ff d8'
* - 04 ba/bb oo oo = image piece
* where 'oo oo' is the image offset
- (not cheked)
+ (not checked)
* - (other -> bad frame)
* The images are JPEG encoded with full header and
* normal ff escape.
* The end of image ('ff d9') may occur in any URB.
- * (not cheked)
+ * (not checked)
*/
data = (u8 *) urb0->transfer_buffer
+ urb0->iso_frame_desc[i].offset;
diff --git a/drivers/media/usb/gspca/cpia1.c b/drivers/media/usb/gspca/cpia1.c
index 2b09af8865f4..7c817a4a93c4 100644
--- a/drivers/media/usb/gspca/cpia1.c
+++ b/drivers/media/usb/gspca/cpia1.c
@@ -547,10 +547,14 @@ static int do_command(struct gspca_dev *gspca_dev, u16 command,
}
if (sd->params.qx3.button) {
/* button pressed - unlock the latch */
- do_command(gspca_dev, CPIA_COMMAND_WriteMCPort,
+ ret = do_command(gspca_dev, CPIA_COMMAND_WriteMCPort,
3, 0xdf, 0xdf, 0);
- do_command(gspca_dev, CPIA_COMMAND_WriteMCPort,
+ if (ret)
+ return ret;
+ ret = do_command(gspca_dev, CPIA_COMMAND_WriteMCPort,
3, 0xff, 0xff, 0);
+ if (ret)
+ return ret;
}
/* test whether microscope is cradled */
@@ -1430,6 +1434,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
{
struct sd *sd = (struct sd *) gspca_dev;
struct cam *cam;
+ int ret;
sd->mainsFreq = FREQ_DEF == V4L2_CID_POWER_LINE_FREQUENCY_60HZ;
reset_camera_params(gspca_dev);
@@ -1441,7 +1446,10 @@ static int sd_config(struct gspca_dev *gspca_dev,
cam->cam_mode = mode;
cam->nmodes = ARRAY_SIZE(mode);
- goto_low_power(gspca_dev);
+ ret = goto_low_power(gspca_dev);
+ if (ret)
+ gspca_err(gspca_dev, "Cannot go to low power mode: %d\n",
+ ret);
/* Check the firmware version. */
sd->params.version.firmwareVersion = 0;
get_version_information(gspca_dev);
diff --git a/drivers/media/usb/gspca/gspca.c b/drivers/media/usb/gspca/gspca.c
index 3137f5d89d80..ac70b36d67b7 100644
--- a/drivers/media/usb/gspca/gspca.c
+++ b/drivers/media/usb/gspca/gspca.c
@@ -912,25 +912,32 @@ static void gspca_set_default_mode(struct gspca_dev *gspca_dev)
}
static int wxh_to_mode(struct gspca_dev *gspca_dev,
- int width, int height)
+ int width, int height, u32 pixelformat)
{
int i;
for (i = 0; i < gspca_dev->cam.nmodes; i++) {
if (width == gspca_dev->cam.cam_mode[i].width
- && height == gspca_dev->cam.cam_mode[i].height)
+ && height == gspca_dev->cam.cam_mode[i].height
+ && pixelformat == gspca_dev->cam.cam_mode[i].pixelformat)
return i;
}
return -EINVAL;
}
static int wxh_to_nearest_mode(struct gspca_dev *gspca_dev,
- int width, int height)
+ int width, int height, u32 pixelformat)
{
int i;
for (i = gspca_dev->cam.nmodes; --i > 0; ) {
if (width >= gspca_dev->cam.cam_mode[i].width
+ && height >= gspca_dev->cam.cam_mode[i].height
+ && pixelformat == gspca_dev->cam.cam_mode[i].pixelformat)
+ return i;
+ }
+ for (i = gspca_dev->cam.nmodes; --i > 0; ) {
+ if (width >= gspca_dev->cam.cam_mode[i].width
&& height >= gspca_dev->cam.cam_mode[i].height)
break;
}
@@ -1058,7 +1065,7 @@ static int try_fmt_vid_cap(struct gspca_dev *gspca_dev,
fmt->fmt.pix.pixelformat, w, h);
/* search the nearest mode for width and height */
- mode = wxh_to_nearest_mode(gspca_dev, w, h);
+ mode = wxh_to_nearest_mode(gspca_dev, w, h, fmt->fmt.pix.pixelformat);
/* OK if right palette */
if (gspca_dev->cam.cam_mode[mode].pixelformat
@@ -1152,7 +1159,8 @@ static int vidioc_enum_frameintervals(struct file *filp, void *priv,
int mode;
__u32 i;
- mode = wxh_to_mode(gspca_dev, fival->width, fival->height);
+ mode = wxh_to_mode(gspca_dev, fival->width, fival->height,
+ fival->pixel_format);
if (mode < 0)
return -EINVAL;
diff --git a/drivers/media/usb/gspca/m5602/m5602_mt9m111.c b/drivers/media/usb/gspca/m5602/m5602_mt9m111.c
index c9947c4a0f63..8fac814f4779 100644
--- a/drivers/media/usb/gspca/m5602/m5602_mt9m111.c
+++ b/drivers/media/usb/gspca/m5602/m5602_mt9m111.c
@@ -199,7 +199,7 @@ static const struct v4l2_ctrl_config mt9m111_greenbal_cfg = {
int mt9m111_probe(struct sd *sd)
{
u8 data[2] = {0x00, 0x00};
- int i;
+ int i, rc = 0;
struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
if (force_sensor) {
@@ -217,16 +217,18 @@ int mt9m111_probe(struct sd *sd)
/* Do the preinit */
for (i = 0; i < ARRAY_SIZE(preinit_mt9m111); i++) {
if (preinit_mt9m111[i][0] == BRIDGE) {
- m5602_write_bridge(sd,
+ rc |= m5602_write_bridge(sd,
preinit_mt9m111[i][1],
preinit_mt9m111[i][2]);
} else {
data[0] = preinit_mt9m111[i][2];
data[1] = preinit_mt9m111[i][3];
- m5602_write_sensor(sd,
+ rc |= m5602_write_sensor(sd,
preinit_mt9m111[i][1], data, 2);
}
}
+ if (rc < 0)
+ return rc;
if (m5602_read_sensor(sd, MT9M111_SC_CHIPVER, data, 2))
return -ENODEV;
diff --git a/drivers/media/usb/gspca/m5602/m5602_po1030.c b/drivers/media/usb/gspca/m5602/m5602_po1030.c
index 37d2891e5f5b..5e43b4782f02 100644
--- a/drivers/media/usb/gspca/m5602/m5602_po1030.c
+++ b/drivers/media/usb/gspca/m5602/m5602_po1030.c
@@ -158,6 +158,7 @@ static const struct v4l2_ctrl_config po1030_greenbal_cfg = {
int po1030_probe(struct sd *sd)
{
+ int rc = 0;
u8 dev_id_h = 0, i;
struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
@@ -177,11 +178,14 @@ int po1030_probe(struct sd *sd)
for (i = 0; i < ARRAY_SIZE(preinit_po1030); i++) {
u8 data = preinit_po1030[i][2];
if (preinit_po1030[i][0] == SENSOR)
- m5602_write_sensor(sd,
+ rc |= m5602_write_sensor(sd,
preinit_po1030[i][1], &data, 1);
else
- m5602_write_bridge(sd, preinit_po1030[i][1], data);
+ rc |= m5602_write_bridge(sd, preinit_po1030[i][1],
+ data);
}
+ if (rc < 0)
+ return rc;
if (m5602_read_sensor(sd, PO1030_DEVID_H, &dev_id_h, 1))
return -ENODEV;
diff --git a/drivers/media/usb/gspca/mr97310a.c b/drivers/media/usb/gspca/mr97310a.c
index bea196361215..af454663e295 100644
--- a/drivers/media/usb/gspca/mr97310a.c
+++ b/drivers/media/usb/gspca/mr97310a.c
@@ -520,7 +520,7 @@ static int start_cif_cam(struct gspca_dev *gspca_dev)
switch (gspca_dev->pixfmt.width) {
case 160:
data[9] |= 0x04; /* reg 8, 2:1 scale down from 320 */
- /* fall thru */
+ /* fall through */
case 320:
default:
data[3] = 0x28; /* reg 2, H size/8 */
@@ -530,7 +530,7 @@ static int start_cif_cam(struct gspca_dev *gspca_dev)
break;
case 176:
data[9] |= 0x04; /* reg 8, 2:1 scale down from 352 */
- /* fall thru */
+ /* fall through */
case 352:
data[3] = 0x2c; /* reg 2, H size/8 */
data[4] = 0x48; /* reg 3, V size/4 */
@@ -617,10 +617,10 @@ static int start_vga_cam(struct gspca_dev *gspca_dev)
switch (gspca_dev->pixfmt.width) {
case 160:
data[9] |= 0x0c; /* reg 8, 4:1 scale down */
- /* fall thru */
+ /* fall through */
case 320:
data[9] |= 0x04; /* reg 8, 2:1 scale down */
- /* fall thru */
+ /* fall through */
case 640:
default:
data[3] = 0x50; /* reg 2, H size/8 */
@@ -637,7 +637,7 @@ static int start_vga_cam(struct gspca_dev *gspca_dev)
case 176:
data[9] |= 0x04; /* reg 8, 2:1 scale down */
- /* fall thru */
+ /* fall through */
case 352:
data[3] = 0x2c; /* reg 2, H size */
data[4] = 0x48; /* reg 3, V size */
diff --git a/drivers/media/usb/gspca/ov519.c b/drivers/media/usb/gspca/ov519.c
index 10fcbe9e8614..f2799e8cb8e7 100644
--- a/drivers/media/usb/gspca/ov519.c
+++ b/drivers/media/usb/gspca/ov519.c
@@ -1945,7 +1945,7 @@ static const struct ov_i2c_regvals norm_8610[] = {
{ 0x62, 0x5f }, /* was 0xd7, new from windrv 090403 */
{ 0x63, 0xff },
{ 0x64, 0x53 }, /* new windrv 090403 says 0x57,
- * maybe thats wrong */
+ * maybe that's wrong */
{ 0x65, 0x00 },
{ 0x66, 0x55 },
{ 0x67, 0xb0 },
@@ -3658,7 +3658,7 @@ static void ov518_mode_init_regs(struct sd *sd)
case SEN_OV7620AE:
/*
* HdG: 640x480 needs special handling on device
- * revision 2, we check for device revison > 0 to
+ * revision 2, we check for device revision > 0 to
* avoid regressions, as we don't know the correct
* thing todo for revision 1.
*
diff --git a/drivers/media/usb/gspca/ov534.c b/drivers/media/usb/gspca/ov534.c
index d06dc0755b9a..02c90ad96b76 100644
--- a/drivers/media/usb/gspca/ov534.c
+++ b/drivers/media/usb/gspca/ov534.c
@@ -103,6 +103,16 @@ static const struct v4l2_pix_format ov772x_mode[] = {
.sizeimage = 640 * 480 * 2,
.colorspace = V4L2_COLORSPACE_SRGB,
.priv = 0},
+ {320, 240, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1},
+ {640, 480, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0},
};
static const struct v4l2_pix_format ov767x_mode[] = {
{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
@@ -127,6 +137,14 @@ static const struct framerates ov772x_framerates[] = {
.rates = vga_rates,
.nrates = ARRAY_SIZE(vga_rates),
},
+ { /* 320x240 SGBRG8 */
+ .rates = qvga_rates,
+ .nrates = ARRAY_SIZE(qvga_rates),
+ },
+ { /* 640x480 SGBRG8 */
+ .rates = vga_rates,
+ .nrates = ARRAY_SIZE(vga_rates),
+ },
};
struct reg_array {
@@ -411,9 +429,7 @@ static const u8 sensor_start_qvga_767x[][2] = {
};
static const u8 bridge_init_772x[][2] = {
- { 0xc2, 0x0c },
{ 0x88, 0xf8 },
- { 0xc3, 0x69 },
{ 0x89, 0xff },
{ 0x76, 0x03 },
{ 0x92, 0x01 },
@@ -439,7 +455,6 @@ static const u8 bridge_init_772x[][2] = {
{ 0x1f, 0x81 },
{ 0x34, 0x05 },
{ 0xe3, 0x04 },
- { 0x88, 0x00 },
{ 0x89, 0x00 },
{ 0x76, 0x00 },
{ 0xe7, 0x2e },
@@ -447,26 +462,9 @@ static const u8 bridge_init_772x[][2] = {
{ 0x25, 0x42 },
{ 0x21, 0xf0 },
- { 0x1c, 0x00 },
- { 0x1d, 0x40 },
- { 0x1d, 0x02 }, /* payload size 0x0200 * 4 = 2048 bytes */
- { 0x1d, 0x00 }, /* payload size */
-
- { 0x1d, 0x02 }, /* frame size 0x025800 * 4 = 614400 */
- { 0x1d, 0x58 }, /* frame size */
- { 0x1d, 0x00 }, /* frame size */
-
{ 0x1c, 0x0a },
{ 0x1d, 0x08 }, /* turn on UVC header */
{ 0x1d, 0x0e }, /* .. */
-
- { 0x8d, 0x1c },
- { 0x8e, 0x80 },
- { 0xe5, 0x04 },
-
- { 0xc0, 0x50 },
- { 0xc1, 0x3c },
- { 0xc2, 0x0c },
};
static const u8 sensor_init_772x[][2] = {
{ 0x12, 0x80 },
@@ -545,13 +543,10 @@ static const u8 sensor_init_772x[][2] = {
{ 0x8c, 0xe8 },
{ 0x8d, 0x20 },
- { 0x0c, 0x90 },
-
{ 0x2b, 0x00 },
{ 0x22, 0x7f },
{ 0x23, 0x03 },
{ 0x11, 0x01 },
- { 0x0c, 0xd0 },
{ 0x64, 0xff },
{ 0x0d, 0x41 },
@@ -559,9 +554,9 @@ static const u8 sensor_init_772x[][2] = {
{ 0x0e, 0xcd },
{ 0xac, 0xbf },
{ 0x8e, 0x00 }, /* De-noise threshold */
- { 0x0c, 0xd0 }
};
-static const u8 bridge_start_vga_772x[][2] = {
+static const u8 bridge_start_vga_yuyv_772x[][2] = {
+ {0x88, 0x00},
{0x1c, 0x00},
{0x1d, 0x40},
{0x1d, 0x02},
@@ -569,10 +564,14 @@ static const u8 bridge_start_vga_772x[][2] = {
{0x1d, 0x02},
{0x1d, 0x58},
{0x1d, 0x00},
+ {0x8d, 0x1c},
+ {0x8e, 0x80},
{0xc0, 0x50},
{0xc1, 0x3c},
+ {0xc2, 0x0c},
+ {0xc3, 0x69},
};
-static const u8 sensor_start_vga_772x[][2] = {
+static const u8 sensor_start_vga_yuyv_772x[][2] = {
{0x12, 0x00},
{0x17, 0x26},
{0x18, 0xa0},
@@ -581,8 +580,10 @@ static const u8 sensor_start_vga_772x[][2] = {
{0x29, 0xa0},
{0x2c, 0xf0},
{0x65, 0x20},
+ {0x67, 0x00},
};
-static const u8 bridge_start_qvga_772x[][2] = {
+static const u8 bridge_start_qvga_yuyv_772x[][2] = {
+ {0x88, 0x00},
{0x1c, 0x00},
{0x1d, 0x40},
{0x1d, 0x02},
@@ -590,10 +591,14 @@ static const u8 bridge_start_qvga_772x[][2] = {
{0x1d, 0x01},
{0x1d, 0x4b},
{0x1d, 0x00},
+ {0x8d, 0x1c},
+ {0x8e, 0x80},
{0xc0, 0x28},
{0xc1, 0x1e},
+ {0xc2, 0x0c},
+ {0xc3, 0x69},
};
-static const u8 sensor_start_qvga_772x[][2] = {
+static const u8 sensor_start_qvga_yuyv_772x[][2] = {
{0x12, 0x40},
{0x17, 0x3f},
{0x18, 0x50},
@@ -602,6 +607,61 @@ static const u8 sensor_start_qvga_772x[][2] = {
{0x29, 0x50},
{0x2c, 0x78},
{0x65, 0x2f},
+ {0x67, 0x00},
+};
+static const u8 bridge_start_vga_gbrg_772x[][2] = {
+ {0x88, 0x08},
+ {0x1c, 0x00},
+ {0x1d, 0x00},
+ {0x1d, 0x02},
+ {0x1d, 0x00},
+ {0x1d, 0x01},
+ {0x1d, 0x2c},
+ {0x1d, 0x00},
+ {0x8d, 0x00},
+ {0x8e, 0x00},
+ {0xc0, 0x50},
+ {0xc1, 0x3c},
+ {0xc2, 0x01},
+ {0xc3, 0x01},
+};
+static const u8 sensor_start_vga_gbrg_772x[][2] = {
+ {0x12, 0x01},
+ {0x17, 0x26},
+ {0x18, 0xa0},
+ {0x19, 0x07},
+ {0x1a, 0xf0},
+ {0x29, 0xa0},
+ {0x2c, 0xf0},
+ {0x65, 0x20},
+ {0x67, 0x02},
+};
+static const u8 bridge_start_qvga_gbrg_772x[][2] = {
+ {0x88, 0x08},
+ {0x1c, 0x00},
+ {0x1d, 0x00},
+ {0x1d, 0x02},
+ {0x1d, 0x00},
+ {0x1d, 0x00},
+ {0x1d, 0x4b},
+ {0x1d, 0x00},
+ {0x8d, 0x00},
+ {0x8e, 0x00},
+ {0xc0, 0x28},
+ {0xc1, 0x1e},
+ {0xc2, 0x01},
+ {0xc3, 0x01},
+};
+static const u8 sensor_start_qvga_gbrg_772x[][2] = {
+ {0x12, 0x41},
+ {0x17, 0x3f},
+ {0x18, 0x50},
+ {0x19, 0x03},
+ {0x1a, 0x78},
+ {0x29, 0x50},
+ {0x2c, 0x78},
+ {0x65, 0x2f},
+ {0x67, 0x02},
};
static void ov534_reg_write(struct gspca_dev *gspca_dev, u16 reg, u8 val)
@@ -679,7 +739,7 @@ static int sccb_check_status(struct gspca_dev *gspca_dev)
int i;
for (i = 0; i < 5; i++) {
- msleep(10);
+ usleep_range(10000, 20000);
data = ov534_reg_read(gspca_dev, OV534_REG_STATUS);
switch (data) {
@@ -1277,7 +1337,7 @@ static int sd_init(struct gspca_dev *gspca_dev)
/* reset sensor */
sccb_reg_write(gspca_dev, 0x12, 0x80);
- msleep(10);
+ usleep_range(10000, 20000);
/* probe the sensor */
sccb_reg_read(gspca_dev, 0x0a);
@@ -1315,25 +1375,33 @@ static int sd_start(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
int mode;
- static const struct reg_array bridge_start[NSENSORS][2] = {
+ static const struct reg_array bridge_start[NSENSORS][4] = {
[SENSOR_OV767x] = {{bridge_start_qvga_767x,
ARRAY_SIZE(bridge_start_qvga_767x)},
{bridge_start_vga_767x,
ARRAY_SIZE(bridge_start_vga_767x)}},
- [SENSOR_OV772x] = {{bridge_start_qvga_772x,
- ARRAY_SIZE(bridge_start_qvga_772x)},
- {bridge_start_vga_772x,
- ARRAY_SIZE(bridge_start_vga_772x)}},
+ [SENSOR_OV772x] = {{bridge_start_qvga_yuyv_772x,
+ ARRAY_SIZE(bridge_start_qvga_yuyv_772x)},
+ {bridge_start_vga_yuyv_772x,
+ ARRAY_SIZE(bridge_start_vga_yuyv_772x)},
+ {bridge_start_qvga_gbrg_772x,
+ ARRAY_SIZE(bridge_start_qvga_gbrg_772x)},
+ {bridge_start_vga_gbrg_772x,
+ ARRAY_SIZE(bridge_start_vga_gbrg_772x)} },
};
- static const struct reg_array sensor_start[NSENSORS][2] = {
+ static const struct reg_array sensor_start[NSENSORS][4] = {
[SENSOR_OV767x] = {{sensor_start_qvga_767x,
ARRAY_SIZE(sensor_start_qvga_767x)},
{sensor_start_vga_767x,
ARRAY_SIZE(sensor_start_vga_767x)}},
- [SENSOR_OV772x] = {{sensor_start_qvga_772x,
- ARRAY_SIZE(sensor_start_qvga_772x)},
- {sensor_start_vga_772x,
- ARRAY_SIZE(sensor_start_vga_772x)}},
+ [SENSOR_OV772x] = {{sensor_start_qvga_yuyv_772x,
+ ARRAY_SIZE(sensor_start_qvga_yuyv_772x)},
+ {sensor_start_vga_yuyv_772x,
+ ARRAY_SIZE(sensor_start_vga_yuyv_772x)},
+ {sensor_start_qvga_gbrg_772x,
+ ARRAY_SIZE(sensor_start_qvga_gbrg_772x)},
+ {sensor_start_vga_gbrg_772x,
+ ARRAY_SIZE(sensor_start_vga_gbrg_772x)} },
};
/* (from ms-win trace) */
@@ -1439,10 +1507,9 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
/* If this packet is marked as EOF, end the frame */
} else if (data[1] & UVC_STREAM_EOF) {
sd->last_pts = 0;
- if (gspca_dev->pixfmt.pixelformat == V4L2_PIX_FMT_YUYV
+ if (gspca_dev->pixfmt.pixelformat != V4L2_PIX_FMT_JPEG
&& gspca_dev->image_len + len - 12 !=
- gspca_dev->pixfmt.width *
- gspca_dev->pixfmt.height * 2) {
+ gspca_dev->pixfmt.sizeimage) {
gspca_dbg(gspca_dev, D_PACK, "wrong sized frame\n");
goto discard;
}
diff --git a/drivers/media/usb/gspca/pac_common.h b/drivers/media/usb/gspca/pac_common.h
index 31f2a42af4dd..aae97a5534e3 100644
--- a/drivers/media/usb/gspca/pac_common.h
+++ b/drivers/media/usb/gspca/pac_common.h
@@ -21,7 +21,7 @@
/* We calculate the autogain at the end of the transfer of a frame, at this
moment a frame with the old settings is being captured and transmitted. So
- if we adjust the gain or exposure we must ignore atleast the next frame for
+ if we adjust the gain or exposure we must ignore at least the next frame for
the new settings to come into effect before doing any other adjustments. */
#define PAC_AUTOGAIN_IGNORE_FRAMES 2
diff --git a/drivers/media/usb/gspca/sn9c20x.c b/drivers/media/usb/gspca/sn9c20x.c
index 5984bb12bcff..ab912903f8d7 100644
--- a/drivers/media/usb/gspca/sn9c20x.c
+++ b/drivers/media/usb/gspca/sn9c20x.c
@@ -1634,7 +1634,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
break;
case SENSOR_HV7131R:
sd->i2c_intf = 0x81; /* i2c 400 Kb/s */
- /* fall thru */
+ /* fall through */
default:
cam->cam_mode = vga_mode;
cam->nmodes = ARRAY_SIZE(vga_mode);
diff --git a/drivers/media/usb/gspca/sonixb.c b/drivers/media/usb/gspca/sonixb.c
index 5f3f2979540a..583c9f10198c 100644
--- a/drivers/media/usb/gspca/sonixb.c
+++ b/drivers/media/usb/gspca/sonixb.c
@@ -121,7 +121,7 @@ struct sensor_data {
/* We calculate the autogain at the end of the transfer of a frame, at this
moment a frame with the old settings is being captured and transmitted. So
- if we adjust the gain or exposure we must ignore atleast the next frame for
+ if we adjust the gain or exposure we must ignore at least the next frame for
the new settings to come into effect before doing any other adjustments. */
#define AUTOGAIN_IGNORE_FRAMES 1
@@ -757,7 +757,7 @@ static void setexposure(struct gspca_dev *gspca_dev)
/* Don't allow this to get below 10 when using autogain, the
steps become very large (relatively) when below 10 causing
- the image to oscilate from much too dark, to much too bright
+ the image to oscillate from much too dark, to much too bright
and back again. */
if (gspca_dev->autogain->val && reg10 < 10)
reg10 = 10;
diff --git a/drivers/media/usb/gspca/sonixj.c b/drivers/media/usb/gspca/sonixj.c
index df8d8482b795..a63f155f1515 100644
--- a/drivers/media/usb/gspca/sonixj.c
+++ b/drivers/media/usb/gspca/sonixj.c
@@ -2677,7 +2677,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
* which is 62 bytes long and is followed by various information
* including statuses and luminosity.
*
- * A marker may be splitted on two packets.
+ * A marker may be split on two packets.
*
* The 6th byte of a marker contains the bits:
* 0x08: USB full
diff --git a/drivers/media/usb/gspca/spca501.c b/drivers/media/usb/gspca/spca501.c
index 2cce74b166d8..3d215952af18 100644
--- a/drivers/media/usb/gspca/spca501.c
+++ b/drivers/media/usb/gspca/spca501.c
@@ -574,7 +574,7 @@ static const __u16 spca501_3com_open_data[][3] = {
{0x0, 0x0001, 0x0010}, /* TG Start Clock */
/* {0x2, 0x006a, 0x0001}, * C/S Enable ISOSYNCH Packet Engine */
- {0x2, 0x0068, 0x0001}, /* C/S Diable ISOSYNCH Packet Engine */
+ {0x2, 0x0068, 0x0001}, /* C/S Disable ISOSYNCH Packet Engine */
{0x2, 0x0000, 0x0005},
{0x2, 0x0043, 0x0000}, /* C/S Set Timing Mode, Disable TG soft reset */
{0x2, 0x0043, 0x0000}, /* C/S Set Timing Mode, Disable TG soft reset */
diff --git a/drivers/media/usb/gspca/sq905.c b/drivers/media/usb/gspca/sq905.c
index ffea9c35b0a0..d5c48216deb7 100644
--- a/drivers/media/usb/gspca/sq905.c
+++ b/drivers/media/usb/gspca/sq905.c
@@ -18,7 +18,7 @@
* History and Acknowledgments
*
* The original Linux driver for SQ905 based cameras was written by
- * Marcell Lengyel and furter developed by many other contributors
+ * Marcell Lengyel and further developed by many other contributors
* and is available from http://sourceforge.net/projects/sqcam/
*
* This driver takes advantage of the reverse engineering work done for
diff --git a/drivers/media/usb/gspca/sunplus.c b/drivers/media/usb/gspca/sunplus.c
index 437a3367ab97..e1e2a605a46c 100644
--- a/drivers/media/usb/gspca/sunplus.c
+++ b/drivers/media/usb/gspca/sunplus.c
@@ -555,7 +555,7 @@ static void init_ctl_reg(struct gspca_dev *gspca_dev)
case BRIDGE_SPCA504:
case BRIDGE_SPCA504C:
pollreg = 0;
- /* fall thru */
+ /* fall through */
default:
/* case BRIDGE_SPCA533: */
/* case BRIDGE_SPCA504B: */
@@ -638,7 +638,7 @@ static int sd_init(struct gspca_dev *gspca_dev)
reg_w_riv(gspca_dev, 0x00, 0x2000, 0x00);
reg_w_riv(gspca_dev, 0x00, 0x2301, 0x13);
reg_w_riv(gspca_dev, 0x00, 0x2306, 0x00);
- /* fall thru */
+ /* fall through */
case BRIDGE_SPCA533:
spca504B_PollingDataReady(gspca_dev);
spca50x_GetFirmware(gspca_dev);
diff --git a/drivers/media/usb/gspca/t613.c b/drivers/media/usb/gspca/t613.c
index 445782919446..ed9b925b723e 100644
--- a/drivers/media/usb/gspca/t613.c
+++ b/drivers/media/usb/gspca/t613.c
@@ -966,7 +966,7 @@ static int sd_init_controls(struct gspca_dev *gspca_dev)
V4L2_CID_SATURATION, 0, 0xf, 1, 5);
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
V4L2_CID_GAMMA, 0, GAMMA_MAX, 1, 10);
- /* Activate lowlight, some apps dont bring up the
+ /* Activate lowlight, some apps don't bring up the
backlight_compensation control) */
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
V4L2_CID_BACKLIGHT_COMPENSATION, 0, 1, 1, 1);
diff --git a/drivers/media/usb/gspca/touptek.c b/drivers/media/usb/gspca/touptek.c
index d1b9032d7863..6c056a448231 100644
--- a/drivers/media/usb/gspca/touptek.c
+++ b/drivers/media/usb/gspca/touptek.c
@@ -185,7 +185,7 @@ static const struct v4l2_pix_format vga_mode[] = {
};
/*
- * As theres no known frame sync, the only way to keep synced is to try hard
+ * As there's no known frame sync, the only way to keep synced is to try hard
* to never miss any packets
*/
#if MAX_NURBS < 4
@@ -259,7 +259,7 @@ static void setexposure(struct gspca_dev *gspca_dev, s32 val)
return;
}
gspca_dbg(gspca_dev, D_STREAM, "exposure: 0x%04X ms\n\n", value);
- /* Wonder if theres a good reason for sending it twice */
+ /* Wonder if there's a good reason for sending it twice */
/* probably not but leave it in because...why not */
reg_w(gspca_dev, value, REG_COARSE_INTEGRATION_TIME_);
reg_w(gspca_dev, value, REG_COARSE_INTEGRATION_TIME_);
diff --git a/drivers/media/usb/gspca/w996Xcf.c b/drivers/media/usb/gspca/w996Xcf.c
index abfab3de1866..36cc5a5ce77a 100644
--- a/drivers/media/usb/gspca/w996Xcf.c
+++ b/drivers/media/usb/gspca/w996Xcf.c
@@ -431,7 +431,7 @@ static void w9968cf_set_crop_window(struct sd *sd)
start_cropy = 35;
}
- /* Work around to avoid FP arithmetics */
+ /* Work around to avoid FP arithmetic */
#define SC(x) ((x) << 10)
/* Scaling factors */
diff --git a/drivers/media/usb/gspca/zc3xx-reg.h b/drivers/media/usb/gspca/zc3xx-reg.h
index 71fda38e85e0..26f6153b687f 100644
--- a/drivers/media/usb/gspca/zc3xx-reg.h
+++ b/drivers/media/usb/gspca/zc3xx-reg.h
@@ -26,7 +26,7 @@
/* Test mode */
#define ZC3XX_R00B_TESTMODECONTROL 0x000b
-/* Frame retreiving */
+/* Frame retrieving */
#define ZC3XX_R00C_LASTACQTIME 0x000c
#define ZC3XX_R00D_MONITORRES 0x000d
#define ZC3XX_R00E_TIMESTAMPHIGH 0x000e
diff --git a/drivers/media/usb/gspca/zc3xx.c b/drivers/media/usb/gspca/zc3xx.c
index cf21991e3d99..ad7194029031 100644
--- a/drivers/media/usb/gspca/zc3xx.c
+++ b/drivers/media/usb/gspca/zc3xx.c
@@ -3602,7 +3602,7 @@ static const struct usb_action pas106b_InitialScale[] = { /* 176x144 */
{0xaa, 0x14, 0x0081},
/* Other registers */
{0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
-/* Frame retreiving */
+/* Frame retrieving */
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
/* Gains */
{0xa0, 0xa0, ZC3XX_R1A8_DIGITALGAIN},
@@ -3718,7 +3718,7 @@ static const struct usb_action pas106b_Initial[] = { /* 352x288 */
{0xaa, 0x14, 0x0081},
/* Other registers */
{0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
-/* Frame retreiving */
+/* Frame retrieving */
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
/* Gains */
{0xa0, 0xa0, ZC3XX_R1A8_DIGITALGAIN},
@@ -6775,7 +6775,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
case SENSOR_HV7131R:
case SENSOR_TAS5130C:
reg_r(gspca_dev, 0x0008);
- /* fall thru */
+ /* fall through */
case SENSOR_PO2030:
reg_w(gspca_dev, 0x03, 0x0008);
break;
@@ -6824,7 +6824,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
case SENSOR_TAS5130C:
reg_w(gspca_dev, 0x09, 0x01ad); /* (from win traces) */
reg_w(gspca_dev, 0x15, 0x01ae);
- /* fall thru */
+ /* fall through */
case SENSOR_PAS202B:
case SENSOR_PO2030:
/* reg_w(gspca_dev, 0x40, ZC3XX_R117_GGAIN); in win traces */
diff --git a/drivers/media/usb/hdpvr/hdpvr-i2c.c b/drivers/media/usb/hdpvr/hdpvr-i2c.c
index 5a3cb614a211..d76173f1ced1 100644
--- a/drivers/media/usb/hdpvr/hdpvr-i2c.c
+++ b/drivers/media/usb/hdpvr/hdpvr-i2c.c
@@ -61,10 +61,10 @@ static int hdpvr_i2c_read(struct hdpvr_device *dev, int bus,
return -EINVAL;
if (wlen) {
- memcpy(&dev->i2c_buf, wdata, wlen);
+ memcpy(dev->i2c_buf, wdata, wlen);
ret = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
REQTYPE_I2C_WRITE, CTRL_WRITE_REQUEST,
- (bus << 8) | addr, 0, &dev->i2c_buf,
+ (bus << 8) | addr, 0, dev->i2c_buf,
wlen, 1000);
if (ret < 0)
return ret;
@@ -72,10 +72,10 @@ static int hdpvr_i2c_read(struct hdpvr_device *dev, int bus,
ret = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0),
REQTYPE_I2C_READ, CTRL_READ_REQUEST,
- (bus << 8) | addr, 0, &dev->i2c_buf, len, 1000);
+ (bus << 8) | addr, 0, dev->i2c_buf, len, 1000);
if (ret == len) {
- memcpy(data, &dev->i2c_buf, len);
+ memcpy(data, dev->i2c_buf, len);
ret = 0;
} else if (ret >= 0)
ret = -EIO;
@@ -91,17 +91,17 @@ static int hdpvr_i2c_write(struct hdpvr_device *dev, int bus,
if (len > sizeof(dev->i2c_buf))
return -EINVAL;
- memcpy(&dev->i2c_buf, data, len);
+ memcpy(dev->i2c_buf, data, len);
ret = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
REQTYPE_I2C_WRITE, CTRL_WRITE_REQUEST,
- (bus << 8) | addr, 0, &dev->i2c_buf, len, 1000);
+ (bus << 8) | addr, 0, dev->i2c_buf, len, 1000);
if (ret < 0)
return ret;
ret = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0),
REQTYPE_I2C_WRITE_STATT, CTRL_READ_REQUEST,
- 0, 0, &dev->i2c_buf, 2, 1000);
+ 0, 0, dev->i2c_buf, 2, 1000);
if ((ret == 2) && (dev->i2c_buf[1] == (len - 1)))
ret = 0;
diff --git a/drivers/media/usb/hdpvr/hdpvr.h b/drivers/media/usb/hdpvr/hdpvr.h
index 1d65b4185f57..fa43e1d45ea9 100644
--- a/drivers/media/usb/hdpvr/hdpvr.h
+++ b/drivers/media/usb/hdpvr/hdpvr.h
@@ -215,7 +215,7 @@ enum {
*/
/* :0 s 38 01 1700 0003 0001 1 = 00
- * VIDEO STANDARD or FREQUNCY 0 = 60hz, 1 = 50hz
+ * VIDEO STANDARD or FREQUENCY 0 = 60hz, 1 = 50hz
*/
/* :0 s 38 01 3100 0003 0004 4 = 03030000
diff --git a/drivers/media/usb/pwc/pwc-dec23.c b/drivers/media/usb/pwc/pwc-dec23.c
index 1283b3bd9800..854c36a5dec9 100644
--- a/drivers/media/usb/pwc/pwc-dec23.c
+++ b/drivers/media/usb/pwc/pwc-dec23.c
@@ -41,7 +41,7 @@
* UNROLL_LOOP_FOR_COPYING_BLOCK
* 0: use a loop for a smaller code (but little slower)
* 1: when unrolling the loop, gcc produces some faster code (perhaps only
- * valid for intel processor class). Activating this option, automaticaly
+ * valid for intel processor class). Activating this option, automatically
* activate USE_LOOKUP_TABLE_TO_CLAMP
*/
#define UNROLL_LOOP_FOR_COPY 1
@@ -332,7 +332,7 @@ void pwc_dec23_init(struct pwc_device *pdev, const unsigned char *cmd)
build_table_color(TimonRomTable[version][1], pdec->table_0004_pass2, pdec->table_8004_pass2);
}
- /* Informations can be coded on a variable number of bits but never less than 8 */
+ /* Information can be coded on a variable number of bits but never less than 8 */
shift = 8 - pdec->nbits;
pdec->scalebits = SCALEBITS - shift;
pdec->nbitsmask = 0xFF >> shift;
diff --git a/drivers/media/usb/pwc/pwc-if.c b/drivers/media/usb/pwc/pwc-if.c
index 72704f4d5330..4e94197094ad 100644
--- a/drivers/media/usb/pwc/pwc-if.c
+++ b/drivers/media/usb/pwc/pwc-if.c
@@ -76,6 +76,9 @@
#include "pwc-dec23.h"
#include "pwc-dec1.h"
+#define CREATE_TRACE_POINTS
+#include <trace/events/pwc.h>
+
/* Function prototypes and driver templates */
/* hotplug device table support */
@@ -156,6 +159,32 @@ static const struct video_device pwc_template = {
/***************************************************************************/
/* Private functions */
+static void *pwc_alloc_urb_buffer(struct device *dev,
+ size_t size, dma_addr_t *dma_handle)
+{
+ void *buffer = kmalloc(size, GFP_KERNEL);
+
+ if (!buffer)
+ return NULL;
+
+ *dma_handle = dma_map_single(dev, buffer, size, DMA_FROM_DEVICE);
+ if (dma_mapping_error(dev, *dma_handle)) {
+ kfree(buffer);
+ return NULL;
+ }
+
+ return buffer;
+}
+
+static void pwc_free_urb_buffer(struct device *dev,
+ size_t size,
+ void *buffer,
+ dma_addr_t dma_handle)
+{
+ dma_unmap_single(dev, dma_handle, size, DMA_FROM_DEVICE);
+ kfree(buffer);
+}
+
static struct pwc_frame_buf *pwc_get_next_fill_buf(struct pwc_device *pdev)
{
unsigned long flags = 0;
@@ -260,6 +289,8 @@ static void pwc_isoc_handler(struct urb *urb)
int i, fst, flen;
unsigned char *iso_buf = NULL;
+ trace_pwc_handler_enter(urb, pdev);
+
if (urb->status == -ENOENT || urb->status == -ECONNRESET ||
urb->status == -ESHUTDOWN) {
PWC_DEBUG_OPEN("URB (%p) unlinked %ssynchronously.\n",
@@ -301,6 +332,11 @@ static void pwc_isoc_handler(struct urb *urb)
/* Reset ISOC error counter. We did get here, after all. */
pdev->visoc_errors = 0;
+ dma_sync_single_for_cpu(&urb->dev->dev,
+ urb->transfer_dma,
+ urb->transfer_buffer_length,
+ DMA_FROM_DEVICE);
+
/* vsync: 0 = don't copy data
1 = sync-hunt
2 = synched
@@ -347,7 +383,14 @@ static void pwc_isoc_handler(struct urb *urb)
pdev->vlast_packet_size = flen;
}
+ dma_sync_single_for_device(&urb->dev->dev,
+ urb->transfer_dma,
+ urb->transfer_buffer_length,
+ DMA_FROM_DEVICE);
+
handler_end:
+ trace_pwc_handler_exit(urb, pdev);
+
i = usb_submit_urb(urb, GFP_ATOMIC);
if (i != 0)
PWC_ERROR("Error (%d) re-submitting urb in pwc_isoc_handler.\n", i);
@@ -421,16 +464,15 @@ retry:
urb->dev = udev;
urb->pipe = usb_rcvisocpipe(udev, pdev->vendpoint);
urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
- urb->transfer_buffer = usb_alloc_coherent(udev,
- ISO_BUFFER_SIZE,
- GFP_KERNEL,
- &urb->transfer_dma);
+ urb->transfer_buffer_length = ISO_BUFFER_SIZE;
+ urb->transfer_buffer = pwc_alloc_urb_buffer(&udev->dev,
+ urb->transfer_buffer_length,
+ &urb->transfer_dma);
if (urb->transfer_buffer == NULL) {
PWC_ERROR("Failed to allocate urb buffer %d\n", i);
pwc_isoc_cleanup(pdev);
return -ENOMEM;
}
- urb->transfer_buffer_length = ISO_BUFFER_SIZE;
urb->complete = pwc_isoc_handler;
urb->context = pdev;
urb->start_frame = 0;
@@ -481,15 +523,16 @@ static void pwc_iso_free(struct pwc_device *pdev)
/* Freeing ISOC buffers one by one */
for (i = 0; i < MAX_ISO_BUFS; i++) {
- if (pdev->urbs[i]) {
+ struct urb *urb = pdev->urbs[i];
+
+ if (urb) {
PWC_DEBUG_MEMORY("Freeing URB\n");
- if (pdev->urbs[i]->transfer_buffer) {
- usb_free_coherent(pdev->udev,
- pdev->urbs[i]->transfer_buffer_length,
- pdev->urbs[i]->transfer_buffer,
- pdev->urbs[i]->transfer_dma);
- }
- usb_free_urb(pdev->urbs[i]);
+ if (urb->transfer_buffer)
+ pwc_free_urb_buffer(&urb->dev->dev,
+ urb->transfer_buffer_length,
+ urb->transfer_buffer,
+ urb->transfer_dma);
+ usb_free_urb(urb);
pdev->urbs[i] = NULL;
}
}
@@ -610,7 +653,7 @@ static int buffer_prepare(struct vb2_buffer *vb)
{
struct pwc_device *pdev = vb2_get_drv_priv(vb->vb2_queue);
- /* Don't allow queing new buffers after device disconnection */
+ /* Don't allow queueing new buffers after device disconnection */
if (!pdev->udev)
return -ENODEV;
diff --git a/drivers/media/usb/pwc/pwc-misc.c b/drivers/media/usb/pwc/pwc-misc.c
index 9be5adffa874..03888fc3804d 100644
--- a/drivers/media/usb/pwc/pwc-misc.c
+++ b/drivers/media/usb/pwc/pwc-misc.c
@@ -59,7 +59,7 @@ int pwc_get_size(struct pwc_device *pdev, int width, int height)
return i;
}
- /* Never reached there always is atleast one supported mode */
+ /* Never reached there always is at least one supported mode */
return 0;
}
diff --git a/drivers/media/usb/siano/smsusb.c b/drivers/media/usb/siano/smsusb.c
index 2ffded08407b..4fc03ec8a4f1 100644
--- a/drivers/media/usb/siano/smsusb.c
+++ b/drivers/media/usb/siano/smsusb.c
@@ -75,7 +75,7 @@ static int smsusb_submit_urb(struct smsusb_device_t *dev,
struct smsusb_urb_t *surb);
/*
- * Completing URB's callback handler - bottom half (proccess context)
+ * Completing URB's callback handler - bottom half (process context)
* submits the URB prepared on smsusb_onresponse()
*/
static void do_submit_urb(struct work_struct *work)
diff --git a/drivers/media/usb/stk1160/stk1160-core.c b/drivers/media/usb/stk1160/stk1160-core.c
index 468f5ccf4ae6..a44a44ff3bb1 100644
--- a/drivers/media/usb/stk1160/stk1160-core.c
+++ b/drivers/media/usb/stk1160/stk1160-core.c
@@ -297,7 +297,7 @@ static int stk1160_probe(struct usb_interface *interface,
return -ENOMEM;
/*
- * Scan usb posibilities and populate alt_max_pkt_size array.
+ * Scan usb possibilities and populate alt_max_pkt_size array.
* Also, check if device speed is fast enough.
*/
rc = stk1160_scan_usb(interface, udev, alt_max_pkt_size);
@@ -426,7 +426,7 @@ static void stk1160_disconnect(struct usb_interface *interface)
/*
* This calls stk1160_release if it's the last reference.
- * Otherwise, release is posponed until there are no users left.
+ * Otherwise, release is postponed until there are no users left.
*/
v4l2_device_put(&dev->v4l2_dev);
}
diff --git a/drivers/media/usb/stk1160/stk1160-reg.h b/drivers/media/usb/stk1160/stk1160-reg.h
index 7b08a3cc4504..2e400db0ad0e 100644
--- a/drivers/media/usb/stk1160/stk1160-reg.h
+++ b/drivers/media/usb/stk1160/stk1160-reg.h
@@ -23,7 +23,7 @@
/* GPIO Control */
#define STK1160_GCTRL 0x000
-/* Remote Wakup Control */
+/* Remote Wakeup Control */
#define STK1160_RMCTL 0x00c
/* Power-on Strapping Data */
@@ -104,7 +104,7 @@
#define STK1160_SBUSR_RA 0x208
#define STK1160_SBUSR_RD 0x209
-/* Alternate Serial Inteface Control */
+/* Alternate Serial Interface Control */
#define STK1160_ASIC 0x2fc
/* PLL Select Options */
diff --git a/drivers/media/usb/stkwebcam/stk-webcam.c b/drivers/media/usb/stkwebcam/stk-webcam.c
index b8ec74d98e8d..8f545861471e 100644
--- a/drivers/media/usb/stkwebcam/stk-webcam.c
+++ b/drivers/media/usb/stkwebcam/stk-webcam.c
@@ -1006,7 +1006,7 @@ static int stk_setup_format(struct stk_camera *dev)
stk_camera_write_reg(dev, 0x001c, 0x46);
/*
* Registers 0x0115 0x0114 are the size of each line (bytes),
- * regs 0x0117 0x0116 are the heigth of the image.
+ * regs 0x0117 0x0116 are the height of the image.
*/
stk_camera_write_reg(dev, 0x0115,
((stk_sizes[i].w * depth) >> 8) & 0xff);
@@ -1144,7 +1144,7 @@ static int stk_vidioc_dqbuf(struct file *filp,
sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_QUEUED;
sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_DONE;
sbuf->v4lbuf.sequence = ++dev->sequence;
- v4l2_get_timestamp(&sbuf->v4lbuf.timestamp);
+ sbuf->v4lbuf.timestamp = ns_to_timeval(ktime_get_ns());
*buf = sbuf->v4lbuf;
return 0;
diff --git a/drivers/media/usb/tm6000/tm6000-alsa.c b/drivers/media/usb/tm6000/tm6000-alsa.c
index b965931793b5..d6c79c13b332 100644
--- a/drivers/media/usb/tm6000/tm6000-alsa.c
+++ b/drivers/media/usb/tm6000/tm6000-alsa.c
@@ -58,7 +58,7 @@ module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "enable debug messages");
/****************************************************************************
- Module specific funtions
+ Module specific functions
****************************************************************************/
/*
diff --git a/drivers/media/usb/tm6000/tm6000-core.c b/drivers/media/usb/tm6000/tm6000-core.c
index d3229aa45fcb..2c723706f8c8 100644
--- a/drivers/media/usb/tm6000/tm6000-core.c
+++ b/drivers/media/usb/tm6000/tm6000-core.c
@@ -668,7 +668,7 @@ int tm6000_set_audio_rinput(struct tm6000_core *dev)
areg_f0 = 0x04;
break;
default:
- printk(KERN_INFO "%s: audio input dosn't support\n",
+ printk(KERN_INFO "%s: audio input doesn't support\n",
dev->name);
return 0;
break;
@@ -690,7 +690,7 @@ int tm6000_set_audio_rinput(struct tm6000_core *dev)
areg_eb = 0x04;
break;
default:
- printk(KERN_INFO "%s: audio input dosn't support\n",
+ printk(KERN_INFO "%s: audio input doesn't support\n",
dev->name);
return 0;
break;
diff --git a/drivers/media/usb/tm6000/tm6000-dvb.c b/drivers/media/usb/tm6000/tm6000-dvb.c
index 3a4e545c6037..36eea1950e77 100644
--- a/drivers/media/usb/tm6000/tm6000-dvb.c
+++ b/drivers/media/usb/tm6000/tm6000-dvb.c
@@ -149,7 +149,7 @@ static int tm6000_start_stream(struct tm6000_core *dev)
ret, __func__);
return ret;
} else
- printk(KERN_ERR "tm6000: pipe resetted\n");
+ printk(KERN_ERR "tm6000: pipe reset\n");
/* mutex_lock(&tm6000_driver.open_close_mutex); */
ret = usb_submit_urb(dvb->bulk_urb, GFP_ATOMIC);
diff --git a/drivers/media/usb/tm6000/tm6000-i2c.c b/drivers/media/usb/tm6000/tm6000-i2c.c
index 8c0476dfe54f..b37782d6f79c 100644
--- a/drivers/media/usb/tm6000/tm6000-i2c.c
+++ b/drivers/media/usb/tm6000/tm6000-i2c.c
@@ -155,7 +155,7 @@ static int tm6000_i2c_xfer(struct i2c_adapter *i2c_adap,
/*
* The TM6000 only supports a read transaction
* immediately after a 1 or 2 byte write to select
- * a register. We cannot fulfil this request.
+ * a register. We cannot fulfill this request.
*/
i2c_dprintk(2, " read without preceding write not supported");
rc = -EOPNOTSUPP;
diff --git a/drivers/media/usb/tm6000/tm6000-stds.c b/drivers/media/usb/tm6000/tm6000-stds.c
index c0c75951246b..858cb4f3a9ca 100644
--- a/drivers/media/usb/tm6000/tm6000-stds.c
+++ b/drivers/media/usb/tm6000/tm6000-stds.c
@@ -323,7 +323,7 @@ static int tm6000_set_audio_std(struct tm6000_core *dev)
{
uint8_t areg_02 = 0x04; /* GC1 Fixed gain 0dB */
uint8_t areg_05 = 0x01; /* Auto 4.5 = M Japan, Auto 6.5 = DK */
- uint8_t areg_06 = 0x02; /* Auto de-emphasis, mannual channel mode */
+ uint8_t areg_06 = 0x02; /* Auto de-emphasis, manual channel mode */
if (dev->radio) {
tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x00);
diff --git a/drivers/media/usb/tm6000/tm6000-video.c b/drivers/media/usb/tm6000/tm6000-video.c
index ee7b5318b351..072210f5f92f 100644
--- a/drivers/media/usb/tm6000/tm6000-video.c
+++ b/drivers/media/usb/tm6000/tm6000-video.c
@@ -106,7 +106,7 @@ static inline void buffer_filled(struct tm6000_core *dev,
dprintk(dev, V4L2_DEBUG_ISOC, "[%p/%d] wakeup\n", buf, buf->vb.i);
buf->vb.state = VIDEOBUF_DONE;
buf->vb.field_count++;
- v4l2_get_timestamp(&buf->vb.ts);
+ buf->vb.ts = ktime_get_ns();
list_del(&buf->vb.queue);
wake_up(&buf->vb.done);
@@ -180,7 +180,7 @@ static int copy_streams(u8 *data, unsigned long len,
field = (header >> 11) & 0x1;
line = (header >> 12) & 0x1ff;
cmd = (header >> 21) & 0x7;
- /* Validates haeder fields */
+ /* Validates header fields */
if (size > TM6000_URB_MSG_LEN)
size = TM6000_URB_MSG_LEN;
pktsize = TM6000_URB_MSG_LEN;
diff --git a/drivers/media/usb/ttusb-budget/dvb-ttusb-budget.c b/drivers/media/usb/ttusb-budget/dvb-ttusb-budget.c
index 6eb84cf007b4..4db7a013e049 100644
--- a/drivers/media/usb/ttusb-budget/dvb-ttusb-budget.c
+++ b/drivers/media/usb/ttusb-budget/dvb-ttusb-budget.c
@@ -306,7 +306,7 @@ static int ttusb_boot_dsp(struct ttusb *ttusb)
b[3] = 28;
/* upload dsp code in 32 byte steps (36 didn't work for me ...) */
- /* 32 is max packet size, no messages should be splitted. */
+ /* 32 is max packet size, no messages should be split. */
for (i = 0; i < fw->size; i += 28) {
memcpy(&b[4], &fw->data[i], 28);
diff --git a/drivers/media/usb/ttusb-dec/ttusb_dec.c b/drivers/media/usb/ttusb-dec/ttusb_dec.c
index 44ca66cb9b8f..897ef5e1da71 100644
--- a/drivers/media/usb/ttusb-dec/ttusb_dec.c
+++ b/drivers/media/usb/ttusb-dec/ttusb_dec.c
@@ -284,7 +284,7 @@ static void ttusb_dec_handle_irq( struct urb *urb)
*
* this is an fact a bit too simple implementation;
* the box also reports a keyrepeat signal
- * (with buffer[3] == 0x40) in an intervall of ~100ms.
+ * (with buffer[3] == 0x40) in an interval of ~100ms.
* But to handle this correctly we had to imlemenent some
* kind of timer which signals a 'key up' event if no
* keyrepeat signal is received for lets say 200ms.
diff --git a/drivers/media/usb/usbvision/usbvision-core.c b/drivers/media/usb/usbvision/usbvision-core.c
index 31e0e98d6daf..92d166bf8c12 100644
--- a/drivers/media/usb/usbvision/usbvision-core.c
+++ b/drivers/media/usb/usbvision/usbvision-core.c
@@ -900,7 +900,7 @@ static enum parse_state usbvision_parse_lines_420(struct usb_usbvision *usbvisio
if ((frame->curline + 1) >= frame->frmheight)
return parse_state_next_frame;
- block_split = (pixel_per_line%y_block_size) ? 1 : 0; /* are some blocks splitted into different lines? */
+ block_split = (pixel_per_line%y_block_size) ? 1 : 0; /* are some blocks split into different lines? */
y_odd_offset = (pixel_per_line / y_block_size) * (y_block_size + uv_block_size)
+ block_split * uv_block_size;
@@ -1160,7 +1160,7 @@ static void usbvision_parse_data(struct usb_usbvision *usbvision)
if (newstate == parse_state_next_frame) {
frame->grabstate = frame_state_done;
- v4l2_get_timestamp(&(frame->timestamp));
+ frame->ts = ktime_get_ns();
frame->sequence = usbvision->frame_num;
spin_lock_irqsave(&usbvision->queue_lock, lock_flags);
@@ -1865,7 +1865,7 @@ static int usbvision_set_compress_params(struct usb_usbvision *usbvision)
value[4] = 0xA2; /* Reg.48 BUF_THR I'm not sure if this does something in not compressed mode. */
value[5] = 0x00; /* Reg.49 DVI_YUV This has nothing to do with compression */
- /* catched values for NT1004 */
+ /* caught values for NT1004 */
/* value[0] = 0xFF; Never apply intra mode automatically */
/* value[1] = 0xF1; Use full frame height for virtual strip width; One line per strip */
/* value[2] = 0x01; Force intra mode on all new frames */
@@ -1943,7 +1943,7 @@ int usbvision_set_input(struct usb_usbvision *usbvision)
/* SAA7113 uses 8 bit output */
value[0] = USBVISION_8_422_SYNC;
} else {
- /* I'm sure only about d2-d0 [010] 16 bit 4:2:2 usin sync pulses
+ /* I'm sure only about d2-d0 [010] 16 bit 4:2:2 using sync pulses
* as that is how saa7111 is configured */
value[0] = USBVISION_16_422_SYNC;
/* | USBVISION_VSNC_POL | USBVISION_VCLK_POL);*/
@@ -2146,7 +2146,7 @@ int usbvision_power_on(struct usb_usbvision *usbvision)
/*
* usbvision_begin_streaming()
- * Sure you have to put bit 7 to 0, if not incoming frames are droped, but no
+ * Sure you have to put bit 7 to 0, if not incoming frames are dropped, but no
* idea about the rest
*/
int usbvision_begin_streaming(struct usb_usbvision *usbvision)
diff --git a/drivers/media/usb/usbvision/usbvision-video.c b/drivers/media/usb/usbvision/usbvision-video.c
index dd2ff8ed6c6a..e611052ebf59 100644
--- a/drivers/media/usb/usbvision/usbvision-video.c
+++ b/drivers/media/usb/usbvision/usbvision-video.c
@@ -706,7 +706,7 @@ static int vidioc_querybuf(struct file *file,
vb->length = usbvision->curwidth *
usbvision->curheight *
usbvision->palette.bytes_per_pixel;
- vb->timestamp = usbvision->frame[vb->index].timestamp;
+ vb->timestamp = ns_to_timeval(usbvision->frame[vb->index].ts);
vb->sequence = usbvision->frame[vb->index].sequence;
return 0;
}
@@ -775,7 +775,7 @@ static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *vb)
V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
vb->index = f->index;
vb->sequence = f->sequence;
- vb->timestamp = f->timestamp;
+ vb->timestamp = ns_to_timeval(f->ts);
vb->field = V4L2_FIELD_NONE;
vb->bytesused = f->scanlength;
diff --git a/drivers/media/usb/usbvision/usbvision.h b/drivers/media/usb/usbvision/usbvision.h
index 017e7baf5747..668167f8951d 100644
--- a/drivers/media/usb/usbvision/usbvision.h
+++ b/drivers/media/usb/usbvision/usbvision.h
@@ -135,11 +135,11 @@
#define MIN_FRAME_WIDTH 64
#define MAX_USB_WIDTH 320 /* 384 */
-#define MAX_FRAME_WIDTH 320 /* 384 */ /* streching sometimes causes crashes*/
+#define MAX_FRAME_WIDTH 320 /* 384 */ /* stretching sometimes causes crashes*/
#define MIN_FRAME_HEIGHT 48
#define MAX_USB_HEIGHT 240 /* 288 */
-#define MAX_FRAME_HEIGHT 240 /* 288 */ /* Streching sometimes causes crashes*/
+#define MAX_FRAME_HEIGHT 240 /* 288 */ /* Stretching sometimes causes crashes*/
#define MAX_FRAME_SIZE (MAX_FRAME_WIDTH * MAX_FRAME_HEIGHT * MAX_BYTES_PER_PIXEL)
#define USBVISION_CLIPMASK_SIZE (MAX_FRAME_WIDTH * MAX_FRAME_HEIGHT / 8) /* bytesize of clipmask */
@@ -177,7 +177,7 @@ enum {
* G = 1.164*(Y-16) - 0.813*(U-128) - 0.391*(V-128)
* R = 1.164*(Y-16) + 1.596*(U-128)
*
- * If you fancy integer arithmetics (as you should), hear this:
+ * If you fancy integer arithmetic (as you should), hear this:
*
* 65536*B = 76284*(Y-16) + 132252*(V-128)
* 65536*G = 76284*(Y-16) - 53281*(U-128) - 25625*(V-128)
@@ -316,7 +316,7 @@ struct usbvision_frame {
long bytes_read; /* amount of scanlength that has been read from data */
struct usbvision_v4l2_format_st v4l2_format; /* format the user needs*/
int v4l2_linesize; /* bytes for one videoline*/
- struct timeval timestamp;
+ u64 ts;
int sequence; /* How many video frames we send to user */
};
@@ -438,7 +438,7 @@ struct usb_usbvision {
int last_compr_level; /* How strong (100) or weak (0) was compression */
int usb_bandwidth; /* Mbit/s */
- /* Statistics that can be overlayed on the screen */
+ /* Statistics that can be overlaid on the screen */
unsigned long isoc_urb_count; /* How many URBs we received so far */
unsigned long urb_length; /* Length of last URB */
unsigned long isoc_data_count; /* How many bytes we received */
diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index d45415cbe6e7..14cff91b7aea 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -1212,7 +1212,7 @@ static void uvc_ctrl_fill_event(struct uvc_video_chain *chain,
__uvc_query_v4l2_ctrl(chain, ctrl, mapping, &v4l2_ctrl);
- memset(ev->reserved, 0, sizeof(ev->reserved));
+ memset(ev, 0, sizeof(*ev));
ev->type = V4L2_EVENT_CTRL;
ev->id = v4l2_ctrl.id;
ev->u.ctrl.value = value;
diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
index b62cbd800111..10cfe8e51626 100644
--- a/drivers/media/usb/uvc/uvc_driver.c
+++ b/drivers/media/usb/uvc/uvc_driver.c
@@ -1106,11 +1106,19 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
return -EINVAL;
}
- /* Make sure the terminal type MSB is not null, otherwise it
- * could be confused with a unit.
+ /*
+ * Reject invalid terminal types that would cause issues:
+ *
+ * - The high byte must be non-zero, otherwise it would be
+ * confused with a unit.
+ *
+ * - Bit 15 must be 0, as we use it internally as a terminal
+ * direction flag.
+ *
+ * Other unknown types are accepted.
*/
type = get_unaligned_le16(&buffer[4]);
- if ((type & 0xff00) == 0) {
+ if ((type & 0x7f00) == 0 || (type & 0x8000) != 0) {
uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
"interface %d INPUT_TERMINAL %d has invalid "
"type 0x%04x, skipping\n", udev->devnum,
@@ -2175,7 +2183,7 @@ static int uvc_probe(struct usb_interface *intf,
if (udev->serial)
strscpy(dev->mdev.serial, udev->serial,
sizeof(dev->mdev.serial));
- strscpy(dev->mdev.bus_info, udev->devpath, sizeof(dev->mdev.bus_info));
+ usb_make_path(udev, dev->mdev.bus_info, sizeof(dev->mdev.bus_info));
dev->mdev.hw_revision = le16_to_cpu(udev->descriptor.bcdDevice);
media_device_init(&dev->mdev);
diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c
index 84525ff04745..182dcac49aa3 100644
--- a/drivers/media/usb/uvc/uvc_video.c
+++ b/drivers/media/usb/uvc/uvc_video.c
@@ -676,6 +676,14 @@ void uvc_video_clock_update(struct uvc_streaming *stream,
if (!uvc_hw_timestamps_param)
return;
+ /*
+ * We will get called from __vb2_queue_cancel() if there are buffers
+ * done but not dequeued by the user, but the sample array has already
+ * been released at that time. Just bail out in that case.
+ */
+ if (!clock->samples)
+ return;
+
spin_lock_irqsave(&clock->lock, flags);
if (clock->count < clock->size)
@@ -2000,7 +2008,7 @@ int uvc_video_init(struct uvc_streaming *stream)
usb_set_interface(stream->dev->udev, stream->intfnum, 0);
/* Set the streaming probe control with default streaming parameters
- * retrieved from the device. Webcams that don't suport GET_DEF
+ * retrieved from the device. Webcams that don't support GET_DEF
* requests on the probe control will just keep their current streaming
* parameters.
*/
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index 9b41b14ce076..c7c1baa90dea 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -620,8 +620,10 @@ struct uvc_streaming {
(uvc_urb) < &(uvc_streaming)->uvc_urb[UVC_URBS]; \
++(uvc_urb))
-#define uvc_urb_index(uvc_urb) \
- (unsigned int)((uvc_urb) - (&(uvc_urb)->stream->uvc_urb[0]))
+static inline u32 uvc_urb_index(const struct uvc_urb *uvc_urb)
+{
+ return uvc_urb - &uvc_urb->stream->uvc_urb[0];
+}
struct uvc_device_info {
u32 quirks;
diff --git a/drivers/media/usb/zr364xx/zr364xx.c b/drivers/media/usb/zr364xx/zr364xx.c
index ab35554cbffa..96fee8d5b865 100644
--- a/drivers/media/usb/zr364xx/zr364xx.c
+++ b/drivers/media/usb/zr364xx/zr364xx.c
@@ -2,7 +2,7 @@
* Zoran 364xx based USB webcam module version 0.73
*
* Allows you to use your USB webcam with V4L2 applications
- * This is still in heavy developpement !
+ * This is still in heavy development !
*
* Copyright (C) 2004 Antoine Jacquet <royale@zerezo.com>
* http://royale.zerezo.com/zr364xx/
@@ -521,7 +521,7 @@ static void zr364xx_fillbuff(struct zr364xx_camera *cam,
/* tell v4l buffer was filled */
buf->vb.field_count = cam->frame_count * 2;
- v4l2_get_timestamp(&buf->vb.ts);
+ buf->vb.ts = ktime_get_ns();
buf->vb.state = VIDEOBUF_DONE;
}
@@ -549,7 +549,7 @@ static int zr364xx_got_frame(struct zr364xx_camera *cam, int jpgsize)
goto unlock;
}
list_del(&buf->vb.queue);
- v4l2_get_timestamp(&buf->vb.ts);
+ buf->vb.ts = ktime_get_ns();
DBG("[%p/%d] wakeup\n", buf, buf->vb.i);
zr364xx_fillbuff(cam, buf, jpgsize);
wake_up(&buf->vb.done);
diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c
index 50763fb42a1b..663730f088cd 100644
--- a/drivers/media/v4l2-core/v4l2-common.c
+++ b/drivers/media/v4l2-core/v4l2-common.c
@@ -398,16 +398,6 @@ __v4l2_find_nearest_size(const void *array, size_t array_size,
}
EXPORT_SYMBOL_GPL(__v4l2_find_nearest_size);
-void v4l2_get_timestamp(struct timeval *tv)
-{
- struct timespec ts;
-
- ktime_get_ts(&ts);
- tv->tv_sec = ts.tv_sec;
- tv->tv_usec = ts.tv_nsec / NSEC_PER_USEC;
-}
-EXPORT_SYMBOL_GPL(v4l2_get_timestamp);
-
int v4l2_g_parm_cap(struct video_device *vdev,
struct v4l2_subdev *sd, struct v4l2_streamparm *a)
{
diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c
index 5e3806feb5d7..b79d3bbd8350 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls.c
@@ -825,6 +825,9 @@ const char *v4l2_ctrl_get_name(u32 id)
case V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER:return "H264 Number of HC Layers";
case V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_QP:
return "H264 Set QP Value for HC Layers";
+ case V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION:
+ return "H264 Constrained Intra Pred";
+ case V4L2_CID_MPEG_VIDEO_H264_CHROMA_QP_INDEX_OFFSET: return "H264 Chroma QP Index Offset";
case V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP: return "MPEG4 I-Frame QP Value";
case V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP: return "MPEG4 P-Frame QP Value";
case V4L2_CID_MPEG_VIDEO_MPEG4_B_FRAME_QP: return "MPEG4 B-Frame QP Value";
@@ -1387,7 +1390,7 @@ static u32 user_flags(const struct v4l2_ctrl *ctrl)
static void fill_event(struct v4l2_event *ev, struct v4l2_ctrl *ctrl, u32 changes)
{
- memset(ev->reserved, 0, sizeof(ev->reserved));
+ memset(ev, 0, sizeof(*ev));
ev->type = V4L2_EVENT_CTRL;
ev->id = ctrl->id;
ev->u.ctrl.changes = changes;
@@ -1661,15 +1664,6 @@ static int std_validate(const struct v4l2_ctrl *ctrl, u32 idx,
return -EINVAL;
}
- if (p_mpeg2_slice_params->backward_ref_index >= VIDEO_MAX_FRAME ||
- p_mpeg2_slice_params->forward_ref_index >= VIDEO_MAX_FRAME)
- return -EINVAL;
-
- if (p_mpeg2_slice_params->pad ||
- p_mpeg2_slice_params->picture.pad ||
- p_mpeg2_slice_params->sequence.pad)
- return -EINVAL;
-
return 0;
case V4L2_CTRL_TYPE_MPEG2_QUANTIZATION:
@@ -4172,9 +4166,9 @@ __poll_t v4l2_ctrl_poll(struct file *file, struct poll_table_struct *wait)
{
struct v4l2_fh *fh = file->private_data;
+ poll_wait(file, &fh->wait, wait);
if (v4l2_event_pending(fh))
return EPOLLPRI;
- poll_wait(file, &fh->wait, wait);
return 0;
}
EXPORT_SYMBOL(v4l2_ctrl_poll);
diff --git a/drivers/media/v4l2-core/v4l2-event.c b/drivers/media/v4l2-core/v4l2-event.c
index 481e3c65cf97..c46d14c996fc 100644
--- a/drivers/media/v4l2-core/v4l2-event.c
+++ b/drivers/media/v4l2-core/v4l2-event.c
@@ -52,6 +52,7 @@ static int __v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event)
kev->event.pending = fh->navailable;
*event = kev->event;
+ event->timestamp = ns_to_timespec(kev->ts);
kev->sev->first = sev_pos(kev->sev, 1);
kev->sev->in_use--;
@@ -103,8 +104,8 @@ static struct v4l2_subscribed_event *v4l2_event_subscribed(
return NULL;
}
-static void __v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *ev,
- const struct timespec *ts)
+static void __v4l2_event_queue_fh(struct v4l2_fh *fh,
+ const struct v4l2_event *ev, u64 ts)
{
struct v4l2_subscribed_event *sev;
struct v4l2_kevent *kev;
@@ -144,7 +145,7 @@ static void __v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *e
if (copy_payload)
kev->event.u = ev->u;
kev->event.id = ev->id;
- kev->event.timestamp = *ts;
+ kev->ts = ts;
kev->event.sequence = fh->sequence;
sev->in_use++;
list_add_tail(&kev->list, &fh->available);
@@ -158,17 +159,17 @@ void v4l2_event_queue(struct video_device *vdev, const struct v4l2_event *ev)
{
struct v4l2_fh *fh;
unsigned long flags;
- struct timespec timestamp;
+ u64 ts;
if (vdev == NULL)
return;
- ktime_get_ts(&timestamp);
+ ts = ktime_get_ns();
spin_lock_irqsave(&vdev->fh_lock, flags);
list_for_each_entry(fh, &vdev->fh_list, list)
- __v4l2_event_queue_fh(fh, ev, &timestamp);
+ __v4l2_event_queue_fh(fh, ev, ts);
spin_unlock_irqrestore(&vdev->fh_lock, flags);
}
@@ -177,12 +178,10 @@ EXPORT_SYMBOL_GPL(v4l2_event_queue);
void v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *ev)
{
unsigned long flags;
- struct timespec timestamp;
-
- ktime_get_ts(&timestamp);
+ u64 ts = ktime_get_ns();
spin_lock_irqsave(&fh->vdev->fh_lock, flags);
- __v4l2_event_queue_fh(fh, ev, &timestamp);
+ __v4l2_event_queue_fh(fh, ev, ts);
spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
}
EXPORT_SYMBOL_GPL(v4l2_event_queue_fh);
diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c
index 9bfedd7596a1..20571846e636 100644
--- a/drivers/media/v4l2-core/v4l2-fwnode.c
+++ b/drivers/media/v4l2-core/v4l2-fwnode.c
@@ -46,7 +46,7 @@ static const struct v4l2_fwnode_bus_conv {
enum v4l2_fwnode_bus_type fwnode_bus_type;
enum v4l2_mbus_type mbus_type;
const char *name;
-} busses[] = {
+} buses[] = {
{
V4L2_FWNODE_BUS_TYPE_GUESS,
V4L2_MBUS_UNKNOWN,
@@ -83,9 +83,9 @@ get_v4l2_fwnode_bus_conv_by_fwnode_bus(enum v4l2_fwnode_bus_type type)
{
unsigned int i;
- for (i = 0; i < ARRAY_SIZE(busses); i++)
- if (busses[i].fwnode_bus_type == type)
- return &busses[i];
+ for (i = 0; i < ARRAY_SIZE(buses); i++)
+ if (buses[i].fwnode_bus_type == type)
+ return &buses[i];
return NULL;
}
@@ -113,9 +113,9 @@ get_v4l2_fwnode_bus_conv_by_mbus(enum v4l2_mbus_type type)
{
unsigned int i;
- for (i = 0; i < ARRAY_SIZE(busses); i++)
- if (busses[i].mbus_type == type)
- return &busses[i];
+ for (i = 0; i < ARRAY_SIZE(buses); i++)
+ if (buses[i].mbus_type == type)
+ return &buses[i];
return NULL;
}
@@ -809,7 +809,7 @@ error:
* root node and the value of that property matching with the integer argument
* of the reference, at the same index.
*
- * The child fwnode reched at the end of the iteration is then returned to the
+ * The child fwnode reached at the end of the iteration is then returned to the
* caller.
*
* The core reason for this is that you cannot refer to just any node in ACPI.
diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index 90aad465f9ed..f6d663934648 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -88,7 +88,7 @@ const char *v4l2_norm_to_name(v4l2_std_id id)
int i;
/* HACK: ppc32 architecture doesn't have __ucmpdi2 function to handle
- 64 bit comparations. So, on that architecture, with some gcc
+ 64 bit comparisons. So, on that architecture, with some gcc
variants, compilation fails. Currently, the max value is 30bit wide.
*/
BUG_ON(myid != id);
@@ -1017,6 +1017,12 @@ static void v4l_sanitize_format(struct v4l2_format *fmt)
{
unsigned int offset;
+ /* Make sure num_planes is not bogus */
+ if (fmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ||
+ fmt->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ fmt->fmt.pix_mp.num_planes = min_t(u32, fmt->fmt.pix_mp.num_planes,
+ VIDEO_MAX_PLANES);
+
/*
* The v4l2_pix_format structure has been extended with fields that were
* not previously required to be set to zero by applications. The priv
@@ -1214,6 +1220,10 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)
case V4L2_PIX_FMT_YUV555: descr = "16-bit A/XYUV 1-5-5-5"; break;
case V4L2_PIX_FMT_YUV565: descr = "16-bit YUV 5-6-5"; break;
case V4L2_PIX_FMT_YUV32: descr = "32-bit A/XYUV 8-8-8-8"; break;
+ case V4L2_PIX_FMT_AYUV32: descr = "32-bit AYUV 8-8-8-8"; break;
+ case V4L2_PIX_FMT_XYUV32: descr = "32-bit XYUV 8-8-8-8"; break;
+ case V4L2_PIX_FMT_VUYA32: descr = "32-bit VUYA 8-8-8-8"; break;
+ case V4L2_PIX_FMT_VUYX32: descr = "32-bit VUYX 8-8-8-8"; break;
case V4L2_PIX_FMT_YUV410: descr = "Planar YUV 4:1:0"; break;
case V4L2_PIX_FMT_YUV420: descr = "Planar YUV 4:2:0"; break;
case V4L2_PIX_FMT_HI240: descr = "8-bit Dithered RGB (BTTV)"; break;
@@ -1553,8 +1563,6 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,
if (unlikely(!ops->vidioc_s_fmt_vid_cap_mplane))
break;
CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
- if (p->fmt.pix_mp.num_planes > VIDEO_MAX_PLANES)
- break;
for (i = 0; i < p->fmt.pix_mp.num_planes; i++)
CLEAR_AFTER_FIELD(&p->fmt.pix_mp.plane_fmt[i],
bytesperline);
@@ -1586,8 +1594,6 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,
if (unlikely(!ops->vidioc_s_fmt_vid_out_mplane))
break;
CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
- if (p->fmt.pix_mp.num_planes > VIDEO_MAX_PLANES)
- break;
for (i = 0; i < p->fmt.pix_mp.num_planes; i++)
CLEAR_AFTER_FIELD(&p->fmt.pix_mp.plane_fmt[i],
bytesperline);
@@ -1656,8 +1662,6 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops,
if (unlikely(!ops->vidioc_try_fmt_vid_cap_mplane))
break;
CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
- if (p->fmt.pix_mp.num_planes > VIDEO_MAX_PLANES)
- break;
for (i = 0; i < p->fmt.pix_mp.num_planes; i++)
CLEAR_AFTER_FIELD(&p->fmt.pix_mp.plane_fmt[i],
bytesperline);
@@ -1689,8 +1693,6 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops,
if (unlikely(!ops->vidioc_try_fmt_vid_out_mplane))
break;
CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
- if (p->fmt.pix_mp.num_planes > VIDEO_MAX_PLANES)
- break;
for (i = 0; i < p->fmt.pix_mp.num_planes; i++)
CLEAR_AFTER_FIELD(&p->fmt.pix_mp.plane_fmt[i],
bytesperline);
diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c
index 5bbdec55b7d7..3392833d9541 100644
--- a/drivers/media/v4l2-core/v4l2-mem2mem.c
+++ b/drivers/media/v4l2-core/v4l2-mem2mem.c
@@ -131,7 +131,7 @@ struct vb2_queue *v4l2_m2m_get_vq(struct v4l2_m2m_ctx *m2m_ctx,
}
EXPORT_SYMBOL(v4l2_m2m_get_vq);
-void *v4l2_m2m_next_buf(struct v4l2_m2m_queue_ctx *q_ctx)
+struct vb2_v4l2_buffer *v4l2_m2m_next_buf(struct v4l2_m2m_queue_ctx *q_ctx)
{
struct v4l2_m2m_buffer *b;
unsigned long flags;
@@ -149,7 +149,7 @@ void *v4l2_m2m_next_buf(struct v4l2_m2m_queue_ctx *q_ctx)
}
EXPORT_SYMBOL_GPL(v4l2_m2m_next_buf);
-void *v4l2_m2m_last_buf(struct v4l2_m2m_queue_ctx *q_ctx)
+struct vb2_v4l2_buffer *v4l2_m2m_last_buf(struct v4l2_m2m_queue_ctx *q_ctx)
{
struct v4l2_m2m_buffer *b;
unsigned long flags;
@@ -167,7 +167,7 @@ void *v4l2_m2m_last_buf(struct v4l2_m2m_queue_ctx *q_ctx)
}
EXPORT_SYMBOL_GPL(v4l2_m2m_last_buf);
-void *v4l2_m2m_buf_remove(struct v4l2_m2m_queue_ctx *q_ctx)
+struct vb2_v4l2_buffer *v4l2_m2m_buf_remove(struct v4l2_m2m_queue_ctx *q_ctx)
{
struct v4l2_m2m_buffer *b;
unsigned long flags;
@@ -617,36 +617,35 @@ __poll_t v4l2_m2m_poll(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
__poll_t rc = 0;
unsigned long flags;
+ src_q = v4l2_m2m_get_src_vq(m2m_ctx);
+ dst_q = v4l2_m2m_get_dst_vq(m2m_ctx);
+
+ poll_wait(file, &src_q->done_wq, wait);
+ poll_wait(file, &dst_q->done_wq, wait);
+
if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) {
struct v4l2_fh *fh = file->private_data;
+ poll_wait(file, &fh->wait, wait);
if (v4l2_event_pending(fh))
rc = EPOLLPRI;
- else if (req_events & EPOLLPRI)
- poll_wait(file, &fh->wait, wait);
if (!(req_events & (EPOLLOUT | EPOLLWRNORM | EPOLLIN | EPOLLRDNORM)))
return rc;
}
- src_q = v4l2_m2m_get_src_vq(m2m_ctx);
- dst_q = v4l2_m2m_get_dst_vq(m2m_ctx);
-
/*
* There has to be at least one buffer queued on each queued_list, which
* means either in driver already or waiting for driver to claim it
* and start processing.
*/
- if ((!src_q->streaming || list_empty(&src_q->queued_list))
- && (!dst_q->streaming || list_empty(&dst_q->queued_list))) {
+ if ((!src_q->streaming || src_q->error ||
+ list_empty(&src_q->queued_list)) &&
+ (!dst_q->streaming || dst_q->error ||
+ list_empty(&dst_q->queued_list))) {
rc |= EPOLLERR;
goto end;
}
- spin_lock_irqsave(&src_q->done_lock, flags);
- if (list_empty(&src_q->done_list))
- poll_wait(file, &src_q->done_wq, wait);
- spin_unlock_irqrestore(&src_q->done_lock, flags);
-
spin_lock_irqsave(&dst_q->done_lock, flags);
if (list_empty(&dst_q->done_list)) {
/*
@@ -657,8 +656,6 @@ __poll_t v4l2_m2m_poll(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
spin_unlock_irqrestore(&dst_q->done_lock, flags);
return rc | EPOLLIN | EPOLLRDNORM;
}
-
- poll_wait(file, &dst_q->done_wq, wait);
}
spin_unlock_irqrestore(&dst_q->done_lock, flags);
@@ -975,6 +972,27 @@ 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)
+{
+ 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;
+
+ cap_vb->vb2_buf.timestamp = out_vb->vb2_buf.timestamp;
+
+ if (out_vb->flags & V4L2_BUF_FLAG_TIMECODE)
+ cap_vb->timecode = out_vb->timecode;
+ cap_vb->field = out_vb->field;
+ cap_vb->flags &= ~mask;
+ cap_vb->flags |= out_vb->flags & mask;
+ cap_vb->vb2_buf.copied_timestamp = 1;
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_buf_copy_metadata);
+
void v4l2_m2m_request_queue(struct media_request *req)
{
struct media_request_object *obj, *obj_safe;
diff --git a/drivers/media/v4l2-core/videobuf-core.c b/drivers/media/v4l2-core/videobuf-core.c
index 7491b337002c..bf7dfb2a34af 100644
--- a/drivers/media/v4l2-core/videobuf-core.c
+++ b/drivers/media/v4l2-core/videobuf-core.c
@@ -214,7 +214,7 @@ int videobuf_queue_is_busy(struct videobuf_queue *q)
return 1;
}
if (q->bufs[i]->state == VIDEOBUF_ACTIVE) {
- dprintk(1, "busy: buffer #%d avtive\n", i);
+ dprintk(1, "busy: buffer #%d active\n", i);
return 1;
}
}
@@ -367,7 +367,7 @@ static void videobuf_status(struct videobuf_queue *q, struct v4l2_buffer *b,
}
b->field = vb->field;
- b->timestamp = vb->ts;
+ b->timestamp = ns_to_timeval(vb->ts);
b->bytesused = vb->size;
b->sequence = vb->field_count >> 1;
}
@@ -581,7 +581,7 @@ int videobuf_qbuf(struct videobuf_queue *q, struct v4l2_buffer *b)
|| q->type == V4L2_BUF_TYPE_SDR_OUTPUT) {
buf->size = b->bytesused;
buf->field = b->field;
- buf->ts = b->timestamp;
+ buf->ts = v4l2_timeval_to_ns(&b->timestamp);
}
break;
case V4L2_MEMORY_USERPTR:
@@ -1119,13 +1119,14 @@ done:
EXPORT_SYMBOL_GPL(videobuf_read_stream);
__poll_t videobuf_poll_stream(struct file *file,
- struct videobuf_queue *q,
- poll_table *wait)
+ struct videobuf_queue *q,
+ poll_table *wait)
{
__poll_t req_events = poll_requested_events(wait);
struct videobuf_buffer *buf = NULL;
__poll_t rc = 0;
+ poll_wait(file, &buf->done, wait);
videobuf_queue_lock(q);
if (q->streaming) {
if (!list_empty(&q->stream))
@@ -1149,7 +1150,6 @@ __poll_t videobuf_poll_stream(struct file *file,
rc = EPOLLERR;
if (0 == rc) {
- poll_wait(file, &buf->done, wait);
if (buf->state == VIDEOBUF_DONE ||
buf->state == VIDEOBUF_ERROR) {
switch (q->type) {
diff --git a/drivers/media/v4l2-core/videobuf-dma-contig.c b/drivers/media/v4l2-core/videobuf-dma-contig.c
index f46132504d88..e1bf50df4c70 100644
--- a/drivers/media/v4l2-core/videobuf-dma-contig.c
+++ b/drivers/media/v4l2-core/videobuf-dma-contig.c
@@ -248,7 +248,7 @@ static int __videobuf_iolock(struct videobuf_queue *q,
/* All handling should be done by __videobuf_mmap_mapper() */
if (!mem->vaddr) {
- dev_err(q->dev, "memory is not alloced/mmapped.\n");
+ dev_err(q->dev, "memory is not allocated/mmapped.\n");
return -EINVAL;
}
break;
diff --git a/drivers/media/v4l2-core/videobuf-vmalloc.c b/drivers/media/v4l2-core/videobuf-vmalloc.c
index 45fe781aeeec..cb50f1957828 100644
--- a/drivers/media/v4l2-core/videobuf-vmalloc.c
+++ b/drivers/media/v4l2-core/videobuf-vmalloc.c
@@ -171,7 +171,7 @@ static int __videobuf_iolock(struct videobuf_queue *q,
/* All handling should be done by __videobuf_mmap_mapper() */
if (!mem->vaddr) {
- printk(KERN_ERR "memory is not alloced/mmapped.\n");
+ printk(KERN_ERR "memory is not allocated/mmapped.\n");
return -EINVAL;
}
break;
@@ -196,26 +196,6 @@ static int __videobuf_iolock(struct videobuf_queue *q,
}
dprintk(1, "vmalloc is at addr %p (%d pages)\n",
mem->vaddr, pages);
-
-#if 0
- int rc;
- /* Kernel userptr is used also by read() method. In this case,
- there's no need to remap, since data will be copied to user
- */
- if (!vb->baddr)
- return 0;
-
- /* FIXME: to properly support USERPTR, remap should occur.
- The code below won't work, since mem->vma = NULL
- */
- /* Try to remap memory */
- rc = remap_vmalloc_range(mem->vma, (void *)vb->baddr, 0);
- if (rc < 0) {
- printk(KERN_ERR "mmap: remap failed with error %d", rc);
- return -ENOMEM;
- }
-#endif
-
break;
case V4L2_MEMORY_OVERLAY:
default:
diff --git a/drivers/memory/tegra/mc.c b/drivers/memory/tegra/mc.c
index 24afc36833bf..0a53598d982f 100644
--- a/drivers/memory/tegra/mc.c
+++ b/drivers/memory/tegra/mc.c
@@ -12,6 +12,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/sort.h>
@@ -38,6 +39,7 @@
#define MC_ERR_ADR 0x0c
+#define MC_GART_ERROR_REQ 0x30
#define MC_DECERR_EMEM_OTHERS_STATUS 0x58
#define MC_SECURITY_VIOLATION_STATUS 0x74
@@ -51,7 +53,7 @@
static const struct of_device_id tegra_mc_of_match[] = {
#ifdef CONFIG_ARCH_TEGRA_2x_SOC
- { .compatible = "nvidia,tegra20-mc", .data = &tegra20_mc_soc },
+ { .compatible = "nvidia,tegra20-mc-gart", .data = &tegra20_mc_soc },
#endif
#ifdef CONFIG_ARCH_TEGRA_3x_SOC
{ .compatible = "nvidia,tegra30-mc", .data = &tegra30_mc_soc },
@@ -161,7 +163,7 @@ static int tegra_mc_hotreset_assert(struct reset_controller_dev *rcdev,
/* block clients DMA requests */
err = rst_ops->block_dma(mc, rst);
if (err) {
- dev_err(mc->dev, "Failed to block %s DMA: %d\n",
+ dev_err(mc->dev, "failed to block %s DMA: %d\n",
rst->name, err);
return err;
}
@@ -171,7 +173,7 @@ static int tegra_mc_hotreset_assert(struct reset_controller_dev *rcdev,
/* wait for completion of the outstanding DMA requests */
while (!rst_ops->dma_idling(mc, rst)) {
if (!retries--) {
- dev_err(mc->dev, "Failed to flush %s DMA\n",
+ dev_err(mc->dev, "failed to flush %s DMA\n",
rst->name);
return -EBUSY;
}
@@ -184,7 +186,7 @@ static int tegra_mc_hotreset_assert(struct reset_controller_dev *rcdev,
/* clear clients DMA requests sitting before arbitration */
err = rst_ops->hotreset_assert(mc, rst);
if (err) {
- dev_err(mc->dev, "Failed to hot reset %s: %d\n",
+ dev_err(mc->dev, "failed to hot reset %s: %d\n",
rst->name, err);
return err;
}
@@ -213,7 +215,7 @@ static int tegra_mc_hotreset_deassert(struct reset_controller_dev *rcdev,
/* take out client from hot reset */
err = rst_ops->hotreset_deassert(mc, rst);
if (err) {
- dev_err(mc->dev, "Failed to deassert hot reset %s: %d\n",
+ dev_err(mc->dev, "failed to deassert hot reset %s: %d\n",
rst->name, err);
return err;
}
@@ -223,7 +225,7 @@ static int tegra_mc_hotreset_deassert(struct reset_controller_dev *rcdev,
/* allow new DMA requests to proceed to arbitration */
err = rst_ops->unblock_dma(mc, rst);
if (err) {
- dev_err(mc->dev, "Failed to unblock %s DMA : %d\n",
+ dev_err(mc->dev, "failed to unblock %s DMA : %d\n",
rst->name, err);
return err;
}
@@ -575,8 +577,15 @@ static __maybe_unused irqreturn_t tegra20_mc_irq(int irq, void *data)
break;
case MC_INT_INVALID_GART_PAGE:
- dev_err_ratelimited(mc->dev, "%s\n", error);
- continue;
+ reg = MC_GART_ERROR_REQ;
+ value = mc_readl(mc, reg);
+
+ id = (value >> 1) & mc->soc->client_id_mask;
+ desc = error_names[2];
+
+ if (value & BIT(0))
+ direction = "write";
+ break;
case MC_INT_SECURITY_VIOLATION:
reg = MC_SECURITY_VIOLATION_STATUS;
@@ -611,23 +620,18 @@ static __maybe_unused irqreturn_t tegra20_mc_irq(int irq, void *data)
static int tegra_mc_probe(struct platform_device *pdev)
{
- const struct of_device_id *match;
struct resource *res;
struct tegra_mc *mc;
void *isr;
int err;
- match = of_match_node(tegra_mc_of_match, pdev->dev.of_node);
- if (!match)
- return -ENODEV;
-
mc = devm_kzalloc(&pdev->dev, sizeof(*mc), GFP_KERNEL);
if (!mc)
return -ENOMEM;
platform_set_drvdata(pdev, mc);
spin_lock_init(&mc->lock);
- mc->soc = match->data;
+ mc->soc = of_device_get_match_data(&pdev->dev);
mc->dev = &pdev->dev;
/* length of MC tick in nanoseconds */
@@ -638,38 +642,35 @@ static int tegra_mc_probe(struct platform_device *pdev)
if (IS_ERR(mc->regs))
return PTR_ERR(mc->regs);
+ mc->clk = devm_clk_get(&pdev->dev, "mc");
+ if (IS_ERR(mc->clk)) {
+ dev_err(&pdev->dev, "failed to get MC clock: %ld\n",
+ PTR_ERR(mc->clk));
+ return PTR_ERR(mc->clk);
+ }
+
#ifdef CONFIG_ARCH_TEGRA_2x_SOC
if (mc->soc == &tegra20_mc_soc) {
- res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- mc->regs2 = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(mc->regs2))
- return PTR_ERR(mc->regs2);
-
isr = tegra20_mc_irq;
} else
#endif
{
- mc->clk = devm_clk_get(&pdev->dev, "mc");
- if (IS_ERR(mc->clk)) {
- dev_err(&pdev->dev, "failed to get MC clock: %ld\n",
- PTR_ERR(mc->clk));
- return PTR_ERR(mc->clk);
- }
-
err = tegra_mc_setup_latency_allowance(mc);
if (err < 0) {
- dev_err(&pdev->dev, "failed to setup latency allowance: %d\n",
+ dev_err(&pdev->dev,
+ "failed to setup latency allowance: %d\n",
err);
return err;
}
isr = tegra_mc_irq;
- }
- err = tegra_mc_setup_timings(mc);
- if (err < 0) {
- dev_err(&pdev->dev, "failed to setup timings: %d\n", err);
- return err;
+ err = tegra_mc_setup_timings(mc);
+ if (err < 0) {
+ dev_err(&pdev->dev, "failed to setup timings: %d\n",
+ err);
+ return err;
+ }
}
mc->irq = platform_get_irq(pdev, 0);
@@ -678,11 +679,11 @@ static int tegra_mc_probe(struct platform_device *pdev)
return mc->irq;
}
- WARN(!mc->soc->client_id_mask, "Missing client ID mask for this SoC\n");
+ WARN(!mc->soc->client_id_mask, "missing client ID mask for this SoC\n");
mc_writel(mc, mc->soc->intmask, MC_INTMASK);
- err = devm_request_irq(&pdev->dev, mc->irq, isr, IRQF_SHARED,
+ err = devm_request_irq(&pdev->dev, mc->irq, isr, 0,
dev_name(&pdev->dev), mc);
if (err < 0) {
dev_err(&pdev->dev, "failed to request IRQ#%u: %d\n", mc->irq,
@@ -695,20 +696,65 @@ static int tegra_mc_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "failed to register reset controller: %d\n",
err);
- if (IS_ENABLED(CONFIG_TEGRA_IOMMU_SMMU)) {
+ if (IS_ENABLED(CONFIG_TEGRA_IOMMU_SMMU) && mc->soc->smmu) {
mc->smmu = tegra_smmu_probe(&pdev->dev, mc->soc->smmu, mc);
- if (IS_ERR(mc->smmu))
+ if (IS_ERR(mc->smmu)) {
dev_err(&pdev->dev, "failed to probe SMMU: %ld\n",
PTR_ERR(mc->smmu));
+ mc->smmu = NULL;
+ }
+ }
+
+ if (IS_ENABLED(CONFIG_TEGRA_IOMMU_GART) && !mc->soc->smmu) {
+ mc->gart = tegra_gart_probe(&pdev->dev, mc);
+ if (IS_ERR(mc->gart)) {
+ dev_err(&pdev->dev, "failed to probe GART: %ld\n",
+ PTR_ERR(mc->gart));
+ mc->gart = NULL;
+ }
+ }
+
+ return 0;
+}
+
+static int tegra_mc_suspend(struct device *dev)
+{
+ struct tegra_mc *mc = dev_get_drvdata(dev);
+ int err;
+
+ if (IS_ENABLED(CONFIG_TEGRA_IOMMU_GART) && mc->gart) {
+ err = tegra_gart_suspend(mc->gart);
+ if (err)
+ return err;
}
return 0;
}
+static int tegra_mc_resume(struct device *dev)
+{
+ struct tegra_mc *mc = dev_get_drvdata(dev);
+ int err;
+
+ if (IS_ENABLED(CONFIG_TEGRA_IOMMU_GART) && mc->gart) {
+ err = tegra_gart_resume(mc->gart);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static const struct dev_pm_ops tegra_mc_pm_ops = {
+ .suspend = tegra_mc_suspend,
+ .resume = tegra_mc_resume,
+};
+
static struct platform_driver tegra_mc_driver = {
.driver = {
.name = "tegra-mc",
.of_match_table = tegra_mc_of_match,
+ .pm = &tegra_mc_pm_ops,
.suppress_bind_attrs = true,
},
.prevent_deferred_probe = true,
diff --git a/drivers/memory/tegra/mc.h b/drivers/memory/tegra/mc.h
index 01065f12ebeb..887a3b07334f 100644
--- a/drivers/memory/tegra/mc.h
+++ b/drivers/memory/tegra/mc.h
@@ -26,19 +26,13 @@
static inline u32 mc_readl(struct tegra_mc *mc, unsigned long offset)
{
- if (mc->regs2 && offset >= 0x24)
- return readl(mc->regs2 + offset - 0x3c);
-
- return readl(mc->regs + offset);
+ return readl_relaxed(mc->regs + offset);
}
static inline void mc_writel(struct tegra_mc *mc, u32 value,
unsigned long offset)
{
- if (mc->regs2 && offset >= 0x24)
- return writel(value, mc->regs2 + offset - 0x3c);
-
- writel(value, mc->regs + offset);
+ writel_relaxed(value, mc->regs + offset);
}
extern const struct tegra_mc_reset_ops terga_mc_reset_ops_common;
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 76f9909cf396..0ce2d8dfc5f1 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -215,7 +215,6 @@ config MFD_CROS_EC
config MFD_CROS_EC_CHARDEV
tristate "Chrome OS Embedded Controller userspace device interface"
depends on MFD_CROS_EC
- select CROS_EC_CTL
---help---
This driver adds support to talk with the ChromeOS EC from userspace.
@@ -519,10 +518,10 @@ config INTEL_SOC_PMIC
bool "Support for Crystal Cove PMIC"
depends on ACPI && HAS_IOMEM && I2C=y && GPIOLIB && COMMON_CLK
depends on X86 || COMPILE_TEST
+ depends on I2C_DESIGNWARE_PLATFORM=y
select MFD_CORE
select REGMAP_I2C
select REGMAP_IRQ
- select I2C_DESIGNWARE_PLATFORM
help
Select this option to enable support for Crystal Cove PMIC
on some Intel SoC systems. The PMIC provides ADC, GPIO,
@@ -548,10 +547,10 @@ config INTEL_SOC_PMIC_CHTWC
bool "Support for Intel Cherry Trail Whiskey Cove PMIC"
depends on ACPI && HAS_IOMEM && I2C=y && COMMON_CLK
depends on X86 || COMPILE_TEST
+ depends on I2C_DESIGNWARE_PLATFORM=y
select MFD_CORE
select REGMAP_I2C
select REGMAP_IRQ
- select I2C_DESIGNWARE_PLATFORM
help
Select this option to enable support for the Intel Cherry Trail
Whiskey Cove PMIC found on some Intel Cherry Trail systems.
@@ -928,7 +927,7 @@ config UCB1400_CORE
config MFD_PM8XXX
tristate "Qualcomm PM8xxx PMIC chips driver"
depends on (ARM || HEXAGON || COMPILE_TEST)
- select IRQ_DOMAIN
+ select IRQ_DOMAIN_HIERARCHY
select MFD_CORE
select REGMAP
help
@@ -1066,6 +1065,8 @@ config MFD_SI476X_CORE
config MFD_SM501
tristate "Silicon Motion SM501"
+ depends on HAS_DMA
+ select DMA_DECLARE_COHERENT
---help---
This is the core driver for the Silicon Motion SM501 multimedia
companion chip. This device is a multifunction device which may
@@ -1205,7 +1206,7 @@ config MFD_STMPE
Currently supported devices are:
- STMPE811: GPIO, Touchscreen
+ STMPE811: GPIO, Touchscreen, ADC
STMPE1601: GPIO, Keypad
STMPE1801: GPIO, Keypad
STMPE2401: GPIO, Keypad
@@ -1218,6 +1219,7 @@ config MFD_STMPE
GPIO: stmpe-gpio
Keypad: stmpe-keypad
Touchscreen: stmpe-ts
+ ADC: stmpe-adc
menu "STMicroelectronics STMPE Interface Drivers"
depends on MFD_STMPE
@@ -1420,9 +1422,9 @@ config MFD_TPS65217
config MFD_TPS68470
bool "TI TPS68470 Power Management / LED chips"
depends on ACPI && PCI && I2C=y
+ depends on I2C_DESIGNWARE_PLATFORM=y
select MFD_CORE
select REGMAP_I2C
- select I2C_DESIGNWARE_PLATFORM
help
If you say yes here you get support for the TPS68470 series of
Power Management / LED chips.
@@ -1674,9 +1676,18 @@ config MFD_TC6393XB
select GPIOLIB
select MFD_CORE
select MFD_TMIO
+ select DMA_DECLARE_COHERENT
help
Support for Toshiba Mobile IO Controller TC6393XB
+config MFD_TQMX86
+ tristate "TQ-Systems IO controller TQMX86"
+ select MFD_CORE
+ help
+ Say yes here to enable support for various functions of the
+ TQ-Systems IO controller and watchdog device, found on their
+ ComExpress CPU modules.
+
config MFD_VX855
tristate "VIA VX855/VX875 integrated south bridge"
depends on PCI
@@ -1686,6 +1697,14 @@ config MFD_VX855
VIA VX855/VX875 south bridge. You will need to enable the vx855_spi
and/or vx855_gpio drivers for this to do anything useful.
+config MFD_LOCHNAGAR
+ bool "Cirrus Logic Lochnagar Audio Development Board"
+ select MFD_CORE
+ select REGMAP_I2C
+ depends on I2C=y && OF
+ help
+ Support for Cirrus Logic Lochnagar audio development board.
+
config MFD_ARIZONA
select REGMAP
select REGMAP_IRQ
@@ -1872,6 +1891,22 @@ config MFD_STM32_TIMERS
for PWM and IIO Timer. This driver allow to share the
registers between the others drivers.
+config MFD_STPMIC1
+ tristate "Support for STPMIC1 PMIC"
+ depends on (I2C=y && OF)
+ select REGMAP_I2C
+ select REGMAP_IRQ
+ select MFD_CORE
+ help
+ Support for ST Microelectronics STPMIC1 PMIC. STPMIC1 has power on
+ key, watchdog and regulator functionalities which are supported via
+ the relevant subsystems. This driver provides core support for the
+ STPMIC1. In order to use the actual functionaltiy of the device other
+ drivers must be enabled.
+
+ To compile this driver as a module, choose M here: the
+ module will be called stpmic1.
+
menu "Multimedia Capabilities Port drivers"
depends on ARCH_SA1100
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index ee6fb6af655e..b4569ed7f3f3 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -37,6 +37,9 @@ obj-$(CONFIG_MFD_TC3589X) += tc3589x.o
obj-$(CONFIG_MFD_T7L66XB) += t7l66xb.o tmio_core.o
obj-$(CONFIG_MFD_TC6387XB) += tc6387xb.o tmio_core.o
obj-$(CONFIG_MFD_TC6393XB) += tc6393xb.o tmio_core.o
+obj-$(CONFIG_MFD_TQMX86) += tqmx86.o
+
+obj-$(CONFIG_MFD_LOCHNAGAR) += lochnagar-i2c.o
obj-$(CONFIG_MFD_ARIZONA) += arizona-core.o
obj-$(CONFIG_MFD_ARIZONA) += arizona-irq.o
@@ -234,6 +237,7 @@ obj-$(CONFIG_INTEL_SOC_PMIC_CHTDC_TI) += intel_soc_pmic_chtdc_ti.o
obj-$(CONFIG_MFD_MT6397) += mt6397-core.o
obj-$(CONFIG_MFD_ALTERA_A10SR) += altera-a10sr.o
+obj-$(CONFIG_MFD_STPMIC1) += stpmic1.o
obj-$(CONFIG_MFD_SUN4I_GPADC) += sun4i-gpadc.o
obj-$(CONFIG_MFD_STM32_LPTIMER) += stm32-lptimer.o
diff --git a/drivers/mfd/aat2870-core.c b/drivers/mfd/aat2870-core.c
index 3ba19a45f199..9d3d90d386c2 100644
--- a/drivers/mfd/aat2870-core.c
+++ b/drivers/mfd/aat2870-core.c
@@ -20,7 +20,6 @@
*/
#include <linux/kernel.h>
-#include <linux/module.h>
#include <linux/init.h>
#include <linux/debugfs.h>
#include <linux/slab.h>
@@ -349,18 +348,10 @@ static void aat2870_init_debugfs(struct aat2870_data *aat2870)
"Failed to create debugfs register file\n");
}
-static void aat2870_uninit_debugfs(struct aat2870_data *aat2870)
-{
- debugfs_remove_recursive(aat2870->dentry_root);
-}
#else
static inline void aat2870_init_debugfs(struct aat2870_data *aat2870)
{
}
-
-static inline void aat2870_uninit_debugfs(struct aat2870_data *aat2870)
-{
-}
#endif /* CONFIG_DEBUG_FS */
static int aat2870_i2c_probe(struct i2c_client *client,
@@ -440,20 +431,6 @@ out_disable:
return ret;
}
-static int aat2870_i2c_remove(struct i2c_client *client)
-{
- struct aat2870_data *aat2870 = i2c_get_clientdata(client);
-
- aat2870_uninit_debugfs(aat2870);
-
- mfd_remove_devices(aat2870->dev);
- aat2870_disable(aat2870);
- if (aat2870->uninit)
- aat2870->uninit(aat2870);
-
- return 0;
-}
-
#ifdef CONFIG_PM_SLEEP
static int aat2870_i2c_suspend(struct device *dev)
{
@@ -492,15 +469,14 @@ static const struct i2c_device_id aat2870_i2c_id_table[] = {
{ "aat2870", 0 },
{ }
};
-MODULE_DEVICE_TABLE(i2c, aat2870_i2c_id_table);
static struct i2c_driver aat2870_i2c_driver = {
.driver = {
- .name = "aat2870",
- .pm = &aat2870_pm_ops,
+ .name = "aat2870",
+ .pm = &aat2870_pm_ops,
+ .suppress_bind_attrs = true,
},
.probe = aat2870_i2c_probe,
- .remove = aat2870_i2c_remove,
.id_table = aat2870_i2c_id_table,
};
@@ -509,13 +485,3 @@ static int __init aat2870_init(void)
return i2c_add_driver(&aat2870_i2c_driver);
}
subsys_initcall(aat2870_init);
-
-static void __exit aat2870_exit(void)
-{
- i2c_del_driver(&aat2870_i2c_driver);
-}
-module_exit(aat2870_exit);
-
-MODULE_DESCRIPTION("Core support for the AnalogicTech AAT2870");
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Jin Park <jinyoungp@nvidia.com>");
diff --git a/drivers/mfd/adp5520.c b/drivers/mfd/adp5520.c
index be0497b96720..2cdd39cb8a18 100644
--- a/drivers/mfd/adp5520.c
+++ b/drivers/mfd/adp5520.c
@@ -7,6 +7,8 @@
*
* Copyright 2009 Analog Devices Inc.
*
+ * Author: Michael Hennerich <michael.hennerich@analog.com>
+ *
* Derived from da903x:
* Copyright (C) 2008 Compulab, Ltd.
* Mike Rapoport <mike@compulab.co.il>
@@ -18,7 +20,7 @@
*/
#include <linux/kernel.h>
-#include <linux/module.h>
+#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
@@ -304,18 +306,6 @@ out_free_irq:
return ret;
}
-static int adp5520_remove(struct i2c_client *client)
-{
- struct adp5520_chip *chip = dev_get_drvdata(&client->dev);
-
- if (chip->irq)
- free_irq(chip->irq, chip);
-
- adp5520_remove_subdevs(chip);
- adp5520_write(chip->dev, ADP5520_MODE_STATUS, 0);
- return 0;
-}
-
#ifdef CONFIG_PM_SLEEP
static int adp5520_suspend(struct device *dev)
{
@@ -346,20 +336,14 @@ static const struct i2c_device_id adp5520_id[] = {
{ "pmic-adp5501", ID_ADP5501 },
{ }
};
-MODULE_DEVICE_TABLE(i2c, adp5520_id);
static struct i2c_driver adp5520_driver = {
.driver = {
- .name = "adp5520",
- .pm = &adp5520_pm,
+ .name = "adp5520",
+ .pm = &adp5520_pm,
+ .suppress_bind_attrs = true,
},
.probe = adp5520_probe,
- .remove = adp5520_remove,
.id_table = adp5520_id,
};
-
-module_i2c_driver(adp5520_driver);
-
-MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
-MODULE_DESCRIPTION("ADP5520(01) PMIC-MFD Driver");
-MODULE_LICENSE("GPL");
+builtin_i2c_driver(adp5520_driver);
diff --git a/drivers/mfd/as3711.c b/drivers/mfd/as3711.c
index 67b12417585d..7a74a874b93c 100644
--- a/drivers/mfd/as3711.c
+++ b/drivers/mfd/as3711.c
@@ -16,7 +16,6 @@
#include <linux/kernel.h>
#include <linux/mfd/as3711.h>
#include <linux/mfd/core.h>
-#include <linux/module.h>
#include <linux/of.h>
#include <linux/regmap.h>
#include <linux/slab.h>
@@ -118,7 +117,6 @@ static const struct of_device_id as3711_of_match[] = {
{.compatible = "ams,as3711",},
{}
};
-MODULE_DEVICE_TABLE(of, as3711_of_match);
#endif
static int as3711_i2c_probe(struct i2c_client *client,
@@ -202,8 +200,6 @@ static const struct i2c_device_id as3711_i2c_id[] = {
{}
};
-MODULE_DEVICE_TABLE(i2c, as3711_i2c_id);
-
static struct i2c_driver as3711_i2c_driver = {
.driver = {
.name = "as3711",
@@ -219,13 +215,3 @@ static int __init as3711_i2c_init(void)
}
/* Initialise early */
subsys_initcall(as3711_i2c_init);
-
-static void __exit as3711_i2c_exit(void)
-{
- i2c_del_driver(&as3711_i2c_driver);
-}
-module_exit(as3711_i2c_exit);
-
-MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
-MODULE_DESCRIPTION("AS3711 PMIC driver");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/at91-usart.c b/drivers/mfd/at91-usart.c
index d20747f612c1..6a8351a4588e 100644
--- a/drivers/mfd/at91-usart.c
+++ b/drivers/mfd/at91-usart.c
@@ -15,29 +15,29 @@
#include <linux/of.h>
#include <linux/property.h>
-static struct mfd_cell at91_usart_spi_subdev = {
- .name = "at91_usart_spi",
- .of_compatible = "microchip,at91sam9g45-usart-spi",
- };
+static const struct mfd_cell at91_usart_spi_subdev = {
+ .name = "at91_usart_spi",
+ .of_compatible = "microchip,at91sam9g45-usart-spi",
+};
-static struct mfd_cell at91_usart_serial_subdev = {
- .name = "atmel_usart_serial",
- .of_compatible = "atmel,at91rm9200-usart-serial",
- };
+static const struct mfd_cell at91_usart_serial_subdev = {
+ .name = "atmel_usart_serial",
+ .of_compatible = "atmel,at91rm9200-usart-serial",
+};
static int at91_usart_mode_probe(struct platform_device *pdev)
{
- struct mfd_cell cell;
+ const struct mfd_cell *cell;
u32 opmode = AT91_USART_MODE_SERIAL;
device_property_read_u32(&pdev->dev, "atmel,usart-mode", &opmode);
switch (opmode) {
case AT91_USART_MODE_SPI:
- cell = at91_usart_spi_subdev;
+ cell = &at91_usart_spi_subdev;
break;
case AT91_USART_MODE_SERIAL:
- cell = at91_usart_serial_subdev;
+ cell = &at91_usart_serial_subdev;
break;
default:
dev_err(&pdev->dev, "atmel,usart-mode has an invalid value %u\n",
@@ -45,7 +45,7 @@ static int at91_usart_mode_probe(struct platform_device *pdev)
return -EINVAL;
}
- return devm_mfd_add_devices(&pdev->dev, PLATFORM_DEVID_AUTO, &cell, 1,
+ return devm_mfd_add_devices(&pdev->dev, PLATFORM_DEVID_AUTO, cell, 1,
NULL, 0, NULL);
}
diff --git a/drivers/mfd/cros_ec.c b/drivers/mfd/cros_ec.c
index fe6f83766144..6acfe036d522 100644
--- a/drivers/mfd/cros_ec.c
+++ b/drivers/mfd/cros_ec.c
@@ -129,8 +129,8 @@ int cros_ec_register(struct cros_ec_device *ec_dev)
}
}
- err = mfd_add_devices(ec_dev->dev, PLATFORM_DEVID_AUTO, &ec_cell, 1,
- NULL, ec_dev->irq, NULL);
+ err = devm_mfd_add_devices(ec_dev->dev, PLATFORM_DEVID_AUTO, &ec_cell,
+ 1, NULL, ec_dev->irq, NULL);
if (err) {
dev_err(dev,
"Failed to register Embedded Controller subdevice %d\n",
@@ -147,7 +147,7 @@ int cros_ec_register(struct cros_ec_device *ec_dev)
* - the EC is responsive at init time (it is not true for a
* sensor hub.
*/
- err = mfd_add_devices(ec_dev->dev, PLATFORM_DEVID_AUTO,
+ err = devm_mfd_add_devices(ec_dev->dev, PLATFORM_DEVID_AUTO,
&ec_pd_cell, 1, NULL, ec_dev->irq, NULL);
if (err) {
dev_err(dev,
@@ -181,14 +181,6 @@ int cros_ec_register(struct cros_ec_device *ec_dev)
}
EXPORT_SYMBOL(cros_ec_register);
-int cros_ec_remove(struct cros_ec_device *ec_dev)
-{
- mfd_remove_devices(ec_dev->dev);
-
- return 0;
-}
-EXPORT_SYMBOL(cros_ec_remove);
-
#ifdef CONFIG_PM_SLEEP
int cros_ec_suspend(struct cros_ec_device *ec_dev)
{
diff --git a/drivers/mfd/cros_ec_dev.c b/drivers/mfd/cros_ec_dev.c
index 2d0fee488c5a..d275deaecb12 100644
--- a/drivers/mfd/cros_ec_dev.c
+++ b/drivers/mfd/cros_ec_dev.c
@@ -21,6 +21,7 @@
#include <linux/mfd/core.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
+#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/slab.h>
@@ -34,17 +35,9 @@
#define CROS_MAX_DEV 128
static int ec_major;
-static const struct attribute_group *cros_ec_groups[] = {
- &cros_ec_attr_group,
- &cros_ec_lightbar_attr_group,
- &cros_ec_vbc_attr_group,
- NULL,
-};
-
static struct class cros_class = {
.owner = THIS_MODULE,
.name = "chromeos",
- .dev_groups = cros_ec_groups,
};
/* Basic communication */
@@ -231,7 +224,7 @@ static long ec_device_ioctl_readmem(struct cros_ec_dev *ec, void __user *arg)
if (copy_to_user((void __user *)arg, &s_mem, sizeof(s_mem)))
return -EFAULT;
- return 0;
+ return num;
}
static long ec_device_ioctl(struct file *filp, unsigned int cmd,
@@ -395,9 +388,20 @@ static const struct mfd_cell cros_usbpd_charger_cells[] = {
{ .name = "cros-usbpd-charger" }
};
+static const struct mfd_cell cros_ec_platform_cells[] = {
+ { .name = "cros-ec-debugfs" },
+ { .name = "cros-ec-lightbar" },
+ { .name = "cros-ec-sysfs" },
+};
+
+static const struct mfd_cell cros_ec_vbc_cells[] = {
+ { .name = "cros-ec-vbc" }
+};
+
static int ec_device_probe(struct platform_device *pdev)
{
int retval = -ENOMEM;
+ struct device_node *node;
struct device *dev = &pdev->dev;
struct cros_ec_platform *ec_platform = dev_get_platdata(dev);
struct cros_ec_dev *ec = kzalloc(sizeof(*ec), GFP_KERNEL);
@@ -470,9 +474,6 @@ static int ec_device_probe(struct platform_device *pdev)
retval);
}
- /* Take control of the lightbar from the EC. */
- lb_manual_suspend_ctrl(ec, 1);
-
/* We can now add the sysfs class, we know which parameter to show */
retval = cdev_device_add(&ec->cdev, &ec->class_dev);
if (retval) {
@@ -480,8 +481,26 @@ static int ec_device_probe(struct platform_device *pdev)
goto failed;
}
- if (cros_ec_debugfs_init(ec))
- dev_warn(dev, "failed to create debugfs directory\n");
+ retval = mfd_add_devices(ec->dev, PLATFORM_DEVID_AUTO,
+ cros_ec_platform_cells,
+ ARRAY_SIZE(cros_ec_platform_cells),
+ NULL, 0, NULL);
+ if (retval)
+ dev_warn(ec->dev,
+ "failed to add cros-ec platform devices: %d\n",
+ retval);
+
+ /* Check whether this EC instance has a VBC NVRAM */
+ node = ec->ec_dev->dev->of_node;
+ if (of_property_read_bool(node, "google,has-vbc-nvram")) {
+ retval = mfd_add_devices(ec->dev, PLATFORM_DEVID_AUTO,
+ cros_ec_vbc_cells,
+ ARRAY_SIZE(cros_ec_vbc_cells),
+ NULL, 0, NULL);
+ if (retval)
+ dev_warn(ec->dev, "failed to add VBC devices: %d\n",
+ retval);
+ }
return 0;
@@ -494,69 +513,25 @@ static int ec_device_remove(struct platform_device *pdev)
{
struct cros_ec_dev *ec = dev_get_drvdata(&pdev->dev);
- /* Let the EC take over the lightbar again. */
- lb_manual_suspend_ctrl(ec, 0);
-
- cros_ec_debugfs_remove(ec);
-
mfd_remove_devices(ec->dev);
cdev_del(&ec->cdev);
device_unregister(&ec->class_dev);
return 0;
}
-static void ec_device_shutdown(struct platform_device *pdev)
-{
- struct cros_ec_dev *ec = dev_get_drvdata(&pdev->dev);
-
- /* Be sure to clear up debugfs delayed works */
- cros_ec_debugfs_remove(ec);
-}
-
static const struct platform_device_id cros_ec_id[] = {
{ DRV_NAME, 0 },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(platform, cros_ec_id);
-static __maybe_unused int ec_device_suspend(struct device *dev)
-{
- struct cros_ec_dev *ec = dev_get_drvdata(dev);
-
- cros_ec_debugfs_suspend(ec);
-
- lb_suspend(ec);
-
- return 0;
-}
-
-static __maybe_unused int ec_device_resume(struct device *dev)
-{
- struct cros_ec_dev *ec = dev_get_drvdata(dev);
-
- cros_ec_debugfs_resume(ec);
-
- lb_resume(ec);
-
- return 0;
-}
-
-static const struct dev_pm_ops cros_ec_dev_pm_ops = {
-#ifdef CONFIG_PM_SLEEP
- .suspend = ec_device_suspend,
- .resume = ec_device_resume,
-#endif
-};
-
static struct platform_driver cros_ec_dev_driver = {
.driver = {
.name = DRV_NAME,
- .pm = &cros_ec_dev_pm_ops,
},
.id_table = cros_ec_id,
.probe = ec_device_probe,
.remove = ec_device_remove,
- .shutdown = ec_device_shutdown,
};
static int __init cros_ec_dev_init(void)
diff --git a/drivers/mfd/cros_ec_dev.h b/drivers/mfd/cros_ec_dev.h
index 978d836a0248..ec750433455a 100644
--- a/drivers/mfd/cros_ec_dev.h
+++ b/drivers/mfd/cros_ec_dev.h
@@ -44,10 +44,4 @@ struct cros_ec_readmem {
#define CROS_EC_DEV_IOCXCMD _IOWR(CROS_EC_DEV_IOC, 0, struct cros_ec_command)
#define CROS_EC_DEV_IOCRDMEM _IOWR(CROS_EC_DEV_IOC, 1, struct cros_ec_readmem)
-/* Lightbar utilities */
-extern bool ec_has_lightbar(struct cros_ec_dev *ec);
-extern int lb_manual_suspend_ctrl(struct cros_ec_dev *ec, uint8_t enable);
-extern int lb_suspend(struct cros_ec_dev *ec);
-extern int lb_resume(struct cros_ec_dev *ec);
-
#endif /* _CROS_EC_DEV_H_ */
diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c
index aec20e1c7d3d..65666b624ae8 100644
--- a/drivers/mfd/db8500-prcmu.c
+++ b/drivers/mfd/db8500-prcmu.c
@@ -1,4 +1,6 @@
/*
+ * DB8500 PRCM Unit driver
+ *
* Copyright (C) STMicroelectronics 2009
* Copyright (C) ST-Ericsson SA 2010
*
@@ -10,7 +12,8 @@
* U8500 PRCM Unit interface driver
*
*/
-#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/export.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/errno.h>
@@ -3188,9 +3191,4 @@ static int __init db8500_prcmu_init(void)
{
return platform_driver_register(&db8500_prcmu_driver);
}
-
core_initcall(db8500_prcmu_init);
-
-MODULE_AUTHOR("Mattias Nilsson <mattias.i.nilsson@stericsson.com>");
-MODULE_DESCRIPTION("DB8500 PRCM Unit driver");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/htc-i2cpld.c b/drivers/mfd/htc-i2cpld.c
index 01572b5e79e8..af3c66355270 100644
--- a/drivers/mfd/htc-i2cpld.c
+++ b/drivers/mfd/htc-i2cpld.c
@@ -27,7 +27,6 @@
#include <linux/kernel.h>
#include <linux/init.h>
-#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
@@ -614,8 +613,6 @@ static const struct i2c_device_id htcpld_chip_id[] = {
{ "htcpld-chip", 0 },
{ }
};
-MODULE_DEVICE_TABLE(i2c, htcpld_chip_id);
-
static struct i2c_driver htcpld_chip_driver = {
.driver = {
@@ -643,17 +640,4 @@ static int __init htcpld_core_init(void)
/* Probe for our chips */
return platform_driver_probe(&htcpld_core_driver, htcpld_core_probe);
}
-
-static void __exit htcpld_core_exit(void)
-{
- i2c_del_driver(&htcpld_chip_driver);
- platform_driver_unregister(&htcpld_core_driver);
-}
-
-module_init(htcpld_core_init);
-module_exit(htcpld_core_exit);
-
-MODULE_AUTHOR("Cory Maccarrone <darkstar6262@gmail.com>");
-MODULE_DESCRIPTION("I2C HTC PLD Driver");
-MODULE_LICENSE("GPL");
-
+device_initcall(htcpld_core_init);
diff --git a/drivers/mfd/intel-lpss-acpi.c b/drivers/mfd/intel-lpss-acpi.c
index 7911b0a14a6d..6d9f03363ee7 100644
--- a/drivers/mfd/intel-lpss-acpi.c
+++ b/drivers/mfd/intel-lpss-acpi.c
@@ -15,7 +15,6 @@
#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/platform_device.h>
#include <linux/property.h>
diff --git a/drivers/mfd/intel-lpss-pci.c b/drivers/mfd/intel-lpss-pci.c
index 0e5282fc1467..cba2eb166650 100644
--- a/drivers/mfd/intel-lpss-pci.c
+++ b/drivers/mfd/intel-lpss-pci.c
@@ -15,7 +15,6 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
-#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/property.h>
diff --git a/drivers/mfd/intel-lpss.h b/drivers/mfd/intel-lpss.h
index 865bbeaaf00c..3a120fecd2dc 100644
--- a/drivers/mfd/intel-lpss.h
+++ b/drivers/mfd/intel-lpss.h
@@ -14,6 +14,8 @@
#ifndef __MFD_INTEL_LPSS_H
#define __MFD_INTEL_LPSS_H
+#include <linux/pm.h>
+
struct device;
struct resource;
struct property_entry;
diff --git a/drivers/mfd/lochnagar-i2c.c b/drivers/mfd/lochnagar-i2c.c
new file mode 100644
index 000000000000..3a65d9938902
--- /dev/null
+++ b/drivers/mfd/lochnagar-i2c.c
@@ -0,0 +1,398 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Lochnagar I2C bus interface
+ *
+ * Copyright (c) 2012-2018 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ *
+ * Author: Charles Keepax <ckeepax@opensource.cirrus.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/lockdep.h>
+#include <linux/mfd/core.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/regmap.h>
+
+#include <linux/mfd/lochnagar.h>
+#include <linux/mfd/lochnagar1_regs.h>
+#include <linux/mfd/lochnagar2_regs.h>
+
+#define LOCHNAGAR_BOOT_RETRIES 10
+#define LOCHNAGAR_BOOT_DELAY_MS 350
+
+#define LOCHNAGAR_CONFIG_POLL_US 10000
+
+static bool lochnagar1_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case LOCHNAGAR_SOFTWARE_RESET:
+ case LOCHNAGAR_FIRMWARE_ID1...LOCHNAGAR_FIRMWARE_ID2:
+ case LOCHNAGAR1_CDC_AIF1_SEL...LOCHNAGAR1_CDC_AIF3_SEL:
+ case LOCHNAGAR1_CDC_MCLK1_SEL...LOCHNAGAR1_CDC_MCLK2_SEL:
+ case LOCHNAGAR1_CDC_AIF_CTRL1...LOCHNAGAR1_CDC_AIF_CTRL2:
+ case LOCHNAGAR1_EXT_AIF_CTRL:
+ case LOCHNAGAR1_DSP_AIF1_SEL...LOCHNAGAR1_DSP_AIF2_SEL:
+ case LOCHNAGAR1_DSP_CLKIN_SEL:
+ case LOCHNAGAR1_DSP_AIF:
+ case LOCHNAGAR1_GF_AIF1...LOCHNAGAR1_GF_AIF2:
+ case LOCHNAGAR1_PSIA_AIF:
+ case LOCHNAGAR1_PSIA1_SEL...LOCHNAGAR1_PSIA2_SEL:
+ case LOCHNAGAR1_SPDIF_AIF_SEL:
+ case LOCHNAGAR1_GF_AIF3_SEL...LOCHNAGAR1_GF_AIF4_SEL:
+ case LOCHNAGAR1_GF_CLKOUT1_SEL:
+ case LOCHNAGAR1_GF_AIF1_SEL...LOCHNAGAR1_GF_AIF2_SEL:
+ case LOCHNAGAR1_GF_GPIO2...LOCHNAGAR1_GF_GPIO7:
+ case LOCHNAGAR1_RST:
+ case LOCHNAGAR1_LED1...LOCHNAGAR1_LED2:
+ case LOCHNAGAR1_I2C_CTRL:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config lochnagar1_i2c_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .reg_format_endian = REGMAP_ENDIAN_BIG,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+
+ .max_register = 0x50,
+ .readable_reg = lochnagar1_readable_register,
+
+ .use_single_read = true,
+ .use_single_write = true,
+
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct reg_sequence lochnagar1_patch[] = {
+ { 0x40, 0x0083 },
+ { 0x47, 0x0018 },
+ { 0x50, 0x0000 },
+};
+
+static bool lochnagar2_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case LOCHNAGAR_SOFTWARE_RESET:
+ case LOCHNAGAR_FIRMWARE_ID1...LOCHNAGAR_FIRMWARE_ID2:
+ case LOCHNAGAR2_CDC_AIF1_CTRL...LOCHNAGAR2_CDC_AIF3_CTRL:
+ case LOCHNAGAR2_DSP_AIF1_CTRL...LOCHNAGAR2_DSP_AIF2_CTRL:
+ case LOCHNAGAR2_PSIA1_CTRL...LOCHNAGAR2_PSIA2_CTRL:
+ case LOCHNAGAR2_GF_AIF3_CTRL...LOCHNAGAR2_GF_AIF4_CTRL:
+ case LOCHNAGAR2_GF_AIF1_CTRL...LOCHNAGAR2_GF_AIF2_CTRL:
+ case LOCHNAGAR2_SPDIF_AIF_CTRL:
+ case LOCHNAGAR2_USB_AIF1_CTRL...LOCHNAGAR2_USB_AIF2_CTRL:
+ case LOCHNAGAR2_ADAT_AIF_CTRL:
+ case LOCHNAGAR2_CDC_MCLK1_CTRL...LOCHNAGAR2_CDC_MCLK2_CTRL:
+ case LOCHNAGAR2_DSP_CLKIN_CTRL:
+ case LOCHNAGAR2_PSIA1_MCLK_CTRL...LOCHNAGAR2_PSIA2_MCLK_CTRL:
+ case LOCHNAGAR2_SPDIF_MCLK_CTRL:
+ case LOCHNAGAR2_GF_CLKOUT1_CTRL...LOCHNAGAR2_GF_CLKOUT2_CTRL:
+ case LOCHNAGAR2_ADAT_MCLK_CTRL:
+ case LOCHNAGAR2_SOUNDCARD_MCLK_CTRL:
+ case LOCHNAGAR2_GPIO_FPGA_GPIO1...LOCHNAGAR2_GPIO_FPGA_GPIO6:
+ case LOCHNAGAR2_GPIO_CDC_GPIO1...LOCHNAGAR2_GPIO_CDC_GPIO8:
+ case LOCHNAGAR2_GPIO_DSP_GPIO1...LOCHNAGAR2_GPIO_DSP_GPIO6:
+ case LOCHNAGAR2_GPIO_GF_GPIO2...LOCHNAGAR2_GPIO_GF_GPIO7:
+ case LOCHNAGAR2_GPIO_CDC_AIF1_BCLK...LOCHNAGAR2_GPIO_CDC_AIF3_TXDAT:
+ case LOCHNAGAR2_GPIO_DSP_AIF1_BCLK...LOCHNAGAR2_GPIO_DSP_AIF2_TXDAT:
+ case LOCHNAGAR2_GPIO_PSIA1_BCLK...LOCHNAGAR2_GPIO_PSIA2_TXDAT:
+ case LOCHNAGAR2_GPIO_GF_AIF3_BCLK...LOCHNAGAR2_GPIO_GF_AIF4_TXDAT:
+ case LOCHNAGAR2_GPIO_GF_AIF1_BCLK...LOCHNAGAR2_GPIO_GF_AIF2_TXDAT:
+ case LOCHNAGAR2_GPIO_DSP_UART1_RX...LOCHNAGAR2_GPIO_DSP_UART2_TX:
+ case LOCHNAGAR2_GPIO_GF_UART2_RX...LOCHNAGAR2_GPIO_GF_UART2_TX:
+ case LOCHNAGAR2_GPIO_USB_UART_RX:
+ case LOCHNAGAR2_GPIO_CDC_PDMCLK1...LOCHNAGAR2_GPIO_CDC_PDMDAT2:
+ case LOCHNAGAR2_GPIO_CDC_DMICCLK1...LOCHNAGAR2_GPIO_CDC_DMICDAT4:
+ case LOCHNAGAR2_GPIO_DSP_DMICCLK1...LOCHNAGAR2_GPIO_DSP_DMICDAT2:
+ case LOCHNAGAR2_GPIO_I2C2_SCL...LOCHNAGAR2_GPIO_I2C4_SDA:
+ case LOCHNAGAR2_GPIO_DSP_STANDBY:
+ case LOCHNAGAR2_GPIO_CDC_MCLK1...LOCHNAGAR2_GPIO_CDC_MCLK2:
+ case LOCHNAGAR2_GPIO_DSP_CLKIN:
+ case LOCHNAGAR2_GPIO_PSIA1_MCLK...LOCHNAGAR2_GPIO_PSIA2_MCLK:
+ case LOCHNAGAR2_GPIO_GF_GPIO1...LOCHNAGAR2_GPIO_GF_GPIO5:
+ case LOCHNAGAR2_GPIO_DSP_GPIO20:
+ case LOCHNAGAR2_GPIO_CHANNEL1...LOCHNAGAR2_GPIO_CHANNEL16:
+ case LOCHNAGAR2_MINICARD_RESETS:
+ case LOCHNAGAR2_ANALOGUE_PATH_CTRL1...LOCHNAGAR2_ANALOGUE_PATH_CTRL2:
+ case LOCHNAGAR2_COMMS_CTRL4:
+ case LOCHNAGAR2_SPDIF_CTRL:
+ case LOCHNAGAR2_IMON_CTRL1...LOCHNAGAR2_IMON_CTRL4:
+ case LOCHNAGAR2_IMON_DATA1...LOCHNAGAR2_IMON_DATA2:
+ case LOCHNAGAR2_POWER_CTRL:
+ case LOCHNAGAR2_MICVDD_CTRL1:
+ case LOCHNAGAR2_MICVDD_CTRL2:
+ case LOCHNAGAR2_VDDCORE_CDC_CTRL1:
+ case LOCHNAGAR2_VDDCORE_CDC_CTRL2:
+ case LOCHNAGAR2_SOUNDCARD_AIF_CTRL:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool lochnagar2_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case LOCHNAGAR2_GPIO_CHANNEL1...LOCHNAGAR2_GPIO_CHANNEL16:
+ case LOCHNAGAR2_ANALOGUE_PATH_CTRL1:
+ case LOCHNAGAR2_IMON_CTRL3...LOCHNAGAR2_IMON_CTRL4:
+ case LOCHNAGAR2_IMON_DATA1...LOCHNAGAR2_IMON_DATA2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config lochnagar2_i2c_regmap = {
+ .reg_bits = 16,
+ .val_bits = 16,
+ .reg_format_endian = REGMAP_ENDIAN_BIG,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+
+ .max_register = 0x1F1F,
+ .readable_reg = lochnagar2_readable_register,
+ .volatile_reg = lochnagar2_volatile_register,
+
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct reg_sequence lochnagar2_patch[] = {
+ { 0x00EE, 0x0000 },
+};
+
+struct lochnagar_config {
+ int id;
+ const char * const name;
+ enum lochnagar_type type;
+ const struct regmap_config *regmap;
+ const struct reg_sequence *patch;
+ int npatch;
+};
+
+static struct lochnagar_config lochnagar_configs[] = {
+ {
+ .id = 0x50,
+ .name = "lochnagar1",
+ .type = LOCHNAGAR1,
+ .regmap = &lochnagar1_i2c_regmap,
+ .patch = lochnagar1_patch,
+ .npatch = ARRAY_SIZE(lochnagar1_patch),
+ },
+ {
+ .id = 0xCB58,
+ .name = "lochnagar2",
+ .type = LOCHNAGAR2,
+ .regmap = &lochnagar2_i2c_regmap,
+ .patch = lochnagar2_patch,
+ .npatch = ARRAY_SIZE(lochnagar2_patch),
+ },
+};
+
+static const struct of_device_id lochnagar_of_match[] = {
+ { .compatible = "cirrus,lochnagar1", .data = &lochnagar_configs[0] },
+ { .compatible = "cirrus,lochnagar2", .data = &lochnagar_configs[1] },
+ {},
+};
+
+static int lochnagar_wait_for_boot(struct regmap *regmap, unsigned int *id)
+{
+ int i, ret;
+
+ for (i = 0; i < LOCHNAGAR_BOOT_RETRIES; ++i) {
+ msleep(LOCHNAGAR_BOOT_DELAY_MS);
+
+ /* The reset register will return the device ID when read */
+ ret = regmap_read(regmap, LOCHNAGAR_SOFTWARE_RESET, id);
+ if (!ret)
+ return ret;
+ }
+
+ return -ETIMEDOUT;
+}
+
+/**
+ * lochnagar_update_config - Synchronise the boards analogue configuration to
+ * the hardware.
+ *
+ * @lochnagar: A pointer to the primary core data structure.
+ *
+ * Return: Zero on success or an appropriate negative error code on failure.
+ */
+int lochnagar_update_config(struct lochnagar *lochnagar)
+{
+ struct regmap *regmap = lochnagar->regmap;
+ unsigned int done = LOCHNAGAR2_ANALOGUE_PATH_UPDATE_STS_MASK;
+ int timeout_ms = LOCHNAGAR_BOOT_DELAY_MS * LOCHNAGAR_BOOT_RETRIES;
+ unsigned int val = 0;
+ int ret;
+
+ lockdep_assert_held(&lochnagar->analogue_config_lock);
+
+ if (lochnagar->type != LOCHNAGAR2)
+ return 0;
+
+ /*
+ * Toggle the ANALOGUE_PATH_UPDATE bit and wait for the device to
+ * acknowledge that any outstanding changes to the analogue
+ * configuration have been applied.
+ */
+ ret = regmap_write(regmap, LOCHNAGAR2_ANALOGUE_PATH_CTRL1, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_write(regmap, LOCHNAGAR2_ANALOGUE_PATH_CTRL1,
+ LOCHNAGAR2_ANALOGUE_PATH_UPDATE_MASK);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_read_poll_timeout(regmap,
+ LOCHNAGAR2_ANALOGUE_PATH_CTRL1, val,
+ (val & done), LOCHNAGAR_CONFIG_POLL_US,
+ timeout_ms * 1000);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(lochnagar_update_config);
+
+static int lochnagar_i2c_probe(struct i2c_client *i2c)
+{
+ struct device *dev = &i2c->dev;
+ const struct lochnagar_config *config = NULL;
+ const struct of_device_id *of_id;
+ struct lochnagar *lochnagar;
+ struct gpio_desc *reset, *present;
+ unsigned int val;
+ unsigned int firmwareid;
+ unsigned int devid, rev;
+ int ret;
+
+ lochnagar = devm_kzalloc(dev, sizeof(*lochnagar), GFP_KERNEL);
+ if (!lochnagar)
+ return -ENOMEM;
+
+ of_id = of_match_device(lochnagar_of_match, dev);
+ if (!of_id)
+ return -EINVAL;
+
+ config = of_id->data;
+
+ lochnagar->dev = dev;
+ mutex_init(&lochnagar->analogue_config_lock);
+
+ dev_set_drvdata(dev, lochnagar);
+
+ reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(reset)) {
+ ret = PTR_ERR(reset);
+ dev_err(dev, "Failed to get reset GPIO: %d\n", ret);
+ return ret;
+ }
+
+ present = devm_gpiod_get_optional(dev, "present", GPIOD_OUT_HIGH);
+ if (IS_ERR(present)) {
+ ret = PTR_ERR(present);
+ dev_err(dev, "Failed to get present GPIO: %d\n", ret);
+ return ret;
+ }
+
+ /* Leave the Lochnagar in reset for a reasonable amount of time */
+ msleep(20);
+
+ /* Bring Lochnagar out of reset */
+ gpiod_set_value_cansleep(reset, 1);
+
+ /* Identify Lochnagar */
+ lochnagar->type = config->type;
+
+ lochnagar->regmap = devm_regmap_init_i2c(i2c, config->regmap);
+ if (IS_ERR(lochnagar->regmap)) {
+ ret = PTR_ERR(lochnagar->regmap);
+ dev_err(dev, "Failed to allocate register map: %d\n", ret);
+ return ret;
+ }
+
+ /* Wait for Lochnagar to boot */
+ ret = lochnagar_wait_for_boot(lochnagar->regmap, &val);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read device ID: %d\n", ret);
+ return ret;
+ }
+
+ devid = val & LOCHNAGAR_DEVICE_ID_MASK;
+ rev = val & LOCHNAGAR_REV_ID_MASK;
+
+ if (devid != config->id) {
+ dev_err(dev,
+ "ID does not match %s (expected 0x%x got 0x%x)\n",
+ config->name, config->id, devid);
+ return -ENODEV;
+ }
+
+ /* Identify firmware */
+ ret = regmap_read(lochnagar->regmap, LOCHNAGAR_FIRMWARE_ID1, &val);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read firmware id 1: %d\n", ret);
+ return ret;
+ }
+
+ firmwareid = val;
+
+ ret = regmap_read(lochnagar->regmap, LOCHNAGAR_FIRMWARE_ID2, &val);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read firmware id 2: %d\n", ret);
+ return ret;
+ }
+
+ firmwareid |= (val << config->regmap->val_bits);
+
+ dev_info(dev, "Found %s (0x%x) revision %u firmware 0x%.6x\n",
+ config->name, devid, rev + 1, firmwareid);
+
+ ret = regmap_register_patch(lochnagar->regmap, config->patch,
+ config->npatch);
+ if (ret < 0) {
+ dev_err(dev, "Failed to register patch: %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_of_platform_populate(dev);
+ if (ret < 0) {
+ dev_err(dev, "Failed to populate child nodes: %d\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static struct i2c_driver lochnagar_i2c_driver = {
+ .driver = {
+ .name = "lochnagar",
+ .of_match_table = of_match_ptr(lochnagar_of_match),
+ .suppress_bind_attrs = true,
+ },
+ .probe_new = lochnagar_i2c_probe,
+};
+
+static int __init lochnagar_i2c_init(void)
+{
+ int ret;
+
+ ret = i2c_add_driver(&lochnagar_i2c_driver);
+ if (ret)
+ pr_err("Failed to register Lochnagar driver: %d\n", ret);
+
+ return ret;
+}
+subsys_initcall(lochnagar_i2c_init);
diff --git a/drivers/mfd/max8925-core.c b/drivers/mfd/max8925-core.c
index fd8b15cd84fd..87c724ba9793 100644
--- a/drivers/mfd/max8925-core.c
+++ b/drivers/mfd/max8925-core.c
@@ -10,7 +10,7 @@
*/
#include <linux/kernel.h>
-#include <linux/module.h>
+#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
@@ -919,8 +919,3 @@ void max8925_device_exit(struct max8925_chip *chip)
free_irq(chip->tsc_irq, chip);
mfd_remove_devices(chip->dev);
}
-
-
-MODULE_DESCRIPTION("PMIC Driver for Maxim MAX8925");
-MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com");
-MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/mxs-lradc.c b/drivers/mfd/mxs-lradc.c
index 98e732a7ae96..a09a8d06302b 100644
--- a/drivers/mfd/mxs-lradc.c
+++ b/drivers/mfd/mxs-lradc.c
@@ -181,7 +181,7 @@ static int mxs_lradc_probe(struct platform_device *pdev)
MXS_LRADC_TOUCHSCREEN_5WIRE;
break;
}
- /* fall through to an error message for i.MX23 */
+ /* fall through - to an error message for i.MX23 */
default:
dev_err(&pdev->dev,
"Unsupported number of touchscreen wires (%d)\n"
diff --git a/drivers/mfd/qcom-pm8xxx.c b/drivers/mfd/qcom-pm8xxx.c
index e6e8d81c15fd..8eb2528793f9 100644
--- a/drivers/mfd/qcom-pm8xxx.c
+++ b/drivers/mfd/qcom-pm8xxx.c
@@ -70,22 +70,23 @@
#define PM8XXX_NR_IRQS 256
#define PM8821_NR_IRQS 112
+struct pm_irq_data {
+ int num_irqs;
+ struct irq_chip *irq_chip;
+ void (*irq_handler)(struct irq_desc *desc);
+};
+
struct pm_irq_chip {
struct regmap *regmap;
spinlock_t pm_irq_lock;
struct irq_domain *irqdomain;
- unsigned int num_irqs;
unsigned int num_blocks;
unsigned int num_masters;
+ const struct pm_irq_data *pm_irq_data;
+ /* MUST BE AT THE END OF THIS STRUCT */
u8 config[0];
};
-struct pm_irq_data {
- int num_irqs;
- const struct irq_domain_ops *irq_domain_ops;
- void (*irq_handler)(struct irq_desc *desc);
-};
-
static int pm8xxx_read_block_irq(struct pm_irq_chip *chip, unsigned int bp,
unsigned int *ip)
{
@@ -375,21 +376,38 @@ static struct irq_chip pm8xxx_irq_chip = {
.flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE,
};
-static int pm8xxx_irq_domain_map(struct irq_domain *d, unsigned int irq,
- irq_hw_number_t hwirq)
+static void pm8xxx_irq_domain_map(struct pm_irq_chip *chip,
+ struct irq_domain *domain, unsigned int irq,
+ irq_hw_number_t hwirq, unsigned int type)
{
- struct pm_irq_chip *chip = d->host_data;
-
- irq_set_chip_and_handler(irq, &pm8xxx_irq_chip, handle_level_irq);
- irq_set_chip_data(irq, chip);
+ irq_domain_set_info(domain, irq, hwirq, chip->pm_irq_data->irq_chip,
+ chip, handle_level_irq, NULL, NULL);
irq_set_noprobe(irq);
+}
+
+static int pm8xxx_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
+ unsigned int nr_irqs, void *data)
+{
+ struct pm_irq_chip *chip = domain->host_data;
+ struct irq_fwspec *fwspec = data;
+ irq_hw_number_t hwirq;
+ unsigned int type;
+ int ret, i;
+
+ ret = irq_domain_translate_twocell(domain, fwspec, &hwirq, &type);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < nr_irqs; i++)
+ pm8xxx_irq_domain_map(chip, domain, virq + i, hwirq + i, type);
return 0;
}
static const struct irq_domain_ops pm8xxx_irq_domain_ops = {
- .xlate = irq_domain_xlate_twocell,
- .map = pm8xxx_irq_domain_map,
+ .alloc = pm8xxx_irq_domain_alloc,
+ .free = irq_domain_free_irqs_common,
+ .translate = irq_domain_translate_twocell,
};
static void pm8821_irq_mask_ack(struct irq_data *d)
@@ -473,23 +491,6 @@ static struct irq_chip pm8821_irq_chip = {
.flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE,
};
-static int pm8821_irq_domain_map(struct irq_domain *d, unsigned int irq,
- irq_hw_number_t hwirq)
-{
- struct pm_irq_chip *chip = d->host_data;
-
- irq_set_chip_and_handler(irq, &pm8821_irq_chip, handle_level_irq);
- irq_set_chip_data(irq, chip);
- irq_set_noprobe(irq);
-
- return 0;
-}
-
-static const struct irq_domain_ops pm8821_irq_domain_ops = {
- .xlate = irq_domain_xlate_twocell,
- .map = pm8821_irq_domain_map,
-};
-
static const struct regmap_config ssbi_regmap_config = {
.reg_bits = 16,
.val_bits = 8,
@@ -501,13 +502,13 @@ static const struct regmap_config ssbi_regmap_config = {
static const struct pm_irq_data pm8xxx_data = {
.num_irqs = PM8XXX_NR_IRQS,
- .irq_domain_ops = &pm8xxx_irq_domain_ops,
+ .irq_chip = &pm8xxx_irq_chip,
.irq_handler = pm8xxx_irq_handler,
};
static const struct pm_irq_data pm8821_data = {
.num_irqs = PM8821_NR_IRQS,
- .irq_domain_ops = &pm8821_irq_domain_ops,
+ .irq_chip = &pm8821_irq_chip,
.irq_handler = pm8821_irq_handler,
};
@@ -571,14 +572,14 @@ static int pm8xxx_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, chip);
chip->regmap = regmap;
- chip->num_irqs = data->num_irqs;
- chip->num_blocks = DIV_ROUND_UP(chip->num_irqs, 8);
+ chip->num_blocks = DIV_ROUND_UP(data->num_irqs, 8);
chip->num_masters = DIV_ROUND_UP(chip->num_blocks, 8);
+ chip->pm_irq_data = data;
spin_lock_init(&chip->pm_irq_lock);
chip->irqdomain = irq_domain_add_linear(pdev->dev.of_node,
data->num_irqs,
- data->irq_domain_ops,
+ &pm8xxx_irq_domain_ops,
chip);
if (!chip->irqdomain)
return -ENODEV;
diff --git a/drivers/mfd/rc5t583.c b/drivers/mfd/rc5t583.c
index fd46de02b715..c5cc5cb3dde7 100644
--- a/drivers/mfd/rc5t583.c
+++ b/drivers/mfd/rc5t583.c
@@ -23,7 +23,6 @@
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/kernel.h>
-#include <linux/module.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/slab.h>
@@ -298,8 +297,6 @@ static const struct i2c_device_id rc5t583_i2c_id[] = {
{}
};
-MODULE_DEVICE_TABLE(i2c, rc5t583_i2c_id);
-
static struct i2c_driver rc5t583_i2c_driver = {
.driver = {
.name = "rc5t583",
@@ -313,14 +310,3 @@ static int __init rc5t583_i2c_init(void)
return i2c_add_driver(&rc5t583_i2c_driver);
}
subsys_initcall(rc5t583_i2c_init);
-
-static void __exit rc5t583_i2c_exit(void)
-{
- i2c_del_driver(&rc5t583_i2c_driver);
-}
-
-module_exit(rc5t583_i2c_exit);
-
-MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
-MODULE_DESCRIPTION("RICOH RC5T583 power management system device driver");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/sec-core.c b/drivers/mfd/sec-core.c
index e0835c9df7a1..521319086c81 100644
--- a/drivers/mfd/sec-core.c
+++ b/drivers/mfd/sec-core.c
@@ -114,7 +114,8 @@ static const struct mfd_cell s2mpu02_devs[] = {
#ifdef CONFIG_OF
static const struct of_device_id sec_dt_match[] = {
- { .compatible = "samsung,s5m8767-pmic",
+ {
+ .compatible = "samsung,s5m8767-pmic",
.data = (void *)S5M8767X,
}, {
.compatible = "samsung,s2mps11-pmic",
@@ -309,8 +310,8 @@ static void sec_pmic_configure(struct sec_pmic_dev *sec_pmic)
* the sub-modules need not instantiate another instance while parsing their
* platform data.
*/
-static struct sec_platform_data *sec_pmic_i2c_parse_dt_pdata(
- struct device *dev)
+static struct sec_platform_data *
+sec_pmic_i2c_parse_dt_pdata(struct device *dev)
{
struct sec_platform_data *pd;
@@ -331,8 +332,8 @@ static struct sec_platform_data *sec_pmic_i2c_parse_dt_pdata(
return pd;
}
#else
-static struct sec_platform_data *sec_pmic_i2c_parse_dt_pdata(
- struct device *dev)
+static struct sec_platform_data *
+sec_pmic_i2c_parse_dt_pdata(struct device *dev)
{
return NULL;
}
@@ -471,8 +472,9 @@ static int sec_pmic_probe(struct i2c_client *i2c,
num_sec_devs = ARRAY_SIZE(s2mpu02_devs);
break;
default:
- /* If this happens the probe function is problem */
- BUG();
+ dev_err(&i2c->dev, "Unsupported device type (%lu)\n",
+ sec_pmic->device_type);
+ return -ENODEV;
}
ret = devm_mfd_add_devices(sec_pmic->dev, -1, sec_devs, num_sec_devs,
NULL, 0, NULL);
diff --git a/drivers/mfd/sm501.c b/drivers/mfd/sm501.c
index a530972c5a7e..d217debf382e 100644
--- a/drivers/mfd/sm501.c
+++ b/drivers/mfd/sm501.c
@@ -1142,9 +1142,11 @@ static int sm501_register_gpio_i2c_instance(struct sm501_devdata *sm,
return -ENOMEM;
/* Create a gpiod lookup using gpiochip-local offsets */
- lookup = devm_kzalloc(&pdev->dev,
- sizeof(*lookup) + 3 * sizeof(struct gpiod_lookup),
+ lookup = devm_kzalloc(&pdev->dev, struct_size(lookup, table, 3),
GFP_KERNEL);
+ if (!lookup)
+ return -ENOMEM;
+
lookup->dev_id = "i2c-gpio";
if (iic->pin_sda < 32)
lookup->table[0].chip_label = "SM501-LOW";
diff --git a/drivers/mfd/sta2x11-mfd.c b/drivers/mfd/sta2x11-mfd.c
index 3aeafa228baf..cab9aabcaa1f 100644
--- a/drivers/mfd/sta2x11-mfd.c
+++ b/drivers/mfd/sta2x11-mfd.c
@@ -1,4 +1,6 @@
/*
+ * STA2x11 mfd for GPIO, SCTL and APBREG
+ *
* Copyright (c) 2009-2011 Wind River Systems, Inc.
* Copyright (c) 2011 ST Microelectronics (Alessandro Rubini, Davide Ciminaghi)
*
@@ -18,7 +20,8 @@
*/
#include <linux/kernel.h>
-#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/export.h>
#include <linux/spinlock.h>
#include <linux/errno.h>
#include <linux/device.h>
@@ -653,8 +656,3 @@ static int __init sta2x11_mfd_init(void)
*/
subsys_initcall(sta2x11_drivers_init);
rootfs_initcall(sta2x11_mfd_init);
-
-MODULE_LICENSE("GPL v2");
-MODULE_AUTHOR("Wind River");
-MODULE_DESCRIPTION("STA2x11 mfd for GPIO, SCTL and APBREG");
-MODULE_DEVICE_TABLE(pci, sta2x11_mfd_tbl);
diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c
index 7569a4be0608..f2acb1f6a29c 100644
--- a/drivers/mfd/stmpe.c
+++ b/drivers/mfd/stmpe.c
@@ -464,6 +464,28 @@ static const struct mfd_cell stmpe_ts_cell = {
};
/*
+ * ADC (STMPE811)
+ */
+
+static struct resource stmpe_adc_resources[] = {
+ {
+ .name = "STMPE_TEMP_SENS",
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "STMPE_ADC",
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static const struct mfd_cell stmpe_adc_cell = {
+ .name = "stmpe-adc",
+ .of_compatible = "st,stmpe-adc",
+ .resources = stmpe_adc_resources,
+ .num_resources = ARRAY_SIZE(stmpe_adc_resources),
+};
+
+/*
* STMPE811 or STMPE610
*/
@@ -497,6 +519,11 @@ static struct stmpe_variant_block stmpe811_blocks[] = {
.irq = STMPE811_IRQ_TOUCH_DET,
.block = STMPE_BLOCK_TOUCHSCREEN,
},
+ {
+ .cell = &stmpe_adc_cell,
+ .irq = STMPE811_IRQ_TEMP_SENS,
+ .block = STMPE_BLOCK_ADC,
+ },
};
static int stmpe811_enable(struct stmpe *stmpe, unsigned int blocks,
@@ -517,6 +544,35 @@ static int stmpe811_enable(struct stmpe *stmpe, unsigned int blocks,
enable ? 0 : mask);
}
+int stmpe811_adc_common_init(struct stmpe *stmpe)
+{
+ int ret;
+ u8 adc_ctrl1, adc_ctrl1_mask;
+
+ adc_ctrl1 = STMPE_SAMPLE_TIME(stmpe->sample_time) |
+ STMPE_MOD_12B(stmpe->mod_12b) |
+ STMPE_REF_SEL(stmpe->ref_sel);
+ adc_ctrl1_mask = STMPE_SAMPLE_TIME(0xff) | STMPE_MOD_12B(0xff) |
+ STMPE_REF_SEL(0xff);
+
+ ret = stmpe_set_bits(stmpe, STMPE811_REG_ADC_CTRL1,
+ adc_ctrl1_mask, adc_ctrl1);
+ if (ret) {
+ dev_err(stmpe->dev, "Could not setup ADC\n");
+ return ret;
+ }
+
+ ret = stmpe_set_bits(stmpe, STMPE811_REG_ADC_CTRL2,
+ STMPE_ADC_FREQ(0xff), STMPE_ADC_FREQ(stmpe->adc_freq));
+ if (ret) {
+ dev_err(stmpe->dev, "Could not setup ADC\n");
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(stmpe811_adc_common_init);
+
static int stmpe811_get_altfunc(struct stmpe *stmpe, enum stmpe_block block)
{
/* 0 for touchscreen, 1 for GPIO */
@@ -1325,6 +1381,7 @@ int stmpe_probe(struct stmpe_client_info *ci, enum stmpe_partnum partnum)
struct device_node *np = ci->dev->of_node;
struct stmpe *stmpe;
int ret;
+ u32 val;
pdata = devm_kzalloc(ci->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
@@ -1342,6 +1399,15 @@ int stmpe_probe(struct stmpe_client_info *ci, enum stmpe_partnum partnum)
mutex_init(&stmpe->irq_lock);
mutex_init(&stmpe->lock);
+ if (!of_property_read_u32(np, "st,sample-time", &val))
+ stmpe->sample_time = val;
+ if (!of_property_read_u32(np, "st,mod-12b", &val))
+ stmpe->mod_12b = val;
+ if (!of_property_read_u32(np, "st,ref-sel", &val))
+ stmpe->ref_sel = val;
+ if (!of_property_read_u32(np, "st,adc-freq", &val))
+ stmpe->adc_freq = val;
+
stmpe->dev = ci->dev;
stmpe->client = ci->client;
stmpe->pdata = pdata;
@@ -1433,6 +1499,8 @@ int stmpe_remove(struct stmpe *stmpe)
if (!IS_ERR(stmpe->vcc))
regulator_disable(stmpe->vcc);
+ __stmpe_disable(stmpe, STMPE_BLOCK_ADC);
+
mfd_remove_devices(stmpe->dev);
return 0;
diff --git a/drivers/mfd/stpmic1.c b/drivers/mfd/stpmic1.c
new file mode 100644
index 000000000000..7dfbe8906cb8
--- /dev/null
+++ b/drivers/mfd/stpmic1.c
@@ -0,0 +1,213 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) STMicroelectronics 2018
+// Author: Pascal Paillet <p.paillet@st.com>
+
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/stpmic1.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/pm_wakeirq.h>
+#include <linux/regmap.h>
+
+#include <dt-bindings/mfd/st,stpmic1.h>
+
+#define STPMIC1_MAIN_IRQ 0
+
+static const struct regmap_range stpmic1_readable_ranges[] = {
+ regmap_reg_range(TURN_ON_SR, VERSION_SR),
+ regmap_reg_range(SWOFF_PWRCTRL_CR, LDO6_STDBY_CR),
+ regmap_reg_range(BST_SW_CR, BST_SW_CR),
+ regmap_reg_range(INT_PENDING_R1, INT_PENDING_R4),
+ regmap_reg_range(INT_CLEAR_R1, INT_CLEAR_R4),
+ regmap_reg_range(INT_MASK_R1, INT_MASK_R4),
+ regmap_reg_range(INT_SET_MASK_R1, INT_SET_MASK_R4),
+ regmap_reg_range(INT_CLEAR_MASK_R1, INT_CLEAR_MASK_R4),
+ regmap_reg_range(INT_SRC_R1, INT_SRC_R1),
+};
+
+static const struct regmap_range stpmic1_writeable_ranges[] = {
+ regmap_reg_range(SWOFF_PWRCTRL_CR, LDO6_STDBY_CR),
+ regmap_reg_range(BST_SW_CR, BST_SW_CR),
+ regmap_reg_range(INT_CLEAR_R1, INT_CLEAR_R4),
+ regmap_reg_range(INT_SET_MASK_R1, INT_SET_MASK_R4),
+ regmap_reg_range(INT_CLEAR_MASK_R1, INT_CLEAR_MASK_R4),
+};
+
+static const struct regmap_range stpmic1_volatile_ranges[] = {
+ regmap_reg_range(TURN_ON_SR, VERSION_SR),
+ regmap_reg_range(WCHDG_CR, WCHDG_CR),
+ regmap_reg_range(INT_PENDING_R1, INT_PENDING_R4),
+ regmap_reg_range(INT_SRC_R1, INT_SRC_R4),
+};
+
+static const struct regmap_access_table stpmic1_readable_table = {
+ .yes_ranges = stpmic1_readable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(stpmic1_readable_ranges),
+};
+
+static const struct regmap_access_table stpmic1_writeable_table = {
+ .yes_ranges = stpmic1_writeable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(stpmic1_writeable_ranges),
+};
+
+static const struct regmap_access_table stpmic1_volatile_table = {
+ .yes_ranges = stpmic1_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(stpmic1_volatile_ranges),
+};
+
+const struct regmap_config stpmic1_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .cache_type = REGCACHE_RBTREE,
+ .max_register = PMIC_MAX_REGISTER_ADDRESS,
+ .rd_table = &stpmic1_readable_table,
+ .wr_table = &stpmic1_writeable_table,
+ .volatile_table = &stpmic1_volatile_table,
+};
+
+static const struct regmap_irq stpmic1_irqs[] = {
+ REGMAP_IRQ_REG(IT_PONKEY_F, 0, 0x01),
+ REGMAP_IRQ_REG(IT_PONKEY_R, 0, 0x02),
+ REGMAP_IRQ_REG(IT_WAKEUP_F, 0, 0x04),
+ REGMAP_IRQ_REG(IT_WAKEUP_R, 0, 0x08),
+ REGMAP_IRQ_REG(IT_VBUS_OTG_F, 0, 0x10),
+ REGMAP_IRQ_REG(IT_VBUS_OTG_R, 0, 0x20),
+ REGMAP_IRQ_REG(IT_SWOUT_F, 0, 0x40),
+ REGMAP_IRQ_REG(IT_SWOUT_R, 0, 0x80),
+
+ REGMAP_IRQ_REG(IT_CURLIM_BUCK1, 1, 0x01),
+ REGMAP_IRQ_REG(IT_CURLIM_BUCK2, 1, 0x02),
+ REGMAP_IRQ_REG(IT_CURLIM_BUCK3, 1, 0x04),
+ REGMAP_IRQ_REG(IT_CURLIM_BUCK4, 1, 0x08),
+ REGMAP_IRQ_REG(IT_OCP_OTG, 1, 0x10),
+ REGMAP_IRQ_REG(IT_OCP_SWOUT, 1, 0x20),
+ REGMAP_IRQ_REG(IT_OCP_BOOST, 1, 0x40),
+ REGMAP_IRQ_REG(IT_OVP_BOOST, 1, 0x80),
+
+ REGMAP_IRQ_REG(IT_CURLIM_LDO1, 2, 0x01),
+ REGMAP_IRQ_REG(IT_CURLIM_LDO2, 2, 0x02),
+ REGMAP_IRQ_REG(IT_CURLIM_LDO3, 2, 0x04),
+ REGMAP_IRQ_REG(IT_CURLIM_LDO4, 2, 0x08),
+ REGMAP_IRQ_REG(IT_CURLIM_LDO5, 2, 0x10),
+ REGMAP_IRQ_REG(IT_CURLIM_LDO6, 2, 0x20),
+ REGMAP_IRQ_REG(IT_SHORT_SWOTG, 2, 0x40),
+ REGMAP_IRQ_REG(IT_SHORT_SWOUT, 2, 0x80),
+
+ REGMAP_IRQ_REG(IT_TWARN_F, 3, 0x01),
+ REGMAP_IRQ_REG(IT_TWARN_R, 3, 0x02),
+ REGMAP_IRQ_REG(IT_VINLOW_F, 3, 0x04),
+ REGMAP_IRQ_REG(IT_VINLOW_R, 3, 0x08),
+ REGMAP_IRQ_REG(IT_SWIN_F, 3, 0x40),
+ REGMAP_IRQ_REG(IT_SWIN_R, 3, 0x80),
+};
+
+static const struct regmap_irq_chip stpmic1_regmap_irq_chip = {
+ .name = "pmic_irq",
+ .status_base = INT_PENDING_R1,
+ .mask_base = INT_CLEAR_MASK_R1,
+ .unmask_base = INT_SET_MASK_R1,
+ .ack_base = INT_CLEAR_R1,
+ .num_regs = STPMIC1_PMIC_NUM_IRQ_REGS,
+ .irqs = stpmic1_irqs,
+ .num_irqs = ARRAY_SIZE(stpmic1_irqs),
+};
+
+static int stpmic1_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct stpmic1 *ddata;
+ struct device *dev = &i2c->dev;
+ int ret;
+ struct device_node *np = dev->of_node;
+ u32 reg;
+
+ ddata = devm_kzalloc(dev, sizeof(struct stpmic1), GFP_KERNEL);
+ if (!ddata)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, ddata);
+ ddata->dev = dev;
+
+ ddata->regmap = devm_regmap_init_i2c(i2c, &stpmic1_regmap_config);
+ if (IS_ERR(ddata->regmap))
+ return PTR_ERR(ddata->regmap);
+
+ ddata->irq = of_irq_get(np, STPMIC1_MAIN_IRQ);
+ if (ddata->irq < 0) {
+ dev_err(dev, "Failed to get main IRQ: %d\n", ddata->irq);
+ return ddata->irq;
+ }
+
+ ret = regmap_read(ddata->regmap, VERSION_SR, &reg);
+ if (ret) {
+ dev_err(dev, "Unable to read PMIC version\n");
+ return ret;
+ }
+ dev_info(dev, "PMIC Chip Version: 0x%x\n", reg);
+
+ /* Initialize PMIC IRQ Chip & associated IRQ domains */
+ ret = devm_regmap_add_irq_chip(dev, ddata->regmap, ddata->irq,
+ IRQF_ONESHOT | IRQF_SHARED,
+ 0, &stpmic1_regmap_irq_chip,
+ &ddata->irq_data);
+ if (ret) {
+ dev_err(dev, "IRQ Chip registration failed: %d\n", ret);
+ return ret;
+ }
+
+ return devm_of_platform_populate(dev);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int stpmic1_suspend(struct device *dev)
+{
+ struct i2c_client *i2c = to_i2c_client(dev);
+ struct stpmic1 *pmic_dev = i2c_get_clientdata(i2c);
+
+ disable_irq(pmic_dev->irq);
+
+ return 0;
+}
+
+static int stpmic1_resume(struct device *dev)
+{
+ struct i2c_client *i2c = to_i2c_client(dev);
+ struct stpmic1 *pmic_dev = i2c_get_clientdata(i2c);
+ int ret;
+
+ ret = regcache_sync(pmic_dev->regmap);
+ if (ret)
+ return ret;
+
+ enable_irq(pmic_dev->irq);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(stpmic1_pm, stpmic1_suspend, stpmic1_resume);
+
+static const struct of_device_id stpmic1_of_match[] = {
+ { .compatible = "st,stpmic1", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, stpmic1_of_match);
+
+static struct i2c_driver stpmic1_driver = {
+ .driver = {
+ .name = "stpmic1",
+ .of_match_table = of_match_ptr(stpmic1_of_match),
+ .pm = &stpmic1_pm,
+ },
+ .probe = stpmic1_probe,
+};
+
+module_i2c_driver(stpmic1_driver);
+
+MODULE_DESCRIPTION("STPMIC1 PMIC Driver");
+MODULE_AUTHOR("Pascal Paillet <p.paillet@st.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c
index b6d05cd934e6..0ecdffb3d967 100644
--- a/drivers/mfd/syscon.c
+++ b/drivers/mfd/syscon.c
@@ -15,7 +15,7 @@
#include <linux/err.h>
#include <linux/hwspinlock.h>
#include <linux/io.h>
-#include <linux/module.h>
+#include <linux/init.h>
#include <linux/list.h>
#include <linux/of.h>
#include <linux/of_address.h>
@@ -272,13 +272,3 @@ static int __init syscon_init(void)
return platform_driver_register(&syscon_driver);
}
postcore_initcall(syscon_init);
-
-static void __exit syscon_exit(void)
-{
- platform_driver_unregister(&syscon_driver);
-}
-module_exit(syscon_exit);
-
-MODULE_AUTHOR("Dong Aisheng <dong.aisheng@linaro.org>");
-MODULE_DESCRIPTION("System Control driver");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/tps65090.c b/drivers/mfd/tps65090.c
index f13e4cd06e89..6968df4d7828 100644
--- a/drivers/mfd/tps65090.c
+++ b/drivers/mfd/tps65090.c
@@ -2,7 +2,9 @@
* Core driver for TI TPS65090 PMIC family
*
* Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
-
+ *
+ * Author: Venu Byravarasu <vbyravarasu@nvidia.com>
+ *
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
@@ -19,7 +21,7 @@
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/kernel.h>
-#include <linux/module.h>
+#include <linux/init.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/i2c.h>
@@ -171,7 +173,6 @@ static const struct of_device_id tps65090_of_match[] = {
{ .compatible = "ti,tps65090",},
{},
};
-MODULE_DEVICE_TABLE(of, tps65090_of_match);
#endif
static int tps65090_i2c_probe(struct i2c_client *client,
@@ -236,30 +237,19 @@ err_irq_exit:
return ret;
}
-static int tps65090_i2c_remove(struct i2c_client *client)
-{
- struct tps65090 *tps65090 = i2c_get_clientdata(client);
-
- mfd_remove_devices(tps65090->dev);
- if (client->irq)
- regmap_del_irq_chip(client->irq, tps65090->irq_data);
-
- return 0;
-}
static const struct i2c_device_id tps65090_id_table[] = {
{ "tps65090", 0 },
{ },
};
-MODULE_DEVICE_TABLE(i2c, tps65090_id_table);
static struct i2c_driver tps65090_driver = {
.driver = {
.name = "tps65090",
+ .suppress_bind_attrs = true,
.of_match_table = of_match_ptr(tps65090_of_match),
},
.probe = tps65090_i2c_probe,
- .remove = tps65090_i2c_remove,
.id_table = tps65090_id_table,
};
@@ -268,13 +258,3 @@ static int __init tps65090_init(void)
return i2c_add_driver(&tps65090_driver);
}
subsys_initcall(tps65090_init);
-
-static void __exit tps65090_exit(void)
-{
- i2c_del_driver(&tps65090_driver);
-}
-module_exit(tps65090_exit);
-
-MODULE_DESCRIPTION("TPS65090 core driver");
-MODULE_AUTHOR("Venu Byravarasu <vbyravarasu@nvidia.com>");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/tps65218.c b/drivers/mfd/tps65218.c
index 8bcdecf494d0..a62ea4cb8be7 100644
--- a/drivers/mfd/tps65218.c
+++ b/drivers/mfd/tps65218.c
@@ -211,6 +211,83 @@ static const struct of_device_id of_tps65218_match_table[] = {
};
MODULE_DEVICE_TABLE(of, of_tps65218_match_table);
+static int tps65218_voltage_set_strict(struct tps65218 *tps)
+{
+ u32 strict;
+
+ if (of_property_read_u32(tps->dev->of_node,
+ "ti,strict-supply-voltage-supervision",
+ &strict))
+ return 0;
+
+ if (strict != 0 && strict != 1) {
+ dev_err(tps->dev,
+ "Invalid ti,strict-supply-voltage-supervision value\n");
+ return -EINVAL;
+ }
+
+ tps65218_update_bits(tps, TPS65218_REG_CONFIG1,
+ TPS65218_CONFIG1_STRICT,
+ strict ? TPS65218_CONFIG1_STRICT : 0,
+ TPS65218_PROTECT_L1);
+ return 0;
+}
+
+static int tps65218_voltage_set_uv_hyst(struct tps65218 *tps)
+{
+ u32 hyst;
+
+ if (of_property_read_u32(tps->dev->of_node,
+ "ti,under-voltage-hyst-microvolt", &hyst))
+ return 0;
+
+ if (hyst != 400000 && hyst != 200000) {
+ dev_err(tps->dev,
+ "Invalid ti,under-voltage-hyst-microvolt value\n");
+ return -EINVAL;
+ }
+
+ tps65218_update_bits(tps, TPS65218_REG_CONFIG2,
+ TPS65218_CONFIG2_UVLOHYS,
+ hyst == 400000 ? TPS65218_CONFIG2_UVLOHYS : 0,
+ TPS65218_PROTECT_L1);
+ return 0;
+}
+
+static int tps65218_voltage_set_uvlo(struct tps65218 *tps)
+{
+ u32 uvlo;
+ int uvloval;
+
+ if (of_property_read_u32(tps->dev->of_node,
+ "ti,under-voltage-limit-microvolt", &uvlo))
+ return 0;
+
+ switch (uvlo) {
+ case 2750000:
+ uvloval = TPS65218_CONFIG1_UVLO_2750000;
+ break;
+ case 2950000:
+ uvloval = TPS65218_CONFIG1_UVLO_2950000;
+ break;
+ case 3250000:
+ uvloval = TPS65218_CONFIG1_UVLO_3250000;
+ break;
+ case 3350000:
+ uvloval = TPS65218_CONFIG1_UVLO_3350000;
+ break;
+ default:
+ dev_err(tps->dev,
+ "Invalid ti,under-voltage-limit-microvolt value\n");
+ return -EINVAL;
+ }
+
+ tps65218_update_bits(tps, TPS65218_REG_CONFIG1,
+ TPS65218_CONFIG1_UVLO_MASK, uvloval,
+ TPS65218_PROTECT_L1);
+ return 0;
+}
+
static int tps65218_probe(struct i2c_client *client,
const struct i2c_device_id *ids)
{
@@ -249,6 +326,18 @@ static int tps65218_probe(struct i2c_client *client,
tps->rev = chipid & TPS65218_CHIPID_REV_MASK;
+ ret = tps65218_voltage_set_strict(tps);
+ if (ret)
+ return ret;
+
+ ret = tps65218_voltage_set_uvlo(tps);
+ if (ret)
+ return ret;
+
+ ret = tps65218_voltage_set_uv_hyst(tps);
+ if (ret)
+ return ret;
+
ret = mfd_add_devices(tps->dev, PLATFORM_DEVID_AUTO, tps65218_cells,
ARRAY_SIZE(tps65218_cells), NULL, 0,
regmap_irq_get_domain(tps->irq_data));
diff --git a/drivers/mfd/tps65910.c b/drivers/mfd/tps65910.c
index bf16cbe6fd88..aa3d472a10ff 100644
--- a/drivers/mfd/tps65910.c
+++ b/drivers/mfd/tps65910.c
@@ -1,5 +1,5 @@
/*
- * tps65910.c -- TI TPS6591x
+ * tps65910.c -- TI TPS6591x chip family multi-function driver
*
* Copyright 2010 Texas Instruments Inc.
*
@@ -13,8 +13,6 @@
*
*/
-#include <linux/module.h>
-#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/slab.h>
@@ -374,7 +372,6 @@ static const struct of_device_id tps65910_of_match[] = {
{ .compatible = "ti,tps65911", .data = (void *)TPS65911},
{ },
};
-MODULE_DEVICE_TABLE(of, tps65910_of_match);
static struct tps65910_board *tps65910_parse_dt(struct i2c_client *client,
unsigned long *chip_id)
@@ -527,8 +524,6 @@ static const struct i2c_device_id tps65910_i2c_id[] = {
{ "tps65911", TPS65911 },
{ }
};
-MODULE_DEVICE_TABLE(i2c, tps65910_i2c_id);
-
static struct i2c_driver tps65910_i2c_driver = {
.driver = {
@@ -545,14 +540,3 @@ static int __init tps65910_i2c_init(void)
}
/* init early so consumer devices can complete system boot */
subsys_initcall(tps65910_i2c_init);
-
-static void __exit tps65910_i2c_exit(void)
-{
- i2c_del_driver(&tps65910_i2c_driver);
-}
-module_exit(tps65910_i2c_exit);
-
-MODULE_AUTHOR("Graeme Gregory <gg@slimlogic.co.uk>");
-MODULE_AUTHOR("Jorge Eduardo Candelaria <jedu@slimlogic.co.uk>");
-MODULE_DESCRIPTION("TPS6591x chip family multi-function driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/tps68470.c b/drivers/mfd/tps68470.c
index a5981a79b29a..4a4df4ffd18c 100644
--- a/drivers/mfd/tps68470.c
+++ b/drivers/mfd/tps68470.c
@@ -86,7 +86,6 @@ static const struct acpi_device_id tps68470_acpi_ids[] = {
{"INT3472"},
{},
};
-MODULE_DEVICE_TABLE(acpi, tps68470_acpi_ids);
static struct i2c_driver tps68470_driver = {
.driver = {
diff --git a/drivers/mfd/tps80031.c b/drivers/mfd/tps80031.c
index 608c7f77830e..865257ade8ac 100644
--- a/drivers/mfd/tps80031.c
+++ b/drivers/mfd/tps80031.c
@@ -30,7 +30,6 @@
#include <linux/irq.h>
#include <linux/mfd/core.h>
#include <linux/mfd/tps80031.h>
-#include <linux/module.h>
#include <linux/pm.h>
#include <linux/regmap.h>
#include <linux/slab.h>
@@ -516,40 +515,18 @@ fail_client_reg:
return ret;
}
-static int tps80031_remove(struct i2c_client *client)
-{
- struct tps80031 *tps80031 = i2c_get_clientdata(client);
- int i;
-
- if (tps80031_power_off_dev == tps80031) {
- tps80031_power_off_dev = NULL;
- pm_power_off = NULL;
- }
-
- mfd_remove_devices(tps80031->dev);
-
- regmap_del_irq_chip(client->irq, tps80031->irq_data);
-
- for (i = 0; i < TPS80031_NUM_SLAVES; i++) {
- if (tps80031->clients[i] != client)
- i2c_unregister_device(tps80031->clients[i]);
- }
- return 0;
-}
-
static const struct i2c_device_id tps80031_id_table[] = {
{ "tps80031", TPS80031 },
{ "tps80032", TPS80032 },
{ }
};
-MODULE_DEVICE_TABLE(i2c, tps80031_id_table);
static struct i2c_driver tps80031_driver = {
.driver = {
- .name = "tps80031",
+ .name = "tps80031",
+ .suppress_bind_attrs = true,
},
.probe = tps80031_probe,
- .remove = tps80031_remove,
.id_table = tps80031_id_table,
};
@@ -558,13 +535,3 @@ static int __init tps80031_init(void)
return i2c_add_driver(&tps80031_driver);
}
subsys_initcall(tps80031_init);
-
-static void __exit tps80031_exit(void)
-{
- i2c_del_driver(&tps80031_driver);
-}
-module_exit(tps80031_exit);
-
-MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
-MODULE_DESCRIPTION("TPS80031 core driver");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/tqmx86.c b/drivers/mfd/tqmx86.c
new file mode 100644
index 000000000000..22d2f02d855c
--- /dev/null
+++ b/drivers/mfd/tqmx86.c
@@ -0,0 +1,281 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * TQ-Systems PLD MFD core driver, based on vendor driver by
+ * Vadim V.Vlasov <vvlasov@dev.rtsoft.ru>
+ *
+ * Copyright (c) 2015 TQ-Systems GmbH
+ * Copyright (c) 2019 Andrew Lunn <andrew@lunn.ch>
+ */
+
+#include <linux/delay.h>
+#include <linux/dmi.h>
+#include <linux/i2c.h>
+#include <linux/io.h>
+#include <linux/mfd/core.h>
+#include <linux/module.h>
+#include <linux/platform_data/i2c-ocores.h>
+#include <linux/platform_device.h>
+
+#define TQMX86_IOBASE 0x160
+#define TQMX86_IOSIZE 0x3f
+#define TQMX86_IOBASE_I2C 0x1a0
+#define TQMX86_IOSIZE_I2C 0xa
+#define TQMX86_IOBASE_WATCHDOG 0x18b
+#define TQMX86_IOSIZE_WATCHDOG 0x2
+#define TQMX86_IOBASE_GPIO 0x18d
+#define TQMX86_IOSIZE_GPIO 0x4
+
+#define TQMX86_REG_BOARD_ID 0x20
+#define TQMX86_REG_BOARD_ID_E38M 1
+#define TQMX86_REG_BOARD_ID_50UC 2
+#define TQMX86_REG_BOARD_ID_E38C 3
+#define TQMX86_REG_BOARD_ID_60EB 4
+#define TQMX86_REG_BOARD_ID_E39M 5
+#define TQMX86_REG_BOARD_ID_E39C 6
+#define TQMX86_REG_BOARD_ID_E39x 7
+#define TQMX86_REG_BOARD_ID_70EB 8
+#define TQMX86_REG_BOARD_ID_80UC 9
+#define TQMX86_REG_BOARD_ID_90UC 10
+#define TQMX86_REG_BOARD_REV 0x21
+#define TQMX86_REG_IO_EXT_INT 0x26
+#define TQMX86_REG_IO_EXT_INT_NONE 0
+#define TQMX86_REG_IO_EXT_INT_7 1
+#define TQMX86_REG_IO_EXT_INT_9 2
+#define TQMX86_REG_IO_EXT_INT_12 3
+#define TQMX86_REG_IO_EXT_INT_MASK 0x3
+#define TQMX86_REG_IO_EXT_INT_GPIO_SHIFT 4
+
+#define TQMX86_REG_I2C_DETECT 0x47
+#define TQMX86_REG_I2C_DETECT_SOFT 0xa5
+#define TQMX86_REG_I2C_INT_EN 0x49
+
+static uint gpio_irq;
+module_param(gpio_irq, uint, 0);
+MODULE_PARM_DESC(gpio_irq, "GPIO IRQ number (7, 9, 12)");
+
+static const struct resource tqmx_i2c_soft_resources[] = {
+ DEFINE_RES_IO(TQMX86_IOBASE_I2C, TQMX86_IOSIZE_I2C),
+};
+
+static const struct resource tqmx_watchdog_resources[] = {
+ DEFINE_RES_IO(TQMX86_IOBASE_WATCHDOG, TQMX86_IOSIZE_WATCHDOG),
+};
+
+/*
+ * The IRQ resource must be first, since it is updated with the
+ * configured IRQ in the probe function.
+ */
+static struct resource tqmx_gpio_resources[] = {
+ DEFINE_RES_IRQ(0),
+ DEFINE_RES_IO(TQMX86_IOBASE_GPIO, TQMX86_IOSIZE_GPIO),
+};
+
+static struct i2c_board_info tqmx86_i2c_devices[] = {
+ {
+ /* 4K EEPROM at 0x50 */
+ I2C_BOARD_INFO("24c32", 0x50),
+ },
+};
+
+static struct ocores_i2c_platform_data ocores_platfom_data = {
+ .num_devices = ARRAY_SIZE(tqmx86_i2c_devices),
+ .devices = tqmx86_i2c_devices,
+};
+
+static const struct mfd_cell tqmx86_i2c_soft_dev[] = {
+ {
+ .name = "ocores-i2c",
+ .platform_data = &ocores_platfom_data,
+ .pdata_size = sizeof(ocores_platfom_data),
+ .resources = tqmx_i2c_soft_resources,
+ .num_resources = ARRAY_SIZE(tqmx_i2c_soft_resources),
+ },
+};
+
+static const struct mfd_cell tqmx86_devs[] = {
+ {
+ .name = "tqmx86-wdt",
+ .resources = tqmx_watchdog_resources,
+ .num_resources = ARRAY_SIZE(tqmx_watchdog_resources),
+ .ignore_resource_conflicts = true,
+ },
+ {
+ .name = "tqmx86-gpio",
+ .resources = tqmx_gpio_resources,
+ .num_resources = ARRAY_SIZE(tqmx_gpio_resources),
+ .ignore_resource_conflicts = true,
+ },
+};
+
+static const char *tqmx86_board_id_to_name(u8 board_id)
+{
+ switch (board_id) {
+ case TQMX86_REG_BOARD_ID_E38M:
+ return "TQMxE38M";
+ case TQMX86_REG_BOARD_ID_50UC:
+ return "TQMx50UC";
+ case TQMX86_REG_BOARD_ID_E38C:
+ return "TQMxE38C";
+ case TQMX86_REG_BOARD_ID_60EB:
+ return "TQMx60EB";
+ case TQMX86_REG_BOARD_ID_E39M:
+ return "TQMxE39M";
+ case TQMX86_REG_BOARD_ID_E39C:
+ return "TQMxE39C";
+ case TQMX86_REG_BOARD_ID_E39x:
+ return "TQMxE39x";
+ case TQMX86_REG_BOARD_ID_70EB:
+ return "TQMx70EB";
+ case TQMX86_REG_BOARD_ID_80UC:
+ return "TQMx80UC";
+ case TQMX86_REG_BOARD_ID_90UC:
+ return "TQMx90UC";
+ default:
+ return "Unknown";
+ }
+}
+
+static int tqmx86_board_id_to_clk_rate(u8 board_id)
+{
+ switch (board_id) {
+ case TQMX86_REG_BOARD_ID_50UC:
+ case TQMX86_REG_BOARD_ID_60EB:
+ case TQMX86_REG_BOARD_ID_70EB:
+ case TQMX86_REG_BOARD_ID_80UC:
+ case TQMX86_REG_BOARD_ID_90UC:
+ return 24000;
+ case TQMX86_REG_BOARD_ID_E39M:
+ case TQMX86_REG_BOARD_ID_E39C:
+ case TQMX86_REG_BOARD_ID_E39x:
+ return 25000;
+ case TQMX86_REG_BOARD_ID_E38M:
+ case TQMX86_REG_BOARD_ID_E38C:
+ return 33000;
+ default:
+ return 0;
+ }
+}
+
+static int tqmx86_probe(struct platform_device *pdev)
+{
+ u8 board_id, rev, i2c_det, i2c_ien, io_ext_int_val;
+ struct device *dev = &pdev->dev;
+ u8 gpio_irq_cfg, readback;
+ const char *board_name;
+ void __iomem *io_base;
+ int err;
+
+ switch (gpio_irq) {
+ case 0:
+ gpio_irq_cfg = TQMX86_REG_IO_EXT_INT_NONE;
+ break;
+ case 7:
+ gpio_irq_cfg = TQMX86_REG_IO_EXT_INT_7;
+ break;
+ case 9:
+ gpio_irq_cfg = TQMX86_REG_IO_EXT_INT_9;
+ break;
+ case 12:
+ gpio_irq_cfg = TQMX86_REG_IO_EXT_INT_12;
+ break;
+ default:
+ pr_err("tqmx86: Invalid GPIO IRQ (%d)\n", gpio_irq);
+ return -EINVAL;
+ }
+
+ io_base = devm_ioport_map(dev, TQMX86_IOBASE, TQMX86_IOSIZE);
+ if (!io_base)
+ return -ENOMEM;
+
+ board_id = ioread8(io_base + TQMX86_REG_BOARD_ID);
+ board_name = tqmx86_board_id_to_name(board_id);
+ rev = ioread8(io_base + TQMX86_REG_BOARD_REV);
+
+ dev_info(dev,
+ "Found %s - Board ID %d, PCB Revision %d, PLD Revision %d\n",
+ board_name, board_id, rev >> 4, rev & 0xf);
+
+ i2c_det = ioread8(io_base + TQMX86_REG_I2C_DETECT);
+ i2c_ien = ioread8(io_base + TQMX86_REG_I2C_INT_EN);
+
+ if (gpio_irq_cfg) {
+ io_ext_int_val =
+ gpio_irq_cfg << TQMX86_REG_IO_EXT_INT_GPIO_SHIFT;
+ iowrite8(io_ext_int_val, io_base + TQMX86_REG_IO_EXT_INT);
+ readback = ioread8(io_base + TQMX86_REG_IO_EXT_INT);
+ if (readback != io_ext_int_val) {
+ dev_warn(dev, "GPIO interrupts not supported.\n");
+ return -EINVAL;
+ }
+
+ /* Assumes the IRQ resource is first. */
+ tqmx_gpio_resources[0].start = gpio_irq;
+ }
+
+ ocores_platfom_data.clock_khz = tqmx86_board_id_to_clk_rate(board_id);
+
+ if (i2c_det == TQMX86_REG_I2C_DETECT_SOFT) {
+ err = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE,
+ tqmx86_i2c_soft_dev,
+ ARRAY_SIZE(tqmx86_i2c_soft_dev),
+ NULL, 0, NULL);
+ if (err)
+ return err;
+ }
+
+ return devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE,
+ tqmx86_devs,
+ ARRAY_SIZE(tqmx86_devs),
+ NULL, 0, NULL);
+}
+
+static int tqmx86_create_platform_device(const struct dmi_system_id *id)
+{
+ struct platform_device *pdev;
+ int err;
+
+ pdev = platform_device_alloc("tqmx86", -1);
+ if (!pdev)
+ return -ENOMEM;
+
+ err = platform_device_add(pdev);
+ if (err)
+ platform_device_put(pdev);
+
+ return err;
+}
+
+static const struct dmi_system_id tqmx86_dmi_table[] __initconst = {
+ {
+ .ident = "TQMX86",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "TQ-Group"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TQMx"),
+ },
+ .callback = tqmx86_create_platform_device,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(dmi, tqmx86_dmi_table);
+
+static struct platform_driver tqmx86_driver = {
+ .driver = {
+ .name = "tqmx86",
+ },
+ .probe = tqmx86_probe,
+};
+
+static int __init tqmx86_init(void)
+{
+ if (!dmi_check_system(tqmx86_dmi_table))
+ return -ENODEV;
+
+ return platform_driver_register(&tqmx86_driver);
+}
+
+module_init(tqmx86_init);
+
+MODULE_DESCRIPTION("TQx86 PLD Core Driver");
+MODULE_AUTHOR("Andrew Lunn <andrew@lunn.ch>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:tqmx86");
diff --git a/drivers/mfd/wm831x-core.c b/drivers/mfd/wm831x-core.c
index e70d35ef5c6d..25fbbaf39cb9 100644
--- a/drivers/mfd/wm831x-core.c
+++ b/drivers/mfd/wm831x-core.c
@@ -13,7 +13,8 @@
*/
#include <linux/kernel.h>
-#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/export.h>
#include <linux/bcd.h>
#include <linux/delay.h>
#include <linux/mfd/core.h>
@@ -1892,14 +1893,6 @@ err:
return ret;
}
-void wm831x_device_exit(struct wm831x *wm831x)
-{
- wm831x_otp_exit(wm831x);
- mfd_remove_devices(wm831x->dev);
- free_irq(wm831x_irq(wm831x, WM831X_IRQ_AUXADC_DATA), wm831x);
- wm831x_irq_exit(wm831x);
-}
-
int wm831x_device_suspend(struct wm831x *wm831x)
{
int reg, mask;
@@ -1944,7 +1937,3 @@ void wm831x_device_shutdown(struct wm831x *wm831x)
}
}
EXPORT_SYMBOL_GPL(wm831x_device_shutdown);
-
-MODULE_DESCRIPTION("Core support for the WM831X AudioPlus PMIC");
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mark Brown");
diff --git a/drivers/mfd/wm831x-i2c.c b/drivers/mfd/wm831x-i2c.c
index 22f7054d1b28..0f3af42f7268 100644
--- a/drivers/mfd/wm831x-i2c.c
+++ b/drivers/mfd/wm831x-i2c.c
@@ -13,7 +13,7 @@
*/
#include <linux/kernel.h>
-#include <linux/module.h>
+#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/mfd/core.h>
@@ -68,15 +68,6 @@ static int wm831x_i2c_probe(struct i2c_client *i2c,
return wm831x_device_init(wm831x, i2c->irq);
}
-static int wm831x_i2c_remove(struct i2c_client *i2c)
-{
- struct wm831x *wm831x = i2c_get_clientdata(i2c);
-
- wm831x_device_exit(wm831x);
-
- return 0;
-}
-
static int wm831x_i2c_suspend(struct device *dev)
{
struct wm831x *wm831x = dev_get_drvdata(dev);
@@ -103,7 +94,6 @@ static const struct i2c_device_id wm831x_i2c_id[] = {
{ "wm8326", WM8326 },
{ }
};
-MODULE_DEVICE_TABLE(i2c, wm831x_i2c_id);
static const struct dev_pm_ops wm831x_pm_ops = {
.suspend = wm831x_i2c_suspend,
@@ -115,9 +105,9 @@ static struct i2c_driver wm831x_i2c_driver = {
.name = "wm831x",
.pm = &wm831x_pm_ops,
.of_match_table = of_match_ptr(wm831x_of_match),
+ .suppress_bind_attrs = true,
},
.probe = wm831x_i2c_probe,
- .remove = wm831x_i2c_remove,
.id_table = wm831x_i2c_id,
};
@@ -132,9 +122,3 @@ static int __init wm831x_i2c_init(void)
return ret;
}
subsys_initcall(wm831x_i2c_init);
-
-static void __exit wm831x_i2c_exit(void)
-{
- i2c_del_driver(&wm831x_i2c_driver);
-}
-module_exit(wm831x_i2c_exit);
diff --git a/drivers/mfd/wm831x-spi.c b/drivers/mfd/wm831x-spi.c
index 018ce652ae57..dd4dab419940 100644
--- a/drivers/mfd/wm831x-spi.c
+++ b/drivers/mfd/wm831x-spi.c
@@ -13,7 +13,7 @@
*/
#include <linux/kernel.h>
-#include <linux/module.h>
+#include <linux/init.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/pm.h>
@@ -67,15 +67,6 @@ static int wm831x_spi_probe(struct spi_device *spi)
return wm831x_device_init(wm831x, spi->irq);
}
-static int wm831x_spi_remove(struct spi_device *spi)
-{
- struct wm831x *wm831x = spi_get_drvdata(spi);
-
- wm831x_device_exit(wm831x);
-
- return 0;
-}
-
static int wm831x_spi_suspend(struct device *dev)
{
struct wm831x *wm831x = dev_get_drvdata(dev);
@@ -108,17 +99,16 @@ static const struct spi_device_id wm831x_spi_ids[] = {
{ "wm8326", WM8326 },
{ },
};
-MODULE_DEVICE_TABLE(spi, wm831x_spi_ids);
static struct spi_driver wm831x_spi_driver = {
.driver = {
.name = "wm831x",
.pm = &wm831x_spi_pm,
.of_match_table = of_match_ptr(wm831x_of_match),
+ .suppress_bind_attrs = true,
},
.id_table = wm831x_spi_ids,
.probe = wm831x_spi_probe,
- .remove = wm831x_spi_remove,
};
static int __init wm831x_spi_init(void)
@@ -132,13 +122,3 @@ static int __init wm831x_spi_init(void)
return 0;
}
subsys_initcall(wm831x_spi_init);
-
-static void __exit wm831x_spi_exit(void)
-{
- spi_unregister_driver(&wm831x_spi_driver);
-}
-module_exit(wm831x_spi_exit);
-
-MODULE_DESCRIPTION("SPI support for WM831x/2x AudioPlus PMICs");
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mark Brown");
diff --git a/drivers/mfd/wm8350-core.c b/drivers/mfd/wm8350-core.c
index 8a07c5634aee..9e1070f26b11 100644
--- a/drivers/mfd/wm8350-core.c
+++ b/drivers/mfd/wm8350-core.c
@@ -13,7 +13,8 @@
*/
#include <linux/kernel.h>
-#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/export.h>
#include <linux/slab.h>
#include <linux/bug.h>
#include <linux/device.h>
@@ -442,30 +443,3 @@ err:
return ret;
}
EXPORT_SYMBOL_GPL(wm8350_device_init);
-
-void wm8350_device_exit(struct wm8350 *wm8350)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(wm8350->pmic.led); i++)
- platform_device_unregister(wm8350->pmic.led[i].pdev);
-
- for (i = 0; i < ARRAY_SIZE(wm8350->pmic.pdev); i++)
- platform_device_unregister(wm8350->pmic.pdev[i]);
-
- platform_device_unregister(wm8350->wdt.pdev);
- platform_device_unregister(wm8350->rtc.pdev);
- platform_device_unregister(wm8350->power.pdev);
- platform_device_unregister(wm8350->hwmon.pdev);
- platform_device_unregister(wm8350->gpio.pdev);
- platform_device_unregister(wm8350->codec.pdev);
-
- if (wm8350->irq_base)
- free_irq(wm8350->irq_base + WM8350_IRQ_AUXADC_DATARDY, wm8350);
-
- wm8350_irq_exit(wm8350);
-}
-EXPORT_SYMBOL_GPL(wm8350_device_exit);
-
-MODULE_DESCRIPTION("WM8350 AudioPlus PMIC core driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/wm8350-i2c.c b/drivers/mfd/wm8350-i2c.c
index 9358f03b7938..b4194e068e1b 100644
--- a/drivers/mfd/wm8350-i2c.c
+++ b/drivers/mfd/wm8350-i2c.c
@@ -13,8 +13,6 @@
*
*/
-#include <linux/module.h>
-#include <linux/moduleparam.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/i2c.h>
@@ -48,30 +46,19 @@ static int wm8350_i2c_probe(struct i2c_client *i2c,
return wm8350_device_init(wm8350, i2c->irq, pdata);
}
-static int wm8350_i2c_remove(struct i2c_client *i2c)
-{
- struct wm8350 *wm8350 = i2c_get_clientdata(i2c);
-
- wm8350_device_exit(wm8350);
-
- return 0;
-}
-
static const struct i2c_device_id wm8350_i2c_id[] = {
{ "wm8350", 0 },
{ "wm8351", 0 },
{ "wm8352", 0 },
{ }
};
-MODULE_DEVICE_TABLE(i2c, wm8350_i2c_id);
-
static struct i2c_driver wm8350_i2c_driver = {
.driver = {
.name = "wm8350",
+ .suppress_bind_attrs = true,
},
.probe = wm8350_i2c_probe,
- .remove = wm8350_i2c_remove,
.id_table = wm8350_i2c_id,
};
@@ -81,12 +68,3 @@ static int __init wm8350_i2c_init(void)
}
/* init early so consumer devices can complete system boot */
subsys_initcall(wm8350_i2c_init);
-
-static void __exit wm8350_i2c_exit(void)
-{
- i2c_del_driver(&wm8350_i2c_driver);
-}
-module_exit(wm8350_i2c_exit);
-
-MODULE_DESCRIPTION("I2C support for the WM8350 AudioPlus PMIC");
-MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/wm8400-core.c b/drivers/mfd/wm8400-core.c
index 8a98a2fc74e1..79756c83f5f0 100644
--- a/drivers/mfd/wm8400-core.c
+++ b/drivers/mfd/wm8400-core.c
@@ -12,7 +12,7 @@
*
*/
-#include <linux/module.h>
+#include <linux/init.h>
#include <linux/bug.h>
#include <linux/err.h>
#include <linux/i2c.h>
@@ -150,7 +150,6 @@ static const struct i2c_device_id wm8400_i2c_id[] = {
{ "wm8400", 0 },
{ }
};
-MODULE_DEVICE_TABLE(i2c, wm8400_i2c_id);
static struct i2c_driver wm8400_i2c_driver = {
.driver = {
@@ -161,7 +160,7 @@ static struct i2c_driver wm8400_i2c_driver = {
};
#endif
-static int __init wm8400_module_init(void)
+static int __init wm8400_driver_init(void)
{
int ret = -ENODEV;
@@ -173,15 +172,4 @@ static int __init wm8400_module_init(void)
return ret;
}
-subsys_initcall(wm8400_module_init);
-
-static void __exit wm8400_module_exit(void)
-{
-#if IS_ENABLED(CONFIG_I2C)
- i2c_del_driver(&wm8400_i2c_driver);
-#endif
-}
-module_exit(wm8400_module_exit);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+subsys_initcall(wm8400_driver_init);
diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c
index ddfcf4ade7bf..63aa541c9608 100644
--- a/drivers/misc/eeprom/at24.c
+++ b/drivers/misc/eeprom/at24.c
@@ -22,10 +22,24 @@
#include <linux/i2c.h>
#include <linux/nvmem-provider.h>
#include <linux/regmap.h>
-#include <linux/platform_data/at24.h>
#include <linux/pm_runtime.h>
#include <linux/gpio/consumer.h>
+/* Address pointer is 16 bit. */
+#define AT24_FLAG_ADDR16 BIT(7)
+/* sysfs-entry will be read-only. */
+#define AT24_FLAG_READONLY BIT(6)
+/* sysfs-entry will be world-readable. */
+#define AT24_FLAG_IRUGO BIT(5)
+/* Take always 8 addresses (24c00). */
+#define AT24_FLAG_TAKE8ADDR BIT(4)
+/* Factory-programmed serial number. */
+#define AT24_FLAG_SERIAL BIT(3)
+/* Factory-programmed mac address. */
+#define AT24_FLAG_MAC BIT(2)
+/* Does not auto-rollover reads to the next slave address. */
+#define AT24_FLAG_NO_RDROL BIT(1)
+
/*
* I2C EEPROMs from most vendors are inexpensive and mostly interchangeable.
* Differences between different vendor product lines (like Atmel AT24C or
@@ -107,10 +121,6 @@ module_param_named(write_timeout, at24_write_timeout, uint, 0);
MODULE_PARM_DESC(at24_write_timeout, "Time (in ms) to try writes (default 25)");
struct at24_chip_data {
- /*
- * these fields mirror their equivalents in
- * struct at24_platform_data
- */
u32 byte_len;
u8 flags;
};
@@ -471,63 +481,11 @@ static int at24_write(void *priv, unsigned int off, void *val, size_t count)
return 0;
}
-static void at24_properties_to_pdata(struct device *dev,
- struct at24_platform_data *chip)
-{
- int err;
- u32 val;
-
- if (device_property_present(dev, "read-only"))
- chip->flags |= AT24_FLAG_READONLY;
- if (device_property_present(dev, "no-read-rollover"))
- chip->flags |= AT24_FLAG_NO_RDROL;
-
- err = device_property_read_u32(dev, "address-width", &val);
- if (!err) {
- switch (val) {
- case 8:
- if (chip->flags & AT24_FLAG_ADDR16)
- dev_warn(dev, "Override address width to be 8, while default is 16\n");
- chip->flags &= ~AT24_FLAG_ADDR16;
- break;
- case 16:
- chip->flags |= AT24_FLAG_ADDR16;
- break;
- default:
- dev_warn(dev, "Bad \"address-width\" property: %u\n",
- val);
- }
- }
-
- err = device_property_read_u32(dev, "size", &val);
- if (!err)
- chip->byte_len = val;
-
- err = device_property_read_u32(dev, "pagesize", &val);
- if (!err) {
- chip->page_size = val;
- } else {
- /*
- * This is slow, but we can't know all eeproms, so we better
- * play safe. Specifying custom eeprom-types via platform_data
- * is recommended anyhow.
- */
- chip->page_size = 1;
- }
-}
-
-static int at24_get_pdata(struct device *dev, struct at24_platform_data *pdata)
+static const struct at24_chip_data *at24_get_chip_data(struct device *dev)
{
struct device_node *of_node = dev->of_node;
const struct at24_chip_data *cdata;
const struct i2c_device_id *id;
- struct at24_platform_data *pd;
-
- pd = dev_get_platdata(dev);
- if (pd) {
- memcpy(pdata, pd, sizeof(*pdata));
- return 0;
- }
id = i2c_match_id(at24_ids, to_i2c_client(dev));
@@ -544,13 +502,9 @@ static int at24_get_pdata(struct device *dev, struct at24_platform_data *pdata)
cdata = acpi_device_get_match_data(dev);
if (!cdata)
- return -ENODEV;
+ return ERR_PTR(-ENODEV);
- pdata->byte_len = cdata->byte_len;
- pdata->flags = cdata->flags;
- at24_properties_to_pdata(dev, pdata);
-
- return 0;
+ return cdata;
}
static void at24_remove_dummy_clients(struct at24_data *at24)
@@ -619,7 +573,8 @@ static int at24_probe(struct i2c_client *client)
{
struct regmap_config regmap_config = { };
struct nvmem_config nvmem_config = { };
- struct at24_platform_data pdata = { };
+ u32 byte_len, page_size, flags, addrw;
+ const struct at24_chip_data *cdata;
struct device *dev = &client->dev;
bool i2c_fn_i2c, i2c_fn_block;
unsigned int i, num_addresses;
@@ -634,35 +589,75 @@ static int at24_probe(struct i2c_client *client)
i2c_fn_block = i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_WRITE_I2C_BLOCK);
- err = at24_get_pdata(dev, &pdata);
+ cdata = at24_get_chip_data(dev);
+ if (IS_ERR(cdata))
+ return PTR_ERR(cdata);
+
+ err = device_property_read_u32(dev, "pagesize", &page_size);
+ if (err)
+ /*
+ * This is slow, but we can't know all eeproms, so we better
+ * play safe. Specifying custom eeprom-types via platform_data
+ * is recommended anyhow.
+ */
+ page_size = 1;
+
+ flags = cdata->flags;
+ if (device_property_present(dev, "read-only"))
+ flags |= AT24_FLAG_READONLY;
+ if (device_property_present(dev, "no-read-rollover"))
+ flags |= AT24_FLAG_NO_RDROL;
+
+ err = device_property_read_u32(dev, "address-width", &addrw);
+ if (!err) {
+ switch (addrw) {
+ case 8:
+ if (flags & AT24_FLAG_ADDR16)
+ dev_warn(dev,
+ "Override address width to be 8, while default is 16\n");
+ flags &= ~AT24_FLAG_ADDR16;
+ break;
+ case 16:
+ flags |= AT24_FLAG_ADDR16;
+ break;
+ default:
+ dev_warn(dev, "Bad \"address-width\" property: %u\n",
+ addrw);
+ }
+ }
+
+ err = device_property_read_u32(dev, "size", &byte_len);
if (err)
- return err;
+ byte_len = cdata->byte_len;
if (!i2c_fn_i2c && !i2c_fn_block)
- pdata.page_size = 1;
+ page_size = 1;
- if (!pdata.page_size) {
+ if (!page_size) {
dev_err(dev, "page_size must not be 0!\n");
return -EINVAL;
}
- if (!is_power_of_2(pdata.page_size))
+ if (!is_power_of_2(page_size))
dev_warn(dev, "page_size looks suspicious (no power of 2)!\n");
- if (pdata.flags & AT24_FLAG_TAKE8ADDR)
- num_addresses = 8;
- else
- num_addresses = DIV_ROUND_UP(pdata.byte_len,
- (pdata.flags & AT24_FLAG_ADDR16) ? 65536 : 256);
+ err = device_property_read_u32(dev, "num-addresses", &num_addresses);
+ if (err) {
+ if (flags & AT24_FLAG_TAKE8ADDR)
+ num_addresses = 8;
+ else
+ num_addresses = DIV_ROUND_UP(byte_len,
+ (flags & AT24_FLAG_ADDR16) ? 65536 : 256);
+ }
- if ((pdata.flags & AT24_FLAG_SERIAL) && (pdata.flags & AT24_FLAG_MAC)) {
+ if ((flags & AT24_FLAG_SERIAL) && (flags & AT24_FLAG_MAC)) {
dev_err(dev,
"invalid device data - cannot have both AT24_FLAG_SERIAL & AT24_FLAG_MAC.");
return -EINVAL;
}
regmap_config.val_bits = 8;
- regmap_config.reg_bits = (pdata.flags & AT24_FLAG_ADDR16) ? 16 : 8;
+ regmap_config.reg_bits = (flags & AT24_FLAG_ADDR16) ? 16 : 8;
regmap_config.disable_locking = true;
regmap = devm_regmap_init_i2c(client, &regmap_config);
@@ -675,11 +670,11 @@ static int at24_probe(struct i2c_client *client)
return -ENOMEM;
mutex_init(&at24->lock);
- at24->byte_len = pdata.byte_len;
- at24->page_size = pdata.page_size;
- at24->flags = pdata.flags;
+ at24->byte_len = byte_len;
+ at24->page_size = page_size;
+ at24->flags = flags;
at24->num_addresses = num_addresses;
- at24->offset_adj = at24_get_offset_adj(pdata.flags, pdata.byte_len);
+ at24->offset_adj = at24_get_offset_adj(flags, byte_len);
at24->client[0].client = client;
at24->client[0].regmap = regmap;
@@ -687,10 +682,10 @@ static int at24_probe(struct i2c_client *client)
if (IS_ERR(at24->wp_gpio))
return PTR_ERR(at24->wp_gpio);
- writable = !(pdata.flags & AT24_FLAG_READONLY);
+ writable = !(flags & AT24_FLAG_READONLY);
if (writable) {
at24->write_max = min_t(unsigned int,
- pdata.page_size, at24_io_limit);
+ page_size, at24_io_limit);
if (!i2c_fn_i2c && at24->write_max > I2C_SMBUS_BLOCK_MAX)
at24->write_max = I2C_SMBUS_BLOCK_MAX;
}
@@ -733,7 +728,7 @@ static int at24_probe(struct i2c_client *client)
nvmem_config.priv = at24;
nvmem_config.stride = 1;
nvmem_config.word_size = 1;
- nvmem_config.size = pdata.byte_len;
+ nvmem_config.size = byte_len;
at24->nvmem = devm_nvmem_register(dev, &nvmem_config);
if (IS_ERR(at24->nvmem)) {
@@ -742,13 +737,9 @@ static int at24_probe(struct i2c_client *client)
}
dev_info(dev, "%u byte %s EEPROM, %s, %u bytes/write\n",
- pdata.byte_len, client->name,
+ byte_len, client->name,
writable ? "writable" : "read-only", at24->write_max);
- /* export data to kernel code */
- if (pdata.setup)
- pdata.setup(at24->nvmem, pdata.context);
-
return 0;
err_clients:
diff --git a/drivers/misc/habanalabs/command_submission.c b/drivers/misc/habanalabs/command_submission.c
index 3525236ed8d9..19c84214a7ea 100644
--- a/drivers/misc/habanalabs/command_submission.c
+++ b/drivers/misc/habanalabs/command_submission.c
@@ -179,6 +179,12 @@ static void cs_do_release(struct kref *ref)
/* We also need to update CI for internal queues */
if (cs->submitted) {
+ int cs_cnt = atomic_dec_return(&hdev->cs_active_cnt);
+
+ WARN_ONCE((cs_cnt < 0),
+ "hl%d: error in CS active cnt %d\n",
+ hdev->id, cs_cnt);
+
hl_int_hw_queue_update_ci(cs);
spin_lock(&hdev->hw_queues_mirror_lock);
diff --git a/drivers/misc/habanalabs/debugfs.c b/drivers/misc/habanalabs/debugfs.c
index a53c12aff6ad..974a87789bd8 100644
--- a/drivers/misc/habanalabs/debugfs.c
+++ b/drivers/misc/habanalabs/debugfs.c
@@ -232,6 +232,7 @@ static int vm_show(struct seq_file *s, void *data)
struct hl_vm_phys_pg_pack *phys_pg_pack = NULL;
enum vm_type_t *vm_type;
bool once = true;
+ u64 j;
int i;
if (!dev_entry->hdev->mmu_enable)
@@ -260,7 +261,7 @@ static int vm_show(struct seq_file *s, void *data)
} else {
phys_pg_pack = hnode->ptr;
seq_printf(s,
- " 0x%-14llx %-10u %-4u\n",
+ " 0x%-14llx %-10llu %-4u\n",
hnode->vaddr, phys_pg_pack->total_size,
phys_pg_pack->handle);
}
@@ -282,9 +283,9 @@ static int vm_show(struct seq_file *s, void *data)
phys_pg_pack->page_size);
seq_puts(s, " physical address\n");
seq_puts(s, "---------------------\n");
- for (i = 0 ; i < phys_pg_pack->npages ; i++) {
+ for (j = 0 ; j < phys_pg_pack->npages ; j++) {
seq_printf(s, " 0x%-14llx\n",
- phys_pg_pack->pages[i]);
+ phys_pg_pack->pages[j]);
}
}
spin_unlock(&vm->idr_lock);
diff --git a/drivers/misc/habanalabs/device.c b/drivers/misc/habanalabs/device.c
index de46aa6ed154..77d51be66c7e 100644
--- a/drivers/misc/habanalabs/device.c
+++ b/drivers/misc/habanalabs/device.c
@@ -11,6 +11,8 @@
#include <linux/sched/signal.h>
#include <linux/hwmon.h>
+#define HL_PLDM_PENDING_RESET_PER_SEC (HL_PENDING_RESET_PER_SEC * 10)
+
bool hl_device_disabled_or_in_reset(struct hl_device *hdev)
{
if ((hdev->disabled) || (atomic_read(&hdev->in_reset)))
@@ -216,6 +218,7 @@ static int device_early_init(struct hl_device *hdev)
spin_lock_init(&hdev->hw_queues_mirror_lock);
atomic_set(&hdev->in_reset, 0);
atomic_set(&hdev->fd_open_cnt, 0);
+ atomic_set(&hdev->cs_active_cnt, 0);
return 0;
@@ -413,6 +416,27 @@ int hl_device_suspend(struct hl_device *hdev)
pci_save_state(hdev->pdev);
+ /* Block future CS/VM/JOB completion operations */
+ rc = atomic_cmpxchg(&hdev->in_reset, 0, 1);
+ if (rc) {
+ dev_err(hdev->dev, "Can't suspend while in reset\n");
+ return -EIO;
+ }
+
+ /* This blocks all other stuff that is not blocked by in_reset */
+ hdev->disabled = true;
+
+ /*
+ * Flush anyone that is inside the critical section of enqueue
+ * jobs to the H/W
+ */
+ hdev->asic_funcs->hw_queues_lock(hdev);
+ hdev->asic_funcs->hw_queues_unlock(hdev);
+
+ /* Flush processes that are sending message to CPU */
+ mutex_lock(&hdev->send_cpu_message_lock);
+ mutex_unlock(&hdev->send_cpu_message_lock);
+
rc = hdev->asic_funcs->suspend(hdev);
if (rc)
dev_err(hdev->dev,
@@ -440,21 +464,38 @@ int hl_device_resume(struct hl_device *hdev)
pci_set_power_state(hdev->pdev, PCI_D0);
pci_restore_state(hdev->pdev);
- rc = pci_enable_device(hdev->pdev);
+ rc = pci_enable_device_mem(hdev->pdev);
if (rc) {
dev_err(hdev->dev,
"Failed to enable PCI device in resume\n");
return rc;
}
+ pci_set_master(hdev->pdev);
+
rc = hdev->asic_funcs->resume(hdev);
if (rc) {
- dev_err(hdev->dev,
- "Failed to enable PCI access from device CPU\n");
- return rc;
+ dev_err(hdev->dev, "Failed to resume device after suspend\n");
+ goto disable_device;
+ }
+
+
+ hdev->disabled = false;
+ atomic_set(&hdev->in_reset, 0);
+
+ rc = hl_device_reset(hdev, true, false);
+ if (rc) {
+ dev_err(hdev->dev, "Failed to reset device during resume\n");
+ goto disable_device;
}
return 0;
+
+disable_device:
+ pci_clear_master(hdev->pdev);
+ pci_disable_device(hdev->pdev);
+
+ return rc;
}
static void hl_device_hard_reset_pending(struct work_struct *work)
@@ -462,9 +503,16 @@ static void hl_device_hard_reset_pending(struct work_struct *work)
struct hl_device_reset_work *device_reset_work =
container_of(work, struct hl_device_reset_work, reset_work);
struct hl_device *hdev = device_reset_work->hdev;
- u16 pending_cnt = HL_PENDING_RESET_PER_SEC;
+ u16 pending_total, pending_cnt;
struct task_struct *task = NULL;
+ if (hdev->pldm)
+ pending_total = HL_PLDM_PENDING_RESET_PER_SEC;
+ else
+ pending_total = HL_PENDING_RESET_PER_SEC;
+
+ pending_cnt = pending_total;
+
/* Flush all processes that are inside hl_open */
mutex_lock(&hdev->fd_open_cnt_lock);
@@ -489,6 +537,19 @@ static void hl_device_hard_reset_pending(struct work_struct *work)
}
}
+ pending_cnt = pending_total;
+
+ while ((atomic_read(&hdev->fd_open_cnt)) && (pending_cnt)) {
+
+ pending_cnt--;
+
+ ssleep(1);
+ }
+
+ if (atomic_read(&hdev->fd_open_cnt))
+ dev_crit(hdev->dev,
+ "Going to hard reset with open user contexts\n");
+
mutex_unlock(&hdev->fd_open_cnt_lock);
hl_device_reset(hdev, true, true);
diff --git a/drivers/misc/habanalabs/goya/goya.c b/drivers/misc/habanalabs/goya/goya.c
index 238dd57c541b..ea979ebd62fb 100644
--- a/drivers/misc/habanalabs/goya/goya.c
+++ b/drivers/misc/habanalabs/goya/goya.c
@@ -1201,15 +1201,6 @@ static int goya_stop_external_queues(struct hl_device *hdev)
return retval;
}
-static void goya_resume_external_queues(struct hl_device *hdev)
-{
- WREG32(mmDMA_QM_0_GLBL_CFG1, 0);
- WREG32(mmDMA_QM_1_GLBL_CFG1, 0);
- WREG32(mmDMA_QM_2_GLBL_CFG1, 0);
- WREG32(mmDMA_QM_3_GLBL_CFG1, 0);
- WREG32(mmDMA_QM_4_GLBL_CFG1, 0);
-}
-
/*
* goya_init_cpu_queues - Initialize PQ/CQ/EQ of CPU
*
@@ -2178,36 +2169,6 @@ static int goya_stop_internal_queues(struct hl_device *hdev)
return retval;
}
-static void goya_resume_internal_queues(struct hl_device *hdev)
-{
- WREG32(mmMME_QM_GLBL_CFG1, 0);
- WREG32(mmMME_CMDQ_GLBL_CFG1, 0);
-
- WREG32(mmTPC0_QM_GLBL_CFG1, 0);
- WREG32(mmTPC0_CMDQ_GLBL_CFG1, 0);
-
- WREG32(mmTPC1_QM_GLBL_CFG1, 0);
- WREG32(mmTPC1_CMDQ_GLBL_CFG1, 0);
-
- WREG32(mmTPC2_QM_GLBL_CFG1, 0);
- WREG32(mmTPC2_CMDQ_GLBL_CFG1, 0);
-
- WREG32(mmTPC3_QM_GLBL_CFG1, 0);
- WREG32(mmTPC3_CMDQ_GLBL_CFG1, 0);
-
- WREG32(mmTPC4_QM_GLBL_CFG1, 0);
- WREG32(mmTPC4_CMDQ_GLBL_CFG1, 0);
-
- WREG32(mmTPC5_QM_GLBL_CFG1, 0);
- WREG32(mmTPC5_CMDQ_GLBL_CFG1, 0);
-
- WREG32(mmTPC6_QM_GLBL_CFG1, 0);
- WREG32(mmTPC6_CMDQ_GLBL_CFG1, 0);
-
- WREG32(mmTPC7_QM_GLBL_CFG1, 0);
- WREG32(mmTPC7_CMDQ_GLBL_CFG1, 0);
-}
-
static void goya_dma_stall(struct hl_device *hdev)
{
WREG32(mmDMA_QM_0_GLBL_CFG1, 1 << DMA_QM_0_GLBL_CFG1_DMA_STOP_SHIFT);
@@ -2905,20 +2866,6 @@ int goya_suspend(struct hl_device *hdev)
{
int rc;
- rc = goya_stop_internal_queues(hdev);
-
- if (rc) {
- dev_err(hdev->dev, "failed to stop internal queues\n");
- return rc;
- }
-
- rc = goya_stop_external_queues(hdev);
-
- if (rc) {
- dev_err(hdev->dev, "failed to stop external queues\n");
- return rc;
- }
-
rc = goya_send_pci_access_msg(hdev, ARMCP_PACKET_DISABLE_PCI_ACCESS);
if (rc)
dev_err(hdev->dev, "Failed to disable PCI access from CPU\n");
@@ -2928,15 +2875,7 @@ int goya_suspend(struct hl_device *hdev)
int goya_resume(struct hl_device *hdev)
{
- int rc;
-
- goya_resume_external_queues(hdev);
- goya_resume_internal_queues(hdev);
-
- rc = goya_send_pci_access_msg(hdev, ARMCP_PACKET_ENABLE_PCI_ACCESS);
- if (rc)
- dev_err(hdev->dev, "Failed to enable PCI access from CPU\n");
- return rc;
+ return goya_init_iatu(hdev);
}
static int goya_cb_mmap(struct hl_device *hdev, struct vm_area_struct *vma,
@@ -3070,7 +3009,7 @@ void *goya_get_int_queue_base(struct hl_device *hdev, u32 queue_id,
*dma_handle = hdev->asic_prop.sram_base_address;
- base = hdev->pcie_bar[SRAM_CFG_BAR_ID];
+ base = (void *) hdev->pcie_bar[SRAM_CFG_BAR_ID];
switch (queue_id) {
case GOYA_QUEUE_ID_MME:
diff --git a/drivers/misc/habanalabs/habanalabs.h b/drivers/misc/habanalabs/habanalabs.h
index a7c95e9f9b9a..a8ee52c880cd 100644
--- a/drivers/misc/habanalabs/habanalabs.h
+++ b/drivers/misc/habanalabs/habanalabs.h
@@ -793,11 +793,11 @@ struct hl_vm_hash_node {
* struct hl_vm_phys_pg_pack - physical page pack.
* @vm_type: describes the type of the virtual area descriptor.
* @pages: the physical page array.
+ * @npages: num physical pages in the pack.
+ * @total_size: total size of all the pages in this list.
* @mapping_cnt: number of shared mappings.
* @asid: the context related to this list.
- * @npages: num physical pages in the pack.
* @page_size: size of each page in the pack.
- * @total_size: total size of all the pages in this list.
* @flags: HL_MEM_* flags related to this list.
* @handle: the provided handle related to this list.
* @offset: offset from the first page.
@@ -807,11 +807,11 @@ struct hl_vm_hash_node {
struct hl_vm_phys_pg_pack {
enum vm_type_t vm_type; /* must be first */
u64 *pages;
+ u64 npages;
+ u64 total_size;
atomic_t mapping_cnt;
u32 asid;
- u32 npages;
u32 page_size;
- u32 total_size;
u32 flags;
u32 handle;
u32 offset;
@@ -1056,13 +1056,15 @@ struct hl_device_reset_work {
* @cb_pool_lock: protects the CB pool.
* @user_ctx: current user context executing.
* @dram_used_mem: current DRAM memory consumption.
- * @in_reset: is device in reset flow.
- * @curr_pll_profile: current PLL profile.
- * @fd_open_cnt: number of open user processes.
* @timeout_jiffies: device CS timeout value.
* @max_power: the max power of the device, as configured by the sysadmin. This
* value is saved so in case of hard-reset, KMD will restore this
* value and update the F/W after the re-initialization
+ * @in_reset: is device in reset flow.
+ * @curr_pll_profile: current PLL profile.
+ * @fd_open_cnt: number of open user processes.
+ * @cs_active_cnt: number of active command submissions on this device (active
+ * means already in H/W queues)
* @major: habanalabs KMD major.
* @high_pll: high PLL profile frequency.
* @soft_reset_cnt: number of soft reset since KMD loading.
@@ -1128,11 +1130,12 @@ struct hl_device {
struct hl_ctx *user_ctx;
atomic64_t dram_used_mem;
+ u64 timeout_jiffies;
+ u64 max_power;
atomic_t in_reset;
atomic_t curr_pll_profile;
atomic_t fd_open_cnt;
- u64 timeout_jiffies;
- u64 max_power;
+ atomic_t cs_active_cnt;
u32 major;
u32 high_pll;
u32 soft_reset_cnt;
diff --git a/drivers/misc/habanalabs/hw_queue.c b/drivers/misc/habanalabs/hw_queue.c
index 67bece26417c..ef3bb6951360 100644
--- a/drivers/misc/habanalabs/hw_queue.c
+++ b/drivers/misc/habanalabs/hw_queue.c
@@ -370,12 +370,13 @@ int hl_hw_queue_schedule_cs(struct hl_cs *cs)
spin_unlock(&hdev->hw_queues_mirror_lock);
}
- list_for_each_entry_safe(job, tmp, &cs->job_list, cs_node) {
+ atomic_inc(&hdev->cs_active_cnt);
+
+ list_for_each_entry_safe(job, tmp, &cs->job_list, cs_node)
if (job->ext_queue)
ext_hw_queue_schedule_job(job);
else
int_hw_queue_schedule_job(job);
- }
cs->submitted = true;
diff --git a/drivers/misc/habanalabs/memory.c b/drivers/misc/habanalabs/memory.c
index 3a12fd1a5274..ce1fda40a8b8 100644
--- a/drivers/misc/habanalabs/memory.c
+++ b/drivers/misc/habanalabs/memory.c
@@ -56,9 +56,9 @@ static int alloc_device_memory(struct hl_ctx *ctx, struct hl_mem_in *args,
struct hl_device *hdev = ctx->hdev;
struct hl_vm *vm = &hdev->vm;
struct hl_vm_phys_pg_pack *phys_pg_pack;
- u64 paddr = 0;
- u32 total_size, num_pgs, num_curr_pgs, page_size, page_shift;
- int handle, rc, i;
+ u64 paddr = 0, total_size, num_pgs, i;
+ u32 num_curr_pgs, page_size, page_shift;
+ int handle, rc;
bool contiguous;
num_curr_pgs = 0;
@@ -73,7 +73,7 @@ static int alloc_device_memory(struct hl_ctx *ctx, struct hl_mem_in *args,
paddr = (u64) gen_pool_alloc(vm->dram_pg_pool, total_size);
if (!paddr) {
dev_err(hdev->dev,
- "failed to allocate %u huge contiguous pages\n",
+ "failed to allocate %llu huge contiguous pages\n",
num_pgs);
return -ENOMEM;
}
@@ -93,7 +93,7 @@ static int alloc_device_memory(struct hl_ctx *ctx, struct hl_mem_in *args,
phys_pg_pack->flags = args->flags;
phys_pg_pack->contiguous = contiguous;
- phys_pg_pack->pages = kcalloc(num_pgs, sizeof(u64), GFP_KERNEL);
+ phys_pg_pack->pages = kvmalloc_array(num_pgs, sizeof(u64), GFP_KERNEL);
if (!phys_pg_pack->pages) {
rc = -ENOMEM;
goto pages_arr_err;
@@ -148,7 +148,7 @@ page_err:
gen_pool_free(vm->dram_pg_pool, phys_pg_pack->pages[i],
page_size);
- kfree(phys_pg_pack->pages);
+ kvfree(phys_pg_pack->pages);
pages_arr_err:
kfree(phys_pg_pack);
pages_pack_err:
@@ -267,7 +267,7 @@ static void free_phys_pg_pack(struct hl_device *hdev,
struct hl_vm_phys_pg_pack *phys_pg_pack)
{
struct hl_vm *vm = &hdev->vm;
- int i;
+ u64 i;
if (!phys_pg_pack->created_from_userptr) {
if (phys_pg_pack->contiguous) {
@@ -288,7 +288,7 @@ static void free_phys_pg_pack(struct hl_device *hdev,
}
}
- kfree(phys_pg_pack->pages);
+ kvfree(phys_pg_pack->pages);
kfree(phys_pg_pack);
}
@@ -519,7 +519,7 @@ static inline int add_va_block(struct hl_device *hdev,
* - Return the start address of the virtual block
*/
static u64 get_va_block(struct hl_device *hdev,
- struct hl_va_range *va_range, u32 size, u64 hint_addr,
+ struct hl_va_range *va_range, u64 size, u64 hint_addr,
bool is_userptr)
{
struct hl_vm_va_block *va_block, *new_va_block = NULL;
@@ -577,7 +577,8 @@ static u64 get_va_block(struct hl_device *hdev,
}
if (!new_va_block) {
- dev_err(hdev->dev, "no available va block for size %u\n", size);
+ dev_err(hdev->dev, "no available va block for size %llu\n",
+ size);
goto out;
}
@@ -648,8 +649,8 @@ static int init_phys_pg_pack_from_userptr(struct hl_ctx *ctx,
struct hl_vm_phys_pg_pack *phys_pg_pack;
struct scatterlist *sg;
dma_addr_t dma_addr;
- u64 page_mask;
- u32 npages, total_npages, page_size = PAGE_SIZE;
+ u64 page_mask, total_npages;
+ u32 npages, page_size = PAGE_SIZE;
bool first = true, is_huge_page_opt = true;
int rc, i, j;
@@ -691,7 +692,8 @@ static int init_phys_pg_pack_from_userptr(struct hl_ctx *ctx,
page_mask = ~(((u64) page_size) - 1);
- phys_pg_pack->pages = kcalloc(total_npages, sizeof(u64), GFP_KERNEL);
+ phys_pg_pack->pages = kvmalloc_array(total_npages, sizeof(u64),
+ GFP_KERNEL);
if (!phys_pg_pack->pages) {
rc = -ENOMEM;
goto page_pack_arr_mem_err;
@@ -750,9 +752,9 @@ static int map_phys_page_pack(struct hl_ctx *ctx, u64 vaddr,
struct hl_vm_phys_pg_pack *phys_pg_pack)
{
struct hl_device *hdev = ctx->hdev;
- u64 next_vaddr = vaddr, paddr;
+ u64 next_vaddr = vaddr, paddr, mapped_pg_cnt = 0, i;
u32 page_size = phys_pg_pack->page_size;
- int i, rc = 0, mapped_pg_cnt = 0;
+ int rc = 0;
for (i = 0 ; i < phys_pg_pack->npages ; i++) {
paddr = phys_pg_pack->pages[i];
@@ -764,7 +766,7 @@ static int map_phys_page_pack(struct hl_ctx *ctx, u64 vaddr,
rc = hl_mmu_map(ctx, next_vaddr, paddr, page_size);
if (rc) {
dev_err(hdev->dev,
- "map failed for handle %u, npages: %d, mapped: %d",
+ "map failed for handle %u, npages: %llu, mapped: %llu",
phys_pg_pack->handle, phys_pg_pack->npages,
mapped_pg_cnt);
goto err;
@@ -985,10 +987,10 @@ static int unmap_device_va(struct hl_ctx *ctx, u64 vaddr)
struct hl_vm_hash_node *hnode = NULL;
struct hl_userptr *userptr = NULL;
enum vm_type_t *vm_type;
- u64 next_vaddr;
+ u64 next_vaddr, i;
u32 page_size;
bool is_userptr;
- int i, rc;
+ int rc;
/* protect from double entrance */
mutex_lock(&ctx->mem_hash_lock);
diff --git a/drivers/misc/habanalabs/mmu.c b/drivers/misc/habanalabs/mmu.c
index 2f2e99cb2743..3a5a2cec8305 100644
--- a/drivers/misc/habanalabs/mmu.c
+++ b/drivers/misc/habanalabs/mmu.c
@@ -832,7 +832,7 @@ err:
int hl_mmu_map(struct hl_ctx *ctx, u64 virt_addr, u64 phys_addr, u32 page_size)
{
struct hl_device *hdev = ctx->hdev;
- u64 real_virt_addr;
+ u64 real_virt_addr, real_phys_addr;
u32 real_page_size, npages;
int i, rc, mapped_cnt = 0;
@@ -857,14 +857,16 @@ int hl_mmu_map(struct hl_ctx *ctx, u64 virt_addr, u64 phys_addr, u32 page_size)
npages = page_size / real_page_size;
real_virt_addr = virt_addr;
+ real_phys_addr = phys_addr;
for (i = 0 ; i < npages ; i++) {
- rc = _hl_mmu_map(ctx, real_virt_addr, phys_addr,
+ rc = _hl_mmu_map(ctx, real_virt_addr, real_phys_addr,
real_page_size);
if (rc)
goto err;
real_virt_addr += real_page_size;
+ real_phys_addr += real_page_size;
mapped_cnt++;
}
diff --git a/drivers/misc/mic/scif/scif_rma.c b/drivers/misc/mic/scif/scif_rma.c
index f62216628fa6..0fb9b440dc70 100644
--- a/drivers/misc/mic/scif/scif_rma.c
+++ b/drivers/misc/mic/scif/scif_rma.c
@@ -272,21 +272,12 @@ static inline void __scif_release_mm(struct mm_struct *mm)
static inline int
__scif_dec_pinned_vm_lock(struct mm_struct *mm,
- int nr_pages, bool try_lock)
+ int nr_pages)
{
if (!mm || !nr_pages || !scif_ulimit_check)
return 0;
- if (try_lock) {
- if (!down_write_trylock(&mm->mmap_sem)) {
- dev_err(scif_info.mdev.this_device,
- "%s %d err\n", __func__, __LINE__);
- return -1;
- }
- } else {
- down_write(&mm->mmap_sem);
- }
- mm->pinned_vm -= nr_pages;
- up_write(&mm->mmap_sem);
+
+ atomic64_sub(nr_pages, &mm->pinned_vm);
return 0;
}
@@ -298,16 +289,16 @@ static inline int __scif_check_inc_pinned_vm(struct mm_struct *mm,
if (!mm || !nr_pages || !scif_ulimit_check)
return 0;
- locked = nr_pages;
- locked += mm->pinned_vm;
lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT;
+ locked = atomic64_add_return(nr_pages, &mm->pinned_vm);
+
if ((locked > lock_limit) && !capable(CAP_IPC_LOCK)) {
+ atomic64_sub(nr_pages, &mm->pinned_vm);
dev_err(scif_info.mdev.this_device,
"locked(%lu) > lock_limit(%lu)\n",
locked, lock_limit);
return -ENOMEM;
}
- mm->pinned_vm = locked;
return 0;
}
@@ -326,7 +317,7 @@ int scif_destroy_window(struct scif_endpt *ep, struct scif_window *window)
might_sleep();
if (!window->temp && window->mm) {
- __scif_dec_pinned_vm_lock(window->mm, window->nr_pages, 0);
+ __scif_dec_pinned_vm_lock(window->mm, window->nr_pages);
__scif_release_mm(window->mm);
window->mm = NULL;
}
@@ -737,7 +728,7 @@ done:
ep->rma_info.dma_chan);
} else {
if (!__scif_dec_pinned_vm_lock(window->mm,
- window->nr_pages, 1)) {
+ window->nr_pages)) {
__scif_release_mm(window->mm);
window->mm = NULL;
}
@@ -1385,28 +1376,23 @@ int __scif_pin_pages(void *addr, size_t len, int *out_prot,
prot |= SCIF_PROT_WRITE;
retry:
mm = current->mm;
- down_write(&mm->mmap_sem);
if (ulimit) {
err = __scif_check_inc_pinned_vm(mm, nr_pages);
if (err) {
- up_write(&mm->mmap_sem);
pinned_pages->nr_pages = 0;
goto error_unmap;
}
}
- pinned_pages->nr_pages = get_user_pages(
+ pinned_pages->nr_pages = get_user_pages_fast(
(u64)addr,
nr_pages,
(prot & SCIF_PROT_WRITE) ? FOLL_WRITE : 0,
- pinned_pages->pages,
- NULL);
- up_write(&mm->mmap_sem);
+ pinned_pages->pages);
if (nr_pages != pinned_pages->nr_pages) {
if (try_upgrade) {
if (ulimit)
- __scif_dec_pinned_vm_lock(mm,
- nr_pages, 0);
+ __scif_dec_pinned_vm_lock(mm, nr_pages);
/* Roll back any pinned pages */
for (i = 0; i < pinned_pages->nr_pages; i++) {
if (pinned_pages->pages[i])
@@ -1433,7 +1419,7 @@ retry:
return err;
dec_pinned:
if (ulimit)
- __scif_dec_pinned_vm_lock(mm, nr_pages, 0);
+ __scif_dec_pinned_vm_lock(mm, nr_pages);
/* Something went wrong! Rollback */
error_unmap:
pinned_pages->nr_pages = nr_pages;
diff --git a/drivers/misc/pch_phub.c b/drivers/misc/pch_phub.c
index 540845651b8c..309703e9c42e 100644
--- a/drivers/misc/pch_phub.c
+++ b/drivers/misc/pch_phub.c
@@ -64,7 +64,6 @@
#define CLKCFG_UARTCLKSEL (1 << 18)
/* Macros for ML7213 */
-#define PCI_VENDOR_ID_ROHM 0x10db
#define PCI_DEVICE_ID_ROHM_ML7213_PHUB 0x801A
/* Macros for ML7223 */
diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c
index 896e2df9400f..29582fe57151 100644
--- a/drivers/misc/pci_endpoint_test.c
+++ b/drivers/misc/pci_endpoint_test.c
@@ -788,6 +788,7 @@ static void pci_endpoint_test_remove(struct pci_dev *pdev)
static const struct pci_device_id pci_endpoint_test_tbl[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_DRA74x) },
{ PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_DRA72x) },
+ { PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, 0x81c0) },
{ PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS, 0xedda) },
{ }
};
diff --git a/drivers/mmc/core/queue.c b/drivers/mmc/core/queue.c
index 15a45ec6518d..7c364a9c4eeb 100644
--- a/drivers/mmc/core/queue.c
+++ b/drivers/mmc/core/queue.c
@@ -417,8 +417,7 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card)
else
mq->tag_set.queue_depth = MMC_QUEUE_DEPTH;
mq->tag_set.numa_node = NUMA_NO_NODE;
- mq->tag_set.flags = BLK_MQ_F_SHOULD_MERGE | BLK_MQ_F_SG_MERGE |
- BLK_MQ_F_BLOCKING;
+ mq->tag_set.flags = BLK_MQ_F_SHOULD_MERGE | BLK_MQ_F_BLOCKING;
mq->tag_set.nr_hw_queues = 1;
mq->tag_set.cmd_size = sizeof(struct mmc_queue_req);
mq->tag_set.driver_data = mq;
diff --git a/drivers/mmc/host/alcor.c b/drivers/mmc/host/alcor.c
index c712b7deb3a9..82a97866e0cf 100644
--- a/drivers/mmc/host/alcor.c
+++ b/drivers/mmc/host/alcor.c
@@ -1044,14 +1044,27 @@ static void alcor_init_mmc(struct alcor_sdmmc_host *host)
mmc->caps2 = MMC_CAP2_NO_SDIO;
mmc->ops = &alcor_sdc_ops;
- /* Hardware cannot do scatter lists */
+ /* The hardware does DMA data transfer of 4096 bytes to/from a single
+ * buffer address. Scatterlists are not supported, but upon DMA
+ * completion (signalled via IRQ), the original vendor driver does
+ * then immediately set up another DMA transfer of the next 4096
+ * bytes.
+ *
+ * This means that we need to handle the I/O in 4096 byte chunks.
+ * Lacking a way to limit the sglist entries to 4096 bytes, we instead
+ * impose that only one segment is provided, with maximum size 4096,
+ * which also happens to be the minimum size. This means that the
+ * single-entry sglist handled by this driver can be handed directly
+ * to the hardware, nice and simple.
+ *
+ * Unfortunately though, that means we only do 4096 bytes I/O per
+ * MMC command. A future improvement would be to make the driver
+ * accept sg lists and entries of any size, and simply iterate
+ * through them 4096 bytes at a time.
+ */
mmc->max_segs = AU6601_MAX_DMA_SEGMENTS;
mmc->max_seg_size = AU6601_MAX_DMA_BLOCK_SIZE;
-
- mmc->max_blk_size = mmc->max_seg_size;
- mmc->max_blk_count = mmc->max_segs;
-
- mmc->max_req_size = mmc->max_seg_size * mmc->max_segs;
+ mmc->max_req_size = mmc->max_seg_size;
}
static int alcor_pci_sdmmc_drv_probe(struct platform_device *pdev)
diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c
index 49e0daf2ef5e..f37003df1e01 100644
--- a/drivers/mmc/host/davinci_mmc.c
+++ b/drivers/mmc/host/davinci_mmc.c
@@ -1117,7 +1117,7 @@ static inline void mmc_davinci_cpufreq_deregister(struct mmc_davinci_host *host)
{
}
#endif
-static void __init init_mmcsd_host(struct mmc_davinci_host *host)
+static void init_mmcsd_host(struct mmc_davinci_host *host)
{
mmc_davinci_reset_ctrl(host, 1);
diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c
index d54612257b06..45f7b9b53d48 100644
--- a/drivers/mmc/host/mxcmmc.c
+++ b/drivers/mmc/host/mxcmmc.c
@@ -290,11 +290,8 @@ static void mxcmci_swap_buffers(struct mmc_data *data)
struct scatterlist *sg;
int i;
- for_each_sg(data->sg, sg, data->sg_len, i) {
- void *buf = kmap_atomic(sg_page(sg) + sg->offset);
- buffer_swap32(buf, sg->length);
- kunmap_atomic(buf);
- }
+ for_each_sg(data->sg, sg, data->sg_len, i)
+ buffer_swap32(sg_virt(sg), sg->length);
}
#else
static inline void mxcmci_swap_buffers(struct mmc_data *data) {}
@@ -611,7 +608,6 @@ static int mxcmci_transfer_data(struct mxcmci_host *host)
{
struct mmc_data *data = host->req->data;
struct scatterlist *sg;
- void *buf;
int stat, i;
host->data = data;
@@ -619,18 +615,14 @@ static int mxcmci_transfer_data(struct mxcmci_host *host)
if (data->flags & MMC_DATA_READ) {
for_each_sg(data->sg, sg, data->sg_len, i) {
- buf = kmap_atomic(sg_page(sg) + sg->offset);
- stat = mxcmci_pull(host, buf, sg->length);
- kunmap(buf);
+ stat = mxcmci_pull(host, sg_virt(sg), sg->length);
if (stat)
return stat;
host->datasize += sg->length;
}
} else {
for_each_sg(data->sg, sg, data->sg_len, i) {
- buf = kmap_atomic(sg_page(sg) + sg->offset);
- stat = mxcmci_push(host, buf, sg->length);
- kunmap(buf);
+ stat = mxcmci_push(host, sg_virt(sg), sg->length);
if (stat)
return stat;
host->datasize += sg->length;
diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c
index c907bf502a12..c1d3f0e38921 100644
--- a/drivers/mmc/host/pxamci.c
+++ b/drivers/mmc/host/pxamci.c
@@ -162,7 +162,7 @@ static void pxamci_dma_irq(void *param);
static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data)
{
struct dma_async_tx_descriptor *tx;
- enum dma_data_direction direction;
+ enum dma_transfer_direction direction;
struct dma_slave_config config;
struct dma_chan *chan;
unsigned int nob = data->blocks;
diff --git a/drivers/mmc/host/renesas_sdhi_core.c b/drivers/mmc/host/renesas_sdhi_core.c
index 71e13844df6c..8742e27e4e8b 100644
--- a/drivers/mmc/host/renesas_sdhi_core.c
+++ b/drivers/mmc/host/renesas_sdhi_core.c
@@ -641,6 +641,7 @@ int renesas_sdhi_probe(struct platform_device *pdev,
struct renesas_sdhi *priv;
struct resource *res;
int irq, ret, i;
+ u16 ver;
of_data = of_device_get_match_data(&pdev->dev);
@@ -773,12 +774,17 @@ int renesas_sdhi_probe(struct platform_device *pdev,
if (ret)
goto efree;
+ ver = sd_ctrl_read16(host, CTL_VERSION);
+ /* GEN2_SDR104 is first known SDHI to use 32bit block count */
+ if (ver < SDHI_VER_GEN2_SDR104 && mmc_data->max_blk_count > U16_MAX)
+ mmc_data->max_blk_count = U16_MAX;
+
ret = tmio_mmc_host_probe(host);
if (ret < 0)
goto edisclk;
/* One Gen2 SDHI incarnation does NOT have a CBSY bit */
- if (sd_ctrl_read16(host, CTL_VERSION) == SDHI_VER_GEN2_SDR50)
+ if (ver == SDHI_VER_GEN2_SDR50)
mmc_data->flags &= ~TMIO_MMC_HAVE_CBSY;
/* Enable tuning iff we have an SCC and a supported mode */
diff --git a/drivers/mmc/host/sdhci-omap.c b/drivers/mmc/host/sdhci-omap.c
index b1a66ca3821a..5bbed477c9b1 100644
--- a/drivers/mmc/host/sdhci-omap.c
+++ b/drivers/mmc/host/sdhci-omap.c
@@ -1056,6 +1056,9 @@ static int sdhci_omap_probe(struct platform_device *pdev)
mmc->f_max = 48000000;
}
+ if (!mmc_can_gpio_ro(mmc))
+ mmc->caps2 |= MMC_CAP2_NO_WRITE_PROTECT;
+
pltfm_host->clk = devm_clk_get(dev, "fck");
if (IS_ERR(pltfm_host->clk)) {
ret = PTR_ERR(pltfm_host->clk);
diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c
index 22547d7a84ea..947a8adbc799 100644
--- a/drivers/mtd/ubi/cdev.c
+++ b/drivers/mtd/ubi/cdev.c
@@ -974,6 +974,36 @@ static long ubi_cdev_ioctl(struct file *file, unsigned int cmd,
break;
}
+ /* Check a specific PEB for bitflips and scrub it if needed */
+ case UBI_IOCRPEB:
+ {
+ int pnum;
+
+ err = get_user(pnum, (__user int32_t *)argp);
+ if (err) {
+ err = -EFAULT;
+ break;
+ }
+
+ err = ubi_bitflip_check(ubi, pnum, 0);
+ break;
+ }
+
+ /* Force scrubbing for a specific PEB */
+ case UBI_IOCSPEB:
+ {
+ int pnum;
+
+ err = get_user(pnum, (__user int32_t *)argp);
+ if (err) {
+ err = -EFAULT;
+ break;
+ }
+
+ err = ubi_bitflip_check(ubi, pnum, 1);
+ break;
+ }
+
default:
err = -ENOTTY;
break;
diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h
index d47b9e436e67..a1b9e764d489 100644
--- a/drivers/mtd/ubi/ubi.h
+++ b/drivers/mtd/ubi/ubi.h
@@ -929,6 +929,7 @@ int ubi_wl_put_fm_peb(struct ubi_device *ubi, struct ubi_wl_entry *used_e,
int ubi_is_erase_work(struct ubi_work *wrk);
void ubi_refill_pools(struct ubi_device *ubi);
int ubi_ensure_anchor_pebs(struct ubi_device *ubi);
+int ubi_bitflip_check(struct ubi_device *ubi, int pnum, int force_scrub);
/* io.c */
int ubi_io_read(const struct ubi_device *ubi, void *buf, int pnum, int offset,
diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c
index 6f2ac865ff05..2709dc02fc24 100644
--- a/drivers/mtd/ubi/wl.c
+++ b/drivers/mtd/ubi/wl.c
@@ -278,6 +278,27 @@ static int in_wl_tree(struct ubi_wl_entry *e, struct rb_root *root)
}
/**
+ * in_pq - check if a wear-leveling entry is present in the protection queue.
+ * @ubi: UBI device description object
+ * @e: the wear-leveling entry to check
+ *
+ * This function returns non-zero if @e is in the protection queue and zero
+ * if it is not.
+ */
+static inline int in_pq(const struct ubi_device *ubi, struct ubi_wl_entry *e)
+{
+ struct ubi_wl_entry *p;
+ int i;
+
+ for (i = 0; i < UBI_PROT_QUEUE_LEN; ++i)
+ list_for_each_entry(p, &ubi->pq[i], u.list)
+ if (p == e)
+ return 1;
+
+ return 0;
+}
+
+/**
* prot_queue_add - add physical eraseblock to the protection queue.
* @ubi: UBI device description object
* @e: the physical eraseblock to add
@@ -1419,6 +1440,150 @@ int ubi_wl_flush(struct ubi_device *ubi, int vol_id, int lnum)
return err;
}
+static bool scrub_possible(struct ubi_device *ubi, struct ubi_wl_entry *e)
+{
+ if (in_wl_tree(e, &ubi->scrub))
+ return false;
+ else if (in_wl_tree(e, &ubi->erroneous))
+ return false;
+ else if (ubi->move_from == e)
+ return false;
+ else if (ubi->move_to == e)
+ return false;
+
+ return true;
+}
+
+/**
+ * ubi_bitflip_check - Check an eraseblock for bitflips and scrub it if needed.
+ * @ubi: UBI device description object
+ * @pnum: the physical eraseblock to schedule
+ * @force: dont't read the block, assume bitflips happened and take action.
+ *
+ * This function reads the given eraseblock and checks if bitflips occured.
+ * In case of bitflips, the eraseblock is scheduled for scrubbing.
+ * If scrubbing is forced with @force, the eraseblock is not read,
+ * but scheduled for scrubbing right away.
+ *
+ * Returns:
+ * %EINVAL, PEB is out of range
+ * %ENOENT, PEB is no longer used by UBI
+ * %EBUSY, PEB cannot be checked now or a check is currently running on it
+ * %EAGAIN, bit flips happened but scrubbing is currently not possible
+ * %EUCLEAN, bit flips happened and PEB is scheduled for scrubbing
+ * %0, no bit flips detected
+ */
+int ubi_bitflip_check(struct ubi_device *ubi, int pnum, int force)
+{
+ int err;
+ struct ubi_wl_entry *e;
+
+ if (pnum < 0 || pnum >= ubi->peb_count) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * Pause all parallel work, otherwise it can happen that the
+ * erase worker frees a wl entry under us.
+ */
+ down_write(&ubi->work_sem);
+
+ /*
+ * Make sure that the wl entry does not change state while
+ * inspecting it.
+ */
+ spin_lock(&ubi->wl_lock);
+ e = ubi->lookuptbl[pnum];
+ if (!e) {
+ spin_unlock(&ubi->wl_lock);
+ err = -ENOENT;
+ goto out_resume;
+ }
+
+ /*
+ * Does it make sense to check this PEB?
+ */
+ if (!scrub_possible(ubi, e)) {
+ spin_unlock(&ubi->wl_lock);
+ err = -EBUSY;
+ goto out_resume;
+ }
+ spin_unlock(&ubi->wl_lock);
+
+ if (!force) {
+ mutex_lock(&ubi->buf_mutex);
+ err = ubi_io_read(ubi, ubi->peb_buf, pnum, 0, ubi->peb_size);
+ mutex_unlock(&ubi->buf_mutex);
+ }
+
+ if (force || err == UBI_IO_BITFLIPS) {
+ /*
+ * Okay, bit flip happened, let's figure out what we can do.
+ */
+ spin_lock(&ubi->wl_lock);
+
+ /*
+ * Recheck. We released wl_lock, UBI might have killed the
+ * wl entry under us.
+ */
+ e = ubi->lookuptbl[pnum];
+ if (!e) {
+ spin_unlock(&ubi->wl_lock);
+ err = -ENOENT;
+ goto out_resume;
+ }
+
+ /*
+ * Need to re-check state
+ */
+ if (!scrub_possible(ubi, e)) {
+ spin_unlock(&ubi->wl_lock);
+ err = -EBUSY;
+ goto out_resume;
+ }
+
+ if (in_pq(ubi, e)) {
+ prot_queue_del(ubi, e->pnum);
+ wl_tree_add(e, &ubi->scrub);
+ spin_unlock(&ubi->wl_lock);
+
+ err = ensure_wear_leveling(ubi, 1);
+ } else if (in_wl_tree(e, &ubi->used)) {
+ rb_erase(&e->u.rb, &ubi->used);
+ wl_tree_add(e, &ubi->scrub);
+ spin_unlock(&ubi->wl_lock);
+
+ err = ensure_wear_leveling(ubi, 1);
+ } else if (in_wl_tree(e, &ubi->free)) {
+ rb_erase(&e->u.rb, &ubi->free);
+ ubi->free_count--;
+ spin_unlock(&ubi->wl_lock);
+
+ /*
+ * This PEB is empty we can schedule it for
+ * erasure right away. No wear leveling needed.
+ */
+ err = schedule_erase(ubi, e, UBI_UNKNOWN, UBI_UNKNOWN,
+ force ? 0 : 1, true);
+ } else {
+ spin_unlock(&ubi->wl_lock);
+ err = -EAGAIN;
+ }
+
+ if (!err && !force)
+ err = -EUCLEAN;
+ } else {
+ err = 0;
+ }
+
+out_resume:
+ up_write(&ubi->work_sem);
+out:
+
+ return err;
+}
+
/**
* tree_destroy - destroy an RB-tree.
* @ubi: UBI device description object
@@ -1848,16 +2013,11 @@ static int self_check_in_wl_tree(const struct ubi_device *ubi,
static int self_check_in_pq(const struct ubi_device *ubi,
struct ubi_wl_entry *e)
{
- struct ubi_wl_entry *p;
- int i;
-
if (!ubi_dbg_chk_gen(ubi))
return 0;
- for (i = 0; i < UBI_PROT_QUEUE_LEN; ++i)
- list_for_each_entry(p, &ubi->pq[i], u.list)
- if (p == e)
- return 0;
+ if (in_pq(ubi, e))
+ return 0;
ubi_err(ubi, "self-check failed for PEB %d, EC %d, Protect queue",
e->pnum, e->ec);
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 5e4ca082cfcd..7a96d168efc4 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -216,8 +216,8 @@ config GENEVE
config GTP
tristate "GPRS Tunneling Protocol datapath (GTP-U)"
- depends on INET && NET_UDP_TUNNEL
- select NET_IP_TUNNEL
+ depends on INET
+ select NET_UDP_TUNNEL
---help---
This allows one to create gtp virtual interfaces that provide
the GPRS Tunneling Protocol datapath (GTP-U). This tunneling protocol
diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_pro.c b/drivers/net/can/usb/peak_usb/pcan_usb_pro.c
index d516def846ab..b388406ac0f5 100644
--- a/drivers/net/can/usb/peak_usb/pcan_usb_pro.c
+++ b/drivers/net/can/usb/peak_usb/pcan_usb_pro.c
@@ -127,7 +127,7 @@ static u8 *pcan_msg_init_empty(struct pcan_usb_pro_msg *pm,
/*
* add one record to a message being built
*/
-static int pcan_msg_add_rec(struct pcan_usb_pro_msg *pm, u8 id, ...)
+static int pcan_msg_add_rec(struct pcan_usb_pro_msg *pm, int id, ...)
{
int len, i;
u8 *pc;
diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
index 96728d1e9824..f4e2db44ad91 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.c
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
@@ -569,6 +569,9 @@ int mv88e6xxx_port_setup_mac(struct mv88e6xxx_chip *chip, int port, int link,
goto restore_link;
}
+ if (speed == SPEED_MAX && chip->info->ops->port_max_speed_mode)
+ mode = chip->info->ops->port_max_speed_mode(port);
+
if (chip->info->ops->port_set_pause) {
err = chip->info->ops->port_set_pause(chip, port, pause);
if (err)
@@ -3067,6 +3070,7 @@ static const struct mv88e6xxx_ops mv88e6141_ops = {
.port_set_duplex = mv88e6xxx_port_set_duplex,
.port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay,
.port_set_speed = mv88e6341_port_set_speed,
+ .port_max_speed_mode = mv88e6341_port_max_speed_mode,
.port_tag_remap = mv88e6095_port_tag_remap,
.port_set_frame_mode = mv88e6351_port_set_frame_mode,
.port_set_egress_floods = mv88e6352_port_set_egress_floods,
@@ -3385,6 +3389,7 @@ static const struct mv88e6xxx_ops mv88e6190_ops = {
.port_set_duplex = mv88e6xxx_port_set_duplex,
.port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay,
.port_set_speed = mv88e6390_port_set_speed,
+ .port_max_speed_mode = mv88e6390_port_max_speed_mode,
.port_tag_remap = mv88e6390_port_tag_remap,
.port_set_frame_mode = mv88e6351_port_set_frame_mode,
.port_set_egress_floods = mv88e6352_port_set_egress_floods,
@@ -3429,6 +3434,7 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = {
.port_set_duplex = mv88e6xxx_port_set_duplex,
.port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay,
.port_set_speed = mv88e6390x_port_set_speed,
+ .port_max_speed_mode = mv88e6390x_port_max_speed_mode,
.port_tag_remap = mv88e6390_port_tag_remap,
.port_set_frame_mode = mv88e6351_port_set_frame_mode,
.port_set_egress_floods = mv88e6352_port_set_egress_floods,
@@ -3473,6 +3479,7 @@ static const struct mv88e6xxx_ops mv88e6191_ops = {
.port_set_duplex = mv88e6xxx_port_set_duplex,
.port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay,
.port_set_speed = mv88e6390_port_set_speed,
+ .port_max_speed_mode = mv88e6390_port_max_speed_mode,
.port_tag_remap = mv88e6390_port_tag_remap,
.port_set_frame_mode = mv88e6351_port_set_frame_mode,
.port_set_egress_floods = mv88e6352_port_set_egress_floods,
@@ -3566,6 +3573,7 @@ static const struct mv88e6xxx_ops mv88e6290_ops = {
.port_set_duplex = mv88e6xxx_port_set_duplex,
.port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay,
.port_set_speed = mv88e6390_port_set_speed,
+ .port_max_speed_mode = mv88e6390_port_max_speed_mode,
.port_tag_remap = mv88e6390_port_tag_remap,
.port_set_frame_mode = mv88e6351_port_set_frame_mode,
.port_set_egress_floods = mv88e6352_port_set_egress_floods,
@@ -3697,6 +3705,7 @@ static const struct mv88e6xxx_ops mv88e6341_ops = {
.port_set_duplex = mv88e6xxx_port_set_duplex,
.port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay,
.port_set_speed = mv88e6341_port_set_speed,
+ .port_max_speed_mode = mv88e6341_port_max_speed_mode,
.port_tag_remap = mv88e6095_port_tag_remap,
.port_set_frame_mode = mv88e6351_port_set_frame_mode,
.port_set_egress_floods = mv88e6352_port_set_egress_floods,
@@ -3872,6 +3881,7 @@ static const struct mv88e6xxx_ops mv88e6390_ops = {
.port_set_duplex = mv88e6xxx_port_set_duplex,
.port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay,
.port_set_speed = mv88e6390_port_set_speed,
+ .port_max_speed_mode = mv88e6390_port_max_speed_mode,
.port_tag_remap = mv88e6390_port_tag_remap,
.port_set_frame_mode = mv88e6351_port_set_frame_mode,
.port_set_egress_floods = mv88e6352_port_set_egress_floods,
@@ -3920,6 +3930,7 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = {
.port_set_duplex = mv88e6xxx_port_set_duplex,
.port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay,
.port_set_speed = mv88e6390x_port_set_speed,
+ .port_max_speed_mode = mv88e6390x_port_max_speed_mode,
.port_tag_remap = mv88e6390_port_tag_remap,
.port_set_frame_mode = mv88e6351_port_set_frame_mode,
.port_set_egress_floods = mv88e6352_port_set_egress_floods,
diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h
index adcf60779895..19c07dff0440 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.h
+++ b/drivers/net/dsa/mv88e6xxx/chip.h
@@ -377,6 +377,9 @@ struct mv88e6xxx_ops {
*/
int (*port_set_speed)(struct mv88e6xxx_chip *chip, int port, int speed);
+ /* What interface mode should be used for maximum speed? */
+ phy_interface_t (*port_max_speed_mode)(int port);
+
int (*port_tag_remap)(struct mv88e6xxx_chip *chip, int port);
int (*port_set_frame_mode)(struct mv88e6xxx_chip *chip, int port,
diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c
index 0796c6feec55..dce84a2a65c7 100644
--- a/drivers/net/dsa/mv88e6xxx/port.c
+++ b/drivers/net/dsa/mv88e6xxx/port.c
@@ -312,6 +312,14 @@ int mv88e6341_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed)
return mv88e6xxx_port_set_speed(chip, port, speed, !port, true);
}
+phy_interface_t mv88e6341_port_max_speed_mode(int port)
+{
+ if (port == 5)
+ return PHY_INTERFACE_MODE_2500BASEX;
+
+ return PHY_INTERFACE_MODE_NA;
+}
+
/* Support 10, 100, 200, 1000 Mbps (e.g. 88E6352 family) */
int mv88e6352_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed)
{
@@ -345,6 +353,14 @@ int mv88e6390_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed)
return mv88e6xxx_port_set_speed(chip, port, speed, true, true);
}
+phy_interface_t mv88e6390_port_max_speed_mode(int port)
+{
+ if (port == 9 || port == 10)
+ return PHY_INTERFACE_MODE_2500BASEX;
+
+ return PHY_INTERFACE_MODE_NA;
+}
+
/* Support 10, 100, 200, 1000, 2500, 10000 Mbps (e.g. 88E6190X) */
int mv88e6390x_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed)
{
@@ -360,6 +376,14 @@ int mv88e6390x_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed)
return mv88e6xxx_port_set_speed(chip, port, speed, true, true);
}
+phy_interface_t mv88e6390x_port_max_speed_mode(int port)
+{
+ if (port == 9 || port == 10)
+ return PHY_INTERFACE_MODE_XAUI;
+
+ return PHY_INTERFACE_MODE_NA;
+}
+
int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
phy_interface_t mode)
{
diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h
index 4aadf321edb7..c7bed263a0f4 100644
--- a/drivers/net/dsa/mv88e6xxx/port.h
+++ b/drivers/net/dsa/mv88e6xxx/port.h
@@ -285,6 +285,10 @@ int mv88e6352_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed);
int mv88e6390_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed);
int mv88e6390x_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed);
+phy_interface_t mv88e6341_port_max_speed_mode(int port);
+phy_interface_t mv88e6390_port_max_speed_mode(int port);
+phy_interface_t mv88e6390x_port_max_speed_mode(int port);
+
int mv88e6xxx_port_set_state(struct mv88e6xxx_chip *chip, int port, u8 state);
int mv88e6xxx_port_set_vlan_map(struct mv88e6xxx_chip *chip, int port, u16 map);
diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c
index 576b37d12a63..c4fa400efdcc 100644
--- a/drivers/net/dsa/qca8k.c
+++ b/drivers/net/dsa/qca8k.c
@@ -481,6 +481,155 @@ qca8k_port_set_status(struct qca8k_priv *priv, int port, int enable)
qca8k_reg_clear(priv, QCA8K_REG_PORT_STATUS(port), mask);
}
+static u32
+qca8k_port_to_phy(int port)
+{
+ /* From Andrew Lunn:
+ * Port 0 has no internal phy.
+ * Port 1 has an internal PHY at MDIO address 0.
+ * Port 2 has an internal PHY at MDIO address 1.
+ * ...
+ * Port 5 has an internal PHY at MDIO address 4.
+ * Port 6 has no internal PHY.
+ */
+
+ return port - 1;
+}
+
+static int
+qca8k_mdio_write(struct qca8k_priv *priv, int port, u32 regnum, u16 data)
+{
+ u32 phy, val;
+
+ if (regnum >= QCA8K_MDIO_MASTER_MAX_REG)
+ return -EINVAL;
+
+ /* callee is responsible for not passing bad ports,
+ * but we still would like to make spills impossible.
+ */
+ phy = qca8k_port_to_phy(port) % PHY_MAX_ADDR;
+ val = QCA8K_MDIO_MASTER_BUSY | QCA8K_MDIO_MASTER_EN |
+ QCA8K_MDIO_MASTER_WRITE | QCA8K_MDIO_MASTER_PHY_ADDR(phy) |
+ QCA8K_MDIO_MASTER_REG_ADDR(regnum) |
+ QCA8K_MDIO_MASTER_DATA(data);
+
+ qca8k_write(priv, QCA8K_MDIO_MASTER_CTRL, val);
+
+ return qca8k_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL,
+ QCA8K_MDIO_MASTER_BUSY);
+}
+
+static int
+qca8k_mdio_read(struct qca8k_priv *priv, int port, u32 regnum)
+{
+ u32 phy, val;
+
+ if (regnum >= QCA8K_MDIO_MASTER_MAX_REG)
+ return -EINVAL;
+
+ /* callee is responsible for not passing bad ports,
+ * but we still would like to make spills impossible.
+ */
+ phy = qca8k_port_to_phy(port) % PHY_MAX_ADDR;
+ val = QCA8K_MDIO_MASTER_BUSY | QCA8K_MDIO_MASTER_EN |
+ QCA8K_MDIO_MASTER_READ | QCA8K_MDIO_MASTER_PHY_ADDR(phy) |
+ QCA8K_MDIO_MASTER_REG_ADDR(regnum);
+
+ qca8k_write(priv, QCA8K_MDIO_MASTER_CTRL, val);
+
+ if (qca8k_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL,
+ QCA8K_MDIO_MASTER_BUSY))
+ return -ETIMEDOUT;
+
+ val = (qca8k_read(priv, QCA8K_MDIO_MASTER_CTRL) &
+ QCA8K_MDIO_MASTER_DATA_MASK);
+
+ return val;
+}
+
+static int
+qca8k_phy_write(struct dsa_switch *ds, int port, int regnum, u16 data)
+{
+ struct qca8k_priv *priv = ds->priv;
+
+ return qca8k_mdio_write(priv, port, regnum, data);
+}
+
+static int
+qca8k_phy_read(struct dsa_switch *ds, int port, int regnum)
+{
+ struct qca8k_priv *priv = ds->priv;
+ int ret;
+
+ ret = qca8k_mdio_read(priv, port, regnum);
+
+ if (ret < 0)
+ return 0xffff;
+
+ return ret;
+}
+
+static int
+qca8k_setup_mdio_bus(struct qca8k_priv *priv)
+{
+ u32 internal_mdio_mask = 0, external_mdio_mask = 0, reg;
+ struct device_node *ports, *port;
+ int err;
+
+ ports = of_get_child_by_name(priv->dev->of_node, "ports");
+ if (!ports)
+ return -EINVAL;
+
+ for_each_available_child_of_node(ports, port) {
+ err = of_property_read_u32(port, "reg", &reg);
+ if (err)
+ return err;
+
+ if (!dsa_is_user_port(priv->ds, reg))
+ continue;
+
+ if (of_property_read_bool(port, "phy-handle"))
+ external_mdio_mask |= BIT(reg);
+ else
+ internal_mdio_mask |= BIT(reg);
+ }
+
+ if (!external_mdio_mask && !internal_mdio_mask) {
+ dev_err(priv->dev, "no PHYs are defined.\n");
+ return -EINVAL;
+ }
+
+ /* The QCA8K_MDIO_MASTER_EN Bit, which grants access to PHYs through
+ * the MDIO_MASTER register also _disconnects_ the external MDC
+ * passthrough to the internal PHYs. It's not possible to use both
+ * configurations at the same time!
+ *
+ * Because this came up during the review process:
+ * If the external mdio-bus driver is capable magically disabling
+ * the QCA8K_MDIO_MASTER_EN and mutex/spin-locking out the qca8k's
+ * accessors for the time being, it would be possible to pull this
+ * off.
+ */
+ if (!!external_mdio_mask && !!internal_mdio_mask) {
+ dev_err(priv->dev, "either internal or external mdio bus configuration is supported.\n");
+ return -EINVAL;
+ }
+
+ if (external_mdio_mask) {
+ /* Make sure to disable the internal mdio bus in cases
+ * a dt-overlay and driver reload changed the configuration
+ */
+
+ qca8k_reg_clear(priv, QCA8K_MDIO_MASTER_CTRL,
+ QCA8K_MDIO_MASTER_EN);
+ return 0;
+ }
+
+ priv->ops.phy_read = qca8k_phy_read;
+ priv->ops.phy_write = qca8k_phy_write;
+ return 0;
+}
+
static int
qca8k_setup(struct dsa_switch *ds)
{
@@ -502,6 +651,10 @@ qca8k_setup(struct dsa_switch *ds)
if (IS_ERR(priv->regmap))
pr_warn("regmap initialization failed");
+ ret = qca8k_setup_mdio_bus(priv);
+ if (ret)
+ return ret;
+
/* Initialize CPU port pad mode (xMII type, delays...) */
phy_mode = of_get_phy_mode(ds->ports[QCA8K_CPU_PORT].dn);
if (phy_mode < 0) {
@@ -624,22 +777,6 @@ qca8k_adjust_link(struct dsa_switch *ds, int port, struct phy_device *phy)
qca8k_port_set_status(priv, port, 1);
}
-static int
-qca8k_phy_read(struct dsa_switch *ds, int phy, int regnum)
-{
- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
-
- return mdiobus_read(priv->bus, phy, regnum);
-}
-
-static int
-qca8k_phy_write(struct dsa_switch *ds, int phy, int regnum, u16 val)
-{
- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
-
- return mdiobus_write(priv->bus, phy, regnum, val);
-}
-
static void
qca8k_get_strings(struct dsa_switch *ds, int port, u32 stringset, uint8_t *data)
{
@@ -879,8 +1016,6 @@ static const struct dsa_switch_ops qca8k_switch_ops = {
.setup = qca8k_setup,
.adjust_link = qca8k_adjust_link,
.get_strings = qca8k_get_strings,
- .phy_read = qca8k_phy_read,
- .phy_write = qca8k_phy_write,
.get_ethtool_stats = qca8k_get_ethtool_stats,
.get_sset_count = qca8k_get_sset_count,
.get_mac_eee = qca8k_get_mac_eee,
@@ -923,7 +1058,8 @@ qca8k_sw_probe(struct mdio_device *mdiodev)
return -ENOMEM;
priv->ds->priv = priv;
- priv->ds->ops = &qca8k_switch_ops;
+ priv->ops = qca8k_switch_ops;
+ priv->ds->ops = &priv->ops;
mutex_init(&priv->reg_mutex);
dev_set_drvdata(&mdiodev->dev, priv);
diff --git a/drivers/net/dsa/qca8k.h b/drivers/net/dsa/qca8k.h
index d146e54c8a6c..249fd62268e5 100644
--- a/drivers/net/dsa/qca8k.h
+++ b/drivers/net/dsa/qca8k.h
@@ -49,6 +49,18 @@
#define QCA8K_MIB_FLUSH BIT(24)
#define QCA8K_MIB_CPU_KEEP BIT(20)
#define QCA8K_MIB_BUSY BIT(17)
+#define QCA8K_MDIO_MASTER_CTRL 0x3c
+#define QCA8K_MDIO_MASTER_BUSY BIT(31)
+#define QCA8K_MDIO_MASTER_EN BIT(30)
+#define QCA8K_MDIO_MASTER_READ BIT(27)
+#define QCA8K_MDIO_MASTER_WRITE 0
+#define QCA8K_MDIO_MASTER_SUP_PRE BIT(26)
+#define QCA8K_MDIO_MASTER_PHY_ADDR(x) ((x) << 21)
+#define QCA8K_MDIO_MASTER_REG_ADDR(x) ((x) << 16)
+#define QCA8K_MDIO_MASTER_DATA(x) (x)
+#define QCA8K_MDIO_MASTER_DATA_MASK GENMASK(15, 0)
+#define QCA8K_MDIO_MASTER_MAX_PORTS 5
+#define QCA8K_MDIO_MASTER_MAX_REG 32
#define QCA8K_GOL_MAC_ADDR0 0x60
#define QCA8K_GOL_MAC_ADDR1 0x64
#define QCA8K_REG_PORT_STATUS(_i) (0x07c + (_i) * 4)
@@ -169,6 +181,7 @@ struct qca8k_priv {
struct dsa_switch *ds;
struct mutex reg_mutex;
struct device *dev;
+ struct dsa_switch_ops ops;
};
struct qca8k_mib_desc {
diff --git a/drivers/net/ethernet/3com/3c515.c b/drivers/net/ethernet/3com/3c515.c
index 808abb6b3671..b15752267c8d 100644
--- a/drivers/net/ethernet/3com/3c515.c
+++ b/drivers/net/ethernet/3com/3c515.c
@@ -1521,7 +1521,7 @@ static void update_stats(int ioaddr, struct net_device *dev)
static void set_rx_mode(struct net_device *dev)
{
int ioaddr = dev->base_addr;
- short new_mode;
+ unsigned short new_mode;
if (dev->flags & IFF_PROMISC) {
if (corkscrew_debug > 3)
diff --git a/drivers/net/ethernet/8390/mac8390.c b/drivers/net/ethernet/8390/mac8390.c
index 342ae08ec3c2..d60a86aa8aa8 100644
--- a/drivers/net/ethernet/8390/mac8390.c
+++ b/drivers/net/ethernet/8390/mac8390.c
@@ -153,8 +153,6 @@ static void dayna_block_input(struct net_device *dev, int count,
static void dayna_block_output(struct net_device *dev, int count,
const unsigned char *buf, int start_page);
-#define memcmp_withio(a, b, c) memcmp((a), (void *)(b), (c))
-
/* Slow Sane (16-bit chunk memory read/write) Cabletron uses this */
static void slow_sane_get_8390_hdr(struct net_device *dev,
struct e8390_pkt_hdr *hdr, int ring_page);
@@ -233,19 +231,26 @@ static enum mac8390_type mac8390_ident(struct nubus_rsrc *fres)
static enum mac8390_access mac8390_testio(unsigned long membase)
{
- unsigned long outdata = 0xA5A0B5B0;
- unsigned long indata = 0x00000000;
+ u32 outdata = 0xA5A0B5B0;
+ u32 indata = 0;
+
/* Try writing 32 bits */
- memcpy_toio((void __iomem *)membase, &outdata, 4);
- /* Now compare them */
- if (memcmp_withio(&outdata, membase, 4) == 0)
+ nubus_writel(outdata, membase);
+ /* Now read it back */
+ indata = nubus_readl(membase);
+ if (outdata == indata)
return ACCESS_32;
+
+ outdata = 0xC5C0D5D0;
+ indata = 0;
+
/* Write 16 bit output */
word_memcpy_tocard(membase, &outdata, 4);
/* Now read it back */
word_memcpy_fromcard(&indata, membase, 4);
if (outdata == indata)
return ACCESS_16;
+
return ACCESS_UNKNOWN;
}
diff --git a/drivers/net/ethernet/8390/pcnet_cs.c b/drivers/net/ethernet/8390/pcnet_cs.c
index 61e43802b9a5..645efac6310d 100644
--- a/drivers/net/ethernet/8390/pcnet_cs.c
+++ b/drivers/net/ethernet/8390/pcnet_cs.c
@@ -289,6 +289,11 @@ static struct hw_info *get_hwinfo(struct pcmcia_device *link)
virt = ioremap(link->resource[2]->start,
resource_size(link->resource[2]));
+ if (unlikely(!virt)) {
+ pcmcia_release_window(link, link->resource[2]);
+ return NULL;
+ }
+
for (i = 0; i < NR_INFO; i++) {
pcmcia_map_mem_page(link, link->resource[2],
hw_info[i].offset & ~(resource_size(link->resource[2])-1));
@@ -1423,6 +1428,11 @@ static int setup_shmem_window(struct pcmcia_device *link, int start_pg,
/* Try scribbling on the buffer */
info->base = ioremap(link->resource[3]->start,
resource_size(link->resource[3]));
+ if (unlikely(!info->base)) {
+ ret = -ENOMEM;
+ goto failed;
+ }
+
for (i = 0; i < (TX_PAGES<<8); i += 2)
__raw_writew((i>>1), info->base+offset+i);
udelay(100);
diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_ring.c b/drivers/net/ethernet/aquantia/atlantic/aq_ring.c
index 74550ccc7a20..e2ffb159cbe2 100644
--- a/drivers/net/ethernet/aquantia/atlantic/aq_ring.c
+++ b/drivers/net/ethernet/aquantia/atlantic/aq_ring.c
@@ -186,11 +186,12 @@ static void aq_rx_checksum(struct aq_ring_s *self,
}
if (buff->is_ip_cso) {
__skb_incr_checksum_unnecessary(skb);
- if (buff->is_udp_cso || buff->is_tcp_cso)
- __skb_incr_checksum_unnecessary(skb);
} else {
skb->ip_summed = CHECKSUM_NONE;
}
+
+ if (buff->is_udp_cso || buff->is_tcp_cso)
+ __skb_incr_checksum_unnecessary(skb);
}
#define AQ_SKB_ALIGN SKB_DATA_ALIGN(sizeof(struct skb_shared_info))
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c
index ea45a9b8179e..cf475873ce81 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c
@@ -43,9 +43,6 @@ static int bnxt_register_dev(struct bnxt_en_dev *edev, int ulp_id,
if (ulp_id == BNXT_ROCE_ULP) {
unsigned int max_stat_ctxs;
- if (bp->flags & BNXT_FLAG_CHIP_P5)
- return -EOPNOTSUPP;
-
max_stat_ctxs = bnxt_get_max_func_stat_ctxs(bp);
if (max_stat_ctxs <= BNXT_MIN_ROCE_STAT_CTXS ||
bp->cp_nr_rings == max_stat_ctxs)
diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c
index ad099fd01b45..1522aee81884 100644
--- a/drivers/net/ethernet/cadence/macb_main.c
+++ b/drivers/net/ethernet/cadence/macb_main.c
@@ -3370,14 +3370,20 @@ static int macb_clk_init(struct platform_device *pdev, struct clk **pclk,
*hclk = devm_clk_get(&pdev->dev, "hclk");
}
- if (IS_ERR(*pclk)) {
+ if (IS_ERR_OR_NULL(*pclk)) {
err = PTR_ERR(*pclk);
+ if (!err)
+ err = -ENODEV;
+
dev_err(&pdev->dev, "failed to get macb_clk (%u)\n", err);
return err;
}
- if (IS_ERR(*hclk)) {
+ if (IS_ERR_OR_NULL(*hclk)) {
err = PTR_ERR(*hclk);
+ if (!err)
+ err = -ENODEV;
+
dev_err(&pdev->dev, "failed to get hclk (%u)\n", err);
return err;
}
diff --git a/drivers/net/ethernet/cavium/liquidio/lio_core.c b/drivers/net/ethernet/cavium/liquidio/lio_core.c
index e21bf3724611..1c50c10b5a16 100644
--- a/drivers/net/ethernet/cavium/liquidio/lio_core.c
+++ b/drivers/net/ethernet/cavium/liquidio/lio_core.c
@@ -1211,6 +1211,11 @@ int liquidio_change_mtu(struct net_device *netdev, int new_mtu)
sc = (struct octeon_soft_command *)
octeon_alloc_soft_command(oct, OCTNET_CMD_SIZE, 16, 0);
+ if (!sc) {
+ netif_info(lio, rx_err, lio->netdev,
+ "Failed to allocate soft command\n");
+ return -ENOMEM;
+ }
ncmd = (union octnet_cmd *)sc->virtdptr;
@@ -1684,6 +1689,11 @@ int liquidio_set_fec(struct lio *lio, int on_off)
sc = octeon_alloc_soft_command(oct, OCTNET_CMD_SIZE,
sizeof(struct oct_nic_seapi_resp), 0);
+ if (!sc) {
+ dev_err(&oct->pci_dev->dev,
+ "Failed to allocate soft command\n");
+ return -ENOMEM;
+ }
ncmd = sc->virtdptr;
resp = sc->virtrptr;
diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c
index 9b7819fdc9de..fb6f813cff65 100644
--- a/drivers/net/ethernet/cavium/liquidio/lio_main.c
+++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c
@@ -1192,6 +1192,11 @@ static void send_rx_ctrl_cmd(struct lio *lio, int start_stop)
sc = (struct octeon_soft_command *)
octeon_alloc_soft_command(oct, OCTNET_CMD_SIZE,
16, 0);
+ if (!sc) {
+ netif_info(lio, rx_err, lio->netdev,
+ "Failed to allocate octeon_soft_command\n");
+ return;
+ }
ncmd = (union octnet_cmd *)sc->virtdptr;
diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_main.c b/drivers/net/ethernet/cavium/thunder/nicvf_main.c
index 503cfadff4ac..aa2be4807191 100644
--- a/drivers/net/ethernet/cavium/thunder/nicvf_main.c
+++ b/drivers/net/ethernet/cavium/thunder/nicvf_main.c
@@ -2234,6 +2234,12 @@ static int nicvf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
nic->nicvf_rx_mode_wq = alloc_ordered_workqueue("nicvf_rx_mode_wq_VF%d",
WQ_MEM_RECLAIM,
nic->vf_id);
+ if (!nic->nicvf_rx_mode_wq) {
+ err = -ENOMEM;
+ dev_err(dev, "Failed to allocate work queue\n");
+ goto err_unregister_interrupts;
+ }
+
INIT_WORK(&nic->rx_mode_work.work, nicvf_set_rx_mode_task);
spin_lock_init(&nic->rx_mode_wq_lock);
mutex_init(&nic->rx_mode_mtx);
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c
index 3130b43bba52..02959035ed3f 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c
@@ -2620,7 +2620,7 @@ static inline struct port_info *ethqset2pinfo(struct adapter *adap, int qset)
}
/* should never happen! */
- BUG_ON(1);
+ BUG();
return NULL;
}
diff --git a/drivers/net/ethernet/chelsio/cxgb4/sge.c b/drivers/net/ethernet/chelsio/cxgb4/sge.c
index 88773ca58e6b..b3da81e90132 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/sge.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/sge.c
@@ -476,7 +476,7 @@ static inline int get_buf_size(struct adapter *adapter,
break;
default:
- BUG_ON(1);
+ BUG();
}
return buf_size;
diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_msg.h b/drivers/net/ethernet/chelsio/cxgb4/t4_msg.h
index c62a0c830705..38dd41eb959e 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/t4_msg.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/t4_msg.h
@@ -56,6 +56,7 @@ enum {
CPL_TX_DATA_ISO = 0x1F,
CPL_CLOSE_LISTSRV_RPL = 0x20,
+ CPL_GET_TCB_RPL = 0x22,
CPL_L2T_WRITE_RPL = 0x23,
CPL_PASS_OPEN_RPL = 0x24,
CPL_ACT_OPEN_RPL = 0x25,
@@ -688,6 +689,13 @@ struct cpl_get_tcb {
#define NO_REPLY_V(x) ((x) << NO_REPLY_S)
#define NO_REPLY_F NO_REPLY_V(1U)
+struct cpl_get_tcb_rpl {
+ union opcode_tid ot;
+ __u8 cookie;
+ __u8 status;
+ __be16 len;
+};
+
struct cpl_set_tcb_field {
WR_HDR;
union opcode_tid ot;
diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_tcb.h b/drivers/net/ethernet/chelsio/cxgb4/t4_tcb.h
index 3297ce025e8b..1b9afb192f7f 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/t4_tcb.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/t4_tcb.h
@@ -41,6 +41,14 @@
#define TCB_SMAC_SEL_V(x) ((x) << TCB_SMAC_SEL_S)
#define TCB_T_FLAGS_W 1
+#define TCB_T_FLAGS_S 0
+#define TCB_T_FLAGS_M 0xffffffffffffffffULL
+#define TCB_T_FLAGS_V(x) ((__u64)(x) << TCB_T_FLAGS_S)
+
+#define TCB_RQ_START_W 30
+#define TCB_RQ_START_S 0
+#define TCB_RQ_START_M 0x3ffffffULL
+#define TCB_RQ_START_V(x) ((x) << TCB_RQ_START_S)
#define TF_CCTRL_ECE_S 60
#define TF_CCTRL_CWR_S 61
@@ -66,4 +74,8 @@
#define TCB_RX_FRAG3_LEN_RAW_W 29
#define TCB_RX_FRAG3_START_IDX_OFFSET_RAW_W 30
#define TCB_PDU_HDR_LEN_W 31
+
+#define TF_RX_PDU_OUT_S 49
+#define TF_RX_PDU_OUT_V(x) ((__u64)(x) << TF_RX_PDU_OUT_S)
+
#endif /* __T4_TCB_H */
diff --git a/drivers/net/ethernet/cisco/enic/enic_main.c b/drivers/net/ethernet/cisco/enic/enic_main.c
index 9a7f70db20c7..733d9172425b 100644
--- a/drivers/net/ethernet/cisco/enic/enic_main.c
+++ b/drivers/net/ethernet/cisco/enic/enic_main.c
@@ -119,7 +119,7 @@ static void enic_init_affinity_hint(struct enic *enic)
for (i = 0; i < enic->intr_count; i++) {
if (enic_is_err_intr(enic, i) || enic_is_notify_intr(enic, i) ||
- (enic->msix[i].affinity_mask &&
+ (cpumask_available(enic->msix[i].affinity_mask) &&
!cpumask_empty(enic->msix[i].affinity_mask)))
continue;
if (zalloc_cpumask_var(&enic->msix[i].affinity_mask,
@@ -148,7 +148,7 @@ static void enic_set_affinity_hint(struct enic *enic)
for (i = 0; i < enic->intr_count; i++) {
if (enic_is_err_intr(enic, i) ||
enic_is_notify_intr(enic, i) ||
- !enic->msix[i].affinity_mask ||
+ !cpumask_available(enic->msix[i].affinity_mask) ||
cpumask_empty(enic->msix[i].affinity_mask))
continue;
err = irq_set_affinity_hint(enic->msix_entry[i].vector,
@@ -161,7 +161,7 @@ static void enic_set_affinity_hint(struct enic *enic)
for (i = 0; i < enic->wq_count; i++) {
int wq_intr = enic_msix_wq_intr(enic, i);
- if (enic->msix[wq_intr].affinity_mask &&
+ if (cpumask_available(enic->msix[wq_intr].affinity_mask) &&
!cpumask_empty(enic->msix[wq_intr].affinity_mask))
netif_set_xps_queue(enic->netdev,
enic->msix[wq_intr].affinity_mask,
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
index 2ba49e959c3f..dc339dc1adb2 100644
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
@@ -815,6 +815,14 @@ static netdev_tx_t dpaa2_eth_tx(struct sk_buff *skb, struct net_device *net_dev)
*/
queue_mapping = skb_get_queue_mapping(skb);
fq = &priv->fq[queue_mapping];
+
+ fd_len = dpaa2_fd_get_len(&fd);
+ nq = netdev_get_tx_queue(net_dev, queue_mapping);
+ netdev_tx_sent_queue(nq, fd_len);
+
+ /* Everything that happens after this enqueues might race with
+ * the Tx confirmation callback for this frame
+ */
for (i = 0; i < DPAA2_ETH_ENQUEUE_RETRIES; i++) {
err = priv->enqueue(priv, fq, &fd, 0);
if (err != -EBUSY)
@@ -825,13 +833,10 @@ static netdev_tx_t dpaa2_eth_tx(struct sk_buff *skb, struct net_device *net_dev)
percpu_stats->tx_errors++;
/* Clean up everything, including freeing the skb */
free_tx_fd(priv, fq, &fd, false);
+ netdev_tx_completed_queue(nq, 1, fd_len);
} else {
- fd_len = dpaa2_fd_get_len(&fd);
percpu_stats->tx_packets++;
percpu_stats->tx_bytes += fd_len;
-
- nq = netdev_get_tx_queue(net_dev, queue_mapping);
- netdev_tx_sent_queue(nq, fd_len);
}
return NETDEV_TX_OK;
@@ -1817,7 +1822,7 @@ static int dpaa2_eth_xdp_xmit_frame(struct net_device *net_dev,
dpaa2_fd_set_format(&fd, dpaa2_fd_single);
dpaa2_fd_set_ctrl(&fd, FD_CTRL_PTA);
- fq = &priv->fq[smp_processor_id()];
+ fq = &priv->fq[smp_processor_id() % dpaa2_eth_queue_count(priv)];
for (i = 0; i < DPAA2_ETH_ENQUEUE_RETRIES; i++) {
err = priv->enqueue(priv, fq, &fd, 0);
if (err != -EBUSY)
diff --git a/drivers/net/ethernet/fujitsu/fmvj18x_cs.c b/drivers/net/ethernet/fujitsu/fmvj18x_cs.c
index a69cd19a55ae..1eca0fdb9933 100644
--- a/drivers/net/ethernet/fujitsu/fmvj18x_cs.c
+++ b/drivers/net/ethernet/fujitsu/fmvj18x_cs.c
@@ -547,6 +547,11 @@ static int fmvj18x_get_hwinfo(struct pcmcia_device *link, u_char *node_id)
return -1;
base = ioremap(link->resource[2]->start, resource_size(link->resource[2]));
+ if (!base) {
+ pcmcia_release_window(link, link->resource[2]);
+ return -ENOMEM;
+ }
+
pcmcia_map_mem_page(link, link->resource[2], 0);
/*
diff --git a/drivers/net/ethernet/hisilicon/hns3/hnae3.h b/drivers/net/ethernet/hisilicon/hns3/hnae3.h
index 66d7a8b80e76..38b430f11fc1 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hnae3.h
+++ b/drivers/net/ethernet/hisilicon/hns3/hnae3.h
@@ -194,6 +194,7 @@ struct hnae3_ae_dev {
const struct hnae3_ae_ops *ops;
struct list_head node;
u32 flag;
+ u8 override_pci_need_reset; /* fix to stop multiple reset happening */
enum hnae3_dev_type dev_type;
enum hnae3_reset_type reset_type;
void *priv;
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
index 3cb43b1f1c2e..162cb9afa0e7 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
@@ -22,6 +22,7 @@
#include "hns3_enet.h"
#define hns3_set_field(origin, shift, val) ((origin) |= ((val) << (shift)))
+#define hns3_tx_bd_count(S) DIV_ROUND_UP(S, HNS3_MAX_BD_SIZE)
static void hns3_clear_all_ring(struct hnae3_handle *h);
static void hns3_force_clear_all_rx_ring(struct hnae3_handle *h);
@@ -1079,7 +1080,7 @@ static int hns3_fill_desc(struct hns3_enet_ring *ring, void *priv,
desc_cb->length = size;
- frag_buf_num = (size + HNS3_MAX_BD_SIZE - 1) >> HNS3_MAX_BD_SIZE_OFFSET;
+ frag_buf_num = hns3_tx_bd_count(size);
sizeoflast = size & HNS3_TX_LAST_SIZE_M;
sizeoflast = sizeoflast ? sizeoflast : HNS3_MAX_BD_SIZE;
@@ -1124,14 +1125,13 @@ static int hns3_nic_maybe_stop_tso(struct sk_buff **out_skb, int *bnum,
int i;
size = skb_headlen(skb);
- buf_num = (size + HNS3_MAX_BD_SIZE - 1) >> HNS3_MAX_BD_SIZE_OFFSET;
+ buf_num = hns3_tx_bd_count(size);
frag_num = skb_shinfo(skb)->nr_frags;
for (i = 0; i < frag_num; i++) {
frag = &skb_shinfo(skb)->frags[i];
size = skb_frag_size(frag);
- bdnum_for_frag = (size + HNS3_MAX_BD_SIZE - 1) >>
- HNS3_MAX_BD_SIZE_OFFSET;
+ bdnum_for_frag = hns3_tx_bd_count(size);
if (unlikely(bdnum_for_frag > HNS3_MAX_BD_PER_FRAG))
return -ENOMEM;
@@ -1139,8 +1139,7 @@ static int hns3_nic_maybe_stop_tso(struct sk_buff **out_skb, int *bnum,
}
if (unlikely(buf_num > HNS3_MAX_BD_PER_FRAG)) {
- buf_num = (skb->len + HNS3_MAX_BD_SIZE - 1) >>
- HNS3_MAX_BD_SIZE_OFFSET;
+ buf_num = hns3_tx_bd_count(skb->len);
if (ring_space(ring) < buf_num)
return -EBUSY;
/* manual split the send packet */
@@ -1169,7 +1168,7 @@ static int hns3_nic_maybe_stop_tx(struct sk_buff **out_skb, int *bnum,
buf_num = skb_shinfo(skb)->nr_frags + 1;
if (unlikely(buf_num > HNS3_MAX_BD_PER_FRAG)) {
- buf_num = (skb->len + HNS3_MAX_BD_SIZE - 1) / HNS3_MAX_BD_SIZE;
+ buf_num = hns3_tx_bd_count(skb->len);
if (ring_space(ring) < buf_num)
return -EBUSY;
/* manual split the send packet */
@@ -1850,7 +1849,9 @@ static pci_ers_result_t hns3_slot_reset(struct pci_dev *pdev)
/* request the reset */
if (ae_dev->ops->reset_event) {
- ae_dev->ops->reset_event(pdev, NULL);
+ if (!ae_dev->override_pci_need_reset)
+ ae_dev->ops->reset_event(pdev, NULL);
+
return PCI_ERS_RESULT_RECOVERED;
}
@@ -2321,8 +2322,8 @@ static void hns3_rx_checksum(struct hns3_enet_ring *ring, struct sk_buff *skb,
if (!(bd_base_info & BIT(HNS3_RXD_L3L4P_B)))
return;
- if (unlikely(l234info & (BIT(HNS3_RXD_L3E_B) | BIT(HNS3_RXD_L4E_B) ||
- BIT(HNS3_RXD_OL3E_B) ||
+ if (unlikely(l234info & (BIT(HNS3_RXD_L3E_B) | BIT(HNS3_RXD_L4E_B) |
+ BIT(HNS3_RXD_OL3E_B) |
BIT(HNS3_RXD_OL4E_B)))) {
u64_stats_update_begin(&ring->syncp);
ring->stats.l3l4_csum_err++;
@@ -2472,6 +2473,8 @@ static int hns3_add_frag(struct hns3_enet_ring *ring, struct hns3_desc *desc,
desc = &ring->desc[ring->next_to_clean];
desc_cb = &ring->desc_cb[ring->next_to_clean];
bd_base_info = le32_to_cpu(desc->rx.bd_base_info);
+ /* make sure HW write desc complete */
+ dma_rmb();
if (!(bd_base_info & BIT(HNS3_RXD_VLD_B)))
return -ENXIO;
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h
index 1db0bd41d209..75669cd0c311 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h
@@ -193,7 +193,6 @@ enum hns3_nic_state {
#define HNS3_VECTOR_INITED 1
#define HNS3_MAX_BD_SIZE 65535
-#define HNS3_MAX_BD_SIZE_OFFSET 16
#define HNS3_MAX_BD_PER_FRAG 8
#define HNS3_MAX_BD_PER_PKT MAX_SKB_FRAGS
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_err.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_err.c
index 1feceff1477c..1f52d11f77b5 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_err.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_err.c
@@ -1317,8 +1317,10 @@ pci_ers_result_t hclge_handle_hw_ras_error(struct hnae3_ae_dev *ae_dev)
hclge_handle_all_ras_errors(hdev);
} else {
if (test_bit(HCLGE_STATE_RST_HANDLING, &hdev->state) ||
- hdev->pdev->revision < 0x21)
+ hdev->pdev->revision < 0x21) {
+ ae_dev->override_pci_need_reset = 1;
return PCI_ERS_RESULT_RECOVERED;
+ }
}
if (status & HCLGE_RAS_REG_ROCEE_ERR_MASK) {
@@ -1327,8 +1329,11 @@ pci_ers_result_t hclge_handle_hw_ras_error(struct hnae3_ae_dev *ae_dev)
}
if (status & HCLGE_RAS_REG_NFE_MASK ||
- status & HCLGE_RAS_REG_ROCEE_ERR_MASK)
+ status & HCLGE_RAS_REG_ROCEE_ERR_MASK) {
+ ae_dev->override_pci_need_reset = 0;
return PCI_ERS_RESULT_NEED_RESET;
+ }
+ ae_dev->override_pci_need_reset = 1;
return PCI_ERS_RESULT_RECOVERED;
}
diff --git a/drivers/net/ethernet/ibm/ehea/ehea_main.c b/drivers/net/ethernet/ibm/ehea/ehea_main.c
index 3baabdc89726..90b62c1412c8 100644
--- a/drivers/net/ethernet/ibm/ehea/ehea_main.c
+++ b/drivers/net/ethernet/ibm/ehea/ehea_main.c
@@ -3160,6 +3160,7 @@ static ssize_t ehea_probe_port(struct device *dev,
if (ehea_add_adapter_mr(adapter)) {
pr_err("creating MR failed\n");
+ of_node_put(eth_dn);
return -EIO;
}
diff --git a/drivers/net/ethernet/mellanox/mlx4/cmd.c b/drivers/net/ethernet/mellanox/mlx4/cmd.c
index c19e74e6ac94..a5d5d6fc1da0 100644
--- a/drivers/net/ethernet/mellanox/mlx4/cmd.c
+++ b/drivers/net/ethernet/mellanox/mlx4/cmd.c
@@ -2645,6 +2645,8 @@ int mlx4_cmd_use_events(struct mlx4_dev *dev)
if (!priv->cmd.context)
return -ENOMEM;
+ if (mlx4_is_mfunc(dev))
+ mutex_lock(&priv->cmd.slave_cmd_mutex);
down_write(&priv->cmd.switch_sem);
for (i = 0; i < priv->cmd.max_cmds; ++i) {
priv->cmd.context[i].token = i;
@@ -2670,6 +2672,8 @@ int mlx4_cmd_use_events(struct mlx4_dev *dev)
down(&priv->cmd.poll_sem);
priv->cmd.use_events = 1;
up_write(&priv->cmd.switch_sem);
+ if (mlx4_is_mfunc(dev))
+ mutex_unlock(&priv->cmd.slave_cmd_mutex);
return err;
}
@@ -2682,6 +2686,8 @@ void mlx4_cmd_use_polling(struct mlx4_dev *dev)
struct mlx4_priv *priv = mlx4_priv(dev);
int i;
+ if (mlx4_is_mfunc(dev))
+ mutex_lock(&priv->cmd.slave_cmd_mutex);
down_write(&priv->cmd.switch_sem);
priv->cmd.use_events = 0;
@@ -2689,9 +2695,12 @@ void mlx4_cmd_use_polling(struct mlx4_dev *dev)
down(&priv->cmd.event_sem);
kfree(priv->cmd.context);
+ priv->cmd.context = NULL;
up(&priv->cmd.poll_sem);
up_write(&priv->cmd.switch_sem);
+ if (mlx4_is_mfunc(dev))
+ mutex_unlock(&priv->cmd.slave_cmd_mutex);
}
struct mlx4_cmd_mailbox *mlx4_alloc_cmd_mailbox(struct mlx4_dev *dev)
diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c
index eb13d3618162..4356f3a58002 100644
--- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c
+++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c
@@ -2719,13 +2719,13 @@ static int qp_get_mtt_size(struct mlx4_qp_context *qpc)
int total_pages;
int total_mem;
int page_offset = (be32_to_cpu(qpc->params2) >> 6) & 0x3f;
+ int tot;
sq_size = 1 << (log_sq_size + log_sq_sride + 4);
rq_size = (srq|rss|xrc) ? 0 : (1 << (log_rq_size + log_rq_stride + 4));
total_mem = sq_size + rq_size;
- total_pages =
- roundup_pow_of_two((total_mem + (page_offset << 6)) >>
- page_shift);
+ tot = (total_mem + (page_offset << 6)) >> page_shift;
+ total_pages = !tot ? 1 : roundup_pow_of_two(tot);
return total_pages;
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c
index 0804b478ad19..a0987cc5fe4a 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c
@@ -424,6 +424,9 @@ int mlx5e_ethtool_set_channels(struct mlx5e_priv *priv,
if (!test_bit(MLX5E_STATE_OPENED, &priv->state)) {
priv->channels.params = new_channels.params;
+ if (!netif_is_rxfh_configured(priv->netdev))
+ mlx5e_build_default_indir_rqt(priv->rss_params.indirection_rqt,
+ MLX5E_INDIR_RQT_SIZE, count);
goto out;
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c
index a1a3e2774989..a66b6ed80b30 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c
@@ -1129,16 +1129,17 @@ static int mlx5e_rep_get_phys_port_name(struct net_device *dev,
struct mlx5e_priv *priv = netdev_priv(dev);
struct mlx5e_rep_priv *rpriv = priv->ppriv;
struct mlx5_eswitch_rep *rep = rpriv->rep;
- int ret, pf_num;
+ unsigned int fn;
+ int ret;
- ret = mlx5_lag_get_pf_num(priv->mdev, &pf_num);
- if (ret)
- return ret;
+ fn = PCI_FUNC(priv->mdev->pdev->devfn);
+ if (fn >= MLX5_MAX_PORTS)
+ return -EOPNOTSUPP;
if (rep->vport == MLX5_VPORT_UPLINK)
- ret = snprintf(buf, len, "p%d", pf_num);
+ ret = snprintf(buf, len, "p%d", fn);
else
- ret = snprintf(buf, len, "pf%dvf%d", pf_num, rep->vport - 1);
+ ret = snprintf(buf, len, "pf%dvf%d", fn, rep->vport - 1);
if (ret >= len)
return -EOPNOTSUPP;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
index be396e5e4e39..3dde5c7e0739 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
@@ -1295,8 +1295,14 @@ static inline void mlx5i_complete_rx_cqe(struct mlx5e_rq *rq,
skb->protocol = *((__be16 *)(skb->data));
- skb->ip_summed = CHECKSUM_COMPLETE;
- skb->csum = csum_unfold((__force __sum16)cqe->check_sum);
+ if (netdev->features & NETIF_F_RXCSUM) {
+ skb->ip_summed = CHECKSUM_COMPLETE;
+ skb->csum = csum_unfold((__force __sum16)cqe->check_sum);
+ stats->csum_complete++;
+ } else {
+ skb->ip_summed = CHECKSUM_NONE;
+ stats->csum_none++;
+ }
if (unlikely(mlx5e_rx_hw_stamp(tstamp)))
skb_hwtstamps(skb)->hwtstamp =
@@ -1315,7 +1321,6 @@ static inline void mlx5i_complete_rx_cqe(struct mlx5e_rq *rq,
skb->dev = netdev;
- stats->csum_complete++;
stats->packets++;
stats->bytes += cqe_bcnt;
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c
index d0b28251abf2..ecd2c747f726 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c
@@ -1931,7 +1931,7 @@ int mlx5_eswitch_set_vport_mac(struct mlx5_eswitch *esw,
u64 node_guid;
int err = 0;
- if (!MLX5_CAP_GEN(esw->dev, vport_group_manager))
+ if (!esw || !MLX5_CAP_GEN(esw->dev, vport_group_manager))
return -EPERM;
if (!LEGAL_VPORT(esw, vport) || is_multicast_ether_addr(mac))
return -EINVAL;
@@ -2005,7 +2005,7 @@ int mlx5_eswitch_get_vport_config(struct mlx5_eswitch *esw,
{
struct mlx5_vport *evport;
- if (!MLX5_CAP_GEN(esw->dev, vport_group_manager))
+ if (!esw || !MLX5_CAP_GEN(esw->dev, vport_group_manager))
return -EPERM;
if (!LEGAL_VPORT(esw, vport))
return -EINVAL;
@@ -2297,19 +2297,24 @@ static int normalize_vports_min_rate(struct mlx5_eswitch *esw, u32 divider)
int mlx5_eswitch_set_vport_rate(struct mlx5_eswitch *esw, int vport,
u32 max_rate, u32 min_rate)
{
- u32 fw_max_bw_share = MLX5_CAP_QOS(esw->dev, max_tsar_bw_share);
- bool min_rate_supported = MLX5_CAP_QOS(esw->dev, esw_bw_share) &&
- fw_max_bw_share >= MLX5_MIN_BW_SHARE;
- bool max_rate_supported = MLX5_CAP_QOS(esw->dev, esw_rate_limit);
struct mlx5_vport *evport;
+ u32 fw_max_bw_share;
u32 previous_min_rate;
u32 divider;
+ bool min_rate_supported;
+ bool max_rate_supported;
int err = 0;
if (!ESW_ALLOWED(esw))
return -EPERM;
if (!LEGAL_VPORT(esw, vport))
return -EINVAL;
+
+ fw_max_bw_share = MLX5_CAP_QOS(esw->dev, max_tsar_bw_share);
+ min_rate_supported = MLX5_CAP_QOS(esw->dev, esw_bw_share) &&
+ fw_max_bw_share >= MLX5_MIN_BW_SHARE;
+ max_rate_supported = MLX5_CAP_QOS(esw->dev, esw_rate_limit);
+
if ((min_rate && !min_rate_supported) || (max_rate && !max_rate_supported))
return -EOPNOTSUPP;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c
index f2cfa012315e..0be3eb86dd84 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c
@@ -263,10 +263,11 @@ static void nested_down_write_ref_node(struct fs_node *node,
}
}
-static void down_write_ref_node(struct fs_node *node)
+static void down_write_ref_node(struct fs_node *node, bool locked)
{
if (node) {
- down_write(&node->lock);
+ if (!locked)
+ down_write(&node->lock);
refcount_inc(&node->refcount);
}
}
@@ -277,13 +278,14 @@ static void up_read_ref_node(struct fs_node *node)
up_read(&node->lock);
}
-static void up_write_ref_node(struct fs_node *node)
+static void up_write_ref_node(struct fs_node *node, bool locked)
{
refcount_dec(&node->refcount);
- up_write(&node->lock);
+ if (!locked)
+ up_write(&node->lock);
}
-static void tree_put_node(struct fs_node *node)
+static void tree_put_node(struct fs_node *node, bool locked)
{
struct fs_node *parent_node = node->parent;
@@ -294,27 +296,27 @@ static void tree_put_node(struct fs_node *node)
/* Only root namespace doesn't have parent and we just
* need to free its node.
*/
- down_write_ref_node(parent_node);
+ down_write_ref_node(parent_node, locked);
list_del_init(&node->list);
if (node->del_sw_func)
node->del_sw_func(node);
- up_write_ref_node(parent_node);
+ up_write_ref_node(parent_node, locked);
} else {
kfree(node);
}
node = NULL;
}
if (!node && parent_node)
- tree_put_node(parent_node);
+ tree_put_node(parent_node, locked);
}
-static int tree_remove_node(struct fs_node *node)
+static int tree_remove_node(struct fs_node *node, bool locked)
{
if (refcount_read(&node->refcount) > 1) {
refcount_dec(&node->refcount);
return -EEXIST;
}
- tree_put_node(node);
+ tree_put_node(node, locked);
return 0;
}
@@ -420,22 +422,34 @@ static void del_sw_flow_table(struct fs_node *node)
kfree(ft);
}
-static void del_sw_hw_rule(struct fs_node *node)
+static void modify_fte(struct fs_fte *fte)
{
struct mlx5_flow_root_namespace *root;
- struct mlx5_flow_rule *rule;
struct mlx5_flow_table *ft;
struct mlx5_flow_group *fg;
- struct fs_fte *fte;
- int modify_mask;
- struct mlx5_core_dev *dev = get_dev(node);
+ struct mlx5_core_dev *dev;
int err;
- bool update_fte = false;
- fs_get_obj(rule, node);
- fs_get_obj(fte, rule->node.parent);
fs_get_obj(fg, fte->node.parent);
fs_get_obj(ft, fg->node.parent);
+ dev = get_dev(&fte->node);
+
+ root = find_root(&ft->node);
+ err = root->cmds->update_fte(dev, ft, fg->id, fte->modify_mask, fte);
+ if (err)
+ mlx5_core_warn(dev,
+ "%s can't del rule fg id=%d fte_index=%d\n",
+ __func__, fg->id, fte->index);
+ fte->modify_mask = 0;
+}
+
+static void del_sw_hw_rule(struct fs_node *node)
+{
+ struct mlx5_flow_rule *rule;
+ struct fs_fte *fte;
+
+ fs_get_obj(rule, node);
+ fs_get_obj(fte, rule->node.parent);
trace_mlx5_fs_del_rule(rule);
if (rule->sw_action == MLX5_FLOW_CONTEXT_ACTION_FWD_NEXT_PRIO) {
mutex_lock(&rule->dest_attr.ft->lock);
@@ -445,27 +459,19 @@ static void del_sw_hw_rule(struct fs_node *node)
if (rule->dest_attr.type == MLX5_FLOW_DESTINATION_TYPE_COUNTER &&
--fte->dests_size) {
- modify_mask = BIT(MLX5_SET_FTE_MODIFY_ENABLE_MASK_ACTION) |
- BIT(MLX5_SET_FTE_MODIFY_ENABLE_MASK_FLOW_COUNTERS);
+ fte->modify_mask |=
+ BIT(MLX5_SET_FTE_MODIFY_ENABLE_MASK_ACTION) |
+ BIT(MLX5_SET_FTE_MODIFY_ENABLE_MASK_FLOW_COUNTERS);
fte->action.action &= ~MLX5_FLOW_CONTEXT_ACTION_COUNT;
- update_fte = true;
goto out;
}
if ((fte->action.action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST) &&
--fte->dests_size) {
- modify_mask = BIT(MLX5_SET_FTE_MODIFY_ENABLE_MASK_DESTINATION_LIST);
- update_fte = true;
+ fte->modify_mask |=
+ BIT(MLX5_SET_FTE_MODIFY_ENABLE_MASK_DESTINATION_LIST);
}
out:
- root = find_root(&ft->node);
- if (update_fte && fte->dests_size) {
- err = root->cmds->update_fte(dev, ft, fg->id, modify_mask, fte);
- if (err)
- mlx5_core_warn(dev,
- "%s can't del rule fg id=%d fte_index=%d\n",
- __func__, fg->id, fte->index);
- }
kfree(rule);
}
@@ -491,6 +497,7 @@ static void del_hw_fte(struct fs_node *node)
mlx5_core_warn(dev,
"flow steering can't delete fte in index %d of flow group id %d\n",
fte->index, fg->id);
+ node->active = 0;
}
}
@@ -591,7 +598,7 @@ static struct fs_fte *alloc_fte(struct mlx5_flow_table *ft,
fte->node.type = FS_TYPE_FLOW_ENTRY;
fte->action = *flow_act;
- tree_init_node(&fte->node, del_hw_fte, del_sw_fte);
+ tree_init_node(&fte->node, NULL, del_sw_fte);
return fte;
}
@@ -858,7 +865,7 @@ static int _mlx5_modify_rule_destination(struct mlx5_flow_rule *rule,
fs_get_obj(fte, rule->node.parent);
if (!(fte->action.action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST))
return -EINVAL;
- down_write_ref_node(&fte->node);
+ down_write_ref_node(&fte->node, false);
fs_get_obj(fg, fte->node.parent);
fs_get_obj(ft, fg->node.parent);
@@ -866,7 +873,7 @@ static int _mlx5_modify_rule_destination(struct mlx5_flow_rule *rule,
root = find_root(&ft->node);
err = root->cmds->update_fte(get_dev(&ft->node), ft, fg->id,
modify_mask, fte);
- up_write_ref_node(&fte->node);
+ up_write_ref_node(&fte->node, false);
return err;
}
@@ -1016,11 +1023,11 @@ static struct mlx5_flow_table *__mlx5_create_flow_table(struct mlx5_flow_namespa
if (err)
goto destroy_ft;
ft->node.active = true;
- down_write_ref_node(&fs_prio->node);
+ down_write_ref_node(&fs_prio->node, false);
tree_add_node(&ft->node, &fs_prio->node);
list_add_flow_table(ft, fs_prio);
fs_prio->num_ft++;
- up_write_ref_node(&fs_prio->node);
+ up_write_ref_node(&fs_prio->node, false);
mutex_unlock(&root->chain_lock);
trace_mlx5_fs_add_ft(ft);
return ft;
@@ -1114,17 +1121,17 @@ struct mlx5_flow_group *mlx5_create_flow_group(struct mlx5_flow_table *ft,
if (ft->autogroup.active)
return ERR_PTR(-EPERM);
- down_write_ref_node(&ft->node);
+ down_write_ref_node(&ft->node, false);
fg = alloc_insert_flow_group(ft, match_criteria_enable, match_criteria,
start_index, end_index,
ft->node.children.prev);
- up_write_ref_node(&ft->node);
+ up_write_ref_node(&ft->node, false);
if (IS_ERR(fg))
return fg;
err = root->cmds->create_flow_group(dev, ft, fg_in, &fg->id);
if (err) {
- tree_put_node(&fg->node);
+ tree_put_node(&fg->node, false);
return ERR_PTR(err);
}
trace_mlx5_fs_add_fg(fg);
@@ -1521,10 +1528,10 @@ static void free_match_list(struct match_list_head *head)
struct match_list *iter, *match_tmp;
list_del(&head->first.list);
- tree_put_node(&head->first.g->node);
+ tree_put_node(&head->first.g->node, false);
list_for_each_entry_safe(iter, match_tmp, &head->list,
list) {
- tree_put_node(&iter->g->node);
+ tree_put_node(&iter->g->node, false);
list_del(&iter->list);
kfree(iter);
}
@@ -1601,11 +1608,16 @@ lookup_fte_locked(struct mlx5_flow_group *g,
fte_tmp = NULL;
goto out;
}
+ if (!fte_tmp->node.active) {
+ tree_put_node(&fte_tmp->node, false);
+ fte_tmp = NULL;
+ goto out;
+ }
nested_down_write_ref_node(&fte_tmp->node, FS_LOCK_CHILD);
out:
if (take_write)
- up_write_ref_node(&g->node);
+ up_write_ref_node(&g->node, false);
else
up_read_ref_node(&g->node);
return fte_tmp;
@@ -1647,8 +1659,8 @@ search_again_locked:
continue;
rule = add_rule_fg(g, spec->match_value,
flow_act, dest, dest_num, fte_tmp);
- up_write_ref_node(&fte_tmp->node);
- tree_put_node(&fte_tmp->node);
+ up_write_ref_node(&fte_tmp->node, false);
+ tree_put_node(&fte_tmp->node, false);
kmem_cache_free(steering->ftes_cache, fte);
return rule;
}
@@ -1684,7 +1696,7 @@ skip_search:
err = insert_fte(g, fte);
if (err) {
- up_write_ref_node(&g->node);
+ up_write_ref_node(&g->node, false);
if (err == -ENOSPC)
continue;
kmem_cache_free(steering->ftes_cache, fte);
@@ -1692,11 +1704,11 @@ skip_search:
}
nested_down_write_ref_node(&fte->node, FS_LOCK_CHILD);
- up_write_ref_node(&g->node);
+ up_write_ref_node(&g->node, false);
rule = add_rule_fg(g, spec->match_value,
flow_act, dest, dest_num, fte);
- up_write_ref_node(&fte->node);
- tree_put_node(&fte->node);
+ up_write_ref_node(&fte->node, false);
+ tree_put_node(&fte->node, false);
return rule;
}
rule = ERR_PTR(-ENOENT);
@@ -1738,7 +1750,7 @@ search_again_locked:
err = build_match_list(&match_head, ft, spec);
if (err) {
if (take_write)
- up_write_ref_node(&ft->node);
+ up_write_ref_node(&ft->node, false);
else
up_read_ref_node(&ft->node);
return ERR_PTR(err);
@@ -1753,7 +1765,7 @@ search_again_locked:
if (!IS_ERR(rule) ||
(PTR_ERR(rule) != -ENOENT && PTR_ERR(rule) != -EAGAIN)) {
if (take_write)
- up_write_ref_node(&ft->node);
+ up_write_ref_node(&ft->node, false);
return rule;
}
@@ -1769,12 +1781,12 @@ search_again_locked:
g = alloc_auto_flow_group(ft, spec);
if (IS_ERR(g)) {
rule = ERR_CAST(g);
- up_write_ref_node(&ft->node);
+ up_write_ref_node(&ft->node, false);
return rule;
}
nested_down_write_ref_node(&g->node, FS_LOCK_PARENT);
- up_write_ref_node(&ft->node);
+ up_write_ref_node(&ft->node, false);
err = create_auto_flow_group(ft, g);
if (err)
@@ -1793,17 +1805,17 @@ search_again_locked:
}
nested_down_write_ref_node(&fte->node, FS_LOCK_CHILD);
- up_write_ref_node(&g->node);
+ up_write_ref_node(&g->node, false);
rule = add_rule_fg(g, spec->match_value, flow_act, dest,
dest_num, fte);
- up_write_ref_node(&fte->node);
- tree_put_node(&fte->node);
- tree_put_node(&g->node);
+ up_write_ref_node(&fte->node, false);
+ tree_put_node(&fte->node, false);
+ tree_put_node(&g->node, false);
return rule;
err_release_fg:
- up_write_ref_node(&g->node);
- tree_put_node(&g->node);
+ up_write_ref_node(&g->node, false);
+ tree_put_node(&g->node, false);
return ERR_PTR(err);
}
@@ -1866,10 +1878,33 @@ EXPORT_SYMBOL(mlx5_add_flow_rules);
void mlx5_del_flow_rules(struct mlx5_flow_handle *handle)
{
+ struct fs_fte *fte;
int i;
+ /* In order to consolidate the HW changes we lock the FTE for other
+ * changes, and increase its refcount, in order not to perform the
+ * "del" functions of the FTE. Will handle them here.
+ * The removal of the rules is done under locked FTE.
+ * After removing all the handle's rules, if there are remaining
+ * rules, it means we just need to modify the FTE in FW, and
+ * unlock/decrease the refcount we increased before.
+ * Otherwise, it means the FTE should be deleted. First delete the
+ * FTE in FW. Then, unlock the FTE, and proceed the tree_put_node of
+ * the FTE, which will handle the last decrease of the refcount, as
+ * well as required handling of its parent.
+ */
+ fs_get_obj(fte, handle->rule[0]->node.parent);
+ down_write_ref_node(&fte->node, false);
for (i = handle->num_rules - 1; i >= 0; i--)
- tree_remove_node(&handle->rule[i]->node);
+ tree_remove_node(&handle->rule[i]->node, true);
+ if (fte->modify_mask && fte->dests_size) {
+ modify_fte(fte);
+ up_write_ref_node(&fte->node, false);
+ } else {
+ del_hw_fte(&fte->node);
+ up_write(&fte->node.lock);
+ tree_put_node(&fte->node, false);
+ }
kfree(handle);
}
EXPORT_SYMBOL(mlx5_del_flow_rules);
@@ -1972,7 +2007,7 @@ int mlx5_destroy_flow_table(struct mlx5_flow_table *ft)
mutex_unlock(&root->chain_lock);
return err;
}
- if (tree_remove_node(&ft->node))
+ if (tree_remove_node(&ft->node, false))
mlx5_core_warn(get_dev(&ft->node), "Flow table %d wasn't destroyed, refcount > 1\n",
ft->id);
mutex_unlock(&root->chain_lock);
@@ -1983,7 +2018,7 @@ EXPORT_SYMBOL(mlx5_destroy_flow_table);
void mlx5_destroy_flow_group(struct mlx5_flow_group *fg)
{
- if (tree_remove_node(&fg->node))
+ if (tree_remove_node(&fg->node, false))
mlx5_core_warn(get_dev(&fg->node), "Flow group %d wasn't destroyed, refcount > 1\n",
fg->id);
}
@@ -2367,8 +2402,8 @@ static void clean_tree(struct fs_node *node)
tree_get_node(node);
list_for_each_entry_safe(iter, temp, &node->children, list)
clean_tree(iter);
- tree_put_node(node);
- tree_remove_node(node);
+ tree_put_node(node, false);
+ tree_remove_node(node, false);
}
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h
index 2dc86347af58..87de0e4d9124 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h
@@ -172,6 +172,7 @@ struct fs_fte {
enum fs_fte_status status;
struct mlx5_fc *counter;
struct rhash_head hash;
+ int modify_mask;
};
/* Type of children is mlx5_flow_table/namespace */
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag.c b/drivers/net/ethernet/mellanox/mlx5/core/lag.c
index 48aa6e030bcf..959605559858 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/lag.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/lag.c
@@ -595,27 +595,6 @@ void mlx5_lag_add(struct mlx5_core_dev *dev, struct net_device *netdev)
err);
}
-int mlx5_lag_get_pf_num(struct mlx5_core_dev *dev, int *pf_num)
-{
- struct mlx5_lag *ldev;
- int n;
-
- ldev = mlx5_lag_dev_get(dev);
- if (!ldev) {
- mlx5_core_warn(dev, "no lag device, can't get pf num\n");
- return -EINVAL;
- }
-
- for (n = 0; n < MLX5_MAX_PORTS; n++)
- if (ldev->pf[n].dev == dev) {
- *pf_num = n;
- return 0;
- }
-
- mlx5_core_warn(dev, "wasn't able to locate pf in the lag device\n");
- return -EINVAL;
-}
-
/* Must be called with intf_mutex held */
void mlx5_lag_remove(struct mlx5_core_dev *dev)
{
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c
index 8391dde869a7..70cc906a102b 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/main.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c
@@ -465,20 +465,17 @@ static int handle_hca_cap_odp(struct mlx5_core_dev *dev)
void *set_hca_cap;
void *set_ctx;
int set_sz;
+ bool do_set = false;
int err;
- if (!MLX5_CAP_GEN(dev, pg))
+ if (!IS_ENABLED(CONFIG_INFINIBAND_ON_DEMAND_PAGING) ||
+ !MLX5_CAP_GEN(dev, pg))
return 0;
err = mlx5_core_get_caps(dev, MLX5_CAP_ODP);
if (err)
return err;
- if (!(MLX5_CAP_ODP_MAX(dev, ud_odp_caps.srq_receive) ||
- MLX5_CAP_ODP_MAX(dev, rc_odp_caps.srq_receive) ||
- MLX5_CAP_ODP_MAX(dev, xrc_odp_caps.srq_receive)))
- return 0;
-
set_sz = MLX5_ST_SZ_BYTES(set_hca_cap_in);
set_ctx = kzalloc(set_sz, GFP_KERNEL);
if (!set_ctx)
@@ -488,19 +485,30 @@ static int handle_hca_cap_odp(struct mlx5_core_dev *dev)
memcpy(set_hca_cap, dev->caps.hca_cur[MLX5_CAP_ODP],
MLX5_ST_SZ_BYTES(odp_cap));
- /* set ODP SRQ support for RC/UD and XRC transports */
- MLX5_SET(odp_cap, set_hca_cap, ud_odp_caps.srq_receive,
- MLX5_CAP_ODP_MAX(dev, ud_odp_caps.srq_receive));
-
- MLX5_SET(odp_cap, set_hca_cap, rc_odp_caps.srq_receive,
- MLX5_CAP_ODP_MAX(dev, rc_odp_caps.srq_receive));
-
- MLX5_SET(odp_cap, set_hca_cap, xrc_odp_caps.srq_receive,
- MLX5_CAP_ODP_MAX(dev, xrc_odp_caps.srq_receive));
-
- err = set_caps(dev, set_ctx, set_sz, MLX5_SET_HCA_CAP_OP_MOD_ODP);
+#define ODP_CAP_SET_MAX(dev, field) \
+ do { \
+ u32 _res = MLX5_CAP_ODP_MAX(dev, field); \
+ if (_res) { \
+ do_set = true; \
+ MLX5_SET(odp_cap, set_hca_cap, field, _res); \
+ } \
+ } while (0)
+
+ ODP_CAP_SET_MAX(dev, ud_odp_caps.srq_receive);
+ ODP_CAP_SET_MAX(dev, rc_odp_caps.srq_receive);
+ ODP_CAP_SET_MAX(dev, xrc_odp_caps.srq_receive);
+ ODP_CAP_SET_MAX(dev, xrc_odp_caps.send);
+ ODP_CAP_SET_MAX(dev, xrc_odp_caps.receive);
+ ODP_CAP_SET_MAX(dev, xrc_odp_caps.write);
+ ODP_CAP_SET_MAX(dev, xrc_odp_caps.read);
+ ODP_CAP_SET_MAX(dev, xrc_odp_caps.atomic);
+
+ if (do_set)
+ err = set_caps(dev, set_ctx, set_sz,
+ MLX5_SET_HCA_CAP_OP_MOD_ODP);
kfree(set_ctx);
+
return err;
}
@@ -577,6 +585,33 @@ query_ex:
return err;
}
+static int set_hca_cap(struct mlx5_core_dev *dev)
+{
+ struct pci_dev *pdev = dev->pdev;
+ int err;
+
+ err = handle_hca_cap(dev);
+ if (err) {
+ dev_err(&pdev->dev, "handle_hca_cap failed\n");
+ goto out;
+ }
+
+ err = handle_hca_cap_atomic(dev);
+ if (err) {
+ dev_err(&pdev->dev, "handle_hca_cap_atomic failed\n");
+ goto out;
+ }
+
+ err = handle_hca_cap_odp(dev);
+ if (err) {
+ dev_err(&pdev->dev, "handle_hca_cap_odp failed\n");
+ goto out;
+ }
+
+out:
+ return err;
+}
+
static int set_hca_ctrl(struct mlx5_core_dev *dev)
{
struct mlx5_reg_host_endianness he_in;
@@ -969,21 +1004,9 @@ static int mlx5_load_one(struct mlx5_core_dev *dev, struct mlx5_priv *priv,
goto reclaim_boot_pages;
}
- err = handle_hca_cap(dev);
+ err = set_hca_cap(dev);
if (err) {
- dev_err(&pdev->dev, "handle_hca_cap failed\n");
- goto reclaim_boot_pages;
- }
-
- err = handle_hca_cap_atomic(dev);
- if (err) {
- dev_err(&pdev->dev, "handle_hca_cap_atomic failed\n");
- goto reclaim_boot_pages;
- }
-
- err = handle_hca_cap_odp(dev);
- if (err) {
- dev_err(&pdev->dev, "handle_hca_cap_odp failed\n");
+ dev_err(&pdev->dev, "set_hca_cap failed\n");
goto reclaim_boot_pages;
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h
index 9529cf9623e3..7b331674622c 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h
@@ -188,8 +188,6 @@ static inline int mlx5_lag_is_lacp_owner(struct mlx5_core_dev *dev)
MLX5_CAP_GEN(dev, lag_master);
}
-int mlx5_lag_get_pf_num(struct mlx5_core_dev *dev, int *pf_num);
-
void mlx5_reload_interface(struct mlx5_core_dev *mdev, int protocol);
void mlx5_lag_update(struct mlx5_core_dev *dev);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/qp.c b/drivers/net/ethernet/mellanox/mlx5/core/qp.c
index 370ca94b6775..b8ba74de9555 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/qp.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/qp.c
@@ -40,6 +40,9 @@
#include "mlx5_core.h"
#include "lib/eq.h"
+static int mlx5_core_drain_dct(struct mlx5_core_dev *dev,
+ struct mlx5_core_dct *dct);
+
static struct mlx5_core_rsc_common *
mlx5_get_rsc(struct mlx5_qp_table *table, u32 rsn)
{
@@ -227,20 +230,49 @@ static void destroy_resource_common(struct mlx5_core_dev *dev,
wait_for_completion(&qp->common.free);
}
+static int _mlx5_core_destroy_dct(struct mlx5_core_dev *dev,
+ struct mlx5_core_dct *dct, bool need_cleanup)
+{
+ u32 out[MLX5_ST_SZ_DW(destroy_dct_out)] = {0};
+ u32 in[MLX5_ST_SZ_DW(destroy_dct_in)] = {0};
+ struct mlx5_core_qp *qp = &dct->mqp;
+ int err;
+
+ err = mlx5_core_drain_dct(dev, dct);
+ if (err) {
+ if (dev->state == MLX5_DEVICE_STATE_INTERNAL_ERROR) {
+ goto destroy;
+ } else {
+ mlx5_core_warn(
+ dev, "failed drain DCT 0x%x with error 0x%x\n",
+ qp->qpn, err);
+ return err;
+ }
+ }
+ wait_for_completion(&dct->drained);
+destroy:
+ if (need_cleanup)
+ destroy_resource_common(dev, &dct->mqp);
+ MLX5_SET(destroy_dct_in, in, opcode, MLX5_CMD_OP_DESTROY_DCT);
+ MLX5_SET(destroy_dct_in, in, dctn, qp->qpn);
+ MLX5_SET(destroy_dct_in, in, uid, qp->uid);
+ err = mlx5_cmd_exec(dev, (void *)&in, sizeof(in),
+ (void *)&out, sizeof(out));
+ return err;
+}
+
int mlx5_core_create_dct(struct mlx5_core_dev *dev,
struct mlx5_core_dct *dct,
- u32 *in, int inlen)
+ u32 *in, int inlen,
+ u32 *out, int outlen)
{
- u32 out[MLX5_ST_SZ_DW(create_dct_out)] = {0};
- u32 din[MLX5_ST_SZ_DW(destroy_dct_in)] = {0};
- u32 dout[MLX5_ST_SZ_DW(destroy_dct_out)] = {0};
struct mlx5_core_qp *qp = &dct->mqp;
int err;
init_completion(&dct->drained);
MLX5_SET(create_dct_in, in, opcode, MLX5_CMD_OP_CREATE_DCT);
- err = mlx5_cmd_exec(dev, in, inlen, &out, sizeof(out));
+ err = mlx5_cmd_exec(dev, in, inlen, out, outlen);
if (err) {
mlx5_core_warn(dev, "create DCT failed, ret %d\n", err);
return err;
@@ -254,11 +286,7 @@ int mlx5_core_create_dct(struct mlx5_core_dev *dev,
return 0;
err_cmd:
- MLX5_SET(destroy_dct_in, din, opcode, MLX5_CMD_OP_DESTROY_DCT);
- MLX5_SET(destroy_dct_in, din, dctn, qp->qpn);
- MLX5_SET(destroy_dct_in, din, uid, qp->uid);
- mlx5_cmd_exec(dev, (void *)&in, sizeof(din),
- (void *)&out, sizeof(dout));
+ _mlx5_core_destroy_dct(dev, dct, false);
return err;
}
EXPORT_SYMBOL_GPL(mlx5_core_create_dct);
@@ -323,29 +351,7 @@ static int mlx5_core_drain_dct(struct mlx5_core_dev *dev,
int mlx5_core_destroy_dct(struct mlx5_core_dev *dev,
struct mlx5_core_dct *dct)
{
- u32 out[MLX5_ST_SZ_DW(destroy_dct_out)] = {0};
- u32 in[MLX5_ST_SZ_DW(destroy_dct_in)] = {0};
- struct mlx5_core_qp *qp = &dct->mqp;
- int err;
-
- err = mlx5_core_drain_dct(dev, dct);
- if (err) {
- if (dev->state == MLX5_DEVICE_STATE_INTERNAL_ERROR) {
- goto destroy;
- } else {
- mlx5_core_warn(dev, "failed drain DCT 0x%x with error 0x%x\n", qp->qpn, err);
- return err;
- }
- }
- wait_for_completion(&dct->drained);
-destroy:
- destroy_resource_common(dev, &dct->mqp);
- MLX5_SET(destroy_dct_in, in, opcode, MLX5_CMD_OP_DESTROY_DCT);
- MLX5_SET(destroy_dct_in, in, dctn, qp->qpn);
- MLX5_SET(destroy_dct_in, in, uid, qp->uid);
- err = mlx5_cmd_exec(dev, (void *)&in, sizeof(in),
- (void *)&out, sizeof(out));
- return err;
+ return _mlx5_core_destroy_dct(dev, dct, true);
}
EXPORT_SYMBOL_GPL(mlx5_core_destroy_dct);
diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_env.c b/drivers/net/ethernet/mellanox/mlxsw/core_env.c
index 7a15e932ed2f..c1c1965d7acc 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/core_env.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/core_env.c
@@ -113,7 +113,7 @@ int mlxsw_env_module_temp_thresholds_get(struct mlxsw_core *core, int module,
return 0;
default:
/* Do not consider thresholds for zero temperature. */
- if (!MLXSW_REG_MTMP_TEMP_TO_MC(module_temp)) {
+ if (MLXSW_REG_MTMP_TEMP_TO_MC(module_temp) == 0) {
*temp = 0;
return 0;
}
diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_thermal.c b/drivers/net/ethernet/mellanox/mlxsw/core_thermal.c
index 0b85c7252f9e..472f63f9fac5 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/core_thermal.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/core_thermal.c
@@ -111,7 +111,6 @@ struct mlxsw_thermal {
struct mlxsw_thermal_trip trips[MLXSW_THERMAL_NUM_TRIPS];
enum thermal_device_mode mode;
struct mlxsw_thermal_module *tz_module_arr;
- unsigned int tz_module_num;
};
static inline u8 mlxsw_state_to_duty(int state)
@@ -711,6 +710,9 @@ mlxsw_thermal_module_init(struct device *dev, struct mlxsw_core *core,
module = mlxsw_reg_pmlp_module_get(pmlp_pl, 0);
module_tz = &thermal->tz_module_arr[module];
+ /* Skip if parent is already set (case of port split). */
+ if (module_tz->parent)
+ return 0;
module_tz->module = module;
module_tz->parent = thermal;
memcpy(module_tz->trips, default_thermal_trips,
@@ -718,13 +720,7 @@ mlxsw_thermal_module_init(struct device *dev, struct mlxsw_core *core,
/* Initialize all trip point. */
mlxsw_thermal_module_trips_reset(module_tz);
/* Update trip point according to the module data. */
- err = mlxsw_thermal_module_trips_update(dev, core, module_tz);
- if (err)
- return err;
-
- thermal->tz_module_num++;
-
- return 0;
+ return mlxsw_thermal_module_trips_update(dev, core, module_tz);
}
static void mlxsw_thermal_module_fini(struct mlxsw_thermal_module *module_tz)
@@ -732,6 +728,7 @@ static void mlxsw_thermal_module_fini(struct mlxsw_thermal_module *module_tz)
if (module_tz && module_tz->tzdev) {
mlxsw_thermal_module_tz_fini(module_tz->tzdev);
module_tz->tzdev = NULL;
+ module_tz->parent = NULL;
}
}
@@ -740,6 +737,7 @@ mlxsw_thermal_modules_init(struct device *dev, struct mlxsw_core *core,
struct mlxsw_thermal *thermal)
{
unsigned int module_count = mlxsw_core_max_ports(core);
+ struct mlxsw_thermal_module *module_tz;
int i, err;
thermal->tz_module_arr = kcalloc(module_count,
@@ -754,8 +752,11 @@ mlxsw_thermal_modules_init(struct device *dev, struct mlxsw_core *core,
goto err_unreg_tz_module_arr;
}
- for (i = 0; i < thermal->tz_module_num; i++) {
- err = mlxsw_thermal_module_tz_init(&thermal->tz_module_arr[i]);
+ for (i = 0; i < module_count - 1; i++) {
+ module_tz = &thermal->tz_module_arr[i];
+ if (!module_tz->parent)
+ continue;
+ err = mlxsw_thermal_module_tz_init(module_tz);
if (err)
goto err_unreg_tz_module_arr;
}
diff --git a/drivers/net/ethernet/mellanox/mlxsw/minimal.c b/drivers/net/ethernet/mellanox/mlxsw/minimal.c
index 68bee9572a1b..00c390024350 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/minimal.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/minimal.c
@@ -34,6 +34,18 @@ struct mlxsw_m_port {
u8 module;
};
+static int mlxsw_m_base_mac_get(struct mlxsw_m *mlxsw_m)
+{
+ char spad_pl[MLXSW_REG_SPAD_LEN] = {0};
+ int err;
+
+ err = mlxsw_reg_query(mlxsw_m->core, MLXSW_REG(spad), spad_pl);
+ if (err)
+ return err;
+ mlxsw_reg_spad_base_mac_memcpy_from(spad_pl, mlxsw_m->base_mac);
+ return 0;
+}
+
static int mlxsw_m_port_dummy_open_stop(struct net_device *dev)
{
return 0;
@@ -314,6 +326,12 @@ static int mlxsw_m_init(struct mlxsw_core *mlxsw_core,
mlxsw_m->core = mlxsw_core;
mlxsw_m->bus_info = mlxsw_bus_info;
+ err = mlxsw_m_base_mac_get(mlxsw_m);
+ if (err) {
+ dev_err(mlxsw_m->bus_info->dev, "Failed to get base mac\n");
+ return err;
+ }
+
err = mlxsw_m_ports_create(mlxsw_m);
if (err) {
dev_err(mlxsw_m->bus_info->dev, "Failed to create ports\n");
diff --git a/drivers/net/ethernet/micrel/ks8851.c b/drivers/net/ethernet/micrel/ks8851.c
index bd6e9014bc74..7849119d407a 100644
--- a/drivers/net/ethernet/micrel/ks8851.c
+++ b/drivers/net/ethernet/micrel/ks8851.c
@@ -142,6 +142,12 @@ struct ks8851_net {
static int msg_enable;
+/* SPI frame opcodes */
+#define KS_SPIOP_RD (0x00)
+#define KS_SPIOP_WR (0x40)
+#define KS_SPIOP_RXFIFO (0x80)
+#define KS_SPIOP_TXFIFO (0xC0)
+
/* shift for byte-enable data */
#define BYTE_EN(_x) ((_x) << 2)
@@ -535,9 +541,8 @@ static void ks8851_rx_pkts(struct ks8851_net *ks)
/* set dma read address */
ks8851_wrreg16(ks, KS_RXFDPR, RXFDPR_RXFPAI | 0x00);
- /* start the packet dma process, and set auto-dequeue rx */
- ks8851_wrreg16(ks, KS_RXQCR,
- ks->rc_rxqcr | RXQCR_SDA | RXQCR_ADRFE);
+ /* start DMA access */
+ ks8851_wrreg16(ks, KS_RXQCR, ks->rc_rxqcr | RXQCR_SDA);
if (rxlen > 4) {
unsigned int rxalign;
@@ -568,7 +573,8 @@ static void ks8851_rx_pkts(struct ks8851_net *ks)
}
}
- ks8851_wrreg16(ks, KS_RXQCR, ks->rc_rxqcr);
+ /* end DMA access and dequeue packet */
+ ks8851_wrreg16(ks, KS_RXQCR, ks->rc_rxqcr | RXQCR_RRXEF);
}
}
@@ -785,6 +791,15 @@ static void ks8851_tx_work(struct work_struct *work)
static int ks8851_net_open(struct net_device *dev)
{
struct ks8851_net *ks = netdev_priv(dev);
+ int ret;
+
+ ret = request_threaded_irq(dev->irq, NULL, ks8851_irq,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ dev->name, ks);
+ if (ret < 0) {
+ netdev_err(dev, "failed to get irq\n");
+ return ret;
+ }
/* lock the card, even if we may not actually be doing anything
* else at the moment */
@@ -849,6 +864,7 @@ static int ks8851_net_open(struct net_device *dev)
netif_dbg(ks, ifup, ks->netdev, "network device up\n");
mutex_unlock(&ks->lock);
+ mii_check_link(&ks->mii);
return 0;
}
@@ -899,6 +915,8 @@ static int ks8851_net_stop(struct net_device *dev)
dev_kfree_skb(txb);
}
+ free_irq(dev->irq, ks);
+
return 0;
}
@@ -1508,6 +1526,7 @@ static int ks8851_probe(struct spi_device *spi)
spi_set_drvdata(spi, ks);
+ netif_carrier_off(ks->netdev);
ndev->if_port = IF_PORT_100BASET;
ndev->netdev_ops = &ks8851_netdev_ops;
ndev->irq = spi->irq;
@@ -1529,14 +1548,6 @@ static int ks8851_probe(struct spi_device *spi)
ks8851_read_selftest(ks);
ks8851_init_mac(ks);
- ret = request_threaded_irq(spi->irq, NULL, ks8851_irq,
- IRQF_TRIGGER_LOW | IRQF_ONESHOT,
- ndev->name, ks);
- if (ret < 0) {
- dev_err(&spi->dev, "failed to get irq\n");
- goto err_irq;
- }
-
ret = register_netdev(ndev);
if (ret) {
dev_err(&spi->dev, "failed to register network device\n");
@@ -1549,14 +1560,10 @@ static int ks8851_probe(struct spi_device *spi)
return 0;
-
err_netdev:
- free_irq(ndev->irq, ks);
-
-err_irq:
+err_id:
if (gpio_is_valid(gpio))
gpio_set_value(gpio, 0);
-err_id:
regulator_disable(ks->vdd_reg);
err_reg:
regulator_disable(ks->vdd_io);
@@ -1574,7 +1581,6 @@ static int ks8851_remove(struct spi_device *spi)
dev_info(&spi->dev, "remove\n");
unregister_netdev(priv->netdev);
- free_irq(spi->irq, priv);
if (gpio_is_valid(priv->gpio))
gpio_set_value(priv->gpio, 0);
regulator_disable(priv->vdd_reg);
diff --git a/drivers/net/ethernet/micrel/ks8851.h b/drivers/net/ethernet/micrel/ks8851.h
index 852256ef1f22..23da1e3ee429 100644
--- a/drivers/net/ethernet/micrel/ks8851.h
+++ b/drivers/net/ethernet/micrel/ks8851.h
@@ -11,9 +11,15 @@
*/
#define KS_CCR 0x08
+#define CCR_LE (1 << 10) /* KSZ8851-16MLL */
#define CCR_EEPROM (1 << 9)
-#define CCR_SPI (1 << 8)
-#define CCR_32PIN (1 << 0)
+#define CCR_SPI (1 << 8) /* KSZ8851SNL */
+#define CCR_8BIT (1 << 7) /* KSZ8851-16MLL */
+#define CCR_16BIT (1 << 6) /* KSZ8851-16MLL */
+#define CCR_32BIT (1 << 5) /* KSZ8851-16MLL */
+#define CCR_SHARED (1 << 4) /* KSZ8851-16MLL */
+#define CCR_48PIN (1 << 1) /* KSZ8851-16MLL */
+#define CCR_32PIN (1 << 0) /* KSZ8851SNL */
/* MAC address registers */
#define KS_MAR(_m) (0x15 - (_m))
@@ -112,13 +118,13 @@
#define RXCR1_RXE (1 << 0)
#define KS_RXCR2 0x76
-#define RXCR2_SRDBL_MASK (0x7 << 5)
-#define RXCR2_SRDBL_SHIFT (5)
-#define RXCR2_SRDBL_4B (0x0 << 5)
-#define RXCR2_SRDBL_8B (0x1 << 5)
-#define RXCR2_SRDBL_16B (0x2 << 5)
-#define RXCR2_SRDBL_32B (0x3 << 5)
-#define RXCR2_SRDBL_FRAME (0x4 << 5)
+#define RXCR2_SRDBL_MASK (0x7 << 5) /* KSZ8851SNL */
+#define RXCR2_SRDBL_SHIFT (5) /* KSZ8851SNL */
+#define RXCR2_SRDBL_4B (0x0 << 5) /* KSZ8851SNL */
+#define RXCR2_SRDBL_8B (0x1 << 5) /* KSZ8851SNL */
+#define RXCR2_SRDBL_16B (0x2 << 5) /* KSZ8851SNL */
+#define RXCR2_SRDBL_32B (0x3 << 5) /* KSZ8851SNL */
+#define RXCR2_SRDBL_FRAME (0x4 << 5) /* KSZ8851SNL */
#define RXCR2_IUFFP (1 << 4)
#define RXCR2_RXIUFCEZ (1 << 3)
#define RXCR2_UDPLFE (1 << 2)
@@ -143,8 +149,10 @@
#define RXFSHR_RXCE (1 << 0)
#define KS_RXFHBCR 0x7E
+#define RXFHBCR_CNT_MASK (0xfff << 0)
+
#define KS_TXQCR 0x80
-#define TXQCR_AETFE (1 << 2)
+#define TXQCR_AETFE (1 << 2) /* KSZ8851SNL */
#define TXQCR_TXQMAM (1 << 1)
#define TXQCR_METFE (1 << 0)
@@ -167,6 +175,10 @@
#define KS_RXFDPR 0x86
#define RXFDPR_RXFPAI (1 << 14)
+#define RXFDPR_WST (1 << 12) /* KSZ8851-16MLL */
+#define RXFDPR_EMS (1 << 11) /* KSZ8851-16MLL */
+#define RXFDPR_RXFP_MASK (0x7ff << 0)
+#define RXFDPR_RXFP_SHIFT (0)
#define KS_RXDTTR 0x8C
#define KS_RXDBCTR 0x8E
@@ -184,7 +196,7 @@
#define IRQ_RXMPDI (1 << 4)
#define IRQ_LDI (1 << 3)
#define IRQ_EDI (1 << 2)
-#define IRQ_SPIBEI (1 << 1)
+#define IRQ_SPIBEI (1 << 1) /* KSZ8851SNL */
#define IRQ_DEDI (1 << 0)
#define KS_RXFCTR 0x9C
@@ -257,42 +269,37 @@
#define KS_P1ANLPR 0xEE
#define KS_P1SCLMD 0xF4
-#define P1SCLMD_LEDOFF (1 << 15)
-#define P1SCLMD_TXIDS (1 << 14)
-#define P1SCLMD_RESTARTAN (1 << 13)
-#define P1SCLMD_DISAUTOMDIX (1 << 10)
-#define P1SCLMD_FORCEMDIX (1 << 9)
-#define P1SCLMD_AUTONEGEN (1 << 7)
-#define P1SCLMD_FORCE100 (1 << 6)
-#define P1SCLMD_FORCEFDX (1 << 5)
-#define P1SCLMD_ADV_FLOW (1 << 4)
-#define P1SCLMD_ADV_100BT_FDX (1 << 3)
-#define P1SCLMD_ADV_100BT_HDX (1 << 2)
-#define P1SCLMD_ADV_10BT_FDX (1 << 1)
-#define P1SCLMD_ADV_10BT_HDX (1 << 0)
#define KS_P1CR 0xF6
-#define P1CR_HP_MDIX (1 << 15)
-#define P1CR_REV_POL (1 << 13)
-#define P1CR_OP_100M (1 << 10)
-#define P1CR_OP_FDX (1 << 9)
-#define P1CR_OP_MDI (1 << 7)
-#define P1CR_AN_DONE (1 << 6)
-#define P1CR_LINK_GOOD (1 << 5)
-#define P1CR_PNTR_FLOW (1 << 4)
-#define P1CR_PNTR_100BT_FDX (1 << 3)
-#define P1CR_PNTR_100BT_HDX (1 << 2)
-#define P1CR_PNTR_10BT_FDX (1 << 1)
-#define P1CR_PNTR_10BT_HDX (1 << 0)
+#define P1CR_LEDOFF (1 << 15)
+#define P1CR_TXIDS (1 << 14)
+#define P1CR_RESTARTAN (1 << 13)
+#define P1CR_DISAUTOMDIX (1 << 10)
+#define P1CR_FORCEMDIX (1 << 9)
+#define P1CR_AUTONEGEN (1 << 7)
+#define P1CR_FORCE100 (1 << 6)
+#define P1CR_FORCEFDX (1 << 5)
+#define P1CR_ADV_FLOW (1 << 4)
+#define P1CR_ADV_100BT_FDX (1 << 3)
+#define P1CR_ADV_100BT_HDX (1 << 2)
+#define P1CR_ADV_10BT_FDX (1 << 1)
+#define P1CR_ADV_10BT_HDX (1 << 0)
+
+#define KS_P1SR 0xF8
+#define P1SR_HP_MDIX (1 << 15)
+#define P1SR_REV_POL (1 << 13)
+#define P1SR_OP_100M (1 << 10)
+#define P1SR_OP_FDX (1 << 9)
+#define P1SR_OP_MDI (1 << 7)
+#define P1SR_AN_DONE (1 << 6)
+#define P1SR_LINK_GOOD (1 << 5)
+#define P1SR_PNTR_FLOW (1 << 4)
+#define P1SR_PNTR_100BT_FDX (1 << 3)
+#define P1SR_PNTR_100BT_HDX (1 << 2)
+#define P1SR_PNTR_10BT_FDX (1 << 1)
+#define P1SR_PNTR_10BT_HDX (1 << 0)
/* TX Frame control */
-
#define TXFR_TXIC (1 << 15)
#define TXFR_TXFID_MASK (0x3f << 0)
#define TXFR_TXFID_SHIFT (0)
-
-/* SPI frame opcodes */
-#define KS_SPIOP_RD (0x00)
-#define KS_SPIOP_WR (0x40)
-#define KS_SPIOP_RXFIFO (0x80)
-#define KS_SPIOP_TXFIFO (0xC0)
diff --git a/drivers/net/ethernet/micrel/ks8851_mll.c b/drivers/net/ethernet/micrel/ks8851_mll.c
index 35f8c9ef204d..c946841c0a06 100644
--- a/drivers/net/ethernet/micrel/ks8851_mll.c
+++ b/drivers/net/ethernet/micrel/ks8851_mll.c
@@ -40,6 +40,8 @@
#include <linux/of_device.h>
#include <linux/of_net.h>
+#include "ks8851.h"
+
#define DRV_NAME "ks8851_mll"
static u8 KS_DEFAULT_MAC_ADDRESS[] = { 0x00, 0x10, 0xA1, 0x86, 0x95, 0x11 };
@@ -48,319 +50,10 @@ static u8 KS_DEFAULT_MAC_ADDRESS[] = { 0x00, 0x10, 0xA1, 0x86, 0x95, 0x11 };
#define TX_BUF_SIZE 2000
#define RX_BUF_SIZE 2000
-#define KS_CCR 0x08
-#define CCR_EEPROM (1 << 9)
-#define CCR_SPI (1 << 8)
-#define CCR_8BIT (1 << 7)
-#define CCR_16BIT (1 << 6)
-#define CCR_32BIT (1 << 5)
-#define CCR_SHARED (1 << 4)
-#define CCR_32PIN (1 << 0)
-
-/* MAC address registers */
-#define KS_MARL 0x10
-#define KS_MARM 0x12
-#define KS_MARH 0x14
-
-#define KS_OBCR 0x20
-#define OBCR_ODS_16MA (1 << 6)
-
-#define KS_EEPCR 0x22
-#define EEPCR_EESA (1 << 4)
-#define EEPCR_EESB (1 << 3)
-#define EEPCR_EEDO (1 << 2)
-#define EEPCR_EESCK (1 << 1)
-#define EEPCR_EECS (1 << 0)
-
-#define KS_MBIR 0x24
-#define MBIR_TXMBF (1 << 12)
-#define MBIR_TXMBFA (1 << 11)
-#define MBIR_RXMBF (1 << 4)
-#define MBIR_RXMBFA (1 << 3)
-
-#define KS_GRR 0x26
-#define GRR_QMU (1 << 1)
-#define GRR_GSR (1 << 0)
-
-#define KS_WFCR 0x2A
-#define WFCR_MPRXE (1 << 7)
-#define WFCR_WF3E (1 << 3)
-#define WFCR_WF2E (1 << 2)
-#define WFCR_WF1E (1 << 1)
-#define WFCR_WF0E (1 << 0)
-
-#define KS_WF0CRC0 0x30
-#define KS_WF0CRC1 0x32
-#define KS_WF0BM0 0x34
-#define KS_WF0BM1 0x36
-#define KS_WF0BM2 0x38
-#define KS_WF0BM3 0x3A
-
-#define KS_WF1CRC0 0x40
-#define KS_WF1CRC1 0x42
-#define KS_WF1BM0 0x44
-#define KS_WF1BM1 0x46
-#define KS_WF1BM2 0x48
-#define KS_WF1BM3 0x4A
-
-#define KS_WF2CRC0 0x50
-#define KS_WF2CRC1 0x52
-#define KS_WF2BM0 0x54
-#define KS_WF2BM1 0x56
-#define KS_WF2BM2 0x58
-#define KS_WF2BM3 0x5A
-
-#define KS_WF3CRC0 0x60
-#define KS_WF3CRC1 0x62
-#define KS_WF3BM0 0x64
-#define KS_WF3BM1 0x66
-#define KS_WF3BM2 0x68
-#define KS_WF3BM3 0x6A
-
-#define KS_TXCR 0x70
-#define TXCR_TCGICMP (1 << 8)
-#define TXCR_TCGUDP (1 << 7)
-#define TXCR_TCGTCP (1 << 6)
-#define TXCR_TCGIP (1 << 5)
-#define TXCR_FTXQ (1 << 4)
-#define TXCR_TXFCE (1 << 3)
-#define TXCR_TXPE (1 << 2)
-#define TXCR_TXCRC (1 << 1)
-#define TXCR_TXE (1 << 0)
-
-#define KS_TXSR 0x72
-#define TXSR_TXLC (1 << 13)
-#define TXSR_TXMC (1 << 12)
-#define TXSR_TXFID_MASK (0x3f << 0)
-#define TXSR_TXFID_SHIFT (0)
-#define TXSR_TXFID_GET(_v) (((_v) >> 0) & 0x3f)
-
-
-#define KS_RXCR1 0x74
-#define RXCR1_FRXQ (1 << 15)
-#define RXCR1_RXUDPFCC (1 << 14)
-#define RXCR1_RXTCPFCC (1 << 13)
-#define RXCR1_RXIPFCC (1 << 12)
-#define RXCR1_RXPAFMA (1 << 11)
-#define RXCR1_RXFCE (1 << 10)
-#define RXCR1_RXEFE (1 << 9)
-#define RXCR1_RXMAFMA (1 << 8)
-#define RXCR1_RXBE (1 << 7)
-#define RXCR1_RXME (1 << 6)
-#define RXCR1_RXUE (1 << 5)
-#define RXCR1_RXAE (1 << 4)
-#define RXCR1_RXINVF (1 << 1)
-#define RXCR1_RXE (1 << 0)
#define RXCR1_FILTER_MASK (RXCR1_RXINVF | RXCR1_RXAE | \
RXCR1_RXMAFMA | RXCR1_RXPAFMA)
-
-#define KS_RXCR2 0x76
-#define RXCR2_SRDBL_MASK (0x7 << 5)
-#define RXCR2_SRDBL_SHIFT (5)
-#define RXCR2_SRDBL_4B (0x0 << 5)
-#define RXCR2_SRDBL_8B (0x1 << 5)
-#define RXCR2_SRDBL_16B (0x2 << 5)
-#define RXCR2_SRDBL_32B (0x3 << 5)
-/* #define RXCR2_SRDBL_FRAME (0x4 << 5) */
-#define RXCR2_IUFFP (1 << 4)
-#define RXCR2_RXIUFCEZ (1 << 3)
-#define RXCR2_UDPLFE (1 << 2)
-#define RXCR2_RXICMPFCC (1 << 1)
-#define RXCR2_RXSAF (1 << 0)
-
-#define KS_TXMIR 0x78
-
-#define KS_RXFHSR 0x7C
-#define RXFSHR_RXFV (1 << 15)
-#define RXFSHR_RXICMPFCS (1 << 13)
-#define RXFSHR_RXIPFCS (1 << 12)
-#define RXFSHR_RXTCPFCS (1 << 11)
-#define RXFSHR_RXUDPFCS (1 << 10)
-#define RXFSHR_RXBF (1 << 7)
-#define RXFSHR_RXMF (1 << 6)
-#define RXFSHR_RXUF (1 << 5)
-#define RXFSHR_RXMR (1 << 4)
-#define RXFSHR_RXFT (1 << 3)
-#define RXFSHR_RXFTL (1 << 2)
-#define RXFSHR_RXRF (1 << 1)
-#define RXFSHR_RXCE (1 << 0)
-#define RXFSHR_ERR (RXFSHR_RXCE | RXFSHR_RXRF |\
- RXFSHR_RXFTL | RXFSHR_RXMR |\
- RXFSHR_RXICMPFCS | RXFSHR_RXIPFCS |\
- RXFSHR_RXTCPFCS)
-#define KS_RXFHBCR 0x7E
-#define RXFHBCR_CNT_MASK 0x0FFF
-
-#define KS_TXQCR 0x80
-#define TXQCR_AETFE (1 << 2)
-#define TXQCR_TXQMAM (1 << 1)
-#define TXQCR_METFE (1 << 0)
-
-#define KS_RXQCR 0x82
-#define RXQCR_RXDTTS (1 << 12)
-#define RXQCR_RXDBCTS (1 << 11)
-#define RXQCR_RXFCTS (1 << 10)
-#define RXQCR_RXIPHTOE (1 << 9)
-#define RXQCR_RXDTTE (1 << 7)
-#define RXQCR_RXDBCTE (1 << 6)
-#define RXQCR_RXFCTE (1 << 5)
-#define RXQCR_ADRFE (1 << 4)
-#define RXQCR_SDA (1 << 3)
-#define RXQCR_RRXEF (1 << 0)
#define RXQCR_CMD_CNTL (RXQCR_RXFCTE|RXQCR_ADRFE)
-#define KS_TXFDPR 0x84
-#define TXFDPR_TXFPAI (1 << 14)
-#define TXFDPR_TXFP_MASK (0x7ff << 0)
-#define TXFDPR_TXFP_SHIFT (0)
-
-#define KS_RXFDPR 0x86
-#define RXFDPR_RXFPAI (1 << 14)
-
-#define KS_RXDTTR 0x8C
-#define KS_RXDBCTR 0x8E
-
-#define KS_IER 0x90
-#define KS_ISR 0x92
-#define IRQ_LCI (1 << 15)
-#define IRQ_TXI (1 << 14)
-#define IRQ_RXI (1 << 13)
-#define IRQ_RXOI (1 << 11)
-#define IRQ_TXPSI (1 << 9)
-#define IRQ_RXPSI (1 << 8)
-#define IRQ_TXSAI (1 << 6)
-#define IRQ_RXWFDI (1 << 5)
-#define IRQ_RXMPDI (1 << 4)
-#define IRQ_LDI (1 << 3)
-#define IRQ_EDI (1 << 2)
-#define IRQ_SPIBEI (1 << 1)
-#define IRQ_DEDI (1 << 0)
-
-#define KS_RXFCTR 0x9C
-#define RXFCTR_THRESHOLD_MASK 0x00FF
-
-#define KS_RXFC 0x9D
-#define RXFCTR_RXFC_MASK (0xff << 8)
-#define RXFCTR_RXFC_SHIFT (8)
-#define RXFCTR_RXFC_GET(_v) (((_v) >> 8) & 0xff)
-#define RXFCTR_RXFCT_MASK (0xff << 0)
-#define RXFCTR_RXFCT_SHIFT (0)
-
-#define KS_TXNTFSR 0x9E
-
-#define KS_MAHTR0 0xA0
-#define KS_MAHTR1 0xA2
-#define KS_MAHTR2 0xA4
-#define KS_MAHTR3 0xA6
-
-#define KS_FCLWR 0xB0
-#define KS_FCHWR 0xB2
-#define KS_FCOWR 0xB4
-
-#define KS_CIDER 0xC0
-#define CIDER_ID 0x8870
-#define CIDER_REV_MASK (0x7 << 1)
-#define CIDER_REV_SHIFT (1)
-#define CIDER_REV_GET(_v) (((_v) >> 1) & 0x7)
-
-#define KS_CGCR 0xC6
-#define KS_IACR 0xC8
-#define IACR_RDEN (1 << 12)
-#define IACR_TSEL_MASK (0x3 << 10)
-#define IACR_TSEL_SHIFT (10)
-#define IACR_TSEL_MIB (0x3 << 10)
-#define IACR_ADDR_MASK (0x1f << 0)
-#define IACR_ADDR_SHIFT (0)
-
-#define KS_IADLR 0xD0
-#define KS_IAHDR 0xD2
-
-#define KS_PMECR 0xD4
-#define PMECR_PME_DELAY (1 << 14)
-#define PMECR_PME_POL (1 << 12)
-#define PMECR_WOL_WAKEUP (1 << 11)
-#define PMECR_WOL_MAGICPKT (1 << 10)
-#define PMECR_WOL_LINKUP (1 << 9)
-#define PMECR_WOL_ENERGY (1 << 8)
-#define PMECR_AUTO_WAKE_EN (1 << 7)
-#define PMECR_WAKEUP_NORMAL (1 << 6)
-#define PMECR_WKEVT_MASK (0xf << 2)
-#define PMECR_WKEVT_SHIFT (2)
-#define PMECR_WKEVT_GET(_v) (((_v) >> 2) & 0xf)
-#define PMECR_WKEVT_ENERGY (0x1 << 2)
-#define PMECR_WKEVT_LINK (0x2 << 2)
-#define PMECR_WKEVT_MAGICPKT (0x4 << 2)
-#define PMECR_WKEVT_FRAME (0x8 << 2)
-#define PMECR_PM_MASK (0x3 << 0)
-#define PMECR_PM_SHIFT (0)
-#define PMECR_PM_NORMAL (0x0 << 0)
-#define PMECR_PM_ENERGY (0x1 << 0)
-#define PMECR_PM_SOFTDOWN (0x2 << 0)
-#define PMECR_PM_POWERSAVE (0x3 << 0)
-
-/* Standard MII PHY data */
-#define KS_P1MBCR 0xE4
-#define P1MBCR_FORCE_FDX (1 << 8)
-
-#define KS_P1MBSR 0xE6
-#define P1MBSR_AN_COMPLETE (1 << 5)
-#define P1MBSR_AN_CAPABLE (1 << 3)
-#define P1MBSR_LINK_UP (1 << 2)
-
-#define KS_PHY1ILR 0xE8
-#define KS_PHY1IHR 0xEA
-#define KS_P1ANAR 0xEC
-#define KS_P1ANLPR 0xEE
-
-#define KS_P1SCLMD 0xF4
-#define P1SCLMD_LEDOFF (1 << 15)
-#define P1SCLMD_TXIDS (1 << 14)
-#define P1SCLMD_RESTARTAN (1 << 13)
-#define P1SCLMD_DISAUTOMDIX (1 << 10)
-#define P1SCLMD_FORCEMDIX (1 << 9)
-#define P1SCLMD_AUTONEGEN (1 << 7)
-#define P1SCLMD_FORCE100 (1 << 6)
-#define P1SCLMD_FORCEFDX (1 << 5)
-#define P1SCLMD_ADV_FLOW (1 << 4)
-#define P1SCLMD_ADV_100BT_FDX (1 << 3)
-#define P1SCLMD_ADV_100BT_HDX (1 << 2)
-#define P1SCLMD_ADV_10BT_FDX (1 << 1)
-#define P1SCLMD_ADV_10BT_HDX (1 << 0)
-
-#define KS_P1CR 0xF6
-#define P1CR_HP_MDIX (1 << 15)
-#define P1CR_REV_POL (1 << 13)
-#define P1CR_OP_100M (1 << 10)
-#define P1CR_OP_FDX (1 << 9)
-#define P1CR_OP_MDI (1 << 7)
-#define P1CR_AN_DONE (1 << 6)
-#define P1CR_LINK_GOOD (1 << 5)
-#define P1CR_PNTR_FLOW (1 << 4)
-#define P1CR_PNTR_100BT_FDX (1 << 3)
-#define P1CR_PNTR_100BT_HDX (1 << 2)
-#define P1CR_PNTR_10BT_FDX (1 << 1)
-#define P1CR_PNTR_10BT_HDX (1 << 0)
-
-/* TX Frame control */
-
-#define TXFR_TXIC (1 << 15)
-#define TXFR_TXFID_MASK (0x3f << 0)
-#define TXFR_TXFID_SHIFT (0)
-
-#define KS_P1SR 0xF8
-#define P1SR_HP_MDIX (1 << 15)
-#define P1SR_REV_POL (1 << 13)
-#define P1SR_OP_100M (1 << 10)
-#define P1SR_OP_FDX (1 << 9)
-#define P1SR_OP_MDI (1 << 7)
-#define P1SR_AN_DONE (1 << 6)
-#define P1SR_LINK_GOOD (1 << 5)
-#define P1SR_PNTR_FLOW (1 << 4)
-#define P1SR_PNTR_100BT_FDX (1 << 3)
-#define P1SR_PNTR_100BT_HDX (1 << 2)
-#define P1SR_PNTR_10BT_FDX (1 << 1)
-#define P1SR_PNTR_10BT_HDX (1 << 0)
-
#define ENUM_BUS_NONE 0
#define ENUM_BUS_8BIT 1
#define ENUM_BUS_16BIT 2
@@ -1475,7 +1168,7 @@ static void ks_setup(struct ks_net *ks)
ks_wrreg16(ks, KS_RXFDPR, RXFDPR_RXFPAI);
/* Setup Receive Frame Threshold - 1 frame (RXFCTFC) */
- ks_wrreg16(ks, KS_RXFCTR, 1 & RXFCTR_THRESHOLD_MASK);
+ ks_wrreg16(ks, KS_RXFCTR, 1 & RXFCTR_RXFCT_MASK);
/* Setup RxQ Command Control (RXQCR) */
ks->rc_rxqcr = RXQCR_CMD_CNTL;
@@ -1488,7 +1181,7 @@ static void ks_setup(struct ks_net *ks)
*/
w = ks_rdreg16(ks, KS_P1MBCR);
- w &= ~P1MBCR_FORCE_FDX;
+ w &= ~BMCR_FULLDPLX;
ks_wrreg16(ks, KS_P1MBCR, w);
w = TXCR_TXFCE | TXCR_TXPE | TXCR_TXCRC | TXCR_TCGIP;
@@ -1629,7 +1322,7 @@ static int ks8851_probe(struct platform_device *pdev)
ks_setup_int(ks);
data = ks_rdreg16(ks, KS_OBCR);
- ks_wrreg16(ks, KS_OBCR, data | OBCR_ODS_16MA);
+ ks_wrreg16(ks, KS_OBCR, data | OBCR_ODS_16mA);
/* overwriting the default MAC address */
if (pdev->dev.of_node) {
diff --git a/drivers/net/ethernet/microchip/lan743x_main.c b/drivers/net/ethernet/microchip/lan743x_main.c
index 4d1b4a24907f..13e6bf13ac4d 100644
--- a/drivers/net/ethernet/microchip/lan743x_main.c
+++ b/drivers/net/ethernet/microchip/lan743x_main.c
@@ -585,8 +585,7 @@ static int lan743x_intr_open(struct lan743x_adapter *adapter)
if (adapter->csr.flags &
LAN743X_CSR_FLAG_SUPPORTS_INTR_AUTO_SET_CLR) {
- flags = LAN743X_VECTOR_FLAG_VECTOR_ENABLE_AUTO_CLEAR |
- LAN743X_VECTOR_FLAG_VECTOR_ENABLE_AUTO_SET |
+ flags = LAN743X_VECTOR_FLAG_VECTOR_ENABLE_AUTO_SET |
LAN743X_VECTOR_FLAG_SOURCE_ENABLE_AUTO_SET |
LAN743X_VECTOR_FLAG_SOURCE_ENABLE_AUTO_CLEAR |
LAN743X_VECTOR_FLAG_SOURCE_STATUS_AUTO_CLEAR;
@@ -599,12 +598,6 @@ static int lan743x_intr_open(struct lan743x_adapter *adapter)
/* map TX interrupt to vector */
int_vec_map1 |= INT_VEC_MAP1_TX_VEC_(index, vector);
lan743x_csr_write(adapter, INT_VEC_MAP1, int_vec_map1);
- if (flags &
- LAN743X_VECTOR_FLAG_VECTOR_ENABLE_AUTO_CLEAR) {
- int_vec_en_auto_clr |= INT_VEC_EN_(vector);
- lan743x_csr_write(adapter, INT_VEC_EN_AUTO_CLR,
- int_vec_en_auto_clr);
- }
/* Remove TX interrupt from shared mask */
intr->vector_list[0].int_mask &= ~int_bit;
@@ -1902,7 +1895,17 @@ static int lan743x_rx_next_index(struct lan743x_rx *rx, int index)
return ((++index) % rx->ring_size);
}
-static int lan743x_rx_allocate_ring_element(struct lan743x_rx *rx, int index)
+static struct sk_buff *lan743x_rx_allocate_skb(struct lan743x_rx *rx)
+{
+ int length = 0;
+
+ length = (LAN743X_MAX_FRAME_SIZE + ETH_HLEN + 4 + RX_HEAD_PADDING);
+ return __netdev_alloc_skb(rx->adapter->netdev,
+ length, GFP_ATOMIC | GFP_DMA);
+}
+
+static int lan743x_rx_init_ring_element(struct lan743x_rx *rx, int index,
+ struct sk_buff *skb)
{
struct lan743x_rx_buffer_info *buffer_info;
struct lan743x_rx_descriptor *descriptor;
@@ -1911,9 +1914,7 @@ static int lan743x_rx_allocate_ring_element(struct lan743x_rx *rx, int index)
length = (LAN743X_MAX_FRAME_SIZE + ETH_HLEN + 4 + RX_HEAD_PADDING);
descriptor = &rx->ring_cpu_ptr[index];
buffer_info = &rx->buffer_info[index];
- buffer_info->skb = __netdev_alloc_skb(rx->adapter->netdev,
- length,
- GFP_ATOMIC | GFP_DMA);
+ buffer_info->skb = skb;
if (!(buffer_info->skb))
return -ENOMEM;
buffer_info->dma_ptr = dma_map_single(&rx->adapter->pdev->dev,
@@ -2060,8 +2061,19 @@ static int lan743x_rx_process_packet(struct lan743x_rx *rx)
/* packet is available */
if (first_index == last_index) {
/* single buffer packet */
+ struct sk_buff *new_skb = NULL;
int packet_length;
+ new_skb = lan743x_rx_allocate_skb(rx);
+ if (!new_skb) {
+ /* failed to allocate next skb.
+ * Memory is very low.
+ * Drop this packet and reuse buffer.
+ */
+ lan743x_rx_reuse_ring_element(rx, first_index);
+ goto process_extension;
+ }
+
buffer_info = &rx->buffer_info[first_index];
skb = buffer_info->skb;
descriptor = &rx->ring_cpu_ptr[first_index];
@@ -2081,7 +2093,7 @@ static int lan743x_rx_process_packet(struct lan743x_rx *rx)
skb_put(skb, packet_length - 4);
skb->protocol = eth_type_trans(skb,
rx->adapter->netdev);
- lan743x_rx_allocate_ring_element(rx, first_index);
+ lan743x_rx_init_ring_element(rx, first_index, new_skb);
} else {
int index = first_index;
@@ -2094,26 +2106,23 @@ static int lan743x_rx_process_packet(struct lan743x_rx *rx)
if (first_index <= last_index) {
while ((index >= first_index) &&
(index <= last_index)) {
- lan743x_rx_release_ring_element(rx,
- index);
- lan743x_rx_allocate_ring_element(rx,
- index);
+ lan743x_rx_reuse_ring_element(rx,
+ index);
index = lan743x_rx_next_index(rx,
index);
}
} else {
while ((index >= first_index) ||
(index <= last_index)) {
- lan743x_rx_release_ring_element(rx,
- index);
- lan743x_rx_allocate_ring_element(rx,
- index);
+ lan743x_rx_reuse_ring_element(rx,
+ index);
index = lan743x_rx_next_index(rx,
index);
}
}
}
+process_extension:
if (extension_index >= 0) {
descriptor = &rx->ring_cpu_ptr[extension_index];
buffer_info = &rx->buffer_info[extension_index];
@@ -2290,7 +2299,9 @@ static int lan743x_rx_ring_init(struct lan743x_rx *rx)
rx->last_head = 0;
for (index = 0; index < rx->ring_size; index++) {
- ret = lan743x_rx_allocate_ring_element(rx, index);
+ struct sk_buff *new_skb = lan743x_rx_allocate_skb(rx);
+
+ ret = lan743x_rx_init_ring_element(rx, index, new_skb);
if (ret)
goto cleanup;
}
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h b/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h
index 166d7f71442e..372adea10e14 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h
+++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h
@@ -392,7 +392,7 @@
#define NFP_NET_CFG_MBOX_SIMPLE_CMD 0x0
#define NFP_NET_CFG_MBOX_SIMPLE_RET 0x4
#define NFP_NET_CFG_MBOX_SIMPLE_VAL 0x8
-#define NFP_NET_CFG_MBOX_SIMPLE_LEN 0x12
+#define NFP_NET_CFG_MBOX_SIMPLE_LEN 12
#define NFP_NET_CFG_MBOX_CMD_CTAG_FILTER_ADD 1
#define NFP_NET_CFG_MBOX_CMD_CTAG_FILTER_KILL 2
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 552d930e3940..528f6b4fd16a 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
@@ -27,7 +27,6 @@
#define DRV_VERSION "1.01"
const char pch_driver_version[] = DRV_VERSION;
-#define PCI_DEVICE_ID_INTEL_IOH1_GBE 0x8802 /* Pci device ID */
#define PCH_GBE_MAR_ENTRIES 16
#define PCH_GBE_SHORT_PKT 64
#define DSC_INIT16 0xC000
@@ -37,11 +36,9 @@ const char pch_driver_version[] = DRV_VERSION;
#define PCH_GBE_PCI_BAR 1
#define PCH_GBE_RESERVE_MEMORY 0x200000 /* 2MB */
-/* Macros for ML7223 */
-#define PCI_VENDOR_ID_ROHM 0x10db
-#define PCI_DEVICE_ID_ROHM_ML7223_GBE 0x8013
+#define PCI_DEVICE_ID_INTEL_IOH1_GBE 0x8802
-/* Macros for ML7831 */
+#define PCI_DEVICE_ID_ROHM_ML7223_GBE 0x8013
#define PCI_DEVICE_ID_ROHM_ML7831_GBE 0x8802
#define PCH_GBE_TX_WEIGHT 64
diff --git a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c
index c6238083e898..b4c8949933f1 100644
--- a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c
+++ b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c
@@ -1663,8 +1663,11 @@ static int qede_selftest_run_loopback(struct qede_dev *edev, u32 loopback_mode)
/* Wait for loopback configuration to apply */
msleep_interruptible(500);
- /* prepare the loopback packet */
- pkt_size = edev->ndev->mtu + ETH_HLEN;
+ /* Setting max packet size to 1.5K to avoid data being split over
+ * multiple BDs in cases where MTU > PAGE_SIZE.
+ */
+ pkt_size = (((edev->ndev->mtu < ETH_DATA_LEN) ?
+ edev->ndev->mtu : ETH_DATA_LEN) + ETH_HLEN);
skb = netdev_alloc_skb(edev->ndev, pkt_size);
if (!skb) {
diff --git a/drivers/net/ethernet/qlogic/qla3xxx.c b/drivers/net/ethernet/qlogic/qla3xxx.c
index 10b075bc5959..b61b88cbc0c7 100644
--- a/drivers/net/ethernet/qlogic/qla3xxx.c
+++ b/drivers/net/ethernet/qlogic/qla3xxx.c
@@ -3886,6 +3886,12 @@ static int ql3xxx_probe(struct pci_dev *pdev,
netif_stop_queue(ndev);
qdev->workqueue = create_singlethread_workqueue(ndev->name);
+ if (!qdev->workqueue) {
+ unregister_netdev(ndev);
+ err = -ENOMEM;
+ goto err_out_iounmap;
+ }
+
INIT_DELAYED_WORK(&qdev->reset_work, ql_reset_work);
INIT_DELAYED_WORK(&qdev->tx_timeout_work, ql_tx_timeout_work);
INIT_DELAYED_WORK(&qdev->link_state_work, ql_link_state_machine_work);
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c
index 3b0adda7cc9c..a4cd6f2cfb86 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c
@@ -1048,6 +1048,8 @@ int qlcnic_do_lb_test(struct qlcnic_adapter *adapter, u8 mode)
for (i = 0; i < QLCNIC_NUM_ILB_PKT; i++) {
skb = netdev_alloc_skb(adapter->netdev, QLCNIC_ILB_PKT_SIZE);
+ if (!skb)
+ break;
qlcnic_create_loopback_buff(skb->data, adapter->mac_addr);
skb_put(skb, QLCNIC_ILB_PKT_SIZE);
adapter->ahw->diag_cnt = 0;
diff --git a/drivers/net/ethernet/qlogic/qlge/qlge_main.c b/drivers/net/ethernet/qlogic/qlge/qlge_main.c
index 096515c27263..07e1c623048e 100644
--- a/drivers/net/ethernet/qlogic/qlge/qlge_main.c
+++ b/drivers/net/ethernet/qlogic/qlge/qlge_main.c
@@ -4681,6 +4681,11 @@ static int ql_init_device(struct pci_dev *pdev, struct net_device *ndev,
*/
qdev->workqueue = alloc_ordered_workqueue("%s", WQ_MEM_RECLAIM,
ndev->name);
+ if (!qdev->workqueue) {
+ err = -ENOMEM;
+ goto err_out2;
+ }
+
INIT_DELAYED_WORK(&qdev->asic_reset_work, ql_asic_reset_work);
INIT_DELAYED_WORK(&qdev->mpi_reset_work, ql_mpi_reset_work);
INIT_DELAYED_WORK(&qdev->mpi_work, ql_mpi_work);
diff --git a/drivers/net/ethernet/realtek/8139too.c b/drivers/net/ethernet/realtek/8139too.c
index 69d752f0b621..55d01266e615 100644
--- a/drivers/net/ethernet/realtek/8139too.c
+++ b/drivers/net/ethernet/realtek/8139too.c
@@ -258,6 +258,7 @@ static const struct pci_device_id rtl8139_pci_tbl[] = {
{0x126c, 0x1211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
{0x1743, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
{0x021b, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x16ec, 0xab06, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
#ifdef CONFIG_SH_SECUREEDGE5410
/* Bogus 8139 silicon reports 8129 without external PROM :-( */
diff --git a/drivers/net/ethernet/realtek/atp.c b/drivers/net/ethernet/realtek/atp.c
index cfb67b746595..58e0ca9093d3 100644
--- a/drivers/net/ethernet/realtek/atp.c
+++ b/drivers/net/ethernet/realtek/atp.c
@@ -482,7 +482,7 @@ static void hardware_init(struct net_device *dev)
write_reg_high(ioaddr, IMR, ISRh_RxErr);
lp->tx_unit_busy = 0;
- lp->pac_cnt_in_tx_buf = 0;
+ lp->pac_cnt_in_tx_buf = 0;
lp->saved_tx_size = 0;
}
diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c
index c29dde064078..7562ccbbb39a 100644
--- a/drivers/net/ethernet/realtek/r8169.c
+++ b/drivers/net/ethernet/realtek/r8169.c
@@ -678,6 +678,7 @@ struct rtl8169_private {
struct work_struct work;
} wk;
+ unsigned irq_enabled:1;
unsigned supports_gmii:1;
dma_addr_t counters_phys_addr;
struct rtl8169_counters *counters;
@@ -1293,6 +1294,7 @@ static void rtl_ack_events(struct rtl8169_private *tp, u16 bits)
static void rtl_irq_disable(struct rtl8169_private *tp)
{
RTL_W16(tp, IntrMask, 0);
+ tp->irq_enabled = 0;
}
#define RTL_EVENT_NAPI_RX (RxOK | RxErr)
@@ -1301,6 +1303,7 @@ static void rtl_irq_disable(struct rtl8169_private *tp)
static void rtl_irq_enable(struct rtl8169_private *tp)
{
+ tp->irq_enabled = 1;
RTL_W16(tp, IntrMask, tp->irq_mask);
}
@@ -6520,9 +6523,8 @@ static irqreturn_t rtl8169_interrupt(int irq, void *dev_instance)
{
struct rtl8169_private *tp = dev_instance;
u16 status = RTL_R16(tp, IntrStatus);
- u16 irq_mask = RTL_R16(tp, IntrMask);
- if (status == 0xffff || !(status & irq_mask))
+ if (!tp->irq_enabled || status == 0xffff || !(status & tp->irq_mask))
return IRQ_NONE;
if (unlikely(status & SYSErr)) {
@@ -6540,7 +6542,7 @@ static irqreturn_t rtl8169_interrupt(int irq, void *dev_instance)
set_bit(RTL_FLAG_TASK_RESET_PENDING, tp->wk.flags);
}
- if (status & RTL_EVENT_NAPI) {
+ if (status & (RTL_EVENT_NAPI | LinkChg)) {
rtl_irq_disable(tp);
napi_schedule_irqoff(&tp->napi);
}
diff --git a/drivers/net/ethernet/renesas/ravb_main.c b/drivers/net/ethernet/renesas/ravb_main.c
index d28c8f9ca55b..8154b38c08f7 100644
--- a/drivers/net/ethernet/renesas/ravb_main.c
+++ b/drivers/net/ethernet/renesas/ravb_main.c
@@ -458,7 +458,7 @@ static int ravb_dmac_init(struct net_device *ndev)
RCR_EFFS | RCR_ENCF | RCR_ETS0 | RCR_ESF | 0x18000000, RCR);
/* Set FIFO size */
- ravb_write(ndev, TGC_TQP_AVBMODE1 | 0x00222200, TGC);
+ ravb_write(ndev, TGC_TQP_AVBMODE1 | 0x00112200, TGC);
/* Timestamp enable */
ravb_write(ndev, TCCR_TFEN, TCCR);
diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c
index 339b2eae2100..e33af371b169 100644
--- a/drivers/net/ethernet/renesas/sh_eth.c
+++ b/drivers/net/ethernet/renesas/sh_eth.c
@@ -3181,12 +3181,16 @@ static struct sh_eth_plat_data *sh_eth_parse_dt(struct device *dev)
struct device_node *np = dev->of_node;
struct sh_eth_plat_data *pdata;
const char *mac_addr;
+ int ret;
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return NULL;
- pdata->phy_interface = of_get_phy_mode(np);
+ ret = of_get_phy_mode(np);
+ if (ret < 0)
+ return NULL;
+ pdata->phy_interface = ret;
mac_addr = of_get_mac_address(np);
if (mac_addr)
diff --git a/drivers/net/ethernet/rocker/rocker_main.c b/drivers/net/ethernet/rocker/rocker_main.c
index c883aa89b7ca..a71c900ca04f 100644
--- a/drivers/net/ethernet/rocker/rocker_main.c
+++ b/drivers/net/ethernet/rocker/rocker_main.c
@@ -2805,6 +2805,11 @@ static int rocker_switchdev_event(struct notifier_block *unused,
memcpy(&switchdev_work->fdb_info, ptr,
sizeof(switchdev_work->fdb_info));
switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC);
+ if (unlikely(!switchdev_work->fdb_info.addr)) {
+ kfree(switchdev_work);
+ return NOTIFY_BAD;
+ }
+
ether_addr_copy((u8 *)switchdev_work->fdb_info.addr,
fdb_info->addr);
/* Take a reference on the rocker device */
diff --git a/drivers/net/ethernet/sis/sis900.c b/drivers/net/ethernet/sis/sis900.c
index 6073387511f8..67f9bb6e941b 100644
--- a/drivers/net/ethernet/sis/sis900.c
+++ b/drivers/net/ethernet/sis/sis900.c
@@ -730,10 +730,10 @@ static u16 sis900_default_phy(struct net_device * net_dev)
status = mdio_read(net_dev, phy->phy_addr, MII_STATUS);
/* Link ON & Not select default PHY & not ghost PHY */
- if ((status & MII_STAT_LINK) && !default_phy &&
- (phy->phy_types != UNKNOWN))
- default_phy = phy;
- else {
+ if ((status & MII_STAT_LINK) && !default_phy &&
+ (phy->phy_types != UNKNOWN)) {
+ default_phy = phy;
+ } else {
status = mdio_read(net_dev, phy->phy_addr, MII_CONTROL);
mdio_write(net_dev, phy->phy_addr, MII_CONTROL,
status | MII_CNTL_AUTO | MII_CNTL_ISOLATE);
@@ -741,7 +741,7 @@ static u16 sis900_default_phy(struct net_device * net_dev)
phy_home = phy;
else if(phy->phy_types == LAN)
phy_lan = phy;
- }
+ }
}
if (!default_phy && phy_home)
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c
index 7e2e79dedebf..062a600fa5a7 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c
@@ -25,9 +25,24 @@
#define SYSCFG_MCU_ETH_MASK BIT(23)
#define SYSCFG_MP1_ETH_MASK GENMASK(23, 16)
+#define SYSCFG_PMCCLRR_OFFSET 0x40
#define SYSCFG_PMCR_ETH_CLK_SEL BIT(16)
#define SYSCFG_PMCR_ETH_REF_CLK_SEL BIT(17)
+
+/* Ethernet PHY interface selection in register SYSCFG Configuration
+ *------------------------------------------
+ * src |BIT(23)| BIT(22)| BIT(21)|BIT(20)|
+ *------------------------------------------
+ * MII | 0 | 0 | 0 | 1 |
+ *------------------------------------------
+ * GMII | 0 | 0 | 0 | 0 |
+ *------------------------------------------
+ * RGMII | 0 | 0 | 1 | n/a |
+ *------------------------------------------
+ * RMII | 1 | 0 | 0 | n/a |
+ *------------------------------------------
+ */
#define SYSCFG_PMCR_ETH_SEL_MII BIT(20)
#define SYSCFG_PMCR_ETH_SEL_RGMII BIT(21)
#define SYSCFG_PMCR_ETH_SEL_RMII BIT(23)
@@ -35,14 +50,54 @@
#define SYSCFG_MCU_ETH_SEL_MII 0
#define SYSCFG_MCU_ETH_SEL_RMII 1
+/* STM32MP1 register definitions
+ *
+ * Below table summarizes the clock requirement and clock sources for
+ * supported phy interface modes.
+ * __________________________________________________________________________
+ *|PHY_MODE | Normal | PHY wo crystal| PHY wo crystal |No 125Mhz from PHY|
+ *| | | 25MHz | 50MHz | |
+ * ---------------------------------------------------------------------------
+ *| MII | - | eth-ck | n/a | n/a |
+ *| | | | | |
+ * ---------------------------------------------------------------------------
+ *| GMII | - | eth-ck | n/a | n/a |
+ *| | | | | |
+ * ---------------------------------------------------------------------------
+ *| RGMII | - | eth-ck | n/a | eth-ck (no pin) |
+ *| | | | | st,eth-clk-sel |
+ * ---------------------------------------------------------------------------
+ *| RMII | - | eth-ck | eth-ck | n/a |
+ *| | | | st,eth-ref-clk-sel | |
+ * ---------------------------------------------------------------------------
+ *
+ * BIT(17) : set this bit in RMII mode when you have PHY without crystal 50MHz
+ * BIT(16) : set this bit in GMII/RGMII PHY when you do not want use 125Mhz
+ * from PHY
+ *-----------------------------------------------------
+ * src | BIT(17) | BIT(16) |
+ *-----------------------------------------------------
+ * MII | n/a | n/a |
+ *-----------------------------------------------------
+ * GMII | n/a | st,eth-clk-sel |
+ *-----------------------------------------------------
+ * RGMII | n/a | st,eth-clk-sel |
+ *-----------------------------------------------------
+ * RMII | st,eth-ref-clk-sel | n/a |
+ *-----------------------------------------------------
+ *
+ */
+
struct stm32_dwmac {
struct clk *clk_tx;
struct clk *clk_rx;
struct clk *clk_eth_ck;
struct clk *clk_ethstp;
struct clk *syscfg_clk;
- bool int_phyclk; /* Clock from RCC to drive PHY */
- u32 mode_reg; /* MAC glue-logic mode register */
+ int eth_clk_sel_reg;
+ int eth_ref_clk_sel_reg;
+ int irq_pwr_wakeup;
+ u32 mode_reg; /* MAC glue-logic mode register */
struct regmap *regmap;
u32 speed;
const struct stm32_ops *ops;
@@ -102,7 +157,7 @@ static int stm32mp1_clk_prepare(struct stm32_dwmac *dwmac, bool prepare)
if (ret)
return ret;
- if (dwmac->int_phyclk) {
+ if (dwmac->clk_eth_ck) {
ret = clk_prepare_enable(dwmac->clk_eth_ck);
if (ret) {
clk_disable_unprepare(dwmac->syscfg_clk);
@@ -111,7 +166,7 @@ static int stm32mp1_clk_prepare(struct stm32_dwmac *dwmac, bool prepare)
}
} else {
clk_disable_unprepare(dwmac->syscfg_clk);
- if (dwmac->int_phyclk)
+ if (dwmac->clk_eth_ck)
clk_disable_unprepare(dwmac->clk_eth_ck);
}
return ret;
@@ -121,7 +176,7 @@ static int stm32mp1_set_mode(struct plat_stmmacenet_data *plat_dat)
{
struct stm32_dwmac *dwmac = plat_dat->bsp_priv;
u32 reg = dwmac->mode_reg;
- int val;
+ int val, ret;
switch (plat_dat->interface) {
case PHY_INTERFACE_MODE_MII:
@@ -130,19 +185,22 @@ static int stm32mp1_set_mode(struct plat_stmmacenet_data *plat_dat)
break;
case PHY_INTERFACE_MODE_GMII:
val = SYSCFG_PMCR_ETH_SEL_GMII;
- if (dwmac->int_phyclk)
+ if (dwmac->eth_clk_sel_reg)
val |= SYSCFG_PMCR_ETH_CLK_SEL;
pr_debug("SYSCFG init : PHY_INTERFACE_MODE_GMII\n");
break;
case PHY_INTERFACE_MODE_RMII:
val = SYSCFG_PMCR_ETH_SEL_RMII;
- if (dwmac->int_phyclk)
+ if (dwmac->eth_ref_clk_sel_reg)
val |= SYSCFG_PMCR_ETH_REF_CLK_SEL;
pr_debug("SYSCFG init : PHY_INTERFACE_MODE_RMII\n");
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 = SYSCFG_PMCR_ETH_SEL_RGMII;
- if (dwmac->int_phyclk)
+ if (dwmac->eth_clk_sel_reg)
val |= SYSCFG_PMCR_ETH_CLK_SEL;
pr_debug("SYSCFG init : PHY_INTERFACE_MODE_RGMII\n");
break;
@@ -153,6 +211,11 @@ static int stm32mp1_set_mode(struct plat_stmmacenet_data *plat_dat)
return -EINVAL;
}
+ /* Need to update PMCCLRR (clear register) */
+ ret = regmap_write(dwmac->regmap, reg + SYSCFG_PMCCLRR_OFFSET,
+ dwmac->ops->syscfg_eth_mask);
+
+ /* Update PMCSETR (set register) */
return regmap_update_bits(dwmac->regmap, reg,
dwmac->ops->syscfg_eth_mask, val);
}
@@ -180,7 +243,7 @@ static int stm32mcu_set_mode(struct plat_stmmacenet_data *plat_dat)
}
return regmap_update_bits(dwmac->regmap, reg,
- dwmac->ops->syscfg_eth_mask, val);
+ dwmac->ops->syscfg_eth_mask, val << 23);
}
static void stm32_dwmac_clk_disable(struct stm32_dwmac *dwmac)
@@ -232,24 +295,29 @@ static int stm32_dwmac_parse_data(struct stm32_dwmac *dwmac,
static int stm32mp1_parse_data(struct stm32_dwmac *dwmac,
struct device *dev)
{
+ struct platform_device *pdev = to_platform_device(dev);
struct device_node *np = dev->of_node;
+ int err = 0;
- dwmac->int_phyclk = of_property_read_bool(np, "st,int-phyclk");
+ /* Gigabit Ethernet 125MHz clock selection. */
+ dwmac->eth_clk_sel_reg = of_property_read_bool(np, "st,eth-clk-sel");
- /* Check if internal clk from RCC selected */
- if (dwmac->int_phyclk) {
- /* Get ETH_CLK clocks */
- dwmac->clk_eth_ck = devm_clk_get(dev, "eth-ck");
- if (IS_ERR(dwmac->clk_eth_ck)) {
- dev_err(dev, "No ETH CK clock provided...\n");
- return PTR_ERR(dwmac->clk_eth_ck);
- }
+ /* Ethernet 50Mhz RMII clock selection */
+ dwmac->eth_ref_clk_sel_reg =
+ of_property_read_bool(np, "st,eth-ref-clk-sel");
+
+ /* Get ETH_CLK clocks */
+ dwmac->clk_eth_ck = devm_clk_get(dev, "eth-ck");
+ if (IS_ERR(dwmac->clk_eth_ck)) {
+ dev_warn(dev, "No phy clock provided...\n");
+ dwmac->clk_eth_ck = NULL;
}
/* Clock used for low power mode */
dwmac->clk_ethstp = devm_clk_get(dev, "ethstp");
if (IS_ERR(dwmac->clk_ethstp)) {
- dev_err(dev, "No ETH peripheral clock provided for CStop mode ...\n");
+ dev_err(dev,
+ "No ETH peripheral clock provided for CStop mode ...\n");
return PTR_ERR(dwmac->clk_ethstp);
}
@@ -260,7 +328,26 @@ static int stm32mp1_parse_data(struct stm32_dwmac *dwmac,
return PTR_ERR(dwmac->syscfg_clk);
}
- return 0;
+ /* Get IRQ information early to have an ability to ask for deferred
+ * probe if needed before we went too far with resource allocation.
+ */
+ dwmac->irq_pwr_wakeup = platform_get_irq_byname(pdev,
+ "stm32_pwr_wakeup");
+ if (!dwmac->clk_eth_ck && dwmac->irq_pwr_wakeup >= 0) {
+ err = device_init_wakeup(&pdev->dev, true);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to init wake up irq\n");
+ return err;
+ }
+ err = dev_pm_set_dedicated_wake_irq(&pdev->dev,
+ dwmac->irq_pwr_wakeup);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to set wake up irq\n");
+ device_init_wakeup(&pdev->dev, false);
+ }
+ device_set_wakeup_enable(&pdev->dev, false);
+ }
+ return err;
}
static int stm32_dwmac_probe(struct platform_device *pdev)
@@ -326,9 +413,15 @@ static int stm32_dwmac_remove(struct platform_device *pdev)
struct net_device *ndev = platform_get_drvdata(pdev);
struct stmmac_priv *priv = netdev_priv(ndev);
int ret = stmmac_dvr_remove(&pdev->dev);
+ struct stm32_dwmac *dwmac = priv->plat->bsp_priv;
stm32_dwmac_clk_disable(priv->plat->bsp_priv);
+ if (dwmac->irq_pwr_wakeup >= 0) {
+ dev_pm_clear_wake_irq(&pdev->dev);
+ device_init_wakeup(&pdev->dev, false);
+ }
+
return ret;
}
@@ -342,7 +435,7 @@ static int stm32mp1_suspend(struct stm32_dwmac *dwmac)
clk_disable_unprepare(dwmac->clk_tx);
clk_disable_unprepare(dwmac->syscfg_clk);
- if (dwmac->int_phyclk)
+ if (dwmac->clk_eth_ck)
clk_disable_unprepare(dwmac->clk_eth_ck);
return ret;
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
index 0f660af01a4b..195669f550f0 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
@@ -1147,7 +1147,10 @@ static int sun8i_dwmac_probe(struct platform_device *pdev)
return ret;
}
- plat_dat->interface = of_get_phy_mode(dev->of_node);
+ ret = of_get_phy_mode(dev->of_node);
+ if (ret < 0)
+ return -EINVAL;
+ plat_dat->interface = ret;
/* platform data specifying hardware features and callbacks.
* hardware features were copied from Allwinner drivers.
diff --git a/drivers/net/ethernet/stmicro/stmmac/ring_mode.c b/drivers/net/ethernet/stmicro/stmmac/ring_mode.c
index d8c5bc412219..4d9bcb4d0378 100644
--- a/drivers/net/ethernet/stmicro/stmmac/ring_mode.c
+++ b/drivers/net/ethernet/stmicro/stmmac/ring_mode.c
@@ -59,7 +59,7 @@ static int jumbo_frm(void *p, struct sk_buff *skb, int csum)
desc->des3 = cpu_to_le32(des2 + BUF_SIZE_4KiB);
stmmac_prepare_tx_desc(priv, desc, 1, bmax, csum,
- STMMAC_RING_MODE, 1, false, skb->len);
+ STMMAC_RING_MODE, 0, false, skb->len);
tx_q->tx_skbuff[entry] = NULL;
entry = STMMAC_GET_ENTRY(entry, DMA_TX_SIZE);
@@ -79,7 +79,8 @@ static int jumbo_frm(void *p, struct sk_buff *skb, int csum)
desc->des3 = cpu_to_le32(des2 + BUF_SIZE_4KiB);
stmmac_prepare_tx_desc(priv, desc, 0, len, csum,
- STMMAC_RING_MODE, 1, true, skb->len);
+ STMMAC_RING_MODE, 1, !skb_is_nonlinear(skb),
+ skb->len);
} else {
des2 = dma_map_single(priv->device, skb->data,
nopaged_len, DMA_TO_DEVICE);
@@ -91,7 +92,8 @@ static int jumbo_frm(void *p, struct sk_buff *skb, int csum)
tx_q->tx_skbuff_dma[entry].is_jumbo = true;
desc->des3 = cpu_to_le32(des2 + BUF_SIZE_4KiB);
stmmac_prepare_tx_desc(priv, desc, 1, nopaged_len, csum,
- STMMAC_RING_MODE, 1, true, skb->len);
+ STMMAC_RING_MODE, 0, !skb_is_nonlinear(skb),
+ skb->len);
}
tx_q->cur_tx = entry;
@@ -111,10 +113,11 @@ static unsigned int is_jumbo_frm(int len, int enh_desc)
static void refill_desc3(void *priv_ptr, struct dma_desc *p)
{
- struct stmmac_priv *priv = (struct stmmac_priv *)priv_ptr;
+ struct stmmac_rx_queue *rx_q = priv_ptr;
+ struct stmmac_priv *priv = rx_q->priv_data;
/* Fill DES3 in case of RING mode */
- if (priv->dma_buf_sz >= BUF_SIZE_8KiB)
+ if (priv->dma_buf_sz == BUF_SIZE_16KiB)
p->des3 = cpu_to_le32(le32_to_cpu(p->des2) + BUF_SIZE_8KiB);
}
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index e2a13ec2e30b..6a2e1031a62a 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -480,7 +480,7 @@ static void stmmac_get_tx_hwtstamp(struct stmmac_priv *priv,
struct dma_desc *p, struct sk_buff *skb)
{
struct skb_shared_hwtstamps shhwtstamp;
- u64 ns;
+ u64 ns = 0;
if (!priv->hwts_tx_en)
return;
@@ -519,7 +519,7 @@ static void stmmac_get_rx_hwtstamp(struct stmmac_priv *priv, struct dma_desc *p,
{
struct skb_shared_hwtstamps *shhwtstamp = NULL;
struct dma_desc *desc = p;
- u64 ns;
+ u64 ns = 0;
if (!priv->hwts_rx_en)
return;
@@ -564,8 +564,8 @@ static int stmmac_hwtstamp_set(struct net_device *dev, struct ifreq *ifr)
u32 snap_type_sel = 0;
u32 ts_master_en = 0;
u32 ts_event_en = 0;
+ u32 sec_inc = 0;
u32 value = 0;
- u32 sec_inc;
bool xmac;
xmac = priv->plat->has_gmac4 || priv->plat->has_xgmac;
@@ -3216,14 +3216,16 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
stmmac_prepare_tx_desc(priv, first, 1, nopaged_len,
csum_insertion, priv->mode, 1, last_segment,
skb->len);
-
- /* The own bit must be the latest setting done when prepare the
- * descriptor and then barrier is needed to make sure that
- * all is coherent before granting the DMA engine.
- */
- wmb();
+ } else {
+ stmmac_set_tx_owner(priv, first);
}
+ /* The own bit must be the latest setting done when prepare the
+ * descriptor and then barrier is needed to make sure that
+ * all is coherent before granting the DMA engine.
+ */
+ wmb();
+
netdev_tx_sent_queue(netdev_get_tx_queue(dev, queue), skb->len);
stmmac_enable_dma_transmission(priv, priv->ioaddr);
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
index 2b800ce1d5bf..3031f2bf15d6 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
@@ -408,6 +408,9 @@ stmmac_probe_config_dt(struct platform_device *pdev, const char **mac)
/* Default to phy auto-detection */
plat->phy_addr = -1;
+ /* Get clk_csr from device tree */
+ of_property_read_u32(np, "clk_csr", &plat->clk_csr);
+
/* "snps,phy-addr" is not a standard property. Mark it as deprecated
* and warn of its use. Remove this when phy node support is added.
*/
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c
index 2293e21f789f..cc60b3fb0892 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c
@@ -105,7 +105,7 @@ static int stmmac_get_time(struct ptp_clock_info *ptp, struct timespec64 *ts)
struct stmmac_priv *priv =
container_of(ptp, struct stmmac_priv, ptp_clock_ops);
unsigned long flags;
- u64 ns;
+ u64 ns = 0;
spin_lock_irqsave(&priv->ptp_lock, flags);
stmmac_get_systime(priv, priv->ptpaddr, &ns);
diff --git a/drivers/net/ethernet/sun/niu.c b/drivers/net/ethernet/sun/niu.c
index d84501441edd..6f99437a6962 100644
--- a/drivers/net/ethernet/sun/niu.c
+++ b/drivers/net/ethernet/sun/niu.c
@@ -7464,6 +7464,7 @@ static int niu_add_ethtool_tcam_entry(struct niu *np,
class = CLASS_CODE_USER_PROG4;
break;
default:
+ class = CLASS_CODE_UNRECOG;
break;
}
ret = tcam_user_ip_class_set(np, class, 0,
diff --git a/drivers/net/ethernet/ti/davinci_emac.c b/drivers/net/ethernet/ti/davinci_emac.c
index 840820402cd0..57450b174fc4 100644
--- a/drivers/net/ethernet/ti/davinci_emac.c
+++ b/drivers/net/ethernet/ti/davinci_emac.c
@@ -2029,7 +2029,6 @@ static const struct dev_pm_ops davinci_emac_pm_ops = {
.resume = davinci_emac_resume,
};
-#if IS_ENABLED(CONFIG_OF)
static const struct emac_platform_data am3517_emac_data = {
.version = EMAC_VERSION_2,
.hw_ram_addr = 0x01e20000,
@@ -2046,14 +2045,13 @@ static const struct of_device_id davinci_emac_of_match[] = {
{},
};
MODULE_DEVICE_TABLE(of, davinci_emac_of_match);
-#endif
/* davinci_emac_driver: EMAC platform driver structure */
static struct platform_driver davinci_emac_driver = {
.driver = {
.name = "davinci_emac",
.pm = &davinci_emac_pm_ops,
- .of_match_table = of_match_ptr(davinci_emac_of_match),
+ .of_match_table = davinci_emac_of_match,
},
.probe = davinci_emac_probe,
.remove = davinci_emac_remove,
diff --git a/drivers/net/ethernet/ti/netcp_ethss.c b/drivers/net/ethernet/ti/netcp_ethss.c
index 5174d318901e..0a920c5936b2 100644
--- a/drivers/net/ethernet/ti/netcp_ethss.c
+++ b/drivers/net/ethernet/ti/netcp_ethss.c
@@ -3657,12 +3657,16 @@ static int gbe_probe(struct netcp_device *netcp_device, struct device *dev,
ret = netcp_txpipe_init(&gbe_dev->tx_pipe, netcp_device,
gbe_dev->dma_chan_name, gbe_dev->tx_queue_id);
- if (ret)
+ if (ret) {
+ of_node_put(interfaces);
return ret;
+ }
ret = netcp_txpipe_open(&gbe_dev->tx_pipe);
- if (ret)
+ if (ret) {
+ of_node_put(interfaces);
return ret;
+ }
/* Create network interfaces */
INIT_LIST_HEAD(&gbe_dev->gbe_intf_head);
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
index ec7e7ec24ff9..4041c75997ba 100644
--- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
@@ -1575,12 +1575,14 @@ static int axienet_probe(struct platform_device *pdev)
ret = of_address_to_resource(np, 0, &dmares);
if (ret) {
dev_err(&pdev->dev, "unable to get DMA resource\n");
+ of_node_put(np);
goto free_netdev;
}
lp->dma_regs = devm_ioremap_resource(&pdev->dev, &dmares);
if (IS_ERR(lp->dma_regs)) {
dev_err(&pdev->dev, "could not map DMA regs\n");
ret = PTR_ERR(lp->dma_regs);
+ of_node_put(np);
goto free_netdev;
}
lp->rx_irq = irq_of_parse_and_map(np, 1);
diff --git a/drivers/net/ieee802154/adf7242.c b/drivers/net/ieee802154/adf7242.c
index cd1d8faccca5..cd6b95e673a5 100644
--- a/drivers/net/ieee802154/adf7242.c
+++ b/drivers/net/ieee802154/adf7242.c
@@ -1268,6 +1268,10 @@ static int adf7242_probe(struct spi_device *spi)
INIT_DELAYED_WORK(&lp->work, adf7242_rx_cal_work);
lp->wqueue = alloc_ordered_workqueue(dev_name(&spi->dev),
WQ_MEM_RECLAIM);
+ if (unlikely(!lp->wqueue)) {
+ ret = -ENOMEM;
+ goto err_hw_init;
+ }
ret = adf7242_hw_init(lp);
if (ret)
diff --git a/drivers/net/ieee802154/mac802154_hwsim.c b/drivers/net/ieee802154/mac802154_hwsim.c
index b6743f03dce0..3b88846de31b 100644
--- a/drivers/net/ieee802154/mac802154_hwsim.c
+++ b/drivers/net/ieee802154/mac802154_hwsim.c
@@ -324,7 +324,7 @@ static int hwsim_get_radio_nl(struct sk_buff *msg, struct genl_info *info)
goto out_err;
}
- genlmsg_reply(skb, info);
+ res = genlmsg_reply(skb, info);
break;
}
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 071869db44cf..520657945b82 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -7,6 +7,8 @@ menuconfig MDIO_DEVICE
help
MDIO devices and driver infrastructure code.
+if MDIO_DEVICE
+
config MDIO_BUS
tristate
default m if PHYLIB=m
@@ -179,6 +181,7 @@ config MDIO_XGENE
APM X-Gene SoC's.
endif
+endif
config PHYLINK
tristate
diff --git a/drivers/net/phy/broadcom.c b/drivers/net/phy/broadcom.c
index 9605d4fe540b..cb86a3e90c7d 100644
--- a/drivers/net/phy/broadcom.c
+++ b/drivers/net/phy/broadcom.c
@@ -323,6 +323,19 @@ static int bcm54xx_config_init(struct phy_device *phydev)
bcm54xx_phydsp_config(phydev);
+ /* Encode link speed into LED1 and LED3 pair (green/amber).
+ * Also flash these two LEDs on activity. This means configuring
+ * them for MULTICOLOR and encoding link/activity into them.
+ */
+ val = BCM5482_SHD_LEDS1_LED1(BCM_LED_SRC_MULTICOLOR1) |
+ BCM5482_SHD_LEDS1_LED3(BCM_LED_SRC_MULTICOLOR1);
+ bcm_phy_write_shadow(phydev, BCM5482_SHD_LEDS1, val);
+
+ val = BCM_LED_MULTICOLOR_IN_PHASE |
+ BCM5482_SHD_LEDS1_LED1(BCM_LED_MULTICOLOR_LINK_ACT) |
+ BCM5482_SHD_LEDS1_LED3(BCM_LED_MULTICOLOR_LINK_ACT);
+ bcm_phy_write_exp(phydev, BCM_EXP_MULTICOLOR, val);
+
return 0;
}
diff --git a/drivers/net/phy/dp83822.c b/drivers/net/phy/dp83822.c
index bbd8c22067f3..97d45bd5b38e 100644
--- a/drivers/net/phy/dp83822.c
+++ b/drivers/net/phy/dp83822.c
@@ -15,6 +15,8 @@
#include <linux/netdevice.h>
#define DP83822_PHY_ID 0x2000a240
+#define DP83825I_PHY_ID 0x2000a150
+
#define DP83822_DEVADDR 0x1f
#define MII_DP83822_PHYSCR 0x11
@@ -304,26 +306,30 @@ static int dp83822_resume(struct phy_device *phydev)
return 0;
}
+#define DP83822_PHY_DRIVER(_id, _name) \
+ { \
+ PHY_ID_MATCH_MODEL(_id), \
+ .name = (_name), \
+ .features = PHY_BASIC_FEATURES, \
+ .soft_reset = dp83822_phy_reset, \
+ .config_init = dp83822_config_init, \
+ .get_wol = dp83822_get_wol, \
+ .set_wol = dp83822_set_wol, \
+ .ack_interrupt = dp83822_ack_interrupt, \
+ .config_intr = dp83822_config_intr, \
+ .suspend = dp83822_suspend, \
+ .resume = dp83822_resume, \
+ }
+
static struct phy_driver dp83822_driver[] = {
- {
- .phy_id = DP83822_PHY_ID,
- .phy_id_mask = 0xfffffff0,
- .name = "TI DP83822",
- .features = PHY_BASIC_FEATURES,
- .config_init = dp83822_config_init,
- .soft_reset = dp83822_phy_reset,
- .get_wol = dp83822_get_wol,
- .set_wol = dp83822_set_wol,
- .ack_interrupt = dp83822_ack_interrupt,
- .config_intr = dp83822_config_intr,
- .suspend = dp83822_suspend,
- .resume = dp83822_resume,
- },
+ DP83822_PHY_DRIVER(DP83822_PHY_ID, "TI DP83822"),
+ DP83822_PHY_DRIVER(DP83825I_PHY_ID, "TI DP83825I"),
};
module_phy_driver(dp83822_driver);
static struct mdio_device_id __maybe_unused dp83822_tbl[] = {
{ DP83822_PHY_ID, 0xfffffff0 },
+ { DP83825I_PHY_ID, 0xfffffff0 },
{ },
};
MODULE_DEVICE_TABLE(mdio, dp83822_tbl);
diff --git a/drivers/net/phy/meson-gxl.c b/drivers/net/phy/meson-gxl.c
index a238388eb1a5..0eec2913c289 100644
--- a/drivers/net/phy/meson-gxl.c
+++ b/drivers/net/phy/meson-gxl.c
@@ -201,6 +201,7 @@ static int meson_gxl_ack_interrupt(struct phy_device *phydev)
static int meson_gxl_config_intr(struct phy_device *phydev)
{
u16 val;
+ int ret;
if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
val = INTSRC_ANEG_PR
@@ -213,6 +214,11 @@ static int meson_gxl_config_intr(struct phy_device *phydev)
val = 0;
}
+ /* Ack any pending IRQ */
+ ret = meson_gxl_ack_interrupt(phydev);
+ if (ret)
+ return ret;
+
return phy_write(phydev, INTSRC_MASK, val);
}
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 49fdd1ee798e..77068c545de0 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -1831,7 +1831,7 @@ int genphy_soft_reset(struct phy_device *phydev)
{
int ret;
- ret = phy_write(phydev, MII_BMCR, BMCR_RESET);
+ ret = phy_set_bits(phydev, MII_BMCR, BMCR_RESET);
if (ret < 0)
return ret;
diff --git a/drivers/net/ppp/pptp.c b/drivers/net/ppp/pptp.c
index 8f09edd811e9..50c60550f295 100644
--- a/drivers/net/ppp/pptp.c
+++ b/drivers/net/ppp/pptp.c
@@ -532,6 +532,7 @@ static void pptp_sock_destruct(struct sock *sk)
pppox_unbind_sock(sk);
}
skb_queue_purge(&sk->sk_receive_queue);
+ dst_release(rcu_dereference_protected(sk->sk_dst_cache, 1));
}
static int pptp_create(struct net *net, struct socket *sock, int kern)
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index 1d68921723dc..e9ca1c088d0b 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -1763,9 +1763,6 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
int skb_xdp = 1;
bool frags = tun_napi_frags_enabled(tfile);
- if (!(tun->dev->flags & IFF_UP))
- return -EIO;
-
if (!(tun->flags & IFF_NO_PI)) {
if (len < sizeof(pi))
return -EINVAL;
@@ -1867,6 +1864,8 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
err = skb_copy_datagram_from_iter(skb, 0, from, len);
if (err) {
+ err = -EFAULT;
+drop:
this_cpu_inc(tun->pcpu_stats->rx_dropped);
kfree_skb(skb);
if (frags) {
@@ -1874,7 +1873,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
mutex_unlock(&tfile->napi_mutex);
}
- return -EFAULT;
+ return err;
}
}
@@ -1958,6 +1957,13 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
!tfile->detached)
rxhash = __skb_get_hash_symmetric(skb);
+ rcu_read_lock();
+ if (unlikely(!(tun->dev->flags & IFF_UP))) {
+ err = -EIO;
+ rcu_read_unlock();
+ goto drop;
+ }
+
if (frags) {
/* Exercise flow dissector code path. */
u32 headlen = eth_get_headlen(skb->data, skb_headlen(skb));
@@ -1965,6 +1971,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
if (unlikely(headlen > skb_headlen(skb))) {
this_cpu_inc(tun->pcpu_stats->rx_dropped);
napi_free_frags(&tfile->napi);
+ rcu_read_unlock();
mutex_unlock(&tfile->napi_mutex);
WARN_ON(1);
return -ENOMEM;
@@ -1992,6 +1999,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
} else {
netif_rx_ni(skb);
}
+ rcu_read_unlock();
stats = get_cpu_ptr(tun->pcpu_stats);
u64_stats_update_begin(&stats->syncp);
diff --git a/drivers/net/usb/aqc111.c b/drivers/net/usb/aqc111.c
index 820a2fe7d027..aff995be2a31 100644
--- a/drivers/net/usb/aqc111.c
+++ b/drivers/net/usb/aqc111.c
@@ -1301,6 +1301,20 @@ static const struct driver_info trendnet_info = {
.tx_fixup = aqc111_tx_fixup,
};
+static const struct driver_info qnap_info = {
+ .description = "QNAP QNA-UC5G1T USB to 5GbE Adapter",
+ .bind = aqc111_bind,
+ .unbind = aqc111_unbind,
+ .status = aqc111_status,
+ .link_reset = aqc111_link_reset,
+ .reset = aqc111_reset,
+ .stop = aqc111_stop,
+ .flags = FLAG_ETHER | FLAG_FRAMING_AX |
+ FLAG_AVOID_UNLINK_URBS | FLAG_MULTI_PACKET,
+ .rx_fixup = aqc111_rx_fixup,
+ .tx_fixup = aqc111_tx_fixup,
+};
+
static int aqc111_suspend(struct usb_interface *intf, pm_message_t message)
{
struct usbnet *dev = usb_get_intfdata(intf);
@@ -1455,6 +1469,7 @@ static const struct usb_device_id products[] = {
{AQC111_USB_ETH_DEV(0x0b95, 0x2790, asix111_info)},
{AQC111_USB_ETH_DEV(0x0b95, 0x2791, asix112_info)},
{AQC111_USB_ETH_DEV(0x20f4, 0xe05a, trendnet_info)},
+ {AQC111_USB_ETH_DEV(0x1c04, 0x0015, qnap_info)},
{ },/* END */
};
MODULE_DEVICE_TABLE(usb, products);
diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c
index 5512a1038721..3e9b2c319e45 100644
--- a/drivers/net/usb/cdc_ether.c
+++ b/drivers/net/usb/cdc_ether.c
@@ -851,6 +851,14 @@ static const struct usb_device_id products[] = {
.driver_info = 0,
},
+/* QNAP QNA-UC5G1T USB to 5GbE Adapter (based on AQC111U) */
+{
+ USB_DEVICE_AND_INTERFACE_INFO(0x1c04, 0x0015, USB_CLASS_COMM,
+ USB_CDC_SUBCLASS_ETHERNET,
+ USB_CDC_PROTO_NONE),
+ .driver_info = 0,
+},
+
/* WHITELIST!!!
*
* CDC Ether uses two interfaces, not necessarily consecutive.
diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c
index a3c46d78d216..d76dfed8d9bb 100644
--- a/drivers/net/vxlan.c
+++ b/drivers/net/vxlan.c
@@ -1731,6 +1731,14 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)
goto drop;
}
+ rcu_read_lock();
+
+ if (unlikely(!(vxlan->dev->flags & IFF_UP))) {
+ rcu_read_unlock();
+ atomic_long_inc(&vxlan->dev->rx_dropped);
+ goto drop;
+ }
+
stats = this_cpu_ptr(vxlan->dev->tstats);
u64_stats_update_begin(&stats->syncp);
stats->rx_packets++;
@@ -1738,6 +1746,9 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)
u64_stats_update_end(&stats->syncp);
gro_cells_receive(&vxlan->gro_cells, skb);
+
+ rcu_read_unlock();
+
return 0;
drop:
@@ -2690,7 +2701,7 @@ static void vxlan_cleanup(struct timer_list *t)
for (h = 0; h < FDB_HASH_SIZE; ++h) {
struct hlist_node *p, *n;
- spin_lock_bh(&vxlan->hash_lock);
+ spin_lock(&vxlan->hash_lock);
hlist_for_each_safe(p, n, &vxlan->fdb_head[h]) {
struct vxlan_fdb *f
= container_of(p, struct vxlan_fdb, hlist);
@@ -2712,7 +2723,7 @@ static void vxlan_cleanup(struct timer_list *t)
} else if (time_before(timeout, next_timer))
next_timer = timeout;
}
- spin_unlock_bh(&vxlan->hash_lock);
+ spin_unlock(&vxlan->hash_lock);
}
mod_timer(&vxlan->age_timer, next_timer);
@@ -2767,6 +2778,8 @@ static void vxlan_uninit(struct net_device *dev)
{
struct vxlan_dev *vxlan = netdev_priv(dev);
+ gro_cells_destroy(&vxlan->gro_cells);
+
vxlan_fdb_delete_default(vxlan, vxlan->cfg.vni);
free_percpu(dev->tstats);
@@ -3942,7 +3955,6 @@ static void vxlan_dellink(struct net_device *dev, struct list_head *head)
vxlan_flush(vxlan, true);
- gro_cells_destroy(&vxlan->gro_cells);
list_del(&vxlan->next);
unregister_netdevice_queue(dev, head);
}
@@ -4323,10 +4335,8 @@ static void vxlan_destroy_tunnels(struct net *net, struct list_head *head)
/* If vxlan->dev is in the same netns, it has already been added
* to the list by the previous loop.
*/
- if (!net_eq(dev_net(vxlan->dev), net)) {
- gro_cells_destroy(&vxlan->gro_cells);
+ if (!net_eq(dev_net(vxlan->dev), net))
unregister_netdevice_queue(vxlan->dev, head);
- }
}
for (h = 0; h < PORT_HASH_SIZE; ++h)
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c b/drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c
index e9822a3ec373..94132cfd1f56 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c
@@ -460,9 +460,7 @@ static int iwl_mvm_ftm_range_resp_valid(struct iwl_mvm *mvm, u8 request_id,
static void iwl_mvm_debug_range_resp(struct iwl_mvm *mvm, u8 index,
struct cfg80211_pmsr_result *res)
{
- s64 rtt_avg = res->ftm.rtt_avg * 100;
-
- do_div(rtt_avg, 6666);
+ s64 rtt_avg = div_s64(res->ftm.rtt_avg * 100, 6666);
IWL_DEBUG_INFO(mvm, "entry %d\n", index);
IWL_DEBUG_INFO(mvm, "\tstatus: %d\n", res->status);
diff --git a/drivers/net/wireless/mediatek/mt76/dma.c b/drivers/net/wireless/mediatek/mt76/dma.c
index 6eedc0ec7661..76629b98c78d 100644
--- a/drivers/net/wireless/mediatek/mt76/dma.c
+++ b/drivers/net/wireless/mediatek/mt76/dma.c
@@ -130,6 +130,8 @@ mt76_dma_tx_cleanup_idx(struct mt76_dev *dev, struct mt76_queue *q, int idx,
static void
mt76_dma_sync_idx(struct mt76_dev *dev, struct mt76_queue *q)
{
+ iowrite32(q->desc_dma, &q->regs->desc_base);
+ iowrite32(q->ndesc, &q->regs->ring_size);
q->head = ioread32(&q->regs->dma_idx);
q->tail = q->head;
iowrite32(q->head, &q->regs->cpu_idx);
@@ -180,7 +182,10 @@ mt76_dma_tx_cleanup(struct mt76_dev *dev, enum mt76_txq_id qid, bool flush)
else
mt76_dma_sync_idx(dev, q);
- wake = wake && qid < IEEE80211_NUM_ACS && q->queued < q->ndesc - 8;
+ wake = wake && q->stopped &&
+ qid < IEEE80211_NUM_ACS && q->queued < q->ndesc - 8;
+ if (wake)
+ q->stopped = false;
if (!q->queued)
wake_up(&dev->tx_wait);
diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c
index a033745adb2f..316167404729 100644
--- a/drivers/net/wireless/mediatek/mt76/mac80211.c
+++ b/drivers/net/wireless/mediatek/mt76/mac80211.c
@@ -679,19 +679,15 @@ out:
return ret;
}
-static void
-mt76_sta_remove(struct mt76_dev *dev, struct ieee80211_vif *vif,
- struct ieee80211_sta *sta)
+void __mt76_sta_remove(struct mt76_dev *dev, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
{
struct mt76_wcid *wcid = (struct mt76_wcid *)sta->drv_priv;
- int idx = wcid->idx;
- int i;
+ int i, idx = wcid->idx;
rcu_assign_pointer(dev->wcid[idx], NULL);
synchronize_rcu();
- mutex_lock(&dev->mutex);
-
if (dev->drv->sta_remove)
dev->drv->sta_remove(dev, vif, sta);
@@ -699,7 +695,15 @@ mt76_sta_remove(struct mt76_dev *dev, struct ieee80211_vif *vif,
for (i = 0; i < ARRAY_SIZE(sta->txq); i++)
mt76_txq_remove(dev, sta->txq[i]);
mt76_wcid_free(dev->wcid_mask, idx);
+}
+EXPORT_SYMBOL_GPL(__mt76_sta_remove);
+static void
+mt76_sta_remove(struct mt76_dev *dev, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ mutex_lock(&dev->mutex);
+ __mt76_sta_remove(dev, vif, sta);
mutex_unlock(&dev->mutex);
}
diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h
index 5dfb0601f101..bcbfd3c4a44b 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76.h
@@ -126,6 +126,7 @@ struct mt76_queue {
int ndesc;
int queued;
int buf_size;
+ bool stopped;
u8 buf_offset;
u8 hw_idx;
@@ -143,6 +144,7 @@ struct mt76_mcu_ops {
const struct mt76_reg_pair *rp, int len);
int (*mcu_rd_rp)(struct mt76_dev *dev, u32 base,
struct mt76_reg_pair *rp, int len);
+ int (*mcu_restart)(struct mt76_dev *dev);
};
struct mt76_queue_ops {
@@ -693,6 +695,8 @@ int mt76_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
enum ieee80211_sta_state old_state,
enum ieee80211_sta_state new_state);
+void __mt76_sta_remove(struct mt76_dev *dev, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta);
struct ieee80211_sta *mt76_rx_convert(struct sk_buff *skb);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/beacon.c b/drivers/net/wireless/mediatek/mt76/mt7603/beacon.c
index afcd86f735b4..4dcb465095d1 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7603/beacon.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7603/beacon.c
@@ -135,8 +135,7 @@ void mt7603_pre_tbtt_tasklet(unsigned long arg)
out:
mt76_queue_tx_cleanup(dev, MT_TXQ_BEACON, false);
- if (dev->mt76.q_tx[MT_TXQ_BEACON].queued >
- __sw_hweight8(dev->beacon_mask))
+ if (dev->mt76.q_tx[MT_TXQ_BEACON].queued > hweight8(dev->beacon_mask))
dev->beacon_check++;
}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/dma.c b/drivers/net/wireless/mediatek/mt76/mt7603/dma.c
index d69e82c66ab2..b3ae0aaea62a 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7603/dma.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7603/dma.c
@@ -27,12 +27,16 @@ static void
mt7603_rx_loopback_skb(struct mt7603_dev *dev, struct sk_buff *skb)
{
__le32 *txd = (__le32 *)skb->data;
+ struct ieee80211_hdr *hdr;
+ struct ieee80211_sta *sta;
struct mt7603_sta *msta;
struct mt76_wcid *wcid;
+ void *priv;
int idx;
u32 val;
+ u8 tid;
- if (skb->len < sizeof(MT_TXD_SIZE) + sizeof(struct ieee80211_hdr))
+ if (skb->len < MT_TXD_SIZE + sizeof(struct ieee80211_hdr))
goto free;
val = le32_to_cpu(txd[1]);
@@ -46,10 +50,19 @@ mt7603_rx_loopback_skb(struct mt7603_dev *dev, struct sk_buff *skb)
if (!wcid)
goto free;
- msta = container_of(wcid, struct mt7603_sta, wcid);
+ priv = msta = container_of(wcid, struct mt7603_sta, wcid);
val = le32_to_cpu(txd[0]);
skb_set_queue_mapping(skb, FIELD_GET(MT_TXD0_Q_IDX, val));
+ val &= ~(MT_TXD0_P_IDX | MT_TXD0_Q_IDX);
+ val |= FIELD_PREP(MT_TXD0_Q_IDX, MT_TX_HW_QUEUE_MGMT);
+ txd[0] = cpu_to_le32(val);
+
+ sta = container_of(priv, struct ieee80211_sta, drv_priv);
+ hdr = (struct ieee80211_hdr *) &skb->data[MT_TXD_SIZE];
+ tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK;
+ ieee80211_sta_set_buffered(sta, tid, true);
+
spin_lock_bh(&dev->ps_lock);
__skb_queue_tail(&msta->psq, skb);
if (skb_queue_len(&msta->psq) >= 64) {
diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/init.c b/drivers/net/wireless/mediatek/mt76/mt7603/init.c
index 15cc8f33b34d..d54dda67d036 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7603/init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7603/init.c
@@ -112,7 +112,7 @@ static void
mt7603_phy_init(struct mt7603_dev *dev)
{
int rx_chains = dev->mt76.antenna_mask;
- int tx_chains = __sw_hweight8(rx_chains) - 1;
+ int tx_chains = hweight8(rx_chains) - 1;
mt76_rmw(dev, MT_WF_RMAC_RMCR,
(MT_WF_RMAC_RMCR_SMPS_MODE |
diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/mac.c b/drivers/net/wireless/mediatek/mt76/mt7603/mac.c
index 0a0115861b51..5e31d7da96fc 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7603/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7603/mac.c
@@ -1072,7 +1072,7 @@ out:
case MT_PHY_TYPE_HT:
final_rate_flags |= IEEE80211_TX_RC_MCS;
final_rate &= GENMASK(5, 0);
- if (i > 15)
+ if (final_rate > 15)
return false;
break;
default:
diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/main.c b/drivers/net/wireless/mediatek/mt76/mt7603/main.c
index b10775ed92e6..cc0fe0933b2d 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7603/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7603/main.c
@@ -5,6 +5,7 @@
#include <linux/pci.h>
#include <linux/module.h>
#include "mt7603.h"
+#include "mac.h"
#include "eeprom.h"
static int
@@ -386,6 +387,15 @@ mt7603_sta_ps(struct mt76_dev *mdev, struct ieee80211_sta *sta, bool ps)
}
static void
+mt7603_ps_set_more_data(struct sk_buff *skb)
+{
+ struct ieee80211_hdr *hdr;
+
+ hdr = (struct ieee80211_hdr *) &skb->data[MT_TXD_SIZE];
+ hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA);
+}
+
+static void
mt7603_release_buffered_frames(struct ieee80211_hw *hw,
struct ieee80211_sta *sta,
u16 tids, int nframes,
@@ -399,6 +409,8 @@ mt7603_release_buffered_frames(struct ieee80211_hw *hw,
__skb_queue_head_init(&list);
+ mt7603_wtbl_set_ps(dev, msta, false);
+
spin_lock_bh(&dev->ps_lock);
skb_queue_walk_safe(&msta->psq, skb, tmp) {
if (!nframes)
@@ -409,11 +421,15 @@ mt7603_release_buffered_frames(struct ieee80211_hw *hw,
skb_set_queue_mapping(skb, MT_TXQ_PSD);
__skb_unlink(skb, &msta->psq);
+ mt7603_ps_set_more_data(skb);
__skb_queue_tail(&list, skb);
nframes--;
}
spin_unlock_bh(&dev->ps_lock);
+ if (!skb_queue_empty(&list))
+ ieee80211_sta_eosp(sta);
+
mt7603_ps_tx_list(dev, &list);
if (nframes)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7603/mcu.c
index 4b0713f1fd5e..d06905ea8cc6 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7603/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7603/mcu.c
@@ -433,7 +433,7 @@ int mt7603_mcu_set_channel(struct mt7603_dev *dev)
{
struct cfg80211_chan_def *chandef = &dev->mt76.chandef;
struct ieee80211_hw *hw = mt76_hw(dev);
- int n_chains = __sw_hweight8(dev->mt76.antenna_mask);
+ int n_chains = hweight8(dev->mt76.antenna_mask);
struct {
u8 control_chan;
u8 center_chan;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/soc.c b/drivers/net/wireless/mediatek/mt76/mt7603/soc.c
index e13fea80d970..b920be1f5718 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7603/soc.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7603/soc.c
@@ -23,9 +23,9 @@ mt76_wmac_probe(struct platform_device *pdev)
}
mem_base = devm_ioremap_resource(&pdev->dev, res);
- if (!mem_base) {
+ if (IS_ERR(mem_base)) {
dev_err(&pdev->dev, "Failed to get memory resource\n");
- return -EINVAL;
+ return PTR_ERR(mem_base);
}
mdev = mt76_alloc_device(&pdev->dev, sizeof(*dev), &mt7603_ops,
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/initvals.h b/drivers/net/wireless/mediatek/mt76/mt76x0/initvals.h
index 0290ba5869a5..736f81752b5b 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x0/initvals.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76x0/initvals.h
@@ -46,7 +46,7 @@ static const struct mt76_reg_pair common_mac_reg_table[] = {
{ MT_MM20_PROT_CFG, 0x01742004 },
{ MT_MM40_PROT_CFG, 0x03f42084 },
{ MT_TXOP_CTRL_CFG, 0x0000583f },
- { MT_TX_RTS_CFG, 0x00092b20 },
+ { MT_TX_RTS_CFG, 0x00ffff20 },
{ MT_EXP_ACK_TIME, 0x002400ca },
{ MT_TXOP_HLDR_ET, 0x00000002 },
{ MT_XIFS_TIME_CFG, 0x33a41010 },
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c b/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c
index 91718647da02..e5a06f74a6f7 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c
@@ -229,7 +229,7 @@ static int mt76x0u_probe(struct usb_interface *usb_intf,
struct usb_device *usb_dev = interface_to_usbdev(usb_intf);
struct mt76x02_dev *dev;
struct mt76_dev *mdev;
- u32 asic_rev, mac_rev;
+ u32 mac_rev;
int ret;
mdev = mt76_alloc_device(&usb_intf->dev, sizeof(*dev), &mt76x0u_ops,
@@ -262,10 +262,14 @@ static int mt76x0u_probe(struct usb_interface *usb_intf,
goto err;
}
- asic_rev = mt76_rr(dev, MT_ASIC_VERSION);
+ mdev->rev = mt76_rr(dev, MT_ASIC_VERSION);
mac_rev = mt76_rr(dev, MT_MAC_CSR0);
dev_info(mdev->dev, "ASIC revision: %08x MAC revision: %08x\n",
- asic_rev, mac_rev);
+ mdev->rev, mac_rev);
+ if (!is_mt76x0(dev)) {
+ ret = -ENODEV;
+ goto err;
+ }
/* Note: vendor driver skips this check for MT76X0U */
if (!(mt76_rr(dev, MT_EFUSE_CTRL) & MT_EFUSE_CTRL_SEL))
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02.h b/drivers/net/wireless/mediatek/mt76/mt76x02.h
index 6915cce5def9..07061eb4d1e1 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02.h
@@ -51,6 +51,7 @@ struct mt76x02_calibration {
u16 false_cca;
s8 avg_rssi_all;
s8 agc_gain_adjust;
+ s8 agc_lowest_gain;
s8 low_gain;
s8 temp_vco;
@@ -114,8 +115,11 @@ struct mt76x02_dev {
struct mt76x02_dfs_pattern_detector dfs_pd;
/* edcca monitor */
+ unsigned long ed_trigger_timeout;
bool ed_tx_blocked;
bool ed_monitor;
+ u8 ed_monitor_enabled;
+ u8 ed_monitor_learning;
u8 ed_trigger;
u8 ed_silent;
ktime_t ed_time;
@@ -188,6 +192,13 @@ void mt76x02_mac_start(struct mt76x02_dev *dev);
void mt76x02_init_debugfs(struct mt76x02_dev *dev);
+static inline bool is_mt76x0(struct mt76x02_dev *dev)
+{
+ return mt76_chip(&dev->mt76) == 0x7610 ||
+ mt76_chip(&dev->mt76) == 0x7630 ||
+ mt76_chip(&dev->mt76) == 0x7650;
+}
+
static inline bool is_mt76x2(struct mt76x02_dev *dev)
{
return mt76_chip(&dev->mt76) == 0x7612 ||
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_debugfs.c b/drivers/net/wireless/mediatek/mt76/mt76x02_debugfs.c
index 7580c5c986ff..b1d6fd4861e3 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02_debugfs.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02_debugfs.c
@@ -116,6 +116,32 @@ static int read_agc(struct seq_file *file, void *data)
return 0;
}
+static int
+mt76_edcca_set(void *data, u64 val)
+{
+ struct mt76x02_dev *dev = data;
+ enum nl80211_dfs_regions region = dev->dfs_pd.region;
+
+ dev->ed_monitor_enabled = !!val;
+ dev->ed_monitor = dev->ed_monitor_enabled &&
+ region == NL80211_DFS_ETSI;
+ mt76x02_edcca_init(dev, true);
+
+ return 0;
+}
+
+static int
+mt76_edcca_get(void *data, u64 *val)
+{
+ struct mt76x02_dev *dev = data;
+
+ *val = dev->ed_monitor_enabled;
+ return 0;
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(fops_edcca, mt76_edcca_get, mt76_edcca_set,
+ "%lld\n");
+
void mt76x02_init_debugfs(struct mt76x02_dev *dev)
{
struct dentry *dir;
@@ -127,6 +153,7 @@ void mt76x02_init_debugfs(struct mt76x02_dev *dev)
debugfs_create_u8("temperature", 0400, dir, &dev->cal.temp);
debugfs_create_bool("tpc", 0600, dir, &dev->enable_tpc);
+ debugfs_create_file("edcca", 0400, dir, dev, &fops_edcca);
debugfs_create_file("ampdu_stat", 0400, dir, dev, &fops_ampdu_stat);
debugfs_create_file("dfs_stats", 0400, dir, dev, &fops_dfs_stat);
debugfs_create_devm_seqfile(dev->mt76.dev, "txpower", dir,
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_dfs.c b/drivers/net/wireless/mediatek/mt76/mt76x02_dfs.c
index e4649103efd4..17d12d212d1b 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02_dfs.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02_dfs.c
@@ -885,7 +885,8 @@ mt76x02_dfs_set_domain(struct mt76x02_dev *dev,
if (dfs_pd->region != region) {
tasklet_disable(&dfs_pd->dfs_tasklet);
- dev->ed_monitor = region == NL80211_DFS_ETSI;
+ dev->ed_monitor = dev->ed_monitor_enabled &&
+ region == NL80211_DFS_ETSI;
mt76x02_edcca_init(dev, true);
dfs_pd->region = region;
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c b/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c
index 91ff6598eccf..9ed231abe916 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c
@@ -67,12 +67,39 @@ int mt76x02_mac_shared_key_setup(struct mt76x02_dev *dev, u8 vif_idx,
}
EXPORT_SYMBOL_GPL(mt76x02_mac_shared_key_setup);
+void mt76x02_mac_wcid_sync_pn(struct mt76x02_dev *dev, u8 idx,
+ struct ieee80211_key_conf *key)
+{
+ enum mt76x02_cipher_type cipher;
+ u8 key_data[32];
+ u32 iv, eiv;
+ u64 pn;
+
+ cipher = mt76x02_mac_get_key_info(key, key_data);
+ iv = mt76_rr(dev, MT_WCID_IV(idx));
+ eiv = mt76_rr(dev, MT_WCID_IV(idx) + 4);
+
+ pn = (u64)eiv << 16;
+ if (cipher == MT_CIPHER_TKIP) {
+ pn |= (iv >> 16) & 0xff;
+ pn |= (iv & 0xff) << 8;
+ } else if (cipher >= MT_CIPHER_AES_CCMP) {
+ pn |= iv & 0xffff;
+ } else {
+ return;
+ }
+
+ atomic64_set(&key->tx_pn, pn);
+}
+
+
int mt76x02_mac_wcid_set_key(struct mt76x02_dev *dev, u8 idx,
struct ieee80211_key_conf *key)
{
enum mt76x02_cipher_type cipher;
u8 key_data[32];
u8 iv_data[8];
+ u64 pn;
cipher = mt76x02_mac_get_key_info(key, key_data);
if (cipher == MT_CIPHER_NONE && key)
@@ -85,9 +112,22 @@ int mt76x02_mac_wcid_set_key(struct mt76x02_dev *dev, u8 idx,
if (key) {
mt76_rmw_field(dev, MT_WCID_ATTR(idx), MT_WCID_ATTR_PAIRWISE,
!!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE));
+
+ pn = atomic64_read(&key->tx_pn);
+
iv_data[3] = key->keyidx << 6;
- if (cipher >= MT_CIPHER_TKIP)
+ if (cipher >= MT_CIPHER_TKIP) {
iv_data[3] |= 0x20;
+ put_unaligned_le32(pn >> 16, &iv_data[4]);
+ }
+
+ if (cipher == MT_CIPHER_TKIP) {
+ iv_data[0] = (pn >> 8) & 0xff;
+ iv_data[1] = (iv_data[0] | 0x20) & 0x7f;
+ iv_data[2] = pn & 0xff;
+ } else if (cipher >= MT_CIPHER_AES_CCMP) {
+ put_unaligned_le16((pn & 0xffff), &iv_data[0]);
+ }
}
mt76_wr_copy(dev, MT_WCID_IV(idx), iv_data, sizeof(iv_data));
@@ -920,6 +960,7 @@ void mt76x02_edcca_init(struct mt76x02_dev *dev, bool enable)
}
}
mt76x02_edcca_tx_enable(dev, true);
+ dev->ed_monitor_learning = true;
/* clear previous CCA timer value */
mt76_rr(dev, MT_ED_CCA_TIMER);
@@ -929,6 +970,10 @@ EXPORT_SYMBOL_GPL(mt76x02_edcca_init);
#define MT_EDCCA_TH 92
#define MT_EDCCA_BLOCK_TH 2
+#define MT_EDCCA_LEARN_TH 50
+#define MT_EDCCA_LEARN_CCA 180
+#define MT_EDCCA_LEARN_TIMEOUT (20 * HZ)
+
static void mt76x02_edcca_check(struct mt76x02_dev *dev)
{
ktime_t cur_time;
@@ -951,11 +996,23 @@ static void mt76x02_edcca_check(struct mt76x02_dev *dev)
dev->ed_trigger = 0;
}
- if (dev->ed_trigger > MT_EDCCA_BLOCK_TH &&
- !dev->ed_tx_blocked)
+ if (dev->cal.agc_lowest_gain &&
+ dev->cal.false_cca > MT_EDCCA_LEARN_CCA &&
+ dev->ed_trigger > MT_EDCCA_LEARN_TH) {
+ dev->ed_monitor_learning = false;
+ dev->ed_trigger_timeout = jiffies + 20 * HZ;
+ } else if (!dev->ed_monitor_learning &&
+ time_is_after_jiffies(dev->ed_trigger_timeout)) {
+ dev->ed_monitor_learning = true;
+ mt76x02_edcca_tx_enable(dev, true);
+ }
+
+ if (dev->ed_monitor_learning)
+ return;
+
+ if (dev->ed_trigger > MT_EDCCA_BLOCK_TH && !dev->ed_tx_blocked)
mt76x02_edcca_tx_enable(dev, false);
- else if (dev->ed_silent > MT_EDCCA_BLOCK_TH &&
- dev->ed_tx_blocked)
+ else if (dev->ed_silent > MT_EDCCA_BLOCK_TH && dev->ed_tx_blocked)
mt76x02_edcca_tx_enable(dev, true);
}
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_mac.h b/drivers/net/wireless/mediatek/mt76/mt76x02_mac.h
index 6b1f25d2f64c..caeeef96c42f 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02_mac.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02_mac.h
@@ -177,6 +177,8 @@ int mt76x02_mac_shared_key_setup(struct mt76x02_dev *dev, u8 vif_idx,
u8 key_idx, struct ieee80211_key_conf *key);
int mt76x02_mac_wcid_set_key(struct mt76x02_dev *dev, u8 idx,
struct ieee80211_key_conf *key);
+void mt76x02_mac_wcid_sync_pn(struct mt76x02_dev *dev, u8 idx,
+ struct ieee80211_key_conf *key);
void mt76x02_mac_wcid_setup(struct mt76x02_dev *dev, u8 idx, u8 vif_idx,
u8 *mac);
void mt76x02_mac_wcid_set_drop(struct mt76x02_dev *dev, u8 idx, bool drop);
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c b/drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c
index 1229f19f2b02..daaed1220147 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c
@@ -19,6 +19,7 @@
#include <linux/irq.h>
#include "mt76x02.h"
+#include "mt76x02_mcu.h"
#include "mt76x02_trace.h"
struct beacon_bc_data {
@@ -418,9 +419,66 @@ static bool mt76x02_tx_hang(struct mt76x02_dev *dev)
return i < 4;
}
+static void mt76x02_key_sync(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key, void *data)
+{
+ struct mt76x02_dev *dev = hw->priv;
+ struct mt76_wcid *wcid;
+
+ if (!sta)
+ return;
+
+ wcid = (struct mt76_wcid *) sta->drv_priv;
+
+ if (wcid->hw_key_idx != key->keyidx || wcid->sw_iv)
+ return;
+
+ mt76x02_mac_wcid_sync_pn(dev, wcid->idx, key);
+}
+
+static void mt76x02_reset_state(struct mt76x02_dev *dev)
+{
+ int i;
+
+ lockdep_assert_held(&dev->mt76.mutex);
+
+ clear_bit(MT76_STATE_RUNNING, &dev->mt76.state);
+
+ rcu_read_lock();
+ ieee80211_iter_keys_rcu(dev->mt76.hw, NULL, mt76x02_key_sync, NULL);
+ rcu_read_unlock();
+
+ for (i = 0; i < ARRAY_SIZE(dev->mt76.wcid); i++) {
+ struct ieee80211_sta *sta;
+ struct ieee80211_vif *vif;
+ struct mt76x02_sta *msta;
+ struct mt76_wcid *wcid;
+ void *priv;
+
+ wcid = rcu_dereference_protected(dev->mt76.wcid[i],
+ lockdep_is_held(&dev->mt76.mutex));
+ if (!wcid)
+ continue;
+
+ priv = msta = container_of(wcid, struct mt76x02_sta, wcid);
+ sta = container_of(priv, struct ieee80211_sta, drv_priv);
+
+ priv = msta->vif;
+ vif = container_of(priv, struct ieee80211_vif, drv_priv);
+
+ __mt76_sta_remove(&dev->mt76, vif, sta);
+ memset(msta, 0, sizeof(*msta));
+ }
+
+ dev->vif_mask = 0;
+ dev->beacon_mask = 0;
+}
+
static void mt76x02_watchdog_reset(struct mt76x02_dev *dev)
{
u32 mask = dev->mt76.mmio.irqmask;
+ bool restart = dev->mt76.mcu_ops->mcu_restart;
int i;
ieee80211_stop_queues(dev->mt76.hw);
@@ -434,6 +492,9 @@ static void mt76x02_watchdog_reset(struct mt76x02_dev *dev)
mutex_lock(&dev->mt76.mutex);
+ if (restart)
+ mt76x02_reset_state(dev);
+
if (dev->beacon_mask)
mt76_clear(dev, MT_BEACON_TIME_CFG,
MT_BEACON_TIME_CFG_BEACON_TX |
@@ -452,20 +513,21 @@ static void mt76x02_watchdog_reset(struct mt76x02_dev *dev)
/* let fw reset DMA */
mt76_set(dev, 0x734, 0x3);
+ if (restart)
+ dev->mt76.mcu_ops->mcu_restart(&dev->mt76);
+
for (i = 0; i < ARRAY_SIZE(dev->mt76.q_tx); i++)
mt76_queue_tx_cleanup(dev, i, true);
for (i = 0; i < ARRAY_SIZE(dev->mt76.q_rx); i++)
mt76_queue_rx_reset(dev, i);
- mt76_wr(dev, MT_MAC_SYS_CTRL,
- MT_MAC_SYS_CTRL_ENABLE_TX | MT_MAC_SYS_CTRL_ENABLE_RX);
- mt76_set(dev, MT_WPDMA_GLO_CFG,
- MT_WPDMA_GLO_CFG_TX_DMA_EN | MT_WPDMA_GLO_CFG_RX_DMA_EN);
+ mt76x02_mac_start(dev);
+
if (dev->ed_monitor)
mt76_set(dev, MT_TXOP_CTRL_CFG, MT_TXOP_ED_CCA_EN);
- if (dev->beacon_mask)
+ if (dev->beacon_mask && !restart)
mt76_set(dev, MT_BEACON_TIME_CFG,
MT_BEACON_TIME_CFG_BEACON_TX |
MT_BEACON_TIME_CFG_TBTT_EN);
@@ -486,9 +548,13 @@ static void mt76x02_watchdog_reset(struct mt76x02_dev *dev)
napi_schedule(&dev->mt76.napi[i]);
}
- ieee80211_wake_queues(dev->mt76.hw);
-
- mt76_txq_schedule_all(&dev->mt76);
+ if (restart) {
+ mt76x02_mcu_function_select(dev, Q_SELECT, 1);
+ ieee80211_restart_hw(dev->mt76.hw);
+ } else {
+ ieee80211_wake_queues(dev->mt76.hw);
+ mt76_txq_schedule_all(&dev->mt76);
+ }
}
static void mt76x02_check_tx_hang(struct mt76x02_dev *dev)
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_phy.c b/drivers/net/wireless/mediatek/mt76/mt76x02_phy.c
index a020c757ba5c..a54b63a96eae 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02_phy.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02_phy.c
@@ -194,6 +194,8 @@ bool mt76x02_phy_adjust_vga_gain(struct mt76x02_dev *dev)
ret = true;
}
+ dev->cal.agc_lowest_gain = dev->cal.agc_gain_adjust >= limit;
+
return ret;
}
EXPORT_SYMBOL_GPL(mt76x02_phy_adjust_vga_gain);
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_usb_core.c b/drivers/net/wireless/mediatek/mt76/mt76x02_usb_core.c
index 43f07461c8d3..6fb52b596d42 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02_usb_core.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02_usb_core.c
@@ -85,8 +85,9 @@ int mt76x02u_tx_prepare_skb(struct mt76_dev *mdev, void *data,
mt76x02_insert_hdr_pad(skb);
- txwi = skb_push(skb, sizeof(struct mt76x02_txwi));
+ txwi = (struct mt76x02_txwi *)(skb->data - sizeof(struct mt76x02_txwi));
mt76x02_mac_write_txwi(dev, txwi, skb, wcid, sta, len);
+ skb_push(skb, sizeof(struct mt76x02_txwi));
pid = mt76_tx_status_skb_add(mdev, wcid, skb);
txwi->pktid = pid;
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_util.c b/drivers/net/wireless/mediatek/mt76/mt76x02_util.c
index a48c261b0c63..cd072ac614f7 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02_util.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02_util.c
@@ -237,6 +237,8 @@ int mt76x02_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
struct mt76x02_vif *mvif = (struct mt76x02_vif *)vif->drv_priv;
int idx = 0;
+ memset(msta, 0, sizeof(*msta));
+
idx = mt76_wcid_alloc(dev->mt76.wcid_mask, ARRAY_SIZE(dev->mt76.wcid));
if (idx < 0)
return -ENOSPC;
@@ -274,6 +276,8 @@ mt76x02_vif_init(struct mt76x02_dev *dev, struct ieee80211_vif *vif,
struct mt76x02_vif *mvif = (struct mt76x02_vif *)vif->drv_priv;
struct mt76_txq *mtxq;
+ memset(mvif, 0, sizeof(*mvif));
+
mvif->idx = idx;
mvif->group_wcid.idx = MT_VIF_WCID(idx);
mvif->group_wcid.hw_key_idx = -1;
@@ -289,6 +293,12 @@ mt76x02_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
struct mt76x02_dev *dev = hw->priv;
unsigned int idx = 0;
+ /* Allow to change address in HW if we create first interface. */
+ if (!dev->vif_mask &&
+ (((vif->addr[0] ^ dev->mt76.macaddr[0]) & ~GENMASK(4, 1)) ||
+ memcmp(vif->addr + 1, dev->mt76.macaddr + 1, ETH_ALEN - 1)))
+ mt76x02_mac_setaddr(dev, vif->addr);
+
if (vif->addr[0] & BIT(1))
idx = 1 + (((dev->mt76.macaddr[0] ^ vif->addr[0]) >> 2) & 7);
@@ -311,10 +321,6 @@ mt76x02_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
if (dev->vif_mask & BIT(idx))
return -EBUSY;
- /* Allow to change address in HW if we create first interface. */
- if (!dev->vif_mask && !ether_addr_equal(dev->mt76.macaddr, vif->addr))
- mt76x02_mac_setaddr(dev, vif->addr);
-
dev->vif_mask |= BIT(idx);
mt76x02_vif_init(dev, vif, idx);
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/init.c b/drivers/net/wireless/mediatek/mt76/mt76x2/init.c
index f8534362e2c8..a30ef2c5a9db 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2/init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2/init.c
@@ -106,7 +106,7 @@ void mt76_write_mac_initvals(struct mt76x02_dev *dev)
{ MT_TX_SW_CFG1, 0x00010000 },
{ MT_TX_SW_CFG2, 0x00000000 },
{ MT_TXOP_CTRL_CFG, 0x0400583f },
- { MT_TX_RTS_CFG, 0x00100020 },
+ { MT_TX_RTS_CFG, 0x00ffff20 },
{ MT_TX_TIMEOUT_CFG, 0x000a2290 },
{ MT_TX_RETRY_CFG, 0x47f01f0f },
{ MT_EXP_ACK_TIME, 0x002c00dc },
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/mt76x2.h b/drivers/net/wireless/mediatek/mt76/mt76x2/mt76x2.h
index 6c619f1c65c9..d7abe3d73bad 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2/mt76x2.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2/mt76x2.h
@@ -71,6 +71,7 @@ int mt76x2_mcu_load_cr(struct mt76x02_dev *dev, u8 type, u8 temp_level,
void mt76x2_cleanup(struct mt76x02_dev *dev);
+int mt76x2_mac_reset(struct mt76x02_dev *dev, bool hard);
void mt76x2_reset_wlan(struct mt76x02_dev *dev, bool enable);
void mt76x2_init_txpower(struct mt76x02_dev *dev,
struct ieee80211_supported_band *sband);
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/pci_init.c b/drivers/net/wireless/mediatek/mt76/mt76x2/pci_init.c
index 984d9c4c2e1a..d3927a13e92e 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2/pci_init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2/pci_init.c
@@ -77,7 +77,7 @@ mt76x2_fixup_xtal(struct mt76x02_dev *dev)
}
}
-static int mt76x2_mac_reset(struct mt76x02_dev *dev, bool hard)
+int mt76x2_mac_reset(struct mt76x02_dev *dev, bool hard)
{
const u8 *macaddr = dev->mt76.macaddr;
u32 val;
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/pci_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76x2/pci_mcu.c
index 03e24ae7f66c..605dc66ae83b 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2/pci_mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2/pci_mcu.c
@@ -165,9 +165,30 @@ error:
return -ENOENT;
}
+static int
+mt76pci_mcu_restart(struct mt76_dev *mdev)
+{
+ struct mt76x02_dev *dev;
+ int ret;
+
+ dev = container_of(mdev, struct mt76x02_dev, mt76);
+
+ mt76x02_mcu_cleanup(dev);
+ mt76x2_mac_reset(dev, true);
+
+ ret = mt76pci_load_firmware(dev);
+ if (ret)
+ return ret;
+
+ mt76_wr(dev, MT_WPDMA_RST_IDX, ~0);
+
+ return 0;
+}
+
int mt76x2_mcu_init(struct mt76x02_dev *dev)
{
static const struct mt76_mcu_ops mt76x2_mcu_ops = {
+ .mcu_restart = mt76pci_mcu_restart,
.mcu_send_msg = mt76x02_mcu_msg_send,
};
int ret;
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/phy.c b/drivers/net/wireless/mediatek/mt76/mt76x2/phy.c
index 1848e8ab2e21..769a9b972044 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2/phy.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2/phy.c
@@ -260,10 +260,15 @@ mt76x2_phy_set_gain_val(struct mt76x02_dev *dev)
gain_val[0] = dev->cal.agc_gain_cur[0] - dev->cal.agc_gain_adjust;
gain_val[1] = dev->cal.agc_gain_cur[1] - dev->cal.agc_gain_adjust;
- if (dev->mt76.chandef.width >= NL80211_CHAN_WIDTH_40)
+ val = 0x1836 << 16;
+ if (!mt76x2_has_ext_lna(dev) &&
+ dev->mt76.chandef.width >= NL80211_CHAN_WIDTH_40)
val = 0x1e42 << 16;
- else
- val = 0x1836 << 16;
+
+ if (mt76x2_has_ext_lna(dev) &&
+ dev->mt76.chandef.chan->band == NL80211_BAND_2GHZ &&
+ dev->mt76.chandef.width < NL80211_CHAN_WIDTH_40)
+ val = 0x0f36 << 16;
val |= 0xf8;
@@ -280,6 +285,7 @@ void mt76x2_phy_update_channel_gain(struct mt76x02_dev *dev)
{
u8 *gain = dev->cal.agc_gain_init;
u8 low_gain_delta, gain_delta;
+ u32 agc_35, agc_37;
bool gain_change;
int low_gain;
u32 val;
@@ -318,6 +324,16 @@ void mt76x2_phy_update_channel_gain(struct mt76x02_dev *dev)
else
low_gain_delta = 14;
+ agc_37 = 0x2121262c;
+ if (dev->mt76.chandef.chan->band == NL80211_BAND_2GHZ)
+ agc_35 = 0x11111516;
+ else if (low_gain == 2)
+ agc_35 = agc_37 = 0x08080808;
+ else if (dev->mt76.chandef.width == NL80211_CHAN_WIDTH_80)
+ agc_35 = 0x10101014;
+ else
+ agc_35 = 0x11111116;
+
if (low_gain == 2) {
mt76_wr(dev, MT_BBP(RXO, 18), 0xf000a990);
mt76_wr(dev, MT_BBP(AGC, 35), 0x08080808);
@@ -326,15 +342,13 @@ void mt76x2_phy_update_channel_gain(struct mt76x02_dev *dev)
dev->cal.agc_gain_adjust = 0;
} else {
mt76_wr(dev, MT_BBP(RXO, 18), 0xf000a991);
- if (dev->mt76.chandef.width == NL80211_CHAN_WIDTH_80)
- mt76_wr(dev, MT_BBP(AGC, 35), 0x10101014);
- else
- mt76_wr(dev, MT_BBP(AGC, 35), 0x11111116);
- mt76_wr(dev, MT_BBP(AGC, 37), 0x2121262C);
gain_delta = 0;
dev->cal.agc_gain_adjust = low_gain_delta;
}
+ mt76_wr(dev, MT_BBP(AGC, 35), agc_35);
+ mt76_wr(dev, MT_BBP(AGC, 37), agc_37);
+
dev->cal.agc_gain_cur[0] = gain[0] - gain_delta;
dev->cal.agc_gain_cur[1] = gain[1] - gain_delta;
mt76x2_phy_set_gain_val(dev);
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c b/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c
index ddb6b2c48e01..ac0f13d46299 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c
@@ -21,11 +21,10 @@
#include "mt76x2u.h"
static const struct usb_device_id mt76x2u_device_table[] = {
- { USB_DEVICE(0x0e8d, 0x7612) }, /* Alfa AWUS036ACM */
{ USB_DEVICE(0x0b05, 0x1833) }, /* Asus USB-AC54 */
{ USB_DEVICE(0x0b05, 0x17eb) }, /* Asus USB-AC55 */
{ USB_DEVICE(0x0b05, 0x180b) }, /* Asus USB-N53 B1 */
- { USB_DEVICE(0x0e8d, 0x7612) }, /* Aukey USB-AC1200 */
+ { USB_DEVICE(0x0e8d, 0x7612) }, /* Aukey USBAC1200 - Alfa AWUS036ACM */
{ USB_DEVICE(0x057c, 0x8503) }, /* Avm FRITZ!WLAN AC860 */
{ USB_DEVICE(0x7392, 0xb711) }, /* Edimax EW 7722 UAC */
{ USB_DEVICE(0x0846, 0x9053) }, /* Netgear A6210 */
@@ -66,6 +65,10 @@ static int mt76x2u_probe(struct usb_interface *intf,
mdev->rev = mt76_rr(dev, MT_ASIC_VERSION);
dev_info(mdev->dev, "ASIC revision: %08x\n", mdev->rev);
+ if (!is_mt76x2(dev)) {
+ err = -ENODEV;
+ goto err;
+ }
err = mt76x2u_register_device(dev);
if (err < 0)
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/usb_mac.c b/drivers/net/wireless/mediatek/mt76/mt76x2/usb_mac.c
index 5e84b4535cb1..3b82345756ea 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2/usb_mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2/usb_mac.c
@@ -93,7 +93,6 @@ int mt76x2u_mac_reset(struct mt76x02_dev *dev)
mt76_wr(dev, MT_TX_LINK_CFG, 0x1020);
mt76_wr(dev, MT_AUTO_RSP_CFG, 0x13);
mt76_wr(dev, MT_MAX_LEN_CFG, 0x2f00);
- mt76_wr(dev, MT_TX_RTS_CFG, 0x92b20);
mt76_wr(dev, MT_WMM_AIFSN, 0x2273);
mt76_wr(dev, MT_WMM_CWMIN, 0x2344);
diff --git a/drivers/net/wireless/mediatek/mt76/tx.c b/drivers/net/wireless/mediatek/mt76/tx.c
index 5a349fe3e576..2585df512335 100644
--- a/drivers/net/wireless/mediatek/mt76/tx.c
+++ b/drivers/net/wireless/mediatek/mt76/tx.c
@@ -289,8 +289,11 @@ mt76_tx(struct mt76_dev *dev, struct ieee80211_sta *sta,
dev->queue_ops->tx_queue_skb(dev, q, skb, wcid, sta);
dev->queue_ops->kick(dev, q);
- if (q->queued > q->ndesc - 8)
+ if (q->queued > q->ndesc - 8 && !q->stopped) {
ieee80211_stop_queue(dev->hw, skb_get_queue_mapping(skb));
+ q->stopped = true;
+ }
+
spin_unlock_bh(&q->lock);
}
EXPORT_SYMBOL_GPL(mt76_tx);
@@ -374,7 +377,10 @@ mt76_release_buffered_frames(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
if (last_skb) {
mt76_queue_ps_skb(dev, sta, last_skb, true);
dev->queue_ops->kick(dev, hwq);
+ } else {
+ ieee80211_sta_eosp(sta);
}
+
spin_unlock_bh(&hwq->lock);
}
EXPORT_SYMBOL_GPL(mt76_release_buffered_frames);
@@ -577,6 +583,9 @@ void mt76_wake_tx_queue(struct ieee80211_hw *hw, struct ieee80211_txq *txq)
struct mt76_txq *mtxq = (struct mt76_txq *) txq->drv_priv;
struct mt76_queue *hwq = mtxq->hwq;
+ if (!test_bit(MT76_STATE_RUNNING, &dev->state))
+ return;
+
spin_lock_bh(&hwq->lock);
if (list_empty(&mtxq->list))
list_add_tail(&mtxq->list, &hwq->swq);
diff --git a/drivers/net/wireless/mediatek/mt76/usb.c b/drivers/net/wireless/mediatek/mt76/usb.c
index ae6ada370597..4c1abd492405 100644
--- a/drivers/net/wireless/mediatek/mt76/usb.c
+++ b/drivers/net/wireless/mediatek/mt76/usb.c
@@ -655,7 +655,11 @@ static void mt76u_tx_tasklet(unsigned long data)
spin_lock_bh(&q->lock);
}
mt76_txq_schedule(dev, q);
- wake = i < IEEE80211_NUM_ACS && q->queued < q->ndesc - 8;
+
+ wake = q->stopped && q->queued < q->ndesc - 8;
+ if (wake)
+ q->stopped = false;
+
if (!q->queued)
wake_up(&dev->tx_wait);
diff --git a/drivers/net/wireless/mediatek/mt7601u/usb.c b/drivers/net/wireless/mediatek/mt7601u/usb.c
index d8b7863f7926..6ae7f14dc9bf 100644
--- a/drivers/net/wireless/mediatek/mt7601u/usb.c
+++ b/drivers/net/wireless/mediatek/mt7601u/usb.c
@@ -303,6 +303,10 @@ static int mt7601u_probe(struct usb_interface *usb_intf,
mac_rev = mt7601u_rr(dev, MT_MAC_CSR0);
dev_info(dev->dev, "ASIC revision: %08x MAC revision: %08x\n",
asic_rev, mac_rev);
+ if ((asic_rev >> 16) != 0x7601) {
+ ret = -ENODEV;
+ goto err;
+ }
/* Note: vendor driver skips this check for MT7601U */
if (!(mt7601u_rr(dev, MT_EFUSE_CTRL) & MT_EFUSE_CTRL_SEL))
diff --git a/drivers/ntb/hw/intel/ntb_hw_gen1.c b/drivers/ntb/hw/intel/ntb_hw_gen1.c
index 2ad263f708da..bb57ec239029 100644
--- a/drivers/ntb/hw/intel/ntb_hw_gen1.c
+++ b/drivers/ntb/hw/intel/ntb_hw_gen1.c
@@ -180,7 +180,7 @@ int ndev_mw_to_bar(struct intel_ntb_dev *ndev, int idx)
return ndev->reg->mw_bar[idx];
}
-static inline int ndev_db_addr(struct intel_ntb_dev *ndev,
+void ndev_db_addr(struct intel_ntb_dev *ndev,
phys_addr_t *db_addr, resource_size_t *db_size,
phys_addr_t reg_addr, unsigned long reg)
{
@@ -196,8 +196,6 @@ static inline int ndev_db_addr(struct intel_ntb_dev *ndev,
*db_size = ndev->reg->db_size;
dev_dbg(&ndev->ntb.pdev->dev, "Peer db size %llx\n", *db_size);
}
-
- return 0;
}
u64 ndev_db_read(struct intel_ntb_dev *ndev,
@@ -1111,13 +1109,28 @@ int intel_ntb_db_clear_mask(struct ntb_dev *ntb, u64 db_bits)
ndev->self_reg->db_mask);
}
-int intel_ntb_peer_db_addr(struct ntb_dev *ntb, phys_addr_t *db_addr,
- resource_size_t *db_size)
+static int intel_ntb_peer_db_addr(struct ntb_dev *ntb, phys_addr_t *db_addr,
+ resource_size_t *db_size, u64 *db_data, int db_bit)
{
+ u64 db_bits;
struct intel_ntb_dev *ndev = ntb_ndev(ntb);
- return ndev_db_addr(ndev, db_addr, db_size, ndev->peer_addr,
+ if (unlikely(db_bit >= BITS_PER_LONG_LONG))
+ return -EINVAL;
+
+ db_bits = BIT_ULL(db_bit);
+
+ if (unlikely(db_bits & ~ntb_ndev(ntb)->db_valid_mask))
+ return -EINVAL;
+
+ ndev_db_addr(ndev, db_addr, db_size, ndev->peer_addr,
ndev->peer_reg->db_bell);
+
+ if (db_data)
+ *db_data = db_bits;
+
+
+ return 0;
}
static int intel_ntb_peer_db_set(struct ntb_dev *ntb, u64 db_bits)
diff --git a/drivers/ntb/hw/intel/ntb_hw_gen1.h b/drivers/ntb/hw/intel/ntb_hw_gen1.h
index ad8ec1444436..544cf5c06f4d 100644
--- a/drivers/ntb/hw/intel/ntb_hw_gen1.h
+++ b/drivers/ntb/hw/intel/ntb_hw_gen1.h
@@ -147,6 +147,9 @@ extern struct intel_b2b_addr xeon_b2b_dsd_addr;
int ndev_init_isr(struct intel_ntb_dev *ndev, int msix_min, int msix_max,
int msix_shift, int total_shift);
enum ntb_topo xeon_ppd_topo(struct intel_ntb_dev *ndev, u8 ppd);
+void ndev_db_addr(struct intel_ntb_dev *ndev,
+ phys_addr_t *db_addr, resource_size_t *db_size,
+ phys_addr_t reg_addr, unsigned long reg);
u64 ndev_db_read(struct intel_ntb_dev *ndev, void __iomem *mmio);
int ndev_db_write(struct intel_ntb_dev *ndev, u64 db_bits,
void __iomem *mmio);
@@ -166,8 +169,6 @@ int intel_ntb_db_vector_count(struct ntb_dev *ntb);
u64 intel_ntb_db_vector_mask(struct ntb_dev *ntb, int db_vector);
int intel_ntb_db_set_mask(struct ntb_dev *ntb, u64 db_bits);
int intel_ntb_db_clear_mask(struct ntb_dev *ntb, u64 db_bits);
-int intel_ntb_peer_db_addr(struct ntb_dev *ntb, phys_addr_t *db_addr,
- resource_size_t *db_size);
int intel_ntb_spad_is_unsafe(struct ntb_dev *ntb);
int intel_ntb_spad_count(struct ntb_dev *ntb);
u32 intel_ntb_spad_read(struct ntb_dev *ntb, int idx);
diff --git a/drivers/ntb/hw/intel/ntb_hw_gen3.c b/drivers/ntb/hw/intel/ntb_hw_gen3.c
index b3fa24778f94..f475b56a3f49 100644
--- a/drivers/ntb/hw/intel/ntb_hw_gen3.c
+++ b/drivers/ntb/hw/intel/ntb_hw_gen3.c
@@ -532,6 +532,37 @@ static int intel_ntb3_mw_set_trans(struct ntb_dev *ntb, int pidx, int idx,
return 0;
}
+int intel_ntb3_peer_db_addr(struct ntb_dev *ntb, phys_addr_t *db_addr,
+ resource_size_t *db_size,
+ u64 *db_data, int db_bit)
+{
+ phys_addr_t db_addr_base;
+ struct intel_ntb_dev *ndev = ntb_ndev(ntb);
+
+ if (unlikely(db_bit >= BITS_PER_LONG_LONG))
+ return -EINVAL;
+
+ if (unlikely(BIT_ULL(db_bit) & ~ntb_ndev(ntb)->db_valid_mask))
+ return -EINVAL;
+
+ ndev_db_addr(ndev, &db_addr_base, db_size, ndev->peer_addr,
+ ndev->peer_reg->db_bell);
+
+ if (db_addr) {
+ *db_addr = db_addr_base + (db_bit * 4);
+ dev_dbg(&ndev->ntb.pdev->dev, "Peer db addr %llx db bit %d\n",
+ *db_addr, db_bit);
+ }
+
+ if (db_data) {
+ *db_data = 1;
+ dev_dbg(&ndev->ntb.pdev->dev, "Peer db data %llx db bit %d\n",
+ *db_data, db_bit);
+ }
+
+ return 0;
+}
+
static int intel_ntb3_peer_db_set(struct ntb_dev *ntb, u64 db_bits)
{
struct intel_ntb_dev *ndev = ntb_ndev(ntb);
@@ -584,7 +615,7 @@ const struct ntb_dev_ops intel_ntb3_ops = {
.db_clear = intel_ntb3_db_clear,
.db_set_mask = intel_ntb_db_set_mask,
.db_clear_mask = intel_ntb_db_clear_mask,
- .peer_db_addr = intel_ntb_peer_db_addr,
+ .peer_db_addr = intel_ntb3_peer_db_addr,
.peer_db_set = intel_ntb3_peer_db_set,
.spad_is_unsafe = intel_ntb_spad_is_unsafe,
.spad_count = intel_ntb_spad_count,
diff --git a/drivers/ntb/hw/mscc/ntb_hw_switchtec.c b/drivers/ntb/hw/mscc/ntb_hw_switchtec.c
index f2df2d39c65b..d905d368d28c 100644
--- a/drivers/ntb/hw/mscc/ntb_hw_switchtec.c
+++ b/drivers/ntb/hw/mscc/ntb_hw_switchtec.c
@@ -236,6 +236,7 @@ static void switchtec_ntb_mw_clr_direct(struct switchtec_ntb *sndev, int idx)
ctl_val &= ~NTB_CTRL_BAR_DIR_WIN_EN;
iowrite32(ctl_val, &ctl->bar_entry[bar].ctl);
iowrite32(0, &ctl->bar_entry[bar].win_size);
+ iowrite32(0, &ctl->bar_ext_entry[bar].win_size);
iowrite64(sndev->self_partition, &ctl->bar_entry[bar].xlate_addr);
}
@@ -258,7 +259,9 @@ static void switchtec_ntb_mw_set_direct(struct switchtec_ntb *sndev, int idx,
ctl_val |= NTB_CTRL_BAR_DIR_WIN_EN;
iowrite32(ctl_val, &ctl->bar_entry[bar].ctl);
- iowrite32(xlate_pos | size, &ctl->bar_entry[bar].win_size);
+ iowrite32(xlate_pos | (lower_32_bits(size) & 0xFFFFF000),
+ &ctl->bar_entry[bar].win_size);
+ iowrite32(upper_32_bits(size), &ctl->bar_ext_entry[bar].win_size);
iowrite64(sndev->self_partition | addr,
&ctl->bar_entry[bar].xlate_addr);
}
@@ -679,11 +682,16 @@ static u64 switchtec_ntb_db_read_mask(struct ntb_dev *ntb)
static int switchtec_ntb_peer_db_addr(struct ntb_dev *ntb,
phys_addr_t *db_addr,
- resource_size_t *db_size)
+ resource_size_t *db_size,
+ u64 *db_data,
+ int db_bit)
{
struct switchtec_ntb *sndev = ntb_sndev(ntb);
unsigned long offset;
+ if (unlikely(db_bit >= BITS_PER_LONG_LONG))
+ return -EINVAL;
+
offset = (unsigned long)sndev->mmio_peer_dbmsg->odb -
(unsigned long)sndev->stdev->mmio;
@@ -693,6 +701,8 @@ static int switchtec_ntb_peer_db_addr(struct ntb_dev *ntb,
*db_addr = pci_resource_start(ntb->pdev, 0) + offset;
if (db_size)
*db_size = sizeof(u32);
+ if (db_data)
+ *db_data = BIT_ULL(db_bit) << sndev->db_peer_shift;
return 0;
}
@@ -1025,7 +1035,9 @@ static int crosslink_setup_mws(struct switchtec_ntb *sndev, int ntb_lut_idx,
ctl_val |= NTB_CTRL_BAR_DIR_WIN_EN;
iowrite32(ctl_val, &ctl->bar_entry[bar].ctl);
- iowrite32(xlate_pos | size, &ctl->bar_entry[bar].win_size);
+ iowrite32(xlate_pos | (lower_32_bits(size) & 0xFFFFF000),
+ &ctl->bar_entry[bar].win_size);
+ iowrite32(upper_32_bits(size), &ctl->bar_ext_entry[bar].win_size);
iowrite64(sndev->peer_partition | addr,
&ctl->bar_entry[bar].xlate_addr);
}
@@ -1092,7 +1104,7 @@ static int crosslink_enum_partition(struct switchtec_ntb *sndev,
dev_dbg(&sndev->stdev->dev,
"Crosslink BAR%d addr: %llx\n",
- i, bar_addr);
+ i*2, bar_addr);
if (bar_addr != bar_space * i)
continue;
diff --git a/drivers/ntb/ntb_transport.c b/drivers/ntb/ntb_transport.c
index 3bfdb4562408..d4f39ba1d976 100644
--- a/drivers/ntb/ntb_transport.c
+++ b/drivers/ntb/ntb_transport.c
@@ -144,7 +144,9 @@ struct ntb_transport_qp {
struct list_head tx_free_q;
spinlock_t ntb_tx_free_q_lock;
void __iomem *tx_mw;
- dma_addr_t tx_mw_phys;
+ phys_addr_t tx_mw_phys;
+ size_t tx_mw_size;
+ dma_addr_t tx_mw_dma_addr;
unsigned int tx_index;
unsigned int tx_max_entry;
unsigned int tx_max_frame;
@@ -862,6 +864,9 @@ static void ntb_transport_link_cleanup(struct ntb_transport_ctx *nt)
if (!nt->link_is_up)
cancel_delayed_work_sync(&nt->link_work);
+ for (i = 0; i < nt->mw_count; i++)
+ ntb_free_mw(nt, i);
+
/* The scratchpad registers keep the values if the remote side
* goes down, blast them now to give them a sane value the next
* time they are accessed
@@ -1049,6 +1054,7 @@ static int ntb_transport_init_queue(struct ntb_transport_ctx *nt,
tx_size = (unsigned int)mw_size / num_qps_mw;
qp_offset = tx_size * (qp_num / mw_count);
+ qp->tx_mw_size = tx_size;
qp->tx_mw = nt->mw_vec[mw_num].vbase + qp_offset;
if (!qp->tx_mw)
return -EINVAL;
@@ -1644,7 +1650,7 @@ static int ntb_async_tx_submit(struct ntb_transport_qp *qp,
dma_cookie_t cookie;
device = chan->device;
- dest = qp->tx_mw_phys + qp->tx_max_frame * entry->tx_index;
+ dest = qp->tx_mw_dma_addr + qp->tx_max_frame * entry->tx_index;
buff_off = (size_t)buf & ~PAGE_MASK;
dest_off = (size_t)dest & ~PAGE_MASK;
@@ -1863,6 +1869,18 @@ ntb_transport_create_queue(void *data, struct device *client_dev,
qp->rx_dma_chan = NULL;
}
+ if (qp->tx_dma_chan) {
+ qp->tx_mw_dma_addr =
+ dma_map_resource(qp->tx_dma_chan->device->dev,
+ qp->tx_mw_phys, qp->tx_mw_size,
+ DMA_FROM_DEVICE, 0);
+ if (dma_mapping_error(qp->tx_dma_chan->device->dev,
+ qp->tx_mw_dma_addr)) {
+ qp->tx_mw_dma_addr = 0;
+ goto err1;
+ }
+ }
+
dev_dbg(&pdev->dev, "Using %s memcpy for TX\n",
qp->tx_dma_chan ? "DMA" : "CPU");
@@ -1904,6 +1922,10 @@ err1:
qp->rx_alloc_entry = 0;
while ((entry = ntb_list_rm(&qp->ntb_rx_q_lock, &qp->rx_free_q)))
kfree(entry);
+ if (qp->tx_mw_dma_addr)
+ dma_unmap_resource(qp->tx_dma_chan->device->dev,
+ qp->tx_mw_dma_addr, qp->tx_mw_size,
+ DMA_FROM_DEVICE, 0);
if (qp->tx_dma_chan)
dma_release_channel(qp->tx_dma_chan);
if (qp->rx_dma_chan)
@@ -1945,6 +1967,11 @@ void ntb_transport_free_queue(struct ntb_transport_qp *qp)
*/
dma_sync_wait(chan, qp->last_cookie);
dmaengine_terminate_all(chan);
+
+ dma_unmap_resource(chan->device->dev,
+ qp->tx_mw_dma_addr, qp->tx_mw_size,
+ DMA_FROM_DEVICE, 0);
+
dma_release_channel(chan);
}
diff --git a/drivers/nvdimm/btt.c b/drivers/nvdimm/btt.c
index b123b0dcf274..4671776f5623 100644
--- a/drivers/nvdimm/btt.c
+++ b/drivers/nvdimm/btt.c
@@ -541,9 +541,9 @@ static int arena_clear_freelist_error(struct arena_info *arena, u32 lane)
static int btt_freelist_init(struct arena_info *arena)
{
- int old, new, ret;
- u32 i, map_entry;
- struct log_entry log_new, log_old;
+ int new, ret;
+ struct log_entry log_new;
+ u32 i, map_entry, log_oldmap, log_newmap;
arena->freelist = kcalloc(arena->nfree, sizeof(struct free_entry),
GFP_KERNEL);
@@ -551,24 +551,26 @@ static int btt_freelist_init(struct arena_info *arena)
return -ENOMEM;
for (i = 0; i < arena->nfree; i++) {
- old = btt_log_read(arena, i, &log_old, LOG_OLD_ENT);
- if (old < 0)
- return old;
-
new = btt_log_read(arena, i, &log_new, LOG_NEW_ENT);
if (new < 0)
return new;
+ /* old and new map entries with any flags stripped out */
+ log_oldmap = ent_lba(le32_to_cpu(log_new.old_map));
+ log_newmap = ent_lba(le32_to_cpu(log_new.new_map));
+
/* sub points to the next one to be overwritten */
arena->freelist[i].sub = 1 - new;
arena->freelist[i].seq = nd_inc_seq(le32_to_cpu(log_new.seq));
- arena->freelist[i].block = le32_to_cpu(log_new.old_map);
+ arena->freelist[i].block = log_oldmap;
/*
* FIXME: if error clearing fails during init, we want to make
* the BTT read-only
*/
- if (ent_e_flag(log_new.old_map)) {
+ if (ent_e_flag(log_new.old_map) &&
+ !ent_normal(log_new.old_map)) {
+ arena->freelist[i].has_err = 1;
ret = arena_clear_freelist_error(arena, i);
if (ret)
dev_err_ratelimited(to_dev(arena),
@@ -576,7 +578,7 @@ static int btt_freelist_init(struct arena_info *arena)
}
/* This implies a newly created or untouched flog entry */
- if (log_new.old_map == log_new.new_map)
+ if (log_oldmap == log_newmap)
continue;
/* Check if map recovery is needed */
@@ -584,8 +586,15 @@ static int btt_freelist_init(struct arena_info *arena)
NULL, NULL, 0);
if (ret)
return ret;
- if ((le32_to_cpu(log_new.new_map) != map_entry) &&
- (le32_to_cpu(log_new.old_map) == map_entry)) {
+
+ /*
+ * The map_entry from btt_read_map is stripped of any flag bits,
+ * so use the stripped out versions from the log as well for
+ * testing whether recovery is needed. For restoration, use the
+ * 'raw' version of the log entries as that captured what we
+ * were going to write originally.
+ */
+ if ((log_newmap != map_entry) && (log_oldmap == map_entry)) {
/*
* Last transaction wrote the flog, but wasn't able
* to complete the map write. So fix up the map.
diff --git a/drivers/nvdimm/btt.h b/drivers/nvdimm/btt.h
index db3cb6d4d0d4..ddff49c707b0 100644
--- a/drivers/nvdimm/btt.h
+++ b/drivers/nvdimm/btt.h
@@ -44,6 +44,8 @@
#define ent_e_flag(ent) (!!(ent & MAP_ERR_MASK))
#define ent_z_flag(ent) (!!(ent & MAP_TRIM_MASK))
#define set_e_flag(ent) (ent |= MAP_ERR_MASK)
+/* 'normal' is both e and z flags set */
+#define ent_normal(ent) (ent_e_flag(ent) && ent_z_flag(ent))
enum btt_init_state {
INIT_UNCHECKED = 0,
diff --git a/drivers/nvdimm/btt_devs.c b/drivers/nvdimm/btt_devs.c
index 795ad4ff35ca..b72a303176c7 100644
--- a/drivers/nvdimm/btt_devs.c
+++ b/drivers/nvdimm/btt_devs.c
@@ -159,11 +159,19 @@ static ssize_t size_show(struct device *dev,
}
static DEVICE_ATTR_RO(size);
+static ssize_t log_zero_flags_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "Y\n");
+}
+static DEVICE_ATTR_RO(log_zero_flags);
+
static struct attribute *nd_btt_attributes[] = {
&dev_attr_sector_size.attr,
&dev_attr_namespace.attr,
&dev_attr_uuid.attr,
&dev_attr_size.attr,
+ &dev_attr_log_zero_flags.attr,
NULL,
};
diff --git a/drivers/nvdimm/dimm_devs.c b/drivers/nvdimm/dimm_devs.c
index efe412a6b5b9..91b9abbf689c 100644
--- a/drivers/nvdimm/dimm_devs.c
+++ b/drivers/nvdimm/dimm_devs.c
@@ -11,6 +11,7 @@
* General Public License for more details.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/moduleparam.h>
#include <linux/vmalloc.h>
#include <linux/device.h>
#include <linux/ndctl.h>
@@ -25,6 +26,10 @@
static DEFINE_IDA(dimm_ida);
+static bool noblk;
+module_param(noblk, bool, 0444);
+MODULE_PARM_DESC(noblk, "force disable BLK / local alias support");
+
/*
* Retrieve bus and dimm handle and return if this bus supports
* get_config_data commands
@@ -551,6 +556,8 @@ struct nvdimm *__nvdimm_create(struct nvdimm_bus *nvdimm_bus,
nvdimm->dimm_id = dimm_id;
nvdimm->provider_data = provider_data;
+ if (noblk)
+ flags |= 1 << NDD_NOBLK;
nvdimm->flags = flags;
nvdimm->cmd_mask = cmd_mask;
nvdimm->num_flush = num_flush;
diff --git a/drivers/nvdimm/e820.c b/drivers/nvdimm/e820.c
index 521eaf53a52a..36be9b619187 100644
--- a/drivers/nvdimm/e820.c
+++ b/drivers/nvdimm/e820.c
@@ -47,6 +47,7 @@ static int e820_register_one(struct resource *res, void *data)
ndr_desc.res = res;
ndr_desc.attr_groups = e820_pmem_region_attribute_groups;
ndr_desc.numa_node = e820_range_to_nid(res->start);
+ ndr_desc.target_node = ndr_desc.numa_node;
set_bit(ND_REGION_PAGEMAP, &ndr_desc.flags);
if (!nvdimm_pmem_region_create(nvdimm_bus, &ndr_desc))
return -ENXIO;
diff --git a/drivers/nvdimm/label.c b/drivers/nvdimm/label.c
index a11bf4e6b451..f3d753d3169c 100644
--- a/drivers/nvdimm/label.c
+++ b/drivers/nvdimm/label.c
@@ -392,6 +392,7 @@ int nd_label_reserve_dpa(struct nvdimm_drvdata *ndd)
return 0; /* no label, nothing to reserve */
for_each_clear_bit_le(slot, free, nslot) {
+ struct nvdimm *nvdimm = to_nvdimm(ndd->dev);
struct nd_namespace_label *nd_label;
struct nd_region *nd_region = NULL;
u8 label_uuid[NSLABEL_UUID_LEN];
@@ -406,6 +407,8 @@ int nd_label_reserve_dpa(struct nvdimm_drvdata *ndd)
memcpy(label_uuid, nd_label->uuid, NSLABEL_UUID_LEN);
flags = __le32_to_cpu(nd_label->flags);
+ if (test_bit(NDD_NOBLK, &nvdimm->flags))
+ flags &= ~NSLABEL_FLAG_LOCAL;
nd_label_gen_id(&label_id, label_uuid, flags);
res = nvdimm_allocate_dpa(ndd, &label_id,
__le64_to_cpu(nd_label->dpa),
@@ -755,7 +758,7 @@ static const guid_t *to_abstraction_guid(enum nvdimm_claim_class claim_class,
static int __pmem_label_update(struct nd_region *nd_region,
struct nd_mapping *nd_mapping, struct nd_namespace_pmem *nspm,
- int pos)
+ int pos, unsigned long flags)
{
struct nd_namespace_common *ndns = &nspm->nsio.common;
struct nd_interleave_set *nd_set = nd_region->nd_set;
@@ -796,7 +799,7 @@ static int __pmem_label_update(struct nd_region *nd_region,
memcpy(nd_label->uuid, nspm->uuid, NSLABEL_UUID_LEN);
if (nspm->alt_name)
memcpy(nd_label->name, nspm->alt_name, NSLABEL_NAME_LEN);
- nd_label->flags = __cpu_to_le32(NSLABEL_FLAG_UPDATING);
+ nd_label->flags = __cpu_to_le32(flags);
nd_label->nlabel = __cpu_to_le16(nd_region->ndr_mappings);
nd_label->position = __cpu_to_le16(pos);
nd_label->isetcookie = __cpu_to_le64(cookie);
@@ -1249,13 +1252,13 @@ static int del_labels(struct nd_mapping *nd_mapping, u8 *uuid)
int nd_pmem_namespace_label_update(struct nd_region *nd_region,
struct nd_namespace_pmem *nspm, resource_size_t size)
{
- int i;
+ int i, rc;
for (i = 0; i < nd_region->ndr_mappings; i++) {
struct nd_mapping *nd_mapping = &nd_region->mapping[i];
struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
struct resource *res;
- int rc, count = 0;
+ int count = 0;
if (size == 0) {
rc = del_labels(nd_mapping, nspm->uuid);
@@ -1273,7 +1276,20 @@ int nd_pmem_namespace_label_update(struct nd_region *nd_region,
if (rc < 0)
return rc;
- rc = __pmem_label_update(nd_region, nd_mapping, nspm, i);
+ rc = __pmem_label_update(nd_region, nd_mapping, nspm, i,
+ NSLABEL_FLAG_UPDATING);
+ if (rc)
+ return rc;
+ }
+
+ if (size == 0)
+ return 0;
+
+ /* Clear the UPDATING flag per UEFI 2.7 expectations */
+ for (i = 0; i < nd_region->ndr_mappings; i++) {
+ struct nd_mapping *nd_mapping = &nd_region->mapping[i];
+
+ rc = __pmem_label_update(nd_region, nd_mapping, nspm, i, 0);
if (rc)
return rc;
}
diff --git a/drivers/nvdimm/namespace_devs.c b/drivers/nvdimm/namespace_devs.c
index 4b077555ac70..7849bf1812c4 100644
--- a/drivers/nvdimm/namespace_devs.c
+++ b/drivers/nvdimm/namespace_devs.c
@@ -138,6 +138,7 @@ bool nd_is_uuid_unique(struct device *dev, u8 *uuid)
bool pmem_should_map_pages(struct device *dev)
{
struct nd_region *nd_region = to_nd_region(dev->parent);
+ struct nd_namespace_common *ndns = to_ndns(dev);
struct nd_namespace_io *nsio;
if (!IS_ENABLED(CONFIG_ZONE_DEVICE))
@@ -149,6 +150,9 @@ bool pmem_should_map_pages(struct device *dev)
if (is_nd_pfn(dev) || is_nd_btt(dev))
return false;
+ if (ndns->force_raw)
+ return false;
+
nsio = to_nd_namespace_io(dev);
if (region_intersects(nsio->res.start, resource_size(&nsio->res),
IORESOURCE_SYSTEM_RAM,
@@ -1506,13 +1510,13 @@ static ssize_t __holder_class_store(struct device *dev, const char *buf)
if (dev->driver || ndns->claim)
return -EBUSY;
- if (strcmp(buf, "btt") == 0 || strcmp(buf, "btt\n") == 0)
+ if (sysfs_streq(buf, "btt"))
ndns->claim_class = btt_claim_class(dev);
- else if (strcmp(buf, "pfn") == 0 || strcmp(buf, "pfn\n") == 0)
+ else if (sysfs_streq(buf, "pfn"))
ndns->claim_class = NVDIMM_CCLASS_PFN;
- else if (strcmp(buf, "dax") == 0 || strcmp(buf, "dax\n") == 0)
+ else if (sysfs_streq(buf, "dax"))
ndns->claim_class = NVDIMM_CCLASS_DAX;
- else if (strcmp(buf, "") == 0 || strcmp(buf, "\n") == 0)
+ else if (sysfs_streq(buf, ""))
ndns->claim_class = NVDIMM_CCLASS_NONE;
else
return -EINVAL;
@@ -2492,6 +2496,12 @@ static int init_active_labels(struct nd_region *nd_region)
if (!label_ent)
break;
label = nd_label_active(ndd, j);
+ if (test_bit(NDD_NOBLK, &nvdimm->flags)) {
+ u32 flags = __le32_to_cpu(label->flags);
+
+ flags &= ~NSLABEL_FLAG_LOCAL;
+ label->flags = __cpu_to_le32(flags);
+ }
label_ent->label = label;
mutex_lock(&nd_mapping->lock);
diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h
index 379bf4305e61..a5ac3b240293 100644
--- a/drivers/nvdimm/nd.h
+++ b/drivers/nvdimm/nd.h
@@ -153,7 +153,7 @@ struct nd_region {
u16 ndr_mappings;
u64 ndr_size;
u64 ndr_start;
- int id, num_lanes, ro, numa_node;
+ int id, num_lanes, ro, numa_node, target_node;
void *provider_data;
struct kernfs_node *bb_state;
struct badblocks bb;
diff --git a/drivers/nvdimm/of_pmem.c b/drivers/nvdimm/of_pmem.c
index 0a701837dfc0..a0c8dcfa0bf9 100644
--- a/drivers/nvdimm/of_pmem.c
+++ b/drivers/nvdimm/of_pmem.c
@@ -68,6 +68,7 @@ static int of_pmem_region_probe(struct platform_device *pdev)
memset(&ndr_desc, 0, sizeof(ndr_desc));
ndr_desc.attr_groups = region_attr_groups;
ndr_desc.numa_node = dev_to_node(&pdev->dev);
+ ndr_desc.target_node = ndr_desc.numa_node;
ndr_desc.res = &pdev->resource[i];
ndr_desc.of_node = np;
set_bit(ND_REGION_PAGEMAP, &ndr_desc.flags);
@@ -108,7 +109,6 @@ static struct platform_driver of_pmem_region_driver = {
.remove = of_pmem_region_remove,
.driver = {
.name = "of_pmem",
- .owner = THIS_MODULE,
.of_match_table = of_pmem_region_match,
},
};
diff --git a/drivers/nvdimm/pfn_devs.c b/drivers/nvdimm/pfn_devs.c
index 6f22272e8d80..d271bd731af7 100644
--- a/drivers/nvdimm/pfn_devs.c
+++ b/drivers/nvdimm/pfn_devs.c
@@ -580,6 +580,11 @@ int nd_pfn_probe(struct device *dev, struct nd_namespace_common *ndns)
}
EXPORT_SYMBOL(nd_pfn_probe);
+static u32 info_block_reserve(void)
+{
+ return ALIGN(SZ_8K, PAGE_SIZE);
+}
+
/*
* We hotplug memory at section granularity, pad the reserved area from
* the previous section base to the namespace base address.
@@ -593,7 +598,7 @@ static unsigned long init_altmap_base(resource_size_t base)
static unsigned long init_altmap_reserve(resource_size_t base)
{
- unsigned long reserve = PHYS_PFN(SZ_8K);
+ unsigned long reserve = info_block_reserve() >> PAGE_SHIFT;
unsigned long base_pfn = PHYS_PFN(base);
reserve += base_pfn - PFN_SECTION_ALIGN_DOWN(base_pfn);
@@ -608,6 +613,7 @@ static int __nvdimm_setup_pfn(struct nd_pfn *nd_pfn, struct dev_pagemap *pgmap)
u64 offset = le64_to_cpu(pfn_sb->dataoff);
u32 start_pad = __le32_to_cpu(pfn_sb->start_pad);
u32 end_trunc = __le32_to_cpu(pfn_sb->end_trunc);
+ u32 reserve = info_block_reserve();
struct nd_namespace_common *ndns = nd_pfn->ndns;
struct nd_namespace_io *nsio = to_nd_namespace_io(&ndns->dev);
resource_size_t base = nsio->res.start + start_pad;
@@ -621,7 +627,7 @@ static int __nvdimm_setup_pfn(struct nd_pfn *nd_pfn, struct dev_pagemap *pgmap)
res->end -= end_trunc;
if (nd_pfn->mode == PFN_MODE_RAM) {
- if (offset < SZ_8K)
+ if (offset < reserve)
return -EINVAL;
nd_pfn->npfns = le64_to_cpu(pfn_sb->npfns);
pgmap->altmap_valid = false;
@@ -634,7 +640,7 @@ static int __nvdimm_setup_pfn(struct nd_pfn *nd_pfn, struct dev_pagemap *pgmap)
le64_to_cpu(nd_pfn->pfn_sb->npfns),
nd_pfn->npfns);
memcpy(altmap, &__altmap, sizeof(*altmap));
- altmap->free = PHYS_PFN(offset - SZ_8K);
+ altmap->free = PHYS_PFN(offset - reserve);
altmap->alloc = 0;
pgmap->altmap_valid = true;
} else
@@ -678,18 +684,17 @@ static void trim_pfn_device(struct nd_pfn *nd_pfn, u32 *start_pad, u32 *end_trun
if (region_intersects(start, size, IORESOURCE_SYSTEM_RAM,
IORES_DESC_NONE) == REGION_MIXED
|| !IS_ALIGNED(end, nd_pfn->align)
- || nd_region_conflict(nd_region, start, size + adjust))
+ || nd_region_conflict(nd_region, start, size))
*end_trunc = end - phys_pmem_align_down(nd_pfn, end);
}
static int nd_pfn_init(struct nd_pfn *nd_pfn)
{
- u32 dax_label_reserve = is_nd_dax(&nd_pfn->dev) ? SZ_128K : 0;
struct nd_namespace_common *ndns = nd_pfn->ndns;
struct nd_namespace_io *nsio = to_nd_namespace_io(&ndns->dev);
+ u32 start_pad, end_trunc, reserve = info_block_reserve();
resource_size_t start, size;
struct nd_region *nd_region;
- u32 start_pad, end_trunc;
struct nd_pfn_sb *pfn_sb;
unsigned long npfns;
phys_addr_t offset;
@@ -734,7 +739,7 @@ static int nd_pfn_init(struct nd_pfn *nd_pfn)
*/
start = nsio->res.start + start_pad;
size = resource_size(&nsio->res);
- npfns = PFN_SECTION_ALIGN_UP((size - start_pad - end_trunc - SZ_8K)
+ npfns = PFN_SECTION_ALIGN_UP((size - start_pad - end_trunc - reserve)
/ PAGE_SIZE);
if (nd_pfn->mode == PFN_MODE_PMEM) {
/*
@@ -742,11 +747,10 @@ static int nd_pfn_init(struct nd_pfn *nd_pfn)
* when populating the vmemmap. This *should* be equal to
* PMD_SIZE for most architectures.
*/
- offset = ALIGN(start + SZ_8K + 64 * npfns + dax_label_reserve,
+ offset = ALIGN(start + reserve + 64 * npfns,
max(nd_pfn->align, PMD_SIZE)) - start;
} else if (nd_pfn->mode == PFN_MODE_RAM)
- offset = ALIGN(start + SZ_8K + dax_label_reserve,
- nd_pfn->align) - start;
+ offset = ALIGN(start + reserve, nd_pfn->align) - start;
else
return -ENXIO;
diff --git a/drivers/nvdimm/region_devs.c b/drivers/nvdimm/region_devs.c
index e2818f94f292..b4ef7d9ff22e 100644
--- a/drivers/nvdimm/region_devs.c
+++ b/drivers/nvdimm/region_devs.c
@@ -1003,6 +1003,13 @@ static struct nd_region *nd_region_create(struct nvdimm_bus *nvdimm_bus,
if (test_bit(NDD_UNARMED, &nvdimm->flags))
ro = 1;
+
+ if (test_bit(NDD_NOBLK, &nvdimm->flags)
+ && dev_type == &nd_blk_device_type) {
+ dev_err(&nvdimm_bus->dev, "%s: %s mapping%d is not BLK capable\n",
+ caller, dev_name(&nvdimm->dev), i);
+ return NULL;
+ }
}
if (dev_type == &nd_blk_device_type) {
@@ -1065,6 +1072,7 @@ static struct nd_region *nd_region_create(struct nvdimm_bus *nvdimm_bus,
nd_region->flags = ndr_desc->flags;
nd_region->ro = ro;
nd_region->numa_node = ndr_desc->numa_node;
+ nd_region->target_node = ndr_desc->target_node;
ida_init(&nd_region->ns_ida);
ida_init(&nd_region->btt_ida);
ida_init(&nd_region->pfn_ida);
diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
index 6a9dd68c0f4f..470601980794 100644
--- a/drivers/nvme/host/core.c
+++ b/drivers/nvme/host/core.c
@@ -1,15 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* NVM Express device driver
* Copyright (c) 2011-2014, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
*/
#include <linux/blkdev.h>
@@ -151,11 +143,8 @@ int nvme_reset_ctrl_sync(struct nvme_ctrl *ctrl)
}
EXPORT_SYMBOL_GPL(nvme_reset_ctrl_sync);
-static void nvme_delete_ctrl_work(struct work_struct *work)
+static void nvme_do_delete_ctrl(struct nvme_ctrl *ctrl)
{
- struct nvme_ctrl *ctrl =
- container_of(work, struct nvme_ctrl, delete_work);
-
dev_info(ctrl->device,
"Removing ctrl: NQN \"%s\"\n", ctrl->opts->subsysnqn);
@@ -167,6 +156,14 @@ static void nvme_delete_ctrl_work(struct work_struct *work)
nvme_put_ctrl(ctrl);
}
+static void nvme_delete_ctrl_work(struct work_struct *work)
+{
+ struct nvme_ctrl *ctrl =
+ container_of(work, struct nvme_ctrl, delete_work);
+
+ nvme_do_delete_ctrl(ctrl);
+}
+
int nvme_delete_ctrl(struct nvme_ctrl *ctrl)
{
if (!nvme_change_ctrl_state(ctrl, NVME_CTRL_DELETING))
@@ -177,22 +174,22 @@ int nvme_delete_ctrl(struct nvme_ctrl *ctrl)
}
EXPORT_SYMBOL_GPL(nvme_delete_ctrl);
-int nvme_delete_ctrl_sync(struct nvme_ctrl *ctrl)
+static int nvme_delete_ctrl_sync(struct nvme_ctrl *ctrl)
{
int ret = 0;
/*
- * Keep a reference until the work is flushed since ->delete_ctrl
- * can free the controller.
+ * Keep a reference until nvme_do_delete_ctrl() complete,
+ * since ->delete_ctrl can free the controller.
*/
nvme_get_ctrl(ctrl);
- ret = nvme_delete_ctrl(ctrl);
+ if (!nvme_change_ctrl_state(ctrl, NVME_CTRL_DELETING))
+ ret = -EBUSY;
if (!ret)
- flush_work(&ctrl->delete_work);
+ nvme_do_delete_ctrl(ctrl);
nvme_put_ctrl(ctrl);
return ret;
}
-EXPORT_SYMBOL_GPL(nvme_delete_ctrl_sync);
static inline bool nvme_ns_has_pi(struct nvme_ns *ns)
{
@@ -611,6 +608,22 @@ static blk_status_t nvme_setup_discard(struct nvme_ns *ns, struct request *req,
return BLK_STS_OK;
}
+static inline blk_status_t nvme_setup_write_zeroes(struct nvme_ns *ns,
+ struct request *req, struct nvme_command *cmnd)
+{
+ if (ns->ctrl->quirks & NVME_QUIRK_DEALLOCATE_ZEROES)
+ return nvme_setup_discard(ns, req, cmnd);
+
+ cmnd->write_zeroes.opcode = nvme_cmd_write_zeroes;
+ cmnd->write_zeroes.nsid = cpu_to_le32(ns->head->ns_id);
+ cmnd->write_zeroes.slba =
+ cpu_to_le64(nvme_block_nr(ns, blk_rq_pos(req)));
+ cmnd->write_zeroes.length =
+ cpu_to_le16((blk_rq_bytes(req) >> ns->lba_shift) - 1);
+ cmnd->write_zeroes.control = 0;
+ return BLK_STS_OK;
+}
+
static inline blk_status_t nvme_setup_rw(struct nvme_ns *ns,
struct request *req, struct nvme_command *cmnd)
{
@@ -705,7 +718,8 @@ blk_status_t nvme_setup_cmd(struct nvme_ns *ns, struct request *req,
nvme_setup_flush(ns, cmd);
break;
case REQ_OP_WRITE_ZEROES:
- /* currently only aliased to deallocate for a few ctrls: */
+ ret = nvme_setup_write_zeroes(ns, req, cmd);
+ break;
case REQ_OP_DISCARD:
ret = nvme_setup_discard(ns, req, cmd);
break;
@@ -1236,7 +1250,7 @@ static u32 nvme_passthru_start(struct nvme_ctrl *ctrl, struct nvme_ns *ns,
if (ns) {
if (ctrl->effects)
effects = le32_to_cpu(ctrl->effects->iocs[opcode]);
- if (effects & ~NVME_CMD_EFFECTS_CSUPP)
+ if (effects & ~(NVME_CMD_EFFECTS_CSUPP | NVME_CMD_EFFECTS_LBCC))
dev_warn(ctrl->device,
"IO command:%02x has unhandled effects:%08x\n",
opcode, effects);
@@ -1481,10 +1495,10 @@ static void nvme_set_chunk_size(struct nvme_ns *ns)
blk_queue_chunk_sectors(ns->queue, rounddown_pow_of_two(chunk_size));
}
-static void nvme_config_discard(struct nvme_ns *ns)
+static void nvme_config_discard(struct gendisk *disk, struct nvme_ns *ns)
{
struct nvme_ctrl *ctrl = ns->ctrl;
- struct request_queue *queue = ns->queue;
+ struct request_queue *queue = disk->queue;
u32 size = queue_logical_block_size(queue);
if (!(ctrl->oncs & NVME_CTRL_ONCS_DSM)) {
@@ -1512,6 +1526,32 @@ static void nvme_config_discard(struct nvme_ns *ns)
blk_queue_max_write_zeroes_sectors(queue, UINT_MAX);
}
+static void nvme_config_write_zeroes(struct gendisk *disk, struct nvme_ns *ns)
+{
+ u32 max_sectors;
+ unsigned short bs = 1 << ns->lba_shift;
+
+ if (!(ns->ctrl->oncs & NVME_CTRL_ONCS_WRITE_ZEROES) ||
+ (ns->ctrl->quirks & NVME_QUIRK_DISABLE_WRITE_ZEROES))
+ return;
+ /*
+ * Even though NVMe spec explicitly states that MDTS is not
+ * applicable to the write-zeroes:- "The restriction does not apply to
+ * commands that do not transfer data between the host and the
+ * controller (e.g., Write Uncorrectable ro Write Zeroes command).".
+ * In order to be more cautious use controller's max_hw_sectors value
+ * to configure the maximum sectors for the write-zeroes which is
+ * configured based on the controller's MDTS field in the
+ * nvme_init_identify() if available.
+ */
+ if (ns->ctrl->max_hw_sectors == UINT_MAX)
+ max_sectors = ((u32)(USHRT_MAX + 1) * bs) >> 9;
+ else
+ max_sectors = ((u32)(ns->ctrl->max_hw_sectors + 1) * bs) >> 9;
+
+ blk_queue_max_write_zeroes_sectors(disk->queue, max_sectors);
+}
+
static void nvme_report_ns_ids(struct nvme_ctrl *ctrl, unsigned int nsid,
struct nvme_id_ns *id, struct nvme_ns_ids *ids)
{
@@ -1565,7 +1605,9 @@ static void nvme_update_disk_info(struct gendisk *disk,
capacity = 0;
set_capacity(disk, capacity);
- nvme_config_discard(ns);
+
+ nvme_config_discard(disk, ns);
+ nvme_config_write_zeroes(disk, ns);
if (id->nsattr & (1 << 0))
set_disk_ro(disk, true);
@@ -2280,6 +2322,9 @@ static struct attribute *nvme_subsys_attrs[] = {
&subsys_attr_serial.attr,
&subsys_attr_firmware_rev.attr,
&subsys_attr_subsysnqn.attr,
+#ifdef CONFIG_NVME_MULTIPATH
+ &subsys_attr_iopolicy.attr,
+#endif
NULL,
};
@@ -2332,6 +2377,9 @@ static int nvme_init_subsystem(struct nvme_ctrl *ctrl, struct nvme_id_ctrl *id)
memcpy(subsys->firmware_rev, id->fr, sizeof(subsys->firmware_rev));
subsys->vendor_id = le16_to_cpu(id->vid);
subsys->cmic = id->cmic;
+#ifdef CONFIG_NVME_MULTIPATH
+ subsys->iopolicy = NVME_IOPOLICY_NUMA;
+#endif
subsys->dev.class = nvme_subsys_class;
subsys->dev.release = nvme_release_subsystem;
@@ -3163,21 +3211,23 @@ static int nvme_setup_streams_ns(struct nvme_ctrl *ctrl, struct nvme_ns *ns)
return 0;
}
-static void nvme_alloc_ns(struct nvme_ctrl *ctrl, unsigned nsid)
+static int nvme_alloc_ns(struct nvme_ctrl *ctrl, unsigned nsid)
{
struct nvme_ns *ns;
struct gendisk *disk;
struct nvme_id_ns *id;
char disk_name[DISK_NAME_LEN];
- int node = ctrl->numa_node, flags = GENHD_FL_EXT_DEVT;
+ int node = ctrl->numa_node, flags = GENHD_FL_EXT_DEVT, ret;
ns = kzalloc_node(sizeof(*ns), GFP_KERNEL, node);
if (!ns)
- return;
+ return -ENOMEM;
ns->queue = blk_mq_init_queue(ctrl->tagset);
- if (IS_ERR(ns->queue))
+ if (IS_ERR(ns->queue)) {
+ ret = PTR_ERR(ns->queue);
goto out_free_ns;
+ }
blk_queue_flag_set(QUEUE_FLAG_NONROT, ns->queue);
if (ctrl->ops->flags & NVME_F_PCI_P2PDMA)
@@ -3193,20 +3243,27 @@ static void nvme_alloc_ns(struct nvme_ctrl *ctrl, unsigned nsid)
nvme_set_queue_limits(ctrl, ns->queue);
id = nvme_identify_ns(ctrl, nsid);
- if (!id)
+ if (!id) {
+ ret = -EIO;
goto out_free_queue;
+ }
- if (id->ncap == 0)
+ if (id->ncap == 0) {
+ ret = -EINVAL;
goto out_free_id;
+ }
- if (nvme_init_ns_head(ns, nsid, id))
+ ret = nvme_init_ns_head(ns, nsid, id);
+ if (ret)
goto out_free_id;
nvme_setup_streams_ns(ctrl, ns);
nvme_set_disk_name(disk_name, ns, ctrl, &flags);
disk = alloc_disk_node(0, node);
- if (!disk)
+ if (!disk) {
+ ret = -ENOMEM;
goto out_unlink_ns;
+ }
disk->fops = &nvme_fops;
disk->private_data = ns;
@@ -3218,7 +3275,8 @@ static void nvme_alloc_ns(struct nvme_ctrl *ctrl, unsigned nsid)
__nvme_revalidate_disk(disk, id);
if ((ctrl->quirks & NVME_QUIRK_LIGHTNVM) && id->vs[0] == 0x1) {
- if (nvme_nvm_register(ns, disk_name, node)) {
+ ret = nvme_nvm_register(ns, disk_name, node);
+ if (ret) {
dev_warn(ctrl->device, "LightNVM init failure\n");
goto out_put_disk;
}
@@ -3236,19 +3294,21 @@ static void nvme_alloc_ns(struct nvme_ctrl *ctrl, unsigned nsid)
nvme_fault_inject_init(ns);
kfree(id);
- return;
+ return 0;
out_put_disk:
put_disk(ns->disk);
out_unlink_ns:
mutex_lock(&ctrl->subsys->lock);
list_del_rcu(&ns->siblings);
mutex_unlock(&ctrl->subsys->lock);
+ nvme_put_ns_head(ns->head);
out_free_id:
kfree(id);
out_free_queue:
blk_cleanup_queue(ns->queue);
out_free_ns:
kfree(ns);
+ return ret;
}
static void nvme_ns_remove(struct nvme_ns *ns)
@@ -3596,8 +3656,6 @@ void nvme_stop_ctrl(struct nvme_ctrl *ctrl)
nvme_stop_keep_alive(ctrl);
flush_work(&ctrl->async_event_work);
cancel_work_sync(&ctrl->fw_act_work);
- if (ctrl->ops->stop_ctrl)
- ctrl->ops->stop_ctrl(ctrl);
}
EXPORT_SYMBOL_GPL(nvme_stop_ctrl);
diff --git a/drivers/nvme/host/fabrics.c b/drivers/nvme/host/fabrics.c
index 3eb908c50e1a..d4cb826f58ff 100644
--- a/drivers/nvme/host/fabrics.c
+++ b/drivers/nvme/host/fabrics.c
@@ -1,15 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* NVMe over Fabrics common host code.
* Copyright (c) 2015-2016 HGST, a Western Digital Company.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/init.h>
@@ -430,6 +422,7 @@ EXPORT_SYMBOL_GPL(nvmf_connect_admin_queue);
* @qid: NVMe I/O queue number for the new I/O connection between
* host and target (note qid == 0 is illegal as this is
* the Admin queue, per NVMe standard).
+ * @poll: Whether or not to poll for the completion of the connect cmd.
*
* This function issues a fabrics-protocol connection
* of a NVMe I/O queue (via NVMe Fabrics "Connect" command)
diff --git a/drivers/nvme/host/fabrics.h b/drivers/nvme/host/fabrics.h
index 478343b73e38..3044d8b99a24 100644
--- a/drivers/nvme/host/fabrics.h
+++ b/drivers/nvme/host/fabrics.h
@@ -1,15 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* NVMe over Fabrics common host code.
* Copyright (c) 2015-2016 HGST, a Western Digital Company.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
*/
#ifndef _NVME_FABRICS_H
#define _NVME_FABRICS_H 1
diff --git a/drivers/nvme/host/fault_inject.c b/drivers/nvme/host/fault_inject.c
index 02632266ac06..4cfd2c9222d4 100644
--- a/drivers/nvme/host/fault_inject.c
+++ b/drivers/nvme/host/fault_inject.c
@@ -1,8 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* fault injection support for nvme.
*
* Copyright (c) 2018, Oracle and/or its affiliates
- *
*/
#include <linux/moduleparam.h>
diff --git a/drivers/nvme/host/fc.c b/drivers/nvme/host/fc.c
index 89accc76d71c..f3b9d91ba0df 100644
--- a/drivers/nvme/host/fc.c
+++ b/drivers/nvme/host/fc.c
@@ -1,18 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2016 Avago Technologies. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful.
- * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
- * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
- * PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED, EXCEPT TO
- * THE EXTENT THAT SUCH DISCLAIMERS ARE HELD TO BE LEGALLY INVALID.
- * See the GNU General Public License for more details, a copy of which
- * can be found in the file COPYING included with this package
- *
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
@@ -2119,7 +2107,7 @@ nvme_fc_map_data(struct nvme_fc_ctrl *ctrl, struct request *rq,
freq->sg_cnt = 0;
- if (!blk_rq_payload_bytes(rq))
+ if (!blk_rq_nr_phys_segments(rq))
return 0;
freq->sg_table.sgl = freq->first_sgl;
@@ -2316,12 +2304,23 @@ nvme_fc_queue_rq(struct blk_mq_hw_ctx *hctx,
if (ret)
return ret;
- data_len = blk_rq_payload_bytes(rq);
- if (data_len)
+ /*
+ * nvme core doesn't quite treat the rq opaquely. Commands such
+ * as WRITE ZEROES will return a non-zero rq payload_bytes yet
+ * there is no actual payload to be transferred.
+ * To get it right, key data transmission on there being 1 or
+ * more physical segments in the sg list. If there is no
+ * physical segments, there is no payload.
+ */
+ if (blk_rq_nr_phys_segments(rq)) {
+ data_len = blk_rq_payload_bytes(rq);
io_dir = ((rq_data_dir(rq) == WRITE) ?
NVMEFC_FCP_WRITE : NVMEFC_FCP_READ);
- else
+ } else {
+ data_len = 0;
io_dir = NVMEFC_FCP_NODATA;
+ }
+
return nvme_fc_start_fcp_op(ctrl, queue, op, data_len, io_dir);
}
@@ -2476,6 +2475,7 @@ static int
nvme_fc_recreate_io_queues(struct nvme_fc_ctrl *ctrl)
{
struct nvmf_ctrl_options *opts = ctrl->ctrl.opts;
+ u32 prior_ioq_cnt = ctrl->ctrl.queue_count - 1;
unsigned int nr_io_queues;
int ret;
@@ -2488,6 +2488,13 @@ nvme_fc_recreate_io_queues(struct nvme_fc_ctrl *ctrl)
return ret;
}
+ if (!nr_io_queues && prior_ioq_cnt) {
+ dev_info(ctrl->ctrl.device,
+ "Fail Reconnect: At least 1 io queue "
+ "required (was %d)\n", prior_ioq_cnt);
+ return -ENOSPC;
+ }
+
ctrl->ctrl.queue_count = nr_io_queues + 1;
/* check for io queues existing */
if (ctrl->ctrl.queue_count == 1)
@@ -2501,6 +2508,10 @@ nvme_fc_recreate_io_queues(struct nvme_fc_ctrl *ctrl)
if (ret)
goto out_delete_hw_queues;
+ if (prior_ioq_cnt != nr_io_queues)
+ dev_info(ctrl->ctrl.device,
+ "reconnect: revising io queue count from %d to %d\n",
+ prior_ioq_cnt, nr_io_queues);
blk_mq_update_nr_hw_queues(&ctrl->tag_set, nr_io_queues);
return 0;
@@ -3018,7 +3029,10 @@ nvme_fc_init_ctrl(struct device *dev, struct nvmf_ctrl_options *opts,
ctrl->ctrl.opts = opts;
ctrl->ctrl.nr_reconnects = 0;
- ctrl->ctrl.numa_node = dev_to_node(lport->dev);
+ if (lport->dev)
+ ctrl->ctrl.numa_node = dev_to_node(lport->dev);
+ else
+ ctrl->ctrl.numa_node = NUMA_NO_NODE;
INIT_LIST_HEAD(&ctrl->ctrl_list);
ctrl->lport = lport;
ctrl->rport = rport;
diff --git a/drivers/nvme/host/lightnvm.c b/drivers/nvme/host/lightnvm.c
index b759c25c89c8..949e29e1d782 100644
--- a/drivers/nvme/host/lightnvm.c
+++ b/drivers/nvme/host/lightnvm.c
@@ -1,23 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* nvme-lightnvm.c - LightNVM NVMe device
*
* Copyright (C) 2014-2015 IT University of Copenhagen
* Initial release: Matias Bjorling <mb@lightnvm.io>
- *
- * 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.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139,
- * USA.
- *
*/
#include "nvme.h"
diff --git a/drivers/nvme/host/multipath.c b/drivers/nvme/host/multipath.c
index b9fff3b8ed1b..f0716f6ce41f 100644
--- a/drivers/nvme/host/multipath.c
+++ b/drivers/nvme/host/multipath.c
@@ -1,14 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2017-2018 Christoph Hellwig.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
*/
#include <linux/moduleparam.h>
@@ -141,7 +133,10 @@ static struct nvme_ns *__nvme_find_path(struct nvme_ns_head *head, int node)
test_bit(NVME_NS_ANA_PENDING, &ns->flags))
continue;
- distance = node_distance(node, ns->ctrl->numa_node);
+ if (READ_ONCE(head->subsys->iopolicy) == NVME_IOPOLICY_NUMA)
+ distance = node_distance(node, ns->ctrl->numa_node);
+ else
+ distance = LOCAL_DISTANCE;
switch (ns->ana_state) {
case NVME_ANA_OPTIMIZED:
@@ -168,6 +163,47 @@ static struct nvme_ns *__nvme_find_path(struct nvme_ns_head *head, int node)
return found;
}
+static struct nvme_ns *nvme_next_ns(struct nvme_ns_head *head,
+ struct nvme_ns *ns)
+{
+ ns = list_next_or_null_rcu(&head->list, &ns->siblings, struct nvme_ns,
+ siblings);
+ if (ns)
+ return ns;
+ return list_first_or_null_rcu(&head->list, struct nvme_ns, siblings);
+}
+
+static struct nvme_ns *nvme_round_robin_path(struct nvme_ns_head *head,
+ int node, struct nvme_ns *old)
+{
+ struct nvme_ns *ns, *found, *fallback = NULL;
+
+ if (list_is_singular(&head->list))
+ return old;
+
+ for (ns = nvme_next_ns(head, old);
+ ns != old;
+ ns = nvme_next_ns(head, ns)) {
+ if (ns->ctrl->state != NVME_CTRL_LIVE ||
+ test_bit(NVME_NS_ANA_PENDING, &ns->flags))
+ continue;
+
+ if (ns->ana_state == NVME_ANA_OPTIMIZED) {
+ found = ns;
+ goto out;
+ }
+ if (ns->ana_state == NVME_ANA_NONOPTIMIZED)
+ fallback = ns;
+ }
+
+ if (!fallback)
+ return NULL;
+ found = fallback;
+out:
+ rcu_assign_pointer(head->current_path[node], found);
+ return found;
+}
+
static inline bool nvme_path_is_optimized(struct nvme_ns *ns)
{
return ns->ctrl->state == NVME_CTRL_LIVE &&
@@ -180,6 +216,8 @@ inline struct nvme_ns *nvme_find_path(struct nvme_ns_head *head)
struct nvme_ns *ns;
ns = srcu_dereference(head->current_path[node], &head->srcu);
+ if (READ_ONCE(head->subsys->iopolicy) == NVME_IOPOLICY_RR && ns)
+ ns = nvme_round_robin_path(head, node, ns);
if (unlikely(!ns || !nvme_path_is_optimized(ns)))
ns = __nvme_find_path(head, node);
return ns;
@@ -366,15 +404,12 @@ static inline bool nvme_state_is_live(enum nvme_ana_state state)
static void nvme_update_ns_ana_state(struct nvme_ana_group_desc *desc,
struct nvme_ns *ns)
{
- enum nvme_ana_state old;
-
mutex_lock(&ns->head->lock);
- old = ns->ana_state;
ns->ana_grpid = le32_to_cpu(desc->grpid);
ns->ana_state = desc->state;
clear_bit(NVME_NS_ANA_PENDING, &ns->flags);
- if (nvme_state_is_live(ns->ana_state) && !nvme_state_is_live(old))
+ if (nvme_state_is_live(ns->ana_state))
nvme_mpath_set_live(ns);
mutex_unlock(&ns->head->lock);
}
@@ -471,6 +506,44 @@ void nvme_mpath_stop(struct nvme_ctrl *ctrl)
cancel_work_sync(&ctrl->ana_work);
}
+#define SUBSYS_ATTR_RW(_name, _mode, _show, _store) \
+ struct device_attribute subsys_attr_##_name = \
+ __ATTR(_name, _mode, _show, _store)
+
+static const char *nvme_iopolicy_names[] = {
+ [NVME_IOPOLICY_NUMA] = "numa",
+ [NVME_IOPOLICY_RR] = "round-robin",
+};
+
+static ssize_t nvme_subsys_iopolicy_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct nvme_subsystem *subsys =
+ container_of(dev, struct nvme_subsystem, dev);
+
+ return sprintf(buf, "%s\n",
+ nvme_iopolicy_names[READ_ONCE(subsys->iopolicy)]);
+}
+
+static ssize_t nvme_subsys_iopolicy_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct nvme_subsystem *subsys =
+ container_of(dev, struct nvme_subsystem, dev);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(nvme_iopolicy_names); i++) {
+ if (sysfs_streq(buf, nvme_iopolicy_names[i])) {
+ WRITE_ONCE(subsys->iopolicy, i);
+ return count;
+ }
+ }
+
+ return -EINVAL;
+}
+SUBSYS_ATTR_RW(iopolicy, S_IRUGO | S_IWUSR,
+ nvme_subsys_iopolicy_show, nvme_subsys_iopolicy_store);
+
static ssize_t ana_grpid_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h
index c4a1bb41abf0..527d64545023 100644
--- a/drivers/nvme/host/nvme.h
+++ b/drivers/nvme/host/nvme.h
@@ -1,14 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2011-2014, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
*/
#ifndef _NVME_H
@@ -95,6 +87,11 @@ enum nvme_quirks {
* Ignore device provided subnqn.
*/
NVME_QUIRK_IGNORE_DEV_SUBNQN = (1 << 8),
+
+ /*
+ * Broken Write Zeroes.
+ */
+ NVME_QUIRK_DISABLE_WRITE_ZEROES = (1 << 9),
};
/*
@@ -252,6 +249,11 @@ struct nvme_ctrl {
unsigned long discard_page_busy;
};
+enum nvme_iopolicy {
+ NVME_IOPOLICY_NUMA,
+ NVME_IOPOLICY_RR,
+};
+
struct nvme_subsystem {
int instance;
struct device dev;
@@ -271,6 +273,9 @@ struct nvme_subsystem {
u8 cmic;
u16 vendor_id;
struct ida ns_ida;
+#ifdef CONFIG_NVME_MULTIPATH
+ enum nvme_iopolicy iopolicy;
+#endif
};
/*
@@ -364,7 +369,6 @@ struct nvme_ctrl_ops {
void (*submit_async_event)(struct nvme_ctrl *ctrl);
void (*delete_ctrl)(struct nvme_ctrl *ctrl);
int (*get_address)(struct nvme_ctrl *ctrl, char *buf, int size);
- void (*stop_ctrl)(struct nvme_ctrl *ctrl);
};
#ifdef CONFIG_FAULT_INJECTION_DEBUG_FS
@@ -459,7 +463,6 @@ void nvme_stop_keep_alive(struct nvme_ctrl *ctrl);
int nvme_reset_ctrl(struct nvme_ctrl *ctrl);
int nvme_reset_ctrl_sync(struct nvme_ctrl *ctrl);
int nvme_delete_ctrl(struct nvme_ctrl *ctrl);
-int nvme_delete_ctrl_sync(struct nvme_ctrl *ctrl);
int nvme_get_log(struct nvme_ctrl *ctrl, u32 nsid, u8 log_page, u8 lsp,
void *log, size_t size, u64 offset);
@@ -492,6 +495,7 @@ static inline void nvme_mpath_check_last_path(struct nvme_ns *ns)
extern struct device_attribute dev_attr_ana_grpid;
extern struct device_attribute dev_attr_ana_state;
+extern struct device_attribute subsys_attr_iopolicy;
#else
static inline bool nvme_ctrl_use_ana(struct nvme_ctrl *ctrl)
diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c
index e905861186e3..a90cf5d63aac 100644
--- a/drivers/nvme/host/pci.c
+++ b/drivers/nvme/host/pci.c
@@ -1,15 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* NVM Express device driver
* Copyright (c) 2011-2014, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
*/
#include <linux/aer.h>
@@ -157,6 +149,8 @@ static int queue_count_set(const char *val, const struct kernel_param *kp)
int n = 0, ret;
ret = kstrtoint(val, 10, &n);
+ if (ret)
+ return ret;
if (n > num_possible_cpus())
n = num_possible_cpus();
@@ -2943,7 +2937,8 @@ static const struct pci_device_id nvme_id_table[] = {
{ PCI_VDEVICE(INTEL, 0xf1a6), /* Intel 760p/Pro 7600p */
.driver_data = NVME_QUIRK_IGNORE_DEV_SUBNQN, },
{ PCI_VDEVICE(INTEL, 0x5845), /* Qemu emulated controller */
- .driver_data = NVME_QUIRK_IDENTIFY_CNS, },
+ .driver_data = NVME_QUIRK_IDENTIFY_CNS |
+ NVME_QUIRK_DISABLE_WRITE_ZEROES, },
{ PCI_DEVICE(0x1bb1, 0x0100), /* Seagate Nytro Flash Storage */
.driver_data = NVME_QUIRK_DELAY_BEFORE_CHK_RDY, },
{ PCI_DEVICE(0x1c58, 0x0003), /* HGST adapter */
diff --git a/drivers/nvme/host/rdma.c b/drivers/nvme/host/rdma.c
index 52abc3a6de12..11a5ecae78c8 100644
--- a/drivers/nvme/host/rdma.c
+++ b/drivers/nvme/host/rdma.c
@@ -1,15 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* NVMe over Fabrics RDMA host code.
* Copyright (c) 2015-2016 HGST, a Western Digital Company.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
@@ -942,14 +934,6 @@ static void nvme_rdma_teardown_io_queues(struct nvme_rdma_ctrl *ctrl,
}
}
-static void nvme_rdma_stop_ctrl(struct nvme_ctrl *nctrl)
-{
- struct nvme_rdma_ctrl *ctrl = to_rdma_ctrl(nctrl);
-
- cancel_work_sync(&ctrl->err_work);
- cancel_delayed_work_sync(&ctrl->reconnect_work);
-}
-
static void nvme_rdma_free_ctrl(struct nvme_ctrl *nctrl)
{
struct nvme_rdma_ctrl *ctrl = to_rdma_ctrl(nctrl);
@@ -1158,7 +1142,7 @@ static void nvme_rdma_unmap_data(struct nvme_rdma_queue *queue,
struct nvme_rdma_device *dev = queue->device;
struct ib_device *ibdev = dev->dev;
- if (!blk_rq_payload_bytes(rq))
+ if (!blk_rq_nr_phys_segments(rq))
return;
if (req->mr) {
@@ -1281,7 +1265,7 @@ static int nvme_rdma_map_data(struct nvme_rdma_queue *queue,
c->common.flags |= NVME_CMD_SGL_METABUF;
- if (!blk_rq_payload_bytes(rq))
+ if (!blk_rq_nr_phys_segments(rq))
return nvme_rdma_set_sg_null(c);
req->sg_table.sgl = req->first_sgl;
@@ -1854,6 +1838,9 @@ static const struct blk_mq_ops nvme_rdma_admin_mq_ops = {
static void nvme_rdma_shutdown_ctrl(struct nvme_rdma_ctrl *ctrl, bool shutdown)
{
+ cancel_work_sync(&ctrl->err_work);
+ cancel_delayed_work_sync(&ctrl->reconnect_work);
+
nvme_rdma_teardown_io_queues(ctrl, shutdown);
if (shutdown)
nvme_shutdown_ctrl(&ctrl->ctrl);
@@ -1902,7 +1889,6 @@ static const struct nvme_ctrl_ops nvme_rdma_ctrl_ops = {
.submit_async_event = nvme_rdma_submit_async_event,
.delete_ctrl = nvme_rdma_delete_ctrl,
.get_address = nvmf_get_address,
- .stop_ctrl = nvme_rdma_stop_ctrl,
};
/*
diff --git a/drivers/nvme/host/tcp.c b/drivers/nvme/host/tcp.c
index 5f0a00425242..68c49dd67210 100644
--- a/drivers/nvme/host/tcp.c
+++ b/drivers/nvme/host/tcp.c
@@ -463,6 +463,15 @@ static int nvme_tcp_handle_c2h_data(struct nvme_tcp_queue *queue,
queue->data_remaining = le32_to_cpu(pdu->data_length);
+ if (pdu->hdr.flags & NVME_TCP_F_DATA_SUCCESS &&
+ unlikely(!(pdu->hdr.flags & NVME_TCP_F_DATA_LAST))) {
+ dev_err(queue->ctrl->ctrl.device,
+ "queue %d tag %#x SUCCESS set but not last PDU\n",
+ nvme_tcp_queue_id(queue), rq->tag);
+ nvme_tcp_error_recovery(&queue->ctrl->ctrl);
+ return -EPROTO;
+ }
+
return 0;
}
@@ -618,6 +627,14 @@ static int nvme_tcp_recv_pdu(struct nvme_tcp_queue *queue, struct sk_buff *skb,
return ret;
}
+static inline void nvme_tcp_end_request(struct request *rq, u16 status)
+{
+ union nvme_result res = {};
+
+ nvme_end_request(rq, cpu_to_le16(status << 1), res);
+}
+
+
static int nvme_tcp_recv_data(struct nvme_tcp_queue *queue, struct sk_buff *skb,
unsigned int *offset, size_t *len)
{
@@ -685,6 +702,8 @@ static int nvme_tcp_recv_data(struct nvme_tcp_queue *queue, struct sk_buff *skb,
nvme_tcp_ddgst_final(queue->rcv_hash, &queue->exp_ddgst);
queue->ddgst_remaining = NVME_TCP_DIGEST_LENGTH;
} else {
+ if (pdu->hdr.flags & NVME_TCP_F_DATA_SUCCESS)
+ nvme_tcp_end_request(rq, NVME_SC_SUCCESS);
nvme_tcp_init_recv_ctx(queue);
}
}
@@ -695,6 +714,7 @@ static int nvme_tcp_recv_data(struct nvme_tcp_queue *queue, struct sk_buff *skb,
static int nvme_tcp_recv_ddgst(struct nvme_tcp_queue *queue,
struct sk_buff *skb, unsigned int *offset, size_t *len)
{
+ struct nvme_tcp_data_pdu *pdu = (void *)queue->pdu;
char *ddgst = (char *)&queue->recv_ddgst;
size_t recv_len = min_t(size_t, *len, queue->ddgst_remaining);
off_t off = NVME_TCP_DIGEST_LENGTH - queue->ddgst_remaining;
@@ -718,6 +738,13 @@ static int nvme_tcp_recv_ddgst(struct nvme_tcp_queue *queue,
return -EIO;
}
+ if (pdu->hdr.flags & NVME_TCP_F_DATA_SUCCESS) {
+ struct request *rq = blk_mq_tag_to_rq(nvme_tcp_tagset(queue),
+ pdu->command_id);
+
+ nvme_tcp_end_request(rq, NVME_SC_SUCCESS);
+ }
+
nvme_tcp_init_recv_ctx(queue);
return 0;
}
@@ -815,10 +842,7 @@ static inline void nvme_tcp_done_send_req(struct nvme_tcp_queue *queue)
static void nvme_tcp_fail_request(struct nvme_tcp_request *req)
{
- union nvme_result res = {};
-
- nvme_end_request(blk_mq_rq_from_pdu(req),
- cpu_to_le16(NVME_SC_DATA_XFER_ERROR), res);
+ nvme_tcp_end_request(blk_mq_rq_from_pdu(req), NVME_SC_DATA_XFER_ERROR);
}
static int nvme_tcp_try_send_data(struct nvme_tcp_request *req)
@@ -1822,6 +1846,9 @@ static void nvme_tcp_error_recovery_work(struct work_struct *work)
static void nvme_tcp_teardown_ctrl(struct nvme_ctrl *ctrl, bool shutdown)
{
+ cancel_work_sync(&to_tcp_ctrl(ctrl)->err_work);
+ cancel_delayed_work_sync(&to_tcp_ctrl(ctrl)->connect_work);
+
nvme_tcp_teardown_io_queues(ctrl, shutdown);
if (shutdown)
nvme_shutdown_ctrl(ctrl);
@@ -1859,12 +1886,6 @@ out_fail:
nvme_tcp_reconnect_or_remove(ctrl);
}
-static void nvme_tcp_stop_ctrl(struct nvme_ctrl *ctrl)
-{
- cancel_work_sync(&to_tcp_ctrl(ctrl)->err_work);
- cancel_delayed_work_sync(&to_tcp_ctrl(ctrl)->connect_work);
-}
-
static void nvme_tcp_free_ctrl(struct nvme_ctrl *nctrl)
{
struct nvme_tcp_ctrl *ctrl = to_tcp_ctrl(nctrl);
@@ -2115,7 +2136,6 @@ static const struct nvme_ctrl_ops nvme_tcp_ctrl_ops = {
.submit_async_event = nvme_tcp_submit_async_event,
.delete_ctrl = nvme_tcp_delete_ctrl,
.get_address = nvmf_get_address,
- .stop_ctrl = nvme_tcp_stop_ctrl,
};
static bool
diff --git a/drivers/nvme/host/trace.c b/drivers/nvme/host/trace.c
index 5566dda3237a..5f24ea7a28eb 100644
--- a/drivers/nvme/host/trace.c
+++ b/drivers/nvme/host/trace.c
@@ -1,15 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* NVM Express device driver tracepoints
* Copyright (c) 2018 Johannes Thumshirn, SUSE Linux GmbH
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
*/
#include <asm/unaligned.h>
@@ -58,7 +50,19 @@ static const char *nvme_trace_admin_identify(struct trace_seq *p, u8 *cdw10)
return ret;
}
+static const char *nvme_trace_admin_get_features(struct trace_seq *p,
+ u8 *cdw10)
+{
+ const char *ret = trace_seq_buffer_ptr(p);
+ u8 fid = cdw10[0];
+ u8 sel = cdw10[1] & 0x7;
+ u32 cdw11 = get_unaligned_le32(cdw10 + 4);
+
+ trace_seq_printf(p, "fid=0x%x sel=0x%x cdw11=0x%x", fid, sel, cdw11);
+ trace_seq_putc(p, 0);
+ return ret;
+}
static const char *nvme_trace_read_write(struct trace_seq *p, u8 *cdw10)
{
@@ -109,6 +113,8 @@ const char *nvme_trace_parse_admin_cmd(struct trace_seq *p,
return nvme_trace_create_cq(p, cdw10);
case nvme_admin_identify:
return nvme_trace_admin_identify(p, cdw10);
+ case nvme_admin_get_features:
+ return nvme_trace_admin_get_features(p, cdw10);
default:
return nvme_trace_common(p, cdw10);
}
diff --git a/drivers/nvme/host/trace.h b/drivers/nvme/host/trace.h
index 3564120aa7b3..97d3c77365b8 100644
--- a/drivers/nvme/host/trace.h
+++ b/drivers/nvme/host/trace.h
@@ -1,15 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* NVM Express device driver tracepoints
* Copyright (c) 2018 Johannes Thumshirn, SUSE Linux GmbH
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
*/
#undef TRACE_SYSTEM
@@ -116,7 +108,7 @@ TRACE_EVENT(nvme_setup_cmd,
__entry->metadata = le64_to_cpu(cmd->common.metadata);
__assign_disk_name(__entry->disk, req->rq_disk);
memcpy(__entry->cdw10, &cmd->common.cdw10,
- 6 * sizeof(__entry->cdw10));
+ sizeof(__entry->cdw10));
),
TP_printk("nvme%d: %sqid=%d, cmdid=%u, nsid=%u, flags=0x%x, meta=0x%llx, cmd=(%s %s)",
__entry->ctrl_id, __print_disk_name(__entry->disk),
diff --git a/drivers/nvme/target/admin-cmd.c b/drivers/nvme/target/admin-cmd.c
index 11baeb14c388..76250181fee0 100644
--- a/drivers/nvme/target/admin-cmd.c
+++ b/drivers/nvme/target/admin-cmd.c
@@ -1,15 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* NVMe admin command implementation.
* Copyright (c) 2015-2016 HGST, a Western Digital Company.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
diff --git a/drivers/nvme/target/configfs.c b/drivers/nvme/target/configfs.c
index 618bbd006544..adb79545cdd7 100644
--- a/drivers/nvme/target/configfs.c
+++ b/drivers/nvme/target/configfs.c
@@ -1,15 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Configfs interface for the NVMe target.
* Copyright (c) 2015-2016 HGST, a Western Digital Company.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h>
diff --git a/drivers/nvme/target/core.c b/drivers/nvme/target/core.c
index 88d260f31835..b3e765a95af8 100644
--- a/drivers/nvme/target/core.c
+++ b/drivers/nvme/target/core.c
@@ -1,15 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Common code for the NVMe target.
* Copyright (c) 2015-2016 HGST, a Western Digital Company.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
@@ -517,7 +509,7 @@ int nvmet_ns_enable(struct nvmet_ns *ns)
ret = nvmet_p2pmem_ns_enable(ns);
if (ret)
- goto out_unlock;
+ goto out_dev_disable;
list_for_each_entry(ctrl, &subsys->ctrls, subsys_entry)
nvmet_p2pmem_ns_add_p2p(ctrl, ns);
@@ -558,7 +550,7 @@ out_unlock:
out_dev_put:
list_for_each_entry(ctrl, &subsys->ctrls, subsys_entry)
pci_dev_put(radix_tree_delete(&ctrl->p2p_ns_map, ns->nsid));
-
+out_dev_disable:
nvmet_ns_dev_disable(ns);
goto out_unlock;
}
@@ -1171,6 +1163,15 @@ static void nvmet_release_p2p_ns_map(struct nvmet_ctrl *ctrl)
put_device(ctrl->p2p_client);
}
+static void nvmet_fatal_error_handler(struct work_struct *work)
+{
+ struct nvmet_ctrl *ctrl =
+ container_of(work, struct nvmet_ctrl, fatal_err_work);
+
+ pr_err("ctrl %d fatal error occurred!\n", ctrl->cntlid);
+ ctrl->ops->delete_ctrl(ctrl);
+}
+
u16 nvmet_alloc_ctrl(const char *subsysnqn, const char *hostnqn,
struct nvmet_req *req, u32 kato, struct nvmet_ctrl **ctrlp)
{
@@ -1213,6 +1214,7 @@ u16 nvmet_alloc_ctrl(const char *subsysnqn, const char *hostnqn,
INIT_WORK(&ctrl->async_event_work, nvmet_async_event_work);
INIT_LIST_HEAD(&ctrl->async_events);
INIT_RADIX_TREE(&ctrl->p2p_ns_map, GFP_KERNEL);
+ INIT_WORK(&ctrl->fatal_err_work, nvmet_fatal_error_handler);
memcpy(ctrl->subsysnqn, subsysnqn, NVMF_NQN_SIZE);
memcpy(ctrl->hostnqn, hostnqn, NVMF_NQN_SIZE);
@@ -1316,21 +1318,11 @@ void nvmet_ctrl_put(struct nvmet_ctrl *ctrl)
kref_put(&ctrl->ref, nvmet_ctrl_free);
}
-static void nvmet_fatal_error_handler(struct work_struct *work)
-{
- struct nvmet_ctrl *ctrl =
- container_of(work, struct nvmet_ctrl, fatal_err_work);
-
- pr_err("ctrl %d fatal error occurred!\n", ctrl->cntlid);
- ctrl->ops->delete_ctrl(ctrl);
-}
-
void nvmet_ctrl_fatal_error(struct nvmet_ctrl *ctrl)
{
mutex_lock(&ctrl->lock);
if (!(ctrl->csts & NVME_CSTS_CFS)) {
ctrl->csts |= NVME_CSTS_CFS;
- INIT_WORK(&ctrl->fatal_err_work, nvmet_fatal_error_handler);
schedule_work(&ctrl->fatal_err_work);
}
mutex_unlock(&ctrl->lock);
diff --git a/drivers/nvme/target/discovery.c b/drivers/nvme/target/discovery.c
index d2cb71a0b419..c872b47a88f3 100644
--- a/drivers/nvme/target/discovery.c
+++ b/drivers/nvme/target/discovery.c
@@ -1,15 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Discovery service for the NVMe over Fabrics target.
* Copyright (C) 2016 Intel Corporation. All rights reserved.
- *
- * 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.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/slab.h>
@@ -331,7 +323,7 @@ u16 nvmet_parse_discovery_cmd(struct nvmet_req *req)
cmd->get_log_page.lid);
req->error_loc =
offsetof(struct nvme_get_log_page_command, lid);
- return NVME_SC_INVALID_OPCODE | NVME_SC_DNR;
+ return NVME_SC_INVALID_OPCODE | NVME_SC_DNR;
}
case nvme_admin_identify:
req->data_len = NVME_IDENTIFY_DATA_SIZE;
diff --git a/drivers/nvme/target/fabrics-cmd.c b/drivers/nvme/target/fabrics-cmd.c
index 6cf1fd9eb32e..3a76ebc3d155 100644
--- a/drivers/nvme/target/fabrics-cmd.c
+++ b/drivers/nvme/target/fabrics-cmd.c
@@ -1,15 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* NVMe Fabrics command implementation.
* Copyright (c) 2015-2016 HGST, a Western Digital Company.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/blkdev.h>
diff --git a/drivers/nvme/target/fc.c b/drivers/nvme/target/fc.c
index f98f5c5bea26..98b7b1f4ee96 100644
--- a/drivers/nvme/target/fc.c
+++ b/drivers/nvme/target/fc.c
@@ -1,18 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2016 Avago Technologies. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful.
- * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
- * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
- * PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED, EXCEPT TO
- * THE EXTENT THAT SUCH DISCLAIMERS ARE HELD TO BE LEGALLY INVALID.
- * See the GNU General Public License for more details, a copy of which
- * can be found in the file COPYING included with this package
- *
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
@@ -1155,10 +1143,8 @@ __nvmet_fc_free_assocs(struct nvmet_fc_tgtport *tgtport)
&tgtport->assoc_list, a_list) {
if (!nvmet_fc_tgt_a_get(assoc))
continue;
- spin_unlock_irqrestore(&tgtport->lock, flags);
- nvmet_fc_delete_target_assoc(assoc);
- nvmet_fc_tgt_a_put(assoc);
- spin_lock_irqsave(&tgtport->lock, flags);
+ if (!schedule_work(&assoc->del_work))
+ nvmet_fc_tgt_a_put(assoc);
}
spin_unlock_irqrestore(&tgtport->lock, flags);
}
@@ -1197,7 +1183,8 @@ nvmet_fc_delete_ctrl(struct nvmet_ctrl *ctrl)
nvmet_fc_tgtport_put(tgtport);
if (found_ctrl) {
- schedule_work(&assoc->del_work);
+ if (!schedule_work(&assoc->del_work))
+ nvmet_fc_tgt_a_put(assoc);
return;
}
@@ -1515,10 +1502,8 @@ nvmet_fc_ls_disconnect(struct nvmet_fc_tgtport *tgtport,
(struct fcnvme_ls_disconnect_rqst *)iod->rqstbuf;
struct fcnvme_ls_disconnect_acc *acc =
(struct fcnvme_ls_disconnect_acc *)iod->rspbuf;
- struct nvmet_fc_tgt_queue *queue = NULL;
struct nvmet_fc_tgt_assoc *assoc;
int ret = 0;
- bool del_assoc = false;
memset(acc, 0, sizeof(*acc));
@@ -1549,18 +1534,7 @@ nvmet_fc_ls_disconnect(struct nvmet_fc_tgtport *tgtport,
assoc = nvmet_fc_find_target_assoc(tgtport,
be64_to_cpu(rqst->associd.association_id));
iod->assoc = assoc;
- if (assoc) {
- if (rqst->discon_cmd.scope ==
- FCNVME_DISCONN_CONNECTION) {
- queue = nvmet_fc_find_target_queue(tgtport,
- be64_to_cpu(
- rqst->discon_cmd.id));
- if (!queue) {
- nvmet_fc_tgt_a_put(assoc);
- ret = VERR_NO_CONN;
- }
- }
- } else
+ if (!assoc)
ret = VERR_NO_ASSOC;
}
@@ -1588,26 +1562,10 @@ nvmet_fc_ls_disconnect(struct nvmet_fc_tgtport *tgtport,
sizeof(struct fcnvme_ls_disconnect_acc)),
FCNVME_LS_DISCONNECT);
-
- /* are we to delete a Connection ID (queue) */
- if (queue) {
- int qid = queue->qid;
-
- nvmet_fc_delete_target_queue(queue);
-
- /* release the get taken by find_target_queue */
- nvmet_fc_tgt_q_put(queue);
-
- /* tear association down if io queue terminated */
- if (!qid)
- del_assoc = true;
- }
-
/* release get taken in nvmet_fc_find_target_assoc */
nvmet_fc_tgt_a_put(iod->assoc);
- if (del_assoc)
- nvmet_fc_delete_target_assoc(iod->assoc);
+ nvmet_fc_delete_target_assoc(iod->assoc);
}
diff --git a/drivers/nvme/target/fcloop.c b/drivers/nvme/target/fcloop.c
index 291f4121f516..381b5a90c48b 100644
--- a/drivers/nvme/target/fcloop.c
+++ b/drivers/nvme/target/fcloop.c
@@ -1,17 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2016 Avago Technologies. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful.
- * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
- * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
- * PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED, EXCEPT TO
- * THE EXTENT THAT SUCH DISCLAIMERS ARE HELD TO BE LEGALLY INVALID.
- * See the GNU General Public License for more details, a copy of which
- * can be found in the file COPYING included with this package
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
diff --git a/drivers/nvme/target/io-cmd-bdev.c b/drivers/nvme/target/io-cmd-bdev.c
index b6d030d3259f..a065dbfc43b1 100644
--- a/drivers/nvme/target/io-cmd-bdev.c
+++ b/drivers/nvme/target/io-cmd-bdev.c
@@ -1,15 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* NVMe I/O command implementation.
* Copyright (c) 2015-2016 HGST, a Western Digital Company.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/blkdev.h>
@@ -202,11 +194,11 @@ static u16 nvmet_bdev_discard_range(struct nvmet_req *req,
le64_to_cpu(range->slba) << (ns->blksize_shift - 9),
le32_to_cpu(range->nlb) << (ns->blksize_shift - 9),
GFP_KERNEL, 0, bio);
-
- if (ret)
+ if (ret && ret != -EOPNOTSUPP) {
req->error_slba = le64_to_cpu(range->slba);
-
- return blk_to_nvme_status(req, errno_to_blk_status(ret));
+ return blk_to_nvme_status(req, errno_to_blk_status(ret));
+ }
+ return NVME_SC_SUCCESS;
}
static void nvmet_bdev_execute_discard(struct nvmet_req *req)
diff --git a/drivers/nvme/target/io-cmd-file.c b/drivers/nvme/target/io-cmd-file.c
index 517522305e5c..bc6ebb51b0bf 100644
--- a/drivers/nvme/target/io-cmd-file.c
+++ b/drivers/nvme/target/io-cmd-file.c
@@ -75,11 +75,11 @@ err:
return ret;
}
-static void nvmet_file_init_bvec(struct bio_vec *bv, struct sg_page_iter *iter)
+static void nvmet_file_init_bvec(struct bio_vec *bv, struct scatterlist *sg)
{
- bv->bv_page = sg_page_iter_page(iter);
- bv->bv_offset = iter->sg->offset;
- bv->bv_len = PAGE_SIZE - iter->sg->offset;
+ bv->bv_page = sg_page(sg);
+ bv->bv_offset = sg->offset;
+ bv->bv_len = sg->length;
}
static ssize_t nvmet_file_submit_bvec(struct nvmet_req *req, loff_t pos,
@@ -128,14 +128,14 @@ static void nvmet_file_io_done(struct kiocb *iocb, long ret, long ret2)
static bool nvmet_file_execute_io(struct nvmet_req *req, int ki_flags)
{
- ssize_t nr_bvec = DIV_ROUND_UP(req->data_len, PAGE_SIZE);
- struct sg_page_iter sg_pg_iter;
+ ssize_t nr_bvec = req->sg_cnt;
unsigned long bv_cnt = 0;
bool is_sync = false;
size_t len = 0, total_len = 0;
ssize_t ret = 0;
loff_t pos;
-
+ int i;
+ struct scatterlist *sg;
if (req->f.mpool_alloc && nr_bvec > NVMET_MAX_MPOOL_BVEC)
is_sync = true;
@@ -147,8 +147,8 @@ static bool nvmet_file_execute_io(struct nvmet_req *req, int ki_flags)
}
memset(&req->f.iocb, 0, sizeof(struct kiocb));
- for_each_sg_page(req->sg, &sg_pg_iter, req->sg_cnt, 0) {
- nvmet_file_init_bvec(&req->f.bvec[bv_cnt], &sg_pg_iter);
+ for_each_sg(req->sg, sg, req->sg_cnt, i) {
+ nvmet_file_init_bvec(&req->f.bvec[bv_cnt], sg);
len += req->f.bvec[bv_cnt].bv_len;
total_len += req->f.bvec[bv_cnt].bv_len;
bv_cnt++;
@@ -225,7 +225,7 @@ static void nvmet_file_submit_buffered_io(struct nvmet_req *req)
static void nvmet_file_execute_rw(struct nvmet_req *req)
{
- ssize_t nr_bvec = DIV_ROUND_UP(req->data_len, PAGE_SIZE);
+ ssize_t nr_bvec = req->sg_cnt;
if (!req->sg_cnt || !nr_bvec) {
nvmet_req_complete(req, 0);
@@ -297,7 +297,7 @@ static void nvmet_file_execute_discard(struct nvmet_req *req)
}
ret = vfs_fallocate(req->ns->file, mode, offset, len);
- if (ret) {
+ if (ret && ret != -EOPNOTSUPP) {
req->error_slba = le64_to_cpu(range.slba);
status = errno_to_nvme_status(req, ret);
break;
diff --git a/drivers/nvme/target/loop.c b/drivers/nvme/target/loop.c
index 4aac1b4a8112..b9f623ab01f3 100644
--- a/drivers/nvme/target/loop.c
+++ b/drivers/nvme/target/loop.c
@@ -1,15 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* NVMe over Fabrics loopback device.
* Copyright (c) 2015-2016 HGST, a Western Digital Company.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/scatterlist.h>
diff --git a/drivers/nvme/target/nvmet.h b/drivers/nvme/target/nvmet.h
index 3e4719fdba85..51e49efd7849 100644
--- a/drivers/nvme/target/nvmet.h
+++ b/drivers/nvme/target/nvmet.h
@@ -1,14 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2015-2016 HGST, a Western Digital Company.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
*/
#ifndef _NVMET_H
diff --git a/drivers/nvme/target/rdma.c b/drivers/nvme/target/rdma.c
index a884e3a0e8af..ef893addf341 100644
--- a/drivers/nvme/target/rdma.c
+++ b/drivers/nvme/target/rdma.c
@@ -1,15 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* NVMe over Fabrics RDMA target.
* Copyright (c) 2015-2016 HGST, a Western Digital Company.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/atomic.h>
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
index ad3fcad4d75b..37c2ccbefecd 100644
--- a/drivers/of/Kconfig
+++ b/drivers/of/Kconfig
@@ -43,6 +43,7 @@ config OF_FLATTREE
config OF_EARLY_FLATTREE
bool
+ select DMA_DECLARE_COHERENT if HAS_DMA
select OF_FLATTREE
config OF_PROMTREE
@@ -81,10 +82,9 @@ config OF_MDIO
OpenFirmware MDIO bus (Ethernet PHY) accessors
config OF_RESERVED_MEM
- depends on OF_EARLY_FLATTREE
bool
- help
- Helpers to allow for reservation of memory regions
+ depends on OF_EARLY_FLATTREE
+ default y if DMA_DECLARE_COHERENT || DMA_CMA
config OF_RESOLVE
bool
diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
index 9cc1461aac7d..4734223ab702 100644
--- a/drivers/of/fdt.c
+++ b/drivers/of/fdt.c
@@ -1181,7 +1181,13 @@ int __init __weak early_init_dt_reserve_memory_arch(phys_addr_t base,
static void * __init early_init_dt_alloc_memory_arch(u64 size, u64 align)
{
- return memblock_alloc(size, align);
+ void *ptr = memblock_alloc(size, align);
+
+ if (!ptr)
+ panic("%s: Failed to allocate %llu bytes align=0x%llx\n",
+ __func__, size, align);
+
+ return ptr;
}
bool __init early_init_dt_verify(void *params)
diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c
index 1977ee0adcb1..6a36bc0b3d64 100644
--- a/drivers/of/of_reserved_mem.c
+++ b/drivers/of/of_reserved_mem.c
@@ -26,33 +26,23 @@
static struct reserved_mem reserved_mem[MAX_RESERVED_REGIONS];
static int reserved_mem_count;
-int __init __weak early_init_dt_alloc_reserved_memory_arch(phys_addr_t size,
+static int __init early_init_dt_alloc_reserved_memory_arch(phys_addr_t size,
phys_addr_t align, phys_addr_t start, phys_addr_t end, bool nomap,
phys_addr_t *res_base)
{
phys_addr_t base;
- /*
- * We use __memblock_alloc_base() because memblock_alloc_base()
- * panic()s on allocation failure.
- */
+
end = !end ? MEMBLOCK_ALLOC_ANYWHERE : end;
align = !align ? SMP_CACHE_BYTES : align;
- base = __memblock_alloc_base(size, align, end);
+ base = memblock_find_in_range(start, end, size, align);
if (!base)
return -ENOMEM;
- /*
- * Check if the allocated region fits in to start..end window
- */
- if (base < start) {
- memblock_free(base, size);
- return -ENOMEM;
- }
-
*res_base = base;
if (nomap)
return memblock_remove(base, size);
- return 0;
+
+ return memblock_reserve(base, size);
}
/**
@@ -340,10 +330,6 @@ int of_reserved_mem_device_init_by_idx(struct device *dev,
mutex_lock(&of_rmem_assigned_device_mutex);
list_add(&rd->list, &of_rmem_assigned_device_list);
mutex_unlock(&of_rmem_assigned_device_mutex);
- /* ensure that dma_ops is set for virtual devices
- * using reserved memory
- */
- of_dma_configure(dev, np, true);
dev_info(dev, "assigned reserved memory node %s\n", rmem->name);
} else {
diff --git a/drivers/of/unittest.c b/drivers/of/unittest.c
index 84427384654d..cccde756b510 100644
--- a/drivers/of/unittest.c
+++ b/drivers/of/unittest.c
@@ -1116,15 +1116,22 @@ static void update_node_properties(struct device_node *np,
for (prop = np->properties; prop != NULL; prop = save_next) {
save_next = prop->next;
ret = of_add_property(dup, prop);
- if (ret)
+ if (ret) {
+ if (ret == -EEXIST && !strcmp(prop->name, "name"))
+ continue;
pr_err("unittest internal error: unable to add testdata property %pOF/%s",
np, prop->name);
+ }
}
}
/**
* attach_node_and_children - attaches nodes
- * and its children to live tree
+ * and its children to live tree.
+ * CAUTION: misleading function name - if node @np already exists in
+ * the live tree then children of @np are *not* attached to the live
+ * tree. This works for the current test devicetree nodes because such
+ * nodes do not have child nodes.
*
* @np: Node to attach to live tree
*/
@@ -2234,7 +2241,13 @@ static struct device_node *overlay_base_root;
static void * __init dt_alloc_memory(u64 size, u64 align)
{
- return memblock_alloc(size, align);
+ void *ptr = memblock_alloc(size, align);
+
+ if (!ptr)
+ panic("%s: Failed to allocate %llu bytes align=0x%llx\n",
+ __func__, size, align);
+
+ return ptr;
}
/*
@@ -2514,6 +2527,10 @@ static int __init of_unittest(void)
int res;
/* adding data for unittest */
+
+ if (IS_ENABLED(CONFIG_UML))
+ unittest_unflatten_overlay_base();
+
res = unittest_data_add();
if (res)
return res;
diff --git a/drivers/opp/core.c b/drivers/opp/core.c
index d7f97167cac3..0420f7e8ad5b 100644
--- a/drivers/opp/core.c
+++ b/drivers/opp/core.c
@@ -760,7 +760,7 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq)
old_freq, freq);
/* Scaling up? Configure required OPPs before frequency */
- if (freq > old_freq) {
+ if (freq >= old_freq) {
ret = _set_required_opps(dev, opp_table, opp);
if (ret)
goto put_opp;
diff --git a/drivers/opp/of.c b/drivers/opp/of.c
index 62504b18f198..c10c782d15aa 100644
--- a/drivers/opp/of.c
+++ b/drivers/opp/of.c
@@ -173,7 +173,7 @@ static void _opp_table_alloc_required_tables(struct opp_table *opp_table,
struct opp_table **required_opp_tables;
struct device **genpd_virt_devs = NULL;
struct device_node *required_np, *np;
- int count, i;
+ int count, count_pd, i;
/* Traversing the first OPP node is all we need */
np = of_get_next_available_child(opp_np, NULL);
@@ -186,7 +186,19 @@ static void _opp_table_alloc_required_tables(struct opp_table *opp_table,
if (!count)
goto put_np;
- if (count > 1) {
+ /*
+ * Check the number of power-domains to know if we need to deal
+ * with virtual devices. In some cases we have devices with multiple
+ * power domains but with only one of them being scalable, hence
+ * 'count' could be 1, but we still have to deal with multiple genpds
+ * and virtual devices.
+ */
+ count_pd = of_count_phandle_with_args(dev->of_node, "power-domains",
+ "#power-domain-cells");
+ if (!count_pd)
+ goto put_np;
+
+ if (count_pd > 1) {
genpd_virt_devs = kcalloc(count, sizeof(*genpd_virt_devs),
GFP_KERNEL);
if (!genpd_virt_devs)
diff --git a/drivers/parisc/ccio-dma.c b/drivers/parisc/ccio-dma.c
index 097b0d43d13c..8937ba70d817 100644
--- a/drivers/parisc/ccio-dma.c
+++ b/drivers/parisc/ccio-dma.c
@@ -712,8 +712,8 @@ ccio_dma_supported(struct device *dev, u64 mask)
return 0;
}
- /* only support 32-bit devices (ie PCI/GSC) */
- return (int)(mask == 0xffffffffUL);
+ /* only support 32-bit or better devices (ie PCI/GSC) */
+ return (int)(mask >= 0xffffffffUL);
}
/**
diff --git a/drivers/parport/daisy.c b/drivers/parport/daisy.c
index 56dd83a45e55..5484a46dafda 100644
--- a/drivers/parport/daisy.c
+++ b/drivers/parport/daisy.c
@@ -213,12 +213,10 @@ void parport_daisy_fini(struct parport *port)
struct pardevice *parport_open(int devnum, const char *name)
{
struct daisydev *p = topology;
- struct pardev_cb par_cb;
struct parport *port;
struct pardevice *dev;
int daisy;
- memset(&par_cb, 0, sizeof(par_cb));
spin_lock(&topology_lock);
while (p && p->devnum != devnum)
p = p->next;
@@ -232,7 +230,7 @@ struct pardevice *parport_open(int devnum, const char *name)
port = parport_get_port(p->port);
spin_unlock(&topology_lock);
- dev = parport_register_dev_model(port, name, &par_cb, devnum);
+ dev = parport_register_device(port, name, NULL, NULL, NULL, 0, NULL);
parport_put_port(port);
if (!dev)
return NULL;
@@ -482,31 +480,3 @@ static int assign_addrs(struct parport *port)
kfree(deviceid);
return detected;
}
-
-static int daisy_drv_probe(struct pardevice *par_dev)
-{
- struct device_driver *drv = par_dev->dev.driver;
-
- if (strcmp(drv->name, "daisy_drv"))
- return -ENODEV;
- if (strcmp(par_dev->name, daisy_dev_name))
- return -ENODEV;
-
- return 0;
-}
-
-static struct parport_driver daisy_driver = {
- .name = "daisy_drv",
- .probe = daisy_drv_probe,
- .devmodel = true,
-};
-
-int daisy_drv_init(void)
-{
- return parport_register_driver(&daisy_driver);
-}
-
-void daisy_drv_exit(void)
-{
- parport_unregister_driver(&daisy_driver);
-}
diff --git a/drivers/parport/probe.c b/drivers/parport/probe.c
index e5e6a463a941..e035174ba205 100644
--- a/drivers/parport/probe.c
+++ b/drivers/parport/probe.c
@@ -257,7 +257,7 @@ static ssize_t parport_read_device_id (struct parport *port, char *buffer,
ssize_t parport_device_id (int devnum, char *buffer, size_t count)
{
ssize_t retval = -ENXIO;
- struct pardevice *dev = parport_open(devnum, daisy_dev_name);
+ struct pardevice *dev = parport_open (devnum, "Device ID probe");
if (!dev)
return -ENXIO;
diff --git a/drivers/parport/share.c b/drivers/parport/share.c
index 0171b8dbcdcd..5dc53d420ca8 100644
--- a/drivers/parport/share.c
+++ b/drivers/parport/share.c
@@ -137,19 +137,11 @@ static struct bus_type parport_bus_type = {
int parport_bus_init(void)
{
- int retval;
-
- retval = bus_register(&parport_bus_type);
- if (retval)
- return retval;
- daisy_drv_init();
-
- return 0;
+ return bus_register(&parport_bus_type);
}
void parport_bus_exit(void)
{
- daisy_drv_exit();
bus_unregister(&parport_bus_type);
}
diff --git a/drivers/pci/ats.c b/drivers/pci/ats.c
index 5b78f3b1b918..97c08146534a 100644
--- a/drivers/pci/ats.c
+++ b/drivers/pci/ats.c
@@ -142,6 +142,33 @@ int pci_ats_queue_depth(struct pci_dev *dev)
}
EXPORT_SYMBOL_GPL(pci_ats_queue_depth);
+/**
+ * pci_ats_page_aligned - Return Page Aligned Request bit status.
+ * @pdev: the PCI device
+ *
+ * Returns 1, if the Untranslated Addresses generated by the device
+ * are always aligned or 0 otherwise.
+ *
+ * Per PCIe spec r4.0, sec 10.5.1.2, if the Page Aligned Request bit
+ * is set, it indicates the Untranslated Addresses generated by the
+ * device are always aligned to a 4096 byte boundary.
+ */
+int pci_ats_page_aligned(struct pci_dev *pdev)
+{
+ u16 cap;
+
+ if (!pdev->ats_cap)
+ return 0;
+
+ pci_read_config_word(pdev, pdev->ats_cap + PCI_ATS_CAP, &cap);
+
+ if (cap & PCI_ATS_CAP_PAGE_ALIGNED)
+ return 1;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pci_ats_page_aligned);
+
#ifdef CONFIG_PCI_PRI
/**
* pci_enable_pri - Enable PRI capability
@@ -368,6 +395,36 @@ int pci_pasid_features(struct pci_dev *pdev)
}
EXPORT_SYMBOL_GPL(pci_pasid_features);
+/**
+ * pci_prg_resp_pasid_required - Return PRG Response PASID Required bit
+ * status.
+ * @pdev: PCI device structure
+ *
+ * Returns 1 if PASID is required in PRG Response Message, 0 otherwise.
+ *
+ * Even though the PRG response PASID status is read from PRI Status
+ * Register, since this API will mainly be used by PASID users, this
+ * function is defined within #ifdef CONFIG_PCI_PASID instead of
+ * CONFIG_PCI_PRI.
+ */
+int pci_prg_resp_pasid_required(struct pci_dev *pdev)
+{
+ u16 status;
+ int pos;
+
+ pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PRI);
+ if (!pos)
+ return 0;
+
+ pci_read_config_word(pdev, pos + PCI_PRI_STATUS, &status);
+
+ if (status & PCI_PRI_STATUS_PASID)
+ return 1;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pci_prg_resp_pasid_required);
+
#define PASID_NUMBER_SHIFT 8
#define PASID_NUMBER_MASK (0x1f << PASID_NUMBER_SHIFT)
/**
diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig
index 6671946dbf66..6012f3059acd 100644
--- a/drivers/pci/controller/Kconfig
+++ b/drivers/pci/controller/Kconfig
@@ -175,7 +175,7 @@ config PCIE_IPROC_MSI
config PCIE_ALTERA
bool "Altera PCIe controller"
- depends on ARM || NIOS2 || COMPILE_TEST
+ depends on ARM || NIOS2 || ARM64 || COMPILE_TEST
help
Say Y here if you want to enable PCIe controller support on Altera
FPGA.
diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig
index 548c58223868..6ea74b1c0d94 100644
--- a/drivers/pci/controller/dwc/Kconfig
+++ b/drivers/pci/controller/dwc/Kconfig
@@ -89,8 +89,8 @@ config PCI_EXYNOS
select PCIE_DW_HOST
config PCI_IMX6
- bool "Freescale i.MX6/7 PCIe controller"
- depends on SOC_IMX6Q || SOC_IMX7D || (ARM && COMPILE_TEST)
+ bool "Freescale i.MX6/7/8 PCIe controller"
+ depends on SOC_IMX6Q || SOC_IMX7D || (ARM64 && ARCH_MXC) || COMPILE_TEST
depends on PCI_MSI_IRQ_DOMAIN
select PCIE_DW_HOST
diff --git a/drivers/pci/controller/dwc/Makefile b/drivers/pci/controller/dwc/Makefile
index 7bcdcdf5024e..b5f3b83cc2b3 100644
--- a/drivers/pci/controller/dwc/Makefile
+++ b/drivers/pci/controller/dwc/Makefile
@@ -8,7 +8,7 @@ obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o
obj-$(CONFIG_PCI_IMX6) += pci-imx6.o
obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o
obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone.o
-obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
+obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o pci-layerscape-ep.o
obj-$(CONFIG_PCIE_QCOM) += pcie-qcom.o
obj-$(CONFIG_PCIE_ARMADA_8K) += pcie-armada8k.o
obj-$(CONFIG_PCIE_ARTPEC6) += pcie-artpec6.o
diff --git a/drivers/pci/controller/dwc/pci-dra7xx.c b/drivers/pci/controller/dwc/pci-dra7xx.c
index a32d6dde7a57..ae84a69ae63a 100644
--- a/drivers/pci/controller/dwc/pci-dra7xx.c
+++ b/drivers/pci/controller/dwc/pci-dra7xx.c
@@ -81,6 +81,10 @@
#define MSI_REQ_GRANT BIT(0)
#define MSI_VECTOR_SHIFT 7
+#define PCIE_1LANE_2LANE_SELECTION BIT(13)
+#define PCIE_B1C0_MODE_SEL BIT(2)
+#define PCIE_B0_B1_TSYNCEN BIT(0)
+
struct dra7xx_pcie {
struct dw_pcie *pci;
void __iomem *base; /* DT ti_conf */
@@ -93,6 +97,7 @@ struct dra7xx_pcie {
struct dra7xx_pcie_of_data {
enum dw_pcie_device_mode mode;
+ u32 b1co_mode_sel_mask;
};
#define to_dra7xx_pcie(x) dev_get_drvdata((x)->dev)
@@ -389,9 +394,22 @@ static int dra7xx_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
return 0;
}
+static const struct pci_epc_features dra7xx_pcie_epc_features = {
+ .linkup_notifier = true,
+ .msi_capable = true,
+ .msix_capable = false,
+};
+
+static const struct pci_epc_features*
+dra7xx_pcie_get_features(struct dw_pcie_ep *ep)
+{
+ return &dra7xx_pcie_epc_features;
+}
+
static struct dw_pcie_ep_ops pcie_ep_ops = {
.ep_init = dra7xx_pcie_ep_init,
.raise_irq = dra7xx_pcie_raise_irq,
+ .get_features = dra7xx_pcie_get_features,
};
static int __init dra7xx_add_pcie_ep(struct dra7xx_pcie *dra7xx,
@@ -499,6 +517,10 @@ static int dra7xx_pcie_enable_phy(struct dra7xx_pcie *dra7xx)
int i;
for (i = 0; i < phy_count; i++) {
+ ret = phy_set_mode(dra7xx->phy[i], PHY_MODE_PCIE);
+ if (ret < 0)
+ goto err_phy;
+
ret = phy_init(dra7xx->phy[i]);
if (ret < 0)
goto err_phy;
@@ -529,6 +551,26 @@ static const struct dra7xx_pcie_of_data dra7xx_pcie_ep_of_data = {
.mode = DW_PCIE_EP_TYPE,
};
+static const struct dra7xx_pcie_of_data dra746_pcie_rc_of_data = {
+ .b1co_mode_sel_mask = BIT(2),
+ .mode = DW_PCIE_RC_TYPE,
+};
+
+static const struct dra7xx_pcie_of_data dra726_pcie_rc_of_data = {
+ .b1co_mode_sel_mask = GENMASK(3, 2),
+ .mode = DW_PCIE_RC_TYPE,
+};
+
+static const struct dra7xx_pcie_of_data dra746_pcie_ep_of_data = {
+ .b1co_mode_sel_mask = BIT(2),
+ .mode = DW_PCIE_EP_TYPE,
+};
+
+static const struct dra7xx_pcie_of_data dra726_pcie_ep_of_data = {
+ .b1co_mode_sel_mask = GENMASK(3, 2),
+ .mode = DW_PCIE_EP_TYPE,
+};
+
static const struct of_device_id of_dra7xx_pcie_match[] = {
{
.compatible = "ti,dra7-pcie",
@@ -538,6 +580,22 @@ static const struct of_device_id of_dra7xx_pcie_match[] = {
.compatible = "ti,dra7-pcie-ep",
.data = &dra7xx_pcie_ep_of_data,
},
+ {
+ .compatible = "ti,dra746-pcie-rc",
+ .data = &dra746_pcie_rc_of_data,
+ },
+ {
+ .compatible = "ti,dra726-pcie-rc",
+ .data = &dra726_pcie_rc_of_data,
+ },
+ {
+ .compatible = "ti,dra746-pcie-ep",
+ .data = &dra746_pcie_ep_of_data,
+ },
+ {
+ .compatible = "ti,dra726-pcie-ep",
+ .data = &dra726_pcie_ep_of_data,
+ },
{},
};
@@ -583,6 +641,34 @@ static int dra7xx_pcie_unaligned_memaccess(struct device *dev)
return ret;
}
+static int dra7xx_pcie_configure_two_lane(struct device *dev,
+ u32 b1co_mode_sel_mask)
+{
+ struct device_node *np = dev->of_node;
+ struct regmap *pcie_syscon;
+ unsigned int pcie_reg;
+ u32 mask;
+ u32 val;
+
+ pcie_syscon = syscon_regmap_lookup_by_phandle(np, "ti,syscon-lane-sel");
+ if (IS_ERR(pcie_syscon)) {
+ dev_err(dev, "unable to get ti,syscon-lane-sel\n");
+ return -EINVAL;
+ }
+
+ if (of_property_read_u32_index(np, "ti,syscon-lane-sel", 1,
+ &pcie_reg)) {
+ dev_err(dev, "couldn't get lane selection reg offset\n");
+ return -EINVAL;
+ }
+
+ mask = b1co_mode_sel_mask | PCIE_B0_B1_TSYNCEN;
+ val = PCIE_B1C0_MODE_SEL | PCIE_B0_B1_TSYNCEN;
+ regmap_update_bits(pcie_syscon, pcie_reg, mask, val);
+
+ return 0;
+}
+
static int __init dra7xx_pcie_probe(struct platform_device *pdev)
{
u32 reg;
@@ -603,6 +689,7 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev)
const struct of_device_id *match;
const struct dra7xx_pcie_of_data *data;
enum dw_pcie_device_mode mode;
+ u32 b1co_mode_sel_mask;
match = of_match_device(of_match_ptr(of_dra7xx_pcie_match), dev);
if (!match)
@@ -610,6 +697,7 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev)
data = (struct dra7xx_pcie_of_data *)match->data;
mode = (enum dw_pcie_device_mode)data->mode;
+ b1co_mode_sel_mask = data->b1co_mode_sel_mask;
dra7xx = devm_kzalloc(dev, sizeof(*dra7xx), GFP_KERNEL);
if (!dra7xx)
@@ -665,6 +753,12 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev)
dra7xx->pci = pci;
dra7xx->phy_count = phy_count;
+ if (phy_count == 2) {
+ ret = dra7xx_pcie_configure_two_lane(dev, b1co_mode_sel_mask);
+ if (ret < 0)
+ dra7xx->phy_count = 1; /* Fallback to x1 lane mode */
+ }
+
ret = dra7xx_pcie_enable_phy(dra7xx);
if (ret) {
dev_err(dev, "failed to enable phy\n");
diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c
index 80f843030e36..3d627f94a166 100644
--- a/drivers/pci/controller/dwc/pci-imx6.c
+++ b/drivers/pci/controller/dwc/pci-imx6.c
@@ -8,6 +8,7 @@
* Author: Sean Cross <xobs@kosagi.com>
*/
+#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/gpio.h>
@@ -18,6 +19,7 @@
#include <linux/module.h>
#include <linux/of_gpio.h>
#include <linux/of_device.h>
+#include <linux/of_address.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
@@ -32,6 +34,12 @@
#include "pcie-designware.h"
+#define IMX8MQ_GPR_PCIE_REF_USE_PAD BIT(9)
+#define IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE_EN BIT(10)
+#define IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE BIT(11)
+#define IMX8MQ_GPR12_PCIE2_CTRL_DEVICE_TYPE GENMASK(11, 8)
+#define IMX8MQ_PCIE2_BASE_ADDR 0x33c00000
+
#define to_imx6_pcie(x) dev_get_drvdata((x)->dev)
enum imx6_pcie_variants {
@@ -39,6 +47,15 @@ enum imx6_pcie_variants {
IMX6SX,
IMX6QP,
IMX7D,
+ IMX8MQ,
+};
+
+#define IMX6_PCIE_FLAG_IMX6_PHY BIT(0)
+#define IMX6_PCIE_FLAG_IMX6_SPEED_CHANGE BIT(1)
+
+struct imx6_pcie_drvdata {
+ enum imx6_pcie_variants variant;
+ u32 flags;
};
struct imx6_pcie {
@@ -49,11 +66,12 @@ struct imx6_pcie {
struct clk *pcie_phy;
struct clk *pcie_inbound_axi;
struct clk *pcie;
+ struct clk *pcie_aux;
struct regmap *iomuxc_gpr;
+ u32 controller_id;
struct reset_control *pciephy_reset;
struct reset_control *apps_reset;
struct reset_control *turnoff_reset;
- enum imx6_pcie_variants variant;
u32 tx_deemph_gen1;
u32 tx_deemph_gen2_3p5db;
u32 tx_deemph_gen2_6db;
@@ -61,11 +79,13 @@ struct imx6_pcie {
u32 tx_swing_low;
int link_gen;
struct regulator *vpcie;
+ void __iomem *phy_base;
/* power domain for pcie */
struct device *pd_pcie;
/* power domain for pcie phy */
struct device *pd_pcie_phy;
+ const struct imx6_pcie_drvdata *drvdata;
};
/* Parameters for the waiting for PCIe PHY PLL to lock on i.MX7 */
@@ -101,7 +121,6 @@ struct imx6_pcie {
#define PCIE_PHY_STAT_ACK_LOC 16
#define PCIE_LINK_WIDTH_SPEED_CONTROL 0x80C
-#define PORT_LOGIC_SPEED_CHANGE (0x1 << 17)
/* PHY registers (not memory-mapped) */
#define PCIE_PHY_ATEOVRD 0x10
@@ -117,6 +136,23 @@ struct imx6_pcie {
#define PCIE_PHY_RX_ASIC_OUT 0x100D
#define PCIE_PHY_RX_ASIC_OUT_VALID (1 << 0)
+/* iMX7 PCIe PHY registers */
+#define PCIE_PHY_CMN_REG4 0x14
+/* These are probably the bits that *aren't* DCC_FB_EN */
+#define PCIE_PHY_CMN_REG4_DCC_FB_EN 0x29
+
+#define PCIE_PHY_CMN_REG15 0x54
+#define PCIE_PHY_CMN_REG15_DLY_4 BIT(2)
+#define PCIE_PHY_CMN_REG15_PLL_PD BIT(5)
+#define PCIE_PHY_CMN_REG15_OVRD_PLL_PD BIT(7)
+
+#define PCIE_PHY_CMN_REG24 0x90
+#define PCIE_PHY_CMN_REG24_RX_EQ BIT(6)
+#define PCIE_PHY_CMN_REG24_RX_EQ_SEL BIT(3)
+
+#define PCIE_PHY_CMN_REG26 0x98
+#define PCIE_PHY_CMN_REG26_ATT_MODE 0xBC
+
#define PHY_RX_OVRD_IN_LO 0x1005
#define PHY_RX_OVRD_IN_LO_RX_DATA_EN (1 << 5)
#define PHY_RX_OVRD_IN_LO_RX_PLL_EN (1 << 3)
@@ -251,6 +287,9 @@ static void imx6_pcie_reset_phy(struct imx6_pcie *imx6_pcie)
{
u32 tmp;
+ if (!(imx6_pcie->drvdata->flags & IMX6_PCIE_FLAG_IMX6_PHY))
+ return;
+
pcie_phy_read(imx6_pcie, PHY_RX_OVRD_IN_LO, &tmp);
tmp |= (PHY_RX_OVRD_IN_LO_RX_DATA_EN |
PHY_RX_OVRD_IN_LO_RX_PLL_EN);
@@ -264,6 +303,7 @@ static void imx6_pcie_reset_phy(struct imx6_pcie *imx6_pcie)
pcie_phy_write(imx6_pcie, PHY_RX_OVRD_IN_LO, tmp);
}
+#ifdef CONFIG_ARM
/* Added for PCI abort handling */
static int imx6q_pcie_abort_handler(unsigned long addr,
unsigned int fsr, struct pt_regs *regs)
@@ -297,6 +337,7 @@ static int imx6q_pcie_abort_handler(unsigned long addr,
return 1;
}
+#endif
static int imx6_pcie_attach_pd(struct device *dev)
{
@@ -342,8 +383,9 @@ static void imx6_pcie_assert_core_reset(struct imx6_pcie *imx6_pcie)
{
struct device *dev = imx6_pcie->pci->dev;
- switch (imx6_pcie->variant) {
+ switch (imx6_pcie->drvdata->variant) {
case IMX7D:
+ case IMX8MQ:
reset_control_assert(imx6_pcie->pciephy_reset);
reset_control_assert(imx6_pcie->apps_reset);
break;
@@ -378,13 +420,20 @@ static void imx6_pcie_assert_core_reset(struct imx6_pcie *imx6_pcie)
}
}
+static unsigned int imx6_pcie_grp_offset(const struct imx6_pcie *imx6_pcie)
+{
+ WARN_ON(imx6_pcie->drvdata->variant != IMX8MQ);
+ return imx6_pcie->controller_id == 1 ? IOMUXC_GPR16 : IOMUXC_GPR14;
+}
+
static int imx6_pcie_enable_ref_clk(struct imx6_pcie *imx6_pcie)
{
struct dw_pcie *pci = imx6_pcie->pci;
struct device *dev = pci->dev;
+ unsigned int offset;
int ret = 0;
- switch (imx6_pcie->variant) {
+ switch (imx6_pcie->drvdata->variant) {
case IMX6SX:
ret = clk_prepare_enable(imx6_pcie->pcie_inbound_axi);
if (ret) {
@@ -412,6 +461,25 @@ static int imx6_pcie_enable_ref_clk(struct imx6_pcie *imx6_pcie)
break;
case IMX7D:
break;
+ case IMX8MQ:
+ ret = clk_prepare_enable(imx6_pcie->pcie_aux);
+ if (ret) {
+ dev_err(dev, "unable to enable pcie_aux clock\n");
+ break;
+ }
+
+ offset = imx6_pcie_grp_offset(imx6_pcie);
+ /*
+ * Set the over ride low and enabled
+ * make sure that REF_CLK is turned on.
+ */
+ regmap_update_bits(imx6_pcie->iomuxc_gpr, offset,
+ IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE,
+ 0);
+ regmap_update_bits(imx6_pcie->iomuxc_gpr, offset,
+ IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE_EN,
+ IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE_EN);
+ break;
}
return ret;
@@ -487,9 +555,32 @@ static void imx6_pcie_deassert_core_reset(struct imx6_pcie *imx6_pcie)
!imx6_pcie->gpio_active_high);
}
- switch (imx6_pcie->variant) {
+ switch (imx6_pcie->drvdata->variant) {
+ case IMX8MQ:
+ reset_control_deassert(imx6_pcie->pciephy_reset);
+ break;
case IMX7D:
reset_control_deassert(imx6_pcie->pciephy_reset);
+
+ /* Workaround for ERR010728, failure of PCI-e PLL VCO to
+ * oscillate, especially when cold. This turns off "Duty-cycle
+ * Corrector" and other mysterious undocumented things.
+ */
+ if (likely(imx6_pcie->phy_base)) {
+ /* De-assert DCC_FB_EN */
+ writel(PCIE_PHY_CMN_REG4_DCC_FB_EN,
+ imx6_pcie->phy_base + PCIE_PHY_CMN_REG4);
+ /* Assert RX_EQS and RX_EQS_SEL */
+ writel(PCIE_PHY_CMN_REG24_RX_EQ_SEL
+ | PCIE_PHY_CMN_REG24_RX_EQ,
+ imx6_pcie->phy_base + PCIE_PHY_CMN_REG24);
+ /* Assert ATT_MODE */
+ writel(PCIE_PHY_CMN_REG26_ATT_MODE,
+ imx6_pcie->phy_base + PCIE_PHY_CMN_REG26);
+ } else {
+ dev_warn(dev, "Unable to apply ERR010728 workaround. DT missing fsl,imx7d-pcie-phy phandle ?\n");
+ }
+
imx7d_pcie_wait_for_phy_pll_lock(imx6_pcie);
break;
case IMX6SX:
@@ -523,9 +614,37 @@ err_pcie_phy:
}
}
+static void imx6_pcie_configure_type(struct imx6_pcie *imx6_pcie)
+{
+ unsigned int mask, val;
+
+ if (imx6_pcie->drvdata->variant == IMX8MQ &&
+ imx6_pcie->controller_id == 1) {
+ mask = IMX8MQ_GPR12_PCIE2_CTRL_DEVICE_TYPE;
+ val = FIELD_PREP(IMX8MQ_GPR12_PCIE2_CTRL_DEVICE_TYPE,
+ PCI_EXP_TYPE_ROOT_PORT);
+ } else {
+ mask = IMX6Q_GPR12_DEVICE_TYPE;
+ val = FIELD_PREP(IMX6Q_GPR12_DEVICE_TYPE,
+ PCI_EXP_TYPE_ROOT_PORT);
+ }
+
+ regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, mask, val);
+}
+
static void imx6_pcie_init_phy(struct imx6_pcie *imx6_pcie)
{
- switch (imx6_pcie->variant) {
+ switch (imx6_pcie->drvdata->variant) {
+ case IMX8MQ:
+ /*
+ * TODO: Currently this code assumes external
+ * oscillator is being used
+ */
+ regmap_update_bits(imx6_pcie->iomuxc_gpr,
+ imx6_pcie_grp_offset(imx6_pcie),
+ IMX8MQ_GPR_PCIE_REF_USE_PAD,
+ IMX8MQ_GPR_PCIE_REF_USE_PAD);
+ break;
case IMX7D:
regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
IMX7D_GPR12_PCIE_PHY_REFCLK_SEL, 0);
@@ -561,8 +680,7 @@ static void imx6_pcie_init_phy(struct imx6_pcie *imx6_pcie)
break;
}
- regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
- IMX6Q_GPR12_DEVICE_TYPE, PCI_EXP_TYPE_ROOT_PORT << 12);
+ imx6_pcie_configure_type(imx6_pcie);
}
static int imx6_setup_phy_mpll(struct imx6_pcie *imx6_pcie)
@@ -571,6 +689,9 @@ static int imx6_setup_phy_mpll(struct imx6_pcie *imx6_pcie)
int mult, div;
u32 val;
+ if (!(imx6_pcie->drvdata->flags & IMX6_PCIE_FLAG_IMX6_PHY))
+ return 0;
+
switch (phy_rate) {
case 125000000:
/*
@@ -647,7 +768,7 @@ static void imx6_pcie_ltssm_enable(struct device *dev)
{
struct imx6_pcie *imx6_pcie = dev_get_drvdata(dev);
- switch (imx6_pcie->variant) {
+ switch (imx6_pcie->drvdata->variant) {
case IMX6Q:
case IMX6SX:
case IMX6QP:
@@ -656,6 +777,7 @@ static void imx6_pcie_ltssm_enable(struct device *dev)
IMX6Q_GPR12_PCIE_CTL_2);
break;
case IMX7D:
+ case IMX8MQ:
reset_control_deassert(imx6_pcie->apps_reset);
break;
}
@@ -700,7 +822,8 @@ static int imx6_pcie_establish_link(struct imx6_pcie *imx6_pcie)
tmp |= PORT_LOGIC_SPEED_CHANGE;
dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, tmp);
- if (imx6_pcie->variant != IMX7D) {
+ if (imx6_pcie->drvdata->flags &
+ IMX6_PCIE_FLAG_IMX6_SPEED_CHANGE) {
/*
* On i.MX7, DIRECT_SPEED_CHANGE behaves differently
* from i.MX6 family when no link speed transition
@@ -797,7 +920,7 @@ static void imx6_pcie_ltssm_disable(struct device *dev)
{
struct imx6_pcie *imx6_pcie = dev_get_drvdata(dev);
- switch (imx6_pcie->variant) {
+ switch (imx6_pcie->drvdata->variant) {
case IMX6SX:
case IMX6QP:
regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
@@ -823,7 +946,7 @@ static void imx6_pcie_pm_turnoff(struct imx6_pcie *imx6_pcie)
}
/* Others poke directly at IOMUXC registers */
- switch (imx6_pcie->variant) {
+ switch (imx6_pcie->drvdata->variant) {
case IMX6SX:
regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
IMX6SX_GPR12_PCIE_PM_TURN_OFF,
@@ -853,7 +976,7 @@ static void imx6_pcie_clk_disable(struct imx6_pcie *imx6_pcie)
clk_disable_unprepare(imx6_pcie->pcie_phy);
clk_disable_unprepare(imx6_pcie->pcie_bus);
- switch (imx6_pcie->variant) {
+ switch (imx6_pcie->drvdata->variant) {
case IMX6SX:
clk_disable_unprepare(imx6_pcie->pcie_inbound_axi);
break;
@@ -862,6 +985,9 @@ static void imx6_pcie_clk_disable(struct imx6_pcie *imx6_pcie)
IMX7D_GPR12_PCIE_PHY_REFCLK_SEL,
IMX7D_GPR12_PCIE_PHY_REFCLK_SEL);
break;
+ case IMX8MQ:
+ clk_disable_unprepare(imx6_pcie->pcie_aux);
+ break;
default:
break;
}
@@ -869,8 +995,8 @@ static void imx6_pcie_clk_disable(struct imx6_pcie *imx6_pcie)
static inline bool imx6_pcie_supports_suspend(struct imx6_pcie *imx6_pcie)
{
- return (imx6_pcie->variant == IMX7D ||
- imx6_pcie->variant == IMX6SX);
+ return (imx6_pcie->drvdata->variant == IMX7D ||
+ imx6_pcie->drvdata->variant == IMX6SX);
}
static int imx6_pcie_suspend_noirq(struct device *dev)
@@ -919,6 +1045,7 @@ static int imx6_pcie_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct dw_pcie *pci;
struct imx6_pcie *imx6_pcie;
+ struct device_node *np;
struct resource *dbi_base;
struct device_node *node = dev->of_node;
int ret;
@@ -936,8 +1063,24 @@ static int imx6_pcie_probe(struct platform_device *pdev)
pci->ops = &dw_pcie_ops;
imx6_pcie->pci = pci;
- imx6_pcie->variant =
- (enum imx6_pcie_variants)of_device_get_match_data(dev);
+ imx6_pcie->drvdata = of_device_get_match_data(dev);
+
+ /* Find the PHY if one is defined, only imx7d uses it */
+ np = of_parse_phandle(node, "fsl,imx7d-pcie-phy", 0);
+ if (np) {
+ struct resource res;
+
+ ret = of_address_to_resource(np, 0, &res);
+ if (ret) {
+ dev_err(dev, "Unable to map PCIe PHY\n");
+ return ret;
+ }
+ imx6_pcie->phy_base = devm_ioremap_resource(dev, &res);
+ if (IS_ERR(imx6_pcie->phy_base)) {
+ dev_err(dev, "Unable to map PCIe PHY\n");
+ return PTR_ERR(imx6_pcie->phy_base);
+ }
+ }
dbi_base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
pci->dbi_base = devm_ioremap_resource(dev, dbi_base);
@@ -981,7 +1124,7 @@ static int imx6_pcie_probe(struct platform_device *pdev)
return PTR_ERR(imx6_pcie->pcie);
}
- switch (imx6_pcie->variant) {
+ switch (imx6_pcie->drvdata->variant) {
case IMX6SX:
imx6_pcie->pcie_inbound_axi = devm_clk_get(dev,
"pcie_inbound_axi");
@@ -990,7 +1133,17 @@ static int imx6_pcie_probe(struct platform_device *pdev)
return PTR_ERR(imx6_pcie->pcie_inbound_axi);
}
break;
+ case IMX8MQ:
+ imx6_pcie->pcie_aux = devm_clk_get(dev, "pcie_aux");
+ if (IS_ERR(imx6_pcie->pcie_aux)) {
+ dev_err(dev, "pcie_aux clock source missing or invalid\n");
+ return PTR_ERR(imx6_pcie->pcie_aux);
+ }
+ /* fall through */
case IMX7D:
+ if (dbi_base->start == IMX8MQ_PCIE2_BASE_ADDR)
+ imx6_pcie->controller_id = 1;
+
imx6_pcie->pciephy_reset = devm_reset_control_get_exclusive(dev,
"pciephy");
if (IS_ERR(imx6_pcie->pciephy_reset)) {
@@ -1087,11 +1240,36 @@ static void imx6_pcie_shutdown(struct platform_device *pdev)
imx6_pcie_assert_core_reset(imx6_pcie);
}
+static const struct imx6_pcie_drvdata drvdata[] = {
+ [IMX6Q] = {
+ .variant = IMX6Q,
+ .flags = IMX6_PCIE_FLAG_IMX6_PHY |
+ IMX6_PCIE_FLAG_IMX6_SPEED_CHANGE,
+ },
+ [IMX6SX] = {
+ .variant = IMX6SX,
+ .flags = IMX6_PCIE_FLAG_IMX6_PHY |
+ IMX6_PCIE_FLAG_IMX6_SPEED_CHANGE,
+ },
+ [IMX6QP] = {
+ .variant = IMX6QP,
+ .flags = IMX6_PCIE_FLAG_IMX6_PHY |
+ IMX6_PCIE_FLAG_IMX6_SPEED_CHANGE,
+ },
+ [IMX7D] = {
+ .variant = IMX7D,
+ },
+ [IMX8MQ] = {
+ .variant = IMX8MQ,
+ },
+};
+
static const struct of_device_id imx6_pcie_of_match[] = {
- { .compatible = "fsl,imx6q-pcie", .data = (void *)IMX6Q, },
- { .compatible = "fsl,imx6sx-pcie", .data = (void *)IMX6SX, },
- { .compatible = "fsl,imx6qp-pcie", .data = (void *)IMX6QP, },
- { .compatible = "fsl,imx7d-pcie", .data = (void *)IMX7D, },
+ { .compatible = "fsl,imx6q-pcie", .data = &drvdata[IMX6Q], },
+ { .compatible = "fsl,imx6sx-pcie", .data = &drvdata[IMX6SX], },
+ { .compatible = "fsl,imx6qp-pcie", .data = &drvdata[IMX6QP], },
+ { .compatible = "fsl,imx7d-pcie", .data = &drvdata[IMX7D], },
+ { .compatible = "fsl,imx8mq-pcie", .data = &drvdata[IMX8MQ], } ,
{},
};
@@ -1108,6 +1286,7 @@ static struct platform_driver imx6_pcie_driver = {
static int __init imx6_pcie_init(void)
{
+#ifdef CONFIG_ARM
/*
* Since probe() can be deferred we need to make sure that
* hook_fault_code is not called after __init memory is freed
@@ -1117,6 +1296,7 @@ static int __init imx6_pcie_init(void)
*/
hook_fault_code(8, imx6q_pcie_abort_handler, SIGBUS, 0,
"external abort on non-linefetch");
+#endif
return platform_driver_register(&imx6_pcie_driver);
}
diff --git a/drivers/pci/controller/dwc/pci-layerscape-ep.c b/drivers/pci/controller/dwc/pci-layerscape-ep.c
new file mode 100644
index 000000000000..a42c9c3ae1cc
--- /dev/null
+++ b/drivers/pci/controller/dwc/pci-layerscape-ep.c
@@ -0,0 +1,156 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PCIe controller EP driver for Freescale Layerscape SoCs
+ *
+ * Copyright (C) 2018 NXP Semiconductor.
+ *
+ * Author: Xiaowei Bao <xiaowei.bao@nxp.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/of_pci.h>
+#include <linux/of_platform.h>
+#include <linux/of_address.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/resource.h>
+
+#include "pcie-designware.h"
+
+#define PCIE_DBI2_OFFSET 0x1000 /* DBI2 base address*/
+
+struct ls_pcie_ep {
+ struct dw_pcie *pci;
+};
+
+#define to_ls_pcie_ep(x) dev_get_drvdata((x)->dev)
+
+static int ls_pcie_establish_link(struct dw_pcie *pci)
+{
+ return 0;
+}
+
+static const struct dw_pcie_ops ls_pcie_ep_ops = {
+ .start_link = ls_pcie_establish_link,
+};
+
+static const struct of_device_id ls_pcie_ep_of_match[] = {
+ { .compatible = "fsl,ls-pcie-ep",},
+ { },
+};
+
+static const struct pci_epc_features ls_pcie_epc_features = {
+ .linkup_notifier = false,
+ .msi_capable = true,
+ .msix_capable = false,
+};
+
+static const struct pci_epc_features*
+ls_pcie_ep_get_features(struct dw_pcie_ep *ep)
+{
+ return &ls_pcie_epc_features;
+}
+
+static void ls_pcie_ep_init(struct dw_pcie_ep *ep)
+{
+ struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+ enum pci_barno bar;
+
+ for (bar = BAR_0; bar <= BAR_5; bar++)
+ dw_pcie_ep_reset_bar(pci, bar);
+}
+
+static int ls_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
+ enum pci_epc_irq_type type, u16 interrupt_num)
+{
+ struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+
+ switch (type) {
+ case PCI_EPC_IRQ_LEGACY:
+ return dw_pcie_ep_raise_legacy_irq(ep, func_no);
+ case PCI_EPC_IRQ_MSI:
+ return dw_pcie_ep_raise_msi_irq(ep, func_no, interrupt_num);
+ case PCI_EPC_IRQ_MSIX:
+ return dw_pcie_ep_raise_msix_irq(ep, func_no, interrupt_num);
+ default:
+ dev_err(pci->dev, "UNKNOWN IRQ type\n");
+ return -EINVAL;
+ }
+}
+
+static struct dw_pcie_ep_ops pcie_ep_ops = {
+ .ep_init = ls_pcie_ep_init,
+ .raise_irq = ls_pcie_ep_raise_irq,
+ .get_features = ls_pcie_ep_get_features,
+};
+
+static int __init ls_add_pcie_ep(struct ls_pcie_ep *pcie,
+ struct platform_device *pdev)
+{
+ struct dw_pcie *pci = pcie->pci;
+ struct device *dev = pci->dev;
+ struct dw_pcie_ep *ep;
+ struct resource *res;
+ int ret;
+
+ ep = &pci->ep;
+ ep->ops = &pcie_ep_ops;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "addr_space");
+ if (!res)
+ return -EINVAL;
+
+ ep->phys_base = res->start;
+ ep->addr_size = resource_size(res);
+
+ ret = dw_pcie_ep_init(ep);
+ if (ret) {
+ dev_err(dev, "failed to initialize endpoint\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int __init ls_pcie_ep_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct dw_pcie *pci;
+ struct ls_pcie_ep *pcie;
+ struct resource *dbi_base;
+ int ret;
+
+ pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
+ if (!pcie)
+ return -ENOMEM;
+
+ pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL);
+ if (!pci)
+ return -ENOMEM;
+
+ dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
+ pci->dbi_base = devm_pci_remap_cfg_resource(dev, dbi_base);
+ if (IS_ERR(pci->dbi_base))
+ return PTR_ERR(pci->dbi_base);
+
+ pci->dbi_base2 = pci->dbi_base + PCIE_DBI2_OFFSET;
+ pci->dev = dev;
+ pci->ops = &ls_pcie_ep_ops;
+ pcie->pci = pci;
+
+ platform_set_drvdata(pdev, pcie);
+
+ ret = ls_add_pcie_ep(pcie, pdev);
+
+ return ret;
+}
+
+static struct platform_driver ls_pcie_ep_driver = {
+ .driver = {
+ .name = "layerscape-pcie-ep",
+ .of_match_table = ls_pcie_ep_of_match,
+ .suppress_bind_attrs = true,
+ },
+};
+builtin_platform_driver_probe(ls_pcie_ep_driver, ls_pcie_ep_probe);
diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c
index a543c45c7224..24f5a775ad34 100644
--- a/drivers/pci/controller/dwc/pcie-designware-ep.c
+++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
@@ -355,6 +355,17 @@ static int dw_pcie_ep_start(struct pci_epc *epc)
return pci->ops->start_link(pci);
}
+static const struct pci_epc_features*
+dw_pcie_ep_get_features(struct pci_epc *epc, u8 func_no)
+{
+ struct dw_pcie_ep *ep = epc_get_drvdata(epc);
+
+ if (!ep->ops->get_features)
+ return NULL;
+
+ return ep->ops->get_features(ep);
+}
+
static const struct pci_epc_ops epc_ops = {
.write_header = dw_pcie_ep_write_header,
.set_bar = dw_pcie_ep_set_bar,
@@ -368,6 +379,7 @@ static const struct pci_epc_ops epc_ops = {
.raise_irq = dw_pcie_ep_raise_irq,
.start = dw_pcie_ep_start,
.stop = dw_pcie_ep_stop,
+ .get_features = dw_pcie_ep_get_features,
};
int dw_pcie_ep_raise_legacy_irq(struct dw_pcie_ep *ep, u8 func_no)
@@ -465,8 +477,10 @@ int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no,
iounmap(msix_tbl);
- if (vec_ctrl & PCI_MSIX_ENTRY_CTRL_MASKBIT)
+ if (vec_ctrl & PCI_MSIX_ENTRY_CTRL_MASKBIT) {
+ dev_dbg(pci->dev, "MSI-X entry ctrl set\n");
return -EPERM;
+ }
ret = dw_pcie_ep_map_addr(epc, func_no, ep->msi_mem_phys, msg_addr,
epc->mem->page_size);
diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c
index 721d60a5d9e4..25087d3c9a82 100644
--- a/drivers/pci/controller/dwc/pcie-designware-host.c
+++ b/drivers/pci/controller/dwc/pcie-designware-host.c
@@ -120,9 +120,9 @@ static void dw_chained_msi_isr(struct irq_desc *desc)
chained_irq_exit(chip, desc);
}
-static void dw_pci_setup_msi_msg(struct irq_data *data, struct msi_msg *msg)
+static void dw_pci_setup_msi_msg(struct irq_data *d, struct msi_msg *msg)
{
- struct pcie_port *pp = irq_data_get_irq_chip_data(data);
+ struct pcie_port *pp = irq_data_get_irq_chip_data(d);
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
u64 msi_target;
@@ -135,61 +135,61 @@ static void dw_pci_setup_msi_msg(struct irq_data *data, struct msi_msg *msg)
msg->address_hi = upper_32_bits(msi_target);
if (pp->ops->get_msi_data)
- msg->data = pp->ops->get_msi_data(pp, data->hwirq);
+ msg->data = pp->ops->get_msi_data(pp, d->hwirq);
else
- msg->data = data->hwirq;
+ msg->data = d->hwirq;
dev_dbg(pci->dev, "msi#%d address_hi %#x address_lo %#x\n",
- (int)data->hwirq, msg->address_hi, msg->address_lo);
+ (int)d->hwirq, msg->address_hi, msg->address_lo);
}
-static int dw_pci_msi_set_affinity(struct irq_data *irq_data,
+static int dw_pci_msi_set_affinity(struct irq_data *d,
const struct cpumask *mask, bool force)
{
return -EINVAL;
}
-static void dw_pci_bottom_mask(struct irq_data *data)
+static void dw_pci_bottom_mask(struct irq_data *d)
{
- struct pcie_port *pp = irq_data_get_irq_chip_data(data);
+ struct pcie_port *pp = irq_data_get_irq_chip_data(d);
unsigned int res, bit, ctrl;
unsigned long flags;
raw_spin_lock_irqsave(&pp->lock, flags);
if (pp->ops->msi_clear_irq) {
- pp->ops->msi_clear_irq(pp, data->hwirq);
+ pp->ops->msi_clear_irq(pp, d->hwirq);
} else {
- ctrl = data->hwirq / MAX_MSI_IRQS_PER_CTRL;
+ ctrl = d->hwirq / MAX_MSI_IRQS_PER_CTRL;
res = ctrl * MSI_REG_CTRL_BLOCK_SIZE;
- bit = data->hwirq % MAX_MSI_IRQS_PER_CTRL;
+ bit = d->hwirq % MAX_MSI_IRQS_PER_CTRL;
- pp->irq_status[ctrl] &= ~(1 << bit);
+ pp->irq_mask[ctrl] |= BIT(bit);
dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_MASK + res, 4,
- ~pp->irq_status[ctrl]);
+ pp->irq_mask[ctrl]);
}
raw_spin_unlock_irqrestore(&pp->lock, flags);
}
-static void dw_pci_bottom_unmask(struct irq_data *data)
+static void dw_pci_bottom_unmask(struct irq_data *d)
{
- struct pcie_port *pp = irq_data_get_irq_chip_data(data);
+ struct pcie_port *pp = irq_data_get_irq_chip_data(d);
unsigned int res, bit, ctrl;
unsigned long flags;
raw_spin_lock_irqsave(&pp->lock, flags);
if (pp->ops->msi_set_irq) {
- pp->ops->msi_set_irq(pp, data->hwirq);
+ pp->ops->msi_set_irq(pp, d->hwirq);
} else {
- ctrl = data->hwirq / MAX_MSI_IRQS_PER_CTRL;
+ ctrl = d->hwirq / MAX_MSI_IRQS_PER_CTRL;
res = ctrl * MSI_REG_CTRL_BLOCK_SIZE;
- bit = data->hwirq % MAX_MSI_IRQS_PER_CTRL;
+ bit = d->hwirq % MAX_MSI_IRQS_PER_CTRL;
- pp->irq_status[ctrl] |= 1 << bit;
+ pp->irq_mask[ctrl] &= ~BIT(bit);
dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_MASK + res, 4,
- ~pp->irq_status[ctrl]);
+ pp->irq_mask[ctrl]);
}
raw_spin_unlock_irqrestore(&pp->lock, flags);
@@ -207,7 +207,7 @@ static void dw_pci_bottom_ack(struct irq_data *d)
raw_spin_lock_irqsave(&pp->lock, flags);
- dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_STATUS + res, 4, 1 << bit);
+ dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_STATUS + res, 4, BIT(bit));
if (pp->ops->msi_irq_ack)
pp->ops->msi_irq_ack(d->hwirq, pp);
@@ -255,13 +255,13 @@ static int dw_pcie_irq_domain_alloc(struct irq_domain *domain,
static void dw_pcie_irq_domain_free(struct irq_domain *domain,
unsigned int virq, unsigned int nr_irqs)
{
- struct irq_data *data = irq_domain_get_irq_data(domain, virq);
- struct pcie_port *pp = irq_data_get_irq_chip_data(data);
+ struct irq_data *d = irq_domain_get_irq_data(domain, virq);
+ struct pcie_port *pp = irq_data_get_irq_chip_data(d);
unsigned long flags;
raw_spin_lock_irqsave(&pp->lock, flags);
- bitmap_release_region(pp->msi_irq_in_use, data->hwirq,
+ bitmap_release_region(pp->msi_irq_in_use, d->hwirq,
order_base_2(nr_irqs));
raw_spin_unlock_irqrestore(&pp->lock, flags);
@@ -439,7 +439,7 @@ int dw_pcie_host_init(struct pcie_port *pp)
if (ret)
pci->num_viewport = 2;
- if (IS_ENABLED(CONFIG_PCI_MSI)) {
+ if (IS_ENABLED(CONFIG_PCI_MSI) && pci_msi_enabled()) {
/*
* If a specific SoC driver needs to change the
* default number of vectors, it needs to implement
@@ -512,8 +512,9 @@ error:
return ret;
}
-static int dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus,
- u32 devfn, int where, int size, u32 *val)
+static int dw_pcie_access_other_conf(struct pcie_port *pp, struct pci_bus *bus,
+ u32 devfn, int where, int size, u32 *val,
+ bool write)
{
int ret, type;
u32 busdev, cfg_size;
@@ -521,9 +522,6 @@ static int dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus,
void __iomem *va_cfg_base;
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
- if (pp->ops->rd_other_conf)
- return pp->ops->rd_other_conf(pp, bus, devfn, where, size, val);
-
busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(PCI_SLOT(devfn)) |
PCIE_ATU_FUNC(PCI_FUNC(devfn));
@@ -542,7 +540,11 @@ static int dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus,
dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX1,
type, cpu_addr,
busdev, cfg_size);
- ret = dw_pcie_read(va_cfg_base + where, size, val);
+ if (write)
+ ret = dw_pcie_write(va_cfg_base + where, size, *val);
+ else
+ ret = dw_pcie_read(va_cfg_base + where, size, val);
+
if (pci->num_viewport <= 2)
dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX1,
PCIE_ATU_TYPE_IO, pp->io_base,
@@ -551,43 +553,26 @@ static int dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus,
return ret;
}
+static int dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus,
+ u32 devfn, int where, int size, u32 *val)
+{
+ if (pp->ops->rd_other_conf)
+ return pp->ops->rd_other_conf(pp, bus, devfn, where,
+ size, val);
+
+ return dw_pcie_access_other_conf(pp, bus, devfn, where, size, val,
+ false);
+}
+
static int dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus,
u32 devfn, int where, int size, u32 val)
{
- int ret, type;
- u32 busdev, cfg_size;
- u64 cpu_addr;
- void __iomem *va_cfg_base;
- struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
-
if (pp->ops->wr_other_conf)
- return pp->ops->wr_other_conf(pp, bus, devfn, where, size, val);
-
- busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(PCI_SLOT(devfn)) |
- PCIE_ATU_FUNC(PCI_FUNC(devfn));
+ return pp->ops->wr_other_conf(pp, bus, devfn, where,
+ size, val);
- if (bus->parent->number == pp->root_bus_nr) {
- type = PCIE_ATU_TYPE_CFG0;
- cpu_addr = pp->cfg0_base;
- cfg_size = pp->cfg0_size;
- va_cfg_base = pp->va_cfg0_base;
- } else {
- type = PCIE_ATU_TYPE_CFG1;
- cpu_addr = pp->cfg1_base;
- cfg_size = pp->cfg1_size;
- va_cfg_base = pp->va_cfg1_base;
- }
-
- dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX1,
- type, cpu_addr,
- busdev, cfg_size);
- ret = dw_pcie_write(va_cfg_base + where, size, val);
- if (pci->num_viewport <= 2)
- dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX1,
- PCIE_ATU_TYPE_IO, pp->io_base,
- pp->io_bus_addr, pp->io_size);
-
- return ret;
+ return dw_pcie_access_other_conf(pp, bus, devfn, where, size, &val,
+ true);
}
static int dw_pcie_valid_device(struct pcie_port *pp, struct pci_bus *bus,
@@ -665,13 +650,13 @@ void dw_pcie_setup_rc(struct pcie_port *pp)
/* Initialize IRQ Status array */
for (ctrl = 0; ctrl < num_ctrls; ctrl++) {
+ pp->irq_mask[ctrl] = ~0;
dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_MASK +
(ctrl * MSI_REG_CTRL_BLOCK_SIZE),
- 4, ~0);
+ 4, pp->irq_mask[ctrl]);
dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE +
(ctrl * MSI_REG_CTRL_BLOCK_SIZE),
4, ~0);
- pp->irq_status[ctrl] = 0;
}
/* Setup RC BARs */
diff --git a/drivers/pci/controller/dwc/pcie-designware-plat.c b/drivers/pci/controller/dwc/pcie-designware-plat.c
index c12bf794d69c..932dbd0b34b6 100644
--- a/drivers/pci/controller/dwc/pcie-designware-plat.c
+++ b/drivers/pci/controller/dwc/pcie-designware-plat.c
@@ -13,11 +13,9 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/of_device.h>
-#include <linux/of_gpio.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/resource.h>
-#include <linux/signal.h>
#include <linux/types.h>
#include <linux/regmap.h>
@@ -70,14 +68,10 @@ static const struct dw_pcie_ops dw_pcie_ops = {
static void dw_plat_pcie_ep_init(struct dw_pcie_ep *ep)
{
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
- struct pci_epc *epc = ep->epc;
enum pci_barno bar;
for (bar = BAR_0; bar <= BAR_5; bar++)
dw_pcie_ep_reset_bar(pci, bar);
-
- epc->features |= EPC_FEATURE_NO_LINKUP_NOTIFIER;
- epc->features |= EPC_FEATURE_MSIX_AVAILABLE;
}
static int dw_plat_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
@@ -100,9 +94,22 @@ static int dw_plat_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
return 0;
}
+static const struct pci_epc_features dw_plat_pcie_epc_features = {
+ .linkup_notifier = false,
+ .msi_capable = true,
+ .msix_capable = true,
+};
+
+static const struct pci_epc_features*
+dw_plat_pcie_get_features(struct dw_pcie_ep *ep)
+{
+ return &dw_plat_pcie_epc_features;
+}
+
static struct dw_pcie_ep_ops pcie_ep_ops = {
.ep_init = dw_plat_pcie_ep_init,
.raise_irq = dw_plat_pcie_ep_raise_irq,
+ .get_features = dw_plat_pcie_get_features,
};
static int dw_plat_add_pcie_port(struct dw_plat_pcie *dw_plat_pcie,
diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c
index 93ef8c31fb39..31f6331ca46f 100644
--- a/drivers/pci/controller/dwc/pcie-designware.c
+++ b/drivers/pci/controller/dwc/pcie-designware.c
@@ -22,7 +22,7 @@
int dw_pcie_read(void __iomem *addr, int size, u32 *val)
{
- if ((uintptr_t)addr & (size - 1)) {
+ if (!IS_ALIGNED((uintptr_t)addr, size)) {
*val = 0;
return PCIBIOS_BAD_REGISTER_NUMBER;
}
@@ -43,7 +43,7 @@ int dw_pcie_read(void __iomem *addr, int size, u32 *val)
int dw_pcie_write(void __iomem *addr, int size, u32 val)
{
- if ((uintptr_t)addr & (size - 1))
+ if (!IS_ALIGNED((uintptr_t)addr, size))
return PCIBIOS_BAD_REGISTER_NUMBER;
if (size == 4)
@@ -306,7 +306,7 @@ void dw_pcie_disable_atu(struct dw_pcie *pci, int index,
}
dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT, region | index);
- dw_pcie_writel_dbi(pci, PCIE_ATU_CR2, ~PCIE_ATU_ENABLE);
+ dw_pcie_writel_dbi(pci, PCIE_ATU_CR2, (u32)~PCIE_ATU_ENABLE);
}
int dw_pcie_wait_for_link(struct dw_pcie *pci)
diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h
index 9943d8c68335..377f4c0b52da 100644
--- a/drivers/pci/controller/dwc/pcie-designware.h
+++ b/drivers/pci/controller/dwc/pcie-designware.h
@@ -11,6 +11,7 @@
#ifndef _PCIE_DESIGNWARE_H
#define _PCIE_DESIGNWARE_H
+#include <linux/bitfield.h>
#include <linux/dma-mapping.h>
#include <linux/irq.h>
#include <linux/msi.h>
@@ -30,23 +31,25 @@
/* Synopsys-specific PCIe configuration registers */
#define PCIE_PORT_LINK_CONTROL 0x710
-#define PORT_LINK_MODE_MASK (0x3f << 16)
-#define PORT_LINK_MODE_1_LANES (0x1 << 16)
-#define PORT_LINK_MODE_2_LANES (0x3 << 16)
-#define PORT_LINK_MODE_4_LANES (0x7 << 16)
-#define PORT_LINK_MODE_8_LANES (0xf << 16)
+#define PORT_LINK_MODE_MASK GENMASK(21, 16)
+#define PORT_LINK_MODE(n) FIELD_PREP(PORT_LINK_MODE_MASK, n)
+#define PORT_LINK_MODE_1_LANES PORT_LINK_MODE(0x1)
+#define PORT_LINK_MODE_2_LANES PORT_LINK_MODE(0x3)
+#define PORT_LINK_MODE_4_LANES PORT_LINK_MODE(0x7)
+#define PORT_LINK_MODE_8_LANES PORT_LINK_MODE(0xf)
#define PCIE_PORT_DEBUG0 0x728
#define PORT_LOGIC_LTSSM_STATE_MASK 0x1f
#define PORT_LOGIC_LTSSM_STATE_L0 0x11
#define PCIE_LINK_WIDTH_SPEED_CONTROL 0x80C
-#define PORT_LOGIC_SPEED_CHANGE (0x1 << 17)
-#define PORT_LOGIC_LINK_WIDTH_MASK (0x1f << 8)
-#define PORT_LOGIC_LINK_WIDTH_1_LANES (0x1 << 8)
-#define PORT_LOGIC_LINK_WIDTH_2_LANES (0x2 << 8)
-#define PORT_LOGIC_LINK_WIDTH_4_LANES (0x4 << 8)
-#define PORT_LOGIC_LINK_WIDTH_8_LANES (0x8 << 8)
+#define PORT_LOGIC_SPEED_CHANGE BIT(17)
+#define PORT_LOGIC_LINK_WIDTH_MASK GENMASK(12, 8)
+#define PORT_LOGIC_LINK_WIDTH(n) FIELD_PREP(PORT_LOGIC_LINK_WIDTH_MASK, n)
+#define PORT_LOGIC_LINK_WIDTH_1_LANES PORT_LOGIC_LINK_WIDTH(0x1)
+#define PORT_LOGIC_LINK_WIDTH_2_LANES PORT_LOGIC_LINK_WIDTH(0x2)
+#define PORT_LOGIC_LINK_WIDTH_4_LANES PORT_LOGIC_LINK_WIDTH(0x4)
+#define PORT_LOGIC_LINK_WIDTH_8_LANES PORT_LOGIC_LINK_WIDTH(0x8)
#define PCIE_MSI_ADDR_LO 0x820
#define PCIE_MSI_ADDR_HI 0x824
@@ -55,30 +58,30 @@
#define PCIE_MSI_INTR0_STATUS 0x830
#define PCIE_ATU_VIEWPORT 0x900
-#define PCIE_ATU_REGION_INBOUND (0x1 << 31)
-#define PCIE_ATU_REGION_OUTBOUND (0x0 << 31)
-#define PCIE_ATU_REGION_INDEX2 (0x2 << 0)
-#define PCIE_ATU_REGION_INDEX1 (0x1 << 0)
-#define PCIE_ATU_REGION_INDEX0 (0x0 << 0)
+#define PCIE_ATU_REGION_INBOUND BIT(31)
+#define PCIE_ATU_REGION_OUTBOUND 0
+#define PCIE_ATU_REGION_INDEX2 0x2
+#define PCIE_ATU_REGION_INDEX1 0x1
+#define PCIE_ATU_REGION_INDEX0 0x0
#define PCIE_ATU_CR1 0x904
-#define PCIE_ATU_TYPE_MEM (0x0 << 0)
-#define PCIE_ATU_TYPE_IO (0x2 << 0)
-#define PCIE_ATU_TYPE_CFG0 (0x4 << 0)
-#define PCIE_ATU_TYPE_CFG1 (0x5 << 0)
+#define PCIE_ATU_TYPE_MEM 0x0
+#define PCIE_ATU_TYPE_IO 0x2
+#define PCIE_ATU_TYPE_CFG0 0x4
+#define PCIE_ATU_TYPE_CFG1 0x5
#define PCIE_ATU_CR2 0x908
-#define PCIE_ATU_ENABLE (0x1 << 31)
-#define PCIE_ATU_BAR_MODE_ENABLE (0x1 << 30)
+#define PCIE_ATU_ENABLE BIT(31)
+#define PCIE_ATU_BAR_MODE_ENABLE BIT(30)
#define PCIE_ATU_LOWER_BASE 0x90C
#define PCIE_ATU_UPPER_BASE 0x910
#define PCIE_ATU_LIMIT 0x914
#define PCIE_ATU_LOWER_TARGET 0x918
-#define PCIE_ATU_BUS(x) (((x) & 0xff) << 24)
-#define PCIE_ATU_DEV(x) (((x) & 0x1f) << 19)
-#define PCIE_ATU_FUNC(x) (((x) & 0x7) << 16)
+#define PCIE_ATU_BUS(x) FIELD_PREP(GENMASK(31, 24), x)
+#define PCIE_ATU_DEV(x) FIELD_PREP(GENMASK(23, 19), x)
+#define PCIE_ATU_FUNC(x) FIELD_PREP(GENMASK(18, 16), x)
#define PCIE_ATU_UPPER_TARGET 0x91C
#define PCIE_MISC_CONTROL_1_OFF 0x8BC
-#define PCIE_DBI_RO_WR_EN (0x1 << 0)
+#define PCIE_DBI_RO_WR_EN BIT(0)
/*
* iATU Unroll-specific register definitions
@@ -105,7 +108,7 @@
((region) << 9)
#define PCIE_GET_ATU_INB_UNR_REG_OFFSET(region) \
- (((region) << 9) | (0x1 << 8))
+ (((region) << 9) | BIT(8))
#define MAX_MSI_IRQS 256
#define MAX_MSI_IRQS_PER_CTRL 32
@@ -177,7 +180,7 @@ struct pcie_port {
struct irq_domain *msi_domain;
dma_addr_t msi_data;
u32 num_vectors;
- u32 irq_status[MAX_MSI_CTRLS];
+ u32 irq_mask[MAX_MSI_CTRLS];
raw_spinlock_t lock;
DECLARE_BITMAP(msi_irq_in_use, MAX_MSI_IRQS);
};
@@ -192,6 +195,7 @@ struct dw_pcie_ep_ops {
void (*ep_init)(struct dw_pcie_ep *ep);
int (*raise_irq)(struct dw_pcie_ep *ep, u8 func_no,
enum pci_epc_irq_type type, u16 interrupt_num);
+ const struct pci_epc_features* (*get_features)(struct dw_pcie_ep *ep);
};
struct dw_pcie_ep {
diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c
index d185ea5fe996..a7f703556790 100644
--- a/drivers/pci/controller/dwc/pcie-qcom.c
+++ b/drivers/pci/controller/dwc/pcie-qcom.c
@@ -1228,7 +1228,7 @@ static int qcom_pcie_probe(struct platform_device *pdev)
pcie->ops = of_device_get_match_data(dev);
- pcie->reset = devm_gpiod_get_optional(dev, "perst", GPIOD_OUT_LOW);
+ pcie->reset = devm_gpiod_get_optional(dev, "perst", GPIOD_OUT_HIGH);
if (IS_ERR(pcie->reset)) {
ret = PTR_ERR(pcie->reset);
goto err_pm_runtime_put;
diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c
index 750081c1cb48..eb58dfdaba1b 100644
--- a/drivers/pci/controller/pci-aardvark.c
+++ b/drivers/pci/controller/pci-aardvark.c
@@ -466,7 +466,7 @@ advk_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge,
}
}
-struct pci_bridge_emul_ops advk_pci_bridge_emul_ops = {
+static struct pci_bridge_emul_ops advk_pci_bridge_emul_ops = {
.read_pcie = advk_pci_bridge_emul_pcie_conf_read,
.write_pcie = advk_pci_bridge_emul_pcie_conf_write,
};
@@ -499,7 +499,7 @@ static void advk_sw_pci_bridge_init(struct advk_pcie *pcie)
bridge->data = pcie;
bridge->ops = &advk_pci_bridge_emul_ops;
- pci_bridge_emul_init(bridge);
+ pci_bridge_emul_init(bridge, 0);
}
diff --git a/drivers/pci/controller/pci-hyperv.c b/drivers/pci/controller/pci-hyperv.c
index 9ba4d12c179c..95441a35eceb 100644
--- a/drivers/pci/controller/pci-hyperv.c
+++ b/drivers/pci/controller/pci-hyperv.c
@@ -391,14 +391,6 @@ struct hv_interrupt_entry {
u32 data;
};
-#define HV_VP_SET_BANK_COUNT_MAX 5 /* current implementation limit */
-
-struct hv_vp_set {
- u64 format; /* 0 (HvGenericSetSparse4k) */
- u64 valid_banks;
- u64 masks[HV_VP_SET_BANK_COUNT_MAX];
-};
-
/*
* flags for hv_device_interrupt_target.flags
*/
@@ -410,7 +402,7 @@ struct hv_device_interrupt_target {
u32 flags;
union {
u64 vp_mask;
- struct hv_vp_set vp_set;
+ struct hv_vpset vp_set;
};
};
@@ -420,7 +412,7 @@ struct retarget_msi_interrupt {
struct hv_interrupt_entry int_entry;
u64 reserved2;
struct hv_device_interrupt_target int_target;
-} __packed;
+} __packed __aligned(8);
/*
* Driver specific state.
@@ -460,12 +452,16 @@ struct hv_pcibus_device {
struct msi_controller msi_chip;
struct irq_domain *irq_domain;
- /* hypercall arg, must not cross page boundary */
- struct retarget_msi_interrupt retarget_msi_interrupt_params;
-
spinlock_t retarget_msi_interrupt_lock;
struct workqueue_struct *wq;
+
+ /* hypercall arg, must not cross page boundary */
+ struct retarget_msi_interrupt retarget_msi_interrupt_params;
+
+ /*
+ * Don't put anything here: retarget_msi_interrupt_params must be last
+ */
};
/*
@@ -910,12 +906,12 @@ static void hv_irq_unmask(struct irq_data *data)
struct retarget_msi_interrupt *params;
struct hv_pcibus_device *hbus;
struct cpumask *dest;
+ cpumask_var_t tmp;
struct pci_bus *pbus;
struct pci_dev *pdev;
unsigned long flags;
u32 var_size = 0;
- int cpu_vmbus;
- int cpu;
+ int cpu, nr_bank;
u64 res;
dest = irq_data_get_effective_affinity_mask(data);
@@ -955,28 +951,27 @@ static void hv_irq_unmask(struct irq_data *data)
*/
params->int_target.flags |=
HV_DEVICE_INTERRUPT_TARGET_PROCESSOR_SET;
- params->int_target.vp_set.valid_banks =
- (1ull << HV_VP_SET_BANK_COUNT_MAX) - 1;
- /*
- * var-sized hypercall, var-size starts after vp_mask (thus
- * vp_set.format does not count, but vp_set.valid_banks does).
- */
- var_size = 1 + HV_VP_SET_BANK_COUNT_MAX;
+ if (!alloc_cpumask_var(&tmp, GFP_ATOMIC)) {
+ res = 1;
+ goto exit_unlock;
+ }
- for_each_cpu_and(cpu, dest, cpu_online_mask) {
- cpu_vmbus = hv_cpu_number_to_vp_number(cpu);
+ cpumask_and(tmp, dest, cpu_online_mask);
+ nr_bank = cpumask_to_vpset(&params->int_target.vp_set, tmp);
+ free_cpumask_var(tmp);
- if (cpu_vmbus >= HV_VP_SET_BANK_COUNT_MAX * 64) {
- dev_err(&hbus->hdev->device,
- "too high CPU %d", cpu_vmbus);
- res = 1;
- goto exit_unlock;
- }
-
- params->int_target.vp_set.masks[cpu_vmbus / 64] |=
- (1ULL << (cpu_vmbus & 63));
+ if (nr_bank <= 0) {
+ res = 1;
+ goto exit_unlock;
}
+
+ /*
+ * var-sized hypercall, var-size starts after vp_mask (thus
+ * vp_set.format does not count, but vp_set.valid_bank_mask
+ * does).
+ */
+ var_size = 1 + nr_bank;
} else {
for_each_cpu_and(cpu, dest, cpu_online_mask) {
params->int_target.vp_mask |=
diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c
index fa0fc46edb0c..d3a0419e42f2 100644
--- a/drivers/pci/controller/pci-mvebu.c
+++ b/drivers/pci/controller/pci-mvebu.c
@@ -583,7 +583,7 @@ static void mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port)
bridge->data = port;
bridge->ops = &mvebu_pci_bridge_emul_ops;
- pci_bridge_emul_init(bridge);
+ pci_bridge_emul_init(bridge, PCI_BRIDGE_EMUL_NO_PREFETCHABLE_BAR);
}
static inline struct mvebu_pcie *sys_to_pcie(struct pci_sys_data *sys)
diff --git a/drivers/pci/controller/pcie-altera.c b/drivers/pci/controller/pcie-altera.c
index 7d05e51205b3..27edcebd1726 100644
--- a/drivers/pci/controller/pcie-altera.c
+++ b/drivers/pci/controller/pcie-altera.c
@@ -11,6 +11,7 @@
#include <linux/irqchip/chained_irq.h>
#include <linux/init.h>
#include <linux/of_address.h>
+#include <linux/of_device.h>
#include <linux/of_irq.h>
#include <linux/of_pci.h>
#include <linux/pci.h>
@@ -37,7 +38,12 @@
#define RP_LTSSM_MASK 0x1f
#define LTSSM_L0 0xf
-#define PCIE_CAP_OFFSET 0x80
+#define S10_RP_TX_CNTRL 0x2004
+#define S10_RP_RXCPL_REG 0x2008
+#define S10_RP_RXCPL_STATUS 0x200C
+#define S10_RP_CFG_ADDR(pcie, reg) \
+ (((pcie)->hip_base) + (reg) + (1 << 20))
+
/* TLP configuration type 0 and 1 */
#define TLP_FMTTYPE_CFGRD0 0x04 /* Configuration Read Type 0 */
#define TLP_FMTTYPE_CFGWR0 0x44 /* Configuration Write Type 0 */
@@ -49,18 +55,19 @@
#define RP_DEVFN 0
#define TLP_REQ_ID(bus, devfn) (((bus) << 8) | (devfn))
#define TLP_CFGRD_DW0(pcie, bus) \
- ((((bus == pcie->root_bus_nr) ? TLP_FMTTYPE_CFGRD0 \
- : TLP_FMTTYPE_CFGRD1) << 24) | \
- TLP_PAYLOAD_SIZE)
+ ((((bus == pcie->root_bus_nr) ? pcie->pcie_data->cfgrd0 \
+ : pcie->pcie_data->cfgrd1) << 24) | \
+ TLP_PAYLOAD_SIZE)
#define TLP_CFGWR_DW0(pcie, bus) \
- ((((bus == pcie->root_bus_nr) ? TLP_FMTTYPE_CFGWR0 \
- : TLP_FMTTYPE_CFGWR1) << 24) | \
- TLP_PAYLOAD_SIZE)
+ ((((bus == pcie->root_bus_nr) ? pcie->pcie_data->cfgwr0 \
+ : pcie->pcie_data->cfgwr1) << 24) | \
+ TLP_PAYLOAD_SIZE)
#define TLP_CFG_DW1(pcie, tag, be) \
- (((TLP_REQ_ID(pcie->root_bus_nr, RP_DEVFN)) << 16) | (tag << 8) | (be))
+ (((TLP_REQ_ID(pcie->root_bus_nr, RP_DEVFN)) << 16) | (tag << 8) | (be))
#define TLP_CFG_DW2(bus, devfn, offset) \
(((bus) << 24) | ((devfn) << 16) | (offset))
#define TLP_COMP_STATUS(s) (((s) >> 13) & 7)
+#define TLP_BYTE_COUNT(s) (((s) >> 0) & 0xfff)
#define TLP_HDR_SIZE 3
#define TLP_LOOP 500
@@ -69,14 +76,47 @@
#define DWORD_MASK 3
+#define S10_TLP_FMTTYPE_CFGRD0 0x05
+#define S10_TLP_FMTTYPE_CFGRD1 0x04
+#define S10_TLP_FMTTYPE_CFGWR0 0x45
+#define S10_TLP_FMTTYPE_CFGWR1 0x44
+
+enum altera_pcie_version {
+ ALTERA_PCIE_V1 = 0,
+ ALTERA_PCIE_V2,
+};
+
struct altera_pcie {
struct platform_device *pdev;
- void __iomem *cra_base; /* DT Cra */
+ void __iomem *cra_base;
+ void __iomem *hip_base;
int irq;
u8 root_bus_nr;
struct irq_domain *irq_domain;
struct resource bus_range;
struct list_head resources;
+ const struct altera_pcie_data *pcie_data;
+};
+
+struct altera_pcie_ops {
+ int (*tlp_read_pkt)(struct altera_pcie *pcie, u32 *value);
+ void (*tlp_write_pkt)(struct altera_pcie *pcie, u32 *headers,
+ u32 data, bool align);
+ bool (*get_link_status)(struct altera_pcie *pcie);
+ int (*rp_read_cfg)(struct altera_pcie *pcie, int where,
+ int size, u32 *value);
+ int (*rp_write_cfg)(struct altera_pcie *pcie, u8 busno,
+ int where, int size, u32 value);
+};
+
+struct altera_pcie_data {
+ const struct altera_pcie_ops *ops;
+ enum altera_pcie_version version;
+ u32 cap_offset; /* PCIe capability structure register offset */
+ u32 cfgrd0;
+ u32 cfgrd1;
+ u32 cfgwr0;
+ u32 cfgwr1;
};
struct tlp_rp_regpair_t {
@@ -101,6 +141,15 @@ static bool altera_pcie_link_up(struct altera_pcie *pcie)
return !!((cra_readl(pcie, RP_LTSSM) & RP_LTSSM_MASK) == LTSSM_L0);
}
+static bool s10_altera_pcie_link_up(struct altera_pcie *pcie)
+{
+ void __iomem *addr = S10_RP_CFG_ADDR(pcie,
+ pcie->pcie_data->cap_offset +
+ PCI_EXP_LNKSTA);
+
+ return !!(readw(addr) & PCI_EXP_LNKSTA_DLLLA);
+}
+
/*
* Altera PCIe port uses BAR0 of RC's configuration space as the translation
* from PCI bus to native BUS. Entire DDR region is mapped into PCIe space
@@ -128,12 +177,18 @@ static void tlp_write_tx(struct altera_pcie *pcie,
cra_writel(pcie, tlp_rp_regdata->ctrl, RP_TX_CNTRL);
}
+static void s10_tlp_write_tx(struct altera_pcie *pcie, u32 reg0, u32 ctrl)
+{
+ cra_writel(pcie, reg0, RP_TX_REG0);
+ cra_writel(pcie, ctrl, S10_RP_TX_CNTRL);
+}
+
static bool altera_pcie_valid_device(struct altera_pcie *pcie,
struct pci_bus *bus, int dev)
{
/* If there is no link, then there is no device */
if (bus->number != pcie->root_bus_nr) {
- if (!altera_pcie_link_up(pcie))
+ if (!pcie->pcie_data->ops->get_link_status(pcie))
return false;
}
@@ -183,6 +238,53 @@ static int tlp_read_packet(struct altera_pcie *pcie, u32 *value)
return PCIBIOS_DEVICE_NOT_FOUND;
}
+static int s10_tlp_read_packet(struct altera_pcie *pcie, u32 *value)
+{
+ u32 ctrl;
+ u32 comp_status;
+ u32 dw[4];
+ u32 count;
+ struct device *dev = &pcie->pdev->dev;
+
+ for (count = 0; count < TLP_LOOP; count++) {
+ ctrl = cra_readl(pcie, S10_RP_RXCPL_STATUS);
+ if (ctrl & RP_RXCPL_SOP) {
+ /* Read first DW */
+ dw[0] = cra_readl(pcie, S10_RP_RXCPL_REG);
+ break;
+ }
+
+ udelay(5);
+ }
+
+ /* SOP detection failed, return error */
+ if (count == TLP_LOOP)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ count = 1;
+
+ /* Poll for EOP */
+ while (count < ARRAY_SIZE(dw)) {
+ ctrl = cra_readl(pcie, S10_RP_RXCPL_STATUS);
+ dw[count++] = cra_readl(pcie, S10_RP_RXCPL_REG);
+ if (ctrl & RP_RXCPL_EOP) {
+ comp_status = TLP_COMP_STATUS(dw[1]);
+ if (comp_status)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ if (value && TLP_BYTE_COUNT(dw[1]) == sizeof(u32) &&
+ count == 4)
+ *value = dw[3];
+
+ return PCIBIOS_SUCCESSFUL;
+ }
+ }
+
+ dev_warn(dev, "Malformed TLP packet\n");
+
+ return PCIBIOS_DEVICE_NOT_FOUND;
+}
+
static void tlp_write_packet(struct altera_pcie *pcie, u32 *headers,
u32 data, bool align)
{
@@ -210,6 +312,15 @@ static void tlp_write_packet(struct altera_pcie *pcie, u32 *headers,
tlp_write_tx(pcie, &tlp_rp_regdata);
}
+static void s10_tlp_write_packet(struct altera_pcie *pcie, u32 *headers,
+ u32 data, bool dummy)
+{
+ s10_tlp_write_tx(pcie, headers[0], RP_TX_SOP);
+ s10_tlp_write_tx(pcie, headers[1], 0);
+ s10_tlp_write_tx(pcie, headers[2], 0);
+ s10_tlp_write_tx(pcie, data, RP_TX_EOP);
+}
+
static int tlp_cfg_dword_read(struct altera_pcie *pcie, u8 bus, u32 devfn,
int where, u8 byte_en, u32 *value)
{
@@ -219,9 +330,9 @@ static int tlp_cfg_dword_read(struct altera_pcie *pcie, u8 bus, u32 devfn,
headers[1] = TLP_CFG_DW1(pcie, TLP_READ_TAG, byte_en);
headers[2] = TLP_CFG_DW2(bus, devfn, where);
- tlp_write_packet(pcie, headers, 0, false);
+ pcie->pcie_data->ops->tlp_write_pkt(pcie, headers, 0, false);
- return tlp_read_packet(pcie, value);
+ return pcie->pcie_data->ops->tlp_read_pkt(pcie, value);
}
static int tlp_cfg_dword_write(struct altera_pcie *pcie, u8 bus, u32 devfn,
@@ -236,11 +347,13 @@ static int tlp_cfg_dword_write(struct altera_pcie *pcie, u8 bus, u32 devfn,
/* check alignment to Qword */
if ((where & 0x7) == 0)
- tlp_write_packet(pcie, headers, value, true);
+ pcie->pcie_data->ops->tlp_write_pkt(pcie, headers,
+ value, true);
else
- tlp_write_packet(pcie, headers, value, false);
+ pcie->pcie_data->ops->tlp_write_pkt(pcie, headers,
+ value, false);
- ret = tlp_read_packet(pcie, NULL);
+ ret = pcie->pcie_data->ops->tlp_read_pkt(pcie, NULL);
if (ret != PCIBIOS_SUCCESSFUL)
return ret;
@@ -254,6 +367,53 @@ static int tlp_cfg_dword_write(struct altera_pcie *pcie, u8 bus, u32 devfn,
return PCIBIOS_SUCCESSFUL;
}
+static int s10_rp_read_cfg(struct altera_pcie *pcie, int where,
+ int size, u32 *value)
+{
+ void __iomem *addr = S10_RP_CFG_ADDR(pcie, where);
+
+ switch (size) {
+ case 1:
+ *value = readb(addr);
+ break;
+ case 2:
+ *value = readw(addr);
+ break;
+ default:
+ *value = readl(addr);
+ break;
+ }
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int s10_rp_write_cfg(struct altera_pcie *pcie, u8 busno,
+ int where, int size, u32 value)
+{
+ void __iomem *addr = S10_RP_CFG_ADDR(pcie, where);
+
+ switch (size) {
+ case 1:
+ writeb(value, addr);
+ break;
+ case 2:
+ writew(value, addr);
+ break;
+ default:
+ writel(value, addr);
+ break;
+ }
+
+ /*
+ * Monitor changes to PCI_PRIMARY_BUS register on root port
+ * and update local copy of root bus number accordingly.
+ */
+ if (busno == pcie->root_bus_nr && where == PCI_PRIMARY_BUS)
+ pcie->root_bus_nr = value & 0xff;
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
static int _altera_pcie_cfg_read(struct altera_pcie *pcie, u8 busno,
unsigned int devfn, int where, int size,
u32 *value)
@@ -262,6 +422,10 @@ static int _altera_pcie_cfg_read(struct altera_pcie *pcie, u8 busno,
u32 data;
u8 byte_en;
+ if (busno == pcie->root_bus_nr && pcie->pcie_data->ops->rp_read_cfg)
+ return pcie->pcie_data->ops->rp_read_cfg(pcie, where,
+ size, value);
+
switch (size) {
case 1:
byte_en = 1 << (where & 3);
@@ -302,6 +466,10 @@ static int _altera_pcie_cfg_write(struct altera_pcie *pcie, u8 busno,
u32 shift = 8 * (where & 3);
u8 byte_en;
+ if (busno == pcie->root_bus_nr && pcie->pcie_data->ops->rp_write_cfg)
+ return pcie->pcie_data->ops->rp_write_cfg(pcie, busno,
+ where, size, value);
+
switch (size) {
case 1:
data32 = (value & 0xff) << shift;
@@ -365,7 +533,8 @@ static int altera_read_cap_word(struct altera_pcie *pcie, u8 busno,
int ret;
ret = _altera_pcie_cfg_read(pcie, busno, devfn,
- PCIE_CAP_OFFSET + offset, sizeof(*value),
+ pcie->pcie_data->cap_offset + offset,
+ sizeof(*value),
&data);
*value = data;
return ret;
@@ -375,7 +544,8 @@ static int altera_write_cap_word(struct altera_pcie *pcie, u8 busno,
unsigned int devfn, int offset, u16 value)
{
return _altera_pcie_cfg_write(pcie, busno, devfn,
- PCIE_CAP_OFFSET + offset, sizeof(value),
+ pcie->pcie_data->cap_offset + offset,
+ sizeof(value),
value);
}
@@ -403,7 +573,7 @@ static void altera_wait_link_retrain(struct altera_pcie *pcie)
/* Wait for link is up */
start_jiffies = jiffies;
for (;;) {
- if (altera_pcie_link_up(pcie))
+ if (pcie->pcie_data->ops->get_link_status(pcie))
break;
if (time_after(jiffies, start_jiffies + LINK_UP_TIMEOUT)) {
@@ -418,7 +588,7 @@ static void altera_pcie_retrain(struct altera_pcie *pcie)
{
u16 linkcap, linkstat, linkctl;
- if (!altera_pcie_link_up(pcie))
+ if (!pcie->pcie_data->ops->get_link_status(pcie))
return;
/*
@@ -540,12 +710,20 @@ static int altera_pcie_parse_dt(struct altera_pcie *pcie)
struct device *dev = &pcie->pdev->dev;
struct platform_device *pdev = pcie->pdev;
struct resource *cra;
+ struct resource *hip;
cra = platform_get_resource_byname(pdev, IORESOURCE_MEM, "Cra");
pcie->cra_base = devm_ioremap_resource(dev, cra);
if (IS_ERR(pcie->cra_base))
return PTR_ERR(pcie->cra_base);
+ if (pcie->pcie_data->version == ALTERA_PCIE_V2) {
+ hip = platform_get_resource_byname(pdev, IORESOURCE_MEM, "Hip");
+ pcie->hip_base = devm_ioremap_resource(&pdev->dev, hip);
+ if (IS_ERR(pcie->hip_base))
+ return PTR_ERR(pcie->hip_base);
+ }
+
/* setup IRQ */
pcie->irq = platform_get_irq(pdev, 0);
if (pcie->irq < 0) {
@@ -562,6 +740,48 @@ static void altera_pcie_host_init(struct altera_pcie *pcie)
altera_pcie_retrain(pcie);
}
+static const struct altera_pcie_ops altera_pcie_ops_1_0 = {
+ .tlp_read_pkt = tlp_read_packet,
+ .tlp_write_pkt = tlp_write_packet,
+ .get_link_status = altera_pcie_link_up,
+};
+
+static const struct altera_pcie_ops altera_pcie_ops_2_0 = {
+ .tlp_read_pkt = s10_tlp_read_packet,
+ .tlp_write_pkt = s10_tlp_write_packet,
+ .get_link_status = s10_altera_pcie_link_up,
+ .rp_read_cfg = s10_rp_read_cfg,
+ .rp_write_cfg = s10_rp_write_cfg,
+};
+
+static const struct altera_pcie_data altera_pcie_1_0_data = {
+ .ops = &altera_pcie_ops_1_0,
+ .cap_offset = 0x80,
+ .version = ALTERA_PCIE_V1,
+ .cfgrd0 = TLP_FMTTYPE_CFGRD0,
+ .cfgrd1 = TLP_FMTTYPE_CFGRD1,
+ .cfgwr0 = TLP_FMTTYPE_CFGWR0,
+ .cfgwr1 = TLP_FMTTYPE_CFGWR1,
+};
+
+static const struct altera_pcie_data altera_pcie_2_0_data = {
+ .ops = &altera_pcie_ops_2_0,
+ .version = ALTERA_PCIE_V2,
+ .cap_offset = 0x70,
+ .cfgrd0 = S10_TLP_FMTTYPE_CFGRD0,
+ .cfgrd1 = S10_TLP_FMTTYPE_CFGRD1,
+ .cfgwr0 = S10_TLP_FMTTYPE_CFGWR0,
+ .cfgwr1 = S10_TLP_FMTTYPE_CFGWR1,
+};
+
+static const struct of_device_id altera_pcie_of_match[] = {
+ {.compatible = "altr,pcie-root-port-1.0",
+ .data = &altera_pcie_1_0_data },
+ {.compatible = "altr,pcie-root-port-2.0",
+ .data = &altera_pcie_2_0_data },
+ {},
+};
+
static int altera_pcie_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -570,6 +790,7 @@ static int altera_pcie_probe(struct platform_device *pdev)
struct pci_bus *child;
struct pci_host_bridge *bridge;
int ret;
+ const struct of_device_id *match;
bridge = devm_pci_alloc_host_bridge(dev, sizeof(*pcie));
if (!bridge)
@@ -578,6 +799,12 @@ static int altera_pcie_probe(struct platform_device *pdev)
pcie = pci_host_bridge_priv(bridge);
pcie->pdev = pdev;
+ match = of_match_device(altera_pcie_of_match, &pdev->dev);
+ if (!match)
+ return -ENODEV;
+
+ pcie->pcie_data = match->data;
+
ret = altera_pcie_parse_dt(pcie);
if (ret) {
dev_err(dev, "Parsing DT failed\n");
@@ -628,11 +855,6 @@ static int altera_pcie_probe(struct platform_device *pdev)
return ret;
}
-static const struct of_device_id altera_pcie_of_match[] = {
- { .compatible = "altr,pcie-root-port-1.0", },
- {},
-};
-
static struct platform_driver altera_pcie_driver = {
.probe = altera_pcie_probe,
.driver = {
diff --git a/drivers/pci/controller/pcie-cadence-ep.c b/drivers/pci/controller/pcie-cadence-ep.c
index c3a088910f48..def7820cb824 100644
--- a/drivers/pci/controller/pcie-cadence-ep.c
+++ b/drivers/pci/controller/pcie-cadence-ep.c
@@ -396,21 +396,21 @@ static int cdns_pcie_ep_start(struct pci_epc *epc)
cfg |= BIT(epf->func_no);
cdns_pcie_writel(pcie, CDNS_PCIE_LM_EP_FUNC_CFG, cfg);
- /*
- * The PCIe links are automatically established by the controller
- * once for all at powerup: the software can neither start nor stop
- * those links later at runtime.
- *
- * Then we only have to notify the EP core that our links are already
- * established. However we don't call directly pci_epc_linkup() because
- * we've already locked the epc->lock.
- */
- list_for_each_entry(epf, &epc->pci_epf, list)
- pci_epf_linkup(epf);
-
return 0;
}
+static const struct pci_epc_features cdns_pcie_epc_features = {
+ .linkup_notifier = false,
+ .msi_capable = true,
+ .msix_capable = false,
+};
+
+static const struct pci_epc_features*
+cdns_pcie_ep_get_features(struct pci_epc *epc, u8 func_no)
+{
+ return &cdns_pcie_epc_features;
+}
+
static const struct pci_epc_ops cdns_pcie_epc_ops = {
.write_header = cdns_pcie_ep_write_header,
.set_bar = cdns_pcie_ep_set_bar,
@@ -421,6 +421,7 @@ static const struct pci_epc_ops cdns_pcie_epc_ops = {
.get_msi = cdns_pcie_ep_get_msi,
.raise_irq = cdns_pcie_ep_raise_irq,
.start = cdns_pcie_ep_start,
+ .get_features = cdns_pcie_ep_get_features,
};
static const struct of_device_id cdns_pcie_ep_of_match[] = {
diff --git a/drivers/pci/controller/pcie-mediatek.c b/drivers/pci/controller/pcie-mediatek.c
index 55e471c18e8d..0b6c72804e03 100644
--- a/drivers/pci/controller/pcie-mediatek.c
+++ b/drivers/pci/controller/pcie-mediatek.c
@@ -90,6 +90,12 @@
#define AHB2PCIE_SIZE(x) ((x) & GENMASK(4, 0))
#define PCIE_AXI_WINDOW0 0x448
#define WIN_ENABLE BIT(7)
+/*
+ * Define PCIe to AHB window size as 2^33 to support max 8GB address space
+ * translate, support least 4GB DRAM size access from EP DMA(physical DRAM
+ * start from 0x40000000).
+ */
+#define PCIE2AHB_SIZE 0x21
/* PCIe V2 configuration transaction header */
#define PCIE_CFG_HEADER0 0x460
@@ -654,7 +660,6 @@ static int mtk_pcie_startup_port_v2(struct mtk_pcie_port *port)
struct resource *mem = &pcie->mem;
const struct mtk_pcie_soc *soc = port->pcie->soc;
u32 val;
- size_t size;
int err;
/* MT7622 platforms need to enable LTSSM and ASPM from PCIe subsys */
@@ -706,15 +711,15 @@ static int mtk_pcie_startup_port_v2(struct mtk_pcie_port *port)
mtk_pcie_enable_msi(port);
/* Set AHB to PCIe translation windows */
- size = mem->end - mem->start;
- val = lower_32_bits(mem->start) | AHB2PCIE_SIZE(fls(size));
+ val = lower_32_bits(mem->start) |
+ AHB2PCIE_SIZE(fls(resource_size(mem)));
writel(val, port->base + PCIE_AHB_TRANS_BASE0_L);
val = upper_32_bits(mem->start);
writel(val, port->base + PCIE_AHB_TRANS_BASE0_H);
/* Set PCIe to AXI translation memory space.*/
- val = fls(0xffffffff) | WIN_ENABLE;
+ val = PCIE2AHB_SIZE | WIN_ENABLE;
writel(val, port->base + PCIE_AXI_WINDOW0);
return 0;
diff --git a/drivers/pci/controller/pcie-rockchip-ep.c b/drivers/pci/controller/pcie-rockchip-ep.c
index b8163c56a142..a5d799e2dff2 100644
--- a/drivers/pci/controller/pcie-rockchip-ep.c
+++ b/drivers/pci/controller/pcie-rockchip-ep.c
@@ -499,12 +499,21 @@ static int rockchip_pcie_ep_start(struct pci_epc *epc)
rockchip_pcie_write(rockchip, cfg, PCIE_CORE_PHY_FUNC_CFG);
- list_for_each_entry(epf, &epc->pci_epf, list)
- pci_epf_linkup(epf);
-
return 0;
}
+static const struct pci_epc_features rockchip_pcie_epc_features = {
+ .linkup_notifier = false,
+ .msi_capable = true,
+ .msix_capable = false,
+};
+
+static const struct pci_epc_features*
+rockchip_pcie_ep_get_features(struct pci_epc *epc, u8 func_no)
+{
+ return &rockchip_pcie_epc_features;
+}
+
static const struct pci_epc_ops rockchip_pcie_epc_ops = {
.write_header = rockchip_pcie_ep_write_header,
.set_bar = rockchip_pcie_ep_set_bar,
@@ -515,6 +524,7 @@ static const struct pci_epc_ops rockchip_pcie_epc_ops = {
.get_msi = rockchip_pcie_ep_get_msi,
.raise_irq = rockchip_pcie_ep_raise_irq,
.start = rockchip_pcie_ep_start,
+ .get_features = rockchip_pcie_ep_get_features,
};
static int rockchip_pcie_parse_ep_dt(struct rockchip_pcie *rockchip,
diff --git a/drivers/pci/controller/vmd.c b/drivers/pci/controller/vmd.c
index 3890812cdf87..cf6816b55b5e 100644
--- a/drivers/pci/controller/vmd.c
+++ b/drivers/pci/controller/vmd.c
@@ -571,6 +571,7 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
LIST_HEAD(resources);
resource_size_t offset[2] = {0};
resource_size_t membar2_offset = 0x2000, busn_start = 0;
+ struct pci_bus *child;
/*
* Shadow registers may exist in certain VMD device ids which allow
@@ -698,7 +699,19 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
vmd_attach_resources(vmd);
vmd_setup_dma_ops(vmd);
dev_set_msi_domain(&vmd->bus->dev, vmd->irq_domain);
- pci_rescan_bus(vmd->bus);
+
+ pci_scan_child_bus(vmd->bus);
+ pci_assign_unassigned_bus_resources(vmd->bus);
+
+ /*
+ * VMD root buses are virtual and don't return true on pci_is_pcie()
+ * and will fail pcie_bus_configure_settings() early. It can instead be
+ * run on each of the real root ports.
+ */
+ list_for_each_entry(child, &vmd->bus->children, node)
+ pcie_bus_configure_settings(child);
+
+ pci_bus_add_devices(vmd->bus);
WARN(sysfs_create_link(&vmd->dev->dev.kobj, &vmd->bus->dev.kobj,
"domain"), "Can't create symlink to domain\n");
diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c
index 3e86fa3c7da3..d0b91da49bf4 100644
--- a/drivers/pci/endpoint/functions/pci-epf-test.c
+++ b/drivers/pci/endpoint/functions/pci-epf-test.c
@@ -47,9 +47,8 @@ struct pci_epf_test {
void *reg[6];
struct pci_epf *epf;
enum pci_barno test_reg_bar;
- bool linkup_notifier;
- bool msix_available;
struct delayed_work cmd_handler;
+ const struct pci_epc_features *epc_features;
};
struct pci_epf_test_reg {
@@ -71,11 +70,6 @@ static struct pci_epf_header test_header = {
.interrupt_pin = PCI_INTERRUPT_INTA,
};
-struct pci_epf_test_data {
- enum pci_barno test_reg_bar;
- bool linkup_notifier;
-};
-
static size_t bar_size[] = { 512, 512, 1024, 16384, 131072, 1048576 };
static int pci_epf_test_copy(struct pci_epf_test *epf_test)
@@ -175,7 +169,7 @@ static int pci_epf_test_read(struct pci_epf_test *epf_test)
goto err_map_addr;
}
- memcpy(buf, src_addr, reg->size);
+ memcpy_fromio(buf, src_addr, reg->size);
crc32 = crc32_le(~0, buf, reg->size);
if (crc32 != reg->checksum)
@@ -230,7 +224,7 @@ static int pci_epf_test_write(struct pci_epf_test *epf_test)
get_random_bytes(buf, reg->size);
reg->checksum = crc32_le(~0, buf, reg->size);
- memcpy(dst_addr, buf, reg->size);
+ memcpy_toio(dst_addr, buf, reg->size);
/*
* wait 1ms inorder for the write to complete. Without this delay L3
@@ -402,13 +396,15 @@ static int pci_epf_test_set_bar(struct pci_epf *epf)
struct device *dev = &epf->dev;
struct pci_epf_test *epf_test = epf_get_drvdata(epf);
enum pci_barno test_reg_bar = epf_test->test_reg_bar;
+ const struct pci_epc_features *epc_features;
+
+ epc_features = epf_test->epc_features;
for (bar = BAR_0; bar <= BAR_5; bar++) {
epf_bar = &epf->bar[bar];
- epf_bar->flags |= upper_32_bits(epf_bar->size) ?
- PCI_BASE_ADDRESS_MEM_TYPE_64 :
- PCI_BASE_ADDRESS_MEM_TYPE_32;
+ if (!!(epc_features->reserved_bar & (1 << bar)))
+ continue;
ret = pci_epc_set_bar(epc, epf->func_no, epf_bar);
if (ret) {
@@ -433,9 +429,13 @@ static int pci_epf_test_alloc_space(struct pci_epf *epf)
{
struct pci_epf_test *epf_test = epf_get_drvdata(epf);
struct device *dev = &epf->dev;
+ struct pci_epf_bar *epf_bar;
void *base;
int bar;
enum pci_barno test_reg_bar = epf_test->test_reg_bar;
+ const struct pci_epc_features *epc_features;
+
+ epc_features = epf_test->epc_features;
base = pci_epf_alloc_space(epf, sizeof(struct pci_epf_test_reg),
test_reg_bar);
@@ -446,37 +446,69 @@ static int pci_epf_test_alloc_space(struct pci_epf *epf)
epf_test->reg[test_reg_bar] = base;
for (bar = BAR_0; bar <= BAR_5; bar++) {
+ epf_bar = &epf->bar[bar];
if (bar == test_reg_bar)
continue;
+
+ if (!!(epc_features->reserved_bar & (1 << bar)))
+ continue;
+
base = pci_epf_alloc_space(epf, bar_size[bar], bar);
if (!base)
dev_err(dev, "Failed to allocate space for BAR%d\n",
bar);
epf_test->reg[bar] = base;
+ if (epf_bar->flags & PCI_BASE_ADDRESS_MEM_TYPE_64)
+ bar++;
}
return 0;
}
+static void pci_epf_configure_bar(struct pci_epf *epf,
+ const struct pci_epc_features *epc_features)
+{
+ struct pci_epf_bar *epf_bar;
+ bool bar_fixed_64bit;
+ int i;
+
+ for (i = BAR_0; i <= BAR_5; i++) {
+ epf_bar = &epf->bar[i];
+ bar_fixed_64bit = !!(epc_features->bar_fixed_64bit & (1 << i));
+ if (bar_fixed_64bit)
+ epf_bar->flags |= PCI_BASE_ADDRESS_MEM_TYPE_64;
+ if (epc_features->bar_fixed_size[i])
+ bar_size[i] = epc_features->bar_fixed_size[i];
+ }
+}
+
static int pci_epf_test_bind(struct pci_epf *epf)
{
int ret;
struct pci_epf_test *epf_test = epf_get_drvdata(epf);
struct pci_epf_header *header = epf->header;
+ const struct pci_epc_features *epc_features;
+ enum pci_barno test_reg_bar = BAR_0;
struct pci_epc *epc = epf->epc;
struct device *dev = &epf->dev;
+ bool linkup_notifier = false;
+ bool msix_capable = false;
+ bool msi_capable = true;
if (WARN_ON_ONCE(!epc))
return -EINVAL;
- if (epc->features & EPC_FEATURE_NO_LINKUP_NOTIFIER)
- epf_test->linkup_notifier = false;
- else
- epf_test->linkup_notifier = true;
-
- epf_test->msix_available = epc->features & EPC_FEATURE_MSIX_AVAILABLE;
+ epc_features = pci_epc_get_features(epc, epf->func_no);
+ if (epc_features) {
+ linkup_notifier = epc_features->linkup_notifier;
+ msix_capable = epc_features->msix_capable;
+ msi_capable = epc_features->msi_capable;
+ test_reg_bar = pci_epc_get_first_free_bar(epc_features);
+ pci_epf_configure_bar(epf, epc_features);
+ }
- epf_test->test_reg_bar = EPC_FEATURE_GET_BAR(epc->features);
+ epf_test->test_reg_bar = test_reg_bar;
+ epf_test->epc_features = epc_features;
ret = pci_epc_write_header(epc, epf->func_no, header);
if (ret) {
@@ -492,13 +524,15 @@ static int pci_epf_test_bind(struct pci_epf *epf)
if (ret)
return ret;
- ret = pci_epc_set_msi(epc, epf->func_no, epf->msi_interrupts);
- if (ret) {
- dev_err(dev, "MSI configuration failed\n");
- return ret;
+ if (msi_capable) {
+ ret = pci_epc_set_msi(epc, epf->func_no, epf->msi_interrupts);
+ if (ret) {
+ dev_err(dev, "MSI configuration failed\n");
+ return ret;
+ }
}
- if (epf_test->msix_available) {
+ if (msix_capable) {
ret = pci_epc_set_msix(epc, epf->func_no, epf->msix_interrupts);
if (ret) {
dev_err(dev, "MSI-X configuration failed\n");
@@ -506,7 +540,7 @@ static int pci_epf_test_bind(struct pci_epf *epf)
}
}
- if (!epf_test->linkup_notifier)
+ if (!linkup_notifier)
queue_work(kpcitest_workqueue, &epf_test->cmd_handler.work);
return 0;
@@ -523,17 +557,6 @@ static int pci_epf_test_probe(struct pci_epf *epf)
{
struct pci_epf_test *epf_test;
struct device *dev = &epf->dev;
- const struct pci_epf_device_id *match;
- struct pci_epf_test_data *data;
- enum pci_barno test_reg_bar = BAR_0;
- bool linkup_notifier = true;
-
- match = pci_epf_match_device(pci_epf_test_ids, epf);
- data = (struct pci_epf_test_data *)match->driver_data;
- if (data) {
- test_reg_bar = data->test_reg_bar;
- linkup_notifier = data->linkup_notifier;
- }
epf_test = devm_kzalloc(dev, sizeof(*epf_test), GFP_KERNEL);
if (!epf_test)
@@ -541,8 +564,6 @@ static int pci_epf_test_probe(struct pci_epf *epf)
epf->header = &test_header;
epf_test->epf = epf;
- epf_test->test_reg_bar = test_reg_bar;
- epf_test->linkup_notifier = linkup_notifier;
INIT_DELAYED_WORK(&epf_test->cmd_handler, pci_epf_test_cmd_handler);
diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c
index 094dcc3203b8..e4712a0f249c 100644
--- a/drivers/pci/endpoint/pci-epc-core.c
+++ b/drivers/pci/endpoint/pci-epc-core.c
@@ -84,6 +84,59 @@ err:
EXPORT_SYMBOL_GPL(pci_epc_get);
/**
+ * pci_epc_get_first_free_bar() - helper to get first unreserved BAR
+ * @epc_features: pci_epc_features structure that holds the reserved bar bitmap
+ *
+ * Invoke to get the first unreserved BAR that can be used for endpoint
+ * function. For any incorrect value in reserved_bar return '0'.
+ */
+unsigned int pci_epc_get_first_free_bar(const struct pci_epc_features
+ *epc_features)
+{
+ int free_bar;
+
+ if (!epc_features)
+ return 0;
+
+ free_bar = ffz(epc_features->reserved_bar);
+ if (free_bar > 5)
+ return 0;
+
+ return free_bar;
+}
+EXPORT_SYMBOL_GPL(pci_epc_get_first_free_bar);
+
+/**
+ * pci_epc_get_features() - get the features supported by EPC
+ * @epc: the features supported by *this* EPC device will be returned
+ * @func_no: the features supported by the EPC device specific to the
+ * endpoint function with func_no will be returned
+ *
+ * Invoke to get the features provided by the EPC which may be
+ * specific to an endpoint function. Returns pci_epc_features on success
+ * and NULL for any failures.
+ */
+const struct pci_epc_features *pci_epc_get_features(struct pci_epc *epc,
+ u8 func_no)
+{
+ const struct pci_epc_features *epc_features;
+ unsigned long flags;
+
+ if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions)
+ return NULL;
+
+ if (!epc->ops->get_features)
+ return NULL;
+
+ spin_lock_irqsave(&epc->lock, flags);
+ epc_features = epc->ops->get_features(epc, func_no);
+ spin_unlock_irqrestore(&epc->lock, flags);
+
+ return epc_features;
+}
+EXPORT_SYMBOL_GPL(pci_epc_get_features);
+
+/**
* pci_epc_stop() - stop the PCI link
* @epc: the link of the EPC device that has to be stopped
*
diff --git a/drivers/pci/endpoint/pci-epf-core.c b/drivers/pci/endpoint/pci-epf-core.c
index 825fa24427a3..8bfdcd291196 100644
--- a/drivers/pci/endpoint/pci-epf-core.c
+++ b/drivers/pci/endpoint/pci-epf-core.c
@@ -131,7 +131,9 @@ void *pci_epf_alloc_space(struct pci_epf *epf, size_t size, enum pci_barno bar)
epf->bar[bar].phys_addr = phys_addr;
epf->bar[bar].size = size;
epf->bar[bar].barno = bar;
- epf->bar[bar].flags = PCI_BASE_ADDRESS_SPACE_MEMORY;
+ epf->bar[bar].flags |= upper_32_bits(size) ?
+ PCI_BASE_ADDRESS_MEM_TYPE_64 :
+ PCI_BASE_ADDRESS_MEM_TYPE_32;
return space;
}
diff --git a/drivers/pci/hotplug/ibmphp.h b/drivers/pci/hotplug/ibmphp.h
index b89f850c3a4e..e90a4ebf6550 100644
--- a/drivers/pci/hotplug/ibmphp.h
+++ b/drivers/pci/hotplug/ibmphp.h
@@ -378,7 +378,6 @@ int ibmphp_add_pfmem_from_mem(struct resource_node *);
struct bus_node *ibmphp_find_res_bus(u8);
void ibmphp_print_test(void); /* for debugging purposes */
-void ibmphp_hpc_initvars(void);
int ibmphp_hpc_readslot(struct slot *, u8, u8 *);
int ibmphp_hpc_writeslot(struct slot *, u8);
void ibmphp_lock_operations(void);
diff --git a/drivers/pci/hotplug/ibmphp_core.c b/drivers/pci/hotplug/ibmphp_core.c
index 08a58e911fc2..17124254d897 100644
--- a/drivers/pci/hotplug/ibmphp_core.c
+++ b/drivers/pci/hotplug/ibmphp_core.c
@@ -1277,8 +1277,6 @@ static int __init ibmphp_init(void)
ibmphp_debug = debug;
- ibmphp_hpc_initvars();
-
for (i = 0; i < 16; i++)
irqs[i] = 0;
diff --git a/drivers/pci/hotplug/ibmphp_hpc.c b/drivers/pci/hotplug/ibmphp_hpc.c
index 752c384cbd4c..508a62a6b5f9 100644
--- a/drivers/pci/hotplug/ibmphp_hpc.c
+++ b/drivers/pci/hotplug/ibmphp_hpc.c
@@ -15,13 +15,13 @@
#include <linux/wait.h>
#include <linux/time.h>
+#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/mutex.h>
#include <linux/sched.h>
-#include <linux/semaphore.h>
#include <linux/kthread.h>
#include "ibmphp.h"
@@ -88,10 +88,10 @@ static int to_debug = 0;
//----------------------------------------------------------------------------
// global variables
//----------------------------------------------------------------------------
-static struct mutex sem_hpcaccess; // lock access to HPC
-static struct semaphore semOperations; // lock all operations and
+static DEFINE_MUTEX(sem_hpcaccess); // lock access to HPC
+static DEFINE_MUTEX(operations_mutex); // lock all operations and
// access to data structures
-static struct semaphore sem_exit; // make sure polling thread goes away
+static DECLARE_COMPLETION(exit_complete); // make sure polling thread goes away
static struct task_struct *ibmphp_poll_thread;
//----------------------------------------------------------------------------
// local function prototypes
@@ -110,23 +110,6 @@ static int hpc_wait_ctlr_notworking(int, struct controller *, void __iomem *, u8
/*----------------------------------------------------------------------
-* Name: ibmphp_hpc_initvars
-*
-* Action: initialize semaphores and variables
-*---------------------------------------------------------------------*/
-void __init ibmphp_hpc_initvars(void)
-{
- debug("%s - Entry\n", __func__);
-
- mutex_init(&sem_hpcaccess);
- sema_init(&semOperations, 1);
- sema_init(&sem_exit, 0);
- to_debug = 0;
-
- debug("%s - Exit\n", __func__);
-}
-
-/*----------------------------------------------------------------------
* Name: i2c_ctrl_read
*
* Action: read from HPC over I2C
@@ -780,7 +763,7 @@ void free_hpc_access(void)
*---------------------------------------------------------------------*/
void ibmphp_lock_operations(void)
{
- down(&semOperations);
+ mutex_lock(&operations_mutex);
to_debug = 1;
}
@@ -790,7 +773,7 @@ void ibmphp_lock_operations(void)
void ibmphp_unlock_operations(void)
{
debug("%s - Entry\n", __func__);
- up(&semOperations);
+ mutex_unlock(&operations_mutex);
to_debug = 0;
debug("%s - Exit\n", __func__);
}
@@ -816,7 +799,7 @@ static int poll_hpc(void *data)
while (!kthread_should_stop()) {
/* try to get the lock to do some kind of hardware access */
- down(&semOperations);
+ mutex_lock(&operations_mutex);
switch (poll_state) {
case POLL_LATCH_REGISTER:
@@ -871,13 +854,13 @@ static int poll_hpc(void *data)
break;
case POLL_SLEEP:
/* don't sleep with a lock on the hardware */
- up(&semOperations);
+ mutex_unlock(&operations_mutex);
msleep(POLL_INTERVAL_SEC * 1000);
if (kthread_should_stop())
goto out_sleep;
- down(&semOperations);
+ mutex_lock(&operations_mutex);
if (poll_count >= POLL_LATCH_CNT) {
poll_count = 0;
@@ -887,12 +870,12 @@ static int poll_hpc(void *data)
break;
}
/* give up the hardware semaphore */
- up(&semOperations);
+ mutex_unlock(&operations_mutex);
/* sleep for a short time just for good measure */
out_sleep:
msleep(100);
}
- up(&sem_exit);
+ complete(&exit_complete);
debug("%s - Exit\n", __func__);
return 0;
}
@@ -1060,9 +1043,9 @@ void __exit ibmphp_hpc_stop_poll_thread(void)
debug("after locking operations\n");
// wait for poll thread to exit
- debug("before sem_exit down\n");
- down(&sem_exit);
- debug("after sem_exit down\n");
+ debug("before exit_complete down\n");
+ wait_for_completion(&exit_complete);
+ debug("after exit_completion down\n");
// cleanup
debug("before free_hpc_access\n");
@@ -1070,8 +1053,6 @@ void __exit ibmphp_hpc_stop_poll_thread(void)
debug("after free_hpc_access\n");
ibmphp_unlock_operations();
debug("after unlock operations\n");
- up(&sem_exit);
- debug("after sem exit up\n");
debug("%s - Exit\n", __func__);
}
diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c
index 7dd443aea5a5..6a2365cd794e 100644
--- a/drivers/pci/hotplug/pciehp_hpc.c
+++ b/drivers/pci/hotplug/pciehp_hpc.c
@@ -156,9 +156,9 @@ static void pcie_do_write_cmd(struct controller *ctrl, u16 cmd,
slot_ctrl |= (cmd & mask);
ctrl->cmd_busy = 1;
smp_mb();
+ ctrl->slot_ctrl = slot_ctrl;
pcie_capability_write_word(pdev, PCI_EXP_SLTCTL, slot_ctrl);
ctrl->cmd_started = jiffies;
- ctrl->slot_ctrl = slot_ctrl;
/*
* Controllers with the Intel CF118 and similar errata advertise
@@ -736,12 +736,25 @@ void pcie_clear_hotplug_events(struct controller *ctrl)
void pcie_enable_interrupt(struct controller *ctrl)
{
- pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_HPIE, PCI_EXP_SLTCTL_HPIE);
+ u16 mask;
+
+ mask = PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_DLLSCE;
+ pcie_write_cmd(ctrl, mask, mask);
}
void pcie_disable_interrupt(struct controller *ctrl)
{
- pcie_write_cmd(ctrl, 0, PCI_EXP_SLTCTL_HPIE);
+ u16 mask;
+
+ /*
+ * Mask hot-plug interrupt to prevent it triggering immediately
+ * when the link goes inactive (we still get PME when any of the
+ * enabled events is detected). Same goes with Link Layer State
+ * changed event which generates PME immediately when the link goes
+ * inactive so mask it as well.
+ */
+ mask = PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_DLLSCE;
+ pcie_write_cmd(ctrl, 0, mask);
}
/*
@@ -920,3 +933,5 @@ DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_QCOM, 0x0400,
PCI_CLASS_BRIDGE_PCI, 8, quirk_cmd_compl);
DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_QCOM, 0x0401,
PCI_CLASS_BRIDGE_PCI, 8, quirk_cmd_compl);
+DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_HXT, 0x0401,
+ PCI_CLASS_BRIDGE_PCI, 8, quirk_cmd_compl);
diff --git a/drivers/pci/of.c b/drivers/pci/of.c
index 4c4217d0c3f1..3d32da15c215 100644
--- a/drivers/pci/of.c
+++ b/drivers/pci/of.c
@@ -113,7 +113,7 @@ struct device_node *of_pci_find_child_device(struct device_node *parent,
* a fake root for all functions of a multi-function
* device we go down them as well.
*/
- if (!strcmp(node->name, "multifunc-device")) {
+ if (of_node_name_eq(node, "multifunc-device")) {
for_each_child_of_node(node, node2) {
if (__of_pci_pci_compare(node2, devfn)) {
of_node_put(node);
diff --git a/drivers/pci/pci-bridge-emul.c b/drivers/pci/pci-bridge-emul.c
index 129738362d90..83fb077d0b41 100644
--- a/drivers/pci/pci-bridge-emul.c
+++ b/drivers/pci/pci-bridge-emul.c
@@ -24,29 +24,6 @@
#define PCI_CAP_PCIE_START PCI_BRIDGE_CONF_END
#define PCI_CAP_PCIE_END (PCI_CAP_PCIE_START + PCI_EXP_SLTSTA2 + 2)
-/*
- * Initialize a pci_bridge_emul structure to represent a fake PCI
- * bridge configuration space. The caller needs to have initialized
- * the PCI configuration space with whatever values make sense
- * (typically at least vendor, device, revision), the ->ops pointer,
- * and optionally ->data and ->has_pcie.
- */
-void pci_bridge_emul_init(struct pci_bridge_emul *bridge)
-{
- bridge->conf.class_revision |= PCI_CLASS_BRIDGE_PCI << 16;
- bridge->conf.header_type = PCI_HEADER_TYPE_BRIDGE;
- bridge->conf.cache_line_size = 0x10;
- bridge->conf.status = PCI_STATUS_CAP_LIST;
-
- if (bridge->has_pcie) {
- bridge->conf.capabilities_pointer = PCI_CAP_PCIE_START;
- bridge->pcie_conf.cap_id = PCI_CAP_ID_EXP;
- /* Set PCIe v2, root port, slot support */
- bridge->pcie_conf.cap = PCI_EXP_TYPE_ROOT_PORT << 4 | 2 |
- PCI_EXP_FLAGS_SLOT;
- }
-}
-
struct pci_bridge_reg_behavior {
/* Read-only bits */
u32 ro;
@@ -284,6 +261,61 @@ const static struct pci_bridge_reg_behavior pcie_cap_regs_behavior[] = {
};
/*
+ * Initialize a pci_bridge_emul structure to represent a fake PCI
+ * bridge configuration space. The caller needs to have initialized
+ * the PCI configuration space with whatever values make sense
+ * (typically at least vendor, device, revision), the ->ops pointer,
+ * and optionally ->data and ->has_pcie.
+ */
+int pci_bridge_emul_init(struct pci_bridge_emul *bridge,
+ unsigned int flags)
+{
+ bridge->conf.class_revision |= PCI_CLASS_BRIDGE_PCI << 16;
+ bridge->conf.header_type = PCI_HEADER_TYPE_BRIDGE;
+ bridge->conf.cache_line_size = 0x10;
+ bridge->conf.status = PCI_STATUS_CAP_LIST;
+ bridge->pci_regs_behavior = kmemdup(pci_regs_behavior,
+ sizeof(pci_regs_behavior),
+ GFP_KERNEL);
+ if (!bridge->pci_regs_behavior)
+ return -ENOMEM;
+
+ if (bridge->has_pcie) {
+ bridge->conf.capabilities_pointer = PCI_CAP_PCIE_START;
+ bridge->pcie_conf.cap_id = PCI_CAP_ID_EXP;
+ /* Set PCIe v2, root port, slot support */
+ bridge->pcie_conf.cap = PCI_EXP_TYPE_ROOT_PORT << 4 | 2 |
+ PCI_EXP_FLAGS_SLOT;
+ bridge->pcie_cap_regs_behavior =
+ kmemdup(pcie_cap_regs_behavior,
+ sizeof(pcie_cap_regs_behavior),
+ GFP_KERNEL);
+ if (!bridge->pcie_cap_regs_behavior) {
+ kfree(bridge->pci_regs_behavior);
+ return -ENOMEM;
+ }
+ }
+
+ if (flags & PCI_BRIDGE_EMUL_NO_PREFETCHABLE_BAR) {
+ bridge->pci_regs_behavior[PCI_PREF_MEMORY_BASE / 4].ro = ~0;
+ bridge->pci_regs_behavior[PCI_PREF_MEMORY_BASE / 4].rw = 0;
+ }
+
+ return 0;
+}
+
+/*
+ * Cleanup a pci_bridge_emul structure that was previously initilized
+ * using pci_bridge_emul_init().
+ */
+void pci_bridge_emul_cleanup(struct pci_bridge_emul *bridge)
+{
+ if (bridge->has_pcie)
+ kfree(bridge->pcie_cap_regs_behavior);
+ kfree(bridge->pci_regs_behavior);
+}
+
+/*
* Should be called by the PCI controller driver when reading the PCI
* configuration space of the fake bridge. It will call back the
* ->ops->read_base or ->ops->read_pcie operations.
@@ -312,11 +344,11 @@ int pci_bridge_emul_conf_read(struct pci_bridge_emul *bridge, int where,
reg -= PCI_CAP_PCIE_START;
read_op = bridge->ops->read_pcie;
cfgspace = (u32 *) &bridge->pcie_conf;
- behavior = pcie_cap_regs_behavior;
+ behavior = bridge->pcie_cap_regs_behavior;
} else {
read_op = bridge->ops->read_base;
cfgspace = (u32 *) &bridge->conf;
- behavior = pci_regs_behavior;
+ behavior = bridge->pci_regs_behavior;
}
if (read_op)
@@ -383,11 +415,11 @@ int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where,
reg -= PCI_CAP_PCIE_START;
write_op = bridge->ops->write_pcie;
cfgspace = (u32 *) &bridge->pcie_conf;
- behavior = pcie_cap_regs_behavior;
+ behavior = bridge->pcie_cap_regs_behavior;
} else {
write_op = bridge->ops->write_base;
cfgspace = (u32 *) &bridge->conf;
- behavior = pci_regs_behavior;
+ behavior = bridge->pci_regs_behavior;
}
/* Keep all bits, except the RW bits */
diff --git a/drivers/pci/pci-bridge-emul.h b/drivers/pci/pci-bridge-emul.h
index 9d510ccf738b..e65b1b79899d 100644
--- a/drivers/pci/pci-bridge-emul.h
+++ b/drivers/pci/pci-bridge-emul.h
@@ -107,15 +107,26 @@ struct pci_bridge_emul_ops {
u32 old, u32 new, u32 mask);
};
+struct pci_bridge_reg_behavior;
+
struct pci_bridge_emul {
struct pci_bridge_emul_conf conf;
struct pci_bridge_emul_pcie_conf pcie_conf;
struct pci_bridge_emul_ops *ops;
+ struct pci_bridge_reg_behavior *pci_regs_behavior;
+ struct pci_bridge_reg_behavior *pcie_cap_regs_behavior;
void *data;
bool has_pcie;
};
-void pci_bridge_emul_init(struct pci_bridge_emul *bridge);
+enum {
+ PCI_BRIDGE_EMUL_NO_PREFETCHABLE_BAR = BIT(0),
+};
+
+int pci_bridge_emul_init(struct pci_bridge_emul *bridge,
+ unsigned int flags);
+void pci_bridge_emul_cleanup(struct pci_bridge_emul *bridge);
+
int pci_bridge_emul_conf_read(struct pci_bridge_emul *bridge, int where,
int size, u32 *value);
int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where,
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
index 79b1610a8beb..71853befd435 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -100,7 +100,7 @@ static ssize_t new_id_store(struct device_driver *driver, const char *buf,
{
struct pci_driver *pdrv = to_pci_driver(driver);
const struct pci_device_id *ids = pdrv->id_table;
- __u32 vendor, device, subvendor = PCI_ANY_ID,
+ u32 vendor, device, subvendor = PCI_ANY_ID,
subdevice = PCI_ANY_ID, class = 0, class_mask = 0;
unsigned long driver_data = 0;
int fields = 0;
@@ -168,7 +168,7 @@ static ssize_t remove_id_store(struct device_driver *driver, const char *buf,
{
struct pci_dynid *dynid, *n;
struct pci_driver *pdrv = to_pci_driver(driver);
- __u32 vendor, device, subvendor = PCI_ANY_ID,
+ u32 vendor, device, subvendor = PCI_ANY_ID,
subdevice = PCI_ANY_ID, class = 0, class_mask = 0;
int fields = 0;
size_t retval = -ENODEV;
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index db55acf32a7e..7c1b362f599a 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -861,7 +861,7 @@ static int pci_raw_set_power_state(struct pci_dev *dev, pci_power_t state)
if ((pmcsr & PCI_PM_CTRL_STATE_MASK) == PCI_D3hot
&& !(pmcsr & PCI_PM_CTRL_NO_SOFT_RESET))
need_restore = true;
- /* Fall-through: force to D0 */
+ /* Fall-through - force to D0 */
default:
pmcsr = 0;
break;
@@ -1233,7 +1233,6 @@ static void pci_restore_pcie_state(struct pci_dev *dev)
pcie_capability_write_word(dev, PCI_EXP_SLTCTL2, cap[i++]);
}
-
static int pci_save_pcix_state(struct pci_dev *dev)
{
int pos;
@@ -1270,6 +1269,45 @@ static void pci_restore_pcix_state(struct pci_dev *dev)
pci_write_config_word(dev, pos + PCI_X_CMD, cap[i++]);
}
+static void pci_save_ltr_state(struct pci_dev *dev)
+{
+ int ltr;
+ struct pci_cap_saved_state *save_state;
+ u16 *cap;
+
+ if (!pci_is_pcie(dev))
+ return;
+
+ ltr = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_LTR);
+ if (!ltr)
+ return;
+
+ save_state = pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_LTR);
+ if (!save_state) {
+ pci_err(dev, "no suspend buffer for LTR; ASPM issues possible after resume\n");
+ return;
+ }
+
+ cap = (u16 *)&save_state->cap.data[0];
+ pci_read_config_word(dev, ltr + PCI_LTR_MAX_SNOOP_LAT, cap++);
+ pci_read_config_word(dev, ltr + PCI_LTR_MAX_NOSNOOP_LAT, cap++);
+}
+
+static void pci_restore_ltr_state(struct pci_dev *dev)
+{
+ struct pci_cap_saved_state *save_state;
+ int ltr;
+ u16 *cap;
+
+ save_state = pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_LTR);
+ ltr = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_LTR);
+ if (!save_state || !ltr)
+ return;
+
+ cap = (u16 *)&save_state->cap.data[0];
+ pci_write_config_word(dev, ltr + PCI_LTR_MAX_SNOOP_LAT, *cap++);
+ pci_write_config_word(dev, ltr + PCI_LTR_MAX_NOSNOOP_LAT, *cap++);
+}
/**
* pci_save_state - save the PCI configuration space of a device before suspending
@@ -1291,6 +1329,7 @@ int pci_save_state(struct pci_dev *dev)
if (i != 0)
return i;
+ pci_save_ltr_state(dev);
pci_save_dpc_state(dev);
return pci_save_vc_state(dev);
}
@@ -1390,7 +1429,12 @@ void pci_restore_state(struct pci_dev *dev)
if (!dev->state_saved)
return;
- /* PCI Express register must be restored first */
+ /*
+ * Restore max latencies (in the LTR capability) before enabling
+ * LTR itself (in the PCIe capability).
+ */
+ pci_restore_ltr_state(dev);
+
pci_restore_pcie_state(dev);
pci_restore_pasid_state(dev);
pci_restore_pri_state(dev);
@@ -2260,7 +2304,7 @@ static pci_power_t pci_target_state(struct pci_dev *dev, bool wakeup)
case PCI_D2:
if (pci_no_d1d2(dev))
break;
- /* else: fall through */
+ /* else, fall through */
default:
target_state = state;
}
@@ -2501,6 +2545,25 @@ void pci_config_pm_runtime_put(struct pci_dev *pdev)
pm_runtime_put_sync(parent);
}
+static const struct dmi_system_id bridge_d3_blacklist[] = {
+#ifdef CONFIG_X86
+ {
+ /*
+ * Gigabyte X299 root port is not marked as hotplug capable
+ * which allows Linux to power manage it. However, this
+ * confuses the BIOS SMI handler so don't power manage root
+ * ports on that system.
+ */
+ .ident = "X299 DESIGNARE EX-CF",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Gigabyte Technology Co., Ltd."),
+ DMI_MATCH(DMI_BOARD_NAME, "X299 DESIGNARE EX-CF"),
+ },
+ },
+#endif
+ { }
+};
+
/**
* pci_bridge_d3_possible - Is it possible to put the bridge into D3
* @bridge: Bridge to check
@@ -2546,6 +2609,9 @@ bool pci_bridge_d3_possible(struct pci_dev *bridge)
if (bridge->is_hotplug_bridge)
return false;
+ if (dmi_check_system(bridge_d3_blacklist))
+ return false;
+
/*
* It should be safe to put PCIe ports from 2015 or newer
* to D3.
@@ -2998,6 +3064,11 @@ void pci_allocate_cap_save_buffers(struct pci_dev *dev)
if (error)
pci_err(dev, "unable to preallocate PCI-X save buffer\n");
+ error = pci_add_ext_cap_save_buffer(dev, PCI_EXT_CAP_ID_LTR,
+ 2 * sizeof(u16));
+ if (error)
+ pci_err(dev, "unable to allocate suspend buffer for LTR\n");
+
pci_allocate_vc_save_buffers(dev);
}
@@ -5058,39 +5129,42 @@ unlock:
return 0;
}
-/* Save and disable devices from the top of the tree down */
-static void pci_bus_save_and_disable(struct pci_bus *bus)
+/*
+ * Save and disable devices from the top of the tree down while holding
+ * the @dev mutex lock for the entire tree.
+ */
+static void pci_bus_save_and_disable_locked(struct pci_bus *bus)
{
struct pci_dev *dev;
list_for_each_entry(dev, &bus->devices, bus_list) {
- pci_dev_lock(dev);
pci_dev_save_and_disable(dev);
- pci_dev_unlock(dev);
if (dev->subordinate)
- pci_bus_save_and_disable(dev->subordinate);
+ pci_bus_save_and_disable_locked(dev->subordinate);
}
}
/*
- * Restore devices from top of the tree down - parent bridges need to be
- * restored before we can get to subordinate devices.
+ * Restore devices from top of the tree down while holding @dev mutex lock
+ * for the entire tree. Parent bridges need to be restored before we can
+ * get to subordinate devices.
*/
-static void pci_bus_restore(struct pci_bus *bus)
+static void pci_bus_restore_locked(struct pci_bus *bus)
{
struct pci_dev *dev;
list_for_each_entry(dev, &bus->devices, bus_list) {
- pci_dev_lock(dev);
pci_dev_restore(dev);
- pci_dev_unlock(dev);
if (dev->subordinate)
- pci_bus_restore(dev->subordinate);
+ pci_bus_restore_locked(dev->subordinate);
}
}
-/* Save and disable devices from the top of the tree down */
-static void pci_slot_save_and_disable(struct pci_slot *slot)
+/*
+ * Save and disable devices from the top of the tree down while holding
+ * the @dev mutex lock for the entire tree.
+ */
+static void pci_slot_save_and_disable_locked(struct pci_slot *slot)
{
struct pci_dev *dev;
@@ -5099,26 +5173,25 @@ static void pci_slot_save_and_disable(struct pci_slot *slot)
continue;
pci_dev_save_and_disable(dev);
if (dev->subordinate)
- pci_bus_save_and_disable(dev->subordinate);
+ pci_bus_save_and_disable_locked(dev->subordinate);
}
}
/*
- * Restore devices from top of the tree down - parent bridges need to be
- * restored before we can get to subordinate devices.
+ * Restore devices from top of the tree down while holding @dev mutex lock
+ * for the entire tree. Parent bridges need to be restored before we can
+ * get to subordinate devices.
*/
-static void pci_slot_restore(struct pci_slot *slot)
+static void pci_slot_restore_locked(struct pci_slot *slot)
{
struct pci_dev *dev;
list_for_each_entry(dev, &slot->bus->devices, bus_list) {
if (!dev->slot || dev->slot != slot)
continue;
- pci_dev_lock(dev);
pci_dev_restore(dev);
- pci_dev_unlock(dev);
if (dev->subordinate)
- pci_bus_restore(dev->subordinate);
+ pci_bus_restore_locked(dev->subordinate);
}
}
@@ -5177,17 +5250,15 @@ static int __pci_reset_slot(struct pci_slot *slot)
if (rc)
return rc;
- pci_slot_save_and_disable(slot);
-
if (pci_slot_trylock(slot)) {
+ pci_slot_save_and_disable_locked(slot);
might_sleep();
rc = pci_reset_hotplug_slot(slot->hotplug, 0);
+ pci_slot_restore_locked(slot);
pci_slot_unlock(slot);
} else
rc = -EAGAIN;
- pci_slot_restore(slot);
-
return rc;
}
@@ -5273,17 +5344,15 @@ static int __pci_reset_bus(struct pci_bus *bus)
if (rc)
return rc;
- pci_bus_save_and_disable(bus);
-
if (pci_bus_trylock(bus)) {
+ pci_bus_save_and_disable_locked(bus);
might_sleep();
rc = pci_bridge_secondary_bus_reset(bus->self);
+ pci_bus_restore_locked(bus);
pci_bus_unlock(bus);
} else
rc = -EAGAIN;
- pci_bus_restore(bus);
-
return rc;
}
@@ -6000,8 +6069,7 @@ void pci_reassigndev_resource_alignment(struct pci_dev *dev)
* to enable the kernel to reassign new resource
* window later on.
*/
- if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE &&
- (dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) {
+ if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
for (i = PCI_BRIDGE_RESOURCES; i < PCI_NUM_RESOURCES; i++) {
r = &dev->resource[i];
if (!(r->flags & IORESOURCE_MEM))
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index 224d88634115..d994839a3e24 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -273,6 +273,7 @@ enum pcie_link_width pcie_get_width_cap(struct pci_dev *dev);
u32 pcie_bandwidth_capable(struct pci_dev *dev, enum pci_bus_speed *speed,
enum pcie_link_width *width);
void __pcie_print_link_status(struct pci_dev *dev, bool verbose);
+void pcie_report_downtraining(struct pci_dev *dev);
/* Single Root I/O Virtualization */
struct pci_sriov {
diff --git a/drivers/pci/pcie/Kconfig b/drivers/pci/pcie/Kconfig
index 44742b2e1126..5cbdbca904ac 100644
--- a/drivers/pci/pcie/Kconfig
+++ b/drivers/pci/pcie/Kconfig
@@ -6,10 +6,9 @@ config PCIEPORTBUS
bool "PCI Express Port Bus support"
depends on PCI
help
- This automatically enables PCI Express Port Bus support. Users can
- choose Native Hot-Plug support, Advanced Error Reporting support,
- Power Management Event support and Virtual Channel support to run
- on PCI Express Ports (Root or Switch).
+ This enables PCI Express Port Bus support. Users can then enable
+ support for Native Hot-Plug, Advanced Error Reporting, Power
+ Management Events, and Downstream Port Containment.
#
# Include service Kconfig here
diff --git a/drivers/pci/pcie/Makefile b/drivers/pci/pcie/Makefile
index ab514083d5d4..f1d7bc1e5efa 100644
--- a/drivers/pci/pcie/Makefile
+++ b/drivers/pci/pcie/Makefile
@@ -3,6 +3,7 @@
# Makefile for PCI Express features and port driver
pcieportdrv-y := portdrv_core.o portdrv_pci.o err.o
+pcieportdrv-y += bw_notification.o
obj-$(CONFIG_PCIEPORTBUS) += pcieportdrv.o
diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c
index fed29de783e0..f8fc2114ad39 100644
--- a/drivers/pci/pcie/aer.c
+++ b/drivers/pci/pcie/aer.c
@@ -117,7 +117,7 @@ bool pci_aer_available(void)
static int ecrc_policy = ECRC_POLICY_DEFAULT;
-static const char *ecrc_policy_str[] = {
+static const char * const ecrc_policy_str[] = {
[ECRC_POLICY_DEFAULT] = "bios",
[ECRC_POLICY_OFF] = "off",
[ECRC_POLICY_ON] = "on"
@@ -203,11 +203,8 @@ void pcie_ecrc_get_policy(char *str)
{
int i;
- for (i = 0; i < ARRAY_SIZE(ecrc_policy_str); i++)
- if (!strncmp(str, ecrc_policy_str[i],
- strlen(ecrc_policy_str[i])))
- break;
- if (i >= ARRAY_SIZE(ecrc_policy_str))
+ i = match_string(ecrc_policy_str, ARRAY_SIZE(ecrc_policy_str), str);
+ if (i < 0)
return;
ecrc_policy = i;
diff --git a/drivers/pci/pcie/bw_notification.c b/drivers/pci/pcie/bw_notification.c
new file mode 100644
index 000000000000..4fa9e3523ee1
--- /dev/null
+++ b/drivers/pci/pcie/bw_notification.c
@@ -0,0 +1,121 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * PCI Express Link Bandwidth Notification services driver
+ * Author: Alexandru Gagniuc <mr.nuke.me@gmail.com>
+ *
+ * Copyright (C) 2019, Dell Inc
+ *
+ * The PCIe Link Bandwidth Notification provides a way to notify the
+ * operating system when the link width or data rate changes. This
+ * capability is required for all root ports and downstream ports
+ * supporting links wider than x1 and/or multiple link speeds.
+ *
+ * This service port driver hooks into the bandwidth notification interrupt
+ * and warns when links become degraded in operation.
+ */
+
+#include "../pci.h"
+#include "portdrv.h"
+
+static bool pcie_link_bandwidth_notification_supported(struct pci_dev *dev)
+{
+ int ret;
+ u32 lnk_cap;
+
+ ret = pcie_capability_read_dword(dev, PCI_EXP_LNKCAP, &lnk_cap);
+ return (ret == PCIBIOS_SUCCESSFUL) && (lnk_cap & PCI_EXP_LNKCAP_LBNC);
+}
+
+static void pcie_enable_link_bandwidth_notification(struct pci_dev *dev)
+{
+ u16 lnk_ctl;
+
+ pcie_capability_write_word(dev, PCI_EXP_LNKSTA, PCI_EXP_LNKSTA_LBMS);
+
+ pcie_capability_read_word(dev, PCI_EXP_LNKCTL, &lnk_ctl);
+ lnk_ctl |= PCI_EXP_LNKCTL_LBMIE;
+ pcie_capability_write_word(dev, PCI_EXP_LNKCTL, lnk_ctl);
+}
+
+static void pcie_disable_link_bandwidth_notification(struct pci_dev *dev)
+{
+ u16 lnk_ctl;
+
+ pcie_capability_read_word(dev, PCI_EXP_LNKCTL, &lnk_ctl);
+ lnk_ctl &= ~PCI_EXP_LNKCTL_LBMIE;
+ pcie_capability_write_word(dev, PCI_EXP_LNKCTL, lnk_ctl);
+}
+
+static irqreturn_t pcie_bw_notification_irq(int irq, void *context)
+{
+ struct pcie_device *srv = context;
+ struct pci_dev *port = srv->port;
+ u16 link_status, events;
+ int ret;
+
+ ret = pcie_capability_read_word(port, PCI_EXP_LNKSTA, &link_status);
+ events = link_status & PCI_EXP_LNKSTA_LBMS;
+
+ if (ret != PCIBIOS_SUCCESSFUL || !events)
+ return IRQ_NONE;
+
+ pcie_capability_write_word(port, PCI_EXP_LNKSTA, events);
+ pcie_update_link_speed(port->subordinate, link_status);
+ return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t pcie_bw_notification_handler(int irq, void *context)
+{
+ struct pcie_device *srv = context;
+ struct pci_dev *port = srv->port;
+ struct pci_dev *dev;
+
+ /*
+ * Print status from downstream devices, not this root port or
+ * downstream switch port.
+ */
+ down_read(&pci_bus_sem);
+ list_for_each_entry(dev, &port->subordinate->devices, bus_list)
+ pcie_report_downtraining(dev);
+ up_read(&pci_bus_sem);
+
+ return IRQ_HANDLED;
+}
+
+static int pcie_bandwidth_notification_probe(struct pcie_device *srv)
+{
+ int ret;
+
+ /* Single-width or single-speed ports do not have to support this. */
+ if (!pcie_link_bandwidth_notification_supported(srv->port))
+ return -ENODEV;
+
+ ret = request_threaded_irq(srv->irq, pcie_bw_notification_irq,
+ pcie_bw_notification_handler,
+ IRQF_SHARED, "PCIe BW notif", srv);
+ if (ret)
+ return ret;
+
+ pcie_enable_link_bandwidth_notification(srv->port);
+
+ return 0;
+}
+
+static void pcie_bandwidth_notification_remove(struct pcie_device *srv)
+{
+ pcie_disable_link_bandwidth_notification(srv->port);
+ free_irq(srv->irq, srv);
+}
+
+static struct pcie_port_service_driver pcie_bandwidth_notification_driver = {
+ .name = "pcie_bw_notification",
+ .port_type = PCIE_ANY_PORT,
+ .service = PCIE_PORT_SERVICE_BWNOTIF,
+ .probe = pcie_bandwidth_notification_probe,
+ .remove = pcie_bandwidth_notification_remove,
+};
+
+int __init pcie_bandwidth_notification_init(void)
+{
+ return pcie_port_service_register(&pcie_bandwidth_notification_driver);
+}
diff --git a/drivers/pci/pcie/dpc.c b/drivers/pci/pcie/dpc.c
index e435d12e61a0..7b77754a82de 100644
--- a/drivers/pci/pcie/dpc.c
+++ b/drivers/pci/pcie/dpc.c
@@ -202,6 +202,28 @@ static void dpc_process_rp_pio_error(struct dpc_dev *dpc)
pci_write_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_STATUS, status);
}
+static int dpc_get_aer_uncorrect_severity(struct pci_dev *dev,
+ struct aer_err_info *info)
+{
+ int pos = dev->aer_cap;
+ u32 status, mask, sev;
+
+ pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status);
+ pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, &mask);
+ status &= ~mask;
+ if (!status)
+ return 0;
+
+ pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, &sev);
+ status &= sev;
+ if (status)
+ info->severity = AER_FATAL;
+ else
+ info->severity = AER_NONFATAL;
+
+ return 1;
+}
+
static irqreturn_t dpc_handler(int irq, void *context)
{
struct aer_err_info info;
@@ -229,9 +251,12 @@ static irqreturn_t dpc_handler(int irq, void *context)
/* show RP PIO error detail information */
if (dpc->rp_extensions && reason == 3 && ext_reason == 0)
dpc_process_rp_pio_error(dpc);
- else if (reason == 0 && aer_get_device_error_info(pdev, &info)) {
+ else if (reason == 0 &&
+ dpc_get_aer_uncorrect_severity(pdev, &info) &&
+ aer_get_device_error_info(pdev, &info)) {
aer_print_error(pdev, &info);
pci_cleanup_aer_uncorrect_error_status(pdev);
+ pci_aer_clear_fatal_status(pdev);
}
/* We configure DPC so it only triggers on ERR_FATAL */
diff --git a/drivers/pci/pcie/pme.c b/drivers/pci/pcie/pme.c
index 0dbcf429089f..54d593d10396 100644
--- a/drivers/pci/pcie/pme.c
+++ b/drivers/pci/pcie/pme.c
@@ -363,6 +363,16 @@ static bool pcie_pme_check_wakeup(struct pci_bus *bus)
return false;
}
+static void pcie_pme_disable_interrupt(struct pci_dev *port,
+ struct pcie_pme_service_data *data)
+{
+ spin_lock_irq(&data->lock);
+ pcie_pme_interrupt_enable(port, false);
+ pcie_clear_root_pme_status(port);
+ data->noirq = true;
+ spin_unlock_irq(&data->lock);
+}
+
/**
* pcie_pme_suspend - Suspend PCIe PME service device.
* @srv: PCIe service device to suspend.
@@ -387,11 +397,7 @@ static int pcie_pme_suspend(struct pcie_device *srv)
return 0;
}
- spin_lock_irq(&data->lock);
- pcie_pme_interrupt_enable(port, false);
- pcie_clear_root_pme_status(port);
- data->noirq = true;
- spin_unlock_irq(&data->lock);
+ pcie_pme_disable_interrupt(port, data);
synchronize_irq(srv->irq);
@@ -427,34 +433,12 @@ static int pcie_pme_resume(struct pcie_device *srv)
*/
static void pcie_pme_remove(struct pcie_device *srv)
{
- pcie_pme_suspend(srv);
- free_irq(srv->irq, srv);
- kfree(get_service_data(srv));
-}
-
-static int pcie_pme_runtime_suspend(struct pcie_device *srv)
-{
- struct pcie_pme_service_data *data = get_service_data(srv);
-
- spin_lock_irq(&data->lock);
- pcie_pme_interrupt_enable(srv->port, false);
- pcie_clear_root_pme_status(srv->port);
- data->noirq = true;
- spin_unlock_irq(&data->lock);
-
- return 0;
-}
-
-static int pcie_pme_runtime_resume(struct pcie_device *srv)
-{
struct pcie_pme_service_data *data = get_service_data(srv);
- spin_lock_irq(&data->lock);
- pcie_pme_interrupt_enable(srv->port, true);
- data->noirq = false;
- spin_unlock_irq(&data->lock);
-
- return 0;
+ pcie_pme_disable_interrupt(srv->port, data);
+ free_irq(srv->irq, srv);
+ cancel_work_sync(&data->work);
+ kfree(data);
}
static struct pcie_port_service_driver pcie_pme_driver = {
@@ -464,8 +448,6 @@ static struct pcie_port_service_driver pcie_pme_driver = {
.probe = pcie_pme_probe,
.suspend = pcie_pme_suspend,
- .runtime_suspend = pcie_pme_runtime_suspend,
- .runtime_resume = pcie_pme_runtime_resume,
.resume = pcie_pme_resume,
.remove = pcie_pme_remove,
};
diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h
index fbbf00b0992e..1d50dc58ac40 100644
--- a/drivers/pci/pcie/portdrv.h
+++ b/drivers/pci/pcie/portdrv.h
@@ -20,8 +20,10 @@
#define PCIE_PORT_SERVICE_HP (1 << PCIE_PORT_SERVICE_HP_SHIFT)
#define PCIE_PORT_SERVICE_DPC_SHIFT 3 /* Downstream Port Containment */
#define PCIE_PORT_SERVICE_DPC (1 << PCIE_PORT_SERVICE_DPC_SHIFT)
+#define PCIE_PORT_SERVICE_BWNOTIF_SHIFT 4 /* Bandwidth notification */
+#define PCIE_PORT_SERVICE_BWNOTIF (1 << PCIE_PORT_SERVICE_BWNOTIF_SHIFT)
-#define PCIE_PORT_DEVICE_MAXSERVICES 4
+#define PCIE_PORT_DEVICE_MAXSERVICES 5
#ifdef CONFIG_PCIEAER
int pcie_aer_init(void);
@@ -47,6 +49,8 @@ int pcie_dpc_init(void);
static inline int pcie_dpc_init(void) { return 0; }
#endif
+int pcie_bandwidth_notification_init(void);
+
/* Port Type */
#define PCIE_ANY_PORT (~0)
diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c
index f458ac9cb70c..7d04f9d087a6 100644
--- a/drivers/pci/pcie/portdrv_core.c
+++ b/drivers/pci/pcie/portdrv_core.c
@@ -99,7 +99,7 @@ static int pcie_message_numbers(struct pci_dev *dev, int mask,
*/
static int pcie_port_enable_irq_vec(struct pci_dev *dev, int *irqs, int mask)
{
- int nr_entries, nvec;
+ int nr_entries, nvec, pcie_irq;
u32 pme = 0, aer = 0, dpc = 0;
/* Allocate the maximum possible number of MSI/MSI-X vectors */
@@ -135,10 +135,13 @@ static int pcie_port_enable_irq_vec(struct pci_dev *dev, int *irqs, int mask)
return nr_entries;
}
- /* PME and hotplug share an MSI/MSI-X vector */
- if (mask & (PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP)) {
- irqs[PCIE_PORT_SERVICE_PME_SHIFT] = pci_irq_vector(dev, pme);
- irqs[PCIE_PORT_SERVICE_HP_SHIFT] = pci_irq_vector(dev, pme);
+ /* PME, hotplug and bandwidth notification share an MSI/MSI-X vector */
+ if (mask & (PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP |
+ PCIE_PORT_SERVICE_BWNOTIF)) {
+ pcie_irq = pci_irq_vector(dev, pme);
+ irqs[PCIE_PORT_SERVICE_PME_SHIFT] = pcie_irq;
+ irqs[PCIE_PORT_SERVICE_HP_SHIFT] = pcie_irq;
+ irqs[PCIE_PORT_SERVICE_BWNOTIF_SHIFT] = pcie_irq;
}
if (mask & PCIE_PORT_SERVICE_AER)
@@ -250,6 +253,10 @@ static int get_port_device_capability(struct pci_dev *dev)
pci_aer_available() && services & PCIE_PORT_SERVICE_AER)
services |= PCIE_PORT_SERVICE_DPC;
+ if (pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM ||
+ pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT)
+ services |= PCIE_PORT_SERVICE_BWNOTIF;
+
return services;
}
diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c
index 0acca3596807..0a87091a0800 100644
--- a/drivers/pci/pcie/portdrv_pci.c
+++ b/drivers/pci/pcie/portdrv_pci.c
@@ -182,10 +182,12 @@ static void pcie_portdrv_err_resume(struct pci_dev *dev)
/*
* LINUX Device Driver Model
*/
-static const struct pci_device_id port_pci_ids[] = { {
+static const struct pci_device_id port_pci_ids[] = {
/* handle any PCI-Express port */
- PCI_DEVICE_CLASS(((PCI_CLASS_BRIDGE_PCI << 8) | 0x00), ~0),
- }, { /* end: all zeroes */ }
+ { PCI_DEVICE_CLASS(((PCI_CLASS_BRIDGE_PCI << 8) | 0x00), ~0) },
+ /* subtractive decode PCI-to-PCI bridge, class type is 060401h */
+ { PCI_DEVICE_CLASS(((PCI_CLASS_BRIDGE_PCI << 8) | 0x01), ~0) },
+ { },
};
static const struct pci_error_handlers pcie_portdrv_err_handler = {
@@ -238,6 +240,7 @@ static void __init pcie_init_services(void)
pcie_pme_init();
pcie_dpc_init();
pcie_hp_init();
+ pcie_bandwidth_notification_init();
}
static int __init pcie_portdrv_init(void)
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 257b9f6f2ebb..7e12d0163863 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -121,13 +121,13 @@ static u64 pci_size(u64 base, u64 maxbase, u64 mask)
* Get the lowest of them to find the decode size, and from that
* the extent.
*/
- size = (size & ~(size-1)) - 1;
+ size = size & ~(size-1);
/*
* base == maxbase can be valid only if the BAR has already been
* programmed with all 1s.
*/
- if (base == maxbase && ((base | size) & mask) != mask)
+ if (base == maxbase && ((base | (size - 1)) & mask) != mask)
return 0;
return size;
@@ -278,7 +278,7 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
/* Above 32-bit boundary; try to reallocate */
res->flags |= IORESOURCE_UNSET;
res->start = 0;
- res->end = sz64;
+ res->end = sz64 - 1;
pci_info(dev, "reg 0x%x: can't handle BAR above 4GB (bus address %#010llx)\n",
pos, (unsigned long long)l64);
goto out;
@@ -286,7 +286,7 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
}
region.start = l64;
- region.end = l64 + sz64;
+ region.end = l64 + sz64 - 1;
pcibios_bus_to_resource(dev->bus, res, &region);
pcibios_resource_to_bus(dev->bus, &inverted_region, res);
@@ -348,6 +348,57 @@ static void pci_read_bases(struct pci_dev *dev, unsigned int howmany, int rom)
}
}
+static void pci_read_bridge_windows(struct pci_dev *bridge)
+{
+ u16 io;
+ u32 pmem, tmp;
+
+ pci_read_config_word(bridge, PCI_IO_BASE, &io);
+ if (!io) {
+ pci_write_config_word(bridge, PCI_IO_BASE, 0xe0f0);
+ pci_read_config_word(bridge, PCI_IO_BASE, &io);
+ pci_write_config_word(bridge, PCI_IO_BASE, 0x0);
+ }
+ if (io)
+ bridge->io_window = 1;
+
+ /*
+ * DECchip 21050 pass 2 errata: the bridge may miss an address
+ * disconnect boundary by one PCI data phase. Workaround: do not
+ * use prefetching on this device.
+ */
+ if (bridge->vendor == PCI_VENDOR_ID_DEC && bridge->device == 0x0001)
+ return;
+
+ pci_read_config_dword(bridge, PCI_PREF_MEMORY_BASE, &pmem);
+ if (!pmem) {
+ pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE,
+ 0xffe0fff0);
+ pci_read_config_dword(bridge, PCI_PREF_MEMORY_BASE, &pmem);
+ pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, 0x0);
+ }
+ if (!pmem)
+ return;
+
+ bridge->pref_window = 1;
+
+ if ((pmem & PCI_PREF_RANGE_TYPE_MASK) == PCI_PREF_RANGE_TYPE_64) {
+
+ /*
+ * Bridge claims to have a 64-bit prefetchable memory
+ * window; verify that the upper bits are actually
+ * writable.
+ */
+ pci_read_config_dword(bridge, PCI_PREF_BASE_UPPER32, &pmem);
+ pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32,
+ 0xffffffff);
+ pci_read_config_dword(bridge, PCI_PREF_BASE_UPPER32, &tmp);
+ pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32, pmem);
+ if (tmp)
+ bridge->pref_64_window = 1;
+ }
+}
+
static void pci_read_bridge_io(struct pci_bus *child)
{
struct pci_dev *dev = child->self;
@@ -1728,9 +1779,6 @@ int pci_setup_device(struct pci_dev *dev)
break;
case PCI_HEADER_TYPE_BRIDGE: /* bridge header */
- if (class != PCI_CLASS_BRIDGE_PCI)
- goto bad;
-
/*
* The PCI-to-PCI bridge spec requires that subtractive
* decoding (i.e. transparent) bridge must have programming
@@ -1739,6 +1787,7 @@ int pci_setup_device(struct pci_dev *dev)
pci_read_irq(dev);
dev->transparent = ((dev->class & 0xff) == 1);
pci_read_bases(dev, 2, PCI_ROM_ADDRESS1);
+ pci_read_bridge_windows(dev);
set_pcie_hotplug_bridge(dev);
pos = pci_find_capability(dev, PCI_CAP_ID_SSVID);
if (pos) {
@@ -1856,8 +1905,6 @@ static void program_hpp_type0(struct pci_dev *dev, struct hpp_type0 *hpp)
pci_write_config_byte(dev, PCI_SEC_LATENCY_TIMER,
hpp->latency_timer);
pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &pci_bctl);
- if (hpp->enable_serr)
- pci_bctl |= PCI_BRIDGE_CTL_SERR;
if (hpp->enable_perr)
pci_bctl |= PCI_BRIDGE_CTL_PARITY;
pci_write_config_word(dev, PCI_BRIDGE_CONTROL, pci_bctl);
@@ -2071,11 +2118,8 @@ static void pci_configure_ltr(struct pci_dev *dev)
{
#ifdef CONFIG_PCIEASPM
struct pci_host_bridge *host = pci_find_host_bridge(dev->bus);
- u32 cap;
struct pci_dev *bridge;
-
- if (!host->native_ltr)
- return;
+ u32 cap, ctl;
if (!pci_is_pcie(dev))
return;
@@ -2084,22 +2128,35 @@ static void pci_configure_ltr(struct pci_dev *dev)
if (!(cap & PCI_EXP_DEVCAP2_LTR))
return;
- /*
- * Software must not enable LTR in an Endpoint unless the Root
- * Complex and all intermediate Switches indicate support for LTR.
- * PCIe r3.1, sec 6.18.
- */
- if (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT)
- dev->ltr_path = 1;
- else {
+ pcie_capability_read_dword(dev, PCI_EXP_DEVCTL2, &ctl);
+ if (ctl & PCI_EXP_DEVCTL2_LTR_EN) {
+ if (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT) {
+ dev->ltr_path = 1;
+ return;
+ }
+
bridge = pci_upstream_bridge(dev);
if (bridge && bridge->ltr_path)
dev->ltr_path = 1;
+
+ return;
}
- if (dev->ltr_path)
+ if (!host->native_ltr)
+ return;
+
+ /*
+ * Software must not enable LTR in an Endpoint unless the Root
+ * Complex and all intermediate Switches indicate support for LTR.
+ * PCIe r4.0, sec 6.18.
+ */
+ if (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT ||
+ ((bridge = pci_upstream_bridge(dev)) &&
+ bridge->ltr_path)) {
pcie_capability_set_word(dev, PCI_EXP_DEVCTL2,
PCI_EXP_DEVCTL2_LTR_EN);
+ dev->ltr_path = 1;
+ }
#endif
}
@@ -2129,6 +2186,24 @@ static void pci_configure_eetlp_prefix(struct pci_dev *dev)
#endif
}
+static void pci_configure_serr(struct pci_dev *dev)
+{
+ u16 control;
+
+ if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
+
+ /*
+ * A bridge will not forward ERR_ messages coming from an
+ * endpoint unless SERR# forwarding is enabled.
+ */
+ pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &control);
+ if (!(control & PCI_BRIDGE_CTL_SERR)) {
+ control |= PCI_BRIDGE_CTL_SERR;
+ pci_write_config_word(dev, PCI_BRIDGE_CONTROL, control);
+ }
+ }
+}
+
static void pci_configure_device(struct pci_dev *dev)
{
struct hotplug_params hpp;
@@ -2139,6 +2214,7 @@ static void pci_configure_device(struct pci_dev *dev)
pci_configure_relaxed_ordering(dev);
pci_configure_ltr(dev);
pci_configure_eetlp_prefix(dev);
+ pci_configure_serr(dev);
memset(&hpp, 0, sizeof(hpp));
ret = pci_get_hp_params(dev, &hpp);
@@ -2312,7 +2388,7 @@ static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn)
return dev;
}
-static void pcie_report_downtraining(struct pci_dev *dev)
+void pcie_report_downtraining(struct pci_dev *dev)
{
if (!pci_is_pcie(dev))
return;
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index e2a879e93d86..a59ad09ce911 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -2139,7 +2139,7 @@ static void quirk_netmos(struct pci_dev *dev)
if (dev->subsystem_vendor == PCI_VENDOR_ID_IBM &&
dev->subsystem_device == 0x0299)
return;
- /* else: fall through */
+ /* else, fall through */
case PCI_DEVICE_ID_NETMOS_9735:
case PCI_DEVICE_ID_NETMOS_9745:
case PCI_DEVICE_ID_NETMOS_9845:
@@ -4520,6 +4520,8 @@ static const struct pci_dev_acs_enabled {
/* QCOM QDF2xxx root ports */
{ PCI_VENDOR_ID_QCOM, 0x0400, pci_quirk_qcom_rp_acs },
{ PCI_VENDOR_ID_QCOM, 0x0401, pci_quirk_qcom_rp_acs },
+ /* HXT SD4800 root ports. The ACS design is same as QCOM QDF2xxx */
+ { PCI_VENDOR_ID_HXT, 0x0401, pci_quirk_qcom_rp_acs },
/* Intel PCH root ports */
{ PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_quirk_intel_pch_acs },
{ PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_quirk_intel_spt_pch_acs },
diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c
index ed960436df5e..ec44a0f3a7ac 100644
--- a/drivers/pci/setup-bus.c
+++ b/drivers/pci/setup-bus.c
@@ -735,58 +735,21 @@ int pci_claim_bridge_resource(struct pci_dev *bridge, int i)
base/limit registers must be read-only and read as 0. */
static void pci_bridge_check_ranges(struct pci_bus *bus)
{
- u16 io;
- u32 pmem;
struct pci_dev *bridge = bus->self;
- struct resource *b_res;
+ struct resource *b_res = &bridge->resource[PCI_BRIDGE_RESOURCES];
- b_res = &bridge->resource[PCI_BRIDGE_RESOURCES];
b_res[1].flags |= IORESOURCE_MEM;
- pci_read_config_word(bridge, PCI_IO_BASE, &io);
- if (!io) {
- pci_write_config_word(bridge, PCI_IO_BASE, 0xe0f0);
- pci_read_config_word(bridge, PCI_IO_BASE, &io);
- pci_write_config_word(bridge, PCI_IO_BASE, 0x0);
- }
- if (io)
+ if (bridge->io_window)
b_res[0].flags |= IORESOURCE_IO;
- /* DECchip 21050 pass 2 errata: the bridge may miss an address
- disconnect boundary by one PCI data phase.
- Workaround: do not use prefetching on this device. */
- if (bridge->vendor == PCI_VENDOR_ID_DEC && bridge->device == 0x0001)
- return;
-
- pci_read_config_dword(bridge, PCI_PREF_MEMORY_BASE, &pmem);
- if (!pmem) {
- pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE,
- 0xffe0fff0);
- pci_read_config_dword(bridge, PCI_PREF_MEMORY_BASE, &pmem);
- pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, 0x0);
- }
- if (pmem) {
+ if (bridge->pref_window) {
b_res[2].flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH;
- if ((pmem & PCI_PREF_RANGE_TYPE_MASK) ==
- PCI_PREF_RANGE_TYPE_64) {
+ if (bridge->pref_64_window) {
b_res[2].flags |= IORESOURCE_MEM_64;
b_res[2].flags |= PCI_PREF_RANGE_TYPE_64;
}
}
-
- /* double check if bridge does support 64 bit pref */
- if (b_res[2].flags & IORESOURCE_MEM_64) {
- u32 mem_base_hi, tmp;
- pci_read_config_dword(bridge, PCI_PREF_BASE_UPPER32,
- &mem_base_hi);
- pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32,
- 0xffffffff);
- pci_read_config_dword(bridge, PCI_PREF_BASE_UPPER32, &tmp);
- if (!tmp)
- b_res[2].flags &= ~IORESOURCE_MEM_64;
- pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32,
- mem_base_hi);
- }
}
/* Helper function for sizing routines: find first available
@@ -1223,12 +1186,12 @@ void __pci_bus_size_bridges(struct pci_bus *bus, struct list_head *realloc_head)
if (!b)
continue;
- switch (dev->class >> 8) {
- case PCI_CLASS_BRIDGE_CARDBUS:
+ switch (dev->hdr_type) {
+ case PCI_HEADER_TYPE_CARDBUS:
pci_bus_size_cardbus(b, realloc_head);
break;
- case PCI_CLASS_BRIDGE_PCI:
+ case PCI_HEADER_TYPE_BRIDGE:
default:
__pci_bus_size_bridges(b, realloc_head);
break;
@@ -1239,12 +1202,12 @@ void __pci_bus_size_bridges(struct pci_bus *bus, struct list_head *realloc_head)
if (pci_is_root_bus(bus))
return;
- switch (bus->self->class >> 8) {
- case PCI_CLASS_BRIDGE_CARDBUS:
+ switch (bus->self->hdr_type) {
+ case PCI_HEADER_TYPE_CARDBUS:
/* don't size cardbuses yet. */
break;
- case PCI_CLASS_BRIDGE_PCI:
+ case PCI_HEADER_TYPE_BRIDGE:
pci_bridge_check_ranges(bus);
if (bus->self->is_hotplug_bridge) {
additional_io_size = pci_hotplug_io_size;
@@ -1393,13 +1356,13 @@ void __pci_bus_assign_resources(const struct pci_bus *bus,
__pci_bus_assign_resources(b, realloc_head, fail_head);
- switch (dev->class >> 8) {
- case PCI_CLASS_BRIDGE_PCI:
+ switch (dev->hdr_type) {
+ case PCI_HEADER_TYPE_BRIDGE:
if (!pci_is_enabled(dev))
pci_setup_bridge(b);
break;
- case PCI_CLASS_BRIDGE_CARDBUS:
+ case PCI_HEADER_TYPE_CARDBUS:
pci_setup_cardbus(b);
break;
diff --git a/drivers/perf/xgene_pmu.c b/drivers/perf/xgene_pmu.c
index d4ec04868d59..27574e85f351 100644
--- a/drivers/perf/xgene_pmu.c
+++ b/drivers/perf/xgene_pmu.c
@@ -1052,7 +1052,6 @@ static void xgene_perf_start(struct perf_event *event, int flags)
static void xgene_perf_stop(struct perf_event *event, int flags)
{
struct hw_perf_event *hw = &event->hw;
- u64 config;
if (hw->state & PERF_HES_UPTODATE)
return;
@@ -1064,7 +1063,6 @@ static void xgene_perf_stop(struct perf_event *event, int flags)
if (hw->state & PERF_HES_UPTODATE)
return;
- config = hw->config;
xgene_perf_read(event);
hw->state |= PERF_HES_UPTODATE;
}
diff --git a/drivers/phy/allwinner/phy-sun4i-usb.c b/drivers/phy/allwinner/phy-sun4i-usb.c
index 5163097b43df..4bbd9ede38c8 100644
--- a/drivers/phy/allwinner/phy-sun4i-usb.c
+++ b/drivers/phy/allwinner/phy-sun4i-usb.c
@@ -485,8 +485,11 @@ static int sun4i_usb_phy_set_mode(struct phy *_phy,
struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
int new_mode;
- if (phy->index != 0)
+ if (phy->index != 0) {
+ if (mode == PHY_MODE_USB_HOST)
+ return 0;
return -EINVAL;
+ }
switch (mode) {
case PHY_MODE_USB_HOST:
diff --git a/drivers/pinctrl/bcm/pinctrl-bcm2835.c b/drivers/pinctrl/bcm/pinctrl-bcm2835.c
index f180aa44a422..183d1ffe6a75 100644
--- a/drivers/pinctrl/bcm/pinctrl-bcm2835.c
+++ b/drivers/pinctrl/bcm/pinctrl-bcm2835.c
@@ -341,6 +341,7 @@ static const struct gpio_chip bcm2835_gpio_chip = {
.get_direction = bcm2835_gpio_get_direction,
.get = bcm2835_gpio_get,
.set = bcm2835_gpio_set,
+ .set_config = gpiochip_generic_config,
.base = -1,
.ngpio = BCM2835_NUM_GPIOS,
.can_sleep = false,
@@ -960,7 +961,7 @@ static int bcm2835_pinconf_set(struct pinctrl_dev *pctldev,
break;
default:
- return -EINVAL;
+ return -ENOTSUPP;
} /* switch param type */
} /* for each config */
@@ -969,6 +970,7 @@ static int bcm2835_pinconf_set(struct pinctrl_dev *pctldev,
}
static const struct pinconf_ops bcm2835_pinconf_ops = {
+ .is_generic = true,
.pin_config_get = bcm2835_pinconf_get,
.pin_config_set = bcm2835_pinconf_set,
};
diff --git a/drivers/pinctrl/berlin/pinctrl-as370.c b/drivers/pinctrl/berlin/pinctrl-as370.c
index d2bb811fc5fa..44f8ccdbeeff 100644
--- a/drivers/pinctrl/berlin/pinctrl-as370.c
+++ b/drivers/pinctrl/berlin/pinctrl-as370.c
@@ -36,13 +36,13 @@ static const struct berlin_desc_group as370_soc_pinctrl_groups[] = {
BERLIN_PINCTRL_GROUP("I2S1_DO2", 0x0, 0x3, 0x0c,
BERLIN_PINCTRL_FUNCTION(0x0, "por"), /* CORE RSTB */
BERLIN_PINCTRL_FUNCTION(0x1, "i2s1"), /* DO2 */
- BERLIN_PINCTRL_FUNCTION(0x2, "pwm4"),
+ BERLIN_PINCTRL_FUNCTION(0x2, "pwm"), /* PWM4 */
BERLIN_PINCTRL_FUNCTION(0x3, "gpio"), /* GPIO4 */
BERLIN_PINCTRL_FUNCTION(0x5, "phy")), /* DBG4 */
BERLIN_PINCTRL_GROUP("I2S1_DO3", 0x0, 0x3, 0x0f,
BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO5 */
BERLIN_PINCTRL_FUNCTION(0x1, "i2s1"), /* DO3 */
- BERLIN_PINCTRL_FUNCTION(0x2, "pwm5"),
+ BERLIN_PINCTRL_FUNCTION(0x2, "pwm"), /* PWM5 */
BERLIN_PINCTRL_FUNCTION(0x3, "spififib"), /* SPDIFIB */
BERLIN_PINCTRL_FUNCTION(0x4, "spdifo"), /* SPDIFO */
BERLIN_PINCTRL_FUNCTION(0x5, "phy")), /* DBG5 */
@@ -61,24 +61,24 @@ static const struct berlin_desc_group as370_soc_pinctrl_groups[] = {
BERLIN_PINCTRL_GROUP("I2S2_DI0", 0x0, 0x3, 0x1b,
BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO9 */
BERLIN_PINCTRL_FUNCTION(0x1, "i2s2"), /* DI0 */
- BERLIN_PINCTRL_FUNCTION(0x2, "pwm2"),
+ BERLIN_PINCTRL_FUNCTION(0x2, "pwm"), /* PWM2 */
BERLIN_PINCTRL_FUNCTION(0x5, "phy")), /* DBG9 */
BERLIN_PINCTRL_GROUP("I2S2_DI1", 0x4, 0x3, 0x00,
BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO10 */
BERLIN_PINCTRL_FUNCTION(0x1, "i2s2"), /* DI1 */
- BERLIN_PINCTRL_FUNCTION(0x2, "pwm3"),
+ BERLIN_PINCTRL_FUNCTION(0x2, "pwm"), /* PWM3 */
BERLIN_PINCTRL_FUNCTION(0x5, "phy")), /* DBG10 */
BERLIN_PINCTRL_GROUP("I2S2_DI2", 0x4, 0x3, 0x03,
BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO11 */
BERLIN_PINCTRL_FUNCTION(0x1, "i2s2"), /* DI2 */
- BERLIN_PINCTRL_FUNCTION(0x2, "pwm6"),
+ BERLIN_PINCTRL_FUNCTION(0x2, "pwm"), /* PWM6 */
BERLIN_PINCTRL_FUNCTION(0x3, "spdific"), /* SPDIFIC */
BERLIN_PINCTRL_FUNCTION(0x4, "spdifo"), /* SPDIFO */
BERLIN_PINCTRL_FUNCTION(0x5, "phy")), /* DBG11 */
BERLIN_PINCTRL_GROUP("I2S2_DI3", 0x4, 0x3, 0x06,
BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO12 */
BERLIN_PINCTRL_FUNCTION(0x1, "i2s2"), /* DI3 */
- BERLIN_PINCTRL_FUNCTION(0x2, "pwm7"),
+ BERLIN_PINCTRL_FUNCTION(0x2, "pwm"), /* PWM7 */
BERLIN_PINCTRL_FUNCTION(0x3, "spdifia"), /* SPDIFIA */
BERLIN_PINCTRL_FUNCTION(0x4, "spdifo"), /* SPDIFO */
BERLIN_PINCTRL_FUNCTION(0x5, "phy")), /* DBG12 */
@@ -98,14 +98,14 @@ static const struct berlin_desc_group as370_soc_pinctrl_groups[] = {
BERLIN_PINCTRL_GROUP("PDM_DI2", 0x4, 0x3, 0x12,
BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO16 */
BERLIN_PINCTRL_FUNCTION(0x1, "pdm"), /* DI2 */
- BERLIN_PINCTRL_FUNCTION(0x2, "pwm4"),
+ BERLIN_PINCTRL_FUNCTION(0x2, "pwm"), /* PWM4 */
BERLIN_PINCTRL_FUNCTION(0x3, "spdifid"), /* SPDIFID */
BERLIN_PINCTRL_FUNCTION(0x4, "spdifo"), /* SPDIFO */
BERLIN_PINCTRL_FUNCTION(0x5, "phy")), /* DBG16 */
BERLIN_PINCTRL_GROUP("PDM_DI3", 0x4, 0x3, 0x15,
BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO17 */
BERLIN_PINCTRL_FUNCTION(0x1, "pdm"), /* DI3 */
- BERLIN_PINCTRL_FUNCTION(0x2, "pwm5"),
+ BERLIN_PINCTRL_FUNCTION(0x2, "pwm"), /* PWM5 */
BERLIN_PINCTRL_FUNCTION(0x3, "spdifi"), /* SPDIFI */
BERLIN_PINCTRL_FUNCTION(0x4, "spdifo"), /* SPDIFO */
BERLIN_PINCTRL_FUNCTION(0x5, "phy")), /* DBG17 */
@@ -139,11 +139,11 @@ static const struct berlin_desc_group as370_soc_pinctrl_groups[] = {
BERLIN_PINCTRL_FUNCTION(0x1, "emmc")), /* DATA7 */
BERLIN_PINCTRL_GROUP("NAND_ALE", 0x8, 0x3, 0x12,
BERLIN_PINCTRL_FUNCTION(0x0, "nand"), /* ALE */
- BERLIN_PINCTRL_FUNCTION(0x2, "pwm6"),
+ BERLIN_PINCTRL_FUNCTION(0x2, "pwm"), /* PWM6 */
BERLIN_PINCTRL_FUNCTION(0x3, "gpio")), /* GPIO18 */
BERLIN_PINCTRL_GROUP("NAND_CLE", 0x8, 0x3, 0x15,
BERLIN_PINCTRL_FUNCTION(0x0, "nand"), /* CLE */
- BERLIN_PINCTRL_FUNCTION(0x2, "pwm7"),
+ BERLIN_PINCTRL_FUNCTION(0x2, "pwm"), /* PWM7 */
BERLIN_PINCTRL_FUNCTION(0x3, "gpio")), /* GPIO19 */
BERLIN_PINCTRL_GROUP("NAND_WEn", 0x8, 0x3, 0x18,
BERLIN_PINCTRL_FUNCTION(0x0, "nand"), /* WEn */
@@ -169,12 +169,12 @@ static const struct berlin_desc_group as370_soc_pinctrl_groups[] = {
BERLIN_PINCTRL_GROUP("SPI1_SS1n", 0xc, 0x3, 0x0c,
BERLIN_PINCTRL_FUNCTION(0x0, "spi1"), /* SS1n */
BERLIN_PINCTRL_FUNCTION(0x2, "gpio"), /* GPIO26 */
- BERLIN_PINCTRL_FUNCTION(0x3, "pwm2")),
+ BERLIN_PINCTRL_FUNCTION(0x3, "pwm")), /* PWM2 */
BERLIN_PINCTRL_GROUP("SPI1_SS2n", 0xc, 0x3, 0x0f,
BERLIN_PINCTRL_FUNCTION(0x0, "uart0"), /* RXD */
BERLIN_PINCTRL_FUNCTION(0x1, "spi1"), /* SS2n */
BERLIN_PINCTRL_FUNCTION(0x2, "gpio"), /* GPIO27 */
- BERLIN_PINCTRL_FUNCTION(0x3, "pwm3")),
+ BERLIN_PINCTRL_FUNCTION(0x3, "pwm")), /* PWM3 */
BERLIN_PINCTRL_GROUP("SPI1_SS3n", 0xc, 0x3, 0x12,
BERLIN_PINCTRL_FUNCTION(0x0, "uart0"), /* TXD */
BERLIN_PINCTRL_FUNCTION(0x1, "spi1"), /* SS3n */
@@ -182,11 +182,11 @@ static const struct berlin_desc_group as370_soc_pinctrl_groups[] = {
BERLIN_PINCTRL_GROUP("SPI1_SCLK", 0xc, 0x3, 0x15,
BERLIN_PINCTRL_FUNCTION(0x0, "spi1"), /* SCLK */
BERLIN_PINCTRL_FUNCTION(0x1, "gpio"), /* GPIO29 */
- BERLIN_PINCTRL_FUNCTION(0x3, "pwm4")),
+ BERLIN_PINCTRL_FUNCTION(0x3, "pwm")), /* PWM4 */
BERLIN_PINCTRL_GROUP("SPI1_SDO", 0xc, 0x3, 0x18,
BERLIN_PINCTRL_FUNCTION(0x0, "spi1"), /* SDO */
BERLIN_PINCTRL_FUNCTION(0x1, "gpio"), /* GPIO30 */
- BERLIN_PINCTRL_FUNCTION(0x3, "pwm5")),
+ BERLIN_PINCTRL_FUNCTION(0x3, "pwm")), /* PWM5 */
BERLIN_PINCTRL_GROUP("SPI1_SDI", 0xc, 0x3, 0x1b,
BERLIN_PINCTRL_FUNCTION(0x0, "spi1"), /* SDI */
BERLIN_PINCTRL_FUNCTION(0x1, "gpio")), /* GPIO31 */
@@ -209,51 +209,51 @@ static const struct berlin_desc_group as370_soc_pinctrl_groups[] = {
BERLIN_PINCTRL_GROUP("TMS", 0x10, 0x3, 0x0f,
BERLIN_PINCTRL_FUNCTION(0x0, "jtag"), /* TMS */
BERLIN_PINCTRL_FUNCTION(0x1, "gpio"), /* GPIO37 */
- BERLIN_PINCTRL_FUNCTION(0x4, "pwm0")),
+ BERLIN_PINCTRL_FUNCTION(0x4, "pwm")), /* PWM0 */
BERLIN_PINCTRL_GROUP("TDI", 0x10, 0x3, 0x12,
BERLIN_PINCTRL_FUNCTION(0x0, "jtag"), /* TDI */
BERLIN_PINCTRL_FUNCTION(0x1, "gpio"), /* GPIO38 */
- BERLIN_PINCTRL_FUNCTION(0x4, "pwm1")),
+ BERLIN_PINCTRL_FUNCTION(0x4, "pwm")), /* PWM1 */
BERLIN_PINCTRL_GROUP("TDO", 0x10, 0x3, 0x15,
BERLIN_PINCTRL_FUNCTION(0x0, "jtag"), /* TDO */
BERLIN_PINCTRL_FUNCTION(0x1, "gpio"), /* GPIO39 */
- BERLIN_PINCTRL_FUNCTION(0x4, "pwm0")),
+ BERLIN_PINCTRL_FUNCTION(0x4, "pwm")), /* PWM0 */
BERLIN_PINCTRL_GROUP("PWM6", 0x10, 0x3, 0x18,
BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO40 */
- BERLIN_PINCTRL_FUNCTION(0x1, "pwm6")),
+ BERLIN_PINCTRL_FUNCTION(0x1, "pwm")), /* PWM6 */
BERLIN_PINCTRL_GROUP("PWM7", 0x10, 0x3, 0x1b,
BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO41 */
- BERLIN_PINCTRL_FUNCTION(0x1, "pwm7")),
+ BERLIN_PINCTRL_FUNCTION(0x1, "pwm")), /* PWM7 */
BERLIN_PINCTRL_GROUP("PWM0", 0x14, 0x3, 0x00,
BERLIN_PINCTRL_FUNCTION(0x0, "por"), /* VDDCPUSOC RSTB */
- BERLIN_PINCTRL_FUNCTION(0x1, "pwm0"),
+ BERLIN_PINCTRL_FUNCTION(0x1, "pwm"), /* PWM0 */
BERLIN_PINCTRL_FUNCTION(0x2, "gpio")), /* GPIO42 */
BERLIN_PINCTRL_GROUP("PWM1", 0x14, 0x3, 0x03,
BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO43 */
- BERLIN_PINCTRL_FUNCTION(0x1, "pwm1")),
+ BERLIN_PINCTRL_FUNCTION(0x1, "pwm")), /* PWM1 */
BERLIN_PINCTRL_GROUP("PWM2", 0x14, 0x3, 0x06,
BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO44 */
- BERLIN_PINCTRL_FUNCTION(0x1, "pwm2")),
+ BERLIN_PINCTRL_FUNCTION(0x1, "pwm")), /* PWM2 */
BERLIN_PINCTRL_GROUP("PWM3", 0x14, 0x3, 0x09,
BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO45 */
- BERLIN_PINCTRL_FUNCTION(0x1, "pwm3")),
+ BERLIN_PINCTRL_FUNCTION(0x1, "pwm")), /* PWM3 */
BERLIN_PINCTRL_GROUP("PWM4", 0x14, 0x3, 0x0c,
BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO46 */
- BERLIN_PINCTRL_FUNCTION(0x1, "pwm4")),
+ BERLIN_PINCTRL_FUNCTION(0x1, "pwm")), /* PWM4 */
BERLIN_PINCTRL_GROUP("PWM5", 0x14, 0x3, 0x0f,
BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO47 */
- BERLIN_PINCTRL_FUNCTION(0x1, "pwm5")),
+ BERLIN_PINCTRL_FUNCTION(0x1, "pwm")), /* PWM5 */
BERLIN_PINCTRL_GROUP("URT1_RTSn", 0x14, 0x3, 0x12,
BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO48 */
BERLIN_PINCTRL_FUNCTION(0x1, "uart1"), /* RTSn */
- BERLIN_PINCTRL_FUNCTION(0x2, "pwm6"),
+ BERLIN_PINCTRL_FUNCTION(0x2, "pwm"), /* PWM6 */
BERLIN_PINCTRL_FUNCTION(0x3, "tw1a"), /* SCL */
BERLIN_PINCTRL_FUNCTION(0x4, "aio"), /* DBG0 */
BERLIN_PINCTRL_FUNCTION(0x5, "phy")), /* DBG18 */
BERLIN_PINCTRL_GROUP("URT1_CTSn", 0x14, 0x3, 0x15,
BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO49 */
BERLIN_PINCTRL_FUNCTION(0x1, "uart1"), /* CTSn */
- BERLIN_PINCTRL_FUNCTION(0x2, "pwm7"),
+ BERLIN_PINCTRL_FUNCTION(0x2, "pwm"), /* PWM7 */
BERLIN_PINCTRL_FUNCTION(0x3, "tw1a"), /* SDA */
BERLIN_PINCTRL_FUNCTION(0x4, "aio"), /* DBG1 */
BERLIN_PINCTRL_FUNCTION(0x5, "phy")), /* DBG19 */
@@ -308,11 +308,11 @@ static const struct berlin_desc_group as370_soc_pinctrl_groups[] = {
BERLIN_PINCTRL_GROUP("SD0_CDn", 0x1c, 0x3, 0x00,
BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO62 */
BERLIN_PINCTRL_FUNCTION(0x1, "sd0"), /* CDn */
- BERLIN_PINCTRL_FUNCTION(0x3, "pwm2")),
+ BERLIN_PINCTRL_FUNCTION(0x3, "pwm")), /* PWM2 */
BERLIN_PINCTRL_GROUP("SD0_WP", 0x1c, 0x3, 0x03,
BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO63 */
BERLIN_PINCTRL_FUNCTION(0x1, "sd0"), /* WP */
- BERLIN_PINCTRL_FUNCTION(0x3, "pwm3")),
+ BERLIN_PINCTRL_FUNCTION(0x3, "pwm")), /* PWM3 */
};
static const struct berlin_pinctrl_desc as370_soc_pinctrl_data = {
diff --git a/drivers/pinctrl/cirrus/pinctrl-madera-core.c b/drivers/pinctrl/cirrus/pinctrl-madera-core.c
index a5dda832024a..7c9694593f79 100644
--- a/drivers/pinctrl/cirrus/pinctrl-madera-core.c
+++ b/drivers/pinctrl/cirrus/pinctrl-madera-core.c
@@ -14,6 +14,7 @@
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/slab.h>
+#include <linux/pinctrl/machine.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinmux.h>
#include <linux/pinctrl/pinconf.h>
diff --git a/drivers/pinctrl/freescale/Kconfig b/drivers/pinctrl/freescale/Kconfig
index 72b869d888e2..0d8387851b87 100644
--- a/drivers/pinctrl/freescale/Kconfig
+++ b/drivers/pinctrl/freescale/Kconfig
@@ -122,6 +122,13 @@ config PINCTRL_IMX7ULP
help
Say Y here to enable the imx7ulp pinctrl driver
+config PINCTRL_IMX8MM
+ bool "IMX8MM pinctrl driver"
+ depends on ARCH_MXC && ARM64
+ select PINCTRL_IMX
+ help
+ Say Y here to enable the imx8mm pinctrl driver
+
config PINCTRL_IMX8MQ
bool "IMX8MQ pinctrl driver"
depends on ARCH_MXC && ARM64
@@ -129,9 +136,16 @@ config PINCTRL_IMX8MQ
help
Say Y here to enable the imx8mq pinctrl driver
+config PINCTRL_IMX8QM
+ bool "IMX8QM pinctrl driver"
+ depends on IMX_SCU && ARCH_MXC && ARM64
+ select PINCTRL_IMX_SCU
+ help
+ Say Y here to enable the imx8qm pinctrl driver
+
config PINCTRL_IMX8QXP
bool "IMX8QXP pinctrl driver"
- depends on ARCH_MXC && ARM64
+ depends on IMX_SCU && ARCH_MXC && ARM64
select PINCTRL_IMX_SCU
help
Say Y here to enable the imx8qxp pinctrl driver
diff --git a/drivers/pinctrl/freescale/Makefile b/drivers/pinctrl/freescale/Makefile
index 6ee398a3e406..02020a76bd9c 100644
--- a/drivers/pinctrl/freescale/Makefile
+++ b/drivers/pinctrl/freescale/Makefile
@@ -18,7 +18,9 @@ obj-$(CONFIG_PINCTRL_IMX6SX) += pinctrl-imx6sx.o
obj-$(CONFIG_PINCTRL_IMX6UL) += pinctrl-imx6ul.o
obj-$(CONFIG_PINCTRL_IMX7D) += pinctrl-imx7d.o
obj-$(CONFIG_PINCTRL_IMX7ULP) += pinctrl-imx7ulp.o
+obj-$(CONFIG_PINCTRL_IMX8MM) += pinctrl-imx8mm.o
obj-$(CONFIG_PINCTRL_IMX8MQ) += pinctrl-imx8mq.o
+obj-$(CONFIG_PINCTRL_IMX8QM) += pinctrl-imx8qm.o
obj-$(CONFIG_PINCTRL_IMX8QXP) += pinctrl-imx8qxp.o
obj-$(CONFIG_PINCTRL_VF610) += pinctrl-vf610.o
obj-$(CONFIG_PINCTRL_MXS) += pinctrl-mxs.o
diff --git a/drivers/pinctrl/freescale/pinctrl-imx8mm.c b/drivers/pinctrl/freescale/pinctrl-imx8mm.c
new file mode 100644
index 000000000000..6d1038af59f4
--- /dev/null
+++ b/drivers/pinctrl/freescale/pinctrl-imx8mm.c
@@ -0,0 +1,348 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2017-2018 NXP
+ */
+
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/of_device.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/platform_device.h>
+
+#include "pinctrl-imx.h"
+
+enum imx8mm_pads {
+ MX8MM_PAD_RESERVE0 = 0,
+ MX8MM_PAD_RESERVE1 = 1,
+ MX8MM_PAD_RESERVE2 = 2,
+ MX8MM_PAD_RESERVE3 = 3,
+ MX8MM_PAD_RESERVE4 = 4,
+ MX8MM_PAD_RESERVE5 = 5,
+ MX8MM_PAD_RESERVE6 = 6,
+ MX8MM_PAD_RESERVE7 = 7,
+ MX8MM_PAD_RESERVE8 = 8,
+ MX8MM_PAD_RESERVE9 = 9,
+ MX8MM_IOMUXC_GPIO1_IO00 = 10,
+ MX8MM_IOMUXC_GPIO1_IO01 = 11,
+ MX8MM_IOMUXC_GPIO1_IO02 = 12,
+ MX8MM_IOMUXC_GPIO1_IO03 = 13,
+ MX8MM_IOMUXC_GPIO1_IO04 = 14,
+ MX8MM_IOMUXC_GPIO1_IO05 = 15,
+ MX8MM_IOMUXC_GPIO1_IO06 = 16,
+ MX8MM_IOMUXC_GPIO1_IO07 = 17,
+ MX8MM_IOMUXC_GPIO1_IO08 = 18,
+ MX8MM_IOMUXC_GPIO1_IO09 = 19,
+ MX8MM_IOMUXC_GPIO1_IO10 = 20,
+ MX8MM_IOMUXC_GPIO1_IO11 = 21,
+ MX8MM_IOMUXC_GPIO1_IO12 = 22,
+ MX8MM_IOMUXC_GPIO1_IO13 = 23,
+ MX8MM_IOMUXC_GPIO1_IO14 = 24,
+ MX8MM_IOMUXC_GPIO1_IO15 = 25,
+ MX8MM_IOMUXC_ENET_MDC = 26,
+ MX8MM_IOMUXC_ENET_MDIO = 27,
+ MX8MM_IOMUXC_ENET_TD3 = 28,
+ MX8MM_IOMUXC_ENET_TD2 = 29,
+ MX8MM_IOMUXC_ENET_TD1 = 30,
+ MX8MM_IOMUXC_ENET_TD0 = 31,
+ MX8MM_IOMUXC_ENET_TX_CTL = 32,
+ MX8MM_IOMUXC_ENET_TXC = 33,
+ MX8MM_IOMUXC_ENET_RX_CTL = 34,
+ MX8MM_IOMUXC_ENET_RXC = 35,
+ MX8MM_IOMUXC_ENET_RD0 = 36,
+ MX8MM_IOMUXC_ENET_RD1 = 37,
+ MX8MM_IOMUXC_ENET_RD2 = 38,
+ MX8MM_IOMUXC_ENET_RD3 = 39,
+ MX8MM_IOMUXC_SD1_CLK = 40,
+ MX8MM_IOMUXC_SD1_CMD = 41,
+ MX8MM_IOMUXC_SD1_DATA0 = 42,
+ MX8MM_IOMUXC_SD1_DATA1 = 43,
+ MX8MM_IOMUXC_SD1_DATA2 = 44,
+ MX8MM_IOMUXC_SD1_DATA3 = 45,
+ MX8MM_IOMUXC_SD1_DATA4 = 46,
+ MX8MM_IOMUXC_SD1_DATA5 = 47,
+ MX8MM_IOMUXC_SD1_DATA6 = 48,
+ MX8MM_IOMUXC_SD1_DATA7 = 49,
+ MX8MM_IOMUXC_SD1_RESET_B = 50,
+ MX8MM_IOMUXC_SD1_STROBE = 51,
+ MX8MM_IOMUXC_SD2_CD_B = 52,
+ MX8MM_IOMUXC_SD2_CLK = 53,
+ MX8MM_IOMUXC_SD2_CMD = 54,
+ MX8MM_IOMUXC_SD2_DATA0 = 55,
+ MX8MM_IOMUXC_SD2_DATA1 = 56,
+ MX8MM_IOMUXC_SD2_DATA2 = 57,
+ MX8MM_IOMUXC_SD2_DATA3 = 58,
+ MX8MM_IOMUXC_SD2_RESET_B = 59,
+ MX8MM_IOMUXC_SD2_WP = 60,
+ MX8MM_IOMUXC_NAND_ALE = 61,
+ MX8MM_IOMUXC_NAND_CE0 = 62,
+ MX8MM_IOMUXC_NAND_CE1 = 63,
+ MX8MM_IOMUXC_NAND_CE2 = 64,
+ MX8MM_IOMUXC_NAND_CE3 = 65,
+ MX8MM_IOMUXC_NAND_CLE = 66,
+ MX8MM_IOMUXC_NAND_DATA00 = 67,
+ MX8MM_IOMUXC_NAND_DATA01 = 68,
+ MX8MM_IOMUXC_NAND_DATA02 = 69,
+ MX8MM_IOMUXC_NAND_DATA03 = 70,
+ MX8MM_IOMUXC_NAND_DATA04 = 71,
+ MX8MM_IOMUXC_NAND_DATA05 = 72,
+ MX8MM_IOMUXC_NAND_DATA06 = 73,
+ MX8MM_IOMUXC_NAND_DATA07 = 74,
+ MX8MM_IOMUXC_NAND_DQS = 75,
+ MX8MM_IOMUXC_NAND_RE_B = 76,
+ MX8MM_IOMUXC_NAND_READY_B = 77,
+ MX8MM_IOMUXC_NAND_WE_B = 78,
+ MX8MM_IOMUXC_NAND_WP_B = 79,
+ MX8MM_IOMUXC_SAI5_RXFS = 80,
+ MX8MM_IOMUXC_SAI5_RXC = 81,
+ MX8MM_IOMUXC_SAI5_RXD0 = 82,
+ MX8MM_IOMUXC_SAI5_RXD1 = 83,
+ MX8MM_IOMUXC_SAI5_RXD2 = 84,
+ MX8MM_IOMUXC_SAI5_RXD3 = 85,
+ MX8MM_IOMUXC_SAI5_MCLK = 86,
+ MX8MM_IOMUXC_SAI1_RXFS = 87,
+ MX8MM_IOMUXC_SAI1_RXC = 88,
+ MX8MM_IOMUXC_SAI1_RXD0 = 89,
+ MX8MM_IOMUXC_SAI1_RXD1 = 90,
+ MX8MM_IOMUXC_SAI1_RXD2 = 91,
+ MX8MM_IOMUXC_SAI1_RXD3 = 92,
+ MX8MM_IOMUXC_SAI1_RXD4 = 93,
+ MX8MM_IOMUXC_SAI1_RXD5 = 94,
+ MX8MM_IOMUXC_SAI1_RXD6 = 95,
+ MX8MM_IOMUXC_SAI1_RXD7 = 96,
+ MX8MM_IOMUXC_SAI1_TXFS = 97,
+ MX8MM_IOMUXC_SAI1_TXC = 98,
+ MX8MM_IOMUXC_SAI1_TXD0 = 99,
+ MX8MM_IOMUXC_SAI1_TXD1 = 100,
+ MX8MM_IOMUXC_SAI1_TXD2 = 101,
+ MX8MM_IOMUXC_SAI1_TXD3 = 102,
+ MX8MM_IOMUXC_SAI1_TXD4 = 103,
+ MX8MM_IOMUXC_SAI1_TXD5 = 104,
+ MX8MM_IOMUXC_SAI1_TXD6 = 105,
+ MX8MM_IOMUXC_SAI1_TXD7 = 106,
+ MX8MM_IOMUXC_SAI1_MCLK = 107,
+ MX8MM_IOMUXC_SAI2_RXFS = 108,
+ MX8MM_IOMUXC_SAI2_RXC = 109,
+ MX8MM_IOMUXC_SAI2_RXD0 = 110,
+ MX8MM_IOMUXC_SAI2_TXFS = 111,
+ MX8MM_IOMUXC_SAI2_TXC = 112,
+ MX8MM_IOMUXC_SAI2_TXD0 = 113,
+ MX8MM_IOMUXC_SAI2_MCLK = 114,
+ MX8MM_IOMUXC_SAI3_RXFS = 115,
+ MX8MM_IOMUXC_SAI3_RXC = 116,
+ MX8MM_IOMUXC_SAI3_RXD = 117,
+ MX8MM_IOMUXC_SAI3_TXFS = 118,
+ MX8MM_IOMUXC_SAI3_TXC = 119,
+ MX8MM_IOMUXC_SAI3_TXD = 120,
+ MX8MM_IOMUXC_SAI3_MCLK = 121,
+ MX8MM_IOMUXC_SPDIF_TX = 122,
+ MX8MM_IOMUXC_SPDIF_RX = 123,
+ MX8MM_IOMUXC_SPDIF_EXT_CLK = 124,
+ MX8MM_IOMUXC_ECSPI1_SCLK = 125,
+ MX8MM_IOMUXC_ECSPI1_MOSI = 126,
+ MX8MM_IOMUXC_ECSPI1_MISO = 127,
+ MX8MM_IOMUXC_ECSPI1_SS0 = 128,
+ MX8MM_IOMUXC_ECSPI2_SCLK = 129,
+ MX8MM_IOMUXC_ECSPI2_MOSI = 130,
+ MX8MM_IOMUXC_ECSPI2_MISO = 131,
+ MX8MM_IOMUXC_ECSPI2_SS0 = 132,
+ MX8MM_IOMUXC_I2C1_SCL = 133,
+ MX8MM_IOMUXC_I2C1_SDA = 134,
+ MX8MM_IOMUXC_I2C2_SCL = 135,
+ MX8MM_IOMUXC_I2C2_SDA = 136,
+ MX8MM_IOMUXC_I2C3_SCL = 137,
+ MX8MM_IOMUXC_I2C3_SDA = 138,
+ MX8MM_IOMUXC_I2C4_SCL = 139,
+ MX8MM_IOMUXC_I2C4_SDA = 140,
+ MX8MM_IOMUXC_UART1_RXD = 141,
+ MX8MM_IOMUXC_UART1_TXD = 142,
+ MX8MM_IOMUXC_UART2_RXD = 143,
+ MX8MM_IOMUXC_UART2_TXD = 144,
+ MX8MM_IOMUXC_UART3_RXD = 145,
+ MX8MM_IOMUXC_UART3_TXD = 146,
+ MX8MM_IOMUXC_UART4_RXD = 147,
+ MX8MM_IOMUXC_UART4_TXD = 148,
+};
+
+/* Pad names for the pinmux subsystem */
+static const struct pinctrl_pin_desc imx8mm_pinctrl_pads[] = {
+ IMX_PINCTRL_PIN(MX8MM_PAD_RESERVE0),
+ IMX_PINCTRL_PIN(MX8MM_PAD_RESERVE1),
+ IMX_PINCTRL_PIN(MX8MM_PAD_RESERVE2),
+ IMX_PINCTRL_PIN(MX8MM_PAD_RESERVE3),
+ IMX_PINCTRL_PIN(MX8MM_PAD_RESERVE4),
+ IMX_PINCTRL_PIN(MX8MM_PAD_RESERVE5),
+ IMX_PINCTRL_PIN(MX8MM_PAD_RESERVE6),
+ IMX_PINCTRL_PIN(MX8MM_PAD_RESERVE7),
+ IMX_PINCTRL_PIN(MX8MM_PAD_RESERVE8),
+ IMX_PINCTRL_PIN(MX8MM_PAD_RESERVE9),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_GPIO1_IO00),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_GPIO1_IO01),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_GPIO1_IO02),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_GPIO1_IO03),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_GPIO1_IO04),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_GPIO1_IO05),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_GPIO1_IO06),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_GPIO1_IO07),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_GPIO1_IO08),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_GPIO1_IO09),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_GPIO1_IO10),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_GPIO1_IO11),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_GPIO1_IO12),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_GPIO1_IO13),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_GPIO1_IO14),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_GPIO1_IO15),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_ENET_MDC),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_ENET_MDIO),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_ENET_TD3),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_ENET_TD2),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_ENET_TD1),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_ENET_TD0),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_ENET_TX_CTL),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_ENET_TXC),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_ENET_RX_CTL),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_ENET_RXC),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_ENET_RD0),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_ENET_RD1),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_ENET_RD2),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_ENET_RD3),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD1_CLK),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD1_CMD),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD1_DATA0),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD1_DATA1),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD1_DATA2),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD1_DATA3),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD1_DATA4),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD1_DATA5),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD1_DATA6),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD1_DATA7),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD1_RESET_B),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD1_STROBE),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD2_CD_B),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD2_CLK),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD2_CMD),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD2_DATA0),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD2_DATA1),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD2_DATA2),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD2_DATA3),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD2_RESET_B),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD2_WP),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_NAND_ALE),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_NAND_CE0),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_NAND_CE1),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_NAND_CE2),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_NAND_CE3),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_NAND_CLE),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_NAND_DATA00),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_NAND_DATA01),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_NAND_DATA02),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_NAND_DATA03),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_NAND_DATA04),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_NAND_DATA05),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_NAND_DATA06),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_NAND_DATA07),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_NAND_DQS),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_NAND_RE_B),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_NAND_READY_B),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_NAND_WE_B),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_NAND_WP_B),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI5_RXFS),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI5_RXC),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI5_RXD0),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI5_RXD1),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI5_RXD2),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI5_RXD3),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI5_MCLK),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_RXFS),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_RXC),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_RXD0),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_RXD1),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_RXD2),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_RXD3),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_RXD4),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_RXD5),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_RXD6),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_RXD7),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_TXFS),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_TXC),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_TXD0),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_TXD1),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_TXD2),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_TXD3),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_TXD4),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_TXD5),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_TXD6),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_TXD7),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_MCLK),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI2_RXFS),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI2_RXC),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI2_RXD0),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI2_TXFS),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI2_TXC),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI2_TXD0),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI2_MCLK),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI3_RXFS),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI3_RXC),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI3_RXD),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI3_TXFS),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI3_TXC),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI3_TXD),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI3_MCLK),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SPDIF_TX),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SPDIF_RX),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_SPDIF_EXT_CLK),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_ECSPI1_SCLK),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_ECSPI1_MOSI),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_ECSPI1_MISO),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_ECSPI1_SS0),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_ECSPI2_SCLK),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_ECSPI2_MOSI),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_ECSPI2_MISO),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_ECSPI2_SS0),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_I2C1_SCL),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_I2C1_SDA),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_I2C2_SCL),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_I2C2_SDA),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_I2C3_SCL),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_I2C3_SDA),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_I2C4_SCL),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_I2C4_SDA),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_UART1_RXD),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_UART1_TXD),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_UART2_RXD),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_UART2_TXD),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_UART3_RXD),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_UART3_TXD),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_UART4_RXD),
+ IMX_PINCTRL_PIN(MX8MM_IOMUXC_UART4_TXD),
+};
+
+static const struct imx_pinctrl_soc_info imx8mm_pinctrl_info = {
+ .pins = imx8mm_pinctrl_pads,
+ .npins = ARRAY_SIZE(imx8mm_pinctrl_pads),
+ .gpr_compatible = "fsl,imx8mm-iomuxc-gpr",
+};
+
+static const struct of_device_id imx8mm_pinctrl_of_match[] = {
+ { .compatible = "fsl,imx8mm-iomuxc", .data = &imx8mm_pinctrl_info, },
+ { /* sentinel */ }
+};
+
+static int imx8mm_pinctrl_probe(struct platform_device *pdev)
+{
+ return imx_pinctrl_probe(pdev, &imx8mm_pinctrl_info);
+}
+
+static struct platform_driver imx8mm_pinctrl_driver = {
+ .driver = {
+ .name = "imx8mm-pinctrl",
+ .of_match_table = of_match_ptr(imx8mm_pinctrl_of_match),
+ .suppress_bind_attrs = true,
+ },
+ .probe = imx8mm_pinctrl_probe,
+};
+
+static int __init imx8mm_pinctrl_init(void)
+{
+ return platform_driver_register(&imx8mm_pinctrl_driver);
+}
+arch_initcall(imx8mm_pinctrl_init);
diff --git a/drivers/pinctrl/freescale/pinctrl-imx8qm.c b/drivers/pinctrl/freescale/pinctrl-imx8qm.c
new file mode 100644
index 000000000000..0b6029b29731
--- /dev/null
+++ b/drivers/pinctrl/freescale/pinctrl-imx8qm.c
@@ -0,0 +1,326 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2016 Freescale Semiconductor, Inc.
+ * Copyright 2017~2018 NXP
+ * Dong Aisheng <aisheng.dong@nxp.com>
+ */
+
+#include <dt-bindings/pinctrl/pads-imx8qm.h>
+#include <linux/err.h>
+#include <linux/firmware/imx/sci.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/platform_device.h>
+
+#include "pinctrl-imx.h"
+
+static const struct pinctrl_pin_desc imx8qm_pinctrl_pads[] = {
+ IMX_PINCTRL_PIN(IMX8QM_SIM0_CLK),
+ IMX_PINCTRL_PIN(IMX8QM_SIM0_RST),
+ IMX_PINCTRL_PIN(IMX8QM_SIM0_IO),
+ IMX_PINCTRL_PIN(IMX8QM_SIM0_PD),
+ IMX_PINCTRL_PIN(IMX8QM_SIM0_POWER_EN),
+ IMX_PINCTRL_PIN(IMX8QM_SIM0_GPIO0_00),
+ IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_1V8_3V3_SIM),
+ IMX_PINCTRL_PIN(IMX8QM_M40_I2C0_SCL),
+ IMX_PINCTRL_PIN(IMX8QM_M40_I2C0_SDA),
+ IMX_PINCTRL_PIN(IMX8QM_M40_GPIO0_00),
+ IMX_PINCTRL_PIN(IMX8QM_M40_GPIO0_01),
+ IMX_PINCTRL_PIN(IMX8QM_M41_I2C0_SCL),
+ IMX_PINCTRL_PIN(IMX8QM_M41_I2C0_SDA),
+ IMX_PINCTRL_PIN(IMX8QM_M41_GPIO0_00),
+ IMX_PINCTRL_PIN(IMX8QM_M41_GPIO0_01),
+ IMX_PINCTRL_PIN(IMX8QM_GPT0_CLK),
+ IMX_PINCTRL_PIN(IMX8QM_GPT0_CAPTURE),
+ IMX_PINCTRL_PIN(IMX8QM_GPT0_COMPARE),
+ IMX_PINCTRL_PIN(IMX8QM_GPT1_CLK),
+ IMX_PINCTRL_PIN(IMX8QM_GPT1_CAPTURE),
+ IMX_PINCTRL_PIN(IMX8QM_GPT1_COMPARE),
+ IMX_PINCTRL_PIN(IMX8QM_UART0_RX),
+ IMX_PINCTRL_PIN(IMX8QM_UART0_TX),
+ IMX_PINCTRL_PIN(IMX8QM_UART0_RTS_B),
+ IMX_PINCTRL_PIN(IMX8QM_UART0_CTS_B),
+ IMX_PINCTRL_PIN(IMX8QM_UART1_TX),
+ IMX_PINCTRL_PIN(IMX8QM_UART1_RX),
+ IMX_PINCTRL_PIN(IMX8QM_UART1_RTS_B),
+ IMX_PINCTRL_PIN(IMX8QM_UART1_CTS_B),
+ IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_1V8_3V3_GPIOLH),
+ IMX_PINCTRL_PIN(IMX8QM_SCU_PMIC_MEMC_ON),
+ IMX_PINCTRL_PIN(IMX8QM_SCU_WDOG_OUT),
+ IMX_PINCTRL_PIN(IMX8QM_PMIC_I2C_SDA),
+ IMX_PINCTRL_PIN(IMX8QM_PMIC_I2C_SCL),
+ IMX_PINCTRL_PIN(IMX8QM_PMIC_EARLY_WARNING),
+ IMX_PINCTRL_PIN(IMX8QM_PMIC_INT_B),
+ IMX_PINCTRL_PIN(IMX8QM_SCU_GPIO0_00),
+ IMX_PINCTRL_PIN(IMX8QM_SCU_GPIO0_01),
+ IMX_PINCTRL_PIN(IMX8QM_SCU_GPIO0_02),
+ IMX_PINCTRL_PIN(IMX8QM_SCU_GPIO0_03),
+ IMX_PINCTRL_PIN(IMX8QM_SCU_GPIO0_04),
+ IMX_PINCTRL_PIN(IMX8QM_SCU_GPIO0_05),
+ IMX_PINCTRL_PIN(IMX8QM_SCU_GPIO0_06),
+ IMX_PINCTRL_PIN(IMX8QM_SCU_GPIO0_07),
+ IMX_PINCTRL_PIN(IMX8QM_SCU_BOOT_MODE0),
+ IMX_PINCTRL_PIN(IMX8QM_SCU_BOOT_MODE1),
+ IMX_PINCTRL_PIN(IMX8QM_SCU_BOOT_MODE2),
+ IMX_PINCTRL_PIN(IMX8QM_SCU_BOOT_MODE3),
+ IMX_PINCTRL_PIN(IMX8QM_SCU_BOOT_MODE4),
+ IMX_PINCTRL_PIN(IMX8QM_SCU_BOOT_MODE5),
+ IMX_PINCTRL_PIN(IMX8QM_LVDS0_GPIO00),
+ IMX_PINCTRL_PIN(IMX8QM_LVDS0_GPIO01),
+ IMX_PINCTRL_PIN(IMX8QM_LVDS0_I2C0_SCL),
+ IMX_PINCTRL_PIN(IMX8QM_LVDS0_I2C0_SDA),
+ IMX_PINCTRL_PIN(IMX8QM_LVDS0_I2C1_SCL),
+ IMX_PINCTRL_PIN(IMX8QM_LVDS0_I2C1_SDA),
+ IMX_PINCTRL_PIN(IMX8QM_LVDS1_GPIO00),
+ IMX_PINCTRL_PIN(IMX8QM_LVDS1_GPIO01),
+ IMX_PINCTRL_PIN(IMX8QM_LVDS1_I2C0_SCL),
+ IMX_PINCTRL_PIN(IMX8QM_LVDS1_I2C0_SDA),
+ IMX_PINCTRL_PIN(IMX8QM_LVDS1_I2C1_SCL),
+ IMX_PINCTRL_PIN(IMX8QM_LVDS1_I2C1_SDA),
+ IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_1V8_3V3_LVDSGPIO),
+ IMX_PINCTRL_PIN(IMX8QM_MIPI_DSI0_I2C0_SCL),
+ IMX_PINCTRL_PIN(IMX8QM_MIPI_DSI0_I2C0_SDA),
+ IMX_PINCTRL_PIN(IMX8QM_MIPI_DSI0_GPIO0_00),
+ IMX_PINCTRL_PIN(IMX8QM_MIPI_DSI0_GPIO0_01),
+ IMX_PINCTRL_PIN(IMX8QM_MIPI_DSI1_I2C0_SCL),
+ IMX_PINCTRL_PIN(IMX8QM_MIPI_DSI1_I2C0_SDA),
+ IMX_PINCTRL_PIN(IMX8QM_MIPI_DSI1_GPIO0_00),
+ IMX_PINCTRL_PIN(IMX8QM_MIPI_DSI1_GPIO0_01),
+ IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_1V8_3V3_MIPIDSIGPIO),
+ IMX_PINCTRL_PIN(IMX8QM_MIPI_CSI0_MCLK_OUT),
+ IMX_PINCTRL_PIN(IMX8QM_MIPI_CSI0_I2C0_SCL),
+ IMX_PINCTRL_PIN(IMX8QM_MIPI_CSI0_I2C0_SDA),
+ IMX_PINCTRL_PIN(IMX8QM_MIPI_CSI0_GPIO0_00),
+ IMX_PINCTRL_PIN(IMX8QM_MIPI_CSI0_GPIO0_01),
+ IMX_PINCTRL_PIN(IMX8QM_MIPI_CSI1_MCLK_OUT),
+ IMX_PINCTRL_PIN(IMX8QM_MIPI_CSI1_GPIO0_00),
+ IMX_PINCTRL_PIN(IMX8QM_MIPI_CSI1_GPIO0_01),
+ IMX_PINCTRL_PIN(IMX8QM_MIPI_CSI1_I2C0_SCL),
+ IMX_PINCTRL_PIN(IMX8QM_MIPI_CSI1_I2C0_SDA),
+ IMX_PINCTRL_PIN(IMX8QM_HDMI_TX0_TS_SCL),
+ IMX_PINCTRL_PIN(IMX8QM_HDMI_TX0_TS_SDA),
+ IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_3V3_HDMIGPIO),
+ IMX_PINCTRL_PIN(IMX8QM_ESAI1_FSR),
+ IMX_PINCTRL_PIN(IMX8QM_ESAI1_FST),
+ IMX_PINCTRL_PIN(IMX8QM_ESAI1_SCKR),
+ IMX_PINCTRL_PIN(IMX8QM_ESAI1_SCKT),
+ IMX_PINCTRL_PIN(IMX8QM_ESAI1_TX0),
+ IMX_PINCTRL_PIN(IMX8QM_ESAI1_TX1),
+ IMX_PINCTRL_PIN(IMX8QM_ESAI1_TX2_RX3),
+ IMX_PINCTRL_PIN(IMX8QM_ESAI1_TX3_RX2),
+ IMX_PINCTRL_PIN(IMX8QM_ESAI1_TX4_RX1),
+ IMX_PINCTRL_PIN(IMX8QM_ESAI1_TX5_RX0),
+ IMX_PINCTRL_PIN(IMX8QM_SPDIF0_RX),
+ IMX_PINCTRL_PIN(IMX8QM_SPDIF0_TX),
+ IMX_PINCTRL_PIN(IMX8QM_SPDIF0_EXT_CLK),
+ IMX_PINCTRL_PIN(IMX8QM_SPI3_SCK),
+ IMX_PINCTRL_PIN(IMX8QM_SPI3_SDO),
+ IMX_PINCTRL_PIN(IMX8QM_SPI3_SDI),
+ IMX_PINCTRL_PIN(IMX8QM_SPI3_CS0),
+ IMX_PINCTRL_PIN(IMX8QM_SPI3_CS1),
+ IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_1V8_3V3_GPIORHB),
+ IMX_PINCTRL_PIN(IMX8QM_ESAI0_FSR),
+ IMX_PINCTRL_PIN(IMX8QM_ESAI0_FST),
+ IMX_PINCTRL_PIN(IMX8QM_ESAI0_SCKR),
+ IMX_PINCTRL_PIN(IMX8QM_ESAI0_SCKT),
+ IMX_PINCTRL_PIN(IMX8QM_ESAI0_TX0),
+ IMX_PINCTRL_PIN(IMX8QM_ESAI0_TX1),
+ IMX_PINCTRL_PIN(IMX8QM_ESAI0_TX2_RX3),
+ IMX_PINCTRL_PIN(IMX8QM_ESAI0_TX3_RX2),
+ IMX_PINCTRL_PIN(IMX8QM_ESAI0_TX4_RX1),
+ IMX_PINCTRL_PIN(IMX8QM_ESAI0_TX5_RX0),
+ IMX_PINCTRL_PIN(IMX8QM_MCLK_IN0),
+ IMX_PINCTRL_PIN(IMX8QM_MCLK_OUT0),
+ IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_1V8_3V3_GPIORHC),
+ IMX_PINCTRL_PIN(IMX8QM_SPI0_SCK),
+ IMX_PINCTRL_PIN(IMX8QM_SPI0_SDO),
+ IMX_PINCTRL_PIN(IMX8QM_SPI0_SDI),
+ IMX_PINCTRL_PIN(IMX8QM_SPI0_CS0),
+ IMX_PINCTRL_PIN(IMX8QM_SPI0_CS1),
+ IMX_PINCTRL_PIN(IMX8QM_SPI2_SCK),
+ IMX_PINCTRL_PIN(IMX8QM_SPI2_SDO),
+ IMX_PINCTRL_PIN(IMX8QM_SPI2_SDI),
+ IMX_PINCTRL_PIN(IMX8QM_SPI2_CS0),
+ IMX_PINCTRL_PIN(IMX8QM_SPI2_CS1),
+ IMX_PINCTRL_PIN(IMX8QM_SAI1_RXC),
+ IMX_PINCTRL_PIN(IMX8QM_SAI1_RXD),
+ IMX_PINCTRL_PIN(IMX8QM_SAI1_RXFS),
+ IMX_PINCTRL_PIN(IMX8QM_SAI1_TXC),
+ IMX_PINCTRL_PIN(IMX8QM_SAI1_TXD),
+ IMX_PINCTRL_PIN(IMX8QM_SAI1_TXFS),
+ IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_1V8_3V3_GPIORHT),
+ IMX_PINCTRL_PIN(IMX8QM_ADC_IN7),
+ IMX_PINCTRL_PIN(IMX8QM_ADC_IN6),
+ IMX_PINCTRL_PIN(IMX8QM_ADC_IN5),
+ IMX_PINCTRL_PIN(IMX8QM_ADC_IN4),
+ IMX_PINCTRL_PIN(IMX8QM_ADC_IN3),
+ IMX_PINCTRL_PIN(IMX8QM_ADC_IN2),
+ IMX_PINCTRL_PIN(IMX8QM_ADC_IN1),
+ IMX_PINCTRL_PIN(IMX8QM_ADC_IN0),
+ IMX_PINCTRL_PIN(IMX8QM_MLB_SIG),
+ IMX_PINCTRL_PIN(IMX8QM_MLB_CLK),
+ IMX_PINCTRL_PIN(IMX8QM_MLB_DATA),
+ IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_1V8_3V3_GPIOLHT),
+ IMX_PINCTRL_PIN(IMX8QM_FLEXCAN0_RX),
+ IMX_PINCTRL_PIN(IMX8QM_FLEXCAN0_TX),
+ IMX_PINCTRL_PIN(IMX8QM_FLEXCAN1_RX),
+ IMX_PINCTRL_PIN(IMX8QM_FLEXCAN1_TX),
+ IMX_PINCTRL_PIN(IMX8QM_FLEXCAN2_RX),
+ IMX_PINCTRL_PIN(IMX8QM_FLEXCAN2_TX),
+ IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_1V8_3V3_GPIOTHR),
+ IMX_PINCTRL_PIN(IMX8QM_USB_SS3_TC0),
+ IMX_PINCTRL_PIN(IMX8QM_USB_SS3_TC1),
+ IMX_PINCTRL_PIN(IMX8QM_USB_SS3_TC2),
+ IMX_PINCTRL_PIN(IMX8QM_USB_SS3_TC3),
+ IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_3V3_USB3IO),
+ IMX_PINCTRL_PIN(IMX8QM_USDHC1_RESET_B),
+ IMX_PINCTRL_PIN(IMX8QM_USDHC1_VSELECT),
+ IMX_PINCTRL_PIN(IMX8QM_USDHC2_RESET_B),
+ IMX_PINCTRL_PIN(IMX8QM_USDHC2_VSELECT),
+ IMX_PINCTRL_PIN(IMX8QM_USDHC2_WP),
+ IMX_PINCTRL_PIN(IMX8QM_USDHC2_CD_B),
+ IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_1V8_3V3_VSELSEP),
+ IMX_PINCTRL_PIN(IMX8QM_ENET0_MDIO),
+ IMX_PINCTRL_PIN(IMX8QM_ENET0_MDC),
+ IMX_PINCTRL_PIN(IMX8QM_ENET0_REFCLK_125M_25M),
+ IMX_PINCTRL_PIN(IMX8QM_ENET1_REFCLK_125M_25M),
+ IMX_PINCTRL_PIN(IMX8QM_ENET1_MDIO),
+ IMX_PINCTRL_PIN(IMX8QM_ENET1_MDC),
+ IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_1V8_3V3_GPIOCT),
+ IMX_PINCTRL_PIN(IMX8QM_QSPI1A_SS0_B),
+ IMX_PINCTRL_PIN(IMX8QM_QSPI1A_SS1_B),
+ IMX_PINCTRL_PIN(IMX8QM_QSPI1A_SCLK),
+ IMX_PINCTRL_PIN(IMX8QM_QSPI1A_DQS),
+ IMX_PINCTRL_PIN(IMX8QM_QSPI1A_DATA3),
+ IMX_PINCTRL_PIN(IMX8QM_QSPI1A_DATA2),
+ IMX_PINCTRL_PIN(IMX8QM_QSPI1A_DATA1),
+ IMX_PINCTRL_PIN(IMX8QM_QSPI1A_DATA0),
+ IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_1V8_3V3_QSPI1),
+ IMX_PINCTRL_PIN(IMX8QM_QSPI0A_DATA0),
+ IMX_PINCTRL_PIN(IMX8QM_QSPI0A_DATA1),
+ IMX_PINCTRL_PIN(IMX8QM_QSPI0A_DATA2),
+ IMX_PINCTRL_PIN(IMX8QM_QSPI0A_DATA3),
+ IMX_PINCTRL_PIN(IMX8QM_QSPI0A_DQS),
+ IMX_PINCTRL_PIN(IMX8QM_QSPI0A_SS0_B),
+ IMX_PINCTRL_PIN(IMX8QM_QSPI0A_SS1_B),
+ IMX_PINCTRL_PIN(IMX8QM_QSPI0A_SCLK),
+ IMX_PINCTRL_PIN(IMX8QM_QSPI0B_SCLK),
+ IMX_PINCTRL_PIN(IMX8QM_QSPI0B_DATA0),
+ IMX_PINCTRL_PIN(IMX8QM_QSPI0B_DATA1),
+ IMX_PINCTRL_PIN(IMX8QM_QSPI0B_DATA2),
+ IMX_PINCTRL_PIN(IMX8QM_QSPI0B_DATA3),
+ IMX_PINCTRL_PIN(IMX8QM_QSPI0B_DQS),
+ IMX_PINCTRL_PIN(IMX8QM_QSPI0B_SS0_B),
+ IMX_PINCTRL_PIN(IMX8QM_QSPI0B_SS1_B),
+ IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_1V8_3V3_QSPI0),
+ IMX_PINCTRL_PIN(IMX8QM_PCIE_CTRL0_CLKREQ_B),
+ IMX_PINCTRL_PIN(IMX8QM_PCIE_CTRL0_WAKE_B),
+ IMX_PINCTRL_PIN(IMX8QM_PCIE_CTRL0_PERST_B),
+ IMX_PINCTRL_PIN(IMX8QM_PCIE_CTRL1_CLKREQ_B),
+ IMX_PINCTRL_PIN(IMX8QM_PCIE_CTRL1_WAKE_B),
+ IMX_PINCTRL_PIN(IMX8QM_PCIE_CTRL1_PERST_B),
+ IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_1V8_3V3_PCIESEP),
+ IMX_PINCTRL_PIN(IMX8QM_USB_HSIC0_DATA),
+ IMX_PINCTRL_PIN(IMX8QM_USB_HSIC0_STROBE),
+ IMX_PINCTRL_PIN(IMX8QM_CALIBRATION_0_HSIC),
+ IMX_PINCTRL_PIN(IMX8QM_CALIBRATION_1_HSIC),
+ IMX_PINCTRL_PIN(IMX8QM_EMMC0_CLK),
+ IMX_PINCTRL_PIN(IMX8QM_EMMC0_CMD),
+ IMX_PINCTRL_PIN(IMX8QM_EMMC0_DATA0),
+ IMX_PINCTRL_PIN(IMX8QM_EMMC0_DATA1),
+ IMX_PINCTRL_PIN(IMX8QM_EMMC0_DATA2),
+ IMX_PINCTRL_PIN(IMX8QM_EMMC0_DATA3),
+ IMX_PINCTRL_PIN(IMX8QM_EMMC0_DATA4),
+ IMX_PINCTRL_PIN(IMX8QM_EMMC0_DATA5),
+ IMX_PINCTRL_PIN(IMX8QM_EMMC0_DATA6),
+ IMX_PINCTRL_PIN(IMX8QM_EMMC0_DATA7),
+ IMX_PINCTRL_PIN(IMX8QM_EMMC0_STROBE),
+ IMX_PINCTRL_PIN(IMX8QM_EMMC0_RESET_B),
+ IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_1V8_3V3_SD1FIX),
+ IMX_PINCTRL_PIN(IMX8QM_USDHC1_CLK),
+ IMX_PINCTRL_PIN(IMX8QM_USDHC1_CMD),
+ IMX_PINCTRL_PIN(IMX8QM_USDHC1_DATA0),
+ IMX_PINCTRL_PIN(IMX8QM_USDHC1_DATA1),
+ IMX_PINCTRL_PIN(IMX8QM_CTL_NAND_RE_P_N),
+ IMX_PINCTRL_PIN(IMX8QM_USDHC1_DATA2),
+ IMX_PINCTRL_PIN(IMX8QM_USDHC1_DATA3),
+ IMX_PINCTRL_PIN(IMX8QM_CTL_NAND_DQS_P_N),
+ IMX_PINCTRL_PIN(IMX8QM_USDHC1_DATA4),
+ IMX_PINCTRL_PIN(IMX8QM_USDHC1_DATA5),
+ IMX_PINCTRL_PIN(IMX8QM_USDHC1_DATA6),
+ IMX_PINCTRL_PIN(IMX8QM_USDHC1_DATA7),
+ IMX_PINCTRL_PIN(IMX8QM_USDHC1_STROBE),
+ IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_1V8_3V3_VSEL2),
+ IMX_PINCTRL_PIN(IMX8QM_USDHC2_CLK),
+ IMX_PINCTRL_PIN(IMX8QM_USDHC2_CMD),
+ IMX_PINCTRL_PIN(IMX8QM_USDHC2_DATA0),
+ IMX_PINCTRL_PIN(IMX8QM_USDHC2_DATA1),
+ IMX_PINCTRL_PIN(IMX8QM_USDHC2_DATA2),
+ IMX_PINCTRL_PIN(IMX8QM_USDHC2_DATA3),
+ IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_1V8_3V3_VSEL3),
+ IMX_PINCTRL_PIN(IMX8QM_ENET0_RGMII_TXC),
+ IMX_PINCTRL_PIN(IMX8QM_ENET0_RGMII_TX_CTL),
+ IMX_PINCTRL_PIN(IMX8QM_ENET0_RGMII_TXD0),
+ IMX_PINCTRL_PIN(IMX8QM_ENET0_RGMII_TXD1),
+ IMX_PINCTRL_PIN(IMX8QM_ENET0_RGMII_TXD2),
+ IMX_PINCTRL_PIN(IMX8QM_ENET0_RGMII_TXD3),
+ IMX_PINCTRL_PIN(IMX8QM_ENET0_RGMII_RXC),
+ IMX_PINCTRL_PIN(IMX8QM_ENET0_RGMII_RX_CTL),
+ IMX_PINCTRL_PIN(IMX8QM_ENET0_RGMII_RXD0),
+ IMX_PINCTRL_PIN(IMX8QM_ENET0_RGMII_RXD1),
+ IMX_PINCTRL_PIN(IMX8QM_ENET0_RGMII_RXD2),
+ IMX_PINCTRL_PIN(IMX8QM_ENET0_RGMII_RXD3),
+ IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_1V8_3V3_ENET_ENETB),
+ IMX_PINCTRL_PIN(IMX8QM_ENET1_RGMII_TXC),
+ IMX_PINCTRL_PIN(IMX8QM_ENET1_RGMII_TX_CTL),
+ IMX_PINCTRL_PIN(IMX8QM_ENET1_RGMII_TXD0),
+ IMX_PINCTRL_PIN(IMX8QM_ENET1_RGMII_TXD1),
+ IMX_PINCTRL_PIN(IMX8QM_ENET1_RGMII_TXD2),
+ IMX_PINCTRL_PIN(IMX8QM_ENET1_RGMII_TXD3),
+ IMX_PINCTRL_PIN(IMX8QM_ENET1_RGMII_RXC),
+ IMX_PINCTRL_PIN(IMX8QM_ENET1_RGMII_RX_CTL),
+ IMX_PINCTRL_PIN(IMX8QM_ENET1_RGMII_RXD0),
+ IMX_PINCTRL_PIN(IMX8QM_ENET1_RGMII_RXD1),
+ IMX_PINCTRL_PIN(IMX8QM_ENET1_RGMII_RXD2),
+ IMX_PINCTRL_PIN(IMX8QM_ENET1_RGMII_RXD3),
+ IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_1V8_3V3_ENET_ENETA),
+};
+
+static const struct imx_pinctrl_soc_info imx8qm_pinctrl_info = {
+ .pins = imx8qm_pinctrl_pads,
+ .npins = ARRAY_SIZE(imx8qm_pinctrl_pads),
+ .flags = IMX_USE_SCU,
+};
+
+static const struct of_device_id imx8qm_pinctrl_of_match[] = {
+ { .compatible = "fsl,imx8qm-iomuxc", },
+ { /* sentinel */ }
+};
+
+static int imx8qm_pinctrl_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ ret = imx_pinctrl_sc_ipc_init(pdev);
+ if (ret)
+ return ret;
+
+ return imx_pinctrl_probe(pdev, &imx8qm_pinctrl_info);
+}
+
+static struct platform_driver imx8qm_pinctrl_driver = {
+ .driver = {
+ .name = "imx8qm-pinctrl",
+ .of_match_table = of_match_ptr(imx8qm_pinctrl_of_match),
+ .suppress_bind_attrs = true,
+ },
+ .probe = imx8qm_pinctrl_probe,
+};
+
+static int __init imx8qm_pinctrl_init(void)
+{
+ return platform_driver_register(&imx8qm_pinctrl_driver);
+}
+arch_initcall(imx8qm_pinctrl_init);
diff --git a/drivers/pinctrl/mediatek/pinctrl-mtk-common-v2.c b/drivers/pinctrl/mediatek/pinctrl-mtk-common-v2.c
index 4a9e0d4c2bbc..b1c368455d30 100644
--- a/drivers/pinctrl/mediatek/pinctrl-mtk-common-v2.c
+++ b/drivers/pinctrl/mediatek/pinctrl-mtk-common-v2.c
@@ -290,7 +290,13 @@ static int mtk_xt_set_gpio_as_eint(void *data, unsigned long eint_n)
return err;
err = mtk_hw_set_value(hw, desc, PINCTRL_PIN_REG_SMT, MTK_ENABLE);
- if (err)
+ /* SMT is supposed to be supported by every real GPIO and doesn't
+ * support virtual GPIOs, so the extra condition err != -ENOTSUPP
+ * is just for adding EINT support to these virtual GPIOs. It should
+ * add an extra flag in the pin descriptor when more pins with
+ * distinctive characteristic come out.
+ */
+ if (err && err != -ENOTSUPP)
return err;
return 0;
diff --git a/drivers/pinctrl/meson/pinctrl-meson.c b/drivers/pinctrl/meson/pinctrl-meson.c
index ea87d739f534..96a4a72708e4 100644
--- a/drivers/pinctrl/meson/pinctrl-meson.c
+++ b/drivers/pinctrl/meson/pinctrl-meson.c
@@ -31,6 +31,9 @@
* In some cases the register ranges for pull enable and pull
* direction are the same and thus there are only 3 register ranges.
*
+ * Since Meson G12A SoC, the ao register ranges for gpio, pull enable
+ * and pull direction are the same, so there are only 2 register ranges.
+ *
* For the pull and GPIO configuration every bank uses a contiguous
* set of bits in the register sets described above; the same register
* can be shared by more banks with different offsets.
@@ -488,21 +491,26 @@ static int meson_pinctrl_parse_dt(struct meson_pinctrl *pc,
return PTR_ERR(pc->reg_mux);
}
- pc->reg_pull = meson_map_resource(pc, gpio_np, "pull");
- if (IS_ERR(pc->reg_pull)) {
- dev_err(pc->dev, "pull registers not found\n");
- return PTR_ERR(pc->reg_pull);
+ pc->reg_gpio = meson_map_resource(pc, gpio_np, "gpio");
+ if (IS_ERR(pc->reg_gpio)) {
+ dev_err(pc->dev, "gpio registers not found\n");
+ return PTR_ERR(pc->reg_gpio);
}
+ pc->reg_pull = meson_map_resource(pc, gpio_np, "pull");
+ /* Use gpio region if pull one is not present */
+ if (IS_ERR(pc->reg_pull))
+ pc->reg_pull = pc->reg_gpio;
+
pc->reg_pullen = meson_map_resource(pc, gpio_np, "pull-enable");
/* Use pull region if pull-enable one is not present */
if (IS_ERR(pc->reg_pullen))
pc->reg_pullen = pc->reg_pull;
- pc->reg_gpio = meson_map_resource(pc, gpio_np, "gpio");
- if (IS_ERR(pc->reg_gpio)) {
- dev_err(pc->dev, "gpio registers not found\n");
- return PTR_ERR(pc->reg_gpio);
+ pc->reg_ds = meson_map_resource(pc, gpio_np, "ds");
+ if (IS_ERR(pc->reg_ds)) {
+ dev_dbg(pc->dev, "ds registers not found - skipping\n");
+ pc->reg_ds = NULL;
}
return 0;
diff --git a/drivers/pinctrl/meson/pinctrl-meson.h b/drivers/pinctrl/meson/pinctrl-meson.h
index eff61ea1c67e..5eaab925f427 100644
--- a/drivers/pinctrl/meson/pinctrl-meson.h
+++ b/drivers/pinctrl/meson/pinctrl-meson.h
@@ -120,6 +120,7 @@ struct meson_pinctrl {
struct regmap *reg_pullen;
struct regmap *reg_pull;
struct regmap *reg_gpio;
+ struct regmap *reg_ds;
struct gpio_chip chip;
struct device_node *of_node;
};
diff --git a/drivers/pinctrl/meson/pinctrl-meson8b.c b/drivers/pinctrl/meson/pinctrl-meson8b.c
index 0f140a802137..7f76000cc12e 100644
--- a/drivers/pinctrl/meson/pinctrl-meson8b.c
+++ b/drivers/pinctrl/meson/pinctrl-meson8b.c
@@ -346,6 +346,8 @@ static const unsigned int eth_rx_dv_pins[] = { DIF_1_P };
static const unsigned int eth_rx_clk_pins[] = { DIF_1_N };
static const unsigned int eth_txd0_1_pins[] = { DIF_2_P };
static const unsigned int eth_txd1_1_pins[] = { DIF_2_N };
+static const unsigned int eth_rxd3_pins[] = { DIF_2_P };
+static const unsigned int eth_rxd2_pins[] = { DIF_2_N };
static const unsigned int eth_tx_en_pins[] = { DIF_3_P };
static const unsigned int eth_ref_clk_pins[] = { DIF_3_N };
static const unsigned int eth_mdc_pins[] = { DIF_4_P };
@@ -599,6 +601,8 @@ static struct meson_pmx_group meson8b_cbus_groups[] = {
GROUP(eth_ref_clk, 6, 8),
GROUP(eth_mdc, 6, 9),
GROUP(eth_mdio_en, 6, 10),
+ GROUP(eth_rxd3, 7, 22),
+ GROUP(eth_rxd2, 7, 23),
};
static struct meson_pmx_group meson8b_aobus_groups[] = {
@@ -748,7 +752,7 @@ static const char * const ethernet_groups[] = {
"eth_tx_clk", "eth_tx_en", "eth_txd1_0", "eth_txd1_1",
"eth_txd0_0", "eth_txd0_1", "eth_rx_clk", "eth_rx_dv",
"eth_rxd1", "eth_rxd0", "eth_mdio_en", "eth_mdc", "eth_ref_clk",
- "eth_txd2", "eth_txd3"
+ "eth_txd2", "eth_txd3", "eth_rxd3", "eth_rxd2"
};
static const char * const i2c_a_groups[] = {
diff --git a/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c b/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c
index aa48b3f23c7f..6462d3ca7ceb 100644
--- a/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c
+++ b/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c
@@ -170,8 +170,8 @@ static struct armada_37xx_pin_group armada_37xx_nb_groups[] = {
PIN_GRP_GPIO("pwm1", 12, 1, BIT(4), "pwm"),
PIN_GRP_GPIO("pwm2", 13, 1, BIT(5), "pwm"),
PIN_GRP_GPIO("pwm3", 14, 1, BIT(6), "pwm"),
- PIN_GRP_GPIO("pmic1", 17, 1, BIT(7), "pmic"),
- PIN_GRP_GPIO("pmic0", 16, 1, BIT(8), "pmic"),
+ PIN_GRP_GPIO("pmic1", 7, 1, BIT(7), "pmic"),
+ PIN_GRP_GPIO("pmic0", 6, 1, BIT(8), "pmic"),
PIN_GRP_GPIO("i2c2", 2, 2, BIT(9), "i2c"),
PIN_GRP_GPIO("i2c1", 0, 2, BIT(10), "i2c"),
PIN_GRP_GPIO("spi_cs1", 17, 1, BIT(12), "spi"),
@@ -195,8 +195,11 @@ static struct armada_37xx_pin_group armada_37xx_sb_groups[] = {
PIN_GRP_GPIO("usb2_drvvbus1", 1, 1, BIT(1), "drvbus"),
PIN_GRP_GPIO("sdio_sb", 24, 6, BIT(2), "sdio"),
PIN_GRP_GPIO("rgmii", 6, 12, BIT(3), "mii"),
- PIN_GRP_GPIO("pcie1", 3, 2, BIT(4), "pcie"),
- PIN_GRP_GPIO("ptp", 20, 3, BIT(5), "ptp"),
+ PIN_GRP_GPIO("smi", 18, 2, BIT(4), "smi"),
+ PIN_GRP_GPIO("pcie1", 3, 1, BIT(5), "pcie"),
+ PIN_GRP_GPIO("pcie1_clkreq", 4, 1, BIT(9), "pcie"),
+ PIN_GRP_GPIO("pcie1_wakeup", 5, 1, BIT(10), "pcie"),
+ PIN_GRP_GPIO("ptp", 20, 3, BIT(11) | BIT(12) | BIT(13), "ptp"),
PIN_GRP("ptp_clk", 21, 1, BIT(6), "ptp", "mii"),
PIN_GRP("ptp_trig", 22, 1, BIT(7), "ptp", "mii"),
PIN_GRP_GPIO_3("mii_col", 23, 1, BIT(8) | BIT(14), 0, BIT(8), BIT(14),
@@ -1104,8 +1107,8 @@ static int armada_3700_pinctrl_resume(struct device *dev)
* to other IO drivers.
*/
static const struct dev_pm_ops armada_3700_pinctrl_pm_ops = {
- .suspend_late = armada_3700_pinctrl_suspend,
- .resume_early = armada_3700_pinctrl_resume,
+ .suspend_noirq = armada_3700_pinctrl_suspend,
+ .resume_noirq = armada_3700_pinctrl_resume,
};
#define PINCTRL_ARMADA_37XX_DEV_PM_OPS (&armada_3700_pinctrl_pm_ops)
diff --git a/drivers/pinctrl/nomadik/pinctrl-nomadik.c b/drivers/pinctrl/nomadik/pinctrl-nomadik.c
index 4cc2c47f8778..ec02739bd21b 100644
--- a/drivers/pinctrl/nomadik/pinctrl-nomadik.c
+++ b/drivers/pinctrl/nomadik/pinctrl-nomadik.c
@@ -1056,17 +1056,22 @@ static struct nmk_gpio_chip *nmk_gpio_populate_chip(struct device_node *np,
}
if (of_property_read_u32(np, "gpio-bank", &id)) {
dev_err(&pdev->dev, "populate: gpio-bank property not found\n");
+ platform_device_put(gpio_pdev);
return ERR_PTR(-EINVAL);
}
/* Already populated? */
nmk_chip = nmk_gpio_chips[id];
- if (nmk_chip)
+ if (nmk_chip) {
+ platform_device_put(gpio_pdev);
return nmk_chip;
+ }
nmk_chip = devm_kzalloc(&pdev->dev, sizeof(*nmk_chip), GFP_KERNEL);
- if (!nmk_chip)
+ if (!nmk_chip) {
+ platform_device_put(gpio_pdev);
return ERR_PTR(-ENOMEM);
+ }
nmk_chip->bank = id;
chip = &nmk_chip->chip;
@@ -1077,13 +1082,17 @@ static struct nmk_gpio_chip *nmk_gpio_populate_chip(struct device_node *np,
res = platform_get_resource(gpio_pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(base))
+ if (IS_ERR(base)) {
+ platform_device_put(gpio_pdev);
return ERR_CAST(base);
+ }
nmk_chip->addr = base;
clk = clk_get(&gpio_pdev->dev, NULL);
- if (IS_ERR(clk))
+ if (IS_ERR(clk)) {
+ platform_device_put(gpio_pdev);
return (void *) clk;
+ }
clk_prepare(clk);
nmk_chip->clk = clk;
diff --git a/drivers/pinctrl/pinconf.c b/drivers/pinctrl/pinconf.c
index 2c7229380f08..2678603df14b 100644
--- a/drivers/pinctrl/pinconf.c
+++ b/drivers/pinctrl/pinconf.c
@@ -17,7 +17,6 @@
#include <linux/slab.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
-#include <linux/uaccess.h>
#include <linux/pinctrl/machine.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinconf.h>
@@ -369,225 +368,6 @@ static int pinconf_groups_show(struct seq_file *s, void *what)
DEFINE_SHOW_ATTRIBUTE(pinconf_pins);
DEFINE_SHOW_ATTRIBUTE(pinconf_groups);
-#define MAX_NAME_LEN 15
-
-struct dbg_cfg {
- enum pinctrl_map_type map_type;
- char dev_name[MAX_NAME_LEN + 1];
- char state_name[MAX_NAME_LEN + 1];
- char pin_name[MAX_NAME_LEN + 1];
-};
-
-/*
- * Goal is to keep this structure as global in order to simply read the
- * pinconf-config file after a write to check config is as expected
- */
-static struct dbg_cfg pinconf_dbg_conf;
-
-/**
- * pinconf_dbg_config_print() - display the pinctrl config from the pinctrl
- * map, of the dev/pin/state that was last written to pinconf-config file.
- * @s: string filled in with config description
- * @d: not used
- */
-static int pinconf_dbg_config_print(struct seq_file *s, void *d)
-{
- struct pinctrl_maps *maps_node;
- const struct pinctrl_map *map;
- const struct pinctrl_map *found = NULL;
- struct pinctrl_dev *pctldev;
- struct dbg_cfg *dbg = &pinconf_dbg_conf;
- int i;
-
- mutex_lock(&pinctrl_maps_mutex);
-
- /* Parse the pinctrl map and look for the elected pin/state */
- for_each_maps(maps_node, i, map) {
- if (map->type != dbg->map_type)
- continue;
- if (strcmp(map->dev_name, dbg->dev_name))
- continue;
- if (strcmp(map->name, dbg->state_name))
- continue;
-
- if (!strcmp(map->data.configs.group_or_pin, dbg->pin_name)) {
- /* We found the right pin */
- found = map;
- break;
- }
- }
-
- if (!found) {
- seq_printf(s, "No config found for dev/state/pin, expected:\n");
- seq_printf(s, "Searched dev:%s\n", dbg->dev_name);
- seq_printf(s, "Searched state:%s\n", dbg->state_name);
- seq_printf(s, "Searched pin:%s\n", dbg->pin_name);
- seq_printf(s, "Use: modify config_pin <devname> "\
- "<state> <pinname> <value>\n");
- goto exit;
- }
-
- pctldev = get_pinctrl_dev_from_devname(found->ctrl_dev_name);
- seq_printf(s, "Dev %s has config of %s in state %s:\n",
- dbg->dev_name, dbg->pin_name, dbg->state_name);
- pinconf_show_config(s, pctldev, found->data.configs.configs,
- found->data.configs.num_configs);
-
-exit:
- mutex_unlock(&pinctrl_maps_mutex);
-
- return 0;
-}
-
-/**
- * pinconf_dbg_config_write() - modify the pinctrl config in the pinctrl
- * map, of a dev/pin/state entry based on user entries to pinconf-config
- * @user_buf: contains the modification request with expected format:
- * modify <config> <devicename> <state> <name> <newvalue>
- * modify is literal string, alternatives like add/delete not supported yet
- * <config> is the configuration to be changed. Supported configs are
- * "config_pin" or "config_group", alternatives like config_mux are not
- * supported yet.
- * <devicename> <state> <name> are values that should match the pinctrl-maps
- * <newvalue> reflects the new config and is driver dependent
- */
-static ssize_t pinconf_dbg_config_write(struct file *file,
- const char __user *user_buf, size_t count, loff_t *ppos)
-{
- struct pinctrl_maps *maps_node;
- const struct pinctrl_map *map;
- const struct pinctrl_map *found = NULL;
- struct pinctrl_dev *pctldev;
- const struct pinconf_ops *confops = NULL;
- struct dbg_cfg *dbg = &pinconf_dbg_conf;
- const struct pinctrl_map_configs *configs;
- char config[MAX_NAME_LEN + 1];
- char buf[128];
- char *b = &buf[0];
- int buf_size;
- char *token;
- int i;
-
- /* Get userspace string and assure termination */
- buf_size = min(count, sizeof(buf) - 1);
- if (copy_from_user(buf, user_buf, buf_size))
- return -EFAULT;
- buf[buf_size] = 0;
-
- /*
- * need to parse entry and extract parameters:
- * modify configs_pin devicename state pinname newvalue
- */
-
- /* Get arg: 'modify' */
- token = strsep(&b, " ");
- if (!token)
- return -EINVAL;
- if (strcmp(token, "modify"))
- return -EINVAL;
-
- /*
- * Get arg type: "config_pin" and "config_group"
- * types are supported so far
- */
- token = strsep(&b, " ");
- if (!token)
- return -EINVAL;
- if (!strcmp(token, "config_pin"))
- dbg->map_type = PIN_MAP_TYPE_CONFIGS_PIN;
- else if (!strcmp(token, "config_group"))
- dbg->map_type = PIN_MAP_TYPE_CONFIGS_GROUP;
- else
- return -EINVAL;
-
- /* get arg 'device_name' */
- token = strsep(&b, " ");
- if (!token)
- return -EINVAL;
- if (strlen(token) >= MAX_NAME_LEN)
- return -EINVAL;
- strncpy(dbg->dev_name, token, MAX_NAME_LEN);
-
- /* get arg 'state_name' */
- token = strsep(&b, " ");
- if (!token)
- return -EINVAL;
- if (strlen(token) >= MAX_NAME_LEN)
- return -EINVAL;
- strncpy(dbg->state_name, token, MAX_NAME_LEN);
-
- /* get arg 'pin_name' */
- token = strsep(&b, " ");
- if (!token)
- return -EINVAL;
- if (strlen(token) >= MAX_NAME_LEN)
- return -EINVAL;
- strncpy(dbg->pin_name, token, MAX_NAME_LEN);
-
- /* get new_value of config' */
- token = strsep(&b, " ");
- if (!token)
- return -EINVAL;
- if (strlen(token) >= MAX_NAME_LEN)
- return -EINVAL;
- strncpy(config, token, MAX_NAME_LEN);
-
- mutex_lock(&pinctrl_maps_mutex);
-
- /* Parse the pinctrl map and look for the selected dev/state/pin */
- for_each_maps(maps_node, i, map) {
- if (strcmp(map->dev_name, dbg->dev_name))
- continue;
- if (map->type != dbg->map_type)
- continue;
- if (strcmp(map->name, dbg->state_name))
- continue;
-
- /* we found the right pin / state, so overwrite config */
- if (!strcmp(map->data.configs.group_or_pin, dbg->pin_name)) {
- found = map;
- break;
- }
- }
-
- if (!found) {
- count = -EINVAL;
- goto exit;
- }
-
- pctldev = get_pinctrl_dev_from_devname(found->ctrl_dev_name);
- if (pctldev)
- confops = pctldev->desc->confops;
-
- if (confops && confops->pin_config_dbg_parse_modify) {
- configs = &found->data.configs;
- for (i = 0; i < configs->num_configs; i++) {
- confops->pin_config_dbg_parse_modify(pctldev,
- config,
- &configs->configs[i]);
- }
- }
-
-exit:
- mutex_unlock(&pinctrl_maps_mutex);
-
- return count;
-}
-
-static int pinconf_dbg_config_open(struct inode *inode, struct file *file)
-{
- return single_open(file, pinconf_dbg_config_print, inode->i_private);
-}
-
-static const struct file_operations pinconf_dbg_pinconfig_fops = {
- .open = pinconf_dbg_config_open,
- .write = pinconf_dbg_config_write,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- .owner = THIS_MODULE,
-};
-
void pinconf_init_device_debugfs(struct dentry *devroot,
struct pinctrl_dev *pctldev)
{
@@ -595,8 +375,6 @@ void pinconf_init_device_debugfs(struct dentry *devroot,
devroot, pctldev, &pinconf_pins_fops);
debugfs_create_file("pinconf-groups", S_IFREG | S_IRUGO,
devroot, pctldev, &pinconf_groups_fops);
- debugfs_create_file("pinconf-config", (S_IRUGO | S_IWUSR | S_IWGRP),
- devroot, pctldev, &pinconf_dbg_pinconfig_fops);
}
#endif
diff --git a/drivers/pinctrl/pinctrl-amd.c b/drivers/pinctrl/pinctrl-amd.c
index 2a7d638978d8..6689995fa3aa 100644
--- a/drivers/pinctrl/pinctrl-amd.c
+++ b/drivers/pinctrl/pinctrl-amd.c
@@ -489,7 +489,7 @@ static int amd_gpio_irq_set_type(struct irq_data *d, unsigned int type)
/*
* If WAKE_INT_MASTER_REG.MaskStsEn is set, a software write to the
* debounce registers of any GPIO will block wake/interrupt status
- * generation for *all* GPIOs for a lenght of time that depends on
+ * generation for *all* GPIOs for a length of time that depends on
* WAKE_INT_MASTER_REG.MaskStsLength[11:0]. During this period the
* INTERRUPT_ENABLE bit will read as 0.
*
diff --git a/drivers/pinctrl/pinctrl-at91.c b/drivers/pinctrl/pinctrl-at91.c
index 3d49bbbcdbc7..cb7c432769a5 100644
--- a/drivers/pinctrl/pinctrl-at91.c
+++ b/drivers/pinctrl/pinctrl-at91.c
@@ -59,6 +59,9 @@ static int gpio_banks;
#define OUTPUT (1 << 7)
#define OUTPUT_VAL_SHIFT 8
#define OUTPUT_VAL (0x1 << OUTPUT_VAL_SHIFT)
+#define SLEWRATE_SHIFT 9
+#define SLEWRATE_MASK 0x1
+#define SLEWRATE (SLEWRATE_MASK << SLEWRATE_SHIFT)
#define DEBOUNCE (1 << 16)
#define DEBOUNCE_VAL_SHIFT 17
#define DEBOUNCE_VAL (0x3fff << DEBOUNCE_VAL_SHIFT)
@@ -72,10 +75,22 @@ static int gpio_banks;
* DRIVE_STRENGTH_DEFAULT is just a placeholder to avoid changing the drive
* strength when there is no dt config for it.
*/
-#define DRIVE_STRENGTH_DEFAULT (0 << DRIVE_STRENGTH_SHIFT)
-#define DRIVE_STRENGTH_LOW (1 << DRIVE_STRENGTH_SHIFT)
-#define DRIVE_STRENGTH_MED (2 << DRIVE_STRENGTH_SHIFT)
-#define DRIVE_STRENGTH_HI (3 << DRIVE_STRENGTH_SHIFT)
+enum drive_strength_bit {
+ DRIVE_STRENGTH_BIT_DEF,
+ DRIVE_STRENGTH_BIT_LOW,
+ DRIVE_STRENGTH_BIT_MED,
+ DRIVE_STRENGTH_BIT_HI,
+};
+
+#define DRIVE_STRENGTH_BIT_MSK(name) (DRIVE_STRENGTH_BIT_##name << \
+ DRIVE_STRENGTH_SHIFT)
+
+enum slewrate_bit {
+ SLEWRATE_BIT_DIS,
+ SLEWRATE_BIT_ENA,
+};
+
+#define SLEWRATE_BIT_MSK(name) (SLEWRATE_BIT_##name << SLEWRATE_SHIFT)
/**
* struct at91_pmx_func - describes AT91 pinmux functions
@@ -166,6 +181,8 @@ struct at91_pinctrl_mux_ops {
unsigned (*get_drivestrength)(void __iomem *pio, unsigned pin);
void (*set_drivestrength)(void __iomem *pio, unsigned pin,
u32 strength);
+ unsigned (*get_slewrate)(void __iomem *pio, unsigned pin);
+ void (*set_slewrate)(void __iomem *pio, unsigned pin, u32 slewrate);
/* irq */
int (*irq_type)(struct irq_data *d, unsigned type);
};
@@ -551,7 +568,7 @@ static unsigned at91_mux_sama5d3_get_drivestrength(void __iomem *pio,
/* SAMA5 strength is 1:1 with our defines,
* except 0 is equivalent to low per datasheet */
if (!tmp)
- tmp = DRIVE_STRENGTH_LOW;
+ tmp = DRIVE_STRENGTH_BIT_MSK(LOW);
return tmp;
}
@@ -564,11 +581,32 @@ static unsigned at91_mux_sam9x5_get_drivestrength(void __iomem *pio,
/* strength is inverse in SAM9x5s hardware with the pinctrl defines
* hardware: 0 = hi, 1 = med, 2 = low, 3 = rsvd */
- tmp = DRIVE_STRENGTH_HI - tmp;
+ tmp = DRIVE_STRENGTH_BIT_MSK(HI) - tmp;
return tmp;
}
+static unsigned at91_mux_sam9x60_get_drivestrength(void __iomem *pio,
+ unsigned pin)
+{
+ unsigned tmp = readl_relaxed(pio + SAM9X60_PIO_DRIVER1);
+
+ if (tmp & BIT(pin))
+ return DRIVE_STRENGTH_BIT_HI;
+
+ return DRIVE_STRENGTH_BIT_LOW;
+}
+
+static unsigned at91_mux_sam9x60_get_slewrate(void __iomem *pio, unsigned pin)
+{
+ unsigned tmp = readl_relaxed(pio + SAM9X60_PIO_SLEWR);
+
+ if ((tmp & BIT(pin)))
+ return SLEWRATE_BIT_ENA;
+
+ return SLEWRATE_BIT_DIS;
+}
+
static void set_drive_strength(void __iomem *reg, unsigned pin, u32 strength)
{
unsigned tmp = readl_relaxed(reg);
@@ -600,12 +638,51 @@ static void at91_mux_sam9x5_set_drivestrength(void __iomem *pio, unsigned pin,
/* strength is inverse on SAM9x5s with our defines
* 0 = hi, 1 = med, 2 = low, 3 = rsvd */
- setting = DRIVE_STRENGTH_HI - setting;
+ setting = DRIVE_STRENGTH_BIT_MSK(HI) - setting;
set_drive_strength(pio + at91sam9x5_get_drive_register(pin), pin,
setting);
}
+static void at91_mux_sam9x60_set_drivestrength(void __iomem *pio, unsigned pin,
+ u32 setting)
+{
+ unsigned int tmp;
+
+ if (setting <= DRIVE_STRENGTH_BIT_DEF ||
+ setting == DRIVE_STRENGTH_BIT_MED ||
+ setting > DRIVE_STRENGTH_BIT_HI)
+ return;
+
+ tmp = readl_relaxed(pio + SAM9X60_PIO_DRIVER1);
+
+ /* Strength is 0: low, 1: hi */
+ if (setting == DRIVE_STRENGTH_BIT_LOW)
+ tmp &= ~BIT(pin);
+ else
+ tmp |= BIT(pin);
+
+ writel_relaxed(tmp, pio + SAM9X60_PIO_DRIVER1);
+}
+
+static void at91_mux_sam9x60_set_slewrate(void __iomem *pio, unsigned pin,
+ u32 setting)
+{
+ unsigned int tmp;
+
+ if (setting < SLEWRATE_BIT_DIS || setting > SLEWRATE_BIT_ENA)
+ return;
+
+ tmp = readl_relaxed(pio + SAM9X60_PIO_SLEWR);
+
+ if (setting == SLEWRATE_BIT_DIS)
+ tmp &= ~BIT(pin);
+ else
+ tmp |= BIT(pin);
+
+ writel_relaxed(tmp, pio + SAM9X60_PIO_SLEWR);
+}
+
static struct at91_pinctrl_mux_ops at91rm9200_ops = {
.get_periph = at91_mux_get_periph,
.mux_A_periph = at91_mux_set_A_periph,
@@ -634,6 +711,28 @@ static struct at91_pinctrl_mux_ops at91sam9x5_ops = {
.irq_type = alt_gpio_irq_type,
};
+static const struct at91_pinctrl_mux_ops sam9x60_ops = {
+ .get_periph = at91_mux_pio3_get_periph,
+ .mux_A_periph = at91_mux_pio3_set_A_periph,
+ .mux_B_periph = at91_mux_pio3_set_B_periph,
+ .mux_C_periph = at91_mux_pio3_set_C_periph,
+ .mux_D_periph = at91_mux_pio3_set_D_periph,
+ .get_deglitch = at91_mux_pio3_get_deglitch,
+ .set_deglitch = at91_mux_pio3_set_deglitch,
+ .get_debounce = at91_mux_pio3_get_debounce,
+ .set_debounce = at91_mux_pio3_set_debounce,
+ .get_pulldown = at91_mux_pio3_get_pulldown,
+ .set_pulldown = at91_mux_pio3_set_pulldown,
+ .get_schmitt_trig = at91_mux_pio3_get_schmitt_trig,
+ .disable_schmitt_trig = at91_mux_pio3_disable_schmitt_trig,
+ .get_drivestrength = at91_mux_sam9x60_get_drivestrength,
+ .set_drivestrength = at91_mux_sam9x60_set_drivestrength,
+ .get_slewrate = at91_mux_sam9x60_get_slewrate,
+ .set_slewrate = at91_mux_sam9x60_set_slewrate,
+ .irq_type = alt_gpio_irq_type,
+
+};
+
static struct at91_pinctrl_mux_ops sama5d3_ops = {
.get_periph = at91_mux_pio3_get_periph,
.mux_A_periph = at91_mux_pio3_set_A_periph,
@@ -893,6 +992,8 @@ static int at91_pinconf_get(struct pinctrl_dev *pctldev,
if (info->ops->get_drivestrength)
*config |= (info->ops->get_drivestrength(pio, pin)
<< DRIVE_STRENGTH_SHIFT);
+ if (info->ops->get_slewrate)
+ *config |= (info->ops->get_slewrate(pio, pin) << SLEWRATE_SHIFT);
if (at91_mux_get_output(pio, pin, &out))
*config |= OUTPUT | (out << OUTPUT_VAL_SHIFT);
@@ -944,6 +1045,9 @@ static int at91_pinconf_set(struct pinctrl_dev *pctldev,
info->ops->set_drivestrength(pio, pin,
(config & DRIVE_STRENGTH)
>> DRIVE_STRENGTH_SHIFT);
+ if (info->ops->set_slewrate)
+ info->ops->set_slewrate(pio, pin,
+ (config & SLEWRATE) >> SLEWRATE_SHIFT);
} /* for each config */
@@ -959,11 +1063,11 @@ static int at91_pinconf_set(struct pinctrl_dev *pctldev,
} \
} while (0)
-#define DBG_SHOW_FLAG_MASKED(mask,flag) do { \
+#define DBG_SHOW_FLAG_MASKED(mask, flag, name) do { \
if ((config & mask) == flag) { \
if (num_conf) \
seq_puts(s, "|"); \
- seq_puts(s, #flag); \
+ seq_puts(s, #name); \
num_conf++; \
} \
} while (0)
@@ -981,9 +1085,13 @@ static void at91_pinconf_dbg_show(struct pinctrl_dev *pctldev,
DBG_SHOW_FLAG(PULL_DOWN);
DBG_SHOW_FLAG(DIS_SCHMIT);
DBG_SHOW_FLAG(DEGLITCH);
- DBG_SHOW_FLAG_MASKED(DRIVE_STRENGTH, DRIVE_STRENGTH_LOW);
- DBG_SHOW_FLAG_MASKED(DRIVE_STRENGTH, DRIVE_STRENGTH_MED);
- DBG_SHOW_FLAG_MASKED(DRIVE_STRENGTH, DRIVE_STRENGTH_HI);
+ DBG_SHOW_FLAG_MASKED(DRIVE_STRENGTH, DRIVE_STRENGTH_BIT_MSK(LOW),
+ DRIVE_STRENGTH_LOW);
+ DBG_SHOW_FLAG_MASKED(DRIVE_STRENGTH, DRIVE_STRENGTH_BIT_MSK(MED),
+ DRIVE_STRENGTH_MED);
+ DBG_SHOW_FLAG_MASKED(DRIVE_STRENGTH, DRIVE_STRENGTH_BIT_MSK(HI),
+ DRIVE_STRENGTH_HI);
+ DBG_SHOW_FLAG(SLEWRATE);
DBG_SHOW_FLAG(DEBOUNCE);
if (config & DEBOUNCE) {
val = config >> DEBOUNCE_VAL_SHIFT;
@@ -1155,6 +1263,7 @@ static const struct of_device_id at91_pinctrl_of_match[] = {
{ .compatible = "atmel,sama5d3-pinctrl", .data = &sama5d3_ops },
{ .compatible = "atmel,at91sam9x5-pinctrl", .data = &at91sam9x5_ops },
{ .compatible = "atmel,at91rm9200-pinctrl", .data = &at91rm9200_ops },
+ { .compatible = "microchip,sam9x60-pinctrl", .data = &sam9x60_ops },
{ /* sentinel */ }
};
@@ -1697,6 +1806,7 @@ static const struct gpio_chip at91_gpio_template = {
static const struct of_device_id at91_gpio_of_match[] = {
{ .compatible = "atmel,at91sam9x5-gpio", .data = &at91sam9x5_ops, },
{ .compatible = "atmel,at91rm9200-gpio", .data = &at91rm9200_ops },
+ { .compatible = "microchip,sam9x60-gpio", .data = &sam9x60_ops },
{ /* sentinel */ }
};
diff --git a/drivers/pinctrl/pinctrl-at91.h b/drivers/pinctrl/pinctrl-at91.h
index 79b957f1dfa2..223620f14b05 100644
--- a/drivers/pinctrl/pinctrl-at91.h
+++ b/drivers/pinctrl/pinctrl-at91.h
@@ -69,4 +69,7 @@
#define AT91SAM9X5_PIO_DRIVER1 0x114 /*PIO Driver 1 register offset*/
#define AT91SAM9X5_PIO_DRIVER2 0x118 /*PIO Driver 2 register offset*/
+#define SAM9X60_PIO_SLEWR 0x110 /* PIO Slew Rate Control Register */
+#define SAM9X60_PIO_DRIVER1 0x118 /* PIO Driver 1 register offset */
+
#endif
diff --git a/drivers/pinctrl/pinctrl-ingenic.c b/drivers/pinctrl/pinctrl-ingenic.c
index db6b48ea5f47..bc21ceb15d68 100644
--- a/drivers/pinctrl/pinctrl-ingenic.c
+++ b/drivers/pinctrl/pinctrl-ingenic.c
@@ -233,6 +233,19 @@ static int jz4725b_pwm_pwm2_pins[] = { 0x4c, };
static int jz4725b_pwm_pwm3_pins[] = { 0x4d, };
static int jz4725b_pwm_pwm4_pins[] = { 0x4e, };
static int jz4725b_pwm_pwm5_pins[] = { 0x4f, };
+static int jz4725b_lcd_8bit_pins[] = {
+ 0x72, 0x73, 0x74,
+ 0x60, 0x61, 0x62, 0x63,
+ 0x64, 0x65, 0x66, 0x67,
+};
+static int jz4725b_lcd_16bit_pins[] = {
+ 0x68, 0x69, 0x6a, 0x6b,
+ 0x6c, 0x6d, 0x6e, 0x6f,
+};
+static int jz4725b_lcd_18bit_pins[] = { 0x70, 0x71, };
+static int jz4725b_lcd_24bit_pins[] = { 0x76, 0x77, 0x78, 0x79, };
+static int jz4725b_lcd_special_pins[] = { 0x76, 0x77, 0x78, 0x79, };
+static int jz4725b_lcd_generic_pins[] = { 0x75, };
static int jz4725b_mmc0_1bit_funcs[] = { 1, 1, 1, };
static int jz4725b_mmc0_4bit_funcs[] = { 1, 0, 1, };
@@ -251,6 +264,12 @@ static int jz4725b_pwm_pwm2_funcs[] = { 0, };
static int jz4725b_pwm_pwm3_funcs[] = { 0, };
static int jz4725b_pwm_pwm4_funcs[] = { 0, };
static int jz4725b_pwm_pwm5_funcs[] = { 0, };
+static int jz4725b_lcd_8bit_funcs[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, };
+static int jz4725b_lcd_16bit_funcs[] = { 0, 0, 0, 0, 0, 0, 0, 0, };
+static int jz4725b_lcd_18bit_funcs[] = { 0, 0, };
+static int jz4725b_lcd_24bit_funcs[] = { 1, 1, 1, 1, };
+static int jz4725b_lcd_special_funcs[] = { 0, 0, 0, 0, };
+static int jz4725b_lcd_generic_funcs[] = { 0, };
static const struct group_desc jz4725b_groups[] = {
INGENIC_PIN_GROUP("mmc0-1bit", jz4725b_mmc0_1bit),
@@ -270,6 +289,12 @@ static const struct group_desc jz4725b_groups[] = {
INGENIC_PIN_GROUP("pwm3", jz4725b_pwm_pwm3),
INGENIC_PIN_GROUP("pwm4", jz4725b_pwm_pwm4),
INGENIC_PIN_GROUP("pwm5", jz4725b_pwm_pwm5),
+ INGENIC_PIN_GROUP("lcd-8bit", jz4725b_lcd_8bit),
+ INGENIC_PIN_GROUP("lcd-16bit", jz4725b_lcd_16bit),
+ INGENIC_PIN_GROUP("lcd-18bit", jz4725b_lcd_18bit),
+ INGENIC_PIN_GROUP("lcd-24bit", jz4725b_lcd_24bit),
+ INGENIC_PIN_GROUP("lcd-special", jz4725b_lcd_special),
+ INGENIC_PIN_GROUP("lcd-generic", jz4725b_lcd_generic),
};
static const char *jz4725b_mmc0_groups[] = { "mmc0-1bit", "mmc0-4bit", };
@@ -285,6 +310,10 @@ static const char *jz4725b_pwm2_groups[] = { "pwm2", };
static const char *jz4725b_pwm3_groups[] = { "pwm3", };
static const char *jz4725b_pwm4_groups[] = { "pwm4", };
static const char *jz4725b_pwm5_groups[] = { "pwm5", };
+static const char *jz4725b_lcd_groups[] = {
+ "lcd-8bit", "lcd-16bit", "lcd-18bit", "lcd-24bit",
+ "lcd-special", "lcd-generic",
+};
static const struct function_desc jz4725b_functions[] = {
{ "mmc0", jz4725b_mmc0_groups, ARRAY_SIZE(jz4725b_mmc0_groups), },
@@ -297,6 +326,7 @@ static const struct function_desc jz4725b_functions[] = {
{ "pwm3", jz4725b_pwm3_groups, ARRAY_SIZE(jz4725b_pwm3_groups), },
{ "pwm4", jz4725b_pwm4_groups, ARRAY_SIZE(jz4725b_pwm4_groups), },
{ "pwm5", jz4725b_pwm5_groups, ARRAY_SIZE(jz4725b_pwm5_groups), },
+ { "lcd", jz4725b_lcd_groups, ARRAY_SIZE(jz4725b_lcd_groups), },
};
static const struct ingenic_chip_info jz4725b_chip_info = {
@@ -321,47 +351,57 @@ static int jz4770_uart0_data_pins[] = { 0xa0, 0xa3, };
static int jz4770_uart0_hwflow_pins[] = { 0xa1, 0xa2, };
static int jz4770_uart1_data_pins[] = { 0x7a, 0x7c, };
static int jz4770_uart1_hwflow_pins[] = { 0x7b, 0x7d, };
-static int jz4770_uart2_data_pins[] = { 0x66, 0x67, };
-static int jz4770_uart2_hwflow_pins[] = { 0x65, 0x64, };
+static int jz4770_uart2_data_pins[] = { 0x5c, 0x5e, };
+static int jz4770_uart2_hwflow_pins[] = { 0x5d, 0x5f, };
static int jz4770_uart3_data_pins[] = { 0x6c, 0x85, };
static int jz4770_uart3_hwflow_pins[] = { 0x88, 0x89, };
-static int jz4770_uart4_data_pins[] = { 0x54, 0x4a, };
-static int jz4770_mmc0_8bit_a_pins[] = { 0x04, 0x05, 0x06, 0x07, 0x18, };
-static int jz4770_mmc0_4bit_a_pins[] = { 0x15, 0x16, 0x17, };
static int jz4770_mmc0_1bit_a_pins[] = { 0x12, 0x13, 0x14, };
-static int jz4770_mmc0_4bit_e_pins[] = { 0x95, 0x96, 0x97, };
+static int jz4770_mmc0_4bit_a_pins[] = { 0x15, 0x16, 0x17, };
static int jz4770_mmc0_1bit_e_pins[] = { 0x9c, 0x9d, 0x94, };
-static int jz4770_mmc1_4bit_d_pins[] = { 0x75, 0x76, 0x77, };
+static int jz4770_mmc0_4bit_e_pins[] = { 0x95, 0x96, 0x97, };
+static int jz4770_mmc0_8bit_e_pins[] = { 0x98, 0x99, 0x9a, 0x9b, };
static int jz4770_mmc1_1bit_d_pins[] = { 0x78, 0x79, 0x74, };
-static int jz4770_mmc1_4bit_e_pins[] = { 0x95, 0x96, 0x97, };
+static int jz4770_mmc1_4bit_d_pins[] = { 0x75, 0x76, 0x77, };
static int jz4770_mmc1_1bit_e_pins[] = { 0x9c, 0x9d, 0x94, };
-static int jz4770_nemc_data_pins[] = {
+static int jz4770_mmc1_4bit_e_pins[] = { 0x95, 0x96, 0x97, };
+static int jz4770_mmc1_8bit_e_pins[] = { 0x98, 0x99, 0x9a, 0x9b, };
+static int jz4770_mmc2_1bit_b_pins[] = { 0x3c, 0x3d, 0x34, };
+static int jz4770_mmc2_4bit_b_pins[] = { 0x35, 0x3e, 0x3f, };
+static int jz4770_mmc2_1bit_e_pins[] = { 0x9c, 0x9d, 0x94, };
+static int jz4770_mmc2_4bit_e_pins[] = { 0x95, 0x96, 0x97, };
+static int jz4770_mmc2_8bit_e_pins[] = { 0x98, 0x99, 0x9a, 0x9b, };
+static int jz4770_nemc_8bit_data_pins[] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
};
+static int jz4770_nemc_16bit_data_pins[] = {
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+};
static int jz4770_nemc_cle_ale_pins[] = { 0x20, 0x21, };
static int jz4770_nemc_addr_pins[] = { 0x22, 0x23, 0x24, 0x25, };
static int jz4770_nemc_rd_we_pins[] = { 0x10, 0x11, };
static int jz4770_nemc_frd_fwe_pins[] = { 0x12, 0x13, };
+static int jz4770_nemc_wait_pins[] = { 0x1b, };
static int jz4770_nemc_cs1_pins[] = { 0x15, };
static int jz4770_nemc_cs2_pins[] = { 0x16, };
static int jz4770_nemc_cs3_pins[] = { 0x17, };
static int jz4770_nemc_cs4_pins[] = { 0x18, };
static int jz4770_nemc_cs5_pins[] = { 0x19, };
static int jz4770_nemc_cs6_pins[] = { 0x1a, };
-static int jz4770_i2c0_pins[] = { 0x6e, 0x6f, };
-static int jz4770_i2c1_pins[] = { 0x8e, 0x8f, };
+static int jz4770_i2c0_pins[] = { 0x7e, 0x7f, };
+static int jz4770_i2c1_pins[] = { 0x9e, 0x9f, };
static int jz4770_i2c2_pins[] = { 0xb0, 0xb1, };
-static int jz4770_i2c3_pins[] = { 0x6a, 0x6b, };
-static int jz4770_i2c4_e_pins[] = { 0x8c, 0x8d, };
-static int jz4770_i2c4_f_pins[] = { 0xb9, 0xb8, };
-static int jz4770_cim_pins[] = {
- 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31,
+static int jz4770_cim_8bit_pins[] = {
+ 0x26, 0x27, 0x28, 0x29,
+ 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31,
+};
+static int jz4770_cim_12bit_pins[] = {
+ 0x32, 0x33, 0xb0, 0xb1,
};
-static int jz4770_lcd_32bit_pins[] = {
+static int jz4770_lcd_24bit_pins[] = {
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
- 0x58, 0x59, 0x51,
+ 0x58, 0x59, 0x5a, 0x5b,
};
static int jz4770_pwm_pwm0_pins[] = { 0x80, };
static int jz4770_pwm_pwm1_pins[] = { 0x81, };
@@ -371,30 +411,41 @@ static int jz4770_pwm_pwm4_pins[] = { 0x84, };
static int jz4770_pwm_pwm5_pins[] = { 0x85, };
static int jz4770_pwm_pwm6_pins[] = { 0x6a, };
static int jz4770_pwm_pwm7_pins[] = { 0x6b, };
+static int jz4770_mac_rmii_pins[] = {
+ 0xa9, 0xab, 0xaa, 0xac, 0xa5, 0xa4, 0xad, 0xae, 0xa6, 0xa8,
+};
+static int jz4770_mac_mii_pins[] = { 0xa7, 0xaf, };
static int jz4770_uart0_data_funcs[] = { 0, 0, };
static int jz4770_uart0_hwflow_funcs[] = { 0, 0, };
static int jz4770_uart1_data_funcs[] = { 0, 0, };
static int jz4770_uart1_hwflow_funcs[] = { 0, 0, };
-static int jz4770_uart2_data_funcs[] = { 1, 1, };
-static int jz4770_uart2_hwflow_funcs[] = { 1, 1, };
+static int jz4770_uart2_data_funcs[] = { 0, 0, };
+static int jz4770_uart2_hwflow_funcs[] = { 0, 0, };
static int jz4770_uart3_data_funcs[] = { 0, 1, };
static int jz4770_uart3_hwflow_funcs[] = { 0, 0, };
-static int jz4770_uart4_data_funcs[] = { 2, 2, };
-static int jz4770_mmc0_8bit_a_funcs[] = { 1, 1, 1, 1, 1, };
-static int jz4770_mmc0_4bit_a_funcs[] = { 1, 1, 1, };
static int jz4770_mmc0_1bit_a_funcs[] = { 1, 1, 0, };
-static int jz4770_mmc0_4bit_e_funcs[] = { 0, 0, 0, };
+static int jz4770_mmc0_4bit_a_funcs[] = { 1, 1, 1, };
static int jz4770_mmc0_1bit_e_funcs[] = { 0, 0, 0, };
-static int jz4770_mmc1_4bit_d_funcs[] = { 0, 0, 0, };
+static int jz4770_mmc0_4bit_e_funcs[] = { 0, 0, 0, };
+static int jz4770_mmc0_8bit_e_funcs[] = { 0, 0, 0, 0, };
static int jz4770_mmc1_1bit_d_funcs[] = { 0, 0, 0, };
-static int jz4770_mmc1_4bit_e_funcs[] = { 1, 1, 1, };
+static int jz4770_mmc1_4bit_d_funcs[] = { 0, 0, 0, };
static int jz4770_mmc1_1bit_e_funcs[] = { 1, 1, 1, };
-static int jz4770_nemc_data_funcs[] = { 0, 0, 0, 0, 0, 0, 0, 0, };
+static int jz4770_mmc1_4bit_e_funcs[] = { 1, 1, 1, };
+static int jz4770_mmc1_8bit_e_funcs[] = { 1, 1, 1, 1, };
+static int jz4770_mmc2_1bit_b_funcs[] = { 0, 0, 0, };
+static int jz4770_mmc2_4bit_b_funcs[] = { 0, 0, 0, };
+static int jz4770_mmc2_1bit_e_funcs[] = { 2, 2, 2, };
+static int jz4770_mmc2_4bit_e_funcs[] = { 2, 2, 2, };
+static int jz4770_mmc2_8bit_e_funcs[] = { 2, 2, 2, 2, };
+static int jz4770_nemc_8bit_data_funcs[] = { 0, 0, 0, 0, 0, 0, 0, 0, };
+static int jz4770_nemc_16bit_data_funcs[] = { 0, 0, 0, 0, 0, 0, 0, 0, };
static int jz4770_nemc_cle_ale_funcs[] = { 0, 0, };
static int jz4770_nemc_addr_funcs[] = { 0, 0, 0, 0, };
static int jz4770_nemc_rd_we_funcs[] = { 0, 0, };
static int jz4770_nemc_frd_fwe_funcs[] = { 0, 0, };
+static int jz4770_nemc_wait_funcs[] = { 0, };
static int jz4770_nemc_cs1_funcs[] = { 0, };
static int jz4770_nemc_cs2_funcs[] = { 0, };
static int jz4770_nemc_cs3_funcs[] = { 0, };
@@ -404,14 +455,13 @@ static int jz4770_nemc_cs6_funcs[] = { 0, };
static int jz4770_i2c0_funcs[] = { 0, 0, };
static int jz4770_i2c1_funcs[] = { 0, 0, };
static int jz4770_i2c2_funcs[] = { 2, 2, };
-static int jz4770_i2c3_funcs[] = { 1, 1, };
-static int jz4770_i2c4_e_funcs[] = { 1, 1, };
-static int jz4770_i2c4_f_funcs[] = { 1, 1, };
-static int jz4770_cim_funcs[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, };
-static int jz4770_lcd_32bit_funcs[] = {
+static int jz4770_cim_8bit_funcs[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, };
+static int jz4770_cim_12bit_funcs[] = { 0, 0, 0, 0, };
+static int jz4770_lcd_24bit_funcs[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0,
+ 0, 0, 0, 0,
};
static int jz4770_pwm_pwm0_funcs[] = { 0, };
static int jz4770_pwm_pwm1_funcs[] = { 0, };
@@ -421,6 +471,8 @@ static int jz4770_pwm_pwm4_funcs[] = { 0, };
static int jz4770_pwm_pwm5_funcs[] = { 0, };
static int jz4770_pwm_pwm6_funcs[] = { 0, };
static int jz4770_pwm_pwm7_funcs[] = { 0, };
+static int jz4770_mac_rmii_funcs[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, };
+static int jz4770_mac_mii_funcs[] = { 0, 0, };
static const struct group_desc jz4770_groups[] = {
INGENIC_PIN_GROUP("uart0-data", jz4770_uart0_data),
@@ -431,21 +483,28 @@ static const struct group_desc jz4770_groups[] = {
INGENIC_PIN_GROUP("uart2-hwflow", jz4770_uart2_hwflow),
INGENIC_PIN_GROUP("uart3-data", jz4770_uart3_data),
INGENIC_PIN_GROUP("uart3-hwflow", jz4770_uart3_hwflow),
- INGENIC_PIN_GROUP("uart4-data", jz4770_uart4_data),
- INGENIC_PIN_GROUP("mmc0-8bit-a", jz4770_mmc0_8bit_a),
- INGENIC_PIN_GROUP("mmc0-4bit-a", jz4770_mmc0_4bit_a),
INGENIC_PIN_GROUP("mmc0-1bit-a", jz4770_mmc0_1bit_a),
- INGENIC_PIN_GROUP("mmc0-4bit-e", jz4770_mmc0_4bit_e),
+ INGENIC_PIN_GROUP("mmc0-4bit-a", jz4770_mmc0_4bit_a),
INGENIC_PIN_GROUP("mmc0-1bit-e", jz4770_mmc0_1bit_e),
- INGENIC_PIN_GROUP("mmc1-4bit-d", jz4770_mmc1_4bit_d),
+ INGENIC_PIN_GROUP("mmc0-4bit-e", jz4770_mmc0_4bit_e),
+ INGENIC_PIN_GROUP("mmc0-8bit-e", jz4770_mmc0_8bit_e),
INGENIC_PIN_GROUP("mmc1-1bit-d", jz4770_mmc1_1bit_d),
- INGENIC_PIN_GROUP("mmc1-4bit-e", jz4770_mmc1_4bit_e),
+ INGENIC_PIN_GROUP("mmc1-4bit-d", jz4770_mmc1_4bit_d),
INGENIC_PIN_GROUP("mmc1-1bit-e", jz4770_mmc1_1bit_e),
- INGENIC_PIN_GROUP("nemc-data", jz4770_nemc_data),
+ INGENIC_PIN_GROUP("mmc1-4bit-e", jz4770_mmc1_4bit_e),
+ INGENIC_PIN_GROUP("mmc1-8bit-e", jz4770_mmc1_8bit_e),
+ INGENIC_PIN_GROUP("mmc2-1bit-b", jz4770_mmc2_1bit_b),
+ INGENIC_PIN_GROUP("mmc2-4bit-b", jz4770_mmc2_4bit_b),
+ INGENIC_PIN_GROUP("mmc2-1bit-e", jz4770_mmc2_1bit_e),
+ INGENIC_PIN_GROUP("mmc2-4bit-e", jz4770_mmc2_4bit_e),
+ INGENIC_PIN_GROUP("mmc2-8bit-e", jz4770_mmc2_8bit_e),
+ INGENIC_PIN_GROUP("nemc-8bit-data", jz4770_nemc_8bit_data),
+ INGENIC_PIN_GROUP("nemc-16bit-data", jz4770_nemc_16bit_data),
INGENIC_PIN_GROUP("nemc-cle-ale", jz4770_nemc_cle_ale),
INGENIC_PIN_GROUP("nemc-addr", jz4770_nemc_addr),
INGENIC_PIN_GROUP("nemc-rd-we", jz4770_nemc_rd_we),
INGENIC_PIN_GROUP("nemc-frd-fwe", jz4770_nemc_frd_fwe),
+ INGENIC_PIN_GROUP("nemc-wait", jz4770_nemc_wait),
INGENIC_PIN_GROUP("nemc-cs1", jz4770_nemc_cs1),
INGENIC_PIN_GROUP("nemc-cs2", jz4770_nemc_cs2),
INGENIC_PIN_GROUP("nemc-cs3", jz4770_nemc_cs3),
@@ -455,11 +514,9 @@ static const struct group_desc jz4770_groups[] = {
INGENIC_PIN_GROUP("i2c0-data", jz4770_i2c0),
INGENIC_PIN_GROUP("i2c1-data", jz4770_i2c1),
INGENIC_PIN_GROUP("i2c2-data", jz4770_i2c2),
- INGENIC_PIN_GROUP("i2c3-data", jz4770_i2c3),
- INGENIC_PIN_GROUP("i2c4-data-e", jz4770_i2c4_e),
- INGENIC_PIN_GROUP("i2c4-data-f", jz4770_i2c4_f),
- INGENIC_PIN_GROUP("cim-data", jz4770_cim),
- INGENIC_PIN_GROUP("lcd-32bit", jz4770_lcd_32bit),
+ INGENIC_PIN_GROUP("cim-data-8bit", jz4770_cim_8bit),
+ INGENIC_PIN_GROUP("cim-data-12bit", jz4770_cim_12bit),
+ INGENIC_PIN_GROUP("lcd-24bit", jz4770_lcd_24bit),
{ "lcd-no-pins", },
INGENIC_PIN_GROUP("pwm0", jz4770_pwm_pwm0),
INGENIC_PIN_GROUP("pwm1", jz4770_pwm_pwm1),
@@ -469,32 +526,41 @@ static const struct group_desc jz4770_groups[] = {
INGENIC_PIN_GROUP("pwm5", jz4770_pwm_pwm5),
INGENIC_PIN_GROUP("pwm6", jz4770_pwm_pwm6),
INGENIC_PIN_GROUP("pwm7", jz4770_pwm_pwm7),
+ INGENIC_PIN_GROUP("mac-rmii", jz4770_mac_rmii),
+ INGENIC_PIN_GROUP("mac-mii", jz4770_mac_mii),
};
static const char *jz4770_uart0_groups[] = { "uart0-data", "uart0-hwflow", };
static const char *jz4770_uart1_groups[] = { "uart1-data", "uart1-hwflow", };
static const char *jz4770_uart2_groups[] = { "uart2-data", "uart2-hwflow", };
static const char *jz4770_uart3_groups[] = { "uart3-data", "uart3-hwflow", };
-static const char *jz4770_uart4_groups[] = { "uart4-data", };
static const char *jz4770_mmc0_groups[] = {
- "mmc0-8bit-a", "mmc0-4bit-a", "mmc0-1bit-a",
- "mmc0-1bit-e", "mmc0-4bit-e",
+ "mmc0-1bit-a", "mmc0-4bit-a",
+ "mmc0-1bit-e", "mmc0-4bit-e", "mmc0-8bit-e",
};
static const char *jz4770_mmc1_groups[] = {
- "mmc1-1bit-d", "mmc1-4bit-d", "mmc1-1bit-e", "mmc1-4bit-e",
+ "mmc1-1bit-d", "mmc1-4bit-d",
+ "mmc1-1bit-e", "mmc1-4bit-e", "mmc1-8bit-e",
+};
+static const char *jz4770_mmc2_groups[] = {
+ "mmc2-1bit-b", "mmc2-4bit-b",
+ "mmc2-1bit-e", "mmc2-4bit-e", "mmc2-8bit-e",
};
static const char *jz4770_nemc_groups[] = {
- "nemc-data", "nemc-cle-ale", "nemc-addr", "nemc-rd-we", "nemc-frd-fwe",
+ "nemc-8bit-data", "nemc-16bit-data", "nemc-cle-ale",
+ "nemc-addr", "nemc-rd-we", "nemc-frd-fwe", "nemc-wait",
};
static const char *jz4770_cs1_groups[] = { "nemc-cs1", };
+static const char *jz4770_cs2_groups[] = { "nemc-cs2", };
+static const char *jz4770_cs3_groups[] = { "nemc-cs3", };
+static const char *jz4770_cs4_groups[] = { "nemc-cs4", };
+static const char *jz4770_cs5_groups[] = { "nemc-cs5", };
static const char *jz4770_cs6_groups[] = { "nemc-cs6", };
static const char *jz4770_i2c0_groups[] = { "i2c0-data", };
static const char *jz4770_i2c1_groups[] = { "i2c1-data", };
static const char *jz4770_i2c2_groups[] = { "i2c2-data", };
-static const char *jz4770_i2c3_groups[] = { "i2c3-data", };
-static const char *jz4770_i2c4_groups[] = { "i2c4-data-e", "i2c4-data-f", };
-static const char *jz4770_cim_groups[] = { "cim-data", };
-static const char *jz4770_lcd_groups[] = { "lcd-32bit", "lcd-no-pins", };
+static const char *jz4770_cim_groups[] = { "cim-data-8bit", "cim-data-12bit", };
+static const char *jz4770_lcd_groups[] = { "lcd-24bit", "lcd-no-pins", };
static const char *jz4770_pwm0_groups[] = { "pwm0", };
static const char *jz4770_pwm1_groups[] = { "pwm1", };
static const char *jz4770_pwm2_groups[] = { "pwm2", };
@@ -503,23 +569,26 @@ static const char *jz4770_pwm4_groups[] = { "pwm4", };
static const char *jz4770_pwm5_groups[] = { "pwm5", };
static const char *jz4770_pwm6_groups[] = { "pwm6", };
static const char *jz4770_pwm7_groups[] = { "pwm7", };
+static const char *jz4770_mac_groups[] = { "mac-rmii", "mac-mii", };
static const struct function_desc jz4770_functions[] = {
{ "uart0", jz4770_uart0_groups, ARRAY_SIZE(jz4770_uart0_groups), },
{ "uart1", jz4770_uart1_groups, ARRAY_SIZE(jz4770_uart1_groups), },
{ "uart2", jz4770_uart2_groups, ARRAY_SIZE(jz4770_uart2_groups), },
{ "uart3", jz4770_uart3_groups, ARRAY_SIZE(jz4770_uart3_groups), },
- { "uart4", jz4770_uart4_groups, ARRAY_SIZE(jz4770_uart4_groups), },
{ "mmc0", jz4770_mmc0_groups, ARRAY_SIZE(jz4770_mmc0_groups), },
{ "mmc1", jz4770_mmc1_groups, ARRAY_SIZE(jz4770_mmc1_groups), },
+ { "mmc2", jz4770_mmc2_groups, ARRAY_SIZE(jz4770_mmc2_groups), },
{ "nemc", jz4770_nemc_groups, ARRAY_SIZE(jz4770_nemc_groups), },
{ "nemc-cs1", jz4770_cs1_groups, ARRAY_SIZE(jz4770_cs1_groups), },
+ { "nemc-cs2", jz4770_cs2_groups, ARRAY_SIZE(jz4770_cs2_groups), },
+ { "nemc-cs3", jz4770_cs3_groups, ARRAY_SIZE(jz4770_cs3_groups), },
+ { "nemc-cs4", jz4770_cs4_groups, ARRAY_SIZE(jz4770_cs4_groups), },
+ { "nemc-cs5", jz4770_cs5_groups, ARRAY_SIZE(jz4770_cs5_groups), },
{ "nemc-cs6", jz4770_cs6_groups, ARRAY_SIZE(jz4770_cs6_groups), },
{ "i2c0", jz4770_i2c0_groups, ARRAY_SIZE(jz4770_i2c0_groups), },
{ "i2c1", jz4770_i2c1_groups, ARRAY_SIZE(jz4770_i2c1_groups), },
{ "i2c2", jz4770_i2c2_groups, ARRAY_SIZE(jz4770_i2c2_groups), },
- { "i2c3", jz4770_i2c3_groups, ARRAY_SIZE(jz4770_i2c3_groups), },
- { "i2c4", jz4770_i2c4_groups, ARRAY_SIZE(jz4770_i2c4_groups), },
{ "cim", jz4770_cim_groups, ARRAY_SIZE(jz4770_cim_groups), },
{ "lcd", jz4770_lcd_groups, ARRAY_SIZE(jz4770_lcd_groups), },
{ "pwm0", jz4770_pwm0_groups, ARRAY_SIZE(jz4770_pwm0_groups), },
@@ -530,6 +599,7 @@ static const struct function_desc jz4770_functions[] = {
{ "pwm5", jz4770_pwm5_groups, ARRAY_SIZE(jz4770_pwm5_groups), },
{ "pwm6", jz4770_pwm6_groups, ARRAY_SIZE(jz4770_pwm6_groups), },
{ "pwm7", jz4770_pwm7_groups, ARRAY_SIZE(jz4770_pwm7_groups), },
+ { "mac", jz4770_mac_groups, ARRAY_SIZE(jz4770_mac_groups), },
};
static const struct ingenic_chip_info jz4770_chip_info = {
@@ -542,7 +612,140 @@ static const struct ingenic_chip_info jz4770_chip_info = {
.pull_downs = jz4770_pull_downs,
};
-static u32 gpio_ingenic_read_reg(struct ingenic_gpio_chip *jzgc, u8 reg)
+static int jz4780_uart2_data_pins[] = { 0x66, 0x67, };
+static int jz4780_uart2_hwflow_pins[] = { 0x65, 0x64, };
+static int jz4780_uart4_data_pins[] = { 0x54, 0x4a, };
+static int jz4780_mmc0_8bit_a_pins[] = { 0x04, 0x05, 0x06, 0x07, 0x18, };
+static int jz4780_i2c3_pins[] = { 0x6a, 0x6b, };
+static int jz4780_i2c4_e_pins[] = { 0x8c, 0x8d, };
+static int jz4780_i2c4_f_pins[] = { 0xb9, 0xb8, };
+
+static int jz4780_uart2_data_funcs[] = { 1, 1, };
+static int jz4780_uart2_hwflow_funcs[] = { 1, 1, };
+static int jz4780_uart4_data_funcs[] = { 2, 2, };
+static int jz4780_mmc0_8bit_a_funcs[] = { 1, 1, 1, 1, 1, };
+static int jz4780_i2c3_funcs[] = { 1, 1, };
+static int jz4780_i2c4_e_funcs[] = { 1, 1, };
+static int jz4780_i2c4_f_funcs[] = { 1, 1, };
+
+static const struct group_desc jz4780_groups[] = {
+ INGENIC_PIN_GROUP("uart0-data", jz4770_uart0_data),
+ INGENIC_PIN_GROUP("uart0-hwflow", jz4770_uart0_hwflow),
+ INGENIC_PIN_GROUP("uart1-data", jz4770_uart1_data),
+ INGENIC_PIN_GROUP("uart1-hwflow", jz4770_uart1_hwflow),
+ INGENIC_PIN_GROUP("uart2-data", jz4780_uart2_data),
+ INGENIC_PIN_GROUP("uart2-hwflow", jz4780_uart2_hwflow),
+ INGENIC_PIN_GROUP("uart3-data", jz4770_uart3_data),
+ INGENIC_PIN_GROUP("uart3-hwflow", jz4770_uart3_hwflow),
+ INGENIC_PIN_GROUP("uart4-data", jz4780_uart4_data),
+ INGENIC_PIN_GROUP("mmc0-1bit-a", jz4770_mmc0_1bit_a),
+ INGENIC_PIN_GROUP("mmc0-4bit-a", jz4770_mmc0_4bit_a),
+ INGENIC_PIN_GROUP("mmc0-8bit-a", jz4780_mmc0_8bit_a),
+ INGENIC_PIN_GROUP("mmc0-1bit-e", jz4770_mmc0_1bit_e),
+ INGENIC_PIN_GROUP("mmc0-4bit-e", jz4770_mmc0_4bit_e),
+ INGENIC_PIN_GROUP("mmc1-1bit-d", jz4770_mmc1_1bit_d),
+ INGENIC_PIN_GROUP("mmc1-4bit-d", jz4770_mmc1_4bit_d),
+ INGENIC_PIN_GROUP("mmc1-1bit-e", jz4770_mmc1_1bit_e),
+ INGENIC_PIN_GROUP("mmc1-4bit-e", jz4770_mmc1_4bit_e),
+ INGENIC_PIN_GROUP("mmc2-1bit-b", jz4770_mmc2_1bit_b),
+ INGENIC_PIN_GROUP("mmc2-4bit-b", jz4770_mmc2_4bit_b),
+ INGENIC_PIN_GROUP("mmc2-1bit-e", jz4770_mmc2_1bit_e),
+ INGENIC_PIN_GROUP("mmc2-4bit-e", jz4770_mmc2_4bit_e),
+ INGENIC_PIN_GROUP("nemc-data", jz4770_nemc_8bit_data),
+ INGENIC_PIN_GROUP("nemc-cle-ale", jz4770_nemc_cle_ale),
+ INGENIC_PIN_GROUP("nemc-addr", jz4770_nemc_addr),
+ INGENIC_PIN_GROUP("nemc-rd-we", jz4770_nemc_rd_we),
+ INGENIC_PIN_GROUP("nemc-frd-fwe", jz4770_nemc_frd_fwe),
+ INGENIC_PIN_GROUP("nemc-wait", jz4770_nemc_wait),
+ INGENIC_PIN_GROUP("nemc-cs1", jz4770_nemc_cs1),
+ INGENIC_PIN_GROUP("nemc-cs2", jz4770_nemc_cs2),
+ INGENIC_PIN_GROUP("nemc-cs3", jz4770_nemc_cs3),
+ INGENIC_PIN_GROUP("nemc-cs4", jz4770_nemc_cs4),
+ INGENIC_PIN_GROUP("nemc-cs5", jz4770_nemc_cs5),
+ INGENIC_PIN_GROUP("nemc-cs6", jz4770_nemc_cs6),
+ INGENIC_PIN_GROUP("i2c0-data", jz4770_i2c0),
+ INGENIC_PIN_GROUP("i2c1-data", jz4770_i2c1),
+ INGENIC_PIN_GROUP("i2c2-data", jz4770_i2c2),
+ INGENIC_PIN_GROUP("i2c3-data", jz4780_i2c3),
+ INGENIC_PIN_GROUP("i2c4-data-e", jz4780_i2c4_e),
+ INGENIC_PIN_GROUP("i2c4-data-f", jz4780_i2c4_f),
+ INGENIC_PIN_GROUP("cim-data", jz4770_cim_8bit),
+ INGENIC_PIN_GROUP("lcd-24bit", jz4770_lcd_24bit),
+ { "lcd-no-pins", },
+ INGENIC_PIN_GROUP("pwm0", jz4770_pwm_pwm0),
+ INGENIC_PIN_GROUP("pwm1", jz4770_pwm_pwm1),
+ INGENIC_PIN_GROUP("pwm2", jz4770_pwm_pwm2),
+ INGENIC_PIN_GROUP("pwm3", jz4770_pwm_pwm3),
+ INGENIC_PIN_GROUP("pwm4", jz4770_pwm_pwm4),
+ INGENIC_PIN_GROUP("pwm5", jz4770_pwm_pwm5),
+ INGENIC_PIN_GROUP("pwm6", jz4770_pwm_pwm6),
+ INGENIC_PIN_GROUP("pwm7", jz4770_pwm_pwm7),
+};
+
+static const char *jz4780_uart2_groups[] = { "uart2-data", "uart2-hwflow", };
+static const char *jz4780_uart4_groups[] = { "uart4-data", };
+static const char *jz4780_mmc0_groups[] = {
+ "mmc0-1bit-a", "mmc0-4bit-a", "mmc0-8bit-a",
+ "mmc0-1bit-e", "mmc0-4bit-e",
+};
+static const char *jz4780_mmc1_groups[] = {
+ "mmc1-1bit-d", "mmc1-4bit-d", "mmc1-1bit-e", "mmc1-4bit-e",
+};
+static const char *jz4780_mmc2_groups[] = {
+ "mmc2-1bit-b", "mmc2-4bit-b", "mmc2-1bit-e", "mmc2-4bit-e",
+};
+static const char *jz4780_nemc_groups[] = {
+ "nemc-data", "nemc-cle-ale", "nemc-addr",
+ "nemc-rd-we", "nemc-frd-fwe", "nemc-wait",
+};
+static const char *jz4780_i2c3_groups[] = { "i2c3-data", };
+static const char *jz4780_i2c4_groups[] = { "i2c4-data-e", "i2c4-data-f", };
+static const char *jz4780_cim_groups[] = { "cim-data", };
+
+static const struct function_desc jz4780_functions[] = {
+ { "uart0", jz4770_uart0_groups, ARRAY_SIZE(jz4770_uart0_groups), },
+ { "uart1", jz4770_uart1_groups, ARRAY_SIZE(jz4770_uart1_groups), },
+ { "uart2", jz4780_uart2_groups, ARRAY_SIZE(jz4780_uart2_groups), },
+ { "uart3", jz4770_uart3_groups, ARRAY_SIZE(jz4770_uart3_groups), },
+ { "uart4", jz4780_uart4_groups, ARRAY_SIZE(jz4780_uart4_groups), },
+ { "mmc0", jz4780_mmc0_groups, ARRAY_SIZE(jz4780_mmc0_groups), },
+ { "mmc1", jz4780_mmc1_groups, ARRAY_SIZE(jz4780_mmc1_groups), },
+ { "mmc2", jz4780_mmc2_groups, ARRAY_SIZE(jz4780_mmc2_groups), },
+ { "nemc", jz4780_nemc_groups, ARRAY_SIZE(jz4780_nemc_groups), },
+ { "nemc-cs1", jz4770_cs1_groups, ARRAY_SIZE(jz4770_cs1_groups), },
+ { "nemc-cs2", jz4770_cs2_groups, ARRAY_SIZE(jz4770_cs2_groups), },
+ { "nemc-cs3", jz4770_cs3_groups, ARRAY_SIZE(jz4770_cs3_groups), },
+ { "nemc-cs4", jz4770_cs4_groups, ARRAY_SIZE(jz4770_cs4_groups), },
+ { "nemc-cs5", jz4770_cs5_groups, ARRAY_SIZE(jz4770_cs5_groups), },
+ { "nemc-cs6", jz4770_cs6_groups, ARRAY_SIZE(jz4770_cs6_groups), },
+ { "i2c0", jz4770_i2c0_groups, ARRAY_SIZE(jz4770_i2c0_groups), },
+ { "i2c1", jz4770_i2c1_groups, ARRAY_SIZE(jz4770_i2c1_groups), },
+ { "i2c2", jz4770_i2c2_groups, ARRAY_SIZE(jz4770_i2c2_groups), },
+ { "i2c3", jz4780_i2c3_groups, ARRAY_SIZE(jz4780_i2c3_groups), },
+ { "i2c4", jz4780_i2c4_groups, ARRAY_SIZE(jz4780_i2c4_groups), },
+ { "cim", jz4780_cim_groups, ARRAY_SIZE(jz4780_cim_groups), },
+ { "lcd", jz4770_lcd_groups, ARRAY_SIZE(jz4770_lcd_groups), },
+ { "pwm0", jz4770_pwm0_groups, ARRAY_SIZE(jz4770_pwm0_groups), },
+ { "pwm1", jz4770_pwm1_groups, ARRAY_SIZE(jz4770_pwm1_groups), },
+ { "pwm2", jz4770_pwm2_groups, ARRAY_SIZE(jz4770_pwm2_groups), },
+ { "pwm3", jz4770_pwm3_groups, ARRAY_SIZE(jz4770_pwm3_groups), },
+ { "pwm4", jz4770_pwm4_groups, ARRAY_SIZE(jz4770_pwm4_groups), },
+ { "pwm5", jz4770_pwm5_groups, ARRAY_SIZE(jz4770_pwm5_groups), },
+ { "pwm6", jz4770_pwm6_groups, ARRAY_SIZE(jz4770_pwm6_groups), },
+ { "pwm7", jz4770_pwm7_groups, ARRAY_SIZE(jz4770_pwm7_groups), },
+};
+
+static const struct ingenic_chip_info jz4780_chip_info = {
+ .num_chips = 6,
+ .groups = jz4780_groups,
+ .num_groups = ARRAY_SIZE(jz4780_groups),
+ .functions = jz4780_functions,
+ .num_functions = ARRAY_SIZE(jz4780_functions),
+ .pull_ups = jz4770_pull_ups,
+ .pull_downs = jz4770_pull_downs,
+};
+
+static u32 ingenic_gpio_read_reg(struct ingenic_gpio_chip *jzgc, u8 reg)
{
unsigned int val;
@@ -551,7 +754,7 @@ static u32 gpio_ingenic_read_reg(struct ingenic_gpio_chip *jzgc, u8 reg)
return (u32) val;
}
-static void gpio_ingenic_set_bit(struct ingenic_gpio_chip *jzgc,
+static void ingenic_gpio_set_bit(struct ingenic_gpio_chip *jzgc,
u8 reg, u8 offset, bool set)
{
if (set)
@@ -565,7 +768,7 @@ static void gpio_ingenic_set_bit(struct ingenic_gpio_chip *jzgc,
static inline bool ingenic_gpio_get_value(struct ingenic_gpio_chip *jzgc,
u8 offset)
{
- unsigned int val = gpio_ingenic_read_reg(jzgc, GPIO_PIN);
+ unsigned int val = ingenic_gpio_read_reg(jzgc, GPIO_PIN);
return !!(val & BIT(offset));
}
@@ -574,9 +777,9 @@ static void ingenic_gpio_set_value(struct ingenic_gpio_chip *jzgc,
u8 offset, int value)
{
if (jzgc->jzpc->version >= ID_JZ4770)
- gpio_ingenic_set_bit(jzgc, JZ4770_GPIO_PAT0, offset, !!value);
+ ingenic_gpio_set_bit(jzgc, JZ4770_GPIO_PAT0, offset, !!value);
else
- gpio_ingenic_set_bit(jzgc, JZ4740_GPIO_DATA, offset, !!value);
+ ingenic_gpio_set_bit(jzgc, JZ4740_GPIO_DATA, offset, !!value);
}
static void irq_set_type(struct ingenic_gpio_chip *jzgc,
@@ -594,21 +797,21 @@ static void irq_set_type(struct ingenic_gpio_chip *jzgc,
switch (type) {
case IRQ_TYPE_EDGE_RISING:
- gpio_ingenic_set_bit(jzgc, reg2, offset, true);
- gpio_ingenic_set_bit(jzgc, reg1, offset, true);
+ ingenic_gpio_set_bit(jzgc, reg2, offset, true);
+ ingenic_gpio_set_bit(jzgc, reg1, offset, true);
break;
case IRQ_TYPE_EDGE_FALLING:
- gpio_ingenic_set_bit(jzgc, reg2, offset, false);
- gpio_ingenic_set_bit(jzgc, reg1, offset, true);
+ ingenic_gpio_set_bit(jzgc, reg2, offset, false);
+ ingenic_gpio_set_bit(jzgc, reg1, offset, true);
break;
case IRQ_TYPE_LEVEL_HIGH:
- gpio_ingenic_set_bit(jzgc, reg2, offset, true);
- gpio_ingenic_set_bit(jzgc, reg1, offset, false);
+ ingenic_gpio_set_bit(jzgc, reg2, offset, true);
+ ingenic_gpio_set_bit(jzgc, reg1, offset, false);
break;
case IRQ_TYPE_LEVEL_LOW:
default:
- gpio_ingenic_set_bit(jzgc, reg2, offset, false);
- gpio_ingenic_set_bit(jzgc, reg1, offset, false);
+ ingenic_gpio_set_bit(jzgc, reg2, offset, false);
+ ingenic_gpio_set_bit(jzgc, reg1, offset, false);
break;
}
}
@@ -618,7 +821,7 @@ static void ingenic_gpio_irq_mask(struct irq_data *irqd)
struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd);
struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc);
- gpio_ingenic_set_bit(jzgc, GPIO_MSK, irqd->hwirq, true);
+ ingenic_gpio_set_bit(jzgc, GPIO_MSK, irqd->hwirq, true);
}
static void ingenic_gpio_irq_unmask(struct irq_data *irqd)
@@ -626,7 +829,7 @@ static void ingenic_gpio_irq_unmask(struct irq_data *irqd)
struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd);
struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc);
- gpio_ingenic_set_bit(jzgc, GPIO_MSK, irqd->hwirq, false);
+ ingenic_gpio_set_bit(jzgc, GPIO_MSK, irqd->hwirq, false);
}
static void ingenic_gpio_irq_enable(struct irq_data *irqd)
@@ -636,9 +839,9 @@ static void ingenic_gpio_irq_enable(struct irq_data *irqd)
int irq = irqd->hwirq;
if (jzgc->jzpc->version >= ID_JZ4770)
- gpio_ingenic_set_bit(jzgc, JZ4770_GPIO_INT, irq, true);
+ ingenic_gpio_set_bit(jzgc, JZ4770_GPIO_INT, irq, true);
else
- gpio_ingenic_set_bit(jzgc, JZ4740_GPIO_SELECT, irq, true);
+ ingenic_gpio_set_bit(jzgc, JZ4740_GPIO_SELECT, irq, true);
ingenic_gpio_irq_unmask(irqd);
}
@@ -652,9 +855,9 @@ static void ingenic_gpio_irq_disable(struct irq_data *irqd)
ingenic_gpio_irq_mask(irqd);
if (jzgc->jzpc->version >= ID_JZ4770)
- gpio_ingenic_set_bit(jzgc, JZ4770_GPIO_INT, irq, false);
+ ingenic_gpio_set_bit(jzgc, JZ4770_GPIO_INT, irq, false);
else
- gpio_ingenic_set_bit(jzgc, JZ4740_GPIO_SELECT, irq, false);
+ ingenic_gpio_set_bit(jzgc, JZ4740_GPIO_SELECT, irq, false);
}
static void ingenic_gpio_irq_ack(struct irq_data *irqd)
@@ -677,9 +880,9 @@ static void ingenic_gpio_irq_ack(struct irq_data *irqd)
}
if (jzgc->jzpc->version >= ID_JZ4770)
- gpio_ingenic_set_bit(jzgc, JZ4770_GPIO_FLAG, irq, false);
+ ingenic_gpio_set_bit(jzgc, JZ4770_GPIO_FLAG, irq, false);
else
- gpio_ingenic_set_bit(jzgc, JZ4740_GPIO_DATA, irq, true);
+ ingenic_gpio_set_bit(jzgc, JZ4740_GPIO_DATA, irq, true);
}
static int ingenic_gpio_irq_set_type(struct irq_data *irqd, unsigned int type)
@@ -734,9 +937,9 @@ static void ingenic_gpio_irq_handler(struct irq_desc *desc)
chained_irq_enter(irq_chip, desc);
if (jzgc->jzpc->version >= ID_JZ4770)
- flag = gpio_ingenic_read_reg(jzgc, JZ4770_GPIO_FLAG);
+ flag = ingenic_gpio_read_reg(jzgc, JZ4770_GPIO_FLAG);
else
- flag = gpio_ingenic_read_reg(jzgc, JZ4740_GPIO_FLAG);
+ flag = ingenic_gpio_read_reg(jzgc, JZ4740_GPIO_FLAG);
for_each_set_bit(i, &flag, 32)
generic_handle_irq(irq_linear_revmap(gc->irq.domain, i));
@@ -1185,7 +1388,9 @@ static int __init ingenic_pinctrl_probe(struct platform_device *pdev)
else
jzpc->version = (enum jz_version)id->driver_data;
- if (jzpc->version >= ID_JZ4770)
+ if (jzpc->version >= ID_JZ4780)
+ chip_info = &jz4780_chip_info;
+ else if (jzpc->version >= ID_JZ4770)
chip_info = &jz4770_chip_info;
else if (jzpc->version >= ID_JZ4725B)
chip_info = &jz4725b_chip_info;
diff --git a/drivers/pinctrl/pinctrl-mcp23s08.c b/drivers/pinctrl/pinctrl-mcp23s08.c
index 98905d4a79ca..5d7a8514def9 100644
--- a/drivers/pinctrl/pinctrl-mcp23s08.c
+++ b/drivers/pinctrl/pinctrl-mcp23s08.c
@@ -68,6 +68,7 @@ struct mcp23s08 {
struct mutex lock;
struct gpio_chip chip;
+ struct irq_chip irq_chip;
struct regmap *regmap;
struct device *dev;
@@ -607,15 +608,6 @@ static void mcp23s08_irq_bus_unlock(struct irq_data *data)
mutex_unlock(&mcp->lock);
}
-static struct irq_chip mcp23s08_irq_chip = {
- .name = "gpio-mcp23xxx",
- .irq_mask = mcp23s08_irq_mask,
- .irq_unmask = mcp23s08_irq_unmask,
- .irq_set_type = mcp23s08_irq_set_type,
- .irq_bus_lock = mcp23s08_irq_bus_lock,
- .irq_bus_sync_unlock = mcp23s08_irq_bus_unlock,
-};
-
static int mcp23s08_irq_setup(struct mcp23s08 *mcp)
{
struct gpio_chip *chip = &mcp->chip;
@@ -645,7 +637,7 @@ static int mcp23s08_irqchip_setup(struct mcp23s08 *mcp)
int err;
err = gpiochip_irqchip_add_nested(chip,
- &mcp23s08_irq_chip,
+ &mcp->irq_chip,
0,
handle_simple_irq,
IRQ_TYPE_NONE);
@@ -656,7 +648,7 @@ static int mcp23s08_irqchip_setup(struct mcp23s08 *mcp)
}
gpiochip_set_nested_irqchip(chip,
- &mcp23s08_irq_chip,
+ &mcp->irq_chip,
mcp->irq);
return 0;
@@ -1047,6 +1039,13 @@ static int mcp230xx_probe(struct i2c_client *client,
return -ENOMEM;
mcp->irq = client->irq;
+ mcp->irq_chip.name = dev_name(&client->dev);
+ mcp->irq_chip.irq_mask = mcp23s08_irq_mask;
+ mcp->irq_chip.irq_unmask = mcp23s08_irq_unmask;
+ mcp->irq_chip.irq_set_type = mcp23s08_irq_set_type;
+ mcp->irq_chip.irq_bus_lock = mcp23s08_irq_bus_lock;
+ mcp->irq_chip.irq_bus_sync_unlock = mcp23s08_irq_bus_unlock;
+
status = mcp23s08_probe_one(mcp, &client->dev, client, client->addr,
id->driver_data, pdata->base, 0);
if (status)
@@ -1144,8 +1143,7 @@ static int mcp23s08_probe(struct spi_device *spi)
return -ENODEV;
data = devm_kzalloc(&spi->dev,
- sizeof(*data) + chips * sizeof(struct mcp23s08),
- GFP_KERNEL);
+ struct_size(data, chip, chips), GFP_KERNEL);
if (!data)
return -ENOMEM;
@@ -1157,6 +1155,13 @@ static int mcp23s08_probe(struct spi_device *spi)
chips--;
data->mcp[addr] = &data->chip[chips];
data->mcp[addr]->irq = spi->irq;
+ data->mcp[addr]->irq_chip.name = dev_name(&spi->dev);
+ data->mcp[addr]->irq_chip.irq_mask = mcp23s08_irq_mask;
+ data->mcp[addr]->irq_chip.irq_unmask = mcp23s08_irq_unmask;
+ data->mcp[addr]->irq_chip.irq_set_type = mcp23s08_irq_set_type;
+ data->mcp[addr]->irq_chip.irq_bus_lock = mcp23s08_irq_bus_lock;
+ data->mcp[addr]->irq_chip.irq_bus_sync_unlock =
+ mcp23s08_irq_bus_unlock;
status = mcp23s08_probe_one(data->mcp[addr], &spi->dev, spi,
0x40 | (addr << 1), type,
pdata->base, addr);
diff --git a/drivers/pinctrl/qcom/Kconfig b/drivers/pinctrl/qcom/Kconfig
index 836e9f3eae4c..2e66ab72c10b 100644
--- a/drivers/pinctrl/qcom/Kconfig
+++ b/drivers/pinctrl/qcom/Kconfig
@@ -137,6 +137,7 @@ config PINCTRL_QCOM_SPMI_PMIC
select PINMUX
select PINCONF
select GENERIC_PINCONF
+ select IRQ_DOMAIN_HIERARCHY
help
This is the pinctrl, pinmux, pinconf and gpiolib driver for the
Qualcomm GPIO and MPP blocks found in the Qualcomm PMIC's chips,
@@ -149,6 +150,7 @@ config PINCTRL_QCOM_SSBI_PMIC
select PINMUX
select PINCONF
select GENERIC_PINCONF
+ select IRQ_DOMAIN_HIERARCHY
help
This is the pinctrl, pinmux, pinconf and gpiolib driver for the
Qualcomm GPIO and MPP blocks found in the Qualcomm PMIC's chips,
diff --git a/drivers/pinctrl/qcom/pinctrl-qcs404.c b/drivers/pinctrl/qcom/pinctrl-qcs404.c
index 4ffd56ff809e..1c6ba978c69f 100644
--- a/drivers/pinctrl/qcom/pinctrl-qcs404.c
+++ b/drivers/pinctrl/qcom/pinctrl-qcs404.c
@@ -95,31 +95,6 @@ enum {
.intr_detection_width = -1, \
}
-#define UFS_RESET(pg_name, offset) \
- { \
- .name = #pg_name, \
- .pins = pg_name##_pins, \
- .npins = (unsigned int)ARRAY_SIZE(pg_name##_pins), \
- .ctl_reg = offset, \
- .io_reg = offset + 0x4, \
- .intr_cfg_reg = 0, \
- .intr_status_reg = 0, \
- .intr_target_reg = 0, \
- .tile = NORTH, \
- .mux_bit = -1, \
- .pull_bit = 3, \
- .drv_bit = 0, \
- .oe_bit = -1, \
- .in_bit = -1, \
- .out_bit = 0, \
- .intr_enable_bit = -1, \
- .intr_status_bit = -1, \
- .intr_target_bit = -1, \
- .intr_raw_status_bit = -1, \
- .intr_polarity_bit = -1, \
- .intr_detection_bit = -1, \
- .intr_detection_width = -1, \
- }
static const struct pinctrl_pin_desc qcs404_pins[] = {
PINCTRL_PIN(0, "GPIO_0"),
PINCTRL_PIN(1, "GPIO_1"),
diff --git a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c
index 4458d44dfcf6..76e57ae2f6e8 100644
--- a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c
+++ b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c
@@ -12,6 +12,7 @@
*/
#include <linux/gpio/driver.h>
+#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_irq.h>
@@ -136,7 +137,6 @@ enum pmic_gpio_func_index {
/**
* struct pmic_gpio_pad - keep current GPIO settings
* @base: Address base in SPMI device.
- * @irq: IRQ number which this GPIO generate.
* @is_enabled: Set to false when GPIO should be put in high Z state.
* @out_value: Cached pin output value
* @have_buffer: Set to true if GPIO output could be configured in push-pull,
@@ -156,7 +156,6 @@ enum pmic_gpio_func_index {
*/
struct pmic_gpio_pad {
u16 base;
- int irq;
bool is_enabled;
bool out_value;
bool have_buffer;
@@ -179,6 +178,8 @@ struct pmic_gpio_state {
struct regmap *map;
struct pinctrl_dev *ctrl;
struct gpio_chip chip;
+ struct fwnode_handle *fwnode;
+ struct irq_domain *domain;
};
static const struct pinconf_generic_params pmic_gpio_bindings[] = {
@@ -674,11 +675,11 @@ static void pmic_gpio_config_dbg_show(struct pinctrl_dev *pctldev,
else
seq_printf(s, " %-4s",
pad->output_enabled ? "out" : "in");
+ seq_printf(s, " %-4s", pad->out_value ? "high" : "low");
seq_printf(s, " %-7s", pmic_gpio_functions[function]);
seq_printf(s, " vin-%d", pad->power_source);
seq_printf(s, " %-27s", biases[pad->pullup]);
seq_printf(s, " %-10s", buffer_types[pad->buffer_type]);
- seq_printf(s, " %-4s", pad->out_value ? "high" : "low");
seq_printf(s, " %-7s", strengths[pad->strength]);
seq_printf(s, " atest-%d", pad->atest);
seq_printf(s, " dtest-%d", pad->dtest_buffer);
@@ -761,11 +762,18 @@ static int pmic_gpio_of_xlate(struct gpio_chip *chip,
static int pmic_gpio_to_irq(struct gpio_chip *chip, unsigned pin)
{
struct pmic_gpio_state *state = gpiochip_get_data(chip);
- struct pmic_gpio_pad *pad;
+ struct irq_fwspec fwspec;
- pad = state->ctrl->desc->pins[pin].drv_data;
+ fwspec.fwnode = state->fwnode;
+ fwspec.param_count = 2;
+ fwspec.param[0] = pin + PMIC_GPIO_PHYSICAL_OFFSET;
+ /*
+ * Set the type to a safe value temporarily. This will be overwritten
+ * later with the proper value by irq_set_type.
+ */
+ fwspec.param[1] = IRQ_TYPE_EDGE_RISING;
- return pad->irq;
+ return irq_create_fwspec_mapping(&fwspec);
}
static void pmic_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
@@ -935,8 +943,79 @@ static int pmic_gpio_populate(struct pmic_gpio_state *state,
return 0;
}
+static struct irq_chip pmic_gpio_irq_chip = {
+ .name = "spmi-gpio",
+ .irq_ack = irq_chip_ack_parent,
+ .irq_mask = irq_chip_mask_parent,
+ .irq_unmask = irq_chip_unmask_parent,
+ .irq_set_type = irq_chip_set_type_parent,
+ .irq_set_wake = irq_chip_set_wake_parent,
+ .flags = IRQCHIP_MASK_ON_SUSPEND,
+};
+
+static int pmic_gpio_domain_translate(struct irq_domain *domain,
+ struct irq_fwspec *fwspec,
+ unsigned long *hwirq,
+ unsigned int *type)
+{
+ struct pmic_gpio_state *state = container_of(domain->host_data,
+ struct pmic_gpio_state,
+ chip);
+
+ if (fwspec->param_count != 2 ||
+ fwspec->param[0] < 1 || fwspec->param[0] > state->chip.ngpio)
+ return -EINVAL;
+
+ *hwirq = fwspec->param[0] - PMIC_GPIO_PHYSICAL_OFFSET;
+ *type = fwspec->param[1];
+
+ return 0;
+}
+
+static int pmic_gpio_domain_alloc(struct irq_domain *domain, unsigned int virq,
+ unsigned int nr_irqs, void *data)
+{
+ struct pmic_gpio_state *state = container_of(domain->host_data,
+ struct pmic_gpio_state,
+ chip);
+ struct irq_fwspec *fwspec = data;
+ struct irq_fwspec parent_fwspec;
+ irq_hw_number_t hwirq;
+ unsigned int type;
+ int ret, i;
+
+ ret = pmic_gpio_domain_translate(domain, fwspec, &hwirq, &type);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < nr_irqs; i++)
+ irq_domain_set_info(domain, virq + i, hwirq + i,
+ &pmic_gpio_irq_chip, state,
+ handle_level_irq, NULL, NULL);
+
+ parent_fwspec.fwnode = domain->parent->fwnode;
+ parent_fwspec.param_count = 4;
+ parent_fwspec.param[0] = 0;
+ parent_fwspec.param[1] = hwirq + 0xc0;
+ parent_fwspec.param[2] = 0;
+ parent_fwspec.param[3] = fwspec->param[1];
+
+ return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs,
+ &parent_fwspec);
+}
+
+static const struct irq_domain_ops pmic_gpio_domain_ops = {
+ .activate = gpiochip_irq_domain_activate,
+ .alloc = pmic_gpio_domain_alloc,
+ .deactivate = gpiochip_irq_domain_deactivate,
+ .free = irq_domain_free_irqs_common,
+ .translate = pmic_gpio_domain_translate,
+};
+
static int pmic_gpio_probe(struct platform_device *pdev)
{
+ struct irq_domain *parent_domain;
+ struct device_node *parent_node;
struct device *dev = &pdev->dev;
struct pinctrl_pin_desc *pindesc;
struct pinctrl_desc *pctrldesc;
@@ -951,13 +1030,7 @@ static int pmic_gpio_probe(struct platform_device *pdev)
return ret;
}
- npins = platform_irq_count(pdev);
- if (!npins)
- return -EINVAL;
- if (npins < 0)
- return npins;
-
- BUG_ON(npins > ARRAY_SIZE(pmic_gpio_groups));
+ npins = (uintptr_t) device_get_match_data(&pdev->dev);
state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
if (!state)
@@ -999,10 +1072,6 @@ static int pmic_gpio_probe(struct platform_device *pdev)
pindesc->number = i;
pindesc->name = pmic_gpio_groups[i];
- pad->irq = platform_get_irq(pdev, i);
- if (pad->irq < 0)
- return pad->irq;
-
pad->base = reg + i * PMIC_GPIO_ADDRESS_RANGE;
ret = pmic_gpio_populate(state, pad);
@@ -1022,10 +1091,28 @@ static int pmic_gpio_probe(struct platform_device *pdev)
if (IS_ERR(state->ctrl))
return PTR_ERR(state->ctrl);
+ parent_node = of_irq_find_parent(state->dev->of_node);
+ if (!parent_node)
+ return -ENXIO;
+
+ parent_domain = irq_find_host(parent_node);
+ of_node_put(parent_node);
+ if (!parent_domain)
+ return -ENXIO;
+
+ state->fwnode = of_node_to_fwnode(state->dev->of_node);
+ state->domain = irq_domain_create_hierarchy(parent_domain, 0,
+ state->chip.ngpio,
+ state->fwnode,
+ &pmic_gpio_domain_ops,
+ &state->chip);
+ if (!state->domain)
+ return -ENODEV;
+
ret = gpiochip_add_data(&state->chip, state);
if (ret) {
dev_err(state->dev, "can't add gpio chip\n");
- return ret;
+ goto err_chip_add_data;
}
/*
@@ -1051,6 +1138,8 @@ static int pmic_gpio_probe(struct platform_device *pdev)
err_range:
gpiochip_remove(&state->chip);
+err_chip_add_data:
+ irq_domain_remove(state->domain);
return ret;
}
@@ -1059,17 +1148,21 @@ static int pmic_gpio_remove(struct platform_device *pdev)
struct pmic_gpio_state *state = platform_get_drvdata(pdev);
gpiochip_remove(&state->chip);
+ irq_domain_remove(state->domain);
return 0;
}
static const struct of_device_id pmic_gpio_of_match[] = {
- { .compatible = "qcom,pm8916-gpio" }, /* 4 GPIO's */
- { .compatible = "qcom,pm8941-gpio" }, /* 36 GPIO's */
- { .compatible = "qcom,pm8994-gpio" }, /* 22 GPIO's */
- { .compatible = "qcom,pmi8994-gpio" }, /* 10 GPIO's */
- { .compatible = "qcom,pma8084-gpio" }, /* 22 GPIO's */
- { .compatible = "qcom,pms405-gpio" }, /* 12 GPIO's, holes on 1 9 10 */
- { .compatible = "qcom,spmi-gpio" }, /* Generic */
+ { .compatible = "qcom,pm8005-gpio", .data = (void *) 4 },
+ { .compatible = "qcom,pm8916-gpio", .data = (void *) 4 },
+ { .compatible = "qcom,pm8941-gpio", .data = (void *) 36 },
+ { .compatible = "qcom,pm8994-gpio", .data = (void *) 22 },
+ { .compatible = "qcom,pmi8994-gpio", .data = (void *) 10 },
+ { .compatible = "qcom,pm8998-gpio", .data = (void *) 26 },
+ { .compatible = "qcom,pmi8998-gpio", .data = (void *) 14 },
+ { .compatible = "qcom,pma8084-gpio", .data = (void *) 22 },
+ /* pms405 has 12 GPIOs with holes on 1, 9, and 10 */
+ { .compatible = "qcom,pms405-gpio", .data = (void *) 12 },
{ },
};
diff --git a/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c b/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c
index ded7d765af2e..08dd62b5cebe 100644
--- a/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c
+++ b/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c
@@ -55,6 +55,8 @@
#define PM8XXX_MAX_GPIOS 44
+#define PM8XXX_GPIO_PHYSICAL_OFFSET 1
+
/* custom pinconf parameters */
#define PM8XXX_QCOM_DRIVE_STRENGH (PIN_CONFIG_END + 1)
#define PM8XXX_QCOM_PULL_UP_STRENGTH (PIN_CONFIG_END + 2)
@@ -99,6 +101,9 @@ struct pm8xxx_gpio {
struct pinctrl_desc desc;
unsigned npins;
+
+ struct fwnode_handle *fwnode;
+ struct irq_domain *domain;
};
static const struct pinconf_generic_params pm8xxx_gpio_bindings[] = {
@@ -499,11 +504,12 @@ static int pm8xxx_gpio_get(struct gpio_chip *chip, unsigned offset)
if (pin->mode == PM8XXX_GPIO_MODE_OUTPUT) {
ret = pin->output_value;
- } else {
+ } else if (pin->irq >= 0) {
ret = irq_get_irqchip_state(pin->irq, IRQCHIP_STATE_LINE_LEVEL, &state);
if (!ret)
ret = !!state;
- }
+ } else
+ ret = -EINVAL;
return ret;
}
@@ -533,7 +539,7 @@ static int pm8xxx_gpio_of_xlate(struct gpio_chip *chip,
if (flags)
*flags = gpio_desc->args[1];
- return gpio_desc->args[0] - 1;
+ return gpio_desc->args[0] - PM8XXX_GPIO_PHYSICAL_OFFSET;
}
@@ -541,8 +547,31 @@ static int pm8xxx_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
{
struct pm8xxx_gpio *pctrl = gpiochip_get_data(chip);
struct pm8xxx_pin_data *pin = pctrl->desc.pins[offset].drv_data;
+ struct irq_fwspec fwspec;
+ int ret;
+
+ fwspec.fwnode = pctrl->fwnode;
+ fwspec.param_count = 2;
+ fwspec.param[0] = offset + PM8XXX_GPIO_PHYSICAL_OFFSET;
+ fwspec.param[1] = IRQ_TYPE_EDGE_RISING;
+
+ ret = irq_create_fwspec_mapping(&fwspec);
+
+ /*
+ * Cache the IRQ since pm8xxx_gpio_get() needs this to get determine the
+ * line level.
+ */
+ pin->irq = ret;
+
+ return ret;
+}
+
+static void pm8xxx_gpio_free(struct gpio_chip *chip, unsigned int offset)
+{
+ struct pm8xxx_gpio *pctrl = gpiochip_get_data(chip);
+ struct pm8xxx_pin_data *pin = pctrl->desc.pins[offset].drv_data;
- return pin->irq;
+ pin->irq = -1;
}
#ifdef CONFIG_DEBUG_FS
@@ -571,7 +600,7 @@ static void pm8xxx_gpio_dbg_show_one(struct seq_file *s,
"no", "high", "medium", "low"
};
- seq_printf(s, " gpio%-2d:", offset + 1);
+ seq_printf(s, " gpio%-2d:", offset + PM8XXX_GPIO_PHYSICAL_OFFSET);
if (pin->disable) {
seq_puts(s, " ---");
} else {
@@ -603,6 +632,7 @@ static void pm8xxx_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
#endif
static const struct gpio_chip pm8xxx_gpio_template = {
+ .free = pm8xxx_gpio_free,
.direction_input = pm8xxx_gpio_direction_input,
.direction_output = pm8xxx_gpio_direction_output,
.get = pm8xxx_gpio_get,
@@ -664,13 +694,75 @@ static int pm8xxx_pin_populate(struct pm8xxx_gpio *pctrl,
return 0;
}
+static struct irq_chip pm8xxx_irq_chip = {
+ .name = "ssbi-gpio",
+ .irq_mask_ack = irq_chip_mask_ack_parent,
+ .irq_unmask = irq_chip_unmask_parent,
+ .irq_set_type = irq_chip_set_type_parent,
+ .flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE,
+};
+
+static int pm8xxx_domain_translate(struct irq_domain *domain,
+ struct irq_fwspec *fwspec,
+ unsigned long *hwirq,
+ unsigned int *type)
+{
+ struct pm8xxx_gpio *pctrl = container_of(domain->host_data,
+ struct pm8xxx_gpio, chip);
+
+ if (fwspec->param_count != 2 || fwspec->param[0] < 1 ||
+ fwspec->param[0] > pctrl->chip.ngpio)
+ return -EINVAL;
+
+ *hwirq = fwspec->param[0] - PM8XXX_GPIO_PHYSICAL_OFFSET;
+ *type = fwspec->param[1];
+
+ return 0;
+}
+
+static int pm8xxx_domain_alloc(struct irq_domain *domain, unsigned int virq,
+ unsigned int nr_irqs, void *data)
+{
+ struct pm8xxx_gpio *pctrl = container_of(domain->host_data,
+ struct pm8xxx_gpio, chip);
+ struct irq_fwspec *fwspec = data;
+ struct irq_fwspec parent_fwspec;
+ irq_hw_number_t hwirq;
+ unsigned int type;
+ int ret, i;
+
+ ret = pm8xxx_domain_translate(domain, fwspec, &hwirq, &type);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < nr_irqs; i++)
+ irq_domain_set_info(domain, virq + i, hwirq + i,
+ &pm8xxx_irq_chip, pctrl, handle_level_irq,
+ NULL, NULL);
+
+ parent_fwspec.fwnode = domain->parent->fwnode;
+ parent_fwspec.param_count = 2;
+ parent_fwspec.param[0] = hwirq + 0xc0;
+ parent_fwspec.param[1] = fwspec->param[1];
+
+ return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs,
+ &parent_fwspec);
+}
+
+static const struct irq_domain_ops pm8xxx_domain_ops = {
+ .activate = gpiochip_irq_domain_activate,
+ .alloc = pm8xxx_domain_alloc,
+ .deactivate = gpiochip_irq_domain_deactivate,
+ .free = irq_domain_free_irqs_common,
+ .translate = pm8xxx_domain_translate,
+};
+
static const struct of_device_id pm8xxx_gpio_of_match[] = {
- { .compatible = "qcom,pm8018-gpio" },
- { .compatible = "qcom,pm8038-gpio" },
- { .compatible = "qcom,pm8058-gpio" },
- { .compatible = "qcom,pm8917-gpio" },
- { .compatible = "qcom,pm8921-gpio" },
- { .compatible = "qcom,ssbi-gpio" },
+ { .compatible = "qcom,pm8018-gpio", .data = (void *) 6 },
+ { .compatible = "qcom,pm8038-gpio", .data = (void *) 12 },
+ { .compatible = "qcom,pm8058-gpio", .data = (void *) 44 },
+ { .compatible = "qcom,pm8917-gpio", .data = (void *) 38 },
+ { .compatible = "qcom,pm8921-gpio", .data = (void *) 44 },
{ },
};
MODULE_DEVICE_TABLE(of, pm8xxx_gpio_of_match);
@@ -678,22 +770,18 @@ MODULE_DEVICE_TABLE(of, pm8xxx_gpio_of_match);
static int pm8xxx_gpio_probe(struct platform_device *pdev)
{
struct pm8xxx_pin_data *pin_data;
+ struct irq_domain *parent_domain;
+ struct device_node *parent_node;
struct pinctrl_pin_desc *pins;
struct pm8xxx_gpio *pctrl;
- int ret;
- int i, npins;
+ int ret, i;
pctrl = devm_kzalloc(&pdev->dev, sizeof(*pctrl), GFP_KERNEL);
if (!pctrl)
return -ENOMEM;
pctrl->dev = &pdev->dev;
- npins = platform_irq_count(pdev);
- if (!npins)
- return -EINVAL;
- if (npins < 0)
- return npins;
- pctrl->npins = npins;
+ pctrl->npins = (uintptr_t) device_get_match_data(&pdev->dev);
pctrl->regmap = dev_get_regmap(pdev->dev.parent, NULL);
if (!pctrl->regmap) {
@@ -720,12 +808,7 @@ static int pm8xxx_gpio_probe(struct platform_device *pdev)
for (i = 0; i < pctrl->desc.npins; i++) {
pin_data[i].reg = SSBI_REG_ADDR_GPIO(i);
- pin_data[i].irq = platform_get_irq(pdev, i);
- if (pin_data[i].irq < 0) {
- dev_err(&pdev->dev,
- "missing interrupts for pin %d\n", i);
- return pin_data[i].irq;
- }
+ pin_data[i].irq = -1;
ret = pm8xxx_pin_populate(pctrl, &pin_data[i]);
if (ret)
@@ -756,10 +839,29 @@ static int pm8xxx_gpio_probe(struct platform_device *pdev)
pctrl->chip.of_gpio_n_cells = 2;
pctrl->chip.label = dev_name(pctrl->dev);
pctrl->chip.ngpio = pctrl->npins;
+
+ parent_node = of_irq_find_parent(pctrl->dev->of_node);
+ if (!parent_node)
+ return -ENXIO;
+
+ parent_domain = irq_find_host(parent_node);
+ of_node_put(parent_node);
+ if (!parent_domain)
+ return -ENXIO;
+
+ pctrl->fwnode = of_node_to_fwnode(pctrl->dev->of_node);
+ pctrl->domain = irq_domain_create_hierarchy(parent_domain, 0,
+ pctrl->chip.ngpio,
+ pctrl->fwnode,
+ &pm8xxx_domain_ops,
+ &pctrl->chip);
+ if (!pctrl->domain)
+ return -ENODEV;
+
ret = gpiochip_add_data(&pctrl->chip, pctrl);
if (ret) {
dev_err(&pdev->dev, "failed register gpiochip\n");
- return ret;
+ goto err_chip_add_data;
}
/*
@@ -789,6 +891,8 @@ static int pm8xxx_gpio_probe(struct platform_device *pdev)
unregister_gpiochip:
gpiochip_remove(&pctrl->chip);
+err_chip_add_data:
+ irq_domain_remove(pctrl->domain);
return ret;
}
@@ -798,6 +902,7 @@ static int pm8xxx_gpio_remove(struct platform_device *pdev)
struct pm8xxx_gpio *pctrl = platform_get_drvdata(pdev);
gpiochip_remove(&pctrl->chip);
+ irq_domain_remove(pctrl->domain);
return 0;
}
diff --git a/drivers/pinctrl/samsung/pinctrl-exynos.c b/drivers/pinctrl/samsung/pinctrl-exynos.c
index f49ea3d92aa1..ebc27b06718c 100644
--- a/drivers/pinctrl/samsung/pinctrl-exynos.c
+++ b/drivers/pinctrl/samsung/pinctrl-exynos.c
@@ -325,13 +325,6 @@ err_domains:
return ret;
}
-static u32 exynos_eint_wake_mask = 0xffffffff;
-
-u32 exynos_get_eint_wake_mask(void)
-{
- return exynos_eint_wake_mask;
-}
-
static int exynos_wkup_irq_set_wake(struct irq_data *irqd, unsigned int on)
{
struct irq_chip *chip = irq_data_get_irq_chip(irqd);
@@ -342,10 +335,9 @@ static int exynos_wkup_irq_set_wake(struct irq_data *irqd, unsigned int on)
pr_info("wake %s for irq %d\n", on ? "enabled" : "disabled", irqd->irq);
if (!on)
- exynos_eint_wake_mask |= bit;
+ our_chip->eint_wake_mask_value |= bit;
else
- exynos_eint_wake_mask &= ~bit;
- our_chip->eint_wake_mask_value = exynos_eint_wake_mask;
+ our_chip->eint_wake_mask_value &= ~bit;
return 0;
}
diff --git a/drivers/pinctrl/sh-pfc/pfc-emev2.c b/drivers/pinctrl/sh-pfc/pfc-emev2.c
index dc271c3243df..310c6f3ee7cc 100644
--- a/drivers/pinctrl/sh-pfc/pfc-emev2.c
+++ b/drivers/pinctrl/sh-pfc/pfc-emev2.c
@@ -1260,6 +1260,14 @@ static const char * const dtv_groups[] = {
"dtv_b",
};
+static const char * const err_rst_reqb_groups[] = {
+ "err_rst_reqb",
+};
+
+static const char * const ext_clki_groups[] = {
+ "ext_clki",
+};
+
static const char * const iic0_groups[] = {
"iic0",
};
@@ -1282,6 +1290,10 @@ static const char * const lcd_groups[] = {
"yuv3",
};
+static const char * const lowpwr_groups[] = {
+ "lowpwr",
+};
+
static const char * const ntsc_groups[] = {
"ntsc_clk",
"ntsc_data",
@@ -1295,6 +1307,10 @@ static const char * const pwm1_groups[] = {
"pwm1",
};
+static const char * const ref_clko_groups[] = {
+ "ref_clko",
+};
+
static const char * const sd_groups[] = {
"sd_cki",
};
@@ -1388,13 +1404,17 @@ static const struct sh_pfc_function pinmux_functions[] = {
SH_PFC_FUNCTION(cam),
SH_PFC_FUNCTION(cf),
SH_PFC_FUNCTION(dtv),
+ SH_PFC_FUNCTION(err_rst_reqb),
+ SH_PFC_FUNCTION(ext_clki),
SH_PFC_FUNCTION(iic0),
SH_PFC_FUNCTION(iic1),
SH_PFC_FUNCTION(jtag),
SH_PFC_FUNCTION(lcd),
+ SH_PFC_FUNCTION(lowpwr),
SH_PFC_FUNCTION(ntsc),
SH_PFC_FUNCTION(pwm0),
SH_PFC_FUNCTION(pwm1),
+ SH_PFC_FUNCTION(ref_clko),
SH_PFC_FUNCTION(sd),
SH_PFC_FUNCTION(sdi0),
SH_PFC_FUNCTION(sdi1),
diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7778.c b/drivers/pinctrl/sh-pfc/pfc-r8a7778.c
index 6bcdb4b5e69e..068b5e6334d1 100644
--- a/drivers/pinctrl/sh-pfc/pfc-r8a7778.c
+++ b/drivers/pinctrl/sh-pfc/pfc-r8a7778.c
@@ -1264,8 +1264,8 @@ static const struct sh_pfc_pin pinmux_pins[] = {
/* Pins not associated with a GPIO port */
SH_PFC_PIN_NAMED(3, 20, C20),
- SH_PFC_PIN_NAMED(20, 1, T1),
- SH_PFC_PIN_NAMED(25, 2, Y2),
+ SH_PFC_PIN_NAMED(1, 20, A20),
+ SH_PFC_PIN_NAMED(2, 25, B25),
};
/* - macro */
@@ -1400,7 +1400,7 @@ HSPI_PFC_DAT(hspi1_a, HSPI_CLK1_A, HSPI_CS1_A,
HSPI_RX1_A, HSPI_TX1_A);
HSPI_PFC_PIN(hspi1_b, RCAR_GP_PIN(0, 27), RCAR_GP_PIN(0, 26),
- PIN_NUMBER(20, 1), PIN_NUMBER(25, 2));
+ PIN_NUMBER(1, 20), PIN_NUMBER(2, 25));
HSPI_PFC_DAT(hspi1_b, HSPI_CLK1_B, HSPI_CS1_B,
HSPI_RX1_B, HSPI_TX1_B);
diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7790.c b/drivers/pinctrl/sh-pfc/pfc-r8a7790.c
index ab7a35392cd8..a84229cb8cd4 100644
--- a/drivers/pinctrl/sh-pfc/pfc-r8a7790.c
+++ b/drivers/pinctrl/sh-pfc/pfc-r8a7790.c
@@ -10,7 +10,9 @@
#include <linux/io.h>
#include <linux/kernel.h>
+#include <linux/sys_soc.h>
+#include "core.h"
#include "sh_pfc.h"
/*
@@ -5691,7 +5693,22 @@ static int r8a7790_pin_to_pocctrl(struct sh_pfc *pfc, unsigned int pin, u32 *poc
return 31 - (pin & 0x1f);
}
+static const struct soc_device_attribute r8a7790_tdsel[] = {
+ { .soc_id = "r8a7790", .revision = "ES1.0" },
+ { /* sentinel */ }
+};
+
+static int r8a7790_pinmux_soc_init(struct sh_pfc *pfc)
+{
+ /* Initialize TDSEL on old revisions */
+ if (soc_device_match(r8a7790_tdsel))
+ sh_pfc_write(pfc, 0xe6060088, 0x00155554);
+
+ return 0;
+}
+
static const struct sh_pfc_soc_operations r8a7790_pinmux_ops = {
+ .init = r8a7790_pinmux_soc_init,
.pin_to_pocctrl = r8a7790_pin_to_pocctrl,
};
diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7791.c b/drivers/pinctrl/sh-pfc/pfc-r8a7791.c
index 2859231aaffc..d8b13d4e9bbf 100644
--- a/drivers/pinctrl/sh-pfc/pfc-r8a7791.c
+++ b/drivers/pinctrl/sh-pfc/pfc-r8a7791.c
@@ -4317,7 +4317,7 @@ static const unsigned int vin1_clk_pins[] = {
static const unsigned int vin1_clk_mux[] = {
VI1_CLK_MARK,
};
-static const union vin_data vin1_b_data_pins = {
+static const union vin_data vin1_data_b_pins = {
.data24 = {
/* B */
RCAR_GP_PIN(3, 0), RCAR_GP_PIN(3, 1),
@@ -4336,7 +4336,7 @@ static const union vin_data vin1_b_data_pins = {
RCAR_GP_PIN(2, 19), RCAR_GP_PIN(2, 20),
},
};
-static const union vin_data vin1_b_data_mux = {
+static const union vin_data vin1_data_b_mux = {
.data24 = {
/* B */
VI1_DATA0_B_MARK, VI1_DATA1_B_MARK,
@@ -4355,7 +4355,7 @@ static const union vin_data vin1_b_data_mux = {
VI1_R6_B_MARK, VI1_R7_B_MARK,
},
};
-static const unsigned int vin1_b_data18_pins[] = {
+static const unsigned int vin1_data18_b_pins[] = {
/* B */
RCAR_GP_PIN(3, 8), RCAR_GP_PIN(3, 9),
RCAR_GP_PIN(3, 10), RCAR_GP_PIN(3, 11),
@@ -4369,7 +4369,7 @@ static const unsigned int vin1_b_data18_pins[] = {
RCAR_GP_PIN(2, 17), RCAR_GP_PIN(2, 18),
RCAR_GP_PIN(2, 19), RCAR_GP_PIN(2, 20),
};
-static const unsigned int vin1_b_data18_mux[] = {
+static const unsigned int vin1_data18_b_mux[] = {
/* B */
VI1_DATA2_B_MARK, VI1_DATA3_B_MARK,
VI1_DATA4_B_MARK, VI1_DATA5_B_MARK,
@@ -4383,30 +4383,30 @@ static const unsigned int vin1_b_data18_mux[] = {
VI1_R4_B_MARK, VI1_R5_B_MARK,
VI1_R6_B_MARK, VI1_R7_B_MARK,
};
-static const unsigned int vin1_b_sync_pins[] = {
+static const unsigned int vin1_sync_b_pins[] = {
RCAR_GP_PIN(3, 17), /* HSYNC */
RCAR_GP_PIN(3, 18), /* VSYNC */
};
-static const unsigned int vin1_b_sync_mux[] = {
+static const unsigned int vin1_sync_b_mux[] = {
VI1_HSYNC_N_B_MARK,
VI1_VSYNC_N_B_MARK,
};
-static const unsigned int vin1_b_field_pins[] = {
+static const unsigned int vin1_field_b_pins[] = {
RCAR_GP_PIN(3, 20),
};
-static const unsigned int vin1_b_field_mux[] = {
+static const unsigned int vin1_field_b_mux[] = {
VI1_FIELD_B_MARK,
};
-static const unsigned int vin1_b_clkenb_pins[] = {
+static const unsigned int vin1_clkenb_b_pins[] = {
RCAR_GP_PIN(3, 19),
};
-static const unsigned int vin1_b_clkenb_mux[] = {
+static const unsigned int vin1_clkenb_b_mux[] = {
VI1_CLKENB_B_MARK,
};
-static const unsigned int vin1_b_clk_pins[] = {
+static const unsigned int vin1_clk_b_pins[] = {
RCAR_GP_PIN(3, 16),
};
-static const unsigned int vin1_b_clk_mux[] = {
+static const unsigned int vin1_clk_b_mux[] = {
VI1_CLK_B_MARK,
};
/* - VIN2 ----------------------------------------------------------------- */
@@ -4784,17 +4784,17 @@ static const struct {
SH_PFC_PIN_GROUP(vin1_field),
SH_PFC_PIN_GROUP(vin1_clkenb),
SH_PFC_PIN_GROUP(vin1_clk),
- VIN_DATA_PIN_GROUP(vin1_b_data, 24),
- VIN_DATA_PIN_GROUP(vin1_b_data, 20),
- SH_PFC_PIN_GROUP(vin1_b_data18),
- VIN_DATA_PIN_GROUP(vin1_b_data, 16),
- VIN_DATA_PIN_GROUP(vin1_b_data, 12),
- VIN_DATA_PIN_GROUP(vin1_b_data, 10),
- VIN_DATA_PIN_GROUP(vin1_b_data, 8),
- SH_PFC_PIN_GROUP(vin1_b_sync),
- SH_PFC_PIN_GROUP(vin1_b_field),
- SH_PFC_PIN_GROUP(vin1_b_clkenb),
- SH_PFC_PIN_GROUP(vin1_b_clk),
+ VIN_DATA_PIN_GROUP(vin1_data, 24, _b),
+ VIN_DATA_PIN_GROUP(vin1_data, 20, _b),
+ SH_PFC_PIN_GROUP(vin1_data18_b),
+ VIN_DATA_PIN_GROUP(vin1_data, 16, _b),
+ VIN_DATA_PIN_GROUP(vin1_data, 12, _b),
+ VIN_DATA_PIN_GROUP(vin1_data, 10, _b),
+ VIN_DATA_PIN_GROUP(vin1_data, 8, _b),
+ SH_PFC_PIN_GROUP(vin1_sync_b),
+ SH_PFC_PIN_GROUP(vin1_field_b),
+ SH_PFC_PIN_GROUP(vin1_clkenb_b),
+ SH_PFC_PIN_GROUP(vin1_clk_b),
SH_PFC_PIN_GROUP(vin2_data8),
SH_PFC_PIN_GROUP(vin2_sync),
SH_PFC_PIN_GROUP(vin2_field),
@@ -5236,7 +5236,7 @@ static const char * const scifb2_groups[] = {
"scifb2_data_b",
"scifb2_clk_b",
"scifb2_ctrl_b",
- "scifb0_data_c",
+ "scifb2_data_c",
"scifb2_clk_c",
"scifb2_data_d",
};
@@ -5335,17 +5335,17 @@ static const char * const vin1_groups[] = {
"vin1_field",
"vin1_clkenb",
"vin1_clk",
- "vin1_b_data24",
- "vin1_b_data20",
- "vin1_b_data18",
- "vin1_b_data16",
- "vin1_b_data12",
- "vin1_b_data10",
- "vin1_b_data8",
- "vin1_b_sync",
- "vin1_b_field",
- "vin1_b_clkenb",
- "vin1_b_clk",
+ "vin1_data24_b",
+ "vin1_data20_b",
+ "vin1_data18_b",
+ "vin1_data16_b",
+ "vin1_data12_b",
+ "vin1_data10_b",
+ "vin1_data8_b",
+ "vin1_sync_b",
+ "vin1_field_b",
+ "vin1_clkenb_b",
+ "vin1_clk_b",
};
static const char * const vin2_groups[] = {
diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7792.c b/drivers/pinctrl/sh-pfc/pfc-r8a7792.c
index a623459b234e..d36da5652de6 100644
--- a/drivers/pinctrl/sh-pfc/pfc-r8a7792.c
+++ b/drivers/pinctrl/sh-pfc/pfc-r8a7792.c
@@ -1913,6 +1913,7 @@ static const char * const vin1_groups[] = {
"vin1_data8",
"vin1_data24_b",
"vin1_data20_b",
+ "vin1_data18_b",
"vin1_data16_b",
"vin1_sync",
"vin1_field",
diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7794.c b/drivers/pinctrl/sh-pfc/pfc-r8a7794.c
index fcf1339c4058..958a5f714c93 100644
--- a/drivers/pinctrl/sh-pfc/pfc-r8a7794.c
+++ b/drivers/pinctrl/sh-pfc/pfc-r8a7794.c
@@ -8,6 +8,7 @@
*/
#include <linux/kernel.h>
+#include <linux/sys_soc.h>
#include "core.h"
#include "sh_pfc.h"
@@ -5560,7 +5561,22 @@ static int r8a7794_pin_to_pocctrl(struct sh_pfc *pfc, unsigned int pin, u32 *poc
return -EINVAL;
}
+static const struct soc_device_attribute r8a7794_tdsel[] = {
+ { .soc_id = "r8a7794", .revision = "ES1.0" },
+ { /* sentinel */ }
+};
+
+static int r8a7794_pinmux_soc_init(struct sh_pfc *pfc)
+{
+ /* Initialize TDSEL on old revisions */
+ if (soc_device_match(r8a7794_tdsel))
+ sh_pfc_write(pfc, 0xe6060068, 0x55555500);
+
+ return 0;
+}
+
static const struct sh_pfc_soc_operations r8a7794_pinmux_ops = {
+ .init = r8a7794_pinmux_soc_init,
.pin_to_pocctrl = r8a7794_pin_to_pocctrl,
};
diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7795.c b/drivers/pinctrl/sh-pfc/pfc-r8a7795.c
index 01105bb83598..db9add1405c5 100644
--- a/drivers/pinctrl/sh-pfc/pfc-r8a7795.c
+++ b/drivers/pinctrl/sh-pfc/pfc-r8a7795.c
@@ -4098,67 +4098,29 @@ static const unsigned int vin4_clk_mux[] = {
};
/* - VIN5 ------------------------------------------------------------------- */
-static const unsigned int vin5_data8_pins[] = {
- RCAR_GP_PIN(0, 0), RCAR_GP_PIN(0, 1),
- RCAR_GP_PIN(0, 2), RCAR_GP_PIN(0, 3),
- RCAR_GP_PIN(0, 4), RCAR_GP_PIN(0, 5),
- RCAR_GP_PIN(0, 6), RCAR_GP_PIN(0, 7),
-};
-static const unsigned int vin5_data8_mux[] = {
- VI5_DATA0_MARK, VI5_DATA1_MARK,
- VI5_DATA2_MARK, VI5_DATA3_MARK,
- VI5_DATA4_MARK, VI5_DATA5_MARK,
- VI5_DATA6_MARK, VI5_DATA7_MARK,
-};
-static const unsigned int vin5_data10_pins[] = {
- RCAR_GP_PIN(0, 0), RCAR_GP_PIN(0, 1),
- RCAR_GP_PIN(0, 2), RCAR_GP_PIN(0, 3),
- RCAR_GP_PIN(0, 4), RCAR_GP_PIN(0, 5),
- RCAR_GP_PIN(0, 6), RCAR_GP_PIN(0, 7),
- RCAR_GP_PIN(1, 12), RCAR_GP_PIN(1, 13),
-};
-static const unsigned int vin5_data10_mux[] = {
- VI5_DATA0_MARK, VI5_DATA1_MARK,
- VI5_DATA2_MARK, VI5_DATA3_MARK,
- VI5_DATA4_MARK, VI5_DATA5_MARK,
- VI5_DATA6_MARK, VI5_DATA7_MARK,
- VI5_DATA8_MARK, VI5_DATA9_MARK,
-};
-static const unsigned int vin5_data12_pins[] = {
- RCAR_GP_PIN(0, 0), RCAR_GP_PIN(0, 1),
- RCAR_GP_PIN(0, 2), RCAR_GP_PIN(0, 3),
- RCAR_GP_PIN(0, 4), RCAR_GP_PIN(0, 5),
- RCAR_GP_PIN(0, 6), RCAR_GP_PIN(0, 7),
- RCAR_GP_PIN(1, 12), RCAR_GP_PIN(1, 13),
- RCAR_GP_PIN(1, 14), RCAR_GP_PIN(1, 15),
-};
-static const unsigned int vin5_data12_mux[] = {
- VI5_DATA0_MARK, VI5_DATA1_MARK,
- VI5_DATA2_MARK, VI5_DATA3_MARK,
- VI5_DATA4_MARK, VI5_DATA5_MARK,
- VI5_DATA6_MARK, VI5_DATA7_MARK,
- VI5_DATA8_MARK, VI5_DATA9_MARK,
- VI5_DATA10_MARK, VI5_DATA11_MARK,
-};
-static const unsigned int vin5_data16_pins[] = {
- RCAR_GP_PIN(0, 0), RCAR_GP_PIN(0, 1),
- RCAR_GP_PIN(0, 2), RCAR_GP_PIN(0, 3),
- RCAR_GP_PIN(0, 4), RCAR_GP_PIN(0, 5),
- RCAR_GP_PIN(0, 6), RCAR_GP_PIN(0, 7),
- RCAR_GP_PIN(1, 12), RCAR_GP_PIN(1, 13),
- RCAR_GP_PIN(1, 14), RCAR_GP_PIN(1, 15),
- RCAR_GP_PIN(1, 4), RCAR_GP_PIN(1, 5),
- RCAR_GP_PIN(1, 6), RCAR_GP_PIN(1, 7),
+static const union vin_data16 vin5_data_pins = {
+ .data16 = {
+ RCAR_GP_PIN(0, 0), RCAR_GP_PIN(0, 1),
+ RCAR_GP_PIN(0, 2), RCAR_GP_PIN(0, 3),
+ RCAR_GP_PIN(0, 4), RCAR_GP_PIN(0, 5),
+ RCAR_GP_PIN(0, 6), RCAR_GP_PIN(0, 7),
+ RCAR_GP_PIN(1, 12), RCAR_GP_PIN(1, 13),
+ RCAR_GP_PIN(1, 14), RCAR_GP_PIN(1, 15),
+ RCAR_GP_PIN(1, 4), RCAR_GP_PIN(1, 5),
+ RCAR_GP_PIN(1, 6), RCAR_GP_PIN(1, 7),
+ },
};
-static const unsigned int vin5_data16_mux[] = {
- VI5_DATA0_MARK, VI5_DATA1_MARK,
- VI5_DATA2_MARK, VI5_DATA3_MARK,
- VI5_DATA4_MARK, VI5_DATA5_MARK,
- VI5_DATA6_MARK, VI5_DATA7_MARK,
- VI5_DATA8_MARK, VI5_DATA9_MARK,
- VI5_DATA10_MARK, VI5_DATA11_MARK,
- VI5_DATA12_MARK, VI5_DATA13_MARK,
- VI5_DATA14_MARK, VI5_DATA15_MARK,
+static const union vin_data16 vin5_data_mux = {
+ .data16 = {
+ VI5_DATA0_MARK, VI5_DATA1_MARK,
+ VI5_DATA2_MARK, VI5_DATA3_MARK,
+ VI5_DATA4_MARK, VI5_DATA5_MARK,
+ VI5_DATA6_MARK, VI5_DATA7_MARK,
+ VI5_DATA8_MARK, VI5_DATA9_MARK,
+ VI5_DATA10_MARK, VI5_DATA11_MARK,
+ VI5_DATA12_MARK, VI5_DATA13_MARK,
+ VI5_DATA14_MARK, VI5_DATA15_MARK,
+ },
};
static const unsigned int vin5_sync_pins[] = {
/* HSYNC#, VSYNC# */
@@ -4530,10 +4492,10 @@ static const struct sh_pfc_pin_group pinmux_groups[] = {
SH_PFC_PIN_GROUP(vin4_field),
SH_PFC_PIN_GROUP(vin4_clkenb),
SH_PFC_PIN_GROUP(vin4_clk),
- SH_PFC_PIN_GROUP(vin5_data8),
- SH_PFC_PIN_GROUP(vin5_data10),
- SH_PFC_PIN_GROUP(vin5_data12),
- SH_PFC_PIN_GROUP(vin5_data16),
+ VIN_DATA_PIN_GROUP(vin5_data, 8),
+ VIN_DATA_PIN_GROUP(vin5_data, 10),
+ VIN_DATA_PIN_GROUP(vin5_data, 12),
+ VIN_DATA_PIN_GROUP(vin5_data, 16),
SH_PFC_PIN_GROUP(vin5_sync),
SH_PFC_PIN_GROUP(vin5_field),
SH_PFC_PIN_GROUP(vin5_clkenb),
diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7796.c b/drivers/pinctrl/sh-pfc/pfc-r8a7796.c
index 4b98ad8d93d9..72348a4f2ece 100644
--- a/drivers/pinctrl/sh-pfc/pfc-r8a7796.c
+++ b/drivers/pinctrl/sh-pfc/pfc-r8a7796.c
@@ -4070,67 +4070,29 @@ static const unsigned int vin4_clk_mux[] = {
};
/* - VIN5 ------------------------------------------------------------------- */
-static const unsigned int vin5_data8_pins[] = {
- RCAR_GP_PIN(0, 0), RCAR_GP_PIN(0, 1),
- RCAR_GP_PIN(0, 2), RCAR_GP_PIN(0, 3),
- RCAR_GP_PIN(0, 4), RCAR_GP_PIN(0, 5),
- RCAR_GP_PIN(0, 6), RCAR_GP_PIN(0, 7),
-};
-static const unsigned int vin5_data8_mux[] = {
- VI5_DATA0_MARK, VI5_DATA1_MARK,
- VI5_DATA2_MARK, VI5_DATA3_MARK,
- VI5_DATA4_MARK, VI5_DATA5_MARK,
- VI5_DATA6_MARK, VI5_DATA7_MARK,
-};
-static const unsigned int vin5_data10_pins[] = {
- RCAR_GP_PIN(0, 0), RCAR_GP_PIN(0, 1),
- RCAR_GP_PIN(0, 2), RCAR_GP_PIN(0, 3),
- RCAR_GP_PIN(0, 4), RCAR_GP_PIN(0, 5),
- RCAR_GP_PIN(0, 6), RCAR_GP_PIN(0, 7),
- RCAR_GP_PIN(1, 12), RCAR_GP_PIN(1, 13),
-};
-static const unsigned int vin5_data10_mux[] = {
- VI5_DATA0_MARK, VI5_DATA1_MARK,
- VI5_DATA2_MARK, VI5_DATA3_MARK,
- VI5_DATA4_MARK, VI5_DATA5_MARK,
- VI5_DATA6_MARK, VI5_DATA7_MARK,
- VI5_DATA8_MARK, VI5_DATA9_MARK,
-};
-static const unsigned int vin5_data12_pins[] = {
- RCAR_GP_PIN(0, 0), RCAR_GP_PIN(0, 1),
- RCAR_GP_PIN(0, 2), RCAR_GP_PIN(0, 3),
- RCAR_GP_PIN(0, 4), RCAR_GP_PIN(0, 5),
- RCAR_GP_PIN(0, 6), RCAR_GP_PIN(0, 7),
- RCAR_GP_PIN(1, 12), RCAR_GP_PIN(1, 13),
- RCAR_GP_PIN(1, 14), RCAR_GP_PIN(1, 15),
-};
-static const unsigned int vin5_data12_mux[] = {
- VI5_DATA0_MARK, VI5_DATA1_MARK,
- VI5_DATA2_MARK, VI5_DATA3_MARK,
- VI5_DATA4_MARK, VI5_DATA5_MARK,
- VI5_DATA6_MARK, VI5_DATA7_MARK,
- VI5_DATA8_MARK, VI5_DATA9_MARK,
- VI5_DATA10_MARK, VI5_DATA11_MARK,
-};
-static const unsigned int vin5_data16_pins[] = {
- RCAR_GP_PIN(0, 0), RCAR_GP_PIN(0, 1),
- RCAR_GP_PIN(0, 2), RCAR_GP_PIN(0, 3),
- RCAR_GP_PIN(0, 4), RCAR_GP_PIN(0, 5),
- RCAR_GP_PIN(0, 6), RCAR_GP_PIN(0, 7),
- RCAR_GP_PIN(1, 12), RCAR_GP_PIN(1, 13),
- RCAR_GP_PIN(1, 14), RCAR_GP_PIN(1, 15),
- RCAR_GP_PIN(1, 4), RCAR_GP_PIN(1, 5),
- RCAR_GP_PIN(1, 6), RCAR_GP_PIN(1, 7),
+static const union vin_data16 vin5_data_pins = {
+ .data16 = {
+ RCAR_GP_PIN(0, 0), RCAR_GP_PIN(0, 1),
+ RCAR_GP_PIN(0, 2), RCAR_GP_PIN(0, 3),
+ RCAR_GP_PIN(0, 4), RCAR_GP_PIN(0, 5),
+ RCAR_GP_PIN(0, 6), RCAR_GP_PIN(0, 7),
+ RCAR_GP_PIN(1, 12), RCAR_GP_PIN(1, 13),
+ RCAR_GP_PIN(1, 14), RCAR_GP_PIN(1, 15),
+ RCAR_GP_PIN(1, 4), RCAR_GP_PIN(1, 5),
+ RCAR_GP_PIN(1, 6), RCAR_GP_PIN(1, 7),
+ },
};
-static const unsigned int vin5_data16_mux[] = {
- VI5_DATA0_MARK, VI5_DATA1_MARK,
- VI5_DATA2_MARK, VI5_DATA3_MARK,
- VI5_DATA4_MARK, VI5_DATA5_MARK,
- VI5_DATA6_MARK, VI5_DATA7_MARK,
- VI5_DATA8_MARK, VI5_DATA9_MARK,
- VI5_DATA10_MARK, VI5_DATA11_MARK,
- VI5_DATA12_MARK, VI5_DATA13_MARK,
- VI5_DATA14_MARK, VI5_DATA15_MARK,
+static const union vin_data16 vin5_data_mux = {
+ .data16 = {
+ VI5_DATA0_MARK, VI5_DATA1_MARK,
+ VI5_DATA2_MARK, VI5_DATA3_MARK,
+ VI5_DATA4_MARK, VI5_DATA5_MARK,
+ VI5_DATA6_MARK, VI5_DATA7_MARK,
+ VI5_DATA8_MARK, VI5_DATA9_MARK,
+ VI5_DATA10_MARK, VI5_DATA11_MARK,
+ VI5_DATA12_MARK, VI5_DATA13_MARK,
+ VI5_DATA14_MARK, VI5_DATA15_MARK,
+ },
};
static const unsigned int vin5_sync_pins[] = {
/* HSYNC#, VSYNC# */
@@ -4468,10 +4430,10 @@ static const struct {
SH_PFC_PIN_GROUP(vin4_field),
SH_PFC_PIN_GROUP(vin4_clkenb),
SH_PFC_PIN_GROUP(vin4_clk),
- SH_PFC_PIN_GROUP(vin5_data8),
- SH_PFC_PIN_GROUP(vin5_data10),
- SH_PFC_PIN_GROUP(vin5_data12),
- SH_PFC_PIN_GROUP(vin5_data16),
+ VIN_DATA_PIN_GROUP(vin5_data, 8),
+ VIN_DATA_PIN_GROUP(vin5_data, 10),
+ VIN_DATA_PIN_GROUP(vin5_data, 12),
+ VIN_DATA_PIN_GROUP(vin5_data, 16),
SH_PFC_PIN_GROUP(vin5_sync),
SH_PFC_PIN_GROUP(vin5_field),
SH_PFC_PIN_GROUP(vin5_clkenb),
diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a77965.c b/drivers/pinctrl/sh-pfc/pfc-r8a77965.c
index a7c4882de09e..14c4b671cddf 100644
--- a/drivers/pinctrl/sh-pfc/pfc-r8a77965.c
+++ b/drivers/pinctrl/sh-pfc/pfc-r8a77965.c
@@ -554,7 +554,7 @@ MOD_SEL0_4_3 MOD_SEL1_4 \
FM(AVB_RX_CTL) FM(AVB_RXC) FM(AVB_RD0) FM(AVB_RD1) FM(AVB_RD2) FM(AVB_RD3) \
FM(AVB_TXCREFCLK) FM(AVB_MDIO) \
FM(PRESETOUT) \
- FM(DU_DOTCLKIN0) FM(DU_DOTCLKIN1) FM(DU_DOTCLKIN2) \
+ FM(DU_DOTCLKIN0) FM(DU_DOTCLKIN1) FM(DU_DOTCLKIN3) \
FM(TMS) FM(TDO) FM(ASEBRK) FM(MLB_REF) FM(TDI) FM(TCK) FM(TRST) FM(EXTALR)
enum {
@@ -1566,7 +1566,7 @@ static const struct sh_pfc_pin pinmux_pins[] = {
SH_PFC_PIN_NAMED_CFG(ROW_GROUP_A('E'), 5, QSPI1_MISO_IO1, CFG_FLAGS),
SH_PFC_PIN_NAMED_CFG(ROW_GROUP_A('P'), 7, DU_DOTCLKIN0, CFG_FLAGS),
SH_PFC_PIN_NAMED_CFG(ROW_GROUP_A('P'), 8, DU_DOTCLKIN1, CFG_FLAGS),
- SH_PFC_PIN_NAMED_CFG(ROW_GROUP_A('R'), 8, DU_DOTCLKIN2, CFG_FLAGS),
+ SH_PFC_PIN_NAMED_CFG(ROW_GROUP_A('R'), 8, DU_DOTCLKIN3, CFG_FLAGS),
SH_PFC_PIN_NAMED_CFG(ROW_GROUP_A('R'), 26, TRST#, SH_PFC_PIN_CFG_PULL_UP | SH_PFC_PIN_CFG_PULL_DOWN),
SH_PFC_PIN_NAMED_CFG(ROW_GROUP_A('R'), 29, TDI, SH_PFC_PIN_CFG_PULL_UP | SH_PFC_PIN_CFG_PULL_DOWN),
SH_PFC_PIN_NAMED_CFG(ROW_GROUP_A('R'), 30, TMS, CFG_FLAGS),
@@ -1850,6 +1850,280 @@ static const unsigned int canfd1_data_mux[] = {
CANFD1_TX_MARK, CANFD1_RX_MARK,
};
+/* - DRIF0 --------------------------------------------------------------- */
+static const unsigned int drif0_ctrl_a_pins[] = {
+ /* CLK, SYNC */
+ RCAR_GP_PIN(6, 8), RCAR_GP_PIN(6, 9),
+};
+
+static const unsigned int drif0_ctrl_a_mux[] = {
+ RIF0_CLK_A_MARK, RIF0_SYNC_A_MARK,
+};
+
+static const unsigned int drif0_data0_a_pins[] = {
+ /* D0 */
+ RCAR_GP_PIN(6, 10),
+};
+
+static const unsigned int drif0_data0_a_mux[] = {
+ RIF0_D0_A_MARK,
+};
+
+static const unsigned int drif0_data1_a_pins[] = {
+ /* D1 */
+ RCAR_GP_PIN(6, 7),
+};
+
+static const unsigned int drif0_data1_a_mux[] = {
+ RIF0_D1_A_MARK,
+};
+
+static const unsigned int drif0_ctrl_b_pins[] = {
+ /* CLK, SYNC */
+ RCAR_GP_PIN(5, 0), RCAR_GP_PIN(5, 4),
+};
+
+static const unsigned int drif0_ctrl_b_mux[] = {
+ RIF0_CLK_B_MARK, RIF0_SYNC_B_MARK,
+};
+
+static const unsigned int drif0_data0_b_pins[] = {
+ /* D0 */
+ RCAR_GP_PIN(5, 1),
+};
+
+static const unsigned int drif0_data0_b_mux[] = {
+ RIF0_D0_B_MARK,
+};
+
+static const unsigned int drif0_data1_b_pins[] = {
+ /* D1 */
+ RCAR_GP_PIN(5, 2),
+};
+
+static const unsigned int drif0_data1_b_mux[] = {
+ RIF0_D1_B_MARK,
+};
+
+static const unsigned int drif0_ctrl_c_pins[] = {
+ /* CLK, SYNC */
+ RCAR_GP_PIN(5, 12), RCAR_GP_PIN(5, 15),
+};
+
+static const unsigned int drif0_ctrl_c_mux[] = {
+ RIF0_CLK_C_MARK, RIF0_SYNC_C_MARK,
+};
+
+static const unsigned int drif0_data0_c_pins[] = {
+ /* D0 */
+ RCAR_GP_PIN(5, 13),
+};
+
+static const unsigned int drif0_data0_c_mux[] = {
+ RIF0_D0_C_MARK,
+};
+
+static const unsigned int drif0_data1_c_pins[] = {
+ /* D1 */
+ RCAR_GP_PIN(5, 14),
+};
+
+static const unsigned int drif0_data1_c_mux[] = {
+ RIF0_D1_C_MARK,
+};
+
+/* - DRIF1 --------------------------------------------------------------- */
+static const unsigned int drif1_ctrl_a_pins[] = {
+ /* CLK, SYNC */
+ RCAR_GP_PIN(6, 17), RCAR_GP_PIN(6, 18),
+};
+
+static const unsigned int drif1_ctrl_a_mux[] = {
+ RIF1_CLK_A_MARK, RIF1_SYNC_A_MARK,
+};
+
+static const unsigned int drif1_data0_a_pins[] = {
+ /* D0 */
+ RCAR_GP_PIN(6, 19),
+};
+
+static const unsigned int drif1_data0_a_mux[] = {
+ RIF1_D0_A_MARK,
+};
+
+static const unsigned int drif1_data1_a_pins[] = {
+ /* D1 */
+ RCAR_GP_PIN(6, 20),
+};
+
+static const unsigned int drif1_data1_a_mux[] = {
+ RIF1_D1_A_MARK,
+};
+
+static const unsigned int drif1_ctrl_b_pins[] = {
+ /* CLK, SYNC */
+ RCAR_GP_PIN(5, 9), RCAR_GP_PIN(5, 3),
+};
+
+static const unsigned int drif1_ctrl_b_mux[] = {
+ RIF1_CLK_B_MARK, RIF1_SYNC_B_MARK,
+};
+
+static const unsigned int drif1_data0_b_pins[] = {
+ /* D0 */
+ RCAR_GP_PIN(5, 7),
+};
+
+static const unsigned int drif1_data0_b_mux[] = {
+ RIF1_D0_B_MARK,
+};
+
+static const unsigned int drif1_data1_b_pins[] = {
+ /* D1 */
+ RCAR_GP_PIN(5, 8),
+};
+
+static const unsigned int drif1_data1_b_mux[] = {
+ RIF1_D1_B_MARK,
+};
+
+static const unsigned int drif1_ctrl_c_pins[] = {
+ /* CLK, SYNC */
+ RCAR_GP_PIN(5, 5), RCAR_GP_PIN(5, 11),
+};
+
+static const unsigned int drif1_ctrl_c_mux[] = {
+ RIF1_CLK_C_MARK, RIF1_SYNC_C_MARK,
+};
+
+static const unsigned int drif1_data0_c_pins[] = {
+ /* D0 */
+ RCAR_GP_PIN(5, 6),
+};
+
+static const unsigned int drif1_data0_c_mux[] = {
+ RIF1_D0_C_MARK,
+};
+
+static const unsigned int drif1_data1_c_pins[] = {
+ /* D1 */
+ RCAR_GP_PIN(5, 10),
+};
+
+static const unsigned int drif1_data1_c_mux[] = {
+ RIF1_D1_C_MARK,
+};
+
+/* - DRIF2 --------------------------------------------------------------- */
+static const unsigned int drif2_ctrl_a_pins[] = {
+ /* CLK, SYNC */
+ RCAR_GP_PIN(6, 8), RCAR_GP_PIN(6, 9),
+};
+
+static const unsigned int drif2_ctrl_a_mux[] = {
+ RIF2_CLK_A_MARK, RIF2_SYNC_A_MARK,
+};
+
+static const unsigned int drif2_data0_a_pins[] = {
+ /* D0 */
+ RCAR_GP_PIN(6, 7),
+};
+
+static const unsigned int drif2_data0_a_mux[] = {
+ RIF2_D0_A_MARK,
+};
+
+static const unsigned int drif2_data1_a_pins[] = {
+ /* D1 */
+ RCAR_GP_PIN(6, 10),
+};
+
+static const unsigned int drif2_data1_a_mux[] = {
+ RIF2_D1_A_MARK,
+};
+
+static const unsigned int drif2_ctrl_b_pins[] = {
+ /* CLK, SYNC */
+ RCAR_GP_PIN(6, 26), RCAR_GP_PIN(6, 27),
+};
+
+static const unsigned int drif2_ctrl_b_mux[] = {
+ RIF2_CLK_B_MARK, RIF2_SYNC_B_MARK,
+};
+
+static const unsigned int drif2_data0_b_pins[] = {
+ /* D0 */
+ RCAR_GP_PIN(6, 30),
+};
+
+static const unsigned int drif2_data0_b_mux[] = {
+ RIF2_D0_B_MARK,
+};
+
+static const unsigned int drif2_data1_b_pins[] = {
+ /* D1 */
+ RCAR_GP_PIN(6, 31),
+};
+
+static const unsigned int drif2_data1_b_mux[] = {
+ RIF2_D1_B_MARK,
+};
+
+/* - DRIF3 --------------------------------------------------------------- */
+static const unsigned int drif3_ctrl_a_pins[] = {
+ /* CLK, SYNC */
+ RCAR_GP_PIN(6, 17), RCAR_GP_PIN(6, 18),
+};
+
+static const unsigned int drif3_ctrl_a_mux[] = {
+ RIF3_CLK_A_MARK, RIF3_SYNC_A_MARK,
+};
+
+static const unsigned int drif3_data0_a_pins[] = {
+ /* D0 */
+ RCAR_GP_PIN(6, 19),
+};
+
+static const unsigned int drif3_data0_a_mux[] = {
+ RIF3_D0_A_MARK,
+};
+
+static const unsigned int drif3_data1_a_pins[] = {
+ /* D1 */
+ RCAR_GP_PIN(6, 20),
+};
+
+static const unsigned int drif3_data1_a_mux[] = {
+ RIF3_D1_A_MARK,
+};
+
+static const unsigned int drif3_ctrl_b_pins[] = {
+ /* CLK, SYNC */
+ RCAR_GP_PIN(6, 24), RCAR_GP_PIN(6, 25),
+};
+
+static const unsigned int drif3_ctrl_b_mux[] = {
+ RIF3_CLK_B_MARK, RIF3_SYNC_B_MARK,
+};
+
+static const unsigned int drif3_data0_b_pins[] = {
+ /* D0 */
+ RCAR_GP_PIN(6, 28),
+};
+
+static const unsigned int drif3_data0_b_mux[] = {
+ RIF3_D0_B_MARK,
+};
+
+static const unsigned int drif3_data1_b_pins[] = {
+ /* D1 */
+ RCAR_GP_PIN(6, 29),
+};
+
+static const unsigned int drif3_data1_b_mux[] = {
+ RIF3_D1_B_MARK,
+};
+
/* - DU --------------------------------------------------------------------- */
static const unsigned int du_rgb666_pins[] = {
/* R[7:2], G[7:2], B[7:2] */
@@ -3760,6 +4034,42 @@ static const unsigned int ssi9_ctrl_b_mux[] = {
SSI_SCK9_B_MARK, SSI_WS9_B_MARK,
};
+/* - TMU -------------------------------------------------------------------- */
+static const unsigned int tmu_tclk1_a_pins[] = {
+ /* TCLK */
+ RCAR_GP_PIN(6, 23),
+};
+
+static const unsigned int tmu_tclk1_a_mux[] = {
+ TCLK1_A_MARK,
+};
+
+static const unsigned int tmu_tclk1_b_pins[] = {
+ /* TCLK */
+ RCAR_GP_PIN(5, 19),
+};
+
+static const unsigned int tmu_tclk1_b_mux[] = {
+ TCLK1_B_MARK,
+};
+
+static const unsigned int tmu_tclk2_a_pins[] = {
+ /* TCLK */
+ RCAR_GP_PIN(6, 19),
+};
+
+static const unsigned int tmu_tclk2_a_mux[] = {
+ TCLK2_A_MARK,
+};
+
+static const unsigned int tmu_tclk2_b_pins[] = {
+ /* TCLK */
+ RCAR_GP_PIN(6, 28),
+};
+
+static const unsigned int tmu_tclk2_b_mux[] = {
+ TCLK2_B_MARK,
+};
/* - USB0 ------------------------------------------------------------------- */
static const unsigned int usb0_pins[] = {
@@ -4037,6 +4347,36 @@ static const struct sh_pfc_pin_group pinmux_groups[] = {
SH_PFC_PIN_GROUP(canfd0_data_a),
SH_PFC_PIN_GROUP(canfd0_data_b),
SH_PFC_PIN_GROUP(canfd1_data),
+ SH_PFC_PIN_GROUP(drif0_ctrl_a),
+ SH_PFC_PIN_GROUP(drif0_data0_a),
+ SH_PFC_PIN_GROUP(drif0_data1_a),
+ SH_PFC_PIN_GROUP(drif0_ctrl_b),
+ SH_PFC_PIN_GROUP(drif0_data0_b),
+ SH_PFC_PIN_GROUP(drif0_data1_b),
+ SH_PFC_PIN_GROUP(drif0_ctrl_c),
+ SH_PFC_PIN_GROUP(drif0_data0_c),
+ SH_PFC_PIN_GROUP(drif0_data1_c),
+ SH_PFC_PIN_GROUP(drif1_ctrl_a),
+ SH_PFC_PIN_GROUP(drif1_data0_a),
+ SH_PFC_PIN_GROUP(drif1_data1_a),
+ SH_PFC_PIN_GROUP(drif1_ctrl_b),
+ SH_PFC_PIN_GROUP(drif1_data0_b),
+ SH_PFC_PIN_GROUP(drif1_data1_b),
+ SH_PFC_PIN_GROUP(drif1_ctrl_c),
+ SH_PFC_PIN_GROUP(drif1_data0_c),
+ SH_PFC_PIN_GROUP(drif1_data1_c),
+ SH_PFC_PIN_GROUP(drif2_ctrl_a),
+ SH_PFC_PIN_GROUP(drif2_data0_a),
+ SH_PFC_PIN_GROUP(drif2_data1_a),
+ SH_PFC_PIN_GROUP(drif2_ctrl_b),
+ SH_PFC_PIN_GROUP(drif2_data0_b),
+ SH_PFC_PIN_GROUP(drif2_data1_b),
+ SH_PFC_PIN_GROUP(drif3_ctrl_a),
+ SH_PFC_PIN_GROUP(drif3_data0_a),
+ SH_PFC_PIN_GROUP(drif3_data1_a),
+ SH_PFC_PIN_GROUP(drif3_ctrl_b),
+ SH_PFC_PIN_GROUP(drif3_data0_b),
+ SH_PFC_PIN_GROUP(drif3_data1_b),
SH_PFC_PIN_GROUP(du_rgb666),
SH_PFC_PIN_GROUP(du_rgb888),
SH_PFC_PIN_GROUP(du_clk_out_0),
@@ -4280,6 +4620,10 @@ static const struct sh_pfc_pin_group pinmux_groups[] = {
SH_PFC_PIN_GROUP(ssi9_data_b),
SH_PFC_PIN_GROUP(ssi9_ctrl_a),
SH_PFC_PIN_GROUP(ssi9_ctrl_b),
+ SH_PFC_PIN_GROUP(tmu_tclk1_a),
+ SH_PFC_PIN_GROUP(tmu_tclk1_b),
+ SH_PFC_PIN_GROUP(tmu_tclk2_a),
+ SH_PFC_PIN_GROUP(tmu_tclk2_b),
SH_PFC_PIN_GROUP(usb0),
SH_PFC_PIN_GROUP(usb1),
SH_PFC_PIN_GROUP(usb30),
@@ -4367,6 +4711,48 @@ static const char * const canfd1_groups[] = {
"canfd1_data",
};
+static const char * const drif0_groups[] = {
+ "drif0_ctrl_a",
+ "drif0_data0_a",
+ "drif0_data1_a",
+ "drif0_ctrl_b",
+ "drif0_data0_b",
+ "drif0_data1_b",
+ "drif0_ctrl_c",
+ "drif0_data0_c",
+ "drif0_data1_c",
+};
+
+static const char * const drif1_groups[] = {
+ "drif1_ctrl_a",
+ "drif1_data0_a",
+ "drif1_data1_a",
+ "drif1_ctrl_b",
+ "drif1_data0_b",
+ "drif1_data1_b",
+ "drif1_ctrl_c",
+ "drif1_data0_c",
+ "drif1_data1_c",
+};
+
+static const char * const drif2_groups[] = {
+ "drif2_ctrl_a",
+ "drif2_data0_a",
+ "drif2_data1_a",
+ "drif2_ctrl_b",
+ "drif2_data0_b",
+ "drif2_data1_b",
+};
+
+static const char * const drif3_groups[] = {
+ "drif3_ctrl_a",
+ "drif3_data0_a",
+ "drif3_data1_a",
+ "drif3_ctrl_b",
+ "drif3_data0_b",
+ "drif3_data1_b",
+};
+
static const char * const du_groups[] = {
"du_rgb666",
"du_rgb888",
@@ -4711,6 +5097,13 @@ static const char * const ssi_groups[] = {
"ssi9_ctrl_b",
};
+static const char * const tmu_groups[] = {
+ "tmu_tclk1_a",
+ "tmu_tclk1_b",
+ "tmu_tclk2_a",
+ "tmu_tclk2_b",
+};
+
static const char * const usb0_groups[] = {
"usb0",
};
@@ -4763,6 +5156,10 @@ static const struct sh_pfc_function pinmux_functions[] = {
SH_PFC_FUNCTION(can_clk),
SH_PFC_FUNCTION(canfd0),
SH_PFC_FUNCTION(canfd1),
+ SH_PFC_FUNCTION(drif0),
+ SH_PFC_FUNCTION(drif1),
+ SH_PFC_FUNCTION(drif2),
+ SH_PFC_FUNCTION(drif3),
SH_PFC_FUNCTION(du),
SH_PFC_FUNCTION(hscif0),
SH_PFC_FUNCTION(hscif1),
@@ -4797,6 +5194,7 @@ static const struct sh_pfc_function pinmux_functions[] = {
SH_PFC_FUNCTION(sdhi2),
SH_PFC_FUNCTION(sdhi3),
SH_PFC_FUNCTION(ssi),
+ SH_PFC_FUNCTION(tmu),
SH_PFC_FUNCTION(usb0),
SH_PFC_FUNCTION(usb1),
SH_PFC_FUNCTION(usb30),
@@ -5740,7 +6138,7 @@ static const struct pinmux_bias_reg pinmux_bias_regs[] = {
[31] = PIN_A_NUMBER('P', 8), /* DU_DOTCLKIN1 */
} },
{ PINMUX_BIAS_REG("PUEN3", 0xe606040c, "PUD3", 0xe606044c) {
- [ 0] = PIN_A_NUMBER('R', 8), /* DU_DOTCLKIN2 */
+ [ 0] = PIN_A_NUMBER('R', 8), /* DU_DOTCLKIN3 */
[ 1] = PIN_NONE,
[ 2] = PIN_A_NUMBER('D', 38), /* FSCLKST */
[ 3] = PIN_A_NUMBER('D', 39), /* EXTALR*/
diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a77970.c b/drivers/pinctrl/sh-pfc/pfc-r8a77970.c
index 4a7ab84b366b..c5e67ba29f7c 100644
--- a/drivers/pinctrl/sh-pfc/pfc-r8a77970.c
+++ b/drivers/pinctrl/sh-pfc/pfc-r8a77970.c
@@ -1578,47 +1578,25 @@ static const unsigned int tmu_tclk2_b_mux[] = {
};
/* - VIN0 ------------------------------------------------------------------- */
-static const unsigned int vin0_data8_pins[] = {
- RCAR_GP_PIN(2, 4), RCAR_GP_PIN(2, 5),
- RCAR_GP_PIN(2, 6), RCAR_GP_PIN(2, 7),
- RCAR_GP_PIN(2, 8), RCAR_GP_PIN(2, 9),
- RCAR_GP_PIN(2, 10), RCAR_GP_PIN(2, 11),
-};
-static const unsigned int vin0_data8_mux[] = {
- VI0_DATA0_MARK, VI0_DATA1_MARK,
- VI0_DATA2_MARK, VI0_DATA3_MARK,
- VI0_DATA4_MARK, VI0_DATA5_MARK,
- VI0_DATA6_MARK, VI0_DATA7_MARK,
+static const union vin_data12 vin0_data_pins = {
+ .data12 = {
+ RCAR_GP_PIN(2, 4), RCAR_GP_PIN(2, 5),
+ RCAR_GP_PIN(2, 6), RCAR_GP_PIN(2, 7),
+ RCAR_GP_PIN(2, 8), RCAR_GP_PIN(2, 9),
+ RCAR_GP_PIN(2, 10), RCAR_GP_PIN(2, 11),
+ RCAR_GP_PIN(2, 12), RCAR_GP_PIN(2, 13),
+ RCAR_GP_PIN(2, 14), RCAR_GP_PIN(2, 15),
+ },
};
-static const unsigned int vin0_data10_pins[] = {
- RCAR_GP_PIN(2, 4), RCAR_GP_PIN(2, 5),
- RCAR_GP_PIN(2, 6), RCAR_GP_PIN(2, 7),
- RCAR_GP_PIN(2, 8), RCAR_GP_PIN(2, 9),
- RCAR_GP_PIN(2, 10), RCAR_GP_PIN(2, 11),
- RCAR_GP_PIN(2, 12), RCAR_GP_PIN(2, 13),
-};
-static const unsigned int vin0_data10_mux[] = {
- VI0_DATA0_MARK, VI0_DATA1_MARK,
- VI0_DATA2_MARK, VI0_DATA3_MARK,
- VI0_DATA4_MARK, VI0_DATA5_MARK,
- VI0_DATA6_MARK, VI0_DATA7_MARK,
- VI0_DATA8_MARK, VI0_DATA9_MARK,
-};
-static const unsigned int vin0_data12_pins[] = {
- RCAR_GP_PIN(2, 4), RCAR_GP_PIN(2, 5),
- RCAR_GP_PIN(2, 6), RCAR_GP_PIN(2, 7),
- RCAR_GP_PIN(2, 8), RCAR_GP_PIN(2, 9),
- RCAR_GP_PIN(2, 10), RCAR_GP_PIN(2, 11),
- RCAR_GP_PIN(2, 12), RCAR_GP_PIN(2, 13),
- RCAR_GP_PIN(2, 14), RCAR_GP_PIN(2, 15),
-};
-static const unsigned int vin0_data12_mux[] = {
- VI0_DATA0_MARK, VI0_DATA1_MARK,
- VI0_DATA2_MARK, VI0_DATA3_MARK,
- VI0_DATA4_MARK, VI0_DATA5_MARK,
- VI0_DATA6_MARK, VI0_DATA7_MARK,
- VI0_DATA8_MARK, VI0_DATA9_MARK,
- VI0_DATA10_MARK, VI0_DATA11_MARK,
+static const union vin_data12 vin0_data_mux = {
+ .data12 = {
+ VI0_DATA0_MARK, VI0_DATA1_MARK,
+ VI0_DATA2_MARK, VI0_DATA3_MARK,
+ VI0_DATA4_MARK, VI0_DATA5_MARK,
+ VI0_DATA6_MARK, VI0_DATA7_MARK,
+ VI0_DATA8_MARK, VI0_DATA9_MARK,
+ VI0_DATA10_MARK, VI0_DATA11_MARK,
+ },
};
static const unsigned int vin0_sync_pins[] = {
/* HSYNC#, VSYNC# */
@@ -1650,47 +1628,25 @@ static const unsigned int vin0_clk_mux[] = {
};
/* - VIN1 ------------------------------------------------------------------- */
-static const unsigned int vin1_data8_pins[] = {
- RCAR_GP_PIN(3, 4), RCAR_GP_PIN(3, 5),
- RCAR_GP_PIN(3, 6), RCAR_GP_PIN(3, 7),
- RCAR_GP_PIN(3, 8), RCAR_GP_PIN(3, 9),
- RCAR_GP_PIN(3, 10), RCAR_GP_PIN(3, 11),
-};
-static const unsigned int vin1_data8_mux[] = {
- VI1_DATA0_MARK, VI1_DATA1_MARK,
- VI1_DATA2_MARK, VI1_DATA3_MARK,
- VI1_DATA4_MARK, VI1_DATA5_MARK,
- VI1_DATA6_MARK, VI1_DATA7_MARK,
-};
-static const unsigned int vin1_data10_pins[] = {
- RCAR_GP_PIN(3, 4), RCAR_GP_PIN(3, 5),
- RCAR_GP_PIN(3, 6), RCAR_GP_PIN(3, 7),
- RCAR_GP_PIN(3, 8), RCAR_GP_PIN(3, 9),
- RCAR_GP_PIN(3, 10), RCAR_GP_PIN(3, 11),
- RCAR_GP_PIN(3, 12), RCAR_GP_PIN(3, 13),
-};
-static const unsigned int vin1_data10_mux[] = {
- VI1_DATA0_MARK, VI1_DATA1_MARK,
- VI1_DATA2_MARK, VI1_DATA3_MARK,
- VI1_DATA4_MARK, VI1_DATA5_MARK,
- VI1_DATA6_MARK, VI1_DATA7_MARK,
- VI1_DATA8_MARK, VI1_DATA9_MARK,
-};
-static const unsigned int vin1_data12_pins[] = {
- RCAR_GP_PIN(3, 4), RCAR_GP_PIN(3, 5),
- RCAR_GP_PIN(3, 6), RCAR_GP_PIN(3, 7),
- RCAR_GP_PIN(3, 8), RCAR_GP_PIN(3, 9),
- RCAR_GP_PIN(3, 10), RCAR_GP_PIN(3, 11),
- RCAR_GP_PIN(3, 12), RCAR_GP_PIN(3, 13),
- RCAR_GP_PIN(3, 14), RCAR_GP_PIN(3, 15),
+static const union vin_data12 vin1_data_pins = {
+ .data12 = {
+ RCAR_GP_PIN(3, 4), RCAR_GP_PIN(3, 5),
+ RCAR_GP_PIN(3, 6), RCAR_GP_PIN(3, 7),
+ RCAR_GP_PIN(3, 8), RCAR_GP_PIN(3, 9),
+ RCAR_GP_PIN(3, 10), RCAR_GP_PIN(3, 11),
+ RCAR_GP_PIN(3, 12), RCAR_GP_PIN(3, 13),
+ RCAR_GP_PIN(3, 14), RCAR_GP_PIN(3, 15),
+ },
};
-static const unsigned int vin1_data12_mux[] = {
- VI1_DATA0_MARK, VI1_DATA1_MARK,
- VI1_DATA2_MARK, VI1_DATA3_MARK,
- VI1_DATA4_MARK, VI1_DATA5_MARK,
- VI1_DATA6_MARK, VI1_DATA7_MARK,
- VI1_DATA8_MARK, VI1_DATA9_MARK,
- VI1_DATA10_MARK, VI1_DATA11_MARK,
+static const union vin_data12 vin1_data_mux = {
+ .data12 = {
+ VI1_DATA0_MARK, VI1_DATA1_MARK,
+ VI1_DATA2_MARK, VI1_DATA3_MARK,
+ VI1_DATA4_MARK, VI1_DATA5_MARK,
+ VI1_DATA6_MARK, VI1_DATA7_MARK,
+ VI1_DATA8_MARK, VI1_DATA9_MARK,
+ VI1_DATA10_MARK, VI1_DATA11_MARK,
+ },
};
static const unsigned int vin1_sync_pins[] = {
/* HSYNC#, VSYNC# */
@@ -1831,16 +1787,16 @@ static const struct sh_pfc_pin_group pinmux_groups[] = {
SH_PFC_PIN_GROUP(tmu_tclk1_b),
SH_PFC_PIN_GROUP(tmu_tclk2_a),
SH_PFC_PIN_GROUP(tmu_tclk2_b),
- SH_PFC_PIN_GROUP(vin0_data8),
- SH_PFC_PIN_GROUP(vin0_data10),
- SH_PFC_PIN_GROUP(vin0_data12),
+ VIN_DATA_PIN_GROUP(vin0_data, 8),
+ VIN_DATA_PIN_GROUP(vin0_data, 10),
+ VIN_DATA_PIN_GROUP(vin0_data, 12),
SH_PFC_PIN_GROUP(vin0_sync),
SH_PFC_PIN_GROUP(vin0_field),
SH_PFC_PIN_GROUP(vin0_clkenb),
SH_PFC_PIN_GROUP(vin0_clk),
- SH_PFC_PIN_GROUP(vin1_data8),
- SH_PFC_PIN_GROUP(vin1_data10),
- SH_PFC_PIN_GROUP(vin1_data12),
+ VIN_DATA_PIN_GROUP(vin1_data, 8),
+ VIN_DATA_PIN_GROUP(vin1_data, 10),
+ VIN_DATA_PIN_GROUP(vin1_data, 12),
SH_PFC_PIN_GROUP(vin1_sync),
SH_PFC_PIN_GROUP(vin1_field),
SH_PFC_PIN_GROUP(vin1_clkenb),
diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a77980.c b/drivers/pinctrl/sh-pfc/pfc-r8a77980.c
index 8bef24502f0c..b807b67ae143 100644
--- a/drivers/pinctrl/sh-pfc/pfc-r8a77980.c
+++ b/drivers/pinctrl/sh-pfc/pfc-r8a77980.c
@@ -1970,47 +1970,25 @@ static const unsigned int vin0_clk_mux[] = {
};
/* - VIN1 ------------------------------------------------------------------- */
-static const unsigned int vin1_data8_pins[] = {
- RCAR_GP_PIN(3, 4), RCAR_GP_PIN(3, 5),
- RCAR_GP_PIN(3, 6), RCAR_GP_PIN(3, 7),
- RCAR_GP_PIN(3, 8), RCAR_GP_PIN(3, 9),
- RCAR_GP_PIN(3, 10), RCAR_GP_PIN(3, 11),
-};
-static const unsigned int vin1_data8_mux[] = {
- VI1_DATA0_MARK, VI1_DATA1_MARK,
- VI1_DATA2_MARK, VI1_DATA3_MARK,
- VI1_DATA4_MARK, VI1_DATA5_MARK,
- VI1_DATA6_MARK, VI1_DATA7_MARK,
-};
-static const unsigned int vin1_data10_pins[] = {
- RCAR_GP_PIN(3, 4), RCAR_GP_PIN(3, 5),
- RCAR_GP_PIN(3, 6), RCAR_GP_PIN(3, 7),
- RCAR_GP_PIN(3, 8), RCAR_GP_PIN(3, 9),
- RCAR_GP_PIN(3, 10), RCAR_GP_PIN(3, 11),
- RCAR_GP_PIN(3, 12), RCAR_GP_PIN(3, 13),
-};
-static const unsigned int vin1_data10_mux[] = {
- VI1_DATA0_MARK, VI1_DATA1_MARK,
- VI1_DATA2_MARK, VI1_DATA3_MARK,
- VI1_DATA4_MARK, VI1_DATA5_MARK,
- VI1_DATA6_MARK, VI1_DATA7_MARK,
- VI1_DATA8_MARK, VI1_DATA9_MARK,
-};
-static const unsigned int vin1_data12_pins[] = {
- RCAR_GP_PIN(3, 4), RCAR_GP_PIN(3, 5),
- RCAR_GP_PIN(3, 6), RCAR_GP_PIN(3, 7),
- RCAR_GP_PIN(3, 8), RCAR_GP_PIN(3, 9),
- RCAR_GP_PIN(3, 10), RCAR_GP_PIN(3, 11),
- RCAR_GP_PIN(3, 12), RCAR_GP_PIN(3, 13),
- RCAR_GP_PIN(3, 14), RCAR_GP_PIN(3, 15),
+static const union vin_data12 vin1_data_pins = {
+ .data12 = {
+ RCAR_GP_PIN(3, 4), RCAR_GP_PIN(3, 5),
+ RCAR_GP_PIN(3, 6), RCAR_GP_PIN(3, 7),
+ RCAR_GP_PIN(3, 8), RCAR_GP_PIN(3, 9),
+ RCAR_GP_PIN(3, 10), RCAR_GP_PIN(3, 11),
+ RCAR_GP_PIN(3, 12), RCAR_GP_PIN(3, 13),
+ RCAR_GP_PIN(3, 14), RCAR_GP_PIN(3, 15),
+ },
};
-static const unsigned int vin1_data12_mux[] = {
- VI1_DATA0_MARK, VI1_DATA1_MARK,
- VI1_DATA2_MARK, VI1_DATA3_MARK,
- VI1_DATA4_MARK, VI1_DATA5_MARK,
- VI1_DATA6_MARK, VI1_DATA7_MARK,
- VI1_DATA8_MARK, VI1_DATA9_MARK,
- VI1_DATA10_MARK, VI1_DATA11_MARK,
+static const union vin_data12 vin1_data_mux = {
+ .data12 = {
+ VI1_DATA0_MARK, VI1_DATA1_MARK,
+ VI1_DATA2_MARK, VI1_DATA3_MARK,
+ VI1_DATA4_MARK, VI1_DATA5_MARK,
+ VI1_DATA6_MARK, VI1_DATA7_MARK,
+ VI1_DATA8_MARK, VI1_DATA9_MARK,
+ VI1_DATA10_MARK, VI1_DATA11_MARK,
+ },
};
static const unsigned int vin1_sync_pins[] = {
/* VI1_VSYNC#, VI1_HSYNC# */
@@ -2182,9 +2160,9 @@ static const struct sh_pfc_pin_group pinmux_groups[] = {
SH_PFC_PIN_GROUP(vin0_field),
SH_PFC_PIN_GROUP(vin0_clkenb),
SH_PFC_PIN_GROUP(vin0_clk),
- SH_PFC_PIN_GROUP(vin1_data8),
- SH_PFC_PIN_GROUP(vin1_data10),
- SH_PFC_PIN_GROUP(vin1_data12),
+ VIN_DATA_PIN_GROUP(vin1_data, 8),
+ VIN_DATA_PIN_GROUP(vin1_data, 10),
+ VIN_DATA_PIN_GROUP(vin1_data, 12),
SH_PFC_PIN_GROUP(vin1_sync),
SH_PFC_PIN_GROUP(vin1_field),
SH_PFC_PIN_GROUP(vin1_clkenb),
diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a77990.c b/drivers/pinctrl/sh-pfc/pfc-r8a77990.c
index e40908dc37e0..151640c30e9d 100644
--- a/drivers/pinctrl/sh-pfc/pfc-r8a77990.c
+++ b/drivers/pinctrl/sh-pfc/pfc-r8a77990.c
@@ -30,7 +30,16 @@
PORT_GP_CFG_1(3, 15, fn, sfx, CFG_FLAGS), \
PORT_GP_CFG_11(4, fn, sfx, CFG_FLAGS | SH_PFC_PIN_CFG_IO_VOLTAGE), \
PORT_GP_CFG_20(5, fn, sfx, CFG_FLAGS), \
- PORT_GP_CFG_18(6, fn, sfx, CFG_FLAGS)
+ PORT_GP_CFG_9(6, fn, sfx, CFG_FLAGS), \
+ PORT_GP_CFG_1(6, 9, fn, sfx, SH_PFC_PIN_CFG_PULL_UP), \
+ PORT_GP_CFG_1(6, 10, fn, sfx, CFG_FLAGS), \
+ PORT_GP_CFG_1(6, 11, fn, sfx, CFG_FLAGS), \
+ PORT_GP_CFG_1(6, 12, fn, sfx, CFG_FLAGS), \
+ PORT_GP_CFG_1(6, 13, fn, sfx, CFG_FLAGS), \
+ PORT_GP_CFG_1(6, 14, fn, sfx, CFG_FLAGS), \
+ PORT_GP_CFG_1(6, 15, fn, sfx, CFG_FLAGS), \
+ PORT_GP_CFG_1(6, 16, fn, sfx, CFG_FLAGS), \
+ PORT_GP_CFG_1(6, 17, fn, sfx, CFG_FLAGS)
/*
* F_() : just information
* FM() : macro for FN_xxx / xxx_MARK
@@ -391,29 +400,33 @@ FM(IP12_23_20) IP12_23_20 FM(IP13_23_20) IP13_23_20 FM(IP14_23_20) IP14_23_20 FM
FM(IP12_27_24) IP12_27_24 FM(IP13_27_24) IP13_27_24 FM(IP14_27_24) IP14_27_24 FM(IP15_27_24) IP15_27_24 \
FM(IP12_31_28) IP12_31_28 FM(IP13_31_28) IP13_31_28 FM(IP14_31_28) IP14_31_28 FM(IP15_31_28) IP15_31_28
+/* The bit numbering in MOD_SEL fields is reversed */
+#define REV4(f0, f1, f2, f3) f0 f2 f1 f3
+#define REV8(f0, f1, f2, f3, f4, f5, f6, f7) f0 f4 f2 f6 f1 f5 f3 f7
+
/* MOD_SEL0 */ /* 0 */ /* 1 */ /* 2 */ /* 3 */ /* 4 */ /* 5 */ /* 6 */ /* 7 */
-#define MOD_SEL0_30_29 FM(SEL_ADGB_0) FM(SEL_ADGB_1) FM(SEL_ADGB_2) F_(0, 0)
+#define MOD_SEL0_30_29 REV4(FM(SEL_ADGB_0), FM(SEL_ADGB_1), FM(SEL_ADGB_2), F_(0, 0))
#define MOD_SEL0_28 FM(SEL_DRIF0_0) FM(SEL_DRIF0_1)
-#define MOD_SEL0_27_26 FM(SEL_FM_0) FM(SEL_FM_1) FM(SEL_FM_2) F_(0, 0)
+#define MOD_SEL0_27_26 REV4(FM(SEL_FM_0), FM(SEL_FM_1), FM(SEL_FM_2), F_(0, 0))
#define MOD_SEL0_25 FM(SEL_FSO_0) FM(SEL_FSO_1)
#define MOD_SEL0_24 FM(SEL_HSCIF0_0) FM(SEL_HSCIF0_1)
#define MOD_SEL0_23 FM(SEL_HSCIF1_0) FM(SEL_HSCIF1_1)
#define MOD_SEL0_22 FM(SEL_HSCIF2_0) FM(SEL_HSCIF2_1)
-#define MOD_SEL0_21_20 FM(SEL_I2C1_0) FM(SEL_I2C1_1) FM(SEL_I2C1_2) FM(SEL_I2C1_3)
-#define MOD_SEL0_19_18_17 FM(SEL_I2C2_0) FM(SEL_I2C2_1) FM(SEL_I2C2_2) FM(SEL_I2C2_3) FM(SEL_I2C2_4) F_(0, 0) F_(0, 0) F_(0, 0)
+#define MOD_SEL0_21_20 REV4(FM(SEL_I2C1_0), FM(SEL_I2C1_1), FM(SEL_I2C1_2), FM(SEL_I2C1_3))
+#define MOD_SEL0_19_18_17 REV8(FM(SEL_I2C2_0), FM(SEL_I2C2_1), FM(SEL_I2C2_2), FM(SEL_I2C2_3), FM(SEL_I2C2_4), F_(0, 0), F_(0, 0), F_(0, 0))
#define MOD_SEL0_16 FM(SEL_NDFC_0) FM(SEL_NDFC_1)
#define MOD_SEL0_15 FM(SEL_PWM0_0) FM(SEL_PWM0_1)
#define MOD_SEL0_14 FM(SEL_PWM1_0) FM(SEL_PWM1_1)
-#define MOD_SEL0_13_12 FM(SEL_PWM2_0) FM(SEL_PWM2_1) FM(SEL_PWM2_2) F_(0, 0)
-#define MOD_SEL0_11_10 FM(SEL_PWM3_0) FM(SEL_PWM3_1) FM(SEL_PWM3_2) F_(0, 0)
+#define MOD_SEL0_13_12 REV4(FM(SEL_PWM2_0), FM(SEL_PWM2_1), FM(SEL_PWM2_2), F_(0, 0))
+#define MOD_SEL0_11_10 REV4(FM(SEL_PWM3_0), FM(SEL_PWM3_1), FM(SEL_PWM3_2), F_(0, 0))
#define MOD_SEL0_9 FM(SEL_PWM4_0) FM(SEL_PWM4_1)
#define MOD_SEL0_8 FM(SEL_PWM5_0) FM(SEL_PWM5_1)
#define MOD_SEL0_7 FM(SEL_PWM6_0) FM(SEL_PWM6_1)
-#define MOD_SEL0_6_5 FM(SEL_REMOCON_0) FM(SEL_REMOCON_1) FM(SEL_REMOCON_2) F_(0, 0)
+#define MOD_SEL0_6_5 REV4(FM(SEL_REMOCON_0), FM(SEL_REMOCON_1), FM(SEL_REMOCON_2), F_(0, 0))
#define MOD_SEL0_4 FM(SEL_SCIF_0) FM(SEL_SCIF_1)
#define MOD_SEL0_3 FM(SEL_SCIF0_0) FM(SEL_SCIF0_1)
#define MOD_SEL0_2 FM(SEL_SCIF2_0) FM(SEL_SCIF2_1)
-#define MOD_SEL0_1_0 FM(SEL_SPEED_PULSE_IF_0) FM(SEL_SPEED_PULSE_IF_1) FM(SEL_SPEED_PULSE_IF_2) F_(0, 0)
+#define MOD_SEL0_1_0 REV4(FM(SEL_SPEED_PULSE_IF_0), FM(SEL_SPEED_PULSE_IF_1), FM(SEL_SPEED_PULSE_IF_2), F_(0, 0))
/* MOD_SEL1 */ /* 0 */ /* 1 */ /* 2 */ /* 3 */ /* 4 */ /* 5 */ /* 6 */ /* 7 */
#define MOD_SEL1_31 FM(SEL_SIMCARD_0) FM(SEL_SIMCARD_1)
@@ -422,18 +435,18 @@ FM(IP12_31_28) IP12_31_28 FM(IP13_31_28) IP13_31_28 FM(IP14_31_28) IP14_31_28 FM
#define MOD_SEL1_28 FM(SEL_USB_20_CH0_0) FM(SEL_USB_20_CH0_1)
#define MOD_SEL1_26 FM(SEL_DRIF2_0) FM(SEL_DRIF2_1)
#define MOD_SEL1_25 FM(SEL_DRIF3_0) FM(SEL_DRIF3_1)
-#define MOD_SEL1_24_23_22 FM(SEL_HSCIF3_0) FM(SEL_HSCIF3_1) FM(SEL_HSCIF3_2) FM(SEL_HSCIF3_3) FM(SEL_HSCIF3_4) F_(0, 0) F_(0, 0) F_(0, 0)
-#define MOD_SEL1_21_20_19 FM(SEL_HSCIF4_0) FM(SEL_HSCIF4_1) FM(SEL_HSCIF4_2) FM(SEL_HSCIF4_3) FM(SEL_HSCIF4_4) F_(0, 0) F_(0, 0) F_(0, 0)
+#define MOD_SEL1_24_23_22 REV8(FM(SEL_HSCIF3_0), FM(SEL_HSCIF3_1), FM(SEL_HSCIF3_2), FM(SEL_HSCIF3_3), FM(SEL_HSCIF3_4), F_(0, 0), F_(0, 0), F_(0, 0))
+#define MOD_SEL1_21_20_19 REV8(FM(SEL_HSCIF4_0), FM(SEL_HSCIF4_1), FM(SEL_HSCIF4_2), FM(SEL_HSCIF4_3), FM(SEL_HSCIF4_4), F_(0, 0), F_(0, 0), F_(0, 0))
#define MOD_SEL1_18 FM(SEL_I2C6_0) FM(SEL_I2C6_1)
#define MOD_SEL1_17 FM(SEL_I2C7_0) FM(SEL_I2C7_1)
#define MOD_SEL1_16 FM(SEL_MSIOF2_0) FM(SEL_MSIOF2_1)
#define MOD_SEL1_15 FM(SEL_MSIOF3_0) FM(SEL_MSIOF3_1)
-#define MOD_SEL1_14_13 FM(SEL_SCIF3_0) FM(SEL_SCIF3_1) FM(SEL_SCIF3_2) F_(0, 0)
-#define MOD_SEL1_12_11 FM(SEL_SCIF4_0) FM(SEL_SCIF4_1) FM(SEL_SCIF4_2) F_(0, 0)
-#define MOD_SEL1_10_9 FM(SEL_SCIF5_0) FM(SEL_SCIF5_1) FM(SEL_SCIF5_2) F_(0, 0)
+#define MOD_SEL1_14_13 REV4(FM(SEL_SCIF3_0), FM(SEL_SCIF3_1), FM(SEL_SCIF3_2), F_(0, 0))
+#define MOD_SEL1_12_11 REV4(FM(SEL_SCIF4_0), FM(SEL_SCIF4_1), FM(SEL_SCIF4_2), F_(0, 0))
+#define MOD_SEL1_10_9 REV4(FM(SEL_SCIF5_0), FM(SEL_SCIF5_1), FM(SEL_SCIF5_2), F_(0, 0))
#define MOD_SEL1_8 FM(SEL_VIN4_0) FM(SEL_VIN4_1)
#define MOD_SEL1_7 FM(SEL_VIN5_0) FM(SEL_VIN5_1)
-#define MOD_SEL1_6_5 FM(SEL_ADGC_0) FM(SEL_ADGC_1) FM(SEL_ADGC_2) F_(0, 0)
+#define MOD_SEL1_6_5 REV4(FM(SEL_ADGC_0), FM(SEL_ADGC_1), FM(SEL_ADGC_2), F_(0, 0))
#define MOD_SEL1_4 FM(SEL_SSI9_0) FM(SEL_SSI9_1)
#define PINMUX_MOD_SELS \
@@ -1060,7 +1073,7 @@ static const u16 pinmux_data[] = {
PINMUX_IPSR_GPSR(IP11_11_8, RIF1_SYNC),
PINMUX_IPSR_GPSR(IP11_11_8, TS_SCK1),
- PINMUX_IPSR_GPSR(IP11_15_12, TX0_A),
+ PINMUX_IPSR_MSEL(IP11_15_12, TX0_A, SEL_SCIF0_0),
PINMUX_IPSR_GPSR(IP11_15_12, HTX1_A),
PINMUX_IPSR_MSEL(IP11_15_12, SSI_WS2_A, SEL_SSI2_0),
PINMUX_IPSR_GPSR(IP11_15_12, RIF1_D0),
@@ -1099,7 +1112,7 @@ static const u16 pinmux_data[] = {
PINMUX_IPSR_MSEL(IP12_3_0, SSI_WS9_B, SEL_SSI9_1),
PINMUX_IPSR_GPSR(IP12_3_0, AUDIO_CLKOUT3_B),
- PINMUX_IPSR_GPSR(IP12_7_4, SCK2_A),
+ PINMUX_IPSR_MSEL(IP12_7_4, SCK2_A, SEL_SCIF2_0),
PINMUX_IPSR_MSEL(IP12_7_4, HSCK0_A, SEL_HSCIF0_0),
PINMUX_IPSR_MSEL(IP12_7_4, AUDIO_CLKB_A, SEL_ADGB_0),
PINMUX_IPSR_GPSR(IP12_7_4, CTS1_N),
@@ -1107,14 +1120,14 @@ static const u16 pinmux_data[] = {
PINMUX_IPSR_MSEL(IP12_7_4, REMOCON_A, SEL_REMOCON_0),
PINMUX_IPSR_MSEL(IP12_7_4, SCIF_CLK_B, SEL_SCIF_1),
- PINMUX_IPSR_GPSR(IP12_11_8, TX2_A),
+ PINMUX_IPSR_MSEL(IP12_11_8, TX2_A, SEL_SCIF2_0),
PINMUX_IPSR_MSEL(IP12_11_8, HRX0_A, SEL_HSCIF0_0),
PINMUX_IPSR_GPSR(IP12_11_8, AUDIO_CLKOUT2_A),
PINMUX_IPSR_MSEL(IP12_11_8, SCL1_A, SEL_I2C1_0),
PINMUX_IPSR_MSEL(IP12_11_8, FSO_CFE_0_N_A, SEL_FSO_0),
PINMUX_IPSR_GPSR(IP12_11_8, TS_SDEN1),
- PINMUX_IPSR_GPSR(IP12_15_12, RX2_A),
+ PINMUX_IPSR_MSEL(IP12_15_12, RX2_A, SEL_SCIF2_0),
PINMUX_IPSR_GPSR(IP12_15_12, HTX0_A),
PINMUX_IPSR_GPSR(IP12_15_12, AUDIO_CLKOUT3_A),
PINMUX_IPSR_MSEL(IP12_15_12, SDA1_A, SEL_I2C1_0),
@@ -1126,11 +1139,11 @@ static const u16 pinmux_data[] = {
PINMUX_IPSR_GPSR(IP12_23_20, MSIOF0_RXD),
PINMUX_IPSR_GPSR(IP12_23_20, SSI_WS78),
- PINMUX_IPSR_GPSR(IP12_23_20, TX2_B),
+ PINMUX_IPSR_MSEL(IP12_23_20, TX2_B, SEL_SCIF2_1),
PINMUX_IPSR_GPSR(IP12_27_24, MSIOF0_TXD),
PINMUX_IPSR_GPSR(IP12_27_24, SSI_SDATA7),
- PINMUX_IPSR_GPSR(IP12_27_24, RX2_B),
+ PINMUX_IPSR_MSEL(IP12_27_24, RX2_B, SEL_SCIF2_1),
PINMUX_IPSR_GPSR(IP12_31_28, MSIOF0_SYNC),
PINMUX_IPSR_GPSR(IP12_31_28, AUDIO_CLKOUT_B),
@@ -1170,7 +1183,7 @@ static const u16 pinmux_data[] = {
PINMUX_IPSR_MSEL(IP13_19_16, SIM0_D_A, SEL_SIMCARD_0),
PINMUX_IPSR_GPSR(IP13_23_20, MLB_DAT),
- PINMUX_IPSR_GPSR(IP13_23_20, TX0_B),
+ PINMUX_IPSR_MSEL(IP13_23_20, TX0_B, SEL_SCIF0_1),
PINMUX_IPSR_MSEL(IP13_23_20, RIF0_SYNC_A, SEL_DRIF0_0),
PINMUX_IPSR_GPSR(IP13_23_20, SIM0_CLK_A),
@@ -1586,6 +1599,199 @@ static const unsigned int canfd1_data_mux[] = {
CANFD1_TX_MARK, CANFD1_RX_MARK,
};
+/* - DRIF0 --------------------------------------------------------------- */
+static const unsigned int drif0_ctrl_a_pins[] = {
+ /* CLK, SYNC */
+ RCAR_GP_PIN(5, 7), RCAR_GP_PIN(5, 19),
+};
+
+static const unsigned int drif0_ctrl_a_mux[] = {
+ RIF0_CLK_A_MARK, RIF0_SYNC_A_MARK,
+};
+
+static const unsigned int drif0_data0_a_pins[] = {
+ /* D0 */
+ RCAR_GP_PIN(5, 17),
+};
+
+static const unsigned int drif0_data0_a_mux[] = {
+ RIF0_D0_A_MARK,
+};
+
+static const unsigned int drif0_data1_a_pins[] = {
+ /* D1 */
+ RCAR_GP_PIN(5, 18),
+};
+
+static const unsigned int drif0_data1_a_mux[] = {
+ RIF0_D1_A_MARK,
+};
+
+static const unsigned int drif0_ctrl_b_pins[] = {
+ /* CLK, SYNC */
+ RCAR_GP_PIN(3, 12), RCAR_GP_PIN(3, 15),
+};
+
+static const unsigned int drif0_ctrl_b_mux[] = {
+ RIF0_CLK_B_MARK, RIF0_SYNC_B_MARK,
+};
+
+static const unsigned int drif0_data0_b_pins[] = {
+ /* D0 */
+ RCAR_GP_PIN(3, 13),
+};
+
+static const unsigned int drif0_data0_b_mux[] = {
+ RIF0_D0_B_MARK,
+};
+
+static const unsigned int drif0_data1_b_pins[] = {
+ /* D1 */
+ RCAR_GP_PIN(3, 14),
+};
+
+static const unsigned int drif0_data1_b_mux[] = {
+ RIF0_D1_B_MARK,
+};
+
+/* - DRIF1 --------------------------------------------------------------- */
+static const unsigned int drif1_ctrl_pins[] = {
+ /* CLK, SYNC */
+ RCAR_GP_PIN(5, 4), RCAR_GP_PIN(5, 1),
+};
+
+static const unsigned int drif1_ctrl_mux[] = {
+ RIF1_CLK_MARK, RIF1_SYNC_MARK,
+};
+
+static const unsigned int drif1_data0_pins[] = {
+ /* D0 */
+ RCAR_GP_PIN(5, 2),
+};
+
+static const unsigned int drif1_data0_mux[] = {
+ RIF1_D0_MARK,
+};
+
+static const unsigned int drif1_data1_pins[] = {
+ /* D1 */
+ RCAR_GP_PIN(5, 3),
+};
+
+static const unsigned int drif1_data1_mux[] = {
+ RIF1_D1_MARK,
+};
+
+/* - DRIF2 --------------------------------------------------------------- */
+static const unsigned int drif2_ctrl_a_pins[] = {
+ /* CLK, SYNC */
+ RCAR_GP_PIN(2, 6), RCAR_GP_PIN(2, 7),
+};
+
+static const unsigned int drif2_ctrl_a_mux[] = {
+ RIF2_CLK_A_MARK, RIF2_SYNC_A_MARK,
+};
+
+static const unsigned int drif2_data0_a_pins[] = {
+ /* D0 */
+ RCAR_GP_PIN(2, 8),
+};
+
+static const unsigned int drif2_data0_a_mux[] = {
+ RIF2_D0_A_MARK,
+};
+
+static const unsigned int drif2_data1_a_pins[] = {
+ /* D1 */
+ RCAR_GP_PIN(2, 9),
+};
+
+static const unsigned int drif2_data1_a_mux[] = {
+ RIF2_D1_A_MARK,
+};
+
+static const unsigned int drif2_ctrl_b_pins[] = {
+ /* CLK, SYNC */
+ RCAR_GP_PIN(1, 4), RCAR_GP_PIN(1, 5),
+};
+
+static const unsigned int drif2_ctrl_b_mux[] = {
+ RIF2_CLK_B_MARK, RIF2_SYNC_B_MARK,
+};
+
+static const unsigned int drif2_data0_b_pins[] = {
+ /* D0 */
+ RCAR_GP_PIN(1, 6),
+};
+
+static const unsigned int drif2_data0_b_mux[] = {
+ RIF2_D0_B_MARK,
+};
+
+static const unsigned int drif2_data1_b_pins[] = {
+ /* D1 */
+ RCAR_GP_PIN(1, 7),
+};
+
+static const unsigned int drif2_data1_b_mux[] = {
+ RIF2_D1_B_MARK,
+};
+
+/* - DRIF3 --------------------------------------------------------------- */
+static const unsigned int drif3_ctrl_a_pins[] = {
+ /* CLK, SYNC */
+ RCAR_GP_PIN(2, 10), RCAR_GP_PIN(2, 11),
+};
+
+static const unsigned int drif3_ctrl_a_mux[] = {
+ RIF3_CLK_A_MARK, RIF3_SYNC_A_MARK,
+};
+
+static const unsigned int drif3_data0_a_pins[] = {
+ /* D0 */
+ RCAR_GP_PIN(2, 12),
+};
+
+static const unsigned int drif3_data0_a_mux[] = {
+ RIF3_D0_A_MARK,
+};
+
+static const unsigned int drif3_data1_a_pins[] = {
+ /* D1 */
+ RCAR_GP_PIN(2, 13),
+};
+
+static const unsigned int drif3_data1_a_mux[] = {
+ RIF3_D1_A_MARK,
+};
+
+static const unsigned int drif3_ctrl_b_pins[] = {
+ /* CLK, SYNC */
+ RCAR_GP_PIN(0, 8), RCAR_GP_PIN(0, 9),
+};
+
+static const unsigned int drif3_ctrl_b_mux[] = {
+ RIF3_CLK_B_MARK, RIF3_SYNC_B_MARK,
+};
+
+static const unsigned int drif3_data0_b_pins[] = {
+ /* D0 */
+ RCAR_GP_PIN(0, 10),
+};
+
+static const unsigned int drif3_data0_b_mux[] = {
+ RIF3_D0_B_MARK,
+};
+
+static const unsigned int drif3_data1_b_pins[] = {
+ /* D1 */
+ RCAR_GP_PIN(0, 11),
+};
+
+static const unsigned int drif3_data1_b_mux[] = {
+ RIF3_D1_B_MARK,
+};
+
/* - DU --------------------------------------------------------------------- */
static const unsigned int du_rgb666_pins[] = {
/* R[7:2], G[7:2], B[7:2] */
@@ -3243,6 +3449,43 @@ static const unsigned int ssi9_ctrl_b_mux[] = {
SSI_SCK9_B_MARK, SSI_WS9_B_MARK,
};
+/* - TMU -------------------------------------------------------------------- */
+static const unsigned int tmu_tclk1_a_pins[] = {
+ /* TCLK */
+ RCAR_GP_PIN(3, 12),
+};
+
+static const unsigned int tmu_tclk1_a_mux[] = {
+ TCLK1_A_MARK,
+};
+
+static const unsigned int tmu_tclk1_b_pins[] = {
+ /* TCLK */
+ RCAR_GP_PIN(5, 17),
+};
+
+static const unsigned int tmu_tclk1_b_mux[] = {
+ TCLK1_B_MARK,
+};
+
+static const unsigned int tmu_tclk2_a_pins[] = {
+ /* TCLK */
+ RCAR_GP_PIN(3, 13),
+};
+
+static const unsigned int tmu_tclk2_a_mux[] = {
+ TCLK2_A_MARK,
+};
+
+static const unsigned int tmu_tclk2_b_pins[] = {
+ /* TCLK */
+ RCAR_GP_PIN(5, 18),
+};
+
+static const unsigned int tmu_tclk2_b_mux[] = {
+ TCLK2_B_MARK,
+};
+
/* - USB0 ------------------------------------------------------------------- */
static const unsigned int usb0_a_pins[] = {
/* PWEN, OVC */
@@ -3523,8 +3766,8 @@ static const unsigned int vin5_clk_b_mux[] = {
};
static const struct {
- struct sh_pfc_pin_group common[241];
- struct sh_pfc_pin_group automotive[2];
+ struct sh_pfc_pin_group common[245];
+ struct sh_pfc_pin_group automotive[23];
} pinmux_groups = {
.common = {
SH_PFC_PIN_GROUP(audio_clk_a),
@@ -3735,6 +3978,10 @@ static const struct {
SH_PFC_PIN_GROUP(ssi9_data),
SH_PFC_PIN_GROUP(ssi9_ctrl_a),
SH_PFC_PIN_GROUP(ssi9_ctrl_b),
+ SH_PFC_PIN_GROUP(tmu_tclk1_a),
+ SH_PFC_PIN_GROUP(tmu_tclk1_b),
+ SH_PFC_PIN_GROUP(tmu_tclk2_a),
+ SH_PFC_PIN_GROUP(tmu_tclk2_b),
SH_PFC_PIN_GROUP(usb0_a),
SH_PFC_PIN_GROUP(usb0_b),
SH_PFC_PIN_GROUP(usb0_id),
@@ -3772,6 +4019,27 @@ static const struct {
.automotive = {
SH_PFC_PIN_GROUP(canfd0_data),
SH_PFC_PIN_GROUP(canfd1_data),
+ SH_PFC_PIN_GROUP(drif0_ctrl_a),
+ SH_PFC_PIN_GROUP(drif0_data0_a),
+ SH_PFC_PIN_GROUP(drif0_data1_a),
+ SH_PFC_PIN_GROUP(drif0_ctrl_b),
+ SH_PFC_PIN_GROUP(drif0_data0_b),
+ SH_PFC_PIN_GROUP(drif0_data1_b),
+ SH_PFC_PIN_GROUP(drif1_ctrl),
+ SH_PFC_PIN_GROUP(drif1_data0),
+ SH_PFC_PIN_GROUP(drif1_data1),
+ SH_PFC_PIN_GROUP(drif2_ctrl_a),
+ SH_PFC_PIN_GROUP(drif2_data0_a),
+ SH_PFC_PIN_GROUP(drif2_data1_a),
+ SH_PFC_PIN_GROUP(drif2_ctrl_b),
+ SH_PFC_PIN_GROUP(drif2_data0_b),
+ SH_PFC_PIN_GROUP(drif2_data1_b),
+ SH_PFC_PIN_GROUP(drif3_ctrl_a),
+ SH_PFC_PIN_GROUP(drif3_data0_a),
+ SH_PFC_PIN_GROUP(drif3_data1_a),
+ SH_PFC_PIN_GROUP(drif3_ctrl_b),
+ SH_PFC_PIN_GROUP(drif3_data0_b),
+ SH_PFC_PIN_GROUP(drif3_data1_b),
}
};
@@ -3826,6 +4094,39 @@ static const char * const canfd1_groups[] = {
"canfd1_data",
};
+static const char * const drif0_groups[] = {
+ "drif0_ctrl_a",
+ "drif0_data0_a",
+ "drif0_data1_a",
+ "drif0_ctrl_b",
+ "drif0_data0_b",
+ "drif0_data1_b",
+};
+
+static const char * const drif1_groups[] = {
+ "drif1_ctrl",
+ "drif1_data0",
+ "drif1_data1",
+};
+
+static const char * const drif2_groups[] = {
+ "drif2_ctrl_a",
+ "drif2_data0_a",
+ "drif2_data1_a",
+ "drif2_ctrl_b",
+ "drif2_data0_b",
+ "drif2_data1_b",
+};
+
+static const char * const drif3_groups[] = {
+ "drif3_ctrl_a",
+ "drif3_data0_a",
+ "drif3_data1_a",
+ "drif3_ctrl_b",
+ "drif3_data0_b",
+ "drif3_data1_b",
+};
+
static const char * const du_groups[] = {
"du_rgb666",
"du_rgb888",
@@ -4111,6 +4412,13 @@ static const char * const ssi_groups[] = {
"ssi9_ctrl_b",
};
+static const char * const tmu_groups[] = {
+ "tmu_tclk1_a",
+ "tmu_tclk1_b",
+ "tmu_tclk2_a",
+ "tmu_tclk2_b",
+};
+
static const char * const usb0_groups[] = {
"usb0_a",
"usb0_b",
@@ -4157,8 +4465,8 @@ static const char * const vin5_groups[] = {
};
static const struct {
- struct sh_pfc_function common[44];
- struct sh_pfc_function automotive[2];
+ struct sh_pfc_function common[45];
+ struct sh_pfc_function automotive[6];
} pinmux_functions = {
.common = {
SH_PFC_FUNCTION(audio_clk),
@@ -4201,6 +4509,7 @@ static const struct {
SH_PFC_FUNCTION(sdhi1),
SH_PFC_FUNCTION(sdhi3),
SH_PFC_FUNCTION(ssi),
+ SH_PFC_FUNCTION(tmu),
SH_PFC_FUNCTION(usb0),
SH_PFC_FUNCTION(usb30),
SH_PFC_FUNCTION(vin4),
@@ -4209,6 +4518,10 @@ static const struct {
.automotive = {
SH_PFC_FUNCTION(canfd0),
SH_PFC_FUNCTION(canfd1),
+ SH_PFC_FUNCTION(drif0),
+ SH_PFC_FUNCTION(drif1),
+ SH_PFC_FUNCTION(drif2),
+ SH_PFC_FUNCTION(drif3),
}
};
@@ -4914,17 +5227,6 @@ static const struct pinmux_bias_reg pinmux_bias_regs[] = {
{ /* sentinel */ },
};
-static bool pin_has_pud(unsigned int pin)
-{
- /* Some pins are pull-up only */
- switch (pin) {
- case RCAR_GP_PIN(6, 9): /* USB30_OVC */
- return false;
- }
-
- return true;
-}
-
static unsigned int r8a77990_pinmux_get_bias(struct sh_pfc *pfc,
unsigned int pin)
{
@@ -4937,7 +5239,7 @@ static unsigned int r8a77990_pinmux_get_bias(struct sh_pfc *pfc,
if (!(sh_pfc_read(pfc, reg->puen) & BIT(bit)))
return PIN_CONFIG_BIAS_DISABLE;
- else if (!pin_has_pud(pin) || (sh_pfc_read(pfc, reg->pud) & BIT(bit)))
+ else if (sh_pfc_read(pfc, reg->pud) & BIT(bit))
return PIN_CONFIG_BIAS_PULL_UP;
else
return PIN_CONFIG_BIAS_PULL_DOWN;
@@ -4958,13 +5260,11 @@ static void r8a77990_pinmux_set_bias(struct sh_pfc *pfc, unsigned int pin,
if (bias != PIN_CONFIG_BIAS_DISABLE)
enable |= BIT(bit);
- if (pin_has_pud(pin)) {
- updown = sh_pfc_read(pfc, reg->pud) & ~BIT(bit);
- if (bias == PIN_CONFIG_BIAS_PULL_UP)
- updown |= BIT(bit);
+ updown = sh_pfc_read(pfc, reg->pud) & ~BIT(bit);
+ if (bias == PIN_CONFIG_BIAS_PULL_UP)
+ updown |= BIT(bit);
- sh_pfc_write(pfc, reg->pud, updown);
- }
+ sh_pfc_write(pfc, reg->pud, updown);
sh_pfc_write(pfc, reg->puen, enable);
}
diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a77995.c b/drivers/pinctrl/sh-pfc/pfc-r8a77995.c
index 84d78db381e3..9e377e3b9cb3 100644
--- a/drivers/pinctrl/sh-pfc/pfc-r8a77995.c
+++ b/drivers/pinctrl/sh-pfc/pfc-r8a77995.c
@@ -381,6 +381,9 @@ FM(IP12_23_20) IP12_23_20 \
FM(IP12_27_24) IP12_27_24 \
FM(IP12_31_28) IP12_31_28 \
+/* The bit numbering in MOD_SEL fields is reversed */
+#define REV4(f0, f1, f2, f3) f0 f2 f1 f3
+
/* MOD_SEL0 */ /* 0 */ /* 1 */ /* 2 */ /* 3 */
#define MOD_SEL0_30 FM(SEL_MSIOF2_0) FM(SEL_MSIOF2_1)
#define MOD_SEL0_29 FM(SEL_I2C3_0) FM(SEL_I2C3_1)
@@ -388,10 +391,10 @@ FM(IP12_31_28) IP12_31_28 \
#define MOD_SEL0_27 FM(SEL_MSIOF3_0) FM(SEL_MSIOF3_1)
#define MOD_SEL0_26 FM(SEL_HSCIF3_0) FM(SEL_HSCIF3_1)
#define MOD_SEL0_25 FM(SEL_SCIF4_0) FM(SEL_SCIF4_1)
-#define MOD_SEL0_24_23 FM(SEL_PWM0_0) FM(SEL_PWM0_1) FM(SEL_PWM0_2) F_(0, 0)
-#define MOD_SEL0_22_21 FM(SEL_PWM1_0) FM(SEL_PWM1_1) FM(SEL_PWM1_2) F_(0, 0)
-#define MOD_SEL0_20_19 FM(SEL_PWM2_0) FM(SEL_PWM2_1) FM(SEL_PWM2_2) F_(0, 0)
-#define MOD_SEL0_18_17 FM(SEL_PWM3_0) FM(SEL_PWM3_1) FM(SEL_PWM3_2) F_(0, 0)
+#define MOD_SEL0_24_23 REV4(FM(SEL_PWM0_0), FM(SEL_PWM0_1), FM(SEL_PWM0_2), F_(0, 0))
+#define MOD_SEL0_22_21 REV4(FM(SEL_PWM1_0), FM(SEL_PWM1_1), FM(SEL_PWM1_2), F_(0, 0))
+#define MOD_SEL0_20_19 REV4(FM(SEL_PWM2_0), FM(SEL_PWM2_1), FM(SEL_PWM2_2), F_(0, 0))
+#define MOD_SEL0_18_17 REV4(FM(SEL_PWM3_0), FM(SEL_PWM3_1), FM(SEL_PWM3_2), F_(0, 0))
#define MOD_SEL0_15 FM(SEL_IRQ_0_0) FM(SEL_IRQ_0_1)
#define MOD_SEL0_14 FM(SEL_IRQ_1_0) FM(SEL_IRQ_1_1)
#define MOD_SEL0_13 FM(SEL_IRQ_2_0) FM(SEL_IRQ_2_1)
diff --git a/drivers/pinctrl/sh-pfc/pfc-sh73a0.c b/drivers/pinctrl/sh-pfc/pfc-sh73a0.c
index 085770e66895..ef3da8bf1d87 100644
--- a/drivers/pinctrl/sh-pfc/pfc-sh73a0.c
+++ b/drivers/pinctrl/sh-pfc/pfc-sh73a0.c
@@ -3354,7 +3354,8 @@ static const char * const fsic_groups[] = {
"fsic_sclk_out",
"fsic_data_in",
"fsic_data_out",
- "fsic_spdif",
+ "fsic_spdif_0",
+ "fsic_spdif_1",
};
static const char * const fsid_groups[] = {
diff --git a/drivers/pinctrl/sh-pfc/pinctrl.c b/drivers/pinctrl/sh-pfc/pinctrl.c
index 274d5ff87078..c97d2ba7677c 100644
--- a/drivers/pinctrl/sh-pfc/pinctrl.c
+++ b/drivers/pinctrl/sh-pfc/pinctrl.c
@@ -347,6 +347,8 @@ static int sh_pfc_func_set_mux(struct pinctrl_dev *pctldev, unsigned selector,
unsigned int i;
int ret = 0;
+ dev_dbg(pctldev->dev, "Configuring pin group %s\n", grp->name);
+
spin_lock_irqsave(&pfc->lock, flags);
for (i = 0; i < grp->nr_pins; ++i) {
diff --git a/drivers/pinctrl/sh-pfc/sh_pfc.h b/drivers/pinctrl/sh-pfc/sh_pfc.h
index 46d477ff5109..56016cb76769 100644
--- a/drivers/pinctrl/sh-pfc/sh_pfc.h
+++ b/drivers/pinctrl/sh-pfc/sh_pfc.h
@@ -126,7 +126,8 @@ struct pinmux_cfg_reg {
* one for each possible combination of the register field bit values.
*/
#define PINMUX_CFG_REG(name, r, r_width, f_width) \
- .reg = r, .reg_width = r_width, .field_width = f_width, \
+ .reg = r, .reg_width = r_width, \
+ .field_width = f_width + BUILD_BUG_ON_ZERO(r_width % f_width), \
.enum_ids = (const u16 [(r_width / f_width) * (1 << f_width)])
/*
diff --git a/drivers/pinctrl/sirf/pinctrl-atlas7.c b/drivers/pinctrl/sirf/pinctrl-atlas7.c
index 4ba171827428..8a0eee044264 100644
--- a/drivers/pinctrl/sirf/pinctrl-atlas7.c
+++ b/drivers/pinctrl/sirf/pinctrl-atlas7.c
@@ -6007,8 +6007,8 @@ static int atlas7_gpio_probe(struct platform_device *pdev)
}
/* retrieve gpio descriptor data */
- a7gc = devm_kzalloc(&pdev->dev, sizeof(*a7gc) +
- sizeof(struct atlas7_gpio_bank) * nbank, GFP_KERNEL);
+ a7gc = devm_kzalloc(&pdev->dev, struct_size(a7gc, banks, nbank),
+ GFP_KERNEL);
if (!a7gc)
return -ENOMEM;
diff --git a/drivers/pinctrl/sirf/pinctrl-sirf.c b/drivers/pinctrl/sirf/pinctrl-sirf.c
index 2e42d738b589..2b3bd1a41f21 100644
--- a/drivers/pinctrl/sirf/pinctrl-sirf.c
+++ b/drivers/pinctrl/sirf/pinctrl-sirf.c
@@ -782,7 +782,7 @@ static void sirfsoc_gpio_set_pulldown(struct sirfsoc_gpio_chip *sgpio,
static int sirfsoc_gpio_probe(struct device_node *np)
{
int i, err = 0;
- static struct sirfsoc_gpio_chip *sgpio;
+ struct sirfsoc_gpio_chip *sgpio;
struct sirfsoc_gpio_bank *bank;
void __iomem *regs;
struct platform_device *pdev;
diff --git a/drivers/pinctrl/stm32/pinctrl-stm32.c b/drivers/pinctrl/stm32/pinctrl-stm32.c
index 813eccbb9aaf..0b9ff5aa6bb5 100644
--- a/drivers/pinctrl/stm32/pinctrl-stm32.c
+++ b/drivers/pinctrl/stm32/pinctrl-stm32.c
@@ -414,7 +414,7 @@ static int stm32_pctrl_dt_subnode_to_map(struct pinctrl_dev *pctldev,
unsigned int num_configs;
bool has_config = 0;
unsigned reserve = 0;
- int num_pins, num_funcs, maps_per_pin, i, err;
+ int num_pins, num_funcs, maps_per_pin, i, err = 0;
pctl = pinctrl_dev_get_drvdata(pctldev);
@@ -441,41 +441,45 @@ static int stm32_pctrl_dt_subnode_to_map(struct pinctrl_dev *pctldev,
if (has_config && num_pins >= 1)
maps_per_pin++;
- if (!num_pins || !maps_per_pin)
- return -EINVAL;
+ if (!num_pins || !maps_per_pin) {
+ err = -EINVAL;
+ goto exit;
+ }
reserve = num_pins * maps_per_pin;
err = pinctrl_utils_reserve_map(pctldev, map,
reserved_maps, num_maps, reserve);
if (err)
- return err;
+ goto exit;
for (i = 0; i < num_pins; i++) {
err = of_property_read_u32_index(node, "pinmux",
i, &pinfunc);
if (err)
- return err;
+ goto exit;
pin = STM32_GET_PIN_NO(pinfunc);
func = STM32_GET_PIN_FUNC(pinfunc);
if (!stm32_pctrl_is_function_valid(pctl, pin, func)) {
dev_err(pctl->dev, "invalid function.\n");
- return -EINVAL;
+ err = -EINVAL;
+ goto exit;
}
grp = stm32_pctrl_find_group_by_pin(pctl, pin);
if (!grp) {
dev_err(pctl->dev, "unable to match pin %d to group\n",
pin);
- return -EINVAL;
+ err = -EINVAL;
+ goto exit;
}
err = stm32_pctrl_dt_node_to_map_func(pctl, pin, func, grp, map,
reserved_maps, num_maps);
if (err)
- return err;
+ goto exit;
if (has_config) {
err = pinctrl_utils_add_map_configs(pctldev, map,
@@ -483,11 +487,13 @@ static int stm32_pctrl_dt_subnode_to_map(struct pinctrl_dev *pctldev,
configs, num_configs,
PIN_MAP_TYPE_CONFIGS_GROUP);
if (err)
- return err;
+ goto exit;
}
}
- return 0;
+exit:
+ kfree(configs);
+ return err;
}
static int stm32_pctrl_dt_node_to_map(struct pinctrl_dev *pctldev,
@@ -577,8 +583,8 @@ static int stm32_pmx_get_func_groups(struct pinctrl_dev *pctldev,
return 0;
}
-static void stm32_pmx_set_mode(struct stm32_gpio_bank *bank,
- int pin, u32 mode, u32 alt)
+static int stm32_pmx_set_mode(struct stm32_gpio_bank *bank,
+ int pin, u32 mode, u32 alt)
{
struct stm32_pinctrl *pctl = dev_get_drvdata(bank->gpio_chip.parent);
u32 val;
@@ -614,6 +620,8 @@ static void stm32_pmx_set_mode(struct stm32_gpio_bank *bank,
unlock:
spin_unlock_irqrestore(&bank->lock, flags);
clk_disable(bank->clk);
+
+ return err;
}
void stm32_pmx_get_mode(struct stm32_gpio_bank *bank, int pin, u32 *mode,
@@ -670,9 +678,7 @@ static int stm32_pmx_set_mux(struct pinctrl_dev *pctldev,
mode = stm32_gpio_get_mode(function);
alt = stm32_gpio_get_alt(function);
- stm32_pmx_set_mode(bank, pin, mode, alt);
-
- return 0;
+ return stm32_pmx_set_mode(bank, pin, mode, alt);
}
static int stm32_pmx_gpio_set_direction(struct pinctrl_dev *pctldev,
@@ -682,9 +688,7 @@ static int stm32_pmx_gpio_set_direction(struct pinctrl_dev *pctldev,
struct stm32_gpio_bank *bank = gpiochip_get_data(range->gc);
int pin = stm32_gpio_pin(gpio);
- stm32_pmx_set_mode(bank, pin, !input, 0);
-
- return 0;
+ return stm32_pmx_set_mode(bank, pin, !input, 0);
}
static const struct pinmux_ops stm32_pmx_ops = {
@@ -698,8 +702,8 @@ static const struct pinmux_ops stm32_pmx_ops = {
/* Pinconf functions */
-static void stm32_pconf_set_driving(struct stm32_gpio_bank *bank,
- unsigned offset, u32 drive)
+static int stm32_pconf_set_driving(struct stm32_gpio_bank *bank,
+ unsigned offset, u32 drive)
{
struct stm32_pinctrl *pctl = dev_get_drvdata(bank->gpio_chip.parent);
unsigned long flags;
@@ -728,6 +732,8 @@ static void stm32_pconf_set_driving(struct stm32_gpio_bank *bank,
unlock:
spin_unlock_irqrestore(&bank->lock, flags);
clk_disable(bank->clk);
+
+ return err;
}
static u32 stm32_pconf_get_driving(struct stm32_gpio_bank *bank,
@@ -748,8 +754,8 @@ static u32 stm32_pconf_get_driving(struct stm32_gpio_bank *bank,
return (val >> offset);
}
-static void stm32_pconf_set_speed(struct stm32_gpio_bank *bank,
- unsigned offset, u32 speed)
+static int stm32_pconf_set_speed(struct stm32_gpio_bank *bank,
+ unsigned offset, u32 speed)
{
struct stm32_pinctrl *pctl = dev_get_drvdata(bank->gpio_chip.parent);
unsigned long flags;
@@ -778,6 +784,8 @@ static void stm32_pconf_set_speed(struct stm32_gpio_bank *bank,
unlock:
spin_unlock_irqrestore(&bank->lock, flags);
clk_disable(bank->clk);
+
+ return err;
}
static u32 stm32_pconf_get_speed(struct stm32_gpio_bank *bank,
@@ -798,8 +806,8 @@ static u32 stm32_pconf_get_speed(struct stm32_gpio_bank *bank,
return (val >> (offset * 2));
}
-static void stm32_pconf_set_bias(struct stm32_gpio_bank *bank,
- unsigned offset, u32 bias)
+static int stm32_pconf_set_bias(struct stm32_gpio_bank *bank,
+ unsigned offset, u32 bias)
{
struct stm32_pinctrl *pctl = dev_get_drvdata(bank->gpio_chip.parent);
unsigned long flags;
@@ -828,6 +836,8 @@ static void stm32_pconf_set_bias(struct stm32_gpio_bank *bank,
unlock:
spin_unlock_irqrestore(&bank->lock, flags);
clk_disable(bank->clk);
+
+ return err;
}
static u32 stm32_pconf_get_bias(struct stm32_gpio_bank *bank,
@@ -890,22 +900,22 @@ static int stm32_pconf_parse_conf(struct pinctrl_dev *pctldev,
switch (param) {
case PIN_CONFIG_DRIVE_PUSH_PULL:
- stm32_pconf_set_driving(bank, offset, 0);
+ ret = stm32_pconf_set_driving(bank, offset, 0);
break;
case PIN_CONFIG_DRIVE_OPEN_DRAIN:
- stm32_pconf_set_driving(bank, offset, 1);
+ ret = stm32_pconf_set_driving(bank, offset, 1);
break;
case PIN_CONFIG_SLEW_RATE:
- stm32_pconf_set_speed(bank, offset, arg);
+ ret = stm32_pconf_set_speed(bank, offset, arg);
break;
case PIN_CONFIG_BIAS_DISABLE:
- stm32_pconf_set_bias(bank, offset, 0);
+ ret = stm32_pconf_set_bias(bank, offset, 0);
break;
case PIN_CONFIG_BIAS_PULL_UP:
- stm32_pconf_set_bias(bank, offset, 1);
+ ret = stm32_pconf_set_bias(bank, offset, 1);
break;
case PIN_CONFIG_BIAS_PULL_DOWN:
- stm32_pconf_set_bias(bank, offset, 2);
+ ret = stm32_pconf_set_bias(bank, offset, 2);
break;
case PIN_CONFIG_OUTPUT:
__stm32_gpio_set(bank, offset, arg);
diff --git a/drivers/pinctrl/sunxi/pinctrl-sun9i-a80-r.c b/drivers/pinctrl/sunxi/pinctrl-sun9i-a80-r.c
index c63086c98335..e05dd9a5551d 100644
--- a/drivers/pinctrl/sunxi/pinctrl-sun9i-a80-r.c
+++ b/drivers/pinctrl/sunxi/pinctrl-sun9i-a80-r.c
@@ -153,6 +153,7 @@ static const struct sunxi_pinctrl_desc sun9i_a80_r_pinctrl_data = {
.pin_base = PL_BASE,
.irq_banks = 2,
.disable_strict_mode = true,
+ .has_io_bias_cfg = true,
};
static int sun9i_a80_r_pinctrl_probe(struct platform_device *pdev)
diff --git a/drivers/pinctrl/sunxi/pinctrl-sun9i-a80.c b/drivers/pinctrl/sunxi/pinctrl-sun9i-a80.c
index 5553c0eb0f41..da37d594a13d 100644
--- a/drivers/pinctrl/sunxi/pinctrl-sun9i-a80.c
+++ b/drivers/pinctrl/sunxi/pinctrl-sun9i-a80.c
@@ -722,6 +722,7 @@ static const struct sunxi_pinctrl_desc sun9i_a80_pinctrl_data = {
.npins = ARRAY_SIZE(sun9i_a80_pins),
.irq_banks = 5,
.disable_strict_mode = true,
+ .has_io_bias_cfg = true,
};
static int sun9i_a80_pinctrl_probe(struct platform_device *pdev)
diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
index 0e7fa69e93df..8dd25caea2cf 100644
--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c
+++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
@@ -603,6 +603,45 @@ static const struct pinconf_ops sunxi_pconf_ops = {
.pin_config_group_set = sunxi_pconf_group_set,
};
+static int sunxi_pinctrl_set_io_bias_cfg(struct sunxi_pinctrl *pctl,
+ unsigned pin,
+ struct regulator *supply)
+{
+ u32 val, reg;
+ int uV;
+
+ if (!pctl->desc->has_io_bias_cfg)
+ return 0;
+
+ uV = regulator_get_voltage(supply);
+ if (uV < 0)
+ return uV;
+
+ /* Might be dummy regulator with no voltage set */
+ if (uV == 0)
+ return 0;
+
+ /* Configured value must be equal or greater to actual voltage */
+ if (uV <= 1800000)
+ val = 0x0; /* 1.8V */
+ else if (uV <= 2500000)
+ val = 0x6; /* 2.5V */
+ else if (uV <= 2800000)
+ val = 0x9; /* 2.8V */
+ else if (uV <= 3000000)
+ val = 0xA; /* 3.0V */
+ else
+ val = 0xD; /* 3.3V */
+
+ pin -= pctl->desc->pin_base;
+
+ reg = readl(pctl->membase + sunxi_grp_config_reg(pin));
+ reg &= ~IO_BIAS_MASK;
+ writel(reg | val, pctl->membase + sunxi_grp_config_reg(pin));
+
+ return 0;
+}
+
static int sunxi_pmx_get_funcs_cnt(struct pinctrl_dev *pctldev)
{
struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
@@ -725,6 +764,8 @@ static int sunxi_pmx_request(struct pinctrl_dev *pctldev, unsigned offset)
goto out;
}
+ sunxi_pinctrl_set_io_bias_cfg(pctl, offset, reg);
+
s_reg->regulator = reg;
refcount_set(&s_reg->refcount, 1);
diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.h b/drivers/pinctrl/sunxi/pinctrl-sunxi.h
index 034c0317c8d6..ee15ab067b5f 100644
--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.h
+++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.h
@@ -79,6 +79,10 @@
#define IRQ_LEVEL_LOW 0x03
#define IRQ_EDGE_BOTH 0x04
+#define GRP_CFG_REG 0x300
+
+#define IO_BIAS_MASK GENMASK(3, 0)
+
#define SUN4I_FUNC_INPUT 0
#define SUN4I_FUNC_IRQ 6
@@ -113,6 +117,7 @@ struct sunxi_pinctrl_desc {
const unsigned int *irq_bank_map;
bool irq_read_needs_mux;
bool disable_strict_mode;
+ bool has_io_bias_cfg;
};
struct sunxi_pinctrl_function {
@@ -338,6 +343,13 @@ static inline u32 sunxi_irq_status_offset(u16 irq)
return irq_num * IRQ_STATUS_IRQ_BITS;
}
+static inline u32 sunxi_grp_config_reg(u16 pin)
+{
+ u8 bank = pin / PINS_PER_BANK;
+
+ return GRP_CFG_REG + bank * 0x4;
+}
+
int sunxi_pinctrl_init_with_variant(struct platform_device *pdev,
const struct sunxi_pinctrl_desc *desc,
unsigned long variant);
diff --git a/drivers/pinctrl/ti/pinctrl-ti-iodelay.c b/drivers/pinctrl/ti/pinctrl-ti-iodelay.c
index a4bc506a01a3..e5e7f1f22813 100644
--- a/drivers/pinctrl/ti/pinctrl-ti-iodelay.c
+++ b/drivers/pinctrl/ti/pinctrl-ti-iodelay.c
@@ -263,9 +263,9 @@ static int ti_iodelay_pinconf_set(struct ti_iodelay_device *iod,
reg_val |= reg->unlock_val << __ffs(reg->lock_mask);
r = regmap_update_bits(iod->regmap, cfg->offset, reg_mask, reg_val);
- dev_info(dev, "Set reg 0x%x Delay(a: %d g: %d), Elements(C=%d F=%d)0x%x\n",
- cfg->offset, cfg->a_delay, cfg->g_delay, c_elements,
- f_elements, reg_val);
+ dev_dbg(dev, "Set reg 0x%x Delay(a: %d g: %d), Elements(C=%d F=%d)0x%x\n",
+ cfg->offset, cfg->a_delay, cfg->g_delay, c_elements,
+ f_elements, reg_val);
return r;
}
@@ -923,7 +923,6 @@ static struct platform_driver ti_iodelay_driver = {
.probe = ti_iodelay_probe,
.remove = ti_iodelay_remove,
.driver = {
- .owner = THIS_MODULE,
.name = DRIVER_NAME,
.of_match_table = ti_iodelay_of_match,
},
diff --git a/drivers/platform/chrome/Kconfig b/drivers/platform/chrome/Kconfig
index 16b1615958aa..9186d81a51cc 100644
--- a/drivers/platform/chrome/Kconfig
+++ b/drivers/platform/chrome/Kconfig
@@ -49,9 +49,6 @@ config CHROMEOS_TBMC
To compile this driver as a module, choose M here: the
module will be called chromeos_tbmc.
-config CROS_EC_CTL
- tristate
-
config CROS_EC_I2C
tristate "ChromeOS Embedded Controller (I2C)"
depends on MFD_CROS_EC && I2C
@@ -111,4 +108,50 @@ config CROS_KBD_LED_BACKLIGHT
To compile this driver as a module, choose M here: the
module will be called cros_kbd_led_backlight.
+config CROS_EC_LIGHTBAR
+ tristate "Chromebook Pixel's lightbar support"
+ depends on MFD_CROS_EC_CHARDEV
+ default MFD_CROS_EC_CHARDEV
+ help
+ This option exposes the Chromebook Pixel's lightbar to
+ userspace.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cros_ec_lightbar.
+
+config CROS_EC_VBC
+ tristate "ChromeOS EC vboot context support"
+ depends on MFD_CROS_EC_CHARDEV && OF
+ default MFD_CROS_EC_CHARDEV
+ help
+ This option exposes the ChromeOS EC vboot context nvram to
+ userspace.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cros_ec_vbc.
+
+config CROS_EC_DEBUGFS
+ tristate "Export ChromeOS EC internals in DebugFS"
+ depends on MFD_CROS_EC_CHARDEV && DEBUG_FS
+ default MFD_CROS_EC_CHARDEV
+ help
+ This option exposes the ChromeOS EC device internals to
+ userspace.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cros_ec_debugfs.
+
+config CROS_EC_SYSFS
+ tristate "ChromeOS EC control and information through sysfs"
+ depends on MFD_CROS_EC_CHARDEV && SYSFS
+ default MFD_CROS_EC_CHARDEV
+ help
+ This option exposes some sysfs attributes to control and get
+ information from ChromeOS EC.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cros_ec_sysfs.
+
+source "drivers/platform/chrome/wilco_ec/Kconfig"
+
endif # CHROMEOS_PLATFORMS
diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile
index cd591bf872bb..1e2f0029b597 100644
--- a/drivers/platform/chrome/Makefile
+++ b/drivers/platform/chrome/Makefile
@@ -3,9 +3,6 @@
obj-$(CONFIG_CHROMEOS_LAPTOP) += chromeos_laptop.o
obj-$(CONFIG_CHROMEOS_PSTORE) += chromeos_pstore.o
obj-$(CONFIG_CHROMEOS_TBMC) += chromeos_tbmc.o
-cros_ec_ctl-objs := cros_ec_sysfs.o cros_ec_lightbar.o \
- cros_ec_vbc.o cros_ec_debugfs.o
-obj-$(CONFIG_CROS_EC_CTL) += cros_ec_ctl.o
obj-$(CONFIG_CROS_EC_I2C) += cros_ec_i2c.o
obj-$(CONFIG_CROS_EC_SPI) += cros_ec_spi.o
cros_ec_lpcs-objs := cros_ec_lpc.o cros_ec_lpc_reg.o
@@ -13,3 +10,9 @@ cros_ec_lpcs-$(CONFIG_CROS_EC_LPC_MEC) += cros_ec_lpc_mec.o
obj-$(CONFIG_CROS_EC_LPC) += cros_ec_lpcs.o
obj-$(CONFIG_CROS_EC_PROTO) += cros_ec_proto.o
obj-$(CONFIG_CROS_KBD_LED_BACKLIGHT) += cros_kbd_led_backlight.o
+obj-$(CONFIG_CROS_EC_LIGHTBAR) += cros_ec_lightbar.o
+obj-$(CONFIG_CROS_EC_VBC) += cros_ec_vbc.o
+obj-$(CONFIG_CROS_EC_DEBUGFS) += cros_ec_debugfs.o
+obj-$(CONFIG_CROS_EC_SYSFS) += cros_ec_sysfs.o
+
+obj-$(CONFIG_WILCO_EC) += wilco_ec/
diff --git a/drivers/platform/chrome/chromeos_pstore.c b/drivers/platform/chrome/chromeos_pstore.c
index b0693fdec8c6..d13770785fb5 100644
--- a/drivers/platform/chrome/chromeos_pstore.c
+++ b/drivers/platform/chrome/chromeos_pstore.c
@@ -1,12 +1,7 @@
-/*
- * chromeos_pstore.c - Driver to instantiate Chromebook ramoops device
- *
- * Copyright (C) 2013 Google, Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, version 2 of the License.
- */
+// SPDX-License-Identifier: GPL-2.0
+// Driver to instantiate Chromebook ramoops device.
+//
+// Copyright (C) 2013 Google, Inc.
#include <linux/acpi.h>
#include <linux/dmi.h>
@@ -138,5 +133,5 @@ static void __exit chromeos_pstore_exit(void)
module_init(chromeos_pstore_init);
module_exit(chromeos_pstore_exit);
-MODULE_DESCRIPTION("Chrome OS pstore module");
-MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("ChromeOS pstore module");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/platform/chrome/cros_ec_debugfs.c b/drivers/platform/chrome/cros_ec_debugfs.c
index c62ee8e610a0..71308766e891 100644
--- a/drivers/platform/chrome/cros_ec_debugfs.c
+++ b/drivers/platform/chrome/cros_ec_debugfs.c
@@ -1,21 +1,7 @@
-/*
- * cros_ec_debugfs - debug logs for Chrome OS EC
- *
- * Copyright 2015 Google, Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-License-Identifier: GPL-2.0+
+// Debug logs for the ChromeOS EC
+//
+// Copyright (C) 2015 Google, Inc.
#include <linux/circ_buf.h>
#include <linux/debugfs.h>
@@ -23,12 +9,16 @@
#include <linux/fs.h>
#include <linux/mfd/cros_ec.h>
#include <linux/mfd/cros_ec_commands.h>
+#include <linux/module.h>
#include <linux/mutex.h>
+#include <linux/platform_device.h>
#include <linux/poll.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/wait.h>
+#define DRV_NAME "cros-ec-debugfs"
+
#define LOG_SHIFT 14
#define LOG_SIZE (1 << LOG_SHIFT)
#define LOG_POLL_SEC 10
@@ -423,8 +413,9 @@ static int cros_ec_create_pdinfo(struct cros_ec_debugfs *debug_info)
return 0;
}
-int cros_ec_debugfs_init(struct cros_ec_dev *ec)
+static int cros_ec_debugfs_probe(struct platform_device *pd)
{
+ struct cros_ec_dev *ec = dev_get_drvdata(pd->dev.parent);
struct cros_ec_platform *ec_platform = dev_get_platdata(ec->dev);
const char *name = ec_platform->ec_name;
struct cros_ec_debugfs *debug_info;
@@ -449,44 +440,65 @@ int cros_ec_debugfs_init(struct cros_ec_dev *ec)
ret = cros_ec_create_pdinfo(debug_info);
if (ret)
- goto remove_debugfs;
+ goto remove_log;
ec->debug_info = debug_info;
+ dev_set_drvdata(&pd->dev, ec);
+
return 0;
+remove_log:
+ cros_ec_cleanup_console_log(debug_info);
remove_debugfs:
debugfs_remove_recursive(debug_info->dir);
return ret;
}
-EXPORT_SYMBOL(cros_ec_debugfs_init);
-void cros_ec_debugfs_remove(struct cros_ec_dev *ec)
+static int cros_ec_debugfs_remove(struct platform_device *pd)
{
- if (!ec->debug_info)
- return;
+ struct cros_ec_dev *ec = dev_get_drvdata(pd->dev.parent);
debugfs_remove_recursive(ec->debug_info->dir);
cros_ec_cleanup_console_log(ec->debug_info);
+
+ return 0;
}
-EXPORT_SYMBOL(cros_ec_debugfs_remove);
-void cros_ec_debugfs_suspend(struct cros_ec_dev *ec)
+static int __maybe_unused cros_ec_debugfs_suspend(struct device *dev)
{
- /*
- * cros_ec_debugfs_init() failures are non-fatal; it's also possible
- * that we initted things but decided that console log wasn't supported.
- * We'll use the same set of checks that cros_ec_debugfs_remove() +
- * cros_ec_cleanup_console_log() end up using to handle those cases.
- */
- if (ec->debug_info && ec->debug_info->log_buffer.buf)
+ struct cros_ec_dev *ec = dev_get_drvdata(dev);
+
+ if (ec->debug_info->log_buffer.buf)
cancel_delayed_work_sync(&ec->debug_info->log_poll_work);
+
+ return 0;
}
-EXPORT_SYMBOL(cros_ec_debugfs_suspend);
-void cros_ec_debugfs_resume(struct cros_ec_dev *ec)
+static int __maybe_unused cros_ec_debugfs_resume(struct device *dev)
{
- if (ec->debug_info && ec->debug_info->log_buffer.buf)
+ struct cros_ec_dev *ec = dev_get_drvdata(dev);
+
+ if (ec->debug_info->log_buffer.buf)
schedule_delayed_work(&ec->debug_info->log_poll_work, 0);
+
+ return 0;
}
-EXPORT_SYMBOL(cros_ec_debugfs_resume);
+
+static SIMPLE_DEV_PM_OPS(cros_ec_debugfs_pm_ops,
+ cros_ec_debugfs_suspend, cros_ec_debugfs_resume);
+
+static struct platform_driver cros_ec_debugfs_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .pm = &cros_ec_debugfs_pm_ops,
+ },
+ .probe = cros_ec_debugfs_probe,
+ .remove = cros_ec_debugfs_remove,
+};
+
+module_platform_driver(cros_ec_debugfs_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Debug logs for ChromeOS EC");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/drivers/platform/chrome/cros_ec_i2c.c b/drivers/platform/chrome/cros_ec_i2c.c
index ef9b4763356f..61d75395f86d 100644
--- a/drivers/platform/chrome/cros_ec_i2c.c
+++ b/drivers/platform/chrome/cros_ec_i2c.c
@@ -1,17 +1,7 @@
-/*
- * ChromeOS EC multi-function device (I2C)
- *
- * Copyright (C) 2012 Google, Inc
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
+// SPDX-License-Identifier: GPL-2.0
+// I2C interface for ChromeOS Embedded Controller
+//
+// Copyright (C) 2012 Google, Inc
#include <linux/acpi.h>
#include <linux/delay.h>
@@ -317,15 +307,6 @@ static int cros_ec_i2c_probe(struct i2c_client *client,
return 0;
}
-static int cros_ec_i2c_remove(struct i2c_client *client)
-{
- struct cros_ec_device *ec_dev = i2c_get_clientdata(client);
-
- cros_ec_remove(ec_dev);
-
- return 0;
-}
-
#ifdef CONFIG_PM_SLEEP
static int cros_ec_i2c_suspend(struct device *dev)
{
@@ -376,11 +357,10 @@ static struct i2c_driver cros_ec_driver = {
.pm = &cros_ec_i2c_pm_ops,
},
.probe = cros_ec_i2c_probe,
- .remove = cros_ec_i2c_remove,
.id_table = cros_ec_i2c_id,
};
module_i2c_driver(cros_ec_driver);
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("ChromeOS EC multi function device");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("I2C interface for ChromeOS Embedded Controller");
diff --git a/drivers/platform/chrome/cros_ec_lightbar.c b/drivers/platform/chrome/cros_ec_lightbar.c
index 68193bb53383..d30a6650b0b5 100644
--- a/drivers/platform/chrome/cros_ec_lightbar.c
+++ b/drivers/platform/chrome/cros_ec_lightbar.c
@@ -1,23 +1,7 @@
-/*
- * cros_ec_lightbar - expose the Chromebook Pixel lightbar to userspace
- *
- * Copyright (C) 2014 Google, Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#define pr_fmt(fmt) "cros_ec_lightbar: " fmt
+// SPDX-License-Identifier: GPL-2.0+
+// Expose the Chromebook Pixel lightbar to userspace
+//
+// Copyright (C) 2014 Google, Inc.
#include <linux/ctype.h>
#include <linux/delay.h>
@@ -33,6 +17,8 @@
#include <linux/uaccess.h>
#include <linux/slab.h>
+#define DRV_NAME "cros-ec-lightbar"
+
/* Rate-limit the lightbar interface to prevent DoS. */
static unsigned long lb_interval_jiffies = 50 * HZ / 1000;
@@ -41,7 +27,6 @@ static unsigned long lb_interval_jiffies = 50 * HZ / 1000;
* If this is true, we won't do anything during suspend/resume.
*/
static bool userspace_control;
-static struct cros_ec_dev *ec_with_lightbar;
static ssize_t interval_msec_show(struct device *dev,
struct device_attribute *attr, char *buf)
@@ -373,15 +358,12 @@ error:
return ret;
}
-int lb_manual_suspend_ctrl(struct cros_ec_dev *ec, uint8_t enable)
+static int lb_manual_suspend_ctrl(struct cros_ec_dev *ec, uint8_t enable)
{
struct ec_params_lightbar *param;
struct cros_ec_command *msg;
int ret;
- if (ec != ec_with_lightbar)
- return 0;
-
msg = alloc_lightbar_cmd_msg(ec);
if (!msg)
return -ENOMEM;
@@ -408,25 +390,6 @@ error:
return ret;
}
-EXPORT_SYMBOL(lb_manual_suspend_ctrl);
-
-int lb_suspend(struct cros_ec_dev *ec)
-{
- if (userspace_control || ec != ec_with_lightbar)
- return 0;
-
- return lb_send_empty_cmd(ec, LIGHTBAR_CMD_SUSPEND);
-}
-EXPORT_SYMBOL(lb_suspend);
-
-int lb_resume(struct cros_ec_dev *ec)
-{
- if (userspace_control || ec != ec_with_lightbar)
- return 0;
-
- return lb_send_empty_cmd(ec, LIGHTBAR_CMD_RESUME);
-}
-EXPORT_SYMBOL(lb_resume);
static ssize_t sequence_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
@@ -584,36 +547,91 @@ static struct attribute *__lb_cmds_attrs[] = {
NULL,
};
-bool ec_has_lightbar(struct cros_ec_dev *ec)
+struct attribute_group cros_ec_lightbar_attr_group = {
+ .name = "lightbar",
+ .attrs = __lb_cmds_attrs,
+};
+
+static int cros_ec_lightbar_probe(struct platform_device *pd)
+{
+ struct cros_ec_dev *ec_dev = dev_get_drvdata(pd->dev.parent);
+ struct cros_ec_platform *pdata = dev_get_platdata(ec_dev->dev);
+ struct device *dev = &pd->dev;
+ int ret;
+
+ /*
+ * Only instantiate the lightbar if the EC name is 'cros_ec'. Other EC
+ * devices like 'cros_pd' doesn't have a lightbar.
+ */
+ if (strcmp(pdata->ec_name, CROS_EC_DEV_NAME) != 0)
+ return -ENODEV;
+
+ /*
+ * Ask then for the lightbar version, if it's 0 then the 'cros_ec'
+ * doesn't have a lightbar.
+ */
+ if (!get_lightbar_version(ec_dev, NULL, NULL))
+ return -ENODEV;
+
+ /* Take control of the lightbar from the EC. */
+ lb_manual_suspend_ctrl(ec_dev, 1);
+
+ ret = sysfs_create_group(&ec_dev->class_dev.kobj,
+ &cros_ec_lightbar_attr_group);
+ if (ret < 0)
+ dev_err(dev, "failed to create %s attributes. err=%d\n",
+ cros_ec_lightbar_attr_group.name, ret);
+
+ return ret;
+}
+
+static int cros_ec_lightbar_remove(struct platform_device *pd)
{
- return !!get_lightbar_version(ec, NULL, NULL);
+ struct cros_ec_dev *ec_dev = dev_get_drvdata(pd->dev.parent);
+
+ sysfs_remove_group(&ec_dev->class_dev.kobj,
+ &cros_ec_lightbar_attr_group);
+
+ /* Let the EC take over the lightbar again. */
+ lb_manual_suspend_ctrl(ec_dev, 0);
+
+ return 0;
}
-static umode_t cros_ec_lightbar_attrs_are_visible(struct kobject *kobj,
- struct attribute *a, int n)
+static int __maybe_unused cros_ec_lightbar_resume(struct device *dev)
{
- struct device *dev = container_of(kobj, struct device, kobj);
- struct cros_ec_dev *ec = to_cros_ec_dev(dev);
- struct platform_device *pdev = to_platform_device(ec->dev);
- struct cros_ec_platform *pdata = pdev->dev.platform_data;
- int is_cros_ec;
+ struct cros_ec_dev *ec_dev = dev_get_drvdata(dev);
- is_cros_ec = strcmp(pdata->ec_name, CROS_EC_DEV_NAME);
+ if (userspace_control)
+ return 0;
+
+ return lb_send_empty_cmd(ec_dev, LIGHTBAR_CMD_RESUME);
+}
- if (is_cros_ec != 0)
+static int __maybe_unused cros_ec_lightbar_suspend(struct device *dev)
+{
+ struct cros_ec_dev *ec_dev = dev_get_drvdata(dev);
+
+ if (userspace_control)
return 0;
- /* Only instantiate this stuff if the EC has a lightbar */
- if (ec_has_lightbar(ec)) {
- ec_with_lightbar = ec;
- return a->mode;
- }
- return 0;
+ return lb_send_empty_cmd(ec_dev, LIGHTBAR_CMD_SUSPEND);
}
-struct attribute_group cros_ec_lightbar_attr_group = {
- .name = "lightbar",
- .attrs = __lb_cmds_attrs,
- .is_visible = cros_ec_lightbar_attrs_are_visible,
+static SIMPLE_DEV_PM_OPS(cros_ec_lightbar_pm_ops,
+ cros_ec_lightbar_suspend, cros_ec_lightbar_resume);
+
+static struct platform_driver cros_ec_lightbar_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .pm = &cros_ec_lightbar_pm_ops,
+ },
+ .probe = cros_ec_lightbar_probe,
+ .remove = cros_ec_lightbar_remove,
};
-EXPORT_SYMBOL(cros_ec_lightbar_attr_group);
+
+module_platform_driver(cros_ec_lightbar_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Expose the Chromebook Pixel's lightbar to userspace");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/drivers/platform/chrome/cros_ec_lpc.c b/drivers/platform/chrome/cros_ec_lpc.c
index e1b75775cd4a..c9c240fbe7c6 100644
--- a/drivers/platform/chrome/cros_ec_lpc.c
+++ b/drivers/platform/chrome/cros_ec_lpc.c
@@ -1,25 +1,15 @@
-/*
- * cros_ec_lpc - LPC access to the Chrome OS Embedded Controller
- *
- * Copyright (C) 2012-2015 Google, Inc
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * This driver uses the Chrome OS EC byte-level message-based protocol for
- * communicating the keyboard state (which keys are pressed) from a keyboard EC
- * to the AP over some bus (such as i2c, lpc, spi). The EC does debouncing,
- * but everything else (including deghosting) is done here. The main
- * motivation for this is to keep the EC firmware as simple as possible, since
- * it cannot be easily upgraded and EC flash/IRAM space is relatively
- * expensive.
- */
+// SPDX-License-Identifier: GPL-2.0
+// LPC interface for ChromeOS Embedded Controller
+//
+// Copyright (C) 2012-2015 Google, Inc
+//
+// This driver uses the ChromeOS EC byte-level message-based protocol for
+// communicating the keyboard state (which keys are pressed) from a keyboard EC
+// to the AP over some bus (such as i2c, lpc, spi). The EC does debouncing,
+// but everything else (including deghosting) is done here. The main
+// motivation for this is to keep the EC firmware as simple as possible, since
+// it cannot be easily upgraded and EC flash/IRAM space is relatively
+// expensive.
#include <linux/acpi.h>
#include <linux/dmi.h>
@@ -327,7 +317,6 @@ static int cros_ec_lpc_probe(struct platform_device *pdev)
static int cros_ec_lpc_remove(struct platform_device *pdev)
{
- struct cros_ec_device *ec_dev;
struct acpi_device *adev;
adev = ACPI_COMPANION(&pdev->dev);
@@ -335,9 +324,6 @@ static int cros_ec_lpc_remove(struct platform_device *pdev)
acpi_remove_notify_handler(adev->handle, ACPI_ALL_NOTIFY,
cros_ec_lpc_acpi_notify);
- ec_dev = platform_get_drvdata(pdev);
- cros_ec_remove(ec_dev);
-
return 0;
}
diff --git a/drivers/platform/chrome/cros_ec_lpc_mec.c b/drivers/platform/chrome/cros_ec_lpc_mec.c
index c4edfa83e493..d8890bafb55d 100644
--- a/drivers/platform/chrome/cros_ec_lpc_mec.c
+++ b/drivers/platform/chrome/cros_ec_lpc_mec.c
@@ -1,29 +1,10 @@
-/*
- * cros_ec_lpc_mec - LPC variant I/O for Microchip EC
- *
- * Copyright (C) 2016 Google, Inc
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * This driver uses the Chrome OS EC byte-level message-based protocol for
- * communicating the keyboard state (which keys are pressed) from a keyboard EC
- * to the AP over some bus (such as i2c, lpc, spi). The EC does debouncing,
- * but everything else (including deghosting) is done here. The main
- * motivation for this is to keep the EC firmware as simple as possible, since
- * it cannot be easily upgraded and EC flash/IRAM space is relatively
- * expensive.
- */
+// SPDX-License-Identifier: GPL-2.0
+// LPC variant I/O for Microchip EC
+//
+// Copyright (C) 2016 Google, Inc
#include <linux/delay.h>
#include <linux/io.h>
-#include <linux/mfd/cros_ec_commands.h>
#include <linux/mutex.h>
#include <linux/types.h>
@@ -34,6 +15,7 @@
* EC mutex because memmap data may be accessed without it being held.
*/
static struct mutex io_mutex;
+static u16 mec_emi_base, mec_emi_end;
/*
* cros_ec_lpc_mec_emi_write_address
@@ -46,10 +28,37 @@ static struct mutex io_mutex;
static void cros_ec_lpc_mec_emi_write_address(u16 addr,
enum cros_ec_lpc_mec_emi_access_mode access_type)
{
- /* Address relative to start of EMI range */
- addr -= MEC_EMI_RANGE_START;
- outb((addr & 0xfc) | access_type, MEC_EMI_EC_ADDRESS_B0);
- outb((addr >> 8) & 0x7f, MEC_EMI_EC_ADDRESS_B1);
+ outb((addr & 0xfc) | access_type, MEC_EMI_EC_ADDRESS_B0(mec_emi_base));
+ outb((addr >> 8) & 0x7f, MEC_EMI_EC_ADDRESS_B1(mec_emi_base));
+}
+
+/**
+ * cros_ec_lpc_mec_in_range() - Determine if addresses are in MEC EMI range.
+ *
+ * @offset: Address offset
+ * @length: Number of bytes to check
+ *
+ * Return: 1 if in range, 0 if not, and -EINVAL on failure
+ * such as the mec range not being initialized
+ */
+int cros_ec_lpc_mec_in_range(unsigned int offset, unsigned int length)
+{
+ if (length == 0)
+ return -EINVAL;
+
+ if (WARN_ON(mec_emi_base == 0 || mec_emi_end == 0))
+ return -EINVAL;
+
+ if (offset >= mec_emi_base && offset < mec_emi_end) {
+ if (WARN_ON(offset + length - 1 >= mec_emi_end))
+ return -EINVAL;
+ return 1;
+ }
+
+ if (WARN_ON(offset + length > mec_emi_base && offset < mec_emi_end))
+ return -EINVAL;
+
+ return 0;
}
/*
@@ -71,6 +80,11 @@ u8 cros_ec_lpc_io_bytes_mec(enum cros_ec_lpc_mec_io_type io_type,
u8 sum = 0;
enum cros_ec_lpc_mec_emi_access_mode access, new_access;
+ /* Return checksum of 0 if window is not initialized */
+ WARN_ON(mec_emi_base == 0 || mec_emi_end == 0);
+ if (mec_emi_base == 0 || mec_emi_end == 0)
+ return 0;
+
/*
* Long access cannot be used on misaligned data since reading B0 loads
* the data register and writing B3 flushes.
@@ -86,9 +100,9 @@ u8 cros_ec_lpc_io_bytes_mec(enum cros_ec_lpc_mec_io_type io_type,
cros_ec_lpc_mec_emi_write_address(offset, access);
/* Skip bytes in case of misaligned offset */
- io_addr = MEC_EMI_EC_DATA_B0 + (offset & 0x3);
+ io_addr = MEC_EMI_EC_DATA_B0(mec_emi_base) + (offset & 0x3);
while (i < length) {
- while (io_addr <= MEC_EMI_EC_DATA_B3) {
+ while (io_addr <= MEC_EMI_EC_DATA_B3(mec_emi_base)) {
if (io_type == MEC_IO_READ)
buf[i] = inb(io_addr++);
else
@@ -118,7 +132,7 @@ u8 cros_ec_lpc_io_bytes_mec(enum cros_ec_lpc_mec_io_type io_type,
}
/* Access [B0, B3] on each loop pass */
- io_addr = MEC_EMI_EC_DATA_B0;
+ io_addr = MEC_EMI_EC_DATA_B0(mec_emi_base);
}
done:
@@ -128,9 +142,11 @@ done:
}
EXPORT_SYMBOL(cros_ec_lpc_io_bytes_mec);
-void cros_ec_lpc_mec_init(void)
+void cros_ec_lpc_mec_init(unsigned int base, unsigned int end)
{
mutex_init(&io_mutex);
+ mec_emi_base = base;
+ mec_emi_end = end;
}
EXPORT_SYMBOL(cros_ec_lpc_mec_init);
diff --git a/drivers/platform/chrome/cros_ec_lpc_mec.h b/drivers/platform/chrome/cros_ec_lpc_mec.h
index 105068c0e919..aa1018f6b0f2 100644
--- a/drivers/platform/chrome/cros_ec_lpc_mec.h
+++ b/drivers/platform/chrome/cros_ec_lpc_mec.h
@@ -1,31 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
- * cros_ec_lpc_mec - LPC variant I/O for Microchip EC
+ * LPC variant I/O for Microchip EC
*
* Copyright (C) 2016 Google, Inc
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * This driver uses the Chrome OS EC byte-level message-based protocol for
- * communicating the keyboard state (which keys are pressed) from a keyboard EC
- * to the AP over some bus (such as i2c, lpc, spi). The EC does debouncing,
- * but everything else (including deghosting) is done here. The main
- * motivation for this is to keep the EC firmware as simple as possible, since
- * it cannot be easily upgraded and EC flash/IRAM space is relatively
- * expensive.
*/
#ifndef __CROS_EC_LPC_MEC_H
#define __CROS_EC_LPC_MEC_H
-#include <linux/mfd/cros_ec_commands.h>
-
enum cros_ec_lpc_mec_emi_access_mode {
/* 8-bit access */
ACCESS_TYPE_BYTE = 0x0,
@@ -45,27 +27,23 @@ enum cros_ec_lpc_mec_io_type {
MEC_IO_WRITE,
};
-/* Access IO ranges 0x800 thru 0x9ff using EMI interface instead of LPC */
-#define MEC_EMI_RANGE_START EC_HOST_CMD_REGION0
-#define MEC_EMI_RANGE_END (EC_LPC_ADDR_MEMMAP + EC_MEMMAP_SIZE)
-
/* EMI registers are relative to base */
-#define MEC_EMI_BASE 0x800
-#define MEC_EMI_HOST_TO_EC (MEC_EMI_BASE + 0)
-#define MEC_EMI_EC_TO_HOST (MEC_EMI_BASE + 1)
-#define MEC_EMI_EC_ADDRESS_B0 (MEC_EMI_BASE + 2)
-#define MEC_EMI_EC_ADDRESS_B1 (MEC_EMI_BASE + 3)
-#define MEC_EMI_EC_DATA_B0 (MEC_EMI_BASE + 4)
-#define MEC_EMI_EC_DATA_B1 (MEC_EMI_BASE + 5)
-#define MEC_EMI_EC_DATA_B2 (MEC_EMI_BASE + 6)
-#define MEC_EMI_EC_DATA_B3 (MEC_EMI_BASE + 7)
+#define MEC_EMI_HOST_TO_EC(MEC_EMI_BASE) ((MEC_EMI_BASE) + 0)
+#define MEC_EMI_EC_TO_HOST(MEC_EMI_BASE) ((MEC_EMI_BASE) + 1)
+#define MEC_EMI_EC_ADDRESS_B0(MEC_EMI_BASE) ((MEC_EMI_BASE) + 2)
+#define MEC_EMI_EC_ADDRESS_B1(MEC_EMI_BASE) ((MEC_EMI_BASE) + 3)
+#define MEC_EMI_EC_DATA_B0(MEC_EMI_BASE) ((MEC_EMI_BASE) + 4)
+#define MEC_EMI_EC_DATA_B1(MEC_EMI_BASE) ((MEC_EMI_BASE) + 5)
+#define MEC_EMI_EC_DATA_B2(MEC_EMI_BASE) ((MEC_EMI_BASE) + 6)
+#define MEC_EMI_EC_DATA_B3(MEC_EMI_BASE) ((MEC_EMI_BASE) + 7)
-/*
- * cros_ec_lpc_mec_init
+/**
+ * cros_ec_lpc_mec_init() - Initialize MEC I/O.
*
- * Initialize MEC I/O.
+ * @base: MEC EMI Base address
+ * @end: MEC EMI End address
*/
-void cros_ec_lpc_mec_init(void);
+void cros_ec_lpc_mec_init(unsigned int base, unsigned int end);
/*
* cros_ec_lpc_mec_destroy
@@ -75,6 +53,17 @@ void cros_ec_lpc_mec_init(void);
void cros_ec_lpc_mec_destroy(void);
/**
+ * cros_ec_lpc_mec_in_range() - Determine if addresses are in MEC EMI range.
+ *
+ * @offset: Address offset
+ * @length: Number of bytes to check
+ *
+ * Return: 1 if in range, 0 if not, and -EINVAL on failure
+ * such as the mec range not being initialized
+ */
+int cros_ec_lpc_mec_in_range(unsigned int offset, unsigned int length);
+
+/**
* cros_ec_lpc_io_bytes_mec - Read / write bytes to MEC EMI port
*
* @io_type: MEC_IO_READ or MEC_IO_WRITE, depending on request
diff --git a/drivers/platform/chrome/cros_ec_lpc_reg.c b/drivers/platform/chrome/cros_ec_lpc_reg.c
index fc23d535c404..0f5cd0ac8b49 100644
--- a/drivers/platform/chrome/cros_ec_lpc_reg.c
+++ b/drivers/platform/chrome/cros_ec_lpc_reg.c
@@ -1,25 +1,7 @@
-/*
- * cros_ec_lpc_reg - LPC access to the Chrome OS Embedded Controller
- *
- * Copyright (C) 2016 Google, Inc
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * This driver uses the Chrome OS EC byte-level message-based protocol for
- * communicating the keyboard state (which keys are pressed) from a keyboard EC
- * to the AP over some bus (such as i2c, lpc, spi). The EC does debouncing,
- * but everything else (including deghosting) is done here. The main
- * motivation for this is to keep the EC firmware as simple as possible, since
- * it cannot be easily upgraded and EC flash/IRAM space is relatively
- * expensive.
- */
+// SPDX-License-Identifier: GPL-2.0
+// LPC interface for ChromeOS Embedded Controller
+//
+// Copyright (C) 2016 Google, Inc
#include <linux/io.h>
#include <linux/mfd/cros_ec.h>
@@ -59,51 +41,36 @@ static u8 lpc_write_bytes(unsigned int offset, unsigned int length, u8 *msg)
u8 cros_ec_lpc_read_bytes(unsigned int offset, unsigned int length, u8 *dest)
{
- if (length == 0)
- return 0;
-
- /* Access desired range through EMI interface */
- if (offset >= MEC_EMI_RANGE_START && offset <= MEC_EMI_RANGE_END) {
- /* Ensure we don't straddle EMI region */
- if (WARN_ON(offset + length - 1 > MEC_EMI_RANGE_END))
- return 0;
+ int in_range = cros_ec_lpc_mec_in_range(offset, length);
- return cros_ec_lpc_io_bytes_mec(MEC_IO_READ, offset, length,
- dest);
- }
-
- if (WARN_ON(offset + length > MEC_EMI_RANGE_START &&
- offset < MEC_EMI_RANGE_START))
+ if (in_range < 0)
return 0;
- return lpc_read_bytes(offset, length, dest);
+ return in_range ?
+ cros_ec_lpc_io_bytes_mec(MEC_IO_READ,
+ offset - EC_HOST_CMD_REGION0,
+ length, dest) :
+ lpc_read_bytes(offset, length, dest);
}
u8 cros_ec_lpc_write_bytes(unsigned int offset, unsigned int length, u8 *msg)
{
- if (length == 0)
- return 0;
-
- /* Access desired range through EMI interface */
- if (offset >= MEC_EMI_RANGE_START && offset <= MEC_EMI_RANGE_END) {
- /* Ensure we don't straddle EMI region */
- if (WARN_ON(offset + length - 1 > MEC_EMI_RANGE_END))
- return 0;
+ int in_range = cros_ec_lpc_mec_in_range(offset, length);
- return cros_ec_lpc_io_bytes_mec(MEC_IO_WRITE, offset, length,
- msg);
- }
-
- if (WARN_ON(offset + length > MEC_EMI_RANGE_START &&
- offset < MEC_EMI_RANGE_START))
+ if (in_range < 0)
return 0;
- return lpc_write_bytes(offset, length, msg);
+ return in_range ?
+ cros_ec_lpc_io_bytes_mec(MEC_IO_WRITE,
+ offset - EC_HOST_CMD_REGION0,
+ length, msg) :
+ lpc_write_bytes(offset, length, msg);
}
void cros_ec_lpc_reg_init(void)
{
- cros_ec_lpc_mec_init();
+ cros_ec_lpc_mec_init(EC_HOST_CMD_REGION0,
+ EC_LPC_ADDR_MEMMAP + EC_MEMMAP_SIZE);
}
void cros_ec_lpc_reg_destroy(void)
diff --git a/drivers/platform/chrome/cros_ec_lpc_reg.h b/drivers/platform/chrome/cros_ec_lpc_reg.h
index 1c12c38b306a..416fd2572182 100644
--- a/drivers/platform/chrome/cros_ec_lpc_reg.h
+++ b/drivers/platform/chrome/cros_ec_lpc_reg.h
@@ -1,24 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
- * cros_ec_lpc_reg - LPC access to the Chrome OS Embedded Controller
+ * LPC interface for ChromeOS Embedded Controller
*
* Copyright (C) 2016 Google, Inc
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * This driver uses the Chrome OS EC byte-level message-based protocol for
- * communicating the keyboard state (which keys are pressed) from a keyboard EC
- * to the AP over some bus (such as i2c, lpc, spi). The EC does debouncing,
- * but everything else (including deghosting) is done here. The main
- * motivation for this is to keep the EC firmware as simple as possible, since
- * it cannot be easily upgraded and EC flash/IRAM space is relatively
- * expensive.
*/
#ifndef __CROS_EC_LPC_REG_H
diff --git a/drivers/platform/chrome/cros_ec_proto.c b/drivers/platform/chrome/cros_ec_proto.c
index cc7baf0ecb3c..97a068dff192 100644
--- a/drivers/platform/chrome/cros_ec_proto.c
+++ b/drivers/platform/chrome/cros_ec_proto.c
@@ -1,18 +1,7 @@
-/*
- * ChromeOS EC communication protocol helper functions
- *
- * Copyright (C) 2015 Google, Inc
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
+// SPDX-License-Identifier: GPL-2.0
+// ChromeOS EC communication protocol helper functions
+//
+// Copyright (C) 2015 Google, Inc
#include <linux/mfd/cros_ec.h>
#include <linux/delay.h>
diff --git a/drivers/platform/chrome/cros_ec_spi.c b/drivers/platform/chrome/cros_ec_spi.c
index 2060d1483043..ffc38f9d4829 100644
--- a/drivers/platform/chrome/cros_ec_spi.c
+++ b/drivers/platform/chrome/cros_ec_spi.c
@@ -1,17 +1,7 @@
-/*
- * ChromeOS EC multi-function device (SPI)
- *
- * Copyright (C) 2012 Google, Inc
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
+// SPDX-License-Identifier: GPL-2.0
+// SPI interface for ChromeOS Embedded Controller
+//
+// Copyright (C) 2012 Google, Inc
#include <linux/delay.h>
#include <linux/kernel.h>
@@ -685,16 +675,6 @@ static int cros_ec_spi_probe(struct spi_device *spi)
return 0;
}
-static int cros_ec_spi_remove(struct spi_device *spi)
-{
- struct cros_ec_device *ec_dev;
-
- ec_dev = spi_get_drvdata(spi);
- cros_ec_remove(ec_dev);
-
- return 0;
-}
-
#ifdef CONFIG_PM_SLEEP
static int cros_ec_spi_suspend(struct device *dev)
{
@@ -733,11 +713,10 @@ static struct spi_driver cros_ec_driver_spi = {
.pm = &cros_ec_spi_pm_ops,
},
.probe = cros_ec_spi_probe,
- .remove = cros_ec_spi_remove,
.id_table = cros_ec_spi_id,
};
module_spi_driver(cros_ec_driver_spi);
MODULE_LICENSE("GPL v2");
-MODULE_DESCRIPTION("ChromeOS EC multi function device (SPI)");
+MODULE_DESCRIPTION("SPI interface for ChromeOS Embedded Controller");
diff --git a/drivers/platform/chrome/cros_ec_sysfs.c b/drivers/platform/chrome/cros_ec_sysfs.c
index f34a50121064..fe0b7614ae1b 100644
--- a/drivers/platform/chrome/cros_ec_sysfs.c
+++ b/drivers/platform/chrome/cros_ec_sysfs.c
@@ -1,23 +1,7 @@
-/*
- * cros_ec_sysfs - expose the Chrome OS EC through sysfs
- *
- * Copyright (C) 2014 Google, Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#define pr_fmt(fmt) "cros_ec_sysfs: " fmt
+// SPDX-License-Identifier: GPL-2.0+
+// Expose the ChromeOS EC through sysfs
+//
+// Copyright (C) 2014 Google, Inc.
#include <linux/ctype.h>
#include <linux/delay.h>
@@ -34,6 +18,8 @@
#include <linux/types.h>
#include <linux/uaccess.h>
+#define DRV_NAME "cros-ec-sysfs"
+
/* Accessor functions */
static ssize_t reboot_show(struct device *dev,
@@ -353,7 +339,39 @@ struct attribute_group cros_ec_attr_group = {
.attrs = __ec_attrs,
.is_visible = cros_ec_ctrl_visible,
};
-EXPORT_SYMBOL(cros_ec_attr_group);
+
+static int cros_ec_sysfs_probe(struct platform_device *pd)
+{
+ struct cros_ec_dev *ec_dev = dev_get_drvdata(pd->dev.parent);
+ struct device *dev = &pd->dev;
+ int ret;
+
+ ret = sysfs_create_group(&ec_dev->class_dev.kobj, &cros_ec_attr_group);
+ if (ret < 0)
+ dev_err(dev, "failed to create attributes. err=%d\n", ret);
+
+ return ret;
+}
+
+static int cros_ec_sysfs_remove(struct platform_device *pd)
+{
+ struct cros_ec_dev *ec_dev = dev_get_drvdata(pd->dev.parent);
+
+ sysfs_remove_group(&ec_dev->class_dev.kobj, &cros_ec_attr_group);
+
+ return 0;
+}
+
+static struct platform_driver cros_ec_sysfs_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ },
+ .probe = cros_ec_sysfs_probe,
+ .remove = cros_ec_sysfs_remove,
+};
+
+module_platform_driver(cros_ec_sysfs_driver);
MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("ChromeOS EC control driver");
+MODULE_DESCRIPTION("Expose the ChromeOS EC through sysfs");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/drivers/platform/chrome/cros_ec_vbc.c b/drivers/platform/chrome/cros_ec_vbc.c
index 5356f26bc022..8392a1ec33a7 100644
--- a/drivers/platform/chrome/cros_ec_vbc.c
+++ b/drivers/platform/chrome/cros_ec_vbc.c
@@ -1,29 +1,18 @@
-/*
- * cros_ec_vbc - Expose the vboot context nvram to userspace
- *
- * Copyright (C) 2015 Collabora Ltd.
- *
- * based on vendor driver,
- *
- * Copyright (C) 2012 The Chromium OS Authors
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
+// SPDX-License-Identifier: GPL-2.0+
+// Expose the vboot context nvram to userspace
+//
+// Copyright (C) 2012 Google, Inc.
+// Copyright (C) 2015 Collabora Ltd.
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/mfd/cros_ec.h>
#include <linux/mfd/cros_ec_commands.h>
+#include <linux/module.h>
#include <linux/slab.h>
+#define DRV_NAME "cros-ec-vbc"
+
static ssize_t vboot_context_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *att, char *buf,
loff_t pos, size_t count)
@@ -105,21 +94,6 @@ static ssize_t vboot_context_write(struct file *filp, struct kobject *kobj,
return data_sz;
}
-static umode_t cros_ec_vbc_is_visible(struct kobject *kobj,
- struct bin_attribute *a, int n)
-{
- struct device *dev = container_of(kobj, struct device, kobj);
- struct cros_ec_dev *ec = to_cros_ec_dev(dev);
- struct device_node *np = ec->ec_dev->dev->of_node;
-
- if (IS_ENABLED(CONFIG_OF) && np) {
- if (of_property_read_bool(np, "google,has-vbc-nvram"))
- return a->attr.mode;
- }
-
- return 0;
-}
-
static BIN_ATTR_RW(vboot_context, 16);
static struct bin_attribute *cros_ec_vbc_bin_attrs[] = {
@@ -130,6 +104,43 @@ static struct bin_attribute *cros_ec_vbc_bin_attrs[] = {
struct attribute_group cros_ec_vbc_attr_group = {
.name = "vbc",
.bin_attrs = cros_ec_vbc_bin_attrs,
- .is_bin_visible = cros_ec_vbc_is_visible,
};
-EXPORT_SYMBOL(cros_ec_vbc_attr_group);
+
+static int cros_ec_vbc_probe(struct platform_device *pd)
+{
+ struct cros_ec_dev *ec_dev = dev_get_drvdata(pd->dev.parent);
+ struct device *dev = &pd->dev;
+ int ret;
+
+ ret = sysfs_create_group(&ec_dev->class_dev.kobj,
+ &cros_ec_vbc_attr_group);
+ if (ret < 0)
+ dev_err(dev, "failed to create %s attributes. err=%d\n",
+ cros_ec_vbc_attr_group.name, ret);
+
+ return ret;
+}
+
+static int cros_ec_vbc_remove(struct platform_device *pd)
+{
+ struct cros_ec_dev *ec_dev = dev_get_drvdata(pd->dev.parent);
+
+ sysfs_remove_group(&ec_dev->class_dev.kobj,
+ &cros_ec_vbc_attr_group);
+
+ return 0;
+}
+
+static struct platform_driver cros_ec_vbc_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ },
+ .probe = cros_ec_vbc_probe,
+ .remove = cros_ec_vbc_remove,
+};
+
+module_platform_driver(cros_ec_vbc_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Expose the vboot context nvram to userspace");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/drivers/platform/chrome/cros_kbd_led_backlight.c b/drivers/platform/chrome/cros_kbd_led_backlight.c
index ca3e4da852b4..aa409f0201fb 100644
--- a/drivers/platform/chrome/cros_kbd_led_backlight.c
+++ b/drivers/platform/chrome/cros_kbd_led_backlight.c
@@ -1,18 +1,7 @@
-/*
- * Keyboard backlight LED driver for Chrome OS.
- *
- * Copyright (C) 2012 Google, Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
+// SPDX-License-Identifier: GPL-2.0+
+// Keyboard backlight LED driver for ChromeOS
+//
+// Copyright (C) 2012 Google, Inc.
#include <linux/acpi.h>
#include <linux/leds.h>
diff --git a/drivers/platform/chrome/wilco_ec/Kconfig b/drivers/platform/chrome/wilco_ec/Kconfig
new file mode 100644
index 000000000000..e09e4cebe9b4
--- /dev/null
+++ b/drivers/platform/chrome/wilco_ec/Kconfig
@@ -0,0 +1,20 @@
+config WILCO_EC
+ tristate "ChromeOS Wilco Embedded Controller"
+ depends on ACPI && X86 && CROS_EC_LPC && CROS_EC_LPC_MEC
+ help
+ If you say Y here, you get support for talking to the ChromeOS
+ Wilco EC over an eSPI bus. This uses a simple byte-level protocol
+ with a checksum.
+
+ To compile this driver as a module, choose M here: the
+ module will be called wilco_ec.
+
+config WILCO_EC_DEBUGFS
+ tristate "Enable raw access to EC via debugfs"
+ depends on WILCO_EC
+ help
+ If you say Y here, you get support for sending raw commands to
+ the Wilco EC via debugfs. These commands do not do any byte
+ manipulation and allow for testing arbitrary commands. This
+ interface is intended for debug only and will not be present
+ on production devices.
diff --git a/drivers/platform/chrome/wilco_ec/Makefile b/drivers/platform/chrome/wilco_ec/Makefile
new file mode 100644
index 000000000000..063e7fb4ea17
--- /dev/null
+++ b/drivers/platform/chrome/wilco_ec/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0
+
+wilco_ec-objs := core.o mailbox.o
+obj-$(CONFIG_WILCO_EC) += wilco_ec.o
+wilco_ec_debugfs-objs := debugfs.o
+obj-$(CONFIG_WILCO_EC_DEBUGFS) += wilco_ec_debugfs.o
diff --git a/drivers/platform/chrome/wilco_ec/core.c b/drivers/platform/chrome/wilco_ec/core.c
new file mode 100644
index 000000000000..05e1e2be1c91
--- /dev/null
+++ b/drivers/platform/chrome/wilco_ec/core.c
@@ -0,0 +1,136 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Core driver for Wilco Embedded Controller
+ *
+ * Copyright 2018 Google LLC
+ *
+ * This is the entry point for the drivers that control the Wilco EC.
+ * This driver is responsible for several tasks:
+ * - Initialize the register interface that is used by wilco_ec_mailbox()
+ * - Create a platform device which is picked up by the debugfs driver
+ * - Create a platform device which is picked up by the RTC driver
+ */
+
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/platform_data/wilco-ec.h>
+#include <linux/platform_device.h>
+
+#include "../cros_ec_lpc_mec.h"
+
+#define DRV_NAME "wilco-ec"
+
+static struct resource *wilco_get_resource(struct platform_device *pdev,
+ int index)
+{
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+
+ res = platform_get_resource(pdev, IORESOURCE_IO, index);
+ if (!res) {
+ dev_dbg(dev, "Couldn't find IO resource %d\n", index);
+ return res;
+ }
+
+ return devm_request_region(dev, res->start, resource_size(res),
+ dev_name(dev));
+}
+
+static int wilco_ec_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct wilco_ec_device *ec;
+ int ret;
+
+ ec = devm_kzalloc(dev, sizeof(*ec), GFP_KERNEL);
+ if (!ec)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, ec);
+ ec->dev = dev;
+ mutex_init(&ec->mailbox_lock);
+
+ /* Largest data buffer size requirement is extended data response */
+ ec->data_size = sizeof(struct wilco_ec_response) +
+ EC_MAILBOX_DATA_SIZE_EXTENDED;
+ ec->data_buffer = devm_kzalloc(dev, ec->data_size, GFP_KERNEL);
+ if (!ec->data_buffer)
+ return -ENOMEM;
+
+ /* Prepare access to IO regions provided by ACPI */
+ ec->io_data = wilco_get_resource(pdev, 0); /* Host Data */
+ ec->io_command = wilco_get_resource(pdev, 1); /* Host Command */
+ ec->io_packet = wilco_get_resource(pdev, 2); /* MEC EMI */
+ if (!ec->io_data || !ec->io_command || !ec->io_packet)
+ return -ENODEV;
+
+ /* Initialize cros_ec register interface for communication */
+ cros_ec_lpc_mec_init(ec->io_packet->start,
+ ec->io_packet->start + EC_MAILBOX_DATA_SIZE);
+
+ /*
+ * Register a child device that will be found by the debugfs driver.
+ * Ignore failure.
+ */
+ ec->debugfs_pdev = platform_device_register_data(dev,
+ "wilco-ec-debugfs",
+ PLATFORM_DEVID_AUTO,
+ NULL, 0);
+
+ /* Register a child device that will be found by the RTC driver. */
+ ec->rtc_pdev = platform_device_register_data(dev, "rtc-wilco-ec",
+ PLATFORM_DEVID_AUTO,
+ NULL, 0);
+ if (IS_ERR(ec->rtc_pdev)) {
+ dev_err(dev, "Failed to create RTC platform device\n");
+ ret = PTR_ERR(ec->rtc_pdev);
+ goto unregister_debugfs;
+ }
+
+ return 0;
+
+unregister_debugfs:
+ if (ec->debugfs_pdev)
+ platform_device_unregister(ec->debugfs_pdev);
+ cros_ec_lpc_mec_destroy();
+ return ret;
+}
+
+static int wilco_ec_remove(struct platform_device *pdev)
+{
+ struct wilco_ec_device *ec = platform_get_drvdata(pdev);
+
+ platform_device_unregister(ec->rtc_pdev);
+ if (ec->debugfs_pdev)
+ platform_device_unregister(ec->debugfs_pdev);
+
+ /* Teardown cros_ec interface */
+ cros_ec_lpc_mec_destroy();
+
+ return 0;
+}
+
+static const struct acpi_device_id wilco_ec_acpi_device_ids[] = {
+ { "GOOG000C", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, wilco_ec_acpi_device_ids);
+
+static struct platform_driver wilco_ec_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .acpi_match_table = wilco_ec_acpi_device_ids,
+ },
+ .probe = wilco_ec_probe,
+ .remove = wilco_ec_remove,
+};
+
+module_platform_driver(wilco_ec_driver);
+
+MODULE_AUTHOR("Nick Crews <ncrews@chromium.org>");
+MODULE_AUTHOR("Duncan Laurie <dlaurie@chromium.org>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("ChromeOS Wilco Embedded Controller driver");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/drivers/platform/chrome/wilco_ec/debugfs.c b/drivers/platform/chrome/wilco_ec/debugfs.c
new file mode 100644
index 000000000000..c090db2cd5be
--- /dev/null
+++ b/drivers/platform/chrome/wilco_ec/debugfs.c
@@ -0,0 +1,238 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * debugfs attributes for Wilco EC
+ *
+ * Copyright 2019 Google LLC
+ *
+ * There is only one attribute used for debugging, called raw.
+ * You can write a hexadecimal sentence to raw, and that series of bytes
+ * will be sent to the EC. Then, you can read the bytes of response
+ * by reading from raw.
+ *
+ * For writing:
+ * Bytes 0-1 indicate the message type:
+ * 00 F0 = Execute Legacy Command
+ * 00 F2 = Read/Write NVRAM Property
+ * Byte 2 provides the command code
+ * Bytes 3+ consist of the data passed in the request
+ *
+ * When referencing the EC interface spec, byte 2 corresponds to MBOX[0],
+ * byte 3 corresponds to MBOX[1], etc.
+ *
+ * At least three bytes are required, for the msg type and command,
+ * with additional bytes optional for additional data.
+ *
+ * Example:
+ * // Request EC info type 3 (EC firmware build date)
+ * $ echo 00 f0 38 00 03 00 > raw
+ * // View the result. The decoded ASCII result "12/21/18" is
+ * // included after the raw hex.
+ * $ cat raw
+ * 00 31 32 2f 32 31 2f 31 38 00 38 00 01 00 2f 00 .12/21/18.8...
+ */
+
+#include <linux/ctype.h>
+#include <linux/debugfs.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/platform_data/wilco-ec.h>
+#include <linux/platform_device.h>
+
+#define DRV_NAME "wilco-ec-debugfs"
+
+/* The 256 raw bytes will take up more space when represented as a hex string */
+#define FORMATTED_BUFFER_SIZE (EC_MAILBOX_DATA_SIZE_EXTENDED * 4)
+
+struct wilco_ec_debugfs {
+ struct wilco_ec_device *ec;
+ struct dentry *dir;
+ size_t response_size;
+ u8 raw_data[EC_MAILBOX_DATA_SIZE_EXTENDED];
+ u8 formatted_data[FORMATTED_BUFFER_SIZE];
+};
+static struct wilco_ec_debugfs *debug_info;
+
+/**
+ * parse_hex_sentence() - Convert a ascii hex representation into byte array.
+ * @in: Input buffer of ascii.
+ * @isize: Length of input buffer.
+ * @out: Output buffer.
+ * @osize: Length of output buffer, e.g. max number of bytes to parse.
+ *
+ * An valid input is a series of ascii hexadecimal numbers, separated by spaces.
+ * An example valid input is
+ * " 00 f2 0 000076 6 0 ff"
+ *
+ * If an individual "word" within the hex sentence is longer than MAX_WORD_SIZE,
+ * then the sentence is illegal, and parsing will fail.
+ *
+ * Return: Number of bytes parsed, or negative error code on failure.
+ */
+static int parse_hex_sentence(const char *in, int isize, u8 *out, int osize)
+{
+ int n_parsed = 0;
+ int word_start = 0;
+ int word_end;
+ int word_len;
+ /* Temp buffer for holding a "word" of chars that represents one byte */
+ #define MAX_WORD_SIZE 16
+ char tmp[MAX_WORD_SIZE + 1];
+ u8 byte;
+
+ while (word_start < isize && n_parsed < osize) {
+ /* Find the start of the next word */
+ while (word_start < isize && isspace(in[word_start]))
+ word_start++;
+ /* reached the end of the input before next word? */
+ if (word_start >= isize)
+ break;
+
+ /* Find the end of this word */
+ word_end = word_start;
+ while (word_end < isize && !isspace(in[word_end]))
+ word_end++;
+
+ /* Copy to a tmp NULL terminated string */
+ word_len = word_end - word_start;
+ if (word_len > MAX_WORD_SIZE)
+ return -EINVAL;
+ memcpy(tmp, in + word_start, word_len);
+ tmp[word_len] = '\0';
+
+ /*
+ * Convert from hex string, place in output. If fails to parse,
+ * just return -EINVAL because specific error code is only
+ * relevant for this one word, returning it would be confusing.
+ */
+ if (kstrtou8(tmp, 16, &byte))
+ return -EINVAL;
+ out[n_parsed++] = byte;
+
+ word_start = word_end;
+ }
+ return n_parsed;
+}
+
+/* The message type takes up two bytes*/
+#define TYPE_AND_DATA_SIZE ((EC_MAILBOX_DATA_SIZE) + 2)
+
+static ssize_t raw_write(struct file *file, const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ char *buf = debug_info->formatted_data;
+ struct wilco_ec_message msg;
+ u8 request_data[TYPE_AND_DATA_SIZE];
+ ssize_t kcount;
+ int ret;
+
+ if (count > FORMATTED_BUFFER_SIZE)
+ return -EINVAL;
+
+ kcount = simple_write_to_buffer(buf, FORMATTED_BUFFER_SIZE, ppos,
+ user_buf, count);
+ if (kcount < 0)
+ return kcount;
+
+ ret = parse_hex_sentence(buf, kcount, request_data, TYPE_AND_DATA_SIZE);
+ if (ret < 0)
+ return ret;
+ /* Need at least two bytes for message type and one for command */
+ if (ret < 3)
+ return -EINVAL;
+
+ /* Clear response data buffer */
+ memset(debug_info->raw_data, '\0', EC_MAILBOX_DATA_SIZE_EXTENDED);
+
+ msg.type = request_data[0] << 8 | request_data[1];
+ msg.flags = WILCO_EC_FLAG_RAW;
+ msg.command = request_data[2];
+ msg.request_data = ret > 3 ? request_data + 3 : 0;
+ msg.request_size = ret - 3;
+ msg.response_data = debug_info->raw_data;
+ msg.response_size = EC_MAILBOX_DATA_SIZE;
+
+ /* Telemetry commands use extended response data */
+ if (msg.type == WILCO_EC_MSG_TELEMETRY_LONG) {
+ msg.flags |= WILCO_EC_FLAG_EXTENDED_DATA;
+ msg.response_size = EC_MAILBOX_DATA_SIZE_EXTENDED;
+ }
+
+ ret = wilco_ec_mailbox(debug_info->ec, &msg);
+ if (ret < 0)
+ return ret;
+ debug_info->response_size = ret;
+
+ return count;
+}
+
+static ssize_t raw_read(struct file *file, char __user *user_buf, size_t count,
+ loff_t *ppos)
+{
+ int fmt_len = 0;
+
+ if (debug_info->response_size) {
+ fmt_len = hex_dump_to_buffer(debug_info->raw_data,
+ debug_info->response_size,
+ 16, 1, debug_info->formatted_data,
+ FORMATTED_BUFFER_SIZE, true);
+ /* Only return response the first time it is read */
+ debug_info->response_size = 0;
+ }
+
+ return simple_read_from_buffer(user_buf, count, ppos,
+ debug_info->formatted_data, fmt_len);
+}
+
+static const struct file_operations fops_raw = {
+ .owner = THIS_MODULE,
+ .read = raw_read,
+ .write = raw_write,
+ .llseek = no_llseek,
+};
+
+/**
+ * wilco_ec_debugfs_probe() - Create the debugfs node
+ * @pdev: The platform device, probably created in core.c
+ *
+ * Try to create a debugfs node. If it fails, then we don't want to change
+ * behavior at all, this is for debugging after all. Just fail silently.
+ *
+ * Return: 0 always.
+ */
+static int wilco_ec_debugfs_probe(struct platform_device *pdev)
+{
+ struct wilco_ec_device *ec = dev_get_drvdata(pdev->dev.parent);
+
+ debug_info = devm_kzalloc(&pdev->dev, sizeof(*debug_info), GFP_KERNEL);
+ if (!debug_info)
+ return 0;
+ debug_info->ec = ec;
+ debug_info->dir = debugfs_create_dir("wilco_ec", NULL);
+ if (!debug_info->dir)
+ return 0;
+ debugfs_create_file("raw", 0644, debug_info->dir, NULL, &fops_raw);
+
+ return 0;
+}
+
+static int wilco_ec_debugfs_remove(struct platform_device *pdev)
+{
+ debugfs_remove_recursive(debug_info->dir);
+
+ return 0;
+}
+
+static struct platform_driver wilco_ec_debugfs_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ },
+ .probe = wilco_ec_debugfs_probe,
+ .remove = wilco_ec_debugfs_remove,
+};
+
+module_platform_driver(wilco_ec_debugfs_driver);
+
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_AUTHOR("Nick Crews <ncrews@chromium.org>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Wilco EC debugfs driver");
diff --git a/drivers/platform/chrome/wilco_ec/mailbox.c b/drivers/platform/chrome/wilco_ec/mailbox.c
new file mode 100644
index 000000000000..14355668ddfa
--- /dev/null
+++ b/drivers/platform/chrome/wilco_ec/mailbox.c
@@ -0,0 +1,237 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Mailbox interface for Wilco Embedded Controller
+ *
+ * Copyright 2018 Google LLC
+ *
+ * The Wilco EC is similar to a typical ChromeOS embedded controller.
+ * It uses the same MEC based low-level communication and a similar
+ * protocol, but with some important differences. The EC firmware does
+ * not support the same mailbox commands so it is not registered as a
+ * cros_ec device type.
+ *
+ * Most messages follow a standard format, but there are some exceptions
+ * and an interface is provided to do direct/raw transactions that do not
+ * make assumptions about byte placement.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/platform_data/wilco-ec.h>
+#include <linux/platform_device.h>
+
+#include "../cros_ec_lpc_mec.h"
+
+/* Version of mailbox interface */
+#define EC_MAILBOX_VERSION 0
+
+/* Command to start mailbox transaction */
+#define EC_MAILBOX_START_COMMAND 0xda
+
+/* Version of EC protocol */
+#define EC_MAILBOX_PROTO_VERSION 3
+
+/* Number of header bytes to be counted as data bytes */
+#define EC_MAILBOX_DATA_EXTRA 2
+
+/* Maximum timeout */
+#define EC_MAILBOX_TIMEOUT HZ
+
+/* EC response flags */
+#define EC_CMDR_DATA BIT(0) /* Data ready for host to read */
+#define EC_CMDR_PENDING BIT(1) /* Write pending to EC */
+#define EC_CMDR_BUSY BIT(2) /* EC is busy processing a command */
+#define EC_CMDR_CMD BIT(3) /* Last host write was a command */
+
+/**
+ * wilco_ec_response_timed_out() - Wait for EC response.
+ * @ec: EC device.
+ *
+ * Return: true if EC timed out, false if EC did not time out.
+ */
+static bool wilco_ec_response_timed_out(struct wilco_ec_device *ec)
+{
+ unsigned long timeout = jiffies + EC_MAILBOX_TIMEOUT;
+
+ do {
+ if (!(inb(ec->io_command->start) &
+ (EC_CMDR_PENDING | EC_CMDR_BUSY)))
+ return false;
+ usleep_range(100, 200);
+ } while (time_before(jiffies, timeout));
+
+ return true;
+}
+
+/**
+ * wilco_ec_checksum() - Compute 8-bit checksum over data range.
+ * @data: Data to checksum.
+ * @size: Number of bytes to checksum.
+ *
+ * Return: 8-bit checksum of provided data.
+ */
+static u8 wilco_ec_checksum(const void *data, size_t size)
+{
+ u8 *data_bytes = (u8 *)data;
+ u8 checksum = 0;
+ size_t i;
+
+ for (i = 0; i < size; i++)
+ checksum += data_bytes[i];
+
+ return checksum;
+}
+
+/**
+ * wilco_ec_prepare() - Prepare the request structure for the EC.
+ * @msg: EC message with request information.
+ * @rq: EC request structure to fill.
+ */
+static void wilco_ec_prepare(struct wilco_ec_message *msg,
+ struct wilco_ec_request *rq)
+{
+ memset(rq, 0, sizeof(*rq));
+
+ /* Handle messages without trimming bytes from the request */
+ if (msg->request_size && msg->flags & WILCO_EC_FLAG_RAW_REQUEST) {
+ rq->reserved_raw = *(u8 *)msg->request_data;
+ msg->request_size--;
+ memmove(msg->request_data, msg->request_data + 1,
+ msg->request_size);
+ }
+
+ /* Fill in request packet */
+ rq->struct_version = EC_MAILBOX_PROTO_VERSION;
+ rq->mailbox_id = msg->type;
+ rq->mailbox_version = EC_MAILBOX_VERSION;
+ rq->data_size = msg->request_size + EC_MAILBOX_DATA_EXTRA;
+ rq->command = msg->command;
+
+ /* Checksum header and data */
+ rq->checksum = wilco_ec_checksum(rq, sizeof(*rq));
+ rq->checksum += wilco_ec_checksum(msg->request_data, msg->request_size);
+ rq->checksum = -rq->checksum;
+}
+
+/**
+ * wilco_ec_transfer() - Perform actual data transfer.
+ * @ec: EC device.
+ * @msg: EC message data for request and response.
+ * @rq: Filled in request structure
+ *
+ * Context: ec->mailbox_lock should be held while using this function.
+ * Return: number of bytes received or negative error code on failure.
+ */
+static int wilco_ec_transfer(struct wilco_ec_device *ec,
+ struct wilco_ec_message *msg,
+ struct wilco_ec_request *rq)
+{
+ struct wilco_ec_response *rs;
+ u8 checksum;
+ u8 flag;
+ size_t size;
+
+ /* Write request header, then data */
+ cros_ec_lpc_io_bytes_mec(MEC_IO_WRITE, 0, sizeof(*rq), (u8 *)rq);
+ cros_ec_lpc_io_bytes_mec(MEC_IO_WRITE, sizeof(*rq), msg->request_size,
+ msg->request_data);
+
+ /* Start the command */
+ outb(EC_MAILBOX_START_COMMAND, ec->io_command->start);
+
+ /* For some commands (eg shutdown) the EC will not respond, that's OK */
+ if (msg->flags & WILCO_EC_FLAG_NO_RESPONSE) {
+ dev_dbg(ec->dev, "EC does not respond to this command\n");
+ return 0;
+ }
+
+ /* Wait for it to complete */
+ if (wilco_ec_response_timed_out(ec)) {
+ dev_dbg(ec->dev, "response timed out\n");
+ return -ETIMEDOUT;
+ }
+
+ /* Check result */
+ flag = inb(ec->io_data->start);
+ if (flag) {
+ dev_dbg(ec->dev, "bad response: 0x%02x\n", flag);
+ return -EIO;
+ }
+
+ if (msg->flags & WILCO_EC_FLAG_EXTENDED_DATA)
+ size = EC_MAILBOX_DATA_SIZE_EXTENDED;
+ else
+ size = EC_MAILBOX_DATA_SIZE;
+
+ /* Read back response */
+ rs = ec->data_buffer;
+ checksum = cros_ec_lpc_io_bytes_mec(MEC_IO_READ, 0,
+ sizeof(*rs) + size, (u8 *)rs);
+ if (checksum) {
+ dev_dbg(ec->dev, "bad packet checksum 0x%02x\n", rs->checksum);
+ return -EBADMSG;
+ }
+
+ /* Check that the EC reported success */
+ msg->result = rs->result;
+ if (msg->result) {
+ dev_dbg(ec->dev, "bad response: 0x%02x\n", msg->result);
+ return -EBADMSG;
+ }
+
+ /* Check the returned data size, skipping the header */
+ if (rs->data_size != size) {
+ dev_dbg(ec->dev, "unexpected packet size (%u != %zu)",
+ rs->data_size, size);
+ return -EMSGSIZE;
+ }
+
+ /* Skip 1 response data byte unless specified */
+ size = (msg->flags & WILCO_EC_FLAG_RAW_RESPONSE) ? 0 : 1;
+ if ((ssize_t) rs->data_size - size < msg->response_size) {
+ dev_dbg(ec->dev, "response data too short (%zd < %zu)",
+ (ssize_t) rs->data_size - size, msg->response_size);
+ return -EMSGSIZE;
+ }
+
+ /* Ignore response data bytes as requested */
+ memcpy(msg->response_data, rs->data + size, msg->response_size);
+
+ /* Return actual amount of data received */
+ return msg->response_size;
+}
+
+/**
+ * wilco_ec_mailbox() - Send EC request and receive EC response.
+ * @ec: EC device.
+ * @msg: EC message data for request and response.
+ *
+ * On entry msg->type, msg->flags, msg->command, msg->request_size,
+ * msg->response_size, and msg->request_data should all be filled in.
+ *
+ * On exit msg->result and msg->response_data will be filled.
+ *
+ * Return: number of bytes received or negative error code on failure.
+ */
+int wilco_ec_mailbox(struct wilco_ec_device *ec, struct wilco_ec_message *msg)
+{
+ struct wilco_ec_request *rq;
+ int ret;
+
+ dev_dbg(ec->dev, "cmd=%02x type=%04x flags=%02x rslen=%zu rqlen=%zu\n",
+ msg->command, msg->type, msg->flags, msg->response_size,
+ msg->request_size);
+
+ mutex_lock(&ec->mailbox_lock);
+ /* Prepare request packet */
+ rq = ec->data_buffer;
+ wilco_ec_prepare(msg, rq);
+
+ ret = wilco_ec_transfer(ec, msg, rq);
+ mutex_unlock(&ec->mailbox_lock);
+
+ return ret;
+
+}
+EXPORT_SYMBOL_GPL(wilco_ec_mailbox);
diff --git a/drivers/platform/mellanox/mlxreg-hotplug.c b/drivers/platform/mellanox/mlxreg-hotplug.c
index b6d44550d98c..687ce6817d0d 100644
--- a/drivers/platform/mellanox/mlxreg-hotplug.c
+++ b/drivers/platform/mellanox/mlxreg-hotplug.c
@@ -248,7 +248,8 @@ mlxreg_hotplug_work_helper(struct mlxreg_hotplug_priv_data *priv,
struct mlxreg_core_item *item)
{
struct mlxreg_core_data *data;
- u32 asserted, regval, bit;
+ unsigned long asserted;
+ u32 regval, bit;
int ret;
/*
@@ -281,7 +282,7 @@ mlxreg_hotplug_work_helper(struct mlxreg_hotplug_priv_data *priv,
asserted = item->cache ^ regval;
item->cache = regval;
- for_each_set_bit(bit, (unsigned long *)&asserted, 8) {
+ for_each_set_bit(bit, &asserted, 8) {
data = item->data + bit;
if (regval & BIT(bit)) {
if (item->inversed)
@@ -495,7 +496,9 @@ static int mlxreg_hotplug_set_irq(struct mlxreg_hotplug_priv_data *priv)
{
struct mlxreg_core_hotplug_platform_data *pdata;
struct mlxreg_core_item *item;
- int i, ret;
+ struct mlxreg_core_data *data;
+ u32 regval;
+ int i, j, ret;
pdata = dev_get_platdata(&priv->pdev->dev);
item = pdata->items;
@@ -507,6 +510,25 @@ static int mlxreg_hotplug_set_irq(struct mlxreg_hotplug_priv_data *priv)
if (ret)
goto out;
+ /*
+ * Verify if hardware configuration requires to disable
+ * interrupt capability for some of components.
+ */
+ data = item->data;
+ for (j = 0; j < item->count; j++, data++) {
+ /* Verify if the attribute has capability register. */
+ if (data->capability) {
+ /* Read capability register. */
+ ret = regmap_read(priv->regmap,
+ data->capability, &regval);
+ if (ret)
+ goto out;
+
+ if (!(regval & data->bit))
+ item->mask &= ~BIT(j);
+ }
+ }
+
/* Set group initial status as mask and unmask group event. */
if (item->inversed) {
item->cache = item->mask;
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index b5e9db85e881..a1ed13183559 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -1303,6 +1303,20 @@ config HUAWEI_WMI
To compile this driver as a module, choose M here: the module
will be called huawei-wmi.
+config PCENGINES_APU2
+ tristate "PC Engines APUv2/3 front button and LEDs driver"
+ depends on INPUT && INPUT_KEYBOARD
+ depends on LEDS_CLASS
+ select GPIO_AMD_FCH
+ select KEYBOARD_GPIO_POLLED
+ select LEDS_GPIO
+ help
+ This driver provides support for the front button and LEDs on
+ PC Engines APUv2/APUv3 board.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pcengines-apuv2.
+
endif # X86_PLATFORM_DEVICES
config PMC_ATOM
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index ce8da260c223..86cb76677bc8 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -96,3 +96,4 @@ obj-$(CONFIG_INTEL_TURBO_MAX_3) += intel_turbo_max_3.o
obj-$(CONFIG_INTEL_CHTDC_TI_PWRBTN) += intel_chtdc_ti_pwrbtn.o
obj-$(CONFIG_I2C_MULTI_INSTANTIATE) += i2c-multi-instantiate.o
obj-$(CONFIG_INTEL_ATOMISP2_PM) += intel_atomisp2_pm.o
+obj-$(CONFIG_PCENGINES_APU2) += pcengines-apuv2.o
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index 37b5de541270..ee1fa93708ec 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -2265,12 +2265,12 @@ static int asus_wmi_probe(struct platform_device *pdev)
int ret;
if (!wmi_has_guid(ASUS_WMI_MGMT_GUID)) {
- pr_warn("Management GUID not found\n");
+ pr_warn("ASUS Management GUID not found\n");
return -ENODEV;
}
if (wdrv->event_guid && !wmi_has_guid(wdrv->event_guid)) {
- pr_warn("Event GUID not found\n");
+ pr_warn("ASUS Event GUID not found\n");
return -ENODEV;
}
@@ -2320,11 +2320,6 @@ EXPORT_SYMBOL_GPL(asus_wmi_unregister_driver);
static int __init asus_wmi_init(void)
{
- if (!wmi_has_guid(ASUS_WMI_MGMT_GUID)) {
- pr_info("Asus Management GUID not found\n");
- return -ENODEV;
- }
-
pr_info("ASUS WMI generic driver loaded\n");
return 0;
}
diff --git a/drivers/platform/x86/dell-smbios-wmi.c b/drivers/platform/x86/dell-smbios-wmi.c
index cf2229ece9ff..c3ed3c8c17b9 100644
--- a/drivers/platform/x86/dell-smbios-wmi.c
+++ b/drivers/platform/x86/dell-smbios-wmi.c
@@ -277,4 +277,4 @@ void exit_dell_smbios_wmi(void)
wmi_driver_unregister(&dell_smbios_wmi_driver);
}
-MODULE_ALIAS("wmi:" DELL_WMI_SMBIOS_GUID);
+MODULE_DEVICE_TABLE(wmi, dell_smbios_wmi_id_table);
diff --git a/drivers/platform/x86/dell-wmi-descriptor.c b/drivers/platform/x86/dell-wmi-descriptor.c
index 072821aa47fc..14ab250b7d5a 100644
--- a/drivers/platform/x86/dell-wmi-descriptor.c
+++ b/drivers/platform/x86/dell-wmi-descriptor.c
@@ -207,7 +207,7 @@ static struct wmi_driver dell_wmi_descriptor_driver = {
module_wmi_driver(dell_wmi_descriptor_driver);
-MODULE_ALIAS("wmi:" DELL_WMI_DESCRIPTOR_GUID);
+MODULE_DEVICE_TABLE(wmi, dell_wmi_descriptor_id_table);
MODULE_AUTHOR("Mario Limonciello <mario.limonciello@dell.com>");
MODULE_DESCRIPTION("Dell WMI descriptor driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/platform/x86/dell-wmi.c b/drivers/platform/x86/dell-wmi.c
index 16c7f3d9a335..d118bb73fcae 100644
--- a/drivers/platform/x86/dell-wmi.c
+++ b/drivers/platform/x86/dell-wmi.c
@@ -50,8 +50,6 @@ MODULE_LICENSE("GPL");
static bool wmi_requires_smbios_request;
-MODULE_ALIAS("wmi:"DELL_EVENT_GUID);
-
struct dell_wmi_priv {
struct input_dev *input_dev;
u32 interface_version;
@@ -267,6 +265,9 @@ static const struct key_entry dell_wmi_keymap_type_0010[] = {
/* Fn-lock switched to multimedia keys */
{ KE_IGNORE, 0x1, { KEY_RESERVED } },
+ /* Keyboard backlight change notification */
+ { KE_IGNORE, 0x3f, { KEY_RESERVED } },
+
/* Mic mute */
{ KE_KEY, 0x150, { KEY_MICMUTE } },
@@ -738,3 +739,5 @@ static void __exit dell_wmi_exit(void)
wmi_driver_unregister(&dell_wmi_driver);
}
module_exit(dell_wmi_exit);
+
+MODULE_DEVICE_TABLE(wmi, dell_wmi_id_table);
diff --git a/drivers/platform/x86/dell_rbu.c b/drivers/platform/x86/dell_rbu.c
index ccefa84f7305..031c68903583 100644
--- a/drivers/platform/x86/dell_rbu.c
+++ b/drivers/platform/x86/dell_rbu.c
@@ -59,7 +59,6 @@ static struct _rbu_data {
unsigned long image_update_buffer_size;
unsigned long bios_image_size;
int image_update_ordernum;
- int dma_alloc;
spinlock_t lock;
unsigned long packet_read_count;
unsigned long num_packets;
@@ -89,7 +88,6 @@ static struct packet_data packet_data_head;
static struct platform_device *rbu_device;
static int context;
-static dma_addr_t dell_rbu_dmaaddr;
static void init_packet_head(void)
{
@@ -380,12 +378,8 @@ static void img_update_free(void)
*/
memset(rbu_data.image_update_buffer, 0,
rbu_data.image_update_buffer_size);
- if (rbu_data.dma_alloc == 1)
- dma_free_coherent(NULL, rbu_data.bios_image_size,
- rbu_data.image_update_buffer, dell_rbu_dmaaddr);
- else
- free_pages((unsigned long) rbu_data.image_update_buffer,
- rbu_data.image_update_ordernum);
+ free_pages((unsigned long) rbu_data.image_update_buffer,
+ rbu_data.image_update_ordernum);
/*
* Re-initialize the rbu_data variables after a free
@@ -394,7 +388,6 @@ static void img_update_free(void)
rbu_data.image_update_buffer = NULL;
rbu_data.image_update_buffer_size = 0;
rbu_data.bios_image_size = 0;
- rbu_data.dma_alloc = 0;
}
/*
@@ -410,10 +403,8 @@ static void img_update_free(void)
static int img_update_realloc(unsigned long size)
{
unsigned char *image_update_buffer = NULL;
- unsigned long rc;
unsigned long img_buf_phys_addr;
int ordernum;
- int dma_alloc = 0;
/*
* check if the buffer of sufficient size has been
@@ -444,36 +435,23 @@ static int img_update_realloc(unsigned long size)
ordernum = get_order(size);
image_update_buffer =
- (unsigned char *) __get_free_pages(GFP_KERNEL, ordernum);
-
- img_buf_phys_addr =
- (unsigned long) virt_to_phys(image_update_buffer);
-
- if (img_buf_phys_addr > BIOS_SCAN_LIMIT) {
- free_pages((unsigned long) image_update_buffer, ordernum);
- ordernum = -1;
- image_update_buffer = dma_alloc_coherent(NULL, size,
- &dell_rbu_dmaaddr, GFP_KERNEL);
- dma_alloc = 1;
- }
-
+ (unsigned char *)__get_free_pages(GFP_DMA32, ordernum);
spin_lock(&rbu_data.lock);
-
- if (image_update_buffer != NULL) {
- rbu_data.image_update_buffer = image_update_buffer;
- rbu_data.image_update_buffer_size = size;
- rbu_data.bios_image_size =
- rbu_data.image_update_buffer_size;
- rbu_data.image_update_ordernum = ordernum;
- rbu_data.dma_alloc = dma_alloc;
- rc = 0;
- } else {
+ if (!image_update_buffer) {
pr_debug("Not enough memory for image update:"
"size = %ld\n", size);
- rc = -ENOMEM;
+ return -ENOMEM;
}
- return rc;
+ img_buf_phys_addr = (unsigned long)virt_to_phys(image_update_buffer);
+ if (WARN_ON_ONCE(img_buf_phys_addr > BIOS_SCAN_LIMIT))
+ return -EINVAL; /* can't happen per definition */
+
+ rbu_data.image_update_buffer = image_update_buffer;
+ rbu_data.image_update_buffer_size = size;
+ rbu_data.bios_image_size = rbu_data.image_update_buffer_size;
+ rbu_data.image_update_ordernum = ordernum;
+ return 0;
}
static ssize_t read_packet_data(char *buffer, loff_t pos, size_t count)
diff --git a/drivers/platform/x86/huawei-wmi.c b/drivers/platform/x86/huawei-wmi.c
index 59872f87b741..52fcac5b393a 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -201,8 +201,7 @@ static struct wmi_driver huawei_wmi_driver = {
module_wmi_driver(huawei_wmi_driver);
-MODULE_ALIAS("wmi:"WMI0_EVENT_GUID);
-MODULE_ALIAS("wmi:"AMW0_EVENT_GUID);
+MODULE_DEVICE_TABLE(wmi, huawei_wmi_id_table);
MODULE_AUTHOR("Ayman Bagabas <ayman.bagabas@gmail.com>");
MODULE_DESCRIPTION("Huawei WMI hotkeys");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/platform/x86/i2c-multi-instantiate.c b/drivers/platform/x86/i2c-multi-instantiate.c
index 3d893e0ac250..197d8a192721 100644
--- a/drivers/platform/x86/i2c-multi-instantiate.c
+++ b/drivers/platform/x86/i2c-multi-instantiate.c
@@ -159,6 +159,14 @@ static const struct i2c_inst_data bsg1160_data[] = {
{}
};
+static const struct i2c_inst_data bsg2150_data[] = {
+ { "bmc150_accel", IRQ_RESOURCE_GPIO, 0 },
+ { "bmc150_magn" },
+ /* The resources describe a 3th client, but it is not really there. */
+ { "bsg2150_dummy_dev" },
+ {}
+};
+
static const struct i2c_inst_data int3515_data[] = {
{ "tps6598x", IRQ_RESOURCE_APIC, 0 },
{ "tps6598x", IRQ_RESOURCE_APIC, 1 },
@@ -173,6 +181,7 @@ static const struct i2c_inst_data int3515_data[] = {
*/
static const struct acpi_device_id i2c_multi_inst_acpi_ids[] = {
{ "BSG1160", (unsigned long)bsg1160_data },
+ { "BSG2150", (unsigned long)bsg2150_data },
{ "INT3515", (unsigned long)int3515_data },
{ }
};
diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c
index 1589dffab9fa..c53ae86b59c7 100644
--- a/drivers/platform/x86/ideapad-laptop.c
+++ b/drivers/platform/x86/ideapad-laptop.c
@@ -989,7 +989,7 @@ static const struct dmi_system_id no_hw_rfkill_list[] = {
.ident = "Lenovo RESCUER R720-15IKBN",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
- DMI_MATCH(DMI_BOARD_NAME, "80WW"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo R720-15IKBN"),
},
},
{
@@ -1091,6 +1091,27 @@ static const struct dmi_system_id no_hw_rfkill_list[] = {
},
},
{
+ .ident = "Lenovo ideapad 330-15ICH",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad 330-15ICH"),
+ },
+ },
+ {
+ .ident = "Lenovo ideapad 530S-14ARR",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad 530S-14ARR"),
+ },
+ },
+ {
+ .ident = "Lenovo ideapad S130-14IGM",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad S130-14IGM"),
+ },
+ },
+ {
.ident = "Lenovo ideapad Y700-14ISK",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
@@ -1154,6 +1175,13 @@ static const struct dmi_system_id no_hw_rfkill_list[] = {
},
},
{
+ .ident = "Lenovo Legion Y530-15ICH-1060",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Legion Y530-15ICH-1060"),
+ },
+ },
+ {
.ident = "Lenovo Legion Y720-15IKB",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
@@ -1245,6 +1273,13 @@ static const struct dmi_system_id no_hw_rfkill_list[] = {
},
},
{
+ .ident = "Lenovo YOGA C930-13IKB",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo YOGA C930-13IKB"),
+ },
+ },
+ {
.ident = "Lenovo Zhaoyang E42-80",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
diff --git a/drivers/platform/x86/intel-hid.c b/drivers/platform/x86/intel-hid.c
index e28bcf61b126..bc0d55a59015 100644
--- a/drivers/platform/x86/intel-hid.c
+++ b/drivers/platform/x86/intel-hid.c
@@ -363,7 +363,7 @@ wakeup:
* the 5-button array, but still send notifies with power button
* event code to this device object on power button actions.
*
- * Report the power button press; catch and ignore the button release.
+ * Report the power button press and release.
*/
if (!priv->array) {
if (event == 0xce) {
@@ -372,8 +372,11 @@ wakeup:
return;
}
- if (event == 0xcf)
+ if (event == 0xcf) {
+ input_report_key(priv->input_dev, KEY_POWER, 0);
+ input_sync(priv->input_dev);
return;
+ }
}
/* 0xC0 is for HID events, other values are for 5 button array */
diff --git a/drivers/platform/x86/intel-wmi-thunderbolt.c b/drivers/platform/x86/intel-wmi-thunderbolt.c
index 9ded8e2af312..4dfa61434a76 100644
--- a/drivers/platform/x86/intel-wmi-thunderbolt.c
+++ b/drivers/platform/x86/intel-wmi-thunderbolt.c
@@ -88,7 +88,7 @@ static struct wmi_driver intel_wmi_thunderbolt_driver = {
module_wmi_driver(intel_wmi_thunderbolt_driver);
-MODULE_ALIAS("wmi:" INTEL_WMI_THUNDERBOLT_GUID);
+MODULE_DEVICE_TABLE(wmi, intel_wmi_thunderbolt_id_table);
MODULE_AUTHOR("Mario Limonciello <mario.limonciello@dell.com>");
MODULE_DESCRIPTION("Intel WMI Thunderbolt force power driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/platform/x86/intel_int0002_vgpio.c b/drivers/platform/x86/intel_int0002_vgpio.c
index 4b8f7305fc8a..1694a9aec77c 100644
--- a/drivers/platform/x86/intel_int0002_vgpio.c
+++ b/drivers/platform/x86/intel_int0002_vgpio.c
@@ -51,11 +51,14 @@
#define GPE0A_STS_PORT 0x420
#define GPE0A_EN_PORT 0x428
-#define ICPU(model) { X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY, }
+#define BAYTRAIL 0x01
+#define CHERRYTRAIL 0x02
+
+#define ICPU(model, data) { X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY, data }
static const struct x86_cpu_id int0002_cpu_ids[] = {
- ICPU(INTEL_FAM6_ATOM_SILVERMONT), /* Valleyview, Bay Trail */
- ICPU(INTEL_FAM6_ATOM_AIRMONT), /* Braswell, Cherry Trail */
+ ICPU(INTEL_FAM6_ATOM_SILVERMONT, BAYTRAIL), /* Valleyview, Bay Trail */
+ ICPU(INTEL_FAM6_ATOM_AIRMONT, CHERRYTRAIL), /* Braswell, Cherry Trail */
{}
};
@@ -135,7 +138,7 @@ static irqreturn_t int0002_irq(int irq, void *data)
return IRQ_HANDLED;
}
-static struct irq_chip int0002_irqchip = {
+static struct irq_chip int0002_byt_irqchip = {
.name = DRV_NAME,
.irq_ack = int0002_irq_ack,
.irq_mask = int0002_irq_mask,
@@ -143,10 +146,22 @@ static struct irq_chip int0002_irqchip = {
.irq_set_wake = int0002_irq_set_wake,
};
+static struct irq_chip int0002_cht_irqchip = {
+ .name = DRV_NAME,
+ .irq_ack = int0002_irq_ack,
+ .irq_mask = int0002_irq_mask,
+ .irq_unmask = int0002_irq_unmask,
+ /*
+ * No set_wake, on CHT the IRQ is typically shared with the ACPI SCI
+ * and we don't want to mess with the ACPI SCI irq settings.
+ */
+};
+
static int int0002_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
const struct x86_cpu_id *cpu_id;
+ struct irq_chip *irq_chip;
struct gpio_chip *chip;
int irq, ret;
@@ -195,14 +210,19 @@ static int int0002_probe(struct platform_device *pdev)
return ret;
}
- ret = gpiochip_irqchip_add(chip, &int0002_irqchip, 0, handle_edge_irq,
+ if (cpu_id->driver_data == BAYTRAIL)
+ irq_chip = &int0002_byt_irqchip;
+ else
+ irq_chip = &int0002_cht_irqchip;
+
+ ret = gpiochip_irqchip_add(chip, irq_chip, 0, handle_edge_irq,
IRQ_TYPE_NONE);
if (ret) {
dev_err(dev, "Error adding irqchip: %d\n", ret);
return ret;
}
- gpiochip_set_chained_irqchip(chip, &int0002_irqchip, irq, NULL);
+ gpiochip_set_chained_irqchip(chip, irq_chip, irq, NULL);
return 0;
}
diff --git a/drivers/platform/x86/intel_pmc_core.c b/drivers/platform/x86/intel_pmc_core.c
index 22dbf115782e..f2c621b55f49 100644
--- a/drivers/platform/x86/intel_pmc_core.c
+++ b/drivers/platform/x86/intel_pmc_core.c
@@ -15,6 +15,7 @@
#include <linux/bitfield.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
+#include <linux/dmi.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/pci.h>
@@ -22,14 +23,24 @@
#include <asm/cpu_device_id.h>
#include <asm/intel-family.h>
+#include <asm/msr.h>
#include "intel_pmc_core.h"
-#define ICPU(model, data) \
- { X86_VENDOR_INTEL, 6, model, X86_FEATURE_MWAIT, (kernel_ulong_t)data }
-
static struct pmc_dev pmc;
+/* PKGC MSRs are common across Intel Core SoCs */
+static const struct pmc_bit_map msr_map[] = {
+ {"Package C2", MSR_PKG_C2_RESIDENCY},
+ {"Package C3", MSR_PKG_C3_RESIDENCY},
+ {"Package C6", MSR_PKG_C6_RESIDENCY},
+ {"Package C7", MSR_PKG_C7_RESIDENCY},
+ {"Package C8", MSR_PKG_C8_RESIDENCY},
+ {"Package C9", MSR_PKG_C9_RESIDENCY},
+ {"Package C10", MSR_PKG_C10_RESIDENCY},
+ {}
+};
+
static const struct pmc_bit_map spt_pll_map[] = {
{"MIPI PLL", SPT_PMC_BIT_MPHY_CMN_LANE0},
{"GEN2 USB2PCIE2 PLL", SPT_PMC_BIT_MPHY_CMN_LANE1},
@@ -108,6 +119,7 @@ static const struct pmc_bit_map spt_ltr_show_map[] = {
{"SATA", SPT_PMC_LTR_SATA},
{"GIGABIT_ETHERNET", SPT_PMC_LTR_GBE},
{"XHCI", SPT_PMC_LTR_XHCI},
+ {"Reserved", SPT_PMC_LTR_RESERVED},
{"ME", SPT_PMC_LTR_ME},
/* EVA is Enterprise Value Add, doesn't really exist on PCH */
{"EVA", SPT_PMC_LTR_EVA},
@@ -131,6 +143,7 @@ static const struct pmc_reg_map spt_reg_map = {
.mphy_sts = spt_mphy_map,
.pll_sts = spt_pll_map,
.ltr_show_sts = spt_ltr_show_map,
+ .msr_sts = msr_map,
.slp_s0_offset = SPT_PMC_SLP_S0_RES_COUNTER_OFFSET,
.ltr_ignore_offset = SPT_PMC_LTR_IGNORE_OFFSET,
.regmap_length = SPT_PMC_MMIO_REG_LEN,
@@ -139,6 +152,7 @@ static const struct pmc_reg_map spt_reg_map = {
.pm_cfg_offset = SPT_PMC_PM_CFG_OFFSET,
.pm_read_disable_bit = SPT_PMC_READ_DISABLE_BIT,
.ltr_ignore_max = SPT_NUM_IP_IGN_ALLOWED,
+ .pm_vric1_offset = SPT_PMC_VRIC1_OFFSET,
};
/* Cannonlake: PGD PFET Enable Ack Status Register(s) bitmap */
@@ -168,25 +182,26 @@ static const struct pmc_bit_map cnp_pfear_map[] = {
{"SDX", BIT(4)},
{"SPE", BIT(5)},
{"Fuse", BIT(6)},
- {"Res_23", BIT(7)},
+ /* Reserved for Cannonlake but valid for Icelake */
+ {"SBR8", BIT(7)},
{"CSME_FSC", BIT(0)},
{"USB3_OTG", BIT(1)},
{"EXI", BIT(2)},
{"CSE", BIT(3)},
- {"csme_kvm", BIT(4)},
- {"csme_pmt", BIT(5)},
- {"csme_clink", BIT(6)},
- {"csme_ptio", BIT(7)},
-
- {"csme_usbr", BIT(0)},
- {"csme_susram", BIT(1)},
- {"csme_smt1", BIT(2)},
+ {"CSME_KVM", BIT(4)},
+ {"CSME_PMT", BIT(5)},
+ {"CSME_CLINK", BIT(6)},
+ {"CSME_PTIO", BIT(7)},
+
+ {"CSME_USBR", BIT(0)},
+ {"CSME_SUSRAM", BIT(1)},
+ {"CSME_SMT1", BIT(2)},
{"CSME_SMT4", BIT(3)},
- {"csme_sms2", BIT(4)},
- {"csme_sms1", BIT(5)},
- {"csme_rtc", BIT(6)},
- {"csme_psf", BIT(7)},
+ {"CSME_SMS2", BIT(4)},
+ {"CSME_SMS1", BIT(5)},
+ {"CSME_RTC", BIT(6)},
+ {"CSME_PSF", BIT(7)},
{"SBR0", BIT(0)},
{"SBR1", BIT(1)},
@@ -203,7 +218,7 @@ static const struct pmc_bit_map cnp_pfear_map[] = {
{"CNVI", BIT(3)},
{"UFS0", BIT(4)},
{"EMMC", BIT(5)},
- {"Res_6", BIT(6)},
+ {"SPF", BIT(6)},
{"SBR6", BIT(7)},
{"SBR7", BIT(0)},
@@ -211,6 +226,20 @@ static const struct pmc_bit_map cnp_pfear_map[] = {
{"HDA_PGD4", BIT(2)},
{"HDA_PGD5", BIT(3)},
{"HDA_PGD6", BIT(4)},
+ /* Reserved for Cannonlake but valid for Icelake */
+ {"PSF6", BIT(5)},
+ {"PSF7", BIT(6)},
+ {"PSF8", BIT(7)},
+
+ /* Icelake generation onwards only */
+ {"RES_65", BIT(0)},
+ {"RES_66", BIT(1)},
+ {"RES_67", BIT(2)},
+ {"TAM", BIT(3)},
+ {"GBETSN", BIT(4)},
+ {"TBTLSX", BIT(5)},
+ {"RES_71", BIT(6)},
+ {"RES_72", BIT(7)},
{}
};
@@ -276,6 +305,7 @@ static const struct pmc_bit_map cnp_ltr_show_map[] = {
{"SATA", CNP_PMC_LTR_SATA},
{"GIGABIT_ETHERNET", CNP_PMC_LTR_GBE},
{"XHCI", CNP_PMC_LTR_XHCI},
+ {"Reserved", CNP_PMC_LTR_RESERVED},
{"ME", CNP_PMC_LTR_ME},
/* EVA is Enterprise Value Add, doesn't really exist on PCH */
{"EVA", CNP_PMC_LTR_EVA},
@@ -291,6 +321,8 @@ static const struct pmc_bit_map cnp_ltr_show_map[] = {
{"ISH", CNP_PMC_LTR_ISH},
{"UFSX2", CNP_PMC_LTR_UFSX2},
{"EMMC", CNP_PMC_LTR_EMMC},
+ /* Reserved for Cannonlake but valid for Icelake */
+ {"WIGIG", ICL_PMC_LTR_WIGIG},
/* Below two cannot be used for LTR_IGNORE */
{"CURRENT_PLATFORM", CNP_PMC_LTR_CUR_PLT},
{"AGGREGATED_SYSTEM", CNP_PMC_LTR_CUR_ASLT},
@@ -302,6 +334,7 @@ static const struct pmc_reg_map cnp_reg_map = {
.slp_s0_offset = CNP_PMC_SLP_S0_RES_COUNTER_OFFSET,
.slps0_dbg_maps = cnp_slps0_dbg_maps,
.ltr_show_sts = cnp_ltr_show_map,
+ .msr_sts = msr_map,
.slps0_dbg_offset = CNP_PMC_SLPS0_DBG_OFFSET,
.ltr_ignore_offset = CNP_PMC_LTR_IGNORE_OFFSET,
.regmap_length = CNP_PMC_MMIO_REG_LEN,
@@ -312,6 +345,22 @@ static const struct pmc_reg_map cnp_reg_map = {
.ltr_ignore_max = CNP_NUM_IP_IGN_ALLOWED,
};
+static const struct pmc_reg_map icl_reg_map = {
+ .pfear_sts = cnp_pfear_map,
+ .slp_s0_offset = CNP_PMC_SLP_S0_RES_COUNTER_OFFSET,
+ .slps0_dbg_maps = cnp_slps0_dbg_maps,
+ .ltr_show_sts = cnp_ltr_show_map,
+ .msr_sts = msr_map,
+ .slps0_dbg_offset = CNP_PMC_SLPS0_DBG_OFFSET,
+ .ltr_ignore_offset = CNP_PMC_LTR_IGNORE_OFFSET,
+ .regmap_length = CNP_PMC_MMIO_REG_LEN,
+ .ppfear0_offset = CNP_PMC_HOST_PPFEAR0A,
+ .ppfear_buckets = ICL_PPFEAR_NUM_ENTRIES,
+ .pm_cfg_offset = CNP_PMC_PM_CFG_OFFSET,
+ .pm_read_disable_bit = CNP_PMC_READ_DISABLE_BIT,
+ .ltr_ignore_max = ICL_NUM_IP_IGN_ALLOWED,
+};
+
static inline u8 pmc_core_reg_read_byte(struct pmc_dev *pmcdev, int offset)
{
return readb(pmcdev->regbase + offset);
@@ -328,9 +377,9 @@ static inline void pmc_core_reg_write(struct pmc_dev *pmcdev, int
writel(val, pmcdev->regbase + reg_offset);
}
-static inline u32 pmc_core_adjust_slp_s0_step(u32 value)
+static inline u64 pmc_core_adjust_slp_s0_step(u32 value)
{
- return value * SPT_PMC_SLP_S0_RES_COUNTER_STEP;
+ return (u64)value * SPT_PMC_SLP_S0_RES_COUNTER_STEP;
}
static int pmc_core_dev_state_get(void *data, u64 *val)
@@ -380,7 +429,8 @@ static int pmc_core_ppfear_show(struct seq_file *s, void *unused)
index < PPFEAR_MAX_NUM_ENTRIES; index++, iter++)
pf_regs[index] = pmc_core_reg_read_byte(pmcdev, iter);
- for (index = 0; map[index].name; index++)
+ for (index = 0; map[index].name &&
+ index < pmcdev->map->ppfear_buckets * 8; index++)
pmc_core_display_map(s, index, pf_regs[index / 8], map);
return 0;
@@ -677,6 +727,25 @@ static int pmc_core_ltr_show(struct seq_file *s, void *unused)
}
DEFINE_SHOW_ATTRIBUTE(pmc_core_ltr);
+static int pmc_core_pkgc_show(struct seq_file *s, void *unused)
+{
+ struct pmc_dev *pmcdev = s->private;
+ const struct pmc_bit_map *map = pmcdev->map->msr_sts;
+ u64 pcstate_count;
+ int index;
+
+ for (index = 0; map[index].name ; index++) {
+ if (rdmsrl_safe(map[index].bit_mask, &pcstate_count))
+ continue;
+
+ seq_printf(s, "%-8s : 0x%llx\n", map[index].name,
+ pcstate_count);
+ }
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(pmc_core_pkgc);
+
static void pmc_core_dbgfs_unregister(struct pmc_dev *pmcdev)
{
debugfs_remove_recursive(pmcdev->dbgfs_dir);
@@ -701,7 +770,10 @@ static int pmc_core_dbgfs_register(struct pmc_dev *pmcdev)
debugfs_create_file("ltr_ignore", 0644, dir, pmcdev,
&pmc_core_ltr_ignore_ops);
- debugfs_create_file("ltr_show", 0644, dir, pmcdev, &pmc_core_ltr_fops);
+ debugfs_create_file("ltr_show", 0444, dir, pmcdev, &pmc_core_ltr_fops);
+
+ debugfs_create_file("package_cstate_show", 0444, dir, pmcdev,
+ &pmc_core_pkgc_fops);
if (pmcdev->map->pll_sts)
debugfs_create_file("pll_status", 0444, dir, pmcdev,
@@ -735,11 +807,12 @@ static inline void pmc_core_dbgfs_unregister(struct pmc_dev *pmcdev)
#endif /* CONFIG_DEBUG_FS */
static const struct x86_cpu_id intel_pmc_core_ids[] = {
- ICPU(INTEL_FAM6_SKYLAKE_MOBILE, &spt_reg_map),
- ICPU(INTEL_FAM6_SKYLAKE_DESKTOP, &spt_reg_map),
- ICPU(INTEL_FAM6_KABYLAKE_MOBILE, &spt_reg_map),
- ICPU(INTEL_FAM6_KABYLAKE_DESKTOP, &spt_reg_map),
- ICPU(INTEL_FAM6_CANNONLAKE_MOBILE, &cnp_reg_map),
+ INTEL_CPU_FAM6(SKYLAKE_MOBILE, spt_reg_map),
+ INTEL_CPU_FAM6(SKYLAKE_DESKTOP, spt_reg_map),
+ INTEL_CPU_FAM6(KABYLAKE_MOBILE, spt_reg_map),
+ INTEL_CPU_FAM6(KABYLAKE_DESKTOP, spt_reg_map),
+ INTEL_CPU_FAM6(CANNONLAKE_MOBILE, cnp_reg_map),
+ INTEL_CPU_FAM6(ICELAKE_MOBILE, icl_reg_map),
{}
};
@@ -750,6 +823,37 @@ static const struct pci_device_id pmc_pci_ids[] = {
{ 0, },
};
+/*
+ * This quirk can be used on those platforms where
+ * the platform BIOS enforces 24Mhx Crystal to shutdown
+ * before PMC can assert SLP_S0#.
+ */
+int quirk_xtal_ignore(const struct dmi_system_id *id)
+{
+ struct pmc_dev *pmcdev = &pmc;
+ u32 value;
+
+ value = pmc_core_reg_read(pmcdev, pmcdev->map->pm_vric1_offset);
+ /* 24MHz Crystal Shutdown Qualification Disable */
+ value |= SPT_PMC_VRIC1_XTALSDQDIS;
+ /* Low Voltage Mode Enable */
+ value &= ~SPT_PMC_VRIC1_SLPS0LVEN;
+ pmc_core_reg_write(pmcdev, pmcdev->map->pm_vric1_offset, value);
+ return 0;
+}
+
+static const struct dmi_system_id pmc_core_dmi_table[] = {
+ {
+ .callback = quirk_xtal_ignore,
+ .ident = "HP Elite x2 1013 G3",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "HP"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "HP Elite x2 1013 G3"),
+ },
+ },
+ {}
+};
+
static int __init pmc_core_probe(void)
{
struct pmc_dev *pmcdev = &pmc;
@@ -768,7 +872,7 @@ static int __init pmc_core_probe(void)
* Sunrisepoint PCH regmap can't be used. Use Cannonlake PCH regmap
* in this case.
*/
- if (!pci_dev_present(pmc_pci_ids))
+ if (pmcdev->map == &spt_reg_map && !pci_dev_present(pmc_pci_ids))
pmcdev->map = &cnp_reg_map;
if (lpit_read_residency_count_address(&slp_s0_addr))
@@ -791,6 +895,7 @@ static int __init pmc_core_probe(void)
return err;
}
+ dmi_check_system(pmc_core_dmi_table);
pr_info(" initialized\n");
return 0;
}
diff --git a/drivers/platform/x86/intel_pmc_core.h b/drivers/platform/x86/intel_pmc_core.h
index 89554cba5758..88d9c0653a5f 100644
--- a/drivers/platform/x86/intel_pmc_core.h
+++ b/drivers/platform/x86/intel_pmc_core.h
@@ -25,6 +25,7 @@
#define SPT_PMC_MTPMC_OFFSET 0x20
#define SPT_PMC_MFPMC_OFFSET 0x38
#define SPT_PMC_LTR_IGNORE_OFFSET 0x30C
+#define SPT_PMC_VRIC1_OFFSET 0x31c
#define SPT_PMC_MPHY_CORE_STS_0 0x1143
#define SPT_PMC_MPHY_CORE_STS_1 0x1142
#define SPT_PMC_MPHY_COM_STS_0 0x1155
@@ -32,7 +33,7 @@
#define SPT_PMC_SLP_S0_RES_COUNTER_STEP 0x64
#define PMC_BASE_ADDR_MASK ~(SPT_PMC_MMIO_REG_LEN - 1)
#define MTPMC_MASK 0xffff0000
-#define PPFEAR_MAX_NUM_ENTRIES 5
+#define PPFEAR_MAX_NUM_ENTRIES 12
#define SPT_PPFEAR_NUM_ENTRIES 5
#define SPT_PMC_READ_DISABLE_BIT 0x16
#define SPT_PMC_MSG_FULL_STS_BIT 0x18
@@ -46,6 +47,7 @@
#define SPT_PMC_LTR_SATA 0x368
#define SPT_PMC_LTR_GBE 0x36C
#define SPT_PMC_LTR_XHCI 0x370
+#define SPT_PMC_LTR_RESERVED 0x374
#define SPT_PMC_LTR_ME 0x378
#define SPT_PMC_LTR_EVA 0x37C
#define SPT_PMC_LTR_SPC 0x380
@@ -135,6 +137,9 @@ enum ppfear_regs {
#define SPT_PMC_BIT_MPHY_CMN_LANE2 BIT(2)
#define SPT_PMC_BIT_MPHY_CMN_LANE3 BIT(3)
+#define SPT_PMC_VRIC1_SLPS0LVEN BIT(13)
+#define SPT_PMC_VRIC1_XTALSDQDIS BIT(22)
+
/* Cannonlake Power Management Controller register offsets */
#define CNP_PMC_SLPS0_DBG_OFFSET 0x10B4
#define CNP_PMC_PM_CFG_OFFSET 0x1818
@@ -156,6 +161,7 @@ enum ppfear_regs {
#define CNP_PMC_LTR_SATA 0x1B68
#define CNP_PMC_LTR_GBE 0x1B6C
#define CNP_PMC_LTR_XHCI 0x1B70
+#define CNP_PMC_LTR_RESERVED 0x1B74
#define CNP_PMC_LTR_ME 0x1B78
#define CNP_PMC_LTR_EVA 0x1B7C
#define CNP_PMC_LTR_SPC 0x1B80
@@ -176,6 +182,10 @@ enum ppfear_regs {
#define LTR_REQ_SNOOP BIT(15)
#define LTR_REQ_NONSNOOP BIT(31)
+#define ICL_PPFEAR_NUM_ENTRIES 9
+#define ICL_NUM_IP_IGN_ALLOWED 20
+#define ICL_PMC_LTR_WIGIG 0x1BFC
+
struct pmc_bit_map {
const char *name;
u32 bit_mask;
@@ -208,6 +218,7 @@ struct pmc_reg_map {
const struct pmc_bit_map *pll_sts;
const struct pmc_bit_map **slps0_dbg_maps;
const struct pmc_bit_map *ltr_show_sts;
+ const struct pmc_bit_map *msr_sts;
const u32 slp_s0_offset;
const u32 ltr_ignore_offset;
const int regmap_length;
@@ -217,6 +228,7 @@ struct pmc_reg_map {
const int pm_read_disable_bit;
const u32 slps0_dbg_offset;
const u32 ltr_ignore_max;
+ const u32 pm_vric1_offset;
};
/**
diff --git a/drivers/platform/x86/mlx-platform.c b/drivers/platform/x86/mlx-platform.c
index df3fcd36776a..48fa7573e29b 100644
--- a/drivers/platform/x86/mlx-platform.c
+++ b/drivers/platform/x86/mlx-platform.c
@@ -25,6 +25,7 @@
#define MLXPLAT_CPLD_LPC_REG_CPLD1_VER_OFFSET 0x00
#define MLXPLAT_CPLD_LPC_REG_CPLD2_VER_OFFSET 0x01
#define MLXPLAT_CPLD_LPC_REG_CPLD3_VER_OFFSET 0x02
+#define MLXPLAT_CPLD_LPC_REG_CPLD4_VER_OFFSET 0x03
#define MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFFSET 0x1d
#define MLXPLAT_CPLD_LPC_REG_RST_CAUSE1_OFFSET 0x1e
#define MLXPLAT_CPLD_LPC_REG_RST_CAUSE2_OFFSET 0x1f
@@ -33,6 +34,7 @@
#define MLXPLAT_CPLD_LPC_REG_LED3_OFFSET 0x22
#define MLXPLAT_CPLD_LPC_REG_LED4_OFFSET 0x23
#define MLXPLAT_CPLD_LPC_REG_LED5_OFFSET 0x24
+#define MLXPLAT_CPLD_LPC_REG_FAN_DIRECTION 0x2a
#define MLXPLAT_CPLD_LPC_REG_GP1_OFFSET 0x30
#define MLXPLAT_CPLD_LPC_REG_WP1_OFFSET 0x31
#define MLXPLAT_CPLD_LPC_REG_GP2_OFFSET 0x32
@@ -67,6 +69,9 @@
#define MLXPLAT_CPLD_LPC_REG_TACHO10_OFFSET 0xee
#define MLXPLAT_CPLD_LPC_REG_TACHO11_OFFSET 0xef
#define MLXPLAT_CPLD_LPC_REG_TACHO12_OFFSET 0xf0
+#define MLXPLAT_CPLD_LPC_REG_FAN_CAP1_OFFSET 0xf5
+#define MLXPLAT_CPLD_LPC_REG_FAN_CAP2_OFFSET 0xf6
+#define MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET 0xf7
#define MLXPLAT_CPLD_LPC_IO_RANGE 0x100
#define MLXPLAT_CPLD_LPC_I2C_CH1_OFF 0xdb
#define MLXPLAT_CPLD_LPC_I2C_CH2_OFF 0xda
@@ -584,36 +589,48 @@ static struct mlxreg_core_data mlxplat_mlxcpld_default_ng_fan_items_data[] = {
.label = "fan1",
.reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
.mask = BIT(0),
+ .capability = MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET,
+ .bit = BIT(0),
.hpdev.nr = MLXPLAT_CPLD_NR_NONE,
},
{
.label = "fan2",
.reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
.mask = BIT(1),
+ .capability = MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET,
+ .bit = BIT(1),
.hpdev.nr = MLXPLAT_CPLD_NR_NONE,
},
{
.label = "fan3",
.reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
.mask = BIT(2),
+ .capability = MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET,
+ .bit = BIT(2),
.hpdev.nr = MLXPLAT_CPLD_NR_NONE,
},
{
.label = "fan4",
.reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
.mask = BIT(3),
+ .capability = MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET,
+ .bit = BIT(3),
.hpdev.nr = MLXPLAT_CPLD_NR_NONE,
},
{
.label = "fan5",
.reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
.mask = BIT(4),
+ .capability = MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET,
+ .bit = BIT(4),
.hpdev.nr = MLXPLAT_CPLD_NR_NONE,
},
{
.label = "fan6",
.reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
.mask = BIT(5),
+ .capability = MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET,
+ .bit = BIT(5),
.hpdev.nr = MLXPLAT_CPLD_NR_NONE,
},
};
@@ -816,61 +833,90 @@ static struct mlxreg_core_data mlxplat_mlxcpld_default_ng_led_data[] = {
.label = "fan1:green",
.reg = MLXPLAT_CPLD_LPC_REG_LED2_OFFSET,
.mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK,
+ .capability = MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET,
+ .bit = BIT(0),
},
{
.label = "fan1:orange",
.reg = MLXPLAT_CPLD_LPC_REG_LED2_OFFSET,
.mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK,
+ .capability = MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET,
+ .bit = BIT(0),
},
{
.label = "fan2:green",
.reg = MLXPLAT_CPLD_LPC_REG_LED2_OFFSET,
.mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK,
+ .capability = MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET,
+ .bit = BIT(1),
},
{
.label = "fan2:orange",
.reg = MLXPLAT_CPLD_LPC_REG_LED2_OFFSET,
.mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK,
+ .capability = MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET,
+ .bit = BIT(1),
},
{
.label = "fan3:green",
.reg = MLXPLAT_CPLD_LPC_REG_LED3_OFFSET,
.mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK,
+ .capability = MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET,
+ .bit = BIT(2),
},
{
.label = "fan3:orange",
.reg = MLXPLAT_CPLD_LPC_REG_LED3_OFFSET,
.mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK,
+ .capability = MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET,
+ .bit = BIT(2),
},
{
.label = "fan4:green",
.reg = MLXPLAT_CPLD_LPC_REG_LED3_OFFSET,
.mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK,
+ .capability = MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET,
+ .bit = BIT(3),
},
{
.label = "fan4:orange",
.reg = MLXPLAT_CPLD_LPC_REG_LED3_OFFSET,
.mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK,
+ .capability = MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET,
+ .bit = BIT(3),
},
{
.label = "fan5:green",
.reg = MLXPLAT_CPLD_LPC_REG_LED4_OFFSET,
.mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK,
+ .capability = MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET,
+ .bit = BIT(4),
},
{
.label = "fan5:orange",
.reg = MLXPLAT_CPLD_LPC_REG_LED4_OFFSET,
.mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK,
+ .capability = MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET,
+ .bit = BIT(4),
},
{
.label = "fan6:green",
.reg = MLXPLAT_CPLD_LPC_REG_LED4_OFFSET,
.mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK,
+ .capability = MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET,
+ .bit = BIT(5),
},
{
.label = "fan6:orange",
.reg = MLXPLAT_CPLD_LPC_REG_LED4_OFFSET,
.mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK,
+ .capability = MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET,
+ .bit = BIT(5),
+ },
+ {
+ .label = "uid:blue",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED5_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK,
},
};
@@ -1100,6 +1146,12 @@ static struct mlxreg_core_data mlxplat_mlxcpld_default_ng_regs_io_data[] = {
.mode = 0444,
},
{
+ .label = "cpld4_version",
+ .reg = MLXPLAT_CPLD_LPC_REG_CPLD4_VER_OFFSET,
+ .bit = GENMASK(7, 0),
+ .mode = 0444,
+ },
+ {
.label = "reset_long_pb",
.reg = MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFFSET,
.mask = GENMASK(7, 0) & ~BIT(0),
@@ -1184,6 +1236,12 @@ static struct mlxreg_core_data mlxplat_mlxcpld_default_ng_regs_io_data[] = {
.bit = 1,
.mode = 0444,
},
+ {
+ .label = "fan_dir",
+ .reg = MLXPLAT_CPLD_LPC_REG_FAN_DIRECTION,
+ .bit = GENMASK(7, 0),
+ .mode = 0444,
+ },
};
static struct mlxreg_core_platform_data mlxplat_default_ng_regs_io_data = {
@@ -1201,61 +1259,85 @@ static struct mlxreg_core_data mlxplat_mlxcpld_default_fan_data[] = {
.label = "tacho1",
.reg = MLXPLAT_CPLD_LPC_REG_TACHO1_OFFSET,
.mask = GENMASK(7, 0),
+ .capability = MLXPLAT_CPLD_LPC_REG_FAN_CAP1_OFFSET,
+ .bit = BIT(0),
},
{
.label = "tacho2",
.reg = MLXPLAT_CPLD_LPC_REG_TACHO2_OFFSET,
.mask = GENMASK(7, 0),
+ .capability = MLXPLAT_CPLD_LPC_REG_FAN_CAP1_OFFSET,
+ .bit = BIT(1),
},
{
.label = "tacho3",
.reg = MLXPLAT_CPLD_LPC_REG_TACHO3_OFFSET,
.mask = GENMASK(7, 0),
+ .capability = MLXPLAT_CPLD_LPC_REG_FAN_CAP1_OFFSET,
+ .bit = BIT(2),
},
{
.label = "tacho4",
.reg = MLXPLAT_CPLD_LPC_REG_TACHO4_OFFSET,
.mask = GENMASK(7, 0),
+ .capability = MLXPLAT_CPLD_LPC_REG_FAN_CAP1_OFFSET,
+ .bit = BIT(3),
},
{
.label = "tacho5",
.reg = MLXPLAT_CPLD_LPC_REG_TACHO5_OFFSET,
.mask = GENMASK(7, 0),
+ .capability = MLXPLAT_CPLD_LPC_REG_FAN_CAP1_OFFSET,
+ .bit = BIT(4),
},
{
.label = "tacho6",
.reg = MLXPLAT_CPLD_LPC_REG_TACHO6_OFFSET,
.mask = GENMASK(7, 0),
+ .capability = MLXPLAT_CPLD_LPC_REG_FAN_CAP1_OFFSET,
+ .bit = BIT(5),
},
{
.label = "tacho7",
.reg = MLXPLAT_CPLD_LPC_REG_TACHO7_OFFSET,
.mask = GENMASK(7, 0),
+ .capability = MLXPLAT_CPLD_LPC_REG_FAN_CAP1_OFFSET,
+ .bit = BIT(6),
},
{
.label = "tacho8",
.reg = MLXPLAT_CPLD_LPC_REG_TACHO8_OFFSET,
.mask = GENMASK(7, 0),
+ .capability = MLXPLAT_CPLD_LPC_REG_FAN_CAP1_OFFSET,
+ .bit = BIT(7),
},
{
.label = "tacho9",
.reg = MLXPLAT_CPLD_LPC_REG_TACHO9_OFFSET,
.mask = GENMASK(7, 0),
+ .capability = MLXPLAT_CPLD_LPC_REG_FAN_CAP2_OFFSET,
+ .bit = BIT(0),
},
{
.label = "tacho10",
.reg = MLXPLAT_CPLD_LPC_REG_TACHO10_OFFSET,
.mask = GENMASK(7, 0),
+ .capability = MLXPLAT_CPLD_LPC_REG_FAN_CAP2_OFFSET,
+ .bit = BIT(1),
},
{
.label = "tacho11",
.reg = MLXPLAT_CPLD_LPC_REG_TACHO11_OFFSET,
.mask = GENMASK(7, 0),
+ .capability = MLXPLAT_CPLD_LPC_REG_FAN_CAP2_OFFSET,
+ .bit = BIT(2),
},
{
.label = "tacho12",
.reg = MLXPLAT_CPLD_LPC_REG_TACHO12_OFFSET,
.mask = GENMASK(7, 0),
+ .capability = MLXPLAT_CPLD_LPC_REG_FAN_CAP2_OFFSET,
+ .bit = BIT(3),
},
};
@@ -1299,6 +1381,7 @@ static bool mlxplat_mlxcpld_readable_reg(struct device *dev, unsigned int reg)
case MLXPLAT_CPLD_LPC_REG_CPLD1_VER_OFFSET:
case MLXPLAT_CPLD_LPC_REG_CPLD2_VER_OFFSET:
case MLXPLAT_CPLD_LPC_REG_CPLD3_VER_OFFSET:
+ case MLXPLAT_CPLD_LPC_REG_CPLD4_VER_OFFSET:
case MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFFSET:
case MLXPLAT_CPLD_LPC_REG_RST_CAUSE1_OFFSET:
case MLXPLAT_CPLD_LPC_REG_RST_CAUSE2_OFFSET:
@@ -1307,6 +1390,7 @@ static bool mlxplat_mlxcpld_readable_reg(struct device *dev, unsigned int reg)
case MLXPLAT_CPLD_LPC_REG_LED3_OFFSET:
case MLXPLAT_CPLD_LPC_REG_LED4_OFFSET:
case MLXPLAT_CPLD_LPC_REG_LED5_OFFSET:
+ case MLXPLAT_CPLD_LPC_REG_FAN_DIRECTION:
case MLXPLAT_CPLD_LPC_REG_GP1_OFFSET:
case MLXPLAT_CPLD_LPC_REG_WP1_OFFSET:
case MLXPLAT_CPLD_LPC_REG_GP2_OFFSET:
@@ -1341,6 +1425,9 @@ static bool mlxplat_mlxcpld_readable_reg(struct device *dev, unsigned int reg)
case MLXPLAT_CPLD_LPC_REG_TACHO11_OFFSET:
case MLXPLAT_CPLD_LPC_REG_TACHO12_OFFSET:
case MLXPLAT_CPLD_LPC_REG_PWM_CONTROL_OFFSET:
+ case MLXPLAT_CPLD_LPC_REG_FAN_CAP1_OFFSET:
+ case MLXPLAT_CPLD_LPC_REG_FAN_CAP2_OFFSET:
+ case MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET:
return true;
}
return false;
@@ -1352,6 +1439,7 @@ static bool mlxplat_mlxcpld_volatile_reg(struct device *dev, unsigned int reg)
case MLXPLAT_CPLD_LPC_REG_CPLD1_VER_OFFSET:
case MLXPLAT_CPLD_LPC_REG_CPLD2_VER_OFFSET:
case MLXPLAT_CPLD_LPC_REG_CPLD3_VER_OFFSET:
+ case MLXPLAT_CPLD_LPC_REG_CPLD4_VER_OFFSET:
case MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFFSET:
case MLXPLAT_CPLD_LPC_REG_RST_CAUSE1_OFFSET:
case MLXPLAT_CPLD_LPC_REG_RST_CAUSE2_OFFSET:
@@ -1360,6 +1448,7 @@ static bool mlxplat_mlxcpld_volatile_reg(struct device *dev, unsigned int reg)
case MLXPLAT_CPLD_LPC_REG_LED3_OFFSET:
case MLXPLAT_CPLD_LPC_REG_LED4_OFFSET:
case MLXPLAT_CPLD_LPC_REG_LED5_OFFSET:
+ case MLXPLAT_CPLD_LPC_REG_FAN_DIRECTION:
case MLXPLAT_CPLD_LPC_REG_GP1_OFFSET:
case MLXPLAT_CPLD_LPC_REG_GP2_OFFSET:
case MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET:
@@ -1392,6 +1481,9 @@ static bool mlxplat_mlxcpld_volatile_reg(struct device *dev, unsigned int reg)
case MLXPLAT_CPLD_LPC_REG_TACHO11_OFFSET:
case MLXPLAT_CPLD_LPC_REG_TACHO12_OFFSET:
case MLXPLAT_CPLD_LPC_REG_PWM_CONTROL_OFFSET:
+ case MLXPLAT_CPLD_LPC_REG_FAN_CAP1_OFFSET:
+ case MLXPLAT_CPLD_LPC_REG_FAN_CAP2_OFFSET:
+ case MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET:
return true;
}
return false;
@@ -1614,6 +1706,13 @@ static const struct dmi_system_id mlxplat_dmi_table[] __initconst = {
},
},
{
+ .callback = mlxplat_dmi_qmb7xx_matched,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "MSN38"),
+ },
+ },
+ {
.callback = mlxplat_dmi_default_matched,
.matches = {
DMI_MATCH(DMI_BOARD_NAME, "VMOD0001"),
@@ -1643,6 +1742,12 @@ static const struct dmi_system_id mlxplat_dmi_table[] __initconst = {
DMI_MATCH(DMI_BOARD_NAME, "VMOD0005"),
},
},
+ {
+ .callback = mlxplat_dmi_qmb7xx_matched,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "VMOD0007"),
+ },
+ },
{ }
};
diff --git a/drivers/platform/x86/pcengines-apuv2.c b/drivers/platform/x86/pcengines-apuv2.c
new file mode 100644
index 000000000000..c1ca931e1fab
--- /dev/null
+++ b/drivers/platform/x86/pcengines-apuv2.c
@@ -0,0 +1,260 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/*
+ * PC-Engines APUv2/APUv3 board platform driver
+ * for gpio buttons and LEDs
+ *
+ * Copyright (C) 2018 metux IT consult
+ * Author: Enrico Weigelt <info@metux.net>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/dmi.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/gpio_keys.h>
+#include <linux/gpio/machine.h>
+#include <linux/input.h>
+#include <linux/platform_data/gpio/gpio-amd-fch.h>
+
+/*
+ * NOTE: this driver only supports APUv2/3 - not APUv1, as this one
+ * has completely different register layouts
+ */
+
+/* register mappings */
+#define APU2_GPIO_REG_LED1 AMD_FCH_GPIO_REG_GPIO57
+#define APU2_GPIO_REG_LED2 AMD_FCH_GPIO_REG_GPIO58
+#define APU2_GPIO_REG_LED3 AMD_FCH_GPIO_REG_GPIO59_DEVSLP1
+#define APU2_GPIO_REG_MODESW AMD_FCH_GPIO_REG_GPIO32_GE1
+#define APU2_GPIO_REG_SIMSWAP AMD_FCH_GPIO_REG_GPIO33_GE2
+
+/* order in which the gpio lines are defined in the register list */
+#define APU2_GPIO_LINE_LED1 0
+#define APU2_GPIO_LINE_LED2 1
+#define APU2_GPIO_LINE_LED3 2
+#define APU2_GPIO_LINE_MODESW 3
+#define APU2_GPIO_LINE_SIMSWAP 4
+
+/* gpio device */
+
+static int apu2_gpio_regs[] = {
+ [APU2_GPIO_LINE_LED1] = APU2_GPIO_REG_LED1,
+ [APU2_GPIO_LINE_LED2] = APU2_GPIO_REG_LED2,
+ [APU2_GPIO_LINE_LED3] = APU2_GPIO_REG_LED3,
+ [APU2_GPIO_LINE_MODESW] = APU2_GPIO_REG_MODESW,
+ [APU2_GPIO_LINE_SIMSWAP] = APU2_GPIO_REG_SIMSWAP,
+};
+
+static const char * const apu2_gpio_names[] = {
+ [APU2_GPIO_LINE_LED1] = "front-led1",
+ [APU2_GPIO_LINE_LED2] = "front-led2",
+ [APU2_GPIO_LINE_LED3] = "front-led3",
+ [APU2_GPIO_LINE_MODESW] = "front-button",
+ [APU2_GPIO_LINE_SIMSWAP] = "simswap",
+};
+
+static const struct amd_fch_gpio_pdata board_apu2 = {
+ .gpio_num = ARRAY_SIZE(apu2_gpio_regs),
+ .gpio_reg = apu2_gpio_regs,
+ .gpio_names = apu2_gpio_names,
+};
+
+/* gpio leds device */
+
+static const struct gpio_led apu2_leds[] = {
+ { .name = "apu:green:1" },
+ { .name = "apu:green:2" },
+ { .name = "apu:green:3" }
+};
+
+static const struct gpio_led_platform_data apu2_leds_pdata = {
+ .num_leds = ARRAY_SIZE(apu2_leds),
+ .leds = apu2_leds,
+};
+
+struct gpiod_lookup_table gpios_led_table = {
+ .dev_id = "leds-gpio",
+ .table = {
+ GPIO_LOOKUP_IDX(AMD_FCH_GPIO_DRIVER_NAME, APU2_GPIO_LINE_LED1,
+ NULL, 0, GPIO_ACTIVE_LOW),
+ GPIO_LOOKUP_IDX(AMD_FCH_GPIO_DRIVER_NAME, APU2_GPIO_LINE_LED2,
+ NULL, 1, GPIO_ACTIVE_LOW),
+ GPIO_LOOKUP_IDX(AMD_FCH_GPIO_DRIVER_NAME, APU2_GPIO_LINE_LED3,
+ NULL, 2, GPIO_ACTIVE_LOW),
+ }
+};
+
+/* gpio keyboard device */
+
+static struct gpio_keys_button apu2_keys_buttons[] = {
+ {
+ .code = KEY_SETUP,
+ .active_low = 1,
+ .desc = "front button",
+ .type = EV_KEY,
+ .debounce_interval = 10,
+ .value = 1,
+ },
+};
+
+static const struct gpio_keys_platform_data apu2_keys_pdata = {
+ .buttons = apu2_keys_buttons,
+ .nbuttons = ARRAY_SIZE(apu2_keys_buttons),
+ .poll_interval = 100,
+ .rep = 0,
+ .name = "apu2-keys",
+};
+
+struct gpiod_lookup_table gpios_key_table = {
+ .dev_id = "gpio-keys-polled",
+ .table = {
+ GPIO_LOOKUP_IDX(AMD_FCH_GPIO_DRIVER_NAME, APU2_GPIO_LINE_MODESW,
+ NULL, 0, GPIO_ACTIVE_LOW),
+ }
+};
+
+/* board setup */
+
+/* note: matching works on string prefix, so "apu2" must come before "apu" */
+static const struct dmi_system_id apu_gpio_dmi_table[] __initconst = {
+
+ /* APU2 w/ legacy bios < 4.0.8 */
+ {
+ .ident = "apu2",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
+ DMI_MATCH(DMI_BOARD_NAME, "APU2")
+ },
+ .driver_data = (void *)&board_apu2,
+ },
+ /* APU2 w/ legacy bios >= 4.0.8 */
+ {
+ .ident = "apu2",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
+ DMI_MATCH(DMI_BOARD_NAME, "apu2")
+ },
+ .driver_data = (void *)&board_apu2,
+ },
+ /* APU2 w/ maainline bios */
+ {
+ .ident = "apu2",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
+ DMI_MATCH(DMI_BOARD_NAME, "PC Engines apu2")
+ },
+ .driver_data = (void *)&board_apu2,
+ },
+
+ /* APU3 w/ legacy bios < 4.0.8 */
+ {
+ .ident = "apu3",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
+ DMI_MATCH(DMI_BOARD_NAME, "APU3")
+ },
+ .driver_data = (void *)&board_apu2,
+ },
+ /* APU3 w/ legacy bios >= 4.0.8 */
+ {
+ .ident = "apu3",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
+ DMI_MATCH(DMI_BOARD_NAME, "apu3")
+ },
+ .driver_data = (void *)&board_apu2,
+ },
+ /* APU3 w/ mainline bios */
+ {
+ .ident = "apu3",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
+ DMI_MATCH(DMI_BOARD_NAME, "PC Engines apu3")
+ },
+ .driver_data = (void *)&board_apu2,
+ },
+ {}
+};
+
+static struct platform_device *apu_gpio_pdev;
+static struct platform_device *apu_leds_pdev;
+static struct platform_device *apu_keys_pdev;
+
+static struct platform_device * __init apu_create_pdev(
+ const char *name,
+ const void *pdata,
+ size_t sz)
+{
+ struct platform_device *pdev;
+
+ pdev = platform_device_register_resndata(NULL,
+ name,
+ PLATFORM_DEVID_NONE,
+ NULL,
+ 0,
+ pdata,
+ sz);
+
+ if (IS_ERR(pdev))
+ pr_err("failed registering %s: %ld\n", name, PTR_ERR(pdev));
+
+ return pdev;
+}
+
+static int __init apu_board_init(void)
+{
+ const struct dmi_system_id *id;
+
+ id = dmi_first_match(apu_gpio_dmi_table);
+ if (!id) {
+ pr_err("failed to detect apu board via dmi\n");
+ return -ENODEV;
+ }
+
+ gpiod_add_lookup_table(&gpios_led_table);
+ gpiod_add_lookup_table(&gpios_key_table);
+
+ apu_gpio_pdev = apu_create_pdev(
+ AMD_FCH_GPIO_DRIVER_NAME,
+ id->driver_data,
+ sizeof(struct amd_fch_gpio_pdata));
+
+ apu_leds_pdev = apu_create_pdev(
+ "leds-gpio",
+ &apu2_leds_pdata,
+ sizeof(apu2_leds_pdata));
+
+ apu_keys_pdev = apu_create_pdev(
+ "gpio-keys-polled",
+ &apu2_keys_pdata,
+ sizeof(apu2_keys_pdata));
+
+ return 0;
+}
+
+static void __exit apu_board_exit(void)
+{
+ gpiod_remove_lookup_table(&gpios_led_table);
+ gpiod_remove_lookup_table(&gpios_key_table);
+
+ platform_device_unregister(apu_keys_pdev);
+ platform_device_unregister(apu_leds_pdev);
+ platform_device_unregister(apu_gpio_pdev);
+}
+
+module_init(apu_board_init);
+module_exit(apu_board_exit);
+
+MODULE_AUTHOR("Enrico Weigelt, metux IT consult <info@metux.net>");
+MODULE_DESCRIPTION("PC Engines APUv2/APUv3 board GPIO/LED/keys driver");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(dmi, apu_gpio_dmi_table);
+MODULE_ALIAS("platform:pcengines-apuv2");
+MODULE_SOFTDEP("pre: platform:" AMD_FCH_GPIO_DRIVER_NAME);
+MODULE_SOFTDEP("pre: platform:leds-gpio");
+MODULE_SOFTDEP("pre: platform:gpio_keys_polled");
diff --git a/drivers/platform/x86/touchscreen_dmi.c b/drivers/platform/x86/touchscreen_dmi.c
index 8c5d47c0aea6..2d56ff7c8230 100644
--- a/drivers/platform/x86/touchscreen_dmi.c
+++ b/drivers/platform/x86/touchscreen_dmi.c
@@ -41,6 +41,20 @@ static const struct ts_dmi_data chuwi_hi8_data = {
.properties = chuwi_hi8_props,
};
+static const struct property_entry chuwi_hi8_air_props[] = {
+ PROPERTY_ENTRY_U32("touchscreen-size-x", 1728),
+ PROPERTY_ENTRY_U32("touchscreen-size-y", 1148),
+ PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
+ PROPERTY_ENTRY_STRING("firmware-name", "gsl3676-chuwi-hi8-air.fw"),
+ PROPERTY_ENTRY_U32("silead,max-fingers", 10),
+ { }
+};
+
+static const struct ts_dmi_data chuwi_hi8_air_data = {
+ .acpi_name = "MSSL1680:00",
+ .properties = chuwi_hi8_air_props,
+};
+
static const struct property_entry chuwi_hi8_pro_props[] = {
PROPERTY_ENTRY_U32("touchscreen-min-x", 6),
PROPERTY_ENTRY_U32("touchscreen-min-y", 3),
@@ -58,6 +72,25 @@ static const struct ts_dmi_data chuwi_hi8_pro_data = {
.properties = chuwi_hi8_pro_props,
};
+static const struct property_entry chuwi_hi10_air_props[] = {
+ PROPERTY_ENTRY_U32("touchscreen-size-x", 1981),
+ PROPERTY_ENTRY_U32("touchscreen-size-y", 1271),
+ PROPERTY_ENTRY_U32("touchscreen-min-x", 99),
+ PROPERTY_ENTRY_U32("touchscreen-min-y", 9),
+ PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
+ PROPERTY_ENTRY_U32("touchscreen-fuzz-x", 5),
+ PROPERTY_ENTRY_U32("touchscreen-fuzz-y", 4),
+ PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-chuwi-hi10-air.fw"),
+ PROPERTY_ENTRY_U32("silead,max-fingers", 10),
+ PROPERTY_ENTRY_BOOL("silead,home-button"),
+ { }
+};
+
+static const struct ts_dmi_data chuwi_hi10_air_data = {
+ .acpi_name = "MSSL1680:00",
+ .properties = chuwi_hi10_air_props,
+};
+
static const struct property_entry chuwi_vi8_props[] = {
PROPERTY_ENTRY_U32("touchscreen-min-x", 4),
PROPERTY_ENTRY_U32("touchscreen-min-y", 6),
@@ -369,6 +402,24 @@ static const struct ts_dmi_data pov_mobii_wintab_p800w_v21_data = {
.properties = pov_mobii_wintab_p800w_v21_props,
};
+static const struct property_entry pov_mobii_wintab_p1006w_v10_props[] = {
+ PROPERTY_ENTRY_U32("touchscreen-min-x", 1),
+ PROPERTY_ENTRY_U32("touchscreen-min-y", 3),
+ PROPERTY_ENTRY_U32("touchscreen-size-x", 1984),
+ PROPERTY_ENTRY_U32("touchscreen-size-y", 1520),
+ PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"),
+ PROPERTY_ENTRY_STRING("firmware-name",
+ "gsl3692-pov-mobii-wintab-p1006w-v10.fw"),
+ PROPERTY_ENTRY_U32("silead,max-fingers", 10),
+ PROPERTY_ENTRY_BOOL("silead,home-button"),
+ { }
+};
+
+static const struct ts_dmi_data pov_mobii_wintab_p1006w_v10_data = {
+ .acpi_name = "MSSL1680:00",
+ .properties = pov_mobii_wintab_p1006w_v10_props,
+};
+
static const struct property_entry teclast_x3_plus_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-x", 1980),
PROPERTY_ENTRY_U32("touchscreen-size-y", 1500),
@@ -498,6 +549,15 @@ static const struct dmi_system_id touchscreen_dmi_table[] = {
},
},
{
+ /* Chuwi Hi8 Air (CWI543) */
+ .driver_data = (void *)&chuwi_hi8_air_data,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Default string"),
+ DMI_MATCH(DMI_BOARD_NAME, "Cherry Trail CR"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Hi8 Air"),
+ },
+ },
+ {
/* Chuwi Hi8 Pro (CWI513) */
.driver_data = (void *)&chuwi_hi8_pro_data,
.matches = {
@@ -506,6 +566,14 @@ static const struct dmi_system_id touchscreen_dmi_table[] = {
},
},
{
+ /* Chuwi Hi10 Air */
+ .driver_data = (void *)&chuwi_hi10_air_data,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Hampoo"),
+ DMI_MATCH(DMI_PRODUCT_SKU, "P1W6_C109D_B"),
+ },
+ },
+ {
/* Chuwi Vi8 (CWI506) */
.driver_data = (void *)&chuwi_vi8_data,
.matches = {
@@ -707,6 +775,17 @@ static const struct dmi_system_id touchscreen_dmi_table[] = {
},
},
{
+ /* Point of View mobii wintab p1006w (v1.0) */
+ .driver_data = (void *)&pov_mobii_wintab_p1006w_v10_data,
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Insyde"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "BayTrail"),
+ /* Note 105b is Foxcon's USB/PCI vendor id */
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "105B"),
+ DMI_EXACT_MATCH(DMI_BOARD_NAME, "0E57"),
+ },
+ },
+ {
/* Teclast X3 Plus */
.driver_data = (void *)&teclast_x3_plus_data,
.matches = {
diff --git a/drivers/platform/x86/wmi-bmof.c b/drivers/platform/x86/wmi-bmof.c
index c4530ba715e8..8751a13134be 100644
--- a/drivers/platform/x86/wmi-bmof.c
+++ b/drivers/platform/x86/wmi-bmof.c
@@ -119,7 +119,7 @@ static struct wmi_driver wmi_bmof_driver = {
module_wmi_driver(wmi_bmof_driver);
-MODULE_ALIAS("wmi:" WMI_BMOF_GUID);
+MODULE_DEVICE_TABLE(wmi, wmi_bmof_id_table);
MODULE_AUTHOR("Andrew Lutomirski <luto@kernel.org>");
MODULE_DESCRIPTION("WMI embedded Binary MOF driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c
index bea35be68706..7b26b6ccf1a0 100644
--- a/drivers/platform/x86/wmi.c
+++ b/drivers/platform/x86/wmi.c
@@ -768,7 +768,10 @@ static int wmi_dev_match(struct device *dev, struct device_driver *driver)
struct wmi_block *wblock = dev_to_wblock(dev);
const struct wmi_device_id *id = wmi_driver->id_table;
- while (id->guid_string) {
+ if (id == NULL)
+ return 0;
+
+ while (*id->guid_string) {
uuid_le driver_guid;
if (WARN_ON(uuid_le_to_bin(id->guid_string, &driver_guid)))
diff --git a/drivers/power/reset/at91-reset.c b/drivers/power/reset/at91-reset.c
index f44a9ffcc2ab..44ca983a49a1 100644
--- a/drivers/power/reset/at91-reset.c
+++ b/drivers/power/reset/at91-reset.c
@@ -44,6 +44,9 @@ enum reset_type {
RESET_TYPE_WATCHDOG = 2,
RESET_TYPE_SOFTWARE = 3,
RESET_TYPE_USER = 4,
+ RESET_TYPE_CPU_FAIL = 6,
+ RESET_TYPE_XTAL_FAIL = 7,
+ RESET_TYPE_ULP2 = 8,
};
static void __iomem *at91_ramc_base[2], *at91_rstc_base;
@@ -164,6 +167,15 @@ static void __init at91_reset_status(struct platform_device *pdev)
case RESET_TYPE_USER:
reason = "user reset";
break;
+ case RESET_TYPE_CPU_FAIL:
+ reason = "CPU clock failure detection";
+ break;
+ case RESET_TYPE_XTAL_FAIL:
+ reason = "32.768 kHz crystal failure detection";
+ break;
+ case RESET_TYPE_ULP2:
+ reason = "ULP2 reset";
+ break;
default:
reason = "unknown reset";
break;
@@ -183,6 +195,7 @@ static const struct of_device_id at91_reset_of_match[] = {
{ .compatible = "atmel,at91sam9g45-rstc", .data = at91sam9g45_restart },
{ .compatible = "atmel,sama5d3-rstc", .data = sama5d3_restart },
{ .compatible = "atmel,samx7-rstc", .data = samx7_restart },
+ { .compatible = "microchip,sam9x60-rstc", .data = samx7_restart },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, at91_reset_of_match);
diff --git a/drivers/power/supply/axp288_fuel_gauge.c b/drivers/power/supply/axp288_fuel_gauge.c
index 084c8ba9749d..9ff2461820d8 100644
--- a/drivers/power/supply/axp288_fuel_gauge.c
+++ b/drivers/power/supply/axp288_fuel_gauge.c
@@ -307,22 +307,12 @@ static int fuel_gauge_debug_show(struct seq_file *s, void *data)
return 0;
}
-static int debug_open(struct inode *inode, struct file *file)
-{
- return single_open(file, fuel_gauge_debug_show, inode->i_private);
-}
-
-static const struct file_operations fg_debug_fops = {
- .open = debug_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(fuel_gauge_debug);
static void fuel_gauge_create_debugfs(struct axp288_fg_info *info)
{
info->debug_file = debugfs_create_file("fuelgauge", 0666, NULL,
- info, &fg_debug_fops);
+ info, &fuel_gauge_debug_fops);
}
static void fuel_gauge_remove_debugfs(struct axp288_fg_info *info)
diff --git a/drivers/power/supply/bq25890_charger.c b/drivers/power/supply/bq25890_charger.c
index 3f6fb49c956c..66991e6f75d9 100644
--- a/drivers/power/supply/bq25890_charger.c
+++ b/drivers/power/supply/bq25890_charger.c
@@ -436,7 +436,7 @@ static int bq25890_power_supply_get_property(struct power_supply *psy,
break;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
- val->intval = bq25890_tables[TBL_ICHG].rt.max;
+ val->intval = bq25890_find_val(bq->init_data.ichg, TBL_ICHG);
break;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
@@ -454,7 +454,7 @@ static int bq25890_power_supply_get_property(struct power_supply *psy,
break;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
- val->intval = bq25890_tables[TBL_VREG].rt.max;
+ val->intval = bq25890_find_val(bq->init_data.vreg, TBL_VREG);
break;
case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c
index 6dbbe95844a3..29b3a4056865 100644
--- a/drivers/power/supply/bq27xxx_battery.c
+++ b/drivers/power/supply/bq27xxx_battery.c
@@ -1555,27 +1555,14 @@ static bool bq27xxx_battery_dead(struct bq27xxx_device_info *di, u16 flags)
return flags & (BQ27XXX_FLAG_SOC1 | BQ27XXX_FLAG_SOCF);
}
-/*
- * Read flag register.
- * Return < 0 if something fails.
- */
static int bq27xxx_battery_read_health(struct bq27xxx_device_info *di)
{
- int flags;
- bool has_singe_flag = di->opts & BQ27XXX_O_ZERO;
-
- flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, has_singe_flag);
- if (flags < 0) {
- dev_err(di->dev, "error reading flag register:%d\n", flags);
- return flags;
- }
-
/* Unlikely but important to return first */
- if (unlikely(bq27xxx_battery_overtemp(di, flags)))
+ if (unlikely(bq27xxx_battery_overtemp(di, di->cache.flags)))
return POWER_SUPPLY_HEALTH_OVERHEAT;
- if (unlikely(bq27xxx_battery_undertemp(di, flags)))
+ if (unlikely(bq27xxx_battery_undertemp(di, di->cache.flags)))
return POWER_SUPPLY_HEALTH_COLD;
- if (unlikely(bq27xxx_battery_dead(di, flags)))
+ if (unlikely(bq27xxx_battery_dead(di, di->cache.flags)))
return POWER_SUPPLY_HEALTH_DEAD;
return POWER_SUPPLY_HEALTH_GOOD;
@@ -1612,6 +1599,7 @@ void bq27xxx_battery_update(struct bq27xxx_device_info *di)
cache.capacity = bq27xxx_battery_read_soc(di);
if (di->regs[BQ27XXX_REG_AE] != INVALID_REG_ADDR)
cache.energy = bq27xxx_battery_read_energy(di);
+ di->cache.flags = cache.flags;
cache.health = bq27xxx_battery_read_health(di);
}
if (di->regs[BQ27XXX_REG_CYCT] != INVALID_REG_ADDR)
diff --git a/drivers/power/supply/charger-manager.c b/drivers/power/supply/charger-manager.c
index 38be91f21cc4..2e8db5e6de0b 100644
--- a/drivers/power/supply/charger-manager.c
+++ b/drivers/power/supply/charger-manager.c
@@ -4,7 +4,7 @@
*
* This driver enables to monitor battery health and control charger
* during suspend-to-mem.
- * Charger manager depends on other devices. register this later than
+ * Charger manager depends on other devices. Register this later than
* the depending devices.
*
* This program is free software; you can redistribute it and/or modify
@@ -29,7 +29,7 @@
#include <linux/thermal.h>
/*
- * Default termperature threshold for charging.
+ * Default temperature threshold for charging.
* Every temperature units are in tenth of centigrade.
*/
#define CM_DEFAULT_RECHARGE_TEMP_DIFF 50
@@ -356,7 +356,7 @@ static bool is_polling_required(struct charger_manager *cm)
* Note that Charger Manager keeps the charger enabled regardless whether
* the charger is charging or not (because battery is full or no external
* power source exists) except when CM needs to disable chargers forcibly
- * bacause of emergency causes; when the battery is overheated or too cold.
+ * because of emergency causes; when the battery is overheated or too cold.
*/
static int try_charger_enable(struct charger_manager *cm, bool enable)
{
@@ -643,7 +643,7 @@ static int cm_check_thermal_status(struct charger_manager *cm)
if (ret) {
/* FIXME:
* No information of battery temperature might
- * occur hazadous result. We have to handle it
+ * occur hazardous result. We have to handle it
* depending on battery type.
*/
dev_err(cm->dev, "Failed to get battery temperature\n");
@@ -693,7 +693,7 @@ static bool _cm_monitor(struct charger_manager *cm)
uevent_notify(cm, default_event_names[temp_alrt]);
/*
- * Check whole charging duration and discharing duration
+ * Check whole charging duration and discharging duration
* after full-batt.
*/
} else if (!cm->emergency_stop && check_charging_duration(cm)) {
@@ -866,7 +866,7 @@ static void battout_handler(struct charger_manager *cm)
}
/**
- * misc_event_handler - Handler for other evnets
+ * misc_event_handler - Handler for other events
* @cm: the Charger Manager representing the battery.
* @type: the Charger Manager representing the battery.
*/
@@ -1218,7 +1218,7 @@ static int charger_extcon_init(struct charger_manager *cm,
}
/**
- * charger_manager_register_extcon - Register extcon device to recevie state
+ * charger_manager_register_extcon - Register extcon device to receive state
* of charger cable.
* @cm: the Charger Manager representing the battery.
*
@@ -1538,7 +1538,7 @@ static struct charger_desc *of_cm_parse_desc(struct device *dev)
of_property_read_u32(np, "cm-discharging-max",
&desc->discharging_max_duration_ms);
- /* battery charger regualtors */
+ /* battery charger regulators */
desc->num_charger_regulators = of_get_child_count(np);
if (desc->num_charger_regulators) {
struct charger_regulator *chg_regs;
@@ -1801,7 +1801,7 @@ static int charger_manager_probe(struct platform_device *pdev)
/*
* Charger-manager have to check the charging state right after
- * tialization of charger-manager and then update current charging
+ * initialization of charger-manager and then update current charging
* state.
*/
cm_monitor();
diff --git a/drivers/power/supply/cpcap-charger.c b/drivers/power/supply/cpcap-charger.c
index c843eaff8ad0..c3ed7b476676 100644
--- a/drivers/power/supply/cpcap-charger.c
+++ b/drivers/power/supply/cpcap-charger.c
@@ -458,6 +458,7 @@ static void cpcap_usb_detect(struct work_struct *work)
goto out_err;
}
+ power_supply_changed(ddata->usb);
return;
out_err:
diff --git a/drivers/power/supply/ds2782_battery.c b/drivers/power/supply/ds2782_battery.c
index 019c58493e3d..04b0fe7d7d62 100644
--- a/drivers/power/supply/ds2782_battery.c
+++ b/drivers/power/supply/ds2782_battery.c
@@ -319,17 +319,17 @@ static void ds278x_power_supply_init(struct power_supply_desc *battery)
static int ds278x_battery_remove(struct i2c_client *client)
{
struct ds278x_info *info = i2c_get_clientdata(client);
+ int id = info->id;
power_supply_unregister(info->battery);
+ cancel_delayed_work_sync(&info->bat_work);
kfree(info->battery_desc.name);
+ kfree(info);
mutex_lock(&battery_lock);
- idr_remove(&battery_id, info->id);
+ idr_remove(&battery_id, id);
mutex_unlock(&battery_lock);
- cancel_delayed_work(&info->bat_work);
-
- kfree(info);
return 0;
}
diff --git a/drivers/power/supply/goldfish_battery.c b/drivers/power/supply/goldfish_battery.c
index f5c525e4482a..ad969d9fc981 100644
--- a/drivers/power/supply/goldfish_battery.c
+++ b/drivers/power/supply/goldfish_battery.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL
/*
* Power supply driver for the goldfish emulator
*
@@ -5,15 +6,6 @@
* Copyright (C) 2012 Intel, Inc.
* Copyright (C) 2013 Intel, Inc.
* Author: Mike Lockwood <lockwood@android.com>
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/module.h>
@@ -40,27 +32,30 @@ struct goldfish_battery_data {
#define GOLDFISH_BATTERY_WRITE(data, addr, x) \
(writel(x, data->reg_base + addr))
-/*
- * Temporary variable used between goldfish_battery_probe() and
- * goldfish_battery_open().
- */
-static struct goldfish_battery_data *battery_data;
-
enum {
/* status register */
- BATTERY_INT_STATUS = 0x00,
+ BATTERY_INT_STATUS = 0x00,
/* set this to enable IRQ */
- BATTERY_INT_ENABLE = 0x04,
-
- BATTERY_AC_ONLINE = 0x08,
- BATTERY_STATUS = 0x0C,
- BATTERY_HEALTH = 0x10,
- BATTERY_PRESENT = 0x14,
- BATTERY_CAPACITY = 0x18,
+ BATTERY_INT_ENABLE = 0x04,
+
+ BATTERY_AC_ONLINE = 0x08,
+ BATTERY_STATUS = 0x0C,
+ BATTERY_HEALTH = 0x10,
+ BATTERY_PRESENT = 0x14,
+ BATTERY_CAPACITY = 0x18,
+ BATTERY_VOLTAGE = 0x1C,
+ BATTERY_TEMP = 0x20,
+ BATTERY_CHARGE_COUNTER = 0x24,
+ BATTERY_VOLTAGE_MAX = 0x28,
+ BATTERY_CURRENT_MAX = 0x2C,
+ BATTERY_CURRENT_NOW = 0x30,
+ BATTERY_CURRENT_AVG = 0x34,
+ BATTERY_CHARGE_FULL_UAH = 0x38,
+ BATTERY_CYCLE_COUNT = 0x40,
BATTERY_STATUS_CHANGED = 1U << 0,
AC_STATUS_CHANGED = 1U << 1,
- BATTERY_INT_MASK = BATTERY_STATUS_CHANGED | AC_STATUS_CHANGED,
+ BATTERY_INT_MASK = BATTERY_STATUS_CHANGED | AC_STATUS_CHANGED,
};
@@ -75,6 +70,12 @@ static int goldfish_ac_get_property(struct power_supply *psy,
case POWER_SUPPLY_PROP_ONLINE:
val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_AC_ONLINE);
break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX:
+ val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_VOLTAGE_MAX);
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_MAX:
+ val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_CURRENT_MAX);
+ break;
default:
ret = -EINVAL;
break;
@@ -105,6 +106,29 @@ static int goldfish_battery_get_property(struct power_supply *psy,
case POWER_SUPPLY_PROP_CAPACITY:
val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_CAPACITY);
break;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_VOLTAGE);
+ break;
+ case POWER_SUPPLY_PROP_TEMP:
+ val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_TEMP);
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_COUNTER:
+ val->intval = GOLDFISH_BATTERY_READ(data,
+ BATTERY_CHARGE_COUNTER);
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_CURRENT_NOW);
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_AVG:
+ val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_CURRENT_AVG);
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_FULL:
+ val->intval = GOLDFISH_BATTERY_READ(data,
+ BATTERY_CHARGE_FULL_UAH);
+ break;
+ case POWER_SUPPLY_PROP_CYCLE_COUNT:
+ val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_CYCLE_COUNT);
+ break;
default:
ret = -EINVAL;
break;
@@ -119,10 +143,19 @@ static enum power_supply_property goldfish_battery_props[] = {
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_TECHNOLOGY,
POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_TEMP,
+ POWER_SUPPLY_PROP_CHARGE_COUNTER,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_CURRENT_AVG,
+ POWER_SUPPLY_PROP_CHARGE_FULL,
+ POWER_SUPPLY_PROP_CYCLE_COUNT,
};
static enum power_supply_property goldfish_ac_props[] = {
POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_VOLTAGE_MAX,
+ POWER_SUPPLY_PROP_CURRENT_MAX,
};
static irqreturn_t goldfish_battery_interrupt(int irq, void *dev_id)
@@ -193,8 +226,9 @@ static int goldfish_battery_probe(struct platform_device *pdev)
return -ENODEV;
}
- ret = devm_request_irq(&pdev->dev, data->irq, goldfish_battery_interrupt,
- IRQF_SHARED, pdev->name, data);
+ ret = devm_request_irq(&pdev->dev, data->irq,
+ goldfish_battery_interrupt,
+ IRQF_SHARED, pdev->name, data);
if (ret)
return ret;
@@ -212,7 +246,6 @@ static int goldfish_battery_probe(struct platform_device *pdev)
}
platform_set_drvdata(pdev, data);
- battery_data = data;
GOLDFISH_BATTERY_WRITE(data, BATTERY_INT_ENABLE, BATTERY_INT_MASK);
return 0;
@@ -224,7 +257,6 @@ static int goldfish_battery_remove(struct platform_device *pdev)
power_supply_unregister(data->battery);
power_supply_unregister(data->ac);
- battery_data = NULL;
return 0;
}
diff --git a/drivers/power/supply/isp1704_charger.c b/drivers/power/supply/isp1704_charger.c
index 95af5f305838..a63cb5dcfa08 100644
--- a/drivers/power/supply/isp1704_charger.c
+++ b/drivers/power/supply/isp1704_charger.c
@@ -30,13 +30,12 @@
#include <linux/power_supply.h>
#include <linux/delay.h>
#include <linux/of.h>
-#include <linux/of_gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/usb/otg.h>
#include <linux/usb/ulpi.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
-#include <linux/power/isp1704_charger.h>
/* Vendor specific Power Control register */
#define ISP1704_PWR_CTRL 0x3d
@@ -60,6 +59,7 @@ struct isp1704_charger {
struct device *dev;
struct power_supply *psy;
struct power_supply_desc psy_desc;
+ struct gpio_desc *enable_gpio;
struct usb_phy *phy;
struct notifier_block nb;
struct work_struct work;
@@ -81,18 +81,9 @@ static inline int isp1704_write(struct isp1704_charger *isp, u32 reg, u32 val)
return usb_phy_io_write(isp->phy, val, reg);
}
-/*
- * Disable/enable the power from the isp1704 if a function for it
- * has been provided with platform data.
- */
static void isp1704_charger_set_power(struct isp1704_charger *isp, bool on)
{
- struct isp1704_charger_data *board = isp->dev->platform_data;
-
- if (board && board->set_power)
- board->set_power(on);
- else if (board)
- gpio_set_value(board->enable_gpio, on);
+ gpiod_set_value(isp->enable_gpio, on);
}
/*
@@ -405,46 +396,19 @@ static int isp1704_charger_probe(struct platform_device *pdev)
int ret = -ENODEV;
struct power_supply_config psy_cfg = {};
- struct isp1704_charger_data *pdata = dev_get_platdata(&pdev->dev);
- struct device_node *np = pdev->dev.of_node;
-
- if (np) {
- int gpio = of_get_named_gpio(np, "nxp,enable-gpio", 0);
-
- if (gpio < 0) {
- dev_err(&pdev->dev, "missing DT GPIO nxp,enable-gpio\n");
- return gpio;
- }
-
- pdata = devm_kzalloc(&pdev->dev,
- sizeof(struct isp1704_charger_data), GFP_KERNEL);
- if (!pdata) {
- ret = -ENOMEM;
- goto fail0;
- }
- pdata->enable_gpio = gpio;
-
- dev_info(&pdev->dev, "init gpio %d\n", pdata->enable_gpio);
-
- ret = devm_gpio_request_one(&pdev->dev, pdata->enable_gpio,
- GPIOF_OUT_INIT_HIGH, "isp1704_reset");
- if (ret) {
- dev_err(&pdev->dev, "gpio request failed\n");
- goto fail0;
- }
- }
-
- if (!pdata) {
- dev_err(&pdev->dev, "missing platform data!\n");
- return -ENODEV;
- }
-
-
isp = devm_kzalloc(&pdev->dev, sizeof(*isp), GFP_KERNEL);
if (!isp)
return -ENOMEM;
- if (np)
+ isp->enable_gpio = devm_gpiod_get(&pdev->dev, "nxp,enable",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(isp->enable_gpio)) {
+ ret = PTR_ERR(isp->enable_gpio);
+ dev_err(&pdev->dev, "Could not get reset gpio: %d\n", ret);
+ return ret;
+ }
+
+ if (pdev->dev.of_node)
isp->phy = devm_usb_get_phy_by_phandle(&pdev->dev, "usb-phy", 0);
else
isp->phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c
index 2a8d75e5e930..581c6bd23388 100644
--- a/drivers/power/supply/max17042_battery.c
+++ b/drivers/power/supply/max17042_battery.c
@@ -995,6 +995,13 @@ static const struct power_supply_desc max17042_no_current_sense_psy_desc = {
.num_properties = ARRAY_SIZE(max17042_battery_props) - 2,
};
+static void max17042_stop_work(void *data)
+{
+ struct max17042_chip *chip = data;
+
+ cancel_work_sync(&chip->work);
+}
+
static int max17042_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@@ -1101,6 +1108,9 @@ static int max17042_probe(struct i2c_client *client,
regmap_read(chip->regmap, MAX17042_STATUS, &val);
if (val & STATUS_POR_BIT) {
INIT_WORK(&chip->work, max17042_init_worker);
+ ret = devm_add_action(&client->dev, max17042_stop_work, chip);
+ if (ret)
+ return ret;
schedule_work(&chip->work);
} else {
chip->init_complete = 1;
diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c
index 569790ea6917..c917a8b43b2b 100644
--- a/drivers/power/supply/power_supply_core.c
+++ b/drivers/power/supply/power_supply_core.c
@@ -156,8 +156,6 @@ static void power_supply_deferred_register_work(struct work_struct *work)
}
#ifdef CONFIG_OF
-#include <linux/of.h>
-
static int __power_supply_populate_supplied_from(struct device *dev,
void *data)
{
@@ -575,6 +573,7 @@ int power_supply_get_battery_info(struct power_supply *psy,
info->energy_full_design_uwh = -EINVAL;
info->charge_full_design_uah = -EINVAL;
info->voltage_min_design_uv = -EINVAL;
+ info->voltage_max_design_uv = -EINVAL;
info->precharge_current_ua = -EINVAL;
info->charge_term_current_ua = -EINVAL;
info->constant_charge_current_max_ua = -EINVAL;
@@ -615,6 +614,8 @@ int power_supply_get_battery_info(struct power_supply *psy,
&info->charge_full_design_uah);
of_property_read_u32(battery_np, "voltage-min-design-microvolt",
&info->voltage_min_design_uv);
+ of_property_read_u32(battery_np, "voltage-max-design-microvolt",
+ &info->voltage_max_design_uv);
of_property_read_u32(battery_np, "precharge-current-microamp",
&info->precharge_current_ua);
of_property_read_u32(battery_np, "charge-term-current-microamp",
diff --git a/drivers/power/supply/sc27xx_fuel_gauge.c b/drivers/power/supply/sc27xx_fuel_gauge.c
index 76da1895b782..24895cc3b41e 100644
--- a/drivers/power/supply/sc27xx_fuel_gauge.c
+++ b/drivers/power/supply/sc27xx_fuel_gauge.c
@@ -72,6 +72,7 @@
* @lock: protect the structure
* @gpiod: GPIO for battery detection
* @channel: IIO channel to get battery temperature
+ * @charge_chan: IIO channel to get charge voltage
* @internal_resist: the battery internal resistance in mOhm
* @total_cap: the total capacity of the battery in mAh
* @init_cap: the initial capacity of the battery in mAh
@@ -92,6 +93,7 @@ struct sc27xx_fgu_data {
struct mutex lock;
struct gpio_desc *gpiod;
struct iio_channel *channel;
+ struct iio_channel *charge_chan;
bool bat_present;
int internal_resist;
int total_cap;
@@ -169,10 +171,37 @@ static int sc27xx_fgu_save_boot_mode(struct sc27xx_fgu_data *data,
if (ret)
return ret;
+ /*
+ * Since the user area registers are put on power always-on region,
+ * then these registers changing time will be a little long. Thus
+ * here we should delay 200us to wait until values are updated
+ * successfully according to the datasheet.
+ */
+ udelay(200);
+
+ ret = regmap_update_bits(data->regmap,
+ data->base + SC27XX_FGU_USER_AREA_SET,
+ SC27XX_FGU_MODE_AREA_MASK,
+ boot_mode << SC27XX_FGU_MODE_AREA_SHIFT);
+ if (ret)
+ return ret;
+
+ /*
+ * Since the user area registers are put on power always-on region,
+ * then these registers changing time will be a little long. Thus
+ * here we should delay 200us to wait until values are updated
+ * successfully according to the datasheet.
+ */
+ udelay(200);
+
+ /*
+ * According to the datasheet, we should set the USER_AREA_CLEAR to 0 to
+ * make the user area data available, otherwise we can not save the user
+ * area data.
+ */
return regmap_update_bits(data->regmap,
- data->base + SC27XX_FGU_USER_AREA_SET,
- SC27XX_FGU_MODE_AREA_MASK,
- boot_mode << SC27XX_FGU_MODE_AREA_SHIFT);
+ data->base + SC27XX_FGU_USER_AREA_CLEAR,
+ SC27XX_FGU_MODE_AREA_MASK, 0);
}
static int sc27xx_fgu_save_last_cap(struct sc27xx_fgu_data *data, int cap)
@@ -186,9 +215,36 @@ static int sc27xx_fgu_save_last_cap(struct sc27xx_fgu_data *data, int cap)
if (ret)
return ret;
+ /*
+ * Since the user area registers are put on power always-on region,
+ * then these registers changing time will be a little long. Thus
+ * here we should delay 200us to wait until values are updated
+ * successfully according to the datasheet.
+ */
+ udelay(200);
+
+ ret = regmap_update_bits(data->regmap,
+ data->base + SC27XX_FGU_USER_AREA_SET,
+ SC27XX_FGU_CAP_AREA_MASK, cap);
+ if (ret)
+ return ret;
+
+ /*
+ * Since the user area registers are put on power always-on region,
+ * then these registers changing time will be a little long. Thus
+ * here we should delay 200us to wait until values are updated
+ * successfully according to the datasheet.
+ */
+ udelay(200);
+
+ /*
+ * According to the datasheet, we should set the USER_AREA_CLEAR to 0 to
+ * make the user area data available, otherwise we can not save the user
+ * area data.
+ */
return regmap_update_bits(data->regmap,
- data->base + SC27XX_FGU_USER_AREA_SET,
- SC27XX_FGU_CAP_AREA_MASK, cap);
+ data->base + SC27XX_FGU_USER_AREA_CLEAR,
+ SC27XX_FGU_CAP_AREA_MASK, 0);
}
static int sc27xx_fgu_read_last_cap(struct sc27xx_fgu_data *data, int *cap)
@@ -391,6 +447,18 @@ static int sc27xx_fgu_get_vbat_ocv(struct sc27xx_fgu_data *data, int *val)
return 0;
}
+static int sc27xx_fgu_get_charge_vol(struct sc27xx_fgu_data *data, int *val)
+{
+ int ret, vol;
+
+ ret = iio_read_channel_processed(data->charge_chan, &vol);
+ if (ret < 0)
+ return ret;
+
+ *val = vol * 1000;
+ return 0;
+}
+
static int sc27xx_fgu_get_temp(struct sc27xx_fgu_data *data, int *temp)
{
return iio_read_channel_processed(data->channel, temp);
@@ -502,6 +570,14 @@ static int sc27xx_fgu_get_property(struct power_supply *psy,
val->intval = value;
break;
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+ ret = sc27xx_fgu_get_charge_vol(data, &value);
+ if (ret)
+ goto error;
+
+ val->intval = value;
+ break;
+
case POWER_SUPPLY_PROP_CURRENT_NOW:
case POWER_SUPPLY_PROP_CURRENT_AVG:
ret = sc27xx_fgu_get_current(data, &value);
@@ -567,6 +643,7 @@ static enum power_supply_property sc27xx_fgu_props[] = {
POWER_SUPPLY_PROP_VOLTAGE_OCV,
POWER_SUPPLY_PROP_CURRENT_NOW,
POWER_SUPPLY_PROP_CURRENT_AVG,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
};
static const struct power_supply_desc sc27xx_fgu_desc = {
@@ -708,7 +785,7 @@ static int sc27xx_fgu_cap_to_clbcnt(struct sc27xx_fgu_data *data, int capacity)
* Convert current capacity (mAh) to coulomb counter according to the
* formula: 1 mAh =3.6 coulomb.
*/
- return DIV_ROUND_CLOSEST(cur_cap * 36, 10);
+ return DIV_ROUND_CLOSEST(cur_cap * 36 * data->cur_1000ma_adc, 10);
}
static int sc27xx_fgu_calibration(struct sc27xx_fgu_data *data)
@@ -907,6 +984,12 @@ static int sc27xx_fgu_probe(struct platform_device *pdev)
return PTR_ERR(data->channel);
}
+ data->charge_chan = devm_iio_channel_get(&pdev->dev, "charge-vol");
+ if (IS_ERR(data->charge_chan)) {
+ dev_err(&pdev->dev, "failed to get charge IIO channel\n");
+ return PTR_ERR(data->charge_chan);
+ }
+
data->gpiod = devm_gpiod_get(&pdev->dev, "bat-detect", GPIOD_IN);
if (IS_ERR(data->gpiod)) {
dev_err(&pdev->dev, "failed to get battery detection GPIO\n");
diff --git a/drivers/power/supply/twl4030_charger.c b/drivers/power/supply/twl4030_charger.c
index 0e202d4273fb..4299873a1118 100644
--- a/drivers/power/supply/twl4030_charger.c
+++ b/drivers/power/supply/twl4030_charger.c
@@ -809,7 +809,9 @@ static int twl4030_bci_get_property(struct power_supply *psy,
is_charging = state & TWL4030_MSTATEC_AC;
if (!is_charging) {
u8 s;
- twl4030_bci_read(TWL4030_BCIMDEN, &s);
+ ret = twl4030_bci_read(TWL4030_BCIMDEN, &s);
+ if (ret < 0)
+ return ret;
if (psy->desc->type == POWER_SUPPLY_TYPE_USB)
is_charging = s & 1;
else
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index a8f47df0655a..54f8238aac0d 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -192,14 +192,23 @@ config PWM_IMG
To compile this driver as a module, choose M here: the module
will be called pwm-img
-config PWM_IMX
- tristate "i.MX PWM support"
+config PWM_IMX1
+ tristate "i.MX1 PWM support"
depends on ARCH_MXC
help
- Generic PWM framework driver for i.MX.
+ Generic PWM framework driver for i.MX1 and i.MX21
To compile this driver as a module, choose M here: the module
- will be called pwm-imx.
+ will be called pwm-imx1.
+
+config PWM_IMX27
+ tristate "i.MX27 PWM support"
+ depends on ARCH_MXC
+ help
+ Generic PWM framework driver for i.MX27 and later i.MX SoCs.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-imx27.
config PWM_JZ4740
tristate "Ingenic JZ47xx PWM support"
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index 9c676a0dadf5..448825e892bc 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -17,7 +17,8 @@ obj-$(CONFIG_PWM_EP93XX) += pwm-ep93xx.o
obj-$(CONFIG_PWM_FSL_FTM) += pwm-fsl-ftm.o
obj-$(CONFIG_PWM_HIBVT) += pwm-hibvt.o
obj-$(CONFIG_PWM_IMG) += pwm-img.o
-obj-$(CONFIG_PWM_IMX) += pwm-imx.o
+obj-$(CONFIG_PWM_IMX1) += pwm-imx1.o
+obj-$(CONFIG_PWM_IMX27) += pwm-imx27.o
obj-$(CONFIG_PWM_JZ4740) += pwm-jz4740.o
obj-$(CONFIG_PWM_LP3943) += pwm-lp3943.o
obj-$(CONFIG_PWM_LPC18XX_SCT) += pwm-lpc18xx-sct.o
diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
index 1581f6ab1b1f..3149204567f3 100644
--- a/drivers/pwm/core.c
+++ b/drivers/pwm/core.c
@@ -472,7 +472,10 @@ int pwm_apply_state(struct pwm_device *pwm, struct pwm_state *state)
state->duty_cycle > state->period)
return -EINVAL;
- if (!memcmp(state, &pwm->state, sizeof(*state)))
+ if (state->period == pwm->state.period &&
+ state->duty_cycle == pwm->state.duty_cycle &&
+ state->polarity == pwm->state.polarity &&
+ state->enabled == pwm->state.enabled)
return 0;
if (pwm->chip->ops->apply) {
@@ -1033,10 +1036,7 @@ static int pwm_seq_show(struct seq_file *s, void *v)
dev_name(chip->dev), chip->npwm,
(chip->npwm != 1) ? "s" : "");
- if (chip->ops->dbg_show)
- chip->ops->dbg_show(chip, s);
- else
- pwm_dbg_show(chip, s);
+ pwm_dbg_show(chip, s);
return 0;
}
diff --git a/drivers/pwm/pwm-atmel.c b/drivers/pwm/pwm-atmel.c
index 530d7dc5f1b5..a9fd6f0d408c 100644
--- a/drivers/pwm/pwm-atmel.c
+++ b/drivers/pwm/pwm-atmel.c
@@ -48,16 +48,6 @@
#define PWMV2_CPRD 0x0C
#define PWMV2_CPRDUPD 0x10
-/*
- * Max value for duty and period
- *
- * Although the duty and period register is 32 bit,
- * however only the LSB 16 bits are significant.
- */
-#define PWM_MAX_DTY 0xFFFF
-#define PWM_MAX_PRD 0xFFFF
-#define PRD_MAX_PRES 10
-
struct atmel_pwm_registers {
u8 period;
u8 period_upd;
@@ -65,11 +55,21 @@ struct atmel_pwm_registers {
u8 duty_upd;
};
+struct atmel_pwm_config {
+ u32 max_period;
+ u32 max_pres;
+};
+
+struct atmel_pwm_data {
+ struct atmel_pwm_registers regs;
+ struct atmel_pwm_config cfg;
+};
+
struct atmel_pwm_chip {
struct pwm_chip chip;
struct clk *clk;
void __iomem *base;
- const struct atmel_pwm_registers *regs;
+ const struct atmel_pwm_data *data;
unsigned int updated_pwms;
/* ISR is cleared when read, ensure only one thread does that */
@@ -121,10 +121,10 @@ static int atmel_pwm_calculate_cprd_and_pres(struct pwm_chip *chip,
cycles *= clk_get_rate(atmel_pwm->clk);
do_div(cycles, NSEC_PER_SEC);
- for (*pres = 0; cycles > PWM_MAX_PRD; cycles >>= 1)
+ for (*pres = 0; cycles > atmel_pwm->data->cfg.max_period; cycles >>= 1)
(*pres)++;
- if (*pres > PRD_MAX_PRES) {
+ if (*pres > atmel_pwm->data->cfg.max_pres) {
dev_err(chip->dev, "pres exceeds the maximum value\n");
return -EINVAL;
}
@@ -150,15 +150,15 @@ static void atmel_pwm_update_cdty(struct pwm_chip *chip, struct pwm_device *pwm,
struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip);
u32 val;
- if (atmel_pwm->regs->duty_upd ==
- atmel_pwm->regs->period_upd) {
+ if (atmel_pwm->data->regs.duty_upd ==
+ atmel_pwm->data->regs.period_upd) {
val = atmel_pwm_ch_readl(atmel_pwm, pwm->hwpwm, PWM_CMR);
val &= ~PWM_CMR_UPD_CDTY;
atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, PWM_CMR, val);
}
atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm,
- atmel_pwm->regs->duty_upd, cdty);
+ atmel_pwm->data->regs.duty_upd, cdty);
}
static void atmel_pwm_set_cprd_cdty(struct pwm_chip *chip,
@@ -168,9 +168,9 @@ static void atmel_pwm_set_cprd_cdty(struct pwm_chip *chip,
struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip);
atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm,
- atmel_pwm->regs->duty, cdty);
+ atmel_pwm->data->regs.duty, cdty);
atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm,
- atmel_pwm->regs->period, cprd);
+ atmel_pwm->data->regs.period, cprd);
}
static void atmel_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm,
@@ -225,7 +225,7 @@ static int atmel_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
cstate.polarity == state->polarity &&
cstate.period == state->period) {
cprd = atmel_pwm_ch_readl(atmel_pwm, pwm->hwpwm,
- atmel_pwm->regs->period);
+ atmel_pwm->data->regs.period);
atmel_pwm_calculate_cdty(state, cprd, &cdty);
atmel_pwm_update_cdty(chip, pwm, cdty);
return 0;
@@ -277,27 +277,55 @@ static const struct pwm_ops atmel_pwm_ops = {
.owner = THIS_MODULE,
};
-static const struct atmel_pwm_registers atmel_pwm_regs_v1 = {
- .period = PWMV1_CPRD,
- .period_upd = PWMV1_CUPD,
- .duty = PWMV1_CDTY,
- .duty_upd = PWMV1_CUPD,
+static const struct atmel_pwm_data atmel_sam9rl_pwm_data = {
+ .regs = {
+ .period = PWMV1_CPRD,
+ .period_upd = PWMV1_CUPD,
+ .duty = PWMV1_CDTY,
+ .duty_upd = PWMV1_CUPD,
+ },
+ .cfg = {
+ /* 16 bits to keep period and duty. */
+ .max_period = 0xffff,
+ .max_pres = 10,
+ },
+};
+
+static const struct atmel_pwm_data atmel_sama5_pwm_data = {
+ .regs = {
+ .period = PWMV2_CPRD,
+ .period_upd = PWMV2_CPRDUPD,
+ .duty = PWMV2_CDTY,
+ .duty_upd = PWMV2_CDTYUPD,
+ },
+ .cfg = {
+ /* 16 bits to keep period and duty. */
+ .max_period = 0xffff,
+ .max_pres = 10,
+ },
};
-static const struct atmel_pwm_registers atmel_pwm_regs_v2 = {
- .period = PWMV2_CPRD,
- .period_upd = PWMV2_CPRDUPD,
- .duty = PWMV2_CDTY,
- .duty_upd = PWMV2_CDTYUPD,
+static const struct atmel_pwm_data mchp_sam9x60_pwm_data = {
+ .regs = {
+ .period = PWMV1_CPRD,
+ .period_upd = PWMV1_CUPD,
+ .duty = PWMV1_CDTY,
+ .duty_upd = PWMV1_CUPD,
+ },
+ .cfg = {
+ /* 32 bits to keep period and duty. */
+ .max_period = 0xffffffff,
+ .max_pres = 10,
+ },
};
static const struct platform_device_id atmel_pwm_devtypes[] = {
{
.name = "at91sam9rl-pwm",
- .driver_data = (kernel_ulong_t)&atmel_pwm_regs_v1,
+ .driver_data = (kernel_ulong_t)&atmel_sam9rl_pwm_data,
}, {
.name = "sama5d3-pwm",
- .driver_data = (kernel_ulong_t)&atmel_pwm_regs_v2,
+ .driver_data = (kernel_ulong_t)&atmel_sama5_pwm_data,
}, {
/* sentinel */
},
@@ -307,20 +335,23 @@ MODULE_DEVICE_TABLE(platform, atmel_pwm_devtypes);
static const struct of_device_id atmel_pwm_dt_ids[] = {
{
.compatible = "atmel,at91sam9rl-pwm",
- .data = &atmel_pwm_regs_v1,
+ .data = &atmel_sam9rl_pwm_data,
}, {
.compatible = "atmel,sama5d3-pwm",
- .data = &atmel_pwm_regs_v2,
+ .data = &atmel_sama5_pwm_data,
}, {
.compatible = "atmel,sama5d2-pwm",
- .data = &atmel_pwm_regs_v2,
+ .data = &atmel_sama5_pwm_data,
+ }, {
+ .compatible = "microchip,sam9x60-pwm",
+ .data = &mchp_sam9x60_pwm_data,
}, {
/* sentinel */
},
};
MODULE_DEVICE_TABLE(of, atmel_pwm_dt_ids);
-static inline const struct atmel_pwm_registers *
+static inline const struct atmel_pwm_data *
atmel_pwm_get_driver_data(struct platform_device *pdev)
{
const struct platform_device_id *id;
@@ -330,18 +361,18 @@ atmel_pwm_get_driver_data(struct platform_device *pdev)
id = platform_get_device_id(pdev);
- return (struct atmel_pwm_registers *)id->driver_data;
+ return (struct atmel_pwm_data *)id->driver_data;
}
static int atmel_pwm_probe(struct platform_device *pdev)
{
- const struct atmel_pwm_registers *regs;
+ const struct atmel_pwm_data *data;
struct atmel_pwm_chip *atmel_pwm;
struct resource *res;
int ret;
- regs = atmel_pwm_get_driver_data(pdev);
- if (!regs)
+ data = atmel_pwm_get_driver_data(pdev);
+ if (!data)
return -ENODEV;
atmel_pwm = devm_kzalloc(&pdev->dev, sizeof(*atmel_pwm), GFP_KERNEL);
@@ -373,7 +404,7 @@ static int atmel_pwm_probe(struct platform_device *pdev)
atmel_pwm->chip.base = -1;
atmel_pwm->chip.npwm = 4;
- atmel_pwm->regs = regs;
+ atmel_pwm->data = data;
atmel_pwm->updated_pwms = 0;
mutex_init(&atmel_pwm->isr_lock);
diff --git a/drivers/pwm/pwm-bcm-kona.c b/drivers/pwm/pwm-bcm-kona.c
index 09a95aeb3a70..81da91df2529 100644
--- a/drivers/pwm/pwm-bcm-kona.c
+++ b/drivers/pwm/pwm-bcm-kona.c
@@ -45,25 +45,25 @@
* high or low depending on its state at that exact instant.
*/
-#define PWM_CONTROL_OFFSET (0x00000000)
+#define PWM_CONTROL_OFFSET 0x00000000
#define PWM_CONTROL_SMOOTH_SHIFT(chan) (24 + (chan))
#define PWM_CONTROL_TYPE_SHIFT(chan) (16 + (chan))
#define PWM_CONTROL_POLARITY_SHIFT(chan) (8 + (chan))
#define PWM_CONTROL_TRIGGER_SHIFT(chan) (chan)
-#define PRESCALE_OFFSET (0x00000004)
+#define PRESCALE_OFFSET 0x00000004
#define PRESCALE_SHIFT(chan) ((chan) << 2)
#define PRESCALE_MASK(chan) (0x7 << PRESCALE_SHIFT(chan))
-#define PRESCALE_MIN (0x00000000)
-#define PRESCALE_MAX (0x00000007)
+#define PRESCALE_MIN 0x00000000
+#define PRESCALE_MAX 0x00000007
#define PERIOD_COUNT_OFFSET(chan) (0x00000008 + ((chan) << 3))
-#define PERIOD_COUNT_MIN (0x00000002)
-#define PERIOD_COUNT_MAX (0x00ffffff)
+#define PERIOD_COUNT_MIN 0x00000002
+#define PERIOD_COUNT_MAX 0x00ffffff
#define DUTY_CYCLE_HIGH_OFFSET(chan) (0x0000000c + ((chan) << 3))
-#define DUTY_CYCLE_HIGH_MIN (0x00000000)
-#define DUTY_CYCLE_HIGH_MAX (0x00ffffff)
+#define DUTY_CYCLE_HIGH_MIN 0x00000000
+#define DUTY_CYCLE_HIGH_MAX 0x00ffffff
struct kona_pwmc {
struct pwm_chip chip;
diff --git a/drivers/pwm/pwm-hibvt.c b/drivers/pwm/pwm-hibvt.c
index 27c107e78d59..a0b09603d13d 100644
--- a/drivers/pwm/pwm-hibvt.c
+++ b/drivers/pwm/pwm-hibvt.c
@@ -49,15 +49,30 @@ struct hibvt_pwm_chip {
struct clk *clk;
void __iomem *base;
struct reset_control *rstc;
+ const struct hibvt_pwm_soc *soc;
};
struct hibvt_pwm_soc {
u32 num_pwms;
+ bool quirk_force_enable;
};
-static const struct hibvt_pwm_soc pwm_soc[2] = {
- { .num_pwms = 4 },
- { .num_pwms = 8 },
+static const struct hibvt_pwm_soc hi3516cv300_soc_info = {
+ .num_pwms = 4,
+};
+
+static const struct hibvt_pwm_soc hi3519v100_soc_info = {
+ .num_pwms = 8,
+};
+
+static const struct hibvt_pwm_soc hi3559v100_shub_soc_info = {
+ .num_pwms = 8,
+ .quirk_force_enable = true,
+};
+
+static const struct hibvt_pwm_soc hi3559v100_soc_info = {
+ .num_pwms = 2,
+ .quirk_force_enable = true,
};
static inline struct hibvt_pwm_chip *to_hibvt_pwm_chip(struct pwm_chip *chip)
@@ -148,13 +163,23 @@ static void hibvt_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
static int hibvt_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
struct pwm_state *state)
{
+ struct hibvt_pwm_chip *hi_pwm_chip = to_hibvt_pwm_chip(chip);
+
if (state->polarity != pwm->state.polarity)
hibvt_pwm_set_polarity(chip, pwm, state->polarity);
if (state->period != pwm->state.period ||
- state->duty_cycle != pwm->state.duty_cycle)
+ state->duty_cycle != pwm->state.duty_cycle) {
hibvt_pwm_config(chip, pwm, state->duty_cycle, state->period);
+ /*
+ * Some implementations require the PWM to be enabled twice
+ * each time the duty cycle is refreshed.
+ */
+ if (hi_pwm_chip->soc->quirk_force_enable && state->enabled)
+ hibvt_pwm_enable(chip, pwm);
+ }
+
if (state->enabled != pwm->state.enabled) {
if (state->enabled)
hibvt_pwm_enable(chip, pwm);
@@ -198,6 +223,7 @@ static int hibvt_pwm_probe(struct platform_device *pdev)
pwm_chip->chip.npwm = soc->num_pwms;
pwm_chip->chip.of_xlate = of_pwm_xlate_with_flags;
pwm_chip->chip.of_pwm_n_cells = 3;
+ pwm_chip->soc = soc;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
pwm_chip->base = devm_ioremap_resource(&pdev->dev, res);
@@ -250,8 +276,14 @@ static int hibvt_pwm_remove(struct platform_device *pdev)
}
static const struct of_device_id hibvt_pwm_of_match[] = {
- { .compatible = "hisilicon,hi3516cv300-pwm", .data = &pwm_soc[0] },
- { .compatible = "hisilicon,hi3519v100-pwm", .data = &pwm_soc[1] },
+ { .compatible = "hisilicon,hi3516cv300-pwm",
+ .data = &hi3516cv300_soc_info },
+ { .compatible = "hisilicon,hi3519v100-pwm",
+ .data = &hi3519v100_soc_info },
+ { .compatible = "hisilicon,hi3559v100-shub-pwm",
+ .data = &hi3559v100_shub_soc_info },
+ { .compatible = "hisilicon,hi3559v100-pwm",
+ .data = &hi3559v100_soc_info },
{ }
};
MODULE_DEVICE_TABLE(of, hibvt_pwm_of_match);
diff --git a/drivers/pwm/pwm-imx1.c b/drivers/pwm/pwm-imx1.c
new file mode 100644
index 000000000000..f8b2c2e001a7
--- /dev/null
+++ b/drivers/pwm/pwm-imx1.c
@@ -0,0 +1,199 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * simple driver for PWM (Pulse Width Modulator) controller
+ *
+ * Derived from pxa PWM driver by eric miao <eric.miao@marvell.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/slab.h>
+
+#define MX1_PWMC 0x00 /* PWM Control Register */
+#define MX1_PWMS 0x04 /* PWM Sample Register */
+#define MX1_PWMP 0x08 /* PWM Period Register */
+
+#define MX1_PWMC_EN BIT(4)
+
+struct pwm_imx1_chip {
+ struct clk *clk_ipg;
+ struct clk *clk_per;
+ void __iomem *mmio_base;
+ struct pwm_chip chip;
+};
+
+#define to_pwm_imx1_chip(chip) container_of(chip, struct pwm_imx1_chip, chip)
+
+static int pwm_imx1_clk_prepare_enable(struct pwm_chip *chip)
+{
+ struct pwm_imx1_chip *imx = to_pwm_imx1_chip(chip);
+ int ret;
+
+ ret = clk_prepare_enable(imx->clk_ipg);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(imx->clk_per);
+ if (ret) {
+ clk_disable_unprepare(imx->clk_ipg);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void pwm_imx1_clk_disable_unprepare(struct pwm_chip *chip)
+{
+ struct pwm_imx1_chip *imx = to_pwm_imx1_chip(chip);
+
+ clk_disable_unprepare(imx->clk_per);
+ clk_disable_unprepare(imx->clk_ipg);
+}
+
+static int pwm_imx1_config(struct pwm_chip *chip,
+ struct pwm_device *pwm, int duty_ns, int period_ns)
+{
+ struct pwm_imx1_chip *imx = to_pwm_imx1_chip(chip);
+ u32 max, p;
+
+ /*
+ * The PWM subsystem allows for exact frequencies. However,
+ * I cannot connect a scope on my device to the PWM line and
+ * thus cannot provide the program the PWM controller
+ * exactly. Instead, I'm relying on the fact that the
+ * Bootloader (u-boot or WinCE+haret) has programmed the PWM
+ * function group already. So I'll just modify the PWM sample
+ * register to follow the ratio of duty_ns vs. period_ns
+ * accordingly.
+ *
+ * This is good enough for programming the brightness of
+ * the LCD backlight.
+ *
+ * The real implementation would divide PERCLK[0] first by
+ * both the prescaler (/1 .. /128) and then by CLKSEL
+ * (/2 .. /16).
+ */
+ max = readl(imx->mmio_base + MX1_PWMP);
+ p = max * duty_ns / period_ns;
+
+ writel(max - p, imx->mmio_base + MX1_PWMS);
+
+ return 0;
+}
+
+static int pwm_imx1_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct pwm_imx1_chip *imx = to_pwm_imx1_chip(chip);
+ u32 value;
+ int ret;
+
+ ret = pwm_imx1_clk_prepare_enable(chip);
+ if (ret < 0)
+ return ret;
+
+ value = readl(imx->mmio_base + MX1_PWMC);
+ value |= MX1_PWMC_EN;
+ writel(value, imx->mmio_base + MX1_PWMC);
+
+ return 0;
+}
+
+static void pwm_imx1_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct pwm_imx1_chip *imx = to_pwm_imx1_chip(chip);
+ u32 value;
+
+ value = readl(imx->mmio_base + MX1_PWMC);
+ value &= ~MX1_PWMC_EN;
+ writel(value, imx->mmio_base + MX1_PWMC);
+
+ pwm_imx1_clk_disable_unprepare(chip);
+}
+
+static const struct pwm_ops pwm_imx1_ops = {
+ .enable = pwm_imx1_enable,
+ .disable = pwm_imx1_disable,
+ .config = pwm_imx1_config,
+ .owner = THIS_MODULE,
+};
+
+static const struct of_device_id pwm_imx1_dt_ids[] = {
+ { .compatible = "fsl,imx1-pwm", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, pwm_imx1_dt_ids);
+
+static int pwm_imx1_probe(struct platform_device *pdev)
+{
+ struct pwm_imx1_chip *imx;
+ struct resource *r;
+
+ imx = devm_kzalloc(&pdev->dev, sizeof(*imx), GFP_KERNEL);
+ if (!imx)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, imx);
+
+ imx->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
+ if (IS_ERR(imx->clk_ipg)) {
+ dev_err(&pdev->dev, "getting ipg clock failed with %ld\n",
+ PTR_ERR(imx->clk_ipg));
+ return PTR_ERR(imx->clk_ipg);
+ }
+
+ imx->clk_per = devm_clk_get(&pdev->dev, "per");
+ if (IS_ERR(imx->clk_per)) {
+ int ret = PTR_ERR(imx->clk_per);
+
+ if (ret != -EPROBE_DEFER)
+ dev_err(&pdev->dev,
+ "failed to get peripheral clock: %d\n",
+ ret);
+
+ return ret;
+ }
+
+ imx->chip.ops = &pwm_imx1_ops;
+ imx->chip.dev = &pdev->dev;
+ imx->chip.base = -1;
+ imx->chip.npwm = 1;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ imx->mmio_base = devm_ioremap_resource(&pdev->dev, r);
+ if (IS_ERR(imx->mmio_base))
+ return PTR_ERR(imx->mmio_base);
+
+ return pwmchip_add(&imx->chip);
+}
+
+static int pwm_imx1_remove(struct platform_device *pdev)
+{
+ struct pwm_imx1_chip *imx = platform_get_drvdata(pdev);
+
+ pwm_imx1_clk_disable_unprepare(&imx->chip);
+
+ return pwmchip_remove(&imx->chip);
+}
+
+static struct platform_driver pwm_imx1_driver = {
+ .driver = {
+ .name = "pwm-imx1",
+ .of_match_table = pwm_imx1_dt_ids,
+ },
+ .probe = pwm_imx1_probe,
+ .remove = pwm_imx1_remove,
+};
+module_platform_driver(pwm_imx1_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx27.c
index 55a3a363d5be..806130654211 100644
--- a/drivers/pwm/pwm-imx.c
+++ b/drivers/pwm/pwm-imx27.c
@@ -19,16 +19,6 @@
#include <linux/pwm.h>
#include <linux/slab.h>
-/* i.MX1 and i.MX21 share the same PWM function block: */
-
-#define MX1_PWMC 0x00 /* PWM Control Register */
-#define MX1_PWMS 0x04 /* PWM Sample Register */
-#define MX1_PWMP 0x08 /* PWM Period Register */
-
-#define MX1_PWMC_EN BIT(4)
-
-/* i.MX27, i.MX31, i.MX35 share the same PWM function block: */
-
#define MX3_PWMCR 0x00 /* PWM Control Register */
#define MX3_PWMSR 0x04 /* PWM Status Register */
#define MX3_PWMSAR 0x0C /* PWM Sample Register */
@@ -86,21 +76,18 @@
/* PWMPR register value of 0xffff has the same effect as 0xfffe */
#define MX3_PWMPR_MAX 0xfffe
-struct imx_chip {
+struct pwm_imx27_chip {
struct clk *clk_ipg;
-
struct clk *clk_per;
-
void __iomem *mmio_base;
-
struct pwm_chip chip;
};
-#define to_imx_chip(chip) container_of(chip, struct imx_chip, chip)
+#define to_pwm_imx27_chip(chip) container_of(chip, struct pwm_imx27_chip, chip)
-static int imx_pwm_clk_prepare_enable(struct pwm_chip *chip)
+static int pwm_imx27_clk_prepare_enable(struct pwm_chip *chip)
{
- struct imx_chip *imx = to_imx_chip(chip);
+ struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip);
int ret;
ret = clk_prepare_enable(imx->clk_ipg);
@@ -116,35 +103,32 @@ static int imx_pwm_clk_prepare_enable(struct pwm_chip *chip)
return 0;
}
-static void imx_pwm_clk_disable_unprepare(struct pwm_chip *chip)
+static void pwm_imx27_clk_disable_unprepare(struct pwm_chip *chip)
{
- struct imx_chip *imx = to_imx_chip(chip);
+ struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip);
clk_disable_unprepare(imx->clk_per);
clk_disable_unprepare(imx->clk_ipg);
}
-static void imx_pwm_get_state(struct pwm_chip *chip,
- struct pwm_device *pwm, struct pwm_state *state)
+static void pwm_imx27_get_state(struct pwm_chip *chip,
+ struct pwm_device *pwm, struct pwm_state *state)
{
- struct imx_chip *imx = to_imx_chip(chip);
- u32 period, prescaler, pwm_clk, ret, val;
+ struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip);
+ u32 period, prescaler, pwm_clk, val;
u64 tmp;
+ int ret;
- ret = imx_pwm_clk_prepare_enable(chip);
+ ret = pwm_imx27_clk_prepare_enable(chip);
if (ret < 0)
return;
val = readl(imx->mmio_base + MX3_PWMCR);
- if (val & MX3_PWMCR_EN) {
+ if (val & MX3_PWMCR_EN)
state->enabled = true;
- ret = imx_pwm_clk_prepare_enable(chip);
- if (ret)
- return;
- } else {
+ else
state->enabled = false;
- }
switch (FIELD_GET(MX3_PWMCR_POUTC, val)) {
case MX3_PWMCR_POUTC_NORMAL:
@@ -176,70 +160,13 @@ static void imx_pwm_get_state(struct pwm_chip *chip,
state->duty_cycle = 0;
}
- imx_pwm_clk_disable_unprepare(chip);
-}
-
-static int imx_pwm_config_v1(struct pwm_chip *chip,
- struct pwm_device *pwm, int duty_ns, int period_ns)
-{
- struct imx_chip *imx = to_imx_chip(chip);
-
- /*
- * The PWM subsystem allows for exact frequencies. However,
- * I cannot connect a scope on my device to the PWM line and
- * thus cannot provide the program the PWM controller
- * exactly. Instead, I'm relying on the fact that the
- * Bootloader (u-boot or WinCE+haret) has programmed the PWM
- * function group already. So I'll just modify the PWM sample
- * register to follow the ratio of duty_ns vs. period_ns
- * accordingly.
- *
- * This is good enough for programming the brightness of
- * the LCD backlight.
- *
- * The real implementation would divide PERCLK[0] first by
- * both the prescaler (/1 .. /128) and then by CLKSEL
- * (/2 .. /16).
- */
- u32 max = readl(imx->mmio_base + MX1_PWMP);
- u32 p = max * duty_ns / period_ns;
- writel(max - p, imx->mmio_base + MX1_PWMS);
-
- return 0;
-}
-
-static int imx_pwm_enable_v1(struct pwm_chip *chip, struct pwm_device *pwm)
-{
- struct imx_chip *imx = to_imx_chip(chip);
- u32 val;
- int ret;
-
- ret = imx_pwm_clk_prepare_enable(chip);
- if (ret < 0)
- return ret;
-
- val = readl(imx->mmio_base + MX1_PWMC);
- val |= MX1_PWMC_EN;
- writel(val, imx->mmio_base + MX1_PWMC);
-
- return 0;
-}
-
-static void imx_pwm_disable_v1(struct pwm_chip *chip, struct pwm_device *pwm)
-{
- struct imx_chip *imx = to_imx_chip(chip);
- u32 val;
-
- val = readl(imx->mmio_base + MX1_PWMC);
- val &= ~MX1_PWMC_EN;
- writel(val, imx->mmio_base + MX1_PWMC);
-
- imx_pwm_clk_disable_unprepare(chip);
+ if (!state->enabled)
+ pwm_imx27_clk_disable_unprepare(chip);
}
-static void imx_pwm_sw_reset(struct pwm_chip *chip)
+static void pwm_imx27_sw_reset(struct pwm_chip *chip)
{
- struct imx_chip *imx = to_imx_chip(chip);
+ struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip);
struct device *dev = chip->dev;
int wait_count = 0;
u32 cr;
@@ -255,10 +182,10 @@ static void imx_pwm_sw_reset(struct pwm_chip *chip)
dev_warn(dev, "software reset timeout\n");
}
-static void imx_pwm_wait_fifo_slot(struct pwm_chip *chip,
- struct pwm_device *pwm)
+static void pwm_imx27_wait_fifo_slot(struct pwm_chip *chip,
+ struct pwm_device *pwm)
{
- struct imx_chip *imx = to_imx_chip(chip);
+ struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip);
struct device *dev = chip->dev;
unsigned int period_ms;
int fifoav;
@@ -277,11 +204,11 @@ static void imx_pwm_wait_fifo_slot(struct pwm_chip *chip,
}
}
-static int imx_pwm_apply_v2(struct pwm_chip *chip, struct pwm_device *pwm,
- struct pwm_state *state)
+static int pwm_imx27_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ struct pwm_state *state)
{
unsigned long period_cycles, duty_cycles, prescale;
- struct imx_chip *imx = to_imx_chip(chip);
+ struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip);
struct pwm_state cstate;
unsigned long long c;
int ret;
@@ -318,13 +245,13 @@ static int imx_pwm_apply_v2(struct pwm_chip *chip, struct pwm_device *pwm,
* enabled.
*/
if (cstate.enabled) {
- imx_pwm_wait_fifo_slot(chip, pwm);
+ pwm_imx27_wait_fifo_slot(chip, pwm);
} else {
- ret = imx_pwm_clk_prepare_enable(chip);
+ ret = pwm_imx27_clk_prepare_enable(chip);
if (ret)
return ret;
- imx_pwm_sw_reset(chip);
+ pwm_imx27_sw_reset(chip);
}
writel(duty_cycles, imx->mmio_base + MX3_PWMSAR);
@@ -343,64 +270,35 @@ static int imx_pwm_apply_v2(struct pwm_chip *chip, struct pwm_device *pwm,
} else if (cstate.enabled) {
writel(0, imx->mmio_base + MX3_PWMCR);
- imx_pwm_clk_disable_unprepare(chip);
+ pwm_imx27_clk_disable_unprepare(chip);
}
return 0;
}
-static const struct pwm_ops imx_pwm_ops_v1 = {
- .enable = imx_pwm_enable_v1,
- .disable = imx_pwm_disable_v1,
- .config = imx_pwm_config_v1,
+static const struct pwm_ops pwm_imx27_ops = {
+ .apply = pwm_imx27_apply,
+ .get_state = pwm_imx27_get_state,
.owner = THIS_MODULE,
};
-static const struct pwm_ops imx_pwm_ops_v2 = {
- .apply = imx_pwm_apply_v2,
- .get_state = imx_pwm_get_state,
- .owner = THIS_MODULE,
-};
-
-struct imx_pwm_data {
- bool polarity_supported;
- const struct pwm_ops *ops;
-};
-
-static struct imx_pwm_data imx_pwm_data_v1 = {
- .ops = &imx_pwm_ops_v1,
-};
-
-static struct imx_pwm_data imx_pwm_data_v2 = {
- .polarity_supported = true,
- .ops = &imx_pwm_ops_v2,
-};
-
-static const struct of_device_id imx_pwm_dt_ids[] = {
- { .compatible = "fsl,imx1-pwm", .data = &imx_pwm_data_v1, },
- { .compatible = "fsl,imx27-pwm", .data = &imx_pwm_data_v2, },
+static const struct of_device_id pwm_imx27_dt_ids[] = {
+ { .compatible = "fsl,imx27-pwm", },
{ /* sentinel */ }
};
-MODULE_DEVICE_TABLE(of, imx_pwm_dt_ids);
+MODULE_DEVICE_TABLE(of, pwm_imx27_dt_ids);
-static int imx_pwm_probe(struct platform_device *pdev)
+static int pwm_imx27_probe(struct platform_device *pdev)
{
- const struct of_device_id *of_id =
- of_match_device(imx_pwm_dt_ids, &pdev->dev);
- const struct imx_pwm_data *data;
- struct imx_chip *imx;
+ struct pwm_imx27_chip *imx;
struct resource *r;
- int ret = 0;
-
- if (!of_id)
- return -ENODEV;
-
- data = of_id->data;
imx = devm_kzalloc(&pdev->dev, sizeof(*imx), GFP_KERNEL);
if (imx == NULL)
return -ENOMEM;
+ platform_set_drvdata(pdev, imx);
+
imx->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
if (IS_ERR(imx->clk_ipg)) {
dev_err(&pdev->dev, "getting ipg clock failed with %ld\n",
@@ -410,57 +308,51 @@ static int imx_pwm_probe(struct platform_device *pdev)
imx->clk_per = devm_clk_get(&pdev->dev, "per");
if (IS_ERR(imx->clk_per)) {
- dev_err(&pdev->dev, "getting per clock failed with %ld\n",
- PTR_ERR(imx->clk_per));
- return PTR_ERR(imx->clk_per);
+ int ret = PTR_ERR(imx->clk_per);
+
+ if (ret != -EPROBE_DEFER)
+ dev_err(&pdev->dev,
+ "failed to get peripheral clock: %d\n",
+ ret);
+
+ return ret;
}
- imx->chip.ops = data->ops;
+ imx->chip.ops = &pwm_imx27_ops;
imx->chip.dev = &pdev->dev;
imx->chip.base = -1;
imx->chip.npwm = 1;
- if (data->polarity_supported) {
- dev_dbg(&pdev->dev, "PWM supports output inversion\n");
- imx->chip.of_xlate = of_pwm_xlate_with_flags;
- imx->chip.of_pwm_n_cells = 3;
- }
+ imx->chip.of_xlate = of_pwm_xlate_with_flags;
+ imx->chip.of_pwm_n_cells = 3;
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
imx->mmio_base = devm_ioremap_resource(&pdev->dev, r);
if (IS_ERR(imx->mmio_base))
return PTR_ERR(imx->mmio_base);
- ret = pwmchip_add(&imx->chip);
- if (ret < 0)
- return ret;
-
- platform_set_drvdata(pdev, imx);
- return 0;
+ return pwmchip_add(&imx->chip);
}
-static int imx_pwm_remove(struct platform_device *pdev)
+static int pwm_imx27_remove(struct platform_device *pdev)
{
- struct imx_chip *imx;
+ struct pwm_imx27_chip *imx;
imx = platform_get_drvdata(pdev);
- if (imx == NULL)
- return -ENODEV;
- imx_pwm_clk_disable_unprepare(&imx->chip);
+ pwm_imx27_clk_disable_unprepare(&imx->chip);
return pwmchip_remove(&imx->chip);
}
static struct platform_driver imx_pwm_driver = {
- .driver = {
- .name = "imx-pwm",
- .of_match_table = imx_pwm_dt_ids,
+ .driver = {
+ .name = "pwm-imx27",
+ .of_match_table = pwm_imx27_dt_ids,
},
- .probe = imx_pwm_probe,
- .remove = imx_pwm_remove,
+ .probe = pwm_imx27_probe,
+ .remove = pwm_imx27_remove,
};
-
module_platform_driver(imx_pwm_driver);
MODULE_LICENSE("GPL v2");
diff --git a/drivers/pwm/pwm-mtk-disp.c b/drivers/pwm/pwm-mtk-disp.c
index 893940d45f0d..15803c71fe80 100644
--- a/drivers/pwm/pwm-mtk-disp.c
+++ b/drivers/pwm/pwm-mtk-disp.c
@@ -277,10 +277,21 @@ static const struct mtk_pwm_data mt8173_pwm_data = {
.commit_mask = 0x1,
};
+static const struct mtk_pwm_data mt8183_pwm_data = {
+ .enable_mask = BIT(0),
+ .con0 = 0x18,
+ .con0_sel = 0x0,
+ .con1 = 0x1c,
+ .has_commit = false,
+ .bls_debug = 0x80,
+ .bls_debug_mask = 0x3,
+};
+
static const struct of_device_id mtk_disp_pwm_of_match[] = {
{ .compatible = "mediatek,mt2701-disp-pwm", .data = &mt2701_pwm_data},
{ .compatible = "mediatek,mt6595-disp-pwm", .data = &mt8173_pwm_data},
{ .compatible = "mediatek,mt8173-disp-pwm", .data = &mt8173_pwm_data},
+ { .compatible = "mediatek,mt8183-disp-pwm", .data = &mt8183_pwm_data},
{ }
};
MODULE_DEVICE_TABLE(of, mtk_disp_pwm_of_match);
diff --git a/drivers/pwm/pwm-rcar.c b/drivers/pwm/pwm-rcar.c
index a41812fc6f95..cfe7dd1b448e 100644
--- a/drivers/pwm/pwm-rcar.c
+++ b/drivers/pwm/pwm-rcar.c
@@ -8,6 +8,8 @@
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/io.h>
+#include <linux/log2.h>
+#include <linux/math64.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
@@ -68,19 +70,15 @@ static void rcar_pwm_update(struct rcar_pwm_chip *rp, u32 mask, u32 data,
static int rcar_pwm_get_clock_division(struct rcar_pwm_chip *rp, int period_ns)
{
unsigned long clk_rate = clk_get_rate(rp->clk);
- unsigned long long max; /* max cycle / nanoseconds */
- unsigned int div;
+ u64 div, tmp;
if (clk_rate == 0)
return -EINVAL;
- for (div = 0; div <= RCAR_PWM_MAX_DIVISION; div++) {
- max = (unsigned long long)NSEC_PER_SEC * RCAR_PWM_MAX_CYCLE *
- (1 << div);
- do_div(max, clk_rate);
- if (period_ns <= max)
- break;
- }
+ div = (u64)NSEC_PER_SEC * RCAR_PWM_MAX_CYCLE;
+ tmp = (u64)period_ns * clk_rate + div - 1;
+ tmp = div64_u64(tmp, div);
+ div = ilog2(tmp - 1) + 1;
return (div <= RCAR_PWM_MAX_DIVISION) ? div : -ERANGE;
}
@@ -139,39 +137,8 @@ static void rcar_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
pm_runtime_put(chip->dev);
}
-static int rcar_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
- int duty_ns, int period_ns)
+static int rcar_pwm_enable(struct rcar_pwm_chip *rp)
{
- struct rcar_pwm_chip *rp = to_rcar_pwm_chip(chip);
- int div, ret;
-
- div = rcar_pwm_get_clock_division(rp, period_ns);
- if (div < 0)
- return div;
-
- /*
- * Let the core driver set pwm->period if disabled and duty_ns == 0.
- * But, this driver should prevent to set the new duty_ns if current
- * duty_cycle is not set
- */
- if (!pwm_is_enabled(pwm) && !duty_ns && !pwm->state.duty_cycle)
- return 0;
-
- rcar_pwm_update(rp, RCAR_PWMCR_SYNC, RCAR_PWMCR_SYNC, RCAR_PWMCR);
-
- ret = rcar_pwm_set_counter(rp, div, duty_ns, period_ns);
- if (!ret)
- rcar_pwm_set_clock_control(rp, div);
-
- /* The SYNC should be set to 0 even if rcar_pwm_set_counter failed */
- rcar_pwm_update(rp, RCAR_PWMCR_SYNC, 0, RCAR_PWMCR);
-
- return ret;
-}
-
-static int rcar_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
-{
- struct rcar_pwm_chip *rp = to_rcar_pwm_chip(chip);
u32 value;
/* Don't enable the PWM device if CYC0 or PH0 is 0 */
@@ -185,19 +152,51 @@ static int rcar_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
return 0;
}
-static void rcar_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+static void rcar_pwm_disable(struct rcar_pwm_chip *rp)
+{
+ rcar_pwm_update(rp, RCAR_PWMCR_EN0, 0, RCAR_PWMCR);
+}
+
+static int rcar_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ struct pwm_state *state)
{
struct rcar_pwm_chip *rp = to_rcar_pwm_chip(chip);
+ struct pwm_state cur_state;
+ int div, ret;
- rcar_pwm_update(rp, RCAR_PWMCR_EN0, 0, RCAR_PWMCR);
+ /* This HW/driver only supports normal polarity */
+ pwm_get_state(pwm, &cur_state);
+ if (state->polarity != PWM_POLARITY_NORMAL)
+ return -ENOTSUPP;
+
+ if (!state->enabled) {
+ rcar_pwm_disable(rp);
+ return 0;
+ }
+
+ div = rcar_pwm_get_clock_division(rp, state->period);
+ if (div < 0)
+ return div;
+
+ rcar_pwm_update(rp, RCAR_PWMCR_SYNC, RCAR_PWMCR_SYNC, RCAR_PWMCR);
+
+ ret = rcar_pwm_set_counter(rp, div, state->duty_cycle, state->period);
+ if (!ret)
+ rcar_pwm_set_clock_control(rp, div);
+
+ /* The SYNC should be set to 0 even if rcar_pwm_set_counter failed */
+ rcar_pwm_update(rp, RCAR_PWMCR_SYNC, 0, RCAR_PWMCR);
+
+ if (!ret && state->enabled)
+ ret = rcar_pwm_enable(rp);
+
+ return ret;
}
static const struct pwm_ops rcar_pwm_ops = {
.request = rcar_pwm_request,
.free = rcar_pwm_free,
- .config = rcar_pwm_config,
- .enable = rcar_pwm_enable,
- .disable = rcar_pwm_disable,
+ .apply = rcar_pwm_apply,
.owner = THIS_MODULE,
};
@@ -279,18 +278,16 @@ static int rcar_pwm_suspend(struct device *dev)
static int rcar_pwm_resume(struct device *dev)
{
struct pwm_device *pwm = rcar_pwm_dev_to_pwm_dev(dev);
+ struct pwm_state state;
if (!test_bit(PWMF_REQUESTED, &pwm->flags))
return 0;
pm_runtime_get_sync(dev);
- rcar_pwm_config(pwm->chip, pwm, pwm->state.duty_cycle,
- pwm->state.period);
- if (pwm_is_enabled(pwm))
- rcar_pwm_enable(pwm->chip, pwm);
+ pwm_get_state(pwm, &state);
- return 0;
+ return rcar_pwm_apply(pwm->chip, pwm, &state);
}
#endif /* CONFIG_PM_SLEEP */
static SIMPLE_DEV_PM_OPS(rcar_pwm_pm_ops, rcar_pwm_suspend, rcar_pwm_resume);
diff --git a/drivers/ras/ras.c b/drivers/ras/ras.c
index 3f38907320dc..95540ea8dd9d 100644
--- a/drivers/ras/ras.c
+++ b/drivers/ras/ras.c
@@ -14,7 +14,7 @@
#define TRACE_INCLUDE_PATH ../../include/ras
#include <ras/ras_event.h>
-void log_non_standard_event(const uuid_le *sec_type, const uuid_le *fru_id,
+void log_non_standard_event(const guid_t *sec_type, const guid_t *fru_id,
const char *fru_text, const u8 sev, const u8 *err,
const u32 len)
{
diff --git a/drivers/remoteproc/qcom_q6v5_adsp.c b/drivers/remoteproc/qcom_q6v5_adsp.c
index 79374d1de311..1f3ef9ee493c 100644
--- a/drivers/remoteproc/qcom_q6v5_adsp.c
+++ b/drivers/remoteproc/qcom_q6v5_adsp.c
@@ -48,7 +48,7 @@
/* list of clocks required by ADSP PIL */
static const char * const adsp_clk_id[] = {
- "sway_cbcr", "lpass_aon", "lpass_ahbs_aon_cbcr", "lpass_ahbm_aon_cbcr",
+ "sway_cbcr", "lpass_ahbs_aon_cbcr", "lpass_ahbm_aon_cbcr",
"qdsp6ss_xo", "qdsp6ss_sleep", "qdsp6ss_core",
};
@@ -439,6 +439,10 @@ static int adsp_probe(struct platform_device *pdev)
adsp->sysmon = qcom_add_sysmon_subdev(rproc,
desc->sysmon_name,
desc->ssctl_id);
+ if (IS_ERR(adsp->sysmon)) {
+ ret = PTR_ERR(adsp->sysmon);
+ goto disable_pm;
+ }
ret = rproc_add(rproc);
if (ret)
diff --git a/drivers/remoteproc/qcom_q6v5_mss.c b/drivers/remoteproc/qcom_q6v5_mss.c
index 01be7314e176..eacdf10fcfaf 100644
--- a/drivers/remoteproc/qcom_q6v5_mss.c
+++ b/drivers/remoteproc/qcom_q6v5_mss.c
@@ -25,6 +25,8 @@
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
+#include <linux/pm_domain.h>
+#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/remoteproc.h>
@@ -131,6 +133,8 @@ struct rproc_hexagon_res {
char **proxy_clk_names;
char **reset_clk_names;
char **active_clk_names;
+ char **active_pd_names;
+ char **proxy_pd_names;
int version;
bool need_mem_protection;
bool has_alt_reset;
@@ -156,9 +160,13 @@ struct q6v5 {
struct clk *active_clks[8];
struct clk *reset_clks[4];
struct clk *proxy_clks[4];
+ struct device *active_pds[1];
+ struct device *proxy_pds[3];
int active_clk_count;
int reset_clk_count;
int proxy_clk_count;
+ int active_pd_count;
+ int proxy_pd_count;
struct reg_info active_regs[1];
struct reg_info proxy_regs[3];
@@ -188,6 +196,7 @@ struct q6v5 {
bool has_alt_reset;
int mpss_perm;
int mba_perm;
+ const char *hexagon_mdt_image;
int version;
};
@@ -321,6 +330,41 @@ static void q6v5_clk_disable(struct device *dev,
clk_disable_unprepare(clks[i]);
}
+static int q6v5_pds_enable(struct q6v5 *qproc, struct device **pds,
+ size_t pd_count)
+{
+ int ret;
+ int i;
+
+ for (i = 0; i < pd_count; i++) {
+ dev_pm_genpd_set_performance_state(pds[i], INT_MAX);
+ ret = pm_runtime_get_sync(pds[i]);
+ if (ret < 0)
+ goto unroll_pd_votes;
+ }
+
+ return 0;
+
+unroll_pd_votes:
+ for (i--; i >= 0; i--) {
+ dev_pm_genpd_set_performance_state(pds[i], 0);
+ pm_runtime_put(pds[i]);
+ }
+
+ return ret;
+};
+
+static void q6v5_pds_disable(struct q6v5 *qproc, struct device **pds,
+ size_t pd_count)
+{
+ int i;
+
+ for (i = 0; i < pd_count; i++) {
+ dev_pm_genpd_set_performance_state(pds[i], 0);
+ pm_runtime_put(pds[i]);
+ }
+}
+
static int q6v5_xfer_mem_ownership(struct q6v5 *qproc, int *current_perm,
bool remote_owner, phys_addr_t addr,
size_t size)
@@ -690,11 +734,23 @@ static int q6v5_mba_load(struct q6v5 *qproc)
qcom_q6v5_prepare(&qproc->q6v5);
+ ret = q6v5_pds_enable(qproc, qproc->active_pds, qproc->active_pd_count);
+ if (ret < 0) {
+ dev_err(qproc->dev, "failed to enable active power domains\n");
+ goto disable_irqs;
+ }
+
+ ret = q6v5_pds_enable(qproc, qproc->proxy_pds, qproc->proxy_pd_count);
+ if (ret < 0) {
+ dev_err(qproc->dev, "failed to enable proxy power domains\n");
+ goto disable_active_pds;
+ }
+
ret = q6v5_regulator_enable(qproc, qproc->proxy_regs,
qproc->proxy_reg_count);
if (ret) {
dev_err(qproc->dev, "failed to enable proxy supplies\n");
- goto disable_irqs;
+ goto disable_proxy_pds;
}
ret = q6v5_clk_enable(qproc->dev, qproc->proxy_clks,
@@ -791,6 +847,10 @@ disable_proxy_clk:
disable_proxy_reg:
q6v5_regulator_disable(qproc, qproc->proxy_regs,
qproc->proxy_reg_count);
+disable_proxy_pds:
+ q6v5_pds_disable(qproc, qproc->proxy_pds, qproc->proxy_pd_count);
+disable_active_pds:
+ q6v5_pds_disable(qproc, qproc->active_pds, qproc->active_pd_count);
disable_irqs:
qcom_q6v5_unprepare(&qproc->q6v5);
@@ -830,6 +890,7 @@ static void q6v5_mba_reclaim(struct q6v5 *qproc)
qproc->active_clk_count);
q6v5_regulator_disable(qproc, qproc->active_regs,
qproc->active_reg_count);
+ q6v5_pds_disable(qproc, qproc->active_pds, qproc->active_pd_count);
/* In case of failure or coredump scenario where reclaiming MBA memory
* could not happen reclaim it here.
@@ -841,6 +902,8 @@ static void q6v5_mba_reclaim(struct q6v5 *qproc)
ret = qcom_q6v5_unprepare(&qproc->q6v5);
if (ret) {
+ q6v5_pds_disable(qproc, qproc->proxy_pds,
+ qproc->proxy_pd_count);
q6v5_clk_disable(qproc->dev, qproc->proxy_clks,
qproc->proxy_clk_count);
q6v5_regulator_disable(qproc, qproc->proxy_regs,
@@ -860,17 +923,26 @@ static int q6v5_mpss_load(struct q6v5 *qproc)
phys_addr_t min_addr = PHYS_ADDR_MAX;
phys_addr_t max_addr = 0;
bool relocate = false;
- char seg_name[10];
+ char *fw_name;
+ size_t fw_name_len;
ssize_t offset;
size_t size = 0;
void *ptr;
int ret;
int i;
- ret = request_firmware(&fw, "modem.mdt", qproc->dev);
+ fw_name_len = strlen(qproc->hexagon_mdt_image);
+ if (fw_name_len <= 4)
+ return -EINVAL;
+
+ fw_name = kstrdup(qproc->hexagon_mdt_image, GFP_KERNEL);
+ if (!fw_name)
+ return -ENOMEM;
+
+ ret = request_firmware(&fw, fw_name, qproc->dev);
if (ret < 0) {
- dev_err(qproc->dev, "unable to load modem.mdt\n");
- return ret;
+ dev_err(qproc->dev, "unable to load %s\n", fw_name);
+ goto out;
}
/* Initialize the RMB validator */
@@ -918,10 +990,11 @@ static int q6v5_mpss_load(struct q6v5 *qproc)
ptr = qproc->mpss_region + offset;
if (phdr->p_filesz) {
- snprintf(seg_name, sizeof(seg_name), "modem.b%02d", i);
- ret = request_firmware(&seg_fw, seg_name, qproc->dev);
+ /* Replace "xxx.xxx" with "xxx.bxx" */
+ sprintf(fw_name + fw_name_len - 3, "b%02d", i);
+ ret = request_firmware(&seg_fw, fw_name, qproc->dev);
if (ret) {
- dev_err(qproc->dev, "failed to load %s\n", seg_name);
+ dev_err(qproc->dev, "failed to load %s\n", fw_name);
goto release_firmware;
}
@@ -960,6 +1033,8 @@ static int q6v5_mpss_load(struct q6v5 *qproc)
release_firmware:
release_firmware(fw);
+out:
+ kfree(fw_name);
return ret < 0 ? ret : 0;
}
@@ -1075,9 +1150,10 @@ static int qcom_q6v5_register_dump_segments(struct rproc *rproc,
unsigned long i;
int ret;
- ret = request_firmware(&fw, "modem.mdt", qproc->dev);
+ ret = request_firmware(&fw, qproc->hexagon_mdt_image, qproc->dev);
if (ret < 0) {
- dev_err(qproc->dev, "unable to load modem.mdt\n");
+ dev_err(qproc->dev, "unable to load %s\n",
+ qproc->hexagon_mdt_image);
return ret;
}
@@ -1121,6 +1197,7 @@ static void qcom_msa_handover(struct qcom_q6v5 *q6v5)
qproc->proxy_clk_count);
q6v5_regulator_disable(qproc, qproc->proxy_regs,
qproc->proxy_reg_count);
+ q6v5_pds_disable(qproc, qproc->proxy_pds, qproc->proxy_pd_count);
}
static int q6v5_init_mem(struct q6v5 *qproc, struct platform_device *pdev)
@@ -1181,6 +1258,45 @@ static int q6v5_init_clocks(struct device *dev, struct clk **clks,
return i;
}
+static int q6v5_pds_attach(struct device *dev, struct device **devs,
+ char **pd_names)
+{
+ size_t num_pds = 0;
+ int ret;
+ int i;
+
+ if (!pd_names)
+ return 0;
+
+ while (pd_names[num_pds])
+ num_pds++;
+
+ for (i = 0; i < num_pds; i++) {
+ devs[i] = dev_pm_domain_attach_by_name(dev, pd_names[i]);
+ if (IS_ERR(devs[i])) {
+ ret = PTR_ERR(devs[i]);
+ goto unroll_attach;
+ }
+ }
+
+ return num_pds;
+
+unroll_attach:
+ for (i--; i >= 0; i--)
+ dev_pm_domain_detach(devs[i], false);
+
+ return ret;
+};
+
+static void q6v5_pds_detach(struct q6v5 *qproc, struct device **pds,
+ size_t pd_count)
+{
+ int i;
+
+ for (i = 0; i < pd_count; i++)
+ dev_pm_domain_detach(pds[i], false);
+}
+
static int q6v5_init_reset(struct q6v5 *qproc)
{
qproc->mss_restart = devm_reset_control_get_exclusive(qproc->dev,
@@ -1253,6 +1369,7 @@ static int q6v5_probe(struct platform_device *pdev)
const struct rproc_hexagon_res *desc;
struct q6v5 *qproc;
struct rproc *rproc;
+ const char *mba_image;
int ret;
desc = of_device_get_match_data(&pdev->dev);
@@ -1262,16 +1379,30 @@ static int q6v5_probe(struct platform_device *pdev)
if (desc->need_mem_protection && !qcom_scm_is_available())
return -EPROBE_DEFER;
+ mba_image = desc->hexagon_mba_image;
+ ret = of_property_read_string_index(pdev->dev.of_node, "firmware-name",
+ 0, &mba_image);
+ if (ret < 0 && ret != -EINVAL)
+ return ret;
+
rproc = rproc_alloc(&pdev->dev, pdev->name, &q6v5_ops,
- desc->hexagon_mba_image, sizeof(*qproc));
+ mba_image, sizeof(*qproc));
if (!rproc) {
dev_err(&pdev->dev, "failed to allocate rproc\n");
return -ENOMEM;
}
+ rproc->auto_boot = false;
+
qproc = (struct q6v5 *)rproc->priv;
qproc->dev = &pdev->dev;
qproc->rproc = rproc;
+ qproc->hexagon_mdt_image = "modem.mdt";
+ ret = of_property_read_string_index(pdev->dev.of_node, "firmware-name",
+ 1, &qproc->hexagon_mdt_image);
+ if (ret < 0 && ret != -EINVAL)
+ return ret;
+
platform_set_drvdata(pdev, qproc);
ret = q6v5_init_mem(qproc, pdev);
@@ -1322,10 +1453,26 @@ static int q6v5_probe(struct platform_device *pdev)
}
qproc->active_reg_count = ret;
+ ret = q6v5_pds_attach(&pdev->dev, qproc->active_pds,
+ desc->active_pd_names);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to attach active power domains\n");
+ goto free_rproc;
+ }
+ qproc->active_pd_count = ret;
+
+ ret = q6v5_pds_attach(&pdev->dev, qproc->proxy_pds,
+ desc->proxy_pd_names);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to init power domains\n");
+ goto detach_active_pds;
+ }
+ qproc->proxy_pd_count = ret;
+
qproc->has_alt_reset = desc->has_alt_reset;
ret = q6v5_init_reset(qproc);
if (ret)
- goto free_rproc;
+ goto detach_proxy_pds;
qproc->version = desc->version;
qproc->need_mem_protection = desc->need_mem_protection;
@@ -1333,7 +1480,7 @@ static int q6v5_probe(struct platform_device *pdev)
ret = qcom_q6v5_init(&qproc->q6v5, pdev, rproc, MPSS_CRASH_REASON_SMEM,
qcom_msa_handover);
if (ret)
- goto free_rproc;
+ goto detach_proxy_pds;
qproc->mpss_perm = BIT(QCOM_SCM_VMID_HLOS);
qproc->mba_perm = BIT(QCOM_SCM_VMID_HLOS);
@@ -1341,13 +1488,21 @@ static int q6v5_probe(struct platform_device *pdev)
qcom_add_smd_subdev(rproc, &qproc->smd_subdev);
qcom_add_ssr_subdev(rproc, &qproc->ssr_subdev, "mpss");
qproc->sysmon = qcom_add_sysmon_subdev(rproc, "modem", 0x12);
+ if (IS_ERR(qproc->sysmon)) {
+ ret = PTR_ERR(qproc->sysmon);
+ goto detach_proxy_pds;
+ }
ret = rproc_add(rproc);
if (ret)
- goto free_rproc;
+ goto detach_proxy_pds;
return 0;
+detach_proxy_pds:
+ q6v5_pds_detach(qproc, qproc->proxy_pds, qproc->proxy_pd_count);
+detach_active_pds:
+ q6v5_pds_detach(qproc, qproc->active_pds, qproc->active_pd_count);
free_rproc:
rproc_free(rproc);
@@ -1364,6 +1519,10 @@ static int q6v5_remove(struct platform_device *pdev)
qcom_remove_glink_subdev(qproc->rproc, &qproc->glink_subdev);
qcom_remove_smd_subdev(qproc->rproc, &qproc->smd_subdev);
qcom_remove_ssr_subdev(qproc->rproc, &qproc->ssr_subdev);
+
+ q6v5_pds_detach(qproc, qproc->active_pds, qproc->active_pd_count);
+ q6v5_pds_detach(qproc, qproc->proxy_pds, qproc->proxy_pd_count);
+
rproc_free(qproc->rproc);
return 0;
@@ -1388,6 +1547,16 @@ static const struct rproc_hexagon_res sdm845_mss = {
"mnoc_axi",
NULL
},
+ .active_pd_names = (char*[]){
+ "load_state",
+ NULL
+ },
+ .proxy_pd_names = (char*[]){
+ "cx",
+ "mx",
+ "mss",
+ NULL
+ },
.need_mem_protection = true,
.has_alt_reset = true,
.version = MSS_SDM845,
@@ -1395,16 +1564,26 @@ static const struct rproc_hexagon_res sdm845_mss = {
static const struct rproc_hexagon_res msm8996_mss = {
.hexagon_mba_image = "mba.mbn",
+ .proxy_supply = (struct qcom_mss_reg_res[]) {
+ {
+ .supply = "pll",
+ .uA = 100000,
+ },
+ {}
+ },
.proxy_clk_names = (char*[]){
"xo",
"pnoc",
+ "qdss",
NULL
},
.active_clk_names = (char*[]){
"iface",
"bus",
"mem",
- "gpll0_mss_clk",
+ "gpll0_mss",
+ "snoc_axi",
+ "mnoc_axi",
NULL
},
.need_mem_protection = true,
diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c
index b1e63fcd5fdf..f280f196d007 100644
--- a/drivers/remoteproc/qcom_q6v5_pas.c
+++ b/drivers/remoteproc/qcom_q6v5_pas.c
@@ -258,6 +258,7 @@ static int adsp_probe(struct platform_device *pdev)
const struct adsp_data *desc;
struct qcom_adsp *adsp;
struct rproc *rproc;
+ const char *fw_name;
int ret;
desc = of_device_get_match_data(&pdev->dev);
@@ -267,8 +268,14 @@ static int adsp_probe(struct platform_device *pdev)
if (!qcom_scm_is_available())
return -EPROBE_DEFER;
+ fw_name = desc->firmware_name;
+ ret = of_property_read_string(pdev->dev.of_node, "firmware-name",
+ &fw_name);
+ if (ret < 0 && ret != -EINVAL)
+ return ret;
+
rproc = rproc_alloc(&pdev->dev, pdev->name, &adsp_ops,
- desc->firmware_name, sizeof(*adsp));
+ fw_name, sizeof(*adsp));
if (!rproc) {
dev_err(&pdev->dev, "unable to allocate remoteproc\n");
return -ENOMEM;
@@ -304,6 +311,10 @@ static int adsp_probe(struct platform_device *pdev)
adsp->sysmon = qcom_add_sysmon_subdev(rproc,
desc->sysmon_name,
desc->ssctl_id);
+ if (IS_ERR(adsp->sysmon)) {
+ ret = PTR_ERR(adsp->sysmon);
+ goto free_rproc;
+ }
ret = rproc_add(rproc);
if (ret)
diff --git a/drivers/remoteproc/qcom_sysmon.c b/drivers/remoteproc/qcom_sysmon.c
index e976a602b015..c231314eab66 100644
--- a/drivers/remoteproc/qcom_sysmon.c
+++ b/drivers/remoteproc/qcom_sysmon.c
@@ -6,8 +6,9 @@
#include <linux/module.h>
#include <linux/notifier.h>
#include <linux/slab.h>
+#include <linux/interrupt.h>
#include <linux/io.h>
-#include <linux/notifier.h>
+#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/remoteproc/qcom_rproc.h>
@@ -25,6 +26,7 @@ struct qcom_sysmon {
const char *name;
+ int shutdown_irq;
int ssctl_version;
int ssctl_instance;
@@ -34,6 +36,8 @@ struct qcom_sysmon {
struct rpmsg_endpoint *ept;
struct completion comp;
+ struct completion ind_comp;
+ struct completion shutdown_comp;
struct mutex lock;
bool ssr_ack;
@@ -137,6 +141,7 @@ static int sysmon_callback(struct rpmsg_device *rpdev, void *data, int count,
}
#define SSCTL_SHUTDOWN_REQ 0x21
+#define SSCTL_SHUTDOWN_READY_IND 0x21
#define SSCTL_SUBSYS_EVENT_REQ 0x23
#define SSCTL_MAX_MSG_LEN 7
@@ -252,6 +257,29 @@ static struct qmi_elem_info ssctl_subsys_event_resp_ei[] = {
{}
};
+static struct qmi_elem_info ssctl_shutdown_ind_ei[] = {
+ {}
+};
+
+static void sysmon_ind_cb(struct qmi_handle *qmi, struct sockaddr_qrtr *sq,
+ struct qmi_txn *txn, const void *data)
+{
+ struct qcom_sysmon *sysmon = container_of(qmi, struct qcom_sysmon, qmi);
+
+ complete(&sysmon->ind_comp);
+}
+
+static struct qmi_msg_handler qmi_indication_handler[] = {
+ {
+ .type = QMI_INDICATION,
+ .msg_id = SSCTL_SHUTDOWN_READY_IND,
+ .ei = ssctl_shutdown_ind_ei,
+ .decoded_size = 0,
+ .fn = sysmon_ind_cb
+ },
+ {}
+};
+
/**
* ssctl_request_shutdown() - request shutdown via SSCTL QMI service
* @sysmon: sysmon context
@@ -262,6 +290,8 @@ static void ssctl_request_shutdown(struct qcom_sysmon *sysmon)
struct qmi_txn txn;
int ret;
+ reinit_completion(&sysmon->ind_comp);
+ reinit_completion(&sysmon->shutdown_comp);
ret = qmi_txn_init(&sysmon->qmi, &txn, ssctl_shutdown_resp_ei, &resp);
if (ret < 0) {
dev_err(sysmon->dev, "failed to allocate QMI txn\n");
@@ -283,6 +313,17 @@ static void ssctl_request_shutdown(struct qcom_sysmon *sysmon)
dev_err(sysmon->dev, "shutdown request failed\n");
else
dev_dbg(sysmon->dev, "shutdown request completed\n");
+
+ if (sysmon->shutdown_irq > 0) {
+ ret = wait_for_completion_timeout(&sysmon->shutdown_comp,
+ 10 * HZ);
+ if (!ret) {
+ ret = try_wait_for_completion(&sysmon->ind_comp);
+ if (!ret)
+ dev_err(sysmon->dev,
+ "timeout waiting for shutdown ack\n");
+ }
+ }
}
/**
@@ -432,6 +473,15 @@ static int sysmon_notify(struct notifier_block *nb, unsigned long event,
return NOTIFY_DONE;
}
+static irqreturn_t sysmon_shutdown_interrupt(int irq, void *data)
+{
+ struct qcom_sysmon *sysmon = data;
+
+ complete(&sysmon->shutdown_comp);
+
+ return IRQ_HANDLED;
+}
+
/**
* qcom_add_sysmon_subdev() - create a sysmon subdev for the given remoteproc
* @rproc: rproc context to associate the subdev with
@@ -449,7 +499,7 @@ struct qcom_sysmon *qcom_add_sysmon_subdev(struct rproc *rproc,
sysmon = kzalloc(sizeof(*sysmon), GFP_KERNEL);
if (!sysmon)
- return NULL;
+ return ERR_PTR(-ENOMEM);
sysmon->dev = rproc->dev.parent;
sysmon->rproc = rproc;
@@ -458,13 +508,37 @@ struct qcom_sysmon *qcom_add_sysmon_subdev(struct rproc *rproc,
sysmon->ssctl_instance = ssctl_instance;
init_completion(&sysmon->comp);
+ init_completion(&sysmon->ind_comp);
+ init_completion(&sysmon->shutdown_comp);
mutex_init(&sysmon->lock);
- ret = qmi_handle_init(&sysmon->qmi, SSCTL_MAX_MSG_LEN, &ssctl_ops, NULL);
+ sysmon->shutdown_irq = of_irq_get_byname(sysmon->dev->of_node,
+ "shutdown-ack");
+ if (sysmon->shutdown_irq < 0) {
+ if (sysmon->shutdown_irq != -ENODATA) {
+ dev_err(sysmon->dev,
+ "failed to retrieve shutdown-ack IRQ\n");
+ return ERR_PTR(sysmon->shutdown_irq);
+ }
+ } else {
+ ret = devm_request_threaded_irq(sysmon->dev,
+ sysmon->shutdown_irq,
+ NULL, sysmon_shutdown_interrupt,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ "q6v5 shutdown-ack", sysmon);
+ if (ret) {
+ dev_err(sysmon->dev,
+ "failed to acquire shutdown-ack IRQ\n");
+ return ERR_PTR(ret);
+ }
+ }
+
+ ret = qmi_handle_init(&sysmon->qmi, SSCTL_MAX_MSG_LEN, &ssctl_ops,
+ qmi_indication_handler);
if (ret < 0) {
dev_err(sysmon->dev, "failed to initialize qmi handle\n");
kfree(sysmon);
- return NULL;
+ return ERR_PTR(ret);
}
qmi_add_lookup(&sysmon->qmi, 43, 0, 0);
diff --git a/drivers/remoteproc/qcom_wcnss.c b/drivers/remoteproc/qcom_wcnss.c
index b0e07e9f42d5..adcce523971e 100644
--- a/drivers/remoteproc/qcom_wcnss.c
+++ b/drivers/remoteproc/qcom_wcnss.c
@@ -553,6 +553,10 @@ static int wcnss_probe(struct platform_device *pdev)
qcom_add_smd_subdev(rproc, &wcnss->smd_subdev);
wcnss->sysmon = qcom_add_sysmon_subdev(rproc, "wcnss", WCNSS_SSCTL_ID);
+ if (IS_ERR(wcnss->sysmon)) {
+ ret = PTR_ERR(wcnss->sysmon);
+ goto free_rproc;
+ }
ret = rproc_add(rproc);
if (ret)
@@ -622,5 +626,5 @@ static void __exit wcnss_exit(void)
}
module_exit(wcnss_exit);
-MODULE_DESCRIPTION("Qualcomm Peripherial Image Loader for Wireless Subsystem");
+MODULE_DESCRIPTION("Qualcomm Peripheral Image Loader for Wireless Subsystem");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c
index 54ec38fc5dca..48feebd6d0a2 100644
--- a/drivers/remoteproc/remoteproc_core.c
+++ b/drivers/remoteproc/remoteproc_core.c
@@ -39,12 +39,16 @@
#include <linux/idr.h>
#include <linux/elf.h>
#include <linux/crc32.h>
+#include <linux/of_reserved_mem.h>
#include <linux/virtio_ids.h>
#include <linux/virtio_ring.h>
#include <asm/byteorder.h>
+#include <linux/platform_device.h>
#include "remoteproc_internal.h"
+#define HIGH_BITS_MASK 0xFFFFFFFF00000000ULL
+
static DEFINE_MUTEX(rproc_list_mutex);
static LIST_HEAD(rproc_list);
@@ -145,7 +149,7 @@ static void rproc_disable_iommu(struct rproc *rproc)
iommu_domain_free(domain);
}
-static phys_addr_t rproc_va_to_pa(void *cpu_addr)
+phys_addr_t rproc_va_to_pa(void *cpu_addr)
{
/*
* Return physical address according to virtual address location
@@ -160,6 +164,7 @@ static phys_addr_t rproc_va_to_pa(void *cpu_addr)
WARN_ON(!virt_addr_valid(cpu_addr));
return virt_to_phys(cpu_addr);
}
+EXPORT_SYMBOL(rproc_va_to_pa);
/**
* rproc_da_to_va() - lookup the kernel virtual address for a remoteproc address
@@ -204,6 +209,10 @@ void *rproc_da_to_va(struct rproc *rproc, u64 da, int len)
list_for_each_entry(carveout, &rproc->carveouts, node) {
int offset = da - carveout->da;
+ /* Verify that carveout is allocated */
+ if (!carveout->va)
+ continue;
+
/* try next carveout if da is too small */
if (offset < 0)
continue;
@@ -272,25 +281,27 @@ rproc_find_carveout_by_name(struct rproc *rproc, const char *name, ...)
* @len: associated area size
*
* This function is a helper function to verify requested device area (couple
- * da, len) is part of specified carevout.
+ * da, len) is part of specified carveout.
+ * If da is not set (defined as FW_RSC_ADDR_ANY), only requested length is
+ * checked.
*
- * Return: 0 if carveout match request else -ENOMEM
+ * Return: 0 if carveout matches request else error
*/
-int rproc_check_carveout_da(struct rproc *rproc, struct rproc_mem_entry *mem,
- u32 da, u32 len)
+static int rproc_check_carveout_da(struct rproc *rproc,
+ struct rproc_mem_entry *mem, u32 da, u32 len)
{
struct device *dev = &rproc->dev;
- int delta = 0;
+ int delta;
/* Check requested resource length */
if (len > mem->len) {
dev_err(dev, "Registered carveout doesn't fit len request\n");
- return -ENOMEM;
+ return -EINVAL;
}
if (da != FW_RSC_ADDR_ANY && mem->da == FW_RSC_ADDR_ANY) {
- /* Update existing carveout da */
- mem->da = da;
+ /* Address doesn't match registered carveout configuration */
+ return -EINVAL;
} else if (da != FW_RSC_ADDR_ANY && mem->da != FW_RSC_ADDR_ANY) {
delta = da - mem->da;
@@ -298,13 +309,13 @@ int rproc_check_carveout_da(struct rproc *rproc, struct rproc_mem_entry *mem,
if (delta < 0) {
dev_err(dev,
"Registered carveout doesn't fit da request\n");
- return -ENOMEM;
+ return -EINVAL;
}
if (delta + len > mem->len) {
dev_err(dev,
"Registered carveout doesn't fit len request\n");
- return -ENOMEM;
+ return -EINVAL;
}
}
@@ -418,8 +429,25 @@ static int rproc_vdev_do_start(struct rproc_subdev *subdev)
static void rproc_vdev_do_stop(struct rproc_subdev *subdev, bool crashed)
{
struct rproc_vdev *rvdev = container_of(subdev, struct rproc_vdev, subdev);
+ int ret;
- rproc_remove_virtio_dev(rvdev);
+ ret = device_for_each_child(&rvdev->dev, NULL, rproc_remove_virtio_dev);
+ if (ret)
+ dev_warn(&rvdev->dev, "can't remove vdev child device: %d\n", ret);
+}
+
+/**
+ * rproc_rvdev_release() - release the existence of a rvdev
+ *
+ * @dev: the subdevice's dev
+ */
+static void rproc_rvdev_release(struct device *dev)
+{
+ struct rproc_vdev *rvdev = container_of(dev, struct rproc_vdev, dev);
+
+ of_reserved_mem_device_release(dev);
+
+ kfree(rvdev);
}
/**
@@ -455,6 +483,7 @@ static int rproc_handle_vdev(struct rproc *rproc, struct fw_rsc_vdev *rsc,
struct device *dev = &rproc->dev;
struct rproc_vdev *rvdev;
int i, ret;
+ char name[16];
/* make sure resource isn't truncated */
if (sizeof(*rsc) + rsc->num_of_vrings * sizeof(struct fw_rsc_vdev_vring)
@@ -488,6 +517,29 @@ static int rproc_handle_vdev(struct rproc *rproc, struct fw_rsc_vdev *rsc,
rvdev->rproc = rproc;
rvdev->index = rproc->nb_vdev++;
+ /* Initialise vdev subdevice */
+ snprintf(name, sizeof(name), "vdev%dbuffer", rvdev->index);
+ rvdev->dev.parent = rproc->dev.parent;
+ rvdev->dev.release = rproc_rvdev_release;
+ dev_set_name(&rvdev->dev, "%s#%s", dev_name(rvdev->dev.parent), name);
+ dev_set_drvdata(&rvdev->dev, rvdev);
+
+ ret = device_register(&rvdev->dev);
+ if (ret) {
+ put_device(&rvdev->dev);
+ return ret;
+ }
+ /* Make device dma capable by inheriting from parent's capabilities */
+ set_dma_ops(&rvdev->dev, get_dma_ops(rproc->dev.parent));
+
+ ret = dma_coerce_mask_and_coherent(&rvdev->dev,
+ dma_get_mask(rproc->dev.parent));
+ if (ret) {
+ dev_warn(dev,
+ "Failed to set DMA mask %llx. Trying to continue... %x\n",
+ dma_get_mask(rproc->dev.parent), ret);
+ }
+
/* parse the vrings */
for (i = 0; i < rsc->num_of_vrings; i++) {
ret = rproc_parse_vring(rvdev, rsc, i);
@@ -518,7 +570,7 @@ unwind_vring_allocations:
for (i--; i >= 0; i--)
rproc_free_vring(&rvdev->vring[i]);
free_rvdev:
- kfree(rvdev);
+ device_unregister(&rvdev->dev);
return ret;
}
@@ -536,7 +588,7 @@ void rproc_vdev_release(struct kref *ref)
rproc_remove_subdev(rproc, &rvdev->subdev);
list_del(&rvdev->node);
- kfree(rvdev);
+ device_unregister(&rvdev->dev);
}
/**
@@ -558,9 +610,8 @@ void rproc_vdev_release(struct kref *ref)
static int rproc_handle_trace(struct rproc *rproc, struct fw_rsc_trace *rsc,
int offset, int avail)
{
- struct rproc_mem_entry *trace;
+ struct rproc_debug_trace *trace;
struct device *dev = &rproc->dev;
- void *ptr;
char name[15];
if (sizeof(*rsc) > avail) {
@@ -574,28 +625,23 @@ static int rproc_handle_trace(struct rproc *rproc, struct fw_rsc_trace *rsc,
return -EINVAL;
}
- /* what's the kernel address of this resource ? */
- ptr = rproc_da_to_va(rproc, rsc->da, rsc->len);
- if (!ptr) {
- dev_err(dev, "erroneous trace resource entry\n");
- return -EINVAL;
- }
-
trace = kzalloc(sizeof(*trace), GFP_KERNEL);
if (!trace)
return -ENOMEM;
/* set the trace buffer dma properties */
- trace->len = rsc->len;
- trace->va = ptr;
+ trace->trace_mem.len = rsc->len;
+ trace->trace_mem.da = rsc->da;
+
+ /* set pointer on rproc device */
+ trace->rproc = rproc;
/* make sure snprintf always null terminates, even if truncating */
snprintf(name, sizeof(name), "trace%d", rproc->num_traces);
/* create the debugfs entry */
- trace->priv = rproc_create_trace_file(name, rproc, trace);
- if (!trace->priv) {
- trace->va = NULL;
+ trace->tfile = rproc_create_trace_file(name, rproc, trace);
+ if (!trace->tfile) {
kfree(trace);
return -EINVAL;
}
@@ -604,8 +650,8 @@ static int rproc_handle_trace(struct rproc *rproc, struct fw_rsc_trace *rsc,
rproc->num_traces++;
- dev_dbg(dev, "%s added: va %pK, da 0x%x, len 0x%x\n",
- name, ptr, rsc->da, rsc->len);
+ dev_dbg(dev, "%s added: da 0x%x, len 0x%x\n",
+ name, rsc->da, rsc->len);
return 0;
}
@@ -715,6 +761,18 @@ static int rproc_alloc_carveout(struct rproc *rproc,
dev_dbg(dev, "carveout va %pK, dma %pad, len 0x%x\n",
va, &dma, mem->len);
+ if (mem->da != FW_RSC_ADDR_ANY && !rproc->domain) {
+ /*
+ * Check requested da is equal to dma address
+ * and print a warn message in case of missalignment.
+ * Don't stop rproc_start sequence as coprocessor may
+ * build pa to da translation on its side.
+ */
+ if (mem->da != (u32)dma)
+ dev_warn(dev->parent,
+ "Allocated carveout doesn't fit device address request\n");
+ }
+
/*
* Ok, this is non-standard.
*
@@ -732,15 +790,7 @@ static int rproc_alloc_carveout(struct rproc *rproc,
* to use the iommu-based DMA API: we expect 'dma' to contain the
* physical address in this case.
*/
-
- if (mem->da != FW_RSC_ADDR_ANY) {
- if (!rproc->domain) {
- dev_err(dev->parent,
- "Bad carveout rsc configuration\n");
- ret = -ENOMEM;
- goto dma_free;
- }
-
+ if (mem->da != FW_RSC_ADDR_ANY && rproc->domain) {
mapping = kzalloc(sizeof(*mapping), GFP_KERNEL);
if (!mapping) {
ret = -ENOMEM;
@@ -767,11 +817,17 @@ static int rproc_alloc_carveout(struct rproc *rproc,
dev_dbg(dev, "carveout mapped 0x%x to %pad\n",
mem->da, &dma);
- } else {
+ }
+
+ if (mem->da == FW_RSC_ADDR_ANY) {
+ /* Update device address as undefined by requester */
+ if ((u64)dma & HIGH_BITS_MASK)
+ dev_warn(dev, "DMA address cast in 32bit to fit resource table format\n");
+
mem->da = (u32)dma;
}
- mem->dma = (u32)dma;
+ mem->dma = dma;
mem->va = va;
return 0;
@@ -900,7 +956,8 @@ EXPORT_SYMBOL(rproc_add_carveout);
* @dma: dma address
* @len: memory carveout length
* @da: device address
- * @release: memory carveout function
+ * @alloc: memory carveout allocation function
+ * @release: memory carveout release function
* @name: carveout name
*
* This function allocates a rproc_mem_entry struct and fill it with parameters
@@ -1110,6 +1167,7 @@ static int rproc_alloc_registered_carveouts(struct rproc *rproc)
struct rproc_mem_entry *entry, *tmp;
struct fw_rsc_carveout *rsc;
struct device *dev = &rproc->dev;
+ u64 pa;
int ret;
list_for_each_entry_safe(entry, tmp, &rproc->carveouts, node) {
@@ -1146,10 +1204,15 @@ static int rproc_alloc_registered_carveouts(struct rproc *rproc)
/* Use va if defined else dma to generate pa */
if (entry->va)
- rsc->pa = (u32)rproc_va_to_pa(entry->va);
+ pa = (u64)rproc_va_to_pa(entry->va);
else
- rsc->pa = (u32)entry->dma;
+ pa = (u64)entry->dma;
+
+ if (((u64)pa) & HIGH_BITS_MASK)
+ dev_warn(dev,
+ "Physical address cast in 32bit to fit resource table format\n");
+ rsc->pa = (u32)pa;
rsc->da = entry->da;
rsc->len = entry->len;
}
@@ -1182,15 +1245,16 @@ static void rproc_coredump_cleanup(struct rproc *rproc)
static void rproc_resource_cleanup(struct rproc *rproc)
{
struct rproc_mem_entry *entry, *tmp;
+ struct rproc_debug_trace *trace, *ttmp;
struct rproc_vdev *rvdev, *rvtmp;
struct device *dev = &rproc->dev;
/* clean up debugfs trace entries */
- list_for_each_entry_safe(entry, tmp, &rproc->traces, node) {
- rproc_remove_trace_file(entry->priv);
+ list_for_each_entry_safe(trace, ttmp, &rproc->traces, node) {
+ rproc_remove_trace_file(trace->tfile);
rproc->num_traces--;
- list_del(&entry->node);
- kfree(entry);
+ list_del(&trace->node);
+ kfree(trace);
}
/* clean up iommu mapping entries */
diff --git a/drivers/remoteproc/remoteproc_debugfs.c b/drivers/remoteproc/remoteproc_debugfs.c
index e90135c64af0..6da934b8dc4b 100644
--- a/drivers/remoteproc/remoteproc_debugfs.c
+++ b/drivers/remoteproc/remoteproc_debugfs.c
@@ -47,10 +47,23 @@ static struct dentry *rproc_dbg;
static ssize_t rproc_trace_read(struct file *filp, char __user *userbuf,
size_t count, loff_t *ppos)
{
- struct rproc_mem_entry *trace = filp->private_data;
- int len = strnlen(trace->va, trace->len);
+ struct rproc_debug_trace *data = filp->private_data;
+ struct rproc_mem_entry *trace = &data->trace_mem;
+ void *va;
+ char buf[100];
+ int len;
+
+ va = rproc_da_to_va(data->rproc, trace->da, trace->len);
- return simple_read_from_buffer(userbuf, count, ppos, trace->va, len);
+ if (!va) {
+ len = scnprintf(buf, sizeof(buf), "Trace %s not available\n",
+ trace->name);
+ va = buf;
+ } else {
+ len = strnlen(va, trace->len);
+ }
+
+ return simple_read_from_buffer(userbuf, count, ppos, va, len);
}
static const struct file_operations trace_rproc_ops = {
@@ -155,6 +168,30 @@ static const struct file_operations rproc_recovery_ops = {
.llseek = generic_file_llseek,
};
+/* expose the crash trigger via debugfs */
+static ssize_t
+rproc_crash_write(struct file *filp, const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct rproc *rproc = filp->private_data;
+ unsigned int type;
+ int ret;
+
+ ret = kstrtouint_from_user(user_buf, count, 0, &type);
+ if (ret < 0)
+ return ret;
+
+ rproc_report_crash(rproc, type);
+
+ return count;
+}
+
+static const struct file_operations rproc_crash_ops = {
+ .write = rproc_crash_write,
+ .open = simple_open,
+ .llseek = generic_file_llseek,
+};
+
/* Expose resource table content via debugfs */
static int rproc_rsc_table_show(struct seq_file *seq, void *p)
{
@@ -288,7 +325,7 @@ void rproc_remove_trace_file(struct dentry *tfile)
}
struct dentry *rproc_create_trace_file(const char *name, struct rproc *rproc,
- struct rproc_mem_entry *trace)
+ struct rproc_debug_trace *trace)
{
struct dentry *tfile;
@@ -325,6 +362,8 @@ void rproc_create_debug_dir(struct rproc *rproc)
rproc, &rproc_name_ops);
debugfs_create_file("recovery", 0400, rproc->dbg_dir,
rproc, &rproc_recovery_ops);
+ debugfs_create_file("crash", 0200, rproc->dbg_dir,
+ rproc, &rproc_crash_ops);
debugfs_create_file("resource_table", 0400, rproc->dbg_dir,
rproc, &rproc_rsc_table_ops);
debugfs_create_file("carveout_memories", 0400, rproc->dbg_dir,
diff --git a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h
index f6cad243d7ca..45ff76a06c72 100644
--- a/drivers/remoteproc/remoteproc_internal.h
+++ b/drivers/remoteproc/remoteproc_internal.h
@@ -25,6 +25,13 @@
struct rproc;
+struct rproc_debug_trace {
+ struct rproc *rproc;
+ struct dentry *tfile;
+ struct list_head node;
+ struct rproc_mem_entry trace_mem;
+};
+
/* from remoteproc_core.c */
void rproc_release(struct kref *kref);
irqreturn_t rproc_vq_interrupt(struct rproc *rproc, int vq_id);
@@ -32,12 +39,12 @@ void rproc_vdev_release(struct kref *ref);
/* from remoteproc_virtio.c */
int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id);
-void rproc_remove_virtio_dev(struct rproc_vdev *rvdev);
+int rproc_remove_virtio_dev(struct device *dev, void *data);
/* from remoteproc_debugfs.c */
void rproc_remove_trace_file(struct dentry *tfile);
struct dentry *rproc_create_trace_file(const char *name, struct rproc *rproc,
- struct rproc_mem_entry *trace);
+ struct rproc_debug_trace *trace);
void rproc_delete_debug_dir(struct rproc *rproc);
void rproc_create_debug_dir(struct rproc *rproc);
void rproc_init_debugfs(void);
@@ -52,6 +59,7 @@ void rproc_free_vring(struct rproc_vring *rvring);
int rproc_alloc_vring(struct rproc_vdev *rvdev, int i);
void *rproc_da_to_va(struct rproc *rproc, u64 da, int len);
+phys_addr_t rproc_va_to_pa(void *cpu_addr);
int rproc_trigger_recovery(struct rproc *rproc);
int rproc_elf_sanity_check(struct rproc *rproc, const struct firmware *fw);
diff --git a/drivers/remoteproc/remoteproc_virtio.c b/drivers/remoteproc/remoteproc_virtio.c
index 2d7cd344f3bf..44774de6f17b 100644
--- a/drivers/remoteproc/remoteproc_virtio.c
+++ b/drivers/remoteproc/remoteproc_virtio.c
@@ -17,7 +17,9 @@
* GNU General Public License for more details.
*/
+#include <linux/dma-mapping.h>
#include <linux/export.h>
+#include <linux/of_reserved_mem.h>
#include <linux/remoteproc.h>
#include <linux/virtio.h>
#include <linux/virtio_config.h>
@@ -316,6 +318,8 @@ static void rproc_virtio_dev_release(struct device *dev)
struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
struct rproc *rproc = vdev_to_rproc(vdev);
+ kfree(vdev);
+
kref_put(&rvdev->refcount, rproc_vdev_release);
put_device(&rproc->dev);
@@ -333,10 +337,53 @@ static void rproc_virtio_dev_release(struct device *dev)
int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id)
{
struct rproc *rproc = rvdev->rproc;
- struct device *dev = &rproc->dev;
- struct virtio_device *vdev = &rvdev->vdev;
+ struct device *dev = &rvdev->dev;
+ struct virtio_device *vdev;
+ struct rproc_mem_entry *mem;
int ret;
+ /* Try to find dedicated vdev buffer carveout */
+ mem = rproc_find_carveout_by_name(rproc, "vdev%dbuffer", rvdev->index);
+ if (mem) {
+ phys_addr_t pa;
+
+ if (mem->of_resm_idx != -1) {
+ struct device_node *np = rproc->dev.parent->of_node;
+
+ /* Associate reserved memory to vdev device */
+ ret = of_reserved_mem_device_init_by_idx(dev, np,
+ mem->of_resm_idx);
+ if (ret) {
+ dev_err(dev, "Can't associate reserved memory\n");
+ goto out;
+ }
+ } else {
+ if (mem->va) {
+ dev_warn(dev, "vdev %d buffer already mapped\n",
+ rvdev->index);
+ pa = rproc_va_to_pa(mem->va);
+ } else {
+ /* Use dma address as carveout no memmapped yet */
+ pa = (phys_addr_t)mem->dma;
+ }
+
+ /* Associate vdev buffer memory pool to vdev subdev */
+ ret = dma_declare_coherent_memory(dev, pa,
+ mem->da,
+ mem->len);
+ if (ret < 0) {
+ dev_err(dev, "Failed to associate buffer\n");
+ goto out;
+ }
+ }
+ }
+
+ /* Allocate virtio device */
+ vdev = kzalloc(sizeof(*vdev), GFP_KERNEL);
+ if (!vdev) {
+ ret = -ENOMEM;
+ goto out;
+ }
vdev->id.device = id,
vdev->config = &rproc_virtio_config_ops,
vdev->dev.parent = dev;
@@ -370,11 +417,15 @@ out:
/**
* rproc_remove_virtio_dev() - remove an rproc-induced virtio device
- * @rvdev: the remote vdev
+ * @dev: the virtio device
+ * @data: must be null
*
* This function unregisters an existing virtio device.
*/
-void rproc_remove_virtio_dev(struct rproc_vdev *rvdev)
+int rproc_remove_virtio_dev(struct device *dev, void *data)
{
- unregister_virtio_device(&rvdev->vdev);
+ struct virtio_device *vdev = dev_to_virtio(dev);
+
+ unregister_virtio_device(vdev);
+ return 0;
}
diff --git a/drivers/remoteproc/st_remoteproc.c b/drivers/remoteproc/st_remoteproc.c
index aacef0ea3b90..51049d17b1e5 100644
--- a/drivers/remoteproc/st_remoteproc.c
+++ b/drivers/remoteproc/st_remoteproc.c
@@ -19,6 +19,7 @@
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/of_reserved_mem.h>
#include <linux/platform_device.h>
@@ -91,6 +92,77 @@ static void st_rproc_kick(struct rproc *rproc, int vqid)
dev_err(dev, "failed to send message via mbox: %d\n", ret);
}
+static int st_rproc_mem_alloc(struct rproc *rproc,
+ struct rproc_mem_entry *mem)
+{
+ struct device *dev = rproc->dev.parent;
+ void *va;
+
+ va = ioremap_wc(mem->dma, mem->len);
+ if (!va) {
+ dev_err(dev, "Unable to map memory region: %pa+%zx\n",
+ &mem->dma, mem->len);
+ return -ENOMEM;
+ }
+
+ /* Update memory entry va */
+ mem->va = va;
+
+ return 0;
+}
+
+static int st_rproc_mem_release(struct rproc *rproc,
+ struct rproc_mem_entry *mem)
+{
+ iounmap(mem->va);
+
+ return 0;
+}
+
+static int st_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw)
+{
+ struct device *dev = rproc->dev.parent;
+ struct device_node *np = dev->of_node;
+ struct rproc_mem_entry *mem;
+ struct reserved_mem *rmem;
+ struct of_phandle_iterator it;
+ int index = 0;
+
+ of_phandle_iterator_init(&it, np, "memory-region", NULL, 0);
+ while (of_phandle_iterator_next(&it) == 0) {
+ rmem = of_reserved_mem_lookup(it.node);
+ if (!rmem) {
+ dev_err(dev, "unable to acquire memory-region\n");
+ return -EINVAL;
+ }
+
+ /* No need to map vdev buffer */
+ if (strcmp(it.node->name, "vdev0buffer")) {
+ /* Register memory region */
+ mem = rproc_mem_entry_init(dev, NULL,
+ (dma_addr_t)rmem->base,
+ rmem->size, rmem->base,
+ st_rproc_mem_alloc,
+ st_rproc_mem_release,
+ it.node->name);
+ } else {
+ /* Register reserved memory for vdev buffer allocation */
+ mem = rproc_of_resm_mem_entry_init(dev, index,
+ rmem->size,
+ rmem->base,
+ it.node->name);
+ }
+
+ if (!mem)
+ return -ENOMEM;
+
+ rproc_add_carveout(rproc, mem);
+ index++;
+ }
+
+ return rproc_elf_load_rsc_table(rproc, fw);
+}
+
static int st_rproc_start(struct rproc *rproc)
{
struct st_rproc *ddata = rproc->priv;
@@ -158,9 +230,14 @@ static int st_rproc_stop(struct rproc *rproc)
}
static const struct rproc_ops st_rproc_ops = {
- .kick = st_rproc_kick,
- .start = st_rproc_start,
- .stop = st_rproc_stop,
+ .kick = st_rproc_kick,
+ .start = st_rproc_start,
+ .stop = st_rproc_stop,
+ .parse_fw = st_rproc_parse_fw,
+ .load = rproc_elf_load_segments,
+ .find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table,
+ .sanity_check = rproc_elf_sanity_check,
+ .get_boot_addr = rproc_elf_get_boot_addr,
};
/*
@@ -254,12 +331,6 @@ static int st_rproc_parse_dt(struct platform_device *pdev)
return -EINVAL;
}
- err = of_reserved_mem_device_init(dev);
- if (err) {
- dev_err(dev, "Failed to obtain shared memory\n");
- return err;
- }
-
err = clk_prepare(ddata->clk);
if (err)
dev_err(dev, "failed to get clock\n");
@@ -387,8 +458,6 @@ static int st_rproc_remove(struct platform_device *pdev)
clk_disable_unprepare(ddata->clk);
- of_reserved_mem_device_release(&pdev->dev);
-
for (i = 0; i < ST_RPROC_MAX_VRING * MBOX_MAX; i++)
mbox_free_channel(ddata->mbox_chan[i]);
diff --git a/drivers/rpmsg/virtio_rpmsg_bus.c b/drivers/rpmsg/virtio_rpmsg_bus.c
index 664f957012cd..5d3685bd76a2 100644
--- a/drivers/rpmsg/virtio_rpmsg_bus.c
+++ b/drivers/rpmsg/virtio_rpmsg_bus.c
@@ -11,21 +11,21 @@
#define pr_fmt(fmt) "%s: " fmt, __func__
+#include <linux/dma-mapping.h>
+#include <linux/idr.h>
+#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/virtio.h>
-#include <linux/virtio_ids.h>
-#include <linux/virtio_config.h>
+#include <linux/mutex.h>
+#include <linux/of_device.h>
+#include <linux/rpmsg.h>
#include <linux/scatterlist.h>
-#include <linux/dma-mapping.h>
#include <linux/slab.h>
-#include <linux/idr.h>
-#include <linux/jiffies.h>
#include <linux/sched.h>
+#include <linux/virtio.h>
+#include <linux/virtio_ids.h>
+#include <linux/virtio_config.h>
#include <linux/wait.h>
-#include <linux/rpmsg.h>
-#include <linux/mutex.h>
-#include <linux/of_device.h>
#include "rpmsg_internal.h"
@@ -912,7 +912,7 @@ static int rpmsg_probe(struct virtio_device *vdev)
total_buf_space = vrp->num_bufs * vrp->buf_size;
/* allocate coherent memory for the buffers */
- bufs_va = dma_alloc_coherent(vdev->dev.parent->parent,
+ bufs_va = dma_alloc_coherent(vdev->dev.parent,
total_buf_space, &vrp->bufs_dma,
GFP_KERNEL);
if (!bufs_va) {
@@ -980,7 +980,7 @@ static int rpmsg_probe(struct virtio_device *vdev)
return 0;
free_coherent:
- dma_free_coherent(vdev->dev.parent->parent, total_buf_space,
+ dma_free_coherent(vdev->dev.parent, total_buf_space,
bufs_va, vrp->bufs_dma);
vqs_del:
vdev->config->del_vqs(vrp->vdev);
@@ -1015,7 +1015,7 @@ static void rpmsg_remove(struct virtio_device *vdev)
vdev->config->del_vqs(vrp->vdev);
- dma_free_coherent(vdev->dev.parent->parent, total_buf_space,
+ dma_free_coherent(vdev->dev.parent, total_buf_space,
vrp->rbufs, vrp->bufs_dma);
kfree(vrp);
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 225b0b8516f3..a71734c41693 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -185,6 +185,16 @@ config RTC_DRV_ABB5ZES3
This driver can also be built as a module. If so, the module
will be called rtc-ab-b5ze-s3.
+config RTC_DRV_ABEOZ9
+ select REGMAP_I2C
+ tristate "Abracon AB-RTCMC-32.768kHz-EOZ9"
+ help
+ If you say yes here you get support for the Abracon
+ AB-RTCMC-32.768kHz-EOA9 I2C RTC chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-ab-e0z9.
+
config RTC_DRV_ABX80X
tristate "Abracon ABx80x"
select WATCHDOG_CORE if WATCHDOG
@@ -601,9 +611,10 @@ config RTC_DRV_RX8010
will be called rtc-rx8010.
config RTC_DRV_RX8581
- tristate "Epson RX-8581"
+ tristate "Epson RX-8571/RX-8581"
help
- If you say yes here you will get support for the Epson RX-8581.
+ If you say yes here you will get support for the Epson RX-8571/
+ RX-8581.
This driver can also be built as a module. If so the module
will be called rtc-rx8581.
@@ -626,6 +637,15 @@ config RTC_DRV_EM3027
This driver can also be built as a module. If so, the module
will be called rtc-em3027.
+config RTC_DRV_RV3028
+ tristate "Micro Crystal RV3028"
+ help
+ If you say yes here you get support for the Micro Crystal
+ RV3028.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-rv3028.
+
config RTC_DRV_RV8803
tristate "Micro Crystal RV8803, Epson RX8900"
help
@@ -646,6 +666,15 @@ config RTC_DRV_S5M
This driver can also be built as a module. If so, the module
will be called rtc-s5m.
+config RTC_DRV_SD3078
+ tristate "ZXW Crystal SD3078"
+ help
+ If you say yes here you get support for the ZXW Crystal
+ SD3078 RTC chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-sd3078
+
endif # I2C
comment "SPI RTC drivers"
@@ -1285,6 +1314,17 @@ config RTC_DRV_IMXDI
This driver can also be built as a module, if so, the module
will be called "rtc-imxdi".
+config RTC_DRV_MESON
+ tristate "Amlogic Meson RTC"
+ depends on (ARM && ARCH_MESON) || COMPILE_TEST
+ select REGMAP_MMIO
+ help
+ Support for the RTC block on the Amlogic Meson6, Meson8, Meson8b
+ and Meson8m2 SoCs.
+
+ This driver can also be built as a module, if so, the module
+ will be called "rtc-meson".
+
config RTC_DRV_OMAP
tristate "TI OMAP Real Time Clock"
depends on ARCH_OMAP || ARCH_DAVINCI || COMPILE_TEST
@@ -1508,6 +1548,16 @@ config RTC_DRV_ARMADA38X
This driver can also be built as a module. If so, the module
will be called armada38x-rtc.
+config RTC_DRV_CADENCE
+ tristate "Cadence RTC driver"
+ depends on OF && HAS_IOMEM
+ help
+ If you say Y here you will get access to Cadence RTC IP
+ found on certain SOCs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called rtc-cadence.
+
config RTC_DRV_FTRTC010
tristate "Faraday Technology FTRTC010 RTC"
depends on HAS_IOMEM
@@ -1679,6 +1729,7 @@ config RTC_DRV_SNVS
config RTC_DRV_IMX_SC
depends on IMX_SCU
+ depends on HAVE_ARM_SMCCC
tristate "NXP i.MX System Controller RTC support"
help
If you say yes here you get support for the NXP i.MX System
@@ -1795,8 +1846,7 @@ comment "HID Sensor RTC drivers"
config RTC_DRV_HID_SENSOR_TIME
tristate "HID Sensor Time"
depends on USB_HID
- select IIO
- select HID_SENSOR_HUB
+ depends on HID_SENSOR_HUB && IIO
select HID_SENSOR_IIO_COMMON
help
Say yes here to build support for the HID Sensors of type Time.
@@ -1814,4 +1864,15 @@ config RTC_DRV_GOLDFISH
Goldfish is a code name for the virtual platform developed by Google
for Android emulation.
+config RTC_DRV_WILCO_EC
+ tristate "Wilco EC RTC"
+ depends on WILCO_EC
+ default m
+ help
+ If you say yes here, you get read/write support for the Real Time
+ Clock on the Wilco Embedded Controller (Wilco is a kind of Chromebook)
+
+ This can also be built as a module. If so, the module will
+ be named "rtc_wilco_ec".
+
endif # RTC_CLASS
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index df022d820bee..fe3962496685 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -28,6 +28,7 @@ obj-$(CONFIG_RTC_DRV_88PM860X) += rtc-88pm860x.o
obj-$(CONFIG_RTC_DRV_AB3100) += rtc-ab3100.o
obj-$(CONFIG_RTC_DRV_AB8500) += rtc-ab8500.o
obj-$(CONFIG_RTC_DRV_ABB5ZES3) += rtc-ab-b5ze-s3.o
+obj-$(CONFIG_RTC_DRV_ABEOZ9) += rtc-ab-eoz9.o
obj-$(CONFIG_RTC_DRV_ABX80X) += rtc-abx80x.o
obj-$(CONFIG_RTC_DRV_AC100) += rtc-ac100.o
obj-$(CONFIG_RTC_DRV_ARMADA38X) += rtc-armada38x.o
@@ -39,6 +40,7 @@ obj-$(CONFIG_RTC_DRV_AU1XXX) += rtc-au1xxx.o
obj-$(CONFIG_RTC_DRV_BQ32K) += rtc-bq32k.o
obj-$(CONFIG_RTC_DRV_BQ4802) += rtc-bq4802.o
obj-$(CONFIG_RTC_DRV_BRCMSTB) += rtc-brcmstb-waketimer.o
+obj-$(CONFIG_RTC_DRV_CADENCE) += rtc-cadence.o
obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o
obj-$(CONFIG_RTC_DRV_COH901331) += rtc-coh901331.o
obj-$(CONFIG_RTC_DRV_CPCAP) += rtc-cpcap.o
@@ -100,6 +102,7 @@ obj-$(CONFIG_RTC_DRV_MAX8997) += rtc-max8997.o
obj-$(CONFIG_RTC_DRV_MAX8998) += rtc-max8998.o
obj-$(CONFIG_RTC_DRV_MC13XXX) += rtc-mc13xxx.o
obj-$(CONFIG_RTC_DRV_MCP795) += rtc-mcp795.o
+obj-$(CONFIG_RTC_DRV_MESON) += rtc-meson.o
obj-$(CONFIG_RTC_DRV_MOXART) += rtc-moxart.o
obj-$(CONFIG_RTC_DRV_MPC5121) += rtc-mpc5121.o
obj-$(CONFIG_RTC_DRV_MSM6242) += rtc-msm6242.o
@@ -137,6 +140,7 @@ obj-$(CONFIG_RTC_DRV_RS5C313) += rtc-rs5c313.o
obj-$(CONFIG_RTC_DRV_RS5C348) += rtc-rs5c348.o
obj-$(CONFIG_RTC_DRV_RS5C372) += rtc-rs5c372.o
obj-$(CONFIG_RTC_DRV_RTD119X) += rtc-rtd119x.o
+obj-$(CONFIG_RTC_DRV_RV3028) += rtc-rv3028.o
obj-$(CONFIG_RTC_DRV_RV3029C2) += rtc-rv3029c2.o
obj-$(CONFIG_RTC_DRV_RV8803) += rtc-rv8803.o
obj-$(CONFIG_RTC_DRV_RX4581) += rtc-rx4581.o
@@ -149,6 +153,7 @@ obj-$(CONFIG_RTC_DRV_S3C) += rtc-s3c.o
obj-$(CONFIG_RTC_DRV_S5M) += rtc-s5m.o
obj-$(CONFIG_RTC_DRV_SA1100) += rtc-sa1100.o
obj-$(CONFIG_RTC_DRV_SC27XX) += rtc-sc27xx.o
+obj-$(CONFIG_RTC_DRV_SD3078) += rtc-sd3078.o
obj-$(CONFIG_RTC_DRV_SH) += rtc-sh.o
obj-$(CONFIG_RTC_DRV_SIRFSOC) += rtc-sirfsoc.o
obj-$(CONFIG_RTC_DRV_SNVS) += rtc-snvs.o
@@ -172,6 +177,7 @@ obj-$(CONFIG_RTC_DRV_V3020) += rtc-v3020.o
obj-$(CONFIG_RTC_DRV_VR41XX) += rtc-vr41xx.o
obj-$(CONFIG_RTC_DRV_VRTC) += rtc-mrst.o
obj-$(CONFIG_RTC_DRV_VT8500) += rtc-vt8500.o
+obj-$(CONFIG_RTC_DRV_WILCO_EC) += rtc-wilco-ec.o
obj-$(CONFIG_RTC_DRV_WM831X) += rtc-wm831x.o
obj-$(CONFIG_RTC_DRV_WM8350) += rtc-wm8350.o
obj-$(CONFIG_RTC_DRV_X1205) += rtc-x1205.o
diff --git a/drivers/rtc/dev.c b/drivers/rtc/dev.c
index 43d962a9c210..1d006ef4bb57 100644
--- a/drivers/rtc/dev.c
+++ b/drivers/rtc/dev.c
@@ -178,11 +178,6 @@ rtc_dev_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
remove_wait_queue(&rtc->irq_queue, &wait);
if (ret == 0) {
- /* Check for any data updates */
- if (rtc->ops->read_callback)
- data = rtc->ops->read_callback(rtc->dev.parent,
- data);
-
if (sizeof(int) != sizeof(long) &&
count == sizeof(unsigned int))
ret = put_user(data, (unsigned int __user *)buf) ?:
diff --git a/drivers/rtc/lib.c b/drivers/rtc/lib.c
index ef160da84220..9714cb3d1e29 100644
--- a/drivers/rtc/lib.c
+++ b/drivers/rtc/lib.c
@@ -100,7 +100,7 @@ int rtc_valid_tm(struct rtc_time *tm)
if (tm->tm_year < 70
|| ((unsigned)tm->tm_mon) >= 12
|| tm->tm_mday < 1
- || tm->tm_mday > rtc_month_days(tm->tm_mon, tm->tm_year + 1900)
+ || tm->tm_mday > rtc_month_days(tm->tm_mon, ((unsigned)tm->tm_year + 1900))
|| ((unsigned)tm->tm_hour) >= 24
|| ((unsigned)tm->tm_min) >= 60
|| ((unsigned)tm->tm_sec) >= 60)
@@ -116,8 +116,8 @@ EXPORT_SYMBOL(rtc_valid_tm);
*/
time64_t rtc_tm_to_time64(struct rtc_time *tm)
{
- return mktime64(tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
- tm->tm_hour, tm->tm_min, tm->tm_sec);
+ return mktime64(((unsigned)tm->tm_year + 1900), tm->tm_mon + 1,
+ tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
}
EXPORT_SYMBOL(rtc_tm_to_time64);
diff --git a/drivers/rtc/rtc-88pm80x.c b/drivers/rtc/rtc-88pm80x.c
index cab293cb2bf0..1fc48ebd3cd0 100644
--- a/drivers/rtc/rtc-88pm80x.c
+++ b/drivers/rtc/rtc-88pm80x.c
@@ -114,12 +114,14 @@ static int pm80x_rtc_read_time(struct device *dev, struct rtc_time *tm)
unsigned char buf[4];
unsigned long ticks, base, data;
regmap_raw_read(info->map, PM800_RTC_EXPIRE2_1, buf, 4);
- base = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+ base = ((unsigned long)buf[3] << 24) | (buf[2] << 16) |
+ (buf[1] << 8) | buf[0];
dev_dbg(info->dev, "%x-%x-%x-%x\n", buf[0], buf[1], buf[2], buf[3]);
/* load 32-bit read-only counter */
regmap_raw_read(info->map, PM800_RTC_COUNTER1, buf, 4);
- data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+ data = ((unsigned long)buf[3] << 24) | (buf[2] << 16) |
+ (buf[1] << 8) | buf[0];
ticks = base + data;
dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n",
base, data, ticks);
@@ -137,7 +139,8 @@ static int pm80x_rtc_set_time(struct device *dev, struct rtc_time *tm)
/* load 32-bit read-only counter */
regmap_raw_read(info->map, PM800_RTC_COUNTER1, buf, 4);
- data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+ data = ((unsigned long)buf[3] << 24) | (buf[2] << 16) |
+ (buf[1] << 8) | buf[0];
base = ticks - data;
dev_dbg(info->dev, "set base:0x%lx, RO count:0x%lx, ticks:0x%lx\n",
base, data, ticks);
@@ -158,11 +161,13 @@ static int pm80x_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
int ret;
regmap_raw_read(info->map, PM800_RTC_EXPIRE2_1, buf, 4);
- base = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+ base = ((unsigned long)buf[3] << 24) | (buf[2] << 16) |
+ (buf[1] << 8) | buf[0];
dev_dbg(info->dev, "%x-%x-%x-%x\n", buf[0], buf[1], buf[2], buf[3]);
regmap_raw_read(info->map, PM800_RTC_EXPIRE1_1, buf, 4);
- data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+ data = ((unsigned long)buf[3] << 24) | (buf[2] << 16) |
+ (buf[1] << 8) | buf[0];
ticks = base + data;
dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n",
base, data, ticks);
@@ -185,12 +190,14 @@ static int pm80x_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
regmap_update_bits(info->map, PM800_RTC_CONTROL, PM800_ALARM1_EN, 0);
regmap_raw_read(info->map, PM800_RTC_EXPIRE2_1, buf, 4);
- base = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+ base = ((unsigned long)buf[3] << 24) | (buf[2] << 16) |
+ (buf[1] << 8) | buf[0];
dev_dbg(info->dev, "%x-%x-%x-%x\n", buf[0], buf[1], buf[2], buf[3]);
/* load 32-bit read-only counter */
regmap_raw_read(info->map, PM800_RTC_COUNTER1, buf, 4);
- data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+ data = ((unsigned long)buf[3] << 24) | (buf[2] << 16) |
+ (buf[1] << 8) | buf[0];
ticks = base + data;
dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n",
base, data, ticks);
diff --git a/drivers/rtc/rtc-88pm860x.c b/drivers/rtc/rtc-88pm860x.c
index 01ffc0ef8033..d25282b4a7dd 100644
--- a/drivers/rtc/rtc-88pm860x.c
+++ b/drivers/rtc/rtc-88pm860x.c
@@ -115,11 +115,13 @@ static int pm860x_rtc_read_time(struct device *dev, struct rtc_time *tm)
pm860x_page_bulk_read(info->i2c, REG0_ADDR, 8, buf);
dev_dbg(info->dev, "%x-%x-%x-%x-%x-%x-%x-%x\n", buf[0], buf[1],
buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);
- base = (buf[1] << 24) | (buf[3] << 16) | (buf[5] << 8) | buf[7];
+ base = ((unsigned long)buf[1] << 24) | (buf[3] << 16) |
+ (buf[5] << 8) | buf[7];
/* load 32-bit read-only counter */
pm860x_bulk_read(info->i2c, PM8607_RTC_COUNTER1, 4, buf);
- data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+ data = ((unsigned long)buf[3] << 24) | (buf[2] << 16) |
+ (buf[1] << 8) | buf[0];
ticks = base + data;
dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n",
base, data, ticks);
@@ -145,7 +147,8 @@ static int pm860x_rtc_set_time(struct device *dev, struct rtc_time *tm)
/* load 32-bit read-only counter */
pm860x_bulk_read(info->i2c, PM8607_RTC_COUNTER1, 4, buf);
- data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+ data = ((unsigned long)buf[3] << 24) | (buf[2] << 16) |
+ (buf[1] << 8) | buf[0];
base = ticks - data;
dev_dbg(info->dev, "set base:0x%lx, RO count:0x%lx, ticks:0x%lx\n",
base, data, ticks);
@@ -170,10 +173,12 @@ static int pm860x_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
pm860x_page_bulk_read(info->i2c, REG0_ADDR, 8, buf);
dev_dbg(info->dev, "%x-%x-%x-%x-%x-%x-%x-%x\n", buf[0], buf[1],
buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);
- base = (buf[1] << 24) | (buf[3] << 16) | (buf[5] << 8) | buf[7];
+ base = ((unsigned long)buf[1] << 24) | (buf[3] << 16) |
+ (buf[5] << 8) | buf[7];
pm860x_bulk_read(info->i2c, PM8607_RTC_EXPIRE1, 4, buf);
- data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+ data = ((unsigned long)buf[3] << 24) | (buf[2] << 16) |
+ (buf[1] << 8) | buf[0];
ticks = base + data;
dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n",
base, data, ticks);
@@ -198,11 +203,13 @@ static int pm860x_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
pm860x_page_bulk_read(info->i2c, REG0_ADDR, 8, buf);
dev_dbg(info->dev, "%x-%x-%x-%x-%x-%x-%x-%x\n", buf[0], buf[1],
buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);
- base = (buf[1] << 24) | (buf[3] << 16) | (buf[5] << 8) | buf[7];
+ base = ((unsigned long)buf[1] << 24) | (buf[3] << 16) |
+ (buf[5] << 8) | buf[7];
/* load 32-bit read-only counter */
pm860x_bulk_read(info->i2c, PM8607_RTC_COUNTER1, 4, buf);
- data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+ data = ((unsigned long)buf[3] << 24) | (buf[2] << 16) |
+ (buf[1] << 8) | buf[0];
ticks = base + data;
dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n",
base, data, ticks);
diff --git a/drivers/rtc/rtc-ab-eoz9.c b/drivers/rtc/rtc-ab-eoz9.c
new file mode 100644
index 000000000000..e4f6e0061ccf
--- /dev/null
+++ b/drivers/rtc/rtc-ab-eoz9.c
@@ -0,0 +1,465 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Real Time Clock driver for AB-RTCMC-32.768kHz-EOZ9 chip.
+ * Copyright (C) 2019 Orolia
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/rtc.h>
+#include <linux/i2c.h>
+#include <linux/bcd.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+
+#define ABEOZ9_REG_CTRL1 0x00
+#define ABEOZ9_REG_CTRL1_MASK GENMASK(7, 0)
+#define ABEOZ9_REG_CTRL1_WE BIT(0)
+#define ABEOZ9_REG_CTRL1_TE BIT(1)
+#define ABEOZ9_REG_CTRL1_TAR BIT(2)
+#define ABEOZ9_REG_CTRL1_EERE BIT(3)
+#define ABEOZ9_REG_CTRL1_SRON BIT(4)
+#define ABEOZ9_REG_CTRL1_TD0 BIT(5)
+#define ABEOZ9_REG_CTRL1_TD1 BIT(6)
+#define ABEOZ9_REG_CTRL1_CLKINT BIT(7)
+
+#define ABEOZ9_REG_CTRL_INT 0x01
+#define ABEOZ9_REG_CTRL_INT_AIE BIT(0)
+#define ABEOZ9_REG_CTRL_INT_TIE BIT(1)
+#define ABEOZ9_REG_CTRL_INT_V1IE BIT(2)
+#define ABEOZ9_REG_CTRL_INT_V2IE BIT(3)
+#define ABEOZ9_REG_CTRL_INT_SRIE BIT(4)
+
+#define ABEOZ9_REG_CTRL_INT_FLAG 0x02
+#define ABEOZ9_REG_CTRL_INT_FLAG_AF BIT(0)
+#define ABEOZ9_REG_CTRL_INT_FLAG_TF BIT(1)
+#define ABEOZ9_REG_CTRL_INT_FLAG_V1IF BIT(2)
+#define ABEOZ9_REG_CTRL_INT_FLAG_V2IF BIT(3)
+#define ABEOZ9_REG_CTRL_INT_FLAG_SRF BIT(4)
+
+#define ABEOZ9_REG_CTRL_STATUS 0x03
+#define ABEOZ9_REG_CTRL_STATUS_V1F BIT(2)
+#define ABEOZ9_REG_CTRL_STATUS_V2F BIT(3)
+#define ABEOZ9_REG_CTRL_STATUS_SR BIT(4)
+#define ABEOZ9_REG_CTRL_STATUS_PON BIT(5)
+#define ABEOZ9_REG_CTRL_STATUS_EEBUSY BIT(7)
+
+#define ABEOZ9_REG_SEC 0x08
+#define ABEOZ9_REG_MIN 0x09
+#define ABEOZ9_REG_HOURS 0x0A
+#define ABEOZ9_HOURS_PM BIT(6)
+#define ABEOZ9_REG_DAYS 0x0B
+#define ABEOZ9_REG_WEEKDAYS 0x0C
+#define ABEOZ9_REG_MONTHS 0x0D
+#define ABEOZ9_REG_YEARS 0x0E
+
+#define ABEOZ9_SEC_LEN 7
+
+#define ABEOZ9_REG_REG_TEMP 0x20
+#define ABEOZ953_TEMP_MAX 120
+#define ABEOZ953_TEMP_MIN -60
+
+#define ABEOZ9_REG_EEPROM 0x30
+#define ABEOZ9_REG_EEPROM_MASK GENMASK(8, 0)
+#define ABEOZ9_REG_EEPROM_THP BIT(0)
+#define ABEOZ9_REG_EEPROM_THE BIT(1)
+#define ABEOZ9_REG_EEPROM_FD0 BIT(2)
+#define ABEOZ9_REG_EEPROM_FD1 BIT(3)
+#define ABEOZ9_REG_EEPROM_R1K BIT(4)
+#define ABEOZ9_REG_EEPROM_R5K BIT(5)
+#define ABEOZ9_REG_EEPROM_R20K BIT(6)
+#define ABEOZ9_REG_EEPROM_R80K BIT(7)
+
+struct abeoz9_rtc_data {
+ struct rtc_device *rtc;
+ struct regmap *regmap;
+ struct device *hwmon_dev;
+};
+
+static int abeoz9_check_validity(struct device *dev)
+{
+ struct abeoz9_rtc_data *data = dev_get_drvdata(dev);
+ struct regmap *regmap = data->regmap;
+ int ret;
+ int val;
+
+ ret = regmap_read(regmap, ABEOZ9_REG_CTRL_STATUS, &val);
+ if (ret < 0) {
+ dev_err(dev,
+ "unable to get CTRL_STATUS register (%d)\n", ret);
+ return ret;
+ }
+
+ if (val & ABEOZ9_REG_CTRL_STATUS_PON) {
+ dev_warn(dev, "power-on reset detected, date is invalid\n");
+ return -EINVAL;
+ }
+
+ if (val & ABEOZ9_REG_CTRL_STATUS_V1F) {
+ dev_warn(dev,
+ "voltage drops below VLOW1 threshold, date is invalid\n");
+ return -EINVAL;
+ }
+
+ if ((val & ABEOZ9_REG_CTRL_STATUS_V2F)) {
+ dev_warn(dev,
+ "voltage drops below VLOW2 threshold, date is invalid\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int abeoz9_reset_validity(struct regmap *regmap)
+{
+ return regmap_update_bits(regmap, ABEOZ9_REG_CTRL_STATUS,
+ ABEOZ9_REG_CTRL_STATUS_V1F |
+ ABEOZ9_REG_CTRL_STATUS_V2F |
+ ABEOZ9_REG_CTRL_STATUS_PON,
+ 0);
+}
+
+static int abeoz9_rtc_get_time(struct device *dev, struct rtc_time *tm)
+{
+ struct abeoz9_rtc_data *data = dev_get_drvdata(dev);
+ u8 regs[ABEOZ9_SEC_LEN];
+ int ret;
+
+ ret = abeoz9_check_validity(dev);
+ if (ret)
+ return ret;
+
+ ret = regmap_bulk_read(data->regmap, ABEOZ9_REG_SEC,
+ regs,
+ sizeof(regs));
+ if (ret) {
+ dev_err(dev, "reading RTC time failed (%d)\n", ret);
+ return ret;
+ }
+
+ tm->tm_sec = bcd2bin(regs[ABEOZ9_REG_SEC - ABEOZ9_REG_SEC] & 0x7F);
+ tm->tm_min = bcd2bin(regs[ABEOZ9_REG_MIN - ABEOZ9_REG_SEC] & 0x7F);
+
+ if (regs[ABEOZ9_REG_HOURS - ABEOZ9_REG_SEC] & ABEOZ9_HOURS_PM) {
+ tm->tm_hour =
+ bcd2bin(regs[ABEOZ9_REG_HOURS - ABEOZ9_REG_SEC] & 0x1f);
+ if (regs[ABEOZ9_REG_HOURS - ABEOZ9_REG_SEC] & ABEOZ9_HOURS_PM)
+ tm->tm_hour += 12;
+ } else {
+ tm->tm_hour = bcd2bin(regs[ABEOZ9_REG_HOURS - ABEOZ9_REG_SEC]);
+ }
+
+ tm->tm_mday = bcd2bin(regs[ABEOZ9_REG_DAYS - ABEOZ9_REG_SEC]);
+ tm->tm_wday = bcd2bin(regs[ABEOZ9_REG_WEEKDAYS - ABEOZ9_REG_SEC]);
+ tm->tm_mon = bcd2bin(regs[ABEOZ9_REG_MONTHS - ABEOZ9_REG_SEC]) - 1;
+ tm->tm_year = bcd2bin(regs[ABEOZ9_REG_YEARS - ABEOZ9_REG_SEC]) + 100;
+
+ return ret;
+}
+
+static int abeoz9_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct abeoz9_rtc_data *data = dev_get_drvdata(dev);
+ struct regmap *regmap = data->regmap;
+ u8 regs[ABEOZ9_SEC_LEN];
+ int ret;
+
+ regs[ABEOZ9_REG_SEC - ABEOZ9_REG_SEC] = bin2bcd(tm->tm_sec);
+ regs[ABEOZ9_REG_MIN - ABEOZ9_REG_SEC] = bin2bcd(tm->tm_min);
+ regs[ABEOZ9_REG_HOURS - ABEOZ9_REG_SEC] = bin2bcd(tm->tm_hour);
+ regs[ABEOZ9_REG_DAYS - ABEOZ9_REG_SEC] = bin2bcd(tm->tm_mday);
+ regs[ABEOZ9_REG_WEEKDAYS - ABEOZ9_REG_SEC] = bin2bcd(tm->tm_wday);
+ regs[ABEOZ9_REG_MONTHS - ABEOZ9_REG_SEC] = bin2bcd(tm->tm_mon + 1);
+ regs[ABEOZ9_REG_YEARS - ABEOZ9_REG_SEC] = bin2bcd(tm->tm_year - 100);
+
+ ret = regmap_bulk_write(data->regmap, ABEOZ9_REG_SEC,
+ regs,
+ sizeof(regs));
+
+ if (ret) {
+ dev_err(dev, "set RTC time failed (%d)\n", ret);
+ return ret;
+ }
+
+ return abeoz9_reset_validity(regmap);
+}
+
+static int abeoz9_trickle_parse_dt(struct device_node *node)
+{
+ u32 ohms = 0;
+
+ if (of_property_read_u32(node, "trickle-resistor-ohms", &ohms))
+ return 0;
+
+ switch (ohms) {
+ case 1000:
+ return ABEOZ9_REG_EEPROM_R1K;
+ case 5000:
+ return ABEOZ9_REG_EEPROM_R5K;
+ case 20000:
+ return ABEOZ9_REG_EEPROM_R20K;
+ case 80000:
+ return ABEOZ9_REG_EEPROM_R80K;
+ default:
+ return 0;
+ }
+}
+
+static int abeoz9_rtc_setup(struct device *dev, struct device_node *node)
+{
+ struct abeoz9_rtc_data *data = dev_get_drvdata(dev);
+ struct regmap *regmap = data->regmap;
+ int ret;
+
+ /* Enable Self Recovery, Clock for Watch and EEPROM refresh functions */
+ ret = regmap_update_bits(regmap, ABEOZ9_REG_CTRL1,
+ ABEOZ9_REG_CTRL1_MASK,
+ ABEOZ9_REG_CTRL1_WE |
+ ABEOZ9_REG_CTRL1_EERE |
+ ABEOZ9_REG_CTRL1_SRON);
+ if (ret < 0) {
+ dev_err(dev, "unable to set CTRL_1 register (%d)\n", ret);
+ return ret;
+ }
+
+ ret = regmap_write(regmap, ABEOZ9_REG_CTRL_INT, 0);
+ if (ret < 0) {
+ dev_err(dev,
+ "unable to set control CTRL_INT register (%d)\n",
+ ret);
+ return ret;
+ }
+
+ ret = regmap_write(regmap, ABEOZ9_REG_CTRL_INT_FLAG, 0);
+ if (ret < 0) {
+ dev_err(dev,
+ "unable to set control CTRL_INT_FLAG register (%d)\n",
+ ret);
+ return ret;
+ }
+
+ ret = abeoz9_trickle_parse_dt(node);
+
+ /* Enable built-in termometer */
+ ret |= ABEOZ9_REG_EEPROM_THE;
+
+ ret = regmap_update_bits(regmap, ABEOZ9_REG_EEPROM,
+ ABEOZ9_REG_EEPROM_MASK,
+ ret);
+ if (ret < 0) {
+ dev_err(dev, "unable to set EEPROM register (%d)\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static const struct rtc_class_ops rtc_ops = {
+ .read_time = abeoz9_rtc_get_time,
+ .set_time = abeoz9_rtc_set_time,
+};
+
+static const struct regmap_config abeoz9_rtc_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
+#if IS_REACHABLE(CONFIG_HWMON)
+
+static int abeoz9z3_temp_read(struct device *dev,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel, long *temp)
+{
+ struct abeoz9_rtc_data *data = dev_get_drvdata(dev);
+ struct regmap *regmap = data->regmap;
+ int ret;
+ unsigned int val;
+
+ ret = regmap_read(regmap, ABEOZ9_REG_CTRL_STATUS, &val);
+ if (ret < 0)
+ return ret;
+
+ if ((val & ABEOZ9_REG_CTRL_STATUS_V1F) ||
+ (val & ABEOZ9_REG_CTRL_STATUS_V2F)) {
+ dev_err(dev,
+ "thermometer might be disabled due to low voltage\n");
+ return -EINVAL;
+ }
+
+ switch (attr) {
+ case hwmon_temp_input:
+ ret = regmap_read(regmap, ABEOZ9_REG_REG_TEMP, &val);
+ if (ret < 0)
+ return ret;
+ *temp = 1000 * (val + ABEOZ953_TEMP_MIN);
+ return 0;
+ case hwmon_temp_max:
+ *temp = 1000 * ABEOZ953_TEMP_MAX;
+ return 0;
+ case hwmon_temp_min:
+ *temp = 1000 * ABEOZ953_TEMP_MIN;
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static umode_t abeoz9_is_visible(const void *data,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ switch (attr) {
+ case hwmon_temp_input:
+ case hwmon_temp_max:
+ case hwmon_temp_min:
+ return 0444;
+ default:
+ return 0;
+ }
+}
+
+static const u32 abeoz9_chip_config[] = {
+ HWMON_C_REGISTER_TZ,
+ 0
+};
+
+static const struct hwmon_channel_info abeoz9_chip = {
+ .type = hwmon_chip,
+ .config = abeoz9_chip_config,
+};
+
+static const u32 abeoz9_temp_config[] = {
+ HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MIN,
+ 0
+};
+
+static const struct hwmon_channel_info abeoz9_temp = {
+ .type = hwmon_temp,
+ .config = abeoz9_temp_config,
+};
+
+static const struct hwmon_channel_info *abeoz9_info[] = {
+ &abeoz9_chip,
+ &abeoz9_temp,
+ NULL
+};
+
+static const struct hwmon_ops abeoz9_hwmon_ops = {
+ .is_visible = abeoz9_is_visible,
+ .read = abeoz9z3_temp_read,
+};
+
+static const struct hwmon_chip_info abeoz9_chip_info = {
+ .ops = &abeoz9_hwmon_ops,
+ .info = abeoz9_info,
+};
+
+static void abeoz9_hwmon_register(struct device *dev,
+ struct abeoz9_rtc_data *data)
+{
+ data->hwmon_dev =
+ devm_hwmon_device_register_with_info(dev,
+ "abeoz9",
+ data,
+ &abeoz9_chip_info,
+ NULL);
+ if (IS_ERR(data->hwmon_dev)) {
+ dev_warn(dev, "unable to register hwmon device %ld\n",
+ PTR_ERR(data->hwmon_dev));
+ }
+}
+
+#else
+
+static void abeoz9_hwmon_register(struct device *dev,
+ struct abeoz9_rtc_data *data)
+{
+}
+
+#endif
+
+static int abeoz9_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct abeoz9_rtc_data *data = NULL;
+ struct device *dev = &client->dev;
+ struct regmap *regmap;
+ int ret;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C |
+ I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_I2C_BLOCK)) {
+ ret = -ENODEV;
+ goto err;
+ }
+
+ regmap = devm_regmap_init_i2c(client, &abeoz9_rtc_regmap_config);
+ if (IS_ERR(regmap)) {
+ ret = PTR_ERR(regmap);
+ dev_err(dev, "regmap allocation failed: %d\n", ret);
+ goto err;
+ }
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ data->regmap = regmap;
+ dev_set_drvdata(dev, data);
+
+ ret = abeoz9_rtc_setup(dev, client->dev.of_node);
+ if (ret)
+ goto err;
+
+ data->rtc = devm_rtc_allocate_device(dev);
+ ret = PTR_ERR_OR_ZERO(data->rtc);
+ if (ret)
+ goto err;
+
+ data->rtc->ops = &rtc_ops;
+ data->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
+ data->rtc->range_max = RTC_TIMESTAMP_END_2099;
+
+ ret = rtc_register_device(data->rtc);
+ if (ret)
+ goto err;
+
+ abeoz9_hwmon_register(dev, data);
+ return 0;
+
+err:
+ dev_err(dev, "unable to register RTC device (%d)\n", ret);
+ return ret;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id abeoz9_dt_match[] = {
+ { .compatible = "abracon,abeoz9" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, abeoz9_dt_match);
+#endif
+
+static const struct i2c_device_id abeoz9_id[] = {
+ { "abeoz9", 0 },
+ { }
+};
+
+static struct i2c_driver abeoz9_driver = {
+ .driver = {
+ .name = "rtc-ab-eoz9",
+ .of_match_table = of_match_ptr(abeoz9_dt_match),
+ },
+ .probe = abeoz9_probe,
+ .id_table = abeoz9_id,
+};
+
+module_i2c_driver(abeoz9_driver);
+
+MODULE_AUTHOR("Artem Panfilov <panfilov.artyom@gmail.com>");
+MODULE_DESCRIPTION("Abracon AB-RTCMC-32.768kHz-EOZ9 RTC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-abx80x.c b/drivers/rtc/rtc-abx80x.c
index 4d24f7288ad7..6ddcad642d1e 100644
--- a/drivers/rtc/rtc-abx80x.c
+++ b/drivers/rtc/rtc-abx80x.c
@@ -5,7 +5,7 @@
* Copyright 2014-2015 Macq S.A.
*
* Author: Philippe De Muyter <phdm@macqel.be>
- * Author: Alexandre Belloni <alexandre.belloni@free-electrons.com>
+ * Author: Alexandre Belloni <alexandre.belloni@bootlin.com>
*
* 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
@@ -46,6 +46,9 @@
#define ABX8XX_CTRL_ARST BIT(2)
#define ABX8XX_CTRL_12_24 BIT(6)
+#define ABX8XX_REG_CTRL2 0x11
+#define ABX8XX_CTRL2_RSVD BIT(5)
+
#define ABX8XX_REG_IRQ 0x12
#define ABX8XX_IRQ_AIE BIT(2)
#define ABX8XX_IRQ_IM_1_4 (0x3 << 5)
@@ -78,6 +81,9 @@
#define ABX8XX_REG_ID0 0x28
+#define ABX8XX_REG_OUT_CTRL 0x30
+#define ABX8XX_OUT_CTRL_EXDS BIT(4)
+
#define ABX8XX_REG_TRICKLE 0x20
#define ABX8XX_TRICKLE_CHARGE_ENABLE 0xa0
#define ABX8XX_TRICKLE_STANDARD_DIODE 0x8
@@ -86,7 +92,7 @@
static u8 trickle_resistors[] = {0, 3, 6, 11};
enum abx80x_chip {AB0801, AB0803, AB0804, AB0805,
- AB1801, AB1803, AB1804, AB1805, ABX80X};
+ AB1801, AB1803, AB1804, AB1805, RV1805, ABX80X};
struct abx80x_cap {
u16 pn;
@@ -103,6 +109,7 @@ static struct abx80x_cap abx80x_caps[] = {
[AB1803] = {.pn = 0x1803},
[AB1804] = {.pn = 0x1804, .has_tc = true, .has_wdog = true},
[AB1805] = {.pn = 0x1805, .has_tc = true, .has_wdog = true},
+ [RV1805] = {.pn = 0x1805, .has_tc = true, .has_wdog = true},
[ABX80X] = {.pn = 0}
};
@@ -723,6 +730,62 @@ static int abx80x_probe(struct i2c_client *client,
return -EIO;
}
+ /* Configure RV1805 specifics */
+ if (part == RV1805) {
+ /*
+ * Avoid accidentally entering test mode. This can happen
+ * on the RV1805 in case the reserved bit 5 in control2
+ * register is set. RV-1805-C3 datasheet indicates that
+ * the bit should be cleared in section 11h - Control2.
+ */
+ data = i2c_smbus_read_byte_data(client, ABX8XX_REG_CTRL2);
+ if (data < 0) {
+ dev_err(&client->dev,
+ "Unable to read control2 register\n");
+ return -EIO;
+ }
+
+ err = i2c_smbus_write_byte_data(client, ABX8XX_REG_CTRL2,
+ data & ~ABX8XX_CTRL2_RSVD);
+ if (err < 0) {
+ dev_err(&client->dev,
+ "Unable to write control2 register\n");
+ return -EIO;
+ }
+
+ /*
+ * Avoid extra power leakage. The RV1805 uses smaller
+ * 10pin package and the EXTI input is not present.
+ * Disable it to avoid leakage.
+ */
+ data = i2c_smbus_read_byte_data(client, ABX8XX_REG_OUT_CTRL);
+ if (data < 0) {
+ dev_err(&client->dev,
+ "Unable to read output control register\n");
+ return -EIO;
+ }
+
+ /*
+ * Write the configuration key register to enable access to
+ * the config2 register
+ */
+ err = i2c_smbus_write_byte_data(client, ABX8XX_REG_CFG_KEY,
+ ABX8XX_CFG_KEY_MISC);
+ if (err < 0) {
+ dev_err(&client->dev,
+ "Unable to write configuration key\n");
+ return -EIO;
+ }
+
+ err = i2c_smbus_write_byte_data(client, ABX8XX_REG_OUT_CTRL,
+ data | ABX8XX_OUT_CTRL_EXDS);
+ if (err < 0) {
+ dev_err(&client->dev,
+ "Unable to write output control register\n");
+ return -EIO;
+ }
+ }
+
/* part autodetection */
if (part == ABX80X) {
for (i = 0; abx80x_caps[i].pn; i++)
@@ -826,7 +889,7 @@ static const struct i2c_device_id abx80x_id[] = {
{ "ab1803", AB1803 },
{ "ab1804", AB1804 },
{ "ab1805", AB1805 },
- { "rv1805", AB1805 },
+ { "rv1805", RV1805 },
{ }
};
MODULE_DEVICE_TABLE(i2c, abx80x_id);
@@ -843,6 +906,6 @@ static struct i2c_driver abx80x_driver = {
module_i2c_driver(abx80x_driver);
MODULE_AUTHOR("Philippe De Muyter <phdm@macqel.be>");
-MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@free-electrons.com>");
+MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@bootlin.com>");
MODULE_DESCRIPTION("Abracon ABX80X RTC driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/rtc/rtc-cadence.c b/drivers/rtc/rtc-cadence.c
new file mode 100644
index 000000000000..3b7d643c8a63
--- /dev/null
+++ b/drivers/rtc/rtc-cadence.c
@@ -0,0 +1,423 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright 2019 Cadence
+ *
+ * Authors:
+ * Jan Kotas <jank@cadence.com>
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/io.h>
+#include <linux/rtc.h>
+#include <linux/clk.h>
+#include <linux/bcd.h>
+#include <linux/bitfield.h>
+#include <linux/interrupt.h>
+#include <linux/pm_wakeirq.h>
+
+/* Registers */
+#define CDNS_RTC_CTLR 0x00
+#define CDNS_RTC_HMR 0x04
+#define CDNS_RTC_TIMR 0x08
+#define CDNS_RTC_CALR 0x0C
+#define CDNS_RTC_TIMAR 0x10
+#define CDNS_RTC_CALAR 0x14
+#define CDNS_RTC_AENR 0x18
+#define CDNS_RTC_EFLR 0x1C
+#define CDNS_RTC_IENR 0x20
+#define CDNS_RTC_IDISR 0x24
+#define CDNS_RTC_IMSKR 0x28
+#define CDNS_RTC_STSR 0x2C
+#define CDNS_RTC_KRTCR 0x30
+
+/* Control */
+#define CDNS_RTC_CTLR_TIME BIT(0)
+#define CDNS_RTC_CTLR_CAL BIT(1)
+#define CDNS_RTC_CTLR_TIME_CAL (CDNS_RTC_CTLR_TIME | CDNS_RTC_CTLR_CAL)
+
+/* Status */
+#define CDNS_RTC_STSR_VT BIT(0)
+#define CDNS_RTC_STSR_VC BIT(1)
+#define CDNS_RTC_STSR_VTA BIT(2)
+#define CDNS_RTC_STSR_VCA BIT(3)
+#define CDNS_RTC_STSR_VT_VC (CDNS_RTC_STSR_VT | CDNS_RTC_STSR_VC)
+#define CDNS_RTC_STSR_VTA_VCA (CDNS_RTC_STSR_VTA | CDNS_RTC_STSR_VCA)
+
+/* Keep RTC */
+#define CDNS_RTC_KRTCR_KRTC BIT(0)
+
+/* Alarm, Event, Interrupt */
+#define CDNS_RTC_AEI_HOS BIT(0)
+#define CDNS_RTC_AEI_SEC BIT(1)
+#define CDNS_RTC_AEI_MIN BIT(2)
+#define CDNS_RTC_AEI_HOUR BIT(3)
+#define CDNS_RTC_AEI_DATE BIT(4)
+#define CDNS_RTC_AEI_MNTH BIT(5)
+#define CDNS_RTC_AEI_ALRM BIT(6)
+
+/* Time */
+#define CDNS_RTC_TIME_H GENMASK(7, 0)
+#define CDNS_RTC_TIME_S GENMASK(14, 8)
+#define CDNS_RTC_TIME_M GENMASK(22, 16)
+#define CDNS_RTC_TIME_HR GENMASK(29, 24)
+#define CDNS_RTC_TIME_PM BIT(30)
+#define CDNS_RTC_TIME_CH BIT(31)
+
+/* Calendar */
+#define CDNS_RTC_CAL_DAY GENMASK(2, 0)
+#define CDNS_RTC_CAL_M GENMASK(7, 3)
+#define CDNS_RTC_CAL_D GENMASK(13, 8)
+#define CDNS_RTC_CAL_Y GENMASK(23, 16)
+#define CDNS_RTC_CAL_C GENMASK(29, 24)
+#define CDNS_RTC_CAL_CH BIT(31)
+
+#define CDNS_RTC_MAX_REGS_TRIES 3
+
+struct cdns_rtc {
+ struct rtc_device *rtc_dev;
+ struct clk *pclk;
+ struct clk *ref_clk;
+ void __iomem *regs;
+ int irq;
+};
+
+static void cdns_rtc_set_enabled(struct cdns_rtc *crtc, bool enabled)
+{
+ u32 reg = enabled ? 0x0 : CDNS_RTC_CTLR_TIME_CAL;
+
+ writel(reg, crtc->regs + CDNS_RTC_CTLR);
+}
+
+static bool cdns_rtc_get_enabled(struct cdns_rtc *crtc)
+{
+ return !(readl(crtc->regs + CDNS_RTC_CTLR) & CDNS_RTC_CTLR_TIME_CAL);
+}
+
+static irqreturn_t cdns_rtc_irq_handler(int irq, void *id)
+{
+ struct device *dev = id;
+ struct cdns_rtc *crtc = dev_get_drvdata(dev);
+
+ /* Reading the register clears it */
+ if (!(readl(crtc->regs + CDNS_RTC_EFLR) & CDNS_RTC_AEI_ALRM))
+ return IRQ_NONE;
+
+ rtc_update_irq(crtc->rtc_dev, 1, RTC_IRQF | RTC_AF);
+ return IRQ_HANDLED;
+}
+
+static u32 cdns_rtc_time2reg(struct rtc_time *tm)
+{
+ return FIELD_PREP(CDNS_RTC_TIME_S, bin2bcd(tm->tm_sec))
+ | FIELD_PREP(CDNS_RTC_TIME_M, bin2bcd(tm->tm_min))
+ | FIELD_PREP(CDNS_RTC_TIME_HR, bin2bcd(tm->tm_hour));
+}
+
+static void cdns_rtc_reg2time(u32 reg, struct rtc_time *tm)
+{
+ tm->tm_sec = bcd2bin(FIELD_GET(CDNS_RTC_TIME_S, reg));
+ tm->tm_min = bcd2bin(FIELD_GET(CDNS_RTC_TIME_M, reg));
+ tm->tm_hour = bcd2bin(FIELD_GET(CDNS_RTC_TIME_HR, reg));
+}
+
+static int cdns_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct cdns_rtc *crtc = dev_get_drvdata(dev);
+ u32 reg;
+
+ /* If the RTC is disabled, assume the values are invalid */
+ if (!cdns_rtc_get_enabled(crtc))
+ return -EINVAL;
+
+ cdns_rtc_set_enabled(crtc, false);
+
+ reg = readl(crtc->regs + CDNS_RTC_TIMR);
+ cdns_rtc_reg2time(reg, tm);
+
+ reg = readl(crtc->regs + CDNS_RTC_CALR);
+ tm->tm_mday = bcd2bin(FIELD_GET(CDNS_RTC_CAL_D, reg));
+ tm->tm_mon = bcd2bin(FIELD_GET(CDNS_RTC_CAL_M, reg)) - 1;
+ tm->tm_year = bcd2bin(FIELD_GET(CDNS_RTC_CAL_Y, reg))
+ + bcd2bin(FIELD_GET(CDNS_RTC_CAL_C, reg)) * 100 - 1900;
+ tm->tm_wday = bcd2bin(FIELD_GET(CDNS_RTC_CAL_DAY, reg)) - 1;
+
+ cdns_rtc_set_enabled(crtc, true);
+ return 0;
+}
+
+static int cdns_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct cdns_rtc *crtc = dev_get_drvdata(dev);
+ u32 timr, calr, stsr;
+ int ret = -EIO;
+ int year = tm->tm_year + 1900;
+ int tries;
+
+ cdns_rtc_set_enabled(crtc, false);
+
+ timr = cdns_rtc_time2reg(tm);
+
+ calr = FIELD_PREP(CDNS_RTC_CAL_D, bin2bcd(tm->tm_mday))
+ | FIELD_PREP(CDNS_RTC_CAL_M, bin2bcd(tm->tm_mon + 1))
+ | FIELD_PREP(CDNS_RTC_CAL_Y, bin2bcd(year % 100))
+ | FIELD_PREP(CDNS_RTC_CAL_C, bin2bcd(year / 100))
+ | FIELD_PREP(CDNS_RTC_CAL_DAY, tm->tm_wday + 1);
+
+ /* Update registers, check valid flags */
+ for (tries = 0; tries < CDNS_RTC_MAX_REGS_TRIES; tries++) {
+ writel(timr, crtc->regs + CDNS_RTC_TIMR);
+ writel(calr, crtc->regs + CDNS_RTC_CALR);
+ stsr = readl(crtc->regs + CDNS_RTC_STSR);
+
+ if ((stsr & CDNS_RTC_STSR_VT_VC) == CDNS_RTC_STSR_VT_VC) {
+ ret = 0;
+ break;
+ }
+ }
+
+ cdns_rtc_set_enabled(crtc, true);
+ return ret;
+}
+
+static int cdns_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct cdns_rtc *crtc = dev_get_drvdata(dev);
+
+ if (enabled) {
+ writel((CDNS_RTC_AEI_SEC | CDNS_RTC_AEI_MIN | CDNS_RTC_AEI_HOUR
+ | CDNS_RTC_AEI_DATE | CDNS_RTC_AEI_MNTH),
+ crtc->regs + CDNS_RTC_AENR);
+ writel(CDNS_RTC_AEI_ALRM, crtc->regs + CDNS_RTC_IENR);
+ } else {
+ writel(0, crtc->regs + CDNS_RTC_AENR);
+ writel(CDNS_RTC_AEI_ALRM, crtc->regs + CDNS_RTC_IDISR);
+ }
+
+ return 0;
+}
+
+static int cdns_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct cdns_rtc *crtc = dev_get_drvdata(dev);
+ u32 reg;
+
+ reg = readl(crtc->regs + CDNS_RTC_TIMAR);
+ cdns_rtc_reg2time(reg, &alarm->time);
+
+ reg = readl(crtc->regs + CDNS_RTC_CALAR);
+ alarm->time.tm_mday = bcd2bin(FIELD_GET(CDNS_RTC_CAL_D, reg));
+ alarm->time.tm_mon = bcd2bin(FIELD_GET(CDNS_RTC_CAL_M, reg)) - 1;
+
+ return 0;
+}
+
+static int cdns_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct cdns_rtc *crtc = dev_get_drvdata(dev);
+ int ret = -EIO;
+ int tries;
+ u32 timar, calar, stsr;
+
+ cdns_rtc_alarm_irq_enable(dev, 0);
+
+ timar = cdns_rtc_time2reg(&alarm->time);
+ calar = FIELD_PREP(CDNS_RTC_CAL_D, bin2bcd(alarm->time.tm_mday))
+ | FIELD_PREP(CDNS_RTC_CAL_M, bin2bcd(alarm->time.tm_mon + 1));
+
+ /* Update registers, check valid alarm flags */
+ for (tries = 0; tries < CDNS_RTC_MAX_REGS_TRIES; tries++) {
+ writel(timar, crtc->regs + CDNS_RTC_TIMAR);
+ writel(calar, crtc->regs + CDNS_RTC_CALAR);
+ stsr = readl(crtc->regs + CDNS_RTC_STSR);
+
+ if ((stsr & CDNS_RTC_STSR_VTA_VCA) == CDNS_RTC_STSR_VTA_VCA) {
+ ret = 0;
+ break;
+ }
+ }
+
+ if (!ret)
+ cdns_rtc_alarm_irq_enable(dev, alarm->enabled);
+ return ret;
+}
+
+static const struct rtc_class_ops cdns_rtc_ops = {
+ .read_time = cdns_rtc_read_time,
+ .set_time = cdns_rtc_set_time,
+ .read_alarm = cdns_rtc_read_alarm,
+ .set_alarm = cdns_rtc_set_alarm,
+ .alarm_irq_enable = cdns_rtc_alarm_irq_enable,
+};
+
+static int cdns_rtc_probe(struct platform_device *pdev)
+{
+ struct cdns_rtc *crtc;
+ struct resource *res;
+ int ret;
+ unsigned long ref_clk_freq;
+
+ crtc = devm_kzalloc(&pdev->dev, sizeof(*crtc), GFP_KERNEL);
+ if (!crtc)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ crtc->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(crtc->regs))
+ return PTR_ERR(crtc->regs);
+
+ crtc->irq = platform_get_irq(pdev, 0);
+ if (crtc->irq < 0)
+ return -EINVAL;
+
+ crtc->pclk = devm_clk_get(&pdev->dev, "pclk");
+ if (IS_ERR(crtc->pclk)) {
+ ret = PTR_ERR(crtc->pclk);
+ dev_err(&pdev->dev,
+ "Failed to retrieve the peripheral clock, %d\n", ret);
+ return ret;
+ }
+
+ crtc->ref_clk = devm_clk_get(&pdev->dev, "ref_clk");
+ if (IS_ERR(crtc->ref_clk)) {
+ ret = PTR_ERR(crtc->ref_clk);
+ dev_err(&pdev->dev,
+ "Failed to retrieve the reference clock, %d\n", ret);
+ return ret;
+ }
+
+ crtc->rtc_dev = devm_rtc_allocate_device(&pdev->dev);
+ if (IS_ERR(crtc->rtc_dev)) {
+ ret = PTR_ERR(crtc->rtc_dev);
+ dev_err(&pdev->dev,
+ "Failed to allocate the RTC device, %d\n", ret);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, crtc);
+
+ ret = clk_prepare_enable(crtc->pclk);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Failed to enable the peripheral clock, %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(crtc->ref_clk);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Failed to enable the reference clock, %d\n", ret);
+ goto err_disable_pclk;
+ }
+
+ ref_clk_freq = clk_get_rate(crtc->ref_clk);
+ if ((ref_clk_freq != 1) && (ref_clk_freq != 100)) {
+ dev_err(&pdev->dev,
+ "Invalid reference clock frequency %lu Hz.\n",
+ ref_clk_freq);
+ ret = -EINVAL;
+ goto err_disable_ref_clk;
+ }
+
+ ret = devm_request_irq(&pdev->dev, crtc->irq,
+ cdns_rtc_irq_handler, 0,
+ dev_name(&pdev->dev), &pdev->dev);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Failed to request interrupt for the device, %d\n",
+ ret);
+ goto err_disable_ref_clk;
+ }
+
+ /* The RTC supports 01.01.1900 - 31.12.2999 */
+ crtc->rtc_dev->range_min = mktime64(1900, 1, 1, 0, 0, 0);
+ crtc->rtc_dev->range_max = mktime64(2999, 12, 31, 23, 59, 59);
+
+ crtc->rtc_dev->ops = &cdns_rtc_ops;
+ device_init_wakeup(&pdev->dev, true);
+
+ /* Always use 24-hour mode and keep the RTC values */
+ writel(0, crtc->regs + CDNS_RTC_HMR);
+ writel(CDNS_RTC_KRTCR_KRTC, crtc->regs + CDNS_RTC_KRTCR);
+
+ ret = rtc_register_device(crtc->rtc_dev);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Failed to register the RTC device, %d\n", ret);
+ goto err_disable_wakeup;
+ }
+
+ return 0;
+
+err_disable_wakeup:
+ device_init_wakeup(&pdev->dev, false);
+
+err_disable_ref_clk:
+ clk_disable_unprepare(crtc->ref_clk);
+
+err_disable_pclk:
+ clk_disable_unprepare(crtc->pclk);
+
+ return ret;
+}
+
+static int cdns_rtc_remove(struct platform_device *pdev)
+{
+ struct cdns_rtc *crtc = platform_get_drvdata(pdev);
+
+ cdns_rtc_alarm_irq_enable(&pdev->dev, 0);
+ device_init_wakeup(&pdev->dev, 0);
+
+ clk_disable_unprepare(crtc->pclk);
+ clk_disable_unprepare(crtc->ref_clk);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int cdns_rtc_suspend(struct device *dev)
+{
+ struct cdns_rtc *crtc = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev))
+ enable_irq_wake(crtc->irq);
+
+ return 0;
+}
+
+static int cdns_rtc_resume(struct device *dev)
+{
+ struct cdns_rtc *crtc = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev))
+ disable_irq_wake(crtc->irq);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(cdns_rtc_pm_ops, cdns_rtc_suspend, cdns_rtc_resume);
+
+static const struct of_device_id cdns_rtc_of_match[] = {
+ { .compatible = "cdns,rtc-r109v3" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, cdns_rtc_of_match);
+
+static struct platform_driver cdns_rtc_driver = {
+ .driver = {
+ .name = "cdns-rtc",
+ .of_match_table = cdns_rtc_of_match,
+ .pm = &cdns_rtc_pm_ops,
+ },
+ .probe = cdns_rtc_probe,
+ .remove = cdns_rtc_remove,
+};
+module_platform_driver(cdns_rtc_driver);
+
+MODULE_AUTHOR("Jan Kotas <jank@cadence.com>");
+MODULE_DESCRIPTION("Cadence RTC driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:cdns-rtc");
diff --git a/drivers/rtc/rtc-coh901331.c b/drivers/rtc/rtc-coh901331.c
index fc5cf5c44ae7..0b232c84f674 100644
--- a/drivers/rtc/rtc-coh901331.c
+++ b/drivers/rtc/rtc-coh901331.c
@@ -235,9 +235,13 @@ static int coh901331_suspend(struct device *dev)
static int coh901331_resume(struct device *dev)
{
+ int ret;
struct coh901331_port *rtap = dev_get_drvdata(dev);
- clk_prepare(rtap->clk);
+ ret = clk_prepare(rtap->clk);
+ if (ret)
+ return ret;
+
if (device_may_wakeup(dev)) {
disable_irq_wake(rtap->irq);
} else {
diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c
index 74b31dce484f..07530fe1da2a 100644
--- a/drivers/rtc/rtc-ds1307.c
+++ b/drivers/rtc/rtc-ds1307.c
@@ -114,6 +114,33 @@ enum ds_type {
# define RX8025_BIT_VDET 0x40
# define RX8025_BIT_XST 0x20
+#define RX8130_REG_ALARM_MIN 0x17
+#define RX8130_REG_ALARM_HOUR 0x18
+#define RX8130_REG_ALARM_WEEK_OR_DAY 0x19
+#define RX8130_REG_EXTENSION 0x1c
+#define RX8130_REG_EXTENSION_WADA BIT(3)
+#define RX8130_REG_FLAG 0x1d
+#define RX8130_REG_FLAG_VLF BIT(1)
+#define RX8130_REG_FLAG_AF BIT(3)
+#define RX8130_REG_CONTROL0 0x1e
+#define RX8130_REG_CONTROL0_AIE BIT(3)
+
+#define MCP794XX_REG_CONTROL 0x07
+# define MCP794XX_BIT_ALM0_EN 0x10
+# define MCP794XX_BIT_ALM1_EN 0x20
+#define MCP794XX_REG_ALARM0_BASE 0x0a
+#define MCP794XX_REG_ALARM0_CTRL 0x0d
+#define MCP794XX_REG_ALARM1_BASE 0x11
+#define MCP794XX_REG_ALARM1_CTRL 0x14
+# define MCP794XX_BIT_ALMX_IF BIT(3)
+# define MCP794XX_BIT_ALMX_C0 BIT(4)
+# define MCP794XX_BIT_ALMX_C1 BIT(5)
+# define MCP794XX_BIT_ALMX_C2 BIT(6)
+# define MCP794XX_BIT_ALMX_POL BIT(7)
+# define MCP794XX_MSK_ALMX_MATCH (MCP794XX_BIT_ALMX_C0 | \
+ MCP794XX_BIT_ALMX_C1 | \
+ MCP794XX_BIT_ALMX_C2)
+
#define M41TXX_REG_CONTROL 0x07
# define M41TXX_BIT_OUT BIT(7)
# define M41TXX_BIT_FT BIT(6)
@@ -158,289 +185,7 @@ struct chip_desc {
bool);
};
-static int ds1307_get_time(struct device *dev, struct rtc_time *t);
-static int ds1307_set_time(struct device *dev, struct rtc_time *t);
-static int ds1337_read_alarm(struct device *dev, struct rtc_wkalrm *t);
-static int ds1337_set_alarm(struct device *dev, struct rtc_wkalrm *t);
-static int ds1307_alarm_irq_enable(struct device *dev, unsigned int enabled);
-static u8 do_trickle_setup_ds1339(struct ds1307 *, u32 ohms, bool diode);
-static irqreturn_t rx8130_irq(int irq, void *dev_id);
-static int rx8130_read_alarm(struct device *dev, struct rtc_wkalrm *t);
-static int rx8130_set_alarm(struct device *dev, struct rtc_wkalrm *t);
-static int rx8130_alarm_irq_enable(struct device *dev, unsigned int enabled);
-static irqreturn_t mcp794xx_irq(int irq, void *dev_id);
-static int mcp794xx_read_alarm(struct device *dev, struct rtc_wkalrm *t);
-static int mcp794xx_set_alarm(struct device *dev, struct rtc_wkalrm *t);
-static int mcp794xx_alarm_irq_enable(struct device *dev, unsigned int enabled);
-static int m41txx_rtc_read_offset(struct device *dev, long *offset);
-static int m41txx_rtc_set_offset(struct device *dev, long offset);
-
-static const struct rtc_class_ops rx8130_rtc_ops = {
- .read_time = ds1307_get_time,
- .set_time = ds1307_set_time,
- .read_alarm = rx8130_read_alarm,
- .set_alarm = rx8130_set_alarm,
- .alarm_irq_enable = rx8130_alarm_irq_enable,
-};
-
-static const struct rtc_class_ops mcp794xx_rtc_ops = {
- .read_time = ds1307_get_time,
- .set_time = ds1307_set_time,
- .read_alarm = mcp794xx_read_alarm,
- .set_alarm = mcp794xx_set_alarm,
- .alarm_irq_enable = mcp794xx_alarm_irq_enable,
-};
-
-static const struct rtc_class_ops m41txx_rtc_ops = {
- .read_time = ds1307_get_time,
- .set_time = ds1307_set_time,
- .read_alarm = ds1337_read_alarm,
- .set_alarm = ds1337_set_alarm,
- .alarm_irq_enable = ds1307_alarm_irq_enable,
- .read_offset = m41txx_rtc_read_offset,
- .set_offset = m41txx_rtc_set_offset,
-};
-
-static const struct chip_desc chips[last_ds_type] = {
- [ds_1307] = {
- .nvram_offset = 8,
- .nvram_size = 56,
- },
- [ds_1308] = {
- .nvram_offset = 8,
- .nvram_size = 56,
- },
- [ds_1337] = {
- .alarm = 1,
- .century_reg = DS1307_REG_MONTH,
- .century_bit = DS1337_BIT_CENTURY,
- },
- [ds_1338] = {
- .nvram_offset = 8,
- .nvram_size = 56,
- },
- [ds_1339] = {
- .alarm = 1,
- .century_reg = DS1307_REG_MONTH,
- .century_bit = DS1337_BIT_CENTURY,
- .bbsqi_bit = DS1339_BIT_BBSQI,
- .trickle_charger_reg = 0x10,
- .do_trickle_setup = &do_trickle_setup_ds1339,
- },
- [ds_1340] = {
- .century_reg = DS1307_REG_HOUR,
- .century_enable_bit = DS1340_BIT_CENTURY_EN,
- .century_bit = DS1340_BIT_CENTURY,
- .do_trickle_setup = &do_trickle_setup_ds1339,
- .trickle_charger_reg = 0x08,
- },
- [ds_1341] = {
- .century_reg = DS1307_REG_MONTH,
- .century_bit = DS1337_BIT_CENTURY,
- },
- [ds_1388] = {
- .offset = 1,
- .trickle_charger_reg = 0x0a,
- },
- [ds_3231] = {
- .alarm = 1,
- .century_reg = DS1307_REG_MONTH,
- .century_bit = DS1337_BIT_CENTURY,
- .bbsqi_bit = DS3231_BIT_BBSQW,
- },
- [rx_8130] = {
- .alarm = 1,
- /* this is battery backed SRAM */
- .nvram_offset = 0x20,
- .nvram_size = 4, /* 32bit (4 word x 8 bit) */
- .offset = 0x10,
- .irq_handler = rx8130_irq,
- .rtc_ops = &rx8130_rtc_ops,
- },
- [m41t0] = {
- .rtc_ops = &m41txx_rtc_ops,
- },
- [m41t00] = {
- .rtc_ops = &m41txx_rtc_ops,
- },
- [m41t11] = {
- /* this is battery backed SRAM */
- .nvram_offset = 8,
- .nvram_size = 56,
- .rtc_ops = &m41txx_rtc_ops,
- },
- [mcp794xx] = {
- .alarm = 1,
- /* this is battery backed SRAM */
- .nvram_offset = 0x20,
- .nvram_size = 0x40,
- .irq_handler = mcp794xx_irq,
- .rtc_ops = &mcp794xx_rtc_ops,
- },
-};
-
-static const struct i2c_device_id ds1307_id[] = {
- { "ds1307", ds_1307 },
- { "ds1308", ds_1308 },
- { "ds1337", ds_1337 },
- { "ds1338", ds_1338 },
- { "ds1339", ds_1339 },
- { "ds1388", ds_1388 },
- { "ds1340", ds_1340 },
- { "ds1341", ds_1341 },
- { "ds3231", ds_3231 },
- { "m41t0", m41t0 },
- { "m41t00", m41t00 },
- { "m41t11", m41t11 },
- { "mcp7940x", mcp794xx },
- { "mcp7941x", mcp794xx },
- { "pt7c4338", ds_1307 },
- { "rx8025", rx_8025 },
- { "isl12057", ds_1337 },
- { "rx8130", rx_8130 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, ds1307_id);
-
-#ifdef CONFIG_OF
-static const struct of_device_id ds1307_of_match[] = {
- {
- .compatible = "dallas,ds1307",
- .data = (void *)ds_1307
- },
- {
- .compatible = "dallas,ds1308",
- .data = (void *)ds_1308
- },
- {
- .compatible = "dallas,ds1337",
- .data = (void *)ds_1337
- },
- {
- .compatible = "dallas,ds1338",
- .data = (void *)ds_1338
- },
- {
- .compatible = "dallas,ds1339",
- .data = (void *)ds_1339
- },
- {
- .compatible = "dallas,ds1388",
- .data = (void *)ds_1388
- },
- {
- .compatible = "dallas,ds1340",
- .data = (void *)ds_1340
- },
- {
- .compatible = "dallas,ds1341",
- .data = (void *)ds_1341
- },
- {
- .compatible = "maxim,ds3231",
- .data = (void *)ds_3231
- },
- {
- .compatible = "st,m41t0",
- .data = (void *)m41t0
- },
- {
- .compatible = "st,m41t00",
- .data = (void *)m41t00
- },
- {
- .compatible = "st,m41t11",
- .data = (void *)m41t11
- },
- {
- .compatible = "microchip,mcp7940x",
- .data = (void *)mcp794xx
- },
- {
- .compatible = "microchip,mcp7941x",
- .data = (void *)mcp794xx
- },
- {
- .compatible = "pericom,pt7c4338",
- .data = (void *)ds_1307
- },
- {
- .compatible = "epson,rx8025",
- .data = (void *)rx_8025
- },
- {
- .compatible = "isil,isl12057",
- .data = (void *)ds_1337
- },
- {
- .compatible = "epson,rx8130",
- .data = (void *)rx_8130
- },
- { }
-};
-MODULE_DEVICE_TABLE(of, ds1307_of_match);
-#endif
-
-#ifdef CONFIG_ACPI
-static const struct acpi_device_id ds1307_acpi_ids[] = {
- { .id = "DS1307", .driver_data = ds_1307 },
- { .id = "DS1308", .driver_data = ds_1308 },
- { .id = "DS1337", .driver_data = ds_1337 },
- { .id = "DS1338", .driver_data = ds_1338 },
- { .id = "DS1339", .driver_data = ds_1339 },
- { .id = "DS1388", .driver_data = ds_1388 },
- { .id = "DS1340", .driver_data = ds_1340 },
- { .id = "DS1341", .driver_data = ds_1341 },
- { .id = "DS3231", .driver_data = ds_3231 },
- { .id = "M41T0", .driver_data = m41t0 },
- { .id = "M41T00", .driver_data = m41t00 },
- { .id = "M41T11", .driver_data = m41t11 },
- { .id = "MCP7940X", .driver_data = mcp794xx },
- { .id = "MCP7941X", .driver_data = mcp794xx },
- { .id = "PT7C4338", .driver_data = ds_1307 },
- { .id = "RX8025", .driver_data = rx_8025 },
- { .id = "ISL12057", .driver_data = ds_1337 },
- { .id = "RX8130", .driver_data = rx_8130 },
- { }
-};
-MODULE_DEVICE_TABLE(acpi, ds1307_acpi_ids);
-#endif
-
-/*
- * The ds1337 and ds1339 both have two alarms, but we only use the first
- * one (with a "seconds" field). For ds1337 we expect nINTA is our alarm
- * signal; ds1339 chips have only one alarm signal.
- */
-static irqreturn_t ds1307_irq(int irq, void *dev_id)
-{
- struct ds1307 *ds1307 = dev_id;
- struct mutex *lock = &ds1307->rtc->ops_lock;
- int stat, ret;
-
- mutex_lock(lock);
- ret = regmap_read(ds1307->regmap, DS1337_REG_STATUS, &stat);
- if (ret)
- goto out;
-
- if (stat & DS1337_BIT_A1I) {
- stat &= ~DS1337_BIT_A1I;
- regmap_write(ds1307->regmap, DS1337_REG_STATUS, stat);
-
- ret = regmap_update_bits(ds1307->regmap, DS1337_REG_CONTROL,
- DS1337_BIT_A1IE, 0);
- if (ret)
- goto out;
-
- rtc_update_irq(ds1307->rtc, 1, RTC_AF | RTC_IRQF);
- }
-
-out:
- mutex_unlock(lock);
-
- return IRQ_HANDLED;
-}
-
-/*----------------------------------------------------------------------*/
+static const struct chip_desc chips[last_ds_type];
static int ds1307_get_time(struct device *dev, struct rtc_time *t)
{
@@ -449,6 +194,20 @@ static int ds1307_get_time(struct device *dev, struct rtc_time *t)
const struct chip_desc *chip = &chips[ds1307->type];
u8 regs[7];
+ if (ds1307->type == rx_8130) {
+ unsigned int regflag;
+ ret = regmap_read(ds1307->regmap, RX8130_REG_FLAG, &regflag);
+ if (ret) {
+ dev_err(dev, "%s error %d\n", "read", ret);
+ return ret;
+ }
+
+ if (regflag & RX8130_REG_FLAG_VLF) {
+ dev_warn_once(dev, "oscillator failed, set time!\n");
+ return -EINVAL;
+ }
+ }
+
/* read the RTC date and time registers all at once */
ret = regmap_bulk_read(ds1307->regmap, chip->offset, regs,
sizeof(regs));
@@ -548,6 +307,17 @@ static int ds1307_set_time(struct device *dev, struct rtc_time *t)
dev_err(dev, "%s error %d\n", "write", result);
return result;
}
+
+ if (ds1307->type == rx_8130) {
+ /* clear Voltage Loss Flag as data is available now */
+ result = regmap_write(ds1307->regmap, RX8130_REG_FLAG,
+ ~(u8)RX8130_REG_FLAG_VLF);
+ if (result) {
+ dev_err(dev, "%s error %d\n", "write", result);
+ return result;
+ }
+ }
+
return 0;
}
@@ -666,29 +436,28 @@ static int ds1307_alarm_irq_enable(struct device *dev, unsigned int enabled)
enabled ? DS1337_BIT_A1IE : 0);
}
-static const struct rtc_class_ops ds13xx_rtc_ops = {
- .read_time = ds1307_get_time,
- .set_time = ds1307_set_time,
- .read_alarm = ds1337_read_alarm,
- .set_alarm = ds1337_set_alarm,
- .alarm_irq_enable = ds1307_alarm_irq_enable,
-};
-
-/*----------------------------------------------------------------------*/
-
-/*
- * Alarm support for rx8130 devices.
- */
+static u8 do_trickle_setup_ds1339(struct ds1307 *ds1307, u32 ohms, bool diode)
+{
+ u8 setup = (diode) ? DS1307_TRICKLE_CHARGER_DIODE :
+ DS1307_TRICKLE_CHARGER_NO_DIODE;
-#define RX8130_REG_ALARM_MIN 0x07
-#define RX8130_REG_ALARM_HOUR 0x08
-#define RX8130_REG_ALARM_WEEK_OR_DAY 0x09
-#define RX8130_REG_EXTENSION 0x0c
-#define RX8130_REG_EXTENSION_WADA BIT(3)
-#define RX8130_REG_FLAG 0x0d
-#define RX8130_REG_FLAG_AF BIT(3)
-#define RX8130_REG_CONTROL0 0x0e
-#define RX8130_REG_CONTROL0_AIE BIT(3)
+ switch (ohms) {
+ case 250:
+ setup |= DS1307_TRICKLE_CHARGER_250_OHM;
+ break;
+ case 2000:
+ setup |= DS1307_TRICKLE_CHARGER_2K_OHM;
+ break;
+ case 4000:
+ setup |= DS1307_TRICKLE_CHARGER_4K_OHM;
+ break;
+ default:
+ dev_warn(ds1307->dev,
+ "Unsupported ohm value %u in dt\n", ohms);
+ return 0;
+ }
+ return setup;
+}
static irqreturn_t rx8130_irq(int irq, void *dev_id)
{
@@ -785,8 +554,8 @@ static int rx8130_set_alarm(struct device *dev, struct rtc_wkalrm *t)
if (ret < 0)
return ret;
- ctl[0] &= ~RX8130_REG_EXTENSION_WADA;
- ctl[1] |= RX8130_REG_FLAG_AF;
+ ctl[0] &= RX8130_REG_EXTENSION_WADA;
+ ctl[1] &= ~RX8130_REG_FLAG_AF;
ctl[2] &= ~RX8130_REG_CONTROL0_AIE;
ret = regmap_bulk_write(ds1307->regmap, RX8130_REG_EXTENSION, ctl,
@@ -809,8 +578,7 @@ static int rx8130_set_alarm(struct device *dev, struct rtc_wkalrm *t)
ctl[2] |= RX8130_REG_CONTROL0_AIE;
- return regmap_bulk_write(ds1307->regmap, RX8130_REG_EXTENSION, ctl,
- sizeof(ctl));
+ return regmap_write(ds1307->regmap, RX8130_REG_CONTROL0, ctl[2]);
}
static int rx8130_alarm_irq_enable(struct device *dev, unsigned int enabled)
@@ -833,28 +601,6 @@ static int rx8130_alarm_irq_enable(struct device *dev, unsigned int enabled)
return regmap_write(ds1307->regmap, RX8130_REG_CONTROL0, reg);
}
-/*----------------------------------------------------------------------*/
-
-/*
- * Alarm support for mcp794xx devices.
- */
-
-#define MCP794XX_REG_CONTROL 0x07
-# define MCP794XX_BIT_ALM0_EN 0x10
-# define MCP794XX_BIT_ALM1_EN 0x20
-#define MCP794XX_REG_ALARM0_BASE 0x0a
-#define MCP794XX_REG_ALARM0_CTRL 0x0d
-#define MCP794XX_REG_ALARM1_BASE 0x11
-#define MCP794XX_REG_ALARM1_CTRL 0x14
-# define MCP794XX_BIT_ALMX_IF BIT(3)
-# define MCP794XX_BIT_ALMX_C0 BIT(4)
-# define MCP794XX_BIT_ALMX_C1 BIT(5)
-# define MCP794XX_BIT_ALMX_C2 BIT(6)
-# define MCP794XX_BIT_ALMX_POL BIT(7)
-# define MCP794XX_MSK_ALMX_MATCH (MCP794XX_BIT_ALMX_C0 | \
- MCP794XX_BIT_ALMX_C1 | \
- MCP794XX_BIT_ALMX_C2)
-
static irqreturn_t mcp794xx_irq(int irq, void *dev_id)
{
struct ds1307 *ds1307 = dev_id;
@@ -1050,6 +796,281 @@ static int m41txx_rtc_set_offset(struct device *dev, long offset)
ctrl_reg);
}
+static const struct rtc_class_ops rx8130_rtc_ops = {
+ .read_time = ds1307_get_time,
+ .set_time = ds1307_set_time,
+ .read_alarm = rx8130_read_alarm,
+ .set_alarm = rx8130_set_alarm,
+ .alarm_irq_enable = rx8130_alarm_irq_enable,
+};
+
+static const struct rtc_class_ops mcp794xx_rtc_ops = {
+ .read_time = ds1307_get_time,
+ .set_time = ds1307_set_time,
+ .read_alarm = mcp794xx_read_alarm,
+ .set_alarm = mcp794xx_set_alarm,
+ .alarm_irq_enable = mcp794xx_alarm_irq_enable,
+};
+
+static const struct rtc_class_ops m41txx_rtc_ops = {
+ .read_time = ds1307_get_time,
+ .set_time = ds1307_set_time,
+ .read_alarm = ds1337_read_alarm,
+ .set_alarm = ds1337_set_alarm,
+ .alarm_irq_enable = ds1307_alarm_irq_enable,
+ .read_offset = m41txx_rtc_read_offset,
+ .set_offset = m41txx_rtc_set_offset,
+};
+
+static const struct chip_desc chips[last_ds_type] = {
+ [ds_1307] = {
+ .nvram_offset = 8,
+ .nvram_size = 56,
+ },
+ [ds_1308] = {
+ .nvram_offset = 8,
+ .nvram_size = 56,
+ },
+ [ds_1337] = {
+ .alarm = 1,
+ .century_reg = DS1307_REG_MONTH,
+ .century_bit = DS1337_BIT_CENTURY,
+ },
+ [ds_1338] = {
+ .nvram_offset = 8,
+ .nvram_size = 56,
+ },
+ [ds_1339] = {
+ .alarm = 1,
+ .century_reg = DS1307_REG_MONTH,
+ .century_bit = DS1337_BIT_CENTURY,
+ .bbsqi_bit = DS1339_BIT_BBSQI,
+ .trickle_charger_reg = 0x10,
+ .do_trickle_setup = &do_trickle_setup_ds1339,
+ },
+ [ds_1340] = {
+ .century_reg = DS1307_REG_HOUR,
+ .century_enable_bit = DS1340_BIT_CENTURY_EN,
+ .century_bit = DS1340_BIT_CENTURY,
+ .do_trickle_setup = &do_trickle_setup_ds1339,
+ .trickle_charger_reg = 0x08,
+ },
+ [ds_1341] = {
+ .century_reg = DS1307_REG_MONTH,
+ .century_bit = DS1337_BIT_CENTURY,
+ },
+ [ds_1388] = {
+ .offset = 1,
+ .trickle_charger_reg = 0x0a,
+ },
+ [ds_3231] = {
+ .alarm = 1,
+ .century_reg = DS1307_REG_MONTH,
+ .century_bit = DS1337_BIT_CENTURY,
+ .bbsqi_bit = DS3231_BIT_BBSQW,
+ },
+ [rx_8130] = {
+ .alarm = 1,
+ /* this is battery backed SRAM */
+ .nvram_offset = 0x20,
+ .nvram_size = 4, /* 32bit (4 word x 8 bit) */
+ .offset = 0x10,
+ .irq_handler = rx8130_irq,
+ .rtc_ops = &rx8130_rtc_ops,
+ },
+ [m41t0] = {
+ .rtc_ops = &m41txx_rtc_ops,
+ },
+ [m41t00] = {
+ .rtc_ops = &m41txx_rtc_ops,
+ },
+ [m41t11] = {
+ /* this is battery backed SRAM */
+ .nvram_offset = 8,
+ .nvram_size = 56,
+ .rtc_ops = &m41txx_rtc_ops,
+ },
+ [mcp794xx] = {
+ .alarm = 1,
+ /* this is battery backed SRAM */
+ .nvram_offset = 0x20,
+ .nvram_size = 0x40,
+ .irq_handler = mcp794xx_irq,
+ .rtc_ops = &mcp794xx_rtc_ops,
+ },
+};
+
+static const struct i2c_device_id ds1307_id[] = {
+ { "ds1307", ds_1307 },
+ { "ds1308", ds_1308 },
+ { "ds1337", ds_1337 },
+ { "ds1338", ds_1338 },
+ { "ds1339", ds_1339 },
+ { "ds1388", ds_1388 },
+ { "ds1340", ds_1340 },
+ { "ds1341", ds_1341 },
+ { "ds3231", ds_3231 },
+ { "m41t0", m41t0 },
+ { "m41t00", m41t00 },
+ { "m41t11", m41t11 },
+ { "mcp7940x", mcp794xx },
+ { "mcp7941x", mcp794xx },
+ { "pt7c4338", ds_1307 },
+ { "rx8025", rx_8025 },
+ { "isl12057", ds_1337 },
+ { "rx8130", rx_8130 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ds1307_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id ds1307_of_match[] = {
+ {
+ .compatible = "dallas,ds1307",
+ .data = (void *)ds_1307
+ },
+ {
+ .compatible = "dallas,ds1308",
+ .data = (void *)ds_1308
+ },
+ {
+ .compatible = "dallas,ds1337",
+ .data = (void *)ds_1337
+ },
+ {
+ .compatible = "dallas,ds1338",
+ .data = (void *)ds_1338
+ },
+ {
+ .compatible = "dallas,ds1339",
+ .data = (void *)ds_1339
+ },
+ {
+ .compatible = "dallas,ds1388",
+ .data = (void *)ds_1388
+ },
+ {
+ .compatible = "dallas,ds1340",
+ .data = (void *)ds_1340
+ },
+ {
+ .compatible = "dallas,ds1341",
+ .data = (void *)ds_1341
+ },
+ {
+ .compatible = "maxim,ds3231",
+ .data = (void *)ds_3231
+ },
+ {
+ .compatible = "st,m41t0",
+ .data = (void *)m41t0
+ },
+ {
+ .compatible = "st,m41t00",
+ .data = (void *)m41t00
+ },
+ {
+ .compatible = "st,m41t11",
+ .data = (void *)m41t11
+ },
+ {
+ .compatible = "microchip,mcp7940x",
+ .data = (void *)mcp794xx
+ },
+ {
+ .compatible = "microchip,mcp7941x",
+ .data = (void *)mcp794xx
+ },
+ {
+ .compatible = "pericom,pt7c4338",
+ .data = (void *)ds_1307
+ },
+ {
+ .compatible = "epson,rx8025",
+ .data = (void *)rx_8025
+ },
+ {
+ .compatible = "isil,isl12057",
+ .data = (void *)ds_1337
+ },
+ {
+ .compatible = "epson,rx8130",
+ .data = (void *)rx_8130
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ds1307_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id ds1307_acpi_ids[] = {
+ { .id = "DS1307", .driver_data = ds_1307 },
+ { .id = "DS1308", .driver_data = ds_1308 },
+ { .id = "DS1337", .driver_data = ds_1337 },
+ { .id = "DS1338", .driver_data = ds_1338 },
+ { .id = "DS1339", .driver_data = ds_1339 },
+ { .id = "DS1388", .driver_data = ds_1388 },
+ { .id = "DS1340", .driver_data = ds_1340 },
+ { .id = "DS1341", .driver_data = ds_1341 },
+ { .id = "DS3231", .driver_data = ds_3231 },
+ { .id = "M41T0", .driver_data = m41t0 },
+ { .id = "M41T00", .driver_data = m41t00 },
+ { .id = "M41T11", .driver_data = m41t11 },
+ { .id = "MCP7940X", .driver_data = mcp794xx },
+ { .id = "MCP7941X", .driver_data = mcp794xx },
+ { .id = "PT7C4338", .driver_data = ds_1307 },
+ { .id = "RX8025", .driver_data = rx_8025 },
+ { .id = "ISL12057", .driver_data = ds_1337 },
+ { .id = "RX8130", .driver_data = rx_8130 },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, ds1307_acpi_ids);
+#endif
+
+/*
+ * The ds1337 and ds1339 both have two alarms, but we only use the first
+ * one (with a "seconds" field). For ds1337 we expect nINTA is our alarm
+ * signal; ds1339 chips have only one alarm signal.
+ */
+static irqreturn_t ds1307_irq(int irq, void *dev_id)
+{
+ struct ds1307 *ds1307 = dev_id;
+ struct mutex *lock = &ds1307->rtc->ops_lock;
+ int stat, ret;
+
+ mutex_lock(lock);
+ ret = regmap_read(ds1307->regmap, DS1337_REG_STATUS, &stat);
+ if (ret)
+ goto out;
+
+ if (stat & DS1337_BIT_A1I) {
+ stat &= ~DS1337_BIT_A1I;
+ regmap_write(ds1307->regmap, DS1337_REG_STATUS, stat);
+
+ ret = regmap_update_bits(ds1307->regmap, DS1337_REG_CONTROL,
+ DS1337_BIT_A1IE, 0);
+ if (ret)
+ goto out;
+
+ rtc_update_irq(ds1307->rtc, 1, RTC_AF | RTC_IRQF);
+ }
+
+out:
+ mutex_unlock(lock);
+
+ return IRQ_HANDLED;
+}
+
+/*----------------------------------------------------------------------*/
+
+static const struct rtc_class_ops ds13xx_rtc_ops = {
+ .read_time = ds1307_get_time,
+ .set_time = ds1307_set_time,
+ .read_alarm = ds1337_read_alarm,
+ .set_alarm = ds1337_set_alarm,
+ .alarm_irq_enable = ds1307_alarm_irq_enable,
+};
+
static ssize_t frequency_test_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
@@ -1137,30 +1158,6 @@ static int ds1307_nvram_write(void *priv, unsigned int offset, void *val,
/*----------------------------------------------------------------------*/
-static u8 do_trickle_setup_ds1339(struct ds1307 *ds1307,
- u32 ohms, bool diode)
-{
- u8 setup = (diode) ? DS1307_TRICKLE_CHARGER_DIODE :
- DS1307_TRICKLE_CHARGER_NO_DIODE;
-
- switch (ohms) {
- case 250:
- setup |= DS1307_TRICKLE_CHARGER_250_OHM;
- break;
- case 2000:
- setup |= DS1307_TRICKLE_CHARGER_2K_OHM;
- break;
- case 4000:
- setup |= DS1307_TRICKLE_CHARGER_4K_OHM;
- break;
- default:
- dev_warn(ds1307->dev,
- "Unsupported ohm value %u in dt\n", ohms);
- return 0;
- }
- return setup;
-}
-
static u8 ds1307_trickle_init(struct ds1307 *ds1307,
const struct chip_desc *chip)
{
diff --git a/drivers/rtc/rtc-ds1672.c b/drivers/rtc/rtc-ds1672.c
index 9caaccccaa57..b1ebca099b0d 100644
--- a/drivers/rtc/rtc-ds1672.c
+++ b/drivers/rtc/rtc-ds1672.c
@@ -58,7 +58,8 @@ static int ds1672_get_datetime(struct i2c_client *client, struct rtc_time *tm)
"%s: raw read data - counters=%02x,%02x,%02x,%02x\n",
__func__, buf[0], buf[1], buf[2], buf[3]);
- time = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+ time = ((unsigned long)buf[3] << 24) | (buf[2] << 16) |
+ (buf[1] << 8) | buf[0];
rtc_time_to_tm(time, tm);
diff --git a/drivers/rtc/rtc-hym8563.c b/drivers/rtc/rtc-hym8563.c
index e5ad527cb75e..d03f5d212eea 100644
--- a/drivers/rtc/rtc-hym8563.c
+++ b/drivers/rtc/rtc-hym8563.c
@@ -109,6 +109,8 @@ static int hym8563_rtc_read_time(struct device *dev, struct rtc_time *tm)
}
ret = i2c_smbus_read_i2c_block_data(client, HYM8563_SEC, 7, buf);
+ if (ret < 0)
+ return ret;
tm->tm_sec = bcd2bin(buf[0] & HYM8563_SEC_MASK);
tm->tm_min = bcd2bin(buf[1] & HYM8563_MIN_MASK);
diff --git a/drivers/rtc/rtc-imx-sc.c b/drivers/rtc/rtc-imx-sc.c
index 7ff08544532a..19642bfd913a 100644
--- a/drivers/rtc/rtc-imx-sc.c
+++ b/drivers/rtc/rtc-imx-sc.c
@@ -3,6 +3,7 @@
* Copyright 2018 NXP.
*/
+#include <linux/arm-smccc.h>
#include <linux/firmware/imx/sci.h>
#include <linux/module.h>
#include <linux/of.h>
@@ -12,6 +13,9 @@
#define IMX_SC_TIMER_FUNC_GET_RTC_SEC1970 9
#define IMX_SC_TIMER_FUNC_SET_RTC_TIME 6
+#define IMX_SIP_SRTC 0xC2000002
+#define IMX_SIP_SRTC_SET_TIME 0x0
+
static struct imx_sc_ipc *rtc_ipc_handle;
static struct rtc_device *imx_sc_rtc;
@@ -37,13 +41,28 @@ static int imx_sc_rtc_read_time(struct device *dev, struct rtc_time *tm)
return ret;
}
- rtc_time_to_tm(msg.time, tm);
+ rtc_time64_to_tm(msg.time, tm);
return 0;
}
+static int imx_sc_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct arm_smccc_res res;
+
+ /* pack 2 time parameters into 1 register, 16 bits for each */
+ arm_smccc_smc(IMX_SIP_SRTC, IMX_SIP_SRTC_SET_TIME,
+ ((tm->tm_year + 1900) << 16) | (tm->tm_mon + 1),
+ (tm->tm_mday << 16) | tm->tm_hour,
+ (tm->tm_min << 16) | tm->tm_sec,
+ 0, 0, 0, &res);
+
+ return res.a0;
+}
+
static const struct rtc_class_ops imx_sc_rtc_ops = {
.read_time = imx_sc_rtc_read_time,
+ .set_time = imx_sc_rtc_set_time,
};
static int imx_sc_rtc_probe(struct platform_device *pdev)
diff --git a/drivers/rtc/rtc-isl1208.c b/drivers/rtc/rtc-isl1208.c
index 37ab3e1d25f5..471e395b20db 100644
--- a/drivers/rtc/rtc-isl1208.c
+++ b/drivers/rtc/rtc-isl1208.c
@@ -13,6 +13,7 @@
#include <linux/bcd.h>
#include <linux/i2c.h>
#include <linux/module.h>
+#include <linux/of_device.h>
#include <linux/of_irq.h>
#include <linux/rtc.h>
@@ -73,10 +74,50 @@
static struct i2c_driver isl1208_driver;
/* ISL1208 various variants */
-enum {
+enum isl1208_id {
TYPE_ISL1208 = 0,
+ TYPE_ISL1209,
TYPE_ISL1218,
TYPE_ISL1219,
+ ISL_LAST_ID
+};
+
+/* Chip capabilities table */
+static const struct isl1208_config {
+ const char name[8];
+ unsigned int nvmem_length;
+ unsigned has_tamper:1;
+ unsigned has_timestamp:1;
+} isl1208_configs[] = {
+ [TYPE_ISL1208] = { "isl1208", 2, false, false },
+ [TYPE_ISL1209] = { "isl1209", 2, true, false },
+ [TYPE_ISL1218] = { "isl1218", 8, false, false },
+ [TYPE_ISL1219] = { "isl1219", 2, true, true },
+};
+
+static const struct i2c_device_id isl1208_id[] = {
+ { "isl1208", TYPE_ISL1208 },
+ { "isl1209", TYPE_ISL1209 },
+ { "isl1218", TYPE_ISL1218 },
+ { "isl1219", TYPE_ISL1219 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, isl1208_id);
+
+static const struct of_device_id isl1208_of_match[] = {
+ { .compatible = "isil,isl1208", .data = &isl1208_configs[TYPE_ISL1208] },
+ { .compatible = "isil,isl1209", .data = &isl1208_configs[TYPE_ISL1209] },
+ { .compatible = "isil,isl1218", .data = &isl1208_configs[TYPE_ISL1218] },
+ { .compatible = "isil,isl1219", .data = &isl1208_configs[TYPE_ISL1219] },
+ { }
+};
+MODULE_DEVICE_TABLE(of, isl1208_of_match);
+
+/* Device state */
+struct isl1208_state {
+ struct nvmem_config nvmem_config;
+ struct rtc_device *rtc;
+ const struct isl1208_config *config;
};
/* block read */
@@ -161,6 +202,7 @@ isl1208_i2c_get_atr(struct i2c_client *client)
return atr;
}
+/* returns adjustment value + 100 */
static int
isl1208_i2c_get_dtr(struct i2c_client *client)
{
@@ -171,7 +213,7 @@ isl1208_i2c_get_dtr(struct i2c_client *client)
/* dtr encodes adjustments of {-60,-40,-20,0,20,40,60} ppm */
dtr = ((dtr & 0x3) * 20) * (dtr & (1 << 2) ? -1 : 1);
- return dtr;
+ return dtr + 100;
}
static int
@@ -248,8 +290,8 @@ isl1208_rtc_proc(struct device *dev, struct seq_file *seq)
(sr & ISL1208_REG_SR_RTCF) ? "bad" : "okay");
dtr = isl1208_i2c_get_dtr(client);
- if (dtr >= 0 - 1)
- seq_printf(seq, "digital_trim\t: %d ppm\n", dtr);
+ if (dtr >= 0)
+ seq_printf(seq, "digital_trim\t: %d ppm\n", dtr - 100);
atr = isl1208_i2c_get_atr(client);
if (atr >= 0)
@@ -556,7 +598,7 @@ isl1208_rtc_interrupt(int irq, void *data)
{
unsigned long timeout = jiffies + msecs_to_jiffies(1000);
struct i2c_client *client = data;
- struct rtc_device *rtc = i2c_get_clientdata(client);
+ struct isl1208_state *isl1208 = i2c_get_clientdata(client);
int handled = 0, sr, err;
/*
@@ -579,7 +621,7 @@ isl1208_rtc_interrupt(int irq, void *data)
if (sr & ISL1208_REG_SR_ALM) {
dev_dbg(&client->dev, "alarm!\n");
- rtc_update_irq(rtc, 1, RTC_IRQF | RTC_AF);
+ rtc_update_irq(isl1208->rtc, 1, RTC_IRQF | RTC_AF);
/* Clear the alarm */
sr &= ~ISL1208_REG_SR_ALM;
@@ -596,11 +638,12 @@ isl1208_rtc_interrupt(int irq, void *data)
return err;
}
- if (sr & ISL1208_REG_SR_EVT) {
- sysfs_notify(&rtc->dev.kobj, NULL,
- dev_attr_timestamp0.attr.name);
+ if (isl1208->config->has_tamper && (sr & ISL1208_REG_SR_EVT)) {
dev_warn(&client->dev, "event detected");
handled = 1;
+ if (isl1208->config->has_timestamp)
+ sysfs_notify(&isl1208->rtc->dev.kobj, NULL,
+ dev_attr_timestamp0.attr.name);
}
return handled ? IRQ_HANDLED : IRQ_NONE;
@@ -637,7 +680,7 @@ isl1208_sysfs_show_dtrim(struct device *dev,
if (dtr < 0)
return dtr;
- return sprintf(buf, "%d ppm\n", dtr);
+ return sprintf(buf, "%d ppm\n", dtr - 100);
}
static DEVICE_ATTR(dtrim, S_IRUGO, isl1208_sysfs_show_dtrim, NULL);
@@ -700,6 +743,46 @@ static const struct attribute_group isl1219_rtc_sysfs_files = {
.attrs = isl1219_rtc_attrs,
};
+static int isl1208_nvmem_read(void *priv, unsigned int off, void *buf,
+ size_t count)
+{
+ struct isl1208_state *isl1208 = priv;
+ struct i2c_client *client = to_i2c_client(isl1208->rtc->dev.parent);
+ int ret;
+
+ /* nvmem sanitizes offset/count for us, but count==0 is possible */
+ if (!count)
+ return count;
+ ret = isl1208_i2c_read_regs(client, ISL1208_REG_USR1 + off, buf,
+ count);
+ return ret == 0 ? count : ret;
+}
+
+static int isl1208_nvmem_write(void *priv, unsigned int off, void *buf,
+ size_t count)
+{
+ struct isl1208_state *isl1208 = priv;
+ struct i2c_client *client = to_i2c_client(isl1208->rtc->dev.parent);
+ int ret;
+
+ /* nvmem sanitizes off/count for us, but count==0 is possible */
+ if (!count)
+ return count;
+ ret = isl1208_i2c_set_regs(client, ISL1208_REG_USR1 + off, buf,
+ count);
+
+ return ret == 0 ? count : ret;
+}
+
+static const struct nvmem_config isl1208_nvmem_config = {
+ .name = "isl1208_nvram",
+ .word_size = 1,
+ .stride = 1,
+ /* .size from chip specific config */
+ .reg_read = isl1208_nvmem_read,
+ .reg_write = isl1208_nvmem_write,
+};
+
static int isl1208_setup_irq(struct i2c_client *client, int irq)
{
int rc = devm_request_threaded_irq(&client->dev, irq, NULL,
@@ -722,7 +805,7 @@ static int
isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int rc = 0;
- struct rtc_device *rtc;
+ struct isl1208_state *isl1208;
int evdet_irq = -1;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
@@ -731,13 +814,33 @@ isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id)
if (isl1208_i2c_validate_client(client) < 0)
return -ENODEV;
- rtc = devm_rtc_allocate_device(&client->dev);
- if (IS_ERR(rtc))
- return PTR_ERR(rtc);
+ /* Allocate driver state, point i2c client data to it */
+ isl1208 = devm_kzalloc(&client->dev, sizeof(*isl1208), GFP_KERNEL);
+ if (!isl1208)
+ return -ENOMEM;
+ i2c_set_clientdata(client, isl1208);
+
+ /* Determine which chip we have */
+ if (client->dev.of_node) {
+ isl1208->config = of_device_get_match_data(&client->dev);
+ if (!isl1208->config)
+ return -ENODEV;
+ } else {
+ if (id->driver_data >= ISL_LAST_ID)
+ return -ENODEV;
+ isl1208->config = &isl1208_configs[id->driver_data];
+ }
+
+ isl1208->rtc = devm_rtc_allocate_device(&client->dev);
+ if (IS_ERR(isl1208->rtc))
+ return PTR_ERR(isl1208->rtc);
- rtc->ops = &isl1208_rtc_ops;
+ isl1208->rtc->ops = &isl1208_rtc_ops;
- i2c_set_clientdata(client, rtc);
+ /* Setup nvmem configuration in driver state struct */
+ isl1208->nvmem_config = isl1208_nvmem_config;
+ isl1208->nvmem_config.size = isl1208->config->nvmem_length;
+ isl1208->nvmem_config.priv = isl1208;
rc = isl1208_i2c_get_sr(client);
if (rc < 0) {
@@ -749,7 +852,7 @@ isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id)
dev_warn(&client->dev, "rtc power failure detected, "
"please set clock.\n");
- if (id->driver_data == TYPE_ISL1219) {
+ if (isl1208->config->has_tamper) {
struct device_node *np = client->dev.of_node;
u32 evienb;
@@ -770,13 +873,15 @@ isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id)
dev_err(&client->dev, "could not enable tamper detection\n");
return rc;
}
- rc = rtc_add_group(rtc, &isl1219_rtc_sysfs_files);
+ evdet_irq = of_irq_get_byname(np, "evdet");
+ }
+ if (isl1208->config->has_timestamp) {
+ rc = rtc_add_group(isl1208->rtc, &isl1219_rtc_sysfs_files);
if (rc)
return rc;
- evdet_irq = of_irq_get_byname(np, "evdet");
}
- rc = rtc_add_group(rtc, &isl1208_rtc_sysfs_files);
+ rc = rtc_add_group(isl1208->rtc, &isl1208_rtc_sysfs_files);
if (rc)
return rc;
@@ -790,24 +895,12 @@ isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id)
if (rc)
return rc;
- return rtc_register_device(rtc);
-}
-
-static const struct i2c_device_id isl1208_id[] = {
- { "isl1208", TYPE_ISL1208 },
- { "isl1218", TYPE_ISL1218 },
- { "isl1219", TYPE_ISL1219 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, isl1208_id);
+ rc = rtc_nvmem_register(isl1208->rtc, &isl1208->nvmem_config);
+ if (rc)
+ return rc;
-static const struct of_device_id isl1208_of_match[] = {
- { .compatible = "isil,isl1208" },
- { .compatible = "isil,isl1218" },
- { .compatible = "isil,isl1219" },
- { }
-};
-MODULE_DEVICE_TABLE(of, isl1208_of_match);
+ return rtc_register_device(isl1208->rtc);
+}
static struct i2c_driver isl1208_driver = {
.driver = {
diff --git a/drivers/rtc/rtc-mc146818-lib.c b/drivers/rtc/rtc-mc146818-lib.c
index 2f1772a358ca..18a6f15e313d 100644
--- a/drivers/rtc/rtc-mc146818-lib.c
+++ b/drivers/rtc/rtc-mc146818-lib.c
@@ -82,7 +82,7 @@ unsigned int mc146818_get_time(struct rtc_time *time)
time->tm_year += real_year - 72;
#endif
- if (century)
+ if (century > 20)
time->tm_year += (century - 19) * 100;
/*
diff --git a/drivers/rtc/rtc-meson.c b/drivers/rtc/rtc-meson.c
new file mode 100644
index 000000000000..e08b981dfc21
--- /dev/null
+++ b/drivers/rtc/rtc-meson.c
@@ -0,0 +1,407 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * RTC driver for the interal RTC block in the Amlogic Meson6, Meson8,
+ * Meson8b and Meson8m2 SoCs.
+ *
+ * The RTC is split in to two parts, the AHB front end and a simple serial
+ * connection to the actual registers. This driver manages both parts.
+ *
+ * Copyright (c) 2018 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
+ * Copyright (c) 2015 Ben Dooks <ben.dooks@codethink.co.uk> for Codethink Ltd
+ * Based on origin by Carlo Caione <carlo@endlessm.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/nvmem-provider.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+#include <linux/rtc.h>
+
+/* registers accessed from cpu bus */
+#define RTC_ADDR0 0x00
+ #define RTC_ADDR0_LINE_SCLK BIT(0)
+ #define RTC_ADDR0_LINE_SEN BIT(1)
+ #define RTC_ADDR0_LINE_SDI BIT(2)
+ #define RTC_ADDR0_START_SER BIT(17)
+ #define RTC_ADDR0_WAIT_SER BIT(22)
+ #define RTC_ADDR0_DATA GENMASK(31, 24)
+
+#define RTC_ADDR1 0x04
+ #define RTC_ADDR1_SDO BIT(0)
+ #define RTC_ADDR1_S_READY BIT(1)
+
+#define RTC_ADDR2 0x08
+#define RTC_ADDR3 0x0c
+
+#define RTC_REG4 0x10
+ #define RTC_REG4_STATIC_VALUE GENMASK(7, 0)
+
+/* rtc registers accessed via rtc-serial interface */
+#define RTC_COUNTER (0)
+#define RTC_SEC_ADJ (2)
+#define RTC_REGMEM_0 (4)
+#define RTC_REGMEM_1 (5)
+#define RTC_REGMEM_2 (6)
+#define RTC_REGMEM_3 (7)
+
+#define RTC_ADDR_BITS (3) /* number of address bits to send */
+#define RTC_DATA_BITS (32) /* number of data bits to tx/rx */
+
+#define MESON_STATIC_BIAS_CUR (0x5 << 1)
+#define MESON_STATIC_VOLTAGE (0x3 << 11)
+#define MESON_STATIC_DEFAULT (MESON_STATIC_BIAS_CUR | MESON_STATIC_VOLTAGE)
+
+struct meson_rtc {
+ struct rtc_device *rtc; /* rtc device we created */
+ struct device *dev; /* device we bound from */
+ struct reset_control *reset; /* reset source */
+ struct regulator *vdd; /* voltage input */
+ struct regmap *peripheral; /* peripheral registers */
+ struct regmap *serial; /* serial registers */
+};
+
+static const struct regmap_config meson_rtc_peripheral_regmap_config = {
+ .name = "peripheral-registers",
+ .reg_bits = 8,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = RTC_REG4,
+ .fast_io = true,
+};
+
+/* RTC front-end serialiser controls */
+
+static void meson_rtc_sclk_pulse(struct meson_rtc *rtc)
+{
+ udelay(5);
+ regmap_update_bits(rtc->peripheral, RTC_ADDR0, RTC_ADDR0_LINE_SCLK, 0);
+ udelay(5);
+ regmap_update_bits(rtc->peripheral, RTC_ADDR0, RTC_ADDR0_LINE_SCLK,
+ RTC_ADDR0_LINE_SCLK);
+}
+
+static void meson_rtc_send_bit(struct meson_rtc *rtc, unsigned int bit)
+{
+ regmap_update_bits(rtc->peripheral, RTC_ADDR0, RTC_ADDR0_LINE_SDI,
+ bit ? RTC_ADDR0_LINE_SDI : 0);
+ meson_rtc_sclk_pulse(rtc);
+}
+
+static void meson_rtc_send_bits(struct meson_rtc *rtc, u32 data,
+ unsigned int nr)
+{
+ u32 bit = 1 << (nr - 1);
+
+ while (bit) {
+ meson_rtc_send_bit(rtc, data & bit);
+ bit >>= 1;
+ }
+}
+
+static void meson_rtc_set_dir(struct meson_rtc *rtc, u32 mode)
+{
+ regmap_update_bits(rtc->peripheral, RTC_ADDR0, RTC_ADDR0_LINE_SEN, 0);
+ regmap_update_bits(rtc->peripheral, RTC_ADDR0, RTC_ADDR0_LINE_SDI, 0);
+ meson_rtc_send_bit(rtc, mode);
+ regmap_update_bits(rtc->peripheral, RTC_ADDR0, RTC_ADDR0_LINE_SDI, 0);
+}
+
+static u32 meson_rtc_get_data(struct meson_rtc *rtc)
+{
+ u32 tmp, val = 0;
+ int bit;
+
+ for (bit = 0; bit < RTC_DATA_BITS; bit++) {
+ meson_rtc_sclk_pulse(rtc);
+ val <<= 1;
+
+ regmap_read(rtc->peripheral, RTC_ADDR1, &tmp);
+ val |= tmp & RTC_ADDR1_SDO;
+ }
+
+ return val;
+}
+
+static int meson_rtc_get_bus(struct meson_rtc *rtc)
+{
+ int ret, retries = 3;
+ u32 val;
+
+ /* prepare bus for transfers, set all lines low */
+ val = RTC_ADDR0_LINE_SDI | RTC_ADDR0_LINE_SEN | RTC_ADDR0_LINE_SCLK;
+ regmap_update_bits(rtc->peripheral, RTC_ADDR0, val, 0);
+
+ for (retries = 0; retries < 3; retries++) {
+ /* wait for the bus to be ready */
+ if (!regmap_read_poll_timeout(rtc->peripheral, RTC_ADDR1, val,
+ val & RTC_ADDR1_S_READY, 10,
+ 10000))
+ return 0;
+
+ dev_warn(rtc->dev, "failed to get bus, resetting RTC\n");
+
+ ret = reset_control_reset(rtc->reset);
+ if (ret)
+ return ret;
+ }
+
+ dev_err(rtc->dev, "bus is not ready\n");
+ return -ETIMEDOUT;
+}
+
+static int meson_rtc_serial_bus_reg_read(void *context, unsigned int reg,
+ unsigned int *data)
+{
+ struct meson_rtc *rtc = context;
+ int ret;
+
+ ret = meson_rtc_get_bus(rtc);
+ if (ret)
+ return ret;
+
+ regmap_update_bits(rtc->peripheral, RTC_ADDR0, RTC_ADDR0_LINE_SEN,
+ RTC_ADDR0_LINE_SEN);
+ meson_rtc_send_bits(rtc, reg, RTC_ADDR_BITS);
+ meson_rtc_set_dir(rtc, 0);
+ *data = meson_rtc_get_data(rtc);
+
+ return 0;
+}
+
+static int meson_rtc_serial_bus_reg_write(void *context, unsigned int reg,
+ unsigned int data)
+{
+ struct meson_rtc *rtc = context;
+ int ret;
+
+ ret = meson_rtc_get_bus(rtc);
+ if (ret)
+ return ret;
+
+ regmap_update_bits(rtc->peripheral, RTC_ADDR0, RTC_ADDR0_LINE_SEN,
+ RTC_ADDR0_LINE_SEN);
+ meson_rtc_send_bits(rtc, data, RTC_DATA_BITS);
+ meson_rtc_send_bits(rtc, reg, RTC_ADDR_BITS);
+ meson_rtc_set_dir(rtc, 1);
+
+ return 0;
+}
+
+static const struct regmap_bus meson_rtc_serial_bus = {
+ .reg_read = meson_rtc_serial_bus_reg_read,
+ .reg_write = meson_rtc_serial_bus_reg_write,
+};
+
+static const struct regmap_config meson_rtc_serial_regmap_config = {
+ .name = "serial-registers",
+ .reg_bits = 4,
+ .reg_stride = 1,
+ .val_bits = 32,
+ .max_register = RTC_REGMEM_3,
+ .fast_io = false,
+};
+
+static int meson_rtc_write_static(struct meson_rtc *rtc, u32 data)
+{
+ u32 tmp;
+
+ regmap_write(rtc->peripheral, RTC_REG4,
+ FIELD_PREP(RTC_REG4_STATIC_VALUE, (data >> 8)));
+
+ /* write the static value and start the auto serializer */
+ tmp = FIELD_PREP(RTC_ADDR0_DATA, (data & 0xff)) | RTC_ADDR0_START_SER;
+ regmap_update_bits(rtc->peripheral, RTC_ADDR0,
+ RTC_ADDR0_DATA | RTC_ADDR0_START_SER, tmp);
+
+ /* wait for the auto serializer to complete */
+ return regmap_read_poll_timeout(rtc->peripheral, RTC_REG4, tmp,
+ !(tmp & RTC_ADDR0_WAIT_SER), 10,
+ 10000);
+}
+
+/* RTC interface layer functions */
+
+static int meson_rtc_gettime(struct device *dev, struct rtc_time *tm)
+{
+ struct meson_rtc *rtc = dev_get_drvdata(dev);
+ u32 time;
+ int ret;
+
+ ret = regmap_read(rtc->serial, RTC_COUNTER, &time);
+ if (!ret)
+ rtc_time64_to_tm(time, tm);
+
+ return ret;
+}
+
+static int meson_rtc_settime(struct device *dev, struct rtc_time *tm)
+{
+ struct meson_rtc *rtc = dev_get_drvdata(dev);
+
+ return regmap_write(rtc->serial, RTC_COUNTER, rtc_tm_to_time64(tm));
+}
+
+static const struct rtc_class_ops meson_rtc_ops = {
+ .read_time = meson_rtc_gettime,
+ .set_time = meson_rtc_settime,
+};
+
+/* NVMEM interface layer functions */
+
+static int meson_rtc_regmem_read(void *context, unsigned int offset,
+ void *buf, size_t bytes)
+{
+ struct meson_rtc *rtc = context;
+ unsigned int read_offset, read_size;
+
+ read_offset = RTC_REGMEM_0 + (offset / 4);
+ read_size = bytes / 4;
+
+ return regmap_bulk_read(rtc->serial, read_offset, buf, read_size);
+}
+
+static int meson_rtc_regmem_write(void *context, unsigned int offset,
+ void *buf, size_t bytes)
+{
+ struct meson_rtc *rtc = context;
+ unsigned int write_offset, write_size;
+
+ write_offset = RTC_REGMEM_0 + (offset / 4);
+ write_size = bytes / 4;
+
+ return regmap_bulk_write(rtc->serial, write_offset, buf, write_size);
+}
+
+static int meson_rtc_probe(struct platform_device *pdev)
+{
+ struct nvmem_config meson_rtc_nvmem_config = {
+ .name = "meson-rtc-regmem",
+ .type = NVMEM_TYPE_BATTERY_BACKED,
+ .word_size = 4,
+ .stride = 4,
+ .size = 4 * 4,
+ .reg_read = meson_rtc_regmem_read,
+ .reg_write = meson_rtc_regmem_write,
+ };
+ struct device *dev = &pdev->dev;
+ struct meson_rtc *rtc;
+ struct resource *res;
+ void __iomem *base;
+ int ret;
+ u32 tm;
+
+ rtc = devm_kzalloc(dev, sizeof(struct meson_rtc), GFP_KERNEL);
+ if (!rtc)
+ return -ENOMEM;
+
+ rtc->rtc = devm_rtc_allocate_device(dev);
+ if (IS_ERR(rtc->rtc))
+ return PTR_ERR(rtc->rtc);
+
+ platform_set_drvdata(pdev, rtc);
+
+ rtc->dev = dev;
+
+ rtc->rtc->ops = &meson_rtc_ops;
+ rtc->rtc->range_max = U32_MAX;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ rtc->peripheral = devm_regmap_init_mmio(dev, base,
+ &meson_rtc_peripheral_regmap_config);
+ if (IS_ERR(rtc->peripheral)) {
+ dev_err(dev, "failed to create peripheral regmap\n");
+ return PTR_ERR(rtc->peripheral);
+ }
+
+ rtc->reset = devm_reset_control_get(dev, NULL);
+ if (IS_ERR(rtc->reset)) {
+ dev_err(dev, "missing reset line\n");
+ return PTR_ERR(rtc->reset);
+ }
+
+ rtc->vdd = devm_regulator_get(dev, "vdd");
+ if (IS_ERR(rtc->vdd)) {
+ dev_err(dev, "failed to get the vdd-supply\n");
+ return PTR_ERR(rtc->vdd);
+ }
+
+ ret = regulator_enable(rtc->vdd);
+ if (ret) {
+ dev_err(dev, "failed to enable vdd-supply\n");
+ return ret;
+ }
+
+ ret = meson_rtc_write_static(rtc, MESON_STATIC_DEFAULT);
+ if (ret) {
+ dev_err(dev, "failed to set static values\n");
+ goto out_disable_vdd;
+ }
+
+ rtc->serial = devm_regmap_init(dev, &meson_rtc_serial_bus, rtc,
+ &meson_rtc_serial_regmap_config);
+ if (IS_ERR(rtc->serial)) {
+ dev_err(dev, "failed to create serial regmap\n");
+ ret = PTR_ERR(rtc->serial);
+ goto out_disable_vdd;
+ }
+
+ /*
+ * check if we can read RTC counter, if not then the RTC is probably
+ * not functional. If it isn't probably best to not bind.
+ */
+ ret = regmap_read(rtc->serial, RTC_COUNTER, &tm);
+ if (ret) {
+ dev_err(dev, "cannot read RTC counter, RTC not functional\n");
+ goto out_disable_vdd;
+ }
+
+ meson_rtc_nvmem_config.priv = rtc;
+ ret = rtc_nvmem_register(rtc->rtc, &meson_rtc_nvmem_config);
+ if (ret)
+ goto out_disable_vdd;
+
+ ret = rtc_register_device(rtc->rtc);
+ if (ret)
+ goto out_disable_vdd;
+
+ return 0;
+
+out_disable_vdd:
+ regulator_disable(rtc->vdd);
+ return ret;
+}
+
+static const struct of_device_id meson_rtc_dt_match[] = {
+ { .compatible = "amlogic,meson6-rtc", },
+ { .compatible = "amlogic,meson8-rtc", },
+ { .compatible = "amlogic,meson8b-rtc", },
+ { .compatible = "amlogic,meson8m2-rtc", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, meson_rtc_dt_match);
+
+static struct platform_driver meson_rtc_driver = {
+ .probe = meson_rtc_probe,
+ .driver = {
+ .name = "meson-rtc",
+ .of_match_table = of_match_ptr(meson_rtc_dt_match),
+ },
+};
+module_platform_driver(meson_rtc_driver);
+
+MODULE_DESCRIPTION("Amlogic Meson RTC Driver");
+MODULE_AUTHOR("Ben Dooks <ben.doosk@codethink.co.uk>");
+MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:meson-rtc");
diff --git a/drivers/rtc/rtc-pcf85063.c b/drivers/rtc/rtc-pcf85063.c
index 283c2335b01b..f6ce63c443a0 100644
--- a/drivers/rtc/rtc-pcf85063.c
+++ b/drivers/rtc/rtc-pcf85063.c
@@ -27,17 +27,11 @@
*/
#define PCF85063_REG_CTRL1 0x00 /* status */
+#define PCF85063_REG_CTRL1_CAP_SEL BIT(0)
#define PCF85063_REG_CTRL1_STOP BIT(5)
-#define PCF85063_REG_CTRL2 0x01
#define PCF85063_REG_SC 0x04 /* datetime */
#define PCF85063_REG_SC_OS 0x80
-#define PCF85063_REG_MN 0x05
-#define PCF85063_REG_HR 0x06
-#define PCF85063_REG_DM 0x07
-#define PCF85063_REG_DW 0x08
-#define PCF85063_REG_MO 0x09
-#define PCF85063_REG_YR 0x0A
static struct i2c_driver pcf85063_driver;
@@ -180,6 +174,39 @@ static const struct rtc_class_ops pcf85063_rtc_ops = {
.set_time = pcf85063_rtc_set_time
};
+static int pcf85063_load_capacitance(struct i2c_client *client)
+{
+ u32 load;
+ int rc;
+ u8 reg;
+
+ rc = i2c_smbus_read_byte_data(client, PCF85063_REG_CTRL1);
+ if (rc < 0)
+ return rc;
+
+ reg = rc;
+ load = 7000;
+ of_property_read_u32(client->dev.of_node, "quartz-load-femtofarads",
+ &load);
+
+ switch (load) {
+ default:
+ dev_warn(&client->dev, "Unknown quartz-load-femtofarads value: %d. Assuming 7000",
+ load);
+ /* fall through */
+ case 7000:
+ reg &= ~PCF85063_REG_CTRL1_CAP_SEL;
+ break;
+ case 12500:
+ reg |= PCF85063_REG_CTRL1_CAP_SEL;
+ break;
+ }
+
+ rc = i2c_smbus_write_byte_data(client, PCF85063_REG_CTRL1, reg);
+
+ return rc;
+}
+
static int pcf85063_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@@ -197,6 +224,11 @@ static int pcf85063_probe(struct i2c_client *client,
return err;
}
+ err = pcf85063_load_capacitance(client);
+ if (err < 0)
+ dev_warn(&client->dev, "failed to set xtal load capacitance: %d",
+ err);
+
rtc = devm_rtc_device_register(&client->dev,
pcf85063_driver.driver.name,
&pcf85063_rtc_ops, THIS_MODULE);
diff --git a/drivers/rtc/rtc-pcf8523.c b/drivers/rtc/rtc-pcf8523.c
index 3fcd2cbafc84..b5c61a70b5df 100644
--- a/drivers/rtc/rtc-pcf8523.c
+++ b/drivers/rtc/rtc-pcf8523.c
@@ -97,8 +97,9 @@ static int pcf8523_voltage_low(struct i2c_client *client)
return !!(value & REG_CONTROL3_BLF);
}
-static int pcf8523_select_capacitance(struct i2c_client *client, bool high)
+static int pcf8523_load_capacitance(struct i2c_client *client)
{
+ u32 load;
u8 value;
int err;
@@ -106,14 +107,24 @@ static int pcf8523_select_capacitance(struct i2c_client *client, bool high)
if (err < 0)
return err;
- if (!high)
- value &= ~REG_CONTROL1_CAP_SEL;
- else
+ load = 12500;
+ of_property_read_u32(client->dev.of_node, "quartz-load-femtofarads",
+ &load);
+
+ switch (load) {
+ default:
+ dev_warn(&client->dev, "Unknown quartz-load-femtofarads value: %d. Assuming 12500",
+ load);
+ /* fall through */
+ case 12500:
value |= REG_CONTROL1_CAP_SEL;
+ break;
+ case 7000:
+ value &= ~REG_CONTROL1_CAP_SEL;
+ break;
+ }
err = pcf8523_write(client, REG_CONTROL1, value);
- if (err < 0)
- return err;
return err;
}
@@ -347,9 +358,10 @@ static int pcf8523_probe(struct i2c_client *client,
if (!pcf)
return -ENOMEM;
- err = pcf8523_select_capacitance(client, true);
+ err = pcf8523_load_capacitance(client);
if (err < 0)
- return err;
+ dev_warn(&client->dev, "failed to set xtal load capacitance: %d",
+ err);
err = pcf8523_set_pm(client, 0);
if (err < 0)
@@ -374,6 +386,7 @@ MODULE_DEVICE_TABLE(i2c, pcf8523_id);
#ifdef CONFIG_OF
static const struct of_device_id pcf8523_of_match[] = {
{ .compatible = "nxp,pcf8523" },
+ { .compatible = "microcrystal,rv8523" },
{ }
};
MODULE_DEVICE_TABLE(of, pcf8523_of_match);
diff --git a/drivers/rtc/rtc-pic32.c b/drivers/rtc/rtc-pic32.c
index d7ef0a6f8931..1c4de6e90da0 100644
--- a/drivers/rtc/rtc-pic32.c
+++ b/drivers/rtc/rtc-pic32.c
@@ -1,18 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* PIC32 RTC driver
*
* Joshua Henderson <joshua.henderson@microchip.com>
* Copyright (C) 2016 Microchip Technology Inc. All rights reserved.
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
*/
#include <linux/init.h>
#include <linux/module.h>
@@ -180,22 +172,16 @@ static int pic32_rtc_settime(struct device *dev, struct rtc_time *tm)
{
struct pic32_rtc_dev *pdata = dev_get_drvdata(dev);
void __iomem *base = pdata->reg_base;
- int year = tm->tm_year - 100;
dev_dbg(dev, "set time %ptR\n", tm);
- if (year < 0 || year >= 100) {
- dev_err(dev, "rtc only supports 100 years\n");
- return -EINVAL;
- }
-
clk_enable(pdata->clk);
writeb(bin2bcd(tm->tm_sec), base + PIC32_RTCSEC);
writeb(bin2bcd(tm->tm_min), base + PIC32_RTCMIN);
writeb(bin2bcd(tm->tm_hour), base + PIC32_RTCHOUR);
writeb(bin2bcd(tm->tm_mday), base + PIC32_RTCDAY);
writeb(bin2bcd(tm->tm_mon + 1), base + PIC32_RTCMON);
- writeb(bin2bcd(year), base + PIC32_RTCYEAR);
+ writeb(bin2bcd(tm->tm_year - 100), base + PIC32_RTCYEAR);
clk_disable(pdata->clk);
return 0;
@@ -348,13 +334,17 @@ static int pic32_rtc_probe(struct platform_device *pdev)
device_init_wakeup(&pdev->dev, 1);
- pdata->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
- &pic32_rtcops,
- THIS_MODULE);
- if (IS_ERR(pdata->rtc)) {
- ret = PTR_ERR(pdata->rtc);
+ pdata->rtc = devm_rtc_allocate_device(&pdev->dev);
+ if (IS_ERR(pdata->rtc))
+ return PTR_ERR(pdata->rtc);
+
+ pdata->rtc->ops = &pic32_rtcops;
+ pdata->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
+ pdata->rtc->range_max = RTC_TIMESTAMP_END_2099;
+
+ ret = rtc_register_device(pdata->rtc);
+ if (ret)
goto err_nortc;
- }
pdata->rtc->max_user_freq = 128;
diff --git a/drivers/rtc/rtc-pm8xxx.c b/drivers/rtc/rtc-pm8xxx.c
index 1074e3dbfc1d..cda020700744 100644
--- a/drivers/rtc/rtc-pm8xxx.c
+++ b/drivers/rtc/rtc-pm8xxx.c
@@ -213,7 +213,8 @@ static int pm8xxx_rtc_read_time(struct device *dev, struct rtc_time *tm)
}
}
- secs = value[0] | (value[1] << 8) | (value[2] << 16) | (value[3] << 24);
+ secs = value[0] | (value[1] << 8) | (value[2] << 16) |
+ ((unsigned long)value[3] << 24);
rtc_time_to_tm(secs, tm);
@@ -284,7 +285,8 @@ static int pm8xxx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
return rc;
}
- secs = value[0] | (value[1] << 8) | (value[2] << 16) | (value[3] << 24);
+ secs = value[0] | (value[1] << 8) | (value[2] << 16) |
+ ((unsigned long)value[3] << 24);
rtc_time_to_tm(secs, &alarm->time);
diff --git a/drivers/rtc/rtc-rs5c372.c b/drivers/rtc/rtc-rs5c372.c
index c5038329058c..66a473a3c3fe 100644
--- a/drivers/rtc/rtc-rs5c372.c
+++ b/drivers/rtc/rtc-rs5c372.c
@@ -52,8 +52,10 @@
# define RS5C_CTRL1_CT4 (4 << 0) /* 1 Hz level irq */
#define RS5C_REG_CTRL2 15
# define RS5C372_CTRL2_24 (1 << 5)
-# define R2025_CTRL2_XST (1 << 5)
-# define RS5C_CTRL2_XSTP (1 << 4) /* only if !R2025S/D */
+# define RS5C_CTRL2_XSTP (1 << 4) /* only if !R2x2x */
+# define R2x2x_CTRL2_VDET (1 << 6) /* only if R2x2x */
+# define R2x2x_CTRL2_XSTP (1 << 5) /* only if R2x2x */
+# define R2x2x_CTRL2_PON (1 << 4) /* only if R2x2x */
# define RS5C_CTRL2_CTFG (1 << 2)
# define RS5C_CTRL2_AAFG (1 << 1) /* or WAFG */
# define RS5C_CTRL2_BAFG (1 << 0) /* or DAFG */
@@ -212,10 +214,27 @@ static int rs5c372_rtc_read_time(struct device *dev, struct rtc_time *tm)
struct i2c_client *client = to_i2c_client(dev);
struct rs5c372 *rs5c = i2c_get_clientdata(client);
int status = rs5c_get_regs(rs5c);
+ unsigned char ctrl2 = rs5c->regs[RS5C_REG_CTRL2];
if (status < 0)
return status;
+ switch (rs5c->type) {
+ case rtc_r2025sd:
+ case rtc_r2221tl:
+ if ((rs5c->type == rtc_r2025sd && !(ctrl2 & R2x2x_CTRL2_XSTP)) ||
+ (rs5c->type == rtc_r2221tl && (ctrl2 & R2x2x_CTRL2_XSTP))) {
+ dev_warn(&client->dev, "rtc oscillator interruption detected. Please reset the rtc clock.\n");
+ return -EINVAL;
+ }
+ break;
+ default:
+ if (ctrl2 & RS5C_CTRL2_XSTP) {
+ dev_warn(&client->dev, "rtc oscillator interruption detected. Please reset the rtc clock.\n");
+ return -EINVAL;
+ }
+ }
+
tm->tm_sec = bcd2bin(rs5c->regs[RS5C372_REG_SECS] & 0x7f);
tm->tm_min = bcd2bin(rs5c->regs[RS5C372_REG_MINS] & 0x7f);
tm->tm_hour = rs5c_reg2hr(rs5c, rs5c->regs[RS5C372_REG_HOURS]);
@@ -243,6 +262,7 @@ static int rs5c372_rtc_set_time(struct device *dev, struct rtc_time *tm)
struct i2c_client *client = to_i2c_client(dev);
struct rs5c372 *rs5c = i2c_get_clientdata(client);
unsigned char buf[7];
+ unsigned char ctrl2;
int addr;
dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d "
@@ -261,7 +281,32 @@ static int rs5c372_rtc_set_time(struct device *dev, struct rtc_time *tm)
buf[6] = bin2bcd(tm->tm_year - 100);
if (i2c_smbus_write_i2c_block_data(client, addr, sizeof(buf), buf) < 0) {
- dev_err(&client->dev, "%s: write error\n", __func__);
+ dev_dbg(&client->dev, "%s: write error in line %i\n",
+ __func__, __LINE__);
+ return -EIO;
+ }
+
+ addr = RS5C_ADDR(RS5C_REG_CTRL2);
+ ctrl2 = i2c_smbus_read_byte_data(client, addr);
+
+ /* clear rtc warning bits */
+ switch (rs5c->type) {
+ case rtc_r2025sd:
+ case rtc_r2221tl:
+ ctrl2 &= ~(R2x2x_CTRL2_VDET | R2x2x_CTRL2_PON);
+ if (rs5c->type == rtc_r2025sd)
+ ctrl2 |= R2x2x_CTRL2_XSTP;
+ else
+ ctrl2 &= ~R2x2x_CTRL2_XSTP;
+ break;
+ default:
+ ctrl2 &= ~RS5C_CTRL2_XSTP;
+ break;
+ }
+
+ if (i2c_smbus_write_byte_data(client, addr, ctrl2) < 0) {
+ dev_dbg(&client->dev, "%s: write error in line %i\n",
+ __func__, __LINE__);
return -EIO;
}
@@ -519,20 +564,25 @@ static int rs5c_oscillator_setup(struct rs5c372 *rs5c372)
unsigned char buf[2];
int addr, i, ret = 0;
- if (rs5c372->type == rtc_r2025sd) {
- if (rs5c372->regs[RS5C_REG_CTRL2] & R2025_CTRL2_XST)
- return ret;
- rs5c372->regs[RS5C_REG_CTRL2] |= R2025_CTRL2_XST;
- } else {
- if (!(rs5c372->regs[RS5C_REG_CTRL2] & RS5C_CTRL2_XSTP))
- return ret;
- rs5c372->regs[RS5C_REG_CTRL2] &= ~RS5C_CTRL2_XSTP;
- }
-
addr = RS5C_ADDR(RS5C_REG_CTRL1);
buf[0] = rs5c372->regs[RS5C_REG_CTRL1];
buf[1] = rs5c372->regs[RS5C_REG_CTRL2];
+ switch (rs5c372->type) {
+ case rtc_r2025sd:
+ if (buf[1] & R2x2x_CTRL2_XSTP)
+ return ret;
+ break;
+ case rtc_r2221tl:
+ if (!(buf[1] & R2x2x_CTRL2_XSTP))
+ return ret;
+ break;
+ default:
+ if (!(buf[1] & RS5C_CTRL2_XSTP))
+ return ret;
+ break;
+ }
+
/* use 24hr mode */
switch (rs5c372->type) {
case rtc_rs5c372a:
diff --git a/drivers/rtc/rtc-rv3028.c b/drivers/rtc/rtc-rv3028.c
new file mode 100644
index 000000000000..06884ebb7a61
--- /dev/null
+++ b/drivers/rtc/rtc-rv3028.c
@@ -0,0 +1,732 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * RTC driver for the Micro Crystal RV3028
+ *
+ * Copyright (C) 2019 Micro Crystal SA
+ *
+ * Alexandre Belloni <alexandre.belloni@bootlin.com>
+ *
+ */
+
+#include <linux/bcd.h>
+#include <linux/bitops.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/log2.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/rtc.h>
+
+#define RV3028_SEC 0x00
+#define RV3028_MIN 0x01
+#define RV3028_HOUR 0x02
+#define RV3028_WDAY 0x03
+#define RV3028_DAY 0x04
+#define RV3028_MONTH 0x05
+#define RV3028_YEAR 0x06
+#define RV3028_ALARM_MIN 0x07
+#define RV3028_ALARM_HOUR 0x08
+#define RV3028_ALARM_DAY 0x09
+#define RV3028_STATUS 0x0E
+#define RV3028_CTRL1 0x0F
+#define RV3028_CTRL2 0x10
+#define RV3028_EVT_CTRL 0x13
+#define RV3028_TS_COUNT 0x14
+#define RV3028_TS_SEC 0x15
+#define RV3028_RAM1 0x1F
+#define RV3028_EEPROM_ADDR 0x25
+#define RV3028_EEPROM_DATA 0x26
+#define RV3028_EEPROM_CMD 0x27
+#define RV3028_CLKOUT 0x35
+#define RV3028_OFFSET 0x36
+#define RV3028_BACKUP 0x37
+
+#define RV3028_STATUS_PORF BIT(0)
+#define RV3028_STATUS_EVF BIT(1)
+#define RV3028_STATUS_AF BIT(2)
+#define RV3028_STATUS_TF BIT(3)
+#define RV3028_STATUS_UF BIT(4)
+#define RV3028_STATUS_BSF BIT(5)
+#define RV3028_STATUS_CLKF BIT(6)
+#define RV3028_STATUS_EEBUSY BIT(7)
+
+#define RV3028_CTRL1_EERD BIT(3)
+#define RV3028_CTRL1_WADA BIT(5)
+
+#define RV3028_CTRL2_RESET BIT(0)
+#define RV3028_CTRL2_12_24 BIT(1)
+#define RV3028_CTRL2_EIE BIT(2)
+#define RV3028_CTRL2_AIE BIT(3)
+#define RV3028_CTRL2_TIE BIT(4)
+#define RV3028_CTRL2_UIE BIT(5)
+#define RV3028_CTRL2_TSE BIT(7)
+
+#define RV3028_EVT_CTRL_TSR BIT(2)
+
+#define RV3028_EEPROM_CMD_WRITE 0x21
+#define RV3028_EEPROM_CMD_READ 0x22
+
+#define RV3028_EEBUSY_POLL 10000
+#define RV3028_EEBUSY_TIMEOUT 100000
+
+#define RV3028_BACKUP_TCE BIT(5)
+#define RV3028_BACKUP_TCR_MASK GENMASK(1,0)
+
+#define OFFSET_STEP_PPT 953674
+
+enum rv3028_type {
+ rv_3028,
+};
+
+struct rv3028_data {
+ struct regmap *regmap;
+ struct rtc_device *rtc;
+ enum rv3028_type type;
+};
+
+static u16 rv3028_trickle_resistors[] = {1000, 3000, 6000, 11000};
+
+static ssize_t timestamp0_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct rv3028_data *rv3028 = dev_get_drvdata(dev->parent);
+
+ regmap_update_bits(rv3028->regmap, RV3028_EVT_CTRL, RV3028_EVT_CTRL_TSR,
+ RV3028_EVT_CTRL_TSR);
+
+ return count;
+};
+
+static ssize_t timestamp0_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct rv3028_data *rv3028 = dev_get_drvdata(dev->parent);
+ struct rtc_time tm;
+ int ret, count;
+ u8 date[6];
+
+ ret = regmap_read(rv3028->regmap, RV3028_TS_COUNT, &count);
+ if (ret)
+ return ret;
+
+ if (!count)
+ return 0;
+
+ ret = regmap_bulk_read(rv3028->regmap, RV3028_TS_SEC, date,
+ sizeof(date));
+ if (ret)
+ return ret;
+
+ tm.tm_sec = bcd2bin(date[0]);
+ tm.tm_min = bcd2bin(date[1]);
+ tm.tm_hour = bcd2bin(date[2]);
+ tm.tm_mday = bcd2bin(date[3]);
+ tm.tm_mon = bcd2bin(date[4]) - 1;
+ tm.tm_year = bcd2bin(date[5]) + 100;
+
+ ret = rtc_valid_tm(&tm);
+ if (ret)
+ return ret;
+
+ return sprintf(buf, "%llu\n",
+ (unsigned long long)rtc_tm_to_time64(&tm));
+};
+
+static DEVICE_ATTR_RW(timestamp0);
+
+static ssize_t timestamp0_count_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct rv3028_data *rv3028 = dev_get_drvdata(dev->parent);
+ int ret, count;
+
+ ret = regmap_read(rv3028->regmap, RV3028_TS_COUNT, &count);
+ if (ret)
+ return ret;
+
+ return sprintf(buf, "%u\n", count);
+};
+
+static DEVICE_ATTR_RO(timestamp0_count);
+
+static struct attribute *rv3028_attrs[] = {
+ &dev_attr_timestamp0.attr,
+ &dev_attr_timestamp0_count.attr,
+ NULL
+};
+
+static const struct attribute_group rv3028_attr_group = {
+ .attrs = rv3028_attrs,
+};
+
+static irqreturn_t rv3028_handle_irq(int irq, void *dev_id)
+{
+ struct rv3028_data *rv3028 = dev_id;
+ unsigned long events = 0;
+ u32 status = 0, ctrl = 0;
+
+ if (regmap_read(rv3028->regmap, RV3028_STATUS, &status) < 0 ||
+ status == 0) {
+ return IRQ_NONE;
+ }
+
+ if (status & RV3028_STATUS_PORF)
+ dev_warn(&rv3028->rtc->dev, "Voltage low, data loss detected.\n");
+
+ if (status & RV3028_STATUS_TF) {
+ status |= RV3028_STATUS_TF;
+ ctrl |= RV3028_CTRL2_TIE;
+ events |= RTC_PF;
+ }
+
+ if (status & RV3028_STATUS_AF) {
+ status |= RV3028_STATUS_AF;
+ ctrl |= RV3028_CTRL2_AIE;
+ events |= RTC_AF;
+ }
+
+ if (status & RV3028_STATUS_UF) {
+ status |= RV3028_STATUS_UF;
+ ctrl |= RV3028_CTRL2_UIE;
+ events |= RTC_UF;
+ }
+
+ if (events) {
+ rtc_update_irq(rv3028->rtc, 1, events);
+ regmap_update_bits(rv3028->regmap, RV3028_STATUS, status, 0);
+ regmap_update_bits(rv3028->regmap, RV3028_CTRL2, ctrl, 0);
+ }
+
+ if (status & RV3028_STATUS_EVF) {
+ sysfs_notify(&rv3028->rtc->dev.kobj, NULL,
+ dev_attr_timestamp0.attr.name);
+ dev_warn(&rv3028->rtc->dev, "event detected");
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int rv3028_get_time(struct device *dev, struct rtc_time *tm)
+{
+ struct rv3028_data *rv3028 = dev_get_drvdata(dev);
+ u8 date[7];
+ int ret, status;
+
+ ret = regmap_read(rv3028->regmap, RV3028_STATUS, &status);
+ if (ret < 0)
+ return ret;
+
+ if (status & RV3028_STATUS_PORF) {
+ dev_warn(dev, "Voltage low, data is invalid.\n");
+ return -EINVAL;
+ }
+
+ ret = regmap_bulk_read(rv3028->regmap, RV3028_SEC, date, sizeof(date));
+ if (ret)
+ return ret;
+
+ tm->tm_sec = bcd2bin(date[RV3028_SEC] & 0x7f);
+ tm->tm_min = bcd2bin(date[RV3028_MIN] & 0x7f);
+ tm->tm_hour = bcd2bin(date[RV3028_HOUR] & 0x3f);
+ tm->tm_wday = ilog2(date[RV3028_WDAY] & 0x7f);
+ tm->tm_mday = bcd2bin(date[RV3028_DAY] & 0x3f);
+ tm->tm_mon = bcd2bin(date[RV3028_MONTH] & 0x1f) - 1;
+ tm->tm_year = bcd2bin(date[RV3028_YEAR]) + 100;
+
+ return 0;
+}
+
+static int rv3028_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct rv3028_data *rv3028 = dev_get_drvdata(dev);
+ u8 date[7];
+ int ret;
+
+ date[RV3028_SEC] = bin2bcd(tm->tm_sec);
+ date[RV3028_MIN] = bin2bcd(tm->tm_min);
+ date[RV3028_HOUR] = bin2bcd(tm->tm_hour);
+ date[RV3028_WDAY] = 1 << (tm->tm_wday);
+ date[RV3028_DAY] = bin2bcd(tm->tm_mday);
+ date[RV3028_MONTH] = bin2bcd(tm->tm_mon + 1);
+ date[RV3028_YEAR] = bin2bcd(tm->tm_year - 100);
+
+ /*
+ * Writing to the Seconds register has the same effect as setting RESET
+ * bit to 1
+ */
+ ret = regmap_bulk_write(rv3028->regmap, RV3028_SEC, date,
+ sizeof(date));
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(rv3028->regmap, RV3028_STATUS,
+ RV3028_STATUS_PORF, 0);
+
+ return ret;
+}
+
+static int rv3028_get_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct rv3028_data *rv3028 = dev_get_drvdata(dev);
+ u8 alarmvals[3];
+ int status, ctrl, ret;
+
+ ret = regmap_bulk_read(rv3028->regmap, RV3028_ALARM_MIN, alarmvals,
+ sizeof(alarmvals));
+ if (ret)
+ return ret;
+
+ ret = regmap_read(rv3028->regmap, RV3028_STATUS, &status);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_read(rv3028->regmap, RV3028_CTRL2, &ctrl);
+ if (ret < 0)
+ return ret;
+
+ alrm->time.tm_sec = 0;
+ alrm->time.tm_min = bcd2bin(alarmvals[0] & 0x7f);
+ alrm->time.tm_hour = bcd2bin(alarmvals[1] & 0x3f);
+ alrm->time.tm_mday = bcd2bin(alarmvals[2] & 0x3f);
+
+ alrm->enabled = !!(ctrl & RV3028_CTRL2_AIE);
+ alrm->pending = (status & RV3028_STATUS_AF) && alrm->enabled;
+
+ return 0;
+}
+
+static int rv3028_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct rv3028_data *rv3028 = dev_get_drvdata(dev);
+ u8 alarmvals[3];
+ u8 ctrl = 0;
+ int ret;
+
+ /* The alarm has no seconds, round up to nearest minute */
+ if (alrm->time.tm_sec) {
+ time64_t alarm_time = rtc_tm_to_time64(&alrm->time);
+
+ alarm_time += 60 - alrm->time.tm_sec;
+ rtc_time64_to_tm(alarm_time, &alrm->time);
+ }
+
+ ret = regmap_update_bits(rv3028->regmap, RV3028_CTRL2,
+ RV3028_CTRL2_AIE | RV3028_CTRL2_UIE, 0);
+ if (ret)
+ return ret;
+
+ alarmvals[0] = bin2bcd(alrm->time.tm_min);
+ alarmvals[1] = bin2bcd(alrm->time.tm_hour);
+ alarmvals[2] = bin2bcd(alrm->time.tm_mday);
+
+ ret = regmap_update_bits(rv3028->regmap, RV3028_STATUS,
+ RV3028_STATUS_AF, 0);
+ if (ret)
+ return ret;
+
+ ret = regmap_bulk_write(rv3028->regmap, RV3028_ALARM_MIN, alarmvals,
+ sizeof(alarmvals));
+ if (ret)
+ return ret;
+
+ if (alrm->enabled) {
+ if (rv3028->rtc->uie_rtctimer.enabled)
+ ctrl |= RV3028_CTRL2_UIE;
+ if (rv3028->rtc->aie_timer.enabled)
+ ctrl |= RV3028_CTRL2_AIE;
+ }
+
+ ret = regmap_update_bits(rv3028->regmap, RV3028_CTRL2,
+ RV3028_CTRL2_UIE | RV3028_CTRL2_AIE, ctrl);
+
+ return ret;
+}
+
+static int rv3028_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct rv3028_data *rv3028 = dev_get_drvdata(dev);
+ int ctrl = 0, ret;
+
+ if (enabled) {
+ if (rv3028->rtc->uie_rtctimer.enabled)
+ ctrl |= RV3028_CTRL2_UIE;
+ if (rv3028->rtc->aie_timer.enabled)
+ ctrl |= RV3028_CTRL2_AIE;
+ }
+
+ ret = regmap_update_bits(rv3028->regmap, RV3028_STATUS,
+ RV3028_STATUS_AF | RV3028_STATUS_UF, 0);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(rv3028->regmap, RV3028_CTRL2,
+ RV3028_CTRL2_UIE | RV3028_CTRL2_AIE, ctrl);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int rv3028_read_offset(struct device *dev, long *offset)
+{
+ struct rv3028_data *rv3028 = dev_get_drvdata(dev);
+ int ret, value, steps;
+
+ ret = regmap_read(rv3028->regmap, RV3028_OFFSET, &value);
+ if (ret < 0)
+ return ret;
+
+ steps = sign_extend32(value << 1, 8);
+
+ ret = regmap_read(rv3028->regmap, RV3028_BACKUP, &value);
+ if (ret < 0)
+ return ret;
+
+ steps += value >> 7;
+
+ *offset = DIV_ROUND_CLOSEST(steps * OFFSET_STEP_PPT, 1000);
+
+ return 0;
+}
+
+static int rv3028_set_offset(struct device *dev, long offset)
+{
+ struct rv3028_data *rv3028 = dev_get_drvdata(dev);
+ int ret;
+
+ offset = clamp(offset, -244141L, 243187L) * 1000;
+ offset = DIV_ROUND_CLOSEST(offset, OFFSET_STEP_PPT);
+
+ ret = regmap_write(rv3028->regmap, RV3028_OFFSET, offset >> 1);
+ if (ret < 0)
+ return ret;
+
+ return regmap_update_bits(rv3028->regmap, RV3028_BACKUP, BIT(7),
+ offset << 7);
+}
+
+static int rv3028_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
+{
+ struct rv3028_data *rv3028 = dev_get_drvdata(dev);
+ int status, ret = 0;
+
+ switch (cmd) {
+ case RTC_VL_READ:
+ ret = regmap_read(rv3028->regmap, RV3028_STATUS, &status);
+ if (ret < 0)
+ return ret;
+
+ if (status & RV3028_STATUS_PORF)
+ dev_warn(&rv3028->rtc->dev, "Voltage low, data loss detected.\n");
+
+ status &= RV3028_STATUS_PORF;
+
+ if (copy_to_user((void __user *)arg, &status, sizeof(int)))
+ return -EFAULT;
+
+ return 0;
+
+ case RTC_VL_CLR:
+ ret = regmap_update_bits(rv3028->regmap, RV3028_STATUS,
+ RV3028_STATUS_PORF, 0);
+
+ return ret;
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+static int rv3028_nvram_write(void *priv, unsigned int offset, void *val,
+ size_t bytes)
+{
+ return regmap_bulk_write(priv, RV3028_RAM1 + offset, val, bytes);
+}
+
+static int rv3028_nvram_read(void *priv, unsigned int offset, void *val,
+ size_t bytes)
+{
+ return regmap_bulk_read(priv, RV3028_RAM1 + offset, val, bytes);
+}
+
+static int rv3028_eeprom_write(void *priv, unsigned int offset, void *val,
+ size_t bytes)
+{
+ u32 status, ctrl1;
+ int i, ret, err;
+ u8 *buf = val;
+
+ ret = regmap_read(priv, RV3028_CTRL1, &ctrl1);
+ if (ret)
+ return ret;
+
+ if (!(ctrl1 & RV3028_CTRL1_EERD)) {
+ ret = regmap_update_bits(priv, RV3028_CTRL1,
+ RV3028_CTRL1_EERD, RV3028_CTRL1_EERD);
+ if (ret)
+ return ret;
+
+ ret = regmap_read_poll_timeout(priv, RV3028_STATUS, status,
+ !(status & RV3028_STATUS_EEBUSY),
+ RV3028_EEBUSY_POLL,
+ RV3028_EEBUSY_TIMEOUT);
+ if (ret)
+ goto restore_eerd;
+ }
+
+ for (i = 0; i < bytes; i++) {
+ ret = regmap_write(priv, RV3028_EEPROM_ADDR, offset + i);
+ if (ret)
+ goto restore_eerd;
+
+ ret = regmap_write(priv, RV3028_EEPROM_DATA, buf[i]);
+ if (ret)
+ goto restore_eerd;
+
+ ret = regmap_write(priv, RV3028_EEPROM_CMD, 0x0);
+ if (ret)
+ goto restore_eerd;
+
+ ret = regmap_write(priv, RV3028_EEPROM_CMD,
+ RV3028_EEPROM_CMD_WRITE);
+ if (ret)
+ goto restore_eerd;
+
+ usleep_range(RV3028_EEBUSY_POLL, RV3028_EEBUSY_TIMEOUT);
+
+ ret = regmap_read_poll_timeout(priv, RV3028_STATUS, status,
+ !(status & RV3028_STATUS_EEBUSY),
+ RV3028_EEBUSY_POLL,
+ RV3028_EEBUSY_TIMEOUT);
+ if (ret)
+ goto restore_eerd;
+ }
+
+restore_eerd:
+ if (!(ctrl1 & RV3028_CTRL1_EERD))
+ {
+ err = regmap_update_bits(priv, RV3028_CTRL1, RV3028_CTRL1_EERD,
+ 0);
+ if (err && !ret)
+ ret = err;
+ }
+
+ return ret;
+}
+
+static int rv3028_eeprom_read(void *priv, unsigned int offset, void *val,
+ size_t bytes)
+{
+ u32 status, ctrl1, data;
+ int i, ret, err;
+ u8 *buf = val;
+
+ ret = regmap_read(priv, RV3028_CTRL1, &ctrl1);
+ if (ret)
+ return ret;
+
+ if (!(ctrl1 & RV3028_CTRL1_EERD)) {
+ ret = regmap_update_bits(priv, RV3028_CTRL1,
+ RV3028_CTRL1_EERD, RV3028_CTRL1_EERD);
+ if (ret)
+ return ret;
+
+ ret = regmap_read_poll_timeout(priv, RV3028_STATUS, status,
+ !(status & RV3028_STATUS_EEBUSY),
+ RV3028_EEBUSY_POLL,
+ RV3028_EEBUSY_TIMEOUT);
+ if (ret)
+ goto restore_eerd;
+ }
+
+ for (i = 0; i < bytes; i++) {
+ ret = regmap_write(priv, RV3028_EEPROM_ADDR, offset + i);
+ if (ret)
+ goto restore_eerd;
+
+ ret = regmap_write(priv, RV3028_EEPROM_CMD, 0x0);
+ if (ret)
+ goto restore_eerd;
+
+ ret = regmap_write(priv, RV3028_EEPROM_CMD,
+ RV3028_EEPROM_CMD_READ);
+ if (ret)
+ goto restore_eerd;
+
+ ret = regmap_read_poll_timeout(priv, RV3028_STATUS, status,
+ !(status & RV3028_STATUS_EEBUSY),
+ RV3028_EEBUSY_POLL,
+ RV3028_EEBUSY_TIMEOUT);
+ if (ret)
+ goto restore_eerd;
+
+ ret = regmap_read(priv, RV3028_EEPROM_DATA, &data);
+ if (ret)
+ goto restore_eerd;
+ buf[i] = data;
+ }
+
+restore_eerd:
+ if (!(ctrl1 & RV3028_CTRL1_EERD))
+ {
+ err = regmap_update_bits(priv, RV3028_CTRL1, RV3028_CTRL1_EERD,
+ 0);
+ if (err && !ret)
+ ret = err;
+ }
+
+ return ret;
+}
+
+static struct rtc_class_ops rv3028_rtc_ops = {
+ .read_time = rv3028_get_time,
+ .set_time = rv3028_set_time,
+ .read_offset = rv3028_read_offset,
+ .set_offset = rv3028_set_offset,
+ .ioctl = rv3028_ioctl,
+};
+
+static const struct regmap_config regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0x37,
+};
+
+static int rv3028_probe(struct i2c_client *client)
+{
+ struct rv3028_data *rv3028;
+ int ret, status;
+ u32 ohms;
+ struct nvmem_config nvmem_cfg = {
+ .name = "rv3028_nvram",
+ .word_size = 1,
+ .stride = 1,
+ .size = 2,
+ .type = NVMEM_TYPE_BATTERY_BACKED,
+ .reg_read = rv3028_nvram_read,
+ .reg_write = rv3028_nvram_write,
+ };
+ struct nvmem_config eeprom_cfg = {
+ .name = "rv3028_eeprom",
+ .word_size = 1,
+ .stride = 1,
+ .size = 43,
+ .type = NVMEM_TYPE_EEPROM,
+ .reg_read = rv3028_eeprom_read,
+ .reg_write = rv3028_eeprom_write,
+ };
+
+ rv3028 = devm_kzalloc(&client->dev, sizeof(struct rv3028_data),
+ GFP_KERNEL);
+ if (!rv3028)
+ return -ENOMEM;
+
+ rv3028->regmap = devm_regmap_init_i2c(client, &regmap_config);
+
+ i2c_set_clientdata(client, rv3028);
+
+ ret = regmap_read(rv3028->regmap, RV3028_STATUS, &status);
+ if (ret < 0)
+ return ret;
+
+ if (status & RV3028_STATUS_PORF)
+ dev_warn(&client->dev, "Voltage low, data loss detected.\n");
+
+ if (status & RV3028_STATUS_AF)
+ dev_warn(&client->dev, "An alarm may have been missed.\n");
+
+ rv3028->rtc = devm_rtc_allocate_device(&client->dev);
+ if (IS_ERR(rv3028->rtc)) {
+ return PTR_ERR(rv3028->rtc);
+ }
+
+ if (client->irq > 0) {
+ ret = devm_request_threaded_irq(&client->dev, client->irq,
+ NULL, rv3028_handle_irq,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "rv3028", rv3028);
+ if (ret) {
+ dev_warn(&client->dev, "unable to request IRQ, alarms disabled\n");
+ client->irq = 0;
+ } else {
+ rv3028_rtc_ops.read_alarm = rv3028_get_alarm;
+ rv3028_rtc_ops.set_alarm = rv3028_set_alarm;
+ rv3028_rtc_ops.alarm_irq_enable = rv3028_alarm_irq_enable;
+ }
+ }
+
+ ret = regmap_update_bits(rv3028->regmap, RV3028_CTRL1,
+ RV3028_CTRL1_WADA, RV3028_CTRL1_WADA);
+ if (ret)
+ return ret;
+
+ /* setup timestamping */
+ ret = regmap_update_bits(rv3028->regmap, RV3028_CTRL2,
+ RV3028_CTRL2_EIE | RV3028_CTRL2_TSE,
+ RV3028_CTRL2_EIE | RV3028_CTRL2_TSE);
+ if (ret)
+ return ret;
+
+ /* setup trickle charger */
+ if (!device_property_read_u32(&client->dev, "trickle-resistor-ohms",
+ &ohms)) {
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(rv3028_trickle_resistors); i++)
+ if (ohms == rv3028_trickle_resistors[i])
+ break;
+
+ if (i < ARRAY_SIZE(rv3028_trickle_resistors)) {
+ ret = regmap_update_bits(rv3028->regmap, RV3028_BACKUP,
+ RV3028_BACKUP_TCE |
+ RV3028_BACKUP_TCR_MASK,
+ RV3028_BACKUP_TCE | i);
+ if (ret)
+ return ret;
+ } else {
+ dev_warn(&client->dev, "invalid trickle resistor value\n");
+ }
+ }
+
+ ret = rtc_add_group(rv3028->rtc, &rv3028_attr_group);
+ if (ret)
+ return ret;
+
+ rv3028->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
+ rv3028->rtc->range_max = RTC_TIMESTAMP_END_2099;
+ rv3028->rtc->ops = &rv3028_rtc_ops;
+ ret = rtc_register_device(rv3028->rtc);
+ if (ret)
+ return ret;
+
+ nvmem_cfg.priv = rv3028->regmap;
+ rtc_nvmem_register(rv3028->rtc, &nvmem_cfg);
+ eeprom_cfg.priv = rv3028->regmap;
+ rtc_nvmem_register(rv3028->rtc, &eeprom_cfg);
+
+ rv3028->rtc->max_user_freq = 1;
+
+ return 0;
+}
+
+static const struct of_device_id rv3028_of_match[] = {
+ { .compatible = "microcrystal,rv3028", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, rv3028_of_match);
+
+static struct i2c_driver rv3028_driver = {
+ .driver = {
+ .name = "rtc-rv3028",
+ .of_match_table = of_match_ptr(rv3028_of_match),
+ },
+ .probe_new = rv3028_probe,
+};
+module_i2c_driver(rv3028_driver);
+
+MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@bootlin.com>");
+MODULE_DESCRIPTION("Micro Crystal RV3028 RTC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/rtc/rtc-rv8803.c b/drivers/rtc/rtc-rv8803.c
index 450a0b831a2d..0b102c3cf5a4 100644
--- a/drivers/rtc/rtc-rv8803.c
+++ b/drivers/rtc/rtc-rv8803.c
@@ -1,13 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* RTC driver for the Micro Crystal RV8803
*
* Copyright (C) 2015 Micro Crystal SA
- *
- * Alexandre Belloni <alexandre.belloni@free-electrons.com>
- *
- * 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.
+ * Alexandre Belloni <alexandre.belloni@bootlin.com>
*
*/
@@ -236,9 +232,6 @@ static int rv8803_set_time(struct device *dev, struct rtc_time *tm)
u8 date[7];
int ctrl, flags, ret;
- if ((tm->tm_year < 100) || (tm->tm_year > 199))
- return -EINVAL;
-
ctrl = rv8803_read_reg(rv8803->client, RV8803_CTRL);
if (ctrl < 0)
return ctrl;
@@ -602,6 +595,8 @@ static int rv8803_probe(struct i2c_client *client,
rv8803->rtc->ops = &rv8803_rtc_ops;
rv8803->rtc->nvram_old_abi = true;
+ rv8803->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
+ rv8803->rtc->range_max = RTC_TIMESTAMP_END_2099;
err = rtc_register_device(rv8803->rtc);
if (err)
return err;
@@ -648,6 +643,6 @@ static struct i2c_driver rv8803_driver = {
};
module_i2c_driver(rv8803_driver);
-MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@free-electrons.com>");
+MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@bootlin.com>");
MODULE_DESCRIPTION("Micro Crystal RV8803 RTC driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/rtc/rtc-rx8581.c b/drivers/rtc/rtc-rx8581.c
index eac882169744..776e3a2b89e8 100644
--- a/drivers/rtc/rtc-rx8581.c
+++ b/drivers/rtc/rtc-rx8581.c
@@ -15,6 +15,8 @@
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/bcd.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/regmap.h>
#include <linux/rtc.h>
#include <linux/log2.h>
@@ -51,11 +53,19 @@
#define RX8581_CTRL_STOP 0x02 /* STOP bit */
#define RX8581_CTRL_RESET 0x01 /* RESET bit */
+#define RX8571_USER_RAM 0x10
+#define RX8571_NVRAM_SIZE 0x10
+
struct rx8581 {
struct regmap *regmap;
struct rtc_device *rtc;
};
+struct rx85x1_config {
+ struct regmap_config regmap;
+ unsigned int num_nvram;
+};
+
/*
* In the routines that deal directly with the rx8581 hardware, we use
* rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch.
@@ -181,25 +191,103 @@ static const struct rtc_class_ops rx8581_rtc_ops = {
.set_time = rx8581_rtc_set_time,
};
-static int rx8581_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int rx8571_nvram_read(void *priv, unsigned int offset, void *val,
+ size_t bytes)
{
- struct rx8581 *rx8581;
- static const struct regmap_config config = {
+ struct rx8581 *rx8581 = priv;
+
+ return regmap_bulk_read(rx8581->regmap, RX8571_USER_RAM + offset,
+ val, bytes);
+}
+
+static int rx8571_nvram_write(void *priv, unsigned int offset, void *val,
+ size_t bytes)
+{
+ struct rx8581 *rx8581 = priv;
+
+ return regmap_bulk_write(rx8581->regmap, RX8571_USER_RAM + offset,
+ val, bytes);
+}
+
+static int rx85x1_nvram_read(void *priv, unsigned int offset, void *val,
+ size_t bytes)
+{
+ struct rx8581 *rx8581 = priv;
+ unsigned int tmp_val;
+ int ret;
+
+ ret = regmap_read(rx8581->regmap, RX8581_REG_RAM, &tmp_val);
+ (*(unsigned char *)val) = (unsigned char) tmp_val;
+
+ return ret;
+}
+
+static int rx85x1_nvram_write(void *priv, unsigned int offset, void *val,
+ size_t bytes)
+{
+ struct rx8581 *rx8581 = priv;
+ unsigned char tmp_val;
+
+ tmp_val = *((unsigned char *)val);
+ return regmap_write(rx8581->regmap, RX8581_REG_RAM,
+ (unsigned int)tmp_val);
+}
+
+static const struct rx85x1_config rx8581_config = {
+ .regmap = {
.reg_bits = 8,
.val_bits = 8,
.max_register = 0xf,
+ },
+ .num_nvram = 1
+};
+
+static const struct rx85x1_config rx8571_config = {
+ .regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0x1f,
+ },
+ .num_nvram = 2
+};
+
+static int rx8581_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct rx8581 *rx8581;
+ const struct rx85x1_config *config = &rx8581_config;
+ const void *data = of_device_get_match_data(&client->dev);
+ static struct nvmem_config nvmem_cfg[] = {
+ {
+ .name = "rx85x1-",
+ .word_size = 1,
+ .stride = 1,
+ .size = 1,
+ .reg_read = rx85x1_nvram_read,
+ .reg_write = rx85x1_nvram_write,
+ }, {
+ .name = "rx8571-",
+ .word_size = 1,
+ .stride = 1,
+ .size = RX8571_NVRAM_SIZE,
+ .reg_read = rx8571_nvram_read,
+ .reg_write = rx8571_nvram_write,
+ },
};
+ int ret, i;
dev_dbg(&client->dev, "%s\n", __func__);
+ if (data)
+ config = data;
+
rx8581 = devm_kzalloc(&client->dev, sizeof(struct rx8581), GFP_KERNEL);
if (!rx8581)
return -ENOMEM;
i2c_set_clientdata(client, rx8581);
- rx8581->regmap = devm_regmap_init_i2c(client, &config);
+ rx8581->regmap = devm_regmap_init_i2c(client, &config->regmap);
if (IS_ERR(rx8581->regmap))
return PTR_ERR(rx8581->regmap);
@@ -213,7 +301,14 @@ static int rx8581_probe(struct i2c_client *client,
rx8581->rtc->start_secs = 0;
rx8581->rtc->set_start_time = true;
- return rtc_register_device(rx8581->rtc);
+ ret = rtc_register_device(rx8581->rtc);
+
+ for (i = 0; i < config->num_nvram; i++) {
+ nvmem_cfg[i].priv = rx8581;
+ rtc_nvmem_register(rx8581->rtc, &nvmem_cfg[i]);
+ }
+
+ return ret;
}
static const struct i2c_device_id rx8581_id[] = {
@@ -223,8 +318,9 @@ static const struct i2c_device_id rx8581_id[] = {
MODULE_DEVICE_TABLE(i2c, rx8581_id);
static const struct of_device_id rx8581_of_match[] = {
- { .compatible = "epson,rx8581" },
- { }
+ { .compatible = "epson,rx8571", .data = &rx8571_config },
+ { .compatible = "epson,rx8581", .data = &rx8581_config },
+ { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, rx8581_of_match);
@@ -240,5 +336,5 @@ static struct i2c_driver rx8581_driver = {
module_i2c_driver(rx8581_driver);
MODULE_AUTHOR("Martyn Welch <martyn.welch@ge.com>");
-MODULE_DESCRIPTION("Epson RX-8581 RTC driver");
+MODULE_DESCRIPTION("Epson RX-8571/RX-8581 RTC driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c
index 04c68178c42d..e81a2b22a5c3 100644
--- a/drivers/rtc/rtc-s3c.c
+++ b/drivers/rtc/rtc-s3c.c
@@ -26,6 +26,7 @@
#include <linux/log2.h>
#include <linux/slab.h>
#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/uaccess.h>
#include <linux/io.h>
@@ -39,7 +40,7 @@ struct s3c_rtc {
void __iomem *base;
struct clk *rtc_clk;
struct clk *rtc_src_clk;
- bool clk_disabled;
+ bool alarm_enabled;
const struct s3c_rtc_data *data;
@@ -47,7 +48,7 @@ struct s3c_rtc {
int irq_tick;
spinlock_t pie_lock;
- spinlock_t alarm_clk_lock;
+ spinlock_t alarm_lock;
int ticnt_save;
int ticnt_en_save;
@@ -70,44 +71,27 @@ struct s3c_rtc_data {
static int s3c_rtc_enable_clk(struct s3c_rtc *info)
{
- unsigned long irq_flags;
- int ret = 0;
+ int ret;
- spin_lock_irqsave(&info->alarm_clk_lock, irq_flags);
+ ret = clk_enable(info->rtc_clk);
+ if (ret)
+ return ret;
- if (info->clk_disabled) {
- ret = clk_enable(info->rtc_clk);
- if (ret)
- goto out;
-
- if (info->data->needs_src_clk) {
- ret = clk_enable(info->rtc_src_clk);
- if (ret) {
- clk_disable(info->rtc_clk);
- goto out;
- }
+ if (info->data->needs_src_clk) {
+ ret = clk_enable(info->rtc_src_clk);
+ if (ret) {
+ clk_disable(info->rtc_clk);
+ return ret;
}
- info->clk_disabled = false;
}
-
-out:
- spin_unlock_irqrestore(&info->alarm_clk_lock, irq_flags);
-
- return ret;
+ return 0;
}
static void s3c_rtc_disable_clk(struct s3c_rtc *info)
{
- unsigned long irq_flags;
-
- spin_lock_irqsave(&info->alarm_clk_lock, irq_flags);
- if (!info->clk_disabled) {
- if (info->data->needs_src_clk)
- clk_disable(info->rtc_src_clk);
- clk_disable(info->rtc_clk);
- info->clk_disabled = true;
- }
- spin_unlock_irqrestore(&info->alarm_clk_lock, irq_flags);
+ if (info->data->needs_src_clk)
+ clk_disable(info->rtc_src_clk);
+ clk_disable(info->rtc_clk);
}
/* IRQ Handlers */
@@ -135,6 +119,7 @@ static irqreturn_t s3c_rtc_alarmirq(int irq, void *id)
static int s3c_rtc_setaie(struct device *dev, unsigned int enabled)
{
struct s3c_rtc *info = dev_get_drvdata(dev);
+ unsigned long flags;
unsigned int tmp;
int ret;
@@ -151,17 +136,19 @@ static int s3c_rtc_setaie(struct device *dev, unsigned int enabled)
writeb(tmp, info->base + S3C2410_RTCALM);
- s3c_rtc_disable_clk(info);
+ spin_lock_irqsave(&info->alarm_lock, flags);
- if (enabled) {
- ret = s3c_rtc_enable_clk(info);
- if (ret)
- return ret;
- } else {
+ if (info->alarm_enabled && !enabled)
s3c_rtc_disable_clk(info);
- }
+ else if (!info->alarm_enabled && enabled)
+ ret = s3c_rtc_enable_clk(info);
- return 0;
+ info->alarm_enabled = enabled;
+ spin_unlock_irqrestore(&info->alarm_lock, flags);
+
+ s3c_rtc_disable_clk(info);
+
+ return ret;
}
/* Set RTC frequency */
@@ -357,10 +344,10 @@ static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
writeb(alrm_en, info->base + S3C2410_RTCALM);
- s3c_rtc_disable_clk(info);
-
s3c_rtc_setaie(dev, alrm->enabled);
+ s3c_rtc_disable_clk(info);
+
return 0;
}
@@ -456,16 +443,6 @@ static int s3c_rtc_remove(struct platform_device *pdev)
return 0;
}
-static const struct of_device_id s3c_rtc_dt_match[];
-
-static const struct s3c_rtc_data *s3c_rtc_get_data(struct platform_device *pdev)
-{
- const struct of_device_id *match;
-
- match = of_match_node(s3c_rtc_dt_match, pdev->dev.of_node);
- return match->data;
-}
-
static int s3c_rtc_probe(struct platform_device *pdev)
{
struct s3c_rtc *info = NULL;
@@ -485,13 +462,13 @@ static int s3c_rtc_probe(struct platform_device *pdev)
}
info->dev = &pdev->dev;
- info->data = s3c_rtc_get_data(pdev);
+ info->data = of_device_get_match_data(&pdev->dev);
if (!info->data) {
dev_err(&pdev->dev, "failed getting s3c_rtc_data\n");
return -EINVAL;
}
spin_lock_init(&info->pie_lock);
- spin_lock_init(&info->alarm_clk_lock);
+ spin_lock_init(&info->alarm_lock);
platform_set_drvdata(pdev, info);
@@ -591,6 +568,8 @@ static int s3c_rtc_probe(struct platform_device *pdev)
s3c_rtc_setfreq(info, 1);
+ s3c_rtc_disable_clk(info);
+
return 0;
err_nortc:
diff --git a/drivers/rtc/rtc-sd3078.c b/drivers/rtc/rtc-sd3078.c
new file mode 100644
index 000000000000..42cb90db7f94
--- /dev/null
+++ b/drivers/rtc/rtc-sd3078.c
@@ -0,0 +1,231 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Real Time Clock (RTC) Driver for sd3078
+ * Copyright (C) 2018 Zoro Li
+ */
+
+#include <linux/bcd.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/rtc.h>
+#include <linux/slab.h>
+
+#define SD3078_REG_SC 0x00
+#define SD3078_REG_MN 0x01
+#define SD3078_REG_HR 0x02
+#define SD3078_REG_DW 0x03
+#define SD3078_REG_DM 0x04
+#define SD3078_REG_MO 0x05
+#define SD3078_REG_YR 0x06
+
+#define SD3078_REG_CTRL1 0x0f
+#define SD3078_REG_CTRL2 0x10
+#define SD3078_REG_CTRL3 0x11
+
+#define KEY_WRITE1 0x80
+#define KEY_WRITE2 0x04
+#define KEY_WRITE3 0x80
+
+#define NUM_TIME_REGS (SD3078_REG_YR - SD3078_REG_SC + 1)
+
+/*
+ * The sd3078 has write protection
+ * and we can choose whether or not to use it.
+ * Write protection is turned off by default.
+ */
+#define WRITE_PROTECT_EN 0
+
+struct sd3078 {
+ struct rtc_device *rtc;
+ struct regmap *regmap;
+};
+
+/*
+ * In order to prevent arbitrary modification of the time register,
+ * when modification of the register,
+ * the "write" bit needs to be written in a certain order.
+ * 1. set WRITE1 bit
+ * 2. set WRITE2 bit
+ * 3. set WRITE3 bit
+ */
+static void sd3078_enable_reg_write(struct sd3078 *sd3078)
+{
+ regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL2,
+ KEY_WRITE1, KEY_WRITE1);
+ regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL1,
+ KEY_WRITE2, KEY_WRITE2);
+ regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL1,
+ KEY_WRITE3, KEY_WRITE3);
+}
+
+#if WRITE_PROTECT_EN
+/*
+ * In order to prevent arbitrary modification of the time register,
+ * we should disable the write function.
+ * when disable write,
+ * the "write" bit needs to be clear in a certain order.
+ * 1. clear WRITE2 bit
+ * 2. clear WRITE3 bit
+ * 3. clear WRITE1 bit
+ */
+static void sd3078_disable_reg_write(struct sd3078 *sd3078)
+{
+ regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL1,
+ KEY_WRITE2, 0);
+ regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL1,
+ KEY_WRITE3, 0);
+ regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL2,
+ KEY_WRITE1, 0);
+}
+#endif
+
+static int sd3078_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ unsigned char hour;
+ unsigned char rtc_data[NUM_TIME_REGS] = {0};
+ struct i2c_client *client = to_i2c_client(dev);
+ struct sd3078 *sd3078 = i2c_get_clientdata(client);
+ int ret;
+
+ ret = regmap_bulk_read(sd3078->regmap, SD3078_REG_SC, rtc_data,
+ NUM_TIME_REGS);
+ if (ret < 0) {
+ dev_err(dev, "reading from RTC failed with err:%d\n", ret);
+ return ret;
+ }
+
+ tm->tm_sec = bcd2bin(rtc_data[SD3078_REG_SC] & 0x7F);
+ tm->tm_min = bcd2bin(rtc_data[SD3078_REG_MN] & 0x7F);
+
+ /*
+ * The sd3078 supports 12/24 hour mode.
+ * When getting time,
+ * we need to convert the 12 hour mode to the 24 hour mode.
+ */
+ hour = rtc_data[SD3078_REG_HR];
+ if (hour & 0x80) /* 24H MODE */
+ tm->tm_hour = bcd2bin(rtc_data[SD3078_REG_HR] & 0x3F);
+ else if (hour & 0x20) /* 12H MODE PM */
+ tm->tm_hour = bcd2bin(rtc_data[SD3078_REG_HR] & 0x1F) + 12;
+ else /* 12H MODE AM */
+ tm->tm_hour = bcd2bin(rtc_data[SD3078_REG_HR] & 0x1F);
+
+ tm->tm_mday = bcd2bin(rtc_data[SD3078_REG_DM] & 0x3F);
+ tm->tm_wday = rtc_data[SD3078_REG_DW] & 0x07;
+ tm->tm_mon = bcd2bin(rtc_data[SD3078_REG_MO] & 0x1F) - 1;
+ tm->tm_year = bcd2bin(rtc_data[SD3078_REG_YR]) + 100;
+
+ return 0;
+}
+
+static int sd3078_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ unsigned char rtc_data[NUM_TIME_REGS];
+ struct i2c_client *client = to_i2c_client(dev);
+ struct sd3078 *sd3078 = i2c_get_clientdata(client);
+ int ret;
+
+ rtc_data[SD3078_REG_SC] = bin2bcd(tm->tm_sec);
+ rtc_data[SD3078_REG_MN] = bin2bcd(tm->tm_min);
+ rtc_data[SD3078_REG_HR] = bin2bcd(tm->tm_hour) | 0x80;
+ rtc_data[SD3078_REG_DM] = bin2bcd(tm->tm_mday);
+ rtc_data[SD3078_REG_DW] = tm->tm_wday & 0x07;
+ rtc_data[SD3078_REG_MO] = bin2bcd(tm->tm_mon) + 1;
+ rtc_data[SD3078_REG_YR] = bin2bcd(tm->tm_year - 100);
+
+#if WRITE_PROTECT_EN
+ sd3078_enable_reg_write(sd3078);
+#endif
+
+ ret = regmap_bulk_write(sd3078->regmap, SD3078_REG_SC, rtc_data,
+ NUM_TIME_REGS);
+ if (ret < 0) {
+ dev_err(dev, "writing to RTC failed with err:%d\n", ret);
+ return ret;
+ }
+
+#if WRITE_PROTECT_EN
+ sd3078_disable_reg_write(sd3078);
+#endif
+
+ return 0;
+}
+
+static const struct rtc_class_ops sd3078_rtc_ops = {
+ .read_time = sd3078_rtc_read_time,
+ .set_time = sd3078_rtc_set_time,
+};
+
+static const struct regmap_config regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0x11,
+};
+
+static int sd3078_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret;
+ struct sd3078 *sd3078;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ return -ENODEV;
+
+ sd3078 = devm_kzalloc(&client->dev, sizeof(*sd3078), GFP_KERNEL);
+ if (!sd3078)
+ return -ENOMEM;
+
+ sd3078->regmap = devm_regmap_init_i2c(client, &regmap_config);
+ if (IS_ERR(sd3078->regmap)) {
+ dev_err(&client->dev, "regmap allocation failed\n");
+ return PTR_ERR(sd3078->regmap);
+ }
+
+ i2c_set_clientdata(client, sd3078);
+
+ sd3078->rtc = devm_rtc_allocate_device(&client->dev);
+ if (IS_ERR(sd3078->rtc))
+ return PTR_ERR(sd3078->rtc);
+
+ sd3078->rtc->ops = &sd3078_rtc_ops;
+ sd3078->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
+ sd3078->rtc->range_max = RTC_TIMESTAMP_END_2099;
+
+ ret = rtc_register_device(sd3078->rtc);
+ if (ret) {
+ dev_err(&client->dev, "failed to register rtc device\n");
+ return ret;
+ }
+
+ sd3078_enable_reg_write(sd3078);
+
+ return 0;
+}
+
+static const struct i2c_device_id sd3078_id[] = {
+ {"sd3078", 0},
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, sd3078_id);
+
+static const struct of_device_id rtc_dt_match[] = {
+ { .compatible = "whwave,sd3078" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, rtc_dt_match);
+
+static struct i2c_driver sd3078_driver = {
+ .driver = {
+ .name = "sd3078",
+ .of_match_table = of_match_ptr(rtc_dt_match),
+ },
+ .probe = sd3078_probe,
+ .id_table = sd3078_id,
+};
+
+module_i2c_driver(sd3078_driver);
+
+MODULE_AUTHOR("Dianlong Li <long17.cool@163.com>");
+MODULE_DESCRIPTION("SD3078 RTC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/rtc/rtc-snvs.c b/drivers/rtc/rtc-snvs.c
index b2483a749ac4..0b9eff19149b 100644
--- a/drivers/rtc/rtc-snvs.c
+++ b/drivers/rtc/rtc-snvs.c
@@ -239,6 +239,9 @@ static irqreturn_t snvs_rtc_irq_handler(int irq, void *dev_id)
u32 lpsr;
u32 events = 0;
+ if (data->clk)
+ clk_enable(data->clk);
+
regmap_read(data->regmap, data->offset + SNVS_LPSR, &lpsr);
if (lpsr & SNVS_LPSR_LPTA) {
@@ -253,6 +256,9 @@ static irqreturn_t snvs_rtc_irq_handler(int irq, void *dev_id)
/* clear interrupt status */
regmap_write(data->regmap, data->offset + SNVS_LPSR, lpsr);
+ if (data->clk)
+ clk_disable(data->clk);
+
return events ? IRQ_HANDLED : IRQ_NONE;
}
diff --git a/drivers/rtc/rtc-tx4939.c b/drivers/rtc/rtc-tx4939.c
index 61c110b2045f..2d24babc4057 100644
--- a/drivers/rtc/rtc-tx4939.c
+++ b/drivers/rtc/rtc-tx4939.c
@@ -1,11 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* TX4939 internal RTC driver
* Based on RBTX49xx patch from CELF patch archive.
*
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file "COPYING" in the main directory of this archive
- * for more details.
- *
* (C) Copyright TOSHIBA CORPORATION 2005-2007
*/
#include <linux/rtc.h>
@@ -65,10 +62,11 @@ static int tx4939_rtc_cmd(struct tx4939_rtc_reg __iomem *rtcreg, int cmd)
return 0;
}
-static int tx4939_rtc_set_mmss(struct device *dev, unsigned long secs)
+static int tx4939_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
struct tx4939rtc_plat_data *pdata = get_tx4939rtc_plat_data(dev);
struct tx4939_rtc_reg __iomem *rtcreg = pdata->rtcreg;
+ unsigned long secs = rtc_tm_to_time64(tm);
int i, ret;
unsigned char buf[6];
@@ -111,7 +109,7 @@ static int tx4939_rtc_read_time(struct device *dev, struct rtc_time *tm)
spin_unlock_irq(&pdata->lock);
sec = ((unsigned long)buf[5] << 24) | (buf[4] << 16) |
(buf[3] << 8) | buf[2];
- rtc_time_to_tm(sec, tm);
+ rtc_time64_to_tm(sec, tm);
return 0;
}
@@ -123,14 +121,7 @@ static int tx4939_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
unsigned long sec;
unsigned char buf[6];
- if (alrm->time.tm_sec < 0 ||
- alrm->time.tm_min < 0 ||
- alrm->time.tm_hour < 0 ||
- alrm->time.tm_mday < 0 ||
- alrm->time.tm_mon < 0 ||
- alrm->time.tm_year < 0)
- return -EINVAL;
- rtc_tm_to_time(&alrm->time, &sec);
+ sec = rtc_tm_to_time64(&alrm->time);
buf[0] = 0;
buf[1] = 0;
buf[2] = sec;
@@ -173,7 +164,7 @@ static int tx4939_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
spin_unlock_irq(&pdata->lock);
sec = ((unsigned long)buf[5] << 24) | (buf[4] << 16) |
(buf[3] << 8) | buf[2];
- rtc_time_to_tm(sec, &alrm->time);
+ rtc_time64_to_tm(sec, &alrm->time);
return rtc_valid_tm(&alrm->time);
}
@@ -210,7 +201,7 @@ static const struct rtc_class_ops tx4939_rtc_ops = {
.read_time = tx4939_rtc_read_time,
.read_alarm = tx4939_rtc_read_alarm,
.set_alarm = tx4939_rtc_set_alarm,
- .set_mmss = tx4939_rtc_set_mmss,
+ .set_time = tx4939_rtc_set_time,
.alarm_irq_enable = tx4939_rtc_alarm_irq_enable,
};
@@ -283,6 +274,7 @@ static int __init tx4939_rtc_probe(struct platform_device *pdev)
rtc->ops = &tx4939_rtc_ops;
rtc->nvram_old_abi = true;
+ rtc->range_max = U32_MAX;
pdata->rtc = rtc;
@@ -315,5 +307,5 @@ module_platform_driver_probe(tx4939_rtc_driver, tx4939_rtc_probe);
MODULE_AUTHOR("Atsushi Nemoto <anemo@mba.ocn.ne.jp>");
MODULE_DESCRIPTION("TX4939 internal RTC driver");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:tx4939rtc");
diff --git a/drivers/rtc/rtc-wilco-ec.c b/drivers/rtc/rtc-wilco-ec.c
new file mode 100644
index 000000000000..e62bda0cb53e
--- /dev/null
+++ b/drivers/rtc/rtc-wilco-ec.c
@@ -0,0 +1,177 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * RTC interface for Wilco Embedded Controller with R/W abilities
+ *
+ * Copyright 2018 Google LLC
+ *
+ * The corresponding platform device is typically registered in
+ * drivers/platform/chrome/wilco_ec/core.c
+ */
+
+#include <linux/bcd.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/wilco-ec.h>
+#include <linux/rtc.h>
+#include <linux/timekeeping.h>
+
+#define EC_COMMAND_CMOS 0x7c
+#define EC_CMOS_TOD_WRITE 0x02
+#define EC_CMOS_TOD_READ 0x08
+
+/**
+ * struct ec_rtc_read - Format of RTC returned by EC.
+ * @second: Second value (0..59)
+ * @minute: Minute value (0..59)
+ * @hour: Hour value (0..23)
+ * @day: Day value (1..31)
+ * @month: Month value (1..12)
+ * @year: Year value (full year % 100)
+ * @century: Century value (full year / 100)
+ *
+ * All values are presented in binary (not BCD).
+ */
+struct ec_rtc_read {
+ u8 second;
+ u8 minute;
+ u8 hour;
+ u8 day;
+ u8 month;
+ u8 year;
+ u8 century;
+} __packed;
+
+/**
+ * struct ec_rtc_write - Format of RTC sent to the EC.
+ * @param: EC_CMOS_TOD_WRITE
+ * @century: Century value (full year / 100)
+ * @year: Year value (full year % 100)
+ * @month: Month value (1..12)
+ * @day: Day value (1..31)
+ * @hour: Hour value (0..23)
+ * @minute: Minute value (0..59)
+ * @second: Second value (0..59)
+ * @weekday: Day of the week (0=Saturday)
+ *
+ * All values are presented in BCD.
+ */
+struct ec_rtc_write {
+ u8 param;
+ u8 century;
+ u8 year;
+ u8 month;
+ u8 day;
+ u8 hour;
+ u8 minute;
+ u8 second;
+ u8 weekday;
+} __packed;
+
+static int wilco_ec_rtc_read(struct device *dev, struct rtc_time *tm)
+{
+ struct wilco_ec_device *ec = dev_get_drvdata(dev->parent);
+ u8 param = EC_CMOS_TOD_READ;
+ struct ec_rtc_read rtc;
+ struct wilco_ec_message msg = {
+ .type = WILCO_EC_MSG_LEGACY,
+ .flags = WILCO_EC_FLAG_RAW_RESPONSE,
+ .command = EC_COMMAND_CMOS,
+ .request_data = &param,
+ .request_size = sizeof(param),
+ .response_data = &rtc,
+ .response_size = sizeof(rtc),
+ };
+ int ret;
+
+ ret = wilco_ec_mailbox(ec, &msg);
+ if (ret < 0)
+ return ret;
+
+ tm->tm_sec = rtc.second;
+ tm->tm_min = rtc.minute;
+ tm->tm_hour = rtc.hour;
+ tm->tm_mday = rtc.day;
+ tm->tm_mon = rtc.month - 1;
+ tm->tm_year = rtc.year + (rtc.century * 100) - 1900;
+ tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year);
+
+ /* Don't compute day of week, we don't need it. */
+ tm->tm_wday = -1;
+
+ return 0;
+}
+
+static int wilco_ec_rtc_write(struct device *dev, struct rtc_time *tm)
+{
+ struct wilco_ec_device *ec = dev_get_drvdata(dev->parent);
+ struct ec_rtc_write rtc;
+ struct wilco_ec_message msg = {
+ .type = WILCO_EC_MSG_LEGACY,
+ .flags = WILCO_EC_FLAG_RAW_RESPONSE,
+ .command = EC_COMMAND_CMOS,
+ .request_data = &rtc,
+ .request_size = sizeof(rtc),
+ };
+ int year = tm->tm_year + 1900;
+ /*
+ * Convert from 0=Sunday to 0=Saturday for the EC
+ * We DO need to set weekday because the EC controls battery charging
+ * schedules that depend on the day of the week.
+ */
+ int wday = tm->tm_wday == 6 ? 0 : tm->tm_wday + 1;
+ int ret;
+
+ rtc.param = EC_CMOS_TOD_WRITE;
+ rtc.century = bin2bcd(year / 100);
+ rtc.year = bin2bcd(year % 100);
+ rtc.month = bin2bcd(tm->tm_mon + 1);
+ rtc.day = bin2bcd(tm->tm_mday);
+ rtc.hour = bin2bcd(tm->tm_hour);
+ rtc.minute = bin2bcd(tm->tm_min);
+ rtc.second = bin2bcd(tm->tm_sec);
+ rtc.weekday = bin2bcd(wday);
+
+ ret = wilco_ec_mailbox(ec, &msg);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static const struct rtc_class_ops wilco_ec_rtc_ops = {
+ .read_time = wilco_ec_rtc_read,
+ .set_time = wilco_ec_rtc_write,
+};
+
+static int wilco_ec_rtc_probe(struct platform_device *pdev)
+{
+ struct rtc_device *rtc;
+
+ rtc = devm_rtc_allocate_device(&pdev->dev);
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
+
+ rtc->ops = &wilco_ec_rtc_ops;
+ /* EC only supports this century */
+ rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
+ rtc->range_max = RTC_TIMESTAMP_END_2099;
+ rtc->owner = THIS_MODULE;
+
+ return rtc_register_device(rtc);
+}
+
+static struct platform_driver wilco_ec_rtc_driver = {
+ .driver = {
+ .name = "rtc-wilco-ec",
+ },
+ .probe = wilco_ec_rtc_probe,
+};
+
+module_platform_driver(wilco_ec_rtc_driver);
+
+MODULE_ALIAS("platform:rtc-wilco-ec");
+MODULE_AUTHOR("Nick Crews <ncrews@chromium.org>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Wilco EC RTC driver");
diff --git a/drivers/rtc/rtc-zynqmp.c b/drivers/rtc/rtc-zynqmp.c
index c532bd13fbe5..bb950945ec7f 100644
--- a/drivers/rtc/rtc-zynqmp.c
+++ b/drivers/rtc/rtc-zynqmp.c
@@ -49,7 +49,6 @@
#define RTC_CALIB_DEF 0x198233
#define RTC_CALIB_MASK 0x1FFFFF
-#define RTC_SEC_MAX_VAL 0xFFFFFFFF
struct xlnx_rtc_dev {
struct rtc_device *rtc;
@@ -71,9 +70,6 @@ static int xlnx_rtc_set_time(struct device *dev, struct rtc_time *tm)
*/
new_time = rtc_tm_to_time64(tm) + 1;
- if (new_time > RTC_SEC_MAX_VAL)
- return -EINVAL;
-
/*
* Writing into calibration register will clear the Tick Counter and
* force the next second to be signaled exactly in 1 second period
@@ -154,9 +150,6 @@ static int xlnx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
alarm_time = rtc_tm_to_time64(&alrm->time);
- if (alarm_time > RTC_SEC_MAX_VAL)
- return -EINVAL;
-
writel((u32)alarm_time, (xrtcdev->reg_base + RTC_ALRM));
xlnx_rtc_alarm_irq_enable(dev, alrm->enabled);
@@ -222,6 +215,13 @@ static int xlnx_rtc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, xrtcdev);
+ xrtcdev->rtc = devm_rtc_allocate_device(&pdev->dev);
+ if (IS_ERR(xrtcdev->rtc))
+ return PTR_ERR(xrtcdev->rtc);
+
+ xrtcdev->rtc->ops = &xlnx_rtc_ops;
+ xrtcdev->rtc->range_max = U32_MAX;
+
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
xrtcdev->reg_base = devm_ioremap_resource(&pdev->dev, res);
@@ -263,9 +263,7 @@ static int xlnx_rtc_probe(struct platform_device *pdev)
device_init_wakeup(&pdev->dev, 1);
- xrtcdev->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
- &xlnx_rtc_ops, THIS_MODULE);
- return PTR_ERR_OR_ZERO(xrtcdev->rtc);
+ return rtc_register_device(xrtcdev->rtc);
}
static int xlnx_rtc_remove(struct platform_device *pdev)
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c
index a0baee25134c..a835b31aad99 100644
--- a/drivers/s390/cio/chsc.c
+++ b/drivers/s390/cio/chsc.c
@@ -24,6 +24,7 @@
#include <asm/crw.h>
#include <asm/isc.h>
#include <asm/ebcdic.h>
+#include <asm/ap.h>
#include "css.h"
#include "cio.h"
@@ -586,6 +587,15 @@ static void chsc_process_sei_scm_avail(struct chsc_sei_nt0_area *sei_area)
" failed (rc=%d).\n", ret);
}
+static void chsc_process_sei_ap_cfg_chg(struct chsc_sei_nt0_area *sei_area)
+{
+ CIO_CRW_EVENT(3, "chsc: ap config changed\n");
+ if (sei_area->rs != 5)
+ return;
+
+ ap_bus_cfg_chg();
+}
+
static void chsc_process_sei_nt2(struct chsc_sei_nt2_area *sei_area)
{
switch (sei_area->cc) {
@@ -612,6 +622,9 @@ static void chsc_process_sei_nt0(struct chsc_sei_nt0_area *sei_area)
case 2: /* i/o resource accessibility */
chsc_process_sei_res_acc(sei_area);
break;
+ case 3: /* ap config changed */
+ chsc_process_sei_ap_cfg_chg(sei_area);
+ break;
case 7: /* channel-path-availability information */
chsc_process_sei_chp_avail(sei_area);
break;
@@ -1382,3 +1395,40 @@ int chsc_pnso_brinfo(struct subchannel_id schid,
return chsc_error_from_response(brinfo_area->response.code);
}
EXPORT_SYMBOL_GPL(chsc_pnso_brinfo);
+
+int chsc_sgib(u32 origin)
+{
+ struct {
+ struct chsc_header request;
+ u16 op;
+ u8 reserved01[2];
+ u8 reserved02:4;
+ u8 fmt:4;
+ u8 reserved03[7];
+ /* operation data area begin */
+ u8 reserved04[4];
+ u32 gib_origin;
+ u8 reserved05[10];
+ u8 aix;
+ u8 reserved06[4029];
+ struct chsc_header response;
+ u8 reserved07[4];
+ } *sgib_area;
+ int ret;
+
+ spin_lock_irq(&chsc_page_lock);
+ memset(chsc_page, 0, PAGE_SIZE);
+ sgib_area = chsc_page;
+ sgib_area->request.length = 0x0fe0;
+ sgib_area->request.code = 0x0021;
+ sgib_area->op = 0x1;
+ sgib_area->gib_origin = origin;
+
+ ret = chsc(sgib_area);
+ if (ret == 0)
+ ret = chsc_error_from_response(sgib_area->response.code);
+ spin_unlock_irq(&chsc_page_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(chsc_sgib);
diff --git a/drivers/s390/cio/chsc.h b/drivers/s390/cio/chsc.h
index 78aba8d94eec..e57d68e325a3 100644
--- a/drivers/s390/cio/chsc.h
+++ b/drivers/s390/cio/chsc.h
@@ -164,6 +164,7 @@ int chsc_get_channel_measurement_chars(struct channel_path *chp);
int chsc_ssqd(struct subchannel_id schid, struct chsc_ssqd_area *ssqd);
int chsc_sadc(struct subchannel_id schid, struct chsc_scssc_area *scssc,
u64 summary_indicator_addr, u64 subchannel_indicator_addr);
+int chsc_sgib(u32 origin);
int chsc_error_from_response(int response);
int chsc_siosl(struct subchannel_id schid);
diff --git a/drivers/s390/cio/vfio_ccw_drv.c b/drivers/s390/cio/vfio_ccw_drv.c
index a10cec0e86eb..0b3b9de45c60 100644
--- a/drivers/s390/cio/vfio_ccw_drv.c
+++ b/drivers/s390/cio/vfio_ccw_drv.c
@@ -72,20 +72,24 @@ static void vfio_ccw_sch_io_todo(struct work_struct *work)
{
struct vfio_ccw_private *private;
struct irb *irb;
+ bool is_final;
private = container_of(work, struct vfio_ccw_private, io_work);
irb = &private->irb;
+ is_final = !(scsw_actl(&irb->scsw) &
+ (SCSW_ACTL_DEVACT | SCSW_ACTL_SCHACT));
if (scsw_is_solicited(&irb->scsw)) {
cp_update_scsw(&private->cp, &irb->scsw);
- cp_free(&private->cp);
+ if (is_final)
+ cp_free(&private->cp);
}
memcpy(private->io_region->irb_area, irb, sizeof(*irb));
if (private->io_trigger)
eventfd_signal(private->io_trigger, 1);
- if (private->mdev)
+ if (private->mdev && is_final)
private->state = VFIO_CCW_STATE_IDLE;
}
diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c
index e15816ff1265..1546389d71db 100644
--- a/drivers/s390/crypto/ap_bus.c
+++ b/drivers/s390/crypto/ap_bus.c
@@ -810,11 +810,18 @@ static int ap_device_remove(struct device *dev)
struct ap_device *ap_dev = to_ap_dev(dev);
struct ap_driver *ap_drv = ap_dev->drv;
+ /* prepare ap queue device removal */
if (is_queue_dev(dev))
- ap_queue_remove(to_ap_queue(dev));
+ ap_queue_prepare_remove(to_ap_queue(dev));
+
+ /* driver's chance to clean up gracefully */
if (ap_drv->remove)
ap_drv->remove(ap_dev);
+ /* now do the ap queue device remove */
+ if (is_queue_dev(dev))
+ ap_queue_remove(to_ap_queue(dev));
+
/* Remove queue/card from list of active queues/cards */
spin_lock_bh(&ap_list_lock);
if (is_card_dev(dev))
@@ -861,6 +868,16 @@ void ap_bus_force_rescan(void)
EXPORT_SYMBOL(ap_bus_force_rescan);
/*
+* A config change has happened, force an ap bus rescan.
+*/
+void ap_bus_cfg_chg(void)
+{
+ AP_DBF(DBF_INFO, "%s config change, forcing bus rescan\n", __func__);
+
+ ap_bus_force_rescan();
+}
+
+/*
* hex2bitmap() - parse hex mask string and set bitmap.
* Valid strings are "0x012345678" with at least one valid hex number.
* Rest of the bitmap to the right is padded with 0. No spaces allowed
diff --git a/drivers/s390/crypto/ap_bus.h b/drivers/s390/crypto/ap_bus.h
index d0059eae5d94..15a98a673c5c 100644
--- a/drivers/s390/crypto/ap_bus.h
+++ b/drivers/s390/crypto/ap_bus.h
@@ -91,6 +91,7 @@ enum ap_state {
AP_STATE_WORKING,
AP_STATE_QUEUE_FULL,
AP_STATE_SUSPEND_WAIT,
+ AP_STATE_REMOVE, /* about to be removed from driver */
AP_STATE_UNBOUND, /* momentary not bound to a driver */
AP_STATE_BORKED, /* broken */
NR_AP_STATES
@@ -252,6 +253,7 @@ void ap_bus_force_rescan(void);
void ap_queue_init_reply(struct ap_queue *aq, struct ap_message *ap_msg);
struct ap_queue *ap_queue_create(ap_qid_t qid, int device_type);
+void ap_queue_prepare_remove(struct ap_queue *aq);
void ap_queue_remove(struct ap_queue *aq);
void ap_queue_suspend(struct ap_device *ap_dev);
void ap_queue_resume(struct ap_device *ap_dev);
diff --git a/drivers/s390/crypto/ap_queue.c b/drivers/s390/crypto/ap_queue.c
index ba261210c6da..6a340f2c3556 100644
--- a/drivers/s390/crypto/ap_queue.c
+++ b/drivers/s390/crypto/ap_queue.c
@@ -420,6 +420,10 @@ static ap_func_t *ap_jumptable[NR_AP_STATES][NR_AP_EVENTS] = {
[AP_EVENT_POLL] = ap_sm_suspend_read,
[AP_EVENT_TIMEOUT] = ap_sm_nop,
},
+ [AP_STATE_REMOVE] = {
+ [AP_EVENT_POLL] = ap_sm_nop,
+ [AP_EVENT_TIMEOUT] = ap_sm_nop,
+ },
[AP_STATE_UNBOUND] = {
[AP_EVENT_POLL] = ap_sm_nop,
[AP_EVENT_TIMEOUT] = ap_sm_nop,
@@ -740,18 +744,31 @@ void ap_flush_queue(struct ap_queue *aq)
}
EXPORT_SYMBOL(ap_flush_queue);
-void ap_queue_remove(struct ap_queue *aq)
+void ap_queue_prepare_remove(struct ap_queue *aq)
{
- ap_flush_queue(aq);
+ spin_lock_bh(&aq->lock);
+ /* flush queue */
+ __ap_flush_queue(aq);
+ /* set REMOVE state to prevent new messages are queued in */
+ aq->state = AP_STATE_REMOVE;
del_timer_sync(&aq->timeout);
+ spin_unlock_bh(&aq->lock);
+}
- /* reset with zero, also clears irq registration */
+void ap_queue_remove(struct ap_queue *aq)
+{
+ /*
+ * all messages have been flushed and the state is
+ * AP_STATE_REMOVE. Now reset with zero which also
+ * clears the irq registration and move the state
+ * to AP_STATE_UNBOUND to signal that this queue
+ * is not used by any driver currently.
+ */
spin_lock_bh(&aq->lock);
ap_zapq(aq->qid);
aq->state = AP_STATE_UNBOUND;
spin_unlock_bh(&aq->lock);
}
-EXPORT_SYMBOL(ap_queue_remove);
void ap_queue_reinit_state(struct ap_queue *aq)
{
@@ -760,4 +777,3 @@ void ap_queue_reinit_state(struct ap_queue *aq)
ap_wait(ap_sm_event(aq, AP_EVENT_POLL));
spin_unlock_bh(&aq->lock);
}
-EXPORT_SYMBOL(ap_queue_reinit_state);
diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c
index eb93c2d27d0a..689c2af7026a 100644
--- a/drivers/s390/crypto/zcrypt_api.c
+++ b/drivers/s390/crypto/zcrypt_api.c
@@ -586,6 +586,7 @@ static inline bool zcrypt_check_queue(struct ap_perms *perms, int queue)
static inline struct zcrypt_queue *zcrypt_pick_queue(struct zcrypt_card *zc,
struct zcrypt_queue *zq,
+ struct module **pmod,
unsigned int weight)
{
if (!zq || !try_module_get(zq->queue->ap_dev.drv->driver.owner))
@@ -595,15 +596,15 @@ static inline struct zcrypt_queue *zcrypt_pick_queue(struct zcrypt_card *zc,
atomic_add(weight, &zc->load);
atomic_add(weight, &zq->load);
zq->request_count++;
+ *pmod = zq->queue->ap_dev.drv->driver.owner;
return zq;
}
static inline void zcrypt_drop_queue(struct zcrypt_card *zc,
struct zcrypt_queue *zq,
+ struct module *mod,
unsigned int weight)
{
- struct module *mod = zq->queue->ap_dev.drv->driver.owner;
-
zq->request_count--;
atomic_sub(weight, &zc->load);
atomic_sub(weight, &zq->load);
@@ -653,6 +654,7 @@ static long zcrypt_rsa_modexpo(struct ap_perms *perms,
unsigned int weight, pref_weight;
unsigned int func_code;
int qid = 0, rc = -ENODEV;
+ struct module *mod;
trace_s390_zcrypt_req(mex, TP_ICARSAMODEXPO);
@@ -706,7 +708,7 @@ static long zcrypt_rsa_modexpo(struct ap_perms *perms,
pref_weight = weight;
}
}
- pref_zq = zcrypt_pick_queue(pref_zc, pref_zq, weight);
+ pref_zq = zcrypt_pick_queue(pref_zc, pref_zq, &mod, weight);
spin_unlock(&zcrypt_list_lock);
if (!pref_zq) {
@@ -718,7 +720,7 @@ static long zcrypt_rsa_modexpo(struct ap_perms *perms,
rc = pref_zq->ops->rsa_modexpo(pref_zq, mex);
spin_lock(&zcrypt_list_lock);
- zcrypt_drop_queue(pref_zc, pref_zq, weight);
+ zcrypt_drop_queue(pref_zc, pref_zq, mod, weight);
spin_unlock(&zcrypt_list_lock);
out:
@@ -735,6 +737,7 @@ static long zcrypt_rsa_crt(struct ap_perms *perms,
unsigned int weight, pref_weight;
unsigned int func_code;
int qid = 0, rc = -ENODEV;
+ struct module *mod;
trace_s390_zcrypt_req(crt, TP_ICARSACRT);
@@ -788,7 +791,7 @@ static long zcrypt_rsa_crt(struct ap_perms *perms,
pref_weight = weight;
}
}
- pref_zq = zcrypt_pick_queue(pref_zc, pref_zq, weight);
+ pref_zq = zcrypt_pick_queue(pref_zc, pref_zq, &mod, weight);
spin_unlock(&zcrypt_list_lock);
if (!pref_zq) {
@@ -800,7 +803,7 @@ static long zcrypt_rsa_crt(struct ap_perms *perms,
rc = pref_zq->ops->rsa_modexpo_crt(pref_zq, crt);
spin_lock(&zcrypt_list_lock);
- zcrypt_drop_queue(pref_zc, pref_zq, weight);
+ zcrypt_drop_queue(pref_zc, pref_zq, mod, weight);
spin_unlock(&zcrypt_list_lock);
out:
@@ -819,6 +822,7 @@ static long _zcrypt_send_cprb(struct ap_perms *perms,
unsigned int func_code;
unsigned short *domain;
int qid = 0, rc = -ENODEV;
+ struct module *mod;
trace_s390_zcrypt_req(xcRB, TB_ZSECSENDCPRB);
@@ -865,7 +869,7 @@ static long _zcrypt_send_cprb(struct ap_perms *perms,
pref_weight = weight;
}
}
- pref_zq = zcrypt_pick_queue(pref_zc, pref_zq, weight);
+ pref_zq = zcrypt_pick_queue(pref_zc, pref_zq, &mod, weight);
spin_unlock(&zcrypt_list_lock);
if (!pref_zq) {
@@ -881,7 +885,7 @@ static long _zcrypt_send_cprb(struct ap_perms *perms,
rc = pref_zq->ops->send_cprb(pref_zq, xcRB, &ap_msg);
spin_lock(&zcrypt_list_lock);
- zcrypt_drop_queue(pref_zc, pref_zq, weight);
+ zcrypt_drop_queue(pref_zc, pref_zq, mod, weight);
spin_unlock(&zcrypt_list_lock);
out:
@@ -932,6 +936,7 @@ static long zcrypt_send_ep11_cprb(struct ap_perms *perms,
unsigned int func_code;
struct ap_message ap_msg;
int qid = 0, rc = -ENODEV;
+ struct module *mod;
trace_s390_zcrypt_req(xcrb, TP_ZSENDEP11CPRB);
@@ -1000,7 +1005,7 @@ static long zcrypt_send_ep11_cprb(struct ap_perms *perms,
pref_weight = weight;
}
}
- pref_zq = zcrypt_pick_queue(pref_zc, pref_zq, weight);
+ pref_zq = zcrypt_pick_queue(pref_zc, pref_zq, &mod, weight);
spin_unlock(&zcrypt_list_lock);
if (!pref_zq) {
@@ -1012,7 +1017,7 @@ static long zcrypt_send_ep11_cprb(struct ap_perms *perms,
rc = pref_zq->ops->send_ep11_cprb(pref_zq, xcrb, &ap_msg);
spin_lock(&zcrypt_list_lock);
- zcrypt_drop_queue(pref_zc, pref_zq, weight);
+ zcrypt_drop_queue(pref_zc, pref_zq, mod, weight);
spin_unlock(&zcrypt_list_lock);
out_free:
@@ -1033,6 +1038,7 @@ static long zcrypt_rng(char *buffer)
struct ap_message ap_msg;
unsigned int domain;
int qid = 0, rc = -ENODEV;
+ struct module *mod;
trace_s390_zcrypt_req(buffer, TP_HWRNGCPRB);
@@ -1064,7 +1070,7 @@ static long zcrypt_rng(char *buffer)
pref_weight = weight;
}
}
- pref_zq = zcrypt_pick_queue(pref_zc, pref_zq, weight);
+ pref_zq = zcrypt_pick_queue(pref_zc, pref_zq, &mod, weight);
spin_unlock(&zcrypt_list_lock);
if (!pref_zq) {
@@ -1076,7 +1082,7 @@ static long zcrypt_rng(char *buffer)
rc = pref_zq->ops->rng(pref_zq, buffer, &ap_msg);
spin_lock(&zcrypt_list_lock);
- zcrypt_drop_queue(pref_zc, pref_zq, weight);
+ zcrypt_drop_queue(pref_zc, pref_zq, mod, weight);
spin_unlock(&zcrypt_list_lock);
out:
diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c
index 197b0f5b63e7..44bd6f04c145 100644
--- a/drivers/s390/net/qeth_core_main.c
+++ b/drivers/s390/net/qeth_core_main.c
@@ -1150,13 +1150,16 @@ static void qeth_notify_skbs(struct qeth_qdio_out_q *q,
static void qeth_release_skbs(struct qeth_qdio_out_buffer *buf)
{
+ struct sk_buff *skb;
+
/* release may never happen from within CQ tasklet scope */
WARN_ON_ONCE(atomic_read(&buf->state) == QETH_QDIO_BUF_IN_CQ);
if (atomic_read(&buf->state) == QETH_QDIO_BUF_PENDING)
qeth_notify_skbs(buf->q, buf, TX_NOTIFY_GENERALERROR);
- __skb_queue_purge(&buf->skb_list);
+ while ((skb = __skb_dequeue(&buf->skb_list)) != NULL)
+ consume_skb(skb);
}
static void qeth_clear_output_buffer(struct qeth_qdio_out_q *queue,
diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c
index 8efb2e8ff8f4..c3067fd3bd9e 100644
--- a/drivers/s390/net/qeth_l2_main.c
+++ b/drivers/s390/net/qeth_l2_main.c
@@ -629,8 +629,7 @@ static netdev_tx_t qeth_l2_hard_start_xmit(struct sk_buff *skb,
} /* else fall through */
QETH_TXQ_STAT_INC(queue, tx_dropped);
- QETH_TXQ_STAT_INC(queue, tx_errors);
- dev_kfree_skb_any(skb);
+ kfree_skb(skb);
netif_wake_queue(dev);
return NETDEV_TX_OK;
}
@@ -645,6 +644,8 @@ static int qeth_l2_probe_device(struct ccwgroup_device *gdev)
struct qeth_card *card = dev_get_drvdata(&gdev->dev);
int rc;
+ qeth_l2_vnicc_set_defaults(card);
+
if (gdev->dev.type == &qeth_generic_devtype) {
rc = qeth_l2_create_device_attributes(&gdev->dev);
if (rc)
@@ -652,8 +653,6 @@ static int qeth_l2_probe_device(struct ccwgroup_device *gdev)
}
hash_init(card->mac_htable);
- card->info.hwtrap = 0;
- qeth_l2_vnicc_set_defaults(card);
return 0;
}
diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c
index 7e68d9d16859..53712cf26406 100644
--- a/drivers/s390/net/qeth_l3_main.c
+++ b/drivers/s390/net/qeth_l3_main.c
@@ -2096,8 +2096,7 @@ static netdev_tx_t qeth_l3_hard_start_xmit(struct sk_buff *skb,
tx_drop:
QETH_TXQ_STAT_INC(queue, tx_dropped);
- QETH_TXQ_STAT_INC(queue, tx_errors);
- dev_kfree_skb_any(skb);
+ kfree_skb(skb);
netif_wake_queue(dev);
return NETDEV_TX_OK;
}
@@ -2253,14 +2252,15 @@ static int qeth_l3_probe_device(struct ccwgroup_device *gdev)
struct qeth_card *card = dev_get_drvdata(&gdev->dev);
int rc;
+ hash_init(card->ip_htable);
+
if (gdev->dev.type == &qeth_generic_devtype) {
rc = qeth_l3_create_device_attributes(&gdev->dev);
if (rc)
return rc;
}
- hash_init(card->ip_htable);
+
hash_init(card->ip_mc_htable);
- card->info.hwtrap = 0;
return 0;
}
diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c
index 744a64680d5b..e8fc28dba8df 100644
--- a/drivers/s390/scsi/zfcp_erp.c
+++ b/drivers/s390/scsi/zfcp_erp.c
@@ -624,6 +624,20 @@ static void zfcp_erp_strategy_memwait(struct zfcp_erp_action *erp_action)
add_timer(&erp_action->timer);
}
+void zfcp_erp_port_forced_reopen_all(struct zfcp_adapter *adapter,
+ int clear, char *dbftag)
+{
+ unsigned long flags;
+ struct zfcp_port *port;
+
+ write_lock_irqsave(&adapter->erp_lock, flags);
+ read_lock(&adapter->port_list_lock);
+ list_for_each_entry(port, &adapter->port_list, list)
+ _zfcp_erp_port_forced_reopen(port, clear, dbftag);
+ read_unlock(&adapter->port_list_lock);
+ write_unlock_irqrestore(&adapter->erp_lock, flags);
+}
+
static void _zfcp_erp_port_reopen_all(struct zfcp_adapter *adapter,
int clear, char *dbftag)
{
@@ -1341,6 +1355,9 @@ static void zfcp_erp_try_rport_unblock(struct zfcp_port *port)
struct zfcp_scsi_dev *zsdev = sdev_to_zfcp(sdev);
int lun_status;
+ if (sdev->sdev_state == SDEV_DEL ||
+ sdev->sdev_state == SDEV_CANCEL)
+ continue;
if (zsdev->port != port)
continue;
/* LUN under port of interest */
diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h
index 3fce47b0b21b..c6acca521ffe 100644
--- a/drivers/s390/scsi/zfcp_ext.h
+++ b/drivers/s390/scsi/zfcp_ext.h
@@ -70,6 +70,8 @@ extern void zfcp_erp_port_reopen(struct zfcp_port *port, int clear,
char *dbftag);
extern void zfcp_erp_port_shutdown(struct zfcp_port *, int, char *);
extern void zfcp_erp_port_forced_reopen(struct zfcp_port *, int, char *);
+extern void zfcp_erp_port_forced_reopen_all(struct zfcp_adapter *adapter,
+ int clear, char *dbftag);
extern void zfcp_erp_set_lun_status(struct scsi_device *, u32);
extern void zfcp_erp_clear_lun_status(struct scsi_device *, u32);
extern void zfcp_erp_lun_reopen(struct scsi_device *, int, char *);
diff --git a/drivers/s390/scsi/zfcp_fc.c b/drivers/s390/scsi/zfcp_fc.c
index db00b5e3abbe..33eddb02ee30 100644
--- a/drivers/s390/scsi/zfcp_fc.c
+++ b/drivers/s390/scsi/zfcp_fc.c
@@ -239,10 +239,6 @@ static void _zfcp_fc_incoming_rscn(struct zfcp_fsf_req *fsf_req, u32 range,
list_for_each_entry(port, &adapter->port_list, list) {
if ((port->d_id & range) == (ntoh24(page->rscn_fid) & range))
zfcp_fc_test_link(port);
- if (!port->d_id)
- zfcp_erp_port_reopen(port,
- ZFCP_STATUS_COMMON_ERP_FAILED,
- "fcrscn1");
}
read_unlock_irqrestore(&adapter->port_list_lock, flags);
}
@@ -250,6 +246,7 @@ static void _zfcp_fc_incoming_rscn(struct zfcp_fsf_req *fsf_req, u32 range,
static void zfcp_fc_incoming_rscn(struct zfcp_fsf_req *fsf_req)
{
struct fsf_status_read_buffer *status_buffer = (void *)fsf_req->data;
+ struct zfcp_adapter *adapter = fsf_req->adapter;
struct fc_els_rscn *head;
struct fc_els_rscn_page *page;
u16 i;
@@ -263,6 +260,22 @@ static void zfcp_fc_incoming_rscn(struct zfcp_fsf_req *fsf_req)
no_entries = be16_to_cpu(head->rscn_plen) /
sizeof(struct fc_els_rscn_page);
+ if (no_entries > 1) {
+ /* handle failed ports */
+ unsigned long flags;
+ struct zfcp_port *port;
+
+ read_lock_irqsave(&adapter->port_list_lock, flags);
+ list_for_each_entry(port, &adapter->port_list, list) {
+ if (port->d_id)
+ continue;
+ zfcp_erp_port_reopen(port,
+ ZFCP_STATUS_COMMON_ERP_FAILED,
+ "fcrscn1");
+ }
+ read_unlock_irqrestore(&adapter->port_list_lock, flags);
+ }
+
for (i = 1; i < no_entries; i++) {
/* skip head and start with 1st element */
page++;
diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c
index f4f6a07c5222..221d0dfb8493 100644
--- a/drivers/s390/scsi/zfcp_scsi.c
+++ b/drivers/s390/scsi/zfcp_scsi.c
@@ -368,6 +368,10 @@ static int zfcp_scsi_eh_host_reset_handler(struct scsi_cmnd *scpnt)
struct zfcp_adapter *adapter = zfcp_sdev->port->adapter;
int ret = SUCCESS, fc_ret;
+ if (!(adapter->connection_features & FSF_FEATURE_NPIV_MODE)) {
+ zfcp_erp_port_forced_reopen_all(adapter, 0, "schrh_p");
+ zfcp_erp_wait(adapter);
+ }
zfcp_erp_adapter_reopen(adapter, 0, "schrh_1");
zfcp_erp_wait(adapter);
fc_ret = fc_block_scsi_eh(scpnt);
diff --git a/drivers/s390/virtio/virtio_ccw.c b/drivers/s390/virtio/virtio_ccw.c
index ae1d56da671d..74c328321889 100644
--- a/drivers/s390/virtio/virtio_ccw.c
+++ b/drivers/s390/virtio/virtio_ccw.c
@@ -272,6 +272,8 @@ static void virtio_ccw_drop_indicators(struct virtio_ccw_device *vcdev)
{
struct virtio_ccw_vq_info *info;
+ if (!vcdev->airq_info)
+ return;
list_for_each_entry(info, &vcdev->virtqueues, node)
drop_airq_indicator(info->vq, vcdev->airq_info);
}
@@ -413,7 +415,7 @@ static int virtio_ccw_read_vq_conf(struct virtio_ccw_device *vcdev,
ret = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_READ_VQ_CONF);
if (ret)
return ret;
- return vcdev->config_block->num;
+ return vcdev->config_block->num ?: -ENOENT;
}
static void virtio_ccw_del_vq(struct virtqueue *vq, struct ccw1 *ccw)
@@ -973,6 +975,13 @@ static void virtio_ccw_set_status(struct virtio_device *vdev, u8 status)
kfree(ccw);
}
+static const char *virtio_ccw_bus_name(struct virtio_device *vdev)
+{
+ struct virtio_ccw_device *vcdev = to_vc_device(vdev);
+
+ return dev_name(&vcdev->cdev->dev);
+}
+
static const struct virtio_config_ops virtio_ccw_config_ops = {
.get_features = virtio_ccw_get_features,
.finalize_features = virtio_ccw_finalize_features,
@@ -983,6 +992,7 @@ static const struct virtio_config_ops virtio_ccw_config_ops = {
.reset = virtio_ccw_reset,
.find_vqs = virtio_ccw_find_vqs,
.del_vqs = virtio_ccw_del_vqs,
+ .bus_name = virtio_ccw_bus_name,
};
diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
index 8f9d9e9fa695..d528018e6fa8 100644
--- a/drivers/scsi/Kconfig
+++ b/drivers/scsi/Kconfig
@@ -665,7 +665,7 @@ config SCSI_DMX3191D
config SCSI_GDTH
tristate "Intel/ICP (former GDT SCSI Disk Array) RAID Controller support"
- depends on (ISA || EISA || PCI) && SCSI && ISA_DMA_API
+ depends on PCI && SCSI
---help---
Formerly called GDT SCSI Disk Array Controller Support.
@@ -1196,8 +1196,6 @@ config SCSI_AM53C974
PCscsi/PCnet (Am53/79C974) solutions.
This is a new implementation base on the generic esp_scsi driver.
- Documentation can be found in <file:Documentation/scsi/tmscsim.txt>.
-
Note that this driver does NOT support Tekram DC390W/U/F, which are
based on NCR/Symbios chips. Use "NCR53C8XX SCSI support" for those.
@@ -1517,6 +1515,4 @@ source "drivers/scsi/pcmcia/Kconfig"
source "drivers/scsi/device_handler/Kconfig"
-source "drivers/scsi/osd/Kconfig"
-
endmenu
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
index fcb41ae329c4..8826111fdf4a 100644
--- a/drivers/scsi/Makefile
+++ b/drivers/scsi/Makefile
@@ -150,7 +150,6 @@ obj-$(CONFIG_CHR_DEV_SG) += sg.o
obj-$(CONFIG_CHR_DEV_SCH) += ch.o
obj-$(CONFIG_SCSI_ENCLOSURE) += ses.o
-obj-$(CONFIG_SCSI_OSD_INITIATOR) += osd/
obj-$(CONFIG_SCSI_HISI_SAS) += hisi_sas/
# This goes last, so that "real" scsi devices probe earlier
diff --git a/drivers/scsi/aacraid/Makefile b/drivers/scsi/aacraid/Makefile
index 1bd9fd18f7f3..3893b95b140b 100644
--- a/drivers/scsi/aacraid/Makefile
+++ b/drivers/scsi/aacraid/Makefile
@@ -4,5 +4,3 @@ obj-$(CONFIG_SCSI_AACRAID) := aacraid.o
aacraid-objs := linit.o aachba.o commctrl.o comminit.o commsup.o \
dpcsup.o rx.o sa.o rkt.o nark.o src.o
-
-ccflags-y := -Idrivers/scsi
diff --git a/drivers/scsi/aacraid/aachba.c b/drivers/scsi/aacraid/aachba.c
index 75ab5ff6b78c..6085aa087a2f 100644
--- a/drivers/scsi/aacraid/aachba.c
+++ b/drivers/scsi/aacraid/aachba.c
@@ -3455,7 +3455,7 @@ static int delete_disk(struct aac_dev *dev, void __user *arg)
}
}
-int aac_dev_ioctl(struct aac_dev *dev, int cmd, void __user *arg)
+int aac_dev_ioctl(struct aac_dev *dev, unsigned int cmd, void __user *arg)
{
switch (cmd) {
case FSACTL_QUERY_DISK:
diff --git a/drivers/scsi/aacraid/aacraid.h b/drivers/scsi/aacraid/aacraid.h
index 3291d1c16864..11fb68d7e60d 100644
--- a/drivers/scsi/aacraid/aacraid.h
+++ b/drivers/scsi/aacraid/aacraid.h
@@ -2640,9 +2640,14 @@ static inline unsigned int cap_to_cyls(sector_t capacity, unsigned divisor)
return capacity;
}
+static inline int aac_pci_offline(struct aac_dev *dev)
+{
+ return pci_channel_offline(dev->pdev) || dev->handle_pci_error;
+}
+
static inline int aac_adapter_check_health(struct aac_dev *dev)
{
- if (unlikely(pci_channel_offline(dev->pdev)))
+ if (unlikely(aac_pci_offline(dev)))
return -1;
return (dev)->a_ops.adapter_check_health(dev);
@@ -2706,12 +2711,12 @@ void aac_set_intx_mode(struct aac_dev *dev);
int aac_get_config_status(struct aac_dev *dev, int commit_flag);
int aac_get_containers(struct aac_dev *dev);
int aac_scsi_cmd(struct scsi_cmnd *cmd);
-int aac_dev_ioctl(struct aac_dev *dev, int cmd, void __user *arg);
+int aac_dev_ioctl(struct aac_dev *dev, unsigned int cmd, void __user *arg);
#ifndef shost_to_class
#define shost_to_class(shost) &shost->shost_dev
#endif
ssize_t aac_get_serial_number(struct device *dev, char *buf);
-int aac_do_ioctl(struct aac_dev * dev, int cmd, void __user *arg);
+int aac_do_ioctl(struct aac_dev *dev, unsigned int cmd, void __user *arg);
int aac_rx_init(struct aac_dev *dev);
int aac_rkt_init(struct aac_dev *dev);
int aac_nark_init(struct aac_dev *dev);
diff --git a/drivers/scsi/aacraid/commctrl.c b/drivers/scsi/aacraid/commctrl.c
index e2899ff7913e..f0ff40332753 100644
--- a/drivers/scsi/aacraid/commctrl.c
+++ b/drivers/scsi/aacraid/commctrl.c
@@ -1060,7 +1060,7 @@ static int aac_send_reset_adapter(struct aac_dev *dev, void __user *arg)
return retval;
}
-int aac_do_ioctl(struct aac_dev * dev, int cmd, void __user *arg)
+int aac_do_ioctl(struct aac_dev *dev, unsigned int cmd, void __user *arg)
{
int status;
diff --git a/drivers/scsi/aacraid/commsup.c b/drivers/scsi/aacraid/commsup.c
index d5a6aa9676c8..78430a7b294c 100644
--- a/drivers/scsi/aacraid/commsup.c
+++ b/drivers/scsi/aacraid/commsup.c
@@ -672,7 +672,7 @@ int aac_fib_send(u16 command, struct fib *fibptr, unsigned long size,
return -ETIMEDOUT;
}
- if (unlikely(pci_channel_offline(dev->pdev)))
+ if (unlikely(aac_pci_offline(dev)))
return -EFAULT;
if ((blink = aac_adapter_check_health(dev)) > 0) {
@@ -772,7 +772,7 @@ int aac_hba_send(u8 command, struct fib *fibptr, fib_callback callback,
spin_unlock_irqrestore(&fibptr->event_lock, flags);
- if (unlikely(pci_channel_offline(dev->pdev)))
+ if (unlikely(aac_pci_offline(dev)))
return -EFAULT;
fibptr->flags |= FIB_CONTEXT_FLAG_WAIT;
@@ -1303,8 +1303,9 @@ static void aac_handle_aif(struct aac_dev * dev, struct fib * fibptr)
ADD : DELETE;
break;
}
- case AifBuManagerEvent:
- aac_handle_aif_bu(dev, aifcmd);
+ break;
+ case AifBuManagerEvent:
+ aac_handle_aif_bu(dev, aifcmd);
break;
}
@@ -1376,18 +1377,19 @@ static void aac_handle_aif(struct aac_dev * dev, struct fib * fibptr)
container = 0;
retry_next:
- if (device_config_needed == NOTHING)
- for (; container < dev->maximum_num_containers; ++container) {
- if ((dev->fsa_dev[container].config_waiting_on == 0) &&
- (dev->fsa_dev[container].config_needed != NOTHING) &&
- time_before(jiffies, dev->fsa_dev[container].config_waiting_stamp + AIF_SNIFF_TIMEOUT)) {
- device_config_needed =
- dev->fsa_dev[container].config_needed;
- dev->fsa_dev[container].config_needed = NOTHING;
- channel = CONTAINER_TO_CHANNEL(container);
- id = CONTAINER_TO_ID(container);
- lun = CONTAINER_TO_LUN(container);
- break;
+ if (device_config_needed == NOTHING) {
+ for (; container < dev->maximum_num_containers; ++container) {
+ if ((dev->fsa_dev[container].config_waiting_on == 0) &&
+ (dev->fsa_dev[container].config_needed != NOTHING) &&
+ time_before(jiffies, dev->fsa_dev[container].config_waiting_stamp + AIF_SNIFF_TIMEOUT)) {
+ device_config_needed =
+ dev->fsa_dev[container].config_needed;
+ dev->fsa_dev[container].config_needed = NOTHING;
+ channel = CONTAINER_TO_CHANNEL(container);
+ id = CONTAINER_TO_ID(container);
+ lun = CONTAINER_TO_LUN(container);
+ break;
+ }
}
}
if (device_config_needed == NOTHING)
diff --git a/drivers/scsi/aacraid/linit.c b/drivers/scsi/aacraid/linit.c
index 7e56a11836c1..8e28a505f7e8 100644
--- a/drivers/scsi/aacraid/linit.c
+++ b/drivers/scsi/aacraid/linit.c
@@ -413,13 +413,16 @@ static int aac_slave_configure(struct scsi_device *sdev)
if (chn < AAC_MAX_BUSES && tid < AAC_MAX_TARGETS && aac->sa_firmware) {
devtype = aac->hba_map[chn][tid].devtype;
- if (devtype == AAC_DEVTYPE_NATIVE_RAW)
+ if (devtype == AAC_DEVTYPE_NATIVE_RAW) {
depth = aac->hba_map[chn][tid].qd_limit;
- else if (devtype == AAC_DEVTYPE_ARC_RAW)
+ set_timeout = 1;
+ goto common_config;
+ }
+ if (devtype == AAC_DEVTYPE_ARC_RAW) {
set_qd_dev_type = true;
-
- set_timeout = 1;
- goto common_config;
+ set_timeout = 1;
+ goto common_config;
+ }
}
if (aac->jbod && (sdev->type == TYPE_DISK))
@@ -616,7 +619,8 @@ static struct device_attribute *aac_dev_attrs[] = {
NULL,
};
-static int aac_ioctl(struct scsi_device *sdev, int cmd, void __user * arg)
+static int aac_ioctl(struct scsi_device *sdev, unsigned int cmd,
+ void __user *arg)
{
struct aac_dev *dev = (struct aac_dev *)sdev->host->hostdata;
if (!capable(CAP_SYS_RAWIO))
@@ -852,8 +856,7 @@ static u8 aac_eh_tmf_hard_reset_fib(struct aac_hba_map_info *info,
address = (u64)fib->hw_error_pa;
rst->error_ptr_hi = cpu_to_le32((u32)(address >> 32));
- rst->error_ptr_lo = cpu_to_le32
- ((u32)(address & 0xffffffff));
+ rst->error_ptr_lo = cpu_to_le32((u32)(address & 0xffffffff));
rst->error_length = cpu_to_le32(FW_ERROR_BUFFER_SIZE);
fib->hbacmd_size = sizeof(*rst);
@@ -1206,7 +1209,8 @@ static long aac_compat_do_ioctl(struct aac_dev *dev, unsigned cmd, unsigned long
return ret;
}
-static int aac_compat_ioctl(struct scsi_device *sdev, int cmd, void __user *arg)
+static int aac_compat_ioctl(struct scsi_device *sdev, unsigned int cmd,
+ void __user *arg)
{
struct aac_dev *dev = (struct aac_dev *)sdev->host->hostdata;
if (!capable(CAP_SYS_RAWIO))
diff --git a/drivers/scsi/aacraid/src.c b/drivers/scsi/aacraid/src.c
index 8377aec0649d..97bb9e9d201c 100644
--- a/drivers/scsi/aacraid/src.c
+++ b/drivers/scsi/aacraid/src.c
@@ -1157,7 +1157,7 @@ out:
dev_err(&dev->pdev->dev, "%s: %s status = %d", __func__,
state_str[state], rc);
-return rc;
+ return rc;
}
/**
* aac_srcv_init - initialize an SRCv card
diff --git a/drivers/scsi/aic7xxx/Makefile b/drivers/scsi/aic7xxx/Makefile
index c15be2590d1c..e0188ecd85b2 100644
--- a/drivers/scsi/aic7xxx/Makefile
+++ b/drivers/scsi/aic7xxx/Makefile
@@ -34,7 +34,6 @@ aic79xx-y += aic79xx_osm.o \
aic79xx_proc.o \
aic79xx_osm_pci.o
-ccflags-y += -Idrivers/scsi
ifdef WARNINGS_BECOME_ERRORS
ccflags-y += -Werror
endif
diff --git a/drivers/scsi/aic7xxx/aic79xx_core.c b/drivers/scsi/aic7xxx/aic79xx_core.c
index 9ee75c9a9aa1..7e5044bf05c0 100644
--- a/drivers/scsi/aic7xxx/aic79xx_core.c
+++ b/drivers/scsi/aic7xxx/aic79xx_core.c
@@ -2285,6 +2285,7 @@ ahd_handle_seqint(struct ahd_softc *ahd, u_int intstat)
switch (scb->hscb->task_management) {
case SIU_TASKMGMT_ABORT_TASK:
tag = SCB_GET_TAG(scb);
+ /* fall through */
case SIU_TASKMGMT_ABORT_TASK_SET:
case SIU_TASKMGMT_CLEAR_TASK_SET:
lun = scb->hscb->lun;
@@ -2295,6 +2296,7 @@ ahd_handle_seqint(struct ahd_softc *ahd, u_int intstat)
break;
case SIU_TASKMGMT_LUN_RESET:
lun = scb->hscb->lun;
+ /* fall through */
case SIU_TASKMGMT_TARGET_RESET:
{
struct ahd_devinfo devinfo;
@@ -6550,8 +6552,8 @@ ahd_fini_scbdata(struct ahd_softc *ahd)
kfree(sns_map);
}
ahd_dma_tag_destroy(ahd, scb_data->sense_dmat);
- /* FALLTHROUGH */
}
+ /* fall through */
case 6:
{
struct map_node *sg_map;
@@ -6565,8 +6567,8 @@ ahd_fini_scbdata(struct ahd_softc *ahd)
kfree(sg_map);
}
ahd_dma_tag_destroy(ahd, scb_data->sg_dmat);
- /* FALLTHROUGH */
}
+ /* fall through */
case 5:
{
struct map_node *hscb_map;
@@ -7209,6 +7211,7 @@ ahd_init(struct ahd_softc *ahd)
case FLX_CSTAT_OVER:
case FLX_CSTAT_UNDER:
warn_user++;
+ /* fall through */
case FLX_CSTAT_INVALID:
case FLX_CSTAT_OKAY:
if (warn_user == 0 && bootverbose == 0)
@@ -8413,7 +8416,7 @@ ahd_search_scb_list(struct ahd_softc *ahd, int target, char channel,
if ((scb->flags & SCB_ACTIVE) == 0)
printk("Inactive SCB in Waiting List\n");
ahd_done_with_status(ahd, scb, status);
- /* FALLTHROUGH */
+ /* fall through */
case SEARCH_REMOVE:
ahd_rem_wscb(ahd, scbid, prev, next, tid);
*list_tail = prev;
@@ -8422,6 +8425,7 @@ ahd_search_scb_list(struct ahd_softc *ahd, int target, char channel,
break;
case SEARCH_PRINT:
printk("0x%x ", scbid);
+ /* fall through */
case SEARCH_COUNT:
prev = scbid;
break;
@@ -9547,8 +9551,8 @@ ahd_download_instr(struct ahd_softc *ahd, u_int instrptr, uint8_t *dconsts)
{
fmt3_ins = &instr.format3;
fmt3_ins->address = ahd_resolve_seqaddr(ahd, fmt3_ins->address);
- /* FALLTHROUGH */
}
+ /* fall through */
case AIC_OP_OR:
case AIC_OP_AND:
case AIC_OP_XOR:
@@ -9559,7 +9563,7 @@ ahd_download_instr(struct ahd_softc *ahd, u_int instrptr, uint8_t *dconsts)
fmt1_ins->immediate = dconsts[fmt1_ins->immediate];
}
fmt1_ins->parity = 0;
- /* FALLTHROUGH */
+ /* fall through */
case AIC_OP_ROL:
{
int i, count;
diff --git a/drivers/scsi/arcmsr/arcmsr.h b/drivers/scsi/arcmsr/arcmsr.h
index 9c397a2794d6..9220bcf8388f 100644
--- a/drivers/scsi/arcmsr/arcmsr.h
+++ b/drivers/scsi/arcmsr/arcmsr.h
@@ -49,7 +49,7 @@ struct device_attribute;
#define ARCMSR_MAX_OUTSTANDING_CMD 1024
#define ARCMSR_DEFAULT_OUTSTANDING_CMD 128
#define ARCMSR_MIN_OUTSTANDING_CMD 32
-#define ARCMSR_DRIVER_VERSION "v1.40.00.09-20180709"
+#define ARCMSR_DRIVER_VERSION "v1.40.00.10-20190116"
#define ARCMSR_SCSI_INITIATOR_ID 255
#define ARCMSR_MAX_XFER_SECTORS 512
#define ARCMSR_MAX_XFER_SECTORS_B 4096
@@ -739,7 +739,7 @@ struct AdapterControlBlock
#define ACB_ADAPTER_TYPE_C 0x00000002 /* hbc L IOP */
#define ACB_ADAPTER_TYPE_D 0x00000003 /* hbd M IOP */
#define ACB_ADAPTER_TYPE_E 0x00000004 /* hba L IOP */
- u32 roundup_ccbsize;
+ u32 ioqueue_size;
struct pci_dev * pdev;
struct Scsi_Host * host;
unsigned long vir2phy_offset;
@@ -747,6 +747,7 @@ struct AdapterControlBlock
uint32_t outbound_int_enable;
uint32_t cdb_phyaddr_hi32;
uint32_t reg_mu_acc_handle0;
+ uint64_t cdb_phyadd_hipart;
spinlock_t eh_lock;
spinlock_t ccblist_lock;
spinlock_t postq_lock;
@@ -855,11 +856,11 @@ struct AdapterControlBlock
*******************************************************************************
*/
struct CommandControlBlock{
- /*x32:sizeof struct_CCB=(32+60)byte, x64:sizeof struct_CCB=(64+60)byte*/
+ /*x32:sizeof struct_CCB=(64+60)byte, x64:sizeof struct_CCB=(64+60)byte*/
struct list_head list; /*x32: 8byte, x64: 16byte*/
struct scsi_cmnd *pcmd; /*8 bytes pointer of linux scsi command */
struct AdapterControlBlock *acb; /*x32: 4byte, x64: 8byte*/
- uint32_t cdb_phyaddr; /*x32: 4byte, x64: 4byte*/
+ unsigned long cdb_phyaddr; /*x32: 4byte, x64: 8byte*/
uint32_t arc_cdb_size; /*x32:4byte,x64:4byte*/
uint16_t ccb_flags; /*x32: 2byte, x64: 2byte*/
#define CCB_FLAG_READ 0x0000
@@ -875,10 +876,10 @@ struct CommandControlBlock{
uint32_t smid;
#if BITS_PER_LONG == 64
/* ======================512+64 bytes======================== */
- uint32_t reserved[4]; /*16 byte*/
+ uint32_t reserved[3]; /*12 byte*/
#else
/* ======================512+32 bytes======================== */
- // uint32_t reserved; /*4 byte*/
+ uint32_t reserved[8]; /*32 byte*/
#endif
/* ======================================================= */
struct ARCMSR_CDB arcmsr_cdb;
diff --git a/drivers/scsi/arcmsr/arcmsr_hba.c b/drivers/scsi/arcmsr/arcmsr_hba.c
index 57c6fa388bf6..88053b15c363 100644
--- a/drivers/scsi/arcmsr/arcmsr_hba.c
+++ b/drivers/scsi/arcmsr/arcmsr_hba.c
@@ -91,6 +91,10 @@ static int cmd_per_lun = ARCMSR_DEFAULT_CMD_PERLUN;
module_param(cmd_per_lun, int, S_IRUGO);
MODULE_PARM_DESC(cmd_per_lun, " device queue depth(1 ~ 128), default is 32");
+static int dma_mask_64 = 0;
+module_param(dma_mask_64, int, S_IRUGO);
+MODULE_PARM_DESC(dma_mask_64, " set DMA mask to 64 bits(0 ~ 1), dma_mask_64=1(64 bits), =0(32 bits)");
+
static int set_date_time = 0;
module_param(set_date_time, int, S_IRUGO);
MODULE_PARM_DESC(set_date_time, " send date, time to iop(0 ~ 1), set_date_time=1(enable), default(=0) is disable");
@@ -223,13 +227,13 @@ static struct pci_driver arcmsr_pci_driver = {
****************************************************************************
*/
-static void arcmsr_free_mu(struct AdapterControlBlock *acb)
+static void arcmsr_free_io_queue(struct AdapterControlBlock *acb)
{
switch (acb->adapter_type) {
case ACB_ADAPTER_TYPE_B:
case ACB_ADAPTER_TYPE_D:
case ACB_ADAPTER_TYPE_E: {
- dma_free_coherent(&acb->pdev->dev, acb->roundup_ccbsize,
+ dma_free_coherent(&acb->pdev->dev, acb->ioqueue_size,
acb->dma_coherent2, acb->dma_coherent_handle2);
break;
}
@@ -576,6 +580,58 @@ static void arcmsr_flush_adapter_cache(struct AdapterControlBlock *acb)
}
}
+static void arcmsr_hbaB_assign_regAddr(struct AdapterControlBlock *acb)
+{
+ struct MessageUnit_B *reg = acb->pmuB;
+
+ if (acb->pdev->device == PCI_DEVICE_ID_ARECA_1203) {
+ reg->drv2iop_doorbell = MEM_BASE0(ARCMSR_DRV2IOP_DOORBELL_1203);
+ reg->drv2iop_doorbell_mask = MEM_BASE0(ARCMSR_DRV2IOP_DOORBELL_MASK_1203);
+ reg->iop2drv_doorbell = MEM_BASE0(ARCMSR_IOP2DRV_DOORBELL_1203);
+ reg->iop2drv_doorbell_mask = MEM_BASE0(ARCMSR_IOP2DRV_DOORBELL_MASK_1203);
+ } else {
+ reg->drv2iop_doorbell= MEM_BASE0(ARCMSR_DRV2IOP_DOORBELL);
+ reg->drv2iop_doorbell_mask = MEM_BASE0(ARCMSR_DRV2IOP_DOORBELL_MASK);
+ reg->iop2drv_doorbell = MEM_BASE0(ARCMSR_IOP2DRV_DOORBELL);
+ reg->iop2drv_doorbell_mask = MEM_BASE0(ARCMSR_IOP2DRV_DOORBELL_MASK);
+ }
+ reg->message_wbuffer = MEM_BASE1(ARCMSR_MESSAGE_WBUFFER);
+ reg->message_rbuffer = MEM_BASE1(ARCMSR_MESSAGE_RBUFFER);
+ reg->message_rwbuffer = MEM_BASE1(ARCMSR_MESSAGE_RWBUFFER);
+}
+
+static void arcmsr_hbaD_assign_regAddr(struct AdapterControlBlock *acb)
+{
+ struct MessageUnit_D *reg = acb->pmuD;
+
+ reg->chip_id = MEM_BASE0(ARCMSR_ARC1214_CHIP_ID);
+ reg->cpu_mem_config = MEM_BASE0(ARCMSR_ARC1214_CPU_MEMORY_CONFIGURATION);
+ reg->i2o_host_interrupt_mask = MEM_BASE0(ARCMSR_ARC1214_I2_HOST_INTERRUPT_MASK);
+ reg->sample_at_reset = MEM_BASE0(ARCMSR_ARC1214_SAMPLE_RESET);
+ reg->reset_request = MEM_BASE0(ARCMSR_ARC1214_RESET_REQUEST);
+ reg->host_int_status = MEM_BASE0(ARCMSR_ARC1214_MAIN_INTERRUPT_STATUS);
+ reg->pcief0_int_enable = MEM_BASE0(ARCMSR_ARC1214_PCIE_F0_INTERRUPT_ENABLE);
+ reg->inbound_msgaddr0 = MEM_BASE0(ARCMSR_ARC1214_INBOUND_MESSAGE0);
+ reg->inbound_msgaddr1 = MEM_BASE0(ARCMSR_ARC1214_INBOUND_MESSAGE1);
+ reg->outbound_msgaddr0 = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_MESSAGE0);
+ reg->outbound_msgaddr1 = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_MESSAGE1);
+ reg->inbound_doorbell = MEM_BASE0(ARCMSR_ARC1214_INBOUND_DOORBELL);
+ reg->outbound_doorbell = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_DOORBELL);
+ reg->outbound_doorbell_enable = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_DOORBELL_ENABLE);
+ reg->inboundlist_base_low = MEM_BASE0(ARCMSR_ARC1214_INBOUND_LIST_BASE_LOW);
+ reg->inboundlist_base_high = MEM_BASE0(ARCMSR_ARC1214_INBOUND_LIST_BASE_HIGH);
+ reg->inboundlist_write_pointer = MEM_BASE0(ARCMSR_ARC1214_INBOUND_LIST_WRITE_POINTER);
+ reg->outboundlist_base_low = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_LIST_BASE_LOW);
+ reg->outboundlist_base_high = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_LIST_BASE_HIGH);
+ reg->outboundlist_copy_pointer = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_LIST_COPY_POINTER);
+ reg->outboundlist_read_pointer = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_LIST_READ_POINTER);
+ reg->outboundlist_interrupt_cause = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_INTERRUPT_CAUSE);
+ reg->outboundlist_interrupt_enable = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_INTERRUPT_ENABLE);
+ reg->message_wbuffer = MEM_BASE0(ARCMSR_ARC1214_MESSAGE_WBUFFER);
+ reg->message_rbuffer = MEM_BASE0(ARCMSR_ARC1214_MESSAGE_RBUFFER);
+ reg->msgcode_rwbuffer = MEM_BASE0(ARCMSR_ARC1214_MESSAGE_RWBUFFER);
+}
+
static bool arcmsr_alloc_io_queue(struct AdapterControlBlock *acb)
{
bool rtn = true;
@@ -585,88 +641,39 @@ static bool arcmsr_alloc_io_queue(struct AdapterControlBlock *acb)
switch (acb->adapter_type) {
case ACB_ADAPTER_TYPE_B: {
- struct MessageUnit_B *reg;
- acb->roundup_ccbsize = roundup(sizeof(struct MessageUnit_B), 32);
- dma_coherent = dma_alloc_coherent(&pdev->dev,
- acb->roundup_ccbsize,
- &dma_coherent_handle,
- GFP_KERNEL);
+ acb->ioqueue_size = roundup(sizeof(struct MessageUnit_B), 32);
+ dma_coherent = dma_alloc_coherent(&pdev->dev, acb->ioqueue_size,
+ &dma_coherent_handle, GFP_KERNEL);
if (!dma_coherent) {
pr_notice("arcmsr%d: DMA allocation failed\n", acb->host->host_no);
return false;
}
acb->dma_coherent_handle2 = dma_coherent_handle;
acb->dma_coherent2 = dma_coherent;
- reg = (struct MessageUnit_B *)dma_coherent;
- acb->pmuB = reg;
- if (acb->pdev->device == PCI_DEVICE_ID_ARECA_1203) {
- reg->drv2iop_doorbell = MEM_BASE0(ARCMSR_DRV2IOP_DOORBELL_1203);
- reg->drv2iop_doorbell_mask = MEM_BASE0(ARCMSR_DRV2IOP_DOORBELL_MASK_1203);
- reg->iop2drv_doorbell = MEM_BASE0(ARCMSR_IOP2DRV_DOORBELL_1203);
- reg->iop2drv_doorbell_mask = MEM_BASE0(ARCMSR_IOP2DRV_DOORBELL_MASK_1203);
- } else {
- reg->drv2iop_doorbell = MEM_BASE0(ARCMSR_DRV2IOP_DOORBELL);
- reg->drv2iop_doorbell_mask = MEM_BASE0(ARCMSR_DRV2IOP_DOORBELL_MASK);
- reg->iop2drv_doorbell = MEM_BASE0(ARCMSR_IOP2DRV_DOORBELL);
- reg->iop2drv_doorbell_mask = MEM_BASE0(ARCMSR_IOP2DRV_DOORBELL_MASK);
- }
- reg->message_wbuffer = MEM_BASE1(ARCMSR_MESSAGE_WBUFFER);
- reg->message_rbuffer = MEM_BASE1(ARCMSR_MESSAGE_RBUFFER);
- reg->message_rwbuffer = MEM_BASE1(ARCMSR_MESSAGE_RWBUFFER);
+ acb->pmuB = (struct MessageUnit_B *)dma_coherent;
+ arcmsr_hbaB_assign_regAddr(acb);
}
break;
case ACB_ADAPTER_TYPE_D: {
- struct MessageUnit_D *reg;
-
- acb->roundup_ccbsize = roundup(sizeof(struct MessageUnit_D), 32);
- dma_coherent = dma_alloc_coherent(&pdev->dev,
- acb->roundup_ccbsize,
- &dma_coherent_handle,
- GFP_KERNEL);
+ acb->ioqueue_size = roundup(sizeof(struct MessageUnit_D), 32);
+ dma_coherent = dma_alloc_coherent(&pdev->dev, acb->ioqueue_size,
+ &dma_coherent_handle, GFP_KERNEL);
if (!dma_coherent) {
pr_notice("arcmsr%d: DMA allocation failed\n", acb->host->host_no);
return false;
}
acb->dma_coherent_handle2 = dma_coherent_handle;
acb->dma_coherent2 = dma_coherent;
- reg = (struct MessageUnit_D *)dma_coherent;
- acb->pmuD = reg;
- reg->chip_id = MEM_BASE0(ARCMSR_ARC1214_CHIP_ID);
- reg->cpu_mem_config = MEM_BASE0(ARCMSR_ARC1214_CPU_MEMORY_CONFIGURATION);
- reg->i2o_host_interrupt_mask = MEM_BASE0(ARCMSR_ARC1214_I2_HOST_INTERRUPT_MASK);
- reg->sample_at_reset = MEM_BASE0(ARCMSR_ARC1214_SAMPLE_RESET);
- reg->reset_request = MEM_BASE0(ARCMSR_ARC1214_RESET_REQUEST);
- reg->host_int_status = MEM_BASE0(ARCMSR_ARC1214_MAIN_INTERRUPT_STATUS);
- reg->pcief0_int_enable = MEM_BASE0(ARCMSR_ARC1214_PCIE_F0_INTERRUPT_ENABLE);
- reg->inbound_msgaddr0 = MEM_BASE0(ARCMSR_ARC1214_INBOUND_MESSAGE0);
- reg->inbound_msgaddr1 = MEM_BASE0(ARCMSR_ARC1214_INBOUND_MESSAGE1);
- reg->outbound_msgaddr0 = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_MESSAGE0);
- reg->outbound_msgaddr1 = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_MESSAGE1);
- reg->inbound_doorbell = MEM_BASE0(ARCMSR_ARC1214_INBOUND_DOORBELL);
- reg->outbound_doorbell = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_DOORBELL);
- reg->outbound_doorbell_enable = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_DOORBELL_ENABLE);
- reg->inboundlist_base_low = MEM_BASE0(ARCMSR_ARC1214_INBOUND_LIST_BASE_LOW);
- reg->inboundlist_base_high = MEM_BASE0(ARCMSR_ARC1214_INBOUND_LIST_BASE_HIGH);
- reg->inboundlist_write_pointer = MEM_BASE0(ARCMSR_ARC1214_INBOUND_LIST_WRITE_POINTER);
- reg->outboundlist_base_low = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_LIST_BASE_LOW);
- reg->outboundlist_base_high = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_LIST_BASE_HIGH);
- reg->outboundlist_copy_pointer = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_LIST_COPY_POINTER);
- reg->outboundlist_read_pointer = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_LIST_READ_POINTER);
- reg->outboundlist_interrupt_cause = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_INTERRUPT_CAUSE);
- reg->outboundlist_interrupt_enable = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_INTERRUPT_ENABLE);
- reg->message_wbuffer = MEM_BASE0(ARCMSR_ARC1214_MESSAGE_WBUFFER);
- reg->message_rbuffer = MEM_BASE0(ARCMSR_ARC1214_MESSAGE_RBUFFER);
- reg->msgcode_rwbuffer = MEM_BASE0(ARCMSR_ARC1214_MESSAGE_RWBUFFER);
+ acb->pmuD = (struct MessageUnit_D *)dma_coherent;
+ arcmsr_hbaD_assign_regAddr(acb);
}
break;
case ACB_ADAPTER_TYPE_E: {
uint32_t completeQ_size;
completeQ_size = sizeof(struct deliver_completeQ) * ARCMSR_MAX_HBE_DONEQUEUE + 128;
- acb->roundup_ccbsize = roundup(completeQ_size, 32);
- dma_coherent = dma_alloc_coherent(&pdev->dev,
- acb->roundup_ccbsize,
- &dma_coherent_handle,
- GFP_KERNEL);
+ acb->ioqueue_size = roundup(completeQ_size, 32);
+ dma_coherent = dma_alloc_coherent(&pdev->dev, acb->ioqueue_size,
+ &dma_coherent_handle, GFP_KERNEL);
if (!dma_coherent){
pr_notice("arcmsr%d: DMA allocation failed\n", acb->host->host_no);
return false;
@@ -674,7 +681,7 @@ static bool arcmsr_alloc_io_queue(struct AdapterControlBlock *acb)
acb->dma_coherent_handle2 = dma_coherent_handle;
acb->dma_coherent2 = dma_coherent;
acb->pCompletionQ = dma_coherent;
- acb->completionQ_entry = acb->roundup_ccbsize / sizeof(struct deliver_completeQ);
+ acb->completionQ_entry = acb->ioqueue_size / sizeof(struct deliver_completeQ);
acb->doneq_index = 0;
}
break;
@@ -691,11 +698,11 @@ static int arcmsr_alloc_ccb_pool(struct AdapterControlBlock *acb)
dma_addr_t dma_coherent_handle;
struct CommandControlBlock *ccb_tmp;
int i = 0, j = 0;
- dma_addr_t cdb_phyaddr;
+ unsigned long cdb_phyaddr, next_ccb_phy;
unsigned long roundup_ccbsize;
unsigned long max_xfer_len;
unsigned long max_sg_entrys;
- uint32_t firm_config_version;
+ uint32_t firm_config_version, curr_phy_upper32;
for (i = 0; i < ARCMSR_MAX_TARGETID; i++)
for (j = 0; j < ARCMSR_MAX_TARGETLUN; j++)
@@ -712,6 +719,7 @@ static int arcmsr_alloc_ccb_pool(struct AdapterControlBlock *acb)
acb->host->sg_tablesize = max_sg_entrys;
roundup_ccbsize = roundup(sizeof(struct CommandControlBlock) + (max_sg_entrys - 1) * sizeof(struct SG64ENTRY), 32);
acb->uncache_size = roundup_ccbsize * acb->maxFreeCCB;
+ acb->uncache_size += acb->ioqueue_size;
dma_coherent = dma_alloc_coherent(&pdev->dev, acb->uncache_size, &dma_coherent_handle, GFP_KERNEL);
if(!dma_coherent){
printk(KERN_NOTICE "arcmsr%d: dma_alloc_coherent got error\n", acb->host->host_no);
@@ -722,9 +730,10 @@ static int arcmsr_alloc_ccb_pool(struct AdapterControlBlock *acb)
memset(dma_coherent, 0, acb->uncache_size);
acb->ccbsize = roundup_ccbsize;
ccb_tmp = dma_coherent;
+ curr_phy_upper32 = upper_32_bits(dma_coherent_handle);
acb->vir2phy_offset = (unsigned long)dma_coherent - (unsigned long)dma_coherent_handle;
for(i = 0; i < acb->maxFreeCCB; i++){
- cdb_phyaddr = dma_coherent_handle + offsetof(struct CommandControlBlock, arcmsr_cdb);
+ cdb_phyaddr = (unsigned long)dma_coherent_handle + offsetof(struct CommandControlBlock, arcmsr_cdb);
switch (acb->adapter_type) {
case ACB_ADAPTER_TYPE_A:
case ACB_ADAPTER_TYPE_B:
@@ -740,10 +749,34 @@ static int arcmsr_alloc_ccb_pool(struct AdapterControlBlock *acb)
ccb_tmp->acb = acb;
ccb_tmp->smid = (u32)i << 16;
INIT_LIST_HEAD(&ccb_tmp->list);
- list_add_tail(&ccb_tmp->list, &acb->ccb_free_list);
+ next_ccb_phy = dma_coherent_handle + roundup_ccbsize;
+ if (upper_32_bits(next_ccb_phy) != curr_phy_upper32) {
+ acb->maxFreeCCB = i;
+ acb->host->can_queue = i;
+ break;
+ }
+ else
+ list_add_tail(&ccb_tmp->list, &acb->ccb_free_list);
ccb_tmp = (struct CommandControlBlock *)((unsigned long)ccb_tmp + roundup_ccbsize);
- dma_coherent_handle = dma_coherent_handle + roundup_ccbsize;
+ dma_coherent_handle = next_ccb_phy;
}
+ acb->dma_coherent_handle2 = dma_coherent_handle;
+ acb->dma_coherent2 = ccb_tmp;
+ switch (acb->adapter_type) {
+ case ACB_ADAPTER_TYPE_B:
+ acb->pmuB = (struct MessageUnit_B *)acb->dma_coherent2;
+ arcmsr_hbaB_assign_regAddr(acb);
+ break;
+ case ACB_ADAPTER_TYPE_D:
+ acb->pmuD = (struct MessageUnit_D *)acb->dma_coherent2;
+ arcmsr_hbaD_assign_regAddr(acb);
+ break;
+ case ACB_ADAPTER_TYPE_E:
+ acb->pCompletionQ = acb->dma_coherent2;
+ acb->completionQ_entry = acb->ioqueue_size / sizeof(struct deliver_completeQ);
+ acb->doneq_index = 0;
+ break;
+ }
return 0;
}
@@ -894,6 +927,31 @@ static void arcmsr_init_set_datetime_timer(struct AdapterControlBlock *pacb)
add_timer(&pacb->refresh_timer);
}
+static int arcmsr_set_dma_mask(struct AdapterControlBlock *acb)
+{
+ struct pci_dev *pcidev = acb->pdev;
+
+ if (IS_DMA64) {
+ if (((acb->adapter_type == ACB_ADAPTER_TYPE_A) && !dma_mask_64) ||
+ dma_set_mask(&pcidev->dev, DMA_BIT_MASK(64)))
+ goto dma32;
+ if (dma_set_coherent_mask(&pcidev->dev, DMA_BIT_MASK(64)) ||
+ dma_set_mask_and_coherent(&pcidev->dev, DMA_BIT_MASK(64))) {
+ printk("arcmsr: set DMA 64 mask failed\n");
+ return -ENXIO;
+ }
+ } else {
+dma32:
+ if (dma_set_mask(&pcidev->dev, DMA_BIT_MASK(32)) ||
+ dma_set_coherent_mask(&pcidev->dev, DMA_BIT_MASK(32)) ||
+ dma_set_mask_and_coherent(&pcidev->dev, DMA_BIT_MASK(32))) {
+ printk("arcmsr: set DMA 32-bit mask failed\n");
+ return -ENXIO;
+ }
+ }
+ return 0;
+}
+
static int arcmsr_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
struct Scsi_Host *host;
@@ -908,22 +966,15 @@ static int arcmsr_probe(struct pci_dev *pdev, const struct pci_device_id *id)
if(!host){
goto pci_disable_dev;
}
- error = dma_set_mask(&pdev->dev, DMA_BIT_MASK(64));
- if(error){
- error = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
- if(error){
- printk(KERN_WARNING
- "scsi%d: No suitable DMA mask available\n",
- host->host_no);
- goto scsi_host_release;
- }
- }
init_waitqueue_head(&wait_q);
bus = pdev->bus->number;
dev_fun = pdev->devfn;
acb = (struct AdapterControlBlock *) host->hostdata;
memset(acb,0,sizeof(struct AdapterControlBlock));
acb->pdev = pdev;
+ acb->adapter_type = id->driver_data;
+ if (arcmsr_set_dma_mask(acb))
+ goto scsi_host_release;
acb->host = host;
host->max_lun = ARCMSR_MAX_TARGETLUN;
host->max_id = ARCMSR_MAX_TARGETID; /*16:8*/
@@ -953,7 +1004,6 @@ static int arcmsr_probe(struct pci_dev *pdev, const struct pci_device_id *id)
ACB_F_MESSAGE_WQBUFFER_READED);
acb->acb_flags &= ~ACB_F_SCSISTOPADAPTER;
INIT_LIST_HEAD(&acb->ccb_free_list);
- acb->adapter_type = id->driver_data;
error = arcmsr_remap_pciregion(acb);
if(!error){
goto pci_release_regs;
@@ -965,9 +1015,10 @@ static int arcmsr_probe(struct pci_dev *pdev, const struct pci_device_id *id)
if(!error){
goto free_hbb_mu;
}
+ arcmsr_free_io_queue(acb);
error = arcmsr_alloc_ccb_pool(acb);
if(error){
- goto free_hbb_mu;
+ goto unmap_pci_region;
}
error = scsi_add_host(host, &pdev->dev);
if(error){
@@ -995,8 +1046,9 @@ scsi_host_remove:
scsi_remove_host(host);
free_ccb_pool:
arcmsr_free_ccb_pool(acb);
+ goto unmap_pci_region;
free_hbb_mu:
- arcmsr_free_mu(acb);
+ arcmsr_free_io_queue(acb);
unmap_pci_region:
arcmsr_unmap_pciregion(acb);
pci_release_regs:
@@ -1042,7 +1094,6 @@ static int arcmsr_suspend(struct pci_dev *pdev, pm_message_t state)
static int arcmsr_resume(struct pci_dev *pdev)
{
- int error;
struct Scsi_Host *host = pci_get_drvdata(pdev);
struct AdapterControlBlock *acb =
(struct AdapterControlBlock *)host->hostdata;
@@ -1054,24 +1105,30 @@ static int arcmsr_resume(struct pci_dev *pdev)
pr_warn("%s: pci_enable_device error\n", __func__);
return -ENODEV;
}
- error = dma_set_mask(&pdev->dev, DMA_BIT_MASK(64));
- if (error) {
- error = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
- if (error) {
- pr_warn("scsi%d: No suitable DMA mask available\n",
- host->host_no);
- goto controller_unregister;
- }
- }
+ if (arcmsr_set_dma_mask(acb))
+ goto controller_unregister;
pci_set_master(pdev);
if (arcmsr_request_irq(pdev, acb) == FAILED)
goto controller_stop;
- if (acb->adapter_type == ACB_ADAPTER_TYPE_E) {
+ switch (acb->adapter_type) {
+ case ACB_ADAPTER_TYPE_B: {
+ struct MessageUnit_B *reg = acb->pmuB;
+ uint32_t i;
+ for (i = 0; i < ARCMSR_MAX_HBB_POSTQUEUE; i++) {
+ reg->post_qbuffer[i] = 0;
+ reg->done_qbuffer[i] = 0;
+ }
+ reg->postq_index = 0;
+ reg->doneq_index = 0;
+ break;
+ }
+ case ACB_ADAPTER_TYPE_E:
writel(0, &acb->pmuE->host_int_status);
writel(ARCMSR_HBEMU_DOORBELL_SYNC, &acb->pmuE->iobound_doorbell);
acb->in_doorbell = 0;
acb->out_doorbell = 0;
acb->doneq_index = 0;
+ break;
}
arcmsr_iop_init(acb);
arcmsr_init_get_devmap_timer(acb);
@@ -1351,10 +1408,12 @@ static void arcmsr_drain_donequeue(struct AdapterControlBlock *acb, struct Comma
static void arcmsr_done4abort_postqueue(struct AdapterControlBlock *acb)
{
int i = 0;
- uint32_t flag_ccb, ccb_cdb_phy;
+ uint32_t flag_ccb;
struct ARCMSR_CDB *pARCMSR_CDB;
bool error;
struct CommandControlBlock *pCCB;
+ unsigned long ccb_cdb_phy, cdb_phy_hipart;
+
switch (acb->adapter_type) {
case ACB_ADAPTER_TYPE_A: {
@@ -1366,7 +1425,10 @@ static void arcmsr_done4abort_postqueue(struct AdapterControlBlock *acb)
writel(outbound_intstatus, &reg->outbound_intstatus);/*clear interrupt*/
while(((flag_ccb = readl(&reg->outbound_queueport)) != 0xFFFFFFFF)
&& (i++ < acb->maxOutstanding)) {
- pARCMSR_CDB = (struct ARCMSR_CDB *)(acb->vir2phy_offset + (flag_ccb << 5));/*frame must be 32 bytes aligned*/
+ ccb_cdb_phy = (flag_ccb << 5) & 0xffffffff;
+ if (acb->cdb_phyadd_hipart)
+ ccb_cdb_phy = ccb_cdb_phy | acb->cdb_phyadd_hipart;
+ pARCMSR_CDB = (struct ARCMSR_CDB *)(acb->vir2phy_offset + ccb_cdb_phy);
pCCB = container_of(pARCMSR_CDB, struct CommandControlBlock, arcmsr_cdb);
error = (flag_ccb & ARCMSR_CCBREPLY_FLAG_ERROR_MODE0) ? true : false;
arcmsr_drain_donequeue(acb, pCCB, error);
@@ -1382,7 +1444,10 @@ static void arcmsr_done4abort_postqueue(struct AdapterControlBlock *acb)
flag_ccb = reg->done_qbuffer[i];
if (flag_ccb != 0) {
reg->done_qbuffer[i] = 0;
- pARCMSR_CDB = (struct ARCMSR_CDB *)(acb->vir2phy_offset+(flag_ccb << 5));/*frame must be 32 bytes aligned*/
+ ccb_cdb_phy = (flag_ccb << 5) & 0xffffffff;
+ if (acb->cdb_phyadd_hipart)
+ ccb_cdb_phy = ccb_cdb_phy | acb->cdb_phyadd_hipart;
+ pARCMSR_CDB = (struct ARCMSR_CDB *)(acb->vir2phy_offset + ccb_cdb_phy);
pCCB = container_of(pARCMSR_CDB, struct CommandControlBlock, arcmsr_cdb);
error = (flag_ccb & ARCMSR_CCBREPLY_FLAG_ERROR_MODE0) ? true : false;
arcmsr_drain_donequeue(acb, pCCB, error);
@@ -1399,7 +1464,9 @@ static void arcmsr_done4abort_postqueue(struct AdapterControlBlock *acb)
/*need to do*/
flag_ccb = readl(&reg->outbound_queueport_low);
ccb_cdb_phy = (flag_ccb & 0xFFFFFFF0);
- pARCMSR_CDB = (struct ARCMSR_CDB *)(acb->vir2phy_offset+ccb_cdb_phy);/*frame must be 32 bytes aligned*/
+ if (acb->cdb_phyadd_hipart)
+ ccb_cdb_phy = ccb_cdb_phy | acb->cdb_phyadd_hipart;
+ pARCMSR_CDB = (struct ARCMSR_CDB *)(acb->vir2phy_offset + ccb_cdb_phy);
pCCB = container_of(pARCMSR_CDB, struct CommandControlBlock, arcmsr_cdb);
error = (flag_ccb & ARCMSR_CCBREPLY_FLAG_ERROR_MODE1) ? true : false;
arcmsr_drain_donequeue(acb, pCCB, error);
@@ -1427,9 +1494,13 @@ static void arcmsr_done4abort_postqueue(struct AdapterControlBlock *acb)
((toggle ^ 0x4000) + 1);
doneq_index = pmu->doneq_index;
spin_unlock_irqrestore(&acb->doneq_lock, flags);
+ cdb_phy_hipart = pmu->done_qbuffer[doneq_index &
+ 0xFFF].addressHigh;
addressLow = pmu->done_qbuffer[doneq_index &
0xFFF].addressLow;
ccb_cdb_phy = (addressLow & 0xFFFFFFF0);
+ if (acb->cdb_phyadd_hipart)
+ ccb_cdb_phy = ccb_cdb_phy | acb->cdb_phyadd_hipart;
pARCMSR_CDB = (struct ARCMSR_CDB *)
(acb->vir2phy_offset + ccb_cdb_phy);
pCCB = container_of(pARCMSR_CDB,
@@ -1506,7 +1577,6 @@ static void arcmsr_free_pcidev(struct AdapterControlBlock *acb)
pdev = acb->pdev;
arcmsr_free_irq(pdev, acb);
arcmsr_free_ccb_pool(acb);
- arcmsr_free_mu(acb);
arcmsr_unmap_pciregion(acb);
pci_release_regions(pdev);
scsi_host_put(host);
@@ -1564,7 +1634,6 @@ static void arcmsr_remove(struct pci_dev *pdev)
}
arcmsr_free_irq(pdev, acb);
arcmsr_free_ccb_pool(acb);
- arcmsr_free_mu(acb);
arcmsr_unmap_pciregion(acb);
pci_release_regions(pdev);
scsi_host_put(host);
@@ -1749,12 +1818,8 @@ static void arcmsr_post_ccb(struct AdapterControlBlock *acb, struct CommandContr
arc_cdb_size = (ccb->arc_cdb_size > 0x300) ? 0x300 : ccb->arc_cdb_size;
ccb_post_stamp = (cdb_phyaddr | ((arc_cdb_size - 1) >> 6) | 1);
- if (acb->cdb_phyaddr_hi32) {
- writel(acb->cdb_phyaddr_hi32, &phbcmu->inbound_queueport_high);
- writel(ccb_post_stamp, &phbcmu->inbound_queueport_low);
- } else {
- writel(ccb_post_stamp, &phbcmu->inbound_queueport_low);
- }
+ writel(upper_32_bits(ccb->cdb_phyaddr), &phbcmu->inbound_queueport_high);
+ writel(ccb_post_stamp, &phbcmu->inbound_queueport_low);
}
break;
case ACB_ADAPTER_TYPE_D: {
@@ -1767,8 +1832,8 @@ static void arcmsr_post_ccb(struct AdapterControlBlock *acb, struct CommandContr
spin_lock_irqsave(&acb->postq_lock, flags);
postq_index = pmu->postq_index;
pinbound_srb = (struct InBound_SRB *)&(pmu->post_qbuffer[postq_index & 0xFF]);
- pinbound_srb->addressHigh = dma_addr_hi32(cdb_phyaddr);
- pinbound_srb->addressLow = dma_addr_lo32(cdb_phyaddr);
+ pinbound_srb->addressHigh = upper_32_bits(ccb->cdb_phyaddr);
+ pinbound_srb->addressLow = cdb_phyaddr;
pinbound_srb->length = ccb->arc_cdb_size >> 2;
arcmsr_cdb->msgContext = dma_addr_lo32(cdb_phyaddr);
toggle = postq_index & 0x4000;
@@ -2304,8 +2369,13 @@ static void arcmsr_hbaA_postqueue_isr(struct AdapterControlBlock *acb)
struct ARCMSR_CDB *pARCMSR_CDB;
struct CommandControlBlock *pCCB;
bool error;
+ unsigned long cdb_phy_addr;
+
while ((flag_ccb = readl(&reg->outbound_queueport)) != 0xFFFFFFFF) {
- pARCMSR_CDB = (struct ARCMSR_CDB *)(acb->vir2phy_offset + (flag_ccb << 5));/*frame must be 32 bytes aligned*/
+ cdb_phy_addr = (flag_ccb << 5) & 0xffffffff;
+ if (acb->cdb_phyadd_hipart)
+ cdb_phy_addr = cdb_phy_addr | acb->cdb_phyadd_hipart;
+ pARCMSR_CDB = (struct ARCMSR_CDB *)(acb->vir2phy_offset + cdb_phy_addr);
pCCB = container_of(pARCMSR_CDB, struct CommandControlBlock, arcmsr_cdb);
error = (flag_ccb & ARCMSR_CCBREPLY_FLAG_ERROR_MODE0) ? true : false;
arcmsr_drain_donequeue(acb, pCCB, error);
@@ -2319,13 +2389,18 @@ static void arcmsr_hbaB_postqueue_isr(struct AdapterControlBlock *acb)
struct ARCMSR_CDB *pARCMSR_CDB;
struct CommandControlBlock *pCCB;
bool error;
+ unsigned long cdb_phy_addr;
+
index = reg->doneq_index;
while ((flag_ccb = reg->done_qbuffer[index]) != 0) {
- reg->done_qbuffer[index] = 0;
- pARCMSR_CDB = (struct ARCMSR_CDB *)(acb->vir2phy_offset+(flag_ccb << 5));/*frame must be 32 bytes aligned*/
+ cdb_phy_addr = (flag_ccb << 5) & 0xffffffff;
+ if (acb->cdb_phyadd_hipart)
+ cdb_phy_addr = cdb_phy_addr | acb->cdb_phyadd_hipart;
+ pARCMSR_CDB = (struct ARCMSR_CDB *)(acb->vir2phy_offset + cdb_phy_addr);
pCCB = container_of(pARCMSR_CDB, struct CommandControlBlock, arcmsr_cdb);
error = (flag_ccb & ARCMSR_CCBREPLY_FLAG_ERROR_MODE0) ? true : false;
arcmsr_drain_donequeue(acb, pCCB, error);
+ reg->done_qbuffer[index] = 0;
index++;
index %= ARCMSR_MAX_HBB_POSTQUEUE;
reg->doneq_index = index;
@@ -2337,7 +2412,8 @@ static void arcmsr_hbaC_postqueue_isr(struct AdapterControlBlock *acb)
struct MessageUnit_C __iomem *phbcmu;
struct ARCMSR_CDB *arcmsr_cdb;
struct CommandControlBlock *ccb;
- uint32_t flag_ccb, ccb_cdb_phy, throttling = 0;
+ uint32_t flag_ccb, throttling = 0;
+ unsigned long ccb_cdb_phy;
int error;
phbcmu = acb->pmuC;
@@ -2347,6 +2423,8 @@ static void arcmsr_hbaC_postqueue_isr(struct AdapterControlBlock *acb)
while ((flag_ccb = readl(&phbcmu->outbound_queueport_low)) !=
0xFFFFFFFF) {
ccb_cdb_phy = (flag_ccb & 0xFFFFFFF0);
+ if (acb->cdb_phyadd_hipart)
+ ccb_cdb_phy = ccb_cdb_phy | acb->cdb_phyadd_hipart;
arcmsr_cdb = (struct ARCMSR_CDB *)(acb->vir2phy_offset
+ ccb_cdb_phy);
ccb = container_of(arcmsr_cdb, struct CommandControlBlock,
@@ -2367,12 +2445,12 @@ static void arcmsr_hbaC_postqueue_isr(struct AdapterControlBlock *acb)
static void arcmsr_hbaD_postqueue_isr(struct AdapterControlBlock *acb)
{
u32 outbound_write_pointer, doneq_index, index_stripped, toggle;
- uint32_t addressLow, ccb_cdb_phy;
+ uint32_t addressLow;
int error;
struct MessageUnit_D *pmu;
struct ARCMSR_CDB *arcmsr_cdb;
struct CommandControlBlock *ccb;
- unsigned long flags;
+ unsigned long flags, ccb_cdb_phy, cdb_phy_hipart;
spin_lock_irqsave(&acb->doneq_lock, flags);
pmu = acb->pmuD;
@@ -2386,9 +2464,13 @@ static void arcmsr_hbaD_postqueue_isr(struct AdapterControlBlock *acb)
pmu->doneq_index = index_stripped ? (index_stripped | toggle) :
((toggle ^ 0x4000) + 1);
doneq_index = pmu->doneq_index;
+ cdb_phy_hipart = pmu->done_qbuffer[doneq_index &
+ 0xFFF].addressHigh;
addressLow = pmu->done_qbuffer[doneq_index &
0xFFF].addressLow;
ccb_cdb_phy = (addressLow & 0xFFFFFFF0);
+ if (acb->cdb_phyadd_hipart)
+ ccb_cdb_phy = ccb_cdb_phy | acb->cdb_phyadd_hipart;
arcmsr_cdb = (struct ARCMSR_CDB *)(acb->vir2phy_offset
+ ccb_cdb_phy);
ccb = container_of(arcmsr_cdb,
@@ -3229,7 +3311,9 @@ static int arcmsr_hbaA_polling_ccbdone(struct AdapterControlBlock *acb,
uint32_t flag_ccb, outbound_intstatus, poll_ccb_done = 0, poll_count = 0;
int rtn;
bool error;
- polling_hba_ccb_retry:
+ unsigned long ccb_cdb_phy;
+
+polling_hba_ccb_retry:
poll_count++;
outbound_intstatus = readl(&reg->outbound_intstatus) & acb->outbound_int_enable;
writel(outbound_intstatus, &reg->outbound_intstatus);/*clear interrupt*/
@@ -3247,7 +3331,10 @@ static int arcmsr_hbaA_polling_ccbdone(struct AdapterControlBlock *acb,
goto polling_hba_ccb_retry;
}
}
- arcmsr_cdb = (struct ARCMSR_CDB *)(acb->vir2phy_offset + (flag_ccb << 5));
+ ccb_cdb_phy = (flag_ccb << 5) & 0xffffffff;
+ if (acb->cdb_phyadd_hipart)
+ ccb_cdb_phy = ccb_cdb_phy | acb->cdb_phyadd_hipart;
+ arcmsr_cdb = (struct ARCMSR_CDB *)(acb->vir2phy_offset + ccb_cdb_phy);
ccb = container_of(arcmsr_cdb, struct CommandControlBlock, arcmsr_cdb);
poll_ccb_done |= (ccb == poll_ccb) ? 1 : 0;
if ((ccb->acb != acb) || (ccb->startdone != ARCMSR_CCB_START)) {
@@ -3285,8 +3372,9 @@ static int arcmsr_hbaB_polling_ccbdone(struct AdapterControlBlock *acb,
uint32_t flag_ccb, poll_ccb_done = 0, poll_count = 0;
int index, rtn;
bool error;
- polling_hbb_ccb_retry:
+ unsigned long ccb_cdb_phy;
+polling_hbb_ccb_retry:
poll_count++;
/* clear doorbell interrupt */
writel(ARCMSR_DOORBELL_INT_CLEAR_PATTERN, reg->iop2drv_doorbell);
@@ -3312,7 +3400,10 @@ static int arcmsr_hbaB_polling_ccbdone(struct AdapterControlBlock *acb,
index %= ARCMSR_MAX_HBB_POSTQUEUE;
reg->doneq_index = index;
/* check if command done with no error*/
- arcmsr_cdb = (struct ARCMSR_CDB *)(acb->vir2phy_offset + (flag_ccb << 5));
+ ccb_cdb_phy = (flag_ccb << 5) & 0xffffffff;
+ if (acb->cdb_phyadd_hipart)
+ ccb_cdb_phy = ccb_cdb_phy | acb->cdb_phyadd_hipart;
+ arcmsr_cdb = (struct ARCMSR_CDB *)(acb->vir2phy_offset + ccb_cdb_phy);
ccb = container_of(arcmsr_cdb, struct CommandControlBlock, arcmsr_cdb);
poll_ccb_done |= (ccb == poll_ccb) ? 1 : 0;
if ((ccb->acb != acb) || (ccb->startdone != ARCMSR_CCB_START)) {
@@ -3345,12 +3436,14 @@ static int arcmsr_hbaC_polling_ccbdone(struct AdapterControlBlock *acb,
struct CommandControlBlock *poll_ccb)
{
struct MessageUnit_C __iomem *reg = acb->pmuC;
- uint32_t flag_ccb, ccb_cdb_phy;
+ uint32_t flag_ccb;
struct ARCMSR_CDB *arcmsr_cdb;
bool error;
struct CommandControlBlock *pCCB;
uint32_t poll_ccb_done = 0, poll_count = 0;
int rtn;
+ unsigned long ccb_cdb_phy;
+
polling_hbc_ccb_retry:
poll_count++;
while (1) {
@@ -3369,7 +3462,9 @@ polling_hbc_ccb_retry:
}
flag_ccb = readl(&reg->outbound_queueport_low);
ccb_cdb_phy = (flag_ccb & 0xFFFFFFF0);
- arcmsr_cdb = (struct ARCMSR_CDB *)(acb->vir2phy_offset + ccb_cdb_phy);/*frame must be 32 bytes aligned*/
+ if (acb->cdb_phyadd_hipart)
+ ccb_cdb_phy = ccb_cdb_phy | acb->cdb_phyadd_hipart;
+ arcmsr_cdb = (struct ARCMSR_CDB *)(acb->vir2phy_offset + ccb_cdb_phy);
pCCB = container_of(arcmsr_cdb, struct CommandControlBlock, arcmsr_cdb);
poll_ccb_done |= (pCCB == poll_ccb) ? 1 : 0;
/* check ifcommand done with no error*/
@@ -3403,9 +3498,9 @@ static int arcmsr_hbaD_polling_ccbdone(struct AdapterControlBlock *acb,
struct CommandControlBlock *poll_ccb)
{
bool error;
- uint32_t poll_ccb_done = 0, poll_count = 0, flag_ccb, ccb_cdb_phy;
+ uint32_t poll_ccb_done = 0, poll_count = 0, flag_ccb;
int rtn, doneq_index, index_stripped, outbound_write_pointer, toggle;
- unsigned long flags;
+ unsigned long flags, ccb_cdb_phy, cdb_phy_hipart;
struct ARCMSR_CDB *arcmsr_cdb;
struct CommandControlBlock *pCCB;
struct MessageUnit_D *pmu = acb->pmuD;
@@ -3437,8 +3532,12 @@ polling_hbaD_ccb_retry:
((toggle ^ 0x4000) + 1);
doneq_index = pmu->doneq_index;
spin_unlock_irqrestore(&acb->doneq_lock, flags);
+ cdb_phy_hipart = pmu->done_qbuffer[doneq_index &
+ 0xFFF].addressHigh;
flag_ccb = pmu->done_qbuffer[doneq_index & 0xFFF].addressLow;
ccb_cdb_phy = (flag_ccb & 0xFFFFFFF0);
+ if (acb->cdb_phyadd_hipart)
+ ccb_cdb_phy = ccb_cdb_phy | acb->cdb_phyadd_hipart;
arcmsr_cdb = (struct ARCMSR_CDB *)(acb->vir2phy_offset +
ccb_cdb_phy);
pCCB = container_of(arcmsr_cdb, struct CommandControlBlock,
@@ -3680,6 +3779,7 @@ static int arcmsr_iop_confirm(struct AdapterControlBlock *acb)
cdb_phyaddr = lower_32_bits(dma_coherent_handle);
cdb_phyaddr_hi32 = upper_32_bits(dma_coherent_handle);
acb->cdb_phyaddr_hi32 = cdb_phyaddr_hi32;
+ acb->cdb_phyadd_hipart = ((uint64_t)cdb_phyaddr_hi32) << 32;
/*
***********************************************************************
** if adapter type B, set window of "post command Q"
@@ -3744,7 +3844,6 @@ static int arcmsr_iop_confirm(struct AdapterControlBlock *acb)
}
break;
case ACB_ADAPTER_TYPE_C: {
- if (cdb_phyaddr_hi32 != 0) {
struct MessageUnit_C __iomem *reg = acb->pmuC;
printk(KERN_NOTICE "arcmsr%d: cdb_phyaddr_hi32=0x%x\n",
@@ -3759,7 +3858,6 @@ static int arcmsr_iop_confirm(struct AdapterControlBlock *acb)
return 1;
}
}
- }
break;
case ACB_ADAPTER_TYPE_D: {
uint32_t __iomem *rwbuffer;
@@ -3793,7 +3891,7 @@ static int arcmsr_iop_confirm(struct AdapterControlBlock *acb)
cdb_phyaddr_hi32 = (uint32_t)((dma_coherent_handle >> 16) >> 16);
writel(cdb_phyaddr, &reg->msgcode_rwbuffer[5]);
writel(cdb_phyaddr_hi32, &reg->msgcode_rwbuffer[6]);
- writel(acb->roundup_ccbsize, &reg->msgcode_rwbuffer[7]);
+ writel(acb->ioqueue_size, &reg->msgcode_rwbuffer[7]);
writel(ARCMSR_INBOUND_MESG0_SET_CONFIG, &reg->inbound_msgaddr0);
acb->out_doorbell ^= ARCMSR_HBEMU_DRV2IOP_MESSAGE_CMD_DONE;
writel(acb->out_doorbell, &reg->iobound_doorbell);
diff --git a/drivers/scsi/bfa/bfa_fcs_lport.c b/drivers/scsi/bfa/bfa_fcs_lport.c
index b4f2c1d8742e..646f09f66443 100644
--- a/drivers/scsi/bfa/bfa_fcs_lport.c
+++ b/drivers/scsi/bfa/bfa_fcs_lport.c
@@ -6430,9 +6430,7 @@ bfa_fcs_vport_sm_logo_for_stop(struct bfa_fcs_vport_s *vport,
switch (event) {
case BFA_FCS_VPORT_SM_OFFLINE:
bfa_sm_send_event(vport->lps, BFA_LPS_SM_OFFLINE);
- /*
- * !!! fall through !!!
- */
+ /* fall through */
case BFA_FCS_VPORT_SM_RSP_OK:
case BFA_FCS_VPORT_SM_RSP_ERROR:
@@ -6458,9 +6456,7 @@ bfa_fcs_vport_sm_logo(struct bfa_fcs_vport_s *vport,
switch (event) {
case BFA_FCS_VPORT_SM_OFFLINE:
bfa_sm_send_event(vport->lps, BFA_LPS_SM_OFFLINE);
- /*
- * !!! fall through !!!
- */
+ /* fall through */
case BFA_FCS_VPORT_SM_RSP_OK:
case BFA_FCS_VPORT_SM_RSP_ERROR:
diff --git a/drivers/scsi/bfa/bfa_fcs_rport.c b/drivers/scsi/bfa/bfa_fcs_rport.c
index de50349a39ce..1e400f2aaece 100644
--- a/drivers/scsi/bfa/bfa_fcs_rport.c
+++ b/drivers/scsi/bfa/bfa_fcs_rport.c
@@ -427,17 +427,13 @@ bfa_fcs_rport_sm_plogi(struct bfa_fcs_rport_s *rport, enum rport_event event)
case RPSM_EVENT_LOGO_RCVD:
bfa_fcs_rport_send_logo_acc(rport);
- /*
- * !! fall through !!
- */
+ /* fall through */
case RPSM_EVENT_PRLO_RCVD:
if (rport->prlo == BFA_TRUE)
bfa_fcs_rport_send_prlo_acc(rport);
bfa_fcxp_discard(rport->fcxp);
- /*
- * !! fall through !!
- */
+ /* fall through */
case RPSM_EVENT_FAILED:
if (rport->plogi_retries < BFA_FCS_RPORT_MAX_RETRIES) {
rport->plogi_retries++;
@@ -868,9 +864,7 @@ bfa_fcs_rport_sm_adisc_online(struct bfa_fcs_rport_s *rport,
* At least go offline when a PLOGI is received.
*/
bfa_fcxp_discard(rport->fcxp);
- /*
- * !!! fall through !!!
- */
+ /* fall through */
case RPSM_EVENT_FAILED:
case RPSM_EVENT_ADDRESS_CHANGE:
@@ -1056,6 +1050,7 @@ bfa_fcs_rport_sm_fc4_logosend(struct bfa_fcs_rport_s *rport,
case RPSM_EVENT_LOGO_RCVD:
bfa_fcs_rport_send_logo_acc(rport);
+ /* fall through */
case RPSM_EVENT_PRLO_RCVD:
if (rport->prlo == BFA_TRUE)
bfa_fcs_rport_send_prlo_acc(rport);
@@ -1144,9 +1139,7 @@ bfa_fcs_rport_sm_hcb_offline(struct bfa_fcs_rport_s *rport,
bfa_fcs_rport_send_plogiacc(rport, NULL);
break;
}
- /*
- * !! fall through !!
- */
+ /* fall through */
case RPSM_EVENT_ADDRESS_CHANGE:
if (!bfa_fcs_lport_is_online(rport->port)) {
@@ -1303,6 +1296,7 @@ bfa_fcs_rport_sm_hcb_logosend(struct bfa_fcs_rport_s *rport,
case RPSM_EVENT_LOGO_RCVD:
bfa_fcs_rport_send_logo_acc(rport);
+ /* fall through */
case RPSM_EVENT_PRLO_RCVD:
if (rport->prlo == BFA_TRUE)
bfa_fcs_rport_send_prlo_acc(rport);
@@ -1346,6 +1340,7 @@ bfa_fcs_rport_sm_logo_sending(struct bfa_fcs_rport_s *rport,
case RPSM_EVENT_LOGO_RCVD:
bfa_fcs_rport_send_logo_acc(rport);
+ /* fall through */
case RPSM_EVENT_PRLO_RCVD:
if (rport->prlo == BFA_TRUE)
bfa_fcs_rport_send_prlo_acc(rport);
diff --git a/drivers/scsi/bfa/bfa_ioc.c b/drivers/scsi/bfa/bfa_ioc.c
index 9631877aba4f..79a55c3615be 100644
--- a/drivers/scsi/bfa/bfa_ioc.c
+++ b/drivers/scsi/bfa/bfa_ioc.c
@@ -978,9 +978,7 @@ bfa_iocpf_sm_enabling(struct bfa_iocpf_s *iocpf, enum iocpf_event event)
case IOCPF_E_INITFAIL:
bfa_iocpf_timer_stop(ioc);
- /*
- * !!! fall through !!!
- */
+ /* fall through */
case IOCPF_E_TIMEOUT:
writel(1, ioc->ioc_regs.ioc_sem_reg);
@@ -1056,9 +1054,7 @@ bfa_iocpf_sm_disabling(struct bfa_iocpf_s *iocpf, enum iocpf_event event)
case IOCPF_E_FAIL:
bfa_iocpf_timer_stop(ioc);
- /*
- * !!! fall through !!!
- */
+ /* fall through */
case IOCPF_E_TIMEOUT:
bfa_ioc_set_cur_ioc_fwstate(ioc, BFI_IOC_FAIL);
@@ -6007,6 +6003,7 @@ bfa_dconf_sm_final_sync(struct bfa_dconf_mod_s *dconf,
case BFA_DCONF_SM_IOCDISABLE:
case BFA_DCONF_SM_FLASH_COMP:
bfa_timer_stop(&dconf->timer);
+ /* fall through */
case BFA_DCONF_SM_TIMEOUT:
bfa_sm_set_state(dconf, bfa_dconf_sm_uninit);
bfa_fsm_send_event(&dconf->bfa->iocfc, IOCFC_E_DCONF_DONE);
diff --git a/drivers/scsi/bfa/bfad_debugfs.c b/drivers/scsi/bfa/bfad_debugfs.c
index 349cfe7d055e..bfcd87c0dc74 100644
--- a/drivers/scsi/bfa/bfad_debugfs.c
+++ b/drivers/scsi/bfa/bfad_debugfs.c
@@ -460,11 +460,6 @@ bfad_debugfs_init(struct bfad_port_s *port)
if (!bfa_debugfs_root) {
bfa_debugfs_root = debugfs_create_dir("bfa", NULL);
atomic_set(&bfa_debugfs_port_count, 0);
- if (!bfa_debugfs_root) {
- printk(KERN_WARNING
- "BFA debugfs root dir creation failed\n");
- goto err;
- }
}
/* Setup the pci_dev debugfs directory for the port */
@@ -472,12 +467,6 @@ bfad_debugfs_init(struct bfad_port_s *port)
if (!port->port_debugfs_root) {
port->port_debugfs_root =
debugfs_create_dir(name, bfa_debugfs_root);
- if (!port->port_debugfs_root) {
- printk(KERN_WARNING
- "bfa %s: debugfs root creation failed\n",
- bfad->pci_name);
- goto err;
- }
atomic_inc(&bfa_debugfs_port_count);
@@ -489,16 +478,9 @@ bfad_debugfs_init(struct bfad_port_s *port)
port->port_debugfs_root,
port,
file->fops);
- if (!bfad->bfad_dentry_files[i]) {
- printk(KERN_WARNING
- "bfa %s: debugfs %s creation failed\n",
- bfad->pci_name, file->name);
- goto err;
- }
}
}
-err:
return;
}
diff --git a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c
index 2e4e7159ebf9..a75e74ad1698 100644
--- a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c
+++ b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c
@@ -1438,7 +1438,7 @@ bind_err:
static struct bnx2fc_interface *
bnx2fc_interface_create(struct bnx2fc_hba *hba,
struct net_device *netdev,
- enum fip_state fip_mode)
+ enum fip_mode fip_mode)
{
struct fcoe_ctlr_device *ctlr_dev;
struct bnx2fc_interface *interface;
diff --git a/drivers/scsi/bnx2i/bnx2i_iscsi.c b/drivers/scsi/bnx2i/bnx2i_iscsi.c
index 69c75426c5eb..c5fa5f3b00e9 100644
--- a/drivers/scsi/bnx2i/bnx2i_iscsi.c
+++ b/drivers/scsi/bnx2i/bnx2i_iscsi.c
@@ -577,7 +577,7 @@ static void bnx2i_free_mp_bdt(struct bnx2i_hba *hba)
hba->dummy_buffer, hba->dummy_buf_dma);
hba->dummy_buffer = NULL;
}
- return;
+ return;
}
/**
diff --git a/drivers/scsi/csiostor/csio_attr.c b/drivers/scsi/csiostor/csio_attr.c
index 9bd2bd8dc2be..200e50089711 100644
--- a/drivers/scsi/csiostor/csio_attr.c
+++ b/drivers/scsi/csiostor/csio_attr.c
@@ -497,7 +497,6 @@ out:
static int
csio_fcoe_free_vnp(struct csio_hw *hw, struct csio_lnode *ln)
{
- struct csio_lnode *pln;
struct csio_mb *mbp;
struct fw_fcoe_vnp_cmd *rsp;
int ret = 0;
@@ -514,8 +513,6 @@ csio_fcoe_free_vnp(struct csio_hw *hw, struct csio_lnode *ln)
goto out;
}
- pln = ln->pln;
-
csio_fcoe_vnp_free_init_mb(ln, mbp, CSIO_MB_DEFAULT_TMO,
ln->fcf_flowid, ln->vnp_flowid,
NULL);
diff --git a/drivers/scsi/csiostor/csio_init.c b/drivers/scsi/csiostor/csio_init.c
index 616b25bf7941..a6dd704d7f2d 100644
--- a/drivers/scsi/csiostor/csio_init.c
+++ b/drivers/scsi/csiostor/csio_init.c
@@ -167,14 +167,10 @@ csio_dfs_destroy(struct csio_hw *hw)
* csio_dfs_init - Debug filesystem initialization for the module.
*
*/
-static int
+static void
csio_dfs_init(void)
{
csio_debugfs_root = debugfs_create_dir(KBUILD_MODNAME, NULL);
- if (!csio_debugfs_root)
- pr_warn("Could not create debugfs entry, continuing\n");
-
- return 0;
}
/*
diff --git a/drivers/scsi/csiostor/csio_scsi.c b/drivers/scsi/csiostor/csio_scsi.c
index bc5547a62c00..462560b2855e 100644
--- a/drivers/scsi/csiostor/csio_scsi.c
+++ b/drivers/scsi/csiostor/csio_scsi.c
@@ -1984,15 +1984,15 @@ inval_scmnd:
/* FW successfully aborted the request */
if (host_byte(cmnd->result) == DID_REQUEUE) {
csio_info(hw,
- "Aborted SCSI command to (%d:%llu) serial#:0x%lx\n",
+ "Aborted SCSI command to (%d:%llu) tag %u\n",
cmnd->device->id, cmnd->device->lun,
- cmnd->serial_number);
+ cmnd->request->tag);
return SUCCESS;
} else {
csio_info(hw,
- "Failed to abort SCSI command, (%d:%llu) serial#:0x%lx\n",
+ "Failed to abort SCSI command, (%d:%llu) tag %u\n",
cmnd->device->id, cmnd->device->lun,
- cmnd->serial_number);
+ cmnd->request->tag);
return FAILED;
}
}
diff --git a/drivers/scsi/cxgbi/Makefile b/drivers/scsi/cxgbi/Makefile
index a73781ac1800..f78c9cc460a2 100644
--- a/drivers/scsi/cxgbi/Makefile
+++ b/drivers/scsi/cxgbi/Makefile
@@ -1,4 +1,4 @@
-ccflags-y += -Idrivers/net/ethernet/chelsio/libcxgb
+ccflags-y += -I $(srctree)/drivers/net/ethernet/chelsio/libcxgb
obj-$(CONFIG_SCSI_CXGB3_ISCSI) += libcxgbi.o cxgb3i/
obj-$(CONFIG_SCSI_CXGB4_ISCSI) += libcxgbi.o cxgb4i/
diff --git a/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c b/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c
index d26f50af00ea..d44914e5e415 100644
--- a/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c
+++ b/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c
@@ -1210,7 +1210,8 @@ static void do_rx_iscsi_hdr(struct cxgbi_device *cdev, struct sk_buff *skb)
csk->skb_ulp_lhdr = skb;
cxgbi_skcb_set_flag(skb, SKCBF_RX_HDR);
- if (cxgbi_skcb_tcp_seq(skb) != csk->rcv_nxt) {
+ if ((CHELSIO_CHIP_VERSION(lldi->adapter_type) <= CHELSIO_T5) &&
+ (cxgbi_skcb_tcp_seq(skb) != csk->rcv_nxt)) {
pr_info("tid %u, CPL_ISCSI_HDR, bad seq, 0x%x/0x%x.\n",
csk->tid, cxgbi_skcb_tcp_seq(skb),
csk->rcv_nxt);
@@ -2134,8 +2135,7 @@ static void *t4_uld_add(const struct cxgb4_lld_info *lldi)
cdev->itp = &cxgb4i_iscsi_transport;
cdev->owner = THIS_MODULE;
- cdev->pfvf = FW_VIID_PFN_G(cxgb4_port_viid(lldi->ports[0]))
- << FW_VIID_PFN_S;
+ cdev->pfvf = FW_PFVF_CMD_PFN_V(lldi->pf);
pr_info("cdev 0x%p,%s, pfvf %u.\n",
cdev, lldi->ports[0]->name, cdev->pfvf);
diff --git a/drivers/scsi/cxgbi/libcxgbi.c b/drivers/scsi/cxgbi/libcxgbi.c
index 245742557c03..006372b3fba2 100644
--- a/drivers/scsi/cxgbi/libcxgbi.c
+++ b/drivers/scsi/cxgbi/libcxgbi.c
@@ -1212,7 +1212,7 @@ scmd_get_params(struct scsi_cmnd *sc, struct scatterlist **sgl,
unsigned int *sgcnt, unsigned int *dlen,
unsigned int prot)
{
- struct scsi_data_buffer *sdb = prot ? scsi_prot(sc) : scsi_out(sc);
+ struct scsi_data_buffer *sdb = prot ? scsi_prot(sc) : &sc->sdb;
*sgl = sdb->table.sgl;
*sgcnt = sdb->table.nents;
@@ -1428,8 +1428,7 @@ static void task_release_itt(struct iscsi_task *task, itt_t hdr_itt)
log_debug(1 << CXGBI_DBG_DDP,
"cdev 0x%p, task 0x%p, release tag 0x%x.\n",
cdev, task, tag);
- if (sc &&
- (scsi_bidi_cmnd(sc) || sc->sc_data_direction == DMA_FROM_DEVICE) &&
+ if (sc && sc->sc_data_direction == DMA_FROM_DEVICE &&
cxgbi_ppm_is_ddp_tag(ppm, tag)) {
struct cxgbi_task_data *tdata = iscsi_task_cxgbi_data(task);
struct cxgbi_task_tag_info *ttinfo = &tdata->ttinfo;
@@ -1461,9 +1460,7 @@ static int task_reserve_itt(struct iscsi_task *task, itt_t *hdr_itt)
u32 tag = 0;
int err = -EINVAL;
- if (sc &&
- (scsi_bidi_cmnd(sc) || sc->sc_data_direction == DMA_FROM_DEVICE)
- ) {
+ if (sc && sc->sc_data_direction == DMA_FROM_DEVICE) {
struct cxgbi_task_data *tdata = iscsi_task_cxgbi_data(task);
struct cxgbi_task_tag_info *ttinfo = &tdata->ttinfo;
@@ -1897,7 +1894,7 @@ int cxgbi_conn_alloc_pdu(struct iscsi_task *task, u8 opcode)
if (SKB_MAX_HEAD(cdev->skb_tx_rsvd) > (512 * MAX_SKB_FRAGS) &&
(opcode == ISCSI_OP_SCSI_DATA_OUT ||
(opcode == ISCSI_OP_SCSI_CMD &&
- (scsi_bidi_cmnd(sc) || sc->sc_data_direction == DMA_TO_DEVICE))))
+ sc->sc_data_direction == DMA_TO_DEVICE)))
/* data could goes into skb head */
headroom += min_t(unsigned int,
SKB_MAX_HEAD(cdev->skb_tx_rsvd),
@@ -1972,7 +1969,7 @@ int cxgbi_conn_init_pdu(struct iscsi_task *task, unsigned int offset,
return 0;
if (task->sc) {
- struct scsi_data_buffer *sdb = scsi_out(task->sc);
+ struct scsi_data_buffer *sdb = &task->sc->sdb;
struct scatterlist *sg = NULL;
int err;
diff --git a/drivers/scsi/cxlflash/common.h b/drivers/scsi/cxlflash/common.h
index 8908a20065c8..4d90106fcb37 100644
--- a/drivers/scsi/cxlflash/common.h
+++ b/drivers/scsi/cxlflash/common.h
@@ -334,7 +334,8 @@ int cxlflash_afu_sync(struct afu *afu, ctx_hndl_t c, res_hndl_t r, u8 mode);
void cxlflash_list_init(void);
void cxlflash_term_global_luns(void);
void cxlflash_free_errpage(void);
-int cxlflash_ioctl(struct scsi_device *sdev, int cmd, void __user *arg);
+int cxlflash_ioctl(struct scsi_device *sdev, unsigned int cmd,
+ void __user *arg);
void cxlflash_stop_term_user_contexts(struct cxlflash_cfg *cfg);
int cxlflash_mark_contexts_error(struct cxlflash_cfg *cfg);
void cxlflash_term_local_luns(struct cxlflash_cfg *cfg);
diff --git a/drivers/scsi/cxlflash/main.c b/drivers/scsi/cxlflash/main.c
index c8bad2c093b8..7096810fd222 100644
--- a/drivers/scsi/cxlflash/main.c
+++ b/drivers/scsi/cxlflash/main.c
@@ -3282,7 +3282,7 @@ static int cxlflash_chr_open(struct inode *inode, struct file *file)
*
* Return: A string identifying the decoded host ioctl.
*/
-static char *decode_hioctl(int cmd)
+static char *decode_hioctl(unsigned int cmd)
{
switch (cmd) {
case HT_CXLFLASH_LUN_PROVISION:
diff --git a/drivers/scsi/cxlflash/superpipe.c b/drivers/scsi/cxlflash/superpipe.c
index acac6152f50b..1a94a469051e 100644
--- a/drivers/scsi/cxlflash/superpipe.c
+++ b/drivers/scsi/cxlflash/superpipe.c
@@ -1924,7 +1924,7 @@ out:
*
* Return: A string identifying the decoded ioctl.
*/
-static char *decode_ioctl(int cmd)
+static char *decode_ioctl(unsigned int cmd)
{
switch (cmd) {
case DK_CXLFLASH_ATTACH:
@@ -2051,7 +2051,7 @@ err1:
*
* Return: 0 on success, -errno on failure
*/
-static int ioctl_common(struct scsi_device *sdev, int cmd)
+static int ioctl_common(struct scsi_device *sdev, unsigned int cmd)
{
struct cxlflash_cfg *cfg = shost_priv(sdev->host);
struct device *dev = &cfg->dev->dev;
@@ -2096,7 +2096,7 @@ out:
*
* Return: 0 on success, -errno on failure
*/
-int cxlflash_ioctl(struct scsi_device *sdev, int cmd, void __user *arg)
+int cxlflash_ioctl(struct scsi_device *sdev, unsigned int cmd, void __user *arg)
{
typedef int (*sioctl) (struct scsi_device *, void *);
@@ -2179,8 +2179,7 @@ int cxlflash_ioctl(struct scsi_device *sdev, int cmd, void __user *arg)
}
if (unlikely(copy_from_user(&buf, arg, size))) {
- dev_err(dev, "%s: copy_from_user() fail "
- "size=%lu cmd=%d (%s) arg=%p\n",
+ dev_err(dev, "%s: copy_from_user() fail size=%lu cmd=%u (%s) arg=%p\n",
__func__, size, cmd, decode_ioctl(cmd), arg);
rc = -EFAULT;
goto cxlflash_ioctl_exit;
@@ -2203,8 +2202,7 @@ int cxlflash_ioctl(struct scsi_device *sdev, int cmd, void __user *arg)
rc = do_ioctl(sdev, (void *)&buf);
if (likely(!rc))
if (unlikely(copy_to_user(arg, &buf, size))) {
- dev_err(dev, "%s: copy_to_user() fail "
- "size=%lu cmd=%d (%s) arg=%p\n",
+ dev_err(dev, "%s: copy_to_user() fail size=%lu cmd=%u (%s) arg=%p\n",
__func__, size, cmd, decode_ioctl(cmd), arg);
rc = -EFAULT;
}
diff --git a/drivers/scsi/dpt_i2o.c b/drivers/scsi/dpt_i2o.c
index 70d1a18278af..abdc34affdf6 100644
--- a/drivers/scsi/dpt_i2o.c
+++ b/drivers/scsi/dpt_i2o.c
@@ -589,46 +589,6 @@ static int adpt_show_info(struct seq_file *m, struct Scsi_Host *host)
}
/*
- * Turn a struct scsi_cmnd * into a unique 32 bit 'context'.
- */
-static u32 adpt_cmd_to_context(struct scsi_cmnd *cmd)
-{
- return (u32)cmd->serial_number;
-}
-
-/*
- * Go from a u32 'context' to a struct scsi_cmnd * .
- * This could probably be made more efficient.
- */
-static struct scsi_cmnd *
- adpt_cmd_from_context(adpt_hba * pHba, u32 context)
-{
- struct scsi_cmnd * cmd;
- struct scsi_device * d;
-
- if (context == 0)
- return NULL;
-
- spin_unlock(pHba->host->host_lock);
- shost_for_each_device(d, pHba->host) {
- unsigned long flags;
- spin_lock_irqsave(&d->list_lock, flags);
- list_for_each_entry(cmd, &d->cmd_list, list) {
- if (((u32)cmd->serial_number == context)) {
- spin_unlock_irqrestore(&d->list_lock, flags);
- scsi_device_put(d);
- spin_lock(pHba->host->host_lock);
- return cmd;
- }
- }
- spin_unlock_irqrestore(&d->list_lock, flags);
- }
- spin_lock(pHba->host->host_lock);
-
- return NULL;
-}
-
-/*
* Turn a pointer to ioctl reply data into an u32 'context'
*/
static u32 adpt_ioctl_to_context(adpt_hba * pHba, void *reply)
@@ -685,9 +645,6 @@ static int adpt_abort(struct scsi_cmnd * cmd)
u32 msg[5];
int rcode;
- if(cmd->serial_number == 0){
- return FAILED;
- }
pHba = (adpt_hba*) cmd->device->host->hostdata[0];
printk(KERN_INFO"%s: Trying to Abort\n",pHba->name);
if ((dptdevice = (void*) (cmd->device->hostdata)) == NULL) {
@@ -699,8 +656,9 @@ static int adpt_abort(struct scsi_cmnd * cmd)
msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0;
msg[1] = I2O_CMD_SCSI_ABORT<<24|HOST_TID<<12|dptdevice->tid;
msg[2] = 0;
- msg[3]= 0;
- msg[4] = adpt_cmd_to_context(cmd);
+ msg[3]= 0;
+ /* Add 1 to avoid firmware treating it as invalid command */
+ msg[4] = cmd->request->tag + 1;
if (pHba->host)
spin_lock_irq(pHba->host->host_lock);
rcode = adpt_i2o_post_wait(pHba, msg, sizeof(msg), FOREVER);
@@ -2198,20 +2156,27 @@ static irqreturn_t adpt_isr(int irq, void *dev_id)
status = I2O_POST_WAIT_OK;
}
if(!(context & 0x40000000)) {
- cmd = adpt_cmd_from_context(pHba,
- readl(reply+12));
+ /*
+ * The request tag is one less than the command tag
+ * as the firmware might treat a 0 tag as invalid
+ */
+ cmd = scsi_host_find_tag(pHba->host,
+ readl(reply + 12) - 1);
if(cmd != NULL) {
printk(KERN_WARNING"%s: Apparent SCSI cmd in Post Wait Context - cmd=%p context=%x\n", pHba->name, cmd, context);
}
}
adpt_i2o_post_wait_complete(context, status);
} else { // SCSI message
- cmd = adpt_cmd_from_context (pHba, readl(reply+12));
+ /*
+ * The request tag is one less than the command tag
+ * as the firmware might treat a 0 tag as invalid
+ */
+ cmd = scsi_host_find_tag(pHba->host,
+ readl(reply + 12) - 1);
if(cmd != NULL){
scsi_dma_unmap(cmd);
- if(cmd->serial_number != 0) { // If not timedout
- adpt_i2o_to_scsi(reply, cmd);
- }
+ adpt_i2o_to_scsi(reply, cmd);
}
}
writel(m, pHba->reply_port);
@@ -2277,7 +2242,8 @@ static s32 adpt_scsi_to_i2o(adpt_hba* pHba, struct scsi_cmnd* cmd, struct adpt_d
// I2O_CMD_SCSI_EXEC
msg[1] = ((0xff<<24)|(HOST_TID<<12)|d->tid);
msg[2] = 0;
- msg[3] = adpt_cmd_to_context(cmd); /* Want SCSI control block back */
+ /* Add 1 to avoid firmware treating it as invalid command */
+ msg[3] = cmd->request->tag + 1;
// Our cards use the transaction context as the tag for queueing
// Adaptec/DPT Private stuff
msg[4] = I2O_CMD_SCSI_EXEC|(DPT_ORGANIZATION_ID<<16);
@@ -2693,9 +2659,6 @@ static void adpt_fail_posted_scbs(adpt_hba* pHba)
unsigned long flags;
spin_lock_irqsave(&d->list_lock, flags);
list_for_each_entry(cmd, &d->cmd_list, list) {
- if(cmd->serial_number == 0){
- continue;
- }
cmd->result = (DID_OK << 16) | (QUEUE_FULL <<1);
cmd->scsi_done(cmd);
}
diff --git a/drivers/scsi/esas2r/esas2r.h b/drivers/scsi/esas2r/esas2r.h
index 858c3b33db78..7f43b95f4e94 100644
--- a/drivers/scsi/esas2r/esas2r.h
+++ b/drivers/scsi/esas2r/esas2r.h
@@ -965,8 +965,8 @@ struct esas2r_adapter {
const char *esas2r_info(struct Scsi_Host *);
int esas2r_write_params(struct esas2r_adapter *a, struct esas2r_request *rq,
struct esas2r_sas_nvram *data);
-int esas2r_ioctl_handler(void *hostdata, int cmd, void __user *arg);
-int esas2r_ioctl(struct scsi_device *dev, int cmd, void __user *arg);
+int esas2r_ioctl_handler(void *hostdata, unsigned int cmd, void __user *arg);
+int esas2r_ioctl(struct scsi_device *dev, unsigned int cmd, void __user *arg);
u8 handle_hba_ioctl(struct esas2r_adapter *a,
struct atto_ioctl *ioctl_hba);
int esas2r_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd);
diff --git a/drivers/scsi/esas2r/esas2r_init.c b/drivers/scsi/esas2r/esas2r_init.c
index 46b2c83ba21f..950cd92df2ff 100644
--- a/drivers/scsi/esas2r/esas2r_init.c
+++ b/drivers/scsi/esas2r/esas2r_init.c
@@ -1241,6 +1241,7 @@ static bool esas2r_format_init_msg(struct esas2r_adapter *a,
a->init_msg = ESAS2R_INIT_MSG_GET_INIT;
break;
}
+ /* fall through */
case ESAS2R_INIT_MSG_GET_INIT:
if (msg == ESAS2R_INIT_MSG_GET_INIT) {
@@ -1254,7 +1255,7 @@ static bool esas2r_format_init_msg(struct esas2r_adapter *a,
esas2r_hdebug("FAILED");
}
}
- /* fall through */
+ /* fall through */
default:
rq->req_stat = RS_SUCCESS;
diff --git a/drivers/scsi/esas2r/esas2r_ioctl.c b/drivers/scsi/esas2r/esas2r_ioctl.c
index 34bcc8c04ff4..3d130523c288 100644
--- a/drivers/scsi/esas2r/esas2r_ioctl.c
+++ b/drivers/scsi/esas2r/esas2r_ioctl.c
@@ -1274,7 +1274,7 @@ int esas2r_write_params(struct esas2r_adapter *a, struct esas2r_request *rq,
/* This function only cares about ATTO-specific ioctls (atto_express_ioctl) */
-int esas2r_ioctl_handler(void *hostdata, int cmd, void __user *arg)
+int esas2r_ioctl_handler(void *hostdata, unsigned int cmd, void __user *arg)
{
struct atto_express_ioctl *ioctl = NULL;
struct esas2r_adapter *a;
@@ -1292,9 +1292,8 @@ int esas2r_ioctl_handler(void *hostdata, int cmd, void __user *arg)
ioctl = memdup_user(arg, sizeof(struct atto_express_ioctl));
if (IS_ERR(ioctl)) {
esas2r_log(ESAS2R_LOG_WARN,
- "ioctl_handler access_ok failed for cmd %d, "
- "address %p", cmd,
- arg);
+ "ioctl_handler access_ok failed for cmd %u, address %p",
+ cmd, arg);
return PTR_ERR(ioctl);
}
@@ -1493,7 +1492,7 @@ int esas2r_ioctl_handler(void *hostdata, int cmd, void __user *arg)
ioctl_done:
if (err < 0) {
- esas2r_log(ESAS2R_LOG_WARN, "err %d on ioctl cmd %d", err,
+ esas2r_log(ESAS2R_LOG_WARN, "err %d on ioctl cmd %u", err,
cmd);
switch (err) {
@@ -1518,9 +1517,8 @@ ioctl_done:
err = __copy_to_user(arg, ioctl, sizeof(struct atto_express_ioctl));
if (err != 0) {
esas2r_log(ESAS2R_LOG_WARN,
- "ioctl_handler copy_to_user didn't copy "
- "everything (err %d, cmd %d)", err,
- cmd);
+ "ioctl_handler copy_to_user didn't copy everything (err %d, cmd %u)",
+ err, cmd);
kfree(ioctl);
return -EFAULT;
@@ -1531,7 +1529,7 @@ ioctl_done:
return 0;
}
-int esas2r_ioctl(struct scsi_device *sd, int cmd, void __user *arg)
+int esas2r_ioctl(struct scsi_device *sd, unsigned int cmd, void __user *arg)
{
return esas2r_ioctl_handler(sd->host->hostdata, cmd, arg);
}
diff --git a/drivers/scsi/esas2r/esas2r_main.c b/drivers/scsi/esas2r/esas2r_main.c
index 64397d441bae..fdbda5c05aa0 100644
--- a/drivers/scsi/esas2r/esas2r_main.c
+++ b/drivers/scsi/esas2r/esas2r_main.c
@@ -623,7 +623,7 @@ static int esas2r_proc_major;
long esas2r_proc_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
{
return esas2r_ioctl_handler(esas2r_proc_host->hostdata,
- (int)cmd, (void __user *)arg);
+ cmd, (void __user *)arg);
}
static void __exit esas2r_exit(void)
diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c
index cd19be3f3405..8ba8862d3292 100644
--- a/drivers/scsi/fcoe/fcoe.c
+++ b/drivers/scsi/fcoe/fcoe.c
@@ -389,7 +389,7 @@ static int fcoe_interface_setup(struct fcoe_interface *fcoe,
* Returns: pointer to a struct fcoe_interface or NULL on error
*/
static struct fcoe_interface *fcoe_interface_create(struct net_device *netdev,
- enum fip_state fip_mode)
+ enum fip_mode fip_mode)
{
struct fcoe_ctlr_device *ctlr_dev;
struct fcoe_ctlr *ctlr;
diff --git a/drivers/scsi/fcoe/fcoe_ctlr.c b/drivers/scsi/fcoe/fcoe_ctlr.c
index 54da3166da8d..7dc4ffa24430 100644
--- a/drivers/scsi/fcoe/fcoe_ctlr.c
+++ b/drivers/scsi/fcoe/fcoe_ctlr.c
@@ -147,7 +147,7 @@ static void fcoe_ctlr_map_dest(struct fcoe_ctlr *fip)
* fcoe_ctlr_init() - Initialize the FCoE Controller instance
* @fip: The FCoE controller to initialize
*/
-void fcoe_ctlr_init(struct fcoe_ctlr *fip, enum fip_state mode)
+void fcoe_ctlr_init(struct fcoe_ctlr *fip, enum fip_mode mode)
{
fcoe_ctlr_set_state(fip, FIP_ST_LINK_WAIT);
fip->mode = mode;
@@ -454,7 +454,10 @@ void fcoe_ctlr_link_up(struct fcoe_ctlr *fip)
mutex_unlock(&fip->ctlr_mutex);
fc_linkup(fip->lp);
} else if (fip->state == FIP_ST_LINK_WAIT) {
- fcoe_ctlr_set_state(fip, fip->mode);
+ if (fip->mode == FIP_MODE_NON_FIP)
+ fcoe_ctlr_set_state(fip, FIP_ST_NON_FIP);
+ else
+ fcoe_ctlr_set_state(fip, FIP_ST_AUTO);
switch (fip->mode) {
default:
LIBFCOE_FIP_DBG(fip, "invalid mode %d\n", fip->mode);
diff --git a/drivers/scsi/fcoe/fcoe_sysfs.c b/drivers/scsi/fcoe/fcoe_sysfs.c
index 5c8310bade61..c3dcbdc3aa64 100644
--- a/drivers/scsi/fcoe/fcoe_sysfs.c
+++ b/drivers/scsi/fcoe/fcoe_sysfs.c
@@ -671,8 +671,19 @@ static const struct device_type fcoe_fcf_device_type = {
.release = fcoe_fcf_device_release,
};
-static BUS_ATTR(ctlr_create, S_IWUSR, NULL, fcoe_ctlr_create_store);
-static BUS_ATTR(ctlr_destroy, S_IWUSR, NULL, fcoe_ctlr_destroy_store);
+static ssize_t ctlr_create_store(struct bus_type *bus, const char *buf,
+ size_t count)
+{
+ return fcoe_ctlr_create_store(bus, buf, count);
+}
+static BUS_ATTR_WO(ctlr_create);
+
+static ssize_t ctlr_destroy_store(struct bus_type *bus, const char *buf,
+ size_t count)
+{
+ return fcoe_ctlr_destroy_store(bus, buf, count);
+}
+static BUS_ATTR_WO(ctlr_destroy);
static struct attribute *fcoe_bus_attrs[] = {
&bus_attr_ctlr_create.attr,
diff --git a/drivers/scsi/fcoe/fcoe_transport.c b/drivers/scsi/fcoe/fcoe_transport.c
index f4909cd206d3..29fe3426f9f2 100644
--- a/drivers/scsi/fcoe/fcoe_transport.c
+++ b/drivers/scsi/fcoe/fcoe_transport.c
@@ -855,7 +855,6 @@ out_nodev:
mutex_unlock(&ft_mutex);
return rc;
}
-EXPORT_SYMBOL(fcoe_ctlr_destroy_store);
/**
* fcoe_transport_create() - Create a fcoe interface
@@ -873,7 +872,7 @@ static int fcoe_transport_create(const char *buffer,
int rc = -ENODEV;
struct net_device *netdev = NULL;
struct fcoe_transport *ft = NULL;
- enum fip_state fip_mode = (enum fip_state)(long)kp->arg;
+ enum fip_mode fip_mode = (enum fip_mode)kp->arg;
mutex_lock(&ft_mutex);
diff --git a/drivers/scsi/fnic/fnic.h b/drivers/scsi/fnic/fnic.h
index d094ba59ed15..477513dc23b7 100644
--- a/drivers/scsi/fnic/fnic.h
+++ b/drivers/scsi/fnic/fnic.h
@@ -39,7 +39,7 @@
#define DRV_NAME "fnic"
#define DRV_DESCRIPTION "Cisco FCoE HBA Driver"
-#define DRV_VERSION "1.6.0.34"
+#define DRV_VERSION "1.6.0.47"
#define PFX DRV_NAME ": "
#define DFX DRV_NAME "%d: "
@@ -49,7 +49,7 @@
#define FNIC_MAX_IO_REQ 1024 /* scsi_cmnd tag map entries */
#define FNIC_DFLT_IO_REQ 256 /* Default scsi_cmnd tag map entries */
#define FNIC_IO_LOCKS 64 /* IO locks: power of 2 */
-#define FNIC_DFLT_QUEUE_DEPTH 32
+#define FNIC_DFLT_QUEUE_DEPTH 256
#define FNIC_STATS_RATE_LIMIT 4 /* limit rate at which stats are pulled up */
/*
@@ -128,6 +128,7 @@
__fnic_set_state_flags(fnicp, st_flags, 1)
extern unsigned int fnic_log_level;
+extern unsigned int io_completions;
#define FNIC_MAIN_LOGGING 0x01
#define FNIC_FCS_LOGGING 0x02
@@ -196,6 +197,7 @@ enum fnic_state {
#define FNIC_WQ_MAX 1
#define FNIC_RQ_MAX 1
#define FNIC_CQ_MAX (FNIC_WQ_COPY_MAX + FNIC_WQ_MAX + FNIC_RQ_MAX)
+#define FNIC_DFLT_IO_COMPLETIONS 256
struct mempool;
diff --git a/drivers/scsi/fnic/fnic_debugfs.c b/drivers/scsi/fnic/fnic_debugfs.c
index 139fffa3658a..21991c99db7c 100644
--- a/drivers/scsi/fnic/fnic_debugfs.c
+++ b/drivers/scsi/fnic/fnic_debugfs.c
@@ -54,23 +54,9 @@ int fnic_debugfs_init(void)
{
int rc = -1;
fnic_trace_debugfs_root = debugfs_create_dir("fnic", NULL);
- if (!fnic_trace_debugfs_root) {
- printk(KERN_DEBUG "Cannot create debugfs root\n");
- return rc;
- }
-
- if (!fnic_trace_debugfs_root) {
- printk(KERN_DEBUG
- "fnic root directory doesn't exist in debugfs\n");
- return rc;
- }
fnic_stats_debugfs_root = debugfs_create_dir("statistics",
fnic_trace_debugfs_root);
- if (!fnic_stats_debugfs_root) {
- printk(KERN_DEBUG "Cannot create Statistics directory\n");
- return rc;
- }
/* Allocate memory to structure */
fc_trc_flag = (struct fc_trace_flag_type *)
@@ -356,39 +342,19 @@ static const struct file_operations fnic_trace_debugfs_fops = {
* it will also create file trace_enable to control enable/disable of
* trace logging into trace buffer.
*/
-int fnic_trace_debugfs_init(void)
+void fnic_trace_debugfs_init(void)
{
- int rc = -1;
- if (!fnic_trace_debugfs_root) {
- printk(KERN_DEBUG
- "FNIC Debugfs root directory doesn't exist\n");
- return rc;
- }
fnic_trace_enable = debugfs_create_file("tracing_enable",
S_IFREG|S_IRUGO|S_IWUSR,
fnic_trace_debugfs_root,
&(fc_trc_flag->fnic_trace),
&fnic_trace_ctrl_fops);
- if (!fnic_trace_enable) {
- printk(KERN_DEBUG
- "Cannot create trace_enable file under debugfs\n");
- return rc;
- }
-
fnic_trace_debugfs_file = debugfs_create_file("trace",
S_IFREG|S_IRUGO|S_IWUSR,
fnic_trace_debugfs_root,
&(fc_trc_flag->fnic_trace),
&fnic_trace_debugfs_fops);
-
- if (!fnic_trace_debugfs_file) {
- printk(KERN_DEBUG
- "Cannot create trace file under debugfs\n");
- return rc;
- }
- rc = 0;
- return rc;
}
/*
@@ -419,37 +385,20 @@ void fnic_trace_debugfs_terminate(void)
* trace logging into trace buffer.
*/
-int fnic_fc_trace_debugfs_init(void)
+void fnic_fc_trace_debugfs_init(void)
{
- int rc = -1;
-
- if (!fnic_trace_debugfs_root) {
- pr_err("fnic:Debugfs root directory doesn't exist\n");
- return rc;
- }
-
fnic_fc_trace_enable = debugfs_create_file("fc_trace_enable",
S_IFREG|S_IRUGO|S_IWUSR,
fnic_trace_debugfs_root,
&(fc_trc_flag->fc_trace),
&fnic_trace_ctrl_fops);
- if (!fnic_fc_trace_enable) {
- pr_err("fnic: Failed create fc_trace_enable file\n");
- return rc;
- }
-
fnic_fc_trace_clear = debugfs_create_file("fc_trace_clear",
S_IFREG|S_IRUGO|S_IWUSR,
fnic_trace_debugfs_root,
&(fc_trc_flag->fc_clear),
&fnic_trace_ctrl_fops);
- if (!fnic_fc_trace_clear) {
- pr_err("fnic: Failed to create fc_trace_enable file\n");
- return rc;
- }
-
fnic_fc_rdata_trace_debugfs_file =
debugfs_create_file("fc_trace_rdata",
S_IFREG|S_IRUGO|S_IWUSR,
@@ -457,24 +406,12 @@ int fnic_fc_trace_debugfs_init(void)
&(fc_trc_flag->fc_normal_file),
&fnic_trace_debugfs_fops);
- if (!fnic_fc_rdata_trace_debugfs_file) {
- pr_err("fnic: Failed create fc_rdata_trace file\n");
- return rc;
- }
-
fnic_fc_trace_debugfs_file =
debugfs_create_file("fc_trace",
S_IFREG|S_IRUGO|S_IWUSR,
fnic_trace_debugfs_root,
&(fc_trc_flag->fc_row_file),
&fnic_trace_debugfs_fops);
-
- if (!fnic_fc_trace_debugfs_file) {
- pr_err("fnic: Failed to create fc_trace file\n");
- return rc;
- }
- rc = 0;
- return rc;
}
/*
@@ -757,45 +694,26 @@ static const struct file_operations fnic_reset_debugfs_fops = {
* It will create file stats and reset_stats under statistics/host# directory
* to log per fnic stats.
*/
-int fnic_stats_debugfs_init(struct fnic *fnic)
+void fnic_stats_debugfs_init(struct fnic *fnic)
{
- int rc = -1;
char name[16];
snprintf(name, sizeof(name), "host%d", fnic->lport->host->host_no);
- if (!fnic_stats_debugfs_root) {
- printk(KERN_DEBUG "fnic_stats root doesn't exist\n");
- return rc;
- }
fnic->fnic_stats_debugfs_host = debugfs_create_dir(name,
fnic_stats_debugfs_root);
- if (!fnic->fnic_stats_debugfs_host) {
- printk(KERN_DEBUG "Cannot create host directory\n");
- return rc;
- }
fnic->fnic_stats_debugfs_file = debugfs_create_file("stats",
S_IFREG|S_IRUGO|S_IWUSR,
fnic->fnic_stats_debugfs_host,
fnic,
&fnic_stats_debugfs_fops);
- if (!fnic->fnic_stats_debugfs_file) {
- printk(KERN_DEBUG "Cannot create host stats file\n");
- return rc;
- }
fnic->fnic_reset_debugfs_file = debugfs_create_file("reset_stats",
S_IFREG|S_IRUGO|S_IWUSR,
fnic->fnic_stats_debugfs_host,
fnic,
&fnic_reset_debugfs_fops);
- if (!fnic->fnic_reset_debugfs_file) {
- printk(KERN_DEBUG "Cannot create host stats file\n");
- return rc;
- }
- rc = 0;
- return rc;
}
/*
diff --git a/drivers/scsi/fnic/fnic_fcs.c b/drivers/scsi/fnic/fnic_fcs.c
index 844ef688fa91..911a5adc289c 100644
--- a/drivers/scsi/fnic/fnic_fcs.c
+++ b/drivers/scsi/fnic/fnic_fcs.c
@@ -65,11 +65,21 @@ void fnic_handle_link(struct work_struct *work)
fnic->link_status = vnic_dev_link_status(fnic->vdev);
fnic->link_down_cnt = vnic_dev_link_down_cnt(fnic->vdev);
+ atomic64_set(&fnic->fnic_stats.misc_stats.current_port_speed,
+ vnic_dev_port_speed(fnic->vdev));
+ shost_printk(KERN_INFO, fnic->lport->host, "Current vnic speed set to : %llu\n",
+ (u64)atomic64_read(
+ &fnic->fnic_stats.misc_stats.current_port_speed));
+
switch (vnic_dev_port_speed(fnic->vdev)) {
case DCEM_PORTSPEED_10G:
fc_host_speed(fnic->lport->host) = FC_PORTSPEED_10GBIT;
fnic->lport->link_supported_speeds = FC_PORTSPEED_10GBIT;
break;
+ case DCEM_PORTSPEED_20G:
+ fc_host_speed(fnic->lport->host) = FC_PORTSPEED_20GBIT;
+ fnic->lport->link_supported_speeds = FC_PORTSPEED_20GBIT;
+ break;
case DCEM_PORTSPEED_25G:
fc_host_speed(fnic->lport->host) = FC_PORTSPEED_25GBIT;
fnic->lport->link_supported_speeds = FC_PORTSPEED_25GBIT;
diff --git a/drivers/scsi/fnic/fnic_io.h b/drivers/scsi/fnic/fnic_io.h
index e0bc659ed71f..1cb6a68c8e4e 100644
--- a/drivers/scsi/fnic/fnic_io.h
+++ b/drivers/scsi/fnic/fnic_io.h
@@ -70,9 +70,10 @@ enum fnic_port_speeds {
DCEM_PORTSPEED_NONE = 0,
DCEM_PORTSPEED_1G = 1000,
DCEM_PORTSPEED_10G = 10000,
+ DCEM_PORTSPEED_20G = 20000,
+ DCEM_PORTSPEED_25G = 25000,
DCEM_PORTSPEED_40G = 40000,
DCEM_PORTSPEED_4x10G = 41000,
- DCEM_PORTSPEED_25G = 25000,
DCEM_PORTSPEED_100G = 100000,
};
#endif /* _FNIC_IO_H_ */
diff --git a/drivers/scsi/fnic/fnic_isr.c b/drivers/scsi/fnic/fnic_isr.c
index 4e3a50202e8c..da4602b63495 100644
--- a/drivers/scsi/fnic/fnic_isr.c
+++ b/drivers/scsi/fnic/fnic_isr.c
@@ -51,7 +51,7 @@ static irqreturn_t fnic_isr_legacy(int irq, void *data)
}
if (pba & (1 << FNIC_INTX_WQ_RQ_COPYWQ)) {
- work_done += fnic_wq_copy_cmpl_handler(fnic, -1);
+ work_done += fnic_wq_copy_cmpl_handler(fnic, io_completions);
work_done += fnic_wq_cmpl_handler(fnic, -1);
work_done += fnic_rq_cmpl_handler(fnic, -1);
@@ -72,7 +72,7 @@ static irqreturn_t fnic_isr_msi(int irq, void *data)
fnic->fnic_stats.misc_stats.last_isr_time = jiffies;
atomic64_inc(&fnic->fnic_stats.misc_stats.isr_count);
- work_done += fnic_wq_copy_cmpl_handler(fnic, -1);
+ work_done += fnic_wq_copy_cmpl_handler(fnic, io_completions);
work_done += fnic_wq_cmpl_handler(fnic, -1);
work_done += fnic_rq_cmpl_handler(fnic, -1);
@@ -125,7 +125,7 @@ static irqreturn_t fnic_isr_msix_wq_copy(int irq, void *data)
fnic->fnic_stats.misc_stats.last_isr_time = jiffies;
atomic64_inc(&fnic->fnic_stats.misc_stats.isr_count);
- wq_copy_work_done = fnic_wq_copy_cmpl_handler(fnic, -1);
+ wq_copy_work_done = fnic_wq_copy_cmpl_handler(fnic, io_completions);
vnic_intr_return_credits(&fnic->intr[FNIC_MSIX_WQ_COPY],
wq_copy_work_done,
1 /* unmask intr */,
diff --git a/drivers/scsi/fnic/fnic_main.c b/drivers/scsi/fnic/fnic_main.c
index 5b3534b0deda..18584ab27c32 100644
--- a/drivers/scsi/fnic/fnic_main.c
+++ b/drivers/scsi/fnic/fnic_main.c
@@ -69,6 +69,11 @@ unsigned int fnic_log_level;
module_param(fnic_log_level, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(fnic_log_level, "bit mask of fnic logging levels");
+
+unsigned int io_completions = FNIC_DFLT_IO_COMPLETIONS;
+module_param(io_completions, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(io_completions, "Max CQ entries to process at a time");
+
unsigned int fnic_trace_max_pages = 16;
module_param(fnic_trace_max_pages, uint, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(fnic_trace_max_pages, "Total allocated memory pages "
@@ -178,6 +183,9 @@ static void fnic_get_host_speed(struct Scsi_Host *shost)
case DCEM_PORTSPEED_10G:
fc_host_speed(shost) = FC_PORTSPEED_10GBIT;
break;
+ case DCEM_PORTSPEED_20G:
+ fc_host_speed(shost) = FC_PORTSPEED_20GBIT;
+ break;
case DCEM_PORTSPEED_25G:
fc_host_speed(shost) = FC_PORTSPEED_25GBIT;
break;
@@ -500,7 +508,7 @@ static int fnic_cleanup(struct fnic *fnic)
}
/* Clean up completed IOs and FCS frames */
- fnic_wq_copy_cmpl_handler(fnic, -1);
+ fnic_wq_copy_cmpl_handler(fnic, io_completions);
fnic_wq_cmpl_handler(fnic, -1);
fnic_rq_cmpl_handler(fnic, -1);
@@ -578,12 +586,7 @@ static int fnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
host->transportt = fnic_fc_transport;
- err = fnic_stats_debugfs_init(fnic);
- if (err) {
- shost_printk(KERN_ERR, fnic->lport->host,
- "Failed to initialize debugfs for stats\n");
- fnic_stats_debugfs_remove(fnic);
- }
+ fnic_stats_debugfs_init(fnic);
/* Setup PCI resources */
pci_set_drvdata(pdev, fnic);
@@ -650,12 +653,20 @@ static int fnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
goto err_out_iounmap;
}
+ err = vnic_dev_cmd_init(fnic->vdev);
+ if (err) {
+ shost_printk(KERN_ERR, fnic->lport->host,
+ "vnic_dev_cmd_init() returns %d, aborting\n",
+ err);
+ goto err_out_vnic_unregister;
+ }
+
err = fnic_dev_wait(fnic->vdev, vnic_dev_open,
- vnic_dev_open_done, 0);
+ vnic_dev_open_done, CMD_OPENF_RQ_ENABLE_THEN_POST);
if (err) {
shost_printk(KERN_ERR, fnic->lport->host,
"vNIC dev open failed, aborting.\n");
- goto err_out_vnic_unregister;
+ goto err_out_dev_cmd_deinit;
}
err = vnic_dev_init(fnic->vdev, 0);
@@ -796,6 +807,7 @@ static int fnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
/* allocate RQ buffers and post them to RQ*/
for (i = 0; i < fnic->rq_count; i++) {
+ vnic_rq_enable(&fnic->rq[i]);
err = vnic_rq_fill(&fnic->rq[i], fnic_alloc_rq_frame);
if (err) {
shost_printk(KERN_ERR, fnic->lport->host,
@@ -870,15 +882,11 @@ static int fnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
/* Enable all queues */
for (i = 0; i < fnic->raw_wq_count; i++)
vnic_wq_enable(&fnic->wq[i]);
- for (i = 0; i < fnic->rq_count; i++)
- vnic_rq_enable(&fnic->rq[i]);
for (i = 0; i < fnic->wq_copy_count; i++)
vnic_wq_copy_enable(&fnic->wq_copy[i]);
fc_fabric_login(lp);
- vnic_dev_enable(fnic->vdev);
-
err = fnic_request_intr(fnic);
if (err) {
shost_printk(KERN_ERR, fnic->lport->host,
@@ -886,6 +894,8 @@ static int fnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
goto err_out_free_exch_mgr;
}
+ vnic_dev_enable(fnic->vdev);
+
for (i = 0; i < fnic->intr_count; i++)
vnic_intr_unmask(&fnic->intr[i]);
@@ -914,6 +924,7 @@ err_out_clear_intr:
fnic_clear_intr_mode(fnic);
err_out_dev_close:
vnic_dev_close(fnic->vdev);
+err_out_dev_cmd_deinit:
err_out_vnic_unregister:
vnic_dev_unregister(fnic->vdev);
err_out_iounmap:
diff --git a/drivers/scsi/fnic/fnic_scsi.c b/drivers/scsi/fnic/fnic_scsi.c
index cafbcfb85bfa..80608b53897b 100644
--- a/drivers/scsi/fnic/fnic_scsi.c
+++ b/drivers/scsi/fnic/fnic_scsi.c
@@ -180,20 +180,19 @@ void
__fnic_set_state_flags(struct fnic *fnic, unsigned long st_flags,
unsigned long clearbits)
{
- struct Scsi_Host *host = fnic->lport->host;
- int sh_locked = spin_is_locked(host->host_lock);
unsigned long flags = 0;
+ unsigned long host_lock_flags = 0;
- if (!sh_locked)
- spin_lock_irqsave(host->host_lock, flags);
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+ spin_lock_irqsave(fnic->lport->host->host_lock, host_lock_flags);
if (clearbits)
fnic->state_flags &= ~st_flags;
else
fnic->state_flags |= st_flags;
- if (!sh_locked)
- spin_unlock_irqrestore(host->host_lock, flags);
+ spin_unlock_irqrestore(fnic->lport->host->host_lock, host_lock_flags);
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
return;
}
@@ -1326,13 +1325,32 @@ int fnic_wq_copy_cmpl_handler(struct fnic *fnic, int copy_work_to_do)
unsigned int wq_work_done = 0;
unsigned int i, cq_index;
unsigned int cur_work_done;
+ struct misc_stats *misc_stats = &fnic->fnic_stats.misc_stats;
+ u64 start_jiffies = 0;
+ u64 end_jiffies = 0;
+ u64 delta_jiffies = 0;
+ u64 delta_ms = 0;
for (i = 0; i < fnic->wq_copy_count; i++) {
cq_index = i + fnic->raw_wq_count + fnic->rq_count;
+
+ start_jiffies = jiffies;
cur_work_done = vnic_cq_copy_service(&fnic->cq[cq_index],
fnic_fcpio_cmpl_handler,
copy_work_to_do);
+ end_jiffies = jiffies;
+
wq_work_done += cur_work_done;
+ delta_jiffies = end_jiffies - start_jiffies;
+ if (delta_jiffies >
+ (u64) atomic64_read(&misc_stats->max_isr_jiffies)) {
+ atomic64_set(&misc_stats->max_isr_jiffies,
+ delta_jiffies);
+ delta_ms = jiffies_to_msecs(delta_jiffies);
+ atomic64_set(&misc_stats->max_isr_time_ms, delta_ms);
+ atomic64_set(&misc_stats->corr_work_done,
+ cur_work_done);
+ }
}
return wq_work_done;
}
@@ -1397,8 +1415,9 @@ static void fnic_cleanup_io(struct fnic *fnic, int exclude_id)
cleanup_scsi_cmd:
sc->result = DID_TRANSPORT_DISRUPTED << 16;
FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
- "%s: sc duration = %lu DID_TRANSPORT_DISRUPTED\n",
- __func__, (jiffies - start_time));
+ "%s: tag:0x%x : sc:0x%p duration = %lu DID_TRANSPORT_DISRUPTED\n",
+ __func__, sc->request->tag, sc,
+ (jiffies - start_time));
if (atomic64_read(&fnic->io_cmpl_skip))
atomic64_dec(&fnic->io_cmpl_skip);
@@ -1407,6 +1426,11 @@ cleanup_scsi_cmd:
/* Complete the command to SCSI */
if (sc->scsi_done) {
+ if (!(CMD_FLAGS(sc) & FNIC_IO_ISSUED))
+ shost_printk(KERN_ERR, fnic->lport->host,
+ "Calling done for IO not issued to fw: tag:0x%x sc:0x%p\n",
+ sc->request->tag, sc);
+
FNIC_TRACE(fnic_cleanup_io,
sc->device->host->host_no, i, sc,
jiffies_to_msecs(jiffies - start_time),
diff --git a/drivers/scsi/fnic/fnic_stats.h b/drivers/scsi/fnic/fnic_stats.h
index 9daa6ada6fa0..086f729f3c46 100644
--- a/drivers/scsi/fnic/fnic_stats.h
+++ b/drivers/scsi/fnic/fnic_stats.h
@@ -97,6 +97,9 @@ struct vlan_stats {
struct misc_stats {
u64 last_isr_time;
u64 last_ack_time;
+ atomic64_t max_isr_jiffies;
+ atomic64_t max_isr_time_ms;
+ atomic64_t corr_work_done;
atomic64_t isr_count;
atomic64_t max_cq_entries;
atomic64_t ack_index_out_of_range;
@@ -113,6 +116,7 @@ struct misc_stats {
atomic64_t queue_fulls;
atomic64_t rport_not_ready;
atomic64_t frame_errors;
+ atomic64_t current_port_speed;
};
struct fnic_stats {
@@ -134,6 +138,6 @@ struct stats_debug_info {
};
int fnic_get_stats_data(struct stats_debug_info *, struct fnic_stats *);
-int fnic_stats_debugfs_init(struct fnic *);
+void fnic_stats_debugfs_init(struct fnic *);
void fnic_stats_debugfs_remove(struct fnic *);
#endif /* _FNIC_STATS_H_ */
diff --git a/drivers/scsi/fnic/fnic_trace.c b/drivers/scsi/fnic/fnic_trace.c
index bf0fd2aeb92e..9621831e17ba 100644
--- a/drivers/scsi/fnic/fnic_trace.c
+++ b/drivers/scsi/fnic/fnic_trace.c
@@ -409,6 +409,9 @@ int fnic_get_stats_data(struct stats_debug_info *debug,
len += snprintf(debug->debug_buffer + len, buf_size - len,
"Last ISR time: %llu (%8llu.%09lu)\n"
"Last ACK time: %llu (%8llu.%09lu)\n"
+ "Max ISR jiffies: %llu\n"
+ "Max ISR time (ms) (0 denotes < 1 ms): %llu\n"
+ "Corr. work done: %llu\n"
"Number of ISRs: %lld\n"
"Maximum CQ Entries: %lld\n"
"Number of ACK index out of range: %lld\n"
@@ -428,6 +431,9 @@ int fnic_get_stats_data(struct stats_debug_info *debug,
(s64)val1.tv_sec, val1.tv_nsec,
(u64)stats->misc_stats.last_ack_time,
(s64)val2.tv_sec, val2.tv_nsec,
+ (u64)atomic64_read(&stats->misc_stats.max_isr_jiffies),
+ (u64)atomic64_read(&stats->misc_stats.max_isr_time_ms),
+ (u64)atomic64_read(&stats->misc_stats.corr_work_done),
(u64)atomic64_read(&stats->misc_stats.isr_count),
(u64)atomic64_read(&stats->misc_stats.max_cq_entries),
(u64)atomic64_read(&stats->misc_stats.ack_index_out_of_range),
@@ -446,6 +452,11 @@ int fnic_get_stats_data(struct stats_debug_info *debug,
(u64)atomic64_read(&stats->misc_stats.rport_not_ready),
(u64)atomic64_read(&stats->misc_stats.frame_errors));
+ len += snprintf(debug->debug_buffer + len, buf_size - len,
+ "Firmware reported port seed: %llu\n",
+ (u64)atomic64_read(
+ &stats->misc_stats.current_port_speed));
+
return len;
}
@@ -503,15 +514,10 @@ int fnic_trace_buf_init(void)
fnic_trace_entries.page_offset[i] = fnic_buf_head;
fnic_buf_head += FNIC_ENTRY_SIZE_BYTES;
}
- err = fnic_trace_debugfs_init();
- if (err < 0) {
- pr_err("fnic: Failed to initialize debugfs for tracing\n");
- goto err_fnic_trace_debugfs_init;
- }
+ fnic_trace_debugfs_init();
pr_info("fnic: Successfully Initialized Trace Buffer\n");
return err;
-err_fnic_trace_debugfs_init:
- fnic_trace_free();
+
err_fnic_trace_buf_init:
return err;
}
@@ -596,16 +602,10 @@ int fnic_fc_trace_init(void)
fc_trace_entries.page_offset[i] = fc_trace_buf_head;
fc_trace_buf_head += FC_TRC_SIZE_BYTES;
}
- err = fnic_fc_trace_debugfs_init();
- if (err < 0) {
- pr_err("fnic: Failed to initialize FC_CTLR tracing.\n");
- goto err_fnic_fc_ctlr_trace_debugfs_init;
- }
+ fnic_fc_trace_debugfs_init();
pr_info("fnic: Successfully Initialized FC_CTLR Trace Buffer\n");
return err;
-err_fnic_fc_ctlr_trace_debugfs_init:
- fnic_fc_trace_free();
err_fnic_fc_ctlr_trace_buf_init:
return err;
}
diff --git a/drivers/scsi/fnic/fnic_trace.h b/drivers/scsi/fnic/fnic_trace.h
index e375d0c2eaaf..8aa55c1e2025 100644
--- a/drivers/scsi/fnic/fnic_trace.h
+++ b/drivers/scsi/fnic/fnic_trace.h
@@ -111,7 +111,7 @@ int fnic_trace_buf_init(void);
void fnic_trace_free(void);
int fnic_debugfs_init(void);
void fnic_debugfs_terminate(void);
-int fnic_trace_debugfs_init(void);
+void fnic_trace_debugfs_init(void);
void fnic_trace_debugfs_terminate(void);
/* Fnic FC CTLR Trace releated function */
@@ -123,7 +123,7 @@ int fnic_fc_trace_get_data(fnic_dbgfs_t *fnic_dbgfs_prt, u8 rdata_flag);
void copy_and_format_trace_data(struct fc_trace_hdr *tdata,
fnic_dbgfs_t *fnic_dbgfs_prt,
int *len, u8 rdata_flag);
-int fnic_fc_trace_debugfs_init(void);
+void fnic_fc_trace_debugfs_init(void);
void fnic_fc_trace_debugfs_terminate(void);
#endif
diff --git a/drivers/scsi/fnic/vnic_dev.c b/drivers/scsi/fnic/vnic_dev.c
index 434447ea24b8..78af9cc2009b 100644
--- a/drivers/scsi/fnic/vnic_dev.c
+++ b/drivers/scsi/fnic/vnic_dev.c
@@ -27,6 +27,24 @@
#include "vnic_devcmd.h"
#include "vnic_dev.h"
#include "vnic_stats.h"
+#include "vnic_wq.h"
+
+struct devcmd2_controller {
+ struct vnic_wq_ctrl *wq_ctrl;
+ struct vnic_dev_ring results_ring;
+ struct vnic_wq wq;
+ struct vnic_devcmd2 *cmd_ring;
+ struct devcmd2_result *result;
+ u16 next_result;
+ u16 result_size;
+ int color;
+};
+
+enum vnic_proxy_type {
+ PROXY_NONE,
+ PROXY_BY_BDF,
+ PROXY_BY_INDEX,
+};
struct vnic_res {
void __iomem *vaddr;
@@ -48,6 +66,12 @@ struct vnic_dev {
dma_addr_t stats_pa;
struct vnic_devcmd_fw_info *fw_info;
dma_addr_t fw_info_pa;
+ enum vnic_proxy_type proxy;
+ u32 proxy_index;
+ u64 args[VNIC_DEVCMD_NARGS];
+ struct devcmd2_controller *devcmd2;
+ int (*devcmd_rtn)(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd,
+ int wait);
};
#define VNIC_MAX_RES_HDR_SIZE \
@@ -119,6 +143,7 @@ static int vnic_dev_discover_res(struct vnic_dev *vdev,
}
break;
case RES_TYPE_INTR_PBA_LEGACY:
+ case RES_TYPE_DEVCMD2:
case RES_TYPE_DEVCMD:
len = count;
break;
@@ -229,8 +254,7 @@ void vnic_dev_free_desc_ring(struct vnic_dev *vdev, struct vnic_dev_ring *ring)
}
}
-int vnic_dev_cmd(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd,
- u64 *a0, u64 *a1, int wait)
+int vnic_dev_cmd1(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd, int wait)
{
struct vnic_devcmd __iomem *devcmd = vdev->devcmd;
int delay;
@@ -244,6 +268,8 @@ int vnic_dev_cmd(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd,
EBUSY, /* ERR_EBUSY */
};
int err;
+ u64 *a0 = &vdev->args[0];
+ u64 *a1 = &vdev->args[1];
status = ioread32(&devcmd->status);
if (status & STAT_BUSY) {
@@ -290,6 +316,223 @@ int vnic_dev_cmd(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd,
return -ETIMEDOUT;
}
+int vnic_dev_cmd2(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd,
+ int wait)
+{
+ struct devcmd2_controller *dc2c = vdev->devcmd2;
+ struct devcmd2_result *result;
+ u8 color;
+ unsigned int i;
+ int delay;
+ int err;
+ u32 fetch_index;
+ u32 posted;
+ u32 new_posted;
+
+ posted = ioread32(&dc2c->wq_ctrl->posted_index);
+ fetch_index = ioread32(&dc2c->wq_ctrl->fetch_index);
+
+ if (posted == 0xFFFFFFFF || fetch_index == 0xFFFFFFFF) {
+ /* Hardware surprise removal: return error */
+ pr_err("%s: devcmd2 invalid posted or fetch index on cmd %d\n",
+ pci_name(vdev->pdev), _CMD_N(cmd));
+ pr_err("%s: fetch index: %u, posted index: %u\n",
+ pci_name(vdev->pdev), fetch_index, posted);
+
+ return -ENODEV;
+
+ }
+
+ new_posted = (posted + 1) % DEVCMD2_RING_SIZE;
+
+ if (new_posted == fetch_index) {
+ pr_err("%s: devcmd2 wq full while issuing cmd %d\n",
+ pci_name(vdev->pdev), _CMD_N(cmd));
+ pr_err("%s: fetch index: %u, posted index: %u\n",
+ pci_name(vdev->pdev), fetch_index, posted);
+ return -EBUSY;
+
+ }
+ dc2c->cmd_ring[posted].cmd = cmd;
+ dc2c->cmd_ring[posted].flags = 0;
+
+ if ((_CMD_FLAGS(cmd) & _CMD_FLAGS_NOWAIT))
+ dc2c->cmd_ring[posted].flags |= DEVCMD2_FNORESULT;
+ if (_CMD_DIR(cmd) & _CMD_DIR_WRITE) {
+ for (i = 0; i < VNIC_DEVCMD_NARGS; i++)
+ dc2c->cmd_ring[posted].args[i] = vdev->args[i];
+
+ }
+
+ /* Adding write memory barrier prevents compiler and/or CPU
+ * reordering, thus avoiding descriptor posting before
+ * descriptor is initialized. Otherwise, hardware can read
+ * stale descriptor fields.
+ */
+ wmb();
+ iowrite32(new_posted, &dc2c->wq_ctrl->posted_index);
+
+ if (dc2c->cmd_ring[posted].flags & DEVCMD2_FNORESULT)
+ return 0;
+
+ result = dc2c->result + dc2c->next_result;
+ color = dc2c->color;
+
+ dc2c->next_result++;
+ if (dc2c->next_result == dc2c->result_size) {
+ dc2c->next_result = 0;
+ dc2c->color = dc2c->color ? 0 : 1;
+ }
+
+ for (delay = 0; delay < wait; delay++) {
+ udelay(100);
+ if (result->color == color) {
+ if (result->error) {
+ err = -(int) result->error;
+ if (err != ERR_ECMDUNKNOWN ||
+ cmd != CMD_CAPABILITY)
+ pr_err("%s:Error %d devcmd %d\n",
+ pci_name(vdev->pdev),
+ err, _CMD_N(cmd));
+ return err;
+ }
+ if (_CMD_DIR(cmd) & _CMD_DIR_READ) {
+ rmb(); /*prevent reorder while reding result*/
+ for (i = 0; i < VNIC_DEVCMD_NARGS; i++)
+ vdev->args[i] = result->results[i];
+ }
+ return 0;
+ }
+ }
+
+ pr_err("%s:Timed out devcmd %d\n", pci_name(vdev->pdev), _CMD_N(cmd));
+
+ return -ETIMEDOUT;
+}
+
+
+int vnic_dev_init_devcmd1(struct vnic_dev *vdev)
+{
+ vdev->devcmd = vnic_dev_get_res(vdev, RES_TYPE_DEVCMD, 0);
+ if (!vdev->devcmd)
+ return -ENODEV;
+
+ vdev->devcmd_rtn = &vnic_dev_cmd1;
+ return 0;
+}
+
+
+int vnic_dev_init_devcmd2(struct vnic_dev *vdev)
+{
+ int err;
+ unsigned int fetch_index;
+
+ if (vdev->devcmd2)
+ return 0;
+
+ vdev->devcmd2 = kzalloc(sizeof(*vdev->devcmd2), GFP_ATOMIC);
+ if (!vdev->devcmd2)
+ return -ENOMEM;
+
+ vdev->devcmd2->color = 1;
+ vdev->devcmd2->result_size = DEVCMD2_RING_SIZE;
+ err = vnic_wq_devcmd2_alloc(vdev, &vdev->devcmd2->wq,
+ DEVCMD2_RING_SIZE, DEVCMD2_DESC_SIZE);
+ if (err)
+ goto err_free_devcmd2;
+
+ fetch_index = ioread32(&vdev->devcmd2->wq.ctrl->fetch_index);
+ if (fetch_index == 0xFFFFFFFF) { /* check for hardware gone */
+ pr_err("error in devcmd2 init");
+ return -ENODEV;
+ }
+
+ /*
+ * Don't change fetch_index ever and
+ * set posted_index same as fetch_index
+ * when setting up the WQ for devcmd2.
+ */
+ vnic_wq_init_start(&vdev->devcmd2->wq, 0, fetch_index,
+ fetch_index, 0, 0);
+
+ vnic_wq_enable(&vdev->devcmd2->wq);
+
+ err = vnic_dev_alloc_desc_ring(vdev, &vdev->devcmd2->results_ring,
+ DEVCMD2_RING_SIZE, DEVCMD2_DESC_SIZE);
+ if (err)
+ goto err_free_wq;
+
+ vdev->devcmd2->result =
+ (struct devcmd2_result *) vdev->devcmd2->results_ring.descs;
+ vdev->devcmd2->cmd_ring =
+ (struct vnic_devcmd2 *) vdev->devcmd2->wq.ring.descs;
+ vdev->devcmd2->wq_ctrl = vdev->devcmd2->wq.ctrl;
+ vdev->args[0] = (u64) vdev->devcmd2->results_ring.base_addr |
+ VNIC_PADDR_TARGET;
+ vdev->args[1] = DEVCMD2_RING_SIZE;
+
+ err = vnic_dev_cmd2(vdev, CMD_INITIALIZE_DEVCMD2, 1000);
+ if (err)
+ goto err_free_desc_ring;
+
+ vdev->devcmd_rtn = &vnic_dev_cmd2;
+
+ return 0;
+
+err_free_desc_ring:
+ vnic_dev_free_desc_ring(vdev, &vdev->devcmd2->results_ring);
+err_free_wq:
+ vnic_wq_disable(&vdev->devcmd2->wq);
+ vnic_wq_free(&vdev->devcmd2->wq);
+err_free_devcmd2:
+ kfree(vdev->devcmd2);
+ vdev->devcmd2 = NULL;
+
+ return err;
+}
+
+
+void vnic_dev_deinit_devcmd2(struct vnic_dev *vdev)
+{
+ vnic_dev_free_desc_ring(vdev, &vdev->devcmd2->results_ring);
+ vnic_wq_disable(&vdev->devcmd2->wq);
+ vnic_wq_free(&vdev->devcmd2->wq);
+ kfree(vdev->devcmd2);
+ vdev->devcmd2 = NULL;
+ vdev->devcmd_rtn = &vnic_dev_cmd1;
+}
+
+
+int vnic_dev_cmd_no_proxy(struct vnic_dev *vdev,
+ enum vnic_devcmd_cmd cmd, u64 *a0, u64 *a1, int wait)
+{
+ int err;
+
+ vdev->args[0] = *a0;
+ vdev->args[1] = *a1;
+
+ err = (*vdev->devcmd_rtn)(vdev, cmd, wait);
+
+ *a0 = vdev->args[0];
+ *a1 = vdev->args[1];
+
+ return err;
+}
+
+
+int vnic_dev_cmd(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd,
+ u64 *a0, u64 *a1, int wait)
+{
+ memset(vdev->args, 0, sizeof(vdev->args));
+
+ switch (vdev->proxy) {
+ case PROXY_NONE:
+ default:
+ return vnic_dev_cmd_no_proxy(vdev, cmd, a0, a1, wait);
+ }
+}
+
+
int vnic_dev_fw_info(struct vnic_dev *vdev,
struct vnic_devcmd_fw_info **fw_info)
{
@@ -664,6 +907,8 @@ void vnic_dev_unregister(struct vnic_dev *vdev)
dma_free_coherent(&vdev->pdev->dev,
sizeof(struct vnic_devcmd_fw_info),
vdev->fw_info, vdev->fw_info_pa);
+ if (vdev->devcmd2)
+ vnic_dev_deinit_devcmd2(vdev);
kfree(vdev);
}
}
@@ -683,13 +928,26 @@ struct vnic_dev *vnic_dev_register(struct vnic_dev *vdev,
if (vnic_dev_discover_res(vdev, bar))
goto err_out;
- vdev->devcmd = vnic_dev_get_res(vdev, RES_TYPE_DEVCMD, 0);
- if (!vdev->devcmd)
- goto err_out;
-
return vdev;
err_out:
vnic_dev_unregister(vdev);
return NULL;
}
+
+int vnic_dev_cmd_init(struct vnic_dev *vdev)
+{
+ int err;
+ void *p;
+
+ p = vnic_dev_get_res(vdev, RES_TYPE_DEVCMD2, 0);
+ if (p) {
+ pr_err("fnic: DEVCMD2 resource found!\n");
+ err = vnic_dev_init_devcmd2(vdev);
+ } else {
+ pr_err("fnic: DEVCMD2 not found, fall back to Devcmd\n");
+ err = vnic_dev_init_devcmd1(vdev);
+ }
+
+ return err;
+}
diff --git a/drivers/scsi/fnic/vnic_dev.h b/drivers/scsi/fnic/vnic_dev.h
index 40d4195f562b..ef5309a5df5d 100644
--- a/drivers/scsi/fnic/vnic_dev.h
+++ b/drivers/scsi/fnic/vnic_dev.h
@@ -36,6 +36,7 @@
#define vnic_dev_fw_info fnic_dev_fw_info
#define vnic_dev_spec fnic_dev_spec
#define vnic_dev_stats_clear fnic_dev_stats_clear
+#define vnic_dev_cmd_init fnic_dev_cmd_init
#define vnic_dev_stats_dump fnic_dev_stats_dump
#define vnic_dev_hang_notify fnic_dev_hang_notify
#define vnic_dev_packet_filter fnic_dev_packet_filter
@@ -128,6 +129,7 @@ int vnic_dev_fw_info(struct vnic_dev *vdev,
int vnic_dev_spec(struct vnic_dev *vdev, unsigned int offset,
unsigned int size, void *value);
int vnic_dev_stats_clear(struct vnic_dev *vdev);
+int vnic_dev_cmd_init(struct vnic_dev *vdev);
int vnic_dev_stats_dump(struct vnic_dev *vdev, struct vnic_stats **stats);
int vnic_dev_hang_notify(struct vnic_dev *vdev);
void vnic_dev_packet_filter(struct vnic_dev *vdev, int directed, int multicast,
diff --git a/drivers/scsi/fnic/vnic_devcmd.h b/drivers/scsi/fnic/vnic_devcmd.h
index 3e2fcbda6aed..c5dde556dc7c 100644
--- a/drivers/scsi/fnic/vnic_devcmd.h
+++ b/drivers/scsi/fnic/vnic_devcmd.h
@@ -170,7 +170,8 @@ enum vnic_devcmd_cmd {
/* variant of CMD_INIT, with provisioning info
* (u64)a0=paddr of vnic_devcmd_provinfo
- * (u32)a1=sizeof provision info */
+ * (u32)a1=sizeof provision info
+ */
CMD_INIT_PROV_INFO = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ENET, 27),
/* enable virtual link */
@@ -262,12 +263,132 @@ enum vnic_devcmd_cmd {
* non-zero for resetting vlan to the default
* out: (u16)a0=old default vlan
*/
- CMD_SET_DEFAULT_VLAN = _CMDC(_CMD_DIR_RW, _CMD_VTYPE_ALL, 46)
+ CMD_SET_DEFAULT_VLAN = _CMDC(_CMD_DIR_RW, _CMD_VTYPE_ALL, 46),
+
+ /* init_prov_info2:
+ * Variant of CMD_INIT_PROV_INFO, where it will not try to enable
+ * the vnic until CMD_ENABLE2 is issued.
+ * (u64)a0=paddr of vnic_devcmd_provinfo
+ * (u32)a1=sizeof provision info
+ */
+ CMD_INIT_PROV_INFO2 = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ENET, 47),
+
+ /* enable2:
+ * (u32)a0=0 ==> standby
+ * =CMD_ENABLE2_ACTIVE ==> active
+ */
+ CMD_ENABLE2 = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ENET, 48),
+
+ /*
+ * cmd_status:
+ * Returns the status of the specified command
+ * Input:
+ * a0 = command for which status is being queried.
+ * Possible values are:
+ * CMD_SOFT_RESET
+ * CMD_HANG_RESET
+ * CMD_OPEN
+ * CMD_INIT
+ * CMD_INIT_PROV_INFO
+ * CMD_DEINIT
+ * CMD_INIT_PROV_INFO2
+ * CMD_ENABLE2
+ * Output:
+ * if status == STAT_ERROR
+ * a0 = ERR_ENOTSUPPORTED - status for command in a0 is
+ * not supported
+ * if status == STAT_NONE
+ * a0 = status of the devcmd specified in a0 as follows.
+ * ERR_SUCCESS - command in a0 completed successfully
+ * ERR_EINPROGRESS - command in a0 is still in progress
+ */
+ CMD_STATUS = _CMDC(_CMD_DIR_RW, _CMD_VTYPE_ALL, 49),
+
+ /*
+ * Returns interrupt coalescing timer conversion factors.
+ * After calling this devcmd, ENIC driver can convert
+ * interrupt coalescing timer in usec into CPU cycles as follows:
+ *
+ * intr_timer_cycles = intr_timer_usec * multiplier / divisor
+ *
+ * Interrupt coalescing timer in usecs can be be converted/obtained
+ * from CPU cycles as follows:
+ *
+ * intr_timer_usec = intr_timer_cycles * divisor / multiplier
+ *
+ * in: none
+ * out: (u32)a0 = multiplier
+ * (u32)a1 = divisor
+ * (u32)a2 = maximum timer value in usec
+ */
+ CMD_INTR_COAL_CONVERT = _CMDC(_CMD_DIR_READ, _CMD_VTYPE_ALL, 50),
+
+ /*
+ * ISCSI DUMP API:
+ * in: (u64)a0=paddr of the param or param itself
+ * (u32)a1=ISCSI_CMD_xxx
+ */
+ CMD_ISCSI_DUMP_REQ = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ALL, 51),
+
+ /*
+ * ISCSI DUMP STATUS API:
+ * in: (u32)a0=cmd tag
+ * in: (u32)a1=ISCSI_CMD_xxx
+ * out: (u32)a0=cmd status
+ */
+ CMD_ISCSI_DUMP_STATUS = _CMDC(_CMD_DIR_RW, _CMD_VTYPE_ALL, 52),
+
+ /*
+ * Subvnic migration from MQ <--> VF.
+ * Enable the LIF migration from MQ to VF and vice versa. MQ and VF
+ * indexes are statically bound at the time of initialization.
+ * Based on the
+ * direction of migration, the resources of either MQ or the VF shall
+ * be attached to the LIF.
+ * in: (u32)a0=Direction of Migration
+ * 0=> Migrate to VF
+ * 1=> Migrate to MQ
+ * (u32)a1=VF index (MQ index)
+ */
+ CMD_MIGRATE_SUBVNIC = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ENET, 53),
+
+ /*
+ * Register / Deregister the notification block for MQ subvnics
+ * in:
+ * (u64)a0=paddr to notify (set paddr=0 to unset)
+ * (u32)a1 & 0x00000000ffffffff=sizeof(struct vnic_devcmd_notify)
+ * (u16)a1 & 0x0000ffff00000000=intr num (-1 for no intr)
+ * out:
+ * (u32)a1 = effective size
+ */
+ CMD_SUBVNIC_NOTIFY = _CMDC(_CMD_DIR_RW, _CMD_VTYPE_ALL, 54),
+
+ /*
+ * Set the predefined mac address as default
+ * in:
+ * (u48)a0=mac addr
+ */
+ CMD_SET_MAC_ADDR = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ENET, 55),
+
+ /* Update the provisioning info of the given VIF
+ * (u64)a0=paddr of vnic_devcmd_provinfo
+ * (u32)a1=sizeof provision info
+ */
+ CMD_PROV_INFO_UPDATE = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ENET, 56),
+
+ /*
+ * Initialization for the devcmd2 interface.
+ * in: (u64) a0=host result buffer physical address
+ * in: (u16) a1=number of entries in result buffer
+ */
+ CMD_INITIALIZE_DEVCMD2 = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ALL, 57)
};
/* flags for CMD_OPEN */
#define CMD_OPENF_OPROM 0x1 /* open coming from option rom */
+#define CMD_OPENF_RQ_ENABLE_THEN_POST 0x2
+
/* flags for CMD_INIT */
#define CMD_INITF_DEFAULT_MAC 0x1 /* init with default mac addr */
@@ -345,4 +466,39 @@ struct vnic_devcmd {
u64 args[VNIC_DEVCMD_NARGS]; /* RW cmd args (little-endian) */
};
+/*
+ * Version 2 of the interface.
+ *
+ * Some things are carried over, notably the vnic_devcmd_cmd enum.
+ */
+
+/*
+ * Flags for vnic_devcmd2.flags
+ */
+
+#define DEVCMD2_FNORESULT 0x1 /* Don't copy result to host */
+
+#define VNIC_DEVCMD2_NARGS VNIC_DEVCMD_NARGS
+
+struct vnic_devcmd2 {
+ u16 pad;
+ u16 flags;
+ u32 cmd; /* same command #defines as original */
+ u64 args[VNIC_DEVCMD2_NARGS];
+};
+
+#define VNIC_DEVCMD2_NRESULTS VNIC_DEVCMD_NARGS
+struct devcmd2_result {
+ u64 results[VNIC_DEVCMD2_NRESULTS];
+ u32 pad;
+ u16 completed_index; /* into copy WQ */
+ u8 error; /* same error codes as original */
+ u8 color; /* 0 or 1 as with completion queues */
+};
+
+#define DEVCMD2_RING_SIZE 32
+#define DEVCMD2_DESC_SIZE 128
+
+#define DEVCMD2_RESULTS_SIZE_MAX ((1 << 16) - 1)
+
#endif /* _VNIC_DEVCMD_H_ */
diff --git a/drivers/scsi/fnic/vnic_resource.h b/drivers/scsi/fnic/vnic_resource.h
index 2d842f79d41a..7c6163f73bd3 100644
--- a/drivers/scsi/fnic/vnic_resource.h
+++ b/drivers/scsi/fnic/vnic_resource.h
@@ -41,6 +41,13 @@ enum vnic_res_type {
RES_TYPE_RSVD7,
RES_TYPE_DEVCMD, /* Device command region */
RES_TYPE_PASS_THRU_PAGE, /* Pass-thru page */
+ RES_TYPE_SUBVNIC, /* subvnic resource type */
+ RES_TYPE_MQ_WQ, /* MQ Work queues */
+ RES_TYPE_MQ_RQ, /* MQ Receive queues */
+ RES_TYPE_MQ_CQ, /* MQ Completion queues */
+ RES_TYPE_DEPRECATED1, /* Old version of devcmd 2 */
+ RES_TYPE_DEPRECATED2, /* Old version of devcmd 2 */
+ RES_TYPE_DEVCMD2, /* Device control region */
RES_TYPE_MAX, /* Count of resource types */
};
diff --git a/drivers/scsi/fnic/vnic_rq.c b/drivers/scsi/fnic/vnic_rq.c
index fd2068f5ae16..6a35b1be0032 100644
--- a/drivers/scsi/fnic/vnic_rq.c
+++ b/drivers/scsi/fnic/vnic_rq.c
@@ -27,12 +27,9 @@
static int vnic_rq_alloc_bufs(struct vnic_rq *rq)
{
struct vnic_rq_buf *buf;
- struct vnic_dev *vdev;
unsigned int i, j, count = rq->ring.desc_count;
unsigned int blks = VNIC_RQ_BUF_BLKS_NEEDED(count);
- vdev = rq->vdev;
-
for (i = 0; i < blks; i++) {
rq->bufs[i] = kzalloc(VNIC_RQ_BUF_BLK_SZ, GFP_ATOMIC);
if (!rq->bufs[i]) {
@@ -171,7 +168,7 @@ void vnic_rq_clean(struct vnic_rq *rq,
struct vnic_rq_buf *buf;
u32 fetch_index;
- BUG_ON(ioread32(&rq->ctrl->enable));
+ WARN_ON(ioread32(&rq->ctrl->enable));
buf = rq->to_clean;
diff --git a/drivers/scsi/fnic/vnic_wq.c b/drivers/scsi/fnic/vnic_wq.c
index a414135460db..015af2cdabaf 100644
--- a/drivers/scsi/fnic/vnic_wq.c
+++ b/drivers/scsi/fnic/vnic_wq.c
@@ -24,15 +24,32 @@
#include "vnic_dev.h"
#include "vnic_wq.h"
+
+int vnic_wq_get_ctrl(struct vnic_dev *vdev, struct vnic_wq *wq,
+ unsigned int index, enum vnic_res_type res_type)
+{
+ wq->ctrl = vnic_dev_get_res(vdev, res_type, index);
+
+ if (!wq->ctrl)
+ return -EINVAL;
+
+ return 0;
+}
+
+
+int vnic_wq_alloc_ring(struct vnic_dev *vdev, struct vnic_wq *wq,
+ unsigned int desc_count, unsigned int desc_size)
+{
+ return vnic_dev_alloc_desc_ring(vdev, &wq->ring, desc_count, desc_size);
+}
+
+
static int vnic_wq_alloc_bufs(struct vnic_wq *wq)
{
struct vnic_wq_buf *buf;
- struct vnic_dev *vdev;
unsigned int i, j, count = wq->ring.desc_count;
unsigned int blks = VNIC_WQ_BUF_BLKS_NEEDED(count);
- vdev = wq->vdev;
-
for (i = 0; i < blks; i++) {
wq->bufs[i] = kzalloc(VNIC_WQ_BUF_BLK_SZ, GFP_ATOMIC);
if (!wq->bufs[i]) {
@@ -111,6 +128,52 @@ int vnic_wq_alloc(struct vnic_dev *vdev, struct vnic_wq *wq, unsigned int index,
return 0;
}
+
+int vnic_wq_devcmd2_alloc(struct vnic_dev *vdev, struct vnic_wq *wq,
+ unsigned int desc_count, unsigned int desc_size)
+{
+ int err;
+
+ wq->index = 0;
+ wq->vdev = vdev;
+
+ err = vnic_wq_get_ctrl(vdev, wq, 0, RES_TYPE_DEVCMD2);
+ if (err) {
+ pr_err("Failed to get devcmd2 resource\n");
+ return err;
+ }
+ vnic_wq_disable(wq);
+
+ err = vnic_wq_alloc_ring(vdev, wq, desc_count, desc_size);
+ if (err)
+ return err;
+ return 0;
+}
+
+void vnic_wq_init_start(struct vnic_wq *wq, unsigned int cq_index,
+ unsigned int fetch_index, unsigned int posted_index,
+ unsigned int error_interrupt_enable,
+ unsigned int error_interrupt_offset)
+{
+ u64 paddr;
+ unsigned int count = wq->ring.desc_count;
+
+ paddr = (u64)wq->ring.base_addr | VNIC_PADDR_TARGET;
+ writeq(paddr, &wq->ctrl->ring_base);
+ iowrite32(count, &wq->ctrl->ring_size);
+ iowrite32(fetch_index, &wq->ctrl->fetch_index);
+ iowrite32(posted_index, &wq->ctrl->posted_index);
+ iowrite32(cq_index, &wq->ctrl->cq_index);
+ iowrite32(error_interrupt_enable, &wq->ctrl->error_interrupt_enable);
+ iowrite32(error_interrupt_offset, &wq->ctrl->error_interrupt_offset);
+ iowrite32(0, &wq->ctrl->error_status);
+
+ wq->to_use = wq->to_clean =
+ &wq->bufs[fetch_index / VNIC_WQ_BUF_BLK_ENTRIES]
+ [fetch_index % VNIC_WQ_BUF_BLK_ENTRIES];
+}
+
+
void vnic_wq_init(struct vnic_wq *wq, unsigned int cq_index,
unsigned int error_interrupt_enable,
unsigned int error_interrupt_offset)
diff --git a/drivers/scsi/fnic/vnic_wq.h b/drivers/scsi/fnic/vnic_wq.h
index 5cd094f79281..5d1e0a44d94a 100644
--- a/drivers/scsi/fnic/vnic_wq.h
+++ b/drivers/scsi/fnic/vnic_wq.h
@@ -33,6 +33,8 @@
#define vnic_wq_service fnic_wq_service
#define vnic_wq_free fnic_wq_free
#define vnic_wq_alloc fnic_wq_alloc
+#define vnic_wq_devcmd2_alloc fnic_wq_devcmd2_alloc
+#define vnic_wq_init_start fnic_wq_init_start
#define vnic_wq_init fnic_wq_init
#define vnic_wq_error_status fnic_wq_error_status
#define vnic_wq_enable fnic_wq_enable
@@ -163,6 +165,12 @@ static inline void vnic_wq_service(struct vnic_wq *wq,
void vnic_wq_free(struct vnic_wq *wq);
int vnic_wq_alloc(struct vnic_dev *vdev, struct vnic_wq *wq, unsigned int index,
unsigned int desc_count, unsigned int desc_size);
+int vnic_wq_devcmd2_alloc(struct vnic_dev *vdev, struct vnic_wq *wq,
+ unsigned int desc_count, unsigned int desc_size);
+void vnic_wq_init_start(struct vnic_wq *wq, unsigned int cq_index,
+ unsigned int fetch_index, unsigned int posted_index,
+ unsigned int error_interrupt_enable,
+ unsigned int error_interrupt_offset);
void vnic_wq_init(struct vnic_wq *wq, unsigned int cq_index,
unsigned int error_interrupt_enable,
unsigned int error_interrupt_offset);
diff --git a/drivers/scsi/gdth.c b/drivers/scsi/gdth.c
index 194c294f9b6c..e7f1dd4f3b66 100644
--- a/drivers/scsi/gdth.c
+++ b/drivers/scsi/gdth.c
@@ -1,6 +1,6 @@
/************************************************************************
* Linux driver for *
- * ICP vortex GmbH: GDT ISA/EISA/PCI Disk Array Controllers *
+ * ICP vortex GmbH: GDT PCI Disk Array Controllers *
* Intel Corporation: Storage RAID Controllers *
* *
* gdth.c *
@@ -32,15 +32,10 @@
************************************************************************/
/* All GDT Disk Array Controllers are fully supported by this driver.
- * This includes the PCI/EISA/ISA SCSI Disk Array Controllers and the
+ * This includes the PCI SCSI Disk Array Controllers and the
* PCI Fibre Channel Disk Array Controllers. See gdth.h for a complete
* list of all controller types.
*
- * If you have one or more GDT3000/3020 EISA controllers with
- * controller BIOS disabled, you have to set the IRQ values with the
- * command line option "gdth=irq1,irq2,...", where the irq1,irq2,... are
- * the IRQ values for the EISA controllers.
- *
* After the optional list of IRQ values, other possible
* command line options are:
* disable:Y disable driver
@@ -61,14 +56,12 @@
* access a shared resource from several nodes,
* appropriate controller firmware required
* shared_access:N enable driver reserve/release protocol
- * probe_eisa_isa:Y scan for EISA/ISA controllers
- * probe_eisa_isa:N do not scan for EISA/ISA controllers
* force_dma32:Y use only 32 bit DMA mode
* force_dma32:N use 64 bit DMA mode, if supported
*
* The default values are: "gdth=disable:N,reserve_mode:1,reverse_scan:N,
* max_ids:127,rescan:N,hdr_channel:0,
- * shared_access:Y,probe_eisa_isa:N,force_dma32:N".
+ * shared_access:Y,force_dma32:N".
* Here is another example: "gdth=reserve_list:0,1,2,0,0,1,3,0,rescan:Y".
*
* When loading the gdth driver as a module, the same options are available.
@@ -79,7 +72,7 @@
*
* Default: "modprobe gdth disable=0 reserve_mode=1 reverse_scan=0
* max_ids=127 rescan=0 hdr_channel=0 shared_access=0
- * probe_eisa_isa=0 force_dma32=0"
+ * force_dma32=0"
* The other example: "modprobe gdth reserve_list=0,1,2,0,0,1,3,0 rescan=1".
*/
@@ -96,10 +89,6 @@
* phase: unused
*/
-
-/* interrupt coalescing */
-/* #define INT_COAL */
-
/* statistics */
#define GDTH_STATISTICS
@@ -122,10 +111,6 @@
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/slab.h>
-
-#ifdef GDTH_RTC
-#include <linux/mc146818rtc.h>
-#endif
#include <linux/reboot.h>
#include <asm/dma.h>
@@ -192,79 +177,9 @@ static void gdth_scsi_done(struct scsi_cmnd *scp);
#ifdef DEBUG_GDTH
static u8 DebugState = DEBUG_GDTH;
-
-#ifdef __SERIAL__
-#define MAX_SERBUF 160
-static void ser_init(void);
-static void ser_puts(char *str);
-static void ser_putc(char c);
-static int ser_printk(const char *fmt, ...);
-static char strbuf[MAX_SERBUF+1];
-#ifdef __COM2__
-#define COM_BASE 0x2f8
-#else
-#define COM_BASE 0x3f8
-#endif
-static void ser_init()
-{
- unsigned port=COM_BASE;
-
- outb(0x80,port+3);
- outb(0,port+1);
- /* 19200 Baud, if 9600: outb(12,port) */
- outb(6, port);
- outb(3,port+3);
- outb(0,port+1);
- /*
- ser_putc('I');
- ser_putc(' ');
- */
-}
-
-static void ser_puts(char *str)
-{
- char *ptr;
-
- ser_init();
- for (ptr=str;*ptr;++ptr)
- ser_putc(*ptr);
-}
-
-static void ser_putc(char c)
-{
- unsigned port=COM_BASE;
-
- while ((inb(port+5) & 0x20)==0);
- outb(c,port);
- if (c==0x0a)
- {
- while ((inb(port+5) & 0x20)==0);
- outb(0x0d,port);
- }
-}
-
-static int ser_printk(const char *fmt, ...)
-{
- va_list args;
- int i;
-
- va_start(args,fmt);
- i = vsprintf(strbuf,fmt,args);
- ser_puts(strbuf);
- va_end(args);
- return i;
-}
-
-#define TRACE(a) {if (DebugState==1) {ser_printk a;}}
-#define TRACE2(a) {if (DebugState==1 || DebugState==2) {ser_printk a;}}
-#define TRACE3(a) {if (DebugState!=0) {ser_printk a;}}
-
-#else /* !__SERIAL__ */
#define TRACE(a) {if (DebugState==1) {printk a;}}
#define TRACE2(a) {if (DebugState==1 || DebugState==2) {printk a;}}
#define TRACE3(a) {if (DebugState!=0) {printk a;}}
-#endif
-
#else /* !DEBUG */
#define TRACE(a)
#define TRACE2(a)
@@ -273,9 +188,6 @@ static int ser_printk(const char *fmt, ...)
#ifdef GDTH_STATISTICS
static u32 max_rq=0, max_index=0, max_sg=0;
-#ifdef INT_COAL
-static u32 max_int_coal=0;
-#endif
static u32 act_ints=0, act_ios=0, act_stats=0, act_rq=0;
static struct timer_list gdth_timer;
#endif
@@ -286,12 +198,6 @@ static struct timer_list gdth_timer;
#define BUS_L2P(a,b) ((b)>(a)->virt_bus ? (b-1):(b))
-#ifdef CONFIG_ISA
-static u8 gdth_drq_tab[4] = {5,6,7,7}; /* DRQ table */
-#endif
-#if defined(CONFIG_EISA) || defined(CONFIG_ISA)
-static u8 gdth_irq_tab[6] = {0,10,11,12,14,0}; /* IRQ table */
-#endif
static u8 gdth_polling; /* polling if TRUE */
static int gdth_ctr_count = 0; /* controller count */
static LIST_HEAD(gdth_instances); /* controller list */
@@ -325,10 +231,6 @@ static u8 gdth_direction_tab[0x100] = {
};
/* LILO and modprobe/insmod parameters */
-/* IRQ list for GDT3000/3020 EISA controllers */
-static int irq[MAXHA] __initdata =
-{0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
- 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};
/* disable driver flag */
static int disable __initdata = 0;
/* reserve flag */
@@ -348,13 +250,10 @@ static int max_ids = MAXID;
static int rescan = 0;
/* shared access */
static int shared_access = 1;
-/* enable support for EISA and ISA controllers */
-static int probe_eisa_isa = 0;
/* 64 bit DMA mode, support for drives > 2 TB, if force_dma32 = 0 */
static int force_dma32 = 0;
/* parameters for modprobe/insmod */
-module_param_hw_array(irq, int, irq, NULL, 0);
module_param(disable, int, 0);
module_param(reserve_mode, int, 0);
module_param_array(reserve_list, int, NULL, 0);
@@ -363,7 +262,6 @@ module_param(hdr_channel, int, 0);
module_param(max_ids, int, 0);
module_param(rescan, int, 0);
module_param(shared_access, int, 0);
-module_param(probe_eisa_isa, int, 0);
module_param(force_dma32, int, 0);
MODULE_AUTHOR("Achim Leubner");
MODULE_LICENSE("GPL");
@@ -515,45 +413,6 @@ static void gdth_eval_mapping(u32 size, u32 *cyls, int *heads, int *secs)
}
}
-/* controller search and initialization functions */
-#ifdef CONFIG_EISA
-static int __init gdth_search_eisa(u16 eisa_adr)
-{
- u32 id;
-
- TRACE(("gdth_search_eisa() adr. %x\n",eisa_adr));
- id = inl(eisa_adr+ID0REG);
- if (id == GDT3A_ID || id == GDT3B_ID) { /* GDT3000A or GDT3000B */
- if ((inb(eisa_adr+EISAREG) & 8) == 0)
- return 0; /* not EISA configured */
- return 1;
- }
- if (id == GDT3_ID) /* GDT3000 */
- return 1;
-
- return 0;
-}
-#endif /* CONFIG_EISA */
-
-#ifdef CONFIG_ISA
-static int __init gdth_search_isa(u32 bios_adr)
-{
- void __iomem *addr;
- u32 id;
-
- TRACE(("gdth_search_isa() bios adr. %x\n",bios_adr));
- if ((addr = ioremap(bios_adr+BIOS_ID_OFFS, sizeof(u32))) != NULL) {
- id = readl(addr);
- iounmap(addr);
- if (id == GDT2_ID) /* GDT2000 */
- return 1;
- }
- return 0;
-}
-#endif /* CONFIG_ISA */
-
-#ifdef CONFIG_PCI
-
static bool gdth_search_vortex(u16 device)
{
if (device <= PCI_DEVICE_ID_VORTEX_GDT6555)
@@ -656,204 +515,7 @@ static int gdth_pci_init_one(struct pci_dev *pdev,
return 0;
}
-#endif /* CONFIG_PCI */
-#ifdef CONFIG_EISA
-static int __init gdth_init_eisa(u16 eisa_adr,gdth_ha_str *ha)
-{
- u32 retries,id;
- u8 prot_ver,eisacf,i,irq_found;
-
- TRACE(("gdth_init_eisa() adr. %x\n",eisa_adr));
-
- /* disable board interrupts, deinitialize services */
- outb(0xff,eisa_adr+EDOORREG);
- outb(0x00,eisa_adr+EDENABREG);
- outb(0x00,eisa_adr+EINTENABREG);
-
- outb(0xff,eisa_adr+LDOORREG);
- retries = INIT_RETRIES;
- gdth_delay(20);
- while (inb(eisa_adr+EDOORREG) != 0xff) {
- if (--retries == 0) {
- printk("GDT-EISA: Initialization error (DEINIT failed)\n");
- return 0;
- }
- gdth_delay(1);
- TRACE2(("wait for DEINIT: retries=%d\n",retries));
- }
- prot_ver = inb(eisa_adr+MAILBOXREG);
- outb(0xff,eisa_adr+EDOORREG);
- if (prot_ver != PROTOCOL_VERSION) {
- printk("GDT-EISA: Illegal protocol version\n");
- return 0;
- }
- ha->bmic = eisa_adr;
- ha->brd_phys = (u32)eisa_adr >> 12;
-
- outl(0,eisa_adr+MAILBOXREG);
- outl(0,eisa_adr+MAILBOXREG+4);
- outl(0,eisa_adr+MAILBOXREG+8);
- outl(0,eisa_adr+MAILBOXREG+12);
-
- /* detect IRQ */
- if ((id = inl(eisa_adr+ID0REG)) == GDT3_ID) {
- ha->oem_id = OEM_ID_ICP;
- ha->type = GDT_EISA;
- ha->stype = id;
- outl(1,eisa_adr+MAILBOXREG+8);
- outb(0xfe,eisa_adr+LDOORREG);
- retries = INIT_RETRIES;
- gdth_delay(20);
- while (inb(eisa_adr+EDOORREG) != 0xfe) {
- if (--retries == 0) {
- printk("GDT-EISA: Initialization error (get IRQ failed)\n");
- return 0;
- }
- gdth_delay(1);
- }
- ha->irq = inb(eisa_adr+MAILBOXREG);
- outb(0xff,eisa_adr+EDOORREG);
- TRACE2(("GDT3000/3020: IRQ=%d\n",ha->irq));
- /* check the result */
- if (ha->irq == 0) {
- TRACE2(("Unknown IRQ, use IRQ table from cmd line !\n"));
- for (i = 0, irq_found = FALSE;
- i < MAXHA && irq[i] != 0xff; ++i) {
- if (irq[i]==10 || irq[i]==11 || irq[i]==12 || irq[i]==14) {
- irq_found = TRUE;
- break;
- }
- }
- if (irq_found) {
- ha->irq = irq[i];
- irq[i] = 0;
- printk("GDT-EISA: Can not detect controller IRQ,\n");
- printk("Use IRQ setting from command line (IRQ = %d)\n",
- ha->irq);
- } else {
- printk("GDT-EISA: Initialization error (unknown IRQ), Enable\n");
- printk("the controller BIOS or use command line parameters\n");
- return 0;
- }
- }
- } else {
- eisacf = inb(eisa_adr+EISAREG) & 7;
- if (eisacf > 4) /* level triggered */
- eisacf -= 4;
- ha->irq = gdth_irq_tab[eisacf];
- ha->oem_id = OEM_ID_ICP;
- ha->type = GDT_EISA;
- ha->stype = id;
- }
-
- ha->dma64_support = 0;
- return 1;
-}
-#endif /* CONFIG_EISA */
-
-#ifdef CONFIG_ISA
-static int __init gdth_init_isa(u32 bios_adr,gdth_ha_str *ha)
-{
- register gdt2_dpram_str __iomem *dp2_ptr;
- int i;
- u8 irq_drq,prot_ver;
- u32 retries;
-
- TRACE(("gdth_init_isa() bios adr. %x\n",bios_adr));
-
- ha->brd = ioremap(bios_adr, sizeof(gdt2_dpram_str));
- if (ha->brd == NULL) {
- printk("GDT-ISA: Initialization error (DPMEM remap error)\n");
- return 0;
- }
- dp2_ptr = ha->brd;
- writeb(1, &dp2_ptr->io.memlock); /* switch off write protection */
- /* reset interface area */
- memset_io(&dp2_ptr->u, 0, sizeof(dp2_ptr->u));
- if (readl(&dp2_ptr->u) != 0) {
- printk("GDT-ISA: Initialization error (DPMEM write error)\n");
- iounmap(ha->brd);
- return 0;
- }
-
- /* disable board interrupts, read DRQ and IRQ */
- writeb(0xff, &dp2_ptr->io.irqdel);
- writeb(0x00, &dp2_ptr->io.irqen);
- writeb(0x00, &dp2_ptr->u.ic.S_Status);
- writeb(0x00, &dp2_ptr->u.ic.Cmd_Index);
-
- irq_drq = readb(&dp2_ptr->io.rq);
- for (i=0; i<3; ++i) {
- if ((irq_drq & 1)==0)
- break;
- irq_drq >>= 1;
- }
- ha->drq = gdth_drq_tab[i];
-
- irq_drq = readb(&dp2_ptr->io.rq) >> 3;
- for (i=1; i<5; ++i) {
- if ((irq_drq & 1)==0)
- break;
- irq_drq >>= 1;
- }
- ha->irq = gdth_irq_tab[i];
-
- /* deinitialize services */
- writel(bios_adr, &dp2_ptr->u.ic.S_Info[0]);
- writeb(0xff, &dp2_ptr->u.ic.S_Cmd_Indx);
- writeb(0, &dp2_ptr->io.event);
- retries = INIT_RETRIES;
- gdth_delay(20);
- while (readb(&dp2_ptr->u.ic.S_Status) != 0xff) {
- if (--retries == 0) {
- printk("GDT-ISA: Initialization error (DEINIT failed)\n");
- iounmap(ha->brd);
- return 0;
- }
- gdth_delay(1);
- }
- prot_ver = (u8)readl(&dp2_ptr->u.ic.S_Info[0]);
- writeb(0, &dp2_ptr->u.ic.Status);
- writeb(0xff, &dp2_ptr->io.irqdel);
- if (prot_ver != PROTOCOL_VERSION) {
- printk("GDT-ISA: Illegal protocol version\n");
- iounmap(ha->brd);
- return 0;
- }
-
- ha->oem_id = OEM_ID_ICP;
- ha->type = GDT_ISA;
- ha->ic_all_size = sizeof(dp2_ptr->u);
- ha->stype= GDT2_ID;
- ha->brd_phys = bios_adr >> 4;
-
- /* special request to controller BIOS */
- writel(0x00, &dp2_ptr->u.ic.S_Info[0]);
- writel(0x00, &dp2_ptr->u.ic.S_Info[1]);
- writel(0x01, &dp2_ptr->u.ic.S_Info[2]);
- writel(0x00, &dp2_ptr->u.ic.S_Info[3]);
- writeb(0xfe, &dp2_ptr->u.ic.S_Cmd_Indx);
- writeb(0, &dp2_ptr->io.event);
- retries = INIT_RETRIES;
- gdth_delay(20);
- while (readb(&dp2_ptr->u.ic.S_Status) != 0xfe) {
- if (--retries == 0) {
- printk("GDT-ISA: Initialization error\n");
- iounmap(ha->brd);
- return 0;
- }
- gdth_delay(1);
- }
- writeb(0, &dp2_ptr->u.ic.Status);
- writeb(0xff, &dp2_ptr->io.irqdel);
-
- ha->dma64_support = 0;
- return 1;
-}
-#endif /* CONFIG_ISA */
-
-#ifdef CONFIG_PCI
static int gdth_init_pci(struct pci_dev *pdev, gdth_pci_str *pcistr,
gdth_ha_str *ha)
{
@@ -1228,30 +890,19 @@ static int gdth_init_pci(struct pci_dev *pdev, gdth_pci_str *pcistr,
return 1;
}
-#endif /* CONFIG_PCI */
/* controller protocol functions */
static void gdth_enable_int(gdth_ha_str *ha)
{
unsigned long flags;
- gdt2_dpram_str __iomem *dp2_ptr;
gdt6_dpram_str __iomem *dp6_ptr;
gdt6m_dpram_str __iomem *dp6m_ptr;
TRACE(("gdth_enable_int() hanum %d\n",ha->hanum));
spin_lock_irqsave(&ha->smp_lock, flags);
- if (ha->type == GDT_EISA) {
- outb(0xff, ha->bmic + EDOORREG);
- outb(0xff, ha->bmic + EDENABREG);
- outb(0x01, ha->bmic + EINTENABREG);
- } else if (ha->type == GDT_ISA) {
- dp2_ptr = ha->brd;
- writeb(1, &dp2_ptr->io.irqdel);
- writeb(0, &dp2_ptr->u.ic.Cmd_Index);
- writeb(1, &dp2_ptr->io.irqen);
- } else if (ha->type == GDT_PCI) {
+ if (ha->type == GDT_PCI) {
dp6_ptr = ha->brd;
writeb(1, &dp6_ptr->io.irqdel);
writeb(0, &dp6_ptr->u.ic.Cmd_Index);
@@ -1275,12 +926,7 @@ static u8 gdth_get_status(gdth_ha_str *ha)
TRACE(("gdth_get_status() irq %d ctr_count %d\n", ha->irq, gdth_ctr_count));
- if (ha->type == GDT_EISA)
- IStatus = inb((u16)ha->bmic + EDOORREG);
- else if (ha->type == GDT_ISA)
- IStatus =
- readb(&((gdt2_dpram_str __iomem *)ha->brd)->u.ic.Cmd_Index);
- else if (ha->type == GDT_PCI)
+ if (ha->type == GDT_PCI)
IStatus =
readb(&((gdt6_dpram_str __iomem *)ha->brd)->u.ic.Cmd_Index);
else if (ha->type == GDT_PCINEW)
@@ -1298,11 +944,7 @@ static int gdth_test_busy(gdth_ha_str *ha)
TRACE(("gdth_test_busy() hanum %d\n", ha->hanum));
- if (ha->type == GDT_EISA)
- gdtsema0 = (int)inb(ha->bmic + SEMA0REG);
- else if (ha->type == GDT_ISA)
- gdtsema0 = (int)readb(&((gdt2_dpram_str __iomem *)ha->brd)->u.ic.Sema0);
- else if (ha->type == GDT_PCI)
+ if (ha->type == GDT_PCI)
gdtsema0 = (int)readb(&((gdt6_dpram_str __iomem *)ha->brd)->u.ic.Sema0);
else if (ha->type == GDT_PCINEW)
gdtsema0 = (int)inb(PTR2USHORT(&ha->plx->sema0_reg));
@@ -1336,11 +978,7 @@ static void gdth_set_sema0(gdth_ha_str *ha)
{
TRACE(("gdth_set_sema0() hanum %d\n", ha->hanum));
- if (ha->type == GDT_EISA) {
- outb(1, ha->bmic + SEMA0REG);
- } else if (ha->type == GDT_ISA) {
- writeb(1, &((gdt2_dpram_str __iomem *)ha->brd)->u.ic.Sema0);
- } else if (ha->type == GDT_PCI) {
+ if (ha->type == GDT_PCI) {
writeb(1, &((gdt6_dpram_str __iomem *)ha->brd)->u.ic.Sema0);
} else if (ha->type == GDT_PCINEW) {
outb(1, PTR2USHORT(&ha->plx->sema0_reg));
@@ -1356,7 +994,6 @@ static void gdth_copy_command(gdth_ha_str *ha)
register gdt6m_dpram_str __iomem *dp6m_ptr;
register gdt6c_dpram_str __iomem *dp6c_ptr;
gdt6_dpram_str __iomem *dp6_ptr;
- gdt2_dpram_str __iomem *dp2_ptr;
u16 cp_count,dp_offset,cmd_no;
TRACE(("gdth_copy_command() hanum %d\n", ha->hanum));
@@ -1367,8 +1004,6 @@ static void gdth_copy_command(gdth_ha_str *ha)
cmd_ptr = ha->pccb;
++ha->cmd_cnt;
- if (ha->type == GDT_EISA)
- return; /* no DPMEM, no copy */
/* set cpcount dword aligned */
if (cp_count & 3)
@@ -1377,14 +1012,7 @@ static void gdth_copy_command(gdth_ha_str *ha)
ha->cmd_offs_dpmem += cp_count;
/* set offset and service, copy command to DPMEM */
- if (ha->type == GDT_ISA) {
- dp2_ptr = ha->brd;
- writew(dp_offset + DPMEM_COMMAND_OFFSET,
- &dp2_ptr->u.ic.comm_queue[cmd_no].offset);
- writew((u16)cmd_ptr->Service,
- &dp2_ptr->u.ic.comm_queue[cmd_no].serv_id);
- memcpy_toio(&dp2_ptr->u.ic.gdt_dpr_cmd[dp_offset],cmd_ptr,cp_count);
- } else if (ha->type == GDT_PCI) {
+ if (ha->type == GDT_PCI) {
dp6_ptr = ha->brd;
writew(dp_offset + DPMEM_COMMAND_OFFSET,
&dp6_ptr->u.ic.comm_queue[cmd_no].offset);
@@ -1430,13 +1058,7 @@ static void gdth_release_event(gdth_ha_str *ha)
if (ha->pccb->OpCode == GDT_INIT)
ha->pccb->Service |= 0x80;
- if (ha->type == GDT_EISA) {
- if (ha->pccb->OpCode == GDT_INIT) /* store DMA buffer */
- outl(ha->ccb_phys, ha->bmic + MAILBOXREG);
- outb(ha->pccb->Service, ha->bmic + LDOORREG);
- } else if (ha->type == GDT_ISA) {
- writeb(0, &((gdt2_dpram_str __iomem *)ha->brd)->io.event);
- } else if (ha->type == GDT_PCI) {
+ if (ha->type == GDT_PCI) {
writeb(0, &((gdt6_dpram_str __iomem *)ha->brd)->io.event);
} else if (ha->type == GDT_PCINEW) {
outb(1, PTR2USHORT(&ha->plx->ldoor_reg));
@@ -1560,15 +1182,7 @@ static int gdth_search_drives(gdth_ha_str *ha)
gdth_arcdl_str *alst;
gdth_alist_str *alst2;
gdth_oem_str_ioctl *oemstr;
-#ifdef INT_COAL
- gdth_perf_modes *pmod;
-#endif
-#ifdef GDTH_RTC
- u8 rtc[12];
- unsigned long flags;
-#endif
-
TRACE(("gdth_search_drives() hanum %d\n", ha->hanum));
ok = 0;
@@ -1588,29 +1202,6 @@ static int gdth_search_drives(gdth_ha_str *ha)
}
TRACE2(("gdth_search_drives(): SCREENSERVICE initialized\n"));
-#ifdef GDTH_RTC
- /* read realtime clock info, send to controller */
- /* 1. wait for the falling edge of update flag */
- spin_lock_irqsave(&rtc_lock, flags);
- for (j = 0; j < 1000000; ++j)
- if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP)
- break;
- for (j = 0; j < 1000000; ++j)
- if (!(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP))
- break;
- /* 2. read info */
- do {
- for (j = 0; j < 12; ++j)
- rtc[j] = CMOS_READ(j);
- } while (rtc[0] != CMOS_READ(0));
- spin_unlock_irqrestore(&rtc_lock, flags);
- TRACE2(("gdth_search_drives(): RTC: %x/%x/%x\n",*(u32 *)&rtc[0],
- *(u32 *)&rtc[4], *(u32 *)&rtc[8]));
- /* 3. send to controller firmware */
- gdth_internal_cmd(ha, SCREENSERVICE, GDT_REALTIME, *(u32 *)&rtc[0],
- *(u32 *)&rtc[4], *(u32 *)&rtc[8]);
-#endif
-
/* unfreeze all IOs */
gdth_internal_cmd(ha, CACHESERVICE, GDT_UNFREEZE_IO, 0, 0, 0);
@@ -1633,35 +1224,6 @@ static int gdth_search_drives(gdth_ha_str *ha)
cdev_cnt = (u16)ha->info;
ha->fw_vers = ha->service;
-#ifdef INT_COAL
- if (ha->type == GDT_PCIMPR) {
- /* set perf. modes */
- pmod = (gdth_perf_modes *)ha->pscratch;
- pmod->version = 1;
- pmod->st_mode = 1; /* enable one status buffer */
- *((u64 *)&pmod->st_buff_addr1) = ha->coal_stat_phys;
- pmod->st_buff_indx1 = COALINDEX;
- pmod->st_buff_addr2 = 0;
- pmod->st_buff_u_addr2 = 0;
- pmod->st_buff_indx2 = 0;
- pmod->st_buff_size = sizeof(gdth_coal_status) * MAXOFFSETS;
- pmod->cmd_mode = 0; // disable all cmd buffers
- pmod->cmd_buff_addr1 = 0;
- pmod->cmd_buff_u_addr1 = 0;
- pmod->cmd_buff_indx1 = 0;
- pmod->cmd_buff_addr2 = 0;
- pmod->cmd_buff_u_addr2 = 0;
- pmod->cmd_buff_indx2 = 0;
- pmod->cmd_buff_size = 0;
- pmod->reserved1 = 0;
- pmod->reserved2 = 0;
- if (gdth_internal_cmd(ha, CACHESERVICE, GDT_IOCTL, SET_PERF_MODES,
- INVALID_CHANNEL,sizeof(gdth_perf_modes))) {
- printk("GDT-HA %d: Interrupt coalescing activated\n", ha->hanum);
- }
- }
-#endif
-
/* detect number of buses - try new IOCTL */
iocr = (gdth_raw_iochan_str *)ha->pscratch;
iocr->hdr.version = 0xffffffff;
@@ -2433,9 +1995,6 @@ static int gdth_fill_cache_cmd(gdth_ha_str *ha, struct scsi_cmnd *scp,
TRACE(("gdth_fill_cache_cmd() cmd 0x%x cmdsize %d hdrive %d\n",
scp->cmnd[0],scp->cmd_len,hdrive));
- if (ha->type==GDT_EISA && ha->cmd_cnt>0)
- return 0;
-
mode64 = (ha->cache_feat & GDT_64BIT) ? TRUE : FALSE;
/* test for READ_16, WRITE_16 if !mode64 ? ---
not required, should not occur due to error return on
@@ -2518,9 +2077,9 @@ static int gdth_fill_cache_cmd(gdth_ha_str *ha, struct scsi_cmnd *scp,
if (scsi_bufflen(scp)) {
cmndinfo->dma_dir = (read_write == 1 ?
- PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);
- sgcnt = pci_map_sg(ha->pdev, scsi_sglist(scp), scsi_sg_count(scp),
- cmndinfo->dma_dir);
+ DMA_TO_DEVICE : DMA_FROM_DEVICE);
+ sgcnt = dma_map_sg(&ha->pdev->dev, scsi_sglist(scp),
+ scsi_sg_count(scp), cmndinfo->dma_dir);
if (mode64) {
struct scatterlist *sl;
@@ -2528,12 +2087,6 @@ static int gdth_fill_cache_cmd(gdth_ha_str *ha, struct scsi_cmnd *scp,
cmdp->u.cache64.sg_canz = sgcnt;
scsi_for_each_sg(scp, sl, sgcnt, i) {
cmdp->u.cache64.sg_lst[i].sg_ptr = sg_dma_address(sl);
-#ifdef GDTH_DMA_STATISTICS
- if (cmdp->u.cache64.sg_lst[i].sg_ptr > (u64)0xffffffff)
- ha->dma64_cnt++;
- else
- ha->dma32_cnt++;
-#endif
cmdp->u.cache64.sg_lst[i].sg_len = sg_dma_len(sl);
}
} else {
@@ -2543,9 +2096,6 @@ static int gdth_fill_cache_cmd(gdth_ha_str *ha, struct scsi_cmnd *scp,
cmdp->u.cache.sg_canz = sgcnt;
scsi_for_each_sg(scp, sl, sgcnt, i) {
cmdp->u.cache.sg_lst[i].sg_ptr = sg_dma_address(sl);
-#ifdef GDTH_DMA_STATISTICS
- ha->dma32_cnt++;
-#endif
cmdp->u.cache.sg_lst[i].sg_len = sg_dma_len(sl);
}
}
@@ -2603,8 +2153,6 @@ static int gdth_fill_raw_cmd(gdth_ha_str *ha, struct scsi_cmnd *scp, u8 b)
dma_addr_t sense_paddr;
int cmd_index, sgcnt, mode64;
u8 t,l;
- struct page *page;
- unsigned long offset;
struct gdth_cmndinfo *cmndinfo;
t = scp->device->id;
@@ -2613,9 +2161,6 @@ static int gdth_fill_raw_cmd(gdth_ha_str *ha, struct scsi_cmnd *scp, u8 b)
TRACE(("gdth_fill_raw_cmd() cmd 0x%x bus %d ID %d LUN %d\n",
scp->cmnd[0],b,t,l));
- if (ha->type==GDT_EISA && ha->cmd_cnt>0)
- return 0;
-
mode64 = (ha->raw_feat & GDT_64BIT) ? TRUE : FALSE;
cmdp->Service = SCSIRAWSERVICE;
@@ -2649,10 +2194,8 @@ static int gdth_fill_raw_cmd(gdth_ha_str *ha, struct scsi_cmnd *scp, u8 b)
}
} else {
- page = virt_to_page(scp->sense_buffer);
- offset = (unsigned long)scp->sense_buffer & ~PAGE_MASK;
- sense_paddr = pci_map_page(ha->pdev,page,offset,
- 16,PCI_DMA_FROMDEVICE);
+ sense_paddr = dma_map_single(&ha->pdev->dev, scp->sense_buffer, 16,
+ DMA_FROM_DEVICE);
cmndinfo->sense_paddr = sense_paddr;
cmdp->OpCode = GDT_WRITE; /* always */
@@ -2693,9 +2236,9 @@ static int gdth_fill_raw_cmd(gdth_ha_str *ha, struct scsi_cmnd *scp, u8 b)
}
if (scsi_bufflen(scp)) {
- cmndinfo->dma_dir = PCI_DMA_BIDIRECTIONAL;
- sgcnt = pci_map_sg(ha->pdev, scsi_sglist(scp), scsi_sg_count(scp),
- cmndinfo->dma_dir);
+ cmndinfo->dma_dir = DMA_BIDIRECTIONAL;
+ sgcnt = dma_map_sg(&ha->pdev->dev, scsi_sglist(scp),
+ scsi_sg_count(scp), cmndinfo->dma_dir);
if (mode64) {
struct scatterlist *sl;
@@ -2703,12 +2246,6 @@ static int gdth_fill_raw_cmd(gdth_ha_str *ha, struct scsi_cmnd *scp, u8 b)
cmdp->u.raw64.sg_ranz = sgcnt;
scsi_for_each_sg(scp, sl, sgcnt, i) {
cmdp->u.raw64.sg_lst[i].sg_ptr = sg_dma_address(sl);
-#ifdef GDTH_DMA_STATISTICS
- if (cmdp->u.raw64.sg_lst[i].sg_ptr > (u64)0xffffffff)
- ha->dma64_cnt++;
- else
- ha->dma32_cnt++;
-#endif
cmdp->u.raw64.sg_lst[i].sg_len = sg_dma_len(sl);
}
} else {
@@ -2718,9 +2255,6 @@ static int gdth_fill_raw_cmd(gdth_ha_str *ha, struct scsi_cmnd *scp, u8 b)
cmdp->u.raw.sg_ranz = sgcnt;
scsi_for_each_sg(scp, sl, sgcnt, i) {
cmdp->u.raw.sg_lst[i].sg_ptr = sg_dma_address(sl);
-#ifdef GDTH_DMA_STATISTICS
- ha->dma32_cnt++;
-#endif
cmdp->u.raw.sg_lst[i].sg_len = sg_dma_len(sl);
}
}
@@ -2778,9 +2312,6 @@ static int gdth_special_cmd(gdth_ha_str *ha, struct scsi_cmnd *scp)
cmdp= ha->pccb;
TRACE2(("gdth_special_cmd(): "));
- if (ha->type==GDT_EISA && ha->cmd_cnt>0)
- return 0;
-
*cmdp = *cmndinfo->internal_cmd_str;
cmdp->RequestBuffer = scp;
@@ -2959,18 +2490,11 @@ static irqreturn_t __gdth_interrupt(gdth_ha_str *ha,
{
gdt6m_dpram_str __iomem *dp6m_ptr = NULL;
gdt6_dpram_str __iomem *dp6_ptr;
- gdt2_dpram_str __iomem *dp2_ptr;
struct scsi_cmnd *scp;
int rval, i;
u8 IStatus;
u16 Service;
unsigned long flags = 0;
-#ifdef INT_COAL
- int coalesced = FALSE;
- int next = FALSE;
- gdth_coal_status *pcs = NULL;
- int act_int_coal = 0;
-#endif
TRACE(("gdth_interrupt() IRQ %d\n", ha->irq));
@@ -2997,53 +2521,7 @@ static irqreturn_t __gdth_interrupt(gdth_ha_str *ha,
++act_ints;
#endif
-#ifdef INT_COAL
- /* See if the fw is returning coalesced status */
- if (IStatus == COALINDEX) {
- /* Coalesced status. Setup the initial status
- buffer pointer and flags */
- pcs = ha->coal_stat;
- coalesced = TRUE;
- next = TRUE;
- }
-
- do {
- if (coalesced) {
- /* For coalesced requests all status
- information is found in the status buffer */
- IStatus = (u8)(pcs->status & 0xff);
- }
-#endif
-
- if (ha->type == GDT_EISA) {
- if (IStatus & 0x80) { /* error flag */
- IStatus &= ~0x80;
- ha->status = inw(ha->bmic + MAILBOXREG+8);
- TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,ha->status));
- } else /* no error */
- ha->status = S_OK;
- ha->info = inl(ha->bmic + MAILBOXREG+12);
- ha->service = inw(ha->bmic + MAILBOXREG+10);
- ha->info2 = inl(ha->bmic + MAILBOXREG+4);
-
- outb(0xff, ha->bmic + EDOORREG); /* acknowledge interrupt */
- outb(0x00, ha->bmic + SEMA1REG); /* reset status semaphore */
- } else if (ha->type == GDT_ISA) {
- dp2_ptr = ha->brd;
- if (IStatus & 0x80) { /* error flag */
- IStatus &= ~0x80;
- ha->status = readw(&dp2_ptr->u.ic.Status);
- TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,ha->status));
- } else /* no error */
- ha->status = S_OK;
- ha->info = readl(&dp2_ptr->u.ic.Info[0]);
- ha->service = readw(&dp2_ptr->u.ic.Service);
- ha->info2 = readl(&dp2_ptr->u.ic.Info[1]);
-
- writeb(0xff, &dp2_ptr->io.irqdel); /* acknowledge interrupt */
- writeb(0, &dp2_ptr->u.ic.Cmd_Index);/* reset command index */
- writeb(0, &dp2_ptr->io.Sema1); /* reset status semaphore */
- } else if (ha->type == GDT_PCI) {
+ if (ha->type == GDT_PCI) {
dp6_ptr = ha->brd;
if (IStatus & 0x80) { /* error flag */
IStatus &= ~0x80;
@@ -3075,28 +2553,15 @@ static irqreturn_t __gdth_interrupt(gdth_ha_str *ha,
dp6m_ptr = ha->brd;
if (IStatus & 0x80) { /* error flag */
IStatus &= ~0x80;
-#ifdef INT_COAL
- if (coalesced)
- ha->status = pcs->ext_status & 0xffff;
- else
-#endif
- ha->status = readw(&dp6m_ptr->i960r.status);
+ ha->status = readw(&dp6m_ptr->i960r.status);
TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,ha->status));
} else /* no error */
ha->status = S_OK;
-#ifdef INT_COAL
- /* get information */
- if (coalesced) {
- ha->info = pcs->info0;
- ha->info2 = pcs->info1;
- ha->service = (pcs->ext_status >> 16) & 0xffff;
- } else
-#endif
- {
- ha->info = readl(&dp6m_ptr->i960r.info[0]);
- ha->service = readw(&dp6m_ptr->i960r.service);
- ha->info2 = readl(&dp6m_ptr->i960r.info[1]);
- }
+
+ ha->info = readl(&dp6m_ptr->i960r.info[0]);
+ ha->service = readw(&dp6m_ptr->i960r.service);
+ ha->info2 = readl(&dp6m_ptr->i960r.info[1]);
+
/* event string */
if (IStatus == ASYNCINDEX) {
if (ha->service != SCREENSERVICE &&
@@ -3111,15 +2576,8 @@ static irqreturn_t __gdth_interrupt(gdth_ha_str *ha,
}
}
}
-#ifdef INT_COAL
- /* Make sure that non coalesced interrupts get cleared
- before being handled by gdth_async_event/gdth_sync_event */
- if (!coalesced)
-#endif
- {
- writeb(0xff, &dp6m_ptr->i960r.edoor_reg);
- writeb(0, &dp6m_ptr->i960r.sema1_reg);
- }
+ writeb(0xff, &dp6m_ptr->i960r.edoor_reg);
+ writeb(0, &dp6m_ptr->i960r.sema1_reg);
} else {
TRACE2(("gdth_interrupt() unknown controller type\n"));
if (!gdth_polling)
@@ -3182,31 +2640,6 @@ static irqreturn_t __gdth_interrupt(gdth_ha_str *ha,
gdth_scsi_done(scp);
}
-#ifdef INT_COAL
- if (coalesced) {
- /* go to the next status in the status buffer */
- ++pcs;
-#ifdef GDTH_STATISTICS
- ++act_int_coal;
- if (act_int_coal > max_int_coal) {
- max_int_coal = act_int_coal;
- printk("GDT: max_int_coal = %d\n",(u16)max_int_coal);
- }
-#endif
- /* see if there is another status */
- if (pcs->status == 0)
- /* Stop the coalesce loop */
- next = FALSE;
- }
- } while (next);
-
- /* coalescing only for new GDT_PCIMPR controllers available */
- if (ha->type == GDT_PCIMPR && coalesced) {
- writeb(0xff, &dp6m_ptr->i960r.edoor_reg);
- writeb(0, &dp6m_ptr->i960r.sema1_reg);
- }
-#endif
-
gdth_next(ha);
return IRQ_HANDLED;
}
@@ -3313,12 +2746,12 @@ static int gdth_sync_event(gdth_ha_str *ha, int service, u8 index,
return 2;
}
if (scsi_bufflen(scp))
- pci_unmap_sg(ha->pdev, scsi_sglist(scp), scsi_sg_count(scp),
+ dma_unmap_sg(&ha->pdev->dev, scsi_sglist(scp), scsi_sg_count(scp),
cmndinfo->dma_dir);
if (cmndinfo->sense_paddr)
- pci_unmap_page(ha->pdev, cmndinfo->sense_paddr, 16,
- PCI_DMA_FROMDEVICE);
+ dma_unmap_page(&ha->pdev->dev, cmndinfo->sense_paddr, 16,
+ DMA_FROM_DEVICE);
if (ha->status == S_OK) {
cmndinfo->status = S_OK;
@@ -3610,12 +3043,7 @@ static int gdth_async_event(gdth_ha_str *ha)
+ sizeof(u64);
ha->cmd_cnt = 0;
gdth_copy_command(ha);
- if (ha->type == GDT_EISA)
- printk("[EISA slot %d] ",(u16)ha->brd_phys);
- else if (ha->type == GDT_ISA)
- printk("[DPMEM 0x%4X] ",(u16)ha->brd_phys);
- else
- printk("[PCI %d/%d] ",(u16)(ha->brd_phys>>8),
+ printk("[PCI %d/%d] ",(u16)(ha->brd_phys>>8),
(u16)((ha->brd_phys>>3)&0x1f));
gdth_release_event(ha);
}
@@ -3756,23 +3184,12 @@ static inline void gdth_timer_init(void)
static void __init internal_setup(char *str,int *ints)
{
- int i, argc;
+ int i;
char *cur_str, *argv;
TRACE2(("internal_setup() str %s ints[0] %d\n",
str ? str:"NULL", ints ? ints[0]:0));
- /* read irq[] from ints[] */
- if (ints) {
- argc = ints[0];
- if (argc > 0) {
- if (argc > MAXHA)
- argc = MAXHA;
- for (i = 0; i < argc; ++i)
- irq[i] = ints[i+1];
- }
- }
-
/* analyse string */
argv = str;
while (argv && (cur_str = strchr(argv, ':'))) {
@@ -3799,8 +3216,6 @@ static void __init internal_setup(char *str,int *ints)
rescan = val;
else if (!strncmp(argv, "shared_access:", 14))
shared_access = val;
- else if (!strncmp(argv, "probe_eisa_isa:", 15))
- probe_eisa_isa = val;
else if (!strncmp(argv, "reserve_list:", 13)) {
reserve_list[0] = val;
for (i = 1; i < MAX_RES_ARGS; i++) {
@@ -3847,18 +3262,7 @@ static const char *gdth_ctr_name(gdth_ha_str *ha)
{
TRACE2(("gdth_ctr_name()\n"));
- if (ha->type == GDT_EISA) {
- switch (ha->stype) {
- case GDT3_ID:
- return("GDT3000/3020");
- case GDT3A_ID:
- return("GDT3000A/3020A/3050A");
- case GDT3B_ID:
- return("GDT3000B/3010A");
- }
- } else if (ha->type == GDT_ISA) {
- return("GDT2000/2020");
- } else if (ha->type == GDT_PCI) {
+ if (ha->type == GDT_PCI) {
switch (ha->pdev->device) {
case PCI_DEVICE_ID_VORTEX_GDT60x0:
return("GDT6000/6020/6050");
@@ -4155,131 +3559,147 @@ static int ioc_resetdrv(void __user *arg, char *cmnd)
return 0;
}
-static int ioc_general(void __user *arg, char *cmnd)
+static void gdth_ioc_cacheservice(gdth_ha_str *ha, gdth_ioctl_general *gen,
+ u64 paddr)
{
- gdth_ioctl_general gen;
- char *buf = NULL;
- u64 paddr;
- gdth_ha_str *ha;
- int rval;
-
- if (copy_from_user(&gen, arg, sizeof(gdth_ioctl_general)))
- return -EFAULT;
- ha = gdth_find_ha(gen.ionode);
- if (!ha)
- return -EFAULT;
-
- if (gen.data_len > INT_MAX)
- return -EINVAL;
- if (gen.sense_len > INT_MAX)
- return -EINVAL;
- if (gen.data_len + gen.sense_len > INT_MAX)
- return -EINVAL;
-
- if (gen.data_len + gen.sense_len != 0) {
- if (!(buf = gdth_ioctl_alloc(ha, gen.data_len + gen.sense_len,
- FALSE, &paddr)))
- return -EFAULT;
- if (copy_from_user(buf, arg + sizeof(gdth_ioctl_general),
- gen.data_len + gen.sense_len)) {
- gdth_ioctl_free(ha, gen.data_len+gen.sense_len, buf, paddr);
- return -EFAULT;
- }
+ if (ha->cache_feat & GDT_64BIT) {
+ /* copy elements from 32-bit IOCTL structure */
+ gen->command.u.cache64.BlockCnt = gen->command.u.cache.BlockCnt;
+ gen->command.u.cache64.BlockNo = gen->command.u.cache.BlockNo;
+ gen->command.u.cache64.DeviceNo = gen->command.u.cache.DeviceNo;
+
+ if (ha->cache_feat & SCATTER_GATHER) {
+ gen->command.u.cache64.DestAddr = (u64)-1;
+ gen->command.u.cache64.sg_canz = 1;
+ gen->command.u.cache64.sg_lst[0].sg_ptr = paddr;
+ gen->command.u.cache64.sg_lst[0].sg_len = gen->data_len;
+ gen->command.u.cache64.sg_lst[1].sg_len = 0;
+ } else {
+ gen->command.u.cache64.DestAddr = paddr;
+ gen->command.u.cache64.sg_canz = 0;
+ }
+ } else {
+ if (ha->cache_feat & SCATTER_GATHER) {
+ gen->command.u.cache.DestAddr = 0xffffffff;
+ gen->command.u.cache.sg_canz = 1;
+ gen->command.u.cache.sg_lst[0].sg_ptr = (u32)paddr;
+ gen->command.u.cache.sg_lst[0].sg_len = gen->data_len;
+ gen->command.u.cache.sg_lst[1].sg_len = 0;
+ } else {
+ gen->command.u.cache.DestAddr = paddr;
+ gen->command.u.cache.sg_canz = 0;
+ }
+ }
+}
- if (gen.command.OpCode == GDT_IOCTL) {
- gen.command.u.ioctl.p_param = paddr;
- } else if (gen.command.Service == CACHESERVICE) {
- if (ha->cache_feat & GDT_64BIT) {
- /* copy elements from 32-bit IOCTL structure */
- gen.command.u.cache64.BlockCnt = gen.command.u.cache.BlockCnt;
- gen.command.u.cache64.BlockNo = gen.command.u.cache.BlockNo;
- gen.command.u.cache64.DeviceNo = gen.command.u.cache.DeviceNo;
- /* addresses */
- if (ha->cache_feat & SCATTER_GATHER) {
- gen.command.u.cache64.DestAddr = (u64)-1;
- gen.command.u.cache64.sg_canz = 1;
- gen.command.u.cache64.sg_lst[0].sg_ptr = paddr;
- gen.command.u.cache64.sg_lst[0].sg_len = gen.data_len;
- gen.command.u.cache64.sg_lst[1].sg_len = 0;
- } else {
- gen.command.u.cache64.DestAddr = paddr;
- gen.command.u.cache64.sg_canz = 0;
- }
- } else {
- if (ha->cache_feat & SCATTER_GATHER) {
- gen.command.u.cache.DestAddr = 0xffffffff;
- gen.command.u.cache.sg_canz = 1;
- gen.command.u.cache.sg_lst[0].sg_ptr = (u32)paddr;
- gen.command.u.cache.sg_lst[0].sg_len = gen.data_len;
- gen.command.u.cache.sg_lst[1].sg_len = 0;
- } else {
- gen.command.u.cache.DestAddr = paddr;
- gen.command.u.cache.sg_canz = 0;
- }
- }
- } else if (gen.command.Service == SCSIRAWSERVICE) {
- if (ha->raw_feat & GDT_64BIT) {
- /* copy elements from 32-bit IOCTL structure */
- char cmd[16];
- gen.command.u.raw64.sense_len = gen.command.u.raw.sense_len;
- gen.command.u.raw64.bus = gen.command.u.raw.bus;
- gen.command.u.raw64.lun = gen.command.u.raw.lun;
- gen.command.u.raw64.target = gen.command.u.raw.target;
- memcpy(cmd, gen.command.u.raw.cmd, 16);
- memcpy(gen.command.u.raw64.cmd, cmd, 16);
- gen.command.u.raw64.clen = gen.command.u.raw.clen;
- gen.command.u.raw64.sdlen = gen.command.u.raw.sdlen;
- gen.command.u.raw64.direction = gen.command.u.raw.direction;
- /* addresses */
- if (ha->raw_feat & SCATTER_GATHER) {
- gen.command.u.raw64.sdata = (u64)-1;
- gen.command.u.raw64.sg_ranz = 1;
- gen.command.u.raw64.sg_lst[0].sg_ptr = paddr;
- gen.command.u.raw64.sg_lst[0].sg_len = gen.data_len;
- gen.command.u.raw64.sg_lst[1].sg_len = 0;
- } else {
- gen.command.u.raw64.sdata = paddr;
- gen.command.u.raw64.sg_ranz = 0;
+static void gdth_ioc_scsiraw(gdth_ha_str *ha, gdth_ioctl_general *gen,
+ u64 paddr)
+{
+ if (ha->raw_feat & GDT_64BIT) {
+ /* copy elements from 32-bit IOCTL structure */
+ char cmd[16];
+
+ gen->command.u.raw64.sense_len = gen->command.u.raw.sense_len;
+ gen->command.u.raw64.bus = gen->command.u.raw.bus;
+ gen->command.u.raw64.lun = gen->command.u.raw.lun;
+ gen->command.u.raw64.target = gen->command.u.raw.target;
+ memcpy(cmd, gen->command.u.raw.cmd, 16);
+ memcpy(gen->command.u.raw64.cmd, cmd, 16);
+ gen->command.u.raw64.clen = gen->command.u.raw.clen;
+ gen->command.u.raw64.sdlen = gen->command.u.raw.sdlen;
+ gen->command.u.raw64.direction = gen->command.u.raw.direction;
+
+ /* addresses */
+ if (ha->raw_feat & SCATTER_GATHER) {
+ gen->command.u.raw64.sdata = (u64)-1;
+ gen->command.u.raw64.sg_ranz = 1;
+ gen->command.u.raw64.sg_lst[0].sg_ptr = paddr;
+ gen->command.u.raw64.sg_lst[0].sg_len = gen->data_len;
+ gen->command.u.raw64.sg_lst[1].sg_len = 0;
+ } else {
+ gen->command.u.raw64.sdata = paddr;
+ gen->command.u.raw64.sg_ranz = 0;
}
- gen.command.u.raw64.sense_data = paddr + gen.data_len;
- } else {
- if (ha->raw_feat & SCATTER_GATHER) {
- gen.command.u.raw.sdata = 0xffffffff;
- gen.command.u.raw.sg_ranz = 1;
- gen.command.u.raw.sg_lst[0].sg_ptr = (u32)paddr;
- gen.command.u.raw.sg_lst[0].sg_len = gen.data_len;
- gen.command.u.raw.sg_lst[1].sg_len = 0;
- } else {
- gen.command.u.raw.sdata = paddr;
- gen.command.u.raw.sg_ranz = 0;
+
+ gen->command.u.raw64.sense_data = paddr + gen->data_len;
+ } else {
+ if (ha->raw_feat & SCATTER_GATHER) {
+ gen->command.u.raw.sdata = 0xffffffff;
+ gen->command.u.raw.sg_ranz = 1;
+ gen->command.u.raw.sg_lst[0].sg_ptr = (u32)paddr;
+ gen->command.u.raw.sg_lst[0].sg_len = gen->data_len;
+ gen->command.u.raw.sg_lst[1].sg_len = 0;
+ } else {
+ gen->command.u.raw.sdata = paddr;
+ gen->command.u.raw.sg_ranz = 0;
}
- gen.command.u.raw.sense_data = (u32)paddr + gen.data_len;
- }
- } else {
- gdth_ioctl_free(ha, gen.data_len+gen.sense_len, buf, paddr);
- return -EFAULT;
- }
- }
- rval = __gdth_execute(ha->sdev, &gen.command, cmnd, gen.timeout, &gen.info);
- if (rval < 0) {
- gdth_ioctl_free(ha, gen.data_len+gen.sense_len, buf, paddr);
- return rval;
- }
- gen.status = rval;
+ gen->command.u.raw.sense_data = (u32)paddr + gen->data_len;
+ }
+}
- if (copy_to_user(arg + sizeof(gdth_ioctl_general), buf,
- gen.data_len + gen.sense_len)) {
- gdth_ioctl_free(ha, gen.data_len+gen.sense_len, buf, paddr);
- return -EFAULT;
- }
- if (copy_to_user(arg, &gen,
- sizeof(gdth_ioctl_general) - sizeof(gdth_cmd_str))) {
- gdth_ioctl_free(ha, gen.data_len+gen.sense_len, buf, paddr);
- return -EFAULT;
- }
- gdth_ioctl_free(ha, gen.data_len+gen.sense_len, buf, paddr);
- return 0;
+static int ioc_general(void __user *arg, char *cmnd)
+{
+ gdth_ioctl_general gen;
+ gdth_ha_str *ha;
+ char *buf = NULL;
+ dma_addr_t paddr;
+ int rval;
+
+ if (copy_from_user(&gen, arg, sizeof(gdth_ioctl_general)))
+ return -EFAULT;
+ ha = gdth_find_ha(gen.ionode);
+ if (!ha)
+ return -EFAULT;
+
+ if (gen.data_len > INT_MAX)
+ return -EINVAL;
+ if (gen.sense_len > INT_MAX)
+ return -EINVAL;
+ if (gen.data_len + gen.sense_len > INT_MAX)
+ return -EINVAL;
+
+ if (gen.data_len + gen.sense_len > 0) {
+ buf = dma_alloc_coherent(&ha->pdev->dev,
+ gen.data_len + gen.sense_len, &paddr,
+ GFP_KERNEL);
+ if (!buf)
+ return -EFAULT;
+
+ rval = -EFAULT;
+ if (copy_from_user(buf, arg + sizeof(gdth_ioctl_general),
+ gen.data_len + gen.sense_len))
+ goto out_free_buf;
+
+ if (gen.command.OpCode == GDT_IOCTL)
+ gen.command.u.ioctl.p_param = paddr;
+ else if (gen.command.Service == CACHESERVICE)
+ gdth_ioc_cacheservice(ha, &gen, paddr);
+ else if (gen.command.Service == SCSIRAWSERVICE)
+ gdth_ioc_scsiraw(ha, &gen, paddr);
+ else
+ goto out_free_buf;
+ }
+
+ rval = __gdth_execute(ha->sdev, &gen.command, cmnd, gen.timeout,
+ &gen.info);
+ if (rval < 0)
+ goto out_free_buf;
+ gen.status = rval;
+
+ rval = -EFAULT;
+ if (copy_to_user(arg + sizeof(gdth_ioctl_general), buf,
+ gen.data_len + gen.sense_len))
+ goto out_free_buf;
+ if (copy_to_user(arg, &gen,
+ sizeof(gdth_ioctl_general) - sizeof(gdth_cmd_str)))
+ goto out_free_buf;
+
+ rval = 0;
+out_free_buf:
+ dma_free_coherent(&ha->pdev->dev, gen.data_len + gen.sense_len, buf,
+ paddr);
+ return rval;
}
static int ioc_hdrlist(void __user *arg, char *cmnd)
@@ -4514,22 +3934,17 @@ static int gdth_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
(NULL == (ha = gdth_find_ha(ctrt.ionode))))
return -EFAULT;
- if (ha->type == GDT_ISA || ha->type == GDT_EISA) {
- ctrt.type = (u8)((ha->stype>>20) - 0x10);
+ if (ha->type != GDT_PCIMPR) {
+ ctrt.type = (u8)((ha->stype<<4) + 6);
} else {
- if (ha->type != GDT_PCIMPR) {
- ctrt.type = (u8)((ha->stype<<4) + 6);
- } else {
- ctrt.type =
- (ha->oem_id == OEM_ID_INTEL ? 0xfd : 0xfe);
- if (ha->stype >= 0x300)
- ctrt.ext_type = 0x6000 | ha->pdev->subsystem_device;
- else
- ctrt.ext_type = 0x6000 | ha->stype;
- }
- ctrt.device_id = ha->pdev->device;
- ctrt.sub_device_id = ha->pdev->subsystem_device;
+ ctrt.type = (ha->oem_id == OEM_ID_INTEL ? 0xfd : 0xfe);
+ if (ha->stype >= 0x300)
+ ctrt.ext_type = 0x6000 | ha->pdev->subsystem_device;
+ else
+ ctrt.ext_type = 0x6000 | ha->stype;
}
+ ctrt.device_id = ha->pdev->device;
+ ctrt.sub_device_id = ha->pdev->subsystem_device;
ctrt.info = ha->brd_phys;
ctrt.oem_id = ha->oem_id;
if (copy_to_user(argp, &ctrt, sizeof(gdth_ioctl_ctrtype)))
@@ -4683,272 +4098,6 @@ static struct scsi_host_template gdth_template = {
.no_write_same = 1,
};
-#ifdef CONFIG_ISA
-static int __init gdth_isa_probe_one(u32 isa_bios)
-{
- struct Scsi_Host *shp;
- gdth_ha_str *ha;
- dma_addr_t scratch_dma_handle = 0;
- int error, i;
-
- if (!gdth_search_isa(isa_bios))
- return -ENXIO;
-
- shp = scsi_host_alloc(&gdth_template, sizeof(gdth_ha_str));
- if (!shp)
- return -ENOMEM;
- ha = shost_priv(shp);
-
- error = -ENODEV;
- if (!gdth_init_isa(isa_bios,ha))
- goto out_host_put;
-
- /* controller found and initialized */
- printk("Configuring GDT-ISA HA at BIOS 0x%05X IRQ %u DRQ %u\n",
- isa_bios, ha->irq, ha->drq);
-
- error = request_irq(ha->irq, gdth_interrupt, 0, "gdth", ha);
- if (error) {
- printk("GDT-ISA: Unable to allocate IRQ\n");
- goto out_host_put;
- }
-
- error = request_dma(ha->drq, "gdth");
- if (error) {
- printk("GDT-ISA: Unable to allocate DMA channel\n");
- goto out_free_irq;
- }
-
- set_dma_mode(ha->drq,DMA_MODE_CASCADE);
- enable_dma(ha->drq);
- shp->unchecked_isa_dma = 1;
- shp->irq = ha->irq;
- shp->dma_channel = ha->drq;
-
- ha->hanum = gdth_ctr_count++;
- ha->shost = shp;
-
- ha->pccb = &ha->cmdext;
- ha->ccb_phys = 0L;
- ha->pdev = NULL;
-
- error = -ENOMEM;
-
- ha->pscratch = pci_alloc_consistent(ha->pdev, GDTH_SCRATCH,
- &scratch_dma_handle);
- if (!ha->pscratch)
- goto out_dec_counters;
- ha->scratch_phys = scratch_dma_handle;
-
- ha->pmsg = pci_alloc_consistent(ha->pdev, sizeof(gdth_msg_str),
- &scratch_dma_handle);
- if (!ha->pmsg)
- goto out_free_pscratch;
- ha->msg_phys = scratch_dma_handle;
-
-#ifdef INT_COAL
- ha->coal_stat = pci_alloc_consistent(ha->pdev,
- sizeof(gdth_coal_status) * MAXOFFSETS,
- &scratch_dma_handle);
- if (!ha->coal_stat)
- goto out_free_pmsg;
- ha->coal_stat_phys = scratch_dma_handle;
-#endif
-
- ha->scratch_busy = FALSE;
- ha->req_first = NULL;
- ha->tid_cnt = MAX_HDRIVES;
- if (max_ids > 0 && max_ids < ha->tid_cnt)
- ha->tid_cnt = max_ids;
- for (i = 0; i < GDTH_MAXCMDS; ++i)
- ha->cmd_tab[i].cmnd = UNUSED_CMND;
- ha->scan_mode = rescan ? 0x10 : 0;
-
- error = -ENODEV;
- if (!gdth_search_drives(ha)) {
- printk("GDT-ISA: Error during device scan\n");
- goto out_free_coal_stat;
- }
-
- if (hdr_channel < 0 || hdr_channel > ha->bus_cnt)
- hdr_channel = ha->bus_cnt;
- ha->virt_bus = hdr_channel;
-
- if (ha->cache_feat & ha->raw_feat & ha->screen_feat & GDT_64BIT)
- shp->max_cmd_len = 16;
-
- shp->max_id = ha->tid_cnt;
- shp->max_lun = MAXLUN;
- shp->max_channel = ha->bus_cnt;
-
- spin_lock_init(&ha->smp_lock);
- gdth_enable_int(ha);
-
- error = scsi_add_host(shp, NULL);
- if (error)
- goto out_free_coal_stat;
- list_add_tail(&ha->list, &gdth_instances);
- gdth_timer_init();
-
- scsi_scan_host(shp);
-
- return 0;
-
- out_free_coal_stat:
-#ifdef INT_COAL
- pci_free_consistent(ha->pdev, sizeof(gdth_coal_status) * MAXOFFSETS,
- ha->coal_stat, ha->coal_stat_phys);
- out_free_pmsg:
-#endif
- pci_free_consistent(ha->pdev, sizeof(gdth_msg_str),
- ha->pmsg, ha->msg_phys);
- out_free_pscratch:
- pci_free_consistent(ha->pdev, GDTH_SCRATCH,
- ha->pscratch, ha->scratch_phys);
- out_dec_counters:
- gdth_ctr_count--;
- out_free_irq:
- free_irq(ha->irq, ha);
- out_host_put:
- scsi_host_put(shp);
- return error;
-}
-#endif /* CONFIG_ISA */
-
-#ifdef CONFIG_EISA
-static int __init gdth_eisa_probe_one(u16 eisa_slot)
-{
- struct Scsi_Host *shp;
- gdth_ha_str *ha;
- dma_addr_t scratch_dma_handle = 0;
- int error, i;
-
- if (!gdth_search_eisa(eisa_slot))
- return -ENXIO;
-
- shp = scsi_host_alloc(&gdth_template, sizeof(gdth_ha_str));
- if (!shp)
- return -ENOMEM;
- ha = shost_priv(shp);
-
- error = -ENODEV;
- if (!gdth_init_eisa(eisa_slot,ha))
- goto out_host_put;
-
- /* controller found and initialized */
- printk("Configuring GDT-EISA HA at Slot %d IRQ %u\n",
- eisa_slot >> 12, ha->irq);
-
- error = request_irq(ha->irq, gdth_interrupt, 0, "gdth", ha);
- if (error) {
- printk("GDT-EISA: Unable to allocate IRQ\n");
- goto out_host_put;
- }
-
- shp->unchecked_isa_dma = 0;
- shp->irq = ha->irq;
- shp->dma_channel = 0xff;
-
- ha->hanum = gdth_ctr_count++;
- ha->shost = shp;
-
- TRACE2(("EISA detect Bus 0: hanum %d\n", ha->hanum));
-
- ha->pccb = &ha->cmdext;
- ha->ccb_phys = 0L;
-
- error = -ENOMEM;
-
- ha->pdev = NULL;
- ha->pscratch = pci_alloc_consistent(ha->pdev, GDTH_SCRATCH,
- &scratch_dma_handle);
- if (!ha->pscratch)
- goto out_free_irq;
- ha->scratch_phys = scratch_dma_handle;
-
- ha->pmsg = pci_alloc_consistent(ha->pdev, sizeof(gdth_msg_str),
- &scratch_dma_handle);
- if (!ha->pmsg)
- goto out_free_pscratch;
- ha->msg_phys = scratch_dma_handle;
-
-#ifdef INT_COAL
- ha->coal_stat = pci_alloc_consistent(ha->pdev,
- sizeof(gdth_coal_status) * MAXOFFSETS,
- &scratch_dma_handle);
- if (!ha->coal_stat)
- goto out_free_pmsg;
- ha->coal_stat_phys = scratch_dma_handle;
-#endif
-
- ha->ccb_phys = pci_map_single(ha->pdev,ha->pccb,
- sizeof(gdth_cmd_str), PCI_DMA_BIDIRECTIONAL);
- if (!ha->ccb_phys)
- goto out_free_coal_stat;
-
- ha->scratch_busy = FALSE;
- ha->req_first = NULL;
- ha->tid_cnt = MAX_HDRIVES;
- if (max_ids > 0 && max_ids < ha->tid_cnt)
- ha->tid_cnt = max_ids;
- for (i = 0; i < GDTH_MAXCMDS; ++i)
- ha->cmd_tab[i].cmnd = UNUSED_CMND;
- ha->scan_mode = rescan ? 0x10 : 0;
-
- if (!gdth_search_drives(ha)) {
- printk("GDT-EISA: Error during device scan\n");
- error = -ENODEV;
- goto out_free_ccb_phys;
- }
-
- if (hdr_channel < 0 || hdr_channel > ha->bus_cnt)
- hdr_channel = ha->bus_cnt;
- ha->virt_bus = hdr_channel;
-
- if (ha->cache_feat & ha->raw_feat & ha->screen_feat & GDT_64BIT)
- shp->max_cmd_len = 16;
-
- shp->max_id = ha->tid_cnt;
- shp->max_lun = MAXLUN;
- shp->max_channel = ha->bus_cnt;
-
- spin_lock_init(&ha->smp_lock);
- gdth_enable_int(ha);
-
- error = scsi_add_host(shp, NULL);
- if (error)
- goto out_free_ccb_phys;
- list_add_tail(&ha->list, &gdth_instances);
- gdth_timer_init();
-
- scsi_scan_host(shp);
-
- return 0;
-
- out_free_ccb_phys:
- pci_unmap_single(ha->pdev,ha->ccb_phys, sizeof(gdth_cmd_str),
- PCI_DMA_BIDIRECTIONAL);
- out_free_coal_stat:
-#ifdef INT_COAL
- pci_free_consistent(ha->pdev, sizeof(gdth_coal_status) * MAXOFFSETS,
- ha->coal_stat, ha->coal_stat_phys);
- out_free_pmsg:
-#endif
- pci_free_consistent(ha->pdev, sizeof(gdth_msg_str),
- ha->pmsg, ha->msg_phys);
- out_free_pscratch:
- pci_free_consistent(ha->pdev, GDTH_SCRATCH,
- ha->pscratch, ha->scratch_phys);
- out_free_irq:
- free_irq(ha->irq, ha);
- gdth_ctr_count--;
- out_host_put:
- scsi_host_put(shp);
- return error;
-}
-#endif /* CONFIG_EISA */
-
-#ifdef CONFIG_PCI
static int gdth_pci_probe_one(gdth_pci_str *pcistr, gdth_ha_str **ha_out)
{
struct Scsi_Host *shp;
@@ -4993,27 +4142,18 @@ static int gdth_pci_probe_one(gdth_pci_str *pcistr, gdth_ha_str **ha_out)
error = -ENOMEM;
- ha->pscratch = pci_alloc_consistent(ha->pdev, GDTH_SCRATCH,
- &scratch_dma_handle);
+ ha->pscratch = dma_alloc_coherent(&ha->pdev->dev, GDTH_SCRATCH,
+ &scratch_dma_handle, GFP_KERNEL);
if (!ha->pscratch)
goto out_free_irq;
ha->scratch_phys = scratch_dma_handle;
- ha->pmsg = pci_alloc_consistent(ha->pdev, sizeof(gdth_msg_str),
- &scratch_dma_handle);
+ ha->pmsg = dma_alloc_coherent(&ha->pdev->dev, sizeof(gdth_msg_str),
+ &scratch_dma_handle, GFP_KERNEL);
if (!ha->pmsg)
goto out_free_pscratch;
ha->msg_phys = scratch_dma_handle;
-#ifdef INT_COAL
- ha->coal_stat = pci_alloc_consistent(ha->pdev,
- sizeof(gdth_coal_status) * MAXOFFSETS,
- &scratch_dma_handle);
- if (!ha->coal_stat)
- goto out_free_pmsg;
- ha->coal_stat_phys = scratch_dma_handle;
-#endif
-
ha->scratch_busy = FALSE;
ha->req_first = NULL;
ha->tid_cnt = pdev->device >= 0x200 ? MAXID : MAX_HDRIVES;
@@ -5026,7 +4166,7 @@ static int gdth_pci_probe_one(gdth_pci_str *pcistr, gdth_ha_str **ha_out)
error = -ENODEV;
if (!gdth_search_drives(ha)) {
printk("GDT-PCI %d: Error during device scan\n", ha->hanum);
- goto out_free_coal_stat;
+ goto out_free_pmsg;
}
if (hdr_channel < 0 || hdr_channel > ha->bus_cnt)
@@ -5036,19 +4176,19 @@ static int gdth_pci_probe_one(gdth_pci_str *pcistr, gdth_ha_str **ha_out)
/* 64-bit DMA only supported from FW >= x.43 */
if (!(ha->cache_feat & ha->raw_feat & ha->screen_feat & GDT_64BIT) ||
!ha->dma64_support) {
- if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) {
+ if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(32))) {
printk(KERN_WARNING "GDT-PCI %d: "
"Unable to set 32-bit DMA\n", ha->hanum);
- goto out_free_coal_stat;
+ goto out_free_pmsg;
}
} else {
shp->max_cmd_len = 16;
- if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(64))) {
+ if (!dma_set_mask(&pdev->dev, DMA_BIT_MASK(64))) {
printk("GDT-PCI %d: 64-bit DMA enabled\n", ha->hanum);
- } else if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) {
+ } else if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(32))) {
printk(KERN_WARNING "GDT-PCI %d: "
"Unable to set 64/32-bit DMA\n", ha->hanum);
- goto out_free_coal_stat;
+ goto out_free_pmsg;
}
}
@@ -5061,7 +4201,7 @@ static int gdth_pci_probe_one(gdth_pci_str *pcistr, gdth_ha_str **ha_out)
error = scsi_add_host(shp, &pdev->dev);
if (error)
- goto out_free_coal_stat;
+ goto out_free_pmsg;
list_add_tail(&ha->list, &gdth_instances);
pci_set_drvdata(ha->pdev, ha);
@@ -5073,16 +4213,11 @@ static int gdth_pci_probe_one(gdth_pci_str *pcistr, gdth_ha_str **ha_out)
return 0;
- out_free_coal_stat:
-#ifdef INT_COAL
- pci_free_consistent(ha->pdev, sizeof(gdth_coal_status) * MAXOFFSETS,
- ha->coal_stat, ha->coal_stat_phys);
out_free_pmsg:
-#endif
- pci_free_consistent(ha->pdev, sizeof(gdth_msg_str),
+ dma_free_coherent(&ha->pdev->dev, sizeof(gdth_msg_str),
ha->pmsg, ha->msg_phys);
out_free_pscratch:
- pci_free_consistent(ha->pdev, GDTH_SCRATCH,
+ dma_free_coherent(&ha->pdev->dev, GDTH_SCRATCH,
ha->pscratch, ha->scratch_phys);
out_free_irq:
free_irq(ha->irq, ha);
@@ -5091,7 +4226,6 @@ static int gdth_pci_probe_one(gdth_pci_str *pcistr, gdth_ha_str **ha_out)
scsi_host_put(shp);
return error;
}
-#endif /* CONFIG_PCI */
static void gdth_remove_one(gdth_ha_str *ha)
{
@@ -5111,24 +4245,15 @@ static void gdth_remove_one(gdth_ha_str *ha)
if (shp->irq)
free_irq(shp->irq,ha);
-#ifdef CONFIG_ISA
- if (shp->dma_channel != 0xff)
- free_dma(shp->dma_channel);
-#endif
-#ifdef INT_COAL
- if (ha->coal_stat)
- pci_free_consistent(ha->pdev, sizeof(gdth_coal_status) *
- MAXOFFSETS, ha->coal_stat, ha->coal_stat_phys);
-#endif
if (ha->pscratch)
- pci_free_consistent(ha->pdev, GDTH_SCRATCH,
+ dma_free_coherent(&ha->pdev->dev, GDTH_SCRATCH,
ha->pscratch, ha->scratch_phys);
if (ha->pmsg)
- pci_free_consistent(ha->pdev, sizeof(gdth_msg_str),
+ dma_free_coherent(&ha->pdev->dev, sizeof(gdth_msg_str),
ha->pmsg, ha->msg_phys);
if (ha->ccb_phys)
- pci_unmap_single(ha->pdev,ha->ccb_phys,
- sizeof(gdth_cmd_str),PCI_DMA_BIDIRECTIONAL);
+ dma_unmap_single(&ha->pdev->dev, ha->ccb_phys,
+ sizeof(gdth_cmd_str), DMA_BIDIRECTIONAL);
scsi_host_put(shp);
}
@@ -5167,26 +4292,6 @@ static int __init gdth_init(void)
gdth_clear_events();
timer_setup(&gdth_timer, gdth_timeout, 0);
- /* As default we do not probe for EISA or ISA controllers */
- if (probe_eisa_isa) {
- /* scanning for controllers, at first: ISA controller */
-#ifdef CONFIG_ISA
- u32 isa_bios;
- for (isa_bios = 0xc8000UL; isa_bios <= 0xd8000UL;
- isa_bios += 0x8000UL)
- gdth_isa_probe_one(isa_bios);
-#endif
-#ifdef CONFIG_EISA
- {
- u16 eisa_slot;
- for (eisa_slot = 0x1000; eisa_slot <= 0x8000;
- eisa_slot += 0x1000)
- gdth_eisa_probe_one(eisa_slot);
- }
-#endif
- }
-
-#ifdef CONFIG_PCI
/* scanning for PCI controllers */
if (pci_register_driver(&gdth_pci_driver)) {
gdth_ha_str *ha;
@@ -5195,7 +4300,6 @@ static int __init gdth_init(void)
gdth_remove_one(ha);
return -ENODEV;
}
-#endif /* CONFIG_PCI */
TRACE2(("gdth_detect() %d controller detected\n", gdth_ctr_count));
@@ -5216,9 +4320,7 @@ static void __exit gdth_exit(void)
del_timer_sync(&gdth_timer);
#endif
-#ifdef CONFIG_PCI
pci_unregister_driver(&gdth_pci_driver);
-#endif
list_for_each_entry(ha, &gdth_instances, list)
gdth_remove_one(ha);
diff --git a/drivers/scsi/gdth.h b/drivers/scsi/gdth.h
index ee6ffcf388e8..5a13d406d40e 100644
--- a/drivers/scsi/gdth.h
+++ b/drivers/scsi/gdth.h
@@ -38,17 +38,9 @@
#define OEM_ID_INTEL 0x8000
/* controller classes */
-#define GDT_ISA 0x01 /* ISA controller */
-#define GDT_EISA 0x02 /* EISA controller */
#define GDT_PCI 0x03 /* PCI controller */
#define GDT_PCINEW 0x04 /* new PCI controller */
#define GDT_PCIMPR 0x05 /* PCI MPR controller */
-/* GDT_EISA, controller subtypes EISA */
-#define GDT3_ID 0x0130941c /* GDT3000/3020 */
-#define GDT3A_ID 0x0230941c /* GDT3000A/3020A/3050A */
-#define GDT3B_ID 0x0330941c /* GDT3000B/3010A */
-/* GDT_ISA */
-#define GDT2_ID 0x0120941c /* GDT2000/2020 */
#ifndef PCI_DEVICE_ID_VORTEX_GDT60x0
/* GDT_PCI */
@@ -281,17 +273,6 @@
#define GDTH_DATA_IN 0x01000000L /* data from target */
#define GDTH_DATA_OUT 0x00000000L /* data to target */
-/* BMIC registers (EISA controllers) */
-#define ID0REG 0x0c80 /* board ID */
-#define EINTENABREG 0x0c89 /* interrupt enable */
-#define SEMA0REG 0x0c8a /* command semaphore */
-#define SEMA1REG 0x0c8b /* status semaphore */
-#define LDOORREG 0x0c8d /* local doorbell */
-#define EDENABREG 0x0c8e /* EISA system doorbell enab. */
-#define EDOORREG 0x0c8f /* EISA system doorbell */
-#define MAILBOXREG 0x0c90 /* mailbox reg. (16 bytes) */
-#define EISAREG 0x0cc0 /* EISA configuration */
-
/* other defines */
#define LINUX_OS 8 /* used for cache optim. */
#define SECS32 0x1f /* round capacity */
@@ -706,21 +687,11 @@ typedef struct {
u8 fw_magic; /* contr. ID from firmware */
} __attribute__((packed)) gdt_pci_sram;
-/* SRAM structure EISA controllers (but NOT GDT3000/3020) */
-typedef struct {
- u8 os_used[16]; /* OS code per service */
- u16 need_deinit; /* switch betw. BIOS/driver */
- u8 switch_support; /* see need_deinit */
- u8 padding;
-} __attribute__((packed)) gdt_eisa_sram;
-
-
/* DPRAM ISA controllers */
typedef struct {
union {
struct {
u8 bios_used[0x3c00-32]; /* 15KB - 32Bytes BIOS */
- u32 magic; /* controller (EISA) ID */
u16 need_deinit; /* switch betw. BIOS/driver */
u8 switch_support; /* see need_deinit */
u8 padding[9];
@@ -843,7 +814,6 @@ typedef struct {
u16 cache_feat; /* feat. cache serv. (s/g,..)*/
u16 raw_feat; /* feat. raw service (s/g,..)*/
u16 screen_feat; /* feat. raw service (s/g,..)*/
- u16 bmic; /* BMIC address (EISA) */
void __iomem *brd; /* DPRAM address */
u32 brd_phys; /* slot number/BIOS address */
gdt6c_plx_regs *plx; /* PLX regs (new PCI contr.) */
diff --git a/drivers/scsi/gdth_ioctl.h b/drivers/scsi/gdth_ioctl.h
index 4c91894ac244..ee4c9bf1022a 100644
--- a/drivers/scsi/gdth_ioctl.h
+++ b/drivers/scsi/gdth_ioctl.h
@@ -27,11 +27,7 @@
#define GDTH_MAXSG 32 /* max. s/g elements */
#define MAX_LDRIVES 255 /* max. log. drive count */
-#ifdef GDTH_IOCTL_PROC
-#define MAX_HDRIVES 100 /* max. host drive count */
-#else
#define MAX_HDRIVES MAX_LDRIVES /* max. host drive count */
-#endif
/* scatter/gather element */
typedef struct {
@@ -178,91 +174,6 @@ typedef struct {
gdth_evt_data event_data;
} __attribute__((packed)) gdth_evt_str;
-
-#ifdef GDTH_IOCTL_PROC
-/* IOCTL structure (write) */
-typedef struct {
- u32 magic; /* IOCTL magic */
- u16 ioctl; /* IOCTL */
- u16 ionode; /* controller number */
- u16 service; /* controller service */
- u16 timeout; /* timeout */
- union {
- struct {
- u8 command[512]; /* controller command */
- u8 data[1]; /* add. data */
- } general;
- struct {
- u8 lock; /* lock/unlock */
- u8 drive_cnt; /* drive count */
- u16 drives[MAX_HDRIVES];/* drives */
- } lockdrv;
- struct {
- u8 lock; /* lock/unlock */
- u8 channel; /* channel */
- } lockchn;
- struct {
- int erase; /* erase event ? */
- int handle;
- u8 evt[EVENT_SIZE]; /* event structure */
- } event;
- struct {
- u8 bus; /* SCSI bus */
- u8 target; /* target ID */
- u8 lun; /* LUN */
- u8 cmd_len; /* command length */
- u8 cmd[12]; /* SCSI command */
- } scsi;
- struct {
- u16 hdr_no; /* host drive number */
- u8 flag; /* old meth./add/remove */
- } rescan;
- } iu;
-} gdth_iowr_str;
-
-/* IOCTL structure (read) */
-typedef struct {
- u32 size; /* buffer size */
- u32 status; /* IOCTL error code */
- union {
- struct {
- u8 data[1]; /* data */
- } general;
- struct {
- u16 version; /* driver version */
- } drvers;
- struct {
- u8 type; /* controller type */
- u16 info; /* slot etc. */
- u16 oem_id; /* OEM ID */
- u16 bios_ver; /* not used */
- u16 access; /* not used */
- u16 ext_type; /* extended type */
- u16 device_id; /* device ID */
- u16 sub_device_id; /* sub device ID */
- } ctrtype;
- struct {
- u8 version; /* OS version */
- u8 subversion; /* OS subversion */
- u16 revision; /* revision */
- } osvers;
- struct {
- u16 count; /* controller count */
- } ctrcnt;
- struct {
- int handle;
- u8 evt[EVENT_SIZE]; /* event structure */
- } event;
- struct {
- u8 bus; /* SCSI bus, 0xff: invalid */
- u8 target; /* target ID */
- u8 lun; /* LUN */
- u8 cluster_type; /* cluster properties */
- } hdr_list[MAX_HDRIVES]; /* index is host drive number */
- } iu;
-} gdth_iord_str;
-#endif
-
/* GDTIOCTL_GENERAL */
typedef struct {
u16 ionode; /* controller number */
diff --git a/drivers/scsi/gdth_proc.c b/drivers/scsi/gdth_proc.c
index 3a9751a80225..381d849726ac 100644
--- a/drivers/scsi/gdth_proc.c
+++ b/drivers/scsi/gdth_proc.c
@@ -31,7 +31,6 @@ static int gdth_set_asc_info(struct Scsi_Host *host, char *buffer,
int i, found;
gdth_cmd_str gdtcmd;
gdth_cpar_str *pcpar;
- u64 paddr;
char cmnd[MAX_COMMAND_SIZE];
memset(cmnd, 0xff, 12);
@@ -113,13 +112,23 @@ static int gdth_set_asc_info(struct Scsi_Host *host, char *buffer,
}
if (wb_mode) {
- if (!gdth_ioctl_alloc(ha, sizeof(gdth_cpar_str), TRUE, &paddr))
- return(-EBUSY);
+ unsigned long flags;
+
+ BUILD_BUG_ON(sizeof(gdth_cpar_str) > GDTH_SCRATCH);
+
+ spin_lock_irqsave(&ha->smp_lock, flags);
+ if (ha->scratch_busy) {
+ spin_unlock_irqrestore(&ha->smp_lock, flags);
+ return -EBUSY;
+ }
+ ha->scratch_busy = TRUE;
+ spin_unlock_irqrestore(&ha->smp_lock, flags);
+
pcpar = (gdth_cpar_str *)ha->pscratch;
memcpy( pcpar, &ha->cpar, sizeof(gdth_cpar_str) );
gdtcmd.Service = CACHESERVICE;
gdtcmd.OpCode = GDT_IOCTL;
- gdtcmd.u.ioctl.p_param = paddr;
+ gdtcmd.u.ioctl.p_param = ha->scratch_phys;
gdtcmd.u.ioctl.param_size = sizeof(gdth_cpar_str);
gdtcmd.u.ioctl.subfunc = CACHE_CONFIG;
gdtcmd.u.ioctl.channel = INVALID_CHANNEL;
@@ -127,7 +136,10 @@ static int gdth_set_asc_info(struct Scsi_Host *host, char *buffer,
gdth_execute(host, &gdtcmd, cmnd, 30, NULL);
- gdth_ioctl_free(ha, GDTH_SCRATCH, ha->pscratch, paddr);
+ spin_lock_irqsave(&ha->smp_lock, flags);
+ ha->scratch_busy = FALSE;
+ spin_unlock_irqrestore(&ha->smp_lock, flags);
+
printk("Done.\n");
return(orig_length);
}
@@ -143,7 +155,7 @@ int gdth_show_info(struct seq_file *m, struct Scsi_Host *host)
int id, i, j, k, sec, flag;
int no_mdrv = 0, drv_no, is_mirr;
u32 cnt;
- u64 paddr;
+ dma_addr_t paddr;
int rc = -ENOMEM;
gdth_cmd_str *gdtcmd;
@@ -217,20 +229,14 @@ int gdth_show_info(struct seq_file *m, struct Scsi_Host *host)
" Serial No.: \t0x%8X\tCache RAM size:\t%d KB\n",
ha->binfo.ser_no, ha->binfo.memsize / 1024);
-#ifdef GDTH_DMA_STATISTICS
- /* controller statistics */
- seq_puts(m, "\nController Statistics:\n");
- seq_printf(m,
- " 32-bit DMA buffer:\t%lu\t64-bit DMA buffer:\t%lu\n",
- ha->dma32_cnt, ha->dma64_cnt);
-#endif
-
if (ha->more_proc) {
+ size_t size = max_t(size_t, GDTH_SCRATCH, sizeof(gdth_hget_str));
+
/* more information: 2. about physical devices */
seq_puts(m, "\nPhysical Devices:");
flag = FALSE;
- buf = gdth_ioctl_alloc(ha, GDTH_SCRATCH, FALSE, &paddr);
+ buf = dma_alloc_coherent(&ha->pdev->dev, size, &paddr, GFP_KERNEL);
if (!buf)
goto stop_output;
for (i = 0; i < ha->bus_cnt; ++i) {
@@ -323,7 +329,6 @@ int gdth_show_info(struct seq_file *m, struct Scsi_Host *host)
}
}
}
- gdth_ioctl_free(ha, GDTH_SCRATCH, buf, paddr);
if (!flag)
seq_puts(m, "\n --\n");
@@ -332,9 +337,6 @@ int gdth_show_info(struct seq_file *m, struct Scsi_Host *host)
seq_puts(m, "\nLogical Drives:");
flag = FALSE;
- buf = gdth_ioctl_alloc(ha, GDTH_SCRATCH, FALSE, &paddr);
- if (!buf)
- goto stop_output;
for (i = 0; i < MAX_LDRIVES; ++i) {
if (!ha->hdr[i].is_logdrv)
continue;
@@ -408,8 +410,7 @@ int gdth_show_info(struct seq_file *m, struct Scsi_Host *host)
seq_printf(m,
" To Array Drv.:\t%s\n", hrec);
}
- gdth_ioctl_free(ha, GDTH_SCRATCH, buf, paddr);
-
+
if (!flag)
seq_puts(m, "\n --\n");
@@ -417,9 +418,6 @@ int gdth_show_info(struct seq_file *m, struct Scsi_Host *host)
seq_puts(m, "\nArray Drives:");
flag = FALSE;
- buf = gdth_ioctl_alloc(ha, GDTH_SCRATCH, FALSE, &paddr);
- if (!buf)
- goto stop_output;
for (i = 0; i < MAX_LDRIVES; ++i) {
if (!(ha->hdr[i].is_arraydrv && ha->hdr[i].is_master))
continue;
@@ -468,8 +466,7 @@ int gdth_show_info(struct seq_file *m, struct Scsi_Host *host)
hrec);
}
}
- gdth_ioctl_free(ha, GDTH_SCRATCH, buf, paddr);
-
+
if (!flag)
seq_puts(m, "\n --\n");
@@ -477,9 +474,6 @@ int gdth_show_info(struct seq_file *m, struct Scsi_Host *host)
seq_puts(m, "\nHost Drives:");
flag = FALSE;
- buf = gdth_ioctl_alloc(ha, sizeof(gdth_hget_str), FALSE, &paddr);
- if (!buf)
- goto stop_output;
for (i = 0; i < MAX_LDRIVES; ++i) {
if (!ha->hdr[i].is_logdrv ||
(ha->hdr[i].is_arraydrv && !ha->hdr[i].is_master))
@@ -510,7 +504,7 @@ int gdth_show_info(struct seq_file *m, struct Scsi_Host *host)
}
}
}
- gdth_ioctl_free(ha, sizeof(gdth_hget_str), buf, paddr);
+ dma_free_coherent(&ha->pdev->dev, size, buf, paddr);
for (i = 0; i < MAX_HDRIVES; ++i) {
if (!(ha->hdr[i].present))
@@ -563,65 +557,6 @@ free_fail:
return rc;
}
-static char *gdth_ioctl_alloc(gdth_ha_str *ha, int size, int scratch,
- u64 *paddr)
-{
- unsigned long flags;
- char *ret_val;
-
- if (size == 0)
- return NULL;
-
- spin_lock_irqsave(&ha->smp_lock, flags);
-
- if (!ha->scratch_busy && size <= GDTH_SCRATCH) {
- ha->scratch_busy = TRUE;
- ret_val = ha->pscratch;
- *paddr = ha->scratch_phys;
- } else if (scratch) {
- ret_val = NULL;
- } else {
- dma_addr_t dma_addr;
-
- ret_val = pci_alloc_consistent(ha->pdev, size, &dma_addr);
- *paddr = dma_addr;
- }
-
- spin_unlock_irqrestore(&ha->smp_lock, flags);
- return ret_val;
-}
-
-static void gdth_ioctl_free(gdth_ha_str *ha, int size, char *buf, u64 paddr)
-{
- unsigned long flags;
-
- if (buf == ha->pscratch) {
- spin_lock_irqsave(&ha->smp_lock, flags);
- ha->scratch_busy = FALSE;
- spin_unlock_irqrestore(&ha->smp_lock, flags);
- } else {
- pci_free_consistent(ha->pdev, size, buf, paddr);
- }
-}
-
-#ifdef GDTH_IOCTL_PROC
-static int gdth_ioctl_check_bin(gdth_ha_str *ha, u16 size)
-{
- unsigned long flags;
- int ret_val;
-
- spin_lock_irqsave(&ha->smp_lock, flags);
-
- ret_val = FALSE;
- if (ha->scratch_busy) {
- if (((gdth_iord_str *)ha->pscratch)->size == (u32)size)
- ret_val = TRUE;
- }
- spin_unlock_irqrestore(&ha->smp_lock, flags);
- return ret_val;
-}
-#endif
-
static void gdth_wait_completion(gdth_ha_str *ha, int busnum, int id)
{
unsigned long flags;
diff --git a/drivers/scsi/gdth_proc.h b/drivers/scsi/gdth_proc.h
index d7d0aa283695..4cc5377cb92e 100644
--- a/drivers/scsi/gdth_proc.h
+++ b/drivers/scsi/gdth_proc.h
@@ -12,9 +12,6 @@ int gdth_execute(struct Scsi_Host *shost, gdth_cmd_str *gdtcmd, char *cmnd,
static int gdth_set_asc_info(struct Scsi_Host *host, char *buffer,
int length, gdth_ha_str *ha);
-static char *gdth_ioctl_alloc(gdth_ha_str *ha, int size, int scratch,
- u64 *paddr);
-static void gdth_ioctl_free(gdth_ha_str *ha, int size, char *buf, u64 paddr);
static void gdth_wait_completion(gdth_ha_str *ha, int busnum, int id);
#endif
diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h
index af291947a54d..9bfa9f12d81e 100644
--- a/drivers/scsi/hisi_sas/hisi_sas.h
+++ b/drivers/scsi/hisi_sas/hisi_sas.h
@@ -14,9 +14,11 @@
#include <linux/acpi.h>
#include <linux/clk.h>
+#include <linux/debugfs.h>
#include <linux/dmapool.h>
#include <linux/iopoll.h>
#include <linux/lcm.h>
+#include <linux/libata.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of_address.h>
@@ -29,7 +31,7 @@
#define HISI_SAS_MAX_PHYS 9
#define HISI_SAS_MAX_QUEUES 32
-#define HISI_SAS_QUEUE_SLOTS 512
+#define HISI_SAS_QUEUE_SLOTS 4096
#define HISI_SAS_MAX_ITCT_ENTRIES 1024
#define HISI_SAS_MAX_DEVICES HISI_SAS_MAX_ITCT_ENTRIES
#define HISI_SAS_RESET_BIT 0
@@ -40,20 +42,25 @@
#define HISI_SAS_COMMAND_TABLE_SZ (sizeof(union hisi_sas_command_table))
#define hisi_sas_status_buf_addr(buf) \
- (buf + offsetof(struct hisi_sas_slot_buf_table, status_buffer))
-#define hisi_sas_status_buf_addr_mem(slot) hisi_sas_status_buf_addr(slot->buf)
+ ((buf) + offsetof(struct hisi_sas_slot_buf_table, status_buffer))
+#define hisi_sas_status_buf_addr_mem(slot) hisi_sas_status_buf_addr((slot)->buf)
#define hisi_sas_status_buf_addr_dma(slot) \
- hisi_sas_status_buf_addr(slot->buf_dma)
+ hisi_sas_status_buf_addr((slot)->buf_dma)
#define hisi_sas_cmd_hdr_addr(buf) \
- (buf + offsetof(struct hisi_sas_slot_buf_table, command_header))
-#define hisi_sas_cmd_hdr_addr_mem(slot) hisi_sas_cmd_hdr_addr(slot->buf)
-#define hisi_sas_cmd_hdr_addr_dma(slot) hisi_sas_cmd_hdr_addr(slot->buf_dma)
+ ((buf) + offsetof(struct hisi_sas_slot_buf_table, command_header))
+#define hisi_sas_cmd_hdr_addr_mem(slot) hisi_sas_cmd_hdr_addr((slot)->buf)
+#define hisi_sas_cmd_hdr_addr_dma(slot) hisi_sas_cmd_hdr_addr((slot)->buf_dma)
#define hisi_sas_sge_addr(buf) \
- (buf + offsetof(struct hisi_sas_slot_buf_table, sge_page))
-#define hisi_sas_sge_addr_mem(slot) hisi_sas_sge_addr(slot->buf)
-#define hisi_sas_sge_addr_dma(slot) hisi_sas_sge_addr(slot->buf_dma)
+ ((buf) + offsetof(struct hisi_sas_slot_buf_table, sge_page))
+#define hisi_sas_sge_addr_mem(slot) hisi_sas_sge_addr((slot)->buf)
+#define hisi_sas_sge_addr_dma(slot) hisi_sas_sge_addr((slot)->buf_dma)
+
+#define hisi_sas_sge_dif_addr(buf) \
+ ((buf) + offsetof(struct hisi_sas_slot_dif_buf_table, sge_dif_page))
+#define hisi_sas_sge_dif_addr_mem(slot) hisi_sas_sge_dif_addr((slot)->buf)
+#define hisi_sas_sge_dif_addr_dma(slot) hisi_sas_sge_dif_addr((slot)->buf_dma)
#define HISI_SAS_MAX_SSP_RESP_SZ (sizeof(struct ssp_frame_hdr) + 1024)
#define HISI_SAS_MAX_SMP_RESP_SZ 1028
@@ -73,7 +80,13 @@
SHOST_DIF_TYPE2_PROTECTION | \
SHOST_DIF_TYPE3_PROTECTION)
-#define HISI_SAS_PROT_MASK (HISI_SAS_DIF_PROT_MASK)
+#define HISI_SAS_DIX_PROT_MASK (SHOST_DIX_TYPE1_PROTECTION | \
+ SHOST_DIX_TYPE2_PROTECTION | \
+ SHOST_DIX_TYPE3_PROTECTION)
+
+#define HISI_SAS_PROT_MASK (HISI_SAS_DIF_PROT_MASK | HISI_SAS_DIX_PROT_MASK)
+
+#define HISI_SAS_WAIT_PHYUP_TIMEOUT 20
struct hisi_hba;
@@ -83,8 +96,8 @@ enum {
};
enum dev_status {
+ HISI_SAS_DEV_INIT,
HISI_SAS_DEV_NORMAL,
- HISI_SAS_DEV_EH,
};
enum {
@@ -145,6 +158,7 @@ struct hisi_sas_phy {
struct asd_sas_phy sas_phy;
struct sas_identify identify;
struct completion *reset_completion;
+ struct timer_list timer;
spinlock_t lock;
u64 port_id; /* from hw */
u64 frame_rcvd_size;
@@ -153,6 +167,7 @@ struct hisi_sas_phy {
u8 in_reset;
u8 reserved[2];
u32 phy_type;
+ u32 code_violation_err_count;
enum sas_linkrate minimum_linkrate;
enum sas_linkrate maximum_linkrate;
};
@@ -165,6 +180,7 @@ struct hisi_sas_port {
struct hisi_sas_cq {
struct hisi_hba *hisi_hba;
+ const struct cpumask *pci_irq_mask;
struct tasklet_struct tasklet;
int rd_point;
int id;
@@ -185,9 +201,10 @@ struct hisi_sas_device {
struct hisi_sas_dq *dq;
struct list_head list;
enum sas_device_type dev_type;
+ enum dev_status dev_status;
int device_id;
int sata_idx;
- u8 dev_status;
+ spinlock_t lock; /* For protecting slots */
};
struct hisi_sas_tmf_task {
@@ -203,12 +220,14 @@ struct hisi_sas_slot {
struct sas_task *task;
struct hisi_sas_port *port;
u64 n_elem;
+ u64 n_elem_dif;
int dlvry_queue;
int dlvry_queue_slot;
int cmplt_queue;
int cmplt_queue_slot;
int abort;
int ready;
+ int device_id;
void *cmd_hdr;
dma_addr_t cmd_hdr_dma;
struct timer_list internal_abort_timer;
@@ -220,6 +239,24 @@ struct hisi_sas_slot {
u16 idx;
};
+#define HISI_SAS_DEBUGFS_REG(x) {#x, x}
+
+struct hisi_sas_debugfs_reg_lu {
+ char *name;
+ int off;
+};
+
+struct hisi_sas_debugfs_reg {
+ const struct hisi_sas_debugfs_reg_lu *lu;
+ int count;
+ int base_off;
+ union {
+ u32 (*read_global_reg)(struct hisi_hba *hisi_hba, u32 off);
+ u32 (*read_port_reg)(struct hisi_hba *hisi_hba, int port,
+ u32 off);
+ };
+};
+
struct hisi_sas_hw {
int (*hw_init)(struct hisi_hba *hisi_hba);
void (*setup_itct)(struct hisi_hba *hisi_hba,
@@ -227,7 +264,7 @@ struct hisi_sas_hw {
int (*slot_index_alloc)(struct hisi_hba *hisi_hba,
struct domain_device *device);
struct hisi_sas_device *(*alloc_dev)(struct domain_device *device);
- void (*sl_notify)(struct hisi_hba *hisi_hba, int phy_no);
+ void (*sl_notify_ssp)(struct hisi_hba *hisi_hba, int phy_no);
int (*get_free_slot)(struct hisi_hba *hisi_hba, struct hisi_sas_dq *dq);
void (*start_delivery)(struct hisi_sas_dq *dq);
void (*prep_ssp)(struct hisi_hba *hisi_hba,
@@ -259,11 +296,16 @@ struct hisi_sas_hw {
u32 (*get_phys_state)(struct hisi_hba *hisi_hba);
int (*write_gpio)(struct hisi_hba *hisi_hba, u8 reg_type,
u8 reg_index, u8 reg_count, u8 *write_data);
- void (*wait_cmds_complete_timeout)(struct hisi_hba *hisi_hba,
- int delay_ms, int timeout_ms);
+ int (*wait_cmds_complete_timeout)(struct hisi_hba *hisi_hba,
+ int delay_ms, int timeout_ms);
+ void (*snapshot_prepare)(struct hisi_hba *hisi_hba);
+ void (*snapshot_restore)(struct hisi_hba *hisi_hba);
int max_command_entries;
int complete_hdr_size;
struct scsi_host_template *sht;
+
+ const struct hisi_sas_debugfs_reg *debugfs_reg_global;
+ const struct hisi_sas_debugfs_reg *debugfs_reg_port;
};
struct hisi_hba {
@@ -329,9 +371,25 @@ struct hisi_hba {
const struct hisi_sas_hw *hw; /* Low level hw interface */
unsigned long sata_dev_bitmap[BITS_TO_LONGS(HISI_SAS_MAX_DEVICES)];
struct work_struct rst_work;
+ struct work_struct debugfs_work;
u32 phy_state;
u32 intr_coal_ticks; /* Time of interrupt coalesce in us */
u32 intr_coal_count; /* Interrupt count to coalesce */
+
+ int cq_nvecs;
+ unsigned int *reply_map;
+
+ /* debugfs memories */
+ u32 *debugfs_global_reg;
+ u32 *debugfs_port_reg[HISI_SAS_MAX_PHYS];
+ void *debugfs_complete_hdr[HISI_SAS_MAX_QUEUES];
+ struct hisi_sas_cmd_hdr *debugfs_cmd_hdr[HISI_SAS_MAX_QUEUES];
+ struct hisi_sas_iost *debugfs_iost;
+ struct hisi_sas_itct *debugfs_itct;
+
+ struct dentry *debugfs_dir;
+ struct dentry *debugfs_dump_dentry;
+ bool debugfs_snapshot;
};
/* Generic HW DMA host memory structures */
@@ -430,6 +488,11 @@ struct hisi_sas_sge_page {
struct hisi_sas_sge sge[HISI_SAS_SGE_PAGE_CNT];
} __aligned(16);
+#define HISI_SAS_SGE_DIF_PAGE_CNT SG_CHUNK_SIZE
+struct hisi_sas_sge_dif_page {
+ struct hisi_sas_sge sge[HISI_SAS_SGE_DIF_PAGE_CNT];
+} __aligned(16);
+
struct hisi_sas_command_table_ssp {
struct ssp_frame_hdr hdr;
union {
@@ -460,9 +523,18 @@ struct hisi_sas_slot_buf_table {
struct hisi_sas_sge_page sge_page;
};
+struct hisi_sas_slot_dif_buf_table {
+ struct hisi_sas_slot_buf_table slot_buf;
+ struct hisi_sas_sge_dif_page sge_dif_page;
+};
+
extern struct scsi_transport_template *hisi_sas_stt;
+
+extern bool hisi_sas_debugfs_enable;
+extern struct dentry *hisi_sas_debugfs_dir;
+
extern void hisi_sas_stop_phys(struct hisi_hba *hisi_hba);
-extern int hisi_sas_alloc(struct hisi_hba *hisi_hba, struct Scsi_Host *shost);
+extern int hisi_sas_alloc(struct hisi_hba *hisi_hba);
extern void hisi_sas_free(struct hisi_hba *hisi_hba);
extern u8 hisi_sas_get_ata_protocol(struct host_to_dev_fis *fis,
int direction);
@@ -487,10 +559,14 @@ extern void hisi_sas_init_mem(struct hisi_hba *hisi_hba);
extern void hisi_sas_rst_work_handler(struct work_struct *work);
extern void hisi_sas_sync_rst_work_handler(struct work_struct *work);
extern void hisi_sas_kill_tasklets(struct hisi_hba *hisi_hba);
+extern void hisi_sas_phy_oob_ready(struct hisi_hba *hisi_hba, int phy_no);
extern bool hisi_sas_notify_phy_event(struct hisi_sas_phy *phy,
enum hisi_sas_phy_event event);
extern void hisi_sas_release_tasks(struct hisi_hba *hisi_hba);
extern u8 hisi_sas_get_prog_phy_linkrate_mask(enum sas_linkrate max);
extern void hisi_sas_controller_reset_prepare(struct hisi_hba *hisi_hba);
extern void hisi_sas_controller_reset_done(struct hisi_hba *hisi_hba);
+extern void hisi_sas_debugfs_init(struct hisi_hba *hisi_hba);
+extern void hisi_sas_debugfs_exit(struct hisi_hba *hisi_hba);
+extern void hisi_sas_debugfs_work_handler(struct work_struct *work);
#endif
diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c
index bc17fa0d8375..14bac4966c87 100644
--- a/drivers/scsi/hisi_sas/hisi_sas_main.c
+++ b/drivers/scsi/hisi_sas/hisi_sas_main.c
@@ -10,6 +10,7 @@
*/
#include "hisi_sas.h"
+#include "../libsas/sas_internal.h"
#define DRV_NAME "hisi_sas"
#define DEV_IS_GONE(dev) \
@@ -144,7 +145,7 @@ EXPORT_SYMBOL_GPL(hisi_sas_get_ncq_tag);
*/
u8 hisi_sas_get_prog_phy_linkrate_mask(enum sas_linkrate max)
{
- u16 rate = 0;
+ u8 rate = 0;
int i;
max -= SAS_LINK_RATE_1_5_GBPS;
@@ -241,8 +242,9 @@ static void hisi_sas_slot_index_init(struct hisi_hba *hisi_hba)
void hisi_sas_slot_task_free(struct hisi_hba *hisi_hba, struct sas_task *task,
struct hisi_sas_slot *slot)
{
- struct hisi_sas_dq *dq = &hisi_hba->dq[slot->dlvry_queue];
unsigned long flags;
+ int device_id = slot->device_id;
+ struct hisi_sas_device *sas_dev = &hisi_hba->devices[device_id];
if (task) {
struct device *dev = hisi_hba->dev;
@@ -252,17 +254,24 @@ void hisi_sas_slot_task_free(struct hisi_hba *hisi_hba, struct sas_task *task,
task->lldd_task = NULL;
- if (!sas_protocol_ata(task->task_proto))
+ if (!sas_protocol_ata(task->task_proto)) {
+ struct sas_ssp_task *ssp_task = &task->ssp_task;
+ struct scsi_cmnd *scsi_cmnd = ssp_task->cmd;
+
if (slot->n_elem)
dma_unmap_sg(dev, task->scatter,
task->num_scatter,
task->data_dir);
+ if (slot->n_elem_dif)
+ dma_unmap_sg(dev, scsi_prot_sglist(scsi_cmnd),
+ scsi_prot_sg_count(scsi_cmnd),
+ task->data_dir);
+ }
}
-
- spin_lock_irqsave(&dq->lock, flags);
+ spin_lock_irqsave(&sas_dev->lock, flags);
list_del_init(&slot->entry);
- spin_unlock_irqrestore(&dq->lock, flags);
+ spin_unlock_irqrestore(&sas_dev->lock, flags);
memset(slot, 0, offsetof(struct hisi_sas_slot, buf));
@@ -380,6 +389,59 @@ prep_out:
return rc;
}
+static void hisi_sas_dif_dma_unmap(struct hisi_hba *hisi_hba,
+ struct sas_task *task, int n_elem_dif)
+{
+ struct device *dev = hisi_hba->dev;
+
+ if (n_elem_dif) {
+ struct sas_ssp_task *ssp_task = &task->ssp_task;
+ struct scsi_cmnd *scsi_cmnd = ssp_task->cmd;
+
+ dma_unmap_sg(dev, scsi_prot_sglist(scsi_cmnd),
+ scsi_prot_sg_count(scsi_cmnd),
+ task->data_dir);
+ }
+}
+
+static int hisi_sas_dif_dma_map(struct hisi_hba *hisi_hba,
+ int *n_elem_dif, struct sas_task *task)
+{
+ struct device *dev = hisi_hba->dev;
+ struct sas_ssp_task *ssp_task;
+ struct scsi_cmnd *scsi_cmnd;
+ int rc;
+
+ if (task->num_scatter) {
+ ssp_task = &task->ssp_task;
+ scsi_cmnd = ssp_task->cmd;
+
+ if (scsi_prot_sg_count(scsi_cmnd)) {
+ *n_elem_dif = dma_map_sg(dev,
+ scsi_prot_sglist(scsi_cmnd),
+ scsi_prot_sg_count(scsi_cmnd),
+ task->data_dir);
+
+ if (!*n_elem_dif)
+ return -ENOMEM;
+
+ if (*n_elem_dif > HISI_SAS_SGE_DIF_PAGE_CNT) {
+ dev_err(dev, "task prep: n_elem_dif(%d) too large\n",
+ *n_elem_dif);
+ rc = -EINVAL;
+ goto err_out_dif_dma_unmap;
+ }
+ }
+ }
+
+ return 0;
+
+err_out_dif_dma_unmap:
+ dma_unmap_sg(dev, scsi_prot_sglist(scsi_cmnd),
+ scsi_prot_sg_count(scsi_cmnd), task->data_dir);
+ return rc;
+}
+
static int hisi_sas_task_prep(struct sas_task *task,
struct hisi_sas_dq **dq_pointer,
bool is_tmf, struct hisi_sas_tmf_task *tmf,
@@ -394,7 +456,7 @@ static int hisi_sas_task_prep(struct sas_task *task,
struct asd_sas_port *sas_port = device->port;
struct device *dev = hisi_hba->dev;
int dlvry_queue_slot, dlvry_queue, rc, slot_idx;
- int n_elem = 0, n_elem_req = 0, n_elem_resp = 0;
+ int n_elem = 0, n_elem_dif = 0, n_elem_req = 0, n_elem_resp = 0;
struct hisi_sas_dq *dq;
unsigned long flags;
int wr_q_index;
@@ -410,7 +472,14 @@ static int hisi_sas_task_prep(struct sas_task *task,
return -ECOMM;
}
- *dq_pointer = dq = sas_dev->dq;
+ if (hisi_hba->reply_map) {
+ int cpu = raw_smp_processor_id();
+ unsigned int dq_index = hisi_hba->reply_map[cpu];
+
+ *dq_pointer = dq = &hisi_hba->dq[dq_index];
+ } else {
+ *dq_pointer = dq = sas_dev->dq;
+ }
port = to_hisi_sas_port(sas_port);
if (port && !port->port_attached) {
@@ -427,6 +496,12 @@ static int hisi_sas_task_prep(struct sas_task *task,
if (rc < 0)
goto prep_out;
+ if (!sas_protocol_ata(task->task_proto)) {
+ rc = hisi_sas_dif_dma_map(hisi_hba, &n_elem_dif, task);
+ if (rc < 0)
+ goto err_out_dma_unmap;
+ }
+
if (hisi_hba->hw->slot_index_alloc)
rc = hisi_hba->hw->slot_index_alloc(hisi_hba, device);
else {
@@ -445,7 +520,7 @@ static int hisi_sas_task_prep(struct sas_task *task,
rc = hisi_sas_slot_index_alloc(hisi_hba, scsi_cmnd);
}
if (rc < 0)
- goto err_out_dma_unmap;
+ goto err_out_dif_dma_unmap;
slot_idx = rc;
slot = &hisi_hba->slot_info[slot_idx];
@@ -459,13 +534,17 @@ static int hisi_sas_task_prep(struct sas_task *task,
}
list_add_tail(&slot->delivery, &dq->list);
- list_add_tail(&slot->entry, &sas_dev->list);
spin_unlock_irqrestore(&dq->lock, flags);
+ spin_lock_irqsave(&sas_dev->lock, flags);
+ list_add_tail(&slot->entry, &sas_dev->list);
+ spin_unlock_irqrestore(&sas_dev->lock, flags);
dlvry_queue = dq->id;
dlvry_queue_slot = wr_q_index;
+ slot->device_id = sas_dev->device_id;
slot->n_elem = n_elem;
+ slot->n_elem_dif = n_elem_dif;
slot->dlvry_queue = dlvry_queue;
slot->dlvry_queue_slot = dlvry_queue_slot;
cmd_hdr_base = hisi_hba->cmd_hdr[dlvry_queue];
@@ -509,6 +588,9 @@ static int hisi_sas_task_prep(struct sas_task *task,
err_out_tag:
hisi_sas_slot_index_free(hisi_hba, slot_idx);
+err_out_dif_dma_unmap:
+ if (!sas_protocol_ata(task->task_proto))
+ hisi_sas_dif_dma_unmap(hisi_hba, task, n_elem_dif);
err_out_dma_unmap:
hisi_sas_dma_unmap(hisi_hba, task, n_elem,
n_elem_req, n_elem_resp);
@@ -626,11 +708,12 @@ static struct hisi_sas_device *hisi_sas_alloc_dev(struct domain_device *device)
hisi_hba->devices[i].device_id = i;
sas_dev = &hisi_hba->devices[i];
- sas_dev->dev_status = HISI_SAS_DEV_NORMAL;
+ sas_dev->dev_status = HISI_SAS_DEV_INIT;
sas_dev->dev_type = device->dev_type;
sas_dev->hisi_hba = hisi_hba;
sas_dev->sas_device = device;
sas_dev->dq = dq;
+ spin_lock_init(&sas_dev->lock);
INIT_LIST_HEAD(&hisi_hba->devices[i].list);
break;
}
@@ -650,6 +733,8 @@ static int hisi_sas_init_device(struct domain_device *device)
struct hisi_sas_tmf_task tmf_task;
int retry = HISI_SAS_SRST_ATA_DISK_CNT;
struct hisi_hba *hisi_hba = dev_to_hisi_hba(device);
+ struct device *dev = hisi_hba->dev;
+ struct sas_phy *local_phy;
switch (device->dev_type) {
case SAS_END_DEVICE:
@@ -665,6 +750,31 @@ static int hisi_sas_init_device(struct domain_device *device)
case SAS_SATA_PM:
case SAS_SATA_PM_PORT:
case SAS_SATA_PENDING:
+ /*
+ * send HARD RESET to clear previous affiliation of
+ * STP target port
+ */
+ local_phy = sas_get_local_phy(device);
+ if (!scsi_is_sas_phy_local(local_phy)) {
+ unsigned long deadline = ata_deadline(jiffies, 20000);
+ struct sata_device *sata_dev = &device->sata_dev;
+ struct ata_host *ata_host = sata_dev->ata_host;
+ struct ata_port_operations *ops = ata_host->ops;
+ struct ata_port *ap = sata_dev->ap;
+ struct ata_link *link;
+ unsigned int classes;
+
+ ata_for_each_link(link, ap, EDGE)
+ rc = ops->hardreset(link, &classes,
+ deadline);
+ }
+ sas_put_local_phy(local_phy);
+ if (rc) {
+ dev_warn(dev, "SATA disk hardreset fail: 0x%x\n",
+ rc);
+ return rc;
+ }
+
while (retry-- > 0) {
rc = hisi_sas_softreset_ata_disk(device);
if (!rc)
@@ -727,6 +837,7 @@ static int hisi_sas_dev_found(struct domain_device *device)
rc = hisi_sas_init_device(device);
if (rc)
goto err_out;
+ sas_dev->dev_status = HISI_SAS_DEV_NORMAL;
return 0;
err_out:
@@ -778,7 +889,8 @@ static void hisi_sas_phyup_work(struct work_struct *work)
struct asd_sas_phy *sas_phy = &phy->sas_phy;
int phy_no = sas_phy->id;
- hisi_hba->hw->sl_notify(hisi_hba, phy_no); /* This requires a sleep */
+ if (phy->identify.target_port_protocols == SAS_PROTOCOL_SSP)
+ hisi_hba->hw->sl_notify_ssp(hisi_hba, phy_no);
hisi_sas_bytes_dmaed(hisi_hba, phy_no);
}
@@ -808,6 +920,30 @@ bool hisi_sas_notify_phy_event(struct hisi_sas_phy *phy,
}
EXPORT_SYMBOL_GPL(hisi_sas_notify_phy_event);
+static void hisi_sas_wait_phyup_timedout(struct timer_list *t)
+{
+ struct hisi_sas_phy *phy = from_timer(phy, t, timer);
+ struct hisi_hba *hisi_hba = phy->hisi_hba;
+ struct device *dev = hisi_hba->dev;
+ int phy_no = phy->sas_phy.id;
+
+ dev_warn(dev, "phy%d wait phyup timeout, issuing link reset\n", phy_no);
+ hisi_sas_notify_phy_event(phy, HISI_PHYE_LINK_RESET);
+}
+
+void hisi_sas_phy_oob_ready(struct hisi_hba *hisi_hba, int phy_no)
+{
+ struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no];
+ struct device *dev = hisi_hba->dev;
+
+ if (!timer_pending(&phy->timer)) {
+ dev_dbg(dev, "phy%d OOB ready\n", phy_no);
+ phy->timer.expires = jiffies + HISI_SAS_WAIT_PHYUP_TIMEOUT * HZ;
+ add_timer(&phy->timer);
+ }
+}
+EXPORT_SYMBOL_GPL(hisi_sas_phy_oob_ready);
+
static void hisi_sas_phy_init(struct hisi_hba *hisi_hba, int phy_no)
{
struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no];
@@ -836,6 +972,8 @@ static void hisi_sas_phy_init(struct hisi_hba *hisi_hba, int phy_no)
INIT_WORK(&phy->works[i], hisi_sas_phye_fns[i]);
spin_lock_init(&phy->lock);
+
+ timer_setup(&phy->timer, hisi_sas_wait_phyup_timedout, 0);
}
static void hisi_sas_port_notify_formed(struct asd_sas_phy *sas_phy)
@@ -872,7 +1010,8 @@ static void hisi_sas_do_release_task(struct hisi_hba *hisi_hba, struct sas_task
spin_lock_irqsave(&task->task_state_lock, flags);
task->task_state_flags &=
~(SAS_TASK_STATE_PENDING | SAS_TASK_AT_INITIATOR);
- task->task_state_flags |= SAS_TASK_STATE_DONE;
+ if (!slot->is_internal && task->task_proto != SAS_PROTOCOL_SMP)
+ task->task_state_flags |= SAS_TASK_STATE_DONE;
spin_unlock_irqrestore(&task->task_state_lock, flags);
}
@@ -926,7 +1065,7 @@ static void hisi_sas_dev_gone(struct domain_device *device)
if (!test_bit(HISI_SAS_RESET_BIT, &hisi_hba->flags)) {
hisi_sas_internal_task_abort(hisi_hba, device,
- HISI_SAS_INT_ABT_DEV, 0);
+ HISI_SAS_INT_ABT_DEV, 0);
hisi_sas_dereg_device(hisi_hba, device);
@@ -946,7 +1085,7 @@ static int hisi_sas_queue_command(struct sas_task *task, gfp_t gfp_flags)
return hisi_sas_task_exec(task, gfp_flags, 0, NULL);
}
-static void hisi_sas_phy_set_linkrate(struct hisi_hba *hisi_hba, int phy_no,
+static int hisi_sas_phy_set_linkrate(struct hisi_hba *hisi_hba, int phy_no,
struct sas_phy_linkrates *r)
{
struct sas_phy_linkrates _r;
@@ -955,6 +1094,9 @@ static void hisi_sas_phy_set_linkrate(struct hisi_hba *hisi_hba, int phy_no,
struct asd_sas_phy *sas_phy = &phy->sas_phy;
enum sas_linkrate min, max;
+ if (r->minimum_linkrate > SAS_LINK_RATE_1_5_GBPS)
+ return -EINVAL;
+
if (r->maximum_linkrate == SAS_LINK_RATE_UNKNOWN) {
max = sas_phy->phy->maximum_linkrate;
min = r->minimum_linkrate;
@@ -962,7 +1104,7 @@ static void hisi_sas_phy_set_linkrate(struct hisi_hba *hisi_hba, int phy_no,
max = r->maximum_linkrate;
min = sas_phy->phy->minimum_linkrate;
} else
- return;
+ return -EINVAL;
_r.maximum_linkrate = max;
_r.minimum_linkrate = min;
@@ -974,6 +1116,8 @@ static void hisi_sas_phy_set_linkrate(struct hisi_hba *hisi_hba, int phy_no,
msleep(100);
hisi_hba->hw->phy_set_linkrate(hisi_hba, phy_no, &_r);
hisi_hba->hw->phy_start(hisi_hba, phy_no);
+
+ return 0;
}
static int hisi_sas_control_phy(struct asd_sas_phy *sas_phy, enum phy_func func,
@@ -999,8 +1143,7 @@ static int hisi_sas_control_phy(struct asd_sas_phy *sas_phy, enum phy_func func,
break;
case PHY_FUNC_SET_LINK_RATE:
- hisi_sas_phy_set_linkrate(hisi_hba, phy_no, funcdata);
- break;
+ return hisi_sas_phy_set_linkrate(hisi_hba, phy_no, funcdata);
case PHY_FUNC_GET_EVENTS:
if (hisi_hba->hw->get_events) {
hisi_hba->hw->get_events(hisi_hba, phy_no);
@@ -1068,7 +1211,7 @@ static int hisi_sas_exec_internal_tmf_task(struct domain_device *device,
task->task_done = hisi_sas_task_done;
task->slow_task->timer.function = hisi_sas_tmf_timedout;
- task->slow_task->timer.expires = jiffies + TASK_TIMEOUT*HZ;
+ task->slow_task->timer.expires = jiffies + TASK_TIMEOUT * HZ;
add_timer(&task->slow_task->timer);
res = hisi_sas_task_exec(task, GFP_KERNEL, 1, tmf);
@@ -1429,6 +1572,9 @@ static int hisi_sas_controller_reset(struct hisi_hba *hisi_hba)
struct Scsi_Host *shost = hisi_hba->shost;
int rc;
+ if (hisi_sas_debugfs_enable && hisi_hba->debugfs_itct)
+ queue_work(hisi_hba->wq, &hisi_hba->debugfs_work);
+
if (!hisi_hba->hw->soft_reset)
return -1;
@@ -1491,7 +1637,6 @@ static int hisi_sas_abort_task(struct sas_task *task)
task->task_state_flags |= SAS_TASK_STATE_ABORTED;
spin_unlock_irqrestore(&task->task_state_lock, flags);
- sas_dev->dev_status = HISI_SAS_DEV_EH;
if (task->lldd_task && task->task_proto & SAS_PROTOCOL_SSP) {
struct scsi_cmnd *cmnd = task->uldd_task;
struct hisi_sas_slot *slot = task->lldd_task;
@@ -1527,7 +1672,8 @@ static int hisi_sas_abort_task(struct sas_task *task)
task->task_proto & SAS_PROTOCOL_STP) {
if (task->dev->dev_type == SAS_SATA_DEV) {
rc = hisi_sas_internal_task_abort(hisi_hba, device,
- HISI_SAS_INT_ABT_DEV, 0);
+ HISI_SAS_INT_ABT_DEV,
+ 0);
if (rc < 0) {
dev_err(dev, "abort task: internal abort failed\n");
goto out;
@@ -1542,7 +1688,7 @@ static int hisi_sas_abort_task(struct sas_task *task)
struct hisi_sas_cq *cq = &hisi_hba->cq[slot->dlvry_queue];
rc = hisi_sas_internal_task_abort(hisi_hba, device,
- HISI_SAS_INT_ABT_CMD, tag);
+ HISI_SAS_INT_ABT_CMD, tag);
if (((rc < 0) || (rc == TMF_RESP_FUNC_FAILED)) &&
task->lldd_task) {
/*
@@ -1568,7 +1714,7 @@ static int hisi_sas_abort_task_set(struct domain_device *device, u8 *lun)
int rc = TMF_RESP_FUNC_FAILED;
rc = hisi_sas_internal_task_abort(hisi_hba, device,
- HISI_SAS_INT_ABT_DEV, 0);
+ HISI_SAS_INT_ABT_DEV, 0);
if (rc < 0) {
dev_err(dev, "abort task set: internal abort rc=%d\n", rc);
return TMF_RESP_FUNC_FAILED;
@@ -1586,8 +1732,8 @@ static int hisi_sas_abort_task_set(struct domain_device *device, u8 *lun)
static int hisi_sas_clear_aca(struct domain_device *device, u8 *lun)
{
- int rc = TMF_RESP_FUNC_FAILED;
struct hisi_sas_tmf_task tmf_task;
+ int rc;
tmf_task.tmf = TMF_CLEAR_ACA;
rc = hisi_sas_debug_issue_ssp_tmf(device, lun, &tmf_task);
@@ -1598,20 +1744,23 @@ static int hisi_sas_clear_aca(struct domain_device *device, u8 *lun)
static int hisi_sas_debug_I_T_nexus_reset(struct domain_device *device)
{
struct sas_phy *local_phy = sas_get_local_phy(device);
- int rc, reset_type = (device->dev_type == SAS_SATA_DEV ||
- (device->tproto & SAS_PROTOCOL_STP)) ? 0 : 1;
+ struct hisi_sas_device *sas_dev = device->lldd_dev;
struct hisi_hba *hisi_hba = dev_to_hisi_hba(device);
struct sas_ha_struct *sas_ha = &hisi_hba->sha;
struct asd_sas_phy *sas_phy = sas_ha->sas_phy[local_phy->number];
struct hisi_sas_phy *phy = container_of(sas_phy,
struct hisi_sas_phy, sas_phy);
DECLARE_COMPLETION_ONSTACK(phyreset);
+ int rc, reset_type;
if (scsi_is_sas_phy_local(local_phy)) {
phy->in_reset = 1;
phy->reset_completion = &phyreset;
}
+ reset_type = (sas_dev->dev_status == HISI_SAS_DEV_INIT ||
+ !dev_is_sata(device)) ? 1 : 0;
+
rc = sas_phy_reset(local_phy, reset_type);
sas_put_local_phy(local_phy);
@@ -1627,31 +1776,37 @@ static int hisi_sas_debug_I_T_nexus_reset(struct domain_device *device)
/* report PHY down if timed out */
if (!ret)
hisi_sas_phy_down(hisi_hba, sas_phy->id, 0);
- } else
+ } else if (sas_dev->dev_status != HISI_SAS_DEV_INIT) {
+ /*
+ * If in init state, we rely on caller to wait for link to be
+ * ready; otherwise, delay.
+ */
msleep(2000);
+ }
return rc;
}
static int hisi_sas_I_T_nexus_reset(struct domain_device *device)
{
- struct hisi_sas_device *sas_dev = device->lldd_dev;
struct hisi_hba *hisi_hba = dev_to_hisi_hba(device);
struct device *dev = hisi_hba->dev;
- int rc = TMF_RESP_FUNC_FAILED;
-
- if (sas_dev->dev_status != HISI_SAS_DEV_EH)
- return TMF_RESP_FUNC_FAILED;
- sas_dev->dev_status = HISI_SAS_DEV_NORMAL;
+ int rc;
rc = hisi_sas_internal_task_abort(hisi_hba, device,
- HISI_SAS_INT_ABT_DEV, 0);
+ HISI_SAS_INT_ABT_DEV, 0);
if (rc < 0) {
dev_err(dev, "I_T nexus reset: internal abort (%d)\n", rc);
return TMF_RESP_FUNC_FAILED;
}
hisi_sas_dereg_device(hisi_hba, device);
+ if (dev_is_sata(device)) {
+ rc = hisi_sas_softreset_ata_disk(device);
+ if (rc)
+ return TMF_RESP_FUNC_FAILED;
+ }
+
rc = hisi_sas_debug_I_T_nexus_reset(device);
if ((rc == TMF_RESP_FUNC_COMPLETE) || (rc == -ENODEV))
@@ -1667,7 +1822,6 @@ static int hisi_sas_lu_reset(struct domain_device *device, u8 *lun)
struct device *dev = hisi_hba->dev;
int rc = TMF_RESP_FUNC_FAILED;
- sas_dev->dev_status = HISI_SAS_DEV_EH;
if (dev_is_sata(device)) {
struct sas_phy *phy;
@@ -1691,7 +1845,7 @@ static int hisi_sas_lu_reset(struct domain_device *device, u8 *lun)
struct hisi_sas_tmf_task tmf_task = { .tmf = TMF_LU_RESET };
rc = hisi_sas_internal_task_abort(hisi_hba, device,
- HISI_SAS_INT_ABT_DEV, 0);
+ HISI_SAS_INT_ABT_DEV, 0);
if (rc < 0) {
dev_err(dev, "lu_reset: internal abort failed\n");
goto out;
@@ -1777,7 +1931,7 @@ static int hisi_sas_query_task(struct sas_task *task)
static int
hisi_sas_internal_abort_task_exec(struct hisi_hba *hisi_hba, int device_id,
struct sas_task *task, int abort_flag,
- int task_tag)
+ int task_tag, struct hisi_sas_dq *dq)
{
struct domain_device *device = task->dev;
struct hisi_sas_device *sas_dev = device->lldd_dev;
@@ -1786,7 +1940,6 @@ hisi_sas_internal_abort_task_exec(struct hisi_hba *hisi_hba, int device_id,
struct hisi_sas_slot *slot;
struct asd_sas_port *sas_port = device->port;
struct hisi_sas_cmd_hdr *cmd_hdr_base;
- struct hisi_sas_dq *dq = sas_dev->dq;
int dlvry_queue_slot, dlvry_queue, n_elem = 0, rc, slot_idx;
unsigned long flags, flags_dq = 0;
int wr_q_index;
@@ -1816,10 +1969,14 @@ hisi_sas_internal_abort_task_exec(struct hisi_hba *hisi_hba, int device_id,
}
list_add_tail(&slot->delivery, &dq->list);
spin_unlock_irqrestore(&dq->lock, flags_dq);
+ spin_lock_irqsave(&sas_dev->lock, flags);
+ list_add_tail(&slot->entry, &sas_dev->list);
+ spin_unlock_irqrestore(&sas_dev->lock, flags);
dlvry_queue = dq->id;
dlvry_queue_slot = wr_q_index;
+ slot->device_id = sas_dev->device_id;
slot->n_elem = n_elem;
slot->dlvry_queue = dlvry_queue;
slot->dlvry_queue_slot = dlvry_queue_slot;
@@ -1843,7 +2000,6 @@ hisi_sas_internal_abort_task_exec(struct hisi_hba *hisi_hba, int device_id,
WRITE_ONCE(slot->ready, 1);
/* send abort command to the chip */
spin_lock_irqsave(&dq->lock, flags);
- list_add_tail(&slot->entry, &sas_dev->list);
hisi_hba->hw->start_delivery(dq);
spin_unlock_irqrestore(&dq->lock, flags);
@@ -1858,18 +2014,19 @@ err_out:
}
/**
- * hisi_sas_internal_task_abort -- execute an internal
+ * _hisi_sas_internal_task_abort -- execute an internal
* abort command for single IO command or a device
* @hisi_hba: host controller struct
* @device: domain device
* @abort_flag: mode of operation, device or single IO
* @tag: tag of IO to be aborted (only relevant to single
* IO mode)
+ * @dq: delivery queue for this internal abort command
*/
static int
-hisi_sas_internal_task_abort(struct hisi_hba *hisi_hba,
- struct domain_device *device,
- int abort_flag, int tag)
+_hisi_sas_internal_task_abort(struct hisi_hba *hisi_hba,
+ struct domain_device *device, int abort_flag,
+ int tag, struct hisi_sas_dq *dq)
{
struct sas_task *task;
struct hisi_sas_device *sas_dev = device->lldd_dev;
@@ -1893,11 +2050,11 @@ hisi_sas_internal_task_abort(struct hisi_hba *hisi_hba,
task->task_proto = device->tproto;
task->task_done = hisi_sas_task_done;
task->slow_task->timer.function = hisi_sas_tmf_timedout;
- task->slow_task->timer.expires = jiffies + INTERNAL_ABORT_TIMEOUT*HZ;
+ task->slow_task->timer.expires = jiffies + INTERNAL_ABORT_TIMEOUT * HZ;
add_timer(&task->slow_task->timer);
res = hisi_sas_internal_abort_task_exec(hisi_hba, sas_dev->device_id,
- task, abort_flag, tag);
+ task, abort_flag, tag, dq);
if (res) {
del_timer(&task->slow_task->timer);
dev_err(dev, "internal task abort: executing internal task failed: %d\n",
@@ -1923,6 +2080,7 @@ hisi_sas_internal_task_abort(struct hisi_hba *hisi_hba,
slot->task = NULL;
}
dev_err(dev, "internal task abort: timeout and not done.\n");
+
res = -EIO;
goto exit;
} else
@@ -1953,6 +2111,46 @@ exit:
return res;
}
+static int
+hisi_sas_internal_task_abort(struct hisi_hba *hisi_hba,
+ struct domain_device *device,
+ int abort_flag, int tag)
+{
+ struct hisi_sas_slot *slot;
+ struct device *dev = hisi_hba->dev;
+ struct hisi_sas_dq *dq;
+ int i, rc;
+
+ switch (abort_flag) {
+ case HISI_SAS_INT_ABT_CMD:
+ slot = &hisi_hba->slot_info[tag];
+ dq = &hisi_hba->dq[slot->dlvry_queue];
+ return _hisi_sas_internal_task_abort(hisi_hba, device,
+ abort_flag, tag, dq);
+ case HISI_SAS_INT_ABT_DEV:
+ for (i = 0; i < hisi_hba->cq_nvecs; i++) {
+ struct hisi_sas_cq *cq = &hisi_hba->cq[i];
+ const struct cpumask *mask = cq->pci_irq_mask;
+
+ if (mask && !cpumask_intersects(cpu_online_mask, mask))
+ continue;
+ dq = &hisi_hba->dq[i];
+ rc = _hisi_sas_internal_task_abort(hisi_hba, device,
+ abort_flag, tag,
+ dq);
+ if (rc)
+ return rc;
+ }
+ break;
+ default:
+ dev_err(dev, "Unrecognised internal abort flag (%d)\n",
+ abort_flag);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static void hisi_sas_port_formed(struct asd_sas_phy *sas_phy)
{
hisi_sas_port_notify_formed(sas_phy);
@@ -1972,9 +2170,18 @@ static int hisi_sas_write_gpio(struct sas_ha_struct *sha, u8 reg_type,
static void hisi_sas_phy_disconnected(struct hisi_sas_phy *phy)
{
+ struct asd_sas_phy *sas_phy = &phy->sas_phy;
+ struct sas_phy *sphy = sas_phy->phy;
+ struct sas_phy_data *d = sphy->hostdata;
+
phy->phy_attached = 0;
phy->phy_type = 0;
phy->port = NULL;
+
+ if (d->enable)
+ sphy->negotiated_linkrate = SAS_LINK_RATE_UNKNOWN;
+ else
+ sphy->negotiated_linkrate = SAS_PHY_DISABLED;
}
void hisi_sas_phy_down(struct hisi_hba *hisi_hba, int phy_no, int rdy)
@@ -2019,7 +2226,7 @@ void hisi_sas_kill_tasklets(struct hisi_hba *hisi_hba)
{
int i;
- for (i = 0; i < hisi_hba->queue_count; i++) {
+ for (i = 0; i < hisi_hba->cq_nvecs; i++) {
struct hisi_sas_cq *cq = &hisi_hba->cq[i];
tasklet_kill(&cq->tasklet);
@@ -2048,14 +2255,18 @@ static struct sas_domain_function_template hisi_sas_transport_ops = {
void hisi_sas_init_mem(struct hisi_hba *hisi_hba)
{
- int i, s, max_command_entries = hisi_hba->hw->max_command_entries;
+ int i, s, j, max_command_entries = hisi_hba->hw->max_command_entries;
+ struct hisi_sas_breakpoint *sata_breakpoint = hisi_hba->sata_breakpoint;
for (i = 0; i < hisi_hba->queue_count; i++) {
struct hisi_sas_cq *cq = &hisi_hba->cq[i];
struct hisi_sas_dq *dq = &hisi_hba->dq[i];
+ struct hisi_sas_cmd_hdr *cmd_hdr = hisi_hba->cmd_hdr[i];
+
+ s = sizeof(struct hisi_sas_cmd_hdr);
+ for (j = 0; j < HISI_SAS_QUEUE_SLOTS; j++)
+ memset(&cmd_hdr[j], 0, s);
- s = sizeof(struct hisi_sas_cmd_hdr) * HISI_SAS_QUEUE_SLOTS;
- memset(hisi_hba->cmd_hdr[i], 0, s);
dq->wr_point = 0;
s = hisi_hba->hw->complete_hdr_size * HISI_SAS_QUEUE_SLOTS;
@@ -2072,12 +2283,13 @@ void hisi_sas_init_mem(struct hisi_hba *hisi_hba)
s = max_command_entries * sizeof(struct hisi_sas_breakpoint);
memset(hisi_hba->breakpoint, 0, s);
- s = HISI_SAS_MAX_ITCT_ENTRIES * sizeof(struct hisi_sas_sata_breakpoint);
- memset(hisi_hba->sata_breakpoint, 0, s);
+ s = sizeof(struct hisi_sas_sata_breakpoint);
+ for (j = 0; j < HISI_SAS_MAX_ITCT_ENTRIES; j++)
+ memset(&sata_breakpoint[j], 0, s);
}
EXPORT_SYMBOL_GPL(hisi_sas_init_mem);
-int hisi_sas_alloc(struct hisi_hba *hisi_hba, struct Scsi_Host *shost)
+int hisi_sas_alloc(struct hisi_hba *hisi_hba)
{
struct device *dev = hisi_hba->dev;
int i, j, s, max_command_entries = hisi_hba->hw->max_command_entries;
@@ -2095,7 +2307,7 @@ int hisi_sas_alloc(struct hisi_hba *hisi_hba, struct Scsi_Host *shost)
for (i = 0; i < HISI_SAS_MAX_DEVICES; i++) {
hisi_hba->devices[i].dev_type = SAS_PHY_UNUSED;
hisi_hba->devices[i].device_id = i;
- hisi_hba->devices[i].dev_status = HISI_SAS_DEV_NORMAL;
+ hisi_hba->devices[i].dev_status = HISI_SAS_DEV_INIT;
}
for (i = 0; i < hisi_hba->queue_count; i++) {
@@ -2131,10 +2343,9 @@ int hisi_sas_alloc(struct hisi_hba *hisi_hba, struct Scsi_Host *shost)
s = HISI_SAS_MAX_ITCT_ENTRIES * sizeof(struct hisi_sas_itct);
hisi_hba->itct = dmam_alloc_coherent(dev, s, &hisi_hba->itct_dma,
- GFP_KERNEL);
+ GFP_KERNEL | __GFP_ZERO);
if (!hisi_hba->itct)
goto err_out;
- memset(hisi_hba->itct, 0, s);
hisi_hba->slot_info = devm_kcalloc(dev, max_command_entries,
sizeof(struct hisi_sas_slot),
@@ -2144,19 +2355,24 @@ int hisi_sas_alloc(struct hisi_hba *hisi_hba, struct Scsi_Host *shost)
/* roundup to avoid overly large block size */
max_command_entries_ru = roundup(max_command_entries, 64);
- sz_slot_buf_ru = roundup(sizeof(struct hisi_sas_slot_buf_table), 64);
+ if (hisi_hba->prot_mask & HISI_SAS_DIX_PROT_MASK)
+ sz_slot_buf_ru = sizeof(struct hisi_sas_slot_dif_buf_table);
+ else
+ sz_slot_buf_ru = sizeof(struct hisi_sas_slot_buf_table);
+ sz_slot_buf_ru = roundup(sz_slot_buf_ru, 64);
s = lcm(max_command_entries_ru, sz_slot_buf_ru);
blk_cnt = (max_command_entries_ru * sz_slot_buf_ru) / s;
slots_per_blk = s / sz_slot_buf_ru;
+
for (i = 0; i < blk_cnt; i++) {
- struct hisi_sas_slot_buf_table *buf;
- dma_addr_t buf_dma;
int slot_index = i * slots_per_blk;
+ dma_addr_t buf_dma;
+ void *buf;
- buf = dmam_alloc_coherent(dev, s, &buf_dma, GFP_KERNEL);
+ buf = dmam_alloc_coherent(dev, s, &buf_dma,
+ GFP_KERNEL | __GFP_ZERO);
if (!buf)
goto err_out;
- memset(buf, 0, s);
for (j = 0; j < slots_per_blk; j++, slot_index++) {
struct hisi_sas_slot *slot;
@@ -2166,8 +2382,8 @@ int hisi_sas_alloc(struct hisi_hba *hisi_hba, struct Scsi_Host *shost)
slot->buf_dma = buf_dma;
slot->idx = slot_index;
- buf++;
- buf_dma += sizeof(*buf);
+ buf += sz_slot_buf_ru;
+ buf_dma += sz_slot_buf_ru;
}
}
@@ -2365,7 +2581,7 @@ static struct Scsi_Host *hisi_sas_shost_alloc(struct platform_device *pdev,
goto err_out;
}
- if (hisi_sas_alloc(hisi_hba, shost)) {
+ if (hisi_sas_alloc(hisi_hba)) {
hisi_sas_free(hisi_hba);
goto err_out;
}
@@ -2461,6 +2677,555 @@ err_out_ha:
}
EXPORT_SYMBOL_GPL(hisi_sas_probe);
+struct dentry *hisi_sas_debugfs_dir;
+
+static void hisi_sas_debugfs_snapshot_cq_reg(struct hisi_hba *hisi_hba)
+{
+ int queue_entry_size = hisi_hba->hw->complete_hdr_size;
+ int i;
+
+ for (i = 0; i < hisi_hba->queue_count; i++)
+ memcpy(hisi_hba->debugfs_complete_hdr[i],
+ hisi_hba->complete_hdr[i],
+ HISI_SAS_QUEUE_SLOTS * queue_entry_size);
+}
+
+static void hisi_sas_debugfs_snapshot_dq_reg(struct hisi_hba *hisi_hba)
+{
+ int queue_entry_size = sizeof(struct hisi_sas_cmd_hdr);
+ int i;
+
+ for (i = 0; i < hisi_hba->queue_count; i++) {
+ struct hisi_sas_cmd_hdr *debugfs_cmd_hdr, *cmd_hdr;
+ int j;
+
+ debugfs_cmd_hdr = hisi_hba->debugfs_cmd_hdr[i];
+ cmd_hdr = hisi_hba->cmd_hdr[i];
+
+ for (j = 0; j < HISI_SAS_QUEUE_SLOTS; j++)
+ memcpy(&debugfs_cmd_hdr[j], &cmd_hdr[j],
+ queue_entry_size);
+ }
+}
+
+static void hisi_sas_debugfs_snapshot_port_reg(struct hisi_hba *hisi_hba)
+{
+ const struct hisi_sas_debugfs_reg *port =
+ hisi_hba->hw->debugfs_reg_port;
+ int i, phy_cnt;
+ u32 offset;
+ u32 *databuf;
+
+ for (phy_cnt = 0; phy_cnt < hisi_hba->n_phy; phy_cnt++) {
+ databuf = (u32 *)hisi_hba->debugfs_port_reg[phy_cnt];
+ for (i = 0; i < port->count; i++, databuf++) {
+ offset = port->base_off + 4 * i;
+ *databuf = port->read_port_reg(hisi_hba, phy_cnt,
+ offset);
+ }
+ }
+}
+
+static void hisi_sas_debugfs_snapshot_global_reg(struct hisi_hba *hisi_hba)
+{
+ u32 *databuf = (u32 *)hisi_hba->debugfs_global_reg;
+ const struct hisi_sas_debugfs_reg *global =
+ hisi_hba->hw->debugfs_reg_global;
+ int i;
+
+ for (i = 0; i < global->count; i++, databuf++)
+ *databuf = global->read_global_reg(hisi_hba, 4 * i);
+}
+
+static void hisi_sas_debugfs_snapshot_itct_reg(struct hisi_hba *hisi_hba)
+{
+ void *databuf = hisi_hba->debugfs_itct;
+ struct hisi_sas_itct *itct;
+ int i;
+
+ itct = hisi_hba->itct;
+
+ for (i = 0; i < HISI_SAS_MAX_ITCT_ENTRIES; i++, itct++) {
+ memcpy(databuf, itct, sizeof(struct hisi_sas_itct));
+ databuf += sizeof(struct hisi_sas_itct);
+ }
+}
+
+static void hisi_sas_debugfs_snapshot_iost_reg(struct hisi_hba *hisi_hba)
+{
+ int max_command_entries = hisi_hba->hw->max_command_entries;
+ void *databuf = hisi_hba->debugfs_iost;
+ struct hisi_sas_iost *iost;
+ int i;
+
+ iost = hisi_hba->iost;
+
+ for (i = 0; i < max_command_entries; i++, iost++) {
+ memcpy(databuf, iost, sizeof(struct hisi_sas_iost));
+ databuf += sizeof(struct hisi_sas_iost);
+ }
+}
+
+static const char *
+hisi_sas_debugfs_to_reg_name(int off, int base_off,
+ const struct hisi_sas_debugfs_reg_lu *lu)
+{
+ for (; lu->name; lu++) {
+ if (off == lu->off - base_off)
+ return lu->name;
+ }
+
+ return NULL;
+}
+
+static void hisi_sas_debugfs_print_reg(u32 *regs_val, const void *ptr,
+ struct seq_file *s)
+{
+ const struct hisi_sas_debugfs_reg *reg = ptr;
+ int i;
+
+ for (i = 0; i < reg->count; i++) {
+ int off = i * 4;
+ const char *name;
+
+ name = hisi_sas_debugfs_to_reg_name(off, reg->base_off,
+ reg->lu);
+
+ if (name)
+ seq_printf(s, "0x%08x 0x%08x %s\n", off,
+ regs_val[i], name);
+ else
+ seq_printf(s, "0x%08x 0x%08x\n", off,
+ regs_val[i]);
+ }
+}
+
+static int hisi_sas_debugfs_global_show(struct seq_file *s, void *p)
+{
+ struct hisi_hba *hisi_hba = s->private;
+ const struct hisi_sas_hw *hw = hisi_hba->hw;
+ const struct hisi_sas_debugfs_reg *reg_global = hw->debugfs_reg_global;
+
+ hisi_sas_debugfs_print_reg(hisi_hba->debugfs_global_reg,
+ reg_global, s);
+
+ return 0;
+}
+
+static int hisi_sas_debugfs_global_open(struct inode *inode, struct file *filp)
+{
+ return single_open(filp, hisi_sas_debugfs_global_show,
+ inode->i_private);
+}
+
+static const struct file_operations hisi_sas_debugfs_global_fops = {
+ .open = hisi_sas_debugfs_global_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int hisi_sas_debugfs_port_show(struct seq_file *s, void *p)
+{
+ struct hisi_sas_phy *phy = s->private;
+ struct hisi_hba *hisi_hba = phy->hisi_hba;
+ const struct hisi_sas_hw *hw = hisi_hba->hw;
+ const struct hisi_sas_debugfs_reg *reg_port = hw->debugfs_reg_port;
+ u32 *databuf = hisi_hba->debugfs_port_reg[phy->sas_phy.id];
+
+ hisi_sas_debugfs_print_reg(databuf, reg_port, s);
+
+ return 0;
+}
+
+static int hisi_sas_debugfs_port_open(struct inode *inode, struct file *filp)
+{
+ return single_open(filp, hisi_sas_debugfs_port_show, inode->i_private);
+}
+
+static const struct file_operations hisi_sas_debugfs_port_fops = {
+ .open = hisi_sas_debugfs_port_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int hisi_sas_show_row_64(struct seq_file *s, int index,
+ int sz, __le64 *ptr)
+{
+ int i;
+
+ /* completion header size not fixed per HW version */
+ seq_printf(s, "index %04d:\n\t", index);
+ for (i = 1; i <= sz / 8; i++, ptr++) {
+ seq_printf(s, " 0x%016llx", le64_to_cpu(*ptr));
+ if (!(i % 2))
+ seq_puts(s, "\n\t");
+ }
+
+ seq_puts(s, "\n");
+
+ return 0;
+}
+
+static int hisi_sas_show_row_32(struct seq_file *s, int index,
+ int sz, __le32 *ptr)
+{
+ int i;
+
+ /* completion header size not fixed per HW version */
+ seq_printf(s, "index %04d:\n\t", index);
+ for (i = 1; i <= sz / 4; i++, ptr++) {
+ seq_printf(s, " 0x%08x", le32_to_cpu(*ptr));
+ if (!(i % 4))
+ seq_puts(s, "\n\t");
+ }
+ seq_puts(s, "\n");
+
+ return 0;
+}
+
+static int hisi_sas_cq_show_slot(struct seq_file *s, int slot, void *cq_ptr)
+{
+ struct hisi_sas_cq *cq = cq_ptr;
+ struct hisi_hba *hisi_hba = cq->hisi_hba;
+ void *complete_queue = hisi_hba->debugfs_complete_hdr[cq->id];
+ __le32 *complete_hdr = complete_queue +
+ (hisi_hba->hw->complete_hdr_size * slot);
+
+ return hisi_sas_show_row_32(s, slot,
+ hisi_hba->hw->complete_hdr_size,
+ complete_hdr);
+}
+
+static int hisi_sas_debugfs_cq_show(struct seq_file *s, void *p)
+{
+ struct hisi_sas_cq *cq = s->private;
+ int slot, ret;
+
+ for (slot = 0; slot < HISI_SAS_QUEUE_SLOTS; slot++) {
+ ret = hisi_sas_cq_show_slot(s, slot, cq);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+static int hisi_sas_debugfs_cq_open(struct inode *inode, struct file *filp)
+{
+ return single_open(filp, hisi_sas_debugfs_cq_show, inode->i_private);
+}
+
+static const struct file_operations hisi_sas_debugfs_cq_fops = {
+ .open = hisi_sas_debugfs_cq_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int hisi_sas_dq_show_slot(struct seq_file *s, int slot, void *dq_ptr)
+{
+ struct hisi_sas_dq *dq = dq_ptr;
+ struct hisi_hba *hisi_hba = dq->hisi_hba;
+ void *cmd_queue = hisi_hba->debugfs_cmd_hdr[dq->id];
+ __le32 *cmd_hdr = cmd_queue +
+ sizeof(struct hisi_sas_cmd_hdr) * slot;
+
+ return hisi_sas_show_row_32(s, slot, sizeof(struct hisi_sas_cmd_hdr),
+ cmd_hdr);
+}
+
+static int hisi_sas_debugfs_dq_show(struct seq_file *s, void *p)
+{
+ int slot, ret;
+
+ for (slot = 0; slot < HISI_SAS_QUEUE_SLOTS; slot++) {
+ ret = hisi_sas_dq_show_slot(s, slot, s->private);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+static int hisi_sas_debugfs_dq_open(struct inode *inode, struct file *filp)
+{
+ return single_open(filp, hisi_sas_debugfs_dq_show, inode->i_private);
+}
+
+static const struct file_operations hisi_sas_debugfs_dq_fops = {
+ .open = hisi_sas_debugfs_dq_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int hisi_sas_debugfs_iost_show(struct seq_file *s, void *p)
+{
+ struct hisi_hba *hisi_hba = s->private;
+ struct hisi_sas_iost *debugfs_iost = hisi_hba->debugfs_iost;
+ int i, ret, max_command_entries = hisi_hba->hw->max_command_entries;
+ __le64 *iost = &debugfs_iost->qw0;
+
+ for (i = 0; i < max_command_entries; i++, debugfs_iost++) {
+ ret = hisi_sas_show_row_64(s, i, sizeof(*debugfs_iost),
+ iost);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int hisi_sas_debugfs_iost_open(struct inode *inode, struct file *filp)
+{
+ return single_open(filp, hisi_sas_debugfs_iost_show, inode->i_private);
+}
+
+static const struct file_operations hisi_sas_debugfs_iost_fops = {
+ .open = hisi_sas_debugfs_iost_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int hisi_sas_debugfs_itct_show(struct seq_file *s, void *p)
+{
+ int i, ret;
+ struct hisi_hba *hisi_hba = s->private;
+ struct hisi_sas_itct *debugfs_itct = hisi_hba->debugfs_itct;
+ __le64 *itct = &debugfs_itct->qw0;
+
+ for (i = 0; i < HISI_SAS_MAX_ITCT_ENTRIES; i++, debugfs_itct++) {
+ ret = hisi_sas_show_row_64(s, i, sizeof(*debugfs_itct),
+ itct);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int hisi_sas_debugfs_itct_open(struct inode *inode, struct file *filp)
+{
+ return single_open(filp, hisi_sas_debugfs_itct_show, inode->i_private);
+}
+
+static const struct file_operations hisi_sas_debugfs_itct_fops = {
+ .open = hisi_sas_debugfs_itct_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static void hisi_sas_debugfs_create_files(struct hisi_hba *hisi_hba)
+{
+ struct dentry *dump_dentry;
+ struct dentry *dentry;
+ char name[256];
+ int p;
+ int c;
+ int d;
+
+ /* Create dump dir inside device dir */
+ dump_dentry = debugfs_create_dir("dump", hisi_hba->debugfs_dir);
+ hisi_hba->debugfs_dump_dentry = dump_dentry;
+
+ debugfs_create_file("global", 0400, dump_dentry, hisi_hba,
+ &hisi_sas_debugfs_global_fops);
+
+ /* Create port dir and files */
+ dentry = debugfs_create_dir("port", dump_dentry);
+ for (p = 0; p < hisi_hba->n_phy; p++) {
+ snprintf(name, 256, "%d", p);
+
+ debugfs_create_file(name, 0400, dentry, &hisi_hba->phy[p],
+ &hisi_sas_debugfs_port_fops);
+ }
+
+ /* Create CQ dir and files */
+ dentry = debugfs_create_dir("cq", dump_dentry);
+ for (c = 0; c < hisi_hba->queue_count; c++) {
+ snprintf(name, 256, "%d", c);
+
+ debugfs_create_file(name, 0400, dentry, &hisi_hba->cq[c],
+ &hisi_sas_debugfs_cq_fops);
+ }
+
+ /* Create DQ dir and files */
+ dentry = debugfs_create_dir("dq", dump_dentry);
+ for (d = 0; d < hisi_hba->queue_count; d++) {
+ snprintf(name, 256, "%d", d);
+
+ debugfs_create_file(name, 0400, dentry, &hisi_hba->dq[d],
+ &hisi_sas_debugfs_dq_fops);
+ }
+
+ debugfs_create_file("iost", 0400, dump_dentry, hisi_hba,
+ &hisi_sas_debugfs_iost_fops);
+
+ debugfs_create_file("itct", 0400, dump_dentry, hisi_hba,
+ &hisi_sas_debugfs_itct_fops);
+
+ return;
+}
+
+static void hisi_sas_debugfs_snapshot_regs(struct hisi_hba *hisi_hba)
+{
+ hisi_hba->hw->snapshot_prepare(hisi_hba);
+
+ hisi_sas_debugfs_snapshot_global_reg(hisi_hba);
+ hisi_sas_debugfs_snapshot_port_reg(hisi_hba);
+ hisi_sas_debugfs_snapshot_cq_reg(hisi_hba);
+ hisi_sas_debugfs_snapshot_dq_reg(hisi_hba);
+ hisi_sas_debugfs_snapshot_itct_reg(hisi_hba);
+ hisi_sas_debugfs_snapshot_iost_reg(hisi_hba);
+
+ hisi_sas_debugfs_create_files(hisi_hba);
+
+ hisi_hba->hw->snapshot_restore(hisi_hba);
+}
+
+static ssize_t hisi_sas_debugfs_trigger_dump_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct hisi_hba *hisi_hba = file->f_inode->i_private;
+ char buf[8];
+
+ /* A bit racy, but don't care too much since it's only debugfs */
+ if (hisi_hba->debugfs_snapshot)
+ return -EFAULT;
+
+ if (count > 8)
+ return -EFAULT;
+
+ if (copy_from_user(buf, user_buf, count))
+ return -EFAULT;
+
+ if (buf[0] != '1')
+ return -EFAULT;
+
+ queue_work(hisi_hba->wq, &hisi_hba->debugfs_work);
+
+ return count;
+}
+
+static const struct file_operations hisi_sas_debugfs_trigger_dump_fops = {
+ .write = &hisi_sas_debugfs_trigger_dump_write,
+ .owner = THIS_MODULE,
+};
+
+void hisi_sas_debugfs_work_handler(struct work_struct *work)
+{
+ struct hisi_hba *hisi_hba =
+ container_of(work, struct hisi_hba, debugfs_work);
+
+ if (hisi_hba->debugfs_snapshot)
+ return;
+ hisi_hba->debugfs_snapshot = true;
+
+ hisi_sas_debugfs_snapshot_regs(hisi_hba);
+}
+EXPORT_SYMBOL_GPL(hisi_sas_debugfs_work_handler);
+
+void hisi_sas_debugfs_init(struct hisi_hba *hisi_hba)
+{
+ int max_command_entries = hisi_hba->hw->max_command_entries;
+ struct device *dev = hisi_hba->dev;
+ int p, i, c, d;
+ size_t sz;
+
+ hisi_hba->debugfs_dir = debugfs_create_dir(dev_name(dev),
+ hisi_sas_debugfs_dir);
+ debugfs_create_file("trigger_dump", 0600,
+ hisi_hba->debugfs_dir,
+ hisi_hba,
+ &hisi_sas_debugfs_trigger_dump_fops);
+
+ /* Alloc buffer for global */
+ sz = hisi_hba->hw->debugfs_reg_global->count * 4;
+ hisi_hba->debugfs_global_reg =
+ devm_kmalloc(dev, sz, GFP_KERNEL);
+
+ if (!hisi_hba->debugfs_global_reg)
+ goto fail_global;
+
+ /* Alloc buffer for port */
+ sz = hisi_hba->hw->debugfs_reg_port->count * 4;
+ for (p = 0; p < hisi_hba->n_phy; p++) {
+ hisi_hba->debugfs_port_reg[p] =
+ devm_kmalloc(dev, sz, GFP_KERNEL);
+
+ if (!hisi_hba->debugfs_port_reg[p])
+ goto fail_port;
+ }
+
+ /* Alloc buffer for cq */
+ sz = hisi_hba->hw->complete_hdr_size * HISI_SAS_QUEUE_SLOTS;
+ for (c = 0; c < hisi_hba->queue_count; c++) {
+ hisi_hba->debugfs_complete_hdr[c] =
+ devm_kmalloc(dev, sz, GFP_KERNEL);
+
+ if (!hisi_hba->debugfs_complete_hdr[c])
+ goto fail_cq;
+ }
+
+ /* Alloc buffer for dq */
+ sz = sizeof(struct hisi_sas_cmd_hdr) * HISI_SAS_QUEUE_SLOTS;
+ for (d = 0; d < hisi_hba->queue_count; d++) {
+ hisi_hba->debugfs_cmd_hdr[d] =
+ devm_kmalloc(dev, sz, GFP_KERNEL);
+
+ if (!hisi_hba->debugfs_cmd_hdr[d])
+ goto fail_iost_dq;
+ }
+
+ /* Alloc buffer for iost */
+ sz = max_command_entries * sizeof(struct hisi_sas_iost);
+
+ hisi_hba->debugfs_iost = devm_kmalloc(dev, sz, GFP_KERNEL);
+ if (!hisi_hba->debugfs_iost)
+ goto fail_iost_dq;
+
+ /* Alloc buffer for itct */
+ /* New memory allocation must be locate before itct */
+ sz = HISI_SAS_MAX_ITCT_ENTRIES * sizeof(struct hisi_sas_itct);
+
+ hisi_hba->debugfs_itct = devm_kmalloc(dev, sz, GFP_KERNEL);
+ if (!hisi_hba->debugfs_itct)
+ goto fail_itct;
+
+ return;
+fail_itct:
+ devm_kfree(dev, hisi_hba->debugfs_iost);
+fail_iost_dq:
+ for (i = 0; i < d; i++)
+ devm_kfree(dev, hisi_hba->debugfs_cmd_hdr[i]);
+fail_cq:
+ for (i = 0; i < c; i++)
+ devm_kfree(dev, hisi_hba->debugfs_complete_hdr[i]);
+fail_port:
+ for (i = 0; i < p; i++)
+ devm_kfree(dev, hisi_hba->debugfs_port_reg[i]);
+ devm_kfree(dev, hisi_hba->debugfs_global_reg);
+fail_global:
+ debugfs_remove_recursive(hisi_hba->debugfs_dir);
+ dev_dbg(dev, "failed to init debugfs!\n");
+}
+EXPORT_SYMBOL_GPL(hisi_sas_debugfs_init);
+
+void hisi_sas_debugfs_exit(struct hisi_hba *hisi_hba)
+{
+ debugfs_remove_recursive(hisi_hba->debugfs_dir);
+}
+EXPORT_SYMBOL_GPL(hisi_sas_debugfs_exit);
+
int hisi_sas_remove(struct platform_device *pdev)
{
struct sas_ha_struct *sha = platform_get_drvdata(pdev);
@@ -2479,18 +3244,28 @@ int hisi_sas_remove(struct platform_device *pdev)
}
EXPORT_SYMBOL_GPL(hisi_sas_remove);
+bool hisi_sas_debugfs_enable;
+EXPORT_SYMBOL_GPL(hisi_sas_debugfs_enable);
+module_param_named(debugfs_enable, hisi_sas_debugfs_enable, bool, 0444);
+MODULE_PARM_DESC(hisi_sas_debugfs_enable, "Enable driver debugfs (default disabled)");
+
static __init int hisi_sas_init(void)
{
hisi_sas_stt = sas_domain_attach_transport(&hisi_sas_transport_ops);
if (!hisi_sas_stt)
return -ENOMEM;
+ if (hisi_sas_debugfs_enable)
+ hisi_sas_debugfs_dir = debugfs_create_dir("hisi_sas", NULL);
+
return 0;
}
static __exit void hisi_sas_exit(void)
{
sas_release_transport(hisi_sas_stt);
+
+ debugfs_remove(hisi_sas_debugfs_dir);
}
module_init(hisi_sas_init);
diff --git a/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c
index 28ab52a021cf..293807443480 100644
--- a/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c
+++ b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c
@@ -835,7 +835,7 @@ static void phys_init_v1_hw(struct hisi_hba *hisi_hba)
mod_timer(timer, jiffies + HZ);
}
-static void sl_notify_v1_hw(struct hisi_hba *hisi_hba, int phy_no)
+static void sl_notify_ssp_v1_hw(struct hisi_hba *hisi_hba, int phy_no)
{
u32 sl_control;
@@ -1749,6 +1749,8 @@ static int interrupt_init_v1_hw(struct hisi_hba *hisi_hba)
}
}
+ hisi_hba->cq_nvecs = hisi_hba->queue_count;
+
return 0;
}
@@ -1826,7 +1828,7 @@ static struct scsi_host_template sht_v1_hw = {
static const struct hisi_sas_hw hisi_sas_v1_hw = {
.hw_init = hisi_sas_v1_init,
.setup_itct = setup_itct_v1_hw,
- .sl_notify = sl_notify_v1_hw,
+ .sl_notify_ssp = sl_notify_ssp_v1_hw,
.clear_itct = clear_itct_v1_hw,
.prep_smp = prep_smp_v1_hw,
.prep_ssp = prep_ssp_v1_hw,
diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c
index c8ebff3ba559..89160ab3efb0 100644
--- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c
+++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c
@@ -868,12 +868,13 @@ hisi_sas_device *alloc_dev_quirk_v2_hw(struct domain_device *device)
hisi_hba->devices[i].device_id = i;
sas_dev = &hisi_hba->devices[i];
- sas_dev->dev_status = HISI_SAS_DEV_NORMAL;
+ sas_dev->dev_status = HISI_SAS_DEV_INIT;
sas_dev->dev_type = device->dev_type;
sas_dev->hisi_hba = hisi_hba;
sas_dev->sas_device = device;
sas_dev->sata_idx = sata_idx;
sas_dev->dq = dq;
+ spin_lock_init(&sas_dev->lock);
INIT_LIST_HEAD(&hisi_hba->devices[i].list);
break;
}
@@ -1589,7 +1590,7 @@ static void phys_init_v2_hw(struct hisi_hba *hisi_hba)
}
}
-static void sl_notify_v2_hw(struct hisi_hba *hisi_hba, int phy_no)
+static void sl_notify_ssp_v2_hw(struct hisi_hba *hisi_hba, int phy_no)
{
u32 sl_control;
@@ -2677,6 +2678,8 @@ static int phy_up_v2_hw(int phy_no, struct hisi_hba *hisi_hba)
if (is_sata_phy_v2_hw(hisi_hba, phy_no))
goto end;
+ del_timer(&phy->timer);
+
if (phy_no == 8) {
u32 port_state = hisi_sas_read32(hisi_hba, PORT_STATE);
@@ -2756,6 +2759,7 @@ static int phy_down_v2_hw(int phy_no, struct hisi_hba *hisi_hba)
struct hisi_sas_port *port = phy->port;
struct device *dev = hisi_hba->dev;
+ del_timer(&phy->timer);
hisi_sas_phy_write32(hisi_hba, phy_no, PHYCTRL_NOT_RDY_MSK, 1);
phy_state = hisi_sas_read32(hisi_hba, PHY_STATE);
@@ -2944,6 +2948,9 @@ static irqreturn_t int_chnl_int_v2_hw(int irq_no, void *p)
if (irq_value0 & CHL_INT0_SL_RX_BCST_ACK_MSK)
phy_bcast_v2_hw(phy_no, hisi_hba);
+ if (irq_value0 & CHL_INT0_PHY_RDY_MSK)
+ hisi_sas_phy_oob_ready(hisi_hba, phy_no);
+
hisi_sas_phy_write32(hisi_hba, phy_no,
CHL_INT0, irq_value0
& (~CHL_INT0_HOTPLUG_TOUT_MSK)
@@ -3227,6 +3234,8 @@ static irqreturn_t sata_int_v2_hw(int irq_no, void *p)
unsigned long flags;
int phy_no, offset;
+ del_timer(&phy->timer);
+
phy_no = sas_phy->id;
initial_fis = &hisi_hba->initial_fis[phy_no];
fis = &initial_fis->fis;
@@ -3393,6 +3402,8 @@ static int interrupt_init_v2_hw(struct hisi_hba *hisi_hba)
tasklet_init(t, cq_tasklet_v2_hw, (unsigned long)cq);
}
+ hisi_hba->cq_nvecs = hisi_hba->queue_count;
+
return 0;
free_cq_int_irqs:
@@ -3542,8 +3553,8 @@ static int write_gpio_v2_hw(struct hisi_hba *hisi_hba, u8 reg_type,
return 0;
}
-static void wait_cmds_complete_timeout_v2_hw(struct hisi_hba *hisi_hba,
- int delay_ms, int timeout_ms)
+static int wait_cmds_complete_timeout_v2_hw(struct hisi_hba *hisi_hba,
+ int delay_ms, int timeout_ms)
{
struct device *dev = hisi_hba->dev;
int entries, entries_old = 0, time;
@@ -3557,7 +3568,12 @@ static void wait_cmds_complete_timeout_v2_hw(struct hisi_hba *hisi_hba,
msleep(delay_ms);
}
+ if (time >= timeout_ms)
+ return -ETIMEDOUT;
+
dev_dbg(dev, "wait commands complete %dms\n", time);
+
+ return 0;
}
static struct device_attribute *host_attrs_v2_hw[] = {
@@ -3590,7 +3606,7 @@ static const struct hisi_sas_hw hisi_sas_v2_hw = {
.setup_itct = setup_itct_v2_hw,
.slot_index_alloc = slot_index_alloc_quirk_v2_hw,
.alloc_dev = alloc_dev_quirk_v2_hw,
- .sl_notify = sl_notify_v2_hw,
+ .sl_notify_ssp = sl_notify_ssp_v2_hw,
.get_wideport_bitmap = get_wideport_bitmap_v2_hw,
.clear_itct = clear_itct_v2_hw,
.free_device = free_device_v2_hw,
diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
index e0570fd8466e..086695a4099f 100644
--- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
+++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
@@ -11,7 +11,7 @@
#include "hisi_sas.h"
#define DRV_NAME "hisi_sas_v3_hw"
-/* global registers need init*/
+/* global registers need init */
#define DLVRY_QUEUE_ENABLE 0x0
#define IOST_BASE_ADDR_LO 0x8
#define IOST_BASE_ADDR_HI 0xc
@@ -129,6 +129,7 @@
#define PHY_CTRL_RESET_MSK (0x1 << PHY_CTRL_RESET_OFF)
#define CMD_HDR_PIR_OFF 8
#define CMD_HDR_PIR_MSK (0x1 << CMD_HDR_PIR_OFF)
+#define SERDES_CFG (PORT_BASE + 0x1c)
#define SL_CFG (PORT_BASE + 0x84)
#define AIP_LIMIT (PORT_BASE + 0x90)
#define SL_CONTROL (PORT_BASE + 0x94)
@@ -181,11 +182,14 @@
#define CHL_INT1_DMAC_RX_AXI_RD_ERR_OFF 22
#define CHL_INT2 (PORT_BASE + 0x1bc)
#define CHL_INT2_SL_IDAF_TOUT_CONF_OFF 0
+#define CHL_INT2_RX_DISP_ERR_OFF 28
+#define CHL_INT2_RX_CODE_ERR_OFF 29
#define CHL_INT2_RX_INVLD_DW_OFF 30
#define CHL_INT2_STP_LINK_TIMEOUT_OFF 31
#define CHL_INT0_MSK (PORT_BASE + 0x1c0)
#define CHL_INT1_MSK (PORT_BASE + 0x1c4)
#define CHL_INT2_MSK (PORT_BASE + 0x1c8)
+#define SAS_EC_INT_COAL_TIME (PORT_BASE + 0x1cc)
#define CHL_INT_COAL_EN (PORT_BASE + 0x1d0)
#define SAS_RX_TRAIN_TIMER (PORT_BASE + 0x2a4)
#define PHY_CTRL_RDY_MSK (PORT_BASE + 0x2b0)
@@ -205,6 +209,7 @@
#define ERR_CNT_DWS_LOST (PORT_BASE + 0x380)
#define ERR_CNT_RESET_PROB (PORT_BASE + 0x384)
#define ERR_CNT_INVLD_DW (PORT_BASE + 0x390)
+#define ERR_CNT_CODE_ERR (PORT_BASE + 0x394)
#define ERR_CNT_DISP_ERR (PORT_BASE + 0x398)
#define DEFAULT_ITCT_HW 2048 /* reset value, not reprogrammed */
@@ -397,6 +402,11 @@ struct hisi_sas_err_record_v3 {
#define USR_DATA_BLOCK_SZ_OFF 20
#define USR_DATA_BLOCK_SZ_MSK (0x3 << USR_DATA_BLOCK_SZ_OFF)
#define T10_CHK_MSK_OFF 16
+#define T10_CHK_REF_TAG_MSK (0xf0 << T10_CHK_MSK_OFF)
+#define T10_CHK_APP_TAG_MSK (0xc << T10_CHK_MSK_OFF)
+
+#define BASE_VECTORS_V3_HW 16
+#define MIN_AFFINE_VECTORS_V3_HW (BASE_VECTORS_V3_HW + 1)
static bool hisi_sas_intr_conv;
MODULE_PARM_DESC(intr_conv, "interrupt converge enable (0-1)");
@@ -406,6 +416,11 @@ static int prot_mask;
module_param(prot_mask, int, 0);
MODULE_PARM_DESC(prot_mask, " host protection capabilities mask, def=0x0 ");
+static bool auto_affine_msi_experimental;
+module_param(auto_affine_msi_experimental, bool, 0444);
+MODULE_PARM_DESC(auto_affine_msi_experimental, "Enable auto-affinity of MSI IRQs as experimental:\n"
+ "default is off");
+
static u32 hisi_sas_read32(struct hisi_hba *hisi_hba, u32 off)
{
void __iomem *regs = hisi_hba->regs + off;
@@ -511,6 +526,7 @@ static void init_reg_v3_hw(struct hisi_hba *hisi_hba)
}
hisi_sas_phy_write32(hisi_hba, i, PROG_PHY_LINK_RATE,
prog_phy_link_rate);
+ hisi_sas_phy_write32(hisi_hba, i, SERDES_CFG, 0xffc00);
hisi_sas_phy_write32(hisi_hba, i, SAS_RX_TRAIN_TIMER, 0x13e80);
hisi_sas_phy_write32(hisi_hba, i, CHL_INT0, 0xffffffff);
hisi_sas_phy_write32(hisi_hba, i, CHL_INT1, 0xffffffff);
@@ -532,6 +548,8 @@ static void init_reg_v3_hw(struct hisi_hba *hisi_hba)
hisi_sas_phy_write32(hisi_hba, i, STP_LINK_TIMER, 0x7f7a120);
hisi_sas_phy_write32(hisi_hba, i, CON_CFG_DRIVER, 0x2a0a01);
hisi_sas_phy_write32(hisi_hba, i, SAS_SSP_CON_TIMER_CFG, 0x32);
+ hisi_sas_phy_write32(hisi_hba, i, SAS_EC_INT_COAL_TIME,
+ 0x30f4240);
/* used for 12G negotiate */
hisi_sas_phy_write32(hisi_hba, i, COARSETUNE_TIME, 0x1e);
hisi_sas_phy_write32(hisi_hba, i, AIP_LIMIT, 0x2ffff);
@@ -716,7 +734,7 @@ static void clear_itct_v3_hw(struct hisi_hba *hisi_hba,
hisi_sas_write32(hisi_hba, ENT_INT_SRC3,
ENT_INT_SRC3_ITC_INT_MSK);
- /* clear the itct table*/
+ /* clear the itct table */
reg_val = ITCT_CLR_EN_MSK | (dev_id & ITCT_DEV_MSK);
hisi_sas_write32(hisi_hba, ITCT_CLR, reg_val);
@@ -868,7 +886,7 @@ static void phys_init_v3_hw(struct hisi_hba *hisi_hba)
}
}
-static void sl_notify_v3_hw(struct hisi_hba *hisi_hba, int phy_no)
+static void sl_notify_ssp_v3_hw(struct hisi_hba *hisi_hba, int phy_no)
{
u32 sl_control;
@@ -967,19 +985,44 @@ static void prep_prd_sge_v3_hw(struct hisi_hba *hisi_hba,
hdr->prd_table_addr = cpu_to_le64(hisi_sas_sge_addr_dma(slot));
- hdr->sg_len = cpu_to_le32(n_elem << CMD_HDR_DATA_SGL_LEN_OFF);
+ hdr->sg_len |= cpu_to_le32(n_elem << CMD_HDR_DATA_SGL_LEN_OFF);
+}
+
+static void prep_prd_sge_dif_v3_hw(struct hisi_hba *hisi_hba,
+ struct hisi_sas_slot *slot,
+ struct hisi_sas_cmd_hdr *hdr,
+ struct scatterlist *scatter,
+ int n_elem)
+{
+ struct hisi_sas_sge_dif_page *sge_dif_page;
+ struct scatterlist *sg;
+ int i;
+
+ sge_dif_page = hisi_sas_sge_dif_addr_mem(slot);
+
+ for_each_sg(scatter, sg, n_elem, i) {
+ struct hisi_sas_sge *entry = &sge_dif_page->sge[i];
+
+ entry->addr = cpu_to_le64(sg_dma_address(sg));
+ entry->page_ctrl_0 = 0;
+ entry->page_ctrl_1 = 0;
+ entry->data_len = cpu_to_le32(sg_dma_len(sg));
+ entry->data_off = 0;
+ }
+
+ hdr->dif_prd_table_addr =
+ cpu_to_le64(hisi_sas_sge_dif_addr_dma(slot));
+
+ hdr->sg_len |= cpu_to_le32(n_elem << CMD_HDR_DIF_SGL_LEN_OFF);
}
static u32 get_prot_chk_msk_v3_hw(struct scsi_cmnd *scsi_cmnd)
{
unsigned char prot_flags = scsi_cmnd->prot_flags;
- if (prot_flags & SCSI_PROT_TRANSFER_PI) {
- if (prot_flags & SCSI_PROT_REF_CHECK)
- return 0xc << 16;
- return 0xfc << 16;
- }
- return 0;
+ if (prot_flags & SCSI_PROT_REF_CHECK)
+ return T10_CHK_APP_TAG_MSK;
+ return T10_CHK_REF_TAG_MSK | T10_CHK_APP_TAG_MSK;
}
static void fill_prot_v3_hw(struct scsi_cmnd *scsi_cmnd,
@@ -990,15 +1033,33 @@ static void fill_prot_v3_hw(struct scsi_cmnd *scsi_cmnd,
u32 lbrt_chk_val = t10_pi_ref_tag(scsi_cmnd->request);
switch (prot_op) {
+ case SCSI_PROT_READ_INSERT:
+ prot->dw0 |= T10_INSRT_EN_MSK;
+ prot->lbrtgv = lbrt_chk_val;
+ break;
case SCSI_PROT_READ_STRIP:
prot->dw0 |= (T10_RMV_EN_MSK | T10_CHK_EN_MSK);
prot->lbrtcv = lbrt_chk_val;
prot->dw4 |= get_prot_chk_msk_v3_hw(scsi_cmnd);
break;
+ case SCSI_PROT_READ_PASS:
+ prot->dw0 |= T10_CHK_EN_MSK;
+ prot->lbrtcv = lbrt_chk_val;
+ prot->dw4 |= get_prot_chk_msk_v3_hw(scsi_cmnd);
+ break;
case SCSI_PROT_WRITE_INSERT:
prot->dw0 |= T10_INSRT_EN_MSK;
prot->lbrtgv = lbrt_chk_val;
break;
+ case SCSI_PROT_WRITE_STRIP:
+ prot->dw0 |= (T10_RMV_EN_MSK | T10_CHK_EN_MSK);
+ prot->lbrtcv = lbrt_chk_val;
+ break;
+ case SCSI_PROT_WRITE_PASS:
+ prot->dw0 |= T10_CHK_EN_MSK;
+ prot->lbrtcv = lbrt_chk_val;
+ prot->dw4 |= get_prot_chk_msk_v3_hw(scsi_cmnd);
+ break;
default:
WARN(1, "prot_op(0x%x) is not valid\n", prot_op);
break;
@@ -1033,8 +1094,8 @@ static void prep_ssp_v3_hw(struct hisi_hba *hisi_hba,
struct sas_ssp_task *ssp_task = &task->ssp_task;
struct scsi_cmnd *scsi_cmnd = ssp_task->cmd;
struct hisi_sas_tmf_task *tmf = slot->tmf;
- unsigned char prot_op = scsi_get_prot_op(scsi_cmnd);
int has_data = 0, priority = !!tmf;
+ unsigned char prot_op;
u8 *buf_cmd;
u32 dw1 = 0, dw2 = 0, len = 0;
@@ -1049,6 +1110,7 @@ static void prep_ssp_v3_hw(struct hisi_hba *hisi_hba,
dw1 |= 2 << CMD_HDR_FRAME_TYPE_OFF;
dw1 |= DIR_NO_DATA << CMD_HDR_DIR_OFF;
} else {
+ prot_op = scsi_get_prot_op(scsi_cmnd);
dw1 |= 1 << CMD_HDR_FRAME_TYPE_OFF;
switch (scsi_cmnd->sc_data_direction) {
case DMA_TO_DEVICE:
@@ -1074,9 +1136,15 @@ static void prep_ssp_v3_hw(struct hisi_hba *hisi_hba,
hdr->dw2 = cpu_to_le32(dw2);
hdr->transfer_tags = cpu_to_le32(slot->idx);
- if (has_data)
+ if (has_data) {
prep_prd_sge_v3_hw(hisi_hba, slot, hdr, task->scatter,
- slot->n_elem);
+ slot->n_elem);
+
+ if (scsi_prot_sg_count(scsi_cmnd))
+ prep_prd_sge_dif_v3_hw(hisi_hba, slot, hdr,
+ scsi_prot_sglist(scsi_cmnd),
+ slot->n_elem_dif);
+ }
hdr->cmd_table_addr = cpu_to_le64(hisi_sas_cmd_hdr_addr_dma(slot));
hdr->sts_buffer_addr = cpu_to_le64(hisi_sas_status_buf_addr_dma(slot));
@@ -1117,18 +1185,19 @@ static void prep_ssp_v3_hw(struct hisi_hba *hisi_hba,
fill_prot_v3_hw(scsi_cmnd, &prot);
memcpy(buf_cmd_prot, &prot,
sizeof(struct hisi_sas_protect_iu_v3_hw));
-
/*
* For READ, we need length of info read to memory, while for
* WRITE we need length of data written to the disk.
*/
- if (prot_op == SCSI_PROT_WRITE_INSERT) {
+ if (prot_op == SCSI_PROT_WRITE_INSERT ||
+ prot_op == SCSI_PROT_READ_INSERT ||
+ prot_op == SCSI_PROT_WRITE_PASS ||
+ prot_op == SCSI_PROT_READ_PASS) {
unsigned int interval = scsi_prot_interval(scsi_cmnd);
unsigned int ilog2_interval = ilog2(interval);
len = (task->total_xfer_len >> ilog2_interval) * 8;
}
-
}
hdr->dw1 = cpu_to_le32(dw1);
@@ -1281,13 +1350,15 @@ static void prep_abort_v3_hw(struct hisi_hba *hisi_hba,
static irqreturn_t phy_up_v3_hw(int phy_no, struct hisi_hba *hisi_hba)
{
- int i, res;
+ int i;
+ irqreturn_t res;
u32 context, port_id, link_rate;
struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no];
struct asd_sas_phy *sas_phy = &phy->sas_phy;
struct device *dev = hisi_hba->dev;
unsigned long flags;
+ del_timer(&phy->timer);
hisi_sas_phy_write32(hisi_hba, phy_no, PHYCTRL_PHY_ENA_MSK, 1);
port_id = hisi_sas_read32(hisi_hba, PHY_PORT_NUM_MA);
@@ -1381,9 +1452,11 @@ end:
static irqreturn_t phy_down_v3_hw(int phy_no, struct hisi_hba *hisi_hba)
{
+ struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no];
u32 phy_state, sl_ctrl, txid_auto;
struct device *dev = hisi_hba->dev;
+ del_timer(&phy->timer);
hisi_sas_phy_write32(hisi_hba, phy_no, PHYCTRL_NOT_RDY_MSK, 1);
phy_state = hisi_sas_read32(hisi_hba, PHY_STATE);
@@ -1509,6 +1582,39 @@ static void handle_chl_int1_v3_hw(struct hisi_hba *hisi_hba, int phy_no)
hisi_sas_phy_write32(hisi_hba, phy_no, CHL_INT1, irq_value);
}
+static void phy_get_events_v3_hw(struct hisi_hba *hisi_hba, int phy_no)
+{
+ struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no];
+ struct asd_sas_phy *sas_phy = &phy->sas_phy;
+ struct sas_phy *sphy = sas_phy->phy;
+ unsigned long flags;
+ u32 reg_value;
+
+ spin_lock_irqsave(&phy->lock, flags);
+
+ /* loss dword sync */
+ reg_value = hisi_sas_phy_read32(hisi_hba, phy_no, ERR_CNT_DWS_LOST);
+ sphy->loss_of_dword_sync_count += reg_value;
+
+ /* phy reset problem */
+ reg_value = hisi_sas_phy_read32(hisi_hba, phy_no, ERR_CNT_RESET_PROB);
+ sphy->phy_reset_problem_count += reg_value;
+
+ /* invalid dword */
+ reg_value = hisi_sas_phy_read32(hisi_hba, phy_no, ERR_CNT_INVLD_DW);
+ sphy->invalid_dword_count += reg_value;
+
+ /* disparity err */
+ reg_value = hisi_sas_phy_read32(hisi_hba, phy_no, ERR_CNT_DISP_ERR);
+ sphy->running_disparity_error_count += reg_value;
+
+ /* code violation error */
+ reg_value = hisi_sas_phy_read32(hisi_hba, phy_no, ERR_CNT_CODE_ERR);
+ phy->code_violation_err_count += reg_value;
+
+ spin_unlock_irqrestore(&phy->lock, flags);
+}
+
static void handle_chl_int2_v3_hw(struct hisi_hba *hisi_hba, int phy_no)
{
u32 irq_msk = hisi_sas_phy_read32(hisi_hba, phy_no, CHL_INT2_MSK);
@@ -1516,6 +1622,9 @@ static void handle_chl_int2_v3_hw(struct hisi_hba *hisi_hba, int phy_no)
struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no];
struct pci_dev *pci_dev = hisi_hba->pci_dev;
struct device *dev = hisi_hba->dev;
+ static const u32 msk = BIT(CHL_INT2_RX_DISP_ERR_OFF) |
+ BIT(CHL_INT2_RX_CODE_ERR_OFF) |
+ BIT(CHL_INT2_RX_INVLD_DW_OFF);
irq_value &= ~irq_msk;
if (!irq_value)
@@ -1536,6 +1645,25 @@ static void handle_chl_int2_v3_hw(struct hisi_hba *hisi_hba, int phy_no)
hisi_sas_notify_phy_event(phy, HISI_PHYE_LINK_RESET);
}
+ if (pci_dev->revision > 0x20 && (irq_value & msk)) {
+ struct asd_sas_phy *sas_phy = &phy->sas_phy;
+ struct sas_phy *sphy = sas_phy->phy;
+
+ phy_get_events_v3_hw(hisi_hba, phy_no);
+
+ if (irq_value & BIT(CHL_INT2_RX_INVLD_DW_OFF))
+ dev_info(dev, "phy%d invalid dword cnt: %u\n", phy_no,
+ sphy->invalid_dword_count);
+
+ if (irq_value & BIT(CHL_INT2_RX_CODE_ERR_OFF))
+ dev_info(dev, "phy%d code violation cnt: %u\n", phy_no,
+ phy->code_violation_err_count);
+
+ if (irq_value & BIT(CHL_INT2_RX_DISP_ERR_OFF))
+ dev_info(dev, "phy%d disparity error cnt: %u\n", phy_no,
+ sphy->running_disparity_error_count);
+ }
+
if ((irq_value & BIT(CHL_INT2_RX_INVLD_DW_OFF)) &&
(pci_dev->revision == 0x20)) {
u32 reg_value;
@@ -1552,6 +1680,19 @@ static void handle_chl_int2_v3_hw(struct hisi_hba *hisi_hba, int phy_no)
hisi_sas_phy_write32(hisi_hba, phy_no, CHL_INT2, irq_value);
}
+static void handle_chl_int0_v3_hw(struct hisi_hba *hisi_hba, int phy_no)
+{
+ u32 irq_value0 = hisi_sas_phy_read32(hisi_hba, phy_no, CHL_INT0);
+
+ if (irq_value0 & CHL_INT0_PHY_RDY_MSK)
+ hisi_sas_phy_oob_ready(hisi_hba, phy_no);
+
+ hisi_sas_phy_write32(hisi_hba, phy_no, CHL_INT0,
+ irq_value0 & (~CHL_INT0_SL_RX_BCST_ACK_MSK)
+ & (~CHL_INT0_SL_PHY_ENABLE_MSK)
+ & (~CHL_INT0_NOT_RDY_MSK));
+}
+
static irqreturn_t int_chnl_int_v3_hw(int irq_no, void *p)
{
struct hisi_hba *hisi_hba = p;
@@ -1562,8 +1703,8 @@ static irqreturn_t int_chnl_int_v3_hw(int irq_no, void *p)
& 0xeeeeeeee;
while (irq_msk) {
- u32 irq_value0 = hisi_sas_phy_read32(hisi_hba, phy_no,
- CHL_INT0);
+ if (irq_msk & (2 << (phy_no * 4)))
+ handle_chl_int0_v3_hw(hisi_hba, phy_no);
if (irq_msk & (4 << (phy_no * 4)))
handle_chl_int1_v3_hw(hisi_hba, phy_no);
@@ -1571,13 +1712,6 @@ static irqreturn_t int_chnl_int_v3_hw(int irq_no, void *p)
if (irq_msk & (8 << (phy_no * 4)))
handle_chl_int2_v3_hw(hisi_hba, phy_no);
- if (irq_msk & (2 << (phy_no * 4)) && irq_value0) {
- hisi_sas_phy_write32(hisi_hba, phy_no,
- CHL_INT0, irq_value0
- & (~CHL_INT0_SL_RX_BCST_ACK_MSK)
- & (~CHL_INT0_SL_PHY_ENABLE_MSK)
- & (~CHL_INT0_NOT_RDY_MSK));
- }
irq_msk &= ~(0xe << (phy_no * 4));
phy_no++;
}
@@ -1644,6 +1778,7 @@ static irqreturn_t fatal_axi_int_v3_hw(int irq_no, void *p)
u32 irq_value, irq_msk;
struct hisi_hba *hisi_hba = p;
struct device *dev = hisi_hba->dev;
+ struct pci_dev *pdev = hisi_hba->pci_dev;
int i;
irq_msk = hisi_sas_read32(hisi_hba, ENT_INT_SRC_MSK3);
@@ -1675,6 +1810,17 @@ static irqreturn_t fatal_axi_int_v3_hw(int irq_no, void *p)
error->msg, irq_value);
queue_work(hisi_hba->wq, &hisi_hba->rst_work);
}
+
+ if (pdev->revision < 0x21) {
+ u32 reg_val;
+
+ reg_val = hisi_sas_read32(hisi_hba,
+ AXI_MASTER_CFG_BASE +
+ AM_CTRL_GLOBAL);
+ reg_val |= AM_CTRL_SHUTDOWN_REQ_MSK;
+ hisi_sas_write32(hisi_hba, AXI_MASTER_CFG_BASE +
+ AM_CTRL_GLOBAL, reg_val);
+ }
}
if (irq_value & BIT(ENT_INT_SRC3_ITC_INT_OFF)) {
@@ -1959,21 +2105,68 @@ static irqreturn_t cq_interrupt_v3_hw(int irq_no, void *p)
return IRQ_HANDLED;
}
+static void setup_reply_map_v3_hw(struct hisi_hba *hisi_hba, int nvecs)
+{
+ const struct cpumask *mask;
+ int queue, cpu;
+
+ for (queue = 0; queue < nvecs; queue++) {
+ struct hisi_sas_cq *cq = &hisi_hba->cq[queue];
+
+ mask = pci_irq_get_affinity(hisi_hba->pci_dev, queue +
+ BASE_VECTORS_V3_HW);
+ if (!mask)
+ goto fallback;
+ cq->pci_irq_mask = mask;
+ for_each_cpu(cpu, mask)
+ hisi_hba->reply_map[cpu] = queue;
+ }
+ return;
+
+fallback:
+ for_each_possible_cpu(cpu)
+ hisi_hba->reply_map[cpu] = cpu % hisi_hba->queue_count;
+ /* Don't clean all CQ masks */
+}
+
static int interrupt_init_v3_hw(struct hisi_hba *hisi_hba)
{
struct device *dev = hisi_hba->dev;
struct pci_dev *pdev = hisi_hba->pci_dev;
int vectors, rc;
int i, k;
- int max_msi = HISI_SAS_MSI_COUNT_V3_HW;
-
- vectors = pci_alloc_irq_vectors(hisi_hba->pci_dev, 1,
- max_msi, PCI_IRQ_MSI);
- if (vectors < max_msi) {
- dev_err(dev, "could not allocate all msi (%d)\n", vectors);
- return -ENOENT;
+ int max_msi = HISI_SAS_MSI_COUNT_V3_HW, min_msi;
+
+ if (auto_affine_msi_experimental) {
+ struct irq_affinity desc = {
+ .pre_vectors = BASE_VECTORS_V3_HW,
+ };
+
+ min_msi = MIN_AFFINE_VECTORS_V3_HW;
+
+ hisi_hba->reply_map = devm_kcalloc(dev, nr_cpu_ids,
+ sizeof(unsigned int),
+ GFP_KERNEL);
+ if (!hisi_hba->reply_map)
+ return -ENOMEM;
+ vectors = pci_alloc_irq_vectors_affinity(hisi_hba->pci_dev,
+ min_msi, max_msi,
+ PCI_IRQ_MSI |
+ PCI_IRQ_AFFINITY,
+ &desc);
+ if (vectors < 0)
+ return -ENOENT;
+ setup_reply_map_v3_hw(hisi_hba, vectors - BASE_VECTORS_V3_HW);
+ } else {
+ min_msi = max_msi;
+ vectors = pci_alloc_irq_vectors(hisi_hba->pci_dev, min_msi,
+ max_msi, PCI_IRQ_MSI);
+ if (vectors < 0)
+ return vectors;
}
+ hisi_hba->cq_nvecs = vectors - BASE_VECTORS_V3_HW;
+
rc = devm_request_irq(dev, pci_irq_vector(pdev, 1),
int_phy_up_down_bcast_v3_hw, 0,
DRV_NAME " phy", hisi_hba);
@@ -2002,7 +2195,7 @@ static int interrupt_init_v3_hw(struct hisi_hba *hisi_hba)
}
/* Init tasklets for cq only */
- for (i = 0; i < hisi_hba->queue_count; i++) {
+ for (i = 0; i < hisi_hba->cq_nvecs; i++) {
struct hisi_sas_cq *cq = &hisi_hba->cq[i];
struct tasklet_struct *t = &cq->tasklet;
int nr = hisi_sas_intr_conv ? 16 : 16 + i;
@@ -2099,31 +2292,6 @@ static u32 get_phys_state_v3_hw(struct hisi_hba *hisi_hba)
return hisi_sas_read32(hisi_hba, PHY_STATE);
}
-static void phy_get_events_v3_hw(struct hisi_hba *hisi_hba, int phy_no)
-{
- struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no];
- struct asd_sas_phy *sas_phy = &phy->sas_phy;
- struct sas_phy *sphy = sas_phy->phy;
- u32 reg_value;
-
- /* loss dword sync */
- reg_value = hisi_sas_phy_read32(hisi_hba, phy_no, ERR_CNT_DWS_LOST);
- sphy->loss_of_dword_sync_count += reg_value;
-
- /* phy reset problem */
- reg_value = hisi_sas_phy_read32(hisi_hba, phy_no, ERR_CNT_RESET_PROB);
- sphy->phy_reset_problem_count += reg_value;
-
- /* invalid dword */
- reg_value = hisi_sas_phy_read32(hisi_hba, phy_no, ERR_CNT_INVLD_DW);
- sphy->invalid_dword_count += reg_value;
-
- /* disparity err */
- reg_value = hisi_sas_phy_read32(hisi_hba, phy_no, ERR_CNT_DISP_ERR);
- sphy->running_disparity_error_count += reg_value;
-
-}
-
static int disable_host_v3_hw(struct hisi_hba *hisi_hba)
{
struct device *dev = hisi_hba->dev;
@@ -2201,8 +2369,8 @@ static int write_gpio_v3_hw(struct hisi_hba *hisi_hba, u8 reg_type,
return 0;
}
-static void wait_cmds_complete_timeout_v3_hw(struct hisi_hba *hisi_hba,
- int delay_ms, int timeout_ms)
+static int wait_cmds_complete_timeout_v3_hw(struct hisi_hba *hisi_hba,
+ int delay_ms, int timeout_ms)
{
struct device *dev = hisi_hba->dev;
int entries, entries_old = 0, time;
@@ -2216,7 +2384,12 @@ static void wait_cmds_complete_timeout_v3_hw(struct hisi_hba *hisi_hba,
msleep(delay_ms);
}
+ if (time >= timeout_ms)
+ return -ETIMEDOUT;
+
dev_dbg(dev, "wait commands complete %dms\n", time);
+
+ return 0;
}
static ssize_t intr_conv_v3_hw_show(struct device *dev,
@@ -2332,6 +2505,159 @@ static struct device_attribute *host_attrs_v3_hw[] = {
NULL
};
+static const struct hisi_sas_debugfs_reg_lu debugfs_port_reg_lu[] = {
+ HISI_SAS_DEBUGFS_REG(PHY_CFG),
+ HISI_SAS_DEBUGFS_REG(HARD_PHY_LINKRATE),
+ HISI_SAS_DEBUGFS_REG(PROG_PHY_LINK_RATE),
+ HISI_SAS_DEBUGFS_REG(PHY_CTRL),
+ HISI_SAS_DEBUGFS_REG(SL_CFG),
+ HISI_SAS_DEBUGFS_REG(AIP_LIMIT),
+ HISI_SAS_DEBUGFS_REG(SL_CONTROL),
+ HISI_SAS_DEBUGFS_REG(RX_PRIMS_STATUS),
+ HISI_SAS_DEBUGFS_REG(TX_ID_DWORD0),
+ HISI_SAS_DEBUGFS_REG(TX_ID_DWORD1),
+ HISI_SAS_DEBUGFS_REG(TX_ID_DWORD2),
+ HISI_SAS_DEBUGFS_REG(TX_ID_DWORD3),
+ HISI_SAS_DEBUGFS_REG(TX_ID_DWORD4),
+ HISI_SAS_DEBUGFS_REG(TX_ID_DWORD5),
+ HISI_SAS_DEBUGFS_REG(TX_ID_DWORD6),
+ HISI_SAS_DEBUGFS_REG(TXID_AUTO),
+ HISI_SAS_DEBUGFS_REG(RX_IDAF_DWORD0),
+ HISI_SAS_DEBUGFS_REG(RXOP_CHECK_CFG_H),
+ HISI_SAS_DEBUGFS_REG(STP_LINK_TIMER),
+ HISI_SAS_DEBUGFS_REG(STP_LINK_TIMEOUT_STATE),
+ HISI_SAS_DEBUGFS_REG(CON_CFG_DRIVER),
+ HISI_SAS_DEBUGFS_REG(SAS_SSP_CON_TIMER_CFG),
+ HISI_SAS_DEBUGFS_REG(SAS_SMP_CON_TIMER_CFG),
+ HISI_SAS_DEBUGFS_REG(SAS_STP_CON_TIMER_CFG),
+ HISI_SAS_DEBUGFS_REG(CHL_INT0),
+ HISI_SAS_DEBUGFS_REG(CHL_INT1),
+ HISI_SAS_DEBUGFS_REG(CHL_INT2),
+ HISI_SAS_DEBUGFS_REG(CHL_INT0_MSK),
+ HISI_SAS_DEBUGFS_REG(CHL_INT1_MSK),
+ HISI_SAS_DEBUGFS_REG(CHL_INT2_MSK),
+ HISI_SAS_DEBUGFS_REG(SAS_EC_INT_COAL_TIME),
+ HISI_SAS_DEBUGFS_REG(CHL_INT_COAL_EN),
+ HISI_SAS_DEBUGFS_REG(SAS_RX_TRAIN_TIMER),
+ HISI_SAS_DEBUGFS_REG(PHY_CTRL_RDY_MSK),
+ HISI_SAS_DEBUGFS_REG(PHYCTRL_NOT_RDY_MSK),
+ HISI_SAS_DEBUGFS_REG(PHYCTRL_DWS_RESET_MSK),
+ HISI_SAS_DEBUGFS_REG(PHYCTRL_PHY_ENA_MSK),
+ HISI_SAS_DEBUGFS_REG(SL_RX_BCAST_CHK_MSK),
+ HISI_SAS_DEBUGFS_REG(PHYCTRL_OOB_RESTART_MSK),
+ HISI_SAS_DEBUGFS_REG(DMA_TX_STATUS),
+ HISI_SAS_DEBUGFS_REG(DMA_RX_STATUS),
+ HISI_SAS_DEBUGFS_REG(COARSETUNE_TIME),
+ HISI_SAS_DEBUGFS_REG(ERR_CNT_DWS_LOST),
+ HISI_SAS_DEBUGFS_REG(ERR_CNT_RESET_PROB),
+ HISI_SAS_DEBUGFS_REG(ERR_CNT_INVLD_DW),
+ HISI_SAS_DEBUGFS_REG(ERR_CNT_CODE_ERR),
+ HISI_SAS_DEBUGFS_REG(ERR_CNT_DISP_ERR),
+ {}
+};
+
+static const struct hisi_sas_debugfs_reg debugfs_port_reg = {
+ .lu = debugfs_port_reg_lu,
+ .count = 0x100,
+ .base_off = PORT_BASE,
+ .read_port_reg = hisi_sas_phy_read32,
+};
+
+static const struct hisi_sas_debugfs_reg_lu debugfs_global_reg_lu[] = {
+ HISI_SAS_DEBUGFS_REG(DLVRY_QUEUE_ENABLE),
+ HISI_SAS_DEBUGFS_REG(PHY_CONTEXT),
+ HISI_SAS_DEBUGFS_REG(PHY_STATE),
+ HISI_SAS_DEBUGFS_REG(PHY_PORT_NUM_MA),
+ HISI_SAS_DEBUGFS_REG(PHY_CONN_RATE),
+ HISI_SAS_DEBUGFS_REG(ITCT_CLR),
+ HISI_SAS_DEBUGFS_REG(IO_SATA_BROKEN_MSG_ADDR_LO),
+ HISI_SAS_DEBUGFS_REG(IO_SATA_BROKEN_MSG_ADDR_HI),
+ HISI_SAS_DEBUGFS_REG(SATA_INITI_D2H_STORE_ADDR_LO),
+ HISI_SAS_DEBUGFS_REG(SATA_INITI_D2H_STORE_ADDR_HI),
+ HISI_SAS_DEBUGFS_REG(CFG_MAX_TAG),
+ HISI_SAS_DEBUGFS_REG(HGC_SAS_TX_OPEN_FAIL_RETRY_CTRL),
+ HISI_SAS_DEBUGFS_REG(HGC_SAS_TXFAIL_RETRY_CTRL),
+ HISI_SAS_DEBUGFS_REG(HGC_GET_ITV_TIME),
+ HISI_SAS_DEBUGFS_REG(DEVICE_MSG_WORK_MODE),
+ HISI_SAS_DEBUGFS_REG(OPENA_WT_CONTI_TIME),
+ HISI_SAS_DEBUGFS_REG(I_T_NEXUS_LOSS_TIME),
+ HISI_SAS_DEBUGFS_REG(MAX_CON_TIME_LIMIT_TIME),
+ HISI_SAS_DEBUGFS_REG(BUS_INACTIVE_LIMIT_TIME),
+ HISI_SAS_DEBUGFS_REG(REJECT_TO_OPEN_LIMIT_TIME),
+ HISI_SAS_DEBUGFS_REG(CQ_INT_CONVERGE_EN),
+ HISI_SAS_DEBUGFS_REG(CFG_AGING_TIME),
+ HISI_SAS_DEBUGFS_REG(HGC_DFX_CFG2),
+ HISI_SAS_DEBUGFS_REG(CFG_ABT_SET_QUERY_IPTT),
+ HISI_SAS_DEBUGFS_REG(CFG_ABT_SET_IPTT_DONE),
+ HISI_SAS_DEBUGFS_REG(HGC_IOMB_PROC1_STATUS),
+ HISI_SAS_DEBUGFS_REG(CHNL_INT_STATUS),
+ HISI_SAS_DEBUGFS_REG(HGC_AXI_FIFO_ERR_INFO),
+ HISI_SAS_DEBUGFS_REG(INT_COAL_EN),
+ HISI_SAS_DEBUGFS_REG(OQ_INT_COAL_TIME),
+ HISI_SAS_DEBUGFS_REG(OQ_INT_COAL_CNT),
+ HISI_SAS_DEBUGFS_REG(ENT_INT_COAL_TIME),
+ HISI_SAS_DEBUGFS_REG(ENT_INT_COAL_CNT),
+ HISI_SAS_DEBUGFS_REG(OQ_INT_SRC),
+ HISI_SAS_DEBUGFS_REG(OQ_INT_SRC_MSK),
+ HISI_SAS_DEBUGFS_REG(ENT_INT_SRC1),
+ HISI_SAS_DEBUGFS_REG(ENT_INT_SRC2),
+ HISI_SAS_DEBUGFS_REG(ENT_INT_SRC3),
+ HISI_SAS_DEBUGFS_REG(ENT_INT_SRC_MSK1),
+ HISI_SAS_DEBUGFS_REG(ENT_INT_SRC_MSK2),
+ HISI_SAS_DEBUGFS_REG(ENT_INT_SRC_MSK3),
+ HISI_SAS_DEBUGFS_REG(CHNL_PHYUPDOWN_INT_MSK),
+ HISI_SAS_DEBUGFS_REG(CHNL_ENT_INT_MSK),
+ HISI_SAS_DEBUGFS_REG(HGC_COM_INT_MSK),
+ HISI_SAS_DEBUGFS_REG(SAS_ECC_INTR),
+ HISI_SAS_DEBUGFS_REG(SAS_ECC_INTR_MSK),
+ HISI_SAS_DEBUGFS_REG(HGC_ERR_STAT_EN),
+ HISI_SAS_DEBUGFS_REG(CQE_SEND_CNT),
+ HISI_SAS_DEBUGFS_REG(DLVRY_Q_0_DEPTH),
+ HISI_SAS_DEBUGFS_REG(DLVRY_Q_0_WR_PTR),
+ HISI_SAS_DEBUGFS_REG(DLVRY_Q_0_RD_PTR),
+ HISI_SAS_DEBUGFS_REG(HYPER_STREAM_ID_EN_CFG),
+ HISI_SAS_DEBUGFS_REG(OQ0_INT_SRC_MSK),
+ HISI_SAS_DEBUGFS_REG(COMPL_Q_0_DEPTH),
+ HISI_SAS_DEBUGFS_REG(COMPL_Q_0_WR_PTR),
+ HISI_SAS_DEBUGFS_REG(COMPL_Q_0_RD_PTR),
+ HISI_SAS_DEBUGFS_REG(AWQOS_AWCACHE_CFG),
+ HISI_SAS_DEBUGFS_REG(ARQOS_ARCACHE_CFG),
+ HISI_SAS_DEBUGFS_REG(HILINK_ERR_DFX),
+ HISI_SAS_DEBUGFS_REG(SAS_GPIO_CFG_0),
+ HISI_SAS_DEBUGFS_REG(SAS_GPIO_CFG_1),
+ HISI_SAS_DEBUGFS_REG(SAS_GPIO_TX_0_1),
+ HISI_SAS_DEBUGFS_REG(SAS_CFG_DRIVE_VLD),
+ {}
+};
+
+static const struct hisi_sas_debugfs_reg debugfs_global_reg = {
+ .lu = debugfs_global_reg_lu,
+ .count = 0x800,
+ .read_global_reg = hisi_sas_read32,
+};
+
+static void debugfs_snapshot_prepare_v3_hw(struct hisi_hba *hisi_hba)
+{
+ struct device *dev = hisi_hba->dev;
+
+ set_bit(HISI_SAS_REJECT_CMD_BIT, &hisi_hba->flags);
+
+ hisi_sas_write32(hisi_hba, DLVRY_QUEUE_ENABLE, 0);
+
+ if (wait_cmds_complete_timeout_v3_hw(hisi_hba, 100, 5000) == -ETIMEDOUT)
+ dev_dbg(dev, "Wait commands complete timeout!\n");
+
+ hisi_sas_kill_tasklets(hisi_hba);
+}
+
+static void debugfs_snapshot_restore_v3_hw(struct hisi_hba *hisi_hba)
+{
+ hisi_sas_write32(hisi_hba, DLVRY_QUEUE_ENABLE,
+ (u32)((1ULL << hisi_hba->queue_count) - 1));
+
+ clear_bit(HISI_SAS_REJECT_CMD_BIT, &hisi_hba->flags);
+}
+
static struct scsi_host_template sht_v3_hw = {
.name = DRV_NAME,
.module = THIS_MODULE,
@@ -2344,6 +2670,7 @@ static struct scsi_host_template sht_v3_hw = {
.bios_param = sas_bios_param,
.this_id = -1,
.sg_tablesize = HISI_SAS_SGE_PAGE_CNT,
+ .sg_prot_tablesize = HISI_SAS_SGE_PAGE_CNT,
.max_sectors = SCSI_DEFAULT_MAX_SECTORS,
.eh_device_reset_handler = sas_eh_device_reset_handler,
.eh_target_reset_handler = sas_eh_target_reset_handler,
@@ -2360,7 +2687,7 @@ static const struct hisi_sas_hw hisi_sas_v3_hw = {
.get_wideport_bitmap = get_wideport_bitmap_v3_hw,
.complete_hdr_size = sizeof(struct hisi_sas_complete_v3_hdr),
.clear_itct = clear_itct_v3_hw,
- .sl_notify = sl_notify_v3_hw,
+ .sl_notify_ssp = sl_notify_ssp_v3_hw,
.prep_ssp = prep_ssp_v3_hw,
.prep_smp = prep_smp_v3_hw,
.prep_stp = prep_ata_v3_hw,
@@ -2380,6 +2707,10 @@ static const struct hisi_sas_hw hisi_sas_v3_hw = {
.get_events = phy_get_events_v3_hw,
.write_gpio = write_gpio_v3_hw,
.wait_cmds_complete_timeout = wait_cmds_complete_timeout_v3_hw,
+ .debugfs_reg_global = &debugfs_global_reg,
+ .debugfs_reg_port = &debugfs_port_reg,
+ .snapshot_prepare = debugfs_snapshot_prepare_v3_hw,
+ .snapshot_restore = debugfs_snapshot_restore_v3_hw,
};
static struct Scsi_Host *
@@ -2397,6 +2728,7 @@ hisi_sas_shost_alloc_pci(struct pci_dev *pdev)
hisi_hba = shost_priv(shost);
INIT_WORK(&hisi_hba->rst_work, hisi_sas_rst_work_handler);
+ INIT_WORK(&hisi_hba->debugfs_work, hisi_sas_debugfs_work_handler);
hisi_hba->hw = &hisi_sas_v3_hw;
hisi_hba->pci_dev = pdev;
hisi_hba->dev = dev;
@@ -2414,7 +2746,7 @@ hisi_sas_shost_alloc_pci(struct pci_dev *pdev)
if (hisi_sas_get_fw_info(hisi_hba) < 0)
goto err_out;
- if (hisi_sas_alloc(hisi_hba, shost)) {
+ if (hisi_sas_alloc(hisi_hba)) {
hisi_sas_free(hisi_hba);
goto err_out;
}
@@ -2513,8 +2845,14 @@ hisi_sas_v3_probe(struct pci_dev *pdev, const struct pci_device_id *id)
dev_info(dev, "Registering for DIF/DIX prot_mask=0x%x\n",
prot_mask);
scsi_host_set_prot(hisi_hba->shost, prot_mask);
+ if (hisi_hba->prot_mask & HISI_SAS_DIX_PROT_MASK)
+ scsi_host_set_guard(hisi_hba->shost,
+ SHOST_DIX_GUARD_CRC);
}
+ if (hisi_sas_debugfs_enable)
+ hisi_sas_debugfs_init(hisi_hba);
+
rc = scsi_add_host(shost, dev);
if (rc)
goto err_out_ha;
@@ -2551,7 +2889,7 @@ hisi_sas_v3_destroy_irqs(struct pci_dev *pdev, struct hisi_hba *hisi_hba)
free_irq(pci_irq_vector(pdev, 1), hisi_hba);
free_irq(pci_irq_vector(pdev, 2), hisi_hba);
free_irq(pci_irq_vector(pdev, 11), hisi_hba);
- for (i = 0; i < hisi_hba->queue_count; i++) {
+ for (i = 0; i < hisi_hba->cq_nvecs; i++) {
struct hisi_sas_cq *cq = &hisi_hba->cq[i];
int nr = hisi_sas_intr_conv ? 16 : 16 + i;
@@ -2567,6 +2905,8 @@ static void hisi_sas_v3_remove(struct pci_dev *pdev)
struct hisi_hba *hisi_hba = sha->lldd_ha;
struct Scsi_Host *shost = sha->core.shost;
+ hisi_sas_debugfs_exit(hisi_hba);
+
if (timer_pending(&hisi_hba->timer))
del_timer(&hisi_hba->timer);
diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c
index ff67ef5d5347..f044e7d10d63 100644
--- a/drivers/scsi/hpsa.c
+++ b/drivers/scsi/hpsa.c
@@ -251,10 +251,11 @@ static int number_of_controllers;
static irqreturn_t do_hpsa_intr_intx(int irq, void *dev_id);
static irqreturn_t do_hpsa_intr_msi(int irq, void *dev_id);
-static int hpsa_ioctl(struct scsi_device *dev, int cmd, void __user *arg);
+static int hpsa_ioctl(struct scsi_device *dev, unsigned int cmd,
+ void __user *arg);
#ifdef CONFIG_COMPAT
-static int hpsa_compat_ioctl(struct scsi_device *dev, int cmd,
+static int hpsa_compat_ioctl(struct scsi_device *dev, unsigned int cmd,
void __user *arg);
#endif
@@ -1327,7 +1328,7 @@ static int hpsa_scsi_add_entry(struct ctlr_info *h,
dev_warn(&h->pdev->dev, "physical device with no LUN=0,"
" suspect firmware bug or unsupported hardware "
"configuration.\n");
- return -1;
+ return -1;
}
lun_assigned:
@@ -4110,7 +4111,7 @@ static int hpsa_gather_lun_info(struct ctlr_info *h,
"maximum logical LUNs (%d) exceeded. "
"%d LUNs ignored.\n", HPSA_MAX_LUN,
*nlogicals - HPSA_MAX_LUN);
- *nlogicals = HPSA_MAX_LUN;
+ *nlogicals = HPSA_MAX_LUN;
}
if (*nlogicals + *nphysicals > HPSA_MAX_PHYS_LUN) {
dev_warn(&h->pdev->dev,
@@ -6127,7 +6128,7 @@ static void cmd_free(struct ctlr_info *h, struct CommandList *c)
#ifdef CONFIG_COMPAT
-static int hpsa_ioctl32_passthru(struct scsi_device *dev, int cmd,
+static int hpsa_ioctl32_passthru(struct scsi_device *dev, unsigned int cmd,
void __user *arg)
{
IOCTL32_Command_struct __user *arg32 =
@@ -6164,7 +6165,7 @@ static int hpsa_ioctl32_passthru(struct scsi_device *dev, int cmd,
}
static int hpsa_ioctl32_big_passthru(struct scsi_device *dev,
- int cmd, void __user *arg)
+ unsigned int cmd, void __user *arg)
{
BIG_IOCTL32_Command_struct __user *arg32 =
(BIG_IOCTL32_Command_struct __user *) arg;
@@ -6201,7 +6202,8 @@ static int hpsa_ioctl32_big_passthru(struct scsi_device *dev,
return err;
}
-static int hpsa_compat_ioctl(struct scsi_device *dev, int cmd, void __user *arg)
+static int hpsa_compat_ioctl(struct scsi_device *dev, unsigned int cmd,
+ void __user *arg)
{
switch (cmd) {
case CCISS_GETPCIINFO:
@@ -6521,7 +6523,8 @@ static void check_ioctl_unit_attention(struct ctlr_info *h,
/*
* ioctl
*/
-static int hpsa_ioctl(struct scsi_device *dev, int cmd, void __user *arg)
+static int hpsa_ioctl(struct scsi_device *dev, unsigned int cmd,
+ void __user *arg)
{
struct ctlr_info *h;
void __user *argp = (void __user *)arg;
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c
index dbaa4f131433..3ad997ac3510 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc.c
@@ -139,6 +139,7 @@ static const struct {
{ IBMVFC_FC_FAILURE, IBMVFC_VENDOR_SPECIFIC, DID_ERROR, 1, 1, "vendor specific" },
{ IBMVFC_FC_SCSI_ERROR, 0, DID_OK, 1, 0, "SCSI error" },
+ { IBMVFC_FC_SCSI_ERROR, IBMVFC_COMMAND_FAILED, DID_ERROR, 0, 1, "PRLI to device failed." },
};
static void ibmvfc_npiv_login(struct ibmvfc_host *);
@@ -1494,9 +1495,9 @@ static void ibmvfc_log_error(struct ibmvfc_event *evt)
if (rsp->flags & FCP_RSP_LEN_VALID)
rsp_code = rsp->data.info.rsp_code;
- scmd_printk(KERN_ERR, cmnd, "Command (%02X) failed: %s (%x:%x) "
+ scmd_printk(KERN_ERR, cmnd, "Command (%02X) : %s (%x:%x) "
"flags: %x fcp_rsp: %x, resid=%d, scsi_status: %x\n",
- cmnd->cmnd[0], err, vfc_cmd->status, vfc_cmd->error,
+ cmnd->cmnd[0], err, be16_to_cpu(vfc_cmd->status), be16_to_cpu(vfc_cmd->error),
rsp->flags, rsp_code, scsi_get_resid(cmnd), rsp->scsi_status);
}
@@ -2022,7 +2023,7 @@ static int ibmvfc_reset_device(struct scsi_device *sdev, int type, char *desc)
sdev_printk(KERN_ERR, sdev, "%s reset failed: %s (%x:%x) "
"flags: %x fcp_rsp: %x, scsi_status: %x\n", desc,
ibmvfc_get_cmd_error(be16_to_cpu(rsp_iu.cmd.status), be16_to_cpu(rsp_iu.cmd.error)),
- rsp_iu.cmd.status, rsp_iu.cmd.error, fc_rsp->flags, rsp_code,
+ be16_to_cpu(rsp_iu.cmd.status), be16_to_cpu(rsp_iu.cmd.error), fc_rsp->flags, rsp_code,
fc_rsp->scsi_status);
rsp_rc = -EIO;
} else
@@ -2381,7 +2382,7 @@ static int ibmvfc_abort_task_set(struct scsi_device *sdev)
sdev_printk(KERN_ERR, sdev, "Abort failed: %s (%x:%x) "
"flags: %x fcp_rsp: %x, scsi_status: %x\n",
ibmvfc_get_cmd_error(be16_to_cpu(rsp_iu.cmd.status), be16_to_cpu(rsp_iu.cmd.error)),
- rsp_iu.cmd.status, rsp_iu.cmd.error, fc_rsp->flags, rsp_code,
+ be16_to_cpu(rsp_iu.cmd.status), be16_to_cpu(rsp_iu.cmd.error), fc_rsp->flags, rsp_code,
fc_rsp->scsi_status);
rsp_rc = -EIO;
} else
@@ -2755,16 +2756,18 @@ static void ibmvfc_handle_crq(struct ibmvfc_crq *crq, struct ibmvfc_host *vhost)
ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_NONE);
if (crq->format == IBMVFC_PARTITION_MIGRATED) {
/* We need to re-setup the interpartition connection */
- dev_info(vhost->dev, "Re-enabling adapter\n");
+ dev_info(vhost->dev, "Partition migrated, Re-enabling adapter\n");
vhost->client_migrated = 1;
ibmvfc_purge_requests(vhost, DID_REQUEUE);
ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN);
ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_REENABLE);
- } else {
- dev_err(vhost->dev, "Virtual adapter failed (rc=%d)\n", crq->format);
+ } else if (crq->format == IBMVFC_PARTNER_FAILED || crq->format == IBMVFC_PARTNER_DEREGISTER) {
+ dev_err(vhost->dev, "Host partner adapter deregistered or failed (rc=%d)\n", crq->format);
ibmvfc_purge_requests(vhost, DID_ERROR);
ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN);
ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_RESET);
+ } else {
+ dev_err(vhost->dev, "Received unknown transport event from partner (rc=%d)\n", crq->format);
}
return;
case IBMVFC_CRQ_CMD_RSP:
@@ -3348,7 +3351,7 @@ static void ibmvfc_tgt_prli_done(struct ibmvfc_event *evt)
tgt_log(tgt, level, "Process Login failed: %s (%x:%x) rc=0x%02X\n",
ibmvfc_get_cmd_error(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)),
- rsp->status, rsp->error, status);
+ be16_to_cpu(rsp->status), be16_to_cpu(rsp->error), status);
break;
}
@@ -3446,9 +3449,10 @@ static void ibmvfc_tgt_plogi_done(struct ibmvfc_event *evt)
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
tgt_log(tgt, level, "Port Login failed: %s (%x:%x) %s (%x) %s (%x) rc=0x%02X\n",
- ibmvfc_get_cmd_error(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)), rsp->status, rsp->error,
- ibmvfc_get_fc_type(be16_to_cpu(rsp->fc_type)), rsp->fc_type,
- ibmvfc_get_ls_explain(be16_to_cpu(rsp->fc_explain)), rsp->fc_explain, status);
+ ibmvfc_get_cmd_error(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)),
+ be16_to_cpu(rsp->status), be16_to_cpu(rsp->error),
+ ibmvfc_get_fc_type(be16_to_cpu(rsp->fc_type)), be16_to_cpu(rsp->fc_type),
+ ibmvfc_get_ls_explain(be16_to_cpu(rsp->fc_explain)), be16_to_cpu(rsp->fc_explain), status);
break;
}
@@ -3619,7 +3623,7 @@ static void ibmvfc_tgt_adisc_done(struct ibmvfc_event *evt)
fc_explain = (be32_to_cpu(mad->fc_iu.response[1]) & 0x0000ff00) >> 8;
tgt_info(tgt, "ADISC failed: %s (%x:%x) %s (%x) %s (%x) rc=0x%02X\n",
ibmvfc_get_cmd_error(be16_to_cpu(mad->iu.status), be16_to_cpu(mad->iu.error)),
- mad->iu.status, mad->iu.error,
+ be16_to_cpu(mad->iu.status), be16_to_cpu(mad->iu.error),
ibmvfc_get_fc_type(fc_reason), fc_reason,
ibmvfc_get_ls_explain(fc_explain), fc_explain, status);
break;
@@ -3831,9 +3835,10 @@ static void ibmvfc_tgt_query_target_done(struct ibmvfc_event *evt)
tgt_log(tgt, level, "Query Target failed: %s (%x:%x) %s (%x) %s (%x) rc=0x%02X\n",
ibmvfc_get_cmd_error(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)),
- rsp->status, rsp->error, ibmvfc_get_fc_type(be16_to_cpu(rsp->fc_type)),
- rsp->fc_type, ibmvfc_get_gs_explain(be16_to_cpu(rsp->fc_explain)),
- rsp->fc_explain, status);
+ be16_to_cpu(rsp->status), be16_to_cpu(rsp->error),
+ ibmvfc_get_fc_type(be16_to_cpu(rsp->fc_type)), be16_to_cpu(rsp->fc_type),
+ ibmvfc_get_gs_explain(be16_to_cpu(rsp->fc_explain)), be16_to_cpu(rsp->fc_explain),
+ status);
break;
}
@@ -3959,7 +3964,7 @@ static void ibmvfc_discover_targets_done(struct ibmvfc_event *evt)
level += ibmvfc_retry_host_init(vhost);
ibmvfc_log(vhost, level, "Discover Targets failed: %s (%x:%x)\n",
ibmvfc_get_cmd_error(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)),
- rsp->status, rsp->error);
+ be16_to_cpu(rsp->status), be16_to_cpu(rsp->error));
break;
case IBMVFC_MAD_DRIVER_FAILED:
break;
@@ -4024,7 +4029,7 @@ static void ibmvfc_npiv_login_done(struct ibmvfc_event *evt)
ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
ibmvfc_log(vhost, level, "NPIV Login failed: %s (%x:%x)\n",
ibmvfc_get_cmd_error(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)),
- rsp->status, rsp->error);
+ be16_to_cpu(rsp->status), be16_to_cpu(rsp->error));
ibmvfc_free_event(evt);
return;
case IBMVFC_MAD_CRQ_ERROR:
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.h b/drivers/scsi/ibmvscsi/ibmvfc.h
index b81a53c4a9a8..459cc288ba1d 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.h
+++ b/drivers/scsi/ibmvscsi/ibmvfc.h
@@ -78,9 +78,14 @@ enum ibmvfc_crq_valid {
IBMVFC_CRQ_XPORT_EVENT = 0xFF,
};
-enum ibmvfc_crq_format {
+enum ibmvfc_crq_init_msg {
IBMVFC_CRQ_INIT = 0x01,
IBMVFC_CRQ_INIT_COMPLETE = 0x02,
+};
+
+enum ibmvfc_crq_xport_evts {
+ IBMVFC_PARTNER_FAILED = 0x01,
+ IBMVFC_PARTNER_DEREGISTER = 0x02,
IBMVFC_PARTITION_MIGRATED = 0x06,
};
diff --git a/drivers/scsi/ibmvscsi/ibmvscsi.c b/drivers/scsi/ibmvscsi/ibmvscsi.c
index 1135e74646e2..8cec5230fe31 100644
--- a/drivers/scsi/ibmvscsi/ibmvscsi.c
+++ b/drivers/scsi/ibmvscsi/ibmvscsi.c
@@ -96,6 +96,7 @@ static int client_reserve = 1;
static char partition_name[96] = "UNKNOWN";
static unsigned int partition_number = -1;
static LIST_HEAD(ibmvscsi_head);
+static DEFINE_SPINLOCK(ibmvscsi_driver_lock);
static struct scsi_transport_template *ibmvscsi_transport_template;
@@ -2270,7 +2271,9 @@ static int ibmvscsi_probe(struct vio_dev *vdev, const struct vio_device_id *id)
}
dev_set_drvdata(&vdev->dev, hostdata);
+ spin_lock(&ibmvscsi_driver_lock);
list_add_tail(&hostdata->host_list, &ibmvscsi_head);
+ spin_unlock(&ibmvscsi_driver_lock);
return 0;
add_srp_port_failed:
@@ -2292,15 +2295,27 @@ static int ibmvscsi_probe(struct vio_dev *vdev, const struct vio_device_id *id)
static int ibmvscsi_remove(struct vio_dev *vdev)
{
struct ibmvscsi_host_data *hostdata = dev_get_drvdata(&vdev->dev);
- list_del(&hostdata->host_list);
- unmap_persist_bufs(hostdata);
+ unsigned long flags;
+
+ srp_remove_host(hostdata->host);
+ scsi_remove_host(hostdata->host);
+
+ purge_requests(hostdata, DID_ERROR);
+
+ spin_lock_irqsave(hostdata->host->host_lock, flags);
release_event_pool(&hostdata->pool, hostdata);
+ spin_unlock_irqrestore(hostdata->host->host_lock, flags);
+
ibmvscsi_release_crq_queue(&hostdata->queue, hostdata,
max_events);
kthread_stop(hostdata->work_thread);
- srp_remove_host(hostdata->host);
- scsi_remove_host(hostdata->host);
+ unmap_persist_bufs(hostdata);
+
+ spin_lock(&ibmvscsi_driver_lock);
+ list_del(&hostdata->host_list);
+ spin_unlock(&ibmvscsi_driver_lock);
+
scsi_host_put(hostdata->host);
return 0;
diff --git a/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c b/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c
index cc9cae469c4b..7ca277e28d63 100644
--- a/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c
+++ b/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c
@@ -3788,11 +3788,6 @@ static int ibmvscsis_write_pending(struct se_cmd *se_cmd)
return 0;
}
-static int ibmvscsis_write_pending_status(struct se_cmd *se_cmd)
-{
- return 0;
-}
-
static void ibmvscsis_set_default_node_attrs(struct se_node_acl *nacl)
{
}
@@ -4053,7 +4048,6 @@ static const struct target_core_fabric_ops ibmvscsis_ops = {
.release_cmd = ibmvscsis_release_cmd,
.sess_get_index = ibmvscsis_sess_get_index,
.write_pending = ibmvscsis_write_pending,
- .write_pending_status = ibmvscsis_write_pending_status,
.set_default_node_attributes = ibmvscsis_set_default_node_attrs,
.get_cmd_state = ibmvscsis_get_cmd_state,
.queue_data_in = ibmvscsis_queue_data_in,
diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c
index d1b4025a4503..6d053e220153 100644
--- a/drivers/scsi/ipr.c
+++ b/drivers/scsi/ipr.c
@@ -6696,7 +6696,8 @@ err_nodev:
* Return value:
* 0 on success / other on failure
**/
-static int ipr_ioctl(struct scsi_device *sdev, int cmd, void __user *arg)
+static int ipr_ioctl(struct scsi_device *sdev, unsigned int cmd,
+ void __user *arg)
{
struct ipr_resource_entry *res;
diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c
index cae6368ebb98..ed3debce2819 100644
--- a/drivers/scsi/iscsi_tcp.c
+++ b/drivers/scsi/iscsi_tcp.c
@@ -518,7 +518,7 @@ static int iscsi_sw_tcp_pdu_init(struct iscsi_task *task,
if (!task->sc)
iscsi_sw_tcp_send_linear_data_prep(conn, task->data, count);
else {
- struct scsi_data_buffer *sdb = scsi_out(task->sc);
+ struct scsi_data_buffer *sdb = &task->sc->sdb;
err = iscsi_sw_tcp_send_data_prep(conn, sdb->table.sgl,
sdb->table.nents, offset,
@@ -952,12 +952,6 @@ static umode_t iscsi_sw_tcp_attr_is_visible(int param_type, int param)
return 0;
}
-static int iscsi_sw_tcp_slave_alloc(struct scsi_device *sdev)
-{
- blk_queue_flag_set(QUEUE_FLAG_BIDI, sdev->request_queue);
- return 0;
-}
-
static int iscsi_sw_tcp_slave_configure(struct scsi_device *sdev)
{
struct iscsi_sw_tcp_host *tcp_sw_host = iscsi_host_priv(sdev->host);
@@ -985,7 +979,6 @@ static struct scsi_host_template iscsi_sw_tcp_sht = {
.eh_device_reset_handler= iscsi_eh_device_reset,
.eh_target_reset_handler = iscsi_eh_recover_target,
.dma_boundary = PAGE_SIZE - 1,
- .slave_alloc = iscsi_sw_tcp_slave_alloc,
.slave_configure = iscsi_sw_tcp_slave_configure,
.target_alloc = iscsi_target_alloc,
.proc_name = "iscsi_tcp",
diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c
index 120fc520f27a..e893949a3d11 100644
--- a/drivers/scsi/libiscsi.c
+++ b/drivers/scsi/libiscsi.c
@@ -228,32 +228,6 @@ static int iscsi_prep_ecdb_ahs(struct iscsi_task *task)
return 0;
}
-static int iscsi_prep_bidi_ahs(struct iscsi_task *task)
-{
- struct scsi_cmnd *sc = task->sc;
- struct iscsi_rlength_ahdr *rlen_ahdr;
- int rc;
-
- rlen_ahdr = iscsi_next_hdr(task);
- rc = iscsi_add_hdr(task, sizeof(*rlen_ahdr));
- if (rc)
- return rc;
-
- rlen_ahdr->ahslength =
- cpu_to_be16(sizeof(rlen_ahdr->read_length) +
- sizeof(rlen_ahdr->reserved));
- rlen_ahdr->ahstype = ISCSI_AHSTYPE_RLENGTH;
- rlen_ahdr->reserved = 0;
- rlen_ahdr->read_length = cpu_to_be32(scsi_in(sc)->length);
-
- ISCSI_DBG_SESSION(task->conn->session,
- "bidi-in rlen_ahdr->read_length(%d) "
- "rlen_ahdr->ahslength(%d)\n",
- be32_to_cpu(rlen_ahdr->read_length),
- be16_to_cpu(rlen_ahdr->ahslength));
- return 0;
-}
-
/**
* iscsi_check_tmf_restrictions - check if a task is affected by TMF
* @task: iscsi task
@@ -392,13 +366,6 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task)
memcpy(hdr->cdb, sc->cmnd, cmd_len);
task->imm_count = 0;
- if (scsi_bidi_cmnd(sc)) {
- hdr->flags |= ISCSI_FLAG_CMD_READ;
- rc = iscsi_prep_bidi_ahs(task);
- if (rc)
- return rc;
- }
-
if (scsi_get_prot_op(sc) != SCSI_PROT_NORMAL)
task->protected = true;
@@ -473,12 +440,10 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task)
conn->scsicmd_pdus_cnt++;
ISCSI_DBG_SESSION(session, "iscsi prep [%s cid %d sc %p cdb 0x%x "
- "itt 0x%x len %d bidi_len %d cmdsn %d win %d]\n",
- scsi_bidi_cmnd(sc) ? "bidirectional" :
+ "itt 0x%x len %d cmdsn %d win %d]\n",
sc->sc_data_direction == DMA_TO_DEVICE ?
"write" : "read", conn->id, sc, sc->cmnd[0],
task->itt, transfer_length,
- scsi_bidi_cmnd(sc) ? scsi_in(sc)->length : 0,
session->cmdsn,
session->max_cmdsn - session->exp_cmdsn + 1);
return 0;
@@ -647,12 +612,7 @@ static void fail_scsi_task(struct iscsi_task *task, int err)
state = ISCSI_TASK_ABRT_TMF;
sc->result = err << 16;
- if (!scsi_bidi_cmnd(sc))
- scsi_set_resid(sc, scsi_bufflen(sc));
- else {
- scsi_out(sc)->resid = scsi_out(sc)->length;
- scsi_in(sc)->resid = scsi_in(sc)->length;
- }
+ scsi_set_resid(sc, scsi_bufflen(sc));
/* regular RX path uses back_lock */
spin_lock_bh(&conn->session->back_lock);
@@ -838,7 +798,7 @@ EXPORT_SYMBOL_GPL(iscsi_conn_send_pdu);
* @datalen: len of buffer
*
* iscsi_cmd_rsp sets up the scsi_cmnd fields based on the PDU and
- * then completes the command and task.
+ * then completes the command and task. called under back_lock
**/
static void iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
struct iscsi_task *task, char *data,
@@ -907,14 +867,7 @@ invalid_datalen:
if (rhdr->flags & (ISCSI_FLAG_CMD_BIDI_UNDERFLOW |
ISCSI_FLAG_CMD_BIDI_OVERFLOW)) {
- int res_count = be32_to_cpu(rhdr->bi_residual_count);
-
- if (scsi_bidi_cmnd(sc) && res_count > 0 &&
- (rhdr->flags & ISCSI_FLAG_CMD_BIDI_OVERFLOW ||
- res_count <= scsi_in(sc)->length))
- scsi_in(sc)->resid = res_count;
- else
- sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status;
+ sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status;
}
if (rhdr->flags & (ISCSI_FLAG_CMD_UNDERFLOW |
@@ -941,6 +894,9 @@ out:
* @conn: iscsi connection
* @hdr: iscsi pdu
* @task: scsi command task
+ *
+ * iscsi_data_in_rsp sets up the scsi_cmnd fields based on the data received
+ * then completes the command and task. called under back_lock
**/
static void
iscsi_data_in_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
@@ -961,8 +917,8 @@ iscsi_data_in_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
if (res_count > 0 &&
(rhdr->flags & ISCSI_FLAG_CMD_OVERFLOW ||
- res_count <= scsi_in(sc)->length))
- scsi_in(sc)->resid = res_count;
+ res_count <= sc->sdb.length))
+ scsi_set_resid(sc, res_count);
else
sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status;
}
@@ -1025,6 +981,16 @@ static int iscsi_send_nopout(struct iscsi_conn *conn, struct iscsi_nopin *rhdr)
return 0;
}
+/**
+ * iscsi_nop_out_rsp - SCSI NOP Response processing
+ * @task: scsi command task
+ * @nop: the nop structure
+ * @data: where to put the data
+ * @datalen: length of data
+ *
+ * iscsi_nop_out_rsp handles nop response from use or
+ * from user space. called under back_lock
+ **/
static int iscsi_nop_out_rsp(struct iscsi_task *task,
struct iscsi_nopin *nop, char *data, int datalen)
{
@@ -1797,7 +1763,9 @@ int iscsi_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *sc)
return 0;
prepd_reject:
+ spin_lock_bh(&session->back_lock);
iscsi_complete_task(task, ISCSI_TASK_REQUEUE_SCSIQ);
+ spin_unlock_bh(&session->back_lock);
reject:
spin_unlock_bh(&session->frwd_lock);
ISCSI_DBG_SESSION(session, "cmd 0x%x rejected (%d)\n",
@@ -1805,17 +1773,14 @@ reject:
return SCSI_MLQUEUE_TARGET_BUSY;
prepd_fault:
+ spin_lock_bh(&session->back_lock);
iscsi_complete_task(task, ISCSI_TASK_REQUEUE_SCSIQ);
+ spin_unlock_bh(&session->back_lock);
fault:
spin_unlock_bh(&session->frwd_lock);
ISCSI_DBG_SESSION(session, "iscsi: cmd 0x%x is not queued (%d)\n",
sc->cmnd[0], reason);
- if (!scsi_bidi_cmnd(sc))
- scsi_set_resid(sc, scsi_bufflen(sc));
- else {
- scsi_out(sc)->resid = scsi_out(sc)->length;
- scsi_in(sc)->resid = scsi_in(sc)->length;
- }
+ scsi_set_resid(sc, scsi_bufflen(sc));
sc->scsi_done(sc);
return 0;
}
@@ -3127,8 +3092,9 @@ fail_mgmt_tasks(struct iscsi_session *session, struct iscsi_conn *conn)
state = ISCSI_TASK_ABRT_SESS_RECOV;
if (task->state == ISCSI_TASK_PENDING)
state = ISCSI_TASK_COMPLETED;
+ spin_lock_bh(&session->back_lock);
iscsi_complete_task(task, state);
-
+ spin_unlock_bh(&session->back_lock);
}
}
diff --git a/drivers/scsi/libiscsi_tcp.c b/drivers/scsi/libiscsi_tcp.c
index 8a6b1b3f8277..c3fe3f3a78f5 100644
--- a/drivers/scsi/libiscsi_tcp.c
+++ b/drivers/scsi/libiscsi_tcp.c
@@ -129,12 +129,17 @@ static void iscsi_tcp_segment_map(struct iscsi_segment *segment, int recv)
BUG_ON(sg->length == 0);
/*
+ * We always map for the recv path.
+ *
* If the page count is greater than one it is ok to send
* to the network layer's zero copy send path. If not we
- * have to go the slow sendmsg path. We always map for the
- * recv path.
+ * have to go the slow sendmsg path.
+ *
+ * Same goes for slab pages: skb_can_coalesce() allows
+ * coalescing neighboring slab objects into a single frag which
+ * triggers one of hardened usercopy checks.
*/
- if (page_count(sg_page(sg)) >= 1 && !recv)
+ if (!recv && page_count(sg_page(sg)) >= 1 && !PageSlab(sg_page(sg)))
return;
if (recv) {
@@ -495,7 +500,7 @@ static int iscsi_tcp_data_in(struct iscsi_conn *conn, struct iscsi_task *task)
struct iscsi_tcp_task *tcp_task = task->dd_data;
struct iscsi_data_rsp *rhdr = (struct iscsi_data_rsp *)tcp_conn->in.hdr;
int datasn = be32_to_cpu(rhdr->datasn);
- unsigned total_in_length = scsi_in(task->sc)->length;
+ unsigned total_in_length = task->sc->sdb.length;
/*
* lib iscsi will update this in the completion handling if there
@@ -580,11 +585,11 @@ static int iscsi_tcp_r2t_rsp(struct iscsi_conn *conn, struct iscsi_task *task)
data_length, session->max_burst);
data_offset = be32_to_cpu(rhdr->data_offset);
- if (data_offset + data_length > scsi_out(task->sc)->length) {
+ if (data_offset + data_length > task->sc->sdb.length) {
iscsi_conn_printk(KERN_ERR, conn,
"invalid R2T with data len %u at offset %u "
"and total length %d\n", data_length,
- data_offset, scsi_out(task->sc)->length);
+ data_offset, task->sc->sdb.length);
return ISCSI_ERR_DATALEN;
}
@@ -696,7 +701,7 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
if (tcp_conn->in.datalen) {
struct iscsi_tcp_task *tcp_task = task->dd_data;
struct ahash_request *rx_hash = NULL;
- struct scsi_data_buffer *sdb = scsi_in(task->sc);
+ struct scsi_data_buffer *sdb = &task->sc->sdb;
/*
* Setup copy of Data-In into the struct scsi_cmnd
diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c
index f21c93bbb35c..17b45a0c7bc3 100644
--- a/drivers/scsi/libsas/sas_expander.c
+++ b/drivers/scsi/libsas/sas_expander.c
@@ -25,6 +25,7 @@
#include <linux/scatterlist.h>
#include <linux/blkdev.h>
#include <linux/slab.h>
+#include <asm/unaligned.h>
#include "sas_internal.h"
@@ -614,7 +615,14 @@ int sas_smp_phy_control(struct domain_device *dev, int phy_id,
}
res = smp_execute_task(dev, pc_req, PC_REQ_SIZE, pc_resp,PC_RESP_SIZE);
-
+ if (res) {
+ pr_err("ex %016llx phy%02d PHY control failed: %d\n",
+ SAS_ADDR(dev->sas_addr), phy_id, res);
+ } else if (pc_resp[2] != SMP_RESP_FUNC_ACC) {
+ pr_err("ex %016llx phy%02d PHY control failed: function result 0x%x\n",
+ SAS_ADDR(dev->sas_addr), phy_id, pc_resp[2]);
+ res = pc_resp[2];
+ }
kfree(pc_resp);
kfree(pc_req);
return res;
@@ -689,10 +697,10 @@ int sas_smp_get_phy_events(struct sas_phy *phy)
if (res)
goto out;
- phy->invalid_dword_count = scsi_to_u32(&resp[12]);
- phy->running_disparity_error_count = scsi_to_u32(&resp[16]);
- phy->loss_of_dword_sync_count = scsi_to_u32(&resp[20]);
- phy->phy_reset_problem_count = scsi_to_u32(&resp[24]);
+ phy->invalid_dword_count = get_unaligned_be32(&resp[12]);
+ phy->running_disparity_error_count = get_unaligned_be32(&resp[16]);
+ phy->loss_of_dword_sync_count = get_unaligned_be32(&resp[20]);
+ phy->phy_reset_problem_count = get_unaligned_be32(&resp[24]);
out:
kfree(req);
@@ -817,6 +825,26 @@ static struct domain_device *sas_ex_discover_end_dev(
#ifdef CONFIG_SCSI_SAS_ATA
if ((phy->attached_tproto & SAS_PROTOCOL_STP) || phy->attached_sata_dev) {
+ if (child->linkrate > parent->min_linkrate) {
+ struct sas_phy_linkrates rates = {
+ .maximum_linkrate = parent->min_linkrate,
+ .minimum_linkrate = parent->min_linkrate,
+ };
+ int ret;
+
+ pr_notice("ex %016llx phy%02d SATA device linkrate > min pathway connection rate, attempting to lower device linkrate\n",
+ SAS_ADDR(child->sas_addr), phy_id);
+ ret = sas_smp_phy_control(parent, phy_id,
+ PHY_FUNC_LINK_RESET, &rates);
+ if (ret) {
+ pr_err("ex %016llx phy%02d SATA device could not set linkrate (%d)\n",
+ SAS_ADDR(child->sas_addr), phy_id, ret);
+ goto out_free;
+ }
+ pr_notice("ex %016llx phy%02d SATA device set linkrate successfully\n",
+ SAS_ADDR(child->sas_addr), phy_id);
+ child->linkrate = child->min_linkrate;
+ }
res = sas_get_ata_info(child, phy);
if (res)
goto out_free;
diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c
index c43a00a9d819..b775445892af 100644
--- a/drivers/scsi/libsas/sas_scsi_host.c
+++ b/drivers/scsi/libsas/sas_scsi_host.c
@@ -799,7 +799,7 @@ out:
shost->host_failed, tries);
}
-int sas_ioctl(struct scsi_device *sdev, int cmd, void __user *arg)
+int sas_ioctl(struct scsi_device *sdev, unsigned int cmd, void __user *arg)
{
struct domain_device *dev = sdev_to_domain_dev(sdev);
diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h
index ebdfe5b26937..41d849f283f6 100644
--- a/drivers/scsi/lpfc/lpfc.h
+++ b/drivers/scsi/lpfc/lpfc.h
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term *
+ * Copyright (C) 2017-2019 Broadcom. All Rights Reserved. The term *
* “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. *
* Copyright (C) 2004-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
@@ -84,8 +84,6 @@ struct lpfc_sli2_slim;
#define LPFC_HB_MBOX_INTERVAL 5 /* Heart beat interval in seconds. */
#define LPFC_HB_MBOX_TIMEOUT 30 /* Heart beat timeout in seconds. */
-#define LPFC_LOOK_AHEAD_OFF 0 /* Look ahead logic is turned off */
-
/* Error Attention event polling interval */
#define LPFC_ERATT_POLL_INTERVAL 5 /* EATT poll interval in seconds */
@@ -146,6 +144,7 @@ struct lpfc_nvmet_ctxbuf {
struct lpfc_nvmet_rcv_ctx *context;
struct lpfc_iocbq *iocbq;
struct lpfc_sglq *sglq;
+ struct work_struct defer_work;
};
struct lpfc_dma_pool {
@@ -235,8 +234,6 @@ typedef struct lpfc_vpd {
} sli3Feat;
} lpfc_vpd_t;
-struct lpfc_scsi_buf;
-
/*
* lpfc stat counters
@@ -466,6 +463,7 @@ struct lpfc_vport {
uint32_t cfg_use_adisc;
uint32_t cfg_discovery_threads;
uint32_t cfg_log_verbose;
+ uint32_t cfg_enable_fc4_type;
uint32_t cfg_max_luns;
uint32_t cfg_enable_da_id;
uint32_t cfg_max_scsicmpl_time;
@@ -479,6 +477,7 @@ struct lpfc_vport {
struct dentry *debug_disc_trc;
struct dentry *debug_nodelist;
struct dentry *debug_nvmestat;
+ struct dentry *debug_scsistat;
struct dentry *debug_nvmektime;
struct dentry *debug_cpucheck;
struct dentry *vport_debugfs_root;
@@ -596,6 +595,13 @@ struct lpfc_mbox_ext_buf_ctx {
struct list_head ext_dmabuf_list;
};
+struct lpfc_epd_pool {
+ /* Expedite pool */
+ struct list_head list;
+ u32 count;
+ spinlock_t lock; /* lock for expedite pool */
+};
+
struct lpfc_ras_fwlog {
uint8_t *fwlog_buff;
uint32_t fw_buffcount; /* Buffer size posted to FW */
@@ -617,20 +623,19 @@ struct lpfc_ras_fwlog {
struct lpfc_hba {
/* SCSI interface function jump table entries */
- int (*lpfc_new_scsi_buf)
- (struct lpfc_vport *, int);
- struct lpfc_scsi_buf * (*lpfc_get_scsi_buf)
- (struct lpfc_hba *, struct lpfc_nodelist *);
+ struct lpfc_io_buf * (*lpfc_get_scsi_buf)
+ (struct lpfc_hba *phba, struct lpfc_nodelist *ndlp,
+ struct scsi_cmnd *cmnd);
int (*lpfc_scsi_prep_dma_buf)
- (struct lpfc_hba *, struct lpfc_scsi_buf *);
+ (struct lpfc_hba *, struct lpfc_io_buf *);
void (*lpfc_scsi_unprep_dma_buf)
- (struct lpfc_hba *, struct lpfc_scsi_buf *);
+ (struct lpfc_hba *, struct lpfc_io_buf *);
void (*lpfc_release_scsi_buf)
- (struct lpfc_hba *, struct lpfc_scsi_buf *);
+ (struct lpfc_hba *, struct lpfc_io_buf *);
void (*lpfc_rampdown_queue_depth)
(struct lpfc_hba *);
void (*lpfc_scsi_prep_cmnd)
- (struct lpfc_vport *, struct lpfc_scsi_buf *,
+ (struct lpfc_vport *, struct lpfc_io_buf *,
struct lpfc_nodelist *);
/* IOCB interface function jump table entries */
@@ -673,13 +678,17 @@ struct lpfc_hba {
(struct lpfc_hba *);
int (*lpfc_bg_scsi_prep_dma_buf)
- (struct lpfc_hba *, struct lpfc_scsi_buf *);
+ (struct lpfc_hba *, struct lpfc_io_buf *);
/* Add new entries here */
+ /* expedite pool */
+ struct lpfc_epd_pool epd_pool;
+
/* SLI4 specific HBA data structure */
struct lpfc_sli4_hba sli4_hba;
struct workqueue_struct *wq;
+ struct delayed_work eq_delay_work;
struct lpfc_sli sli;
uint8_t pci_dev_grp; /* lpfc PCI dev group: 0x0, 0x1, 0x2,... */
@@ -713,7 +722,6 @@ struct lpfc_hba {
#define HBA_FCOE_MODE 0x4 /* HBA function in FCoE Mode */
#define HBA_SP_QUEUE_EVT 0x8 /* Slow-path qevt posted to worker thread*/
#define HBA_POST_RECEIVE_BUFFER 0x10 /* Rcv buffers need to be posted */
-#define FCP_XRI_ABORT_EVENT 0x20
#define ELS_XRI_ABORT_EVENT 0x40
#define ASYNC_EVENT 0x80
#define LINK_DISABLED 0x100 /* Link disabled by user */
@@ -784,12 +792,12 @@ struct lpfc_hba {
uint8_t nvmet_support; /* driver supports NVMET */
#define LPFC_NVMET_MAX_PORTS 32
uint8_t mds_diags_support;
- uint32_t initial_imax;
uint8_t bbcredit_support;
uint8_t enab_exp_wqcq_pages;
/* HBA Config Parameters */
uint32_t cfg_ack0;
+ uint32_t cfg_xri_rebalancing;
uint32_t cfg_enable_npiv;
uint32_t cfg_enable_rrq;
uint32_t cfg_topology;
@@ -811,12 +819,14 @@ struct lpfc_hba {
uint32_t cfg_use_msi;
uint32_t cfg_auto_imax;
uint32_t cfg_fcp_imax;
+ uint32_t cfg_cq_poll_threshold;
+ uint32_t cfg_cq_max_proc_limit;
uint32_t cfg_fcp_cpu_map;
- uint32_t cfg_fcp_io_channel;
+ uint32_t cfg_hdw_queue;
+ uint32_t cfg_irq_chann;
uint32_t cfg_suppress_rsp;
uint32_t cfg_nvme_oas;
uint32_t cfg_nvme_embed_cmd;
- uint32_t cfg_nvme_io_channel;
uint32_t cfg_nvmet_mrq_post;
uint32_t cfg_nvmet_mrq;
uint32_t cfg_enable_nvmet;
@@ -852,6 +862,7 @@ struct lpfc_hba {
uint32_t cfg_prot_guard;
uint32_t cfg_hostmem_hgp;
uint32_t cfg_log_verbose;
+ uint32_t cfg_enable_fc4_type;
uint32_t cfg_aer_support;
uint32_t cfg_sriov_nr_virtfn;
uint32_t cfg_request_firmware_upgrade;
@@ -872,15 +883,12 @@ struct lpfc_hba {
uint32_t cfg_ras_fwlog_level;
uint32_t cfg_ras_fwlog_buffsize;
uint32_t cfg_ras_fwlog_func;
- uint32_t cfg_enable_fc4_type;
uint32_t cfg_enable_bbcr; /* Enable BB Credit Recovery */
uint32_t cfg_enable_dpp; /* Enable Direct Packet Push */
- uint32_t cfg_xri_split;
#define LPFC_ENABLE_FCP 1
#define LPFC_ENABLE_NVME 2
#define LPFC_ENABLE_BOTH 3
uint32_t cfg_enable_pbde;
- uint32_t io_channel_irqs; /* number of irqs for io channels */
struct nvmet_fc_target_port *targetport;
lpfc_vpd_t vpd; /* vital product data */
@@ -952,14 +960,6 @@ struct lpfc_hba {
struct timer_list eratt_poll;
uint32_t eratt_poll_interval;
- /*
- * stat counters
- */
- atomic_t fc4ScsiInputRequests;
- atomic_t fc4ScsiOutputRequests;
- atomic_t fc4ScsiControlRequests;
- atomic_t fc4ScsiIoCmpls;
-
uint64_t bg_guard_err_cnt;
uint64_t bg_apptag_err_cnt;
uint64_t bg_reftag_err_cnt;
@@ -970,13 +970,6 @@ struct lpfc_hba {
struct list_head lpfc_scsi_buf_list_get;
struct list_head lpfc_scsi_buf_list_put;
uint32_t total_scsi_bufs;
- spinlock_t nvme_buf_list_get_lock; /* NVME buf alloc list lock */
- spinlock_t nvme_buf_list_put_lock; /* NVME buf free list lock */
- struct list_head lpfc_nvme_buf_list_get;
- struct list_head lpfc_nvme_buf_list_put;
- uint32_t total_nvme_bufs;
- uint32_t get_nvme_bufs;
- uint32_t put_nvme_bufs;
struct list_head lpfc_iocb_list;
uint32_t total_iocbq_bufs;
struct list_head active_rrq_list;
@@ -1033,6 +1026,7 @@ struct lpfc_hba {
#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
struct dentry *hba_debugfs_root;
atomic_t debugfs_vport_count;
+ struct dentry *debug_multixri_pools;
struct dentry *debug_hbqinfo;
struct dentry *debug_dumpHostSlim;
struct dentry *debug_dumpHBASlim;
@@ -1050,6 +1044,10 @@ struct lpfc_hba {
struct dentry *debug_nvmeio_trc;
struct lpfc_debugfs_nvmeio_trc *nvmeio_trc;
+ struct dentry *debug_hdwqinfo;
+#ifdef LPFC_HDWQ_LOCK_STAT
+ struct dentry *debug_lockstat;
+#endif
atomic_t nvmeio_trc_cnt;
uint32_t nvmeio_trc_size;
uint32_t nvmeio_trc_output_idx;
@@ -1090,7 +1088,6 @@ struct lpfc_hba {
uint8_t temp_sensor_support;
/* Fields used for heart beat. */
- unsigned long last_eqdelay_time;
unsigned long last_completion_time;
unsigned long skipped_hb;
struct timer_list hb_tmofunc;
@@ -1164,16 +1161,12 @@ struct lpfc_hba {
uint16_t sfp_warning;
#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
-#define LPFC_CHECK_CPU_CNT 32
- uint32_t cpucheck_rcv_io[LPFC_CHECK_CPU_CNT];
- uint32_t cpucheck_xmt_io[LPFC_CHECK_CPU_CNT];
- uint32_t cpucheck_cmpl_io[LPFC_CHECK_CPU_CNT];
- uint32_t cpucheck_ccmpl_io[LPFC_CHECK_CPU_CNT];
uint16_t cpucheck_on;
#define LPFC_CHECK_OFF 0
#define LPFC_CHECK_NVME_IO 1
#define LPFC_CHECK_NVMET_RCV 2
#define LPFC_CHECK_NVMET_IO 4
+#define LPFC_CHECK_SCSI_IO 8
uint16_t ktime_on;
uint64_t ktime_data_samples;
uint64_t ktime_status_samples;
@@ -1297,3 +1290,23 @@ lpfc_phba_elsring(struct lpfc_hba *phba)
}
return &phba->sli.sli3_ring[LPFC_ELS_RING];
}
+
+/**
+ * lpfc_sli4_mod_hba_eq_delay - update EQ delay
+ * @phba: Pointer to HBA context object.
+ * @q: The Event Queue to update.
+ * @delay: The delay value (in us) to be written.
+ *
+ **/
+static inline void
+lpfc_sli4_mod_hba_eq_delay(struct lpfc_hba *phba, struct lpfc_queue *eq,
+ u32 delay)
+{
+ struct lpfc_register reg_data;
+
+ reg_data.word0 = 0;
+ bf_set(lpfc_sliport_eqdelay_id, &reg_data, eq->queue_id);
+ bf_set(lpfc_sliport_eqdelay_delay, &reg_data, delay);
+ writel(reg_data.word0, phba->sli4_hba.u.if_type2.EQDregaddr);
+ eq->q_mode = delay;
+}
diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c
index 4bae72cbf3f6..ce3e541434dc 100644
--- a/drivers/scsi/lpfc/lpfc_attr.c
+++ b/drivers/scsi/lpfc/lpfc_attr.c
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term *
+ * Copyright (C) 2017-2019 Broadcom. All Rights Reserved. The term *
* “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. *
* Copyright (C) 2004-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
@@ -64,9 +64,6 @@
#define LPFC_MIN_MRQ_POST 512
#define LPFC_MAX_MRQ_POST 2048
-#define LPFC_MAX_NVME_INFO_TMP_LEN 100
-#define LPFC_NVME_INFO_MORE_STR "\nCould be more info...\n"
-
/*
* Write key size should be multiple of 4. If write key is changed
* make sure that library write key is also changed.
@@ -155,7 +152,7 @@ lpfc_nvme_info_show(struct device *dev, struct device_attribute *attr,
struct lpfc_nvme_rport *rport;
struct lpfc_nodelist *ndlp;
struct nvme_fc_remote_port *nrport;
- struct lpfc_nvme_ctrl_stat *cstat;
+ struct lpfc_fc4_ctrl_stat *cstat;
uint64_t data1, data2, data3;
uint64_t totin, totout, tot;
char *statep;
@@ -163,7 +160,7 @@ lpfc_nvme_info_show(struct device *dev, struct device_attribute *attr,
int len = 0;
char tmp[LPFC_MAX_NVME_INFO_TMP_LEN] = {0};
- if (!(phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME)) {
+ if (!(vport->cfg_enable_fc4_type & LPFC_ENABLE_NVME)) {
len = scnprintf(buf, PAGE_SIZE, "NVME Disabled\n");
return len;
}
@@ -334,11 +331,10 @@ lpfc_nvme_info_show(struct device *dev, struct device_attribute *attr,
rcu_read_lock();
scnprintf(tmp, sizeof(tmp),
- "XRI Dist lpfc%d Total %d NVME %d SCSI %d ELS %d\n",
+ "XRI Dist lpfc%d Total %d IO %d ELS %d\n",
phba->brd_no,
phba->sli4_hba.max_cfg_param.max_xri,
- phba->sli4_hba.nvme_xri_max,
- phba->sli4_hba.scsi_xri_max,
+ phba->sli4_hba.io_xri_max,
lpfc_sli4_get_els_iocb_cnt(phba));
if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
goto buffer_done;
@@ -457,13 +453,13 @@ lpfc_nvme_info_show(struct device *dev, struct device_attribute *attr,
totin = 0;
totout = 0;
- for (i = 0; i < phba->cfg_nvme_io_channel; i++) {
- cstat = &lport->cstat[i];
- tot = atomic_read(&cstat->fc4NvmeIoCmpls);
+ for (i = 0; i < phba->cfg_hdw_queue; i++) {
+ cstat = &phba->sli4_hba.hdwq[i].nvme_cstat;
+ tot = cstat->io_cmpls;
totin += tot;
- data1 = atomic_read(&cstat->fc4NvmeInputRequests);
- data2 = atomic_read(&cstat->fc4NvmeOutputRequests);
- data3 = atomic_read(&cstat->fc4NvmeControlRequests);
+ data1 = cstat->input_requests;
+ data2 = cstat->output_requests;
+ data3 = cstat->control_requests;
totout += (data1 + data2 + data3);
}
scnprintf(tmp, sizeof(tmp),
@@ -510,6 +506,57 @@ buffer_done:
}
static ssize_t
+lpfc_scsi_stat_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct lpfc_vport *vport = shost_priv(shost);
+ struct lpfc_hba *phba = vport->phba;
+ int len;
+ struct lpfc_fc4_ctrl_stat *cstat;
+ u64 data1, data2, data3;
+ u64 tot, totin, totout;
+ int i;
+ char tmp[LPFC_MAX_SCSI_INFO_TMP_LEN] = {0};
+
+ if (!(vport->cfg_enable_fc4_type & LPFC_ENABLE_FCP) ||
+ (phba->sli_rev != LPFC_SLI_REV4))
+ return 0;
+
+ scnprintf(buf, PAGE_SIZE, "SCSI HDWQ Statistics\n");
+
+ totin = 0;
+ totout = 0;
+ for (i = 0; i < phba->cfg_hdw_queue; i++) {
+ cstat = &phba->sli4_hba.hdwq[i].scsi_cstat;
+ tot = cstat->io_cmpls;
+ totin += tot;
+ data1 = cstat->input_requests;
+ data2 = cstat->output_requests;
+ data3 = cstat->control_requests;
+ totout += (data1 + data2 + data3);
+
+ scnprintf(tmp, sizeof(tmp), "HDWQ (%d): Rd %016llx Wr %016llx "
+ "IO %016llx ", i, data1, data2, data3);
+ if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
+ goto buffer_done;
+
+ scnprintf(tmp, sizeof(tmp), "Cmpl %016llx OutIO %016llx\n",
+ tot, ((data1 + data2 + data3) - tot));
+ if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
+ goto buffer_done;
+ }
+ scnprintf(tmp, sizeof(tmp), "Total FCP Cmpl %016llx Issue %016llx "
+ "OutIO %016llx\n", totin, totout, totout - totin);
+ strlcat(buf, tmp, PAGE_SIZE);
+
+buffer_done:
+ len = strnlen(buf, PAGE_SIZE);
+
+ return len;
+}
+
+static ssize_t
lpfc_bg_info_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
@@ -2574,6 +2621,7 @@ lpfc_##attr##_store(struct device *dev, struct device_attribute *attr, \
static DEVICE_ATTR(nvme_info, 0444, lpfc_nvme_info_show, NULL);
+static DEVICE_ATTR(scsi_stat, 0444, lpfc_scsi_stat_show, NULL);
static DEVICE_ATTR(bg_info, S_IRUGO, lpfc_bg_info_show, NULL);
static DEVICE_ATTR(bg_guard_err, S_IRUGO, lpfc_bg_guard_err_show, NULL);
static DEVICE_ATTR(bg_apptag_err, S_IRUGO, lpfc_bg_apptag_err_show, NULL);
@@ -3724,29 +3772,13 @@ LPFC_ATTR_R(nvmet_mrq_post,
* lpfc_enable_fc4_type: Defines what FC4 types are supported.
* Supported Values: 1 - register just FCP
* 3 - register both FCP and NVME
- * Supported values are [1,3]. Default value is 1
+ * Supported values are [1,3]. Default value is 3
*/
-LPFC_ATTR_R(enable_fc4_type, LPFC_ENABLE_FCP,
+LPFC_ATTR_R(enable_fc4_type, LPFC_ENABLE_BOTH,
LPFC_ENABLE_FCP, LPFC_ENABLE_BOTH,
"Enable FC4 Protocol support - FCP / NVME");
/*
- * lpfc_xri_split: Defines the division of XRI resources between SCSI and NVME
- * This parameter is only used if:
- * lpfc_enable_fc4_type is 3 - register both FCP and NVME and
- * port is not configured for NVMET.
- *
- * ELS/CT always get 10% of XRIs, up to a maximum of 250
- * The remaining XRIs get split up based on lpfc_xri_split per port:
- *
- * Supported Values are in percentages
- * the xri_split value is the percentage the SCSI port will get. The remaining
- * percentage will go to NVME.
- */
-LPFC_ATTR_R(xri_split, 50, 10, 90,
- "Percentage of FCP XRI resources versus NVME");
-
-/*
# lpfc_log_verbose: Only turn this flag on if you are willing to risk being
# deluged with LOTS of information.
# You can set a bit mask to record specific types of verbose messages:
@@ -4903,6 +4935,8 @@ lpfc_fcp_imax_store(struct device *dev, struct device_attribute *attr,
struct Scsi_Host *shost = class_to_shost(dev);
struct lpfc_vport *vport = (struct lpfc_vport *)shost->hostdata;
struct lpfc_hba *phba = vport->phba;
+ struct lpfc_eq_intr_info *eqi;
+ uint32_t usdelay;
int val = 0, i;
/* fcp_imax is only valid for SLI4 */
@@ -4923,12 +4957,27 @@ lpfc_fcp_imax_store(struct device *dev, struct device_attribute *attr,
if (val && (val < LPFC_MIN_IMAX || val > LPFC_MAX_IMAX))
return -EINVAL;
+ phba->cfg_auto_imax = (val) ? 0 : 1;
+ if (phba->cfg_fcp_imax && !val) {
+ queue_delayed_work(phba->wq, &phba->eq_delay_work,
+ msecs_to_jiffies(LPFC_EQ_DELAY_MSECS));
+
+ for_each_present_cpu(i) {
+ eqi = per_cpu_ptr(phba->sli4_hba.eq_info, i);
+ eqi->icnt = 0;
+ }
+ }
+
phba->cfg_fcp_imax = (uint32_t)val;
- phba->initial_imax = phba->cfg_fcp_imax;
- for (i = 0; i < phba->io_channel_irqs; i += LPFC_MAX_EQ_DELAY_EQID_CNT)
+ if (phba->cfg_fcp_imax)
+ usdelay = LPFC_SEC_TO_USEC / phba->cfg_fcp_imax;
+ else
+ usdelay = 0;
+
+ for (i = 0; i < phba->cfg_irq_chann; i += LPFC_MAX_EQ_DELAY_EQID_CNT)
lpfc_modify_hba_eq_delay(phba, i, LPFC_MAX_EQ_DELAY_EQID_CNT,
- val);
+ usdelay);
return strlen(buf);
}
@@ -4982,15 +5031,119 @@ lpfc_fcp_imax_init(struct lpfc_hba *phba, int val)
static DEVICE_ATTR_RW(lpfc_fcp_imax);
+/**
+ * lpfc_cq_max_proc_limit_store
+ *
+ * @dev: class device that is converted into a Scsi_host.
+ * @attr: device attribute, not used.
+ * @buf: string with the cq max processing limit of cqes
+ * @count: unused variable.
+ *
+ * Description:
+ * If val is in a valid range, then set value on each cq
+ *
+ * Returns:
+ * The length of the buf: if successful
+ * -ERANGE: if val is not in the valid range
+ * -EINVAL: if bad value format or intended mode is not supported.
+ **/
+static ssize_t
+lpfc_cq_max_proc_limit_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct lpfc_vport *vport = (struct lpfc_vport *)shost->hostdata;
+ struct lpfc_hba *phba = vport->phba;
+ struct lpfc_queue *eq, *cq;
+ unsigned long val;
+ int i;
+
+ /* cq_max_proc_limit is only valid for SLI4 */
+ if (phba->sli_rev != LPFC_SLI_REV4)
+ return -EINVAL;
+
+ /* Sanity check on user data */
+ if (!isdigit(buf[0]))
+ return -EINVAL;
+ if (kstrtoul(buf, 0, &val))
+ return -EINVAL;
+
+ if (val < LPFC_CQ_MIN_PROC_LIMIT || val > LPFC_CQ_MAX_PROC_LIMIT)
+ return -ERANGE;
+
+ phba->cfg_cq_max_proc_limit = (uint32_t)val;
+
+ /* set the values on the cq's */
+ for (i = 0; i < phba->cfg_irq_chann; i++) {
+ eq = phba->sli4_hba.hdwq[i].hba_eq;
+ if (!eq)
+ continue;
+
+ list_for_each_entry(cq, &eq->child_list, list)
+ cq->max_proc_limit = min(phba->cfg_cq_max_proc_limit,
+ cq->entry_count);
+ }
+
+ return strlen(buf);
+}
+
/*
- * lpfc_auto_imax: Controls Auto-interrupt coalescing values support.
- * 0 No auto_imax support
- * 1 auto imax on
- * Auto imax will change the value of fcp_imax on a per EQ basis, using
- * the EQ Delay Multiplier, depending on the activity for that EQ.
- * Value range [0,1]. Default value is 1.
+ * lpfc_cq_max_proc_limit: The maximum number CQE entries processed in an
+ * itteration of CQ processing.
*/
-LPFC_ATTR_RW(auto_imax, 1, 0, 1, "Enable Auto imax");
+static int lpfc_cq_max_proc_limit = LPFC_CQ_DEF_MAX_PROC_LIMIT;
+module_param(lpfc_cq_max_proc_limit, int, 0644);
+MODULE_PARM_DESC(lpfc_cq_max_proc_limit,
+ "Set the maximum number CQEs processed in an iteration of "
+ "CQ processing");
+lpfc_param_show(cq_max_proc_limit)
+
+/*
+ * lpfc_cq_poll_threshold: Set the threshold of CQE completions in a
+ * single handler call which should request a polled completion rather
+ * than re-enabling interrupts.
+ */
+LPFC_ATTR_RW(cq_poll_threshold, LPFC_CQ_DEF_THRESHOLD_TO_POLL,
+ LPFC_CQ_MIN_THRESHOLD_TO_POLL,
+ LPFC_CQ_MAX_THRESHOLD_TO_POLL,
+ "CQE Processing Threshold to enable Polling");
+
+/**
+ * lpfc_cq_max_proc_limit_init - Set the initial cq max_proc_limit
+ * @phba: lpfc_hba pointer.
+ * @val: entry limit
+ *
+ * Description:
+ * If val is in a valid range, then initialize the adapter's maximum
+ * value.
+ *
+ * Returns:
+ * Always returns 0 for success, even if value not always set to
+ * requested value. If value out of range or not supported, will fall
+ * back to default.
+ **/
+static int
+lpfc_cq_max_proc_limit_init(struct lpfc_hba *phba, int val)
+{
+ phba->cfg_cq_max_proc_limit = LPFC_CQ_DEF_MAX_PROC_LIMIT;
+
+ if (phba->sli_rev != LPFC_SLI_REV4)
+ return 0;
+
+ if (val >= LPFC_CQ_MIN_PROC_LIMIT && val <= LPFC_CQ_MAX_PROC_LIMIT) {
+ phba->cfg_cq_max_proc_limit = val;
+ return 0;
+ }
+
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0371 "LPFC_DRIVER_NAME"_cq_max_proc_limit: "
+ "%d out of range, using default\n",
+ phba->cfg_cq_max_proc_limit);
+
+ return 0;
+}
+
+static DEVICE_ATTR_RW(lpfc_cq_max_proc_limit);
/**
* lpfc_state_show - Display current driver CPU affinity
@@ -5023,50 +5176,70 @@ lpfc_fcp_cpu_map_show(struct device *dev, struct device_attribute *attr,
case 1:
len += snprintf(buf + len, PAGE_SIZE-len,
"fcp_cpu_map: HBA centric mapping (%d): "
- "%d online CPUs\n",
- phba->cfg_fcp_cpu_map,
- phba->sli4_hba.num_online_cpu);
- break;
- case 2:
- len += snprintf(buf + len, PAGE_SIZE-len,
- "fcp_cpu_map: Driver centric mapping (%d): "
- "%d online CPUs\n",
- phba->cfg_fcp_cpu_map,
- phba->sli4_hba.num_online_cpu);
+ "%d of %d CPUs online from %d possible CPUs\n",
+ phba->cfg_fcp_cpu_map, num_online_cpus(),
+ num_present_cpus(),
+ phba->sli4_hba.num_possible_cpu);
break;
}
- while (phba->sli4_hba.curr_disp_cpu < phba->sli4_hba.num_present_cpu) {
+ while (phba->sli4_hba.curr_disp_cpu <
+ phba->sli4_hba.num_possible_cpu) {
cpup = &phba->sli4_hba.cpu_map[phba->sli4_hba.curr_disp_cpu];
- /* margin should fit in this and the truncated message */
- if (cpup->irq == LPFC_VECTOR_MAP_EMPTY)
- len += snprintf(buf + len, PAGE_SIZE-len,
- "CPU %02d io_chan %02d "
- "physid %d coreid %d\n",
+ if (!cpu_present(phba->sli4_hba.curr_disp_cpu))
+ len += snprintf(buf + len, PAGE_SIZE - len,
+ "CPU %02d not present\n",
+ phba->sli4_hba.curr_disp_cpu);
+ else if (cpup->irq == LPFC_VECTOR_MAP_EMPTY) {
+ if (cpup->hdwq == LPFC_VECTOR_MAP_EMPTY)
+ len += snprintf(
+ buf + len, PAGE_SIZE - len,
+ "CPU %02d hdwq None "
+ "physid %d coreid %d ht %d\n",
phba->sli4_hba.curr_disp_cpu,
- cpup->channel_id, cpup->phys_id,
- cpup->core_id);
- else
- len += snprintf(buf + len, PAGE_SIZE-len,
- "CPU %02d io_chan %02d "
- "physid %d coreid %d IRQ %d\n",
+ cpup->phys_id,
+ cpup->core_id, cpup->hyper);
+ else
+ len += snprintf(
+ buf + len, PAGE_SIZE - len,
+ "CPU %02d EQ %04d hdwq %04d "
+ "physid %d coreid %d ht %d\n",
+ phba->sli4_hba.curr_disp_cpu,
+ cpup->eq, cpup->hdwq, cpup->phys_id,
+ cpup->core_id, cpup->hyper);
+ } else {
+ if (cpup->hdwq == LPFC_VECTOR_MAP_EMPTY)
+ len += snprintf(
+ buf + len, PAGE_SIZE - len,
+ "CPU %02d hdwq None "
+ "physid %d coreid %d ht %d IRQ %d\n",
+ phba->sli4_hba.curr_disp_cpu,
+ cpup->phys_id,
+ cpup->core_id, cpup->hyper, cpup->irq);
+ else
+ len += snprintf(
+ buf + len, PAGE_SIZE - len,
+ "CPU %02d EQ %04d hdwq %04d "
+ "physid %d coreid %d ht %d IRQ %d\n",
phba->sli4_hba.curr_disp_cpu,
- cpup->channel_id, cpup->phys_id,
- cpup->core_id, cpup->irq);
+ cpup->eq, cpup->hdwq, cpup->phys_id,
+ cpup->core_id, cpup->hyper, cpup->irq);
+ }
phba->sli4_hba.curr_disp_cpu++;
/* display max number of CPUs keeping some margin */
if (phba->sli4_hba.curr_disp_cpu <
- phba->sli4_hba.num_present_cpu &&
+ phba->sli4_hba.num_possible_cpu &&
(len >= (PAGE_SIZE - 64))) {
- len += snprintf(buf + len, PAGE_SIZE-len, "more...\n");
+ len += snprintf(buf + len,
+ PAGE_SIZE - len, "more...\n");
break;
}
}
- if (phba->sli4_hba.curr_disp_cpu == phba->sli4_hba.num_present_cpu)
+ if (phba->sli4_hba.curr_disp_cpu == phba->sli4_hba.num_possible_cpu)
phba->sli4_hba.curr_disp_cpu = 0;
return len;
@@ -5094,14 +5267,13 @@ lpfc_fcp_cpu_map_store(struct device *dev, struct device_attribute *attr,
# lpfc_fcp_cpu_map: Defines how to map CPUs to IRQ vectors
# for the HBA.
#
-# Value range is [0 to 2]. Default value is LPFC_DRIVER_CPU_MAP (2).
+# Value range is [0 to 1]. Default value is LPFC_HBA_CPU_MAP (1).
# 0 - Do not affinitze IRQ vectors
# 1 - Affintize HBA vectors with respect to each HBA
# (start with CPU0 for each HBA)
-# 2 - Affintize HBA vectors with respect to the entire driver
-# (round robin thru all CPUs across all HBAs)
+# This also defines how Hardware Queues are mapped to specific CPUs.
*/
-static int lpfc_fcp_cpu_map = LPFC_DRIVER_CPU_MAP;
+static int lpfc_fcp_cpu_map = LPFC_HBA_CPU_MAP;
module_param(lpfc_fcp_cpu_map, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(lpfc_fcp_cpu_map,
"Defines how to map CPUs to IRQ vectors per HBA");
@@ -5135,7 +5307,7 @@ lpfc_fcp_cpu_map_init(struct lpfc_hba *phba, int val)
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
"3326 lpfc_fcp_cpu_map: %d out of range, using "
"default\n", val);
- phba->cfg_fcp_cpu_map = LPFC_DRIVER_CPU_MAP;
+ phba->cfg_fcp_cpu_map = LPFC_HBA_CPU_MAP;
return 0;
}
@@ -5235,13 +5407,20 @@ static DEVICE_ATTR_RW(lpfc_max_scsicmpl_time);
LPFC_ATTR_R(ack0, 0, 0, 1, "Enable ACK0 support");
/*
+# lpfc_xri_rebalancing: enable or disable XRI rebalancing feature
+# range is [0,1]. Default value is 1.
+*/
+LPFC_ATTR_R(xri_rebalancing, 1, 0, 1, "Enable/Disable XRI rebalancing");
+
+/*
* lpfc_io_sched: Determine scheduling algrithmn for issuing FCP cmds
* range is [0,1]. Default value is 0.
- * For [0], FCP commands are issued to Work Queues ina round robin fashion.
+ * For [0], FCP commands are issued to Work Queues based on upper layer
+ * hardware queue index.
* For [1], FCP commands are issued to a Work Queue associated with the
* current CPU.
*
- * LPFC_FCP_SCHED_ROUND_ROBIN == 0
+ * LPFC_FCP_SCHED_BY_HDWQ == 0
* LPFC_FCP_SCHED_BY_CPU == 1
*
* The driver dynamically sets this to 1 (BY_CPU) if it's able to set up cpu
@@ -5249,11 +5428,11 @@ LPFC_ATTR_R(ack0, 0, 0, 1, "Enable ACK0 support");
* CPU. Otherwise, the default 0 (Round Robin) scheduling of FCP/NVME I/Os
* through WQs will be used.
*/
-LPFC_ATTR_RW(fcp_io_sched, LPFC_FCP_SCHED_ROUND_ROBIN,
- LPFC_FCP_SCHED_ROUND_ROBIN,
+LPFC_ATTR_RW(fcp_io_sched, LPFC_FCP_SCHED_BY_CPU,
+ LPFC_FCP_SCHED_BY_HDWQ,
LPFC_FCP_SCHED_BY_CPU,
"Determine scheduling algorithm for "
- "issuing commands [0] - Round Robin, [1] - Current CPU");
+ "issuing commands [0] - Hardware Queue, [1] - Current CPU");
/*
* lpfc_ns_query: Determine algrithmn for NameServer queries after RSCN
@@ -5415,41 +5594,39 @@ LPFC_ATTR_RW(nvme_embed_cmd, 1, 0, 2,
"Embed NVME Command in WQE");
/*
- * lpfc_fcp_io_channel: Set the number of FCP IO channels the driver
- * will advertise it supports to the SCSI layer. This also will map to
- * the number of WQs the driver will create.
+ * lpfc_hdw_queue: Set the number of Hardware Queues the driver
+ * will advertise it supports to the NVME and SCSI layers. This also
+ * will map to the number of CQ/WQ pairs the driver will create.
*
- * 0 = Configure the number of io channels to the number of active CPUs.
- * 1,32 = Manually specify how many io channels to use.
+ * The NVME Layer will try to create this many, plus 1 administrative
+ * hardware queue. The administrative queue will always map to WQ 0
+ * A hardware IO queue maps (qidx) to a specific driver CQ/WQ.
*
- * Value range is [0,32]. Default value is 4.
+ * 0 = Configure the number of hdw queues to the number of active CPUs.
+ * 1,128 = Manually specify how many hdw queues to use.
+ *
+ * Value range is [0,128]. Default value is 0.
*/
-LPFC_ATTR_R(fcp_io_channel,
- LPFC_FCP_IO_CHAN_DEF,
- LPFC_HBA_IO_CHAN_MIN, LPFC_HBA_IO_CHAN_MAX,
- "Set the number of FCP I/O channels");
+LPFC_ATTR_R(hdw_queue,
+ LPFC_HBA_HDWQ_DEF,
+ LPFC_HBA_HDWQ_MIN, LPFC_HBA_HDWQ_MAX,
+ "Set the number of I/O Hardware Queues");
/*
- * lpfc_nvme_io_channel: Set the number of IO hardware queues the driver
- * will advertise it supports to the NVME layer. This also will map to
- * the number of WQs the driver will create.
- *
- * This module parameter is valid when lpfc_enable_fc4_type is set
- * to support NVME.
- *
- * The NVME Layer will try to create this many, plus 1 administrative
- * hardware queue. The administrative queue will always map to WQ 0
- * A hardware IO queue maps (qidx) to a specific driver WQ.
+ * lpfc_irq_chann: Set the number of IRQ vectors that are available
+ * for Hardware Queues to utilize. This also will map to the number
+ * of EQ / MSI-X vectors the driver will create. This should never be
+ * more than the number of Hardware Queues
*
- * 0 = Configure the number of io channels to the number of active CPUs.
- * 1,32 = Manually specify how many io channels to use.
+ * 0 = Configure number of IRQ Channels to the number of active CPUs.
+ * 1,128 = Manually specify how many IRQ Channels to use.
*
- * Value range is [0,32]. Default value is 0.
+ * Value range is [0,128]. Default value is 0.
*/
-LPFC_ATTR_R(nvme_io_channel,
- LPFC_NVME_IO_CHAN_DEF,
- LPFC_HBA_IO_CHAN_MIN, LPFC_HBA_IO_CHAN_MAX,
- "Set the number of NVME I/O channels");
+LPFC_ATTR_R(irq_chann,
+ LPFC_HBA_HDWQ_DEF,
+ LPFC_HBA_HDWQ_MIN, LPFC_HBA_HDWQ_MAX,
+ "Set the number of I/O IRQ Channels");
/*
# lpfc_enable_hba_reset: Allow or prevent HBA resets to the hardware.
@@ -5492,16 +5669,6 @@ LPFC_ATTR_RW(XLanePriority, 0, 0x0, 0x7f, "CS_CTL for Express Lane Feature.");
LPFC_ATTR_R(enable_bg, 0, 0, 1, "Enable BlockGuard Support");
/*
-# lpfc_fcp_look_ahead: Look ahead for completions in FCP start routine
-# 0 = disabled (default)
-# 1 = enabled
-# Value range is [0,1]. Default value is 0.
-#
-# This feature in under investigation and may be supported in the future.
-*/
-unsigned int lpfc_fcp_look_ahead = LPFC_LOOK_AHEAD_OFF;
-
-/*
# lpfc_prot_mask: i
# - Bit mask of host protection capabilities used to register with the
# SCSI mid-layer
@@ -5677,6 +5844,7 @@ LPFC_ATTR_RW(enable_dpp, 1, 0, 1, "Enable Direct Packet Push");
struct device_attribute *lpfc_hba_attrs[] = {
&dev_attr_nvme_info,
+ &dev_attr_scsi_stat,
&dev_attr_bg_info,
&dev_attr_bg_guard_err,
&dev_attr_bg_apptag_err,
@@ -5704,11 +5872,11 @@ struct device_attribute *lpfc_hba_attrs[] = {
&dev_attr_lpfc_nodev_tmo,
&dev_attr_lpfc_devloss_tmo,
&dev_attr_lpfc_enable_fc4_type,
- &dev_attr_lpfc_xri_split,
&dev_attr_lpfc_fcp_class,
&dev_attr_lpfc_use_adisc,
&dev_attr_lpfc_first_burst_size,
&dev_attr_lpfc_ack0,
+ &dev_attr_lpfc_xri_rebalancing,
&dev_attr_lpfc_topology,
&dev_attr_lpfc_scan_down,
&dev_attr_lpfc_link_speed,
@@ -5742,12 +5910,13 @@ struct device_attribute *lpfc_hba_attrs[] = {
&dev_attr_lpfc_use_msi,
&dev_attr_lpfc_nvme_oas,
&dev_attr_lpfc_nvme_embed_cmd,
- &dev_attr_lpfc_auto_imax,
&dev_attr_lpfc_fcp_imax,
+ &dev_attr_lpfc_cq_poll_threshold,
+ &dev_attr_lpfc_cq_max_proc_limit,
&dev_attr_lpfc_fcp_cpu_map,
- &dev_attr_lpfc_fcp_io_channel,
+ &dev_attr_lpfc_hdw_queue,
+ &dev_attr_lpfc_irq_chann,
&dev_attr_lpfc_suppress_rsp,
- &dev_attr_lpfc_nvme_io_channel,
&dev_attr_lpfc_nvmet_mrq,
&dev_attr_lpfc_nvmet_mrq_post,
&dev_attr_lpfc_nvme_enable_fb,
@@ -6775,6 +6944,7 @@ lpfc_get_cfgparam(struct lpfc_hba *phba)
lpfc_multi_ring_rctl_init(phba, lpfc_multi_ring_rctl);
lpfc_multi_ring_type_init(phba, lpfc_multi_ring_type);
lpfc_ack0_init(phba, lpfc_ack0);
+ lpfc_xri_rebalancing_init(phba, lpfc_xri_rebalancing);
lpfc_topology_init(phba, lpfc_topology);
lpfc_link_speed_init(phba, lpfc_link_speed);
lpfc_poll_tmo_init(phba, lpfc_poll_tmo);
@@ -6787,8 +6957,9 @@ lpfc_get_cfgparam(struct lpfc_hba *phba)
lpfc_use_msi_init(phba, lpfc_use_msi);
lpfc_nvme_oas_init(phba, lpfc_nvme_oas);
lpfc_nvme_embed_cmd_init(phba, lpfc_nvme_embed_cmd);
- lpfc_auto_imax_init(phba, lpfc_auto_imax);
lpfc_fcp_imax_init(phba, lpfc_fcp_imax);
+ lpfc_cq_poll_threshold_init(phba, lpfc_cq_poll_threshold);
+ lpfc_cq_max_proc_limit_init(phba, lpfc_cq_max_proc_limit);
lpfc_fcp_cpu_map_init(phba, lpfc_fcp_cpu_map);
lpfc_enable_hba_reset_init(phba, lpfc_enable_hba_reset);
lpfc_enable_hba_heartbeat_init(phba, lpfc_enable_hba_heartbeat);
@@ -6824,8 +6995,8 @@ lpfc_get_cfgparam(struct lpfc_hba *phba)
/* Initialize first burst. Target vs Initiator are different. */
lpfc_nvme_enable_fb_init(phba, lpfc_nvme_enable_fb);
lpfc_nvmet_fb_size_init(phba, lpfc_nvmet_fb_size);
- lpfc_fcp_io_channel_init(phba, lpfc_fcp_io_channel);
- lpfc_nvme_io_channel_init(phba, lpfc_nvme_io_channel);
+ lpfc_hdw_queue_init(phba, lpfc_hdw_queue);
+ lpfc_irq_chann_init(phba, lpfc_irq_chann);
lpfc_enable_bbcr_init(phba, lpfc_enable_bbcr);
lpfc_enable_dpp_init(phba, lpfc_enable_dpp);
@@ -6834,38 +7005,27 @@ lpfc_get_cfgparam(struct lpfc_hba *phba)
phba->nvmet_support = 0;
phba->cfg_enable_fc4_type = LPFC_ENABLE_FCP;
phba->cfg_enable_bbcr = 0;
+ phba->cfg_xri_rebalancing = 0;
} else {
/* We MUST have FCP support */
if (!(phba->cfg_enable_fc4_type & LPFC_ENABLE_FCP))
phba->cfg_enable_fc4_type |= LPFC_ENABLE_FCP;
}
- if (phba->cfg_auto_imax && !phba->cfg_fcp_imax)
- phba->cfg_auto_imax = 0;
- phba->initial_imax = phba->cfg_fcp_imax;
+ phba->cfg_auto_imax = (phba->cfg_fcp_imax) ? 0 : 1;
phba->cfg_enable_pbde = 0;
/* A value of 0 means use the number of CPUs found in the system */
- if (phba->cfg_fcp_io_channel == 0)
- phba->cfg_fcp_io_channel = phba->sli4_hba.num_present_cpu;
- if (phba->cfg_nvme_io_channel == 0)
- phba->cfg_nvme_io_channel = phba->sli4_hba.num_present_cpu;
-
- if (phba->cfg_enable_fc4_type == LPFC_ENABLE_NVME)
- phba->cfg_fcp_io_channel = 0;
-
- if (phba->cfg_enable_fc4_type == LPFC_ENABLE_FCP)
- phba->cfg_nvme_io_channel = 0;
-
- if (phba->cfg_fcp_io_channel > phba->cfg_nvme_io_channel)
- phba->io_channel_irqs = phba->cfg_fcp_io_channel;
- else
- phba->io_channel_irqs = phba->cfg_nvme_io_channel;
+ if (phba->cfg_hdw_queue == 0)
+ phba->cfg_hdw_queue = phba->sli4_hba.num_present_cpu;
+ if (phba->cfg_irq_chann == 0)
+ phba->cfg_irq_chann = phba->sli4_hba.num_present_cpu;
+ if (phba->cfg_irq_chann > phba->cfg_hdw_queue)
+ phba->cfg_irq_chann = phba->cfg_hdw_queue;
phba->cfg_soft_wwnn = 0L;
phba->cfg_soft_wwpn = 0L;
- lpfc_xri_split_init(phba, lpfc_xri_split);
lpfc_sg_seg_cnt_init(phba, lpfc_sg_seg_cnt);
lpfc_hba_queue_depth_init(phba, lpfc_hba_queue_depth);
lpfc_hba_log_verbose_init(phba, lpfc_log_verbose);
@@ -6903,16 +7063,16 @@ lpfc_get_cfgparam(struct lpfc_hba *phba)
void
lpfc_nvme_mod_param_dep(struct lpfc_hba *phba)
{
- if (phba->cfg_nvme_io_channel > phba->sli4_hba.num_present_cpu)
- phba->cfg_nvme_io_channel = phba->sli4_hba.num_present_cpu;
-
- if (phba->cfg_fcp_io_channel > phba->sli4_hba.num_present_cpu)
- phba->cfg_fcp_io_channel = phba->sli4_hba.num_present_cpu;
+ if (phba->cfg_hdw_queue > phba->sli4_hba.num_present_cpu)
+ phba->cfg_hdw_queue = phba->sli4_hba.num_present_cpu;
+ if (phba->cfg_irq_chann > phba->sli4_hba.num_present_cpu)
+ phba->cfg_irq_chann = phba->sli4_hba.num_present_cpu;
+ if (phba->cfg_irq_chann > phba->cfg_hdw_queue)
+ phba->cfg_irq_chann = phba->cfg_hdw_queue;
if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME &&
phba->nvmet_support) {
phba->cfg_enable_fc4_type &= ~LPFC_ENABLE_FCP;
- phba->cfg_fcp_io_channel = 0;
lpfc_printf_log(phba, KERN_INFO, LOG_NVME_DISC,
"6013 %s x%x fb_size x%x, fb_max x%x\n",
@@ -6929,11 +7089,11 @@ lpfc_nvme_mod_param_dep(struct lpfc_hba *phba)
}
if (!phba->cfg_nvmet_mrq)
- phba->cfg_nvmet_mrq = phba->cfg_nvme_io_channel;
+ phba->cfg_nvmet_mrq = phba->cfg_irq_chann;
/* Adjust lpfc_nvmet_mrq to avoid running out of WQE slots */
- if (phba->cfg_nvmet_mrq > phba->cfg_nvme_io_channel) {
- phba->cfg_nvmet_mrq = phba->cfg_nvme_io_channel;
+ if (phba->cfg_nvmet_mrq > phba->cfg_irq_chann) {
+ phba->cfg_nvmet_mrq = phba->cfg_irq_chann;
lpfc_printf_log(phba, KERN_ERR, LOG_NVME_DISC,
"6018 Adjust lpfc_nvmet_mrq to %d\n",
phba->cfg_nvmet_mrq);
@@ -6947,11 +7107,6 @@ lpfc_nvme_mod_param_dep(struct lpfc_hba *phba)
phba->cfg_nvmet_mrq = LPFC_NVMET_MRQ_OFF;
phba->cfg_nvmet_fb_size = 0;
}
-
- if (phba->cfg_fcp_io_channel > phba->cfg_nvme_io_channel)
- phba->io_channel_irqs = phba->cfg_fcp_io_channel;
- else
- phba->io_channel_irqs = phba->cfg_nvme_io_channel;
}
/**
diff --git a/drivers/scsi/lpfc/lpfc_bsg.c b/drivers/scsi/lpfc/lpfc_bsg.c
index 2dc564e59430..f2494d3b365c 100644
--- a/drivers/scsi/lpfc/lpfc_bsg.c
+++ b/drivers/scsi/lpfc/lpfc_bsg.c
@@ -2947,7 +2947,7 @@ static int lpfcdiag_loop_post_rxbufs(struct lpfc_hba *phba, uint16_t rxxri,
cmd->un.cont64[i].addrLow = putPaddrLow(mp[i]->phys);
cmd->un.cont64[i].tus.f.bdeSize =
((struct lpfc_dmabufext *)mp[i])->size;
- cmd->ulpBdeCount = ++i;
+ cmd->ulpBdeCount = ++i;
if ((--num_bde > 0) && (i < 2))
continue;
@@ -4682,7 +4682,7 @@ lpfc_bsg_issue_mbox(struct lpfc_hba *phba, struct bsg_job *job,
* Don't allow mailbox commands to be sent when blocked or when in
* the middle of discovery
*/
- if (phba->sli.sli_flag & LPFC_BLOCK_MGMT_IO) {
+ if (phba->sli.sli_flag & LPFC_BLOCK_MGMT_IO) {
rc = -EAGAIN;
goto job_done;
}
diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h
index 39f3fa988732..e0b14d791b8c 100644
--- a/drivers/scsi/lpfc/lpfc_crtn.h
+++ b/drivers/scsi/lpfc/lpfc_crtn.h
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term *
+ * Copyright (C) 2017-2019 Broadcom. All Rights Reserved. The term *
* “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. *
* Copyright (C) 2004-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
@@ -199,11 +199,6 @@ void lpfc_reset_hba(struct lpfc_hba *);
int lpfc_emptyq_wait(struct lpfc_hba *phba, struct list_head *hd,
spinlock_t *slock);
-int lpfc_fof_queue_create(struct lpfc_hba *);
-int lpfc_fof_queue_setup(struct lpfc_hba *);
-int lpfc_fof_queue_destroy(struct lpfc_hba *);
-irqreturn_t lpfc_sli4_fof_intr_handler(int, void *);
-
int lpfc_sli_setup(struct lpfc_hba *);
int lpfc_sli4_setup(struct lpfc_hba *phba);
void lpfc_sli_queue_init(struct lpfc_hba *phba);
@@ -320,8 +315,8 @@ void lpfc_sli_def_mbox_cmpl(struct lpfc_hba *, LPFC_MBOXQ_t *);
void lpfc_sli4_unreg_rpi_cmpl_clr(struct lpfc_hba *, LPFC_MBOXQ_t *);
int lpfc_sli_issue_iocb(struct lpfc_hba *, uint32_t,
struct lpfc_iocbq *, uint32_t);
-int lpfc_sli4_issue_wqe(struct lpfc_hba *phba, uint32_t rnum,
- struct lpfc_iocbq *iocbq);
+int lpfc_sli4_issue_wqe(struct lpfc_hba *phba, struct lpfc_sli4_hdw_queue *qp,
+ struct lpfc_iocbq *pwqe);
struct lpfc_sglq *__lpfc_clear_active_sglq(struct lpfc_hba *phba, uint16_t xri);
struct lpfc_sglq *__lpfc_sli_get_nvmet_sglq(struct lpfc_hba *phba,
struct lpfc_iocbq *piocbq);
@@ -445,7 +440,6 @@ extern spinlock_t _dump_buf_lock;
extern int _dump_buf_done;
extern spinlock_t pgcnt_lock;
extern unsigned int pgcnt;
-extern unsigned int lpfc_fcp_look_ahead;
/* Interface exported by fabric iocb scheduler */
void lpfc_fabric_abort_nport(struct lpfc_nodelist *);
@@ -520,8 +514,13 @@ int lpfc_sli4_read_config(struct lpfc_hba *);
void lpfc_sli4_node_prep(struct lpfc_hba *);
int lpfc_sli4_els_sgl_update(struct lpfc_hba *phba);
int lpfc_sli4_nvmet_sgl_update(struct lpfc_hba *phba);
-int lpfc_sli4_scsi_sgl_update(struct lpfc_hba *phba);
-int lpfc_sli4_nvme_sgl_update(struct lpfc_hba *phba);
+int lpfc_io_buf_flush(struct lpfc_hba *phba, struct list_head *sglist);
+int lpfc_io_buf_replenish(struct lpfc_hba *phba, struct list_head *cbuf);
+int lpfc_sli4_io_sgl_update(struct lpfc_hba *phba);
+int lpfc_sli4_post_io_sgl_list(struct lpfc_hba *phba,
+ struct list_head *blist, int xricnt);
+int lpfc_new_io_buf(struct lpfc_hba *phba, int num_to_alloc);
+void lpfc_io_free(struct lpfc_hba *phba);
void lpfc_free_sgl_list(struct lpfc_hba *, struct list_head *);
uint32_t lpfc_sli_port_speed_get(struct lpfc_hba *);
int lpfc_sli4_request_firmware_update(struct lpfc_hba *, uint8_t);
@@ -574,6 +573,21 @@ void lpfc_nvme_mod_param_dep(struct lpfc_hba *phba);
void lpfc_nvme_abort_fcreq_cmpl(struct lpfc_hba *phba,
struct lpfc_iocbq *cmdiocb,
struct lpfc_wcqe_complete *abts_cmpl);
+void lpfc_create_multixri_pools(struct lpfc_hba *phba);
+void lpfc_create_destroy_pools(struct lpfc_hba *phba);
+void lpfc_move_xri_pvt_to_pbl(struct lpfc_hba *phba, u32 hwqid);
+void lpfc_move_xri_pbl_to_pvt(struct lpfc_hba *phba, u32 hwqid, u32 cnt);
+void lpfc_adjust_high_watermark(struct lpfc_hba *phba, u32 hwqid);
+void lpfc_keep_pvt_pool_above_lowwm(struct lpfc_hba *phba, u32 hwqid);
+void lpfc_adjust_pvt_pool_count(struct lpfc_hba *phba, u32 hwqid);
+#ifdef LPFC_MXP_STAT
+void lpfc_snapshot_mxp(struct lpfc_hba *, u32);
+#endif
+struct lpfc_io_buf *lpfc_get_io_buf(struct lpfc_hba *phba,
+ struct lpfc_nodelist *ndlp, u32 hwqid,
+ int);
+void lpfc_release_io_buf(struct lpfc_hba *phba, struct lpfc_io_buf *ncmd,
+ struct lpfc_sli4_hdw_queue *qp);
void lpfc_nvme_cmd_template(void);
void lpfc_nvmet_cmd_template(void);
extern int lpfc_enable_nvmet_cnt;
diff --git a/drivers/scsi/lpfc/lpfc_ct.c b/drivers/scsi/lpfc/lpfc_ct.c
index 552da8bf43e4..7290573110fe 100644
--- a/drivers/scsi/lpfc/lpfc_ct.c
+++ b/drivers/scsi/lpfc/lpfc_ct.c
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term *
+ * Copyright (C) 2017-2019 Broadcom. All Rights Reserved. The term *
* “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. *
* Copyright (C) 2004-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
@@ -1656,16 +1656,16 @@ lpfc_ns_cmd(struct lpfc_vport *vport, int cmdcode,
CtReq->un.rft.PortId = cpu_to_be32(vport->fc_myDID);
/* Register FC4 FCP type if enabled. */
- if ((phba->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) ||
- (phba->cfg_enable_fc4_type == LPFC_ENABLE_FCP))
+ if (vport->cfg_enable_fc4_type == LPFC_ENABLE_BOTH ||
+ vport->cfg_enable_fc4_type == LPFC_ENABLE_FCP)
CtReq->un.rft.fcpReg = 1;
/* Register NVME type if enabled. Defined LE and swapped.
* rsvd[0] is used as word1 because of the hard-coded
* word0 usage in the ct_request data structure.
*/
- if ((phba->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) ||
- (phba->cfg_enable_fc4_type == LPFC_ENABLE_NVME))
+ if (vport->cfg_enable_fc4_type == LPFC_ENABLE_BOTH ||
+ vport->cfg_enable_fc4_type == LPFC_ENABLE_NVME)
CtReq->un.rft.rsvd[0] =
cpu_to_be32(LPFC_FC4_TYPE_BITMASK);
@@ -1732,8 +1732,8 @@ lpfc_ns_cmd(struct lpfc_vport *vport, int cmdcode,
* caller can specify NVME (type x28) as well. But only
* these that FC4 type is supported.
*/
- if (((phba->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) ||
- (phba->cfg_enable_fc4_type == LPFC_ENABLE_NVME)) &&
+ if (((vport->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) ||
+ (vport->cfg_enable_fc4_type == LPFC_ENABLE_NVME)) &&
(context == FC_TYPE_NVME)) {
if ((vport == phba->pport) && phba->nvmet_support) {
CtReq->un.rff.fbits = (FC4_FEATURE_TARGET |
@@ -1744,8 +1744,8 @@ lpfc_ns_cmd(struct lpfc_vport *vport, int cmdcode,
}
CtReq->un.rff.type_code = context;
- } else if (((phba->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) ||
- (phba->cfg_enable_fc4_type == LPFC_ENABLE_FCP)) &&
+ } else if (((vport->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) ||
+ (vport->cfg_enable_fc4_type == LPFC_ENABLE_FCP)) &&
(context == FC_TYPE_FCP))
CtReq->un.rff.type_code = context;
diff --git a/drivers/scsi/lpfc/lpfc_debugfs.c b/drivers/scsi/lpfc/lpfc_debugfs.c
index a58f0b3f03a9..1215eaa530db 100644
--- a/drivers/scsi/lpfc/lpfc_debugfs.c
+++ b/drivers/scsi/lpfc/lpfc_debugfs.c
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term *
+ * Copyright (C) 2017-2019 Broadcom. All Rights Reserved. The term *
* “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. *
* Copyright (C) 2007-2015 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
@@ -378,6 +378,271 @@ skipit:
return len;
}
+static int lpfc_debugfs_last_xripool;
+
+/**
+ * lpfc_debugfs_common_xri_data - Dump Hardware Queue info to a buffer
+ * @phba: The HBA to gather host buffer info from.
+ * @buf: The buffer to dump log into.
+ * @size: The maximum amount of data to process.
+ *
+ * Description:
+ * This routine dumps the Hardware Queue info from the @phba to @buf up to
+ * @size number of bytes. A header that describes the current hdwq state will be
+ * dumped to @buf first and then info on each hdwq entry will be dumped to @buf
+ * until @size bytes have been dumped or all the hdwq info has been dumped.
+ *
+ * Notes:
+ * This routine will rotate through each configured Hardware Queue each
+ * time called.
+ *
+ * Return Value:
+ * This routine returns the amount of bytes that were dumped into @buf and will
+ * not exceed @size.
+ **/
+static int
+lpfc_debugfs_commonxripools_data(struct lpfc_hba *phba, char *buf, int size)
+{
+ struct lpfc_sli4_hdw_queue *qp;
+ int len = 0;
+ int i, out;
+ unsigned long iflag;
+
+ for (i = 0; i < phba->cfg_hdw_queue; i++) {
+ if (len > (LPFC_DUMP_MULTIXRIPOOL_SIZE - 80))
+ break;
+ qp = &phba->sli4_hba.hdwq[lpfc_debugfs_last_xripool];
+
+ len += snprintf(buf + len, size - len, "HdwQ %d Info ", i);
+ spin_lock_irqsave(&qp->abts_scsi_buf_list_lock, iflag);
+ spin_lock(&qp->abts_nvme_buf_list_lock);
+ spin_lock(&qp->io_buf_list_get_lock);
+ spin_lock(&qp->io_buf_list_put_lock);
+ out = qp->total_io_bufs - (qp->get_io_bufs + qp->put_io_bufs +
+ qp->abts_scsi_io_bufs + qp->abts_nvme_io_bufs);
+ len += snprintf(buf + len, size - len,
+ "tot:%d get:%d put:%d mt:%d "
+ "ABTS scsi:%d nvme:%d Out:%d\n",
+ qp->total_io_bufs, qp->get_io_bufs, qp->put_io_bufs,
+ qp->empty_io_bufs, qp->abts_scsi_io_bufs,
+ qp->abts_nvme_io_bufs, out);
+ spin_unlock(&qp->io_buf_list_put_lock);
+ spin_unlock(&qp->io_buf_list_get_lock);
+ spin_unlock(&qp->abts_nvme_buf_list_lock);
+ spin_unlock_irqrestore(&qp->abts_scsi_buf_list_lock, iflag);
+
+ lpfc_debugfs_last_xripool++;
+ if (lpfc_debugfs_last_xripool >= phba->cfg_hdw_queue)
+ lpfc_debugfs_last_xripool = 0;
+ }
+
+ return len;
+}
+
+/**
+ * lpfc_debugfs_multixripools_data - Display multi-XRI pools information
+ * @phba: The HBA to gather host buffer info from.
+ * @buf: The buffer to dump log into.
+ * @size: The maximum amount of data to process.
+ *
+ * Description:
+ * This routine displays current multi-XRI pools information including XRI
+ * count in public, private and txcmplq. It also displays current high and
+ * low watermark.
+ *
+ * Return Value:
+ * This routine returns the amount of bytes that were dumped into @buf and will
+ * not exceed @size.
+ **/
+static int
+lpfc_debugfs_multixripools_data(struct lpfc_hba *phba, char *buf, int size)
+{
+ u32 i;
+ u32 hwq_count;
+ struct lpfc_sli4_hdw_queue *qp;
+ struct lpfc_multixri_pool *multixri_pool;
+ struct lpfc_pvt_pool *pvt_pool;
+ struct lpfc_pbl_pool *pbl_pool;
+ u32 txcmplq_cnt;
+ char tmp[LPFC_DEBUG_OUT_LINE_SZ] = {0};
+
+ if (phba->sli_rev != LPFC_SLI_REV4)
+ return 0;
+
+ if (!phba->sli4_hba.hdwq)
+ return 0;
+
+ if (!phba->cfg_xri_rebalancing) {
+ i = lpfc_debugfs_commonxripools_data(phba, buf, size);
+ return i;
+ }
+
+ /*
+ * Pbl: Current number of free XRIs in public pool
+ * Pvt: Current number of free XRIs in private pool
+ * Busy: Current number of outstanding XRIs
+ * HWM: Current high watermark
+ * pvt_empty: Incremented by 1 when IO submission fails (no xri)
+ * pbl_empty: Incremented by 1 when all pbl_pool are empty during
+ * IO submission
+ */
+ scnprintf(tmp, sizeof(tmp),
+ "HWQ: Pbl Pvt Busy HWM | pvt_empty pbl_empty ");
+ if (strlcat(buf, tmp, size) >= size)
+ return strnlen(buf, size);
+
+#ifdef LPFC_MXP_STAT
+ /*
+ * MAXH: Max high watermark seen so far
+ * above_lmt: Incremented by 1 if xri_owned > xri_limit during
+ * IO submission
+ * below_lmt: Incremented by 1 if xri_owned <= xri_limit during
+ * IO submission
+ * locPbl_hit: Incremented by 1 if successfully get a batch of XRI from
+ * local pbl_pool
+ * othPbl_hit: Incremented by 1 if successfully get a batch of XRI from
+ * other pbl_pool
+ */
+ scnprintf(tmp, sizeof(tmp),
+ "MAXH above_lmt below_lmt locPbl_hit othPbl_hit");
+ if (strlcat(buf, tmp, size) >= size)
+ return strnlen(buf, size);
+
+ /*
+ * sPbl: snapshot of Pbl 15 sec after stat gets cleared
+ * sPvt: snapshot of Pvt 15 sec after stat gets cleared
+ * sBusy: snapshot of Busy 15 sec after stat gets cleared
+ */
+ scnprintf(tmp, sizeof(tmp),
+ " | sPbl sPvt sBusy");
+ if (strlcat(buf, tmp, size) >= size)
+ return strnlen(buf, size);
+#endif
+
+ scnprintf(tmp, sizeof(tmp), "\n");
+ if (strlcat(buf, tmp, size) >= size)
+ return strnlen(buf, size);
+
+ hwq_count = phba->cfg_hdw_queue;
+ for (i = 0; i < hwq_count; i++) {
+ qp = &phba->sli4_hba.hdwq[i];
+ multixri_pool = qp->p_multixri_pool;
+ if (!multixri_pool)
+ continue;
+ pbl_pool = &multixri_pool->pbl_pool;
+ pvt_pool = &multixri_pool->pvt_pool;
+ txcmplq_cnt = qp->fcp_wq->pring->txcmplq_cnt;
+ if (qp->nvme_wq)
+ txcmplq_cnt += qp->nvme_wq->pring->txcmplq_cnt;
+
+ scnprintf(tmp, sizeof(tmp),
+ "%03d: %4d %4d %4d %4d | %10d %10d ",
+ i, pbl_pool->count, pvt_pool->count,
+ txcmplq_cnt, pvt_pool->high_watermark,
+ qp->empty_io_bufs, multixri_pool->pbl_empty_count);
+ if (strlcat(buf, tmp, size) >= size)
+ break;
+
+#ifdef LPFC_MXP_STAT
+ scnprintf(tmp, sizeof(tmp),
+ "%4d %10d %10d %10d %10d",
+ multixri_pool->stat_max_hwm,
+ multixri_pool->above_limit_count,
+ multixri_pool->below_limit_count,
+ multixri_pool->local_pbl_hit_count,
+ multixri_pool->other_pbl_hit_count);
+ if (strlcat(buf, tmp, size) >= size)
+ break;
+
+ scnprintf(tmp, sizeof(tmp),
+ " | %4d %4d %5d",
+ multixri_pool->stat_pbl_count,
+ multixri_pool->stat_pvt_count,
+ multixri_pool->stat_busy_count);
+ if (strlcat(buf, tmp, size) >= size)
+ break;
+#endif
+
+ scnprintf(tmp, sizeof(tmp), "\n");
+ if (strlcat(buf, tmp, size) >= size)
+ break;
+ }
+ return strnlen(buf, size);
+}
+
+
+#ifdef LPFC_HDWQ_LOCK_STAT
+static int lpfc_debugfs_last_lock;
+
+/**
+ * lpfc_debugfs_lockstat_data - Dump Hardware Queue info to a buffer
+ * @phba: The HBA to gather host buffer info from.
+ * @buf: The buffer to dump log into.
+ * @size: The maximum amount of data to process.
+ *
+ * Description:
+ * This routine dumps the Hardware Queue info from the @phba to @buf up to
+ * @size number of bytes. A header that describes the current hdwq state will be
+ * dumped to @buf first and then info on each hdwq entry will be dumped to @buf
+ * until @size bytes have been dumped or all the hdwq info has been dumped.
+ *
+ * Notes:
+ * This routine will rotate through each configured Hardware Queue each
+ * time called.
+ *
+ * Return Value:
+ * This routine returns the amount of bytes that were dumped into @buf and will
+ * not exceed @size.
+ **/
+static int
+lpfc_debugfs_lockstat_data(struct lpfc_hba *phba, char *buf, int size)
+{
+ struct lpfc_sli4_hdw_queue *qp;
+ int len = 0;
+ int i;
+
+ if (phba->sli_rev != LPFC_SLI_REV4)
+ return 0;
+
+ if (!phba->sli4_hba.hdwq)
+ return 0;
+
+ for (i = 0; i < phba->cfg_hdw_queue; i++) {
+ if (len > (LPFC_HDWQINFO_SIZE - 100))
+ break;
+ qp = &phba->sli4_hba.hdwq[lpfc_debugfs_last_lock];
+
+ len += snprintf(buf + len, size - len, "HdwQ %03d Lock ", i);
+ if (phba->cfg_xri_rebalancing) {
+ len += snprintf(buf + len, size - len,
+ "get_pvt:%d mv_pvt:%d "
+ "mv2pub:%d mv2pvt:%d "
+ "put_pvt:%d put_pub:%d wq:%d\n",
+ qp->lock_conflict.alloc_pvt_pool,
+ qp->lock_conflict.mv_from_pvt_pool,
+ qp->lock_conflict.mv_to_pub_pool,
+ qp->lock_conflict.mv_to_pvt_pool,
+ qp->lock_conflict.free_pvt_pool,
+ qp->lock_conflict.free_pub_pool,
+ qp->lock_conflict.wq_access);
+ } else {
+ len += snprintf(buf + len, size - len,
+ "get:%d put:%d free:%d wq:%d\n",
+ qp->lock_conflict.alloc_xri_get,
+ qp->lock_conflict.alloc_xri_put,
+ qp->lock_conflict.free_xri,
+ qp->lock_conflict.wq_access);
+ }
+
+ lpfc_debugfs_last_lock++;
+ if (lpfc_debugfs_last_lock >= phba->cfg_hdw_queue)
+ lpfc_debugfs_last_lock = 0;
+ }
+
+ return len;
+}
+#endif
+
static int lpfc_debugfs_last_hba_slim_off;
/**
@@ -773,11 +1038,11 @@ lpfc_debugfs_nvmestat_data(struct lpfc_vport *vport, char *buf, int size)
struct lpfc_nvmet_tgtport *tgtp;
struct lpfc_nvmet_rcv_ctx *ctxp, *next_ctxp;
struct nvme_fc_local_port *localport;
- struct lpfc_nvme_ctrl_stat *cstat;
+ struct lpfc_fc4_ctrl_stat *cstat;
struct lpfc_nvme_lport *lport;
uint64_t data1, data2, data3;
uint64_t tot, totin, totout;
- int cnt, i, maxch;
+ int cnt, i;
int len = 0;
if (phba->nvmet_support) {
@@ -863,17 +1128,17 @@ lpfc_debugfs_nvmestat_data(struct lpfc_vport *vport, char *buf, int size)
len += snprintf(buf + len, size - len, "\n");
cnt = 0;
- spin_lock(&phba->sli4_hba.abts_nvme_buf_list_lock);
+ spin_lock(&phba->sli4_hba.abts_nvmet_buf_list_lock);
list_for_each_entry_safe(ctxp, next_ctxp,
&phba->sli4_hba.lpfc_abts_nvmet_ctx_list,
list) {
cnt++;
}
- spin_unlock(&phba->sli4_hba.abts_nvme_buf_list_lock);
+ spin_unlock(&phba->sli4_hba.abts_nvmet_buf_list_lock);
if (cnt) {
len += snprintf(buf + len, size - len,
"ABORT: %d ctx entries\n", cnt);
- spin_lock(&phba->sli4_hba.abts_nvme_buf_list_lock);
+ spin_lock(&phba->sli4_hba.abts_nvmet_buf_list_lock);
list_for_each_entry_safe(ctxp, next_ctxp,
&phba->sli4_hba.lpfc_abts_nvmet_ctx_list,
list) {
@@ -885,7 +1150,7 @@ lpfc_debugfs_nvmestat_data(struct lpfc_vport *vport, char *buf, int size)
ctxp->oxid, ctxp->state,
ctxp->flag);
}
- spin_unlock(&phba->sli4_hba.abts_nvme_buf_list_lock);
+ spin_unlock(&phba->sli4_hba.abts_nvmet_buf_list_lock);
}
/* Calculate outstanding IOs */
@@ -901,7 +1166,7 @@ lpfc_debugfs_nvmestat_data(struct lpfc_vport *vport, char *buf, int size)
phba->sli4_hba.nvmet_io_wait_total,
tot);
} else {
- if (!(phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME))
+ if (!(vport->cfg_enable_fc4_type & LPFC_ENABLE_NVME))
return len;
localport = vport->localport;
@@ -912,26 +1177,22 @@ lpfc_debugfs_nvmestat_data(struct lpfc_vport *vport, char *buf, int size)
return len;
len += snprintf(buf + len, size - len,
- "\nNVME Lport Statistics\n");
+ "\nNVME HDWQ Statistics\n");
len += snprintf(buf + len, size - len,
"LS: Xmt %016x Cmpl %016x\n",
atomic_read(&lport->fc4NvmeLsRequests),
atomic_read(&lport->fc4NvmeLsCmpls));
- if (phba->cfg_nvme_io_channel < 32)
- maxch = phba->cfg_nvme_io_channel;
- else
- maxch = 32;
totin = 0;
totout = 0;
- for (i = 0; i < phba->cfg_nvme_io_channel; i++) {
- cstat = &lport->cstat[i];
- tot = atomic_read(&cstat->fc4NvmeIoCmpls);
+ for (i = 0; i < phba->cfg_hdw_queue; i++) {
+ cstat = &phba->sli4_hba.hdwq[i].nvme_cstat;
+ tot = cstat->io_cmpls;
totin += tot;
- data1 = atomic_read(&cstat->fc4NvmeInputRequests);
- data2 = atomic_read(&cstat->fc4NvmeOutputRequests);
- data3 = atomic_read(&cstat->fc4NvmeControlRequests);
+ data1 = cstat->input_requests;
+ data2 = cstat->output_requests;
+ data3 = cstat->control_requests;
totout += (data1 + data2 + data3);
/* Limit to 32, debugfs display buffer limitation */
@@ -939,7 +1200,7 @@ lpfc_debugfs_nvmestat_data(struct lpfc_vport *vport, char *buf, int size)
continue;
len += snprintf(buf + len, PAGE_SIZE - len,
- "FCP (%d): Rd %016llx Wr %016llx "
+ "HDWQ (%d): Rd %016llx Wr %016llx "
"IO %016llx ",
i, data1, data2, data3);
len += snprintf(buf + len, PAGE_SIZE - len,
@@ -979,6 +1240,66 @@ lpfc_debugfs_nvmestat_data(struct lpfc_vport *vport, char *buf, int size)
return len;
}
+/**
+ * lpfc_debugfs_scsistat_data - Dump target node list to a buffer
+ * @vport: The vport to gather target node info from.
+ * @buf: The buffer to dump log into.
+ * @size: The maximum amount of data to process.
+ *
+ * Description:
+ * This routine dumps the SCSI statistics associated with @vport
+ *
+ * Return Value:
+ * This routine returns the amount of bytes that were dumped into @buf and will
+ * not exceed @size.
+ **/
+static int
+lpfc_debugfs_scsistat_data(struct lpfc_vport *vport, char *buf, int size)
+{
+ int len;
+ struct lpfc_hba *phba = vport->phba;
+ struct lpfc_fc4_ctrl_stat *cstat;
+ u64 data1, data2, data3;
+ u64 tot, totin, totout;
+ int i;
+ char tmp[LPFC_MAX_SCSI_INFO_TMP_LEN] = {0};
+
+ if (!(vport->cfg_enable_fc4_type & LPFC_ENABLE_FCP) ||
+ (phba->sli_rev != LPFC_SLI_REV4))
+ return 0;
+
+ scnprintf(buf, size, "SCSI HDWQ Statistics\n");
+
+ totin = 0;
+ totout = 0;
+ for (i = 0; i < phba->cfg_hdw_queue; i++) {
+ cstat = &phba->sli4_hba.hdwq[i].scsi_cstat;
+ tot = cstat->io_cmpls;
+ totin += tot;
+ data1 = cstat->input_requests;
+ data2 = cstat->output_requests;
+ data3 = cstat->control_requests;
+ totout += (data1 + data2 + data3);
+
+ scnprintf(tmp, sizeof(tmp), "HDWQ (%d): Rd %016llx Wr %016llx "
+ "IO %016llx ", i, data1, data2, data3);
+ if (strlcat(buf, tmp, size) >= size)
+ goto buffer_done;
+
+ scnprintf(tmp, sizeof(tmp), "Cmpl %016llx OutIO %016llx\n",
+ tot, ((data1 + data2 + data3) - tot));
+ if (strlcat(buf, tmp, size) >= size)
+ goto buffer_done;
+ }
+ scnprintf(tmp, sizeof(tmp), "Total FCP Cmpl %016llx Issue %016llx "
+ "OutIO %016llx\n", totin, totout, totout - totin);
+ strlcat(buf, tmp, size);
+
+buffer_done:
+ len = strnlen(buf, size);
+
+ return len;
+}
/**
* lpfc_debugfs_nvmektime_data - Dump target node list to a buffer
@@ -1299,62 +1620,73 @@ static int
lpfc_debugfs_cpucheck_data(struct lpfc_vport *vport, char *buf, int size)
{
struct lpfc_hba *phba = vport->phba;
- int i;
+ struct lpfc_sli4_hdw_queue *qp;
+ int i, j, max_cnt;
int len = 0;
- uint32_t tot_xmt = 0;
- uint32_t tot_rcv = 0;
- uint32_t tot_cmpl = 0;
- uint32_t tot_ccmpl = 0;
+ uint32_t tot_xmt;
+ uint32_t tot_rcv;
+ uint32_t tot_cmpl;
- if (phba->nvmet_support == 0) {
- /* NVME Initiator */
- len += snprintf(buf + len, PAGE_SIZE - len,
- "CPUcheck %s\n",
- (phba->cpucheck_on & LPFC_CHECK_NVME_IO ?
- "Enabled" : "Disabled"));
- for (i = 0; i < phba->sli4_hba.num_present_cpu; i++) {
- if (i >= LPFC_CHECK_CPU_CNT)
- break;
- len += snprintf(buf + len, PAGE_SIZE - len,
- "%02d: xmit x%08x cmpl x%08x\n",
- i, phba->cpucheck_xmt_io[i],
- phba->cpucheck_cmpl_io[i]);
- tot_xmt += phba->cpucheck_xmt_io[i];
- tot_cmpl += phba->cpucheck_cmpl_io[i];
- }
+ len += snprintf(buf + len, PAGE_SIZE - len,
+ "CPUcheck %s ",
+ (phba->cpucheck_on & LPFC_CHECK_NVME_IO ?
+ "Enabled" : "Disabled"));
+ if (phba->nvmet_support) {
len += snprintf(buf + len, PAGE_SIZE - len,
- "tot:xmit x%08x cmpl x%08x\n",
- tot_xmt, tot_cmpl);
- return len;
+ "%s\n",
+ (phba->cpucheck_on & LPFC_CHECK_NVMET_RCV ?
+ "Rcv Enabled\n" : "Rcv Disabled\n"));
+ } else {
+ len += snprintf(buf + len, PAGE_SIZE - len, "\n");
}
+ max_cnt = size - LPFC_DEBUG_OUT_LINE_SZ;
+
+ for (i = 0; i < phba->cfg_hdw_queue; i++) {
+ qp = &phba->sli4_hba.hdwq[i];
+
+ tot_rcv = 0;
+ tot_xmt = 0;
+ tot_cmpl = 0;
+ for (j = 0; j < LPFC_CHECK_CPU_CNT; j++) {
+ tot_xmt += qp->cpucheck_xmt_io[j];
+ tot_cmpl += qp->cpucheck_cmpl_io[j];
+ if (phba->nvmet_support)
+ tot_rcv += qp->cpucheck_rcv_io[j];
+ }
+
+ /* Only display Hardware Qs with something */
+ if (!tot_xmt && !tot_cmpl && !tot_rcv)
+ continue;
- /* NVME Target */
- len += snprintf(buf + len, PAGE_SIZE - len,
- "CPUcheck %s ",
- (phba->cpucheck_on & LPFC_CHECK_NVMET_IO ?
- "IO Enabled - " : "IO Disabled - "));
- len += snprintf(buf + len, PAGE_SIZE - len,
- "%s\n",
- (phba->cpucheck_on & LPFC_CHECK_NVMET_RCV ?
- "Rcv Enabled\n" : "Rcv Disabled\n"));
- for (i = 0; i < phba->sli4_hba.num_present_cpu; i++) {
- if (i >= LPFC_CHECK_CPU_CNT)
- break;
len += snprintf(buf + len, PAGE_SIZE - len,
- "%02d: xmit x%08x ccmpl x%08x "
- "cmpl x%08x rcv x%08x\n",
- i, phba->cpucheck_xmt_io[i],
- phba->cpucheck_ccmpl_io[i],
- phba->cpucheck_cmpl_io[i],
- phba->cpucheck_rcv_io[i]);
- tot_xmt += phba->cpucheck_xmt_io[i];
- tot_rcv += phba->cpucheck_rcv_io[i];
- tot_cmpl += phba->cpucheck_cmpl_io[i];
- tot_ccmpl += phba->cpucheck_ccmpl_io[i];
+ "HDWQ %03d: ", i);
+ for (j = 0; j < LPFC_CHECK_CPU_CNT; j++) {
+ /* Only display non-zero counters */
+ if (!qp->cpucheck_xmt_io[j] &&
+ !qp->cpucheck_cmpl_io[j] &&
+ !qp->cpucheck_rcv_io[j])
+ continue;
+ if (phba->nvmet_support) {
+ len += snprintf(buf + len, PAGE_SIZE - len,
+ "CPU %03d: %x/%x/%x ", j,
+ qp->cpucheck_rcv_io[j],
+ qp->cpucheck_xmt_io[j],
+ qp->cpucheck_cmpl_io[j]);
+ } else {
+ len += snprintf(buf + len, PAGE_SIZE - len,
+ "CPU %03d: %x/%x ", j,
+ qp->cpucheck_xmt_io[j],
+ qp->cpucheck_cmpl_io[j]);
+ }
+ }
+ len += snprintf(buf + len, PAGE_SIZE - len,
+ "Total: %x\n", tot_xmt);
+ if (len >= max_cnt) {
+ len += snprintf(buf + len, PAGE_SIZE - len,
+ "Truncated ...\n");
+ return len;
+ }
}
- len += snprintf(buf + len, PAGE_SIZE - len,
- "tot:xmit x%08x ccmpl x%08x cmpl x%08x rcv x%08x\n",
- tot_xmt, tot_ccmpl, tot_cmpl, tot_rcv);
return len;
}
@@ -1501,7 +1833,7 @@ lpfc_debugfs_disc_trc_open(struct inode *inode, struct file *file)
int rc = -ENOMEM;
if (!lpfc_debugfs_max_disc_trc) {
- rc = -ENOSPC;
+ rc = -ENOSPC;
goto out;
}
@@ -1551,7 +1883,7 @@ lpfc_debugfs_slow_ring_trc_open(struct inode *inode, struct file *file)
int rc = -ENOMEM;
if (!lpfc_debugfs_max_slow_ring_trc) {
- rc = -ENOSPC;
+ rc = -ENOSPC;
goto out;
}
@@ -1620,6 +1952,135 @@ out:
}
/**
+ * lpfc_debugfs_multixripools_open - Open the multixripool debugfs buffer
+ * @inode: The inode pointer that contains a hba pointer.
+ * @file: The file pointer to attach the log output.
+ *
+ * Description:
+ * This routine is the entry point for the debugfs open file operation. It gets
+ * the hba from the i_private field in @inode, allocates the necessary buffer
+ * for the log, fills the buffer from the in-memory log for this hba, and then
+ * returns a pointer to that log in the private_data field in @file.
+ *
+ * Returns:
+ * This function returns zero if successful. On error it will return a negative
+ * error value.
+ **/
+static int
+lpfc_debugfs_multixripools_open(struct inode *inode, struct file *file)
+{
+ struct lpfc_hba *phba = inode->i_private;
+ struct lpfc_debug *debug;
+ int rc = -ENOMEM;
+
+ debug = kmalloc(sizeof(*debug), GFP_KERNEL);
+ if (!debug)
+ goto out;
+
+ /* Round to page boundary */
+ debug->buffer = kzalloc(LPFC_DUMP_MULTIXRIPOOL_SIZE, GFP_KERNEL);
+ if (!debug->buffer) {
+ kfree(debug);
+ goto out;
+ }
+
+ debug->len = lpfc_debugfs_multixripools_data(
+ phba, debug->buffer, LPFC_DUMP_MULTIXRIPOOL_SIZE);
+
+ debug->i_private = inode->i_private;
+ file->private_data = debug;
+
+ rc = 0;
+out:
+ return rc;
+}
+
+#ifdef LPFC_HDWQ_LOCK_STAT
+/**
+ * lpfc_debugfs_lockstat_open - Open the lockstat debugfs buffer
+ * @inode: The inode pointer that contains a vport pointer.
+ * @file: The file pointer to attach the log output.
+ *
+ * Description:
+ * This routine is the entry point for the debugfs open file operation. It gets
+ * the vport from the i_private field in @inode, allocates the necessary buffer
+ * for the log, fills the buffer from the in-memory log for this vport, and then
+ * returns a pointer to that log in the private_data field in @file.
+ *
+ * Returns:
+ * This function returns zero if successful. On error it will return a negative
+ * error value.
+ **/
+static int
+lpfc_debugfs_lockstat_open(struct inode *inode, struct file *file)
+{
+ struct lpfc_hba *phba = inode->i_private;
+ struct lpfc_debug *debug;
+ int rc = -ENOMEM;
+
+ debug = kmalloc(sizeof(*debug), GFP_KERNEL);
+ if (!debug)
+ goto out;
+
+ /* Round to page boundary */
+ debug->buffer = kmalloc(LPFC_HDWQINFO_SIZE, GFP_KERNEL);
+ if (!debug->buffer) {
+ kfree(debug);
+ goto out;
+ }
+
+ debug->len = lpfc_debugfs_lockstat_data(phba, debug->buffer,
+ LPFC_HBQINFO_SIZE);
+ file->private_data = debug;
+
+ rc = 0;
+out:
+ return rc;
+}
+
+static ssize_t
+lpfc_debugfs_lockstat_write(struct file *file, const char __user *buf,
+ size_t nbytes, loff_t *ppos)
+{
+ struct lpfc_debug *debug = file->private_data;
+ struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private;
+ struct lpfc_sli4_hdw_queue *qp;
+ char mybuf[64];
+ char *pbuf;
+ int i;
+
+ /* Protect copy from user */
+ if (!access_ok(buf, nbytes))
+ return -EFAULT;
+
+ memset(mybuf, 0, sizeof(mybuf));
+
+ if (copy_from_user(mybuf, buf, nbytes))
+ return -EFAULT;
+ pbuf = &mybuf[0];
+
+ if ((strncmp(pbuf, "reset", strlen("reset")) == 0) ||
+ (strncmp(pbuf, "zero", strlen("zero")) == 0)) {
+ for (i = 0; i < phba->cfg_hdw_queue; i++) {
+ qp = &phba->sli4_hba.hdwq[i];
+ qp->lock_conflict.alloc_xri_get = 0;
+ qp->lock_conflict.alloc_xri_put = 0;
+ qp->lock_conflict.free_xri = 0;
+ qp->lock_conflict.wq_access = 0;
+ qp->lock_conflict.alloc_pvt_pool = 0;
+ qp->lock_conflict.mv_from_pvt_pool = 0;
+ qp->lock_conflict.mv_to_pub_pool = 0;
+ qp->lock_conflict.mv_to_pvt_pool = 0;
+ qp->lock_conflict.free_pvt_pool = 0;
+ qp->lock_conflict.free_pub_pool = 0;
+ qp->lock_conflict.wq_access = 0;
+ }
+ }
+ return nbytes;
+}
+#endif
+
+/**
* lpfc_debugfs_dumpHBASlim_open - Open the Dump HBA SLIM debugfs buffer
* @inode: The inode pointer that contains a vport pointer.
* @file: The file pointer to attach the log output.
@@ -2008,6 +2469,75 @@ lpfc_debugfs_dumpDataDif_release(struct inode *inode, struct file *file)
return 0;
}
+/**
+ * lpfc_debugfs_multixripools_write - Clear multi-XRI pools statistics
+ * @file: The file pointer to read from.
+ * @buf: The buffer to copy the user data from.
+ * @nbytes: The number of bytes to get.
+ * @ppos: The position in the file to start reading from.
+ *
+ * Description:
+ * This routine clears multi-XRI pools statistics when buf contains "clear".
+ *
+ * Return Value:
+ * It returns the @nbytges passing in from debugfs user space when successful.
+ * In case of error conditions, it returns proper error code back to the user
+ * space.
+ **/
+static ssize_t
+lpfc_debugfs_multixripools_write(struct file *file, const char __user *buf,
+ size_t nbytes, loff_t *ppos)
+{
+ struct lpfc_debug *debug = file->private_data;
+ struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private;
+ char mybuf[64];
+ char *pbuf;
+ u32 i;
+ u32 hwq_count;
+ struct lpfc_sli4_hdw_queue *qp;
+ struct lpfc_multixri_pool *multixri_pool;
+
+ if (nbytes > 64)
+ nbytes = 64;
+
+ /* Protect copy from user */
+ if (!access_ok(buf, nbytes))
+ return -EFAULT;
+
+ memset(mybuf, 0, sizeof(mybuf));
+
+ if (copy_from_user(mybuf, buf, nbytes))
+ return -EFAULT;
+ pbuf = &mybuf[0];
+
+ if ((strncmp(pbuf, "clear", strlen("clear"))) == 0) {
+ hwq_count = phba->cfg_hdw_queue;
+ for (i = 0; i < hwq_count; i++) {
+ qp = &phba->sli4_hba.hdwq[i];
+ multixri_pool = qp->p_multixri_pool;
+ if (!multixri_pool)
+ continue;
+
+ qp->empty_io_bufs = 0;
+ multixri_pool->pbl_empty_count = 0;
+#ifdef LPFC_MXP_STAT
+ multixri_pool->above_limit_count = 0;
+ multixri_pool->below_limit_count = 0;
+ multixri_pool->stat_max_hwm = 0;
+ multixri_pool->local_pbl_hit_count = 0;
+ multixri_pool->other_pbl_hit_count = 0;
+
+ multixri_pool->stat_pbl_count = 0;
+ multixri_pool->stat_pvt_count = 0;
+ multixri_pool->stat_busy_count = 0;
+ multixri_pool->stat_snapshot_taken = 0;
+#endif
+ }
+ return strlen(pbuf);
+ }
+
+ return -EINVAL;
+}
static int
lpfc_debugfs_nvmestat_open(struct inode *inode, struct file *file)
@@ -2098,6 +2628,64 @@ lpfc_debugfs_nvmestat_write(struct file *file, const char __user *buf,
}
static int
+lpfc_debugfs_scsistat_open(struct inode *inode, struct file *file)
+{
+ struct lpfc_vport *vport = inode->i_private;
+ struct lpfc_debug *debug;
+ int rc = -ENOMEM;
+
+ debug = kmalloc(sizeof(*debug), GFP_KERNEL);
+ if (!debug)
+ goto out;
+
+ /* Round to page boundary */
+ debug->buffer = kzalloc(LPFC_SCSISTAT_SIZE, GFP_KERNEL);
+ if (!debug->buffer) {
+ kfree(debug);
+ goto out;
+ }
+
+ debug->len = lpfc_debugfs_scsistat_data(vport, debug->buffer,
+ LPFC_SCSISTAT_SIZE);
+
+ debug->i_private = inode->i_private;
+ file->private_data = debug;
+
+ rc = 0;
+out:
+ return rc;
+}
+
+static ssize_t
+lpfc_debugfs_scsistat_write(struct file *file, const char __user *buf,
+ size_t nbytes, loff_t *ppos)
+{
+ struct lpfc_debug *debug = file->private_data;
+ struct lpfc_vport *vport = (struct lpfc_vport *)debug->i_private;
+ struct lpfc_hba *phba = vport->phba;
+ char mybuf[6] = {0};
+ int i;
+
+ /* Protect copy from user */
+ if (!access_ok(buf, nbytes))
+ return -EFAULT;
+
+ if (copy_from_user(mybuf, buf, (nbytes >= sizeof(mybuf)) ?
+ (sizeof(mybuf) - 1) : nbytes))
+ return -EFAULT;
+
+ if ((strncmp(&mybuf[0], "reset", strlen("reset")) == 0) ||
+ (strncmp(&mybuf[0], "zero", strlen("zero")) == 0)) {
+ for (i = 0; i < phba->cfg_hdw_queue; i++) {
+ memset(&phba->sli4_hba.hdwq[i].scsi_cstat, 0,
+ sizeof(phba->sli4_hba.hdwq[i].scsi_cstat));
+ }
+ }
+
+ return nbytes;
+}
+
+static int
lpfc_debugfs_nvmektime_open(struct inode *inode, struct file *file)
{
struct lpfc_vport *vport = inode->i_private;
@@ -2348,7 +2936,7 @@ lpfc_debugfs_cpucheck_open(struct inode *inode, struct file *file)
}
debug->len = lpfc_debugfs_cpucheck_data(vport, debug->buffer,
- LPFC_NVMEKTIME_SIZE);
+ LPFC_CPUCHECK_SIZE);
debug->i_private = inode->i_private;
file->private_data = debug;
@@ -2365,9 +2953,10 @@ lpfc_debugfs_cpucheck_write(struct file *file, const char __user *buf,
struct lpfc_debug *debug = file->private_data;
struct lpfc_vport *vport = (struct lpfc_vport *)debug->i_private;
struct lpfc_hba *phba = vport->phba;
+ struct lpfc_sli4_hdw_queue *qp;
char mybuf[64];
char *pbuf;
- int i;
+ int i, j;
if (nbytes > 64)
nbytes = 64;
@@ -2382,8 +2971,18 @@ lpfc_debugfs_cpucheck_write(struct file *file, const char __user *buf,
if (phba->nvmet_support)
phba->cpucheck_on |= LPFC_CHECK_NVMET_IO;
else
+ phba->cpucheck_on |= (LPFC_CHECK_NVME_IO |
+ LPFC_CHECK_SCSI_IO);
+ return strlen(pbuf);
+ } else if ((strncmp(pbuf, "nvme_on", sizeof("nvme_on") - 1) == 0)) {
+ if (phba->nvmet_support)
+ phba->cpucheck_on |= LPFC_CHECK_NVMET_IO;
+ else
phba->cpucheck_on |= LPFC_CHECK_NVME_IO;
return strlen(pbuf);
+ } else if ((strncmp(pbuf, "scsi_on", sizeof("scsi_on") - 1) == 0)) {
+ phba->cpucheck_on |= LPFC_CHECK_SCSI_IO;
+ return strlen(pbuf);
} else if ((strncmp(pbuf, "rcv",
sizeof("rcv") - 1) == 0)) {
if (phba->nvmet_support)
@@ -2397,13 +2996,14 @@ lpfc_debugfs_cpucheck_write(struct file *file, const char __user *buf,
return strlen(pbuf);
} else if ((strncmp(pbuf, "zero",
sizeof("zero") - 1) == 0)) {
- for (i = 0; i < phba->sli4_hba.num_present_cpu; i++) {
- if (i >= LPFC_CHECK_CPU_CNT)
- break;
- phba->cpucheck_rcv_io[i] = 0;
- phba->cpucheck_xmt_io[i] = 0;
- phba->cpucheck_cmpl_io[i] = 0;
- phba->cpucheck_ccmpl_io[i] = 0;
+ for (i = 0; i < phba->cfg_hdw_queue; i++) {
+ qp = &phba->sli4_hba.hdwq[i];
+
+ for (j = 0; j < LPFC_CHECK_CPU_CNT; j++) {
+ qp->cpucheck_rcv_io[j] = 0;
+ qp->cpucheck_xmt_io[j] = 0;
+ qp->cpucheck_cmpl_io[j] = 0;
+ }
}
return strlen(pbuf);
}
@@ -3166,10 +3766,10 @@ __lpfc_idiag_print_wq(struct lpfc_queue *qp, char *wqtype,
(unsigned long long)qp->q_cnt_4);
len += snprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len,
"\t\tWQID[%02d], QE-CNT[%04d], QE-SZ[%04d], "
- "HST-IDX[%04d], PRT-IDX[%04d], PST[%03d]",
+ "HST-IDX[%04d], PRT-IDX[%04d], NTFI[%03d]",
qp->queue_id, qp->entry_count,
qp->entry_size, qp->host_index,
- qp->hba_index, qp->entry_repost);
+ qp->hba_index, qp->notify_interval);
len += snprintf(pbuffer + len,
LPFC_QUE_INFO_GET_BUF_SIZE - len, "\n");
return len;
@@ -3182,21 +3782,23 @@ lpfc_idiag_wqs_for_cq(struct lpfc_hba *phba, char *wqtype, char *pbuffer,
struct lpfc_queue *qp;
int qidx;
- for (qidx = 0; qidx < phba->cfg_fcp_io_channel; qidx++) {
- qp = phba->sli4_hba.fcp_wq[qidx];
+ for (qidx = 0; qidx < phba->cfg_hdw_queue; qidx++) {
+ qp = phba->sli4_hba.hdwq[qidx].fcp_wq;
if (qp->assoc_qid != cq_id)
continue;
*len = __lpfc_idiag_print_wq(qp, wqtype, pbuffer, *len);
if (*len >= max_cnt)
return 1;
}
- for (qidx = 0; qidx < phba->cfg_nvme_io_channel; qidx++) {
- qp = phba->sli4_hba.nvme_wq[qidx];
- if (qp->assoc_qid != cq_id)
- continue;
- *len = __lpfc_idiag_print_wq(qp, wqtype, pbuffer, *len);
- if (*len >= max_cnt)
- return 1;
+ if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) {
+ for (qidx = 0; qidx < phba->cfg_hdw_queue; qidx++) {
+ qp = phba->sli4_hba.hdwq[qidx].nvme_wq;
+ if (qp->assoc_qid != cq_id)
+ continue;
+ *len = __lpfc_idiag_print_wq(qp, wqtype, pbuffer, *len);
+ if (*len >= max_cnt)
+ return 1;
+ }
}
return 0;
}
@@ -3217,10 +3819,10 @@ __lpfc_idiag_print_cq(struct lpfc_queue *qp, char *cqtype,
qp->q_cnt_3, (unsigned long long)qp->q_cnt_4);
len += snprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len,
"\tCQID[%02d], QE-CNT[%04d], QE-SZ[%04d], "
- "HST-IDX[%04d], PRT-IDX[%04d], PST[%03d]",
+ "HST-IDX[%04d], NTFI[%03d], PLMT[%03d]",
qp->queue_id, qp->entry_count,
qp->entry_size, qp->host_index,
- qp->hba_index, qp->entry_repost);
+ qp->notify_interval, qp->max_proc_limit);
len += snprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len, "\n");
@@ -3243,15 +3845,15 @@ __lpfc_idiag_print_rqpair(struct lpfc_queue *qp, struct lpfc_queue *datqp,
qp->q_cnt_3, (unsigned long long)qp->q_cnt_4);
len += snprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len,
"\t\tHQID[%02d], QE-CNT[%04d], QE-SZ[%04d], "
- "HST-IDX[%04d], PRT-IDX[%04d], PST[%03d]\n",
+ "HST-IDX[%04d], PRT-IDX[%04d], NTFI[%03d]\n",
qp->queue_id, qp->entry_count, qp->entry_size,
- qp->host_index, qp->hba_index, qp->entry_repost);
+ qp->host_index, qp->hba_index, qp->notify_interval);
len += snprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len,
"\t\tDQID[%02d], QE-CNT[%04d], QE-SZ[%04d], "
- "HST-IDX[%04d], PRT-IDX[%04d], PST[%03d]\n",
+ "HST-IDX[%04d], PRT-IDX[%04d], NTFI[%03d]\n",
datqp->queue_id, datqp->entry_count,
datqp->entry_size, datqp->host_index,
- datqp->hba_index, datqp->entry_repost);
+ datqp->hba_index, datqp->notify_interval);
return len;
}
@@ -3260,31 +3862,25 @@ lpfc_idiag_cqs_for_eq(struct lpfc_hba *phba, char *pbuffer,
int *len, int max_cnt, int eqidx, int eq_id)
{
struct lpfc_queue *qp;
- int qidx, rc;
+ int rc;
- for (qidx = 0; qidx < phba->cfg_fcp_io_channel; qidx++) {
- qp = phba->sli4_hba.fcp_cq[qidx];
- if (qp->assoc_qid != eq_id)
- continue;
+ qp = phba->sli4_hba.hdwq[eqidx].fcp_cq;
- *len = __lpfc_idiag_print_cq(qp, "FCP", pbuffer, *len);
+ *len = __lpfc_idiag_print_cq(qp, "FCP", pbuffer, *len);
- /* Reset max counter */
- qp->CQ_max_cqe = 0;
+ /* Reset max counter */
+ qp->CQ_max_cqe = 0;
- if (*len >= max_cnt)
- return 1;
+ if (*len >= max_cnt)
+ return 1;
- rc = lpfc_idiag_wqs_for_cq(phba, "FCP", pbuffer, len,
- max_cnt, qp->queue_id);
- if (rc)
- return 1;
- }
+ rc = lpfc_idiag_wqs_for_cq(phba, "FCP", pbuffer, len,
+ max_cnt, qp->queue_id);
+ if (rc)
+ return 1;
- for (qidx = 0; qidx < phba->cfg_nvme_io_channel; qidx++) {
- qp = phba->sli4_hba.nvme_cq[qidx];
- if (qp->assoc_qid != eq_id)
- continue;
+ if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) {
+ qp = phba->sli4_hba.hdwq[eqidx].nvme_cq;
*len = __lpfc_idiag_print_cq(qp, "NVME", pbuffer, *len);
@@ -3295,7 +3891,7 @@ lpfc_idiag_cqs_for_eq(struct lpfc_hba *phba, char *pbuffer,
return 1;
rc = lpfc_idiag_wqs_for_cq(phba, "NVME", pbuffer, len,
- max_cnt, qp->queue_id);
+ max_cnt, qp->queue_id);
if (rc)
return 1;
}
@@ -3338,9 +3934,10 @@ __lpfc_idiag_print_eq(struct lpfc_queue *qp, char *eqtype,
(unsigned long long)qp->q_cnt_4, qp->q_mode);
len += snprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len,
"EQID[%02d], QE-CNT[%04d], QE-SZ[%04d], "
- "HST-IDX[%04d], PRT-IDX[%04d], PST[%03d]",
+ "HST-IDX[%04d], NTFI[%03d], PLMT[%03d], AFFIN[%03d]",
qp->queue_id, qp->entry_count, qp->entry_size,
- qp->host_index, qp->hba_index, qp->entry_repost);
+ qp->host_index, qp->notify_interval,
+ qp->max_proc_limit, qp->chann);
len += snprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len, "\n");
return len;
@@ -3387,24 +3984,19 @@ lpfc_idiag_queinfo_read(struct file *file, char __user *buf, size_t nbytes,
spin_lock_irq(&phba->hbalock);
/* Fast-path event queue */
- if (phba->sli4_hba.hba_eq && phba->io_channel_irqs) {
+ if (phba->sli4_hba.hdwq && phba->cfg_hdw_queue) {
x = phba->lpfc_idiag_last_eq;
- if (phba->cfg_fof && (x >= phba->io_channel_irqs)) {
- phba->lpfc_idiag_last_eq = 0;
- goto fof;
- }
phba->lpfc_idiag_last_eq++;
- if (phba->lpfc_idiag_last_eq >= phba->io_channel_irqs)
- if (phba->cfg_fof == 0)
- phba->lpfc_idiag_last_eq = 0;
+ if (phba->lpfc_idiag_last_eq >= phba->cfg_hdw_queue)
+ phba->lpfc_idiag_last_eq = 0;
len += snprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len,
- "EQ %d out of %d HBA EQs\n",
- x, phba->io_channel_irqs);
+ "HDWQ %d out of %d HBA HDWQs\n",
+ x, phba->cfg_hdw_queue);
/* Fast-path EQ */
- qp = phba->sli4_hba.hba_eq[x];
+ qp = phba->sli4_hba.hdwq[x].hba_eq;
if (!qp)
goto out;
@@ -3479,35 +4071,6 @@ lpfc_idiag_queinfo_read(struct file *file, char __user *buf, size_t nbytes,
goto out;
}
-fof:
- if (phba->cfg_fof) {
- /* FOF EQ */
- qp = phba->sli4_hba.fof_eq;
- len = __lpfc_idiag_print_eq(qp, "FOF", pbuffer, len);
-
- /* Reset max counter */
- if (qp)
- qp->EQ_max_eqe = 0;
-
- if (len >= max_cnt)
- goto too_big;
-
- /* OAS CQ */
- qp = phba->sli4_hba.oas_cq;
- len = __lpfc_idiag_print_cq(qp, "OAS", pbuffer, len);
- /* Reset max counter */
- if (qp)
- qp->CQ_max_cqe = 0;
- if (len >= max_cnt)
- goto too_big;
-
- /* OAS WQ */
- qp = phba->sli4_hba.oas_wq;
- len = __lpfc_idiag_print_wq(qp, "OAS", pbuffer, len);
- if (len >= max_cnt)
- goto too_big;
- }
-
spin_unlock_irq(&phba->hbalock);
return simple_read_from_buffer(buf, nbytes, ppos, pbuffer, len);
@@ -3725,9 +4288,9 @@ lpfc_idiag_queacc_write(struct file *file, const char __user *buf,
switch (quetp) {
case LPFC_IDIAG_EQ:
/* HBA event queue */
- if (phba->sli4_hba.hba_eq) {
- for (qidx = 0; qidx < phba->io_channel_irqs; qidx++) {
- qp = phba->sli4_hba.hba_eq[qidx];
+ if (phba->sli4_hba.hdwq) {
+ for (qidx = 0; qidx < phba->cfg_hdw_queue; qidx++) {
+ qp = phba->sli4_hba.hdwq[qidx].hba_eq;
if (qp && qp->queue_id == queid) {
/* Sanity check */
rc = lpfc_idiag_que_param_check(qp,
@@ -3776,10 +4339,10 @@ lpfc_idiag_queacc_write(struct file *file, const char __user *buf,
goto pass_check;
}
/* FCP complete queue */
- if (phba->sli4_hba.fcp_cq) {
- for (qidx = 0; qidx < phba->cfg_fcp_io_channel;
+ if (phba->sli4_hba.hdwq) {
+ for (qidx = 0; qidx < phba->cfg_hdw_queue;
qidx++) {
- qp = phba->sli4_hba.fcp_cq[qidx];
+ qp = phba->sli4_hba.hdwq[qidx].fcp_cq;
if (qp && qp->queue_id == queid) {
/* Sanity check */
rc = lpfc_idiag_que_param_check(
@@ -3792,23 +4355,20 @@ lpfc_idiag_queacc_write(struct file *file, const char __user *buf,
}
}
/* NVME complete queue */
- if (phba->sli4_hba.nvme_cq) {
+ if (phba->sli4_hba.hdwq) {
qidx = 0;
do {
- if (phba->sli4_hba.nvme_cq[qidx] &&
- phba->sli4_hba.nvme_cq[qidx]->queue_id ==
- queid) {
+ qp = phba->sli4_hba.hdwq[qidx].nvme_cq;
+ if (qp && qp->queue_id == queid) {
/* Sanity check */
rc = lpfc_idiag_que_param_check(
- phba->sli4_hba.nvme_cq[qidx],
- index, count);
+ qp, index, count);
if (rc)
goto error_out;
- idiag.ptr_private =
- phba->sli4_hba.nvme_cq[qidx];
+ idiag.ptr_private = qp;
goto pass_check;
}
- } while (++qidx < phba->cfg_nvme_io_channel);
+ } while (++qidx < phba->cfg_hdw_queue);
}
goto error_out;
break;
@@ -3849,11 +4409,11 @@ lpfc_idiag_queacc_write(struct file *file, const char __user *buf,
idiag.ptr_private = phba->sli4_hba.nvmels_wq;
goto pass_check;
}
- /* FCP work queue */
- if (phba->sli4_hba.fcp_wq) {
- for (qidx = 0; qidx < phba->cfg_fcp_io_channel;
- qidx++) {
- qp = phba->sli4_hba.fcp_wq[qidx];
+
+ if (phba->sli4_hba.hdwq) {
+ /* FCP/SCSI work queue */
+ for (qidx = 0; qidx < phba->cfg_hdw_queue; qidx++) {
+ qp = phba->sli4_hba.hdwq[qidx].fcp_wq;
if (qp && qp->queue_id == queid) {
/* Sanity check */
rc = lpfc_idiag_que_param_check(
@@ -3864,12 +4424,9 @@ lpfc_idiag_queacc_write(struct file *file, const char __user *buf,
goto pass_check;
}
}
- }
- /* NVME work queue */
- if (phba->sli4_hba.nvme_wq) {
- for (qidx = 0; qidx < phba->cfg_nvme_io_channel;
- qidx++) {
- qp = phba->sli4_hba.nvme_wq[qidx];
+ /* NVME work queue */
+ for (qidx = 0; qidx < phba->cfg_hdw_queue; qidx++) {
+ qp = phba->sli4_hba.hdwq[qidx].nvme_wq;
if (qp && qp->queue_id == queid) {
/* Sanity check */
rc = lpfc_idiag_que_param_check(
@@ -3882,26 +4439,6 @@ lpfc_idiag_queacc_write(struct file *file, const char __user *buf,
}
}
- /* NVME work queues */
- if (phba->sli4_hba.nvme_wq) {
- for (qidx = 0; qidx < phba->cfg_nvme_io_channel;
- qidx++) {
- if (!phba->sli4_hba.nvme_wq[qidx])
- continue;
- if (phba->sli4_hba.nvme_wq[qidx]->queue_id ==
- queid) {
- /* Sanity check */
- rc = lpfc_idiag_que_param_check(
- phba->sli4_hba.nvme_wq[qidx],
- index, count);
- if (rc)
- goto error_out;
- idiag.ptr_private =
- phba->sli4_hba.nvme_wq[qidx];
- goto pass_check;
- }
- }
- }
goto error_out;
break;
case LPFC_IDIAG_RQ:
@@ -4866,6 +5403,16 @@ static const struct file_operations lpfc_debugfs_op_nodelist = {
.release = lpfc_debugfs_release,
};
+#undef lpfc_debugfs_op_multixripools
+static const struct file_operations lpfc_debugfs_op_multixripools = {
+ .owner = THIS_MODULE,
+ .open = lpfc_debugfs_multixripools_open,
+ .llseek = lpfc_debugfs_lseek,
+ .read = lpfc_debugfs_read,
+ .write = lpfc_debugfs_multixripools_write,
+ .release = lpfc_debugfs_release,
+};
+
#undef lpfc_debugfs_op_hbqinfo
static const struct file_operations lpfc_debugfs_op_hbqinfo = {
.owner = THIS_MODULE,
@@ -4875,6 +5422,18 @@ static const struct file_operations lpfc_debugfs_op_hbqinfo = {
.release = lpfc_debugfs_release,
};
+#ifdef LPFC_HDWQ_LOCK_STAT
+#undef lpfc_debugfs_op_lockstat
+static const struct file_operations lpfc_debugfs_op_lockstat = {
+ .owner = THIS_MODULE,
+ .open = lpfc_debugfs_lockstat_open,
+ .llseek = lpfc_debugfs_lseek,
+ .read = lpfc_debugfs_read,
+ .write = lpfc_debugfs_lockstat_write,
+ .release = lpfc_debugfs_release,
+};
+#endif
+
#undef lpfc_debugfs_op_dumpHBASlim
static const struct file_operations lpfc_debugfs_op_dumpHBASlim = {
.owner = THIS_MODULE,
@@ -4903,6 +5462,16 @@ static const struct file_operations lpfc_debugfs_op_nvmestat = {
.release = lpfc_debugfs_release,
};
+#undef lpfc_debugfs_op_scsistat
+static const struct file_operations lpfc_debugfs_op_scsistat = {
+ .owner = THIS_MODULE,
+ .open = lpfc_debugfs_scsistat_open,
+ .llseek = lpfc_debugfs_lseek,
+ .read = lpfc_debugfs_read,
+ .write = lpfc_debugfs_scsistat_write,
+ .release = lpfc_debugfs_release,
+};
+
#undef lpfc_debugfs_op_nvmektime
static const struct file_operations lpfc_debugfs_op_nvmektime = {
.owner = THIS_MODULE,
@@ -5280,11 +5849,6 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport)
if (!lpfc_debugfs_root) {
lpfc_debugfs_root = debugfs_create_dir("lpfc", NULL);
atomic_set(&lpfc_debugfs_hba_count, 0);
- if (!lpfc_debugfs_root) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
- "0408 Cannot create debugfs root\n");
- goto debug_failed;
- }
}
if (!lpfc_debugfs_start_time)
lpfc_debugfs_start_time = jiffies;
@@ -5295,25 +5859,42 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport)
pport_setup = true;
phba->hba_debugfs_root =
debugfs_create_dir(name, lpfc_debugfs_root);
- if (!phba->hba_debugfs_root) {
+ atomic_inc(&lpfc_debugfs_hba_count);
+ atomic_set(&phba->debugfs_vport_count, 0);
+
+ /* Multi-XRI pools */
+ snprintf(name, sizeof(name), "multixripools");
+ phba->debug_multixri_pools =
+ debugfs_create_file(name, S_IFREG | 0644,
+ phba->hba_debugfs_root,
+ phba,
+ &lpfc_debugfs_op_multixripools);
+ if (!phba->debug_multixri_pools) {
lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
- "0412 Cannot create debugfs hba\n");
+ "0527 Cannot create debugfs multixripools\n");
goto debug_failed;
}
- atomic_inc(&lpfc_debugfs_hba_count);
- atomic_set(&phba->debugfs_vport_count, 0);
/* Setup hbqinfo */
snprintf(name, sizeof(name), "hbqinfo");
phba->debug_hbqinfo =
- debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
- phba->hba_debugfs_root,
- phba, &lpfc_debugfs_op_hbqinfo);
- if (!phba->debug_hbqinfo) {
+ debugfs_create_file(name, S_IFREG | 0644,
+ phba->hba_debugfs_root,
+ phba, &lpfc_debugfs_op_hbqinfo);
+
+#ifdef LPFC_HDWQ_LOCK_STAT
+ /* Setup lockstat */
+ snprintf(name, sizeof(name), "lockstat");
+ phba->debug_lockstat =
+ debugfs_create_file(name, S_IFREG | 0644,
+ phba->hba_debugfs_root,
+ phba, &lpfc_debugfs_op_lockstat);
+ if (!phba->debug_lockstat) {
lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
- "0411 Cannot create debugfs hbqinfo\n");
+ "0913 Cant create debugfs lockstat\n");
goto debug_failed;
}
+#endif
/* Setup dumpHBASlim */
if (phba->sli_rev < LPFC_SLI_REV4) {
@@ -5323,12 +5904,6 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport)
S_IFREG|S_IRUGO|S_IWUSR,
phba->hba_debugfs_root,
phba, &lpfc_debugfs_op_dumpHBASlim);
- if (!phba->debug_dumpHBASlim) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
- "0413 Cannot create debugfs "
- "dumpHBASlim\n");
- goto debug_failed;
- }
} else
phba->debug_dumpHBASlim = NULL;
@@ -5340,12 +5915,6 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport)
S_IFREG|S_IRUGO|S_IWUSR,
phba->hba_debugfs_root,
phba, &lpfc_debugfs_op_dumpHostSlim);
- if (!phba->debug_dumpHostSlim) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
- "0414 Cannot create debugfs "
- "dumpHostSlim\n");
- goto debug_failed;
- }
} else
phba->debug_dumpHostSlim = NULL;
@@ -5355,11 +5924,6 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport)
debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
phba->hba_debugfs_root,
phba, &lpfc_debugfs_op_dumpData);
- if (!phba->debug_dumpData) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
- "0800 Cannot create debugfs dumpData\n");
- goto debug_failed;
- }
/* Setup dumpDif */
snprintf(name, sizeof(name), "dumpDif");
@@ -5367,11 +5931,6 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport)
debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
phba->hba_debugfs_root,
phba, &lpfc_debugfs_op_dumpDif);
- if (!phba->debug_dumpDif) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
- "0801 Cannot create debugfs dumpDif\n");
- goto debug_failed;
- }
/* Setup DIF Error Injections */
snprintf(name, sizeof(name), "InjErrLBA");
@@ -5379,11 +5938,6 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport)
debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
phba->hba_debugfs_root,
phba, &lpfc_debugfs_op_dif_err);
- if (!phba->debug_InjErrLBA) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
- "0807 Cannot create debugfs InjErrLBA\n");
- goto debug_failed;
- }
phba->lpfc_injerr_lba = LPFC_INJERR_LBA_OFF;
snprintf(name, sizeof(name), "InjErrNPortID");
@@ -5391,88 +5945,48 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport)
debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
phba->hba_debugfs_root,
phba, &lpfc_debugfs_op_dif_err);
- if (!phba->debug_InjErrNPortID) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
- "0809 Cannot create debugfs InjErrNPortID\n");
- goto debug_failed;
- }
snprintf(name, sizeof(name), "InjErrWWPN");
phba->debug_InjErrWWPN =
debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
phba->hba_debugfs_root,
phba, &lpfc_debugfs_op_dif_err);
- if (!phba->debug_InjErrWWPN) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
- "0810 Cannot create debugfs InjErrWWPN\n");
- goto debug_failed;
- }
snprintf(name, sizeof(name), "writeGuardInjErr");
phba->debug_writeGuard =
debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
phba->hba_debugfs_root,
phba, &lpfc_debugfs_op_dif_err);
- if (!phba->debug_writeGuard) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
- "0802 Cannot create debugfs writeGuard\n");
- goto debug_failed;
- }
snprintf(name, sizeof(name), "writeAppInjErr");
phba->debug_writeApp =
debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
phba->hba_debugfs_root,
phba, &lpfc_debugfs_op_dif_err);
- if (!phba->debug_writeApp) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
- "0803 Cannot create debugfs writeApp\n");
- goto debug_failed;
- }
snprintf(name, sizeof(name), "writeRefInjErr");
phba->debug_writeRef =
debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
phba->hba_debugfs_root,
phba, &lpfc_debugfs_op_dif_err);
- if (!phba->debug_writeRef) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
- "0804 Cannot create debugfs writeRef\n");
- goto debug_failed;
- }
snprintf(name, sizeof(name), "readGuardInjErr");
phba->debug_readGuard =
debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
phba->hba_debugfs_root,
phba, &lpfc_debugfs_op_dif_err);
- if (!phba->debug_readGuard) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
- "0808 Cannot create debugfs readGuard\n");
- goto debug_failed;
- }
snprintf(name, sizeof(name), "readAppInjErr");
phba->debug_readApp =
debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
phba->hba_debugfs_root,
phba, &lpfc_debugfs_op_dif_err);
- if (!phba->debug_readApp) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
- "0805 Cannot create debugfs readApp\n");
- goto debug_failed;
- }
snprintf(name, sizeof(name), "readRefInjErr");
phba->debug_readRef =
debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
phba->hba_debugfs_root,
phba, &lpfc_debugfs_op_dif_err);
- if (!phba->debug_readRef) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
- "0806 Cannot create debugfs readApp\n");
- goto debug_failed;
- }
/* Setup slow ring trace */
if (lpfc_debugfs_max_slow_ring_trc) {
@@ -5496,12 +6010,6 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport)
debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
phba->hba_debugfs_root,
phba, &lpfc_debugfs_op_slow_ring_trc);
- if (!phba->debug_slow_ring_trc) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
- "0415 Cannot create debugfs "
- "slow_ring_trace\n");
- goto debug_failed;
- }
if (!phba->slow_ring_trc) {
phba->slow_ring_trc = kmalloc(
(sizeof(struct lpfc_debugfs_trc) *
@@ -5524,11 +6032,6 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport)
debugfs_create_file(name, 0644,
phba->hba_debugfs_root,
phba, &lpfc_debugfs_op_nvmeio_trc);
- if (!phba->debug_nvmeio_trc) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
- "0574 No create debugfs nvmeio_trc\n");
- goto debug_failed;
- }
atomic_set(&phba->nvmeio_trc_cnt, 0);
if (lpfc_debugfs_max_nvmeio_trc) {
@@ -5576,11 +6079,6 @@ nvmeio_off:
if (!vport->vport_debugfs_root) {
vport->vport_debugfs_root =
debugfs_create_dir(name, phba->hba_debugfs_root);
- if (!vport->vport_debugfs_root) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
- "0417 Can't create debugfs\n");
- goto debug_failed;
- }
atomic_inc(&phba->debugfs_vport_count);
}
@@ -5617,31 +6115,26 @@ nvmeio_off:
debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
vport->vport_debugfs_root,
vport, &lpfc_debugfs_op_disc_trc);
- if (!vport->debug_disc_trc) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
- "0419 Cannot create debugfs "
- "discovery_trace\n");
- goto debug_failed;
- }
snprintf(name, sizeof(name), "nodelist");
vport->debug_nodelist =
debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
vport->vport_debugfs_root,
vport, &lpfc_debugfs_op_nodelist);
- if (!vport->debug_nodelist) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
- "2985 Can't create debugfs nodelist\n");
- goto debug_failed;
- }
snprintf(name, sizeof(name), "nvmestat");
vport->debug_nvmestat =
debugfs_create_file(name, 0644,
vport->vport_debugfs_root,
vport, &lpfc_debugfs_op_nvmestat);
- if (!vport->debug_nvmestat) {
+
+ snprintf(name, sizeof(name), "scsistat");
+ vport->debug_scsistat =
+ debugfs_create_file(name, 0644,
+ vport->vport_debugfs_root,
+ vport, &lpfc_debugfs_op_scsistat);
+ if (!vport->debug_scsistat) {
lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
- "0811 Cannot create debugfs nvmestat\n");
+ "0914 Cannot create debugfs scsistat\n");
goto debug_failed;
}
@@ -5650,22 +6143,12 @@ nvmeio_off:
debugfs_create_file(name, 0644,
vport->vport_debugfs_root,
vport, &lpfc_debugfs_op_nvmektime);
- if (!vport->debug_nvmektime) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
- "0815 Cannot create debugfs nvmektime\n");
- goto debug_failed;
- }
snprintf(name, sizeof(name), "cpucheck");
vport->debug_cpucheck =
debugfs_create_file(name, 0644,
vport->vport_debugfs_root,
vport, &lpfc_debugfs_op_cpucheck);
- if (!vport->debug_cpucheck) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
- "0819 Cannot create debugfs cpucheck\n");
- goto debug_failed;
- }
/*
* The following section is for additional directories/files for the
@@ -5685,11 +6168,6 @@ nvmeio_off:
if (!phba->idiag_root) {
phba->idiag_root =
debugfs_create_dir(name, phba->hba_debugfs_root);
- if (!phba->idiag_root) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
- "2922 Can't create idiag debugfs\n");
- goto debug_failed;
- }
/* Initialize iDiag data structure */
memset(&idiag, 0, sizeof(idiag));
}
@@ -5700,11 +6178,6 @@ nvmeio_off:
phba->idiag_pci_cfg =
debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
phba->idiag_root, phba, &lpfc_idiag_op_pciCfg);
- if (!phba->idiag_pci_cfg) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
- "2923 Can't create idiag debugfs\n");
- goto debug_failed;
- }
idiag.offset.last_rd = 0;
}
@@ -5714,11 +6187,6 @@ nvmeio_off:
phba->idiag_bar_acc =
debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
phba->idiag_root, phba, &lpfc_idiag_op_barAcc);
- if (!phba->idiag_bar_acc) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
- "3056 Can't create idiag debugfs\n");
- goto debug_failed;
- }
idiag.offset.last_rd = 0;
}
@@ -5728,11 +6196,6 @@ nvmeio_off:
phba->idiag_que_info =
debugfs_create_file(name, S_IFREG|S_IRUGO,
phba->idiag_root, phba, &lpfc_idiag_op_queInfo);
- if (!phba->idiag_que_info) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
- "2924 Can't create idiag debugfs\n");
- goto debug_failed;
- }
}
/* iDiag access PCI function queue */
@@ -5741,11 +6204,6 @@ nvmeio_off:
phba->idiag_que_acc =
debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
phba->idiag_root, phba, &lpfc_idiag_op_queAcc);
- if (!phba->idiag_que_acc) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
- "2926 Can't create idiag debugfs\n");
- goto debug_failed;
- }
}
/* iDiag access PCI function doorbell registers */
@@ -5754,11 +6212,6 @@ nvmeio_off:
phba->idiag_drb_acc =
debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
phba->idiag_root, phba, &lpfc_idiag_op_drbAcc);
- if (!phba->idiag_drb_acc) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
- "2927 Can't create idiag debugfs\n");
- goto debug_failed;
- }
}
/* iDiag access PCI function control registers */
@@ -5767,11 +6220,6 @@ nvmeio_off:
phba->idiag_ctl_acc =
debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
phba->idiag_root, phba, &lpfc_idiag_op_ctlAcc);
- if (!phba->idiag_ctl_acc) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
- "2981 Can't create idiag debugfs\n");
- goto debug_failed;
- }
}
/* iDiag access mbox commands */
@@ -5780,11 +6228,6 @@ nvmeio_off:
phba->idiag_mbx_acc =
debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
phba->idiag_root, phba, &lpfc_idiag_op_mbxAcc);
- if (!phba->idiag_mbx_acc) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
- "2980 Can't create idiag debugfs\n");
- goto debug_failed;
- }
}
/* iDiag extents access commands */
@@ -5796,12 +6239,6 @@ nvmeio_off:
S_IFREG|S_IRUGO|S_IWUSR,
phba->idiag_root, phba,
&lpfc_idiag_op_extAcc);
- if (!phba->idiag_ext_acc) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
- "2986 Cant create "
- "idiag debugfs\n");
- goto debug_failed;
- }
}
}
@@ -5839,6 +6276,9 @@ lpfc_debugfs_terminate(struct lpfc_vport *vport)
debugfs_remove(vport->debug_nvmestat); /* nvmestat */
vport->debug_nvmestat = NULL;
+ debugfs_remove(vport->debug_scsistat); /* scsistat */
+ vport->debug_scsistat = NULL;
+
debugfs_remove(vport->debug_nvmektime); /* nvmektime */
vport->debug_nvmektime = NULL;
@@ -5853,9 +6293,16 @@ lpfc_debugfs_terminate(struct lpfc_vport *vport)
if (atomic_read(&phba->debugfs_vport_count) == 0) {
+ debugfs_remove(phba->debug_multixri_pools); /* multixripools*/
+ phba->debug_multixri_pools = NULL;
+
debugfs_remove(phba->debug_hbqinfo); /* hbqinfo */
phba->debug_hbqinfo = NULL;
+#ifdef LPFC_HDWQ_LOCK_STAT
+ debugfs_remove(phba->debug_lockstat); /* lockstat */
+ phba->debug_lockstat = NULL;
+#endif
debugfs_remove(phba->debug_dumpHBASlim); /* HBASlim */
phba->debug_dumpHBASlim = NULL;
@@ -5988,11 +6435,13 @@ lpfc_debug_dump_all_queues(struct lpfc_hba *phba)
lpfc_debug_dump_wq(phba, DUMP_ELS, 0);
lpfc_debug_dump_wq(phba, DUMP_NVMELS, 0);
- for (idx = 0; idx < phba->cfg_fcp_io_channel; idx++)
+ for (idx = 0; idx < phba->cfg_hdw_queue; idx++)
lpfc_debug_dump_wq(phba, DUMP_FCP, idx);
- for (idx = 0; idx < phba->cfg_nvme_io_channel; idx++)
- lpfc_debug_dump_wq(phba, DUMP_NVME, idx);
+ if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) {
+ for (idx = 0; idx < phba->cfg_hdw_queue; idx++)
+ lpfc_debug_dump_wq(phba, DUMP_NVME, idx);
+ }
lpfc_debug_dump_hdr_rq(phba);
lpfc_debug_dump_dat_rq(phba);
@@ -6003,15 +6452,17 @@ lpfc_debug_dump_all_queues(struct lpfc_hba *phba)
lpfc_debug_dump_cq(phba, DUMP_ELS, 0);
lpfc_debug_dump_cq(phba, DUMP_NVMELS, 0);
- for (idx = 0; idx < phba->cfg_fcp_io_channel; idx++)
+ for (idx = 0; idx < phba->cfg_hdw_queue; idx++)
lpfc_debug_dump_cq(phba, DUMP_FCP, idx);
- for (idx = 0; idx < phba->cfg_nvme_io_channel; idx++)
- lpfc_debug_dump_cq(phba, DUMP_NVME, idx);
+ if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) {
+ for (idx = 0; idx < phba->cfg_hdw_queue; idx++)
+ lpfc_debug_dump_cq(phba, DUMP_NVME, idx);
+ }
/*
* Dump Event Queues (EQs)
*/
- for (idx = 0; idx < phba->io_channel_irqs; idx++)
+ for (idx = 0; idx < phba->cfg_hdw_queue; idx++)
lpfc_debug_dump_hba_eq(phba, idx);
}
diff --git a/drivers/scsi/lpfc/lpfc_debugfs.h b/drivers/scsi/lpfc/lpfc_debugfs.h
index 30efc7bf91bd..93ab7dfb8ee0 100644
--- a/drivers/scsi/lpfc/lpfc_debugfs.h
+++ b/drivers/scsi/lpfc/lpfc_debugfs.h
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term *
+ * Copyright (C) 2017-2019 Broadcom. All Rights Reserved. The term *
* “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. *
* Copyright (C) 2007-2011 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
@@ -50,6 +50,9 @@
#define LPFC_CPUCHECK_SIZE 8192
#define LPFC_NVMEIO_TRC_SIZE 8192
+/* scsistat output buffer size */
+#define LPFC_SCSISTAT_SIZE 8192
+
#define LPFC_DEBUG_OUT_LINE_SZ 80
/*
@@ -284,6 +287,9 @@ struct lpfc_idiag {
#endif
+/* multixripool output buffer size */
+#define LPFC_DUMP_MULTIXRIPOOL_SIZE 8192
+
enum {
DUMP_FCP,
DUMP_NVME,
@@ -410,10 +416,10 @@ lpfc_debug_dump_wq(struct lpfc_hba *phba, int qtype, int wqidx)
char *qtypestr;
if (qtype == DUMP_FCP) {
- wq = phba->sli4_hba.fcp_wq[wqidx];
+ wq = phba->sli4_hba.hdwq[wqidx].fcp_wq;
qtypestr = "FCP";
} else if (qtype == DUMP_NVME) {
- wq = phba->sli4_hba.nvme_wq[wqidx];
+ wq = phba->sli4_hba.hdwq[wqidx].nvme_wq;
qtypestr = "NVME";
} else if (qtype == DUMP_MBX) {
wq = phba->sli4_hba.mbx_wq;
@@ -454,14 +460,15 @@ lpfc_debug_dump_cq(struct lpfc_hba *phba, int qtype, int wqidx)
int eqidx;
/* fcp/nvme wq and cq are 1:1, thus same indexes */
+ eq = NULL;
if (qtype == DUMP_FCP) {
- wq = phba->sli4_hba.fcp_wq[wqidx];
- cq = phba->sli4_hba.fcp_cq[wqidx];
+ wq = phba->sli4_hba.hdwq[wqidx].fcp_wq;
+ cq = phba->sli4_hba.hdwq[wqidx].fcp_cq;
qtypestr = "FCP";
} else if (qtype == DUMP_NVME) {
- wq = phba->sli4_hba.nvme_wq[wqidx];
- cq = phba->sli4_hba.nvme_cq[wqidx];
+ wq = phba->sli4_hba.hdwq[wqidx].nvme_wq;
+ cq = phba->sli4_hba.hdwq[wqidx].nvme_cq;
qtypestr = "NVME";
} else if (qtype == DUMP_MBX) {
wq = phba->sli4_hba.mbx_wq;
@@ -478,17 +485,17 @@ lpfc_debug_dump_cq(struct lpfc_hba *phba, int qtype, int wqidx)
} else
return;
- for (eqidx = 0; eqidx < phba->io_channel_irqs; eqidx++) {
- if (cq->assoc_qid == phba->sli4_hba.hba_eq[eqidx]->queue_id)
+ for (eqidx = 0; eqidx < phba->cfg_hdw_queue; eqidx++) {
+ eq = phba->sli4_hba.hdwq[eqidx].hba_eq;
+ if (cq->assoc_qid == eq->queue_id)
break;
}
- if (eqidx == phba->io_channel_irqs) {
+ if (eqidx == phba->cfg_hdw_queue) {
pr_err("Couldn't find EQ for CQ. Using EQ[0]\n");
eqidx = 0;
+ eq = phba->sli4_hba.hdwq[0].hba_eq;
}
- eq = phba->sli4_hba.hba_eq[eqidx];
-
if (qtype == DUMP_FCP || qtype == DUMP_NVME)
pr_err("%s CQ: WQ[Idx:%d|Qid%d]->CQ[Idx%d|Qid%d]"
"->EQ[Idx:%d|Qid:%d]:\n",
@@ -516,7 +523,7 @@ lpfc_debug_dump_hba_eq(struct lpfc_hba *phba, int qidx)
{
struct lpfc_queue *qp;
- qp = phba->sli4_hba.hba_eq[qidx];
+ qp = phba->sli4_hba.hdwq[qidx].hba_eq;
pr_err("EQ[Idx:%d|Qid:%d]\n", qidx, qp->queue_id);
@@ -564,21 +571,21 @@ lpfc_debug_dump_wq_by_id(struct lpfc_hba *phba, int qid)
{
int wq_idx;
- for (wq_idx = 0; wq_idx < phba->cfg_fcp_io_channel; wq_idx++)
- if (phba->sli4_hba.fcp_wq[wq_idx]->queue_id == qid)
+ for (wq_idx = 0; wq_idx < phba->cfg_hdw_queue; wq_idx++)
+ if (phba->sli4_hba.hdwq[wq_idx].fcp_wq->queue_id == qid)
break;
- if (wq_idx < phba->cfg_fcp_io_channel) {
+ if (wq_idx < phba->cfg_hdw_queue) {
pr_err("FCP WQ[Idx:%d|Qid:%d]\n", wq_idx, qid);
- lpfc_debug_dump_q(phba->sli4_hba.fcp_wq[wq_idx]);
+ lpfc_debug_dump_q(phba->sli4_hba.hdwq[wq_idx].fcp_wq);
return;
}
- for (wq_idx = 0; wq_idx < phba->cfg_nvme_io_channel; wq_idx++)
- if (phba->sli4_hba.nvme_wq[wq_idx]->queue_id == qid)
+ for (wq_idx = 0; wq_idx < phba->cfg_hdw_queue; wq_idx++)
+ if (phba->sli4_hba.hdwq[wq_idx].nvme_wq->queue_id == qid)
break;
- if (wq_idx < phba->cfg_nvme_io_channel) {
+ if (wq_idx < phba->cfg_hdw_queue) {
pr_err("NVME WQ[Idx:%d|Qid:%d]\n", wq_idx, qid);
- lpfc_debug_dump_q(phba->sli4_hba.nvme_wq[wq_idx]);
+ lpfc_debug_dump_q(phba->sli4_hba.hdwq[wq_idx].nvme_wq);
return;
}
@@ -646,23 +653,23 @@ lpfc_debug_dump_cq_by_id(struct lpfc_hba *phba, int qid)
{
int cq_idx;
- for (cq_idx = 0; cq_idx < phba->cfg_fcp_io_channel; cq_idx++)
- if (phba->sli4_hba.fcp_cq[cq_idx]->queue_id == qid)
+ for (cq_idx = 0; cq_idx < phba->cfg_hdw_queue; cq_idx++)
+ if (phba->sli4_hba.hdwq[cq_idx].fcp_cq->queue_id == qid)
break;
- if (cq_idx < phba->cfg_fcp_io_channel) {
+ if (cq_idx < phba->cfg_hdw_queue) {
pr_err("FCP CQ[Idx:%d|Qid:%d]\n", cq_idx, qid);
- lpfc_debug_dump_q(phba->sli4_hba.fcp_cq[cq_idx]);
+ lpfc_debug_dump_q(phba->sli4_hba.hdwq[cq_idx].fcp_cq);
return;
}
- for (cq_idx = 0; cq_idx < phba->cfg_nvme_io_channel; cq_idx++)
- if (phba->sli4_hba.nvme_cq[cq_idx]->queue_id == qid)
+ for (cq_idx = 0; cq_idx < phba->cfg_hdw_queue; cq_idx++)
+ if (phba->sli4_hba.hdwq[cq_idx].nvme_cq->queue_id == qid)
break;
- if (cq_idx < phba->cfg_nvme_io_channel) {
+ if (cq_idx < phba->cfg_hdw_queue) {
pr_err("NVME CQ[Idx:%d|Qid:%d]\n", cq_idx, qid);
- lpfc_debug_dump_q(phba->sli4_hba.nvme_cq[cq_idx]);
+ lpfc_debug_dump_q(phba->sli4_hba.hdwq[cq_idx].nvme_cq);
return;
}
@@ -697,13 +704,13 @@ lpfc_debug_dump_eq_by_id(struct lpfc_hba *phba, int qid)
{
int eq_idx;
- for (eq_idx = 0; eq_idx < phba->io_channel_irqs; eq_idx++)
- if (phba->sli4_hba.hba_eq[eq_idx]->queue_id == qid)
+ for (eq_idx = 0; eq_idx < phba->cfg_hdw_queue; eq_idx++)
+ if (phba->sli4_hba.hdwq[eq_idx].hba_eq->queue_id == qid)
break;
- if (eq_idx < phba->io_channel_irqs) {
+ if (eq_idx < phba->cfg_hdw_queue) {
printk(KERN_ERR "FCP EQ[Idx:%d|Qid:%d]\n", eq_idx, qid);
- lpfc_debug_dump_q(phba->sli4_hba.hba_eq[eq_idx]);
+ lpfc_debug_dump_q(phba->sli4_hba.hdwq[eq_idx].hba_eq);
return;
}
}
diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c
index b3a4789468c3..fc077cb87900 100644
--- a/drivers/scsi/lpfc/lpfc_els.c
+++ b/drivers/scsi/lpfc/lpfc_els.c
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term *
+ * Copyright (C) 2017-2019 Broadcom. All Rights Reserved. The term *
* “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. *
* Copyright (C) 2004-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
@@ -2827,8 +2827,8 @@ out:
!(vport->fc_flag & FC_PT2PT_PLOGI)) {
phba->pport->fc_myDID = 0;
- if ((phba->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) ||
- (phba->cfg_enable_fc4_type == LPFC_ENABLE_NVME)) {
+ if ((vport->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) ||
+ (vport->cfg_enable_fc4_type == LPFC_ENABLE_NVME)) {
if (phba->nvmet_support)
lpfc_nvmet_update_targetport(phba);
else
diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c
index b183b882d506..aa4961a2caf8 100644
--- a/drivers/scsi/lpfc/lpfc_hbadisc.c
+++ b/drivers/scsi/lpfc/lpfc_hbadisc.c
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term *
+ * Copyright (C) 2017-2019 Broadcom. All Rights Reserved. The term *
* “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. *
* Copyright (C) 2004-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
@@ -638,8 +638,6 @@ lpfc_work_done(struct lpfc_hba *phba)
if (phba->pci_dev_grp == LPFC_PCI_DEV_OC) {
if (phba->hba_flag & HBA_RRQ_ACTIVE)
lpfc_handle_rrq_active(phba);
- if (phba->hba_flag & FCP_XRI_ABORT_EVENT)
- lpfc_sli4_fcp_xri_abort_event_proc(phba);
if (phba->hba_flag & ELS_XRI_ABORT_EVENT)
lpfc_sli4_els_xri_abort_event_proc(phba);
if (phba->hba_flag & ASYNC_EVENT)
@@ -859,10 +857,9 @@ lpfc_port_link_failure(struct lpfc_vport *vport)
void
lpfc_linkdown_port(struct lpfc_vport *vport)
{
- struct lpfc_hba *phba = vport->phba;
struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
- if (phba->cfg_enable_fc4_type != LPFC_ENABLE_NVME)
+ if (vport->cfg_enable_fc4_type != LPFC_ENABLE_NVME)
fc_host_post_event(shost, fc_get_event_number(),
FCH_EVT_LINKDOWN, 0);
@@ -925,8 +922,8 @@ lpfc_linkdown(struct lpfc_hba *phba)
vports[i]->fc_myDID = 0;
- if ((phba->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) ||
- (phba->cfg_enable_fc4_type == LPFC_ENABLE_NVME)) {
+ if ((vport->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) ||
+ (vport->cfg_enable_fc4_type == LPFC_ENABLE_NVME)) {
if (phba->nvmet_support)
lpfc_nvmet_update_targetport(phba);
else
@@ -1012,7 +1009,7 @@ lpfc_linkup_port(struct lpfc_vport *vport)
(vport != phba->pport))
return;
- if (phba->cfg_enable_fc4_type != LPFC_ENABLE_NVME)
+ if (vport->cfg_enable_fc4_type != LPFC_ENABLE_NVME)
fc_host_post_event(shost, fc_get_event_number(),
FCH_EVT_LINKUP, 0);
@@ -3660,8 +3657,8 @@ lpfc_mbx_cmpl_reg_vpi(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
spin_unlock_irq(shost->host_lock);
vport->fc_myDID = 0;
- if ((phba->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) ||
- (phba->cfg_enable_fc4_type == LPFC_ENABLE_NVME)) {
+ if ((vport->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) ||
+ (vport->cfg_enable_fc4_type == LPFC_ENABLE_NVME)) {
if (phba->nvmet_support)
lpfc_nvmet_update_targetport(phba);
else
@@ -3923,11 +3920,9 @@ lpfc_mbx_cmpl_fabric_reg_login(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
int
lpfc_issue_gidft(struct lpfc_vport *vport)
{
- struct lpfc_hba *phba = vport->phba;
-
/* Good status, issue CT Request to NameServer */
- if ((phba->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) ||
- (phba->cfg_enable_fc4_type == LPFC_ENABLE_FCP)) {
+ if ((vport->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) ||
+ (vport->cfg_enable_fc4_type == LPFC_ENABLE_FCP)) {
if (lpfc_ns_cmd(vport, SLI_CTNS_GID_FT, 0, SLI_CTPT_FCP)) {
/* Cannot issue NameServer FCP Query, so finish up
* discovery
@@ -3942,8 +3937,8 @@ lpfc_issue_gidft(struct lpfc_vport *vport)
vport->gidft_inp++;
}
- if ((phba->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) ||
- (phba->cfg_enable_fc4_type == LPFC_ENABLE_NVME)) {
+ if ((vport->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) ||
+ (vport->cfg_enable_fc4_type == LPFC_ENABLE_NVME)) {
if (lpfc_ns_cmd(vport, SLI_CTNS_GID_FT, 0, SLI_CTPT_NVME)) {
/* Cannot issue NameServer NVME Query, so finish up
* discovery
@@ -4059,12 +4054,12 @@ out:
lpfc_ns_cmd(vport, SLI_CTNS_RSPN_ID, 0, 0);
lpfc_ns_cmd(vport, SLI_CTNS_RFT_ID, 0, 0);
- if ((phba->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) ||
- (phba->cfg_enable_fc4_type == LPFC_ENABLE_FCP))
+ if ((vport->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) ||
+ (vport->cfg_enable_fc4_type == LPFC_ENABLE_FCP))
lpfc_ns_cmd(vport, SLI_CTNS_RFF_ID, 0, FC_TYPE_FCP);
- if ((phba->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) ||
- (phba->cfg_enable_fc4_type == LPFC_ENABLE_NVME))
+ if ((vport->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) ||
+ (vport->cfg_enable_fc4_type == LPFC_ENABLE_NVME))
lpfc_ns_cmd(vport, SLI_CTNS_RFF_ID, 0,
FC_TYPE_NVME);
@@ -4100,7 +4095,7 @@ lpfc_register_remote_port(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
struct fc_rport_identifiers rport_ids;
struct lpfc_hba *phba = vport->phba;
- if (phba->cfg_enable_fc4_type == LPFC_ENABLE_NVME)
+ if (vport->cfg_enable_fc4_type == LPFC_ENABLE_NVME)
return;
/* Remote port has reappeared. Re-register w/ FC transport */
@@ -4175,9 +4170,8 @@ lpfc_unregister_remote_port(struct lpfc_nodelist *ndlp)
{
struct fc_rport *rport = ndlp->rport;
struct lpfc_vport *vport = ndlp->vport;
- struct lpfc_hba *phba = vport->phba;
- if (phba->cfg_enable_fc4_type == LPFC_ENABLE_NVME)
+ if (vport->cfg_enable_fc4_type == LPFC_ENABLE_NVME)
return;
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_RPORT,
diff --git a/drivers/scsi/lpfc/lpfc_hw4.h b/drivers/scsi/lpfc/lpfc_hw4.h
index c15b9b6fb840..ff875b833192 100644
--- a/drivers/scsi/lpfc/lpfc_hw4.h
+++ b/drivers/scsi/lpfc/lpfc_hw4.h
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term *
+ * Copyright (C) 2017-2019 Broadcom. All Rights Reserved. The term *
* “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. *
* Copyright (C) 2009-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
@@ -194,7 +194,7 @@ struct lpfc_sli_intf {
#define LPFC_ACT_INTR_CNT 4
/* Algrithmns for scheduling FCP commands to WQs */
-#define LPFC_FCP_SCHED_ROUND_ROBIN 0
+#define LPFC_FCP_SCHED_BY_HDWQ 0
#define LPFC_FCP_SCHED_BY_CPU 1
/* Algrithmns for NameServer Query after RSCN */
@@ -208,12 +208,18 @@ struct lpfc_sli_intf {
/* Configuration of Interrupts / sec for entire HBA port */
#define LPFC_MIN_IMAX 5000
#define LPFC_MAX_IMAX 5000000
-#define LPFC_DEF_IMAX 150000
+#define LPFC_DEF_IMAX 0
+
+#define LPFC_IMAX_THRESHOLD 1000
+#define LPFC_MAX_AUTO_EQ_DELAY 120
+#define LPFC_EQ_DELAY_STEP 15
+#define LPFC_EQD_ISR_TRIGGER 20000
+/* 1s intervals */
+#define LPFC_EQ_DELAY_MSECS 1000
#define LPFC_MIN_CPU_MAP 0
-#define LPFC_MAX_CPU_MAP 2
+#define LPFC_MAX_CPU_MAP 1
#define LPFC_HBA_CPU_MAP 1
-#define LPFC_DRIVER_CPU_MAP 2 /* Default */
/* PORT_CAPABILITIES constants. */
#define LPFC_MAX_SUPPORTED_PAGES 8
diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c
index e1129260ed18..7fcdaed3fa94 100644
--- a/drivers/scsi/lpfc/lpfc_init.c
+++ b/drivers/scsi/lpfc/lpfc_init.c
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term *
+ * Copyright (C) 2017-2019 Broadcom. All Rights Reserved. The term *
* “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. *
* Copyright (C) 2004-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
@@ -37,6 +37,7 @@
#include <linux/miscdevice.h>
#include <linux/percpu.h>
#include <linux/msi.h>
+#include <linux/irq.h>
#include <linux/bitops.h>
#include <scsi/scsi.h>
@@ -71,7 +72,6 @@ unsigned long _dump_buf_dif_order;
spinlock_t _dump_buf_lock;
/* Used when mapping IRQ vectors in a driver centric manner */
-uint16_t *lpfc_used_cpu;
uint32_t lpfc_present_cpu;
static void lpfc_get_hba_model_desc(struct lpfc_hba *, uint8_t *, uint8_t *);
@@ -93,6 +93,8 @@ static void lpfc_sli4_cq_event_release_all(struct lpfc_hba *);
static void lpfc_sli4_disable_intr(struct lpfc_hba *);
static uint32_t lpfc_sli4_enable_intr(struct lpfc_hba *, uint32_t);
static void lpfc_sli4_oas_verify(struct lpfc_hba *phba);
+static uint16_t lpfc_find_eq_handle(struct lpfc_hba *, uint16_t);
+static uint16_t lpfc_find_cpu_handle(struct lpfc_hba *, uint16_t, int);
static struct scsi_transport_template *lpfc_transport_template = NULL;
static struct scsi_transport_template *lpfc_vport_transport_template = NULL;
@@ -1037,14 +1039,14 @@ lpfc_hba_down_post_s3(struct lpfc_hba *phba)
static int
lpfc_hba_down_post_s4(struct lpfc_hba *phba)
{
- struct lpfc_scsi_buf *psb, *psb_next;
+ struct lpfc_io_buf *psb, *psb_next;
struct lpfc_nvmet_rcv_ctx *ctxp, *ctxp_next;
+ struct lpfc_sli4_hdw_queue *qp;
LIST_HEAD(aborts);
LIST_HEAD(nvme_aborts);
LIST_HEAD(nvmet_aborts);
- unsigned long iflag = 0;
struct lpfc_sglq *sglq_entry = NULL;
- int cnt;
+ int cnt, idx;
lpfc_sli_hbqbuf_free_all(phba);
@@ -1071,55 +1073,65 @@ lpfc_hba_down_post_s4(struct lpfc_hba *phba)
spin_unlock(&phba->sli4_hba.sgl_list_lock);
- /* abts_scsi_buf_list_lock required because worker thread uses this
+
+ /* abts_xxxx_buf_list_lock required because worker thread uses this
* list.
*/
- if (phba->cfg_enable_fc4_type & LPFC_ENABLE_FCP) {
- spin_lock(&phba->sli4_hba.abts_scsi_buf_list_lock);
- list_splice_init(&phba->sli4_hba.lpfc_abts_scsi_buf_list,
- &aborts);
- spin_unlock(&phba->sli4_hba.abts_scsi_buf_list_lock);
- }
-
- if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) {
- spin_lock(&phba->sli4_hba.abts_nvme_buf_list_lock);
- list_splice_init(&phba->sli4_hba.lpfc_abts_nvme_buf_list,
- &nvme_aborts);
- list_splice_init(&phba->sli4_hba.lpfc_abts_nvmet_ctx_list,
- &nvmet_aborts);
- spin_unlock(&phba->sli4_hba.abts_nvme_buf_list_lock);
- }
+ cnt = 0;
+ for (idx = 0; idx < phba->cfg_hdw_queue; idx++) {
+ qp = &phba->sli4_hba.hdwq[idx];
- spin_unlock_irq(&phba->hbalock);
-
- list_for_each_entry_safe(psb, psb_next, &aborts, list) {
- psb->pCmd = NULL;
- psb->status = IOSTAT_SUCCESS;
- }
- spin_lock_irqsave(&phba->scsi_buf_list_put_lock, iflag);
- list_splice(&aborts, &phba->lpfc_scsi_buf_list_put);
- spin_unlock_irqrestore(&phba->scsi_buf_list_put_lock, iflag);
+ spin_lock(&qp->abts_scsi_buf_list_lock);
+ list_splice_init(&qp->lpfc_abts_scsi_buf_list,
+ &aborts);
- if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) {
- cnt = 0;
- list_for_each_entry_safe(psb, psb_next, &nvme_aborts, list) {
+ list_for_each_entry_safe(psb, psb_next, &aborts, list) {
psb->pCmd = NULL;
psb->status = IOSTAT_SUCCESS;
cnt++;
}
- spin_lock_irqsave(&phba->nvme_buf_list_put_lock, iflag);
- phba->put_nvme_bufs += cnt;
- list_splice(&nvme_aborts, &phba->lpfc_nvme_buf_list_put);
- spin_unlock_irqrestore(&phba->nvme_buf_list_put_lock, iflag);
+ spin_lock(&qp->io_buf_list_put_lock);
+ list_splice_init(&aborts, &qp->lpfc_io_buf_list_put);
+ qp->put_io_bufs += qp->abts_scsi_io_bufs;
+ qp->abts_scsi_io_bufs = 0;
+ spin_unlock(&qp->io_buf_list_put_lock);
+ spin_unlock(&qp->abts_scsi_buf_list_lock);
+
+ if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) {
+ spin_lock(&qp->abts_nvme_buf_list_lock);
+ list_splice_init(&qp->lpfc_abts_nvme_buf_list,
+ &nvme_aborts);
+ list_for_each_entry_safe(psb, psb_next, &nvme_aborts,
+ list) {
+ psb->pCmd = NULL;
+ psb->status = IOSTAT_SUCCESS;
+ cnt++;
+ }
+ spin_lock(&qp->io_buf_list_put_lock);
+ qp->put_io_bufs += qp->abts_nvme_io_bufs;
+ qp->abts_nvme_io_bufs = 0;
+ list_splice_init(&nvme_aborts,
+ &qp->lpfc_io_buf_list_put);
+ spin_unlock(&qp->io_buf_list_put_lock);
+ spin_unlock(&qp->abts_nvme_buf_list_lock);
+
+ }
+ }
+ if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) {
+ spin_lock(&phba->sli4_hba.abts_nvmet_buf_list_lock);
+ list_splice_init(&phba->sli4_hba.lpfc_abts_nvmet_ctx_list,
+ &nvmet_aborts);
+ spin_unlock(&phba->sli4_hba.abts_nvmet_buf_list_lock);
list_for_each_entry_safe(ctxp, ctxp_next, &nvmet_aborts, list) {
ctxp->flag &= ~(LPFC_NVMET_XBUSY | LPFC_NVMET_ABORT_OP);
lpfc_nvmet_ctxbuf_post(phba, ctxp->ctxbuf);
}
}
+ spin_unlock_irq(&phba->hbalock);
lpfc_sli4_free_sp_events(phba);
- return 0;
+ return cnt;
}
/**
@@ -1239,6 +1251,96 @@ lpfc_hb_mbox_cmpl(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmboxq)
return;
}
+static void
+lpfc_hb_eq_delay_work(struct work_struct *work)
+{
+ struct lpfc_hba *phba = container_of(to_delayed_work(work),
+ struct lpfc_hba, eq_delay_work);
+ struct lpfc_eq_intr_info *eqi, *eqi_new;
+ struct lpfc_queue *eq, *eq_next;
+ unsigned char *eqcnt = NULL;
+ uint32_t usdelay;
+ int i;
+
+ if (!phba->cfg_auto_imax || phba->pport->load_flag & FC_UNLOADING)
+ return;
+
+ if (phba->link_state == LPFC_HBA_ERROR ||
+ phba->pport->fc_flag & FC_OFFLINE_MODE)
+ goto requeue;
+
+ eqcnt = kcalloc(num_possible_cpus(), sizeof(unsigned char),
+ GFP_KERNEL);
+ if (!eqcnt)
+ goto requeue;
+
+ for (i = 0; i < phba->cfg_irq_chann; i++) {
+ eq = phba->sli4_hba.hdwq[i].hba_eq;
+ if (eq && eqcnt[eq->last_cpu] < 2)
+ eqcnt[eq->last_cpu]++;
+ continue;
+ }
+
+ for_each_present_cpu(i) {
+ if (phba->cfg_irq_chann > 1 && eqcnt[i] < 2)
+ continue;
+
+ eqi = per_cpu_ptr(phba->sli4_hba.eq_info, i);
+
+ usdelay = (eqi->icnt / LPFC_IMAX_THRESHOLD) *
+ LPFC_EQ_DELAY_STEP;
+ if (usdelay > LPFC_MAX_AUTO_EQ_DELAY)
+ usdelay = LPFC_MAX_AUTO_EQ_DELAY;
+
+ eqi->icnt = 0;
+
+ list_for_each_entry_safe(eq, eq_next, &eqi->list, cpu_list) {
+ if (eq->last_cpu != i) {
+ eqi_new = per_cpu_ptr(phba->sli4_hba.eq_info,
+ eq->last_cpu);
+ list_move_tail(&eq->cpu_list, &eqi_new->list);
+ continue;
+ }
+ if (usdelay != eq->q_mode)
+ lpfc_modify_hba_eq_delay(phba, eq->hdwq, 1,
+ usdelay);
+ }
+ }
+
+ kfree(eqcnt);
+
+requeue:
+ queue_delayed_work(phba->wq, &phba->eq_delay_work,
+ msecs_to_jiffies(LPFC_EQ_DELAY_MSECS));
+}
+
+/**
+ * lpfc_hb_mxp_handler - Multi-XRI pools handler to adjust XRI distribution
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * For each heartbeat, this routine does some heuristic methods to adjust
+ * XRI distribution. The goal is to fully utilize free XRIs.
+ **/
+static void lpfc_hb_mxp_handler(struct lpfc_hba *phba)
+{
+ u32 i;
+ u32 hwq_count;
+
+ hwq_count = phba->cfg_hdw_queue;
+ for (i = 0; i < hwq_count; i++) {
+ /* Adjust XRIs in private pool */
+ lpfc_adjust_pvt_pool_count(phba, i);
+
+ /* Adjust high watermark */
+ lpfc_adjust_high_watermark(phba, i);
+
+#ifdef LPFC_MXP_STAT
+ /* Snapshot pbl, pvt and busy count */
+ lpfc_snapshot_mxp(phba, i);
+#endif
+ }
+}
+
/**
* lpfc_hb_timeout_handler - The HBA-timer timeout handler
* @phba: pointer to lpfc hba data structure.
@@ -1264,16 +1366,11 @@ lpfc_hb_timeout_handler(struct lpfc_hba *phba)
int retval, i;
struct lpfc_sli *psli = &phba->sli;
LIST_HEAD(completions);
- struct lpfc_queue *qp;
- unsigned long time_elapsed;
- uint32_t tick_cqe, max_cqe, val;
- uint64_t tot, data1, data2, data3;
- struct lpfc_nvmet_tgtport *tgtp;
- struct lpfc_register reg_data;
- struct nvme_fc_local_port *localport;
- struct lpfc_nvme_lport *lport;
- struct lpfc_nvme_ctrl_stat *cstat;
- void __iomem *eqdreg = phba->sli4_hba.u.if_type2.EQDregaddr;
+
+ if (phba->cfg_xri_rebalancing) {
+ /* Multi-XRI pools handler */
+ lpfc_hb_mxp_handler(phba);
+ }
vports = lpfc_create_vport_work_array(phba);
if (vports != NULL)
@@ -1288,107 +1385,6 @@ lpfc_hb_timeout_handler(struct lpfc_hba *phba)
(phba->pport->fc_flag & FC_OFFLINE_MODE))
return;
- if (phba->cfg_auto_imax) {
- if (!phba->last_eqdelay_time) {
- phba->last_eqdelay_time = jiffies;
- goto skip_eqdelay;
- }
- time_elapsed = jiffies - phba->last_eqdelay_time;
- phba->last_eqdelay_time = jiffies;
-
- tot = 0xffff;
- /* Check outstanding IO count */
- if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) {
- if (phba->nvmet_support) {
- tgtp = phba->targetport->private;
- /* Calculate outstanding IOs */
- tot = atomic_read(&tgtp->rcv_fcp_cmd_drop);
- tot += atomic_read(&tgtp->xmt_fcp_release);
- tot = atomic_read(&tgtp->rcv_fcp_cmd_in) - tot;
- } else {
- localport = phba->pport->localport;
- if (!localport || !localport->private)
- goto skip_eqdelay;
- lport = (struct lpfc_nvme_lport *)
- localport->private;
- tot = 0;
- for (i = 0;
- i < phba->cfg_nvme_io_channel; i++) {
- cstat = &lport->cstat[i];
- data1 = atomic_read(
- &cstat->fc4NvmeInputRequests);
- data2 = atomic_read(
- &cstat->fc4NvmeOutputRequests);
- data3 = atomic_read(
- &cstat->fc4NvmeControlRequests);
- tot += (data1 + data2 + data3);
- tot -= atomic_read(
- &cstat->fc4NvmeIoCmpls);
- }
- }
- }
-
- /* Interrupts per sec per EQ */
- val = phba->cfg_fcp_imax / phba->io_channel_irqs;
- tick_cqe = val / CONFIG_HZ; /* Per tick per EQ */
-
- /* Assume 1 CQE/ISR, calc max CQEs allowed for time duration */
- max_cqe = time_elapsed * tick_cqe;
-
- for (i = 0; i < phba->io_channel_irqs; i++) {
- /* Fast-path EQ */
- qp = phba->sli4_hba.hba_eq[i];
- if (!qp)
- continue;
-
- /* Use no EQ delay if we don't have many outstanding
- * IOs, or if we are only processing 1 CQE/ISR or less.
- * Otherwise, assume we can process up to lpfc_fcp_imax
- * interrupts per HBA.
- */
- if (tot < LPFC_NODELAY_MAX_IO ||
- qp->EQ_cqe_cnt <= max_cqe)
- val = 0;
- else
- val = phba->cfg_fcp_imax;
-
- if (phba->sli.sli_flag & LPFC_SLI_USE_EQDR) {
- /* Use EQ Delay Register method */
-
- /* Convert for EQ Delay register */
- if (val) {
- /* First, interrupts per sec per EQ */
- val = phba->cfg_fcp_imax /
- phba->io_channel_irqs;
-
- /* us delay between each interrupt */
- val = LPFC_SEC_TO_USEC / val;
- }
- if (val != qp->q_mode) {
- reg_data.word0 = 0;
- bf_set(lpfc_sliport_eqdelay_id,
- &reg_data, qp->queue_id);
- bf_set(lpfc_sliport_eqdelay_delay,
- &reg_data, val);
- writel(reg_data.word0, eqdreg);
- }
- } else {
- /* Use mbox command method */
- if (val != qp->q_mode)
- lpfc_modify_hba_eq_delay(phba, i,
- 1, val);
- }
-
- /*
- * val is cfg_fcp_imax or 0 for mbox delay or us delay
- * between interrupts for EQDR.
- */
- qp->q_mode = val;
- qp->EQ_cqe_cnt = 0;
- }
- }
-
-skip_eqdelay:
spin_lock_irq(&phba->pport->work_port_lock);
if (time_after(phba->last_completion_time +
@@ -2943,7 +2939,9 @@ lpfc_sli4_stop_fcf_redisc_wait_timer(struct lpfc_hba *phba)
void
lpfc_stop_hba_timers(struct lpfc_hba *phba)
{
- lpfc_stop_vport_timers(phba->pport);
+ if (phba->pport)
+ lpfc_stop_vport_timers(phba->pport);
+ cancel_delayed_work_sync(&phba->eq_delay_work);
del_timer_sync(&phba->sli.mbox_tmo);
del_timer_sync(&phba->fabric_block_timer);
del_timer_sync(&phba->eratt_poll);
@@ -3071,6 +3069,242 @@ lpfc_sli4_node_prep(struct lpfc_hba *phba)
}
/**
+ * lpfc_create_expedite_pool - create expedite pool
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine moves a batch of XRIs from lpfc_io_buf_list_put of HWQ 0
+ * to expedite pool. Mark them as expedite.
+ **/
+void lpfc_create_expedite_pool(struct lpfc_hba *phba)
+{
+ struct lpfc_sli4_hdw_queue *qp;
+ struct lpfc_io_buf *lpfc_ncmd;
+ struct lpfc_io_buf *lpfc_ncmd_next;
+ struct lpfc_epd_pool *epd_pool;
+ unsigned long iflag;
+
+ epd_pool = &phba->epd_pool;
+ qp = &phba->sli4_hba.hdwq[0];
+
+ spin_lock_init(&epd_pool->lock);
+ spin_lock_irqsave(&qp->io_buf_list_put_lock, iflag);
+ spin_lock(&epd_pool->lock);
+ INIT_LIST_HEAD(&epd_pool->list);
+ list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next,
+ &qp->lpfc_io_buf_list_put, list) {
+ list_move_tail(&lpfc_ncmd->list, &epd_pool->list);
+ lpfc_ncmd->expedite = true;
+ qp->put_io_bufs--;
+ epd_pool->count++;
+ if (epd_pool->count >= XRI_BATCH)
+ break;
+ }
+ spin_unlock(&epd_pool->lock);
+ spin_unlock_irqrestore(&qp->io_buf_list_put_lock, iflag);
+}
+
+/**
+ * lpfc_destroy_expedite_pool - destroy expedite pool
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine returns XRIs from expedite pool to lpfc_io_buf_list_put
+ * of HWQ 0. Clear the mark.
+ **/
+void lpfc_destroy_expedite_pool(struct lpfc_hba *phba)
+{
+ struct lpfc_sli4_hdw_queue *qp;
+ struct lpfc_io_buf *lpfc_ncmd;
+ struct lpfc_io_buf *lpfc_ncmd_next;
+ struct lpfc_epd_pool *epd_pool;
+ unsigned long iflag;
+
+ epd_pool = &phba->epd_pool;
+ qp = &phba->sli4_hba.hdwq[0];
+
+ spin_lock_irqsave(&qp->io_buf_list_put_lock, iflag);
+ spin_lock(&epd_pool->lock);
+ list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next,
+ &epd_pool->list, list) {
+ list_move_tail(&lpfc_ncmd->list,
+ &qp->lpfc_io_buf_list_put);
+ lpfc_ncmd->flags = false;
+ qp->put_io_bufs++;
+ epd_pool->count--;
+ }
+ spin_unlock(&epd_pool->lock);
+ spin_unlock_irqrestore(&qp->io_buf_list_put_lock, iflag);
+}
+
+/**
+ * lpfc_create_multixri_pools - create multi-XRI pools
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine initialize public, private per HWQ. Then, move XRIs from
+ * lpfc_io_buf_list_put to public pool. High and low watermark are also
+ * Initialized.
+ **/
+void lpfc_create_multixri_pools(struct lpfc_hba *phba)
+{
+ u32 i, j;
+ u32 hwq_count;
+ u32 count_per_hwq;
+ struct lpfc_io_buf *lpfc_ncmd;
+ struct lpfc_io_buf *lpfc_ncmd_next;
+ unsigned long iflag;
+ struct lpfc_sli4_hdw_queue *qp;
+ struct lpfc_multixri_pool *multixri_pool;
+ struct lpfc_pbl_pool *pbl_pool;
+ struct lpfc_pvt_pool *pvt_pool;
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "1234 num_hdw_queue=%d num_present_cpu=%d common_xri_cnt=%d\n",
+ phba->cfg_hdw_queue, phba->sli4_hba.num_present_cpu,
+ phba->sli4_hba.io_xri_cnt);
+
+ if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME)
+ lpfc_create_expedite_pool(phba);
+
+ hwq_count = phba->cfg_hdw_queue;
+ count_per_hwq = phba->sli4_hba.io_xri_cnt / hwq_count;
+
+ for (i = 0; i < hwq_count; i++) {
+ multixri_pool = kzalloc(sizeof(*multixri_pool), GFP_KERNEL);
+
+ if (!multixri_pool) {
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "1238 Failed to allocate memory for "
+ "multixri_pool\n");
+
+ if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME)
+ lpfc_destroy_expedite_pool(phba);
+
+ j = 0;
+ while (j < i) {
+ qp = &phba->sli4_hba.hdwq[j];
+ kfree(qp->p_multixri_pool);
+ j++;
+ }
+ phba->cfg_xri_rebalancing = 0;
+ return;
+ }
+
+ qp = &phba->sli4_hba.hdwq[i];
+ qp->p_multixri_pool = multixri_pool;
+
+ multixri_pool->xri_limit = count_per_hwq;
+ multixri_pool->rrb_next_hwqid = i;
+
+ /* Deal with public free xri pool */
+ pbl_pool = &multixri_pool->pbl_pool;
+ spin_lock_init(&pbl_pool->lock);
+ spin_lock_irqsave(&qp->io_buf_list_put_lock, iflag);
+ spin_lock(&pbl_pool->lock);
+ INIT_LIST_HEAD(&pbl_pool->list);
+ list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next,
+ &qp->lpfc_io_buf_list_put, list) {
+ list_move_tail(&lpfc_ncmd->list, &pbl_pool->list);
+ qp->put_io_bufs--;
+ pbl_pool->count++;
+ }
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "1235 Moved %d buffers from PUT list over to pbl_pool[%d]\n",
+ pbl_pool->count, i);
+ spin_unlock(&pbl_pool->lock);
+ spin_unlock_irqrestore(&qp->io_buf_list_put_lock, iflag);
+
+ /* Deal with private free xri pool */
+ pvt_pool = &multixri_pool->pvt_pool;
+ pvt_pool->high_watermark = multixri_pool->xri_limit / 2;
+ pvt_pool->low_watermark = XRI_BATCH;
+ spin_lock_init(&pvt_pool->lock);
+ spin_lock_irqsave(&pvt_pool->lock, iflag);
+ INIT_LIST_HEAD(&pvt_pool->list);
+ pvt_pool->count = 0;
+ spin_unlock_irqrestore(&pvt_pool->lock, iflag);
+ }
+}
+
+/**
+ * lpfc_destroy_multixri_pools - destroy multi-XRI pools
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine returns XRIs from public/private to lpfc_io_buf_list_put.
+ **/
+void lpfc_destroy_multixri_pools(struct lpfc_hba *phba)
+{
+ u32 i;
+ u32 hwq_count;
+ struct lpfc_io_buf *lpfc_ncmd;
+ struct lpfc_io_buf *lpfc_ncmd_next;
+ unsigned long iflag;
+ struct lpfc_sli4_hdw_queue *qp;
+ struct lpfc_multixri_pool *multixri_pool;
+ struct lpfc_pbl_pool *pbl_pool;
+ struct lpfc_pvt_pool *pvt_pool;
+
+ if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME)
+ lpfc_destroy_expedite_pool(phba);
+
+ hwq_count = phba->cfg_hdw_queue;
+
+ for (i = 0; i < hwq_count; i++) {
+ qp = &phba->sli4_hba.hdwq[i];
+ multixri_pool = qp->p_multixri_pool;
+ if (!multixri_pool)
+ continue;
+
+ qp->p_multixri_pool = NULL;
+
+ spin_lock_irqsave(&qp->io_buf_list_put_lock, iflag);
+
+ /* Deal with public free xri pool */
+ pbl_pool = &multixri_pool->pbl_pool;
+ spin_lock(&pbl_pool->lock);
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "1236 Moving %d buffers from pbl_pool[%d] TO PUT list\n",
+ pbl_pool->count, i);
+
+ list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next,
+ &pbl_pool->list, list) {
+ list_move_tail(&lpfc_ncmd->list,
+ &qp->lpfc_io_buf_list_put);
+ qp->put_io_bufs++;
+ pbl_pool->count--;
+ }
+
+ INIT_LIST_HEAD(&pbl_pool->list);
+ pbl_pool->count = 0;
+
+ spin_unlock(&pbl_pool->lock);
+
+ /* Deal with private free xri pool */
+ pvt_pool = &multixri_pool->pvt_pool;
+ spin_lock(&pvt_pool->lock);
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "1237 Moving %d buffers from pvt_pool[%d] TO PUT list\n",
+ pvt_pool->count, i);
+
+ list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next,
+ &pvt_pool->list, list) {
+ list_move_tail(&lpfc_ncmd->list,
+ &qp->lpfc_io_buf_list_put);
+ qp->put_io_bufs++;
+ pvt_pool->count--;
+ }
+
+ INIT_LIST_HEAD(&pvt_pool->list);
+ pvt_pool->count = 0;
+
+ spin_unlock(&pvt_pool->lock);
+ spin_unlock_irqrestore(&qp->io_buf_list_put_lock, iflag);
+
+ kfree(multixri_pool);
+ }
+}
+
+/**
* lpfc_online - Initialize and bring a HBA online
* @phba: pointer to lpfc hba data structure.
*
@@ -3152,6 +3386,9 @@ lpfc_online(struct lpfc_hba *phba)
}
lpfc_destroy_vport_work_array(phba, vports);
+ if (phba->cfg_xri_rebalancing)
+ lpfc_create_multixri_pools(phba);
+
lpfc_unblock_mgmt_io(phba);
return 0;
}
@@ -3310,6 +3547,9 @@ lpfc_offline(struct lpfc_hba *phba)
spin_unlock_irq(shost->host_lock);
}
lpfc_destroy_vport_work_array(phba, vports);
+
+ if (phba->cfg_xri_rebalancing)
+ lpfc_destroy_multixri_pools(phba);
}
/**
@@ -3323,7 +3563,7 @@ lpfc_offline(struct lpfc_hba *phba)
static void
lpfc_scsi_free(struct lpfc_hba *phba)
{
- struct lpfc_scsi_buf *sb, *sb_next;
+ struct lpfc_io_buf *sb, *sb_next;
if (!(phba->cfg_enable_fc4_type & LPFC_ENABLE_FCP))
return;
@@ -3355,50 +3595,57 @@ lpfc_scsi_free(struct lpfc_hba *phba)
spin_unlock(&phba->scsi_buf_list_get_lock);
spin_unlock_irq(&phba->hbalock);
}
+
/**
- * lpfc_nvme_free - Free all the NVME buffers and IOCBs from driver lists
+ * lpfc_io_free - Free all the IO buffers and IOCBs from driver lists
* @phba: pointer to lpfc hba data structure.
*
- * This routine is to free all the NVME buffers and IOCBs from the driver
+ * This routine is to free all the IO buffers and IOCBs from the driver
* list back to kernel. It is called from lpfc_pci_remove_one to free
* the internal resources before the device is removed from the system.
**/
-static void
-lpfc_nvme_free(struct lpfc_hba *phba)
+void
+lpfc_io_free(struct lpfc_hba *phba)
{
- struct lpfc_nvme_buf *lpfc_ncmd, *lpfc_ncmd_next;
-
- if (!(phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME))
- return;
+ struct lpfc_io_buf *lpfc_ncmd, *lpfc_ncmd_next;
+ struct lpfc_sli4_hdw_queue *qp;
+ int idx;
spin_lock_irq(&phba->hbalock);
- /* Release all the lpfc_nvme_bufs maintained by this host. */
- spin_lock(&phba->nvme_buf_list_put_lock);
- list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next,
- &phba->lpfc_nvme_buf_list_put, list) {
- list_del(&lpfc_ncmd->list);
- phba->put_nvme_bufs--;
- dma_pool_free(phba->lpfc_sg_dma_buf_pool, lpfc_ncmd->data,
- lpfc_ncmd->dma_handle);
- kfree(lpfc_ncmd);
- phba->total_nvme_bufs--;
+ for (idx = 0; idx < phba->cfg_hdw_queue; idx++) {
+ qp = &phba->sli4_hba.hdwq[idx];
+ /* Release all the lpfc_nvme_bufs maintained by this host. */
+ spin_lock(&qp->io_buf_list_put_lock);
+ list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next,
+ &qp->lpfc_io_buf_list_put,
+ list) {
+ list_del(&lpfc_ncmd->list);
+ qp->put_io_bufs--;
+ dma_pool_free(phba->lpfc_sg_dma_buf_pool,
+ lpfc_ncmd->data, lpfc_ncmd->dma_handle);
+ kfree(lpfc_ncmd);
+ qp->total_io_bufs--;
+ }
+ spin_unlock(&qp->io_buf_list_put_lock);
+
+ spin_lock(&qp->io_buf_list_get_lock);
+ list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next,
+ &qp->lpfc_io_buf_list_get,
+ list) {
+ list_del(&lpfc_ncmd->list);
+ qp->get_io_bufs--;
+ dma_pool_free(phba->lpfc_sg_dma_buf_pool,
+ lpfc_ncmd->data, lpfc_ncmd->dma_handle);
+ kfree(lpfc_ncmd);
+ qp->total_io_bufs--;
+ }
+ spin_unlock(&qp->io_buf_list_get_lock);
}
- spin_unlock(&phba->nvme_buf_list_put_lock);
- spin_lock(&phba->nvme_buf_list_get_lock);
- list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next,
- &phba->lpfc_nvme_buf_list_get, list) {
- list_del(&lpfc_ncmd->list);
- phba->get_nvme_bufs--;
- dma_pool_free(phba->lpfc_sg_dma_buf_pool, lpfc_ncmd->data,
- lpfc_ncmd->dma_handle);
- kfree(lpfc_ncmd);
- phba->total_nvme_bufs--;
- }
- spin_unlock(&phba->nvme_buf_list_get_lock);
spin_unlock_irq(&phba->hbalock);
}
+
/**
* lpfc_sli4_els_sgl_update - update ELS xri-sgl sizing and mapping
* @phba: pointer to lpfc hba data structure.
@@ -3640,8 +3887,102 @@ out_free_mem:
return rc;
}
+int
+lpfc_io_buf_flush(struct lpfc_hba *phba, struct list_head *cbuf)
+{
+ LIST_HEAD(blist);
+ struct lpfc_sli4_hdw_queue *qp;
+ struct lpfc_io_buf *lpfc_cmd;
+ struct lpfc_io_buf *iobufp, *prev_iobufp;
+ int idx, cnt, xri, inserted;
+
+ cnt = 0;
+ for (idx = 0; idx < phba->cfg_hdw_queue; idx++) {
+ qp = &phba->sli4_hba.hdwq[idx];
+ spin_lock_irq(&qp->io_buf_list_get_lock);
+ spin_lock(&qp->io_buf_list_put_lock);
+
+ /* Take everything off the get and put lists */
+ list_splice_init(&qp->lpfc_io_buf_list_get, &blist);
+ list_splice(&qp->lpfc_io_buf_list_put, &blist);
+ INIT_LIST_HEAD(&qp->lpfc_io_buf_list_get);
+ INIT_LIST_HEAD(&qp->lpfc_io_buf_list_put);
+ cnt += qp->get_io_bufs + qp->put_io_bufs;
+ qp->get_io_bufs = 0;
+ qp->put_io_bufs = 0;
+ qp->total_io_bufs = 0;
+ spin_unlock(&qp->io_buf_list_put_lock);
+ spin_unlock_irq(&qp->io_buf_list_get_lock);
+ }
+
+ /*
+ * Take IO buffers off blist and put on cbuf sorted by XRI.
+ * This is because POST_SGL takes a sequential range of XRIs
+ * to post to the firmware.
+ */
+ for (idx = 0; idx < cnt; idx++) {
+ list_remove_head(&blist, lpfc_cmd, struct lpfc_io_buf, list);
+ if (!lpfc_cmd)
+ return cnt;
+ if (idx == 0) {
+ list_add_tail(&lpfc_cmd->list, cbuf);
+ continue;
+ }
+ xri = lpfc_cmd->cur_iocbq.sli4_xritag;
+ inserted = 0;
+ prev_iobufp = NULL;
+ list_for_each_entry(iobufp, cbuf, list) {
+ if (xri < iobufp->cur_iocbq.sli4_xritag) {
+ if (prev_iobufp)
+ list_add(&lpfc_cmd->list,
+ &prev_iobufp->list);
+ else
+ list_add(&lpfc_cmd->list, cbuf);
+ inserted = 1;
+ break;
+ }
+ prev_iobufp = iobufp;
+ }
+ if (!inserted)
+ list_add_tail(&lpfc_cmd->list, cbuf);
+ }
+ return cnt;
+}
+
+int
+lpfc_io_buf_replenish(struct lpfc_hba *phba, struct list_head *cbuf)
+{
+ struct lpfc_sli4_hdw_queue *qp;
+ struct lpfc_io_buf *lpfc_cmd;
+ int idx, cnt;
+
+ qp = phba->sli4_hba.hdwq;
+ cnt = 0;
+ while (!list_empty(cbuf)) {
+ for (idx = 0; idx < phba->cfg_hdw_queue; idx++) {
+ list_remove_head(cbuf, lpfc_cmd,
+ struct lpfc_io_buf, list);
+ if (!lpfc_cmd)
+ return cnt;
+ cnt++;
+ qp = &phba->sli4_hba.hdwq[idx];
+ lpfc_cmd->hdwq_no = idx;
+ lpfc_cmd->hdwq = qp;
+ lpfc_cmd->cur_iocbq.wqe_cmpl = NULL;
+ lpfc_cmd->cur_iocbq.iocb_cmpl = NULL;
+ spin_lock(&qp->io_buf_list_put_lock);
+ list_add_tail(&lpfc_cmd->list,
+ &qp->lpfc_io_buf_list_put);
+ qp->put_io_bufs++;
+ qp->total_io_bufs++;
+ spin_unlock(&qp->io_buf_list_put_lock);
+ }
+ }
+ return cnt;
+}
+
/**
- * lpfc_sli4_scsi_sgl_update - update xri-sgl sizing and mapping
+ * lpfc_sli4_io_sgl_update - update xri-sgl sizing and mapping
* @phba: pointer to lpfc hba data structure.
*
* This routine first calculates the sizes of the current els and allocated
@@ -3653,94 +3994,192 @@ out_free_mem:
* 0 - successful (for now, it always returns 0)
**/
int
-lpfc_sli4_scsi_sgl_update(struct lpfc_hba *phba)
+lpfc_sli4_io_sgl_update(struct lpfc_hba *phba)
{
- struct lpfc_scsi_buf *psb, *psb_next;
- uint16_t i, lxri, els_xri_cnt, scsi_xri_cnt;
- LIST_HEAD(scsi_sgl_list);
- int rc;
-
- /*
- * update on pci function's els xri-sgl list
- */
- els_xri_cnt = lpfc_sli4_get_els_iocb_cnt(phba);
- phba->total_scsi_bufs = 0;
+ struct lpfc_io_buf *lpfc_ncmd = NULL, *lpfc_ncmd_next = NULL;
+ uint16_t i, lxri, els_xri_cnt;
+ uint16_t io_xri_cnt, io_xri_max;
+ LIST_HEAD(io_sgl_list);
+ int rc, cnt;
/*
- * update on pci function's allocated scsi xri-sgl list
+ * update on pci function's allocated nvme xri-sgl list
*/
- /* maximum number of xris available for scsi buffers */
- phba->sli4_hba.scsi_xri_max = phba->sli4_hba.max_cfg_param.max_xri -
- els_xri_cnt;
- if (!(phba->cfg_enable_fc4_type & LPFC_ENABLE_FCP))
- return 0;
+ /* maximum number of xris available for nvme buffers */
+ els_xri_cnt = lpfc_sli4_get_els_iocb_cnt(phba);
+ io_xri_max = phba->sli4_hba.max_cfg_param.max_xri - els_xri_cnt;
+ phba->sli4_hba.io_xri_max = io_xri_max;
- if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME)
- phba->sli4_hba.scsi_xri_max = /* Split them up */
- (phba->sli4_hba.scsi_xri_max *
- phba->cfg_xri_split) / 100;
+ lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
+ "6074 Current allocated XRI sgl count:%d, "
+ "maximum XRI count:%d\n",
+ phba->sli4_hba.io_xri_cnt,
+ phba->sli4_hba.io_xri_max);
- spin_lock_irq(&phba->scsi_buf_list_get_lock);
- spin_lock(&phba->scsi_buf_list_put_lock);
- list_splice_init(&phba->lpfc_scsi_buf_list_get, &scsi_sgl_list);
- list_splice(&phba->lpfc_scsi_buf_list_put, &scsi_sgl_list);
- spin_unlock(&phba->scsi_buf_list_put_lock);
- spin_unlock_irq(&phba->scsi_buf_list_get_lock);
+ cnt = lpfc_io_buf_flush(phba, &io_sgl_list);
- lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
- "6060 Current allocated SCSI xri-sgl count:%d, "
- "maximum SCSI xri count:%d (split:%d)\n",
- phba->sli4_hba.scsi_xri_cnt,
- phba->sli4_hba.scsi_xri_max, phba->cfg_xri_split);
-
- if (phba->sli4_hba.scsi_xri_cnt > phba->sli4_hba.scsi_xri_max) {
- /* max scsi xri shrinked below the allocated scsi buffers */
- scsi_xri_cnt = phba->sli4_hba.scsi_xri_cnt -
- phba->sli4_hba.scsi_xri_max;
- /* release the extra allocated scsi buffers */
- for (i = 0; i < scsi_xri_cnt; i++) {
- list_remove_head(&scsi_sgl_list, psb,
- struct lpfc_scsi_buf, list);
- if (psb) {
+ if (phba->sli4_hba.io_xri_cnt > phba->sli4_hba.io_xri_max) {
+ /* max nvme xri shrunk below the allocated nvme buffers */
+ io_xri_cnt = phba->sli4_hba.io_xri_cnt -
+ phba->sli4_hba.io_xri_max;
+ /* release the extra allocated nvme buffers */
+ for (i = 0; i < io_xri_cnt; i++) {
+ list_remove_head(&io_sgl_list, lpfc_ncmd,
+ struct lpfc_io_buf, list);
+ if (lpfc_ncmd) {
dma_pool_free(phba->lpfc_sg_dma_buf_pool,
- psb->data, psb->dma_handle);
- kfree(psb);
+ lpfc_ncmd->data,
+ lpfc_ncmd->dma_handle);
+ kfree(lpfc_ncmd);
}
}
- spin_lock_irq(&phba->scsi_buf_list_get_lock);
- phba->sli4_hba.scsi_xri_cnt -= scsi_xri_cnt;
- spin_unlock_irq(&phba->scsi_buf_list_get_lock);
+ phba->sli4_hba.io_xri_cnt -= io_xri_cnt;
}
- /* update xris associated to remaining allocated scsi buffers */
- psb = NULL;
- psb_next = NULL;
- list_for_each_entry_safe(psb, psb_next, &scsi_sgl_list, list) {
+ /* update xris associated to remaining allocated nvme buffers */
+ lpfc_ncmd = NULL;
+ lpfc_ncmd_next = NULL;
+ phba->sli4_hba.io_xri_cnt = cnt;
+ list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next,
+ &io_sgl_list, list) {
lxri = lpfc_sli4_next_xritag(phba);
if (lxri == NO_XRI) {
lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
- "2560 Failed to allocate xri for "
- "scsi buffer\n");
+ "6075 Failed to allocate xri for "
+ "nvme buffer\n");
rc = -ENOMEM;
goto out_free_mem;
}
- psb->cur_iocbq.sli4_lxritag = lxri;
- psb->cur_iocbq.sli4_xritag = phba->sli4_hba.xri_ids[lxri];
+ lpfc_ncmd->cur_iocbq.sli4_lxritag = lxri;
+ lpfc_ncmd->cur_iocbq.sli4_xritag = phba->sli4_hba.xri_ids[lxri];
}
- spin_lock_irq(&phba->scsi_buf_list_get_lock);
- spin_lock(&phba->scsi_buf_list_put_lock);
- list_splice_init(&scsi_sgl_list, &phba->lpfc_scsi_buf_list_get);
- INIT_LIST_HEAD(&phba->lpfc_scsi_buf_list_put);
- spin_unlock(&phba->scsi_buf_list_put_lock);
- spin_unlock_irq(&phba->scsi_buf_list_get_lock);
+ cnt = lpfc_io_buf_replenish(phba, &io_sgl_list);
return 0;
out_free_mem:
- lpfc_scsi_free(phba);
+ lpfc_io_free(phba);
return rc;
}
+/**
+ * lpfc_new_io_buf - IO buffer allocator for HBA with SLI4 IF spec
+ * @vport: The virtual port for which this call being executed.
+ * @num_to_allocate: The requested number of buffers to allocate.
+ *
+ * This routine allocates nvme buffers for device with SLI-4 interface spec,
+ * the nvme buffer contains all the necessary information needed to initiate
+ * an I/O. After allocating up to @num_to_allocate IO buffers and put
+ * them on a list, it post them to the port by using SGL block post.
+ *
+ * Return codes:
+ * int - number of IO buffers that were allocated and posted.
+ * 0 = failure, less than num_to_alloc is a partial failure.
+ **/
+int
+lpfc_new_io_buf(struct lpfc_hba *phba, int num_to_alloc)
+{
+ struct lpfc_io_buf *lpfc_ncmd;
+ struct lpfc_iocbq *pwqeq;
+ uint16_t iotag, lxri = 0;
+ int bcnt, num_posted;
+ LIST_HEAD(prep_nblist);
+ LIST_HEAD(post_nblist);
+ LIST_HEAD(nvme_nblist);
+
+ /* Sanity check to ensure our sizing is right for both SCSI and NVME */
+ if (sizeof(struct lpfc_io_buf) > LPFC_COMMON_IO_BUF_SZ) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_FCP,
+ "6426 Common buffer size %zd exceeds %d\n",
+ sizeof(struct lpfc_io_buf),
+ LPFC_COMMON_IO_BUF_SZ);
+ return 0;
+ }
+
+ phba->sli4_hba.io_xri_cnt = 0;
+ for (bcnt = 0; bcnt < num_to_alloc; bcnt++) {
+ lpfc_ncmd = kzalloc(LPFC_COMMON_IO_BUF_SZ, GFP_KERNEL);
+ if (!lpfc_ncmd)
+ break;
+ /*
+ * Get memory from the pci pool to map the virt space to
+ * pci bus space for an I/O. The DMA buffer includes the
+ * number of SGE's necessary to support the sg_tablesize.
+ */
+ lpfc_ncmd->data = dma_pool_alloc(phba->lpfc_sg_dma_buf_pool,
+ GFP_KERNEL,
+ &lpfc_ncmd->dma_handle);
+ if (!lpfc_ncmd->data) {
+ kfree(lpfc_ncmd);
+ break;
+ }
+ memset(lpfc_ncmd->data, 0, phba->cfg_sg_dma_buf_size);
+
+ /*
+ * 4K Page alignment is CRITICAL to BlockGuard, double check
+ * to be sure.
+ */
+ if ((phba->sli3_options & LPFC_SLI3_BG_ENABLED) &&
+ (((unsigned long)(lpfc_ncmd->data) &
+ (unsigned long)(SLI4_PAGE_SIZE - 1)) != 0)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_FCP,
+ "3369 Memory alignment err: addr=%lx\n",
+ (unsigned long)lpfc_ncmd->data);
+ dma_pool_free(phba->lpfc_sg_dma_buf_pool,
+ lpfc_ncmd->data, lpfc_ncmd->dma_handle);
+ kfree(lpfc_ncmd);
+ break;
+ }
+
+ lxri = lpfc_sli4_next_xritag(phba);
+ if (lxri == NO_XRI) {
+ dma_pool_free(phba->lpfc_sg_dma_buf_pool,
+ lpfc_ncmd->data, lpfc_ncmd->dma_handle);
+ kfree(lpfc_ncmd);
+ break;
+ }
+ pwqeq = &lpfc_ncmd->cur_iocbq;
+
+ /* Allocate iotag for lpfc_ncmd->cur_iocbq. */
+ iotag = lpfc_sli_next_iotag(phba, pwqeq);
+ if (iotag == 0) {
+ dma_pool_free(phba->lpfc_sg_dma_buf_pool,
+ lpfc_ncmd->data, lpfc_ncmd->dma_handle);
+ kfree(lpfc_ncmd);
+ lpfc_printf_log(phba, KERN_ERR, LOG_NVME_IOERR,
+ "6121 Failed to allocate IOTAG for"
+ " XRI:0x%x\n", lxri);
+ lpfc_sli4_free_xri(phba, lxri);
+ break;
+ }
+ pwqeq->sli4_lxritag = lxri;
+ pwqeq->sli4_xritag = phba->sli4_hba.xri_ids[lxri];
+ pwqeq->context1 = lpfc_ncmd;
+
+ /* Initialize local short-hand pointers. */
+ lpfc_ncmd->dma_sgl = lpfc_ncmd->data;
+ lpfc_ncmd->dma_phys_sgl = lpfc_ncmd->dma_handle;
+ lpfc_ncmd->cur_iocbq.context1 = lpfc_ncmd;
+ spin_lock_init(&lpfc_ncmd->buf_lock);
+
+ /* add the nvme buffer to a post list */
+ list_add_tail(&lpfc_ncmd->list, &post_nblist);
+ phba->sli4_hba.io_xri_cnt++;
+ }
+ lpfc_printf_log(phba, KERN_INFO, LOG_NVME,
+ "6114 Allocate %d out of %d requested new NVME "
+ "buffers\n", bcnt, num_to_alloc);
+
+ /* post the list of nvme buffer sgls to port if available */
+ if (!list_empty(&post_nblist))
+ num_posted = lpfc_sli4_post_io_sgl_list(
+ phba, &post_nblist, bcnt);
+ else
+ num_posted = 0;
+
+ return num_posted;
+}
+
static uint64_t
lpfc_get_wwpn(struct lpfc_hba *phba)
{
@@ -3777,111 +4216,6 @@ lpfc_get_wwpn(struct lpfc_hba *phba)
}
/**
- * lpfc_sli4_nvme_sgl_update - update xri-sgl sizing and mapping
- * @phba: pointer to lpfc hba data structure.
- *
- * This routine first calculates the sizes of the current els and allocated
- * scsi sgl lists, and then goes through all sgls to updates the physical
- * XRIs assigned due to port function reset. During port initialization, the
- * current els and allocated scsi sgl lists are 0s.
- *
- * Return codes
- * 0 - successful (for now, it always returns 0)
- **/
-int
-lpfc_sli4_nvme_sgl_update(struct lpfc_hba *phba)
-{
- struct lpfc_nvme_buf *lpfc_ncmd = NULL, *lpfc_ncmd_next = NULL;
- uint16_t i, lxri, els_xri_cnt;
- uint16_t nvme_xri_cnt, nvme_xri_max;
- LIST_HEAD(nvme_sgl_list);
- int rc, cnt;
-
- phba->total_nvme_bufs = 0;
- phba->get_nvme_bufs = 0;
- phba->put_nvme_bufs = 0;
-
- if (!(phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME))
- return 0;
- /*
- * update on pci function's allocated nvme xri-sgl list
- */
-
- /* maximum number of xris available for nvme buffers */
- els_xri_cnt = lpfc_sli4_get_els_iocb_cnt(phba);
- nvme_xri_max = phba->sli4_hba.max_cfg_param.max_xri - els_xri_cnt;
- phba->sli4_hba.nvme_xri_max = nvme_xri_max;
- phba->sli4_hba.nvme_xri_max -= phba->sli4_hba.scsi_xri_max;
-
- lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
- "6074 Current allocated NVME xri-sgl count:%d, "
- "maximum NVME xri count:%d\n",
- phba->sli4_hba.nvme_xri_cnt,
- phba->sli4_hba.nvme_xri_max);
-
- spin_lock_irq(&phba->nvme_buf_list_get_lock);
- spin_lock(&phba->nvme_buf_list_put_lock);
- list_splice_init(&phba->lpfc_nvme_buf_list_get, &nvme_sgl_list);
- list_splice(&phba->lpfc_nvme_buf_list_put, &nvme_sgl_list);
- cnt = phba->get_nvme_bufs + phba->put_nvme_bufs;
- phba->get_nvme_bufs = 0;
- phba->put_nvme_bufs = 0;
- spin_unlock(&phba->nvme_buf_list_put_lock);
- spin_unlock_irq(&phba->nvme_buf_list_get_lock);
-
- if (phba->sli4_hba.nvme_xri_cnt > phba->sli4_hba.nvme_xri_max) {
- /* max nvme xri shrunk below the allocated nvme buffers */
- spin_lock_irq(&phba->nvme_buf_list_get_lock);
- nvme_xri_cnt = phba->sli4_hba.nvme_xri_cnt -
- phba->sli4_hba.nvme_xri_max;
- spin_unlock_irq(&phba->nvme_buf_list_get_lock);
- /* release the extra allocated nvme buffers */
- for (i = 0; i < nvme_xri_cnt; i++) {
- list_remove_head(&nvme_sgl_list, lpfc_ncmd,
- struct lpfc_nvme_buf, list);
- if (lpfc_ncmd) {
- dma_pool_free(phba->lpfc_sg_dma_buf_pool,
- lpfc_ncmd->data,
- lpfc_ncmd->dma_handle);
- kfree(lpfc_ncmd);
- }
- }
- spin_lock_irq(&phba->nvme_buf_list_get_lock);
- phba->sli4_hba.nvme_xri_cnt -= nvme_xri_cnt;
- spin_unlock_irq(&phba->nvme_buf_list_get_lock);
- }
-
- /* update xris associated to remaining allocated nvme buffers */
- lpfc_ncmd = NULL;
- lpfc_ncmd_next = NULL;
- list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next,
- &nvme_sgl_list, list) {
- lxri = lpfc_sli4_next_xritag(phba);
- if (lxri == NO_XRI) {
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
- "6075 Failed to allocate xri for "
- "nvme buffer\n");
- rc = -ENOMEM;
- goto out_free_mem;
- }
- lpfc_ncmd->cur_iocbq.sli4_lxritag = lxri;
- lpfc_ncmd->cur_iocbq.sli4_xritag = phba->sli4_hba.xri_ids[lxri];
- }
- spin_lock_irq(&phba->nvme_buf_list_get_lock);
- spin_lock(&phba->nvme_buf_list_put_lock);
- list_splice_init(&nvme_sgl_list, &phba->lpfc_nvme_buf_list_get);
- phba->get_nvme_bufs = cnt;
- INIT_LIST_HEAD(&phba->lpfc_nvme_buf_list_put);
- spin_unlock(&phba->nvme_buf_list_put_lock);
- spin_unlock_irq(&phba->nvme_buf_list_get_lock);
- return 0;
-
-out_free_mem:
- lpfc_nvme_free(phba);
- return rc;
-}
-
-/**
* lpfc_create_port - Create an FC port
* @phba: pointer to lpfc hba data structure.
* @instance: a unique integer ID to this FC port.
@@ -3956,17 +4290,29 @@ lpfc_create_port(struct lpfc_hba *phba, int instance, struct device *dev)
vport->fc_rscn_flush = 0;
lpfc_get_vport_cfgparam(vport);
+ /* Adjust value in vport */
+ vport->cfg_enable_fc4_type = phba->cfg_enable_fc4_type;
+
shost->unique_id = instance;
shost->max_id = LPFC_MAX_TARGET;
shost->max_lun = vport->cfg_max_luns;
shost->this_id = -1;
shost->max_cmd_len = 16;
- shost->nr_hw_queues = phba->cfg_fcp_io_channel;
+
if (phba->sli_rev == LPFC_SLI_REV4) {
+ if (phba->cfg_fcp_io_sched == LPFC_FCP_SCHED_BY_HDWQ)
+ shost->nr_hw_queues = phba->cfg_hdw_queue;
+ else
+ shost->nr_hw_queues = phba->sli4_hba.num_present_cpu;
+
shost->dma_boundary =
phba->sli4_hba.pc_sli4_params.sge_supp_len-1;
shost->sg_tablesize = phba->cfg_scsi_seg_cnt;
- }
+ } else
+ /* SLI-3 has a limited number of hardware queues (3),
+ * thus there is only one for FCP processing.
+ */
+ shost->nr_hw_queues = 1;
/*
* Set initial can_queue value since 0 is no longer supported and
@@ -4220,7 +4566,8 @@ lpfc_stop_port_s4(struct lpfc_hba *phba)
{
/* Reset some HBA SLI4 setup states */
lpfc_stop_hba_timers(phba);
- phba->pport->work_port_events = 0;
+ if (phba->pport)
+ phba->pport->work_port_events = 0;
phba->sli4_hba.intr_enable = 0;
}
@@ -5819,24 +6166,11 @@ lpfc_setup_driver_resource_phase1(struct lpfc_hba *phba)
"NVME" : " "),
(phba->nvmet_support ? "NVMET" : " "));
- if (phba->cfg_enable_fc4_type & LPFC_ENABLE_FCP) {
- /* Initialize the scsi buffer list used by driver for scsi IO */
- spin_lock_init(&phba->scsi_buf_list_get_lock);
- INIT_LIST_HEAD(&phba->lpfc_scsi_buf_list_get);
- spin_lock_init(&phba->scsi_buf_list_put_lock);
- INIT_LIST_HEAD(&phba->lpfc_scsi_buf_list_put);
- }
-
- if ((phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) &&
- (phba->nvmet_support == 0)) {
- /* Initialize the NVME buffer list used by driver for NVME IO */
- spin_lock_init(&phba->nvme_buf_list_get_lock);
- INIT_LIST_HEAD(&phba->lpfc_nvme_buf_list_get);
- phba->get_nvme_bufs = 0;
- spin_lock_init(&phba->nvme_buf_list_put_lock);
- INIT_LIST_HEAD(&phba->lpfc_nvme_buf_list_put);
- phba->put_nvme_bufs = 0;
- }
+ /* Initialize the IO buffer list used by driver for SLI3 SCSI */
+ spin_lock_init(&phba->scsi_buf_list_get_lock);
+ INIT_LIST_HEAD(&phba->lpfc_scsi_buf_list_get);
+ spin_lock_init(&phba->scsi_buf_list_put_lock);
+ INIT_LIST_HEAD(&phba->lpfc_scsi_buf_list_put);
/* Initialize the fabric iocb list */
INIT_LIST_HEAD(&phba->fabric_iocb_list);
@@ -5860,6 +6194,8 @@ lpfc_setup_driver_resource_phase1(struct lpfc_hba *phba)
/* Heartbeat timer */
timer_setup(&phba->hb_tmofunc, lpfc_hb_timeout, 0);
+ INIT_DELAYED_WORK(&phba->eq_delay_work, lpfc_hb_eq_delay_work);
+
return 0;
}
@@ -5877,7 +6213,7 @@ lpfc_setup_driver_resource_phase1(struct lpfc_hba *phba)
static int
lpfc_sli_driver_resource_setup(struct lpfc_hba *phba)
{
- int rc;
+ int rc, entry_sz;
/*
* Initialize timers used by driver
@@ -5922,6 +6258,11 @@ lpfc_sli_driver_resource_setup(struct lpfc_hba *phba)
lpfc_template_no_hr.sg_tablesize = phba->cfg_sg_seg_cnt;
lpfc_template.sg_tablesize = phba->cfg_sg_seg_cnt;
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ entry_sz = sizeof(struct sli4_sge);
+ else
+ entry_sz = sizeof(struct ulp_bde64);
+
/* There are going to be 2 reserved BDEs: 1 FCP cmnd + 1 FCP rsp */
if (phba->cfg_enable_bg) {
/*
@@ -5935,7 +6276,7 @@ lpfc_sli_driver_resource_setup(struct lpfc_hba *phba)
*/
phba->cfg_sg_dma_buf_size = sizeof(struct fcp_cmnd) +
sizeof(struct fcp_rsp) +
- (LPFC_MAX_SG_SEG_CNT * sizeof(struct ulp_bde64));
+ (LPFC_MAX_SG_SEG_CNT * entry_sz);
if (phba->cfg_sg_seg_cnt > LPFC_MAX_SG_SEG_CNT_DIF)
phba->cfg_sg_seg_cnt = LPFC_MAX_SG_SEG_CNT_DIF;
@@ -5950,7 +6291,7 @@ lpfc_sli_driver_resource_setup(struct lpfc_hba *phba)
*/
phba->cfg_sg_dma_buf_size = sizeof(struct fcp_cmnd) +
sizeof(struct fcp_rsp) +
- ((phba->cfg_sg_seg_cnt + 2) * sizeof(struct ulp_bde64));
+ ((phba->cfg_sg_seg_cnt + 2) * entry_sz);
/* Total BDEs in BPL for scsi_sg_list */
phba->cfg_total_seg_cnt = phba->cfg_sg_seg_cnt + 2;
@@ -6031,14 +6372,13 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)
uint8_t pn_page[LPFC_MAX_SUPPORTED_PAGES] = {0};
struct lpfc_mqe *mqe;
int longs;
- int fof_vectors = 0;
int extra;
uint64_t wwn;
u32 if_type;
u32 if_fam;
- phba->sli4_hba.num_online_cpu = num_online_cpus();
phba->sli4_hba.num_present_cpu = lpfc_present_cpu;
+ phba->sli4_hba.num_possible_cpu = num_possible_cpus();
phba->sli4_hba.curr_disp_cpu = 0;
/* Get all the module params for configuring this host */
@@ -6200,8 +6540,7 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)
if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) {
/* Initialize the Abort nvme buffer list used by driver */
- spin_lock_init(&phba->sli4_hba.abts_nvme_buf_list_lock);
- INIT_LIST_HEAD(&phba->sli4_hba.lpfc_abts_nvme_buf_list);
+ spin_lock_init(&phba->sli4_hba.abts_nvmet_buf_list_lock);
INIT_LIST_HEAD(&phba->sli4_hba.lpfc_abts_nvmet_ctx_list);
INIT_LIST_HEAD(&phba->sli4_hba.lpfc_nvmet_io_wait_list);
}
@@ -6337,6 +6676,8 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)
" NVME_TARGET_FC infrastructure"
" is not in kernel\n");
#endif
+ /* Not supported for NVMET */
+ phba->cfg_xri_rebalancing = 0;
break;
}
}
@@ -6405,8 +6746,6 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)
/* Verify OAS is supported */
lpfc_sli4_oas_verify(phba);
- if (phba->cfg_fof)
- fof_vectors = 1;
/* Verify RAS support on adapter */
lpfc_sli4_ras_init(phba);
@@ -6450,9 +6789,9 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)
goto out_remove_rpi_hdrs;
}
- phba->sli4_hba.hba_eq_hdl = kcalloc(fof_vectors + phba->io_channel_irqs,
- sizeof(struct lpfc_hba_eq_hdl),
- GFP_KERNEL);
+ phba->sli4_hba.hba_eq_hdl = kcalloc(phba->cfg_irq_chann,
+ sizeof(struct lpfc_hba_eq_hdl),
+ GFP_KERNEL);
if (!phba->sli4_hba.hba_eq_hdl) {
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
"2572 Failed allocate memory for "
@@ -6461,7 +6800,7 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)
goto out_free_fcf_rr_bmask;
}
- phba->sli4_hba.cpu_map = kcalloc(phba->sli4_hba.num_present_cpu,
+ phba->sli4_hba.cpu_map = kcalloc(phba->sli4_hba.num_possible_cpu,
sizeof(struct lpfc_vector_map_info),
GFP_KERNEL);
if (!phba->sli4_hba.cpu_map) {
@@ -6471,21 +6810,14 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)
rc = -ENOMEM;
goto out_free_hba_eq_hdl;
}
- if (lpfc_used_cpu == NULL) {
- lpfc_used_cpu = kcalloc(lpfc_present_cpu, sizeof(uint16_t),
- GFP_KERNEL);
- if (!lpfc_used_cpu) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "3335 Failed allocate memory for msi-x "
- "interrupt vector mapping\n");
- kfree(phba->sli4_hba.cpu_map);
- rc = -ENOMEM;
- goto out_free_hba_eq_hdl;
- }
- for (i = 0; i < lpfc_present_cpu; i++)
- lpfc_used_cpu[i] = LPFC_VECTOR_MAP_EMPTY;
- }
+ phba->sli4_hba.eq_info = alloc_percpu(struct lpfc_eq_intr_info);
+ if (!phba->sli4_hba.eq_info) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "3321 Failed allocation for per_cpu stats\n");
+ rc = -ENOMEM;
+ goto out_free_hba_cpu_map;
+ }
/*
* Enable sr-iov virtual functions if supported and configured
* through the module parameter.
@@ -6505,6 +6837,8 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)
return 0;
+out_free_hba_cpu_map:
+ kfree(phba->sli4_hba.cpu_map);
out_free_hba_eq_hdl:
kfree(phba->sli4_hba.hba_eq_hdl);
out_free_fcf_rr_bmask:
@@ -6534,10 +6868,12 @@ lpfc_sli4_driver_resource_unset(struct lpfc_hba *phba)
{
struct lpfc_fcf_conn_entry *conn_entry, *next_conn_entry;
+ free_percpu(phba->sli4_hba.eq_info);
+
/* Free memory allocated for msi-x interrupt vector to CPU mapping */
kfree(phba->sli4_hba.cpu_map);
+ phba->sli4_hba.num_possible_cpu = 0;
phba->sli4_hba.num_present_cpu = 0;
- phba->sli4_hba.num_online_cpu = 0;
phba->sli4_hba.curr_disp_cpu = 0;
/* Free memory allocated for fast-path work queue handles */
@@ -6875,11 +7211,8 @@ lpfc_init_sgl_list(struct lpfc_hba *phba)
/* els xri-sgl book keeping */
phba->sli4_hba.els_xri_cnt = 0;
- /* scsi xri-buffer book keeping */
- phba->sli4_hba.scsi_xri_cnt = 0;
-
/* nvme xri-buffer book keeping */
- phba->sli4_hba.nvme_xri_cnt = 0;
+ phba->sli4_hba.io_xri_cnt = 0;
}
/**
@@ -7093,6 +7426,9 @@ lpfc_hba_alloc(struct pci_dev *pdev)
static void
lpfc_hba_free(struct lpfc_hba *phba)
{
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ kfree(phba->sli4_hba.hdwq);
+
/* Release the driver assigned board number */
idr_remove(&lpfc_hba_index, phba->brd_no);
@@ -7128,10 +7464,6 @@ lpfc_create_shost(struct lpfc_hba *phba)
phba->fc_arbtov = FF_DEF_ARBTOV;
atomic_set(&phba->sdev_cnt, 0);
- atomic_set(&phba->fc4ScsiInputRequests, 0);
- atomic_set(&phba->fc4ScsiOutputRequests, 0);
- atomic_set(&phba->fc4ScsiControlRequests, 0);
- atomic_set(&phba->fc4ScsiIoCmpls, 0);
vport = lpfc_create_port(phba, phba->brd_no, &phba->pcidev->dev);
if (!vport)
return -ENODEV;
@@ -7909,7 +8241,7 @@ lpfc_sli4_read_config(struct lpfc_hba *phba)
struct lpfc_rsrc_desc_fcfcoe *desc;
char *pdesc_0;
uint16_t forced_link_speed;
- uint32_t if_type;
+ uint32_t if_type, qmin;
int length, i, rc = 0, rc2;
pmb = (LPFC_MBOXQ_t *) mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
@@ -8014,38 +8346,44 @@ lpfc_sli4_read_config(struct lpfc_hba *phba)
phba->sli4_hba.max_cfg_param.max_rq);
/*
- * Calculate NVME queue resources based on how
- * many WQ/CQs are available.
+ * Calculate queue resources based on how
+ * many WQ/CQ/EQs are available.
*/
- if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) {
- length = phba->sli4_hba.max_cfg_param.max_wq;
- if (phba->sli4_hba.max_cfg_param.max_cq <
- phba->sli4_hba.max_cfg_param.max_wq)
- length = phba->sli4_hba.max_cfg_param.max_cq;
+ qmin = phba->sli4_hba.max_cfg_param.max_wq;
+ if (phba->sli4_hba.max_cfg_param.max_cq < qmin)
+ qmin = phba->sli4_hba.max_cfg_param.max_cq;
+ if (phba->sli4_hba.max_cfg_param.max_eq < qmin)
+ qmin = phba->sli4_hba.max_cfg_param.max_eq;
+ /*
+ * Whats left after this can go toward NVME / FCP.
+ * The minus 4 accounts for ELS, NVME LS, MBOX
+ * plus one extra. When configured for
+ * NVMET, FCP io channel WQs are not created.
+ */
+ qmin -= 4;
- /*
- * Whats left after this can go toward NVME.
- * The minus 6 accounts for ELS, NVME LS, MBOX
- * fof plus a couple extra. When configured for
- * NVMET, FCP io channel WQs are not created.
- */
- length -= 6;
- if (!phba->nvmet_support)
- length -= phba->cfg_fcp_io_channel;
-
- if (phba->cfg_nvme_io_channel > length) {
- lpfc_printf_log(
- phba, KERN_ERR, LOG_SLI,
- "2005 Reducing NVME IO channel to %d: "
- "WQ %d CQ %d NVMEIO %d FCPIO %d\n",
- length,
+ /* If NVME is configured, double the number of CQ/WQs needed */
+ if ((phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) &&
+ !phba->nvmet_support)
+ qmin /= 2;
+
+ /* Check to see if there is enough for NVME */
+ if ((phba->cfg_irq_chann > qmin) ||
+ (phba->cfg_hdw_queue > qmin)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "2005 Reducing Queues: "
+ "WQ %d CQ %d EQ %d: min %d: "
+ "IRQ %d HDWQ %d\n",
phba->sli4_hba.max_cfg_param.max_wq,
phba->sli4_hba.max_cfg_param.max_cq,
- phba->cfg_nvme_io_channel,
- phba->cfg_fcp_io_channel);
+ phba->sli4_hba.max_cfg_param.max_eq,
+ qmin, phba->cfg_irq_chann,
+ phba->cfg_hdw_queue);
- phba->cfg_nvme_io_channel = length;
- }
+ if (phba->cfg_irq_chann > qmin)
+ phba->cfg_irq_chann = qmin;
+ if (phba->cfg_hdw_queue > qmin)
+ phba->cfg_hdw_queue = qmin;
}
}
@@ -8257,53 +8595,22 @@ lpfc_setup_endian_order(struct lpfc_hba *phba)
static int
lpfc_sli4_queue_verify(struct lpfc_hba *phba)
{
- int io_channel;
- int fof_vectors = phba->cfg_fof ? 1 : 0;
-
/*
* Sanity check for configured queue parameters against the run-time
* device parameters
*/
- /* Sanity check on HBA EQ parameters */
- io_channel = phba->io_channel_irqs;
-
- if (phba->sli4_hba.num_online_cpu < io_channel) {
- lpfc_printf_log(phba,
- KERN_ERR, LOG_INIT,
- "3188 Reducing IO channels to match number of "
- "online CPUs: from %d to %d\n",
- io_channel, phba->sli4_hba.num_online_cpu);
- io_channel = phba->sli4_hba.num_online_cpu;
- }
-
- if (io_channel + fof_vectors > phba->sli4_hba.max_cfg_param.max_eq) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "2575 Reducing IO channels to match number of "
- "available EQs: from %d to %d\n",
- io_channel,
- phba->sli4_hba.max_cfg_param.max_eq);
- io_channel = phba->sli4_hba.max_cfg_param.max_eq - fof_vectors;
- }
-
- /* The actual number of FCP / NVME event queues adopted */
- if (io_channel != phba->io_channel_irqs)
- phba->io_channel_irqs = io_channel;
- if (phba->cfg_fcp_io_channel > io_channel)
- phba->cfg_fcp_io_channel = io_channel;
- if (phba->cfg_nvme_io_channel > io_channel)
- phba->cfg_nvme_io_channel = io_channel;
if (phba->nvmet_support) {
- if (phba->cfg_nvme_io_channel < phba->cfg_nvmet_mrq)
- phba->cfg_nvmet_mrq = phba->cfg_nvme_io_channel;
+ if (phba->cfg_irq_chann < phba->cfg_nvmet_mrq)
+ phba->cfg_nvmet_mrq = phba->cfg_irq_chann;
}
if (phba->cfg_nvmet_mrq > LPFC_NVMET_MRQ_MAX)
phba->cfg_nvmet_mrq = LPFC_NVMET_MRQ_MAX;
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "2574 IO channels: irqs %d fcp %d nvme %d MRQ: %d\n",
- phba->io_channel_irqs, phba->cfg_fcp_io_channel,
- phba->cfg_nvme_io_channel, phba->cfg_nvmet_mrq);
+ "2574 IO channels: hdwQ %d IRQ %d MRQ: %d\n",
+ phba->cfg_hdw_queue, phba->cfg_irq_chann,
+ phba->cfg_nvmet_mrq);
/* Get EQ depth from module parameter, fake the default for now */
phba->sli4_hba.eq_esize = LPFC_EQE_SIZE_4B;
@@ -8330,7 +8637,9 @@ lpfc_alloc_nvme_wq_cq(struct lpfc_hba *phba, int wqidx)
return 1;
}
qdesc->qe_valid = 1;
- phba->sli4_hba.nvme_cq[wqidx] = qdesc;
+ qdesc->hdwq = wqidx;
+ qdesc->chann = lpfc_find_cpu_handle(phba, wqidx, LPFC_FIND_BY_HDWQ);
+ phba->sli4_hba.hdwq[wqidx].nvme_cq = qdesc;
qdesc = lpfc_sli4_queue_alloc(phba, LPFC_EXPANDED_PAGE_SIZE,
LPFC_WQE128_SIZE, LPFC_WQE_EXP_COUNT);
@@ -8340,7 +8649,9 @@ lpfc_alloc_nvme_wq_cq(struct lpfc_hba *phba, int wqidx)
wqidx);
return 1;
}
- phba->sli4_hba.nvme_wq[wqidx] = qdesc;
+ qdesc->hdwq = wqidx;
+ qdesc->chann = wqidx;
+ phba->sli4_hba.hdwq[wqidx].nvme_wq = qdesc;
list_add_tail(&qdesc->wq_list, &phba->sli4_hba.lpfc_wq_list);
return 0;
}
@@ -8368,7 +8679,9 @@ lpfc_alloc_fcp_wq_cq(struct lpfc_hba *phba, int wqidx)
return 1;
}
qdesc->qe_valid = 1;
- phba->sli4_hba.fcp_cq[wqidx] = qdesc;
+ qdesc->hdwq = wqidx;
+ qdesc->chann = lpfc_find_cpu_handle(phba, wqidx, LPFC_FIND_BY_HDWQ);
+ phba->sli4_hba.hdwq[wqidx].fcp_cq = qdesc;
/* Create Fast Path FCP WQs */
if (phba->enab_exp_wqcq_pages) {
@@ -8389,7 +8702,9 @@ lpfc_alloc_fcp_wq_cq(struct lpfc_hba *phba, int wqidx)
wqidx);
return 1;
}
- phba->sli4_hba.fcp_wq[wqidx] = qdesc;
+ qdesc->hdwq = wqidx;
+ qdesc->chann = wqidx;
+ phba->sli4_hba.hdwq[wqidx].fcp_wq = qdesc;
list_add_tail(&qdesc->wq_list, &phba->sli4_hba.lpfc_wq_list);
return 0;
}
@@ -8412,16 +8727,14 @@ int
lpfc_sli4_queue_create(struct lpfc_hba *phba)
{
struct lpfc_queue *qdesc;
- int idx, io_channel;
+ int idx, eqidx;
+ struct lpfc_sli4_hdw_queue *qp;
+ struct lpfc_eq_intr_info *eqi;
/*
* Create HBA Record arrays.
* Both NVME and FCP will share that same vectors / EQs
*/
- io_channel = phba->io_channel_irqs;
- if (!io_channel)
- return -ERANGE;
-
phba->sli4_hba.mq_esize = LPFC_MQE_SIZE;
phba->sli4_hba.mq_ecount = LPFC_MQE_DEF_COUNT;
phba->sli4_hba.wq_esize = LPFC_WQE_SIZE;
@@ -8433,87 +8746,36 @@ lpfc_sli4_queue_create(struct lpfc_hba *phba)
phba->sli4_hba.cq_esize = LPFC_CQE_SIZE;
phba->sli4_hba.cq_ecount = LPFC_CQE_DEF_COUNT;
- phba->sli4_hba.hba_eq = kcalloc(io_channel,
- sizeof(struct lpfc_queue *),
- GFP_KERNEL);
- if (!phba->sli4_hba.hba_eq) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "2576 Failed allocate memory for "
- "fast-path EQ record array\n");
- goto out_error;
- }
-
- if (phba->cfg_fcp_io_channel) {
- phba->sli4_hba.fcp_cq = kcalloc(phba->cfg_fcp_io_channel,
- sizeof(struct lpfc_queue *),
- GFP_KERNEL);
- if (!phba->sli4_hba.fcp_cq) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "2577 Failed allocate memory for "
- "fast-path CQ record array\n");
- goto out_error;
- }
- phba->sli4_hba.fcp_wq = kcalloc(phba->cfg_fcp_io_channel,
- sizeof(struct lpfc_queue *),
- GFP_KERNEL);
- if (!phba->sli4_hba.fcp_wq) {
+ if (!phba->sli4_hba.hdwq) {
+ phba->sli4_hba.hdwq = kcalloc(
+ phba->cfg_hdw_queue, sizeof(struct lpfc_sli4_hdw_queue),
+ GFP_KERNEL);
+ if (!phba->sli4_hba.hdwq) {
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "2578 Failed allocate memory for "
- "fast-path FCP WQ record array\n");
+ "6427 Failed allocate memory for "
+ "fast-path Hardware Queue array\n");
goto out_error;
}
- /*
- * Since the first EQ can have multiple CQs associated with it,
- * this array is used to quickly see if we have a FCP fast-path
- * CQ match.
- */
- phba->sli4_hba.fcp_cq_map = kcalloc(phba->cfg_fcp_io_channel,
- sizeof(uint16_t),
- GFP_KERNEL);
- if (!phba->sli4_hba.fcp_cq_map) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "2545 Failed allocate memory for "
- "fast-path CQ map\n");
- goto out_error;
+ /* Prepare hardware queues to take IO buffers */
+ for (idx = 0; idx < phba->cfg_hdw_queue; idx++) {
+ qp = &phba->sli4_hba.hdwq[idx];
+ spin_lock_init(&qp->io_buf_list_get_lock);
+ spin_lock_init(&qp->io_buf_list_put_lock);
+ INIT_LIST_HEAD(&qp->lpfc_io_buf_list_get);
+ INIT_LIST_HEAD(&qp->lpfc_io_buf_list_put);
+ qp->get_io_bufs = 0;
+ qp->put_io_bufs = 0;
+ qp->total_io_bufs = 0;
+ spin_lock_init(&qp->abts_scsi_buf_list_lock);
+ INIT_LIST_HEAD(&qp->lpfc_abts_scsi_buf_list);
+ qp->abts_scsi_io_bufs = 0;
+ spin_lock_init(&qp->abts_nvme_buf_list_lock);
+ INIT_LIST_HEAD(&qp->lpfc_abts_nvme_buf_list);
+ qp->abts_nvme_io_bufs = 0;
}
}
- if (phba->cfg_nvme_io_channel) {
- phba->sli4_hba.nvme_cq = kcalloc(phba->cfg_nvme_io_channel,
- sizeof(struct lpfc_queue *),
- GFP_KERNEL);
- if (!phba->sli4_hba.nvme_cq) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "6077 Failed allocate memory for "
- "fast-path CQ record array\n");
- goto out_error;
- }
-
- phba->sli4_hba.nvme_wq = kcalloc(phba->cfg_nvme_io_channel,
- sizeof(struct lpfc_queue *),
- GFP_KERNEL);
- if (!phba->sli4_hba.nvme_wq) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "2581 Failed allocate memory for "
- "fast-path NVME WQ record array\n");
- goto out_error;
- }
-
- /*
- * Since the first EQ can have multiple CQs associated with it,
- * this array is used to quickly see if we have a NVME fast-path
- * CQ match.
- */
- phba->sli4_hba.nvme_cq_map = kcalloc(phba->cfg_nvme_io_channel,
- sizeof(uint16_t),
- GFP_KERNEL);
- if (!phba->sli4_hba.nvme_cq_map) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "6078 Failed allocate memory for "
- "fast-path CQ map\n");
- goto out_error;
- }
-
+ if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) {
if (phba->nvmet_support) {
phba->sli4_hba.nvmet_cqset = kcalloc(
phba->cfg_nvmet_mrq,
@@ -8551,8 +8813,19 @@ lpfc_sli4_queue_create(struct lpfc_hba *phba)
INIT_LIST_HEAD(&phba->sli4_hba.lpfc_wq_list);
/* Create HBA Event Queues (EQs) */
- for (idx = 0; idx < io_channel; idx++) {
- /* Create EQs */
+ for (idx = 0; idx < phba->cfg_hdw_queue; idx++) {
+ /*
+ * If there are more Hardware Queues than available
+ * CQs, multiple Hardware Queues may share a common EQ.
+ */
+ if (idx >= phba->cfg_irq_chann) {
+ /* Share an existing EQ */
+ eqidx = lpfc_find_eq_handle(phba, idx);
+ phba->sli4_hba.hdwq[idx].hba_eq =
+ phba->sli4_hba.hdwq[eqidx].hba_eq;
+ continue;
+ }
+ /* Create an EQ */
qdesc = lpfc_sli4_queue_alloc(phba, LPFC_DEFAULT_PAGE_SIZE,
phba->sli4_hba.eq_esize,
phba->sli4_hba.eq_ecount);
@@ -8562,33 +8835,51 @@ lpfc_sli4_queue_create(struct lpfc_hba *phba)
goto out_error;
}
qdesc->qe_valid = 1;
- phba->sli4_hba.hba_eq[idx] = qdesc;
+ qdesc->hdwq = idx;
+
+ /* Save the CPU this EQ is affinitised to */
+ eqidx = lpfc_find_eq_handle(phba, idx);
+ qdesc->chann = lpfc_find_cpu_handle(phba, eqidx,
+ LPFC_FIND_BY_EQ);
+ phba->sli4_hba.hdwq[idx].hba_eq = qdesc;
+ qdesc->last_cpu = qdesc->chann;
+ eqi = per_cpu_ptr(phba->sli4_hba.eq_info, qdesc->last_cpu);
+ list_add(&qdesc->cpu_list, &eqi->list);
}
- /* FCP and NVME io channels are not required to be balanced */
- for (idx = 0; idx < phba->cfg_fcp_io_channel; idx++)
+ /* Allocate SCSI SLI4 CQ/WQs */
+ for (idx = 0; idx < phba->cfg_hdw_queue; idx++) {
if (lpfc_alloc_fcp_wq_cq(phba, idx))
goto out_error;
+ }
- for (idx = 0; idx < phba->cfg_nvme_io_channel; idx++)
- if (lpfc_alloc_nvme_wq_cq(phba, idx))
- goto out_error;
+ /* Allocate NVME SLI4 CQ/WQs */
+ if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) {
+ for (idx = 0; idx < phba->cfg_hdw_queue; idx++) {
+ if (lpfc_alloc_nvme_wq_cq(phba, idx))
+ goto out_error;
+ }
- if (phba->nvmet_support) {
- for (idx = 0; idx < phba->cfg_nvmet_mrq; idx++) {
- qdesc = lpfc_sli4_queue_alloc(phba,
+ if (phba->nvmet_support) {
+ for (idx = 0; idx < phba->cfg_nvmet_mrq; idx++) {
+ qdesc = lpfc_sli4_queue_alloc(
+ phba,
LPFC_DEFAULT_PAGE_SIZE,
phba->sli4_hba.cq_esize,
phba->sli4_hba.cq_ecount);
- if (!qdesc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "3142 Failed allocate NVME "
- "CQ Set (%d)\n", idx);
- goto out_error;
+ if (!qdesc) {
+ lpfc_printf_log(
+ phba, KERN_ERR, LOG_INIT,
+ "3142 Failed allocate NVME "
+ "CQ Set (%d)\n", idx);
+ goto out_error;
+ }
+ qdesc->qe_valid = 1;
+ qdesc->hdwq = idx;
+ qdesc->chann = idx;
+ phba->sli4_hba.nvmet_cqset[idx] = qdesc;
}
- qdesc->qe_valid = 1;
- phba->sli4_hba.nvmet_cqset[idx] = qdesc;
}
}
@@ -8618,6 +8909,7 @@ lpfc_sli4_queue_create(struct lpfc_hba *phba)
goto out_error;
}
qdesc->qe_valid = 1;
+ qdesc->chann = 0;
phba->sli4_hba.els_cq = qdesc;
@@ -8635,6 +8927,7 @@ lpfc_sli4_queue_create(struct lpfc_hba *phba)
"0505 Failed allocate slow-path MQ\n");
goto out_error;
}
+ qdesc->chann = 0;
phba->sli4_hba.mbx_wq = qdesc;
/*
@@ -8650,6 +8943,7 @@ lpfc_sli4_queue_create(struct lpfc_hba *phba)
"0504 Failed allocate slow-path ELS WQ\n");
goto out_error;
}
+ qdesc->chann = 0;
phba->sli4_hba.els_wq = qdesc;
list_add_tail(&qdesc->wq_list, &phba->sli4_hba.lpfc_wq_list);
@@ -8663,6 +8957,7 @@ lpfc_sli4_queue_create(struct lpfc_hba *phba)
"6079 Failed allocate NVME LS CQ\n");
goto out_error;
}
+ qdesc->chann = 0;
qdesc->qe_valid = 1;
phba->sli4_hba.nvmels_cq = qdesc;
@@ -8675,6 +8970,7 @@ lpfc_sli4_queue_create(struct lpfc_hba *phba)
"6080 Failed allocate NVME LS WQ\n");
goto out_error;
}
+ qdesc->chann = 0;
phba->sli4_hba.nvmels_wq = qdesc;
list_add_tail(&qdesc->wq_list, &phba->sli4_hba.lpfc_wq_list);
}
@@ -8705,7 +9001,8 @@ lpfc_sli4_queue_create(struct lpfc_hba *phba)
}
phba->sli4_hba.dat_rq = qdesc;
- if (phba->nvmet_support) {
+ if ((phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) &&
+ phba->nvmet_support) {
for (idx = 0; idx < phba->cfg_nvmet_mrq; idx++) {
/* Create NVMET Receive Queue for header */
qdesc = lpfc_sli4_queue_alloc(phba,
@@ -8718,6 +9015,7 @@ lpfc_sli4_queue_create(struct lpfc_hba *phba)
"receive HRQ\n");
goto out_error;
}
+ qdesc->hdwq = idx;
phba->sli4_hba.nvmet_mrq_hdr[idx] = qdesc;
/* Only needed for header of RQ pair */
@@ -8744,13 +9042,29 @@ lpfc_sli4_queue_create(struct lpfc_hba *phba)
"receive DRQ\n");
goto out_error;
}
+ qdesc->hdwq = idx;
phba->sli4_hba.nvmet_mrq_data[idx] = qdesc;
}
}
- /* Create the Queues needed for Flash Optimized Fabric operations */
- if (phba->cfg_fof)
- lpfc_fof_queue_create(phba);
+#if defined(BUILD_NVME)
+ /* Clear NVME stats */
+ if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) {
+ for (idx = 0; idx < phba->cfg_hdw_queue; idx++) {
+ memset(&phba->sli4_hba.hdwq[idx].nvme_cstat, 0,
+ sizeof(phba->sli4_hba.hdwq[idx].nvme_cstat));
+ }
+ }
+#endif
+
+ /* Clear SCSI stats */
+ if (phba->cfg_enable_fc4_type & LPFC_ENABLE_FCP) {
+ for (idx = 0; idx < phba->cfg_hdw_queue; idx++) {
+ memset(&phba->sli4_hba.hdwq[idx].scsi_cstat, 0,
+ sizeof(phba->sli4_hba.hdwq[idx].scsi_cstat));
+ }
+ }
+
return 0;
out_error:
@@ -8783,11 +9097,25 @@ lpfc_sli4_release_queues(struct lpfc_queue ***qs, int max)
}
static inline void
-lpfc_sli4_release_queue_map(uint16_t **qmap)
+lpfc_sli4_release_hdwq(struct lpfc_hba *phba)
{
- if (*qmap != NULL) {
- kfree(*qmap);
- *qmap = NULL;
+ struct lpfc_sli4_hdw_queue *hdwq;
+ uint32_t idx;
+
+ hdwq = phba->sli4_hba.hdwq;
+ for (idx = 0; idx < phba->cfg_hdw_queue; idx++) {
+ if (idx < phba->cfg_irq_chann)
+ lpfc_sli4_queue_free(hdwq[idx].hba_eq);
+ hdwq[idx].hba_eq = NULL;
+
+ lpfc_sli4_queue_free(hdwq[idx].fcp_cq);
+ lpfc_sli4_queue_free(hdwq[idx].nvme_cq);
+ lpfc_sli4_queue_free(hdwq[idx].fcp_wq);
+ lpfc_sli4_queue_free(hdwq[idx].nvme_wq);
+ hdwq[idx].fcp_cq = NULL;
+ hdwq[idx].nvme_cq = NULL;
+ hdwq[idx].fcp_wq = NULL;
+ hdwq[idx].nvme_wq = NULL;
}
}
@@ -8806,33 +9134,9 @@ lpfc_sli4_release_queue_map(uint16_t **qmap)
void
lpfc_sli4_queue_destroy(struct lpfc_hba *phba)
{
- if (phba->cfg_fof)
- lpfc_fof_queue_destroy(phba);
-
/* Release HBA eqs */
- lpfc_sli4_release_queues(&phba->sli4_hba.hba_eq, phba->io_channel_irqs);
-
- /* Release FCP cqs */
- lpfc_sli4_release_queues(&phba->sli4_hba.fcp_cq,
- phba->cfg_fcp_io_channel);
-
- /* Release FCP wqs */
- lpfc_sli4_release_queues(&phba->sli4_hba.fcp_wq,
- phba->cfg_fcp_io_channel);
-
- /* Release FCP CQ mapping array */
- lpfc_sli4_release_queue_map(&phba->sli4_hba.fcp_cq_map);
-
- /* Release NVME cqs */
- lpfc_sli4_release_queues(&phba->sli4_hba.nvme_cq,
- phba->cfg_nvme_io_channel);
-
- /* Release NVME wqs */
- lpfc_sli4_release_queues(&phba->sli4_hba.nvme_wq,
- phba->cfg_nvme_io_channel);
-
- /* Release NVME CQ mapping array */
- lpfc_sli4_release_queue_map(&phba->sli4_hba.nvme_cq_map);
+ if (phba->sli4_hba.hdwq)
+ lpfc_sli4_release_hdwq(phba);
if (phba->nvmet_support) {
lpfc_sli4_release_queues(&phba->sli4_hba.nvmet_cqset,
@@ -8913,10 +9217,9 @@ lpfc_create_wq_cq(struct lpfc_hba *phba, struct lpfc_queue *eq,
qidx, (uint32_t)rc);
return rc;
}
- cq->chann = qidx;
if (qtype != LPFC_MBOX) {
- /* Setup nvme_cq_map for fast lookup */
+ /* Setup cq_map for fast lookup */
if (cq_map)
*cq_map = cq->queue_id;
@@ -8933,7 +9236,6 @@ lpfc_create_wq_cq(struct lpfc_hba *phba, struct lpfc_queue *eq,
/* no need to tear down cq - caller will do so */
return rc;
}
- wq->chann = qidx;
/* Bind this CQ/WQ to the NVME ring */
pring = wq->pring;
@@ -8963,6 +9265,38 @@ lpfc_create_wq_cq(struct lpfc_hba *phba, struct lpfc_queue *eq,
}
/**
+ * lpfc_setup_cq_lookup - Setup the CQ lookup table
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine will populate the cq_lookup table by all
+ * available CQ queue_id's.
+ **/
+void
+lpfc_setup_cq_lookup(struct lpfc_hba *phba)
+{
+ struct lpfc_queue *eq, *childq;
+ struct lpfc_sli4_hdw_queue *qp;
+ int qidx;
+
+ qp = phba->sli4_hba.hdwq;
+ memset(phba->sli4_hba.cq_lookup, 0,
+ (sizeof(struct lpfc_queue *) * (phba->sli4_hba.cq_max + 1)));
+ for (qidx = 0; qidx < phba->cfg_irq_chann; qidx++) {
+ eq = qp[qidx].hba_eq;
+ if (!eq)
+ continue;
+ list_for_each_entry(childq, &eq->child_list, list) {
+ if (childq->queue_id > phba->sli4_hba.cq_max)
+ continue;
+ if ((childq->subtype == LPFC_FCP) ||
+ (childq->subtype == LPFC_NVME))
+ phba->sli4_hba.cq_lookup[childq->queue_id] =
+ childq;
+ }
+ }
+}
+
+/**
* lpfc_sli4_queue_setup - Set up all the SLI4 queues
* @phba: pointer to lpfc hba data structure.
*
@@ -8979,9 +9313,10 @@ lpfc_sli4_queue_setup(struct lpfc_hba *phba)
{
uint32_t shdr_status, shdr_add_status;
union lpfc_sli4_cfg_shdr *shdr;
+ struct lpfc_sli4_hdw_queue *qp;
LPFC_MBOXQ_t *mboxq;
int qidx;
- uint32_t length, io_channel;
+ uint32_t length, usdelay;
int rc = -ENOMEM;
/* Check for dual-ULP support */
@@ -9032,25 +9367,25 @@ lpfc_sli4_queue_setup(struct lpfc_hba *phba)
/*
* Set up HBA Event Queues (EQs)
*/
- io_channel = phba->io_channel_irqs;
+ qp = phba->sli4_hba.hdwq;
/* Set up HBA event queue */
- if (io_channel && !phba->sli4_hba.hba_eq) {
+ if (!qp) {
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
"3147 Fast-path EQs not allocated\n");
rc = -ENOMEM;
goto out_error;
}
- for (qidx = 0; qidx < io_channel; qidx++) {
- if (!phba->sli4_hba.hba_eq[qidx]) {
+ for (qidx = 0; qidx < phba->cfg_irq_chann; qidx++) {
+ if (!qp[qidx].hba_eq) {
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
"0522 Fast-path EQ (%d) not "
"allocated\n", qidx);
rc = -ENOMEM;
goto out_destroy;
}
- rc = lpfc_eq_create(phba, phba->sli4_hba.hba_eq[qidx],
- phba->cfg_fcp_imax);
+ rc = lpfc_eq_create(phba, qp[qidx].hba_eq,
+ phba->cfg_fcp_imax);
if (rc) {
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
"0523 Failed setup of fast-path EQ "
@@ -9059,26 +9394,17 @@ lpfc_sli4_queue_setup(struct lpfc_hba *phba)
goto out_destroy;
}
lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "2584 HBA EQ setup: queue[%d]-id=%d\n",
- qidx, phba->sli4_hba.hba_eq[qidx]->queue_id);
+ "2584 HBA EQ setup: queue[%d]-id=%d\n", qidx,
+ qp[qidx].hba_eq->queue_id);
}
- if (phba->cfg_nvme_io_channel) {
- if (!phba->sli4_hba.nvme_cq || !phba->sli4_hba.nvme_wq) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "6084 Fast-path NVME %s array not allocated\n",
- (phba->sli4_hba.nvme_cq) ? "CQ" : "WQ");
- rc = -ENOMEM;
- goto out_destroy;
- }
-
- for (qidx = 0; qidx < phba->cfg_nvme_io_channel; qidx++) {
+ if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) {
+ for (qidx = 0; qidx < phba->cfg_hdw_queue; qidx++) {
rc = lpfc_create_wq_cq(phba,
- phba->sli4_hba.hba_eq[
- qidx % io_channel],
- phba->sli4_hba.nvme_cq[qidx],
- phba->sli4_hba.nvme_wq[qidx],
- &phba->sli4_hba.nvme_cq_map[qidx],
+ qp[qidx].hba_eq,
+ qp[qidx].nvme_cq,
+ qp[qidx].nvme_wq,
+ &phba->sli4_hba.hdwq[qidx].nvme_cq_map,
qidx, LPFC_NVME);
if (rc) {
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
@@ -9090,31 +9416,19 @@ lpfc_sli4_queue_setup(struct lpfc_hba *phba)
}
}
- if (phba->cfg_fcp_io_channel) {
- /* Set up fast-path FCP Response Complete Queue */
- if (!phba->sli4_hba.fcp_cq || !phba->sli4_hba.fcp_wq) {
+ for (qidx = 0; qidx < phba->cfg_hdw_queue; qidx++) {
+ rc = lpfc_create_wq_cq(phba,
+ qp[qidx].hba_eq,
+ qp[qidx].fcp_cq,
+ qp[qidx].fcp_wq,
+ &phba->sli4_hba.hdwq[qidx].fcp_cq_map,
+ qidx, LPFC_FCP);
+ if (rc) {
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "3148 Fast-path FCP %s array not allocated\n",
- phba->sli4_hba.fcp_cq ? "WQ" : "CQ");
- rc = -ENOMEM;
- goto out_destroy;
- }
-
- for (qidx = 0; qidx < phba->cfg_fcp_io_channel; qidx++) {
- rc = lpfc_create_wq_cq(phba,
- phba->sli4_hba.hba_eq[
- qidx % io_channel],
- phba->sli4_hba.fcp_cq[qidx],
- phba->sli4_hba.fcp_wq[qidx],
- &phba->sli4_hba.fcp_cq_map[qidx],
- qidx, LPFC_FCP);
- if (rc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
"0535 Failed to setup fastpath "
"FCP WQ/CQ (%d), rc = 0x%x\n",
qidx, (uint32_t)rc);
- goto out_destroy;
- }
+ goto out_destroy;
}
}
@@ -9133,7 +9447,7 @@ lpfc_sli4_queue_setup(struct lpfc_hba *phba)
goto out_destroy;
}
- rc = lpfc_create_wq_cq(phba, phba->sli4_hba.hba_eq[0],
+ rc = lpfc_create_wq_cq(phba, qp[0].hba_eq,
phba->sli4_hba.mbx_cq,
phba->sli4_hba.mbx_wq,
NULL, 0, LPFC_MBOX);
@@ -9154,7 +9468,7 @@ lpfc_sli4_queue_setup(struct lpfc_hba *phba)
if (phba->cfg_nvmet_mrq > 1) {
rc = lpfc_cq_create_set(phba,
phba->sli4_hba.nvmet_cqset,
- phba->sli4_hba.hba_eq,
+ qp,
LPFC_WCQ, LPFC_NVMET);
if (rc) {
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
@@ -9166,7 +9480,7 @@ lpfc_sli4_queue_setup(struct lpfc_hba *phba)
} else {
/* Set up NVMET Receive Complete Queue */
rc = lpfc_cq_create(phba, phba->sli4_hba.nvmet_cqset[0],
- phba->sli4_hba.hba_eq[0],
+ qp[0].hba_eq,
LPFC_WCQ, LPFC_NVMET);
if (rc) {
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
@@ -9180,7 +9494,7 @@ lpfc_sli4_queue_setup(struct lpfc_hba *phba)
"6090 NVMET CQ setup: cq-id=%d, "
"parent eq-id=%d\n",
phba->sli4_hba.nvmet_cqset[0]->queue_id,
- phba->sli4_hba.hba_eq[0]->queue_id);
+ qp[0].hba_eq->queue_id);
}
}
@@ -9192,14 +9506,14 @@ lpfc_sli4_queue_setup(struct lpfc_hba *phba)
rc = -ENOMEM;
goto out_destroy;
}
- rc = lpfc_create_wq_cq(phba, phba->sli4_hba.hba_eq[0],
- phba->sli4_hba.els_cq,
- phba->sli4_hba.els_wq,
- NULL, 0, LPFC_ELS);
+ rc = lpfc_create_wq_cq(phba, qp[0].hba_eq,
+ phba->sli4_hba.els_cq,
+ phba->sli4_hba.els_wq,
+ NULL, 0, LPFC_ELS);
if (rc) {
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "0529 Failed setup of ELS WQ/CQ: rc = 0x%x\n",
- (uint32_t)rc);
+ "0525 Failed setup of ELS WQ/CQ: rc = 0x%x\n",
+ (uint32_t)rc);
goto out_destroy;
}
lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
@@ -9207,7 +9521,7 @@ lpfc_sli4_queue_setup(struct lpfc_hba *phba)
phba->sli4_hba.els_wq->queue_id,
phba->sli4_hba.els_cq->queue_id);
- if (phba->cfg_nvme_io_channel) {
+ if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) {
/* Set up NVME LS Complete Queue */
if (!phba->sli4_hba.nvmels_cq || !phba->sli4_hba.nvmels_wq) {
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
@@ -9216,14 +9530,14 @@ lpfc_sli4_queue_setup(struct lpfc_hba *phba)
rc = -ENOMEM;
goto out_destroy;
}
- rc = lpfc_create_wq_cq(phba, phba->sli4_hba.hba_eq[0],
- phba->sli4_hba.nvmels_cq,
- phba->sli4_hba.nvmels_wq,
- NULL, 0, LPFC_NVME_LS);
+ rc = lpfc_create_wq_cq(phba, qp[0].hba_eq,
+ phba->sli4_hba.nvmels_cq,
+ phba->sli4_hba.nvmels_wq,
+ NULL, 0, LPFC_NVME_LS);
if (rc) {
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "0529 Failed setup of NVVME LS WQ/CQ: "
- "rc = 0x%x\n", (uint32_t)rc);
+ "0526 Failed setup of NVVME LS WQ/CQ: "
+ "rc = 0x%x\n", (uint32_t)rc);
goto out_destroy;
}
@@ -9309,20 +9623,29 @@ lpfc_sli4_queue_setup(struct lpfc_hba *phba)
phba->sli4_hba.dat_rq->queue_id,
phba->sli4_hba.els_cq->queue_id);
- if (phba->cfg_fof) {
- rc = lpfc_fof_queue_setup(phba);
- if (rc) {
+ if (phba->cfg_fcp_imax)
+ usdelay = LPFC_SEC_TO_USEC / phba->cfg_fcp_imax;
+ else
+ usdelay = 0;
+
+ for (qidx = 0; qidx < phba->cfg_irq_chann;
+ qidx += LPFC_MAX_EQ_DELAY_EQID_CNT)
+ lpfc_modify_hba_eq_delay(phba, qidx, LPFC_MAX_EQ_DELAY_EQID_CNT,
+ usdelay);
+
+ if (phba->sli4_hba.cq_max) {
+ kfree(phba->sli4_hba.cq_lookup);
+ phba->sli4_hba.cq_lookup = kcalloc((phba->sli4_hba.cq_max + 1),
+ sizeof(struct lpfc_queue *), GFP_KERNEL);
+ if (!phba->sli4_hba.cq_lookup) {
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "0549 Failed setup of FOF Queues: "
- "rc = 0x%x\n", rc);
+ "0549 Failed setup of CQ Lookup table: "
+ "size 0x%x\n", phba->sli4_hba.cq_max);
+ rc = -ENOMEM;
goto out_destroy;
}
+ lpfc_setup_cq_lookup(phba);
}
-
- for (qidx = 0; qidx < io_channel; qidx += LPFC_MAX_EQ_DELAY_EQID_CNT)
- lpfc_modify_hba_eq_delay(phba, qidx, LPFC_MAX_EQ_DELAY_EQID_CNT,
- phba->cfg_fcp_imax);
-
return 0;
out_destroy:
@@ -9346,12 +9669,9 @@ out_error:
void
lpfc_sli4_queue_unset(struct lpfc_hba *phba)
{
+ struct lpfc_sli4_hdw_queue *qp;
int qidx;
- /* Unset the queues created for Flash Optimized Fabric operations */
- if (phba->cfg_fof)
- lpfc_fof_queue_destroy(phba);
-
/* Unset mailbox command work queue */
if (phba->sli4_hba.mbx_wq)
lpfc_mq_destroy(phba, phba->sli4_hba.mbx_wq);
@@ -9369,17 +9689,6 @@ lpfc_sli4_queue_unset(struct lpfc_hba *phba)
lpfc_rq_destroy(phba, phba->sli4_hba.hdr_rq,
phba->sli4_hba.dat_rq);
- /* Unset FCP work queue */
- if (phba->sli4_hba.fcp_wq)
- for (qidx = 0; qidx < phba->cfg_fcp_io_channel; qidx++)
- lpfc_wq_destroy(phba, phba->sli4_hba.fcp_wq[qidx]);
-
- /* Unset NVME work queue */
- if (phba->sli4_hba.nvme_wq) {
- for (qidx = 0; qidx < phba->cfg_nvme_io_channel; qidx++)
- lpfc_wq_destroy(phba, phba->sli4_hba.nvme_wq[qidx]);
- }
-
/* Unset mailbox command complete queue */
if (phba->sli4_hba.mbx_cq)
lpfc_cq_destroy(phba, phba->sli4_hba.mbx_cq);
@@ -9392,11 +9701,6 @@ lpfc_sli4_queue_unset(struct lpfc_hba *phba)
if (phba->sli4_hba.nvmels_cq)
lpfc_cq_destroy(phba, phba->sli4_hba.nvmels_cq);
- /* Unset NVME response complete queue */
- if (phba->sli4_hba.nvme_cq)
- for (qidx = 0; qidx < phba->cfg_nvme_io_channel; qidx++)
- lpfc_cq_destroy(phba, phba->sli4_hba.nvme_cq[qidx]);
-
if (phba->nvmet_support) {
/* Unset NVMET MRQ queue */
if (phba->sli4_hba.nvmet_mrq_hdr) {
@@ -9415,15 +9719,22 @@ lpfc_sli4_queue_unset(struct lpfc_hba *phba)
}
}
- /* Unset FCP response complete queue */
- if (phba->sli4_hba.fcp_cq)
- for (qidx = 0; qidx < phba->cfg_fcp_io_channel; qidx++)
- lpfc_cq_destroy(phba, phba->sli4_hba.fcp_cq[qidx]);
+ /* Unset fast-path SLI4 queues */
+ if (phba->sli4_hba.hdwq) {
+ for (qidx = 0; qidx < phba->cfg_hdw_queue; qidx++) {
+ qp = &phba->sli4_hba.hdwq[qidx];
+ lpfc_wq_destroy(phba, qp->fcp_wq);
+ lpfc_wq_destroy(phba, qp->nvme_wq);
+ lpfc_cq_destroy(phba, qp->fcp_cq);
+ lpfc_cq_destroy(phba, qp->nvme_cq);
+ if (qidx < phba->cfg_irq_chann)
+ lpfc_eq_destroy(phba, qp->hba_eq);
+ }
+ }
- /* Unset fast-path event queue */
- if (phba->sli4_hba.hba_eq)
- for (qidx = 0; qidx < phba->io_channel_irqs; qidx++)
- lpfc_eq_destroy(phba, phba->sli4_hba.hba_eq[qidx]);
+ kfree(phba->sli4_hba.cq_lookup);
+ phba->sli4_hba.cq_lookup = NULL;
+ phba->sli4_hba.cq_max = 0;
}
/**
@@ -9741,7 +10052,7 @@ lpfc_sli4_pci_mem_setup(struct lpfc_hba *phba)
{
struct pci_dev *pdev = phba->pcidev;
unsigned long bar0map_len, bar1map_len, bar2map_len;
- int error = -ENODEV;
+ int error;
uint32_t if_type;
if (!pdev)
@@ -9760,7 +10071,7 @@ lpfc_sli4_pci_mem_setup(struct lpfc_hba *phba)
*/
if (pci_read_config_dword(pdev, LPFC_SLI_INTF,
&phba->sli4_hba.sli_intf.word0)) {
- return error;
+ return -ENODEV;
}
/* There is no SLI3 failback for SLI4 devices. */
@@ -9770,7 +10081,7 @@ lpfc_sli4_pci_mem_setup(struct lpfc_hba *phba)
"2894 SLI_INTF reg contents invalid "
"sli_intf reg 0x%x\n",
phba->sli4_hba.sli_intf.word0);
- return error;
+ return -ENODEV;
}
if_type = bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf);
@@ -9794,7 +10105,7 @@ lpfc_sli4_pci_mem_setup(struct lpfc_hba *phba)
dev_printk(KERN_ERR, &pdev->dev,
"ioremap failed for SLI4 PCI config "
"registers.\n");
- goto out;
+ return -ENODEV;
}
phba->pci_bar0_memmap_p = phba->sli4_hba.conf_regs_memmap_p;
/* Set up BAR0 PCI config space register memory map */
@@ -9805,7 +10116,7 @@ lpfc_sli4_pci_mem_setup(struct lpfc_hba *phba)
if (if_type >= LPFC_SLI_INTF_IF_TYPE_2) {
dev_printk(KERN_ERR, &pdev->dev,
"FATAL - No BAR0 mapping for SLI4, if_type 2\n");
- goto out;
+ return -ENODEV;
}
phba->sli4_hba.conf_regs_memmap_p =
ioremap(phba->pci_bar0_map, bar0map_len);
@@ -9813,7 +10124,7 @@ lpfc_sli4_pci_mem_setup(struct lpfc_hba *phba)
dev_printk(KERN_ERR, &pdev->dev,
"ioremap failed for SLI4 PCI config "
"registers.\n");
- goto out;
+ return -ENODEV;
}
lpfc_sli4_bar0_register_memmap(phba, if_type);
}
@@ -9859,6 +10170,7 @@ lpfc_sli4_pci_mem_setup(struct lpfc_hba *phba)
if (!phba->sli4_hba.drbl_regs_memmap_p) {
dev_err(&pdev->dev,
"ioremap failed for SLI4 HBA doorbell registers.\n");
+ error = -ENOMEM;
goto out_iounmap_conf;
}
phba->pci_bar2_memmap_p = phba->sli4_hba.drbl_regs_memmap_p;
@@ -9908,6 +10220,7 @@ lpfc_sli4_pci_mem_setup(struct lpfc_hba *phba)
if (!phba->sli4_hba.dpp_regs_memmap_p) {
dev_err(&pdev->dev,
"ioremap failed for SLI4 HBA dpp registers.\n");
+ error = -ENOMEM;
goto out_iounmap_ctrl;
}
phba->pci_bar4_memmap_p = phba->sli4_hba.dpp_regs_memmap_p;
@@ -9918,13 +10231,13 @@ lpfc_sli4_pci_mem_setup(struct lpfc_hba *phba)
case LPFC_SLI_INTF_IF_TYPE_0:
case LPFC_SLI_INTF_IF_TYPE_2:
phba->sli4_hba.sli4_eq_clr_intr = lpfc_sli4_eq_clr_intr;
- phba->sli4_hba.sli4_eq_release = lpfc_sli4_eq_release;
- phba->sli4_hba.sli4_cq_release = lpfc_sli4_cq_release;
+ phba->sli4_hba.sli4_write_eq_db = lpfc_sli4_write_eq_db;
+ phba->sli4_hba.sli4_write_cq_db = lpfc_sli4_write_cq_db;
break;
case LPFC_SLI_INTF_IF_TYPE_6:
phba->sli4_hba.sli4_eq_clr_intr = lpfc_sli4_if6_eq_clr_intr;
- phba->sli4_hba.sli4_eq_release = lpfc_sli4_if6_eq_release;
- phba->sli4_hba.sli4_cq_release = lpfc_sli4_if6_cq_release;
+ phba->sli4_hba.sli4_write_eq_db = lpfc_sli4_if6_write_eq_db;
+ phba->sli4_hba.sli4_write_cq_db = lpfc_sli4_if6_write_cq_db;
break;
default:
break;
@@ -9938,7 +10251,7 @@ out_iounmap_ctrl:
iounmap(phba->sli4_hba.ctrl_regs_memmap_p);
out_iounmap_conf:
iounmap(phba->sli4_hba.conf_regs_memmap_p);
-out:
+
return error;
}
@@ -10205,25 +10518,97 @@ lpfc_sli_disable_intr(struct lpfc_hba *phba)
}
/**
+ * lpfc_find_cpu_handle - Find the CPU that corresponds to the specified EQ
+ * @phba: pointer to lpfc hba data structure.
+ * @id: EQ vector index or Hardware Queue index
+ * @match: LPFC_FIND_BY_EQ = match by EQ
+ * LPFC_FIND_BY_HDWQ = match by Hardware Queue
+ */
+static uint16_t
+lpfc_find_cpu_handle(struct lpfc_hba *phba, uint16_t id, int match)
+{
+ struct lpfc_vector_map_info *cpup;
+ int cpu;
+
+ /* Find the desired phys_id for the specified EQ */
+ for_each_present_cpu(cpu) {
+ cpup = &phba->sli4_hba.cpu_map[cpu];
+ if ((match == LPFC_FIND_BY_EQ) &&
+ (cpup->irq != LPFC_VECTOR_MAP_EMPTY) &&
+ (cpup->eq == id))
+ return cpu;
+ if ((match == LPFC_FIND_BY_HDWQ) && (cpup->hdwq == id))
+ return cpu;
+ }
+ return 0;
+}
+
+/**
+ * lpfc_find_eq_handle - Find the EQ that corresponds to the specified
+ * Hardware Queue
+ * @phba: pointer to lpfc hba data structure.
+ * @hdwq: Hardware Queue index
+ */
+static uint16_t
+lpfc_find_eq_handle(struct lpfc_hba *phba, uint16_t hdwq)
+{
+ struct lpfc_vector_map_info *cpup;
+ int cpu;
+
+ /* Find the desired phys_id for the specified EQ */
+ for_each_present_cpu(cpu) {
+ cpup = &phba->sli4_hba.cpu_map[cpu];
+ if (cpup->hdwq == hdwq)
+ return cpup->eq;
+ }
+ return 0;
+}
+
+#ifdef CONFIG_X86
+/**
+ * lpfc_find_hyper - Determine if the CPU map entry is hyper-threaded
+ * @phba: pointer to lpfc hba data structure.
+ * @cpu: CPU map index
+ * @phys_id: CPU package physical id
+ * @core_id: CPU core id
+ */
+static int
+lpfc_find_hyper(struct lpfc_hba *phba, int cpu,
+ uint16_t phys_id, uint16_t core_id)
+{
+ struct lpfc_vector_map_info *cpup;
+ int idx;
+
+ for_each_present_cpu(idx) {
+ cpup = &phba->sli4_hba.cpu_map[idx];
+ /* Does the cpup match the one we are looking for */
+ if ((cpup->phys_id == phys_id) &&
+ (cpup->core_id == core_id) &&
+ (cpu != idx))
+ return 1;
+ }
+ return 0;
+}
+#endif
+
+/**
* lpfc_cpu_affinity_check - Check vector CPU affinity mappings
* @phba: pointer to lpfc hba data structure.
* @vectors: number of msix vectors allocated.
*
* The routine will figure out the CPU affinity assignment for every
- * MSI-X vector allocated for the HBA. The hba_eq_hdl will be updated
- * with a pointer to the CPU mask that defines ALL the CPUs this vector
- * can be associated with. If the vector can be unquely associated with
- * a single CPU, that CPU will be recorded in hba_eq_hdl[index].cpu.
+ * MSI-X vector allocated for the HBA.
* In addition, the CPU to IO channel mapping will be calculated
* and the phba->sli4_hba.cpu_map array will reflect this.
*/
static void
lpfc_cpu_affinity_check(struct lpfc_hba *phba, int vectors)
{
+ int i, cpu, idx;
+ int max_phys_id, min_phys_id;
+ int max_core_id, min_core_id;
struct lpfc_vector_map_info *cpup;
- int index = 0;
- int vec = 0;
- int cpu;
+ const struct cpumask *maskp;
#ifdef CONFIG_X86
struct cpuinfo_x86 *cpuinfo;
#endif
@@ -10231,32 +10616,71 @@ lpfc_cpu_affinity_check(struct lpfc_hba *phba, int vectors)
/* Init cpu_map array */
memset(phba->sli4_hba.cpu_map, 0xff,
(sizeof(struct lpfc_vector_map_info) *
- phba->sli4_hba.num_present_cpu));
+ phba->sli4_hba.num_possible_cpu));
+
+ max_phys_id = 0;
+ min_phys_id = 0xffff;
+ max_core_id = 0;
+ min_core_id = 0xffff;
/* Update CPU map with physical id and core id of each CPU */
- cpup = phba->sli4_hba.cpu_map;
- for (cpu = 0; cpu < phba->sli4_hba.num_present_cpu; cpu++) {
+ for_each_present_cpu(cpu) {
+ cpup = &phba->sli4_hba.cpu_map[cpu];
#ifdef CONFIG_X86
cpuinfo = &cpu_data(cpu);
cpup->phys_id = cpuinfo->phys_proc_id;
cpup->core_id = cpuinfo->cpu_core_id;
+ cpup->hyper = lpfc_find_hyper(phba, cpu,
+ cpup->phys_id, cpup->core_id);
#else
/* No distinction between CPUs for other platforms */
cpup->phys_id = 0;
- cpup->core_id = 0;
+ cpup->core_id = cpu;
+ cpup->hyper = 0;
#endif
- cpup->channel_id = index; /* For now round robin */
- cpup->irq = pci_irq_vector(phba->pcidev, vec);
- vec++;
- if (vec >= vectors)
- vec = 0;
- index++;
- if (index >= phba->cfg_fcp_io_channel)
- index = 0;
- cpup++;
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "3328 CPU physid %d coreid %d\n",
+ cpup->phys_id, cpup->core_id);
+
+ if (cpup->phys_id > max_phys_id)
+ max_phys_id = cpup->phys_id;
+ if (cpup->phys_id < min_phys_id)
+ min_phys_id = cpup->phys_id;
+
+ if (cpup->core_id > max_core_id)
+ max_core_id = cpup->core_id;
+ if (cpup->core_id < min_core_id)
+ min_core_id = cpup->core_id;
}
-}
+ for_each_possible_cpu(i) {
+ struct lpfc_eq_intr_info *eqi =
+ per_cpu_ptr(phba->sli4_hba.eq_info, i);
+
+ INIT_LIST_HEAD(&eqi->list);
+ eqi->icnt = 0;
+ }
+
+ for (idx = 0; idx < phba->cfg_irq_chann; idx++) {
+ maskp = pci_irq_get_affinity(phba->pcidev, idx);
+ if (!maskp)
+ continue;
+
+ for_each_cpu_and(cpu, maskp, cpu_present_mask) {
+ cpup = &phba->sli4_hba.cpu_map[cpu];
+ cpup->eq = idx;
+ cpup->hdwq = idx;
+ cpup->irq = pci_irq_vector(phba->pcidev, idx);
+
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "3336 Set Affinity: CPU %d "
+ "hdwq %d irq %d\n",
+ cpu, cpup->hdwq, cpup->irq);
+ }
+ }
+ return;
+}
/**
* lpfc_sli4_enable_msix - Enable MSI-X interrupt mode to SLI-4 device
@@ -10276,12 +10700,10 @@ lpfc_sli4_enable_msix(struct lpfc_hba *phba)
char *name;
/* Set up MSI-X multi-message vectors */
- vectors = phba->io_channel_irqs;
- if (phba->cfg_fof)
- vectors++;
+ vectors = phba->cfg_irq_chann;
rc = pci_alloc_irq_vectors(phba->pcidev,
- (phba->nvmet_support) ? 1 : 2,
+ 1,
vectors, PCI_IRQ_MSIX | PCI_IRQ_AFFINITY);
if (rc < 0) {
lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
@@ -10299,17 +10721,10 @@ lpfc_sli4_enable_msix(struct lpfc_hba *phba)
phba->sli4_hba.hba_eq_hdl[index].idx = index;
phba->sli4_hba.hba_eq_hdl[index].phba = phba;
- atomic_set(&phba->sli4_hba.hba_eq_hdl[index].hba_eq_in_use, 1);
- if (phba->cfg_fof && (index == (vectors - 1)))
- rc = request_irq(pci_irq_vector(phba->pcidev, index),
- &lpfc_sli4_fof_intr_handler, 0,
- name,
- &phba->sli4_hba.hba_eq_hdl[index]);
- else
- rc = request_irq(pci_irq_vector(phba->pcidev, index),
- &lpfc_sli4_hba_intr_handler, 0,
- name,
- &phba->sli4_hba.hba_eq_hdl[index]);
+ rc = request_irq(pci_irq_vector(phba->pcidev, index),
+ &lpfc_sli4_hba_intr_handler, 0,
+ name,
+ &phba->sli4_hba.hba_eq_hdl[index]);
if (rc) {
lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
"0486 MSI-X fast-path (%d) "
@@ -10318,24 +10733,16 @@ lpfc_sli4_enable_msix(struct lpfc_hba *phba)
}
}
- if (phba->cfg_fof)
- vectors--;
-
- if (vectors != phba->io_channel_irqs) {
+ if (vectors != phba->cfg_irq_chann) {
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
"3238 Reducing IO channels to match number of "
"MSI-X vectors, requested %d got %d\n",
- phba->io_channel_irqs, vectors);
- if (phba->cfg_fcp_io_channel > vectors)
- phba->cfg_fcp_io_channel = vectors;
- if (phba->cfg_nvme_io_channel > vectors)
- phba->cfg_nvme_io_channel = vectors;
- if (phba->cfg_fcp_io_channel > phba->cfg_nvme_io_channel)
- phba->io_channel_irqs = phba->cfg_fcp_io_channel;
- else
- phba->io_channel_irqs = phba->cfg_nvme_io_channel;
+ phba->cfg_irq_chann, vectors);
+ if (phba->cfg_irq_chann > vectors)
+ phba->cfg_irq_chann = vectors;
+ if (phba->cfg_nvmet_mrq > vectors)
+ phba->cfg_nvmet_mrq = vectors;
}
- lpfc_cpu_affinity_check(phba, vectors);
return rc;
@@ -10390,15 +10797,11 @@ lpfc_sli4_enable_msi(struct lpfc_hba *phba)
return rc;
}
- for (index = 0; index < phba->io_channel_irqs; index++) {
+ for (index = 0; index < phba->cfg_irq_chann; index++) {
phba->sli4_hba.hba_eq_hdl[index].idx = index;
phba->sli4_hba.hba_eq_hdl[index].phba = phba;
}
- if (phba->cfg_fof) {
- phba->sli4_hba.hba_eq_hdl[index].idx = index;
- phba->sli4_hba.hba_eq_hdl[index].phba = phba;
- }
return 0;
}
@@ -10459,17 +10862,10 @@ lpfc_sli4_enable_intr(struct lpfc_hba *phba, uint32_t cfg_mode)
phba->intr_type = INTx;
intr_mode = 0;
- for (idx = 0; idx < phba->io_channel_irqs; idx++) {
- eqhdl = &phba->sli4_hba.hba_eq_hdl[idx];
- eqhdl->idx = idx;
- eqhdl->phba = phba;
- atomic_set(&eqhdl->hba_eq_in_use, 1);
- }
- if (phba->cfg_fof) {
+ for (idx = 0; idx < phba->cfg_irq_chann; idx++) {
eqhdl = &phba->sli4_hba.hba_eq_hdl[idx];
eqhdl->idx = idx;
eqhdl->phba = phba;
- atomic_set(&eqhdl->hba_eq_in_use, 1);
}
}
}
@@ -10493,13 +10889,13 @@ lpfc_sli4_disable_intr(struct lpfc_hba *phba)
int index;
/* Free up MSI-X multi-message vectors */
- for (index = 0; index < phba->io_channel_irqs; index++)
- free_irq(pci_irq_vector(phba->pcidev, index),
- &phba->sli4_hba.hba_eq_hdl[index]);
-
- if (phba->cfg_fof)
+ for (index = 0; index < phba->cfg_irq_chann; index++) {
+ irq_set_affinity_hint(
+ pci_irq_vector(phba->pcidev, index),
+ NULL);
free_irq(pci_irq_vector(phba->pcidev, index),
&phba->sli4_hba.hba_eq_hdl[index]);
+ }
} else {
free_irq(phba->pcidev->irq, phba);
}
@@ -10560,8 +10956,10 @@ lpfc_unset_hba(struct lpfc_hba *phba)
static void
lpfc_sli4_xri_exchange_busy_wait(struct lpfc_hba *phba)
{
+ struct lpfc_sli4_hdw_queue *qp;
+ int idx, ccnt, fcnt;
int wait_time = 0;
- int nvme_xri_cmpl = 1;
+ int io_xri_cmpl = 1;
int nvmet_xri_cmpl = 1;
int fcp_xri_cmpl = 1;
int els_xri_cmpl = list_empty(&phba->sli4_hba.lpfc_abts_els_sgl_list);
@@ -10576,17 +10974,32 @@ lpfc_sli4_xri_exchange_busy_wait(struct lpfc_hba *phba)
if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME)
lpfc_nvme_wait_for_io_drain(phba);
- if (phba->cfg_enable_fc4_type & LPFC_ENABLE_FCP)
- fcp_xri_cmpl =
- list_empty(&phba->sli4_hba.lpfc_abts_scsi_buf_list);
+ ccnt = 0;
+ fcnt = 0;
+ for (idx = 0; idx < phba->cfg_hdw_queue; idx++) {
+ qp = &phba->sli4_hba.hdwq[idx];
+ fcp_xri_cmpl = list_empty(
+ &qp->lpfc_abts_scsi_buf_list);
+ if (!fcp_xri_cmpl) /* if list is NOT empty */
+ fcnt++;
+ if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) {
+ io_xri_cmpl = list_empty(
+ &qp->lpfc_abts_nvme_buf_list);
+ if (!io_xri_cmpl) /* if list is NOT empty */
+ ccnt++;
+ }
+ }
+ if (ccnt)
+ io_xri_cmpl = 0;
+ if (fcnt)
+ fcp_xri_cmpl = 0;
+
if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) {
- nvme_xri_cmpl =
- list_empty(&phba->sli4_hba.lpfc_abts_nvme_buf_list);
nvmet_xri_cmpl =
list_empty(&phba->sli4_hba.lpfc_abts_nvmet_ctx_list);
}
- while (!fcp_xri_cmpl || !els_xri_cmpl || !nvme_xri_cmpl ||
+ while (!fcp_xri_cmpl || !els_xri_cmpl || !io_xri_cmpl ||
!nvmet_xri_cmpl) {
if (wait_time > LPFC_XRI_EXCH_BUSY_WAIT_TMO) {
if (!nvmet_xri_cmpl)
@@ -10594,7 +11007,7 @@ lpfc_sli4_xri_exchange_busy_wait(struct lpfc_hba *phba)
"6424 NVMET XRI exchange busy "
"wait time: %d seconds.\n",
wait_time/1000);
- if (!nvme_xri_cmpl)
+ if (!io_xri_cmpl)
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
"6100 NVME XRI exchange busy "
"wait time: %d seconds.\n",
@@ -10615,17 +11028,31 @@ lpfc_sli4_xri_exchange_busy_wait(struct lpfc_hba *phba)
msleep(LPFC_XRI_EXCH_BUSY_WAIT_T1);
wait_time += LPFC_XRI_EXCH_BUSY_WAIT_T1;
}
+
+ ccnt = 0;
+ fcnt = 0;
+ for (idx = 0; idx < phba->cfg_hdw_queue; idx++) {
+ qp = &phba->sli4_hba.hdwq[idx];
+ fcp_xri_cmpl = list_empty(
+ &qp->lpfc_abts_scsi_buf_list);
+ if (!fcp_xri_cmpl) /* if list is NOT empty */
+ fcnt++;
+ if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) {
+ io_xri_cmpl = list_empty(
+ &qp->lpfc_abts_nvme_buf_list);
+ if (!io_xri_cmpl) /* if list is NOT empty */
+ ccnt++;
+ }
+ }
+ if (ccnt)
+ io_xri_cmpl = 0;
+ if (fcnt)
+ fcp_xri_cmpl = 0;
+
if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) {
- nvme_xri_cmpl = list_empty(
- &phba->sli4_hba.lpfc_abts_nvme_buf_list);
nvmet_xri_cmpl = list_empty(
&phba->sli4_hba.lpfc_abts_nvmet_ctx_list);
}
-
- if (phba->cfg_enable_fc4_type & LPFC_ENABLE_FCP)
- fcp_xri_cmpl = list_empty(
- &phba->sli4_hba.lpfc_abts_scsi_buf_list);
-
els_xri_cmpl =
list_empty(&phba->sli4_hba.lpfc_abts_els_sgl_list);
@@ -10650,7 +11077,8 @@ lpfc_sli4_hba_unset(struct lpfc_hba *phba)
struct pci_dev *pdev = phba->pcidev;
lpfc_stop_hba_timers(phba);
- phba->sli4_hba.intr_enable = 0;
+ if (phba->pport)
+ phba->sli4_hba.intr_enable = 0;
/*
* Gracefully wait out the potential current outstanding asynchronous
@@ -10711,7 +11139,8 @@ lpfc_sli4_hba_unset(struct lpfc_hba *phba)
lpfc_sli4_ras_dma_free(phba);
/* Stop the SLI4 device port */
- phba->pport->work_port_events = 0;
+ if (phba->pport)
+ phba->pport->work_port_events = 0;
}
/**
@@ -10869,8 +11298,6 @@ lpfc_get_sli4_parameters(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
phba->nvme_support = 0;
phba->nvmet_support = 0;
phba->cfg_nvmet_mrq = LPFC_NVMET_MRQ_OFF;
- phba->cfg_nvme_io_channel = 0;
- phba->io_channel_irqs = phba->cfg_fcp_io_channel;
lpfc_printf_log(phba, KERN_ERR, LOG_INIT | LOG_NVME,
"6101 Disabling NVME support: "
"Not supported by firmware: %d %d\n",
@@ -11195,6 +11622,8 @@ lpfc_pci_remove_one_s3(struct pci_dev *pdev)
* corresponding pools here.
*/
lpfc_scsi_free(phba);
+ lpfc_free_iocb_list(phba);
+
lpfc_mem_free_all(phba);
dma_free_coherent(&pdev->dev, lpfc_sli_hbq_size(),
@@ -11820,28 +12249,11 @@ lpfc_pci_probe_one_s4(struct pci_dev *pdev, const struct pci_device_id *pid)
/* Get the default values for Model Name and Description */
lpfc_get_hba_model_desc(phba, phba->ModelName, phba->ModelDesc);
- /* Create SCSI host to the physical port */
- error = lpfc_create_shost(phba);
- if (error) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "1415 Failed to create scsi host.\n");
- goto out_unset_driver_resource;
- }
-
- /* Configure sysfs attributes */
- vport = phba->pport;
- error = lpfc_alloc_sysfs_attr(vport);
- if (error) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "1416 Failed to allocate sysfs attr\n");
- goto out_destroy_shost;
- }
-
- shost = lpfc_shost_from_vport(vport); /* save shost for error cleanup */
/* Now, trying to enable interrupt and bring up the device */
cfg_mode = phba->cfg_use_msi;
/* Put device to a known state before enabling interrupt */
+ phba->pport = NULL;
lpfc_stop_port(phba);
/* Configure and enable interrupt */
@@ -11850,18 +12262,34 @@ lpfc_pci_probe_one_s4(struct pci_dev *pdev, const struct pci_device_id *pid)
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
"0426 Failed to enable interrupt.\n");
error = -ENODEV;
- goto out_free_sysfs_attr;
+ goto out_unset_driver_resource;
}
/* Default to single EQ for non-MSI-X */
if (phba->intr_type != MSIX) {
- if (phba->cfg_enable_fc4_type & LPFC_ENABLE_FCP)
- phba->cfg_fcp_io_channel = 1;
+ phba->cfg_irq_chann = 1;
if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) {
- phba->cfg_nvme_io_channel = 1;
if (phba->nvmet_support)
phba->cfg_nvmet_mrq = 1;
}
- phba->io_channel_irqs = 1;
+ }
+ lpfc_cpu_affinity_check(phba, phba->cfg_irq_chann);
+
+ /* Create SCSI host to the physical port */
+ error = lpfc_create_shost(phba);
+ if (error) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "1415 Failed to create scsi host.\n");
+ goto out_disable_intr;
+ }
+ vport = phba->pport;
+ shost = lpfc_shost_from_vport(vport); /* save shost for error cleanup */
+
+ /* Configure sysfs attributes */
+ error = lpfc_alloc_sysfs_attr(vport);
+ if (error) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "1416 Failed to allocate sysfs attr\n");
+ goto out_destroy_shost;
}
/* Set up SLI-4 HBA */
@@ -11869,7 +12297,7 @@ lpfc_pci_probe_one_s4(struct pci_dev *pdev, const struct pci_device_id *pid)
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
"1421 Failed to set up hba\n");
error = -ENODEV;
- goto out_disable_intr;
+ goto out_free_sysfs_attr;
}
/* Log the current active interrupt mode */
@@ -11882,19 +12310,20 @@ lpfc_pci_probe_one_s4(struct pci_dev *pdev, const struct pci_device_id *pid)
/* NVME support in FW earlier in the driver load corrects the
* FC4 type making a check for nvme_support unnecessary.
*/
- if ((phba->nvmet_support == 0) &&
- (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME)) {
- /* Create NVME binding with nvme_fc_transport. This
- * ensures the vport is initialized. If the localport
- * create fails, it should not unload the driver to
- * support field issues.
- */
- error = lpfc_nvme_create_localport(vport);
- if (error) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "6004 NVME registration failed, "
- "error x%x\n",
- error);
+ if (phba->nvmet_support == 0) {
+ if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) {
+ /* Create NVME binding with nvme_fc_transport. This
+ * ensures the vport is initialized. If the localport
+ * create fails, it should not unload the driver to
+ * support field issues.
+ */
+ error = lpfc_nvme_create_localport(vport);
+ if (error) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "6004 NVME registration "
+ "failed, error x%x\n",
+ error);
+ }
}
}
@@ -11910,12 +12339,12 @@ lpfc_pci_probe_one_s4(struct pci_dev *pdev, const struct pci_device_id *pid)
return 0;
-out_disable_intr:
- lpfc_sli4_disable_intr(phba);
out_free_sysfs_attr:
lpfc_free_sysfs_attr(vport);
out_destroy_shost:
lpfc_destroy_shost(phba);
+out_disable_intr:
+ lpfc_sli4_disable_intr(phba);
out_unset_driver_resource:
lpfc_unset_driver_resource_phase2(phba);
out_unset_driver_resource_s4:
@@ -11978,13 +12407,16 @@ lpfc_pci_remove_one_s4(struct pci_dev *pdev)
lpfc_nvmet_destroy_targetport(phba);
lpfc_nvme_destroy_localport(vport);
+ /* De-allocate multi-XRI pools */
+ if (phba->cfg_xri_rebalancing)
+ lpfc_destroy_multixri_pools(phba);
+
/*
* Bring down the SLI Layer. This step disables all interrupts,
* clears the rings, discards all mailbox commands, and resets
* the HBA FCoE function.
*/
lpfc_debugfs_terminate(vport);
- lpfc_sli4_hba_unset(phba);
lpfc_stop_hba_timers(phba);
spin_lock_irq(&phba->port_list_lock);
@@ -11994,9 +12426,9 @@ lpfc_pci_remove_one_s4(struct pci_dev *pdev)
/* Perform scsi free before driver resource_unset since scsi
* buffers are released to their corresponding pools here.
*/
- lpfc_scsi_free(phba);
- lpfc_nvme_free(phba);
+ lpfc_io_free(phba);
lpfc_free_iocb_list(phba);
+ lpfc_sli4_hba_unset(phba);
lpfc_unset_driver_resource_phase2(phba);
lpfc_sli4_driver_resource_unset(phba);
@@ -12658,165 +13090,6 @@ lpfc_sli4_ras_init(struct lpfc_hba *phba)
}
}
-/**
- * lpfc_fof_queue_setup - Set up all the fof queues
- * @phba: pointer to lpfc hba data structure.
- *
- * This routine is invoked to set up all the fof queues for the FC HBA
- * operation.
- *
- * Return codes
- * 0 - successful
- * -ENOMEM - No available memory
- **/
-int
-lpfc_fof_queue_setup(struct lpfc_hba *phba)
-{
- struct lpfc_sli_ring *pring;
- int rc;
-
- rc = lpfc_eq_create(phba, phba->sli4_hba.fof_eq, LPFC_MAX_IMAX);
- if (rc)
- return -ENOMEM;
-
- if (phba->cfg_fof) {
-
- rc = lpfc_cq_create(phba, phba->sli4_hba.oas_cq,
- phba->sli4_hba.fof_eq, LPFC_WCQ, LPFC_FCP);
- if (rc)
- goto out_oas_cq;
-
- rc = lpfc_wq_create(phba, phba->sli4_hba.oas_wq,
- phba->sli4_hba.oas_cq, LPFC_FCP);
- if (rc)
- goto out_oas_wq;
-
- /* Bind this CQ/WQ to the NVME ring */
- pring = phba->sli4_hba.oas_wq->pring;
- pring->sli.sli4.wqp =
- (void *)phba->sli4_hba.oas_wq;
- phba->sli4_hba.oas_cq->pring = pring;
- }
-
- return 0;
-
-out_oas_wq:
- lpfc_cq_destroy(phba, phba->sli4_hba.oas_cq);
-out_oas_cq:
- lpfc_eq_destroy(phba, phba->sli4_hba.fof_eq);
- return rc;
-
-}
-
-/**
- * lpfc_fof_queue_create - Create all the fof queues
- * @phba: pointer to lpfc hba data structure.
- *
- * This routine is invoked to allocate all the fof queues for the FC HBA
- * operation. For each SLI4 queue type, the parameters such as queue entry
- * count (queue depth) shall be taken from the module parameter. For now,
- * we just use some constant number as place holder.
- *
- * Return codes
- * 0 - successful
- * -ENOMEM - No availble memory
- * -EIO - The mailbox failed to complete successfully.
- **/
-int
-lpfc_fof_queue_create(struct lpfc_hba *phba)
-{
- struct lpfc_queue *qdesc;
- uint32_t wqesize;
-
- /* Create FOF EQ */
- qdesc = lpfc_sli4_queue_alloc(phba, LPFC_DEFAULT_PAGE_SIZE,
- phba->sli4_hba.eq_esize,
- phba->sli4_hba.eq_ecount);
- if (!qdesc)
- goto out_error;
-
- qdesc->qe_valid = 1;
- phba->sli4_hba.fof_eq = qdesc;
-
- if (phba->cfg_fof) {
-
- /* Create OAS CQ */
- if (phba->enab_exp_wqcq_pages)
- qdesc = lpfc_sli4_queue_alloc(phba,
- LPFC_EXPANDED_PAGE_SIZE,
- phba->sli4_hba.cq_esize,
- LPFC_CQE_EXP_COUNT);
- else
- qdesc = lpfc_sli4_queue_alloc(phba,
- LPFC_DEFAULT_PAGE_SIZE,
- phba->sli4_hba.cq_esize,
- phba->sli4_hba.cq_ecount);
- if (!qdesc)
- goto out_error;
-
- qdesc->qe_valid = 1;
- phba->sli4_hba.oas_cq = qdesc;
-
- /* Create OAS WQ */
- if (phba->enab_exp_wqcq_pages) {
- wqesize = (phba->fcp_embed_io) ?
- LPFC_WQE128_SIZE : phba->sli4_hba.wq_esize;
- qdesc = lpfc_sli4_queue_alloc(phba,
- LPFC_EXPANDED_PAGE_SIZE,
- wqesize,
- LPFC_WQE_EXP_COUNT);
- } else
- qdesc = lpfc_sli4_queue_alloc(phba,
- LPFC_DEFAULT_PAGE_SIZE,
- phba->sli4_hba.wq_esize,
- phba->sli4_hba.wq_ecount);
-
- if (!qdesc)
- goto out_error;
-
- phba->sli4_hba.oas_wq = qdesc;
- list_add_tail(&qdesc->wq_list, &phba->sli4_hba.lpfc_wq_list);
-
- }
- return 0;
-
-out_error:
- lpfc_fof_queue_destroy(phba);
- return -ENOMEM;
-}
-
-/**
- * lpfc_fof_queue_destroy - Destroy all the fof queues
- * @phba: pointer to lpfc hba data structure.
- *
- * This routine is invoked to release all the SLI4 queues with the FC HBA
- * operation.
- *
- * Return codes
- * 0 - successful
- **/
-int
-lpfc_fof_queue_destroy(struct lpfc_hba *phba)
-{
- /* Release FOF Event queue */
- if (phba->sli4_hba.fof_eq != NULL) {
- lpfc_sli4_queue_free(phba->sli4_hba.fof_eq);
- phba->sli4_hba.fof_eq = NULL;
- }
-
- /* Release OAS Completion queue */
- if (phba->sli4_hba.oas_cq != NULL) {
- lpfc_sli4_queue_free(phba->sli4_hba.oas_cq);
- phba->sli4_hba.oas_cq = NULL;
- }
-
- /* Release OAS Work queue */
- if (phba->sli4_hba.oas_wq != NULL) {
- lpfc_sli4_queue_free(phba->sli4_hba.oas_wq);
- phba->sli4_hba.oas_wq = NULL;
- }
- return 0;
-}
MODULE_DEVICE_TABLE(pci, lpfc_id_table);
@@ -12888,7 +13161,6 @@ lpfc_init(void)
lpfc_nvmet_cmd_template();
/* Initialize in case vector mapping is needed */
- lpfc_used_cpu = NULL;
lpfc_present_cpu = num_present_cpus();
error = pci_register_driver(&lpfc_driver);
@@ -12927,7 +13199,6 @@ lpfc_exit(void)
(1L << _dump_buf_dif_order), _dump_buf_dif);
free_pages((unsigned long)_dump_buf_dif, _dump_buf_dif_order);
}
- kfree(lpfc_used_cpu);
idr_destroy(&lpfc_hba_index);
}
diff --git a/drivers/scsi/lpfc/lpfc_mbox.c b/drivers/scsi/lpfc/lpfc_mbox.c
index 4d3b94317515..8abe933bad09 100644
--- a/drivers/scsi/lpfc/lpfc_mbox.c
+++ b/drivers/scsi/lpfc/lpfc_mbox.c
@@ -2095,8 +2095,8 @@ lpfc_request_features(struct lpfc_hba *phba, struct lpfcMboxq *mboxq)
if (phba->nvmet_support) {
bf_set(lpfc_mbx_rq_ftr_rq_mrqp, &mboxq->u.mqe.un.req_ftrs, 1);
/* iaab/iaar NOT set for now */
- bf_set(lpfc_mbx_rq_ftr_rq_iaab, &mboxq->u.mqe.un.req_ftrs, 0);
- bf_set(lpfc_mbx_rq_ftr_rq_iaar, &mboxq->u.mqe.un.req_ftrs, 0);
+ bf_set(lpfc_mbx_rq_ftr_rq_iaab, &mboxq->u.mqe.un.req_ftrs, 0);
+ bf_set(lpfc_mbx_rq_ftr_rq_iaar, &mboxq->u.mqe.un.req_ftrs, 0);
}
return;
}
diff --git a/drivers/scsi/lpfc/lpfc_nportdisc.c b/drivers/scsi/lpfc/lpfc_nportdisc.c
index 96bc3789a166..6172682a24ba 100644
--- a/drivers/scsi/lpfc/lpfc_nportdisc.c
+++ b/drivers/scsi/lpfc/lpfc_nportdisc.c
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term *
+ * Copyright (C) 2017-2019 Broadcom. All Rights Reserved. The term *
* “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. *
* Copyright (C) 2004-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
@@ -825,7 +825,7 @@ lpfc_rcv_prli(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
"rport rolechg: role:x%x did:x%x flg:x%x",
roles, ndlp->nlp_DID, ndlp->nlp_flag);
- if (phba->cfg_enable_fc4_type != LPFC_ENABLE_NVME)
+ if (vport->cfg_enable_fc4_type != LPFC_ENABLE_NVME)
fc_remote_port_rolechg(rport, roles);
}
}
@@ -1789,8 +1789,8 @@ lpfc_cmpl_reglogin_reglogin_issue(struct lpfc_vport *vport,
* is configured try it.
*/
ndlp->nlp_fc4_type |= NLP_FC4_FCP;
- if ((phba->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) ||
- (phba->cfg_enable_fc4_type == LPFC_ENABLE_NVME)) {
+ if ((vport->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) ||
+ (vport->cfg_enable_fc4_type == LPFC_ENABLE_NVME)) {
ndlp->nlp_fc4_type |= NLP_FC4_NVME;
/* We need to update the localport also */
lpfc_nvme_update_localport(vport);
@@ -1804,7 +1804,7 @@ lpfc_cmpl_reglogin_reglogin_issue(struct lpfc_vport *vport,
* should just issue PRLI for FCP. Otherwise issue
* GFT_ID to determine if remote port supports NVME.
*/
- if (phba->cfg_enable_fc4_type != LPFC_ENABLE_FCP) {
+ if (vport->cfg_enable_fc4_type != LPFC_ENABLE_FCP) {
rc = lpfc_ns_cmd(vport, SLI_CTNS_GFT_ID,
0, ndlp->nlp_DID);
return ndlp->nlp_state;
diff --git a/drivers/scsi/lpfc/lpfc_nvme.c b/drivers/scsi/lpfc/lpfc_nvme.c
index 8c9f79042228..1aa00d2c3f74 100644
--- a/drivers/scsi/lpfc/lpfc_nvme.c
+++ b/drivers/scsi/lpfc/lpfc_nvme.c
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term *
+ * Copyright (C) 2017-2019 Broadcom. All Rights Reserved. The term *
* “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. *
* Copyright (C) 2004-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
@@ -56,12 +56,12 @@
/* NVME initiator-based functions */
-static struct lpfc_nvme_buf *
+static struct lpfc_io_buf *
lpfc_get_nvme_buf(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp,
- int expedite);
+ int idx, int expedite);
static void
-lpfc_release_nvme_buf(struct lpfc_hba *, struct lpfc_nvme_buf *);
+lpfc_release_nvme_buf(struct lpfc_hba *, struct lpfc_io_buf *);
static struct nvme_fc_port_template lpfc_nvme_template;
@@ -239,7 +239,7 @@ lpfc_nvme_create_queue(struct nvme_fc_local_port *pnvme_lport,
if (qidx) {
str = "IO "; /* IO queue */
qhandle->index = ((qidx - 1) %
- vport->phba->cfg_nvme_io_channel);
+ lpfc_nvme_template.max_hw_queues);
} else {
str = "ADM"; /* Admin queue */
qhandle->index = qidx;
@@ -247,7 +247,7 @@ lpfc_nvme_create_queue(struct nvme_fc_local_port *pnvme_lport,
lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME,
"6073 Binding %s HdwQueue %d (cpu %d) to "
- "io_channel %d qhandle %p\n", str,
+ "hdw_queue %d qhandle %p\n", str,
qidx, qhandle->cpu_id, qhandle->index, qhandle);
*handle = (void *)qhandle;
return 0;
@@ -529,7 +529,7 @@ lpfc_nvme_gen_req(struct lpfc_vport *vport, struct lpfc_dmabuf *bmp,
lpfc_nvmeio_data(phba, "NVME LS XMIT: xri x%x iotag x%x to x%06x\n",
genwqe->sli4_xritag, genwqe->iotag, ndlp->nlp_DID);
- rc = lpfc_sli4_issue_wqe(phba, LPFC_ELS_RING, genwqe);
+ rc = lpfc_sli4_issue_wqe(phba, &phba->sli4_hba.hdwq[0], genwqe);
if (rc) {
lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
"6045 Issue GEN REQ WQE to NPORT x%x "
@@ -761,7 +761,7 @@ lpfc_nvme_ls_abort(struct nvme_fc_local_port *pnvme_lport,
/* Fix up the existing sgls for NVME IO. */
static inline void
lpfc_nvme_adj_fcp_sgls(struct lpfc_vport *vport,
- struct lpfc_nvme_buf *lpfc_ncmd,
+ struct lpfc_io_buf *lpfc_ncmd,
struct nvmefc_fcp_req *nCmd)
{
struct lpfc_hba *phba = vport->phba;
@@ -784,7 +784,7 @@ lpfc_nvme_adj_fcp_sgls(struct lpfc_vport *vport,
* rather than the virtual memory to ease the restore
* operation.
*/
- sgl = lpfc_ncmd->nvme_sgl;
+ sgl = lpfc_ncmd->dma_sgl;
sgl->sge_len = cpu_to_le32(nCmd->cmdlen);
if (phba->cfg_nvme_embed_cmd) {
sgl->addr_hi = 0;
@@ -858,7 +858,7 @@ lpfc_nvme_adj_fcp_sgls(struct lpfc_vport *vport,
#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
static void
lpfc_nvme_ktime(struct lpfc_hba *phba,
- struct lpfc_nvme_buf *lpfc_ncmd)
+ struct lpfc_io_buf *lpfc_ncmd)
{
uint64_t seg1, seg2, seg3, seg4;
uint64_t segsum;
@@ -956,57 +956,54 @@ static void
lpfc_nvme_io_cmd_wqe_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pwqeIn,
struct lpfc_wcqe_complete *wcqe)
{
- struct lpfc_nvme_buf *lpfc_ncmd =
- (struct lpfc_nvme_buf *)pwqeIn->context1;
+ struct lpfc_io_buf *lpfc_ncmd =
+ (struct lpfc_io_buf *)pwqeIn->context1;
struct lpfc_vport *vport = pwqeIn->vport;
struct nvmefc_fcp_req *nCmd;
struct nvme_fc_ersp_iu *ep;
struct nvme_fc_cmd_iu *cp;
- struct lpfc_nvme_rport *rport;
struct lpfc_nodelist *ndlp;
struct lpfc_nvme_fcpreq_priv *freqpriv;
struct lpfc_nvme_lport *lport;
- struct lpfc_nvme_ctrl_stat *cstat;
- unsigned long flags;
uint32_t code, status, idx;
uint16_t cid, sqhd, data;
uint32_t *ptr;
/* Sanity check on return of outstanding command */
- if (!lpfc_ncmd || !lpfc_ncmd->nvmeCmd || !lpfc_ncmd->nrport) {
- if (!lpfc_ncmd) {
- lpfc_printf_vlog(vport, KERN_ERR,
- LOG_NODE | LOG_NVME_IOERR,
- "6071 Null lpfc_ncmd pointer. No "
- "release, skip completion\n");
- return;
- }
+ if (!lpfc_ncmd) {
+ lpfc_printf_vlog(vport, KERN_ERR,
+ LOG_NODE | LOG_NVME_IOERR,
+ "6071 Null lpfc_ncmd pointer. No "
+ "release, skip completion\n");
+ return;
+ }
+
+ /* Guard against abort handler being called at same time */
+ spin_lock(&lpfc_ncmd->buf_lock);
+ if (!lpfc_ncmd->nvmeCmd) {
+ spin_unlock(&lpfc_ncmd->buf_lock);
lpfc_printf_vlog(vport, KERN_ERR, LOG_NODE | LOG_NVME_IOERR,
"6066 Missing cmpl ptrs: lpfc_ncmd %p, "
- "nvmeCmd %p nrport %p\n",
- lpfc_ncmd, lpfc_ncmd->nvmeCmd,
- lpfc_ncmd->nrport);
+ "nvmeCmd %p\n",
+ lpfc_ncmd, lpfc_ncmd->nvmeCmd);
/* Release the lpfc_ncmd regardless of the missing elements. */
lpfc_release_nvme_buf(phba, lpfc_ncmd);
return;
}
nCmd = lpfc_ncmd->nvmeCmd;
- rport = lpfc_ncmd->nrport;
status = bf_get(lpfc_wcqe_c_status, wcqe);
+ idx = lpfc_ncmd->cur_iocbq.hba_wqidx;
+ phba->sli4_hba.hdwq[idx].nvme_cstat.io_cmpls++;
+
if (vport->localport) {
lport = (struct lpfc_nvme_lport *)vport->localport->private;
- if (lport) {
- idx = lpfc_ncmd->cur_iocbq.hba_wqidx;
- cstat = &lport->cstat[idx];
- atomic_inc(&cstat->fc4NvmeIoCmpls);
- if (status) {
- if (bf_get(lpfc_wcqe_c_xb, wcqe))
- atomic_inc(&lport->cmpl_fcp_xb);
- atomic_inc(&lport->cmpl_fcp_err);
- }
+ if (lport && status) {
+ if (bf_get(lpfc_wcqe_c_xb, wcqe))
+ atomic_inc(&lport->cmpl_fcp_xb);
+ atomic_inc(&lport->cmpl_fcp_err);
}
}
@@ -1017,18 +1014,11 @@ lpfc_nvme_io_cmd_wqe_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pwqeIn,
* Catch race where our node has transitioned, but the
* transport is still transitioning.
*/
- ndlp = rport->ndlp;
+ ndlp = lpfc_ncmd->ndlp;
if (!ndlp || !NLP_CHK_NODE_ACT(ndlp)) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_NODE | LOG_NVME_IOERR,
- "6061 rport %p, DID x%06x node not ready.\n",
- rport, rport->remoteport->port_id);
-
- ndlp = lpfc_findnode_did(vport, rport->remoteport->port_id);
- if (!ndlp) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME_IOERR,
- "6062 Ignoring NVME cmpl. No ndlp\n");
- goto out_err;
- }
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME_IOERR,
+ "6062 Ignoring NVME cmpl. No ndlp\n");
+ goto out_err;
}
code = bf_get(lpfc_wcqe_c_code, wcqe);
@@ -1148,13 +1138,18 @@ out_err:
lpfc_nvme_ktime(phba, lpfc_ncmd);
}
if (phba->cpucheck_on & LPFC_CHECK_NVME_IO) {
- if (lpfc_ncmd->cpu != smp_processor_id())
- lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME_IOERR,
- "6701 CPU Check cmpl: "
- "cpu %d expect %d\n",
- smp_processor_id(), lpfc_ncmd->cpu);
- if (lpfc_ncmd->cpu < LPFC_CHECK_CPU_CNT)
- phba->cpucheck_cmpl_io[lpfc_ncmd->cpu]++;
+ uint32_t cpu;
+ idx = lpfc_ncmd->cur_iocbq.hba_wqidx;
+ cpu = smp_processor_id();
+ if (cpu < LPFC_CHECK_CPU_CNT) {
+ if (lpfc_ncmd->cpu != cpu)
+ lpfc_printf_vlog(vport,
+ KERN_INFO, LOG_NVME_IOERR,
+ "6701 CPU Check cmpl: "
+ "cpu %d expect %d\n",
+ cpu, lpfc_ncmd->cpu);
+ phba->sli4_hba.hdwq[idx].cpucheck_cmpl_io[cpu]++;
+ }
}
#endif
@@ -1165,13 +1160,11 @@ out_err:
if (!(lpfc_ncmd->flags & LPFC_SBUF_XBUSY)) {
freqpriv = nCmd->private;
freqpriv->nvme_buf = NULL;
- nCmd->done(nCmd);
lpfc_ncmd->nvmeCmd = NULL;
- }
-
- spin_lock_irqsave(&phba->hbalock, flags);
- lpfc_ncmd->nrport = NULL;
- spin_unlock_irqrestore(&phba->hbalock, flags);
+ spin_unlock(&lpfc_ncmd->buf_lock);
+ nCmd->done(nCmd);
+ } else
+ spin_unlock(&lpfc_ncmd->buf_lock);
/* Call release with XB=1 to queue the IO into the abort list. */
lpfc_release_nvme_buf(phba, lpfc_ncmd);
@@ -1196,9 +1189,9 @@ out_err:
**/
static int
lpfc_nvme_prep_io_cmd(struct lpfc_vport *vport,
- struct lpfc_nvme_buf *lpfc_ncmd,
+ struct lpfc_io_buf *lpfc_ncmd,
struct lpfc_nodelist *pnode,
- struct lpfc_nvme_ctrl_stat *cstat)
+ struct lpfc_fc4_ctrl_stat *cstat)
{
struct lpfc_hba *phba = vport->phba;
struct nvmefc_fcp_req *nCmd = lpfc_ncmd->nvmeCmd;
@@ -1206,7 +1199,7 @@ lpfc_nvme_prep_io_cmd(struct lpfc_vport *vport,
union lpfc_wqe128 *wqe = &pwqeq->wqe;
uint32_t req_len;
- if (!pnode || !NLP_CHK_NODE_ACT(pnode))
+ if (!NLP_CHK_NODE_ACT(pnode))
return -EINVAL;
/*
@@ -1236,7 +1229,7 @@ lpfc_nvme_prep_io_cmd(struct lpfc_vport *vport,
} else {
wqe->fcp_iwrite.initial_xfer_len = 0;
}
- atomic_inc(&cstat->fc4NvmeOutputRequests);
+ cstat->output_requests++;
} else {
/* From the iread template, initialize words 7 - 11 */
memcpy(&wqe->words[7],
@@ -1249,13 +1242,13 @@ lpfc_nvme_prep_io_cmd(struct lpfc_vport *vport,
/* Word 5 */
wqe->fcp_iread.rsrvd5 = 0;
- atomic_inc(&cstat->fc4NvmeInputRequests);
+ cstat->input_requests++;
}
} else {
/* From the icmnd template, initialize words 4 - 11 */
memcpy(&wqe->words[4], &lpfc_icmnd_cmd_template.words[4],
sizeof(uint32_t) * 8);
- atomic_inc(&cstat->fc4NvmeControlRequests);
+ cstat->control_requests++;
}
/*
* Finish initializing those WQE fields that are independent
@@ -1302,12 +1295,12 @@ lpfc_nvme_prep_io_cmd(struct lpfc_vport *vport,
**/
static int
lpfc_nvme_prep_io_dma(struct lpfc_vport *vport,
- struct lpfc_nvme_buf *lpfc_ncmd)
+ struct lpfc_io_buf *lpfc_ncmd)
{
struct lpfc_hba *phba = vport->phba;
struct nvmefc_fcp_req *nCmd = lpfc_ncmd->nvmeCmd;
union lpfc_wqe128 *wqe = &lpfc_ncmd->cur_iocbq.wqe;
- struct sli4_sge *sgl = lpfc_ncmd->nvme_sgl;
+ struct sli4_sge *sgl = lpfc_ncmd->dma_sgl;
struct scatterlist *data_sg;
struct sli4_sge *first_data_sgl;
struct ulp_bde64 *bde;
@@ -1396,6 +1389,8 @@ lpfc_nvme_prep_io_dma(struct lpfc_vport *vport,
}
} else {
+ lpfc_ncmd->seg_cnt = 0;
+
/* For this clause to be valid, the payload_length
* and sg_cnt must zero.
*/
@@ -1435,13 +1430,13 @@ lpfc_nvme_fcp_io_submit(struct nvme_fc_local_port *pnvme_lport,
{
int ret = 0;
int expedite = 0;
- int idx;
+ int idx, cpu;
struct lpfc_nvme_lport *lport;
- struct lpfc_nvme_ctrl_stat *cstat;
+ struct lpfc_fc4_ctrl_stat *cstat;
struct lpfc_vport *vport;
struct lpfc_hba *phba;
struct lpfc_nodelist *ndlp;
- struct lpfc_nvme_buf *lpfc_ncmd;
+ struct lpfc_io_buf *lpfc_ncmd;
struct lpfc_nvme_rport *rport;
struct lpfc_nvme_qhandle *lpfc_queue_info;
struct lpfc_nvme_fcpreq_priv *freqpriv;
@@ -1559,7 +1554,15 @@ lpfc_nvme_fcp_io_submit(struct nvme_fc_local_port *pnvme_lport,
}
}
- lpfc_ncmd = lpfc_get_nvme_buf(phba, ndlp, expedite);
+ /* Lookup Hardware Queue index based on fcp_io_sched module parameter */
+ if (phba->cfg_fcp_io_sched == LPFC_FCP_SCHED_BY_HDWQ) {
+ idx = lpfc_queue_info->index;
+ } else {
+ cpu = smp_processor_id();
+ idx = phba->sli4_hba.cpu_map[cpu].hdwq;
+ }
+
+ lpfc_ncmd = lpfc_get_nvme_buf(phba, ndlp, idx, expedite);
if (lpfc_ncmd == NULL) {
atomic_inc(&lport->xmt_fcp_noxri);
lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_IOERR,
@@ -1586,9 +1589,8 @@ lpfc_nvme_fcp_io_submit(struct nvme_fc_local_port *pnvme_lport,
*/
freqpriv->nvme_buf = lpfc_ncmd;
lpfc_ncmd->nvmeCmd = pnvme_fcreq;
- lpfc_ncmd->nrport = rport;
lpfc_ncmd->ndlp = ndlp;
- lpfc_ncmd->start_time = jiffies;
+ lpfc_ncmd->qidx = lpfc_queue_info->qidx;
/*
* Issue the IO on the WQ indicated by index in the hw_queue_handle.
@@ -1598,9 +1600,8 @@ lpfc_nvme_fcp_io_submit(struct nvme_fc_local_port *pnvme_lport,
* index to use and that they have affinitized a CPU to this hardware
* queue. A hardware queue maps to a driver MSI-X vector/EQ/CQ/WQ.
*/
- idx = lpfc_queue_info->index;
lpfc_ncmd->cur_iocbq.hba_wqidx = idx;
- cstat = &lport->cstat[idx];
+ cstat = &phba->sli4_hba.hdwq[idx].nvme_cstat;
lpfc_nvme_prep_io_cmd(vport, lpfc_ncmd, ndlp, cstat);
ret = lpfc_nvme_prep_io_dma(vport, lpfc_ncmd);
@@ -1618,7 +1619,7 @@ lpfc_nvme_fcp_io_submit(struct nvme_fc_local_port *pnvme_lport,
lpfc_ncmd->cur_iocbq.sli4_xritag,
lpfc_queue_info->index, ndlp->nlp_DID);
- ret = lpfc_sli4_issue_wqe(phba, LPFC_FCP_RING, &lpfc_ncmd->cur_iocbq);
+ ret = lpfc_sli4_issue_wqe(phba, lpfc_ncmd->hdwq, &lpfc_ncmd->cur_iocbq);
if (ret) {
atomic_inc(&lport->xmt_fcp_wqerr);
lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_IOERR,
@@ -1629,26 +1630,26 @@ lpfc_nvme_fcp_io_submit(struct nvme_fc_local_port *pnvme_lport,
goto out_free_nvme_buf;
}
+ if (phba->cfg_xri_rebalancing)
+ lpfc_keep_pvt_pool_above_lowwm(phba, lpfc_ncmd->hdwq_no);
+
#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
if (lpfc_ncmd->ts_cmd_start)
lpfc_ncmd->ts_cmd_wqput = ktime_get_ns();
if (phba->cpucheck_on & LPFC_CHECK_NVME_IO) {
- lpfc_ncmd->cpu = smp_processor_id();
- if (lpfc_ncmd->cpu != lpfc_queue_info->index) {
- /* Check for admin queue */
- if (lpfc_queue_info->qidx) {
+ cpu = smp_processor_id();
+ if (cpu < LPFC_CHECK_CPU_CNT) {
+ lpfc_ncmd->cpu = cpu;
+ if (idx != cpu)
lpfc_printf_vlog(vport,
- KERN_ERR, LOG_NVME_IOERR,
+ KERN_INFO, LOG_NVME_IOERR,
"6702 CPU Check cmd: "
"cpu %d wq %d\n",
lpfc_ncmd->cpu,
lpfc_queue_info->index);
- }
- lpfc_ncmd->cpu = lpfc_queue_info->index;
+ phba->sli4_hba.hdwq[idx].cpucheck_xmt_io[cpu]++;
}
- if (lpfc_ncmd->cpu < LPFC_CHECK_CPU_CNT)
- phba->cpucheck_xmt_io[lpfc_ncmd->cpu]++;
}
#endif
return 0;
@@ -1656,11 +1657,11 @@ lpfc_nvme_fcp_io_submit(struct nvme_fc_local_port *pnvme_lport,
out_free_nvme_buf:
if (lpfc_ncmd->nvmeCmd->sg_cnt) {
if (lpfc_ncmd->nvmeCmd->io_dir == NVMEFC_FCP_WRITE)
- atomic_dec(&cstat->fc4NvmeOutputRequests);
+ cstat->output_requests--;
else
- atomic_dec(&cstat->fc4NvmeInputRequests);
+ cstat->input_requests--;
} else
- atomic_dec(&cstat->fc4NvmeControlRequests);
+ cstat->control_requests--;
lpfc_release_nvme_buf(phba, lpfc_ncmd);
out_fail:
return ret;
@@ -1720,7 +1721,7 @@ lpfc_nvme_fcp_abort(struct nvme_fc_local_port *pnvme_lport,
struct lpfc_nvme_lport *lport;
struct lpfc_vport *vport;
struct lpfc_hba *phba;
- struct lpfc_nvme_buf *lpfc_nbuf;
+ struct lpfc_io_buf *lpfc_nbuf;
struct lpfc_iocbq *abts_buf;
struct lpfc_iocbq *nvmereq_wqe;
struct lpfc_nvme_fcpreq_priv *freqpriv;
@@ -1788,6 +1789,9 @@ lpfc_nvme_fcp_abort(struct nvme_fc_local_port *pnvme_lport,
}
nvmereq_wqe = &lpfc_nbuf->cur_iocbq;
+ /* Guard against IO completion being called at same time */
+ spin_lock(&lpfc_nbuf->buf_lock);
+
/*
* The lpfc_nbuf and the mapped nvme_fcreq in the driver's
* state must match the nvme_fcreq passed by the nvme
@@ -1796,24 +1800,22 @@ lpfc_nvme_fcp_abort(struct nvme_fc_local_port *pnvme_lport,
* has not seen it yet.
*/
if (lpfc_nbuf->nvmeCmd != pnvme_fcreq) {
- spin_unlock_irqrestore(&phba->hbalock, flags);
lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME_ABTS,
"6143 NVME req mismatch: "
"lpfc_nbuf %p nvmeCmd %p, "
"pnvme_fcreq %p. Skipping Abort xri x%x\n",
lpfc_nbuf, lpfc_nbuf->nvmeCmd,
pnvme_fcreq, nvmereq_wqe->sli4_xritag);
- return;
+ goto out_unlock;
}
/* Don't abort IOs no longer on the pending queue. */
if (!(nvmereq_wqe->iocb_flag & LPFC_IO_ON_TXCMPLQ)) {
- spin_unlock_irqrestore(&phba->hbalock, flags);
lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME_ABTS,
"6142 NVME IO req %p not queued - skipping "
"abort req xri x%x\n",
pnvme_fcreq, nvmereq_wqe->sli4_xritag);
- return;
+ goto out_unlock;
}
atomic_inc(&lport->xmt_fcp_abort);
@@ -1823,24 +1825,22 @@ lpfc_nvme_fcp_abort(struct nvme_fc_local_port *pnvme_lport,
/* Outstanding abort is in progress */
if (nvmereq_wqe->iocb_flag & LPFC_DRIVER_ABORTED) {
- spin_unlock_irqrestore(&phba->hbalock, flags);
lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME_ABTS,
"6144 Outstanding NVME I/O Abort Request "
"still pending on nvme_fcreq %p, "
"lpfc_ncmd %p xri x%x\n",
pnvme_fcreq, lpfc_nbuf,
nvmereq_wqe->sli4_xritag);
- return;
+ goto out_unlock;
}
abts_buf = __lpfc_sli_get_iocbq(phba);
if (!abts_buf) {
- spin_unlock_irqrestore(&phba->hbalock, flags);
lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME_ABTS,
"6136 No available abort wqes. Skipping "
"Abts req for nvme_fcreq %p xri x%x\n",
pnvme_fcreq, nvmereq_wqe->sli4_xritag);
- return;
+ goto out_unlock;
}
/* Ready - mark outstanding as aborted by driver. */
@@ -1883,7 +1883,8 @@ lpfc_nvme_fcp_abort(struct nvme_fc_local_port *pnvme_lport,
abts_buf->hba_wqidx = nvmereq_wqe->hba_wqidx;
abts_buf->vport = vport;
abts_buf->wqe_cmpl = lpfc_nvme_abort_fcreq_cmpl;
- ret_val = lpfc_sli4_issue_wqe(phba, LPFC_FCP_RING, abts_buf);
+ ret_val = lpfc_sli4_issue_wqe(phba, lpfc_nbuf->hdwq, abts_buf);
+ spin_unlock(&lpfc_nbuf->buf_lock);
spin_unlock_irqrestore(&phba->hbalock, flags);
if (ret_val) {
lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME_ABTS,
@@ -1899,6 +1900,12 @@ lpfc_nvme_fcp_abort(struct nvme_fc_local_port *pnvme_lport,
"ox_id x%x on reqtag x%x\n",
nvmereq_wqe->sli4_xritag,
abts_buf->iotag);
+ return;
+
+out_unlock:
+ spin_unlock(&lpfc_nbuf->buf_lock);
+ spin_unlock_irqrestore(&phba->hbalock, flags);
+ return;
}
/* Declare and initialization an instance of the FC NVME template. */
@@ -1928,454 +1935,63 @@ static struct nvme_fc_port_template lpfc_nvme_template = {
};
/**
- * lpfc_sli4_post_nvme_sgl_block - post a block of nvme sgl list to firmware
- * @phba: pointer to lpfc hba data structure.
- * @nblist: pointer to nvme buffer list.
- * @count: number of scsi buffers on the list.
- *
- * This routine is invoked to post a block of @count scsi sgl pages from a
- * SCSI buffer list @nblist to the HBA using non-embedded mailbox command.
- * No Lock is held.
- *
- **/
-static int
-lpfc_sli4_post_nvme_sgl_block(struct lpfc_hba *phba,
- struct list_head *nblist,
- int count)
-{
- struct lpfc_nvme_buf *lpfc_ncmd;
- struct lpfc_mbx_post_uembed_sgl_page1 *sgl;
- struct sgl_page_pairs *sgl_pg_pairs;
- void *viraddr;
- LPFC_MBOXQ_t *mbox;
- uint32_t reqlen, alloclen, pg_pairs;
- uint32_t mbox_tmo;
- uint16_t xritag_start = 0;
- int rc = 0;
- uint32_t shdr_status, shdr_add_status;
- dma_addr_t pdma_phys_bpl1;
- union lpfc_sli4_cfg_shdr *shdr;
-
- /* Calculate the requested length of the dma memory */
- reqlen = count * sizeof(struct sgl_page_pairs) +
- sizeof(union lpfc_sli4_cfg_shdr) + sizeof(uint32_t);
- if (reqlen > SLI4_PAGE_SIZE) {
- lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
- "6118 Block sgl registration required DMA "
- "size (%d) great than a page\n", reqlen);
- return -ENOMEM;
- }
- mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
- if (!mbox) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "6119 Failed to allocate mbox cmd memory\n");
- return -ENOMEM;
- }
-
- /* Allocate DMA memory and set up the non-embedded mailbox command */
- alloclen = lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_FCOE,
- LPFC_MBOX_OPCODE_FCOE_POST_SGL_PAGES, reqlen,
- LPFC_SLI4_MBX_NEMBED);
-
- if (alloclen < reqlen) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "6120 Allocated DMA memory size (%d) is "
- "less than the requested DMA memory "
- "size (%d)\n", alloclen, reqlen);
- lpfc_sli4_mbox_cmd_free(phba, mbox);
- return -ENOMEM;
- }
-
- /* Get the first SGE entry from the non-embedded DMA memory */
- viraddr = mbox->sge_array->addr[0];
-
- /* Set up the SGL pages in the non-embedded DMA pages */
- sgl = (struct lpfc_mbx_post_uembed_sgl_page1 *)viraddr;
- sgl_pg_pairs = &sgl->sgl_pg_pairs;
-
- pg_pairs = 0;
- list_for_each_entry(lpfc_ncmd, nblist, list) {
- /* Set up the sge entry */
- sgl_pg_pairs->sgl_pg0_addr_lo =
- cpu_to_le32(putPaddrLow(lpfc_ncmd->dma_phys_sgl));
- sgl_pg_pairs->sgl_pg0_addr_hi =
- cpu_to_le32(putPaddrHigh(lpfc_ncmd->dma_phys_sgl));
- if (phba->cfg_sg_dma_buf_size > SGL_PAGE_SIZE)
- pdma_phys_bpl1 = lpfc_ncmd->dma_phys_sgl +
- SGL_PAGE_SIZE;
- else
- pdma_phys_bpl1 = 0;
- sgl_pg_pairs->sgl_pg1_addr_lo =
- cpu_to_le32(putPaddrLow(pdma_phys_bpl1));
- sgl_pg_pairs->sgl_pg1_addr_hi =
- cpu_to_le32(putPaddrHigh(pdma_phys_bpl1));
- /* Keep the first xritag on the list */
- if (pg_pairs == 0)
- xritag_start = lpfc_ncmd->cur_iocbq.sli4_xritag;
- sgl_pg_pairs++;
- pg_pairs++;
- }
- bf_set(lpfc_post_sgl_pages_xri, sgl, xritag_start);
- bf_set(lpfc_post_sgl_pages_xricnt, sgl, pg_pairs);
- /* Perform endian conversion if necessary */
- sgl->word0 = cpu_to_le32(sgl->word0);
-
- if (!phba->sli4_hba.intr_enable)
- rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL);
- else {
- mbox_tmo = lpfc_mbox_tmo_val(phba, mbox);
- rc = lpfc_sli_issue_mbox_wait(phba, mbox, mbox_tmo);
- }
- shdr = (union lpfc_sli4_cfg_shdr *)&sgl->cfg_shdr;
- shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
- shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
- if (rc != MBX_TIMEOUT)
- lpfc_sli4_mbox_cmd_free(phba, mbox);
- if (shdr_status || shdr_add_status || rc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
- "6125 POST_SGL_BLOCK mailbox command failed "
- "status x%x add_status x%x mbx status x%x\n",
- shdr_status, shdr_add_status, rc);
- rc = -ENXIO;
- }
- return rc;
-}
-
-/**
- * lpfc_post_nvme_sgl_list - Post blocks of nvme buffer sgls from a list
- * @phba: pointer to lpfc hba data structure.
- * @post_nblist: pointer to the nvme buffer list.
- *
- * This routine walks a list of nvme buffers that was passed in. It attempts
- * to construct blocks of nvme buffer sgls which contains contiguous xris and
- * uses the non-embedded SGL block post mailbox commands to post to the port.
- * For single NVME buffer sgl with non-contiguous xri, if any, it shall use
- * embedded SGL post mailbox command for posting. The @post_nblist passed in
- * must be local list, thus no lock is needed when manipulate the list.
- *
- * Returns: 0 = failure, non-zero number of successfully posted buffers.
- **/
-static int
-lpfc_post_nvme_sgl_list(struct lpfc_hba *phba,
- struct list_head *post_nblist, int sb_count)
-{
- struct lpfc_nvme_buf *lpfc_ncmd, *lpfc_ncmd_next;
- int status, sgl_size;
- int post_cnt = 0, block_cnt = 0, num_posting = 0, num_posted = 0;
- dma_addr_t pdma_phys_sgl1;
- int last_xritag = NO_XRI;
- int cur_xritag;
- LIST_HEAD(prep_nblist);
- LIST_HEAD(blck_nblist);
- LIST_HEAD(nvme_nblist);
-
- /* sanity check */
- if (sb_count <= 0)
- return -EINVAL;
-
- sgl_size = phba->cfg_sg_dma_buf_size;
-
- list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next, post_nblist, list) {
- list_del_init(&lpfc_ncmd->list);
- block_cnt++;
- if ((last_xritag != NO_XRI) &&
- (lpfc_ncmd->cur_iocbq.sli4_xritag != last_xritag + 1)) {
- /* a hole in xri block, form a sgl posting block */
- list_splice_init(&prep_nblist, &blck_nblist);
- post_cnt = block_cnt - 1;
- /* prepare list for next posting block */
- list_add_tail(&lpfc_ncmd->list, &prep_nblist);
- block_cnt = 1;
- } else {
- /* prepare list for next posting block */
- list_add_tail(&lpfc_ncmd->list, &prep_nblist);
- /* enough sgls for non-embed sgl mbox command */
- if (block_cnt == LPFC_NEMBED_MBOX_SGL_CNT) {
- list_splice_init(&prep_nblist, &blck_nblist);
- post_cnt = block_cnt;
- block_cnt = 0;
- }
- }
- num_posting++;
- last_xritag = lpfc_ncmd->cur_iocbq.sli4_xritag;
-
- /* end of repost sgl list condition for NVME buffers */
- if (num_posting == sb_count) {
- if (post_cnt == 0) {
- /* last sgl posting block */
- list_splice_init(&prep_nblist, &blck_nblist);
- post_cnt = block_cnt;
- } else if (block_cnt == 1) {
- /* last single sgl with non-contiguous xri */
- if (sgl_size > SGL_PAGE_SIZE)
- pdma_phys_sgl1 =
- lpfc_ncmd->dma_phys_sgl +
- SGL_PAGE_SIZE;
- else
- pdma_phys_sgl1 = 0;
- cur_xritag = lpfc_ncmd->cur_iocbq.sli4_xritag;
- status = lpfc_sli4_post_sgl(phba,
- lpfc_ncmd->dma_phys_sgl,
- pdma_phys_sgl1, cur_xritag);
- if (status) {
- /* failure, put on abort nvme list */
- lpfc_ncmd->flags |= LPFC_SBUF_XBUSY;
- } else {
- /* success, put on NVME buffer list */
- lpfc_ncmd->flags &= ~LPFC_SBUF_XBUSY;
- lpfc_ncmd->status = IOSTAT_SUCCESS;
- num_posted++;
- }
- /* success, put on NVME buffer sgl list */
- list_add_tail(&lpfc_ncmd->list, &nvme_nblist);
- }
- }
-
- /* continue until a nembed page worth of sgls */
- if (post_cnt == 0)
- continue;
-
- /* post block of NVME buffer list sgls */
- status = lpfc_sli4_post_nvme_sgl_block(phba, &blck_nblist,
- post_cnt);
-
- /* don't reset xirtag due to hole in xri block */
- if (block_cnt == 0)
- last_xritag = NO_XRI;
-
- /* reset NVME buffer post count for next round of posting */
- post_cnt = 0;
-
- /* put posted NVME buffer-sgl posted on NVME buffer sgl list */
- while (!list_empty(&blck_nblist)) {
- list_remove_head(&blck_nblist, lpfc_ncmd,
- struct lpfc_nvme_buf, list);
- if (status) {
- /* failure, put on abort nvme list */
- lpfc_ncmd->flags |= LPFC_SBUF_XBUSY;
- } else {
- /* success, put on NVME buffer list */
- lpfc_ncmd->flags &= ~LPFC_SBUF_XBUSY;
- lpfc_ncmd->status = IOSTAT_SUCCESS;
- num_posted++;
- }
- list_add_tail(&lpfc_ncmd->list, &nvme_nblist);
- }
- }
- /* Push NVME buffers with sgl posted to the available list */
- while (!list_empty(&nvme_nblist)) {
- list_remove_head(&nvme_nblist, lpfc_ncmd,
- struct lpfc_nvme_buf, list);
- lpfc_release_nvme_buf(phba, lpfc_ncmd);
- }
- return num_posted;
-}
-
-/**
- * lpfc_repost_nvme_sgl_list - Repost all the allocated nvme buffer sgls
- * @phba: pointer to lpfc hba data structure.
- *
- * This routine walks the list of nvme buffers that have been allocated and
- * repost them to the port by using SGL block post. This is needed after a
- * pci_function_reset/warm_start or start. The lpfc_hba_down_post_s4 routine
- * is responsible for moving all nvme buffers on the lpfc_abts_nvme_sgl_list
- * to the lpfc_nvme_buf_list. If the repost fails, reject all nvme buffers.
- *
- * Returns: 0 = success, non-zero failure.
- **/
-int
-lpfc_repost_nvme_sgl_list(struct lpfc_hba *phba)
-{
- LIST_HEAD(post_nblist);
- int num_posted, rc = 0;
-
- /* get all NVME buffers need to repost to a local list */
- spin_lock_irq(&phba->nvme_buf_list_get_lock);
- spin_lock(&phba->nvme_buf_list_put_lock);
- list_splice_init(&phba->lpfc_nvme_buf_list_get, &post_nblist);
- list_splice(&phba->lpfc_nvme_buf_list_put, &post_nblist);
- phba->get_nvme_bufs = 0;
- phba->put_nvme_bufs = 0;
- spin_unlock(&phba->nvme_buf_list_put_lock);
- spin_unlock_irq(&phba->nvme_buf_list_get_lock);
-
- /* post the list of nvme buffer sgls to port if available */
- if (!list_empty(&post_nblist)) {
- num_posted = lpfc_post_nvme_sgl_list(phba, &post_nblist,
- phba->sli4_hba.nvme_xri_cnt);
- /* failed to post any nvme buffer, return error */
- if (num_posted == 0)
- rc = -EIO;
- }
- return rc;
-}
-
-/**
- * lpfc_new_nvme_buf - Scsi buffer allocator for HBA with SLI4 IF spec
- * @vport: The virtual port for which this call being executed.
- * @num_to_allocate: The requested number of buffers to allocate.
+ * lpfc_get_nvme_buf - Get a nvme buffer from io_buf_list of the HBA
+ * @phba: The HBA for which this call is being executed.
*
- * This routine allocates nvme buffers for device with SLI-4 interface spec,
- * the nvme buffer contains all the necessary information needed to initiate
- * a NVME I/O. After allocating up to @num_to_allocate NVME buffers and put
- * them on a list, it post them to the port by using SGL block post.
+ * This routine removes a nvme buffer from head of @hdwq io_buf_list
+ * and returns to caller.
*
* Return codes:
- * int - number of nvme buffers that were allocated and posted.
- * 0 = failure, less than num_to_alloc is a partial failure.
+ * NULL - Error
+ * Pointer to lpfc_nvme_buf - Success
**/
-static int
-lpfc_new_nvme_buf(struct lpfc_vport *vport, int num_to_alloc)
+static struct lpfc_io_buf *
+lpfc_get_nvme_buf(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp,
+ int idx, int expedite)
{
- struct lpfc_hba *phba = vport->phba;
- struct lpfc_nvme_buf *lpfc_ncmd;
+ struct lpfc_io_buf *lpfc_ncmd;
+ struct lpfc_sli4_hdw_queue *qp;
+ struct sli4_sge *sgl;
struct lpfc_iocbq *pwqeq;
union lpfc_wqe128 *wqe;
- struct sli4_sge *sgl;
- dma_addr_t pdma_phys_sgl;
- uint16_t iotag, lxri = 0;
- int bcnt, num_posted;
- LIST_HEAD(prep_nblist);
- LIST_HEAD(post_nblist);
- LIST_HEAD(nvme_nblist);
-
- for (bcnt = 0; bcnt < num_to_alloc; bcnt++) {
- lpfc_ncmd = kzalloc(sizeof(struct lpfc_nvme_buf), GFP_KERNEL);
- if (!lpfc_ncmd)
- break;
- /*
- * Get memory from the pci pool to map the virt space to
- * pci bus space for an I/O. The DMA buffer includes the
- * number of SGE's necessary to support the sg_tablesize.
- */
- lpfc_ncmd->data = dma_pool_zalloc(phba->lpfc_sg_dma_buf_pool,
- GFP_KERNEL,
- &lpfc_ncmd->dma_handle);
- if (!lpfc_ncmd->data) {
- kfree(lpfc_ncmd);
- break;
- }
- lxri = lpfc_sli4_next_xritag(phba);
- if (lxri == NO_XRI) {
- dma_pool_free(phba->lpfc_sg_dma_buf_pool,
- lpfc_ncmd->data, lpfc_ncmd->dma_handle);
- kfree(lpfc_ncmd);
- break;
- }
+ lpfc_ncmd = lpfc_get_io_buf(phba, NULL, idx, expedite);
+
+ if (lpfc_ncmd) {
pwqeq = &(lpfc_ncmd->cur_iocbq);
wqe = &pwqeq->wqe;
- /* Allocate iotag for lpfc_ncmd->cur_iocbq. */
- iotag = lpfc_sli_next_iotag(phba, pwqeq);
- if (iotag == 0) {
- dma_pool_free(phba->lpfc_sg_dma_buf_pool,
- lpfc_ncmd->data, lpfc_ncmd->dma_handle);
- kfree(lpfc_ncmd);
- lpfc_printf_log(phba, KERN_ERR, LOG_NVME_IOERR,
- "6121 Failed to allocated IOTAG for"
- " XRI:0x%x\n", lxri);
- lpfc_sli4_free_xri(phba, lxri);
- break;
- }
- pwqeq->sli4_lxritag = lxri;
- pwqeq->sli4_xritag = phba->sli4_hba.xri_ids[lxri];
- pwqeq->iocb_flag |= LPFC_IO_NVME;
- pwqeq->context1 = lpfc_ncmd;
+ /* Setup key fields in buffer that may have been changed
+ * if other protocols used this buffer.
+ */
+ pwqeq->iocb_flag = LPFC_IO_NVME;
pwqeq->wqe_cmpl = lpfc_nvme_io_cmd_wqe_cmpl;
-
- /* Initialize local short-hand pointers. */
- lpfc_ncmd->nvme_sgl = lpfc_ncmd->data;
- sgl = lpfc_ncmd->nvme_sgl;
- pdma_phys_sgl = lpfc_ncmd->dma_handle;
- lpfc_ncmd->dma_phys_sgl = pdma_phys_sgl;
+ lpfc_ncmd->start_time = jiffies;
+ lpfc_ncmd->flags = 0;
/* Rsp SGE will be filled in when we rcv an IO
* from the NVME Layer to be sent.
* The cmd is going to be embedded so we need a SKIP SGE.
*/
+ sgl = lpfc_ncmd->dma_sgl;
bf_set(lpfc_sli4_sge_type, sgl, LPFC_SGE_TYPE_SKIP);
bf_set(lpfc_sli4_sge_last, sgl, 0);
sgl->word2 = cpu_to_le32(sgl->word2);
/* Fill in word 3 / sgl_len during cmd submission */
- lpfc_ncmd->cur_iocbq.context1 = lpfc_ncmd;
-
/* Initialize WQE */
memset(wqe, 0, sizeof(union lpfc_wqe));
- /* add the nvme buffer to a post list */
- list_add_tail(&lpfc_ncmd->list, &post_nblist);
- spin_lock_irq(&phba->nvme_buf_list_get_lock);
- phba->sli4_hba.nvme_xri_cnt++;
- spin_unlock_irq(&phba->nvme_buf_list_get_lock);
- }
- lpfc_printf_log(phba, KERN_INFO, LOG_NVME,
- "6114 Allocate %d out of %d requested new NVME "
- "buffers\n", bcnt, num_to_alloc);
-
- /* post the list of nvme buffer sgls to port if available */
- if (!list_empty(&post_nblist))
- num_posted = lpfc_post_nvme_sgl_list(phba,
- &post_nblist, bcnt);
- else
- num_posted = 0;
-
- return num_posted;
-}
-
-static inline struct lpfc_nvme_buf *
-lpfc_nvme_buf(struct lpfc_hba *phba)
-{
- struct lpfc_nvme_buf *lpfc_ncmd, *lpfc_ncmd_next;
-
- list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next,
- &phba->lpfc_nvme_buf_list_get, list) {
- list_del_init(&lpfc_ncmd->list);
- phba->get_nvme_bufs--;
- return lpfc_ncmd;
- }
- return NULL;
-}
-
-/**
- * lpfc_get_nvme_buf - Get a nvme buffer from lpfc_nvme_buf_list of the HBA
- * @phba: The HBA for which this call is being executed.
- *
- * This routine removes a nvme buffer from head of @phba lpfc_nvme_buf_list list
- * and returns to caller.
- *
- * Return codes:
- * NULL - Error
- * Pointer to lpfc_nvme_buf - Success
- **/
-static struct lpfc_nvme_buf *
-lpfc_get_nvme_buf(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp,
- int expedite)
-{
- struct lpfc_nvme_buf *lpfc_ncmd = NULL;
- unsigned long iflag = 0;
+ if (lpfc_ndlp_check_qdepth(phba, ndlp)) {
+ atomic_inc(&ndlp->cmd_pending);
+ lpfc_ncmd->flags |= LPFC_SBUF_BUMP_QDEPTH;
+ }
- spin_lock_irqsave(&phba->nvme_buf_list_get_lock, iflag);
- if (phba->get_nvme_bufs > LPFC_NVME_EXPEDITE_XRICNT || expedite)
- lpfc_ncmd = lpfc_nvme_buf(phba);
- if (!lpfc_ncmd) {
- spin_lock(&phba->nvme_buf_list_put_lock);
- list_splice(&phba->lpfc_nvme_buf_list_put,
- &phba->lpfc_nvme_buf_list_get);
- phba->get_nvme_bufs += phba->put_nvme_bufs;
- INIT_LIST_HEAD(&phba->lpfc_nvme_buf_list_put);
- phba->put_nvme_bufs = 0;
- spin_unlock(&phba->nvme_buf_list_put_lock);
- if (phba->get_nvme_bufs > LPFC_NVME_EXPEDITE_XRICNT || expedite)
- lpfc_ncmd = lpfc_nvme_buf(phba);
+ } else {
+ qp = &phba->sli4_hba.hdwq[idx];
+ qp->empty_io_bufs++;
}
- spin_unlock_irqrestore(&phba->nvme_buf_list_get_lock, iflag);
- if (lpfc_ndlp_check_qdepth(phba, ndlp) && lpfc_ncmd) {
- atomic_inc(&ndlp->cmd_pending);
- lpfc_ncmd->flags |= LPFC_BUMP_QDEPTH;
- }
return lpfc_ncmd;
}
@@ -2385,22 +2001,23 @@ lpfc_get_nvme_buf(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp,
* @lpfc_ncmd: The nvme buffer which is being released.
*
* This routine releases @lpfc_ncmd nvme buffer by adding it to tail of @phba
- * lpfc_nvme_buf_list list. For SLI4 XRI's are tied to the nvme buffer
+ * lpfc_io_buf_list list. For SLI4 XRI's are tied to the nvme buffer
* and cannot be reused for at least RA_TOV amount of time if it was
* aborted.
**/
static void
-lpfc_release_nvme_buf(struct lpfc_hba *phba, struct lpfc_nvme_buf *lpfc_ncmd)
+lpfc_release_nvme_buf(struct lpfc_hba *phba, struct lpfc_io_buf *lpfc_ncmd)
{
+ struct lpfc_sli4_hdw_queue *qp;
unsigned long iflag = 0;
- if ((lpfc_ncmd->flags & LPFC_BUMP_QDEPTH) && lpfc_ncmd->ndlp)
+ if ((lpfc_ncmd->flags & LPFC_SBUF_BUMP_QDEPTH) && lpfc_ncmd->ndlp)
atomic_dec(&lpfc_ncmd->ndlp->cmd_pending);
- lpfc_ncmd->nonsg_phys = 0;
lpfc_ncmd->ndlp = NULL;
- lpfc_ncmd->flags &= ~LPFC_BUMP_QDEPTH;
+ lpfc_ncmd->flags &= ~LPFC_SBUF_BUMP_QDEPTH;
+ qp = lpfc_ncmd->hdwq;
if (lpfc_ncmd->flags & LPFC_SBUF_XBUSY) {
lpfc_printf_log(phba, KERN_INFO, LOG_NVME_ABTS,
"6310 XB release deferred for "
@@ -2408,20 +2025,13 @@ lpfc_release_nvme_buf(struct lpfc_hba *phba, struct lpfc_nvme_buf *lpfc_ncmd)
lpfc_ncmd->cur_iocbq.sli4_xritag,
lpfc_ncmd->cur_iocbq.iotag);
- spin_lock_irqsave(&phba->sli4_hba.abts_nvme_buf_list_lock,
- iflag);
+ spin_lock_irqsave(&qp->abts_nvme_buf_list_lock, iflag);
list_add_tail(&lpfc_ncmd->list,
- &phba->sli4_hba.lpfc_abts_nvme_buf_list);
- spin_unlock_irqrestore(&phba->sli4_hba.abts_nvme_buf_list_lock,
- iflag);
- } else {
- lpfc_ncmd->nvmeCmd = NULL;
- lpfc_ncmd->cur_iocbq.iocb_flag = LPFC_IO_NVME;
- spin_lock_irqsave(&phba->nvme_buf_list_put_lock, iflag);
- list_add_tail(&lpfc_ncmd->list, &phba->lpfc_nvme_buf_list_put);
- phba->put_nvme_bufs++;
- spin_unlock_irqrestore(&phba->nvme_buf_list_put_lock, iflag);
- }
+ &qp->lpfc_abts_nvme_buf_list);
+ qp->abts_nvme_io_bufs++;
+ spin_unlock_irqrestore(&qp->abts_nvme_buf_list_lock, iflag);
+ } else
+ lpfc_release_io_buf(phba, (struct lpfc_io_buf *)lpfc_ncmd, qp);
}
/**
@@ -2448,8 +2058,6 @@ lpfc_nvme_create_localport(struct lpfc_vport *vport)
struct nvme_fc_port_info nfcp_info;
struct nvme_fc_local_port *localport;
struct lpfc_nvme_lport *lport;
- struct lpfc_nvme_ctrl_stat *cstat;
- int len, i;
/* Initialize this localport instance. The vport wwn usage ensures
* that NPIV is accounted for.
@@ -2464,12 +2072,13 @@ lpfc_nvme_create_localport(struct lpfc_vport *vport)
* allocate + 3, one for cmd, one for rsp and one for this alignment
*/
lpfc_nvme_template.max_sgl_segments = phba->cfg_nvme_seg_cnt + 1;
- lpfc_nvme_template.max_hw_queues = phba->cfg_nvme_io_channel;
- cstat = kmalloc((sizeof(struct lpfc_nvme_ctrl_stat) *
- phba->cfg_nvme_io_channel), GFP_KERNEL);
- if (!cstat)
- return -ENOMEM;
+ /* Advertise how many hw queues we support based on fcp_io_sched */
+ if (phba->cfg_fcp_io_sched == LPFC_FCP_SCHED_BY_HDWQ)
+ lpfc_nvme_template.max_hw_queues = phba->cfg_hdw_queue;
+ else
+ lpfc_nvme_template.max_hw_queues =
+ phba->sli4_hba.num_present_cpu;
/* localport is allocated from the stack, but the registration
* call allocates heap memory as well as the private area.
@@ -2493,7 +2102,6 @@ lpfc_nvme_create_localport(struct lpfc_vport *vport)
lport = (struct lpfc_nvme_lport *)localport->private;
vport->localport = localport;
lport->vport = vport;
- lport->cstat = cstat;
vport->nvmei_support = 1;
atomic_set(&lport->xmt_fcp_noxri, 0);
@@ -2510,25 +2118,6 @@ lpfc_nvme_create_localport(struct lpfc_vport *vport)
atomic_set(&lport->cmpl_ls_err, 0);
atomic_set(&lport->fc4NvmeLsRequests, 0);
atomic_set(&lport->fc4NvmeLsCmpls, 0);
-
- for (i = 0; i < phba->cfg_nvme_io_channel; i++) {
- cstat = &lport->cstat[i];
- atomic_set(&cstat->fc4NvmeInputRequests, 0);
- atomic_set(&cstat->fc4NvmeOutputRequests, 0);
- atomic_set(&cstat->fc4NvmeControlRequests, 0);
- atomic_set(&cstat->fc4NvmeIoCmpls, 0);
- }
-
- /* Don't post more new bufs if repost already recovered
- * the nvme sgls.
- */
- if (phba->sli4_hba.nvme_xri_cnt == 0) {
- len = lpfc_new_nvme_buf(vport,
- phba->sli4_hba.nvme_xri_max);
- vport->phba->total_nvme_bufs += len;
- }
- } else {
- kfree(cstat);
}
return ret;
@@ -2591,7 +2180,6 @@ lpfc_nvme_destroy_localport(struct lpfc_vport *vport)
#if (IS_ENABLED(CONFIG_NVME_FC))
struct nvme_fc_local_port *localport;
struct lpfc_nvme_lport *lport;
- struct lpfc_nvme_ctrl_stat *cstat;
int ret;
DECLARE_COMPLETION_ONSTACK(lport_unreg_cmp);
@@ -2600,7 +2188,6 @@ lpfc_nvme_destroy_localport(struct lpfc_vport *vport)
localport = vport->localport;
lport = (struct lpfc_nvme_lport *)localport->private;
- cstat = lport->cstat;
lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME,
"6011 Destroying NVME localport %p\n",
@@ -2617,7 +2204,6 @@ lpfc_nvme_destroy_localport(struct lpfc_vport *vport)
*/
lpfc_nvme_lport_unreg_wait(vport, lport, &lport_unreg_cmp);
vport->localport = NULL;
- kfree(cstat);
/* Regardless of the unregister upcall response, clear
* nvmei_support. All rports are unregistered and the
@@ -2909,27 +2495,28 @@ lpfc_nvme_unregister_port(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
**/
void
lpfc_sli4_nvme_xri_aborted(struct lpfc_hba *phba,
- struct sli4_wcqe_xri_aborted *axri)
+ struct sli4_wcqe_xri_aborted *axri, int idx)
{
uint16_t xri = bf_get(lpfc_wcqe_xa_xri, axri);
- struct lpfc_nvme_buf *lpfc_ncmd, *next_lpfc_ncmd;
+ struct lpfc_io_buf *lpfc_ncmd, *next_lpfc_ncmd;
struct nvmefc_fcp_req *nvme_cmd = NULL;
struct lpfc_nodelist *ndlp;
+ struct lpfc_sli4_hdw_queue *qp;
unsigned long iflag = 0;
if (!(phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME))
return;
+ qp = &phba->sli4_hba.hdwq[idx];
spin_lock_irqsave(&phba->hbalock, iflag);
- spin_lock(&phba->sli4_hba.abts_nvme_buf_list_lock);
+ spin_lock(&qp->abts_nvme_buf_list_lock);
list_for_each_entry_safe(lpfc_ncmd, next_lpfc_ncmd,
- &phba->sli4_hba.lpfc_abts_nvme_buf_list,
- list) {
+ &qp->lpfc_abts_nvme_buf_list, list) {
if (lpfc_ncmd->cur_iocbq.sli4_xritag == xri) {
list_del_init(&lpfc_ncmd->list);
+ qp->abts_nvme_io_bufs--;
lpfc_ncmd->flags &= ~LPFC_SBUF_XBUSY;
lpfc_ncmd->status = IOSTAT_SUCCESS;
- spin_unlock(
- &phba->sli4_hba.abts_nvme_buf_list_lock);
+ spin_unlock(&qp->abts_nvme_buf_list_lock);
spin_unlock_irqrestore(&phba->hbalock, iflag);
ndlp = lpfc_ncmd->ndlp;
@@ -2955,7 +2542,7 @@ lpfc_sli4_nvme_xri_aborted(struct lpfc_hba *phba,
return;
}
}
- spin_unlock(&phba->sli4_hba.abts_nvme_buf_list_lock);
+ spin_unlock(&qp->abts_nvme_buf_list_lock);
spin_unlock_irqrestore(&phba->hbalock, iflag);
lpfc_printf_log(phba, KERN_INFO, LOG_NVME_ABTS,
@@ -2979,14 +2566,16 @@ lpfc_nvme_wait_for_io_drain(struct lpfc_hba *phba)
struct lpfc_sli_ring *pring;
u32 i, wait_cnt = 0;
- if (phba->sli_rev < LPFC_SLI_REV4 || !phba->sli4_hba.nvme_wq)
+ if (phba->sli_rev < LPFC_SLI_REV4 || !phba->sli4_hba.hdwq)
return;
/* Cycle through all NVME rings and make sure all outstanding
* WQEs have been removed from the txcmplqs.
*/
- for (i = 0; i < phba->cfg_nvme_io_channel; i++) {
- pring = phba->sli4_hba.nvme_wq[i]->pring;
+ for (i = 0; i < phba->cfg_hdw_queue; i++) {
+ if (!phba->sli4_hba.hdwq[i].nvme_wq)
+ continue;
+ pring = phba->sli4_hba.hdwq[i].nvme_wq->pring;
if (!pring)
continue;
diff --git a/drivers/scsi/lpfc/lpfc_nvme.h b/drivers/scsi/lpfc/lpfc_nvme.h
index b234d0298994..593c48ff634e 100644
--- a/drivers/scsi/lpfc/lpfc_nvme.h
+++ b/drivers/scsi/lpfc/lpfc_nvme.h
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term *
+ * Copyright (C) 2017-2019 Broadcom. All Rights Reserved. The term *
* “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. *
* Copyright (C) 2004-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
@@ -30,6 +30,9 @@
#define LPFC_NVME_FB_SHIFT 9
#define LPFC_NVME_MAX_FB (1 << 20) /* 1M */
+#define LPFC_MAX_NVME_INFO_TMP_LEN 100
+#define LPFC_NVME_INFO_MORE_STR "\nCould be more info...\n"
+
#define lpfc_ndlp_get_nrport(ndlp) \
((!ndlp->nrport || (ndlp->upcall_flags & NLP_WAIT_FOR_UNREG)) \
? NULL : ndlp->nrport)
@@ -40,19 +43,11 @@ struct lpfc_nvme_qhandle {
uint32_t cpu_id; /* current cpu id at time of create */
};
-struct lpfc_nvme_ctrl_stat {
- atomic_t fc4NvmeInputRequests;
- atomic_t fc4NvmeOutputRequests;
- atomic_t fc4NvmeControlRequests;
- atomic_t fc4NvmeIoCmpls;
-};
-
/* Declare nvme-based local and remote port definitions. */
struct lpfc_nvme_lport {
struct lpfc_vport *vport;
struct completion *lport_unreg_cmp;
/* Add stats counters here */
- struct lpfc_nvme_ctrl_stat *cstat;
atomic_t fc4NvmeLsRequests;
atomic_t fc4NvmeLsCmpls;
atomic_t xmt_fcp_noxri;
@@ -76,57 +71,6 @@ struct lpfc_nvme_rport {
struct completion rport_unreg_done;
};
-struct lpfc_nvme_buf {
- struct list_head list;
- struct nvmefc_fcp_req *nvmeCmd;
- struct lpfc_nvme_rport *nrport;
- struct lpfc_nodelist *ndlp;
-
- uint32_t timeout;
-
- uint16_t flags; /* TBD convert exch_busy to flags */
-#define LPFC_SBUF_XBUSY 0x1 /* SLI4 hba reported XB on WCQE cmpl */
-#define LPFC_BUMP_QDEPTH 0x2 /* bumped queue depth counter */
- uint16_t exch_busy; /* SLI4 hba reported XB on complete WCQE */
- uint16_t status; /* From IOCB Word 7- ulpStatus */
- uint16_t cpu;
- uint16_t qidx;
- uint16_t sqid;
- uint32_t result; /* From IOCB Word 4. */
-
- uint32_t seg_cnt; /* Number of scatter-gather segments returned by
- * dma_map_sg. The driver needs this for calls
- * to dma_unmap_sg.
- */
- dma_addr_t nonsg_phys; /* Non scatter-gather physical address. */
-
- /*
- * data and dma_handle are the kernel virtual and bus address of the
- * dma-able buffer containing the fcp_cmd, fcp_rsp and a scatter
- * gather bde list that supports the sg_tablesize value.
- */
- void *data;
- dma_addr_t dma_handle;
-
- struct sli4_sge *nvme_sgl;
- dma_addr_t dma_phys_sgl;
-
- /* cur_iocbq has phys of the dma-able buffer.
- * Iotag is in here
- */
- struct lpfc_iocbq cur_iocbq;
-
- wait_queue_head_t *waitq;
- unsigned long start_time;
-#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
- uint64_t ts_cmd_start;
- uint64_t ts_last_cmd;
- uint64_t ts_cmd_wqput;
- uint64_t ts_isr_cmpl;
- uint64_t ts_data_nvme;
-#endif
-};
-
struct lpfc_nvme_fcpreq_priv {
- struct lpfc_nvme_buf *nvme_buf;
+ struct lpfc_io_buf *nvme_buf;
};
diff --git a/drivers/scsi/lpfc/lpfc_nvmet.c b/drivers/scsi/lpfc/lpfc_nvmet.c
index 95fee83090eb..361e2b103648 100644
--- a/drivers/scsi/lpfc/lpfc_nvmet.c
+++ b/drivers/scsi/lpfc/lpfc_nvmet.c
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channsel Host Bus Adapters. *
- * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term *
+ * Copyright (C) 2017-2019 Broadcom. All Rights Reserved. The term *
* “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. *
* Copyright (C) 2004-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
@@ -73,6 +73,9 @@ static int lpfc_nvmet_unsol_ls_issue_abort(struct lpfc_hba *,
uint32_t, uint16_t);
static void lpfc_nvmet_wqfull_flush(struct lpfc_hba *, struct lpfc_queue *,
struct lpfc_nvmet_rcv_ctx *);
+static void lpfc_nvmet_fcp_rqst_defer_work(struct work_struct *);
+
+static void lpfc_nvmet_process_rcv_fcp_req(struct lpfc_nvmet_ctxbuf *ctx_buf);
static union lpfc_wqe128 lpfc_tsend_cmd_template;
static union lpfc_wqe128 lpfc_treceive_cmd_template;
@@ -220,21 +223,19 @@ lpfc_nvmet_cmd_template(void)
void
lpfc_nvmet_defer_release(struct lpfc_hba *phba, struct lpfc_nvmet_rcv_ctx *ctxp)
{
- unsigned long iflag;
+ lockdep_assert_held(&ctxp->ctxlock);
lpfc_printf_log(phba, KERN_INFO, LOG_NVME_ABTS,
"6313 NVMET Defer ctx release xri x%x flg x%x\n",
ctxp->oxid, ctxp->flag);
- spin_lock_irqsave(&phba->sli4_hba.abts_nvme_buf_list_lock, iflag);
- if (ctxp->flag & LPFC_NVMET_CTX_RLS) {
- spin_unlock_irqrestore(&phba->sli4_hba.abts_nvme_buf_list_lock,
- iflag);
+ if (ctxp->flag & LPFC_NVMET_CTX_RLS)
return;
- }
+
ctxp->flag |= LPFC_NVMET_CTX_RLS;
+ spin_lock(&phba->sli4_hba.abts_nvmet_buf_list_lock);
list_add_tail(&ctxp->list, &phba->sli4_hba.lpfc_abts_nvmet_ctx_list);
- spin_unlock_irqrestore(&phba->sli4_hba.abts_nvme_buf_list_lock, iflag);
+ spin_unlock(&phba->sli4_hba.abts_nvmet_buf_list_lock);
}
/**
@@ -325,7 +326,7 @@ lpfc_nvmet_ctxbuf_post(struct lpfc_hba *phba, struct lpfc_nvmet_ctxbuf *ctx_buf)
struct rqb_dmabuf *nvmebuf;
struct lpfc_nvmet_ctx_info *infop;
uint32_t *payload;
- uint32_t size, oxid, sid, rc;
+ uint32_t size, oxid, sid;
int cpu;
unsigned long iflag;
@@ -341,6 +342,20 @@ lpfc_nvmet_ctxbuf_post(struct lpfc_hba *phba, struct lpfc_nvmet_ctxbuf *ctx_buf)
"6411 NVMET free, already free IO x%x: %d %d\n",
ctxp->oxid, ctxp->state, ctxp->entry_cnt);
}
+
+ if (ctxp->rqb_buffer) {
+ nvmebuf = ctxp->rqb_buffer;
+ spin_lock_irqsave(&ctxp->ctxlock, iflag);
+ ctxp->rqb_buffer = NULL;
+ if (ctxp->flag & LPFC_NVMET_CTX_REUSE_WQ) {
+ ctxp->flag &= ~LPFC_NVMET_CTX_REUSE_WQ;
+ spin_unlock_irqrestore(&ctxp->ctxlock, iflag);
+ nvmebuf->hrq->rqbp->rqb_free_buffer(phba, nvmebuf);
+ } else {
+ spin_unlock_irqrestore(&ctxp->ctxlock, iflag);
+ lpfc_rq_buf_free(phba, &nvmebuf->hbuf); /* repost */
+ }
+ }
ctxp->state = LPFC_NVMET_STE_FREE;
spin_lock_irqsave(&phba->sli4_hba.nvmet_io_wait_lock, iflag);
@@ -388,46 +403,30 @@ lpfc_nvmet_ctxbuf_post(struct lpfc_hba *phba, struct lpfc_nvmet_ctxbuf *ctx_buf)
}
#endif
atomic_inc(&tgtp->rcv_fcp_cmd_in);
- /*
- * The calling sequence should be:
- * nvmet_fc_rcv_fcp_req->lpfc_nvmet_xmt_fcp_op/cmp- req->done
- * lpfc_nvmet_xmt_fcp_op_cmp should free the allocated ctxp.
- * When we return from nvmet_fc_rcv_fcp_req, all relevant info
- * the NVME command / FC header is stored.
- * A buffer has already been reposted for this IO, so just free
- * the nvmebuf.
- */
- rc = nvmet_fc_rcv_fcp_req(phba->targetport, &ctxp->ctx.fcp_req,
- payload, size);
- /* Process FCP command */
- if (rc == 0) {
- ctxp->rqb_buffer = NULL;
- atomic_inc(&tgtp->rcv_fcp_cmd_out);
- nvmebuf->hrq->rqbp->rqb_free_buffer(phba, nvmebuf);
- return;
- }
+ /* flag new work queued, replacement buffer has already
+ * been reposted
+ */
+ spin_lock_irqsave(&ctxp->ctxlock, iflag);
+ ctxp->flag |= LPFC_NVMET_CTX_REUSE_WQ;
+ spin_unlock_irqrestore(&ctxp->ctxlock, iflag);
- /* Processing of FCP command is deferred */
- if (rc == -EOVERFLOW) {
- lpfc_nvmeio_data(phba,
- "NVMET RCV BUSY: xri x%x sz %d "
- "from %06x\n",
- oxid, size, sid);
- atomic_inc(&tgtp->rcv_fcp_cmd_out);
- return;
+ if (!queue_work(phba->wq, &ctx_buf->defer_work)) {
+ atomic_inc(&tgtp->rcv_fcp_cmd_drop);
+ lpfc_printf_log(phba, KERN_ERR, LOG_NVME,
+ "6181 Unable to queue deferred work "
+ "for oxid x%x. "
+ "FCP Drop IO [x%x x%x x%x]\n",
+ ctxp->oxid,
+ atomic_read(&tgtp->rcv_fcp_cmd_in),
+ atomic_read(&tgtp->rcv_fcp_cmd_out),
+ atomic_read(&tgtp->xmt_fcp_release));
+
+ spin_lock_irqsave(&ctxp->ctxlock, iflag);
+ lpfc_nvmet_defer_release(phba, ctxp);
+ spin_unlock_irqrestore(&ctxp->ctxlock, iflag);
+ lpfc_nvmet_unsol_fcp_issue_abort(phba, ctxp, sid, oxid);
}
- atomic_inc(&tgtp->rcv_fcp_cmd_drop);
- lpfc_printf_log(phba, KERN_ERR, LOG_NVME_IOERR,
- "2582 FCP Drop IO x%x: err x%x: x%x x%x x%x\n",
- ctxp->oxid, rc,
- atomic_read(&tgtp->rcv_fcp_cmd_in),
- atomic_read(&tgtp->rcv_fcp_cmd_out),
- atomic_read(&tgtp->xmt_fcp_release));
-
- lpfc_nvmet_defer_release(phba, ctxp);
- lpfc_nvmet_unsol_fcp_issue_abort(phba, ctxp, sid, oxid);
- nvmebuf->hrq->rqbp->rqb_free_buffer(phba, nvmebuf);
return;
}
spin_unlock_irqrestore(&phba->sli4_hba.nvmet_io_wait_lock, iflag);
@@ -744,16 +743,6 @@ lpfc_nvmet_xmt_fcp_op_cmp(struct lpfc_hba *phba, struct lpfc_iocbq *cmdwqe,
ktime_get_ns();
}
}
- if (phba->cpucheck_on & LPFC_CHECK_NVMET_IO) {
- id = smp_processor_id();
- if (ctxp->cpu != id)
- lpfc_printf_log(phba, KERN_ERR, LOG_NVME_IOERR,
- "6703 CPU Check cmpl: "
- "cpu %d expect %d\n",
- id, ctxp->cpu);
- if (ctxp->cpu < LPFC_CHECK_CPU_CNT)
- phba->cpucheck_cmpl_io[id]++;
- }
#endif
rsp->done(rsp);
#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
@@ -771,19 +760,22 @@ lpfc_nvmet_xmt_fcp_op_cmp(struct lpfc_hba *phba, struct lpfc_iocbq *cmdwqe,
ctxp->ts_isr_data = cmdwqe->isr_timestamp;
ctxp->ts_data_nvme = ktime_get_ns();
}
- if (phba->cpucheck_on & LPFC_CHECK_NVMET_IO) {
- id = smp_processor_id();
+#endif
+ rsp->done(rsp);
+ }
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ if (phba->cpucheck_on & LPFC_CHECK_NVMET_IO) {
+ id = smp_processor_id();
+ if (id < LPFC_CHECK_CPU_CNT) {
if (ctxp->cpu != id)
- lpfc_printf_log(phba, KERN_ERR, LOG_NVME_IOERR,
+ lpfc_printf_log(phba, KERN_INFO, LOG_NVME_IOERR,
"6704 CPU Check cmdcmpl: "
"cpu %d expect %d\n",
id, ctxp->cpu);
- if (ctxp->cpu < LPFC_CHECK_CPU_CNT)
- phba->cpucheck_ccmpl_io[id]++;
+ phba->sli4_hba.hdwq[rsp->hwqid].cpucheck_cmpl_io[id]++;
}
-#endif
- rsp->done(rsp);
}
+#endif
}
static int
@@ -852,7 +844,7 @@ lpfc_nvmet_xmt_ls_rsp(struct nvmet_fc_target_port *tgtport,
lpfc_nvmeio_data(phba, "NVMET LS RESP: xri x%x wqidx x%x len x%x\n",
ctxp->oxid, nvmewqeq->hba_wqidx, rsp->rsplen);
- rc = lpfc_sli4_issue_wqe(phba, LPFC_ELS_RING, nvmewqeq);
+ rc = lpfc_sli4_issue_wqe(phba, ctxp->hdwq, nvmewqeq);
if (rc == WQE_SUCCESS) {
/*
* Okay to repost buffer here, but wait till cmpl
@@ -908,18 +900,22 @@ lpfc_nvmet_xmt_fcp_op(struct nvmet_fc_target_port *tgtport,
else
ctxp->ts_nvme_data = ktime_get_ns();
}
+
+ /* Setup the hdw queue if not already set */
+ if (!ctxp->hdwq)
+ ctxp->hdwq = &phba->sli4_hba.hdwq[rsp->hwqid];
+
if (phba->cpucheck_on & LPFC_CHECK_NVMET_IO) {
int id = smp_processor_id();
- ctxp->cpu = id;
- if (id < LPFC_CHECK_CPU_CNT)
- phba->cpucheck_xmt_io[id]++;
- if (rsp->hwqid != id) {
- lpfc_printf_log(phba, KERN_ERR, LOG_NVME_IOERR,
- "6705 CPU Check OP: "
- "cpu %d expect %d\n",
- id, rsp->hwqid);
- ctxp->cpu = rsp->hwqid;
+ if (id < LPFC_CHECK_CPU_CNT) {
+ if (rsp->hwqid != id)
+ lpfc_printf_log(phba, KERN_INFO, LOG_NVME_IOERR,
+ "6705 CPU Check OP: "
+ "cpu %d expect %d\n",
+ id, rsp->hwqid);
+ phba->sli4_hba.hdwq[rsp->hwqid].cpucheck_xmt_io[id]++;
}
+ ctxp->cpu = id; /* Setup cpu for cmpl check */
}
#endif
@@ -954,7 +950,7 @@ lpfc_nvmet_xmt_fcp_op(struct nvmet_fc_target_port *tgtport,
ctxp->oxid, rsp->op, rsp->rsplen);
ctxp->flag |= LPFC_NVMET_IO_INP;
- rc = lpfc_sli4_issue_wqe(phba, LPFC_FCP_RING, nvmewqeq);
+ rc = lpfc_sli4_issue_wqe(phba, ctxp->hdwq, nvmewqeq);
if (rc == WQE_SUCCESS) {
#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
if (!ctxp->ts_cmd_nvme)
@@ -973,7 +969,7 @@ lpfc_nvmet_xmt_fcp_op(struct nvmet_fc_target_port *tgtport,
* WQE release CQE
*/
ctxp->flag |= LPFC_NVMET_DEFER_WQFULL;
- wq = phba->sli4_hba.nvme_wq[rsp->hwqid];
+ wq = ctxp->hdwq->nvme_wq;
pring = wq->pring;
spin_lock_irqsave(&pring->ring_lock, iflags);
list_add_tail(&nvmewqeq->list, &wq->wqfull_list);
@@ -1024,6 +1020,9 @@ lpfc_nvmet_xmt_fcp_abort(struct nvmet_fc_target_port *tgtport,
if (phba->pport->load_flag & FC_UNLOADING)
return;
+ if (!ctxp->hdwq)
+ ctxp->hdwq = &phba->sli4_hba.hdwq[0];
+
lpfc_printf_log(phba, KERN_INFO, LOG_NVME_ABTS,
"6103 NVMET Abort op: oxri x%x flg x%x ste %d\n",
ctxp->oxid, ctxp->flag, ctxp->state);
@@ -1034,7 +1033,6 @@ lpfc_nvmet_xmt_fcp_abort(struct nvmet_fc_target_port *tgtport,
atomic_inc(&lpfc_nvmep->xmt_fcp_abort);
spin_lock_irqsave(&ctxp->ctxlock, flags);
- ctxp->state = LPFC_NVMET_STE_ABORT;
/* Since iaab/iaar are NOT set, we need to check
* if the firmware is in process of aborting IO
@@ -1046,13 +1044,14 @@ lpfc_nvmet_xmt_fcp_abort(struct nvmet_fc_target_port *tgtport,
ctxp->flag |= LPFC_NVMET_ABORT_OP;
if (ctxp->flag & LPFC_NVMET_DEFER_WQFULL) {
+ spin_unlock_irqrestore(&ctxp->ctxlock, flags);
lpfc_nvmet_unsol_fcp_issue_abort(phba, ctxp, ctxp->sid,
ctxp->oxid);
- wq = phba->sli4_hba.nvme_wq[ctxp->wqeq->hba_wqidx];
- spin_unlock_irqrestore(&ctxp->ctxlock, flags);
+ wq = ctxp->hdwq->nvme_wq;
lpfc_nvmet_wqfull_flush(phba, wq, ctxp);
return;
}
+ spin_unlock_irqrestore(&ctxp->ctxlock, flags);
/* An state of LPFC_NVMET_STE_RCV means we have just received
* the NVME command and have not started processing it.
@@ -1064,7 +1063,6 @@ lpfc_nvmet_xmt_fcp_abort(struct nvmet_fc_target_port *tgtport,
else
lpfc_nvmet_sol_fcp_issue_abort(phba, ctxp, ctxp->sid,
ctxp->oxid);
- spin_unlock_irqrestore(&ctxp->ctxlock, flags);
}
static void
@@ -1078,14 +1076,18 @@ lpfc_nvmet_xmt_fcp_release(struct nvmet_fc_target_port *tgtport,
unsigned long flags;
bool aborting = false;
- if (ctxp->state != LPFC_NVMET_STE_DONE &&
- ctxp->state != LPFC_NVMET_STE_ABORT) {
+ spin_lock_irqsave(&ctxp->ctxlock, flags);
+ if (ctxp->flag & LPFC_NVMET_XBUSY)
+ lpfc_printf_log(phba, KERN_INFO, LOG_NVME_IOERR,
+ "6027 NVMET release with XBUSY flag x%x"
+ " oxid x%x\n",
+ ctxp->flag, ctxp->oxid);
+ else if (ctxp->state != LPFC_NVMET_STE_DONE &&
+ ctxp->state != LPFC_NVMET_STE_ABORT)
lpfc_printf_log(phba, KERN_ERR, LOG_NVME_IOERR,
"6413 NVMET release bad state %d %d oxid x%x\n",
ctxp->state, ctxp->entry_cnt, ctxp->oxid);
- }
- spin_lock_irqsave(&ctxp->ctxlock, flags);
if ((ctxp->flag & LPFC_NVMET_ABORT_OP) ||
(ctxp->flag & LPFC_NVMET_XBUSY)) {
aborting = true;
@@ -1114,6 +1116,8 @@ lpfc_nvmet_defer_rcv(struct nvmet_fc_target_port *tgtport,
container_of(rsp, struct lpfc_nvmet_rcv_ctx, ctx.fcp_req);
struct rqb_dmabuf *nvmebuf = ctxp->rqb_buffer;
struct lpfc_hba *phba = ctxp->phba;
+ unsigned long iflag;
+
lpfc_nvmeio_data(phba, "NVMET DEFERRCV: xri x%x sz %d CPU %02x\n",
ctxp->oxid, ctxp->size, smp_processor_id());
@@ -1132,6 +1136,9 @@ lpfc_nvmet_defer_rcv(struct nvmet_fc_target_port *tgtport,
/* Free the nvmebuf since a new buffer already replaced it */
nvmebuf->hrq->rqbp->rqb_free_buffer(phba, nvmebuf);
+ spin_lock_irqsave(&ctxp->ctxlock, iflag);
+ ctxp->rqb_buffer = NULL;
+ spin_unlock_irqrestore(&ctxp->ctxlock, iflag);
}
static struct nvmet_fc_target_template lpfc_tgttemplate = {
@@ -1163,9 +1170,9 @@ __lpfc_nvmet_clean_io_for_cpu(struct lpfc_hba *phba,
spin_lock_irqsave(&infop->nvmet_ctx_list_lock, flags);
list_for_each_entry_safe(ctx_buf, next_ctx_buf,
&infop->nvmet_ctx_list, list) {
- spin_lock(&phba->sli4_hba.abts_nvme_buf_list_lock);
+ spin_lock(&phba->sli4_hba.abts_nvmet_buf_list_lock);
list_del_init(&ctx_buf->list);
- spin_unlock(&phba->sli4_hba.abts_nvme_buf_list_lock);
+ spin_unlock(&phba->sli4_hba.abts_nvmet_buf_list_lock);
__lpfc_clear_active_sglq(phba, ctx_buf->sglq->sli4_lxritag);
ctx_buf->sglq->state = SGL_FREED;
@@ -1195,9 +1202,9 @@ lpfc_nvmet_cleanup_io_context(struct lpfc_hba *phba)
/* Cycle the the entire CPU context list for every MRQ */
for (i = 0; i < phba->cfg_nvmet_mrq; i++) {
- for (j = 0; j < phba->sli4_hba.num_present_cpu; j++) {
+ for_each_present_cpu(j) {
+ infop = lpfc_get_ctx_list(phba, j, i);
__lpfc_nvmet_clean_io_for_cpu(phba, infop);
- infop++; /* next */
}
}
kfree(phba->sli4_hba.nvmet_ctx_info);
@@ -1212,14 +1219,14 @@ lpfc_nvmet_setup_io_context(struct lpfc_hba *phba)
union lpfc_wqe128 *wqe;
struct lpfc_nvmet_ctx_info *last_infop;
struct lpfc_nvmet_ctx_info *infop;
- int i, j, idx;
+ int i, j, idx, cpu;
lpfc_printf_log(phba, KERN_INFO, LOG_NVME,
"6403 Allocate NVMET resources for %d XRIs\n",
phba->sli4_hba.nvmet_xri_cnt);
phba->sli4_hba.nvmet_ctx_info = kcalloc(
- phba->sli4_hba.num_present_cpu * phba->cfg_nvmet_mrq,
+ phba->sli4_hba.num_possible_cpu * phba->cfg_nvmet_mrq,
sizeof(struct lpfc_nvmet_ctx_info), GFP_KERNEL);
if (!phba->sli4_hba.nvmet_ctx_info) {
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
@@ -1247,13 +1254,12 @@ lpfc_nvmet_setup_io_context(struct lpfc_hba *phba)
* of the IO completion. Thus a context that was allocated for MRQ A
* whose IO completed on CPU B will be freed to cpuB/mrqA.
*/
- infop = phba->sli4_hba.nvmet_ctx_info;
- for (i = 0; i < phba->sli4_hba.num_present_cpu; i++) {
+ for_each_possible_cpu(i) {
for (j = 0; j < phba->cfg_nvmet_mrq; j++) {
+ infop = lpfc_get_ctx_list(phba, i, j);
INIT_LIST_HEAD(&infop->nvmet_ctx_list);
spin_lock_init(&infop->nvmet_ctx_list_lock);
infop->nvmet_ctx_list_cnt = 0;
- infop++;
}
}
@@ -1263,8 +1269,10 @@ lpfc_nvmet_setup_io_context(struct lpfc_hba *phba)
* MRQ 1 cycling thru CPUs 0 - X, and so on.
*/
for (j = 0; j < phba->cfg_nvmet_mrq; j++) {
- last_infop = lpfc_get_ctx_list(phba, 0, j);
- for (i = phba->sli4_hba.num_present_cpu - 1; i >= 0; i--) {
+ last_infop = lpfc_get_ctx_list(phba,
+ cpumask_first(cpu_present_mask),
+ j);
+ for (i = phba->sli4_hba.num_possible_cpu - 1; i >= 0; i--) {
infop = lpfc_get_ctx_list(phba, i, j);
infop->nvmet_ctx_next_cpu = last_infop;
last_infop = infop;
@@ -1275,6 +1283,7 @@ lpfc_nvmet_setup_io_context(struct lpfc_hba *phba)
* received command on a per xri basis.
*/
idx = 0;
+ cpu = cpumask_first(cpu_present_mask);
for (i = 0; i < phba->sli4_hba.nvmet_xri_cnt; i++) {
ctx_buf = kzalloc(sizeof(*ctx_buf), GFP_KERNEL);
if (!ctx_buf) {
@@ -1322,13 +1331,14 @@ lpfc_nvmet_setup_io_context(struct lpfc_hba *phba)
"6407 Ran out of NVMET XRIs\n");
return -ENOMEM;
}
+ INIT_WORK(&ctx_buf->defer_work, lpfc_nvmet_fcp_rqst_defer_work);
/*
* Add ctx to MRQidx context list. Our initial assumption
* is MRQidx will be associated with CPUidx. This association
* can change on the fly.
*/
- infop = lpfc_get_ctx_list(phba, idx, idx);
+ infop = lpfc_get_ctx_list(phba, cpu, idx);
spin_lock(&infop->nvmet_ctx_list_lock);
list_add_tail(&ctx_buf->list, &infop->nvmet_ctx_list);
infop->nvmet_ctx_list_cnt++;
@@ -1336,11 +1346,18 @@ lpfc_nvmet_setup_io_context(struct lpfc_hba *phba)
/* Spread ctx structures evenly across all MRQs */
idx++;
- if (idx >= phba->cfg_nvmet_mrq)
+ if (idx >= phba->cfg_nvmet_mrq) {
idx = 0;
+ cpu = cpumask_first(cpu_present_mask);
+ continue;
+ }
+ cpu = cpumask_next(cpu, cpu_present_mask);
+ if (cpu == nr_cpu_ids)
+ cpu = cpumask_first(cpu_present_mask);
+
}
- for (i = 0; i < phba->sli4_hba.num_present_cpu; i++) {
+ for_each_present_cpu(i) {
for (j = 0; j < phba->cfg_nvmet_mrq; j++) {
infop = lpfc_get_ctx_list(phba, i, j);
lpfc_printf_log(phba, KERN_INFO, LOG_NVME | LOG_INIT,
@@ -1378,7 +1395,7 @@ lpfc_nvmet_create_targetport(struct lpfc_hba *phba)
* allocate + 3, one for cmd, one for rsp and one for this alignment
*/
lpfc_tgttemplate.max_sgl_segments = phba->cfg_nvme_seg_cnt + 1;
- lpfc_tgttemplate.max_hw_queues = phba->cfg_nvme_io_channel;
+ lpfc_tgttemplate.max_hw_queues = phba->cfg_hdw_queue;
lpfc_tgttemplate.target_features = NVMET_FCTGTFEAT_READDATA_RSP;
#if (IS_ENABLED(CONFIG_NVME_TARGET_FC))
@@ -1503,13 +1520,14 @@ lpfc_sli4_nvmet_xri_aborted(struct lpfc_hba *phba,
}
spin_lock_irqsave(&phba->hbalock, iflag);
- spin_lock(&phba->sli4_hba.abts_nvme_buf_list_lock);
+ spin_lock(&phba->sli4_hba.abts_nvmet_buf_list_lock);
list_for_each_entry_safe(ctxp, next_ctxp,
&phba->sli4_hba.lpfc_abts_nvmet_ctx_list,
list) {
if (ctxp->ctxbuf->sglq->sli4_xritag != xri)
continue;
+ spin_lock(&ctxp->ctxlock);
/* Check if we already received a free context call
* and we have completed processing an abort situation.
*/
@@ -1519,7 +1537,8 @@ lpfc_sli4_nvmet_xri_aborted(struct lpfc_hba *phba,
released = true;
}
ctxp->flag &= ~LPFC_NVMET_XBUSY;
- spin_unlock(&phba->sli4_hba.abts_nvme_buf_list_lock);
+ spin_unlock(&ctxp->ctxlock);
+ spin_unlock(&phba->sli4_hba.abts_nvmet_buf_list_lock);
rrq_empty = list_empty(&phba->active_rrq_list);
spin_unlock_irqrestore(&phba->hbalock, iflag);
@@ -1543,14 +1562,13 @@ lpfc_sli4_nvmet_xri_aborted(struct lpfc_hba *phba,
lpfc_worker_wake_up(phba);
return;
}
- spin_unlock(&phba->sli4_hba.abts_nvme_buf_list_lock);
+ spin_unlock(&phba->sli4_hba.abts_nvmet_buf_list_lock);
spin_unlock_irqrestore(&phba->hbalock, iflag);
}
int
lpfc_nvmet_rcv_unsol_abort(struct lpfc_vport *vport,
struct fc_frame_header *fc_hdr)
-
{
#if (IS_ENABLED(CONFIG_NVME_TARGET_FC))
struct lpfc_hba *phba = vport->phba;
@@ -1562,14 +1580,14 @@ lpfc_nvmet_rcv_unsol_abort(struct lpfc_vport *vport,
xri = be16_to_cpu(fc_hdr->fh_ox_id);
spin_lock_irqsave(&phba->hbalock, iflag);
- spin_lock(&phba->sli4_hba.abts_nvme_buf_list_lock);
+ spin_lock(&phba->sli4_hba.abts_nvmet_buf_list_lock);
list_for_each_entry_safe(ctxp, next_ctxp,
&phba->sli4_hba.lpfc_abts_nvmet_ctx_list,
list) {
if (ctxp->ctxbuf->sglq->sli4_xritag != xri)
continue;
- spin_unlock(&phba->sli4_hba.abts_nvme_buf_list_lock);
+ spin_unlock(&phba->sli4_hba.abts_nvmet_buf_list_lock);
spin_unlock_irqrestore(&phba->hbalock, iflag);
spin_lock_irqsave(&ctxp->ctxlock, iflag);
@@ -1590,7 +1608,7 @@ lpfc_nvmet_rcv_unsol_abort(struct lpfc_vport *vport,
lpfc_sli4_seq_abort_rsp(vport, fc_hdr, 1);
return 0;
}
- spin_unlock(&phba->sli4_hba.abts_nvme_buf_list_lock);
+ spin_unlock(&phba->sli4_hba.abts_nvmet_buf_list_lock);
spin_unlock_irqrestore(&phba->hbalock, iflag);
lpfc_nvmeio_data(phba, "NVMET ABTS RCV: xri x%x CPU %02x rjt %d\n",
@@ -1658,6 +1676,7 @@ lpfc_nvmet_wqfull_process(struct lpfc_hba *phba,
#if (IS_ENABLED(CONFIG_NVME_TARGET_FC))
struct lpfc_sli_ring *pring;
struct lpfc_iocbq *nvmewqeq;
+ struct lpfc_nvmet_rcv_ctx *ctxp;
unsigned long iflags;
int rc;
@@ -1671,7 +1690,8 @@ lpfc_nvmet_wqfull_process(struct lpfc_hba *phba,
list_remove_head(&wq->wqfull_list, nvmewqeq, struct lpfc_iocbq,
list);
spin_unlock_irqrestore(&pring->ring_lock, iflags);
- rc = lpfc_sli4_issue_wqe(phba, LPFC_FCP_RING, nvmewqeq);
+ ctxp = (struct lpfc_nvmet_rcv_ctx *)nvmewqeq->context2;
+ rc = lpfc_sli4_issue_wqe(phba, ctxp->hdwq, nvmewqeq);
spin_lock_irqsave(&pring->ring_lock, iflags);
if (rc == -EBUSY) {
/* WQ was full again, so put it back on the list */
@@ -1699,8 +1719,8 @@ lpfc_nvmet_destroy_targetport(struct lpfc_hba *phba)
return;
if (phba->targetport) {
tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private;
- for (qidx = 0; qidx < phba->cfg_nvme_io_channel; qidx++) {
- wq = phba->sli4_hba.nvme_wq[qidx];
+ for (qidx = 0; qidx < phba->cfg_hdw_queue; qidx++) {
+ wq = phba->sli4_hba.hdwq[qidx].nvme_wq;
lpfc_nvmet_wqfull_flush(phba, wq, NULL);
}
tgtp->tport_unreg_cmp = &tport_unreg_cmp;
@@ -1775,6 +1795,7 @@ dropit:
ctxp->state = LPFC_NVMET_STE_LS_RCV;
ctxp->entry_cnt = 1;
ctxp->rqb_buffer = (void *)nvmebuf;
+ ctxp->hdwq = &phba->sli4_hba.hdwq[0];
lpfc_nvmeio_data(phba, "NVMET LS RCV: xri x%x sz %d from %06x\n",
oxid, size, sid);
@@ -1814,6 +1835,86 @@ dropit:
#endif
}
+static void
+lpfc_nvmet_process_rcv_fcp_req(struct lpfc_nvmet_ctxbuf *ctx_buf)
+{
+#if (IS_ENABLED(CONFIG_NVME_TARGET_FC))
+ struct lpfc_nvmet_rcv_ctx *ctxp = ctx_buf->context;
+ struct lpfc_hba *phba = ctxp->phba;
+ struct rqb_dmabuf *nvmebuf = ctxp->rqb_buffer;
+ struct lpfc_nvmet_tgtport *tgtp;
+ uint32_t *payload;
+ uint32_t rc;
+ unsigned long iflags;
+
+ if (!nvmebuf) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_NVME_IOERR,
+ "6159 process_rcv_fcp_req, nvmebuf is NULL, "
+ "oxid: x%x flg: x%x state: x%x\n",
+ ctxp->oxid, ctxp->flag, ctxp->state);
+ spin_lock_irqsave(&ctxp->ctxlock, iflags);
+ lpfc_nvmet_defer_release(phba, ctxp);
+ spin_unlock_irqrestore(&ctxp->ctxlock, iflags);
+ lpfc_nvmet_unsol_fcp_issue_abort(phba, ctxp, ctxp->sid,
+ ctxp->oxid);
+ return;
+ }
+
+ payload = (uint32_t *)(nvmebuf->dbuf.virt);
+ tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private;
+ /*
+ * The calling sequence should be:
+ * nvmet_fc_rcv_fcp_req->lpfc_nvmet_xmt_fcp_op/cmp- req->done
+ * lpfc_nvmet_xmt_fcp_op_cmp should free the allocated ctxp.
+ * When we return from nvmet_fc_rcv_fcp_req, all relevant info
+ * the NVME command / FC header is stored.
+ * A buffer has already been reposted for this IO, so just free
+ * the nvmebuf.
+ */
+ rc = nvmet_fc_rcv_fcp_req(phba->targetport, &ctxp->ctx.fcp_req,
+ payload, ctxp->size);
+ /* Process FCP command */
+ if (rc == 0) {
+ atomic_inc(&tgtp->rcv_fcp_cmd_out);
+ return;
+ }
+
+ /* Processing of FCP command is deferred */
+ if (rc == -EOVERFLOW) {
+ lpfc_nvmeio_data(phba, "NVMET RCV BUSY: xri x%x sz %d "
+ "from %06x\n",
+ ctxp->oxid, ctxp->size, ctxp->sid);
+ atomic_inc(&tgtp->rcv_fcp_cmd_out);
+ atomic_inc(&tgtp->defer_fod);
+ return;
+ }
+ atomic_inc(&tgtp->rcv_fcp_cmd_drop);
+ lpfc_printf_log(phba, KERN_ERR, LOG_NVME_IOERR,
+ "2582 FCP Drop IO x%x: err x%x: x%x x%x x%x\n",
+ ctxp->oxid, rc,
+ atomic_read(&tgtp->rcv_fcp_cmd_in),
+ atomic_read(&tgtp->rcv_fcp_cmd_out),
+ atomic_read(&tgtp->xmt_fcp_release));
+ lpfc_nvmeio_data(phba, "NVMET FCP DROP: xri x%x sz %d from %06x\n",
+ ctxp->oxid, ctxp->size, ctxp->sid);
+ spin_lock_irqsave(&ctxp->ctxlock, iflags);
+ lpfc_nvmet_defer_release(phba, ctxp);
+ spin_unlock_irqrestore(&ctxp->ctxlock, iflags);
+ lpfc_nvmet_unsol_fcp_issue_abort(phba, ctxp, ctxp->sid, ctxp->oxid);
+#endif
+}
+
+static void
+lpfc_nvmet_fcp_rqst_defer_work(struct work_struct *work)
+{
+#if (IS_ENABLED(CONFIG_NVME_TARGET_FC))
+ struct lpfc_nvmet_ctxbuf *ctx_buf =
+ container_of(work, struct lpfc_nvmet_ctxbuf, defer_work);
+
+ lpfc_nvmet_process_rcv_fcp_req(ctx_buf);
+#endif
+}
+
static struct lpfc_nvmet_ctxbuf *
lpfc_nvmet_replenish_context(struct lpfc_hba *phba,
struct lpfc_nvmet_ctx_info *current_infop)
@@ -1838,7 +1939,7 @@ lpfc_nvmet_replenish_context(struct lpfc_hba *phba,
else
get_infop = current_infop->nvmet_ctx_next_cpu;
- for (i = 0; i < phba->sli4_hba.num_present_cpu; i++) {
+ for (i = 0; i < phba->sli4_hba.num_possible_cpu; i++) {
if (get_infop == current_infop) {
get_infop = get_infop->nvmet_ctx_next_cpu;
continue;
@@ -1896,12 +1997,9 @@ lpfc_nvmet_unsol_fcp_buffer(struct lpfc_hba *phba,
struct lpfc_nvmet_ctxbuf *ctx_buf;
struct lpfc_nvmet_ctx_info *current_infop;
uint32_t *payload;
- uint32_t size, oxid, sid, rc, qno;
+ uint32_t size, oxid, sid, qno;
unsigned long iflag;
int current_cpu;
-#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
- uint32_t id;
-#endif
if (!IS_ENABLED(CONFIG_NVME_TARGET_FC))
return;
@@ -1910,11 +2008,9 @@ lpfc_nvmet_unsol_fcp_buffer(struct lpfc_hba *phba,
if (!nvmebuf || !phba->targetport) {
lpfc_printf_log(phba, KERN_ERR, LOG_NVME_IOERR,
"6157 NVMET FCP Drop IO\n");
- oxid = 0;
- size = 0;
- sid = 0;
- ctxp = NULL;
- goto dropit;
+ if (nvmebuf)
+ lpfc_rq_buf_free(phba, &nvmebuf->hbuf);
+ return;
}
/*
@@ -1942,9 +2038,14 @@ lpfc_nvmet_unsol_fcp_buffer(struct lpfc_hba *phba,
#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
if (phba->cpucheck_on & LPFC_CHECK_NVMET_RCV) {
- id = smp_processor_id();
- if (id < LPFC_CHECK_CPU_CNT)
- phba->cpucheck_rcv_io[id]++;
+ if (current_cpu < LPFC_CHECK_CPU_CNT) {
+ if (idx != current_cpu)
+ lpfc_printf_log(phba, KERN_INFO, LOG_NVME_IOERR,
+ "6703 CPU Check rcv: "
+ "cpu %d expect %d\n",
+ current_cpu, idx);
+ phba->sli4_hba.hdwq[idx].cpucheck_rcv_io[current_cpu]++;
+ }
}
#endif
@@ -1995,6 +2096,7 @@ lpfc_nvmet_unsol_fcp_buffer(struct lpfc_hba *phba,
ctxp->flag = 0;
ctxp->ctxbuf = ctx_buf;
ctxp->rqb_buffer = (void *)nvmebuf;
+ ctxp->hdwq = NULL;
spin_lock_init(&ctxp->ctxlock);
#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
@@ -2015,67 +2117,7 @@ lpfc_nvmet_unsol_fcp_buffer(struct lpfc_hba *phba,
#endif
atomic_inc(&tgtp->rcv_fcp_cmd_in);
- /*
- * The calling sequence should be:
- * nvmet_fc_rcv_fcp_req -> lpfc_nvmet_xmt_fcp_op/cmp -> req->done
- * lpfc_nvmet_xmt_fcp_op_cmp should free the allocated ctxp.
- * When we return from nvmet_fc_rcv_fcp_req, all relevant info in
- * the NVME command / FC header is stored, so we are free to repost
- * the buffer.
- */
- rc = nvmet_fc_rcv_fcp_req(phba->targetport, &ctxp->ctx.fcp_req,
- payload, size);
-
- /* Process FCP command */
- if (rc == 0) {
- ctxp->rqb_buffer = NULL;
- atomic_inc(&tgtp->rcv_fcp_cmd_out);
- lpfc_rq_buf_free(phba, &nvmebuf->hbuf); /* repost */
- return;
- }
-
- /* Processing of FCP command is deferred */
- if (rc == -EOVERFLOW) {
- /*
- * Post a brand new DMA buffer to RQ and defer
- * freeing rcv buffer till .defer_rcv callback
- */
- qno = nvmebuf->idx;
- lpfc_post_rq_buffer(
- phba, phba->sli4_hba.nvmet_mrq_hdr[qno],
- phba->sli4_hba.nvmet_mrq_data[qno], 1, qno);
-
- lpfc_nvmeio_data(phba,
- "NVMET RCV BUSY: xri x%x sz %d from %06x\n",
- oxid, size, sid);
- atomic_inc(&tgtp->rcv_fcp_cmd_out);
- atomic_inc(&tgtp->defer_fod);
- return;
- }
- ctxp->rqb_buffer = nvmebuf;
-
- atomic_inc(&tgtp->rcv_fcp_cmd_drop);
- lpfc_printf_log(phba, KERN_ERR, LOG_NVME_IOERR,
- "6159 FCP Drop IO x%x: err x%x: x%x x%x x%x\n",
- ctxp->oxid, rc,
- atomic_read(&tgtp->rcv_fcp_cmd_in),
- atomic_read(&tgtp->rcv_fcp_cmd_out),
- atomic_read(&tgtp->xmt_fcp_release));
-dropit:
- lpfc_nvmeio_data(phba, "NVMET FCP DROP: xri x%x sz %d from %06x\n",
- oxid, size, sid);
- if (oxid) {
- lpfc_nvmet_defer_release(phba, ctxp);
- lpfc_nvmet_unsol_fcp_issue_abort(phba, ctxp, sid, oxid);
- lpfc_rq_buf_free(phba, &nvmebuf->hbuf); /* repost */
- return;
- }
-
- if (ctx_buf)
- lpfc_nvmet_ctxbuf_post(phba, ctx_buf);
-
- if (nvmebuf)
- lpfc_rq_buf_free(phba, &nvmebuf->hbuf); /* repost */
+ lpfc_nvmet_process_rcv_fcp_req(ctx_buf);
}
/**
@@ -2660,15 +2702,17 @@ lpfc_nvmet_sol_fcp_abort_cmp(struct lpfc_hba *phba, struct lpfc_iocbq *cmdwqe,
if (ctxp->flag & LPFC_NVMET_ABORT_OP)
atomic_inc(&tgtp->xmt_fcp_abort_cmpl);
+ spin_lock_irqsave(&ctxp->ctxlock, flags);
ctxp->state = LPFC_NVMET_STE_DONE;
/* Check if we already received a free context call
* and we have completed processing an abort situation.
*/
- spin_lock_irqsave(&ctxp->ctxlock, flags);
if ((ctxp->flag & LPFC_NVMET_CTX_RLS) &&
!(ctxp->flag & LPFC_NVMET_XBUSY)) {
+ spin_lock(&phba->sli4_hba.abts_nvmet_buf_list_lock);
list_del(&ctxp->list);
+ spin_unlock(&phba->sli4_hba.abts_nvmet_buf_list_lock);
released = true;
}
ctxp->flag &= ~LPFC_NVMET_ABORT_OP;
@@ -2734,6 +2778,7 @@ lpfc_nvmet_unsol_fcp_abort_cmp(struct lpfc_hba *phba, struct lpfc_iocbq *cmdwqe,
}
tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private;
+ spin_lock_irqsave(&ctxp->ctxlock, flags);
if (ctxp->flag & LPFC_NVMET_ABORT_OP)
atomic_inc(&tgtp->xmt_fcp_abort_cmpl);
@@ -2748,10 +2793,11 @@ lpfc_nvmet_unsol_fcp_abort_cmp(struct lpfc_hba *phba, struct lpfc_iocbq *cmdwqe,
* and we have completed processing an abort situation.
*/
ctxp->state = LPFC_NVMET_STE_DONE;
- spin_lock_irqsave(&ctxp->ctxlock, flags);
if ((ctxp->flag & LPFC_NVMET_CTX_RLS) &&
!(ctxp->flag & LPFC_NVMET_XBUSY)) {
+ spin_lock(&phba->sli4_hba.abts_nvmet_buf_list_lock);
list_del(&ctxp->list);
+ spin_unlock(&phba->sli4_hba.abts_nvmet_buf_list_lock);
released = true;
}
ctxp->flag &= ~LPFC_NVMET_ABORT_OP;
@@ -2957,12 +3003,15 @@ lpfc_nvmet_sol_fcp_issue_abort(struct lpfc_hba *phba,
(ndlp) ? ndlp->nlp_state : NLP_STE_MAX_STATE);
/* No failure to an ABTS request. */
+ spin_lock_irqsave(&ctxp->ctxlock, flags);
ctxp->flag &= ~LPFC_NVMET_ABORT_OP;
+ spin_unlock_irqrestore(&ctxp->ctxlock, flags);
return 0;
}
/* Issue ABTS for this WQE based on iotag */
ctxp->abort_wqeq = lpfc_sli_get_iocbq(phba);
+ spin_lock_irqsave(&ctxp->ctxlock, flags);
if (!ctxp->abort_wqeq) {
atomic_inc(&tgtp->xmt_abort_rsp_error);
lpfc_printf_log(phba, KERN_ERR, LOG_NVME_ABTS,
@@ -2970,11 +3019,13 @@ lpfc_nvmet_sol_fcp_issue_abort(struct lpfc_hba *phba,
"xri: x%x\n", ctxp->oxid);
/* No failure to an ABTS request. */
ctxp->flag &= ~LPFC_NVMET_ABORT_OP;
+ spin_unlock_irqrestore(&ctxp->ctxlock, flags);
return 0;
}
abts_wqeq = ctxp->abort_wqeq;
abts_wqe = &abts_wqeq->wqe;
ctxp->state = LPFC_NVMET_STE_ABORT;
+ spin_unlock_irqrestore(&ctxp->ctxlock, flags);
/* Announce entry to new IO submit field. */
lpfc_printf_log(phba, KERN_INFO, LOG_NVME_ABTS,
@@ -2995,7 +3046,9 @@ lpfc_nvmet_sol_fcp_issue_abort(struct lpfc_hba *phba,
"NVME Req now. hba_flag x%x oxid x%x\n",
phba->hba_flag, ctxp->oxid);
lpfc_sli_release_iocbq(phba, abts_wqeq);
+ spin_lock_irqsave(&ctxp->ctxlock, flags);
ctxp->flag &= ~LPFC_NVMET_ABORT_OP;
+ spin_unlock_irqrestore(&ctxp->ctxlock, flags);
return 0;
}
@@ -3008,7 +3061,9 @@ lpfc_nvmet_sol_fcp_issue_abort(struct lpfc_hba *phba,
"still pending on oxid x%x\n",
ctxp->oxid);
lpfc_sli_release_iocbq(phba, abts_wqeq);
+ spin_lock_irqsave(&ctxp->ctxlock, flags);
ctxp->flag &= ~LPFC_NVMET_ABORT_OP;
+ spin_unlock_irqrestore(&ctxp->ctxlock, flags);
return 0;
}
@@ -3052,7 +3107,10 @@ lpfc_nvmet_sol_fcp_issue_abort(struct lpfc_hba *phba,
abts_wqeq->iocb_flag |= LPFC_IO_NVME;
abts_wqeq->context2 = ctxp;
abts_wqeq->vport = phba->pport;
- rc = lpfc_sli4_issue_wqe(phba, LPFC_FCP_RING, abts_wqeq);
+ if (!ctxp->hdwq)
+ ctxp->hdwq = &phba->sli4_hba.hdwq[abts_wqeq->hba_wqidx];
+
+ rc = lpfc_sli4_issue_wqe(phba, ctxp->hdwq, abts_wqeq);
spin_unlock_irqrestore(&phba->hbalock, flags);
if (rc == WQE_SUCCESS) {
atomic_inc(&tgtp->xmt_abort_sol);
@@ -3060,7 +3118,9 @@ lpfc_nvmet_sol_fcp_issue_abort(struct lpfc_hba *phba,
}
atomic_inc(&tgtp->xmt_abort_rsp_error);
+ spin_lock_irqsave(&ctxp->ctxlock, flags);
ctxp->flag &= ~LPFC_NVMET_ABORT_OP;
+ spin_unlock_irqrestore(&ctxp->ctxlock, flags);
lpfc_sli_release_iocbq(phba, abts_wqeq);
lpfc_printf_log(phba, KERN_ERR, LOG_NVME_ABTS,
"6166 Failed ABORT issue_wqe with status x%x "
@@ -3069,7 +3129,6 @@ lpfc_nvmet_sol_fcp_issue_abort(struct lpfc_hba *phba,
return 1;
}
-
static int
lpfc_nvmet_unsol_fcp_issue_abort(struct lpfc_hba *phba,
struct lpfc_nvmet_rcv_ctx *ctxp,
@@ -3078,6 +3137,7 @@ lpfc_nvmet_unsol_fcp_issue_abort(struct lpfc_hba *phba,
struct lpfc_nvmet_tgtport *tgtp;
struct lpfc_iocbq *abts_wqeq;
unsigned long flags;
+ bool released = false;
int rc;
tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private;
@@ -3104,7 +3164,10 @@ lpfc_nvmet_unsol_fcp_issue_abort(struct lpfc_hba *phba,
abts_wqeq->wqe_cmpl = lpfc_nvmet_unsol_fcp_abort_cmp;
abts_wqeq->iocb_cmpl = NULL;
abts_wqeq->iocb_flag |= LPFC_IO_NVMET;
- rc = lpfc_sli4_issue_wqe(phba, LPFC_FCP_RING, abts_wqeq);
+ if (!ctxp->hdwq)
+ ctxp->hdwq = &phba->sli4_hba.hdwq[abts_wqeq->hba_wqidx];
+
+ rc = lpfc_sli4_issue_wqe(phba, ctxp->hdwq, abts_wqeq);
spin_unlock_irqrestore(&phba->hbalock, flags);
if (rc == WQE_SUCCESS) {
return 0;
@@ -3112,8 +3175,12 @@ lpfc_nvmet_unsol_fcp_issue_abort(struct lpfc_hba *phba,
aerr:
spin_lock_irqsave(&ctxp->ctxlock, flags);
- if (ctxp->flag & LPFC_NVMET_CTX_RLS)
+ if (ctxp->flag & LPFC_NVMET_CTX_RLS) {
+ spin_lock(&phba->sli4_hba.abts_nvmet_buf_list_lock);
list_del(&ctxp->list);
+ spin_unlock(&phba->sli4_hba.abts_nvmet_buf_list_lock);
+ released = true;
+ }
ctxp->flag &= ~(LPFC_NVMET_ABORT_OP | LPFC_NVMET_CTX_RLS);
spin_unlock_irqrestore(&ctxp->ctxlock, flags);
@@ -3121,7 +3188,8 @@ aerr:
lpfc_printf_log(phba, KERN_ERR, LOG_NVME_ABTS,
"6135 Failed to Issue ABTS for oxid x%x. Status x%x\n",
ctxp->oxid, rc);
- lpfc_nvmet_ctxbuf_post(phba, ctxp->ctxbuf);
+ if (released)
+ lpfc_nvmet_ctxbuf_post(phba, ctxp->ctxbuf);
return 1;
}
@@ -3173,7 +3241,7 @@ lpfc_nvmet_unsol_ls_issue_abort(struct lpfc_hba *phba,
abts_wqeq->wqe_cmpl = lpfc_nvmet_xmt_ls_abort_cmp;
abts_wqeq->iocb_cmpl = 0;
abts_wqeq->iocb_flag |= LPFC_IO_NVME_LS;
- rc = lpfc_sli4_issue_wqe(phba, LPFC_ELS_RING, abts_wqeq);
+ rc = lpfc_sli4_issue_wqe(phba, ctxp->hdwq, abts_wqeq);
spin_unlock_irqrestore(&phba->hbalock, flags);
if (rc == WQE_SUCCESS) {
atomic_inc(&tgtp->xmt_abort_unsol);
diff --git a/drivers/scsi/lpfc/lpfc_nvmet.h b/drivers/scsi/lpfc/lpfc_nvmet.h
index 0ec1082ce7ef..368deea2bcf8 100644
--- a/drivers/scsi/lpfc/lpfc_nvmet.h
+++ b/drivers/scsi/lpfc/lpfc_nvmet.h
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term *
+ * Copyright (C) 2017-2019 Broadcom. All Rights Reserved. The term *
* “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. *
* Copyright (C) 2004-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
@@ -137,9 +137,11 @@ struct lpfc_nvmet_rcv_ctx {
#define LPFC_NVMET_XBUSY 0x4 /* XB bit set on IO cmpl */
#define LPFC_NVMET_CTX_RLS 0x8 /* ctx free requested */
#define LPFC_NVMET_ABTS_RCV 0x10 /* ABTS received on exchange */
+#define LPFC_NVMET_CTX_REUSE_WQ 0x20 /* ctx reused via WQ */
#define LPFC_NVMET_DEFER_WQFULL 0x40 /* Waiting on a free WQE */
struct rqb_dmabuf *rqb_buffer;
struct lpfc_nvmet_ctxbuf *ctxbuf;
+ struct lpfc_sli4_hdw_queue *hdwq;
#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
uint64_t ts_isr_cmd;
diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c
index b4f1a840b3b4..c98f264f1d83 100644
--- a/drivers/scsi/lpfc/lpfc_scsi.c
+++ b/drivers/scsi/lpfc/lpfc_scsi.c
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term *
+ * Copyright (C) 2017-2019 Broadcom. All Rights Reserved. The term *
* “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. *
* Copyright (C) 2004-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
@@ -83,9 +83,9 @@ lpfc_rport_data_from_scsi_device(struct scsi_device *sdev)
}
static void
-lpfc_release_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb);
+lpfc_release_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_io_buf *psb);
static void
-lpfc_release_scsi_buf_s3(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb);
+lpfc_release_scsi_buf_s3(struct lpfc_hba *phba, struct lpfc_io_buf *psb);
static int
lpfc_prot_group_type(struct lpfc_hba *phba, struct scsi_cmnd *sc);
@@ -180,9 +180,9 @@ lpfc_cmd_guard_csum(struct scsi_cmnd *sc)
**/
static void
lpfc_sli4_set_rsp_sgl_last(struct lpfc_hba *phba,
- struct lpfc_scsi_buf *lpfc_cmd)
+ struct lpfc_io_buf *lpfc_cmd)
{
- struct sli4_sge *sgl = (struct sli4_sge *)lpfc_cmd->fcp_bpl;
+ struct sli4_sge *sgl = (struct sli4_sge *)lpfc_cmd->dma_sgl;
if (sgl) {
sgl += 1;
sgl->word2 = le32_to_cpu(sgl->word2);
@@ -200,7 +200,7 @@ lpfc_sli4_set_rsp_sgl_last(struct lpfc_hba *phba,
* function updates the statistical data for the command completion.
**/
static void
-lpfc_update_stats(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
+lpfc_update_stats(struct lpfc_hba *phba, struct lpfc_io_buf *lpfc_cmd)
{
struct lpfc_rport_data *rdata;
struct lpfc_nodelist *pnode;
@@ -389,12 +389,12 @@ static int
lpfc_new_scsi_buf_s3(struct lpfc_vport *vport, int num_to_alloc)
{
struct lpfc_hba *phba = vport->phba;
- struct lpfc_scsi_buf *psb;
+ struct lpfc_io_buf *psb;
struct ulp_bde64 *bpl;
IOCB_t *iocb;
dma_addr_t pdma_phys_fcp_cmd;
dma_addr_t pdma_phys_fcp_rsp;
- dma_addr_t pdma_phys_bpl;
+ dma_addr_t pdma_phys_sgl;
uint16_t iotag;
int bcnt, bpl_size;
@@ -408,7 +408,7 @@ lpfc_new_scsi_buf_s3(struct lpfc_vport *vport, int num_to_alloc)
(int)sizeof(struct fcp_rsp), bpl_size);
for (bcnt = 0; bcnt < num_to_alloc; bcnt++) {
- psb = kzalloc(sizeof(struct lpfc_scsi_buf), GFP_KERNEL);
+ psb = kzalloc(sizeof(struct lpfc_io_buf), GFP_KERNEL);
if (!psb)
break;
@@ -438,14 +438,14 @@ lpfc_new_scsi_buf_s3(struct lpfc_vport *vport, int num_to_alloc)
psb->fcp_cmnd = psb->data;
psb->fcp_rsp = psb->data + sizeof(struct fcp_cmnd);
- psb->fcp_bpl = psb->data + sizeof(struct fcp_cmnd) +
+ psb->dma_sgl = psb->data + sizeof(struct fcp_cmnd) +
sizeof(struct fcp_rsp);
/* Initialize local short-hand pointers. */
- bpl = psb->fcp_bpl;
+ bpl = (struct ulp_bde64 *)psb->dma_sgl;
pdma_phys_fcp_cmd = psb->dma_handle;
pdma_phys_fcp_rsp = psb->dma_handle + sizeof(struct fcp_cmnd);
- pdma_phys_bpl = psb->dma_handle + sizeof(struct fcp_cmnd) +
+ pdma_phys_sgl = psb->dma_handle + sizeof(struct fcp_cmnd) +
sizeof(struct fcp_rsp);
/*
@@ -496,9 +496,9 @@ lpfc_new_scsi_buf_s3(struct lpfc_vport *vport, int num_to_alloc)
iocb->un.fcpi64.bdl.bdeSize =
(2 * sizeof(struct ulp_bde64));
iocb->un.fcpi64.bdl.addrLow =
- putPaddrLow(pdma_phys_bpl);
+ putPaddrLow(pdma_phys_sgl);
iocb->un.fcpi64.bdl.addrHigh =
- putPaddrHigh(pdma_phys_bpl);
+ putPaddrHigh(pdma_phys_sgl);
iocb->ulpBdeCount = 1;
iocb->ulpLe = 1;
}
@@ -506,6 +506,7 @@ lpfc_new_scsi_buf_s3(struct lpfc_vport *vport, int num_to_alloc)
psb->status = IOSTAT_SUCCESS;
/* Put it back into the SCSI buffer list */
psb->cur_iocbq.context1 = psb;
+ spin_lock_init(&psb->buf_lock);
lpfc_release_scsi_buf_s3(phba, psb);
}
@@ -524,20 +525,27 @@ void
lpfc_sli4_vport_delete_fcp_xri_aborted(struct lpfc_vport *vport)
{
struct lpfc_hba *phba = vport->phba;
- struct lpfc_scsi_buf *psb, *next_psb;
+ struct lpfc_io_buf *psb, *next_psb;
+ struct lpfc_sli4_hdw_queue *qp;
unsigned long iflag = 0;
+ int idx;
- if (!(phba->cfg_enable_fc4_type & LPFC_ENABLE_FCP))
+ if (!(vport->cfg_enable_fc4_type & LPFC_ENABLE_FCP))
return;
+
spin_lock_irqsave(&phba->hbalock, iflag);
- spin_lock(&phba->sli4_hba.abts_scsi_buf_list_lock);
- list_for_each_entry_safe(psb, next_psb,
- &phba->sli4_hba.lpfc_abts_scsi_buf_list, list) {
- if (psb->rdata && psb->rdata->pnode
- && psb->rdata->pnode->vport == vport)
- psb->rdata = NULL;
+ for (idx = 0; idx < phba->cfg_hdw_queue; idx++) {
+ qp = &phba->sli4_hba.hdwq[idx];
+
+ spin_lock(&qp->abts_scsi_buf_list_lock);
+ list_for_each_entry_safe(psb, next_psb,
+ &qp->lpfc_abts_scsi_buf_list, list) {
+ if (psb->rdata && psb->rdata->pnode &&
+ psb->rdata->pnode->vport == vport)
+ psb->rdata = NULL;
+ }
+ spin_unlock(&qp->abts_scsi_buf_list_lock);
}
- spin_unlock(&phba->sli4_hba.abts_scsi_buf_list_lock);
spin_unlock_irqrestore(&phba->hbalock, iflag);
}
@@ -551,11 +559,12 @@ lpfc_sli4_vport_delete_fcp_xri_aborted(struct lpfc_vport *vport)
**/
void
lpfc_sli4_fcp_xri_aborted(struct lpfc_hba *phba,
- struct sli4_wcqe_xri_aborted *axri)
+ struct sli4_wcqe_xri_aborted *axri, int idx)
{
uint16_t xri = bf_get(lpfc_wcqe_xa_xri, axri);
uint16_t rxid = bf_get(lpfc_wcqe_xa_remote_xid, axri);
- struct lpfc_scsi_buf *psb, *next_psb;
+ struct lpfc_io_buf *psb, *next_psb;
+ struct lpfc_sli4_hdw_queue *qp;
unsigned long iflag = 0;
struct lpfc_iocbq *iocbq;
int i;
@@ -565,16 +574,19 @@ lpfc_sli4_fcp_xri_aborted(struct lpfc_hba *phba,
if (!(phba->cfg_enable_fc4_type & LPFC_ENABLE_FCP))
return;
+
+ qp = &phba->sli4_hba.hdwq[idx];
spin_lock_irqsave(&phba->hbalock, iflag);
- spin_lock(&phba->sli4_hba.abts_scsi_buf_list_lock);
+ spin_lock(&qp->abts_scsi_buf_list_lock);
list_for_each_entry_safe(psb, next_psb,
- &phba->sli4_hba.lpfc_abts_scsi_buf_list, list) {
+ &qp->lpfc_abts_scsi_buf_list, list) {
if (psb->cur_iocbq.sli4_xritag == xri) {
list_del(&psb->list);
+ qp->abts_scsi_io_bufs--;
psb->exch_busy = 0;
psb->status = IOSTAT_SUCCESS;
spin_unlock(
- &phba->sli4_hba.abts_scsi_buf_list_lock);
+ &qp->abts_scsi_buf_list_lock);
if (psb->rdata && psb->rdata->pnode)
ndlp = psb->rdata->pnode;
else
@@ -593,7 +605,7 @@ lpfc_sli4_fcp_xri_aborted(struct lpfc_hba *phba,
return;
}
}
- spin_unlock(&phba->sli4_hba.abts_scsi_buf_list_lock);
+ spin_unlock(&qp->abts_scsi_buf_list_lock);
for (i = 1; i <= phba->sli.last_iotag; i++) {
iocbq = phba->sli.iocbq_lookup[i];
@@ -602,7 +614,7 @@ lpfc_sli4_fcp_xri_aborted(struct lpfc_hba *phba,
continue;
if (iocbq->sli4_xritag != xri)
continue;
- psb = container_of(iocbq, struct lpfc_scsi_buf, cur_iocbq);
+ psb = container_of(iocbq, struct lpfc_io_buf, cur_iocbq);
psb->exch_busy = 0;
spin_unlock_irqrestore(&phba->hbalock, iflag);
if (!list_empty(&pring->txq))
@@ -614,359 +626,6 @@ lpfc_sli4_fcp_xri_aborted(struct lpfc_hba *phba,
}
/**
- * lpfc_sli4_post_scsi_sgl_list - Post blocks of scsi buffer sgls from a list
- * @phba: pointer to lpfc hba data structure.
- * @post_sblist: pointer to the scsi buffer list.
- *
- * This routine walks a list of scsi buffers that was passed in. It attempts
- * to construct blocks of scsi buffer sgls which contains contiguous xris and
- * uses the non-embedded SGL block post mailbox commands to post to the port.
- * For single SCSI buffer sgl with non-contiguous xri, if any, it shall use
- * embedded SGL post mailbox command for posting. The @post_sblist passed in
- * must be local list, thus no lock is needed when manipulate the list.
- *
- * Returns: 0 = failure, non-zero number of successfully posted buffers.
- **/
-static int
-lpfc_sli4_post_scsi_sgl_list(struct lpfc_hba *phba,
- struct list_head *post_sblist, int sb_count)
-{
- struct lpfc_scsi_buf *psb, *psb_next;
- int status, sgl_size;
- int post_cnt = 0, block_cnt = 0, num_posting = 0, num_posted = 0;
- dma_addr_t pdma_phys_bpl1;
- int last_xritag = NO_XRI;
- LIST_HEAD(prep_sblist);
- LIST_HEAD(blck_sblist);
- LIST_HEAD(scsi_sblist);
-
- /* sanity check */
- if (sb_count <= 0)
- return -EINVAL;
-
- sgl_size = phba->cfg_sg_dma_buf_size -
- (sizeof(struct fcp_cmnd) + sizeof(struct fcp_rsp));
-
- list_for_each_entry_safe(psb, psb_next, post_sblist, list) {
- list_del_init(&psb->list);
- block_cnt++;
- if ((last_xritag != NO_XRI) &&
- (psb->cur_iocbq.sli4_xritag != last_xritag + 1)) {
- /* a hole in xri block, form a sgl posting block */
- list_splice_init(&prep_sblist, &blck_sblist);
- post_cnt = block_cnt - 1;
- /* prepare list for next posting block */
- list_add_tail(&psb->list, &prep_sblist);
- block_cnt = 1;
- } else {
- /* prepare list for next posting block */
- list_add_tail(&psb->list, &prep_sblist);
- /* enough sgls for non-embed sgl mbox command */
- if (block_cnt == LPFC_NEMBED_MBOX_SGL_CNT) {
- list_splice_init(&prep_sblist, &blck_sblist);
- post_cnt = block_cnt;
- block_cnt = 0;
- }
- }
- num_posting++;
- last_xritag = psb->cur_iocbq.sli4_xritag;
-
- /* end of repost sgl list condition for SCSI buffers */
- if (num_posting == sb_count) {
- if (post_cnt == 0) {
- /* last sgl posting block */
- list_splice_init(&prep_sblist, &blck_sblist);
- post_cnt = block_cnt;
- } else if (block_cnt == 1) {
- /* last single sgl with non-contiguous xri */
- if (sgl_size > SGL_PAGE_SIZE)
- pdma_phys_bpl1 = psb->dma_phys_bpl +
- SGL_PAGE_SIZE;
- else
- pdma_phys_bpl1 = 0;
- status = lpfc_sli4_post_sgl(phba,
- psb->dma_phys_bpl,
- pdma_phys_bpl1,
- psb->cur_iocbq.sli4_xritag);
- if (status) {
- /* failure, put on abort scsi list */
- psb->exch_busy = 1;
- } else {
- /* success, put on SCSI buffer list */
- psb->exch_busy = 0;
- psb->status = IOSTAT_SUCCESS;
- num_posted++;
- }
- /* success, put on SCSI buffer sgl list */
- list_add_tail(&psb->list, &scsi_sblist);
- }
- }
-
- /* continue until a nembed page worth of sgls */
- if (post_cnt == 0)
- continue;
-
- /* post block of SCSI buffer list sgls */
- status = lpfc_sli4_post_scsi_sgl_block(phba, &blck_sblist,
- post_cnt);
-
- /* don't reset xirtag due to hole in xri block */
- if (block_cnt == 0)
- last_xritag = NO_XRI;
-
- /* reset SCSI buffer post count for next round of posting */
- post_cnt = 0;
-
- /* put posted SCSI buffer-sgl posted on SCSI buffer sgl list */
- while (!list_empty(&blck_sblist)) {
- list_remove_head(&blck_sblist, psb,
- struct lpfc_scsi_buf, list);
- if (status) {
- /* failure, put on abort scsi list */
- psb->exch_busy = 1;
- } else {
- /* success, put on SCSI buffer list */
- psb->exch_busy = 0;
- psb->status = IOSTAT_SUCCESS;
- num_posted++;
- }
- list_add_tail(&psb->list, &scsi_sblist);
- }
- }
- /* Push SCSI buffers with sgl posted to the availble list */
- while (!list_empty(&scsi_sblist)) {
- list_remove_head(&scsi_sblist, psb,
- struct lpfc_scsi_buf, list);
- lpfc_release_scsi_buf_s4(phba, psb);
- }
- return num_posted;
-}
-
-/**
- * lpfc_sli4_repost_scsi_sgl_list - Repost all the allocated scsi buffer sgls
- * @phba: pointer to lpfc hba data structure.
- *
- * This routine walks the list of scsi buffers that have been allocated and
- * repost them to the port by using SGL block post. This is needed after a
- * pci_function_reset/warm_start or start. The lpfc_hba_down_post_s4 routine
- * is responsible for moving all scsi buffers on the lpfc_abts_scsi_sgl_list
- * to the lpfc_scsi_buf_list. If the repost fails, reject all scsi buffers.
- *
- * Returns: 0 = success, non-zero failure.
- **/
-int
-lpfc_sli4_repost_scsi_sgl_list(struct lpfc_hba *phba)
-{
- LIST_HEAD(post_sblist);
- int num_posted, rc = 0;
-
- /* get all SCSI buffers need to repost to a local list */
- spin_lock_irq(&phba->scsi_buf_list_get_lock);
- spin_lock(&phba->scsi_buf_list_put_lock);
- list_splice_init(&phba->lpfc_scsi_buf_list_get, &post_sblist);
- list_splice(&phba->lpfc_scsi_buf_list_put, &post_sblist);
- spin_unlock(&phba->scsi_buf_list_put_lock);
- spin_unlock_irq(&phba->scsi_buf_list_get_lock);
-
- /* post the list of scsi buffer sgls to port if available */
- if (!list_empty(&post_sblist)) {
- num_posted = lpfc_sli4_post_scsi_sgl_list(phba, &post_sblist,
- phba->sli4_hba.scsi_xri_cnt);
- /* failed to post any scsi buffer, return error */
- if (num_posted == 0)
- rc = -EIO;
- }
- return rc;
-}
-
-/**
- * lpfc_new_scsi_buf_s4 - Scsi buffer allocator for HBA with SLI4 IF spec
- * @vport: The virtual port for which this call being executed.
- * @num_to_allocate: The requested number of buffers to allocate.
- *
- * This routine allocates scsi buffers for device with SLI-4 interface spec,
- * the scsi buffer contains all the necessary information needed to initiate
- * a SCSI I/O. After allocating up to @num_to_allocate SCSI buffers and put
- * them on a list, it post them to the port by using SGL block post.
- *
- * Return codes:
- * int - number of scsi buffers that were allocated and posted.
- * 0 = failure, less than num_to_alloc is a partial failure.
- **/
-static int
-lpfc_new_scsi_buf_s4(struct lpfc_vport *vport, int num_to_alloc)
-{
- struct lpfc_hba *phba = vport->phba;
- struct lpfc_scsi_buf *psb;
- struct sli4_sge *sgl;
- IOCB_t *iocb;
- dma_addr_t pdma_phys_fcp_cmd;
- dma_addr_t pdma_phys_fcp_rsp;
- dma_addr_t pdma_phys_bpl;
- uint16_t iotag, lxri = 0;
- int bcnt, num_posted, sgl_size;
- LIST_HEAD(prep_sblist);
- LIST_HEAD(post_sblist);
- LIST_HEAD(scsi_sblist);
-
- sgl_size = phba->cfg_sg_dma_buf_size -
- (sizeof(struct fcp_cmnd) + sizeof(struct fcp_rsp));
-
- lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP,
- "9068 ALLOC %d scsi_bufs: %d (%d + %d + %d)\n",
- num_to_alloc, phba->cfg_sg_dma_buf_size, sgl_size,
- (int)sizeof(struct fcp_cmnd),
- (int)sizeof(struct fcp_rsp));
-
- for (bcnt = 0; bcnt < num_to_alloc; bcnt++) {
- psb = kzalloc(sizeof(struct lpfc_scsi_buf), GFP_KERNEL);
- if (!psb)
- break;
- /*
- * Get memory from the pci pool to map the virt space to
- * pci bus space for an I/O. The DMA buffer includes space
- * for the struct fcp_cmnd, struct fcp_rsp and the number
- * of bde's necessary to support the sg_tablesize.
- */
- psb->data = dma_pool_zalloc(phba->lpfc_sg_dma_buf_pool,
- GFP_KERNEL, &psb->dma_handle);
- if (!psb->data) {
- kfree(psb);
- break;
- }
-
- /*
- * 4K Page alignment is CRITICAL to BlockGuard, double check
- * to be sure.
- */
- if ((phba->sli3_options & LPFC_SLI3_BG_ENABLED) &&
- (((unsigned long)(psb->data) &
- (unsigned long)(SLI4_PAGE_SIZE - 1)) != 0)) {
- lpfc_printf_log(phba, KERN_ERR, LOG_FCP,
- "3369 Memory alignment error "
- "addr=%lx\n",
- (unsigned long)psb->data);
- dma_pool_free(phba->lpfc_sg_dma_buf_pool,
- psb->data, psb->dma_handle);
- kfree(psb);
- break;
- }
-
-
- lxri = lpfc_sli4_next_xritag(phba);
- if (lxri == NO_XRI) {
- dma_pool_free(phba->lpfc_sg_dma_buf_pool,
- psb->data, psb->dma_handle);
- kfree(psb);
- break;
- }
-
- /* Allocate iotag for psb->cur_iocbq. */
- iotag = lpfc_sli_next_iotag(phba, &psb->cur_iocbq);
- if (iotag == 0) {
- dma_pool_free(phba->lpfc_sg_dma_buf_pool,
- psb->data, psb->dma_handle);
- kfree(psb);
- lpfc_printf_log(phba, KERN_ERR, LOG_FCP,
- "3368 Failed to allocate IOTAG for"
- " XRI:0x%x\n", lxri);
- lpfc_sli4_free_xri(phba, lxri);
- break;
- }
- psb->cur_iocbq.sli4_lxritag = lxri;
- psb->cur_iocbq.sli4_xritag = phba->sli4_hba.xri_ids[lxri];
- psb->cur_iocbq.iocb_flag |= LPFC_IO_FCP;
- psb->fcp_bpl = psb->data;
- psb->fcp_cmnd = (psb->data + sgl_size);
- psb->fcp_rsp = (struct fcp_rsp *)((uint8_t *)psb->fcp_cmnd +
- sizeof(struct fcp_cmnd));
-
- /* Initialize local short-hand pointers. */
- sgl = (struct sli4_sge *)psb->fcp_bpl;
- pdma_phys_bpl = psb->dma_handle;
- pdma_phys_fcp_cmd = (psb->dma_handle + sgl_size);
- pdma_phys_fcp_rsp = pdma_phys_fcp_cmd + sizeof(struct fcp_cmnd);
-
- /*
- * The first two bdes are the FCP_CMD and FCP_RSP.
- * The balance are sg list bdes. Initialize the
- * first two and leave the rest for queuecommand.
- */
- sgl->addr_hi = cpu_to_le32(putPaddrHigh(pdma_phys_fcp_cmd));
- sgl->addr_lo = cpu_to_le32(putPaddrLow(pdma_phys_fcp_cmd));
- sgl->word2 = le32_to_cpu(sgl->word2);
- bf_set(lpfc_sli4_sge_last, sgl, 0);
- sgl->word2 = cpu_to_le32(sgl->word2);
- sgl->sge_len = cpu_to_le32(sizeof(struct fcp_cmnd));
- sgl++;
-
- /* Setup the physical region for the FCP RSP */
- sgl->addr_hi = cpu_to_le32(putPaddrHigh(pdma_phys_fcp_rsp));
- sgl->addr_lo = cpu_to_le32(putPaddrLow(pdma_phys_fcp_rsp));
- sgl->word2 = le32_to_cpu(sgl->word2);
- bf_set(lpfc_sli4_sge_last, sgl, 1);
- sgl->word2 = cpu_to_le32(sgl->word2);
- sgl->sge_len = cpu_to_le32(sizeof(struct fcp_rsp));
-
- /*
- * Since the IOCB for the FCP I/O is built into this
- * lpfc_scsi_buf, initialize it with all known data now.
- */
- iocb = &psb->cur_iocbq.iocb;
- iocb->un.fcpi64.bdl.ulpIoTag32 = 0;
- iocb->un.fcpi64.bdl.bdeFlags = BUFF_TYPE_BDE_64;
- /* setting the BLP size to 2 * sizeof BDE may not be correct.
- * We are setting the bpl to point to out sgl. An sgl's
- * entries are 16 bytes, a bpl entries are 12 bytes.
- */
- iocb->un.fcpi64.bdl.bdeSize = sizeof(struct fcp_cmnd);
- iocb->un.fcpi64.bdl.addrLow = putPaddrLow(pdma_phys_fcp_cmd);
- iocb->un.fcpi64.bdl.addrHigh = putPaddrHigh(pdma_phys_fcp_cmd);
- iocb->ulpBdeCount = 1;
- iocb->ulpLe = 1;
- iocb->ulpClass = CLASS3;
- psb->cur_iocbq.context1 = psb;
- psb->dma_phys_bpl = pdma_phys_bpl;
-
- /* add the scsi buffer to a post list */
- list_add_tail(&psb->list, &post_sblist);
- spin_lock_irq(&phba->scsi_buf_list_get_lock);
- phba->sli4_hba.scsi_xri_cnt++;
- spin_unlock_irq(&phba->scsi_buf_list_get_lock);
- }
- lpfc_printf_log(phba, KERN_INFO, LOG_BG | LOG_FCP,
- "3021 Allocate %d out of %d requested new SCSI "
- "buffers\n", bcnt, num_to_alloc);
-
- /* post the list of scsi buffer sgls to port if available */
- if (!list_empty(&post_sblist))
- num_posted = lpfc_sli4_post_scsi_sgl_list(phba,
- &post_sblist, bcnt);
- else
- num_posted = 0;
-
- return num_posted;
-}
-
-/**
- * lpfc_new_scsi_buf - Wrapper funciton for scsi buffer allocator
- * @vport: The virtual port for which this call being executed.
- * @num_to_allocate: The requested number of buffers to allocate.
- *
- * This routine wraps the actual SCSI buffer allocator function pointer from
- * the lpfc_hba struct.
- *
- * Return codes:
- * int - number of scsi buffers that were allocated.
- * 0 = failure, less than num_to_alloc is a partial failure.
- **/
-static inline int
-lpfc_new_scsi_buf(struct lpfc_vport *vport, int num_to_alloc)
-{
- return vport->phba->lpfc_new_scsi_buf(vport, num_to_alloc);
-}
-
-/**
* lpfc_get_scsi_buf_s3 - Get a scsi buffer from lpfc_scsi_buf_list of the HBA
* @phba: The HBA for which this call is being executed.
*
@@ -977,15 +636,16 @@ lpfc_new_scsi_buf(struct lpfc_vport *vport, int num_to_alloc)
* NULL - Error
* Pointer to lpfc_scsi_buf - Success
**/
-static struct lpfc_scsi_buf*
-lpfc_get_scsi_buf_s3(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp)
+static struct lpfc_io_buf *
+lpfc_get_scsi_buf_s3(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp,
+ struct scsi_cmnd *cmnd)
{
- struct lpfc_scsi_buf * lpfc_cmd = NULL;
+ struct lpfc_io_buf *lpfc_cmd = NULL;
struct list_head *scsi_buf_list_get = &phba->lpfc_scsi_buf_list_get;
unsigned long iflag = 0;
spin_lock_irqsave(&phba->scsi_buf_list_get_lock, iflag);
- list_remove_head(scsi_buf_list_get, lpfc_cmd, struct lpfc_scsi_buf,
+ list_remove_head(scsi_buf_list_get, lpfc_cmd, struct lpfc_io_buf,
list);
if (!lpfc_cmd) {
spin_lock(&phba->scsi_buf_list_put_lock);
@@ -993,7 +653,7 @@ lpfc_get_scsi_buf_s3(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp)
&phba->lpfc_scsi_buf_list_get);
INIT_LIST_HEAD(&phba->lpfc_scsi_buf_list_put);
list_remove_head(scsi_buf_list_get, lpfc_cmd,
- struct lpfc_scsi_buf, list);
+ struct lpfc_io_buf, list);
spin_unlock(&phba->scsi_buf_list_put_lock);
}
spin_unlock_irqrestore(&phba->scsi_buf_list_get_lock, iflag);
@@ -1005,52 +665,107 @@ lpfc_get_scsi_buf_s3(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp)
return lpfc_cmd;
}
/**
- * lpfc_get_scsi_buf_s4 - Get a scsi buffer from lpfc_scsi_buf_list of the HBA
+ * lpfc_get_scsi_buf_s4 - Get a scsi buffer from io_buf_list of the HBA
* @phba: The HBA for which this call is being executed.
*
- * This routine removes a scsi buffer from head of @phba lpfc_scsi_buf_list list
+ * This routine removes a scsi buffer from head of @hdwq io_buf_list
* and returns to caller.
*
* Return codes:
* NULL - Error
* Pointer to lpfc_scsi_buf - Success
**/
-static struct lpfc_scsi_buf*
-lpfc_get_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp)
+static struct lpfc_io_buf *
+lpfc_get_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp,
+ struct scsi_cmnd *cmnd)
{
- struct lpfc_scsi_buf *lpfc_cmd, *lpfc_cmd_next;
- unsigned long iflag = 0;
- int found = 0;
+ struct lpfc_io_buf *lpfc_cmd;
+ struct lpfc_sli4_hdw_queue *qp;
+ struct sli4_sge *sgl;
+ IOCB_t *iocb;
+ dma_addr_t pdma_phys_fcp_rsp;
+ dma_addr_t pdma_phys_fcp_cmd;
+ uint32_t sgl_size, cpu, idx;
+ int tag;
- spin_lock_irqsave(&phba->scsi_buf_list_get_lock, iflag);
- list_for_each_entry_safe(lpfc_cmd, lpfc_cmd_next,
- &phba->lpfc_scsi_buf_list_get, list) {
- if (lpfc_test_rrq_active(phba, ndlp,
- lpfc_cmd->cur_iocbq.sli4_lxritag))
- continue;
- list_del_init(&lpfc_cmd->list);
- found = 1;
- break;
- }
- if (!found) {
- spin_lock(&phba->scsi_buf_list_put_lock);
- list_splice(&phba->lpfc_scsi_buf_list_put,
- &phba->lpfc_scsi_buf_list_get);
- INIT_LIST_HEAD(&phba->lpfc_scsi_buf_list_put);
- spin_unlock(&phba->scsi_buf_list_put_lock);
- list_for_each_entry_safe(lpfc_cmd, lpfc_cmd_next,
- &phba->lpfc_scsi_buf_list_get, list) {
- if (lpfc_test_rrq_active(
- phba, ndlp, lpfc_cmd->cur_iocbq.sli4_lxritag))
- continue;
- list_del_init(&lpfc_cmd->list);
- found = 1;
- break;
- }
+ cpu = smp_processor_id();
+ if (cmnd && phba->cfg_fcp_io_sched == LPFC_FCP_SCHED_BY_HDWQ) {
+ tag = blk_mq_unique_tag(cmnd->request);
+ idx = blk_mq_unique_tag_to_hwq(tag);
+ } else {
+ idx = phba->sli4_hba.cpu_map[cpu].hdwq;
}
- spin_unlock_irqrestore(&phba->scsi_buf_list_get_lock, iflag);
- if (!found)
+
+ lpfc_cmd = lpfc_get_io_buf(phba, ndlp, idx,
+ !phba->cfg_xri_rebalancing);
+ if (!lpfc_cmd) {
+ qp = &phba->sli4_hba.hdwq[idx];
+ qp->empty_io_bufs++;
return NULL;
+ }
+
+ sgl_size = phba->cfg_sg_dma_buf_size -
+ (sizeof(struct fcp_cmnd) + sizeof(struct fcp_rsp));
+
+ /* Setup key fields in buffer that may have been changed
+ * if other protocols used this buffer.
+ */
+ lpfc_cmd->cur_iocbq.iocb_flag = LPFC_IO_FCP;
+ lpfc_cmd->prot_seg_cnt = 0;
+ lpfc_cmd->seg_cnt = 0;
+ lpfc_cmd->timeout = 0;
+ lpfc_cmd->flags = 0;
+ lpfc_cmd->start_time = jiffies;
+ lpfc_cmd->waitq = NULL;
+ lpfc_cmd->cpu = cpu;
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ lpfc_cmd->prot_data_type = 0;
+#endif
+ lpfc_cmd->fcp_cmnd = (lpfc_cmd->data + sgl_size);
+ lpfc_cmd->fcp_rsp = (struct fcp_rsp *)((uint8_t *)lpfc_cmd->fcp_cmnd +
+ sizeof(struct fcp_cmnd));
+
+ /*
+ * The first two SGEs are the FCP_CMD and FCP_RSP.
+ * The balance are sg list bdes. Initialize the
+ * first two and leave the rest for queuecommand.
+ */
+ sgl = (struct sli4_sge *)lpfc_cmd->dma_sgl;
+ pdma_phys_fcp_cmd = (lpfc_cmd->dma_handle + sgl_size);
+ sgl->addr_hi = cpu_to_le32(putPaddrHigh(pdma_phys_fcp_cmd));
+ sgl->addr_lo = cpu_to_le32(putPaddrLow(pdma_phys_fcp_cmd));
+ sgl->word2 = le32_to_cpu(sgl->word2);
+ bf_set(lpfc_sli4_sge_last, sgl, 0);
+ sgl->word2 = cpu_to_le32(sgl->word2);
+ sgl->sge_len = cpu_to_le32(sizeof(struct fcp_cmnd));
+ sgl++;
+
+ /* Setup the physical region for the FCP RSP */
+ pdma_phys_fcp_rsp = pdma_phys_fcp_cmd + sizeof(struct fcp_cmnd);
+ sgl->addr_hi = cpu_to_le32(putPaddrHigh(pdma_phys_fcp_rsp));
+ sgl->addr_lo = cpu_to_le32(putPaddrLow(pdma_phys_fcp_rsp));
+ sgl->word2 = le32_to_cpu(sgl->word2);
+ bf_set(lpfc_sli4_sge_last, sgl, 1);
+ sgl->word2 = cpu_to_le32(sgl->word2);
+ sgl->sge_len = cpu_to_le32(sizeof(struct fcp_rsp));
+
+ /*
+ * Since the IOCB for the FCP I/O is built into this
+ * lpfc_io_buf, initialize it with all known data now.
+ */
+ iocb = &lpfc_cmd->cur_iocbq.iocb;
+ iocb->un.fcpi64.bdl.ulpIoTag32 = 0;
+ iocb->un.fcpi64.bdl.bdeFlags = BUFF_TYPE_BDE_64;
+ /* setting the BLP size to 2 * sizeof BDE may not be correct.
+ * We are setting the bpl to point to out sgl. An sgl's
+ * entries are 16 bytes, a bpl entries are 12 bytes.
+ */
+ iocb->un.fcpi64.bdl.bdeSize = sizeof(struct fcp_cmnd);
+ iocb->un.fcpi64.bdl.addrLow = putPaddrLow(pdma_phys_fcp_cmd);
+ iocb->un.fcpi64.bdl.addrHigh = putPaddrHigh(pdma_phys_fcp_cmd);
+ iocb->ulpBdeCount = 1;
+ iocb->ulpLe = 1;
+ iocb->ulpClass = CLASS3;
if (lpfc_ndlp_check_qdepth(phba, ndlp)) {
atomic_inc(&ndlp->cmd_pending);
@@ -1069,10 +784,11 @@ lpfc_get_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp)
* NULL - Error
* Pointer to lpfc_scsi_buf - Success
**/
-static struct lpfc_scsi_buf*
-lpfc_get_scsi_buf(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp)
+static struct lpfc_io_buf*
+lpfc_get_scsi_buf(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp,
+ struct scsi_cmnd *cmnd)
{
- return phba->lpfc_get_scsi_buf(phba, ndlp);
+ return phba->lpfc_get_scsi_buf(phba, ndlp, cmnd);
}
/**
@@ -1084,12 +800,11 @@ lpfc_get_scsi_buf(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp)
* lpfc_scsi_buf_list list.
**/
static void
-lpfc_release_scsi_buf_s3(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb)
+lpfc_release_scsi_buf_s3(struct lpfc_hba *phba, struct lpfc_io_buf *psb)
{
unsigned long iflag = 0;
psb->seg_cnt = 0;
- psb->nonsg_phys = 0;
psb->prot_seg_cnt = 0;
spin_lock_irqsave(&phba->scsi_buf_list_put_lock, iflag);
@@ -1104,34 +819,29 @@ lpfc_release_scsi_buf_s3(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb)
* @phba: The Hba for which this call is being executed.
* @psb: The scsi buffer which is being released.
*
- * This routine releases @psb scsi buffer by adding it to tail of @phba
- * lpfc_scsi_buf_list list. For SLI4 XRI's are tied to the scsi buffer
+ * This routine releases @psb scsi buffer by adding it to tail of @hdwq
+ * io_buf_list list. For SLI4 XRI's are tied to the scsi buffer
* and cannot be reused for at least RA_TOV amount of time if it was
* aborted.
**/
static void
-lpfc_release_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb)
+lpfc_release_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_io_buf *psb)
{
+ struct lpfc_sli4_hdw_queue *qp;
unsigned long iflag = 0;
psb->seg_cnt = 0;
- psb->nonsg_phys = 0;
psb->prot_seg_cnt = 0;
+ qp = psb->hdwq;
if (psb->exch_busy) {
- spin_lock_irqsave(&phba->sli4_hba.abts_scsi_buf_list_lock,
- iflag);
+ spin_lock_irqsave(&qp->abts_scsi_buf_list_lock, iflag);
psb->pCmd = NULL;
- list_add_tail(&psb->list,
- &phba->sli4_hba.lpfc_abts_scsi_buf_list);
- spin_unlock_irqrestore(&phba->sli4_hba.abts_scsi_buf_list_lock,
- iflag);
+ list_add_tail(&psb->list, &qp->lpfc_abts_scsi_buf_list);
+ qp->abts_scsi_io_bufs++;
+ spin_unlock_irqrestore(&qp->abts_scsi_buf_list_lock, iflag);
} else {
- psb->pCmd = NULL;
- psb->cur_iocbq.iocb_flag = LPFC_IO_FCP;
- spin_lock_irqsave(&phba->scsi_buf_list_put_lock, iflag);
- list_add_tail(&psb->list, &phba->lpfc_scsi_buf_list_put);
- spin_unlock_irqrestore(&phba->scsi_buf_list_put_lock, iflag);
+ lpfc_release_io_buf(phba, (struct lpfc_io_buf *)psb, qp);
}
}
@@ -1144,7 +854,7 @@ lpfc_release_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb)
* lpfc_scsi_buf_list list.
**/
static void
-lpfc_release_scsi_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb)
+lpfc_release_scsi_buf(struct lpfc_hba *phba, struct lpfc_io_buf *psb)
{
if ((psb->flags & LPFC_SBUF_BUMP_QDEPTH) && psb->ndlp)
atomic_dec(&psb->ndlp->cmd_pending);
@@ -1168,12 +878,12 @@ lpfc_release_scsi_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb)
* 0 - Success
**/
static int
-lpfc_scsi_prep_dma_buf_s3(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
+lpfc_scsi_prep_dma_buf_s3(struct lpfc_hba *phba, struct lpfc_io_buf *lpfc_cmd)
{
struct scsi_cmnd *scsi_cmnd = lpfc_cmd->pCmd;
struct scatterlist *sgel = NULL;
struct fcp_cmnd *fcp_cmnd = lpfc_cmd->fcp_cmnd;
- struct ulp_bde64 *bpl = lpfc_cmd->fcp_bpl;
+ struct ulp_bde64 *bpl = (struct ulp_bde64 *)lpfc_cmd->dma_sgl;
struct lpfc_iocbq *iocbq = &lpfc_cmd->cur_iocbq;
IOCB_t *iocb_cmd = &lpfc_cmd->cur_iocbq.iocb;
struct ulp_bde64 *data_bde = iocb_cmd->unsli3.fcp_ext.dbde;
@@ -1320,7 +1030,7 @@ lpfc_bg_err_inject(struct lpfc_hba *phba, struct scsi_cmnd *sc,
uint32_t *reftag, uint16_t *apptag, uint32_t new_guard)
{
struct scatterlist *sgpe; /* s/g prot entry */
- struct lpfc_scsi_buf *lpfc_cmd = NULL;
+ struct lpfc_io_buf *lpfc_cmd = NULL;
struct scsi_dif_tuple *src = NULL;
struct lpfc_nodelist *ndlp;
struct lpfc_rport_data *rdata;
@@ -1379,7 +1089,7 @@ lpfc_bg_err_inject(struct lpfc_hba *phba, struct scsi_cmnd *sc,
if (sgpe) {
src = (struct scsi_dif_tuple *)sg_virt(sgpe);
src += blockoff;
- lpfc_cmd = (struct lpfc_scsi_buf *)sc->host_scribble;
+ lpfc_cmd = (struct lpfc_io_buf *)sc->host_scribble;
}
/* Should we change the Reference Tag */
@@ -2684,7 +2394,7 @@ lpfc_prot_group_type(struct lpfc_hba *phba, struct scsi_cmnd *sc)
**/
static int
lpfc_bg_scsi_adjust_dl(struct lpfc_hba *phba,
- struct lpfc_scsi_buf *lpfc_cmd)
+ struct lpfc_io_buf *lpfc_cmd)
{
struct scsi_cmnd *sc = lpfc_cmd->pCmd;
int fcpdl;
@@ -2724,11 +2434,11 @@ lpfc_bg_scsi_adjust_dl(struct lpfc_hba *phba,
**/
static int
lpfc_bg_scsi_prep_dma_buf_s3(struct lpfc_hba *phba,
- struct lpfc_scsi_buf *lpfc_cmd)
+ struct lpfc_io_buf *lpfc_cmd)
{
struct scsi_cmnd *scsi_cmnd = lpfc_cmd->pCmd;
struct fcp_cmnd *fcp_cmnd = lpfc_cmd->fcp_cmnd;
- struct ulp_bde64 *bpl = lpfc_cmd->fcp_bpl;
+ struct ulp_bde64 *bpl = (struct ulp_bde64 *)lpfc_cmd->dma_sgl;
IOCB_t *iocb_cmd = &lpfc_cmd->cur_iocbq.iocb;
uint32_t num_bde = 0;
int datasegcnt, protsegcnt, datadir = scsi_cmnd->sc_data_direction;
@@ -2904,7 +2614,7 @@ lpfc_bg_csum(uint8_t *data, int count)
* what type of T10-DIF error occurred.
*/
static void
-lpfc_calc_bg_err(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
+lpfc_calc_bg_err(struct lpfc_hba *phba, struct lpfc_io_buf *lpfc_cmd)
{
struct scatterlist *sgpe; /* s/g prot entry */
struct scatterlist *sgde; /* s/g data entry */
@@ -3089,8 +2799,8 @@ out:
* -1 - Internal error (bad profile, ...etc)
*/
static int
-lpfc_parse_bg_err(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd,
- struct lpfc_iocbq *pIocbOut)
+lpfc_parse_bg_err(struct lpfc_hba *phba, struct lpfc_io_buf *lpfc_cmd,
+ struct lpfc_iocbq *pIocbOut)
{
struct scsi_cmnd *cmd = lpfc_cmd->pCmd;
struct sli3_bg_fields *bgf = &pIocbOut->iocb.unsli3.sli3_bg;
@@ -3256,12 +2966,12 @@ out:
* 0 - Success
**/
static int
-lpfc_scsi_prep_dma_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
+lpfc_scsi_prep_dma_buf_s4(struct lpfc_hba *phba, struct lpfc_io_buf *lpfc_cmd)
{
struct scsi_cmnd *scsi_cmnd = lpfc_cmd->pCmd;
struct scatterlist *sgel = NULL;
struct fcp_cmnd *fcp_cmnd = lpfc_cmd->fcp_cmnd;
- struct sli4_sge *sgl = (struct sli4_sge *)lpfc_cmd->fcp_bpl;
+ struct sli4_sge *sgl = (struct sli4_sge *)lpfc_cmd->dma_sgl;
struct sli4_sge *first_data_sgl;
IOCB_t *iocb_cmd = &lpfc_cmd->cur_iocbq.iocb;
dma_addr_t physaddr;
@@ -3388,6 +3098,7 @@ lpfc_scsi_prep_dma_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
lpfc_cmd->cur_iocbq.priority = ((struct lpfc_device_data *)
scsi_cmnd->device->hostdata)->priority;
}
+
return 0;
}
@@ -3402,11 +3113,11 @@ lpfc_scsi_prep_dma_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
**/
static int
lpfc_bg_scsi_prep_dma_buf_s4(struct lpfc_hba *phba,
- struct lpfc_scsi_buf *lpfc_cmd)
+ struct lpfc_io_buf *lpfc_cmd)
{
struct scsi_cmnd *scsi_cmnd = lpfc_cmd->pCmd;
struct fcp_cmnd *fcp_cmnd = lpfc_cmd->fcp_cmnd;
- struct sli4_sge *sgl = (struct sli4_sge *)(lpfc_cmd->fcp_bpl);
+ struct sli4_sge *sgl = (struct sli4_sge *)(lpfc_cmd->dma_sgl);
IOCB_t *iocb_cmd = &lpfc_cmd->cur_iocbq.iocb;
uint32_t num_sge = 0;
int datasegcnt, protsegcnt, datadir = scsi_cmnd->sc_data_direction;
@@ -3578,7 +3289,7 @@ err:
* 0 - Success
**/
static inline int
-lpfc_scsi_prep_dma_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
+lpfc_scsi_prep_dma_buf(struct lpfc_hba *phba, struct lpfc_io_buf *lpfc_cmd)
{
return phba->lpfc_scsi_prep_dma_buf(phba, lpfc_cmd);
}
@@ -3597,7 +3308,7 @@ lpfc_scsi_prep_dma_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
* 0 - Success
**/
static inline int
-lpfc_bg_scsi_prep_dma_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
+lpfc_bg_scsi_prep_dma_buf(struct lpfc_hba *phba, struct lpfc_io_buf *lpfc_cmd)
{
return phba->lpfc_bg_scsi_prep_dma_buf(phba, lpfc_cmd);
}
@@ -3614,7 +3325,7 @@ lpfc_bg_scsi_prep_dma_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
**/
static void
lpfc_send_scsi_error_event(struct lpfc_hba *phba, struct lpfc_vport *vport,
- struct lpfc_scsi_buf *lpfc_cmd, struct lpfc_iocbq *rsp_iocb) {
+ struct lpfc_io_buf *lpfc_cmd, struct lpfc_iocbq *rsp_iocb) {
struct scsi_cmnd *cmnd = lpfc_cmd->pCmd;
struct fcp_rsp *fcprsp = lpfc_cmd->fcp_rsp;
uint32_t resp_info = fcprsp->rspStatus2;
@@ -3706,7 +3417,7 @@ lpfc_send_scsi_error_event(struct lpfc_hba *phba, struct lpfc_vport *vport,
* field of @lpfc_cmd for device with SLI-3 interface spec.
**/
static void
-lpfc_scsi_unprep_dma_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb)
+lpfc_scsi_unprep_dma_buf(struct lpfc_hba *phba, struct lpfc_io_buf *psb)
{
/*
* There are only two special cases to consider. (1) the scsi command
@@ -3725,7 +3436,7 @@ lpfc_scsi_unprep_dma_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb)
/**
* lpfc_handler_fcp_err - FCP response handler
* @vport: The virtual port for which this call is being executed.
- * @lpfc_cmd: Pointer to lpfc_scsi_buf data structure.
+ * @lpfc_cmd: Pointer to lpfc_io_buf data structure.
* @rsp_iocb: The response IOCB which contains FCP error.
*
* This routine is called to process response IOCB with status field
@@ -3733,7 +3444,7 @@ lpfc_scsi_unprep_dma_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb)
* based upon SCSI and FCP error.
**/
static void
-lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
+lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_io_buf *lpfc_cmd,
struct lpfc_iocbq *rsp_iocb)
{
struct lpfc_hba *phba = vport->phba;
@@ -3912,49 +3623,6 @@ lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
}
/**
- * lpfc_sli4_scmd_to_wqidx_distr - scsi command to SLI4 WQ index distribution
- * @phba: Pointer to HBA context object.
- *
- * This routine performs a roundrobin SCSI command to SLI4 FCP WQ index
- * distribution. This is called by __lpfc_sli_issue_iocb_s4() with the hbalock
- * held.
- * If scsi-mq is enabled, get the default block layer mapping of software queues
- * to hardware queues. This information is saved in request tag.
- *
- * Return: index into SLI4 fast-path FCP queue index.
- **/
-int lpfc_sli4_scmd_to_wqidx_distr(struct lpfc_hba *phba,
- struct lpfc_scsi_buf *lpfc_cmd)
-{
- struct scsi_cmnd *cmnd = lpfc_cmd->pCmd;
- struct lpfc_vector_map_info *cpup;
- int chann, cpu;
- uint32_t tag;
- uint16_t hwq;
-
- if (cmnd) {
- tag = blk_mq_unique_tag(cmnd->request);
- hwq = blk_mq_unique_tag_to_hwq(tag);
-
- return hwq;
- }
-
- if (phba->cfg_fcp_io_sched == LPFC_FCP_SCHED_BY_CPU
- && phba->cfg_fcp_io_channel > 1) {
- cpu = smp_processor_id();
- if (cpu < phba->sli4_hba.num_present_cpu) {
- cpup = phba->sli4_hba.cpu_map;
- cpup += cpu;
- return cpup->channel_id;
- }
- }
- chann = atomic_add_return(1, &phba->fcp_qidx);
- chann = chann % phba->cfg_fcp_io_channel;
- return chann;
-}
-
-
-/**
* lpfc_scsi_cmd_iocb_cmpl - Scsi cmnd IOCB completion routine
* @phba: The Hba for which this call is being executed.
* @pIocbIn: The command IOCBQ for the scsi cmnd.
@@ -3968,8 +3636,8 @@ static void
lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
struct lpfc_iocbq *pIocbOut)
{
- struct lpfc_scsi_buf *lpfc_cmd =
- (struct lpfc_scsi_buf *) pIocbIn->context1;
+ struct lpfc_io_buf *lpfc_cmd =
+ (struct lpfc_io_buf *) pIocbIn->context1;
struct lpfc_vport *vport = pIocbIn->vport;
struct lpfc_rport_data *rdata = lpfc_cmd->rdata;
struct lpfc_nodelist *pnode = rdata->pnode;
@@ -3977,14 +3645,35 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
unsigned long flags;
struct lpfc_fast_path_event *fast_path_evt;
struct Scsi_Host *shost;
+ int idx;
uint32_t logit = LOG_FCP;
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ int cpu;
+#endif
- atomic_inc(&phba->fc4ScsiIoCmpls);
+ /* Guard against abort handler being called at same time */
+ spin_lock(&lpfc_cmd->buf_lock);
/* Sanity check on return of outstanding command */
cmd = lpfc_cmd->pCmd;
- if (!cmd)
+ if (!cmd) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
+ "2621 IO completion: Not an active IO\n");
+ spin_unlock(&lpfc_cmd->buf_lock);
return;
+ }
+
+ idx = lpfc_cmd->cur_iocbq.hba_wqidx;
+ if (phba->sli4_hba.hdwq)
+ phba->sli4_hba.hdwq[idx].scsi_cstat.io_cmpls++;
+
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ if (phba->cpucheck_on & LPFC_CHECK_SCSI_IO) {
+ cpu = smp_processor_id();
+ if (cpu < LPFC_CHECK_CPU_CNT)
+ phba->sli4_hba.hdwq[idx].cpucheck_cmpl_io[cpu]++;
+ }
+#endif
shost = cmd->device->host;
lpfc_cmd->result = (pIocbOut->iocb.un.ulpWord[4] & IOERR_PARAM_MASK);
@@ -4178,29 +3867,24 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
}
lpfc_scsi_unprep_dma_buf(phba, lpfc_cmd);
- /* If pCmd was set to NULL from abort path, do not call scsi_done */
- if (xchg(&lpfc_cmd->pCmd, NULL) == NULL) {
- lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP,
- "5688 FCP cmd already NULL, sid: 0x%06x, "
- "did: 0x%06x, oxid: 0x%04x\n",
- vport->fc_myDID,
- (pnode) ? pnode->nlp_DID : 0,
- phba->sli_rev == LPFC_SLI_REV4 ?
- lpfc_cmd->cur_iocbq.sli4_xritag : 0xffff);
- return;
- }
+ lpfc_cmd->pCmd = NULL;
+ spin_unlock(&lpfc_cmd->buf_lock);
/* The sdev is not guaranteed to be valid post scsi_done upcall. */
cmd->scsi_done(cmd);
/*
- * If there is a thread waiting for command completion
+ * If there is an abort thread waiting for command completion
* wake up the thread.
*/
- spin_lock_irqsave(shost->host_lock, flags);
- if (lpfc_cmd->waitq)
- wake_up(lpfc_cmd->waitq);
- spin_unlock_irqrestore(shost->host_lock, flags);
+ spin_lock(&lpfc_cmd->buf_lock);
+ if (unlikely(lpfc_cmd->cur_iocbq.iocb_flag & LPFC_DRIVER_ABORTED)) {
+ lpfc_cmd->cur_iocbq.iocb_flag &= ~LPFC_DRIVER_ABORTED;
+ if (lpfc_cmd->waitq)
+ wake_up(lpfc_cmd->waitq);
+ lpfc_cmd->waitq = NULL;
+ }
+ spin_unlock(&lpfc_cmd->buf_lock);
lpfc_release_scsi_buf(phba, lpfc_cmd);
}
@@ -4233,7 +3917,7 @@ lpfc_fcpcmd_to_iocb(uint8_t *data, struct fcp_cmnd *fcp_cmnd)
* to transfer for device with SLI3 interface spec.
**/
static void
-lpfc_scsi_prep_cmnd(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
+lpfc_scsi_prep_cmnd(struct lpfc_vport *vport, struct lpfc_io_buf *lpfc_cmd,
struct lpfc_nodelist *pnode)
{
struct lpfc_hba *phba = vport->phba;
@@ -4241,7 +3925,9 @@ lpfc_scsi_prep_cmnd(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
struct fcp_cmnd *fcp_cmnd = lpfc_cmd->fcp_cmnd;
IOCB_t *iocb_cmd = &lpfc_cmd->cur_iocbq.iocb;
struct lpfc_iocbq *piocbq = &(lpfc_cmd->cur_iocbq);
+ struct lpfc_sli4_hdw_queue *hdwq = NULL;
int datadir = scsi_cmnd->sc_data_direction;
+ int idx;
uint8_t *ptr;
bool sli4;
uint32_t fcpdl;
@@ -4267,6 +3953,9 @@ lpfc_scsi_prep_cmnd(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
sli4 = (phba->sli_rev == LPFC_SLI_REV4);
piocbq->iocb.un.fcpi.fcpi_XRdy = 0;
+ idx = lpfc_cmd->hdwq_no;
+ if (phba->sli4_hba.hdwq)
+ hdwq = &phba->sli4_hba.hdwq[idx];
/*
* There are three possibilities here - use scatter-gather segment, use
@@ -4288,19 +3977,22 @@ lpfc_scsi_prep_cmnd(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
vport->cfg_first_burst_size;
}
fcp_cmnd->fcpCntl3 = WRITE_DATA;
- atomic_inc(&phba->fc4ScsiOutputRequests);
+ if (hdwq)
+ hdwq->scsi_cstat.output_requests++;
} else {
iocb_cmd->ulpCommand = CMD_FCP_IREAD64_CR;
iocb_cmd->ulpPU = PARM_READ_CHECK;
fcp_cmnd->fcpCntl3 = READ_DATA;
- atomic_inc(&phba->fc4ScsiInputRequests);
+ if (hdwq)
+ hdwq->scsi_cstat.input_requests++;
}
} else {
iocb_cmd->ulpCommand = CMD_FCP_ICMND64_CR;
iocb_cmd->un.fcpi.fcpi_parm = 0;
iocb_cmd->ulpPU = 0;
fcp_cmnd->fcpCntl3 = 0;
- atomic_inc(&phba->fc4ScsiControlRequests);
+ if (hdwq)
+ hdwq->scsi_cstat.control_requests++;
}
if (phba->sli_rev == 3 &&
!(phba->sli3_options & LPFC_SLI3_BG_ENABLED))
@@ -4328,7 +4020,7 @@ lpfc_scsi_prep_cmnd(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
/**
* lpfc_scsi_prep_task_mgmt_cmd - Convert SLI3 scsi TM cmd to FCP info unit
* @vport: The virtual port for which this call is being executed.
- * @lpfc_cmd: Pointer to lpfc_scsi_buf data structure.
+ * @lpfc_cmd: Pointer to lpfc_io_buf data structure.
* @lun: Logical unit number.
* @task_mgmt_cmd: SCSI task management command.
*
@@ -4341,7 +4033,7 @@ lpfc_scsi_prep_cmnd(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
**/
static int
lpfc_scsi_prep_task_mgmt_cmd(struct lpfc_vport *vport,
- struct lpfc_scsi_buf *lpfc_cmd,
+ struct lpfc_io_buf *lpfc_cmd,
uint64_t lun,
uint8_t task_mgmt_cmd)
{
@@ -4413,14 +4105,12 @@ lpfc_scsi_api_table_setup(struct lpfc_hba *phba, uint8_t dev_grp)
switch (dev_grp) {
case LPFC_PCI_DEV_LP:
- phba->lpfc_new_scsi_buf = lpfc_new_scsi_buf_s3;
phba->lpfc_scsi_prep_dma_buf = lpfc_scsi_prep_dma_buf_s3;
phba->lpfc_bg_scsi_prep_dma_buf = lpfc_bg_scsi_prep_dma_buf_s3;
phba->lpfc_release_scsi_buf = lpfc_release_scsi_buf_s3;
phba->lpfc_get_scsi_buf = lpfc_get_scsi_buf_s3;
break;
case LPFC_PCI_DEV_OC:
- phba->lpfc_new_scsi_buf = lpfc_new_scsi_buf_s4;
phba->lpfc_scsi_prep_dma_buf = lpfc_scsi_prep_dma_buf_s4;
phba->lpfc_bg_scsi_prep_dma_buf = lpfc_bg_scsi_prep_dma_buf_s4;
phba->lpfc_release_scsi_buf = lpfc_release_scsi_buf_s4;
@@ -4452,8 +4142,8 @@ lpfc_tskmgmt_def_cmpl(struct lpfc_hba *phba,
struct lpfc_iocbq *cmdiocbq,
struct lpfc_iocbq *rspiocbq)
{
- struct lpfc_scsi_buf *lpfc_cmd =
- (struct lpfc_scsi_buf *) cmdiocbq->context1;
+ struct lpfc_io_buf *lpfc_cmd =
+ (struct lpfc_io_buf *) cmdiocbq->context1;
if (lpfc_cmd)
lpfc_release_scsi_buf(phba, lpfc_cmd);
return;
@@ -4652,9 +4342,12 @@ lpfc_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *cmnd)
struct lpfc_hba *phba = vport->phba;
struct lpfc_rport_data *rdata;
struct lpfc_nodelist *ndlp;
- struct lpfc_scsi_buf *lpfc_cmd;
+ struct lpfc_io_buf *lpfc_cmd;
struct fc_rport *rport = starget_to_rport(scsi_target(cmnd->device));
- int err;
+ int err, idx;
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ int cpu;
+#endif
rdata = lpfc_rport_data_from_scsi_device(cmnd->device);
@@ -4718,7 +4411,7 @@ lpfc_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *cmnd)
}
}
- lpfc_cmd = lpfc_get_scsi_buf(phba, ndlp);
+ lpfc_cmd = lpfc_get_scsi_buf(phba, ndlp, cmnd);
if (lpfc_cmd == NULL) {
lpfc_rampdown_queue_depth(phba);
@@ -4735,8 +4428,6 @@ lpfc_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *cmnd)
lpfc_cmd->pCmd = cmnd;
lpfc_cmd->rdata = rdata;
lpfc_cmd->ndlp = ndlp;
- lpfc_cmd->timeout = 0;
- lpfc_cmd->start_time = jiffies;
cmnd->host_scribble = (unsigned char *)lpfc_cmd;
if (scsi_get_prot_op(cmnd) != SCSI_PROT_NORMAL) {
@@ -4771,6 +4462,16 @@ lpfc_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *cmnd)
lpfc_scsi_prep_cmnd(vport, lpfc_cmd, ndlp);
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ if (phba->cpucheck_on & LPFC_CHECK_SCSI_IO) {
+ cpu = smp_processor_id();
+ if (cpu < LPFC_CHECK_CPU_CNT) {
+ struct lpfc_sli4_hdw_queue *hdwq =
+ &phba->sli4_hba.hdwq[lpfc_cmd->hdwq_no];
+ hdwq->cpucheck_xmt_io[cpu]++;
+ }
+ }
+#endif
err = lpfc_sli_issue_iocb(phba, LPFC_FCP_RING,
&lpfc_cmd->cur_iocbq, SLI_IOCB_RET_IOCB);
if (err) {
@@ -4791,16 +4492,6 @@ lpfc_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *cmnd)
(uint32_t)
(cmnd->request->timeout / 1000));
- switch (lpfc_cmd->fcp_cmnd->fcpCntl3) {
- case WRITE_DATA:
- atomic_dec(&phba->fc4ScsiOutputRequests);
- break;
- case READ_DATA:
- atomic_dec(&phba->fc4ScsiInputRequests);
- break;
- default:
- atomic_dec(&phba->fc4ScsiControlRequests);
- }
goto out_host_busy_free_buf;
}
if (phba->cfg_poll & ENABLE_FCP_RING_POLLING) {
@@ -4811,10 +4502,26 @@ lpfc_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *cmnd)
lpfc_poll_rearm_timer(phba);
}
+ if (phba->cfg_xri_rebalancing)
+ lpfc_keep_pvt_pool_above_lowwm(phba, lpfc_cmd->hdwq_no);
+
return 0;
out_host_busy_free_buf:
+ idx = lpfc_cmd->hdwq_no;
lpfc_scsi_unprep_dma_buf(phba, lpfc_cmd);
+ if (phba->sli4_hba.hdwq) {
+ switch (lpfc_cmd->fcp_cmnd->fcpCntl3) {
+ case WRITE_DATA:
+ phba->sli4_hba.hdwq[idx].scsi_cstat.output_requests--;
+ break;
+ case READ_DATA:
+ phba->sli4_hba.hdwq[idx].scsi_cstat.input_requests--;
+ break;
+ default:
+ phba->sli4_hba.hdwq[idx].scsi_cstat.control_requests--;
+ }
+ }
lpfc_release_scsi_buf(phba, lpfc_cmd);
out_host_busy:
return SCSI_MLQUEUE_HOST_BUSY;
@@ -4846,7 +4553,7 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd)
struct lpfc_hba *phba = vport->phba;
struct lpfc_iocbq *iocb;
struct lpfc_iocbq *abtsiocb;
- struct lpfc_scsi_buf *lpfc_cmd;
+ struct lpfc_io_buf *lpfc_cmd;
IOCB_t *cmd, *icmd;
int ret = SUCCESS, status = 0;
struct lpfc_sli_ring *pring_s4 = NULL;
@@ -4858,65 +4565,59 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd)
if (status != 0 && status != SUCCESS)
return status;
+ lpfc_cmd = (struct lpfc_io_buf *)cmnd->host_scribble;
+ if (!lpfc_cmd)
+ return ret;
+
spin_lock_irqsave(&phba->hbalock, flags);
/* driver queued commands are in process of being flushed */
if (phba->hba_flag & HBA_FCP_IOQ_FLUSH) {
- spin_unlock_irqrestore(&phba->hbalock, flags);
lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP,
"3168 SCSI Layer abort requested I/O has been "
"flushed by LLD.\n");
- return FAILED;
+ ret = FAILED;
+ goto out_unlock;
}
- lpfc_cmd = (struct lpfc_scsi_buf *)cmnd->host_scribble;
- if (!lpfc_cmd || !lpfc_cmd->pCmd) {
- spin_unlock_irqrestore(&phba->hbalock, flags);
+ /* Guard against IO completion being called at same time */
+ spin_lock(&lpfc_cmd->buf_lock);
+
+ if (!lpfc_cmd->pCmd) {
lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP,
"2873 SCSI Layer I/O Abort Request IO CMPL Status "
"x%x ID %d LUN %llu\n",
SUCCESS, cmnd->device->id, cmnd->device->lun);
- return SUCCESS;
+ goto out_unlock_buf;
}
iocb = &lpfc_cmd->cur_iocbq;
if (phba->sli_rev == LPFC_SLI_REV4) {
- if (!(phba->cfg_fof) ||
- (!(iocb->iocb_flag & LPFC_IO_FOF))) {
- pring_s4 =
- phba->sli4_hba.fcp_wq[iocb->hba_wqidx]->pring;
- } else {
- iocb->hba_wqidx = 0;
- pring_s4 = phba->sli4_hba.oas_wq->pring;
- }
+ pring_s4 = phba->sli4_hba.hdwq[iocb->hba_wqidx].fcp_wq->pring;
if (!pring_s4) {
ret = FAILED;
- goto out_unlock;
+ goto out_unlock_buf;
}
spin_lock(&pring_s4->ring_lock);
}
/* the command is in process of being cancelled */
if (!(iocb->iocb_flag & LPFC_IO_ON_TXCMPLQ)) {
- if (phba->sli_rev == LPFC_SLI_REV4)
- spin_unlock(&pring_s4->ring_lock);
- spin_unlock_irqrestore(&phba->hbalock, flags);
lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP,
"3169 SCSI Layer abort requested I/O has been "
"cancelled by LLD.\n");
- return FAILED;
+ ret = FAILED;
+ goto out_unlock_ring;
}
/*
- * If pCmd field of the corresponding lpfc_scsi_buf structure
+ * If pCmd field of the corresponding lpfc_io_buf structure
* points to a different SCSI command, then the driver has
* already completed this command, but the midlayer did not
* see the completion before the eh fired. Just return SUCCESS.
*/
if (lpfc_cmd->pCmd != cmnd) {
- if (phba->sli_rev == LPFC_SLI_REV4)
- spin_unlock(&pring_s4->ring_lock);
lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP,
"3170 SCSI Layer abort requested I/O has been "
"completed by LLD.\n");
- goto out_unlock;
+ goto out_unlock_ring;
}
BUG_ON(iocb->context1 != lpfc_cmd);
@@ -4927,6 +4628,7 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd)
"3389 SCSI Layer I/O Abort Request is pending\n");
if (phba->sli_rev == LPFC_SLI_REV4)
spin_unlock(&pring_s4->ring_lock);
+ spin_unlock(&lpfc_cmd->buf_lock);
spin_unlock_irqrestore(&phba->hbalock, flags);
goto wait_for_cmpl;
}
@@ -4934,9 +4636,7 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd)
abtsiocb = __lpfc_sli_get_iocbq(phba);
if (abtsiocb == NULL) {
ret = FAILED;
- if (phba->sli_rev == LPFC_SLI_REV4)
- spin_unlock(&pring_s4->ring_lock);
- goto out_unlock;
+ goto out_unlock_ring;
}
/* Indicate the IO is being aborted by the driver. */
@@ -4986,24 +4686,18 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd)
/* no longer need the lock after this point */
spin_unlock_irqrestore(&phba->hbalock, flags);
-
if (ret_val == IOCB_ERROR) {
- if (phba->sli_rev == LPFC_SLI_REV4)
- spin_lock_irqsave(&pring_s4->ring_lock, flags);
- else
- spin_lock_irqsave(&phba->hbalock, flags);
/* Indicate the IO is not being aborted by the driver. */
iocb->iocb_flag &= ~LPFC_DRIVER_ABORTED;
lpfc_cmd->waitq = NULL;
- if (phba->sli_rev == LPFC_SLI_REV4)
- spin_unlock_irqrestore(&pring_s4->ring_lock, flags);
- else
- spin_unlock_irqrestore(&phba->hbalock, flags);
+ spin_unlock(&lpfc_cmd->buf_lock);
lpfc_sli_release_iocbq(phba, abtsiocb);
ret = FAILED;
goto out;
}
+ spin_unlock(&lpfc_cmd->buf_lock);
+
if (phba->cfg_poll & DISABLE_FCP_RING_INT)
lpfc_sli_handle_fast_ring_event(phba,
&phba->sli.sli3_ring[LPFC_FCP_RING], HA_R0RE_REQ);
@@ -5014,9 +4708,7 @@ wait_for_cmpl:
(lpfc_cmd->pCmd != cmnd),
msecs_to_jiffies(2*vport->cfg_devloss_tmo*1000));
- spin_lock_irqsave(shost->host_lock, flags);
- lpfc_cmd->waitq = NULL;
- spin_unlock_irqrestore(shost->host_lock, flags);
+ spin_lock(&lpfc_cmd->buf_lock);
if (lpfc_cmd->pCmd == cmnd) {
ret = FAILED;
@@ -5027,8 +4719,14 @@ wait_for_cmpl:
iocb->sli4_xritag, ret,
cmnd->device->id, cmnd->device->lun);
}
+ spin_unlock(&lpfc_cmd->buf_lock);
goto out;
+out_unlock_ring:
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ spin_unlock(&pring_s4->ring_lock);
+out_unlock_buf:
+ spin_unlock(&lpfc_cmd->buf_lock);
out_unlock:
spin_unlock_irqrestore(&phba->hbalock, flags);
out:
@@ -5066,7 +4764,7 @@ lpfc_taskmgmt_name(uint8_t task_mgmt_cmd)
/**
* lpfc_check_fcp_rsp - check the returned fcp_rsp to see if task failed
* @vport: The virtual port for which this call is being executed.
- * @lpfc_cmd: Pointer to lpfc_scsi_buf data structure.
+ * @lpfc_cmd: Pointer to lpfc_io_buf data structure.
*
* This routine checks the FCP RSP INFO to see if the tsk mgmt command succeded
*
@@ -5075,7 +4773,7 @@ lpfc_taskmgmt_name(uint8_t task_mgmt_cmd)
* 0x2002 - Success
**/
static int
-lpfc_check_fcp_rsp(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd)
+lpfc_check_fcp_rsp(struct lpfc_vport *vport, struct lpfc_io_buf *lpfc_cmd)
{
struct fcp_rsp *fcprsp = lpfc_cmd->fcp_rsp;
uint32_t rsp_info;
@@ -5150,7 +4848,7 @@ lpfc_send_taskmgmt(struct lpfc_vport *vport, struct scsi_cmnd *cmnd,
uint8_t task_mgmt_cmd)
{
struct lpfc_hba *phba = vport->phba;
- struct lpfc_scsi_buf *lpfc_cmd;
+ struct lpfc_io_buf *lpfc_cmd;
struct lpfc_iocbq *iocbq;
struct lpfc_iocbq *iocbqrsp;
struct lpfc_rport_data *rdata;
@@ -5163,7 +4861,7 @@ lpfc_send_taskmgmt(struct lpfc_vport *vport, struct scsi_cmnd *cmnd,
return FAILED;
pnode = rdata->pnode;
- lpfc_cmd = lpfc_get_scsi_buf(phba, pnode);
+ lpfc_cmd = lpfc_get_scsi_buf(phba, pnode, NULL);
if (lpfc_cmd == NULL)
return FAILED;
lpfc_cmd->timeout = phba->cfg_task_mgmt_tmo;
@@ -5671,6 +5369,12 @@ lpfc_slave_alloc(struct scsi_device *sdev)
}
sdev_cnt = atomic_inc_return(&phba->sdev_cnt);
+ /* For SLI4, all IO buffers are pre-allocated */
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ return 0;
+
+ /* This code path is now ONLY for SLI3 adapters */
+
/*
* Populate the cmds_per_lun count scsi_bufs into this host's globally
* available list of scsi buffers. Don't allocate more than the
@@ -5702,7 +5406,7 @@ lpfc_slave_alloc(struct scsi_device *sdev)
(phba->cfg_hba_queue_depth - total));
num_to_alloc = phba->cfg_hba_queue_depth - total;
}
- num_allocated = lpfc_new_scsi_buf(vport, num_to_alloc);
+ num_allocated = lpfc_new_scsi_buf_s3(vport, num_to_alloc);
if (num_to_alloc != num_allocated) {
lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP,
"0708 Allocation request of %d "
diff --git a/drivers/scsi/lpfc/lpfc_scsi.h b/drivers/scsi/lpfc/lpfc_scsi.h
index b759b089432c..f76667b7da7b 100644
--- a/drivers/scsi/lpfc/lpfc_scsi.h
+++ b/drivers/scsi/lpfc/lpfc_scsi.h
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term *
+ * Copyright (C) 2017-2019 Broadcom. All Rights Reserved. The term *
* “Broadcom” refers to Broadcom Inc and/or its subsidiaries. *
* Copyright (C) 2004-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
@@ -130,62 +130,6 @@ struct lpfc_scsicmd_bkt {
uint32_t cmd_count;
};
-struct lpfc_scsi_buf {
- struct list_head list;
- struct scsi_cmnd *pCmd;
- struct lpfc_rport_data *rdata;
- struct lpfc_nodelist *ndlp;
-
- uint32_t timeout;
-
- uint16_t flags; /* TBD convert exch_busy to flags */
-#define LPFC_SBUF_XBUSY 0x1 /* SLI4 hba reported XB on WCQE cmpl */
-#define LPFC_SBUF_BUMP_QDEPTH 0x8 /* bumped queue depth counter */
- uint16_t exch_busy; /* SLI4 hba reported XB on complete WCQE */
- uint16_t status; /* From IOCB Word 7- ulpStatus */
- uint32_t result; /* From IOCB Word 4. */
-
- uint32_t seg_cnt; /* Number of scatter-gather segments returned by
- * dma_map_sg. The driver needs this for calls
- * to dma_unmap_sg. */
- uint32_t prot_seg_cnt; /* seg_cnt's counterpart for protection data */
-
- dma_addr_t nonsg_phys; /* Non scatter-gather physical address. */
-
- /*
- * data and dma_handle are the kernel virtual and bus address of the
- * dma-able buffer containing the fcp_cmd, fcp_rsp and a scatter
- * gather bde list that supports the sg_tablesize value.
- */
- void *data;
- dma_addr_t dma_handle;
-
- struct fcp_cmnd *fcp_cmnd;
- struct fcp_rsp *fcp_rsp;
- struct ulp_bde64 *fcp_bpl;
-
- dma_addr_t dma_phys_bpl;
-
- /* cur_iocbq has phys of the dma-able buffer.
- * Iotag is in here
- */
- struct lpfc_iocbq cur_iocbq;
- uint16_t cpu;
-
- wait_queue_head_t *waitq;
- unsigned long start_time;
-
-#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
- /* Used to restore any changes to protection data for error injection */
- void *prot_data_segment;
- uint32_t prot_data;
- uint32_t prot_data_type;
-#define LPFC_INJERR_REFTAG 1
-#define LPFC_INJERR_APPTAG 2
-#define LPFC_INJERR_GUARD 3
-#endif
-};
-
#define LPFC_SCSI_DMA_EXT_SIZE 264
#define LPFC_BPL_SIZE 1024
#define MDAC_DIRECT_CMD 0x22
@@ -200,5 +144,6 @@ struct lpfc_scsi_buf {
#define TXRDY_PAYLOAD_LEN 12
-int lpfc_sli4_scmd_to_wqidx_distr(struct lpfc_hba *phba,
- struct lpfc_scsi_buf *lpfc_cmd);
+/* For sysfs/debugfs tmp string max len */
+#define LPFC_MAX_SCSI_INFO_TMP_LEN 79
+
diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c
index 2242e9b3ca12..57b4a463b589 100644
--- a/drivers/scsi/lpfc/lpfc_sli.c
+++ b/drivers/scsi/lpfc/lpfc_sli.c
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term *
+ * Copyright (C) 2017-2019 Broadcom. All Rights Reserved. The term *
* “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. *
* Copyright (C) 2004-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
@@ -78,12 +78,13 @@ static void lpfc_sli4_send_seq_to_ulp(struct lpfc_vport *,
struct hbq_dmabuf *);
static void lpfc_sli4_handle_mds_loopback(struct lpfc_vport *vport,
struct hbq_dmabuf *dmabuf);
-static int lpfc_sli4_fp_handle_cqe(struct lpfc_hba *, struct lpfc_queue *,
- struct lpfc_cqe *);
+static bool lpfc_sli4_fp_handle_cqe(struct lpfc_hba *phba,
+ struct lpfc_queue *cq, struct lpfc_cqe *cqe);
static int lpfc_sli4_post_sgl_list(struct lpfc_hba *, struct list_head *,
int);
static void lpfc_sli4_hba_handle_eqe(struct lpfc_hba *phba,
- struct lpfc_eqe *eqe, uint32_t qidx);
+ struct lpfc_queue *eq,
+ struct lpfc_eqe *eqe);
static bool lpfc_sli4_mbox_completions_pending(struct lpfc_hba *phba);
static bool lpfc_sli4_process_missed_mbox_completions(struct lpfc_hba *phba);
static int lpfc_sli4_abort_nvme_io(struct lpfc_hba *phba,
@@ -160,7 +161,7 @@ lpfc_sli4_wq_put(struct lpfc_queue *q, union lpfc_wqe128 *wqe)
}
q->WQ_posted++;
/* set consumption flag every once in a while */
- if (!((q->host_index + 1) % q->entry_repost))
+ if (!((q->host_index + 1) % q->notify_interval))
bf_set(wqe_wqec, &wqe->generic.wqe_com, 1);
else
bf_set(wqe_wqec, &wqe->generic.wqe_com, 0);
@@ -325,29 +326,16 @@ lpfc_sli4_mq_release(struct lpfc_queue *q)
static struct lpfc_eqe *
lpfc_sli4_eq_get(struct lpfc_queue *q)
{
- struct lpfc_hba *phba;
struct lpfc_eqe *eqe;
- uint32_t idx;
/* sanity check on queue memory */
if (unlikely(!q))
return NULL;
- phba = q->phba;
- eqe = q->qe[q->hba_index].eqe;
+ eqe = q->qe[q->host_index].eqe;
/* If the next EQE is not valid then we are done */
if (bf_get_le32(lpfc_eqe_valid, eqe) != q->qe_valid)
return NULL;
- /* If the host has not yet processed the next entry then we are done */
- idx = ((q->hba_index + 1) % q->entry_count);
- if (idx == q->host_index)
- return NULL;
-
- q->hba_index = idx;
- /* if the index wrapped around, toggle the valid bit */
- if (phba->sli4_hba.pc_sli4_params.eqav && !q->hba_index)
- q->qe_valid = (q->qe_valid) ? 0 : 1;
-
/*
* insert barrier for instruction interlock : data from the hardware
@@ -397,44 +385,25 @@ lpfc_sli4_if6_eq_clr_intr(struct lpfc_queue *q)
}
/**
- * lpfc_sli4_eq_release - Indicates the host has finished processing an EQ
+ * lpfc_sli4_write_eq_db - write EQ DB for eqe's consumed or arm state
+ * @phba: adapter with EQ
* @q: The Event Queue that the host has completed processing for.
+ * @count: Number of elements that have been consumed
* @arm: Indicates whether the host wants to arms this CQ.
*
- * This routine will mark all Event Queue Entries on @q, from the last
- * known completed entry to the last entry that was processed, as completed
- * by clearing the valid bit for each completion queue entry. Then it will
- * notify the HBA, by ringing the doorbell, that the EQEs have been processed.
- * The internal host index in the @q will be updated by this routine to indicate
- * that the host has finished processing the entries. The @arm parameter
- * indicates that the queue should be rearmed when ringing the doorbell.
- *
- * This function will return the number of EQEs that were popped.
+ * This routine will notify the HBA, by ringing the doorbell, that count
+ * number of EQEs have been processed. The @arm parameter indicates whether
+ * the queue should be rearmed when ringing the doorbell.
**/
-uint32_t
-lpfc_sli4_eq_release(struct lpfc_queue *q, bool arm)
+void
+lpfc_sli4_write_eq_db(struct lpfc_hba *phba, struct lpfc_queue *q,
+ uint32_t count, bool arm)
{
- uint32_t released = 0;
- struct lpfc_hba *phba;
- struct lpfc_eqe *temp_eqe;
struct lpfc_register doorbell;
/* sanity check on queue memory */
- if (unlikely(!q))
- return 0;
- phba = q->phba;
-
- /* while there are valid entries */
- while (q->hba_index != q->host_index) {
- if (!phba->sli4_hba.pc_sli4_params.eqav) {
- temp_eqe = q->qe[q->host_index].eqe;
- bf_set_le32(lpfc_eqe_valid, temp_eqe, 0);
- }
- released++;
- q->host_index = ((q->host_index + 1) % q->entry_count);
- }
- if (unlikely(released == 0 && !arm))
- return 0;
+ if (unlikely(!q || (count == 0 && !arm)))
+ return;
/* ring doorbell for number popped */
doorbell.word0 = 0;
@@ -442,7 +411,7 @@ lpfc_sli4_eq_release(struct lpfc_queue *q, bool arm)
bf_set(lpfc_eqcq_doorbell_arm, &doorbell, 1);
bf_set(lpfc_eqcq_doorbell_eqci, &doorbell, 1);
}
- bf_set(lpfc_eqcq_doorbell_num_released, &doorbell, released);
+ bf_set(lpfc_eqcq_doorbell_num_released, &doorbell, count);
bf_set(lpfc_eqcq_doorbell_qt, &doorbell, LPFC_QUEUE_TYPE_EVENT);
bf_set(lpfc_eqcq_doorbell_eqid_hi, &doorbell,
(q->queue_id >> LPFC_EQID_HI_FIELD_SHIFT));
@@ -451,60 +420,112 @@ lpfc_sli4_eq_release(struct lpfc_queue *q, bool arm)
/* PCI read to flush PCI pipeline on re-arming for INTx mode */
if ((q->phba->intr_type == INTx) && (arm == LPFC_QUEUE_REARM))
readl(q->phba->sli4_hba.EQDBregaddr);
- return released;
}
/**
- * lpfc_sli4_if6_eq_release - Indicates the host has finished processing an EQ
+ * lpfc_sli4_if6_write_eq_db - write EQ DB for eqe's consumed or arm state
+ * @phba: adapter with EQ
* @q: The Event Queue that the host has completed processing for.
+ * @count: Number of elements that have been consumed
* @arm: Indicates whether the host wants to arms this CQ.
*
- * This routine will mark all Event Queue Entries on @q, from the last
- * known completed entry to the last entry that was processed, as completed
- * by clearing the valid bit for each completion queue entry. Then it will
- * notify the HBA, by ringing the doorbell, that the EQEs have been processed.
- * The internal host index in the @q will be updated by this routine to indicate
- * that the host has finished processing the entries. The @arm parameter
- * indicates that the queue should be rearmed when ringing the doorbell.
- *
- * This function will return the number of EQEs that were popped.
+ * This routine will notify the HBA, by ringing the doorbell, that count
+ * number of EQEs have been processed. The @arm parameter indicates whether
+ * the queue should be rearmed when ringing the doorbell.
**/
-uint32_t
-lpfc_sli4_if6_eq_release(struct lpfc_queue *q, bool arm)
+void
+lpfc_sli4_if6_write_eq_db(struct lpfc_hba *phba, struct lpfc_queue *q,
+ uint32_t count, bool arm)
{
- uint32_t released = 0;
- struct lpfc_hba *phba;
- struct lpfc_eqe *temp_eqe;
struct lpfc_register doorbell;
/* sanity check on queue memory */
- if (unlikely(!q))
- return 0;
- phba = q->phba;
-
- /* while there are valid entries */
- while (q->hba_index != q->host_index) {
- if (!phba->sli4_hba.pc_sli4_params.eqav) {
- temp_eqe = q->qe[q->host_index].eqe;
- bf_set_le32(lpfc_eqe_valid, temp_eqe, 0);
- }
- released++;
- q->host_index = ((q->host_index + 1) % q->entry_count);
- }
- if (unlikely(released == 0 && !arm))
- return 0;
+ if (unlikely(!q || (count == 0 && !arm)))
+ return;
/* ring doorbell for number popped */
doorbell.word0 = 0;
if (arm)
bf_set(lpfc_if6_eq_doorbell_arm, &doorbell, 1);
- bf_set(lpfc_if6_eq_doorbell_num_released, &doorbell, released);
+ bf_set(lpfc_if6_eq_doorbell_num_released, &doorbell, count);
bf_set(lpfc_if6_eq_doorbell_eqid, &doorbell, q->queue_id);
writel(doorbell.word0, q->phba->sli4_hba.EQDBregaddr);
/* PCI read to flush PCI pipeline on re-arming for INTx mode */
if ((q->phba->intr_type == INTx) && (arm == LPFC_QUEUE_REARM))
readl(q->phba->sli4_hba.EQDBregaddr);
- return released;
+}
+
+static void
+__lpfc_sli4_consume_eqe(struct lpfc_hba *phba, struct lpfc_queue *eq,
+ struct lpfc_eqe *eqe)
+{
+ if (!phba->sli4_hba.pc_sli4_params.eqav)
+ bf_set_le32(lpfc_eqe_valid, eqe, 0);
+
+ eq->host_index = ((eq->host_index + 1) % eq->entry_count);
+
+ /* if the index wrapped around, toggle the valid bit */
+ if (phba->sli4_hba.pc_sli4_params.eqav && !eq->host_index)
+ eq->qe_valid = (eq->qe_valid) ? 0 : 1;
+}
+
+static void
+lpfc_sli4_eq_flush(struct lpfc_hba *phba, struct lpfc_queue *eq)
+{
+ struct lpfc_eqe *eqe;
+ uint32_t count = 0;
+
+ /* walk all the EQ entries and drop on the floor */
+ eqe = lpfc_sli4_eq_get(eq);
+ while (eqe) {
+ __lpfc_sli4_consume_eqe(phba, eq, eqe);
+ count++;
+ eqe = lpfc_sli4_eq_get(eq);
+ }
+
+ /* Clear and re-arm the EQ */
+ phba->sli4_hba.sli4_write_eq_db(phba, eq, count, LPFC_QUEUE_REARM);
+}
+
+static int
+lpfc_sli4_process_eq(struct lpfc_hba *phba, struct lpfc_queue *eq)
+{
+ struct lpfc_eqe *eqe;
+ int count = 0, consumed = 0;
+
+ if (cmpxchg(&eq->queue_claimed, 0, 1) != 0)
+ goto rearm_and_exit;
+
+ eqe = lpfc_sli4_eq_get(eq);
+ while (eqe) {
+ lpfc_sli4_hba_handle_eqe(phba, eq, eqe);
+ __lpfc_sli4_consume_eqe(phba, eq, eqe);
+
+ consumed++;
+ if (!(++count % eq->max_proc_limit))
+ break;
+
+ if (!(count % eq->notify_interval)) {
+ phba->sli4_hba.sli4_write_eq_db(phba, eq, consumed,
+ LPFC_QUEUE_NOARM);
+ consumed = 0;
+ }
+
+ eqe = lpfc_sli4_eq_get(eq);
+ }
+ eq->EQ_processed += count;
+
+ /* Track the max number of EQEs processed in 1 intr */
+ if (count > eq->EQ_max_eqe)
+ eq->EQ_max_eqe = count;
+
+ eq->queue_claimed = 0;
+
+rearm_and_exit:
+ /* Always clear and re-arm the EQ */
+ phba->sli4_hba.sli4_write_eq_db(phba, eq, consumed, LPFC_QUEUE_REARM);
+
+ return count;
}
/**
@@ -519,28 +540,16 @@ lpfc_sli4_if6_eq_release(struct lpfc_queue *q, bool arm)
static struct lpfc_cqe *
lpfc_sli4_cq_get(struct lpfc_queue *q)
{
- struct lpfc_hba *phba;
struct lpfc_cqe *cqe;
- uint32_t idx;
/* sanity check on queue memory */
if (unlikely(!q))
return NULL;
- phba = q->phba;
- cqe = q->qe[q->hba_index].cqe;
+ cqe = q->qe[q->host_index].cqe;
/* If the next CQE is not valid then we are done */
if (bf_get_le32(lpfc_cqe_valid, cqe) != q->qe_valid)
return NULL;
- /* If the host has not yet processed the next entry then we are done */
- idx = ((q->hba_index + 1) % q->entry_count);
- if (idx == q->host_index)
- return NULL;
-
- q->hba_index = idx;
- /* if the index wrapped around, toggle the valid bit */
- if (phba->sli4_hba.pc_sli4_params.cqav && !q->hba_index)
- q->qe_valid = (q->qe_valid) ? 0 : 1;
/*
* insert barrier for instruction interlock : data from the hardware
@@ -554,107 +563,81 @@ lpfc_sli4_cq_get(struct lpfc_queue *q)
return cqe;
}
+static void
+__lpfc_sli4_consume_cqe(struct lpfc_hba *phba, struct lpfc_queue *cq,
+ struct lpfc_cqe *cqe)
+{
+ if (!phba->sli4_hba.pc_sli4_params.cqav)
+ bf_set_le32(lpfc_cqe_valid, cqe, 0);
+
+ cq->host_index = ((cq->host_index + 1) % cq->entry_count);
+
+ /* if the index wrapped around, toggle the valid bit */
+ if (phba->sli4_hba.pc_sli4_params.cqav && !cq->host_index)
+ cq->qe_valid = (cq->qe_valid) ? 0 : 1;
+}
+
/**
- * lpfc_sli4_cq_release - Indicates the host has finished processing a CQ
+ * lpfc_sli4_write_cq_db - write cq DB for entries consumed or arm state.
+ * @phba: the adapter with the CQ
* @q: The Completion Queue that the host has completed processing for.
+ * @count: the number of elements that were consumed
* @arm: Indicates whether the host wants to arms this CQ.
*
- * This routine will mark all Completion queue entries on @q, from the last
- * known completed entry to the last entry that was processed, as completed
- * by clearing the valid bit for each completion queue entry. Then it will
- * notify the HBA, by ringing the doorbell, that the CQEs have been processed.
- * The internal host index in the @q will be updated by this routine to indicate
- * that the host has finished processing the entries. The @arm parameter
- * indicates that the queue should be rearmed when ringing the doorbell.
- *
- * This function will return the number of CQEs that were released.
+ * This routine will notify the HBA, by ringing the doorbell, that the
+ * CQEs have been processed. The @arm parameter specifies whether the
+ * queue should be rearmed when ringing the doorbell.
**/
-uint32_t
-lpfc_sli4_cq_release(struct lpfc_queue *q, bool arm)
+void
+lpfc_sli4_write_cq_db(struct lpfc_hba *phba, struct lpfc_queue *q,
+ uint32_t count, bool arm)
{
- uint32_t released = 0;
- struct lpfc_hba *phba;
- struct lpfc_cqe *temp_qe;
struct lpfc_register doorbell;
/* sanity check on queue memory */
- if (unlikely(!q))
- return 0;
- phba = q->phba;
-
- /* while there are valid entries */
- while (q->hba_index != q->host_index) {
- if (!phba->sli4_hba.pc_sli4_params.cqav) {
- temp_qe = q->qe[q->host_index].cqe;
- bf_set_le32(lpfc_cqe_valid, temp_qe, 0);
- }
- released++;
- q->host_index = ((q->host_index + 1) % q->entry_count);
- }
- if (unlikely(released == 0 && !arm))
- return 0;
+ if (unlikely(!q || (count == 0 && !arm)))
+ return;
/* ring doorbell for number popped */
doorbell.word0 = 0;
if (arm)
bf_set(lpfc_eqcq_doorbell_arm, &doorbell, 1);
- bf_set(lpfc_eqcq_doorbell_num_released, &doorbell, released);
+ bf_set(lpfc_eqcq_doorbell_num_released, &doorbell, count);
bf_set(lpfc_eqcq_doorbell_qt, &doorbell, LPFC_QUEUE_TYPE_COMPLETION);
bf_set(lpfc_eqcq_doorbell_cqid_hi, &doorbell,
(q->queue_id >> LPFC_CQID_HI_FIELD_SHIFT));
bf_set(lpfc_eqcq_doorbell_cqid_lo, &doorbell, q->queue_id);
writel(doorbell.word0, q->phba->sli4_hba.CQDBregaddr);
- return released;
}
/**
- * lpfc_sli4_if6_cq_release - Indicates the host has finished processing a CQ
+ * lpfc_sli4_if6_write_cq_db - write cq DB for entries consumed or arm state.
+ * @phba: the adapter with the CQ
* @q: The Completion Queue that the host has completed processing for.
+ * @count: the number of elements that were consumed
* @arm: Indicates whether the host wants to arms this CQ.
*
- * This routine will mark all Completion queue entries on @q, from the last
- * known completed entry to the last entry that was processed, as completed
- * by clearing the valid bit for each completion queue entry. Then it will
- * notify the HBA, by ringing the doorbell, that the CQEs have been processed.
- * The internal host index in the @q will be updated by this routine to indicate
- * that the host has finished processing the entries. The @arm parameter
- * indicates that the queue should be rearmed when ringing the doorbell.
- *
- * This function will return the number of CQEs that were released.
+ * This routine will notify the HBA, by ringing the doorbell, that the
+ * CQEs have been processed. The @arm parameter specifies whether the
+ * queue should be rearmed when ringing the doorbell.
**/
-uint32_t
-lpfc_sli4_if6_cq_release(struct lpfc_queue *q, bool arm)
+void
+lpfc_sli4_if6_write_cq_db(struct lpfc_hba *phba, struct lpfc_queue *q,
+ uint32_t count, bool arm)
{
- uint32_t released = 0;
- struct lpfc_hba *phba;
- struct lpfc_cqe *temp_qe;
struct lpfc_register doorbell;
/* sanity check on queue memory */
- if (unlikely(!q))
- return 0;
- phba = q->phba;
-
- /* while there are valid entries */
- while (q->hba_index != q->host_index) {
- if (!phba->sli4_hba.pc_sli4_params.cqav) {
- temp_qe = q->qe[q->host_index].cqe;
- bf_set_le32(lpfc_cqe_valid, temp_qe, 0);
- }
- released++;
- q->host_index = ((q->host_index + 1) % q->entry_count);
- }
- if (unlikely(released == 0 && !arm))
- return 0;
+ if (unlikely(!q || (count == 0 && !arm)))
+ return;
/* ring doorbell for number popped */
doorbell.word0 = 0;
if (arm)
bf_set(lpfc_if6_cq_doorbell_arm, &doorbell, 1);
- bf_set(lpfc_if6_cq_doorbell_num_released, &doorbell, released);
+ bf_set(lpfc_if6_cq_doorbell_num_released, &doorbell, count);
bf_set(lpfc_if6_cq_doorbell_cqid, &doorbell, q->queue_id);
writel(doorbell.word0, q->phba->sli4_hba.CQDBregaddr);
- return released;
}
/**
@@ -703,15 +686,15 @@ lpfc_sli4_rq_put(struct lpfc_queue *hq, struct lpfc_queue *dq,
hq->RQ_buf_posted++;
/* Ring The Header Receive Queue Doorbell */
- if (!(hq->host_index % hq->entry_repost)) {
+ if (!(hq->host_index % hq->notify_interval)) {
doorbell.word0 = 0;
if (hq->db_format == LPFC_DB_RING_FORMAT) {
bf_set(lpfc_rq_db_ring_fm_num_posted, &doorbell,
- hq->entry_repost);
+ hq->notify_interval);
bf_set(lpfc_rq_db_ring_fm_id, &doorbell, hq->queue_id);
} else if (hq->db_format == LPFC_DB_LIST_FORMAT) {
bf_set(lpfc_rq_db_list_fm_num_posted, &doorbell,
- hq->entry_repost);
+ hq->notify_interval);
bf_set(lpfc_rq_db_list_fm_index, &doorbell,
hq->host_index);
bf_set(lpfc_rq_db_list_fm_id, &doorbell, hq->queue_id);
@@ -1025,7 +1008,7 @@ lpfc_test_rrq_active(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp,
if (!ndlp->active_rrqs_xri_bitmap)
return 0;
if (test_bit(xritag, ndlp->active_rrqs_xri_bitmap))
- return 1;
+ return 1;
else
return 0;
}
@@ -1133,14 +1116,14 @@ __lpfc_sli_get_els_sglq(struct lpfc_hba *phba, struct lpfc_iocbq *piocbq)
struct list_head *lpfc_els_sgl_list = &phba->sli4_hba.lpfc_els_sgl_list;
struct lpfc_sglq *sglq = NULL;
struct lpfc_sglq *start_sglq = NULL;
- struct lpfc_scsi_buf *lpfc_cmd;
+ struct lpfc_io_buf *lpfc_cmd;
struct lpfc_nodelist *ndlp;
int found = 0;
lockdep_assert_held(&phba->hbalock);
if (piocbq->iocb_flag & LPFC_IO_FCP) {
- lpfc_cmd = (struct lpfc_scsi_buf *) piocbq->context1;
+ lpfc_cmd = (struct lpfc_io_buf *) piocbq->context1;
ndlp = lpfc_cmd->rdata->pnode;
} else if ((piocbq->iocb.ulpCommand == CMD_GEN_REQUEST64_CR) &&
!(piocbq->iocb_flag & LPFC_IO_LIBDFC)) {
@@ -1596,6 +1579,7 @@ lpfc_sli_ringtxcmpl_put(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
list_add_tail(&piocb->list, &pring->txcmplq);
piocb->iocb_flag |= LPFC_IO_ON_TXCMPLQ;
+ pring->txcmplq_cnt++;
if ((unlikely(pring->ringno == LPFC_ELS_RING)) &&
(piocb->iocb.ulpCommand != CMD_ABORT_XRI_CN) &&
@@ -3008,6 +2992,7 @@ lpfc_sli_iocbq_lookup(struct lpfc_hba *phba,
/* remove from txcmpl queue list */
list_del_init(&cmd_iocb->list);
cmd_iocb->iocb_flag &= ~LPFC_IO_ON_TXCMPLQ;
+ pring->txcmplq_cnt--;
return cmd_iocb;
}
}
@@ -3045,6 +3030,7 @@ lpfc_sli_iocbq_lookup_by_tag(struct lpfc_hba *phba,
/* remove from txcmpl queue list */
list_del_init(&cmd_iocb->list);
cmd_iocb->iocb_flag &= ~LPFC_IO_ON_TXCMPLQ;
+ pring->txcmplq_cnt--;
return cmd_iocb;
}
}
@@ -3981,8 +3967,8 @@ lpfc_sli_abort_fcp_rings(struct lpfc_hba *phba)
/* Look on all the FCP Rings for the iotag */
if (phba->sli_rev >= LPFC_SLI_REV4) {
- for (i = 0; i < phba->cfg_fcp_io_channel; i++) {
- pring = phba->sli4_hba.fcp_wq[i]->pring;
+ for (i = 0; i < phba->cfg_hdw_queue; i++) {
+ pring = phba->sli4_hba.hdwq[i].fcp_wq->pring;
lpfc_sli_abort_iocb_ring(phba, pring);
}
} else {
@@ -4006,12 +3992,13 @@ lpfc_sli_abort_nvme_rings(struct lpfc_hba *phba)
struct lpfc_sli_ring *pring;
uint32_t i;
- if (phba->sli_rev < LPFC_SLI_REV4)
+ if ((phba->sli_rev < LPFC_SLI_REV4) ||
+ !(phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME))
return;
/* Abort all IO on each NVME ring. */
- for (i = 0; i < phba->cfg_nvme_io_channel; i++) {
- pring = phba->sli4_hba.nvme_wq[i]->pring;
+ for (i = 0; i < phba->cfg_hdw_queue; i++) {
+ pring = phba->sli4_hba.hdwq[i].nvme_wq->pring;
lpfc_sli_abort_wqe_ring(phba, pring);
}
}
@@ -4044,8 +4031,8 @@ lpfc_sli_flush_fcp_rings(struct lpfc_hba *phba)
/* Look on all the FCP Rings for the iotag */
if (phba->sli_rev >= LPFC_SLI_REV4) {
- for (i = 0; i < phba->cfg_fcp_io_channel; i++) {
- pring = phba->sli4_hba.fcp_wq[i]->pring;
+ for (i = 0; i < phba->cfg_hdw_queue; i++) {
+ pring = phba->sli4_hba.hdwq[i].fcp_wq->pring;
spin_lock_irq(&pring->ring_lock);
/* Retrieve everything on txq */
@@ -4110,7 +4097,8 @@ lpfc_sli_flush_nvme_rings(struct lpfc_hba *phba)
uint32_t i;
struct lpfc_iocbq *piocb, *next_iocb;
- if (phba->sli_rev < LPFC_SLI_REV4)
+ if ((phba->sli_rev < LPFC_SLI_REV4) ||
+ !(phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME))
return;
/* Hint to other driver operations that a flush is in progress. */
@@ -4122,8 +4110,8 @@ lpfc_sli_flush_nvme_rings(struct lpfc_hba *phba)
* a local driver reason code. This is a flush so no
* abort exchange to FW.
*/
- for (i = 0; i < phba->cfg_nvme_io_channel; i++) {
- pring = phba->sli4_hba.nvme_wq[i]->pring;
+ for (i = 0; i < phba->cfg_hdw_queue; i++) {
+ pring = phba->sli4_hba.hdwq[i].nvme_wq->pring;
spin_lock_irq(&pring->ring_lock);
list_for_each_entry_safe(piocb, next_iocb,
@@ -5564,41 +5552,35 @@ lpfc_sli4_arm_cqeq_intr(struct lpfc_hba *phba)
{
int qidx;
struct lpfc_sli4_hba *sli4_hba = &phba->sli4_hba;
+ struct lpfc_sli4_hdw_queue *qp;
- sli4_hba->sli4_cq_release(sli4_hba->mbx_cq, LPFC_QUEUE_REARM);
- sli4_hba->sli4_cq_release(sli4_hba->els_cq, LPFC_QUEUE_REARM);
+ sli4_hba->sli4_write_cq_db(phba, sli4_hba->mbx_cq, 0, LPFC_QUEUE_REARM);
+ sli4_hba->sli4_write_cq_db(phba, sli4_hba->els_cq, 0, LPFC_QUEUE_REARM);
if (sli4_hba->nvmels_cq)
- sli4_hba->sli4_cq_release(sli4_hba->nvmels_cq,
- LPFC_QUEUE_REARM);
+ sli4_hba->sli4_write_cq_db(phba, sli4_hba->nvmels_cq, 0,
+ LPFC_QUEUE_REARM);
- if (sli4_hba->fcp_cq)
- for (qidx = 0; qidx < phba->cfg_fcp_io_channel; qidx++)
- sli4_hba->sli4_cq_release(sli4_hba->fcp_cq[qidx],
- LPFC_QUEUE_REARM);
-
- if (sli4_hba->nvme_cq)
- for (qidx = 0; qidx < phba->cfg_nvme_io_channel; qidx++)
- sli4_hba->sli4_cq_release(sli4_hba->nvme_cq[qidx],
- LPFC_QUEUE_REARM);
-
- if (phba->cfg_fof)
- sli4_hba->sli4_cq_release(sli4_hba->oas_cq, LPFC_QUEUE_REARM);
+ qp = sli4_hba->hdwq;
+ if (sli4_hba->hdwq) {
+ for (qidx = 0; qidx < phba->cfg_hdw_queue; qidx++) {
+ sli4_hba->sli4_write_cq_db(phba, qp[qidx].fcp_cq, 0,
+ LPFC_QUEUE_REARM);
+ sli4_hba->sli4_write_cq_db(phba, qp[qidx].nvme_cq, 0,
+ LPFC_QUEUE_REARM);
+ }
- if (sli4_hba->hba_eq)
- for (qidx = 0; qidx < phba->io_channel_irqs; qidx++)
- sli4_hba->sli4_eq_release(sli4_hba->hba_eq[qidx],
- LPFC_QUEUE_REARM);
+ for (qidx = 0; qidx < phba->cfg_irq_chann; qidx++)
+ sli4_hba->sli4_write_eq_db(phba, qp[qidx].hba_eq,
+ 0, LPFC_QUEUE_REARM);
+ }
if (phba->nvmet_support) {
for (qidx = 0; qidx < phba->cfg_nvmet_mrq; qidx++) {
- sli4_hba->sli4_cq_release(
- sli4_hba->nvmet_cqset[qidx],
+ sli4_hba->sli4_write_cq_db(phba,
+ sli4_hba->nvmet_cqset[qidx], 0,
LPFC_QUEUE_REARM);
}
}
-
- if (phba->cfg_fof)
- sli4_hba->sli4_eq_release(sli4_hba->fof_eq, LPFC_QUEUE_REARM);
}
/**
@@ -6027,11 +6009,8 @@ lpfc_sli4_alloc_extent(struct lpfc_hba *phba, uint16_t type)
list_add_tail(&rsrc_blks->list, ext_blk_list);
rsrc_start = rsrc_id;
if ((type == LPFC_RSC_TYPE_FCOE_XRI) && (j == 0)) {
- phba->sli4_hba.scsi_xri_start = rsrc_start +
+ phba->sli4_hba.io_xri_start = rsrc_start +
lpfc_sli4_get_iocb_cnt(phba);
- phba->sli4_hba.nvme_xri_start =
- phba->sli4_hba.scsi_xri_start +
- phba->sli4_hba.scsi_xri_max;
}
while (rsrc_id < (rsrc_start + rsrc_size)) {
@@ -7056,6 +7035,38 @@ lpfc_sli4_repost_sgl_list(struct lpfc_hba *phba,
return total_cnt;
}
+/**
+ * lpfc_sli4_repost_io_sgl_list - Repost all the allocated nvme buffer sgls
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine walks the list of nvme buffers that have been allocated and
+ * repost them to the port by using SGL block post. This is needed after a
+ * pci_function_reset/warm_start or start. The lpfc_hba_down_post_s4 routine
+ * is responsible for moving all nvme buffers on the lpfc_abts_nvme_sgl_list
+ * to the lpfc_io_buf_list. If the repost fails, reject all nvme buffers.
+ *
+ * Returns: 0 = success, non-zero failure.
+ **/
+int
+lpfc_sli4_repost_io_sgl_list(struct lpfc_hba *phba)
+{
+ LIST_HEAD(post_nblist);
+ int num_posted, rc = 0;
+
+ /* get all NVME buffers need to repost to a local list */
+ lpfc_io_buf_flush(phba, &post_nblist);
+
+ /* post the list of nvme buffer sgls to port if available */
+ if (!list_empty(&post_nblist)) {
+ num_posted = lpfc_sli4_post_io_sgl_list(
+ phba, &post_nblist, phba->sli4_hba.io_xri_cnt);
+ /* failed to post any nvme buffer, return error */
+ if (num_posted == 0)
+ rc = -EIO;
+ }
+ return rc;
+}
+
void
lpfc_set_host_data(struct lpfc_hba *phba, LPFC_MBOXQ_t *mbox)
{
@@ -7144,7 +7155,7 @@ lpfc_post_rq_buffer(struct lpfc_hba *phba, struct lpfc_queue *hrq,
int
lpfc_sli4_hba_setup(struct lpfc_hba *phba)
{
- int rc, i, cnt;
+ int rc, i, cnt, len;
LPFC_MBOXQ_t *mboxq;
struct lpfc_mqe *mqe;
uint8_t *vpd;
@@ -7517,24 +7528,26 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba)
/* We need 1 iocbq for every SGL, for IO processing */
cnt += phba->sli4_hba.nvmet_xri_cnt;
} else {
- /* update host scsi xri-sgl sizes and mappings */
- rc = lpfc_sli4_scsi_sgl_update(phba);
+ /* update host common xri-sgl sizes and mappings */
+ rc = lpfc_sli4_io_sgl_update(phba);
if (unlikely(rc)) {
lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
- "6309 Failed to update scsi-sgl size "
+ "6082 Failed to update nvme-sgl size "
"and mapping: %d\n", rc);
goto out_destroy_queue;
}
- /* update host nvme xri-sgl sizes and mappings */
- rc = lpfc_sli4_nvme_sgl_update(phba);
+ /* register the allocated common sgl pool to the port */
+ rc = lpfc_sli4_repost_io_sgl_list(phba);
if (unlikely(rc)) {
lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
- "6082 Failed to update nvme-sgl size "
- "and mapping: %d\n", rc);
+ "6116 Error %d during nvme sgl post "
+ "operation\n", rc);
+ /* Some NVME buffers were moved to abort nvme list */
+ /* A pci function reset will repost them */
+ rc = -ENODEV;
goto out_destroy_queue;
}
-
cnt = phba->cfg_iocb_cnt * 1024;
}
@@ -7571,36 +7584,6 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba)
}
}
- if (phba->cfg_enable_fc4_type & LPFC_ENABLE_FCP) {
- /* register the allocated scsi sgl pool to the port */
- rc = lpfc_sli4_repost_scsi_sgl_list(phba);
- if (unlikely(rc)) {
- lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
- "0383 Error %d during scsi sgl post "
- "operation\n", rc);
- /* Some Scsi buffers were moved to abort scsi list */
- /* A pci function reset will repost them */
- rc = -ENODEV;
- goto out_destroy_queue;
- }
- }
-
- if ((phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) &&
- (phba->nvmet_support == 0)) {
-
- /* register the allocated nvme sgl pool to the port */
- rc = lpfc_repost_nvme_sgl_list(phba);
- if (unlikely(rc)) {
- lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
- "6116 Error %d during nvme sgl post "
- "operation\n", rc);
- /* Some NVME buffers were moved to abort nvme list */
- /* A pci function reset will repost them */
- rc = -ENODEV;
- goto out_destroy_queue;
- }
- }
-
/* Post the rpi header region to the device. */
rc = lpfc_sli4_post_all_rpi_hdrs(phba);
if (unlikely(rc)) {
@@ -7650,6 +7633,25 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba)
lpfc_sli_read_link_ste(phba);
}
+ /* Don't post more new bufs if repost already recovered
+ * the nvme sgls.
+ */
+ if (phba->nvmet_support == 0) {
+ if (phba->sli4_hba.io_xri_cnt == 0) {
+ len = lpfc_new_io_buf(
+ phba, phba->sli4_hba.io_xri_max);
+ if (len == 0) {
+ rc = -ENOMEM;
+ goto out_unset_queue;
+ }
+
+ if (phba->cfg_xri_rebalancing)
+ lpfc_create_multixri_pools(phba);
+ }
+ } else {
+ phba->cfg_xri_rebalancing = 0;
+ }
+
/* Arm the CQs and then EQs on device */
lpfc_sli4_arm_cqeq_intr(phba);
@@ -7678,6 +7680,11 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba)
phba->hb_outstanding = 0;
phba->last_completion_time = jiffies;
+ /* start eq_delay heartbeat */
+ if (phba->cfg_auto_imax)
+ queue_delayed_work(phba->wq, &phba->eq_delay_work,
+ msecs_to_jiffies(LPFC_EQ_DELAY_MSECS));
+
/* Start error attention (ERATT) polling timer */
mod_timer(&phba->eratt_poll,
jiffies + msecs_to_jiffies(1000 * phba->eratt_poll_interval));
@@ -7729,18 +7736,21 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba)
lpfc_printf_log(phba, KERN_ERR, LOG_INIT | LOG_SLI,
"3104 Adapter failed to issue "
"DOWN_LINK mbox cmd, rc:x%x\n", rc);
- goto out_unset_queue;
+ goto out_io_buff_free;
}
} else if (phba->cfg_suppress_link_up == LPFC_INITIALIZE_LINK) {
/* don't perform init_link on SLI4 FC port loopback test */
if (!(phba->link_flag & LS_LOOPBACK_MODE)) {
rc = phba->lpfc_hba_init_link(phba, MBX_NOWAIT);
if (rc)
- goto out_unset_queue;
+ goto out_io_buff_free;
}
}
mempool_free(mboxq, phba->mbox_mem_pool);
return rc;
+out_io_buff_free:
+ /* Free allocated IO Buffers */
+ lpfc_io_free(phba);
out_unset_queue:
/* Unset all the queues set up in this routine when error out */
lpfc_sli4_queue_unset(phba);
@@ -7846,7 +7856,6 @@ lpfc_sli4_process_missed_mbox_completions(struct lpfc_hba *phba)
struct lpfc_sli4_hba *sli4_hba = &phba->sli4_hba;
uint32_t eqidx;
struct lpfc_queue *fpeq = NULL;
- struct lpfc_eqe *eqe;
bool mbox_pending;
if (unlikely(!phba) || (phba->sli_rev != LPFC_SLI_REV4))
@@ -7854,11 +7863,11 @@ lpfc_sli4_process_missed_mbox_completions(struct lpfc_hba *phba)
/* Find the eq associated with the mcq */
- if (sli4_hba->hba_eq)
- for (eqidx = 0; eqidx < phba->io_channel_irqs; eqidx++)
- if (sli4_hba->hba_eq[eqidx]->queue_id ==
+ if (sli4_hba->hdwq)
+ for (eqidx = 0; eqidx < phba->cfg_irq_chann; eqidx++)
+ if (sli4_hba->hdwq[eqidx].hba_eq->queue_id ==
sli4_hba->mbx_cq->assoc_qid) {
- fpeq = sli4_hba->hba_eq[eqidx];
+ fpeq = sli4_hba->hdwq[eqidx].hba_eq;
break;
}
if (!fpeq)
@@ -7880,14 +7889,11 @@ lpfc_sli4_process_missed_mbox_completions(struct lpfc_hba *phba)
*/
if (mbox_pending)
- while ((eqe = lpfc_sli4_eq_get(fpeq))) {
- lpfc_sli4_hba_handle_eqe(phba, eqe, eqidx);
- fpeq->EQ_processed++;
- }
-
- /* Always clear and re-arm the EQ */
-
- sli4_hba->sli4_eq_release(fpeq, LPFC_QUEUE_REARM);
+ /* process and rearm the EQ */
+ lpfc_sli4_process_eq(phba, fpeq);
+ else
+ /* Always clear and re-arm the EQ */
+ sli4_hba->sli4_write_eq_db(phba, fpeq, 0, LPFC_QUEUE_REARM);
return mbox_pending;
@@ -8557,7 +8563,6 @@ lpfc_sli4_post_sync_mbox(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
rc = lpfc_sli4_wait_bmbx_ready(phba, mboxq);
if (rc)
goto exit;
-
/*
* Initialize the bootstrap memory region to avoid stale data areas
* in the mailbox post. Then copy the caller's mailbox contents to
@@ -9476,7 +9481,7 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
bf_set(wqe_pbde, &wqe->fcp_iwrite.wqe_com, 0);
if (phba->fcp_embed_io) {
- struct lpfc_scsi_buf *lpfc_cmd;
+ struct lpfc_io_buf *lpfc_cmd;
struct sli4_sge *sgl;
struct fcp_cmnd *fcp_cmnd;
uint32_t *ptr;
@@ -9484,7 +9489,7 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
/* 128 byte wqe support here */
lpfc_cmd = iocbq->context1;
- sgl = (struct sli4_sge *)lpfc_cmd->fcp_bpl;
+ sgl = (struct sli4_sge *)lpfc_cmd->dma_sgl;
fcp_cmnd = lpfc_cmd->fcp_cmnd;
/* Word 0-2 - FCP_CMND */
@@ -9540,7 +9545,7 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
bf_set(wqe_pbde, &wqe->fcp_iread.wqe_com, 0);
if (phba->fcp_embed_io) {
- struct lpfc_scsi_buf *lpfc_cmd;
+ struct lpfc_io_buf *lpfc_cmd;
struct sli4_sge *sgl;
struct fcp_cmnd *fcp_cmnd;
uint32_t *ptr;
@@ -9548,7 +9553,7 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
/* 128 byte wqe support here */
lpfc_cmd = iocbq->context1;
- sgl = (struct sli4_sge *)lpfc_cmd->fcp_bpl;
+ sgl = (struct sli4_sge *)lpfc_cmd->dma_sgl;
fcp_cmnd = lpfc_cmd->fcp_cmnd;
/* Word 0-2 - FCP_CMND */
@@ -9597,7 +9602,7 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
/* Note, word 10 is already initialized to 0 */
if (phba->fcp_embed_io) {
- struct lpfc_scsi_buf *lpfc_cmd;
+ struct lpfc_io_buf *lpfc_cmd;
struct sli4_sge *sgl;
struct fcp_cmnd *fcp_cmnd;
uint32_t *ptr;
@@ -9605,7 +9610,7 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
/* 128 byte wqe support here */
lpfc_cmd = iocbq->context1;
- sgl = (struct sli4_sge *)lpfc_cmd->fcp_bpl;
+ sgl = (struct sli4_sge *)lpfc_cmd->dma_sgl;
fcp_cmnd = lpfc_cmd->fcp_cmnd;
/* Word 0-2 - FCP_CMND */
@@ -9864,10 +9869,7 @@ __lpfc_sli_issue_iocb_s4(struct lpfc_hba *phba, uint32_t ring_number,
/* Get the WQ */
if ((piocb->iocb_flag & LPFC_IO_FCP) ||
(piocb->iocb_flag & LPFC_USE_FCPWQIDX)) {
- if (!phba->cfg_fof || (!(piocb->iocb_flag & LPFC_IO_OAS)))
- wq = phba->sli4_hba.fcp_wq[piocb->hba_wqidx];
- else
- wq = phba->sli4_hba.oas_wq;
+ wq = phba->sli4_hba.hdwq[piocb->hba_wqidx].fcp_wq;
} else {
wq = phba->sli4_hba.els_wq;
}
@@ -9879,7 +9881,7 @@ __lpfc_sli_issue_iocb_s4(struct lpfc_hba *phba, uint32_t ring_number,
* The WQE can be either 64 or 128 bytes,
*/
- lockdep_assert_held(&phba->hbalock);
+ lockdep_assert_held(&pring->ring_lock);
if (piocb->sli4_xritag == NO_XRI) {
if (piocb->iocb.ulpCommand == CMD_ABORT_XRI_CN ||
@@ -10001,29 +10003,20 @@ lpfc_sli_api_table_setup(struct lpfc_hba *phba, uint8_t dev_grp)
struct lpfc_sli_ring *
lpfc_sli4_calc_ring(struct lpfc_hba *phba, struct lpfc_iocbq *piocb)
{
+ struct lpfc_io_buf *lpfc_cmd;
+
if (piocb->iocb_flag & (LPFC_IO_FCP | LPFC_USE_FCPWQIDX)) {
- if (!(phba->cfg_fof) ||
- (!(piocb->iocb_flag & LPFC_IO_FOF))) {
- if (unlikely(!phba->sli4_hba.fcp_wq))
- return NULL;
- /*
- * for abort iocb hba_wqidx should already
- * be setup based on what work queue we used.
- */
- if (!(piocb->iocb_flag & LPFC_USE_FCPWQIDX)) {
- piocb->hba_wqidx =
- lpfc_sli4_scmd_to_wqidx_distr(phba,
- piocb->context1);
- piocb->hba_wqidx = piocb->hba_wqidx %
- phba->cfg_fcp_io_channel;
- }
- return phba->sli4_hba.fcp_wq[piocb->hba_wqidx]->pring;
- } else {
- if (unlikely(!phba->sli4_hba.oas_wq))
- return NULL;
- piocb->hba_wqidx = 0;
- return phba->sli4_hba.oas_wq->pring;
+ if (unlikely(!phba->sli4_hba.hdwq))
+ return NULL;
+ /*
+ * for abort iocb hba_wqidx should already
+ * be setup based on what work queue we used.
+ */
+ if (!(piocb->iocb_flag & LPFC_USE_FCPWQIDX)) {
+ lpfc_cmd = (struct lpfc_io_buf *)piocb->context1;
+ piocb->hba_wqidx = lpfc_cmd->hdwq_no;
}
+ return phba->sli4_hba.hdwq[piocb->hba_wqidx].fcp_wq->pring;
} else {
if (unlikely(!phba->sli4_hba.els_wq))
return NULL;
@@ -10049,12 +10042,9 @@ int
lpfc_sli_issue_iocb(struct lpfc_hba *phba, uint32_t ring_number,
struct lpfc_iocbq *piocb, uint32_t flag)
{
- struct lpfc_hba_eq_hdl *hba_eq_hdl;
struct lpfc_sli_ring *pring;
- struct lpfc_queue *fpeq;
- struct lpfc_eqe *eqe;
unsigned long iflags;
- int rc, idx;
+ int rc;
if (phba->sli_rev == LPFC_SLI_REV4) {
pring = lpfc_sli4_calc_ring(phba, piocb);
@@ -10064,34 +10054,6 @@ lpfc_sli_issue_iocb(struct lpfc_hba *phba, uint32_t ring_number,
spin_lock_irqsave(&pring->ring_lock, iflags);
rc = __lpfc_sli_issue_iocb(phba, ring_number, piocb, flag);
spin_unlock_irqrestore(&pring->ring_lock, iflags);
-
- if (lpfc_fcp_look_ahead && (piocb->iocb_flag & LPFC_IO_FCP)) {
- idx = piocb->hba_wqidx;
- hba_eq_hdl = &phba->sli4_hba.hba_eq_hdl[idx];
-
- if (atomic_dec_and_test(&hba_eq_hdl->hba_eq_in_use)) {
-
- /* Get associated EQ with this index */
- fpeq = phba->sli4_hba.hba_eq[idx];
-
- /* Turn off interrupts from this EQ */
- phba->sli4_hba.sli4_eq_clr_intr(fpeq);
-
- /*
- * Process all the events on FCP EQ
- */
- while ((eqe = lpfc_sli4_eq_get(fpeq))) {
- lpfc_sli4_hba_handle_eqe(phba,
- eqe, idx);
- fpeq->EQ_processed++;
- }
-
- /* Always clear and re-arm the EQ */
- phba->sli4_hba.sli4_eq_release(fpeq,
- LPFC_QUEUE_REARM);
- }
- atomic_inc(&hba_eq_hdl->hba_eq_in_use);
- }
} else {
/* For now, SLI2/3 will still use hbalock */
spin_lock_irqsave(&phba->hbalock, iflags);
@@ -10506,19 +10468,11 @@ lpfc_sli4_queue_init(struct lpfc_hba *phba)
INIT_LIST_HEAD(&psli->mboxq);
INIT_LIST_HEAD(&psli->mboxq_cmpl);
/* Initialize list headers for txq and txcmplq as double linked lists */
- for (i = 0; i < phba->cfg_fcp_io_channel; i++) {
- pring = phba->sli4_hba.fcp_wq[i]->pring;
- pring->flag = 0;
- pring->ringno = LPFC_FCP_RING;
- INIT_LIST_HEAD(&pring->txq);
- INIT_LIST_HEAD(&pring->txcmplq);
- INIT_LIST_HEAD(&pring->iocb_continueq);
- spin_lock_init(&pring->ring_lock);
- }
- for (i = 0; i < phba->cfg_nvme_io_channel; i++) {
- pring = phba->sli4_hba.nvme_wq[i]->pring;
+ for (i = 0; i < phba->cfg_hdw_queue; i++) {
+ pring = phba->sli4_hba.hdwq[i].fcp_wq->pring;
pring->flag = 0;
pring->ringno = LPFC_FCP_RING;
+ pring->txcmplq_cnt = 0;
INIT_LIST_HEAD(&pring->txq);
INIT_LIST_HEAD(&pring->txcmplq);
INIT_LIST_HEAD(&pring->iocb_continueq);
@@ -10527,25 +10481,27 @@ lpfc_sli4_queue_init(struct lpfc_hba *phba)
pring = phba->sli4_hba.els_wq->pring;
pring->flag = 0;
pring->ringno = LPFC_ELS_RING;
+ pring->txcmplq_cnt = 0;
INIT_LIST_HEAD(&pring->txq);
INIT_LIST_HEAD(&pring->txcmplq);
INIT_LIST_HEAD(&pring->iocb_continueq);
spin_lock_init(&pring->ring_lock);
- if (phba->cfg_nvme_io_channel) {
+ if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) {
+ for (i = 0; i < phba->cfg_hdw_queue; i++) {
+ pring = phba->sli4_hba.hdwq[i].nvme_wq->pring;
+ pring->flag = 0;
+ pring->ringno = LPFC_FCP_RING;
+ pring->txcmplq_cnt = 0;
+ INIT_LIST_HEAD(&pring->txq);
+ INIT_LIST_HEAD(&pring->txcmplq);
+ INIT_LIST_HEAD(&pring->iocb_continueq);
+ spin_lock_init(&pring->ring_lock);
+ }
pring = phba->sli4_hba.nvmels_wq->pring;
pring->flag = 0;
pring->ringno = LPFC_ELS_RING;
- INIT_LIST_HEAD(&pring->txq);
- INIT_LIST_HEAD(&pring->txcmplq);
- INIT_LIST_HEAD(&pring->iocb_continueq);
- spin_lock_init(&pring->ring_lock);
- }
-
- if (phba->cfg_fof) {
- pring = phba->sli4_hba.oas_wq->pring;
- pring->flag = 0;
- pring->ringno = LPFC_FCP_RING;
+ pring->txcmplq_cnt = 0;
INIT_LIST_HEAD(&pring->txq);
INIT_LIST_HEAD(&pring->txcmplq);
INIT_LIST_HEAD(&pring->iocb_continueq);
@@ -11327,6 +11283,7 @@ lpfc_sli4_abort_nvme_io(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
struct lpfc_iocbq *abtsiocbp;
union lpfc_wqe128 *abts_wqe;
int retval;
+ int idx = cmdiocb->hba_wqidx;
/*
* There are certain command types we don't want to abort. And we
@@ -11382,7 +11339,8 @@ lpfc_sli4_abort_nvme_io(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
abtsiocbp->iocb_flag |= LPFC_IO_NVME;
abtsiocbp->vport = vport;
abtsiocbp->wqe_cmpl = lpfc_nvme_abort_fcreq_cmpl;
- retval = lpfc_sli4_issue_wqe(phba, LPFC_FCP_RING, abtsiocbp);
+ retval = lpfc_sli4_issue_wqe(phba, &phba->sli4_hba.hdwq[idx],
+ abtsiocbp);
if (retval) {
lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME,
"6147 Failed abts issue_wqe with status x%x "
@@ -11457,7 +11415,7 @@ lpfc_sli_validate_fcp_iocb(struct lpfc_iocbq *iocbq, struct lpfc_vport *vport,
uint16_t tgt_id, uint64_t lun_id,
lpfc_ctx_cmd ctx_cmd)
{
- struct lpfc_scsi_buf *lpfc_cmd;
+ struct lpfc_io_buf *lpfc_cmd;
int rc = 1;
if (iocbq->vport != vport)
@@ -11467,7 +11425,7 @@ lpfc_sli_validate_fcp_iocb(struct lpfc_iocbq *iocbq, struct lpfc_vport *vport,
!(iocbq->iocb_flag & LPFC_IO_ON_TXCMPLQ))
return rc;
- lpfc_cmd = container_of(iocbq, struct lpfc_scsi_buf, cur_iocbq);
+ lpfc_cmd = container_of(iocbq, struct lpfc_io_buf, cur_iocbq);
if (lpfc_cmd->pCmd == NULL)
return rc;
@@ -11694,14 +11652,14 @@ lpfc_sli_abort_taskmgmt(struct lpfc_vport *vport, struct lpfc_sli_ring *pring,
uint16_t tgt_id, uint64_t lun_id, lpfc_ctx_cmd cmd)
{
struct lpfc_hba *phba = vport->phba;
- struct lpfc_scsi_buf *lpfc_cmd;
+ struct lpfc_io_buf *lpfc_cmd;
struct lpfc_iocbq *abtsiocbq;
struct lpfc_nodelist *ndlp;
struct lpfc_iocbq *iocbq;
IOCB_t *icmd;
int sum, i, ret_val;
unsigned long iflags;
- struct lpfc_sli_ring *pring_s4;
+ struct lpfc_sli_ring *pring_s4 = NULL;
spin_lock_irqsave(&phba->hbalock, iflags);
@@ -11719,17 +11677,46 @@ lpfc_sli_abort_taskmgmt(struct lpfc_vport *vport, struct lpfc_sli_ring *pring,
cmd) != 0)
continue;
+ /* Guard against IO completion being called at same time */
+ lpfc_cmd = container_of(iocbq, struct lpfc_io_buf, cur_iocbq);
+ spin_lock(&lpfc_cmd->buf_lock);
+
+ if (!lpfc_cmd->pCmd) {
+ spin_unlock(&lpfc_cmd->buf_lock);
+ continue;
+ }
+
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ pring_s4 =
+ phba->sli4_hba.hdwq[iocbq->hba_wqidx].fcp_wq->pring;
+ if (!pring_s4) {
+ spin_unlock(&lpfc_cmd->buf_lock);
+ continue;
+ }
+ /* Note: both hbalock and ring_lock must be set here */
+ spin_lock(&pring_s4->ring_lock);
+ }
+
/*
* If the iocbq is already being aborted, don't take a second
* action, but do count it.
*/
- if (iocbq->iocb_flag & LPFC_DRIVER_ABORTED)
+ if ((iocbq->iocb_flag & LPFC_DRIVER_ABORTED) ||
+ !(iocbq->iocb_flag & LPFC_IO_ON_TXCMPLQ)) {
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ spin_unlock(&pring_s4->ring_lock);
+ spin_unlock(&lpfc_cmd->buf_lock);
continue;
+ }
/* issue ABTS for this IOCB based on iotag */
abtsiocbq = __lpfc_sli_get_iocbq(phba);
- if (abtsiocbq == NULL)
+ if (!abtsiocbq) {
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ spin_unlock(&pring_s4->ring_lock);
+ spin_unlock(&lpfc_cmd->buf_lock);
continue;
+ }
icmd = &iocbq->iocb;
abtsiocbq->iocb.un.acxri.abortType = ABORT_TYPE_ABTS;
@@ -11750,7 +11737,6 @@ lpfc_sli_abort_taskmgmt(struct lpfc_vport *vport, struct lpfc_sli_ring *pring,
if (iocbq->iocb_flag & LPFC_IO_FOF)
abtsiocbq->iocb_flag |= LPFC_IO_FOF;
- lpfc_cmd = container_of(iocbq, struct lpfc_scsi_buf, cur_iocbq);
ndlp = lpfc_cmd->rdata->pnode;
if (lpfc_is_link_up(phba) &&
@@ -11769,11 +11755,6 @@ lpfc_sli_abort_taskmgmt(struct lpfc_vport *vport, struct lpfc_sli_ring *pring,
iocbq->iocb_flag |= LPFC_DRIVER_ABORTED;
if (phba->sli_rev == LPFC_SLI_REV4) {
- pring_s4 = lpfc_sli4_calc_ring(phba, abtsiocbq);
- if (!pring_s4)
- continue;
- /* Note: both hbalock and ring_lock must be set here */
- spin_lock(&pring_s4->ring_lock);
ret_val = __lpfc_sli_issue_iocb(phba, pring_s4->ringno,
abtsiocbq, 0);
spin_unlock(&pring_s4->ring_lock);
@@ -11782,6 +11763,7 @@ lpfc_sli_abort_taskmgmt(struct lpfc_vport *vport, struct lpfc_sli_ring *pring,
abtsiocbq, 0);
}
+ spin_unlock(&lpfc_cmd->buf_lock);
if (ret_val == IOCB_ERROR)
__lpfc_sli_release_iocbq(phba, abtsiocbq);
@@ -11816,7 +11798,7 @@ lpfc_sli_wake_iocb_wait(struct lpfc_hba *phba,
{
wait_queue_head_t *pdone_q;
unsigned long iflags;
- struct lpfc_scsi_buf *lpfc_cmd;
+ struct lpfc_io_buf *lpfc_cmd;
spin_lock_irqsave(&phba->hbalock, iflags);
if (cmdiocbq->iocb_flag & LPFC_IO_WAKE_TMO) {
@@ -11845,7 +11827,7 @@ lpfc_sli_wake_iocb_wait(struct lpfc_hba *phba,
/* Set the exchange busy flag for task management commands */
if ((cmdiocbq->iocb_flag & LPFC_IO_FCP) &&
!(cmdiocbq->iocb_flag & LPFC_IO_LIBDFC)) {
- lpfc_cmd = container_of(cmdiocbq, struct lpfc_scsi_buf,
+ lpfc_cmd = container_of(cmdiocbq, struct lpfc_io_buf,
cur_iocbq);
lpfc_cmd->exch_busy = rspiocbq->iocb_flag & LPFC_EXCHANGE_BUSY;
}
@@ -12919,35 +12901,6 @@ lpfc_sli_intr_handler(int irq, void *dev_id)
} /* lpfc_sli_intr_handler */
/**
- * lpfc_sli4_fcp_xri_abort_event_proc - Process fcp xri abort event
- * @phba: pointer to lpfc hba data structure.
- *
- * This routine is invoked by the worker thread to process all the pending
- * SLI4 FCP abort XRI events.
- **/
-void lpfc_sli4_fcp_xri_abort_event_proc(struct lpfc_hba *phba)
-{
- struct lpfc_cq_event *cq_event;
-
- /* First, declare the fcp xri abort event has been handled */
- spin_lock_irq(&phba->hbalock);
- phba->hba_flag &= ~FCP_XRI_ABORT_EVENT;
- spin_unlock_irq(&phba->hbalock);
- /* Now, handle all the fcp xri abort events */
- while (!list_empty(&phba->sli4_hba.sp_fcp_xri_aborted_work_queue)) {
- /* Get the first event from the head of the event queue */
- spin_lock_irq(&phba->hbalock);
- list_remove_head(&phba->sli4_hba.sp_fcp_xri_aborted_work_queue,
- cq_event, struct lpfc_cq_event, list);
- spin_unlock_irq(&phba->hbalock);
- /* Notify aborted XRI for FCP work queue */
- lpfc_sli4_fcp_xri_aborted(phba, &cq_event->cqe.wcqe_axri);
- /* Free the event processed back to the free pool */
- lpfc_sli4_cq_event_release(phba, cq_event);
- }
-}
-
-/**
* lpfc_sli4_els_xri_abort_event_proc - Process els xri abort event
* @phba: pointer to lpfc hba data structure.
*
@@ -13320,11 +13273,14 @@ out_no_mqe_complete:
* Return: true if work posted to worker thread, otherwise false.
**/
static bool
-lpfc_sli4_sp_handle_mcqe(struct lpfc_hba *phba, struct lpfc_cqe *cqe)
+lpfc_sli4_sp_handle_mcqe(struct lpfc_hba *phba, struct lpfc_queue *cq,
+ struct lpfc_cqe *cqe)
{
struct lpfc_mcqe mcqe;
bool workposted;
+ cq->CQ_mbox++;
+
/* Copy the mailbox MCQE and convert endian order as needed */
lpfc_sli4_pcimem_bcopy(cqe, &mcqe, sizeof(struct lpfc_mcqe));
@@ -13443,17 +13399,8 @@ lpfc_sli4_sp_handle_abort_xri_wcqe(struct lpfc_hba *phba,
switch (cq->subtype) {
case LPFC_FCP:
- cq_event = lpfc_cq_event_setup(
- phba, wcqe, sizeof(struct sli4_wcqe_xri_aborted));
- if (!cq_event)
- return false;
- spin_lock_irqsave(&phba->hbalock, iflags);
- list_add_tail(&cq_event->list,
- &phba->sli4_hba.sp_fcp_xri_aborted_work_queue);
- /* Set the fcp xri abort event flag */
- phba->hba_flag |= FCP_XRI_ABORT_EVENT;
- spin_unlock_irqrestore(&phba->hbalock, iflags);
- workposted = true;
+ lpfc_sli4_fcp_xri_aborted(phba, wcqe, cq->hdwq);
+ workposted = false;
break;
case LPFC_NVME_LS: /* NVME LS uses ELS resources */
case LPFC_ELS:
@@ -13461,6 +13408,7 @@ lpfc_sli4_sp_handle_abort_xri_wcqe(struct lpfc_hba *phba,
phba, wcqe, sizeof(struct sli4_wcqe_xri_aborted));
if (!cq_event)
return false;
+ cq_event->hdwq = cq->hdwq;
spin_lock_irqsave(&phba->hbalock, iflags);
list_add_tail(&cq_event->list,
&phba->sli4_hba.sp_els_xri_aborted_work_queue);
@@ -13474,7 +13422,7 @@ lpfc_sli4_sp_handle_abort_xri_wcqe(struct lpfc_hba *phba,
if (phba->nvmet_support)
lpfc_sli4_nvmet_xri_aborted(phba, wcqe);
else
- lpfc_sli4_nvme_xri_aborted(phba, wcqe);
+ lpfc_sli4_nvme_xri_aborted(phba, wcqe, cq->hdwq);
workposted = false;
break;
@@ -13592,7 +13540,7 @@ out:
* lpfc_sli4_sp_handle_cqe - Process a slow path completion queue entry
* @phba: Pointer to HBA context object.
* @cq: Pointer to the completion queue.
- * @wcqe: Pointer to a completion queue entry.
+ * @cqe: Pointer to a completion queue entry.
*
* This routine process a slow-path work-queue or receive queue completion queue
* entry.
@@ -13684,7 +13632,7 @@ lpfc_sli4_sp_handle_eqe(struct lpfc_hba *phba, struct lpfc_eqe *eqe,
/* Save EQ associated with this CQ */
cq->assoc_qp = speq;
- if (!queue_work(phba->wq, &cq->spwork))
+ if (!queue_work_on(cq->chann, phba->wq, &cq->spwork))
lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
"0390 Cannot schedule soft IRQ "
"for CQ eqcqid=%d, cqid=%d on CPU %d\n",
@@ -13692,60 +13640,129 @@ lpfc_sli4_sp_handle_eqe(struct lpfc_hba *phba, struct lpfc_eqe *eqe,
}
/**
- * lpfc_sli4_sp_process_cq - Process a slow-path event queue entry
+ * __lpfc_sli4_process_cq - Process elements of a CQ
* @phba: Pointer to HBA context object.
+ * @cq: Pointer to CQ to be processed
+ * @handler: Routine to process each cqe
+ * @delay: Pointer to usdelay to set in case of rescheduling of the handler
*
- * This routine process a event queue entry from the slow-path event queue.
- * It will check the MajorCode and MinorCode to determine this is for a
- * completion event on a completion queue, if not, an error shall be logged
- * and just return. Otherwise, it will get to the corresponding completion
- * queue and process all the entries on that completion queue, rearm the
- * completion queue, and then return.
+ * This routine processes completion queue entries in a CQ. While a valid
+ * queue element is found, the handler is called. During processing checks
+ * are made for periodic doorbell writes to let the hardware know of
+ * element consumption.
*
+ * If the max limit on cqes to process is hit, or there are no more valid
+ * entries, the loop stops. If we processed a sufficient number of elements,
+ * meaning there is sufficient load, rather than rearming and generating
+ * another interrupt, a cq rescheduling delay will be set. A delay of 0
+ * indicates no rescheduling.
+ *
+ * Returns True if work scheduled, False otherwise.
**/
-static void
-lpfc_sli4_sp_process_cq(struct work_struct *work)
+static bool
+__lpfc_sli4_process_cq(struct lpfc_hba *phba, struct lpfc_queue *cq,
+ bool (*handler)(struct lpfc_hba *, struct lpfc_queue *,
+ struct lpfc_cqe *), unsigned long *delay)
{
- struct lpfc_queue *cq =
- container_of(work, struct lpfc_queue, spwork);
- struct lpfc_hba *phba = cq->phba;
struct lpfc_cqe *cqe;
bool workposted = false;
- int ccount = 0;
+ int count = 0, consumed = 0;
+ bool arm = true;
+
+ /* default - no reschedule */
+ *delay = 0;
+
+ if (cmpxchg(&cq->queue_claimed, 0, 1) != 0)
+ goto rearm_and_exit;
/* Process all the entries to the CQ */
+ cqe = lpfc_sli4_cq_get(cq);
+ while (cqe) {
+#if defined(CONFIG_SCSI_LPFC_DEBUG_FS) && defined(BUILD_NVME)
+ if (phba->ktime_on)
+ cq->isr_timestamp = ktime_get_ns();
+ else
+ cq->isr_timestamp = 0;
+#endif
+ workposted |= handler(phba, cq, cqe);
+ __lpfc_sli4_consume_cqe(phba, cq, cqe);
+
+ consumed++;
+ if (!(++count % cq->max_proc_limit))
+ break;
+
+ if (!(count % cq->notify_interval)) {
+ phba->sli4_hba.sli4_write_cq_db(phba, cq, consumed,
+ LPFC_QUEUE_NOARM);
+ consumed = 0;
+ }
+
+ cqe = lpfc_sli4_cq_get(cq);
+ }
+ if (count >= phba->cfg_cq_poll_threshold) {
+ *delay = 1;
+ arm = false;
+ }
+
+ /* Track the max number of CQEs processed in 1 EQ */
+ if (count > cq->CQ_max_cqe)
+ cq->CQ_max_cqe = count;
+
+ cq->assoc_qp->EQ_cqe_cnt += count;
+
+ /* Catch the no cq entry condition */
+ if (unlikely(count == 0))
+ lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
+ "0369 No entry from completion queue "
+ "qid=%d\n", cq->queue_id);
+
+ cq->queue_claimed = 0;
+
+rearm_and_exit:
+ phba->sli4_hba.sli4_write_cq_db(phba, cq, consumed,
+ arm ? LPFC_QUEUE_REARM : LPFC_QUEUE_NOARM);
+
+ return workposted;
+}
+
+/**
+ * lpfc_sli4_sp_process_cq - Process a slow-path event queue entry
+ * @cq: pointer to CQ to process
+ *
+ * This routine calls the cq processing routine with a handler specific
+ * to the type of queue bound to it.
+ *
+ * The CQ routine returns two values: the first is the calling status,
+ * which indicates whether work was queued to the background discovery
+ * thread. If true, the routine should wakeup the discovery thread;
+ * the second is the delay parameter. If non-zero, rather than rearming
+ * the CQ and yet another interrupt, the CQ handler should be queued so
+ * that it is processed in a subsequent polling action. The value of
+ * the delay indicates when to reschedule it.
+ **/
+static void
+__lpfc_sli4_sp_process_cq(struct lpfc_queue *cq)
+{
+ struct lpfc_hba *phba = cq->phba;
+ unsigned long delay;
+ bool workposted = false;
+
+ /* Process and rearm the CQ */
switch (cq->type) {
case LPFC_MCQ:
- while ((cqe = lpfc_sli4_cq_get(cq))) {
- workposted |= lpfc_sli4_sp_handle_mcqe(phba, cqe);
- if (!(++ccount % cq->entry_repost))
- break;
- cq->CQ_mbox++;
- }
+ workposted |= __lpfc_sli4_process_cq(phba, cq,
+ lpfc_sli4_sp_handle_mcqe,
+ &delay);
break;
case LPFC_WCQ:
- while ((cqe = lpfc_sli4_cq_get(cq))) {
- if (cq->subtype == LPFC_FCP ||
- cq->subtype == LPFC_NVME) {
-#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
- if (phba->ktime_on)
- cq->isr_timestamp = ktime_get_ns();
- else
- cq->isr_timestamp = 0;
-#endif
- workposted |= lpfc_sli4_fp_handle_cqe(phba, cq,
- cqe);
- } else {
- workposted |= lpfc_sli4_sp_handle_cqe(phba, cq,
- cqe);
- }
- if (!(++ccount % cq->entry_repost))
- break;
- }
-
- /* Track the max number of CQEs processed in 1 EQ */
- if (ccount > cq->CQ_max_cqe)
- cq->CQ_max_cqe = ccount;
+ if (cq->subtype == LPFC_FCP || cq->subtype == LPFC_NVME)
+ workposted |= __lpfc_sli4_process_cq(phba, cq,
+ lpfc_sli4_fp_handle_cqe,
+ &delay);
+ else
+ workposted |= __lpfc_sli4_process_cq(phba, cq,
+ lpfc_sli4_sp_handle_cqe,
+ &delay);
break;
default:
lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
@@ -13754,14 +13771,14 @@ lpfc_sli4_sp_process_cq(struct work_struct *work)
return;
}
- /* Catch the no cq entry condition, log an error */
- if (unlikely(ccount == 0))
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
- "0371 No entry from the CQ: identifier "
- "(x%x), type (%d)\n", cq->queue_id, cq->type);
-
- /* In any case, flash and re-arm the RCQ */
- phba->sli4_hba.sli4_cq_release(cq, LPFC_QUEUE_REARM);
+ if (delay) {
+ if (!queue_delayed_work_on(cq->chann, phba->wq,
+ &cq->sched_spwork, delay))
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "0394 Cannot schedule soft IRQ "
+ "for cqid=%d on CPU %d\n",
+ cq->queue_id, cq->chann);
+ }
/* wake up worker thread if there are works to be done */
if (workposted)
@@ -13769,6 +13786,36 @@ lpfc_sli4_sp_process_cq(struct work_struct *work)
}
/**
+ * lpfc_sli4_sp_process_cq - slow-path work handler when started by
+ * interrupt
+ * @work: pointer to work element
+ *
+ * translates from the work handler and calls the slow-path handler.
+ **/
+static void
+lpfc_sli4_sp_process_cq(struct work_struct *work)
+{
+ struct lpfc_queue *cq = container_of(work, struct lpfc_queue, spwork);
+
+ __lpfc_sli4_sp_process_cq(cq);
+}
+
+/**
+ * lpfc_sli4_dly_sp_process_cq - slow-path work handler when started by timer
+ * @work: pointer to work element
+ *
+ * translates from the work handler and calls the slow-path handler.
+ **/
+static void
+lpfc_sli4_dly_sp_process_cq(struct work_struct *work)
+{
+ struct lpfc_queue *cq = container_of(to_delayed_work(work),
+ struct lpfc_queue, sched_spwork);
+
+ __lpfc_sli4_sp_process_cq(cq);
+}
+
+/**
* lpfc_sli4_fp_handle_fcp_wcqe - Process fast-path work queue completion entry
* @phba: Pointer to HBA context object.
* @cq: Pointer to associated CQ
@@ -13999,13 +14046,16 @@ out:
/**
* lpfc_sli4_fp_handle_cqe - Process fast-path work queue completion entry
+ * @phba: adapter with cq
* @cq: Pointer to the completion queue.
* @eqe: Pointer to fast-path completion queue entry.
*
* This routine process a fast-path work queue completion entry from fast-path
* event queue for FCP command response completion.
+ *
+ * Return: true if work posted to worker thread, otherwise false.
**/
-static int
+static bool
lpfc_sli4_fp_handle_cqe(struct lpfc_hba *phba, struct lpfc_queue *cq,
struct lpfc_cqe *cqe)
{
@@ -14072,10 +14122,11 @@ lpfc_sli4_fp_handle_cqe(struct lpfc_hba *phba, struct lpfc_queue *cq,
* completion queue, and then return.
**/
static void
-lpfc_sli4_hba_handle_eqe(struct lpfc_hba *phba, struct lpfc_eqe *eqe,
- uint32_t qidx)
+lpfc_sli4_hba_handle_eqe(struct lpfc_hba *phba, struct lpfc_queue *eq,
+ struct lpfc_eqe *eqe)
{
struct lpfc_queue *cq = NULL;
+ uint32_t qidx = eq->hdwq;
uint16_t cqid, id;
if (unlikely(bf_get_le32(lpfc_eqe_major_code, eqe) != 0)) {
@@ -14090,6 +14141,14 @@ lpfc_sli4_hba_handle_eqe(struct lpfc_hba *phba, struct lpfc_eqe *eqe,
/* Get the reference to the corresponding CQ */
cqid = bf_get_le32(lpfc_eqe_resource_id, eqe);
+ /* Use the fast lookup method first */
+ if (cqid <= phba->sli4_hba.cq_max) {
+ cq = phba->sli4_hba.cq_lookup[cqid];
+ if (cq)
+ goto work_cq;
+ }
+
+ /* Next check for NVMET completion */
if (phba->cfg_nvmet_mrq && phba->sli4_hba.nvmet_cqset) {
id = phba->sli4_hba.nvmet_cqset[0]->queue_id;
if ((cqid >= id) && (cqid < (id + phba->cfg_nvmet_mrq))) {
@@ -14099,20 +14158,6 @@ lpfc_sli4_hba_handle_eqe(struct lpfc_hba *phba, struct lpfc_eqe *eqe,
}
}
- if (phba->sli4_hba.nvme_cq_map &&
- (cqid == phba->sli4_hba.nvme_cq_map[qidx])) {
- /* Process NVME / NVMET command completion */
- cq = phba->sli4_hba.nvme_cq[qidx];
- goto process_cq;
- }
-
- if (phba->sli4_hba.fcp_cq_map &&
- (cqid == phba->sli4_hba.fcp_cq_map[qidx])) {
- /* Process FCP command completion */
- cq = phba->sli4_hba.fcp_cq[qidx];
- goto process_cq;
- }
-
if (phba->sli4_hba.nvmels_cq &&
(cqid == phba->sli4_hba.nvmels_cq->queue_id)) {
/* Process NVME unsol rcv */
@@ -14121,7 +14166,8 @@ lpfc_sli4_hba_handle_eqe(struct lpfc_hba *phba, struct lpfc_eqe *eqe,
/* Otherwise this is a Slow path event */
if (cq == NULL) {
- lpfc_sli4_sp_handle_eqe(phba, eqe, phba->sli4_hba.hba_eq[qidx]);
+ lpfc_sli4_sp_handle_eqe(phba, eqe,
+ phba->sli4_hba.hdwq[qidx].hba_eq);
return;
}
@@ -14134,10 +14180,8 @@ process_cq:
return;
}
- /* Save EQ associated with this CQ */
- cq->assoc_qp = phba->sli4_hba.hba_eq[qidx];
-
- if (!queue_work(phba->wq, &cq->irqwork))
+work_cq:
+ if (!queue_work_on(cq->chann, phba->wq, &cq->irqwork))
lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
"0363 Cannot schedule soft IRQ "
"for CQ eqcqid=%d, cqid=%d on CPU %d\n",
@@ -14145,219 +14189,73 @@ process_cq:
}
/**
- * lpfc_sli4_hba_process_cq - Process a fast-path event queue entry
- * @phba: Pointer to HBA context object.
- * @eqe: Pointer to fast-path event queue entry.
+ * __lpfc_sli4_hba_process_cq - Process a fast-path event queue entry
+ * @cq: Pointer to CQ to be processed
*
- * This routine process a event queue entry from the fast-path event queue.
- * It will check the MajorCode and MinorCode to determine this is for a
- * completion event on a completion queue, if not, an error shall be logged
- * and just return. Otherwise, it will get to the corresponding completion
- * queue and process all the entries on the completion queue, rearm the
- * completion queue, and then return.
+ * This routine calls the cq processing routine with the handler for
+ * fast path CQEs.
+ *
+ * The CQ routine returns two values: the first is the calling status,
+ * which indicates whether work was queued to the background discovery
+ * thread. If true, the routine should wakeup the discovery thread;
+ * the second is the delay parameter. If non-zero, rather than rearming
+ * the CQ and yet another interrupt, the CQ handler should be queued so
+ * that it is processed in a subsequent polling action. The value of
+ * the delay indicates when to reschedule it.
**/
static void
-lpfc_sli4_hba_process_cq(struct work_struct *work)
+__lpfc_sli4_hba_process_cq(struct lpfc_queue *cq)
{
- struct lpfc_queue *cq =
- container_of(work, struct lpfc_queue, irqwork);
struct lpfc_hba *phba = cq->phba;
- struct lpfc_cqe *cqe;
+ unsigned long delay;
bool workposted = false;
- int ccount = 0;
-
- /* Process all the entries to the CQ */
- while ((cqe = lpfc_sli4_cq_get(cq))) {
-#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
- if (phba->ktime_on)
- cq->isr_timestamp = ktime_get_ns();
- else
- cq->isr_timestamp = 0;
-#endif
- workposted |= lpfc_sli4_fp_handle_cqe(phba, cq, cqe);
- if (!(++ccount % cq->entry_repost))
- break;
- }
- /* Track the max number of CQEs processed in 1 EQ */
- if (ccount > cq->CQ_max_cqe)
- cq->CQ_max_cqe = ccount;
- cq->assoc_qp->EQ_cqe_cnt += ccount;
+ /* process and rearm the CQ */
+ workposted |= __lpfc_sli4_process_cq(phba, cq, lpfc_sli4_fp_handle_cqe,
+ &delay);
- /* Catch the no cq entry condition */
- if (unlikely(ccount == 0))
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
- "0369 No entry from fast-path completion "
- "queue fcpcqid=%d\n", cq->queue_id);
-
- /* In any case, flash and re-arm the CQ */
- phba->sli4_hba.sli4_cq_release(cq, LPFC_QUEUE_REARM);
+ if (delay) {
+ if (!queue_delayed_work_on(cq->chann, phba->wq,
+ &cq->sched_irqwork, delay))
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "0367 Cannot schedule soft IRQ "
+ "for cqid=%d on CPU %d\n",
+ cq->queue_id, cq->chann);
+ }
/* wake up worker thread if there are works to be done */
if (workposted)
lpfc_worker_wake_up(phba);
}
-static void
-lpfc_sli4_eq_flush(struct lpfc_hba *phba, struct lpfc_queue *eq)
-{
- struct lpfc_eqe *eqe;
-
- /* walk all the EQ entries and drop on the floor */
- while ((eqe = lpfc_sli4_eq_get(eq)))
- ;
-
- /* Clear and re-arm the EQ */
- phba->sli4_hba.sli4_eq_release(eq, LPFC_QUEUE_REARM);
-}
-
-
/**
- * lpfc_sli4_fof_handle_eqe - Process a Flash Optimized Fabric event queue
- * entry
- * @phba: Pointer to HBA context object.
- * @eqe: Pointer to fast-path event queue entry.
+ * lpfc_sli4_hba_process_cq - fast-path work handler when started by
+ * interrupt
+ * @work: pointer to work element
*
- * This routine process a event queue entry from the Flash Optimized Fabric
- * event queue. It will check the MajorCode and MinorCode to determine this
- * is for a completion event on a completion queue, if not, an error shall be
- * logged and just return. Otherwise, it will get to the corresponding
- * completion queue and process all the entries on the completion queue, rearm
- * the completion queue, and then return.
+ * translates from the work handler and calls the fast-path handler.
**/
static void
-lpfc_sli4_fof_handle_eqe(struct lpfc_hba *phba, struct lpfc_eqe *eqe)
+lpfc_sli4_hba_process_cq(struct work_struct *work)
{
- struct lpfc_queue *cq;
- uint16_t cqid;
-
- if (unlikely(bf_get_le32(lpfc_eqe_major_code, eqe) != 0)) {
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
- "9147 Not a valid completion "
- "event: majorcode=x%x, minorcode=x%x\n",
- bf_get_le32(lpfc_eqe_major_code, eqe),
- bf_get_le32(lpfc_eqe_minor_code, eqe));
- return;
- }
+ struct lpfc_queue *cq = container_of(work, struct lpfc_queue, irqwork);
- /* Get the reference to the corresponding CQ */
- cqid = bf_get_le32(lpfc_eqe_resource_id, eqe);
-
- /* Next check for OAS */
- cq = phba->sli4_hba.oas_cq;
- if (unlikely(!cq)) {
- if (phba->sli.sli_flag & LPFC_SLI_ACTIVE)
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
- "9148 OAS completion queue "
- "does not exist\n");
- return;
- }
-
- if (unlikely(cqid != cq->queue_id)) {
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
- "9149 Miss-matched fast-path compl "
- "queue id: eqcqid=%d, fcpcqid=%d\n",
- cqid, cq->queue_id);
- return;
- }
-
- /* Save EQ associated with this CQ */
- cq->assoc_qp = phba->sli4_hba.fof_eq;
-
- /* CQ work will be processed on CPU affinitized to this IRQ */
- if (!queue_work(phba->wq, &cq->irqwork))
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
- "0367 Cannot schedule soft IRQ "
- "for CQ eqcqid=%d, cqid=%d on CPU %d\n",
- cqid, cq->queue_id, smp_processor_id());
+ __lpfc_sli4_hba_process_cq(cq);
}
/**
- * lpfc_sli4_fof_intr_handler - HBA interrupt handler to SLI-4 device
- * @irq: Interrupt number.
- * @dev_id: The device context pointer.
+ * lpfc_sli4_hba_process_cq - fast-path work handler when started by timer
+ * @work: pointer to work element
*
- * This function is directly called from the PCI layer as an interrupt
- * service routine when device with SLI-4 interface spec is enabled with
- * MSI-X multi-message interrupt mode and there is a Flash Optimized Fabric
- * IOCB ring event in the HBA. However, when the device is enabled with either
- * MSI or Pin-IRQ interrupt mode, this function is called as part of the
- * device-level interrupt handler. When the PCI slot is in error recovery
- * or the HBA is undergoing initialization, the interrupt handler will not
- * process the interrupt. The Flash Optimized Fabric ring event are handled in
- * the intrrupt context. This function is called without any lock held.
- * It gets the hbalock to access and update SLI data structures. Note that,
- * the EQ to CQ are one-to-one map such that the EQ index is
- * equal to that of CQ index.
- *
- * This function returns IRQ_HANDLED when interrupt is handled else it
- * returns IRQ_NONE.
+ * translates from the work handler and calls the fast-path handler.
**/
-irqreturn_t
-lpfc_sli4_fof_intr_handler(int irq, void *dev_id)
+static void
+lpfc_sli4_dly_hba_process_cq(struct work_struct *work)
{
- struct lpfc_hba *phba;
- struct lpfc_hba_eq_hdl *hba_eq_hdl;
- struct lpfc_queue *eq;
- struct lpfc_eqe *eqe;
- unsigned long iflag;
- int ecount = 0;
-
- /* Get the driver's phba structure from the dev_id */
- hba_eq_hdl = (struct lpfc_hba_eq_hdl *)dev_id;
- phba = hba_eq_hdl->phba;
+ struct lpfc_queue *cq = container_of(to_delayed_work(work),
+ struct lpfc_queue, sched_irqwork);
- if (unlikely(!phba))
- return IRQ_NONE;
-
- /* Get to the EQ struct associated with this vector */
- eq = phba->sli4_hba.fof_eq;
- if (unlikely(!eq))
- return IRQ_NONE;
-
- /* Check device state for handling interrupt */
- if (unlikely(lpfc_intr_state_check(phba))) {
- /* Check again for link_state with lock held */
- spin_lock_irqsave(&phba->hbalock, iflag);
- if (phba->link_state < LPFC_LINK_DOWN)
- /* Flush, clear interrupt, and rearm the EQ */
- lpfc_sli4_eq_flush(phba, eq);
- spin_unlock_irqrestore(&phba->hbalock, iflag);
- return IRQ_NONE;
- }
-
- /*
- * Process all the event on FCP fast-path EQ
- */
- while ((eqe = lpfc_sli4_eq_get(eq))) {
- lpfc_sli4_fof_handle_eqe(phba, eqe);
- if (!(++ecount % eq->entry_repost))
- break;
- eq->EQ_processed++;
- }
-
- /* Track the max number of EQEs processed in 1 intr */
- if (ecount > eq->EQ_max_eqe)
- eq->EQ_max_eqe = ecount;
-
-
- if (unlikely(ecount == 0)) {
- eq->EQ_no_entry++;
-
- if (phba->intr_type == MSIX)
- /* MSI-X treated interrupt served as no EQ share INT */
- lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
- "9145 MSI-X interrupt with no EQE\n");
- else {
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
- "9146 ISR interrupt with no EQE\n");
- /* Non MSI-X treated on interrupt as EQ share INT */
- return IRQ_NONE;
- }
- }
- /* Always clear and re-arm the fast-path EQ */
- phba->sli4_hba.sli4_eq_release(eq, LPFC_QUEUE_REARM);
- return IRQ_HANDLED;
+ __lpfc_sli4_hba_process_cq(cq);
}
/**
@@ -14392,10 +14290,11 @@ lpfc_sli4_hba_intr_handler(int irq, void *dev_id)
struct lpfc_hba *phba;
struct lpfc_hba_eq_hdl *hba_eq_hdl;
struct lpfc_queue *fpeq;
- struct lpfc_eqe *eqe;
unsigned long iflag;
int ecount = 0;
int hba_eqidx;
+ struct lpfc_eq_intr_info *eqi;
+ uint32_t icnt;
/* Get the driver's phba structure from the dev_id */
hba_eq_hdl = (struct lpfc_hba_eq_hdl *)dev_id;
@@ -14404,23 +14303,14 @@ lpfc_sli4_hba_intr_handler(int irq, void *dev_id)
if (unlikely(!phba))
return IRQ_NONE;
- if (unlikely(!phba->sli4_hba.hba_eq))
+ if (unlikely(!phba->sli4_hba.hdwq))
return IRQ_NONE;
/* Get to the EQ struct associated with this vector */
- fpeq = phba->sli4_hba.hba_eq[hba_eqidx];
+ fpeq = phba->sli4_hba.hdwq[hba_eqidx].hba_eq;
if (unlikely(!fpeq))
return IRQ_NONE;
- if (lpfc_fcp_look_ahead) {
- if (atomic_dec_and_test(&hba_eq_hdl->hba_eq_in_use))
- phba->sli4_hba.sli4_eq_clr_intr(fpeq);
- else {
- atomic_inc(&hba_eq_hdl->hba_eq_in_use);
- return IRQ_NONE;
- }
- }
-
/* Check device state for handling interrupt */
if (unlikely(lpfc_intr_state_check(phba))) {
/* Check again for link_state with lock held */
@@ -14429,36 +14319,25 @@ lpfc_sli4_hba_intr_handler(int irq, void *dev_id)
/* Flush, clear interrupt, and rearm the EQ */
lpfc_sli4_eq_flush(phba, fpeq);
spin_unlock_irqrestore(&phba->hbalock, iflag);
- if (lpfc_fcp_look_ahead)
- atomic_inc(&hba_eq_hdl->hba_eq_in_use);
return IRQ_NONE;
}
- /*
- * Process all the event on FCP fast-path EQ
- */
- while ((eqe = lpfc_sli4_eq_get(fpeq))) {
- lpfc_sli4_hba_handle_eqe(phba, eqe, hba_eqidx);
- if (!(++ecount % fpeq->entry_repost))
- break;
- fpeq->EQ_processed++;
- }
+ eqi = phba->sli4_hba.eq_info;
+ icnt = this_cpu_inc_return(eqi->icnt);
+ fpeq->last_cpu = smp_processor_id();
- /* Track the max number of EQEs processed in 1 intr */
- if (ecount > fpeq->EQ_max_eqe)
- fpeq->EQ_max_eqe = ecount;
+ if (icnt > LPFC_EQD_ISR_TRIGGER &&
+ phba->cfg_irq_chann == 1 &&
+ phba->cfg_auto_imax &&
+ fpeq->q_mode != LPFC_MAX_AUTO_EQ_DELAY &&
+ phba->sli.sli_flag & LPFC_SLI_USE_EQDR)
+ lpfc_sli4_mod_hba_eq_delay(phba, fpeq, LPFC_MAX_AUTO_EQ_DELAY);
- /* Always clear and re-arm the fast-path EQ */
- phba->sli4_hba.sli4_eq_release(fpeq, LPFC_QUEUE_REARM);
+ /* process and rearm the EQ */
+ ecount = lpfc_sli4_process_eq(phba, fpeq);
if (unlikely(ecount == 0)) {
fpeq->EQ_no_entry++;
-
- if (lpfc_fcp_look_ahead) {
- atomic_inc(&hba_eq_hdl->hba_eq_in_use);
- return IRQ_NONE;
- }
-
if (phba->intr_type == MSIX)
/* MSI-X treated interrupt served as no EQ share INT */
lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
@@ -14468,9 +14347,6 @@ lpfc_sli4_hba_intr_handler(int irq, void *dev_id)
return IRQ_NONE;
}
- if (lpfc_fcp_look_ahead)
- atomic_inc(&hba_eq_hdl->hba_eq_in_use);
-
return IRQ_HANDLED;
} /* lpfc_sli4_fp_intr_handler */
@@ -14508,20 +14384,13 @@ lpfc_sli4_intr_handler(int irq, void *dev_id)
/*
* Invoke fast-path host attention interrupt handling as appropriate.
*/
- for (qidx = 0; qidx < phba->io_channel_irqs; qidx++) {
+ for (qidx = 0; qidx < phba->cfg_irq_chann; qidx++) {
hba_irq_rc = lpfc_sli4_hba_intr_handler(irq,
&phba->sli4_hba.hba_eq_hdl[qidx]);
if (hba_irq_rc == IRQ_HANDLED)
hba_handled |= true;
}
- if (phba->cfg_fof) {
- hba_irq_rc = lpfc_sli4_fof_intr_handler(irq,
- &phba->sli4_hba.hba_eq_hdl[qidx]);
- if (hba_irq_rc == IRQ_HANDLED)
- hba_handled |= true;
- }
-
return (hba_handled == true) ? IRQ_HANDLED : IRQ_NONE;
} /* lpfc_sli4_intr_handler */
@@ -14553,6 +14422,9 @@ lpfc_sli4_queue_free(struct lpfc_queue *queue)
kfree(queue->rqbp);
}
+ if (!list_empty(&queue->cpu_list))
+ list_del(&queue->cpu_list);
+
if (!list_empty(&queue->wq_list))
list_del(&queue->wq_list);
@@ -14601,6 +14473,7 @@ lpfc_sli4_queue_alloc(struct lpfc_hba *phba, uint32_t page_size,
INIT_LIST_HEAD(&queue->wqfull_list);
INIT_LIST_HEAD(&queue->page_list);
INIT_LIST_HEAD(&queue->child_list);
+ INIT_LIST_HEAD(&queue->cpu_list);
/* Set queue parameters now. If the system cannot provide memory
* resources, the free routine needs to know what was allocated.
@@ -14633,8 +14506,10 @@ lpfc_sli4_queue_alloc(struct lpfc_hba *phba, uint32_t page_size,
}
INIT_WORK(&queue->irqwork, lpfc_sli4_hba_process_cq);
INIT_WORK(&queue->spwork, lpfc_sli4_sp_process_cq);
+ INIT_DELAYED_WORK(&queue->sched_irqwork, lpfc_sli4_dly_hba_process_cq);
+ INIT_DELAYED_WORK(&queue->sched_spwork, lpfc_sli4_dly_sp_process_cq);
- /* entry_repost will be set during q creation */
+ /* notify_interval will be set during q creation */
return queue;
out_fail:
@@ -14671,43 +14546,76 @@ lpfc_dual_chute_pci_bar_map(struct lpfc_hba *phba, uint16_t pci_barset)
}
/**
- * lpfc_modify_hba_eq_delay - Modify Delay Multiplier on FCP EQs
- * @phba: HBA structure that indicates port to create a queue on.
- * @startq: The starting FCP EQ to modify
+ * lpfc_modify_hba_eq_delay - Modify Delay Multiplier on EQs
+ * @phba: HBA structure that EQs are on.
+ * @startq: The starting EQ index to modify
+ * @numq: The number of EQs (consecutive indexes) to modify
+ * @usdelay: amount of delay
*
- * This function sends an MODIFY_EQ_DELAY mailbox command to the HBA.
- * The command allows up to LPFC_MAX_EQ_DELAY_EQID_CNT EQ ID's to be
- * updated in one mailbox command.
+ * This function revises the EQ delay on 1 or more EQs. The EQ delay
+ * is set either by writing to a register (if supported by the SLI Port)
+ * or by mailbox command. The mailbox command allows several EQs to be
+ * updated at once.
*
- * The @phba struct is used to send mailbox command to HBA. The @startq
- * is used to get the starting FCP EQ to change.
- * This function is asynchronous and will wait for the mailbox
- * command to finish before continuing.
+ * The @phba struct is used to send a mailbox command to HBA. The @startq
+ * is used to get the starting EQ index to change. The @numq value is
+ * used to specify how many consecutive EQ indexes, starting at EQ index,
+ * are to be changed. This function is asynchronous and will wait for any
+ * mailbox commands to finish before returning.
*
- * On success this function will return a zero. If unable to allocate enough
- * memory this function will return -ENOMEM. If the queue create mailbox command
- * fails this function will return -ENXIO.
+ * On success this function will return a zero. If unable to allocate
+ * enough memory this function will return -ENOMEM. If a mailbox command
+ * fails this function will return -ENXIO. Note: on ENXIO, some EQs may
+ * have had their delay multipler changed.
**/
-int
+void
lpfc_modify_hba_eq_delay(struct lpfc_hba *phba, uint32_t startq,
- uint32_t numq, uint32_t imax)
+ uint32_t numq, uint32_t usdelay)
{
struct lpfc_mbx_modify_eq_delay *eq_delay;
LPFC_MBOXQ_t *mbox;
struct lpfc_queue *eq;
- int cnt, rc, length, status = 0;
+ int cnt = 0, rc, length;
uint32_t shdr_status, shdr_add_status;
- uint32_t result, val;
+ uint32_t dmult;
int qidx;
union lpfc_sli4_cfg_shdr *shdr;
- uint16_t dmult;
- if (startq >= phba->io_channel_irqs)
- return 0;
+ if (startq >= phba->cfg_irq_chann)
+ return;
+
+ if (usdelay > 0xFFFF) {
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT | LOG_FCP | LOG_NVME,
+ "6429 usdelay %d too large. Scaled down to "
+ "0xFFFF.\n", usdelay);
+ usdelay = 0xFFFF;
+ }
+
+ /* set values by EQ_DELAY register if supported */
+ if (phba->sli.sli_flag & LPFC_SLI_USE_EQDR) {
+ for (qidx = startq; qidx < phba->cfg_irq_chann; qidx++) {
+ eq = phba->sli4_hba.hdwq[qidx].hba_eq;
+ if (!eq)
+ continue;
+
+ lpfc_sli4_mod_hba_eq_delay(phba, eq, usdelay);
+
+ if (++cnt >= numq)
+ break;
+ }
+
+ return;
+ }
+
+ /* Otherwise, set values by mailbox cmd */
mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
- if (!mbox)
- return -ENOMEM;
+ if (!mbox) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT | LOG_FCP | LOG_NVME,
+ "6428 Failed allocating mailbox cmd buffer."
+ " EQ delay was not set.\n");
+ return;
+ }
length = (sizeof(struct lpfc_mbx_modify_eq_delay) -
sizeof(struct lpfc_sli4_cfg_mhdr));
lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_COMMON,
@@ -14716,45 +14624,22 @@ lpfc_modify_hba_eq_delay(struct lpfc_hba *phba, uint32_t startq,
eq_delay = &mbox->u.mqe.un.eq_delay;
/* Calculate delay multiper from maximum interrupt per second */
- result = imax / phba->io_channel_irqs;
- if (result > LPFC_DMULT_CONST || result == 0)
- dmult = 0;
- else
- dmult = LPFC_DMULT_CONST/result - 1;
+ dmult = (usdelay * LPFC_DMULT_CONST) / LPFC_SEC_TO_USEC;
+ if (dmult)
+ dmult--;
if (dmult > LPFC_DMULT_MAX)
dmult = LPFC_DMULT_MAX;
- cnt = 0;
- for (qidx = startq; qidx < phba->io_channel_irqs; qidx++) {
- eq = phba->sli4_hba.hba_eq[qidx];
+ for (qidx = startq; qidx < phba->cfg_irq_chann; qidx++) {
+ eq = phba->sli4_hba.hdwq[qidx].hba_eq;
if (!eq)
continue;
- eq->q_mode = imax;
+ eq->q_mode = usdelay;
eq_delay->u.request.eq[cnt].eq_id = eq->queue_id;
eq_delay->u.request.eq[cnt].phase = 0;
eq_delay->u.request.eq[cnt].delay_multi = dmult;
- cnt++;
-
- /* q_mode is only used for auto_imax */
- if (phba->sli.sli_flag & LPFC_SLI_USE_EQDR) {
- /* Use EQ Delay Register method for q_mode */
-
- /* Convert for EQ Delay register */
- val = phba->cfg_fcp_imax;
- if (val) {
- /* First, interrupts per sec per EQ */
- val = phba->cfg_fcp_imax /
- phba->io_channel_irqs;
-
- /* us delay between each interrupt */
- val = LPFC_SEC_TO_USEC / val;
- }
- eq->q_mode = val;
- } else {
- eq->q_mode = imax;
- }
- if (cnt >= numq)
+ if (++cnt >= numq)
break;
}
eq_delay->u.request.num_eq = cnt;
@@ -14772,10 +14657,9 @@ lpfc_modify_hba_eq_delay(struct lpfc_hba *phba, uint32_t startq,
"2512 MODIFY_EQ_DELAY mailbox failed with "
"status x%x add_status x%x, mbx status x%x\n",
shdr_status, shdr_add_status, rc);
- status = -ENXIO;
}
mempool_free(mbox, phba->mbox_mem_pool);
- return status;
+ return;
}
/**
@@ -14900,8 +14784,8 @@ lpfc_eq_create(struct lpfc_hba *phba, struct lpfc_queue *eq, uint32_t imax)
if (eq->queue_id == 0xFFFF)
status = -ENXIO;
eq->host_index = 0;
- eq->hba_index = 0;
- eq->entry_repost = LPFC_EQ_REPOST;
+ eq->notify_interval = LPFC_EQ_NOTIFY_INTRVL;
+ eq->max_proc_limit = LPFC_EQ_MAX_PROC_LIMIT;
mempool_free(mbox, phba->mbox_mem_pool);
return status;
@@ -15039,10 +14923,13 @@ lpfc_cq_create(struct lpfc_hba *phba, struct lpfc_queue *cq,
cq->subtype = subtype;
cq->queue_id = bf_get(lpfc_mbx_cq_create_q_id, &cq_create->u.response);
cq->assoc_qid = eq->queue_id;
+ cq->assoc_qp = eq;
cq->host_index = 0;
- cq->hba_index = 0;
- cq->entry_repost = LPFC_CQ_REPOST;
+ cq->notify_interval = LPFC_CQ_NOTIFY_INTRVL;
+ cq->max_proc_limit = min(phba->cfg_cq_max_proc_limit, cq->entry_count);
+ if (cq->queue_id > phba->sli4_hba.cq_max)
+ phba->sli4_hba.cq_max = cq->queue_id;
out:
mempool_free(mbox, phba->mbox_mem_pool);
return status;
@@ -15052,7 +14939,7 @@ out:
* lpfc_cq_create_set - Create a set of Completion Queues on the HBA for MRQ
* @phba: HBA structure that indicates port to create a queue on.
* @cqp: The queue structure array to use to create the completion queues.
- * @eqp: The event queue array to bind these completion queues to.
+ * @hdwq: The hardware queue array with the EQ to bind completion queues to.
*
* This function creates a set of completion queue, s to support MRQ
* as detailed in @cqp, on a port,
@@ -15072,7 +14959,8 @@ out:
**/
int
lpfc_cq_create_set(struct lpfc_hba *phba, struct lpfc_queue **cqp,
- struct lpfc_queue **eqp, uint32_t type, uint32_t subtype)
+ struct lpfc_sli4_hdw_queue *hdwq, uint32_t type,
+ uint32_t subtype)
{
struct lpfc_queue *cq;
struct lpfc_queue *eq;
@@ -15087,7 +14975,7 @@ lpfc_cq_create_set(struct lpfc_hba *phba, struct lpfc_queue **cqp,
/* sanity check on queue memory */
numcq = phba->cfg_nvmet_mrq;
- if (!cqp || !eqp || !numcq)
+ if (!cqp || !hdwq || !numcq)
return -ENODEV;
mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
@@ -15114,7 +15002,7 @@ lpfc_cq_create_set(struct lpfc_hba *phba, struct lpfc_queue **cqp,
for (idx = 0; idx < numcq; idx++) {
cq = cqp[idx];
- eq = eqp[idx];
+ eq = hdwq[idx].hba_eq;
if (!cq || !eq) {
status = -ENOMEM;
goto out;
@@ -15247,9 +15135,11 @@ lpfc_cq_create_set(struct lpfc_hba *phba, struct lpfc_queue **cqp,
cq->type = type;
cq->subtype = subtype;
cq->assoc_qid = eq->queue_id;
+ cq->assoc_qp = eq;
cq->host_index = 0;
- cq->hba_index = 0;
- cq->entry_repost = LPFC_CQ_REPOST;
+ cq->notify_interval = LPFC_CQ_NOTIFY_INTRVL;
+ cq->max_proc_limit = min(phba->cfg_cq_max_proc_limit,
+ cq->entry_count);
cq->chann = idx;
rc = 0;
@@ -15287,6 +15177,8 @@ lpfc_cq_create_set(struct lpfc_hba *phba, struct lpfc_queue **cqp,
for (idx = 0; idx < numcq; idx++) {
cq = cqp[idx];
cq->queue_id = rc + idx;
+ if (cq->queue_id > phba->sli4_hba.cq_max)
+ phba->sli4_hba.cq_max = cq->queue_id;
}
out:
@@ -15499,7 +15391,6 @@ lpfc_mq_create(struct lpfc_hba *phba, struct lpfc_queue *mq,
mq->subtype = subtype;
mq->host_index = 0;
mq->hba_index = 0;
- mq->entry_repost = LPFC_MQ_REPOST;
/* link the mq onto the parent cq child list */
list_add_tail(&mq->list, &cq->child_list);
@@ -15765,7 +15656,7 @@ lpfc_wq_create(struct lpfc_hba *phba, struct lpfc_queue *wq,
wq->subtype = subtype;
wq->host_index = 0;
wq->hba_index = 0;
- wq->entry_repost = LPFC_RELEASE_NOTIFICATION_INTERVAL;
+ wq->notify_interval = LPFC_WQ_NOTIFY_INTRVL;
/* link the wq onto the parent cq child list */
list_add_tail(&wq->list, &cq->child_list);
@@ -15959,7 +15850,7 @@ lpfc_rq_create(struct lpfc_hba *phba, struct lpfc_queue *hrq,
hrq->subtype = subtype;
hrq->host_index = 0;
hrq->hba_index = 0;
- hrq->entry_repost = LPFC_RQ_REPOST;
+ hrq->notify_interval = LPFC_RQ_NOTIFY_INTRVL;
/* now create the data queue */
lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_FCOE,
@@ -16052,7 +15943,7 @@ lpfc_rq_create(struct lpfc_hba *phba, struct lpfc_queue *hrq,
drq->subtype = subtype;
drq->host_index = 0;
drq->hba_index = 0;
- drq->entry_repost = LPFC_RQ_REPOST;
+ drq->notify_interval = LPFC_RQ_NOTIFY_INTRVL;
/* link the header and data RQs onto the parent cq child list */
list_add_tail(&hrq->list, &cq->child_list);
@@ -16210,7 +16101,7 @@ lpfc_mrq_create(struct lpfc_hba *phba, struct lpfc_queue **hrqp,
hrq->subtype = subtype;
hrq->host_index = 0;
hrq->hba_index = 0;
- hrq->entry_repost = LPFC_RQ_REPOST;
+ hrq->notify_interval = LPFC_RQ_NOTIFY_INTRVL;
drq->db_format = LPFC_DB_RING_FORMAT;
drq->db_regaddr = phba->sli4_hba.RQDBregaddr;
@@ -16219,7 +16110,7 @@ lpfc_mrq_create(struct lpfc_hba *phba, struct lpfc_queue **hrqp,
drq->subtype = subtype;
drq->host_index = 0;
drq->hba_index = 0;
- drq->entry_repost = LPFC_RQ_REPOST;
+ drq->notify_interval = LPFC_RQ_NOTIFY_INTRVL;
list_add_tail(&hrq->list, &cq->child_list);
list_add_tail(&drq->list, &cq->child_list);
@@ -16279,6 +16170,7 @@ lpfc_eq_destroy(struct lpfc_hba *phba, struct lpfc_queue *eq)
/* sanity check on queue memory */
if (!eq)
return -ENODEV;
+
mbox = mempool_alloc(eq->phba->mbox_mem_pool, GFP_KERNEL);
if (!mbox)
return -ENOMEM;
@@ -16828,22 +16720,21 @@ lpfc_sli4_post_sgl_list(struct lpfc_hba *phba,
}
/**
- * lpfc_sli4_post_scsi_sgl_block - post a block of scsi sgl list to firmware
+ * lpfc_sli4_post_io_sgl_block - post a block of nvme sgl list to firmware
* @phba: pointer to lpfc hba data structure.
- * @sblist: pointer to scsi buffer list.
+ * @nblist: pointer to nvme buffer list.
* @count: number of scsi buffers on the list.
*
* This routine is invoked to post a block of @count scsi sgl pages from a
- * SCSI buffer list @sblist to the HBA using non-embedded mailbox command.
+ * SCSI buffer list @nblist to the HBA using non-embedded mailbox command.
* No Lock is held.
*
**/
-int
-lpfc_sli4_post_scsi_sgl_block(struct lpfc_hba *phba,
- struct list_head *sblist,
- int count)
+static int
+lpfc_sli4_post_io_sgl_block(struct lpfc_hba *phba, struct list_head *nblist,
+ int count)
{
- struct lpfc_scsi_buf *psb;
+ struct lpfc_io_buf *lpfc_ncmd;
struct lpfc_mbx_post_uembed_sgl_page1 *sgl;
struct sgl_page_pairs *sgl_pg_pairs;
void *viraddr;
@@ -16861,25 +16752,25 @@ lpfc_sli4_post_scsi_sgl_block(struct lpfc_hba *phba,
sizeof(union lpfc_sli4_cfg_shdr) + sizeof(uint32_t);
if (reqlen > SLI4_PAGE_SIZE) {
lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
- "0217 Block sgl registration required DMA "
+ "6118 Block sgl registration required DMA "
"size (%d) great than a page\n", reqlen);
return -ENOMEM;
}
mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
if (!mbox) {
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "0283 Failed to allocate mbox cmd memory\n");
+ "6119 Failed to allocate mbox cmd memory\n");
return -ENOMEM;
}
/* Allocate DMA memory and set up the non-embedded mailbox command */
alloclen = lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_FCOE,
- LPFC_MBOX_OPCODE_FCOE_POST_SGL_PAGES, reqlen,
- LPFC_SLI4_MBX_NEMBED);
+ LPFC_MBOX_OPCODE_FCOE_POST_SGL_PAGES,
+ reqlen, LPFC_SLI4_MBX_NEMBED);
if (alloclen < reqlen) {
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "2561 Allocated DMA memory size (%d) is "
+ "6120 Allocated DMA memory size (%d) is "
"less than the requested DMA memory "
"size (%d)\n", alloclen, reqlen);
lpfc_sli4_mbox_cmd_free(phba, mbox);
@@ -16894,14 +16785,15 @@ lpfc_sli4_post_scsi_sgl_block(struct lpfc_hba *phba,
sgl_pg_pairs = &sgl->sgl_pg_pairs;
pg_pairs = 0;
- list_for_each_entry(psb, sblist, list) {
+ list_for_each_entry(lpfc_ncmd, nblist, list) {
/* Set up the sge entry */
sgl_pg_pairs->sgl_pg0_addr_lo =
- cpu_to_le32(putPaddrLow(psb->dma_phys_bpl));
+ cpu_to_le32(putPaddrLow(lpfc_ncmd->dma_phys_sgl));
sgl_pg_pairs->sgl_pg0_addr_hi =
- cpu_to_le32(putPaddrHigh(psb->dma_phys_bpl));
+ cpu_to_le32(putPaddrHigh(lpfc_ncmd->dma_phys_sgl));
if (phba->cfg_sg_dma_buf_size > SGL_PAGE_SIZE)
- pdma_phys_bpl1 = psb->dma_phys_bpl + SGL_PAGE_SIZE;
+ pdma_phys_bpl1 = lpfc_ncmd->dma_phys_sgl +
+ SGL_PAGE_SIZE;
else
pdma_phys_bpl1 = 0;
sgl_pg_pairs->sgl_pg1_addr_lo =
@@ -16910,7 +16802,7 @@ lpfc_sli4_post_scsi_sgl_block(struct lpfc_hba *phba,
cpu_to_le32(putPaddrHigh(pdma_phys_bpl1));
/* Keep the first xritag on the list */
if (pg_pairs == 0)
- xritag_start = psb->cur_iocbq.sli4_xritag;
+ xritag_start = lpfc_ncmd->cur_iocbq.sli4_xritag;
sgl_pg_pairs++;
pg_pairs++;
}
@@ -16919,20 +16811,20 @@ lpfc_sli4_post_scsi_sgl_block(struct lpfc_hba *phba,
/* Perform endian conversion if necessary */
sgl->word0 = cpu_to_le32(sgl->word0);
- if (!phba->sli4_hba.intr_enable)
+ if (!phba->sli4_hba.intr_enable) {
rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL);
- else {
+ } else {
mbox_tmo = lpfc_mbox_tmo_val(phba, mbox);
rc = lpfc_sli_issue_mbox_wait(phba, mbox, mbox_tmo);
}
- shdr = (union lpfc_sli4_cfg_shdr *) &sgl->cfg_shdr;
+ shdr = (union lpfc_sli4_cfg_shdr *)&sgl->cfg_shdr;
shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
if (rc != MBX_TIMEOUT)
lpfc_sli4_mbox_cmd_free(phba, mbox);
if (shdr_status || shdr_add_status || rc) {
lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
- "2564 POST_SGL_BLOCK mailbox command failed "
+ "6125 POST_SGL_BLOCK mailbox command failed "
"status x%x add_status x%x mbx status x%x\n",
shdr_status, shdr_add_status, rc);
rc = -ENXIO;
@@ -16941,6 +16833,134 @@ lpfc_sli4_post_scsi_sgl_block(struct lpfc_hba *phba,
}
/**
+ * lpfc_sli4_post_io_sgl_list - Post blocks of nvme buffer sgls from a list
+ * @phba: pointer to lpfc hba data structure.
+ * @post_nblist: pointer to the nvme buffer list.
+ *
+ * This routine walks a list of nvme buffers that was passed in. It attempts
+ * to construct blocks of nvme buffer sgls which contains contiguous xris and
+ * uses the non-embedded SGL block post mailbox commands to post to the port.
+ * For single NVME buffer sgl with non-contiguous xri, if any, it shall use
+ * embedded SGL post mailbox command for posting. The @post_nblist passed in
+ * must be local list, thus no lock is needed when manipulate the list.
+ *
+ * Returns: 0 = failure, non-zero number of successfully posted buffers.
+ **/
+int
+lpfc_sli4_post_io_sgl_list(struct lpfc_hba *phba,
+ struct list_head *post_nblist, int sb_count)
+{
+ struct lpfc_io_buf *lpfc_ncmd, *lpfc_ncmd_next;
+ int status, sgl_size;
+ int post_cnt = 0, block_cnt = 0, num_posting = 0, num_posted = 0;
+ dma_addr_t pdma_phys_sgl1;
+ int last_xritag = NO_XRI;
+ int cur_xritag;
+ LIST_HEAD(prep_nblist);
+ LIST_HEAD(blck_nblist);
+ LIST_HEAD(nvme_nblist);
+
+ /* sanity check */
+ if (sb_count <= 0)
+ return -EINVAL;
+
+ sgl_size = phba->cfg_sg_dma_buf_size;
+ list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next, post_nblist, list) {
+ list_del_init(&lpfc_ncmd->list);
+ block_cnt++;
+ if ((last_xritag != NO_XRI) &&
+ (lpfc_ncmd->cur_iocbq.sli4_xritag != last_xritag + 1)) {
+ /* a hole in xri block, form a sgl posting block */
+ list_splice_init(&prep_nblist, &blck_nblist);
+ post_cnt = block_cnt - 1;
+ /* prepare list for next posting block */
+ list_add_tail(&lpfc_ncmd->list, &prep_nblist);
+ block_cnt = 1;
+ } else {
+ /* prepare list for next posting block */
+ list_add_tail(&lpfc_ncmd->list, &prep_nblist);
+ /* enough sgls for non-embed sgl mbox command */
+ if (block_cnt == LPFC_NEMBED_MBOX_SGL_CNT) {
+ list_splice_init(&prep_nblist, &blck_nblist);
+ post_cnt = block_cnt;
+ block_cnt = 0;
+ }
+ }
+ num_posting++;
+ last_xritag = lpfc_ncmd->cur_iocbq.sli4_xritag;
+
+ /* end of repost sgl list condition for NVME buffers */
+ if (num_posting == sb_count) {
+ if (post_cnt == 0) {
+ /* last sgl posting block */
+ list_splice_init(&prep_nblist, &blck_nblist);
+ post_cnt = block_cnt;
+ } else if (block_cnt == 1) {
+ /* last single sgl with non-contiguous xri */
+ if (sgl_size > SGL_PAGE_SIZE)
+ pdma_phys_sgl1 =
+ lpfc_ncmd->dma_phys_sgl +
+ SGL_PAGE_SIZE;
+ else
+ pdma_phys_sgl1 = 0;
+ cur_xritag = lpfc_ncmd->cur_iocbq.sli4_xritag;
+ status = lpfc_sli4_post_sgl(
+ phba, lpfc_ncmd->dma_phys_sgl,
+ pdma_phys_sgl1, cur_xritag);
+ if (status) {
+ /* Post error. Buffer unavailable. */
+ lpfc_ncmd->flags |=
+ LPFC_SBUF_NOT_POSTED;
+ } else {
+ /* Post success. Bffer available. */
+ lpfc_ncmd->flags &=
+ ~LPFC_SBUF_NOT_POSTED;
+ lpfc_ncmd->status = IOSTAT_SUCCESS;
+ num_posted++;
+ }
+ /* success, put on NVME buffer sgl list */
+ list_add_tail(&lpfc_ncmd->list, &nvme_nblist);
+ }
+ }
+
+ /* continue until a nembed page worth of sgls */
+ if (post_cnt == 0)
+ continue;
+
+ /* post block of NVME buffer list sgls */
+ status = lpfc_sli4_post_io_sgl_block(phba, &blck_nblist,
+ post_cnt);
+
+ /* don't reset xirtag due to hole in xri block */
+ if (block_cnt == 0)
+ last_xritag = NO_XRI;
+
+ /* reset NVME buffer post count for next round of posting */
+ post_cnt = 0;
+
+ /* put posted NVME buffer-sgl posted on NVME buffer sgl list */
+ while (!list_empty(&blck_nblist)) {
+ list_remove_head(&blck_nblist, lpfc_ncmd,
+ struct lpfc_io_buf, list);
+ if (status) {
+ /* Post error. Mark buffer unavailable. */
+ lpfc_ncmd->flags |= LPFC_SBUF_NOT_POSTED;
+ } else {
+ /* Post success, Mark buffer available. */
+ lpfc_ncmd->flags &= ~LPFC_SBUF_NOT_POSTED;
+ lpfc_ncmd->status = IOSTAT_SUCCESS;
+ num_posted++;
+ }
+ list_add_tail(&lpfc_ncmd->list, &nvme_nblist);
+ }
+ }
+ /* Push NVME buffers with sgl posted to the available list */
+ lpfc_io_buf_replenish(phba, &nvme_nblist);
+
+ return num_posted;
+}
+
+/**
* lpfc_fc_frame_check - Check that this frame is a valid frame to handle
* @phba: pointer to lpfc_hba struct that the frame was received on
* @fc_hdr: A pointer to the FC Header data (In Big Endian Format)
@@ -19500,7 +19520,7 @@ lpfc_drain_txq(struct lpfc_hba *phba)
if (phba->link_flag & LS_MDS_LOOPBACK) {
/* MDS WQE are posted only to first WQ*/
- wq = phba->sli4_hba.fcp_wq[0];
+ wq = phba->sli4_hba.hdwq[0].fcp_wq;
if (unlikely(!wq))
return 0;
pring = wq->pring;
@@ -19708,7 +19728,7 @@ lpfc_wqe_bpl2sgl(struct lpfc_hba *phba, struct lpfc_iocbq *pwqeq,
* @pwqe: Pointer to command WQE.
**/
int
-lpfc_sli4_issue_wqe(struct lpfc_hba *phba, uint32_t ring_number,
+lpfc_sli4_issue_wqe(struct lpfc_hba *phba, struct lpfc_sli4_hdw_queue *qp,
struct lpfc_iocbq *pwqe)
{
union lpfc_wqe128 *wqe = &pwqe->wqe;
@@ -19722,7 +19742,8 @@ lpfc_sli4_issue_wqe(struct lpfc_hba *phba, uint32_t ring_number,
/* NVME_LS and NVME_LS ABTS requests. */
if (pwqe->iocb_flag & LPFC_IO_NVME_LS) {
pring = phba->sli4_hba.nvmels_wq->pring;
- spin_lock_irqsave(&pring->ring_lock, iflags);
+ lpfc_qp_spin_lock_irqsave(&pring->ring_lock, iflags,
+ qp, wq_access);
sglq = __lpfc_sli_get_els_sglq(phba, pwqe);
if (!sglq) {
spin_unlock_irqrestore(&pring->ring_lock, iflags);
@@ -19750,12 +19771,13 @@ lpfc_sli4_issue_wqe(struct lpfc_hba *phba, uint32_t ring_number,
/* NVME_FCREQ and NVME_ABTS requests */
if (pwqe->iocb_flag & LPFC_IO_NVME) {
/* Get the IO distribution (hba_wqidx) for WQ assignment. */
- pring = phba->sli4_hba.nvme_wq[pwqe->hba_wqidx]->pring;
+ wq = qp->nvme_wq;
+ pring = wq->pring;
- spin_lock_irqsave(&pring->ring_lock, iflags);
- wq = phba->sli4_hba.nvme_wq[pwqe->hba_wqidx];
- bf_set(wqe_cqid, &wqe->generic.wqe_com,
- phba->sli4_hba.nvme_cq[pwqe->hba_wqidx]->queue_id);
+ bf_set(wqe_cqid, &wqe->generic.wqe_com, qp->nvme_cq_map);
+
+ lpfc_qp_spin_lock_irqsave(&pring->ring_lock, iflags,
+ qp, wq_access);
ret = lpfc_sli4_wq_put(wq, wqe);
if (ret) {
spin_unlock_irqrestore(&pring->ring_lock, iflags);
@@ -19769,9 +19791,9 @@ lpfc_sli4_issue_wqe(struct lpfc_hba *phba, uint32_t ring_number,
/* NVMET requests */
if (pwqe->iocb_flag & LPFC_IO_NVMET) {
/* Get the IO distribution (hba_wqidx) for WQ assignment. */
- pring = phba->sli4_hba.nvme_wq[pwqe->hba_wqidx]->pring;
+ wq = qp->nvme_wq;
+ pring = wq->pring;
- spin_lock_irqsave(&pring->ring_lock, iflags);
ctxp = pwqe->context2;
sglq = ctxp->ctxbuf->sglq;
if (pwqe->sli4_xritag == NO_XRI) {
@@ -19780,9 +19802,10 @@ lpfc_sli4_issue_wqe(struct lpfc_hba *phba, uint32_t ring_number,
}
bf_set(wqe_xri_tag, &pwqe->wqe.xmit_bls_rsp.wqe_com,
pwqe->sli4_xritag);
- wq = phba->sli4_hba.nvme_wq[pwqe->hba_wqidx];
- bf_set(wqe_cqid, &wqe->generic.wqe_com,
- phba->sli4_hba.nvme_cq[pwqe->hba_wqidx]->queue_id);
+ bf_set(wqe_cqid, &wqe->generic.wqe_com, qp->nvme_cq_map);
+
+ lpfc_qp_spin_lock_irqsave(&pring->ring_lock, iflags,
+ qp, wq_access);
ret = lpfc_sli4_wq_put(wq, wqe);
if (ret) {
spin_unlock_irqrestore(&pring->ring_lock, iflags);
@@ -19794,3 +19817,647 @@ lpfc_sli4_issue_wqe(struct lpfc_hba *phba, uint32_t ring_number,
}
return WQE_ERROR;
}
+
+#ifdef LPFC_MXP_STAT
+/**
+ * lpfc_snapshot_mxp - Snapshot pbl, pvt and busy count
+ * @phba: pointer to lpfc hba data structure.
+ * @hwqid: belong to which HWQ.
+ *
+ * The purpose of this routine is to take a snapshot of pbl, pvt and busy count
+ * 15 seconds after a test case is running.
+ *
+ * The user should call lpfc_debugfs_multixripools_write before running a test
+ * case to clear stat_snapshot_taken. Then the user starts a test case. During
+ * test case is running, stat_snapshot_taken is incremented by 1 every time when
+ * this routine is called from heartbeat timer. When stat_snapshot_taken is
+ * equal to LPFC_MXP_SNAPSHOT_TAKEN, a snapshot is taken.
+ **/
+void lpfc_snapshot_mxp(struct lpfc_hba *phba, u32 hwqid)
+{
+ struct lpfc_sli4_hdw_queue *qp;
+ struct lpfc_multixri_pool *multixri_pool;
+ struct lpfc_pvt_pool *pvt_pool;
+ struct lpfc_pbl_pool *pbl_pool;
+ u32 txcmplq_cnt;
+
+ qp = &phba->sli4_hba.hdwq[hwqid];
+ multixri_pool = qp->p_multixri_pool;
+ if (!multixri_pool)
+ return;
+
+ if (multixri_pool->stat_snapshot_taken == LPFC_MXP_SNAPSHOT_TAKEN) {
+ pvt_pool = &qp->p_multixri_pool->pvt_pool;
+ pbl_pool = &qp->p_multixri_pool->pbl_pool;
+ txcmplq_cnt = qp->fcp_wq->pring->txcmplq_cnt;
+ if (qp->nvme_wq)
+ txcmplq_cnt += qp->nvme_wq->pring->txcmplq_cnt;
+
+ multixri_pool->stat_pbl_count = pbl_pool->count;
+ multixri_pool->stat_pvt_count = pvt_pool->count;
+ multixri_pool->stat_busy_count = txcmplq_cnt;
+ }
+
+ multixri_pool->stat_snapshot_taken++;
+}
+#endif
+
+/**
+ * lpfc_adjust_pvt_pool_count - Adjust private pool count
+ * @phba: pointer to lpfc hba data structure.
+ * @hwqid: belong to which HWQ.
+ *
+ * This routine moves some XRIs from private to public pool when private pool
+ * is not busy.
+ **/
+void lpfc_adjust_pvt_pool_count(struct lpfc_hba *phba, u32 hwqid)
+{
+ struct lpfc_multixri_pool *multixri_pool;
+ u32 io_req_count;
+ u32 prev_io_req_count;
+
+ multixri_pool = phba->sli4_hba.hdwq[hwqid].p_multixri_pool;
+ if (!multixri_pool)
+ return;
+ io_req_count = multixri_pool->io_req_count;
+ prev_io_req_count = multixri_pool->prev_io_req_count;
+
+ if (prev_io_req_count != io_req_count) {
+ /* Private pool is busy */
+ multixri_pool->prev_io_req_count = io_req_count;
+ } else {
+ /* Private pool is not busy.
+ * Move XRIs from private to public pool.
+ */
+ lpfc_move_xri_pvt_to_pbl(phba, hwqid);
+ }
+}
+
+/**
+ * lpfc_adjust_high_watermark - Adjust high watermark
+ * @phba: pointer to lpfc hba data structure.
+ * @hwqid: belong to which HWQ.
+ *
+ * This routine sets high watermark as number of outstanding XRIs,
+ * but make sure the new value is between xri_limit/2 and xri_limit.
+ **/
+void lpfc_adjust_high_watermark(struct lpfc_hba *phba, u32 hwqid)
+{
+ u32 new_watermark;
+ u32 watermark_max;
+ u32 watermark_min;
+ u32 xri_limit;
+ u32 txcmplq_cnt;
+ u32 abts_io_bufs;
+ struct lpfc_multixri_pool *multixri_pool;
+ struct lpfc_sli4_hdw_queue *qp;
+
+ qp = &phba->sli4_hba.hdwq[hwqid];
+ multixri_pool = qp->p_multixri_pool;
+ if (!multixri_pool)
+ return;
+ xri_limit = multixri_pool->xri_limit;
+
+ watermark_max = xri_limit;
+ watermark_min = xri_limit / 2;
+
+ txcmplq_cnt = qp->fcp_wq->pring->txcmplq_cnt;
+ abts_io_bufs = qp->abts_scsi_io_bufs;
+ if (qp->nvme_wq) {
+ txcmplq_cnt += qp->nvme_wq->pring->txcmplq_cnt;
+ abts_io_bufs += qp->abts_nvme_io_bufs;
+ }
+
+ new_watermark = txcmplq_cnt + abts_io_bufs;
+ new_watermark = min(watermark_max, new_watermark);
+ new_watermark = max(watermark_min, new_watermark);
+ multixri_pool->pvt_pool.high_watermark = new_watermark;
+
+#ifdef LPFC_MXP_STAT
+ multixri_pool->stat_max_hwm = max(multixri_pool->stat_max_hwm,
+ new_watermark);
+#endif
+}
+
+/**
+ * lpfc_move_xri_pvt_to_pbl - Move some XRIs from private to public pool
+ * @phba: pointer to lpfc hba data structure.
+ * @hwqid: belong to which HWQ.
+ *
+ * This routine is called from hearbeat timer when pvt_pool is idle.
+ * All free XRIs are moved from private to public pool on hwqid with 2 steps.
+ * The first step moves (all - low_watermark) amount of XRIs.
+ * The second step moves the rest of XRIs.
+ **/
+void lpfc_move_xri_pvt_to_pbl(struct lpfc_hba *phba, u32 hwqid)
+{
+ struct lpfc_pbl_pool *pbl_pool;
+ struct lpfc_pvt_pool *pvt_pool;
+ struct lpfc_sli4_hdw_queue *qp;
+ struct lpfc_io_buf *lpfc_ncmd;
+ struct lpfc_io_buf *lpfc_ncmd_next;
+ unsigned long iflag;
+ struct list_head tmp_list;
+ u32 tmp_count;
+
+ qp = &phba->sli4_hba.hdwq[hwqid];
+ pbl_pool = &qp->p_multixri_pool->pbl_pool;
+ pvt_pool = &qp->p_multixri_pool->pvt_pool;
+ tmp_count = 0;
+
+ lpfc_qp_spin_lock_irqsave(&pbl_pool->lock, iflag, qp, mv_to_pub_pool);
+ lpfc_qp_spin_lock(&pvt_pool->lock, qp, mv_from_pvt_pool);
+
+ if (pvt_pool->count > pvt_pool->low_watermark) {
+ /* Step 1: move (all - low_watermark) from pvt_pool
+ * to pbl_pool
+ */
+
+ /* Move low watermark of bufs from pvt_pool to tmp_list */
+ INIT_LIST_HEAD(&tmp_list);
+ list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next,
+ &pvt_pool->list, list) {
+ list_move_tail(&lpfc_ncmd->list, &tmp_list);
+ tmp_count++;
+ if (tmp_count >= pvt_pool->low_watermark)
+ break;
+ }
+
+ /* Move all bufs from pvt_pool to pbl_pool */
+ list_splice_init(&pvt_pool->list, &pbl_pool->list);
+
+ /* Move all bufs from tmp_list to pvt_pool */
+ list_splice(&tmp_list, &pvt_pool->list);
+
+ pbl_pool->count += (pvt_pool->count - tmp_count);
+ pvt_pool->count = tmp_count;
+ } else {
+ /* Step 2: move the rest from pvt_pool to pbl_pool */
+ list_splice_init(&pvt_pool->list, &pbl_pool->list);
+ pbl_pool->count += pvt_pool->count;
+ pvt_pool->count = 0;
+ }
+
+ spin_unlock(&pvt_pool->lock);
+ spin_unlock_irqrestore(&pbl_pool->lock, iflag);
+}
+
+/**
+ * _lpfc_move_xri_pbl_to_pvt - Move some XRIs from public to private pool
+ * @phba: pointer to lpfc hba data structure
+ * @pbl_pool: specified public free XRI pool
+ * @pvt_pool: specified private free XRI pool
+ * @count: number of XRIs to move
+ *
+ * This routine tries to move some free common bufs from the specified pbl_pool
+ * to the specified pvt_pool. It might move less than count XRIs if there's not
+ * enough in public pool.
+ *
+ * Return:
+ * true - if XRIs are successfully moved from the specified pbl_pool to the
+ * specified pvt_pool
+ * false - if the specified pbl_pool is empty or locked by someone else
+ **/
+static bool
+_lpfc_move_xri_pbl_to_pvt(struct lpfc_hba *phba, struct lpfc_sli4_hdw_queue *qp,
+ struct lpfc_pbl_pool *pbl_pool,
+ struct lpfc_pvt_pool *pvt_pool, u32 count)
+{
+ struct lpfc_io_buf *lpfc_ncmd;
+ struct lpfc_io_buf *lpfc_ncmd_next;
+ unsigned long iflag;
+ int ret;
+
+ ret = spin_trylock_irqsave(&pbl_pool->lock, iflag);
+ if (ret) {
+ if (pbl_pool->count) {
+ /* Move a batch of XRIs from public to private pool */
+ lpfc_qp_spin_lock(&pvt_pool->lock, qp, mv_to_pvt_pool);
+ list_for_each_entry_safe(lpfc_ncmd,
+ lpfc_ncmd_next,
+ &pbl_pool->list,
+ list) {
+ list_move_tail(&lpfc_ncmd->list,
+ &pvt_pool->list);
+ pvt_pool->count++;
+ pbl_pool->count--;
+ count--;
+ if (count == 0)
+ break;
+ }
+
+ spin_unlock(&pvt_pool->lock);
+ spin_unlock_irqrestore(&pbl_pool->lock, iflag);
+ return true;
+ }
+ spin_unlock_irqrestore(&pbl_pool->lock, iflag);
+ }
+
+ return false;
+}
+
+/**
+ * lpfc_move_xri_pbl_to_pvt - Move some XRIs from public to private pool
+ * @phba: pointer to lpfc hba data structure.
+ * @hwqid: belong to which HWQ.
+ * @count: number of XRIs to move
+ *
+ * This routine tries to find some free common bufs in one of public pools with
+ * Round Robin method. The search always starts from local hwqid, then the next
+ * HWQ which was found last time (rrb_next_hwqid). Once a public pool is found,
+ * a batch of free common bufs are moved to private pool on hwqid.
+ * It might move less than count XRIs if there's not enough in public pool.
+ **/
+void lpfc_move_xri_pbl_to_pvt(struct lpfc_hba *phba, u32 hwqid, u32 count)
+{
+ struct lpfc_multixri_pool *multixri_pool;
+ struct lpfc_multixri_pool *next_multixri_pool;
+ struct lpfc_pvt_pool *pvt_pool;
+ struct lpfc_pbl_pool *pbl_pool;
+ struct lpfc_sli4_hdw_queue *qp;
+ u32 next_hwqid;
+ u32 hwq_count;
+ int ret;
+
+ qp = &phba->sli4_hba.hdwq[hwqid];
+ multixri_pool = qp->p_multixri_pool;
+ pvt_pool = &multixri_pool->pvt_pool;
+ pbl_pool = &multixri_pool->pbl_pool;
+
+ /* Check if local pbl_pool is available */
+ ret = _lpfc_move_xri_pbl_to_pvt(phba, qp, pbl_pool, pvt_pool, count);
+ if (ret) {
+#ifdef LPFC_MXP_STAT
+ multixri_pool->local_pbl_hit_count++;
+#endif
+ return;
+ }
+
+ hwq_count = phba->cfg_hdw_queue;
+
+ /* Get the next hwqid which was found last time */
+ next_hwqid = multixri_pool->rrb_next_hwqid;
+
+ do {
+ /* Go to next hwq */
+ next_hwqid = (next_hwqid + 1) % hwq_count;
+
+ next_multixri_pool =
+ phba->sli4_hba.hdwq[next_hwqid].p_multixri_pool;
+ pbl_pool = &next_multixri_pool->pbl_pool;
+
+ /* Check if the public free xri pool is available */
+ ret = _lpfc_move_xri_pbl_to_pvt(
+ phba, qp, pbl_pool, pvt_pool, count);
+
+ /* Exit while-loop if success or all hwqid are checked */
+ } while (!ret && next_hwqid != multixri_pool->rrb_next_hwqid);
+
+ /* Starting point for the next time */
+ multixri_pool->rrb_next_hwqid = next_hwqid;
+
+ if (!ret) {
+ /* stats: all public pools are empty*/
+ multixri_pool->pbl_empty_count++;
+ }
+
+#ifdef LPFC_MXP_STAT
+ if (ret) {
+ if (next_hwqid == hwqid)
+ multixri_pool->local_pbl_hit_count++;
+ else
+ multixri_pool->other_pbl_hit_count++;
+ }
+#endif
+}
+
+/**
+ * lpfc_keep_pvt_pool_above_lowwm - Keep pvt_pool above low watermark
+ * @phba: pointer to lpfc hba data structure.
+ * @qp: belong to which HWQ.
+ *
+ * This routine get a batch of XRIs from pbl_pool if pvt_pool is less than
+ * low watermark.
+ **/
+void lpfc_keep_pvt_pool_above_lowwm(struct lpfc_hba *phba, u32 hwqid)
+{
+ struct lpfc_multixri_pool *multixri_pool;
+ struct lpfc_pvt_pool *pvt_pool;
+
+ multixri_pool = phba->sli4_hba.hdwq[hwqid].p_multixri_pool;
+ pvt_pool = &multixri_pool->pvt_pool;
+
+ if (pvt_pool->count < pvt_pool->low_watermark)
+ lpfc_move_xri_pbl_to_pvt(phba, hwqid, XRI_BATCH);
+}
+
+/**
+ * lpfc_release_io_buf - Return one IO buf back to free pool
+ * @phba: pointer to lpfc hba data structure.
+ * @lpfc_ncmd: IO buf to be returned.
+ * @qp: belong to which HWQ.
+ *
+ * This routine returns one IO buf back to free pool. If this is an urgent IO,
+ * the IO buf is returned to expedite pool. If cfg_xri_rebalancing==1,
+ * the IO buf is returned to pbl_pool or pvt_pool based on watermark and
+ * xri_limit. If cfg_xri_rebalancing==0, the IO buf is returned to
+ * lpfc_io_buf_list_put.
+ **/
+void lpfc_release_io_buf(struct lpfc_hba *phba, struct lpfc_io_buf *lpfc_ncmd,
+ struct lpfc_sli4_hdw_queue *qp)
+{
+ unsigned long iflag;
+ struct lpfc_pbl_pool *pbl_pool;
+ struct lpfc_pvt_pool *pvt_pool;
+ struct lpfc_epd_pool *epd_pool;
+ u32 txcmplq_cnt;
+ u32 xri_owned;
+ u32 xri_limit;
+ u32 abts_io_bufs;
+
+ /* MUST zero fields if buffer is reused by another protocol */
+ lpfc_ncmd->nvmeCmd = NULL;
+ lpfc_ncmd->cur_iocbq.wqe_cmpl = NULL;
+ lpfc_ncmd->cur_iocbq.iocb_cmpl = NULL;
+
+ if (phba->cfg_xri_rebalancing) {
+ if (lpfc_ncmd->expedite) {
+ /* Return to expedite pool */
+ epd_pool = &phba->epd_pool;
+ spin_lock_irqsave(&epd_pool->lock, iflag);
+ list_add_tail(&lpfc_ncmd->list, &epd_pool->list);
+ epd_pool->count++;
+ spin_unlock_irqrestore(&epd_pool->lock, iflag);
+ return;
+ }
+
+ /* Avoid invalid access if an IO sneaks in and is being rejected
+ * just _after_ xri pools are destroyed in lpfc_offline.
+ * Nothing much can be done at this point.
+ */
+ if (!qp->p_multixri_pool)
+ return;
+
+ pbl_pool = &qp->p_multixri_pool->pbl_pool;
+ pvt_pool = &qp->p_multixri_pool->pvt_pool;
+
+ txcmplq_cnt = qp->fcp_wq->pring->txcmplq_cnt;
+ abts_io_bufs = qp->abts_scsi_io_bufs;
+ if (qp->nvme_wq) {
+ txcmplq_cnt += qp->nvme_wq->pring->txcmplq_cnt;
+ abts_io_bufs += qp->abts_nvme_io_bufs;
+ }
+
+ xri_owned = pvt_pool->count + txcmplq_cnt + abts_io_bufs;
+ xri_limit = qp->p_multixri_pool->xri_limit;
+
+#ifdef LPFC_MXP_STAT
+ if (xri_owned <= xri_limit)
+ qp->p_multixri_pool->below_limit_count++;
+ else
+ qp->p_multixri_pool->above_limit_count++;
+#endif
+
+ /* XRI goes to either public or private free xri pool
+ * based on watermark and xri_limit
+ */
+ if ((pvt_pool->count < pvt_pool->low_watermark) ||
+ (xri_owned < xri_limit &&
+ pvt_pool->count < pvt_pool->high_watermark)) {
+ lpfc_qp_spin_lock_irqsave(&pvt_pool->lock, iflag,
+ qp, free_pvt_pool);
+ list_add_tail(&lpfc_ncmd->list,
+ &pvt_pool->list);
+ pvt_pool->count++;
+ spin_unlock_irqrestore(&pvt_pool->lock, iflag);
+ } else {
+ lpfc_qp_spin_lock_irqsave(&pbl_pool->lock, iflag,
+ qp, free_pub_pool);
+ list_add_tail(&lpfc_ncmd->list,
+ &pbl_pool->list);
+ pbl_pool->count++;
+ spin_unlock_irqrestore(&pbl_pool->lock, iflag);
+ }
+ } else {
+ lpfc_qp_spin_lock_irqsave(&qp->io_buf_list_put_lock, iflag,
+ qp, free_xri);
+ list_add_tail(&lpfc_ncmd->list,
+ &qp->lpfc_io_buf_list_put);
+ qp->put_io_bufs++;
+ spin_unlock_irqrestore(&qp->io_buf_list_put_lock,
+ iflag);
+ }
+}
+
+/**
+ * lpfc_get_io_buf_from_private_pool - Get one free IO buf from private pool
+ * @phba: pointer to lpfc hba data structure.
+ * @pvt_pool: pointer to private pool data structure.
+ * @ndlp: pointer to lpfc nodelist data structure.
+ *
+ * This routine tries to get one free IO buf from private pool.
+ *
+ * Return:
+ * pointer to one free IO buf - if private pool is not empty
+ * NULL - if private pool is empty
+ **/
+static struct lpfc_io_buf *
+lpfc_get_io_buf_from_private_pool(struct lpfc_hba *phba,
+ struct lpfc_sli4_hdw_queue *qp,
+ struct lpfc_pvt_pool *pvt_pool,
+ struct lpfc_nodelist *ndlp)
+{
+ struct lpfc_io_buf *lpfc_ncmd;
+ struct lpfc_io_buf *lpfc_ncmd_next;
+ unsigned long iflag;
+
+ lpfc_qp_spin_lock_irqsave(&pvt_pool->lock, iflag, qp, alloc_pvt_pool);
+ list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next,
+ &pvt_pool->list, list) {
+ if (lpfc_test_rrq_active(
+ phba, ndlp, lpfc_ncmd->cur_iocbq.sli4_lxritag))
+ continue;
+ list_del(&lpfc_ncmd->list);
+ pvt_pool->count--;
+ spin_unlock_irqrestore(&pvt_pool->lock, iflag);
+ return lpfc_ncmd;
+ }
+ spin_unlock_irqrestore(&pvt_pool->lock, iflag);
+
+ return NULL;
+}
+
+/**
+ * lpfc_get_io_buf_from_expedite_pool - Get one free IO buf from expedite pool
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine tries to get one free IO buf from expedite pool.
+ *
+ * Return:
+ * pointer to one free IO buf - if expedite pool is not empty
+ * NULL - if expedite pool is empty
+ **/
+static struct lpfc_io_buf *
+lpfc_get_io_buf_from_expedite_pool(struct lpfc_hba *phba)
+{
+ struct lpfc_io_buf *lpfc_ncmd;
+ struct lpfc_io_buf *lpfc_ncmd_next;
+ unsigned long iflag;
+ struct lpfc_epd_pool *epd_pool;
+
+ epd_pool = &phba->epd_pool;
+ lpfc_ncmd = NULL;
+
+ spin_lock_irqsave(&epd_pool->lock, iflag);
+ if (epd_pool->count > 0) {
+ list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next,
+ &epd_pool->list, list) {
+ list_del(&lpfc_ncmd->list);
+ epd_pool->count--;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&epd_pool->lock, iflag);
+
+ return lpfc_ncmd;
+}
+
+/**
+ * lpfc_get_io_buf_from_multixri_pools - Get one free IO bufs
+ * @phba: pointer to lpfc hba data structure.
+ * @ndlp: pointer to lpfc nodelist data structure.
+ * @hwqid: belong to which HWQ
+ * @expedite: 1 means this request is urgent.
+ *
+ * This routine will do the following actions and then return a pointer to
+ * one free IO buf.
+ *
+ * 1. If private free xri count is empty, move some XRIs from public to
+ * private pool.
+ * 2. Get one XRI from private free xri pool.
+ * 3. If we fail to get one from pvt_pool and this is an expedite request,
+ * get one free xri from expedite pool.
+ *
+ * Note: ndlp is only used on SCSI side for RRQ testing.
+ * The caller should pass NULL for ndlp on NVME side.
+ *
+ * Return:
+ * pointer to one free IO buf - if private pool is not empty
+ * NULL - if private pool is empty
+ **/
+static struct lpfc_io_buf *
+lpfc_get_io_buf_from_multixri_pools(struct lpfc_hba *phba,
+ struct lpfc_nodelist *ndlp,
+ int hwqid, int expedite)
+{
+ struct lpfc_sli4_hdw_queue *qp;
+ struct lpfc_multixri_pool *multixri_pool;
+ struct lpfc_pvt_pool *pvt_pool;
+ struct lpfc_io_buf *lpfc_ncmd;
+
+ qp = &phba->sli4_hba.hdwq[hwqid];
+ lpfc_ncmd = NULL;
+ multixri_pool = qp->p_multixri_pool;
+ pvt_pool = &multixri_pool->pvt_pool;
+ multixri_pool->io_req_count++;
+
+ /* If pvt_pool is empty, move some XRIs from public to private pool */
+ if (pvt_pool->count == 0)
+ lpfc_move_xri_pbl_to_pvt(phba, hwqid, XRI_BATCH);
+
+ /* Get one XRI from private free xri pool */
+ lpfc_ncmd = lpfc_get_io_buf_from_private_pool(phba, qp, pvt_pool, ndlp);
+
+ if (lpfc_ncmd) {
+ lpfc_ncmd->hdwq = qp;
+ lpfc_ncmd->hdwq_no = hwqid;
+ } else if (expedite) {
+ /* If we fail to get one from pvt_pool and this is an expedite
+ * request, get one free xri from expedite pool.
+ */
+ lpfc_ncmd = lpfc_get_io_buf_from_expedite_pool(phba);
+ }
+
+ return lpfc_ncmd;
+}
+
+static inline struct lpfc_io_buf *
+lpfc_io_buf(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp, int idx)
+{
+ struct lpfc_sli4_hdw_queue *qp;
+ struct lpfc_io_buf *lpfc_cmd, *lpfc_cmd_next;
+
+ qp = &phba->sli4_hba.hdwq[idx];
+ list_for_each_entry_safe(lpfc_cmd, lpfc_cmd_next,
+ &qp->lpfc_io_buf_list_get, list) {
+ if (lpfc_test_rrq_active(phba, ndlp,
+ lpfc_cmd->cur_iocbq.sli4_lxritag))
+ continue;
+
+ if (lpfc_cmd->flags & LPFC_SBUF_NOT_POSTED)
+ continue;
+
+ list_del_init(&lpfc_cmd->list);
+ qp->get_io_bufs--;
+ lpfc_cmd->hdwq = qp;
+ lpfc_cmd->hdwq_no = idx;
+ return lpfc_cmd;
+ }
+ return NULL;
+}
+
+/**
+ * lpfc_get_io_buf - Get one IO buffer from free pool
+ * @phba: The HBA for which this call is being executed.
+ * @ndlp: pointer to lpfc nodelist data structure.
+ * @hwqid: belong to which HWQ
+ * @expedite: 1 means this request is urgent.
+ *
+ * This routine gets one IO buffer from free pool. If cfg_xri_rebalancing==1,
+ * removes a IO buffer from multiXRI pools. If cfg_xri_rebalancing==0, removes
+ * a IO buffer from head of @hdwq io_buf_list and returns to caller.
+ *
+ * Note: ndlp is only used on SCSI side for RRQ testing.
+ * The caller should pass NULL for ndlp on NVME side.
+ *
+ * Return codes:
+ * NULL - Error
+ * Pointer to lpfc_io_buf - Success
+ **/
+struct lpfc_io_buf *lpfc_get_io_buf(struct lpfc_hba *phba,
+ struct lpfc_nodelist *ndlp,
+ u32 hwqid, int expedite)
+{
+ struct lpfc_sli4_hdw_queue *qp;
+ unsigned long iflag;
+ struct lpfc_io_buf *lpfc_cmd;
+
+ qp = &phba->sli4_hba.hdwq[hwqid];
+ lpfc_cmd = NULL;
+
+ if (phba->cfg_xri_rebalancing)
+ lpfc_cmd = lpfc_get_io_buf_from_multixri_pools(
+ phba, ndlp, hwqid, expedite);
+ else {
+ lpfc_qp_spin_lock_irqsave(&qp->io_buf_list_get_lock, iflag,
+ qp, alloc_xri_get);
+ if (qp->get_io_bufs > LPFC_NVME_EXPEDITE_XRICNT || expedite)
+ lpfc_cmd = lpfc_io_buf(phba, ndlp, hwqid);
+ if (!lpfc_cmd) {
+ lpfc_qp_spin_lock(&qp->io_buf_list_put_lock,
+ qp, alloc_xri_put);
+ list_splice(&qp->lpfc_io_buf_list_put,
+ &qp->lpfc_io_buf_list_get);
+ qp->get_io_bufs += qp->put_io_bufs;
+ INIT_LIST_HEAD(&qp->lpfc_io_buf_list_put);
+ qp->put_io_bufs = 0;
+ spin_unlock(&qp->io_buf_list_put_lock);
+ if (qp->get_io_bufs > LPFC_NVME_EXPEDITE_XRICNT ||
+ expedite)
+ lpfc_cmd = lpfc_io_buf(phba, ndlp, hwqid);
+ }
+ spin_unlock_irqrestore(&qp->io_buf_list_get_lock, iflag);
+ }
+
+ return lpfc_cmd;
+}
diff --git a/drivers/scsi/lpfc/lpfc_sli.h b/drivers/scsi/lpfc/lpfc_sli.h
index 7abb395bb64a..7a1a761efdd6 100644
--- a/drivers/scsi/lpfc/lpfc_sli.h
+++ b/drivers/scsi/lpfc/lpfc_sli.h
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term *
+ * Copyright (C) 2017-2019 Broadcom. All Rights Reserved. The term *
* “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. *
* Copyright (C) 2004-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
@@ -20,6 +20,10 @@
* included with this package. *
*******************************************************************/
+#if defined(CONFIG_DEBUG_FS) && !defined(CONFIG_SCSI_LPFC_DEBUG_FS)
+#define CONFIG_SCSI_LPFC_DEBUG_FS
+#endif
+
/* forward declaration for LPFC_IOCB_t's use */
struct lpfc_hba;
struct lpfc_vport;
@@ -33,6 +37,7 @@ typedef enum _lpfc_ctx_cmd {
struct lpfc_cq_event {
struct list_head list;
+ uint16_t hdwq;
union {
struct lpfc_mcqe mcqe_cmpl;
struct lpfc_acqe_link acqe_link;
@@ -351,3 +356,85 @@ struct lpfc_sli {
#define LPFC_MBOX_SLI4_CONFIG_EXTENDED_TMO 300
/* Timeout for other flash-based outstanding mbox command (Seconds) */
#define LPFC_MBOX_TMO_FLASH_CMD 300
+
+struct lpfc_io_buf {
+ /* Common fields */
+ struct list_head list;
+ void *data;
+ dma_addr_t dma_handle;
+ dma_addr_t dma_phys_sgl;
+ struct sli4_sge *dma_sgl;
+ struct lpfc_iocbq cur_iocbq;
+ struct lpfc_sli4_hdw_queue *hdwq;
+ uint16_t hdwq_no;
+ uint16_t cpu;
+
+ struct lpfc_nodelist *ndlp;
+ uint32_t timeout;
+ uint16_t flags; /* TBD convert exch_busy to flags */
+#define LPFC_SBUF_XBUSY 0x1 /* SLI4 hba reported XB on WCQE cmpl */
+#define LPFC_SBUF_BUMP_QDEPTH 0x2 /* bumped queue depth counter */
+ /* External DIF device IO conversions */
+#define LPFC_SBUF_NORMAL_DIF 0x4 /* normal mode to insert/strip */
+#define LPFC_SBUF_PASS_DIF 0x8 /* insert/strip mode to passthru */
+#define LPFC_SBUF_NOT_POSTED 0x10 /* SGL failed post to FW. */
+ uint16_t exch_busy; /* SLI4 hba reported XB on complete WCQE */
+ uint16_t status; /* From IOCB Word 7- ulpStatus */
+ uint32_t result; /* From IOCB Word 4. */
+
+ uint32_t seg_cnt; /* Number of scatter-gather segments returned by
+ * dma_map_sg. The driver needs this for calls
+ * to dma_unmap_sg.
+ */
+ unsigned long start_time;
+ spinlock_t buf_lock; /* lock used in case of simultaneous abort */
+ bool expedite; /* this is an expedite io_buf */
+
+ union {
+ /* SCSI specific fields */
+ struct {
+ struct scsi_cmnd *pCmd;
+ struct lpfc_rport_data *rdata;
+ uint32_t prot_seg_cnt; /* seg_cnt's counterpart for
+ * protection data
+ */
+
+ /*
+ * data and dma_handle are the kernel virtual and bus
+ * address of the dma-able buffer containing the
+ * fcp_cmd, fcp_rsp and a scatter gather bde list that
+ * supports the sg_tablesize value.
+ */
+ struct fcp_cmnd *fcp_cmnd;
+ struct fcp_rsp *fcp_rsp;
+
+ wait_queue_head_t *waitq;
+
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ /* Used to restore any changes to protection data for
+ * error injection
+ */
+ void *prot_data_segment;
+ uint32_t prot_data;
+ uint32_t prot_data_type;
+#define LPFC_INJERR_REFTAG 1
+#define LPFC_INJERR_APPTAG 2
+#define LPFC_INJERR_GUARD 3
+#endif
+ };
+
+ /* NVME specific fields */
+ struct {
+ struct nvmefc_fcp_req *nvmeCmd;
+ uint16_t qidx;
+
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ uint64_t ts_cmd_start;
+ uint64_t ts_last_cmd;
+ uint64_t ts_cmd_wqput;
+ uint64_t ts_isr_cmpl;
+ uint64_t ts_data_nvme;
+#endif
+ };
+ };
+};
diff --git a/drivers/scsi/lpfc/lpfc_sli4.h b/drivers/scsi/lpfc/lpfc_sli4.h
index 6b2d2350e2c6..40c85091c805 100644
--- a/drivers/scsi/lpfc/lpfc_sli4.h
+++ b/drivers/scsi/lpfc/lpfc_sli4.h
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term *
+ * Copyright (C) 2017-2019 Broadcom. All Rights Reserved. The term *
* “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. *
* Copyright (C) 2009-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
@@ -20,6 +20,10 @@
* included with this package. *
*******************************************************************/
+#if defined(CONFIG_DEBUG_FS) && !defined(CONFIG_SCSI_LPFC_DEBUG_FS)
+#define CONFIG_SCSI_LPFC_DEBUG_FS
+#endif
+
#define LPFC_ACTIVE_MBOX_WAIT_CNT 100
#define LPFC_XRI_EXCH_BUSY_WAIT_TMO 10000
#define LPFC_XRI_EXCH_BUSY_WAIT_T1 10
@@ -36,14 +40,12 @@
#define LPFC_NEMBED_MBOX_SGL_CNT 254
/* Multi-queue arrangement for FCP EQ/CQ/WQ tuples */
-#define LPFC_HBA_IO_CHAN_MIN 0
-#define LPFC_HBA_IO_CHAN_MAX 32
-#define LPFC_FCP_IO_CHAN_DEF 4
-#define LPFC_NVME_IO_CHAN_DEF 0
-
-/* Number of channels used for Flash Optimized Fabric (FOF) operations */
+#define LPFC_HBA_HDWQ_MIN 0
+#define LPFC_HBA_HDWQ_MAX 128
+#define LPFC_HBA_HDWQ_DEF 0
-#define LPFC_FOF_IO_CHAN_NUM 1
+/* Common buffer size to accomidate SCSI and NVME IO buffers */
+#define LPFC_COMMON_IO_BUF_SZ 768
/*
* Provide the default FCF Record attributes used by the driver
@@ -152,28 +154,58 @@ struct lpfc_queue {
struct list_head child_list;
struct list_head page_list;
struct list_head sgl_list;
+ struct list_head cpu_list;
uint32_t entry_count; /* Number of entries to support on the queue */
uint32_t entry_size; /* Size of each queue entry. */
- uint32_t entry_repost; /* Count of entries before doorbell is rung */
-#define LPFC_EQ_REPOST 8
-#define LPFC_MQ_REPOST 8
-#define LPFC_CQ_REPOST 64
-#define LPFC_RQ_REPOST 64
-#define LPFC_RELEASE_NOTIFICATION_INTERVAL 32 /* For WQs */
+ uint32_t notify_interval; /* Queue Notification Interval
+ * For chip->host queues (EQ, CQ, RQ):
+ * specifies the interval (number of
+ * entries) where the doorbell is rung to
+ * notify the chip of entry consumption.
+ * For host->chip queues (WQ):
+ * specifies the interval (number of
+ * entries) where consumption CQE is
+ * requested to indicate WQ entries
+ * consumed by the chip.
+ * Not used on an MQ.
+ */
+#define LPFC_EQ_NOTIFY_INTRVL 16
+#define LPFC_CQ_NOTIFY_INTRVL 16
+#define LPFC_WQ_NOTIFY_INTRVL 16
+#define LPFC_RQ_NOTIFY_INTRVL 16
+ uint32_t max_proc_limit; /* Queue Processing Limit
+ * For chip->host queues (EQ, CQ):
+ * specifies the maximum number of
+ * entries to be consumed in one
+ * processing iteration sequence. Queue
+ * will be rearmed after each iteration.
+ * Not used on an MQ, RQ or WQ.
+ */
+#define LPFC_EQ_MAX_PROC_LIMIT 256
+#define LPFC_CQ_MIN_PROC_LIMIT 64
+#define LPFC_CQ_MAX_PROC_LIMIT LPFC_CQE_EXP_COUNT // 4096
+#define LPFC_CQ_DEF_MAX_PROC_LIMIT LPFC_CQE_DEF_COUNT // 1024
+#define LPFC_CQ_MIN_THRESHOLD_TO_POLL 64
+#define LPFC_CQ_MAX_THRESHOLD_TO_POLL LPFC_CQ_DEF_MAX_PROC_LIMIT
+#define LPFC_CQ_DEF_THRESHOLD_TO_POLL LPFC_CQ_DEF_MAX_PROC_LIMIT
+ uint32_t queue_claimed; /* indicates queue is being processed */
uint32_t queue_id; /* Queue ID assigned by the hardware */
uint32_t assoc_qid; /* Queue ID associated with, for CQ/WQ/MQ */
uint32_t host_index; /* The host's index for putting or getting */
uint32_t hba_index; /* The last known hba index for get or put */
+ uint32_t q_mode;
struct lpfc_sli_ring *pring; /* ptr to io ring associated with q */
struct lpfc_rqb *rqbp; /* ptr to RQ buffers */
- uint32_t q_mode;
uint16_t page_count; /* Number of pages allocated for this queue */
uint16_t page_size; /* size of page allocated for this queue */
#define LPFC_EXPANDED_PAGE_SIZE 16384
#define LPFC_DEFAULT_PAGE_SIZE 4096
- uint16_t chann; /* IO channel this queue is associated with */
+ uint16_t chann; /* Hardware Queue association WQ/CQ */
+ /* CPU affinity for EQ */
+#define LPFC_FIND_BY_EQ 0
+#define LPFC_FIND_BY_HDWQ 1
uint8_t db_format;
#define LPFC_DB_RING_FORMAT 0x01
#define LPFC_DB_LIST_FORMAT 0x02
@@ -212,10 +244,14 @@ struct lpfc_queue {
#define RQ_buf_posted q_cnt_3
#define RQ_rcv_buf q_cnt_4
- struct work_struct irqwork;
- struct work_struct spwork;
+ struct work_struct irqwork;
+ struct work_struct spwork;
+ struct delayed_work sched_irqwork;
+ struct delayed_work sched_spwork;
uint64_t isr_timestamp;
+ uint16_t hdwq;
+ uint16_t last_cpu; /* most recent cpu */
uint8_t qe_valid;
struct lpfc_queue *assoc_qp;
union sli4_qe qe[1]; /* array to index entries (must be last) */
@@ -428,11 +464,6 @@ struct lpfc_hba_eq_hdl {
uint32_t idx;
char handler_name[LPFC_SLI4_HANDLER_NAME_SZ];
struct lpfc_hba *phba;
- atomic_t hba_eq_in_use;
- struct cpumask *cpumask;
- /* CPU affinitsed to or 0xffffffff if multiple */
- uint32_t cpu;
-#define LPFC_MULTI_CPU_AFFINITY 0xffffffff
};
/*BB Credit recovery value*/
@@ -526,11 +557,165 @@ struct lpfc_vector_map_info {
uint16_t phys_id;
uint16_t core_id;
uint16_t irq;
- uint16_t channel_id;
+ uint16_t eq;
+ uint16_t hdwq;
+ uint16_t hyper;
};
#define LPFC_VECTOR_MAP_EMPTY 0xffff
+/* Multi-XRI pool */
+#define XRI_BATCH 8
+
+struct lpfc_pbl_pool {
+ struct list_head list;
+ u32 count;
+ spinlock_t lock; /* lock for pbl_pool*/
+};
+
+struct lpfc_pvt_pool {
+ u32 low_watermark;
+ u32 high_watermark;
+
+ struct list_head list;
+ u32 count;
+ spinlock_t lock; /* lock for pvt_pool */
+};
+
+struct lpfc_multixri_pool {
+ u32 xri_limit;
+
+ /* Starting point when searching a pbl_pool with round-robin method */
+ u32 rrb_next_hwqid;
+
+ /* Used by lpfc_adjust_pvt_pool_count.
+ * io_req_count is incremented by 1 during IO submission. The heartbeat
+ * handler uses these two variables to determine if pvt_pool is idle or
+ * busy.
+ */
+ u32 prev_io_req_count;
+ u32 io_req_count;
+
+ /* statistics */
+ u32 pbl_empty_count;
+#ifdef LPFC_MXP_STAT
+ u32 above_limit_count;
+ u32 below_limit_count;
+ u32 local_pbl_hit_count;
+ u32 other_pbl_hit_count;
+ u32 stat_max_hwm;
+
+#define LPFC_MXP_SNAPSHOT_TAKEN 3 /* snapshot is taken at 3rd heartbeats */
+ u32 stat_pbl_count;
+ u32 stat_pvt_count;
+ u32 stat_busy_count;
+ u32 stat_snapshot_taken;
+#endif
+
+ /* TODO: Separate pvt_pool into get and put list */
+ struct lpfc_pbl_pool pbl_pool; /* Public free XRI pool */
+ struct lpfc_pvt_pool pvt_pool; /* Private free XRI pool */
+};
+
+struct lpfc_fc4_ctrl_stat {
+ u32 input_requests;
+ u32 output_requests;
+ u32 control_requests;
+ u32 io_cmpls;
+};
+
+#ifdef LPFC_HDWQ_LOCK_STAT
+struct lpfc_lock_stat {
+ uint32_t alloc_xri_get;
+ uint32_t alloc_xri_put;
+ uint32_t free_xri;
+ uint32_t wq_access;
+ uint32_t alloc_pvt_pool;
+ uint32_t mv_from_pvt_pool;
+ uint32_t mv_to_pub_pool;
+ uint32_t mv_to_pvt_pool;
+ uint32_t free_pub_pool;
+ uint32_t free_pvt_pool;
+};
+#endif
+
+struct lpfc_eq_intr_info {
+ struct list_head list;
+ uint32_t icnt;
+};
+
/* SLI4 HBA data structure entries */
+struct lpfc_sli4_hdw_queue {
+ /* Pointers to the constructed SLI4 queues */
+ struct lpfc_queue *hba_eq; /* Event queues for HBA */
+ struct lpfc_queue *fcp_cq; /* Fast-path FCP compl queue */
+ struct lpfc_queue *nvme_cq; /* Fast-path NVME compl queue */
+ struct lpfc_queue *fcp_wq; /* Fast-path FCP work queue */
+ struct lpfc_queue *nvme_wq; /* Fast-path NVME work queue */
+ uint16_t fcp_cq_map;
+ uint16_t nvme_cq_map;
+
+ /* Keep track of IO buffers for this hardware queue */
+ spinlock_t io_buf_list_get_lock; /* Common buf alloc list lock */
+ struct list_head lpfc_io_buf_list_get;
+ spinlock_t io_buf_list_put_lock; /* Common buf free list lock */
+ struct list_head lpfc_io_buf_list_put;
+ spinlock_t abts_scsi_buf_list_lock; /* list of aborted SCSI IOs */
+ struct list_head lpfc_abts_scsi_buf_list;
+ spinlock_t abts_nvme_buf_list_lock; /* list of aborted NVME IOs */
+ struct list_head lpfc_abts_nvme_buf_list;
+ uint32_t total_io_bufs;
+ uint32_t get_io_bufs;
+ uint32_t put_io_bufs;
+ uint32_t empty_io_bufs;
+ uint32_t abts_scsi_io_bufs;
+ uint32_t abts_nvme_io_bufs;
+
+ /* Multi-XRI pool per HWQ */
+ struct lpfc_multixri_pool *p_multixri_pool;
+
+ /* FC-4 Stats counters */
+ struct lpfc_fc4_ctrl_stat nvme_cstat;
+ struct lpfc_fc4_ctrl_stat scsi_cstat;
+#ifdef LPFC_HDWQ_LOCK_STAT
+ struct lpfc_lock_stat lock_conflict;
+#endif
+
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+#define LPFC_CHECK_CPU_CNT 128
+ uint32_t cpucheck_rcv_io[LPFC_CHECK_CPU_CNT];
+ uint32_t cpucheck_xmt_io[LPFC_CHECK_CPU_CNT];
+ uint32_t cpucheck_cmpl_io[LPFC_CHECK_CPU_CNT];
+#endif
+};
+
+#ifdef LPFC_HDWQ_LOCK_STAT
+/* compile time trylock stats */
+#define lpfc_qp_spin_lock_irqsave(lock, flag, qp, lstat) \
+ { \
+ int only_once = 1; \
+ while (spin_trylock_irqsave(lock, flag) == 0) { \
+ if (only_once) { \
+ only_once = 0; \
+ qp->lock_conflict.lstat++; \
+ } \
+ } \
+ }
+#define lpfc_qp_spin_lock(lock, qp, lstat) \
+ { \
+ int only_once = 1; \
+ while (spin_trylock(lock) == 0) { \
+ if (only_once) { \
+ only_once = 0; \
+ qp->lock_conflict.lstat++; \
+ } \
+ } \
+ }
+#else
+#define lpfc_qp_spin_lock_irqsave(lock, flag, qp, lstat) \
+ spin_lock_irqsave(lock, flag)
+#define lpfc_qp_spin_lock(lock, qp, lstat) spin_lock(lock)
+#endif
+
struct lpfc_sli4_hba {
void __iomem *conf_regs_memmap_p; /* Kernel memory mapped address for
* config space registers
@@ -599,21 +784,19 @@ struct lpfc_sli4_hba {
struct lpfc_hba_eq_hdl *hba_eq_hdl; /* HBA per-WQ handle */
void (*sli4_eq_clr_intr)(struct lpfc_queue *q);
- uint32_t (*sli4_eq_release)(struct lpfc_queue *q, bool arm);
- uint32_t (*sli4_cq_release)(struct lpfc_queue *q, bool arm);
+ void (*sli4_write_eq_db)(struct lpfc_hba *phba, struct lpfc_queue *eq,
+ uint32_t count, bool arm);
+ void (*sli4_write_cq_db)(struct lpfc_hba *phba, struct lpfc_queue *cq,
+ uint32_t count, bool arm);
/* Pointers to the constructed SLI4 queues */
- struct lpfc_queue **hba_eq; /* Event queues for HBA */
- struct lpfc_queue **fcp_cq; /* Fast-path FCP compl queue */
- struct lpfc_queue **nvme_cq; /* Fast-path NVME compl queue */
+ struct lpfc_sli4_hdw_queue *hdwq;
+ struct list_head lpfc_wq_list;
+
+ /* Pointers to the constructed SLI4 queues for NVMET */
struct lpfc_queue **nvmet_cqset; /* Fast-path NVMET CQ Set queues */
struct lpfc_queue **nvmet_mrq_hdr; /* Fast-path NVMET hdr MRQs */
struct lpfc_queue **nvmet_mrq_data; /* Fast-path NVMET data MRQs */
- struct lpfc_queue **fcp_wq; /* Fast-path FCP work queue */
- struct lpfc_queue **nvme_wq; /* Fast-path NVME work queue */
- uint16_t *fcp_cq_map;
- uint16_t *nvme_cq_map;
- struct list_head lpfc_wq_list;
struct lpfc_queue *mbx_cq; /* Slow-path mailbox complete queue */
struct lpfc_queue *els_cq; /* Slow-path ELS response complete queue */
@@ -631,13 +814,7 @@ struct lpfc_sli4_hba {
uint32_t ulp0_mode; /* ULP0 protocol mode */
uint32_t ulp1_mode; /* ULP1 protocol mode */
- struct lpfc_queue *fof_eq; /* Flash Optimized Fabric Event queue */
-
/* Optimized Access Storage specific queues/structures */
-
- struct lpfc_queue *oas_cq; /* OAS completion queue */
- struct lpfc_queue *oas_wq; /* OAS Work queue */
- struct lpfc_sli_ring *oas_ring;
uint64_t oas_next_lun;
uint8_t oas_next_tgt_wwpn[8];
uint8_t oas_next_vpt_wwpn[8];
@@ -663,22 +840,22 @@ struct lpfc_sli4_hba {
uint16_t rpi_hdrs_in_use; /* must post rpi hdrs if set. */
uint16_t next_xri; /* last_xri - max_cfg_param.xri_base = used */
uint16_t next_rpi;
- uint16_t nvme_xri_max;
- uint16_t nvme_xri_cnt;
- uint16_t nvme_xri_start;
- uint16_t scsi_xri_max;
- uint16_t scsi_xri_cnt;
- uint16_t scsi_xri_start;
+ uint16_t io_xri_max;
+ uint16_t io_xri_cnt;
+ uint16_t io_xri_start;
uint16_t els_xri_cnt;
uint16_t nvmet_xri_cnt;
uint16_t nvmet_io_wait_cnt;
uint16_t nvmet_io_wait_total;
+ uint16_t cq_max;
+ struct lpfc_queue **cq_lookup;
struct list_head lpfc_els_sgl_list;
struct list_head lpfc_abts_els_sgl_list;
+ spinlock_t abts_scsi_buf_list_lock; /* list of aborted SCSI IOs */
+ struct list_head lpfc_abts_scsi_buf_list;
struct list_head lpfc_nvmet_sgl_list;
+ spinlock_t abts_nvmet_buf_list_lock; /* list of aborted NVMET IOs */
struct list_head lpfc_abts_nvmet_ctx_list;
- struct list_head lpfc_abts_scsi_buf_list;
- struct list_head lpfc_abts_nvme_buf_list;
struct list_head lpfc_nvmet_io_wait_list;
struct lpfc_nvmet_ctx_info *nvmet_ctx_info;
struct lpfc_sglq **lpfc_sglq_active_list;
@@ -707,17 +884,16 @@ struct lpfc_sli4_hba {
#define LPFC_SLI4_PPNAME_NON 0
#define LPFC_SLI4_PPNAME_GET 1
struct lpfc_iov iov;
- spinlock_t abts_nvme_buf_list_lock; /* list of aborted SCSI IOs */
- spinlock_t abts_scsi_buf_list_lock; /* list of aborted SCSI IOs */
spinlock_t sgl_list_lock; /* list of aborted els IOs */
spinlock_t nvmet_io_wait_lock; /* IOs waiting for ctx resources */
uint32_t physical_port;
/* CPU to vector mapping information */
struct lpfc_vector_map_info *cpu_map;
- uint16_t num_online_cpu;
+ uint16_t num_possible_cpu;
uint16_t num_present_cpu;
uint16_t curr_disp_cpu;
+ struct lpfc_eq_intr_info __percpu *eq_info;
uint32_t conf_trunk;
#define lpfc_conf_trunk_port0_WORD conf_trunk
#define lpfc_conf_trunk_port0_SHIFT 0
@@ -818,12 +994,12 @@ struct lpfc_queue *lpfc_sli4_queue_alloc(struct lpfc_hba *, uint32_t,
uint32_t, uint32_t);
void lpfc_sli4_queue_free(struct lpfc_queue *);
int lpfc_eq_create(struct lpfc_hba *, struct lpfc_queue *, uint32_t);
-int lpfc_modify_hba_eq_delay(struct lpfc_hba *phba, uint32_t startq,
- uint32_t numq, uint32_t imax);
+void lpfc_modify_hba_eq_delay(struct lpfc_hba *phba, uint32_t startq,
+ uint32_t numq, uint32_t usdelay);
int lpfc_cq_create(struct lpfc_hba *, struct lpfc_queue *,
struct lpfc_queue *, uint32_t, uint32_t);
int lpfc_cq_create_set(struct lpfc_hba *phba, struct lpfc_queue **cqp,
- struct lpfc_queue **eqp, uint32_t type,
+ struct lpfc_sli4_hdw_queue *hdwq, uint32_t type,
uint32_t subtype);
int32_t lpfc_mq_create(struct lpfc_hba *, struct lpfc_queue *,
struct lpfc_queue *, uint32_t);
@@ -843,12 +1019,10 @@ int lpfc_rq_destroy(struct lpfc_hba *, struct lpfc_queue *,
int lpfc_sli4_queue_setup(struct lpfc_hba *);
void lpfc_sli4_queue_unset(struct lpfc_hba *);
int lpfc_sli4_post_sgl(struct lpfc_hba *, dma_addr_t, dma_addr_t, uint16_t);
-int lpfc_sli4_repost_scsi_sgl_list(struct lpfc_hba *);
-int lpfc_repost_nvme_sgl_list(struct lpfc_hba *phba);
+int lpfc_repost_io_sgl_list(struct lpfc_hba *phba);
uint16_t lpfc_sli4_next_xritag(struct lpfc_hba *);
void lpfc_sli4_free_xri(struct lpfc_hba *, int);
int lpfc_sli4_post_async_mbox(struct lpfc_hba *);
-int lpfc_sli4_post_scsi_sgl_block(struct lpfc_hba *, struct list_head *, int);
struct lpfc_cq_event *__lpfc_sli4_cq_event_alloc(struct lpfc_hba *);
struct lpfc_cq_event *lpfc_sli4_cq_event_alloc(struct lpfc_hba *);
void __lpfc_sli4_cq_event_release(struct lpfc_hba *, struct lpfc_cq_event *);
@@ -868,9 +1042,9 @@ int lpfc_sli4_resume_rpi(struct lpfc_nodelist *,
void lpfc_sli4_fcp_xri_abort_event_proc(struct lpfc_hba *);
void lpfc_sli4_els_xri_abort_event_proc(struct lpfc_hba *);
void lpfc_sli4_fcp_xri_aborted(struct lpfc_hba *,
- struct sli4_wcqe_xri_aborted *);
+ struct sli4_wcqe_xri_aborted *, int);
void lpfc_sli4_nvme_xri_aborted(struct lpfc_hba *phba,
- struct sli4_wcqe_xri_aborted *axri);
+ struct sli4_wcqe_xri_aborted *axri, int idx);
void lpfc_sli4_nvmet_xri_aborted(struct lpfc_hba *phba,
struct sli4_wcqe_xri_aborted *axri);
void lpfc_sli4_els_xri_aborted(struct lpfc_hba *,
@@ -884,11 +1058,15 @@ int lpfc_sli4_get_els_iocb_cnt(struct lpfc_hba *);
int lpfc_sli4_get_iocb_cnt(struct lpfc_hba *phba);
int lpfc_sli4_init_vpi(struct lpfc_vport *);
inline void lpfc_sli4_eq_clr_intr(struct lpfc_queue *);
-uint32_t lpfc_sli4_cq_release(struct lpfc_queue *, bool);
-uint32_t lpfc_sli4_eq_release(struct lpfc_queue *, bool);
+void lpfc_sli4_write_cq_db(struct lpfc_hba *phba, struct lpfc_queue *q,
+ uint32_t count, bool arm);
+void lpfc_sli4_write_eq_db(struct lpfc_hba *phba, struct lpfc_queue *q,
+ uint32_t count, bool arm);
inline void lpfc_sli4_if6_eq_clr_intr(struct lpfc_queue *q);
-uint32_t lpfc_sli4_if6_cq_release(struct lpfc_queue *q, bool arm);
-uint32_t lpfc_sli4_if6_eq_release(struct lpfc_queue *q, bool arm);
+void lpfc_sli4_if6_write_cq_db(struct lpfc_hba *phba, struct lpfc_queue *q,
+ uint32_t count, bool arm);
+void lpfc_sli4_if6_write_eq_db(struct lpfc_hba *phba, struct lpfc_queue *q,
+ uint32_t count, bool arm);
void lpfc_sli4_fcfi_unreg(struct lpfc_hba *, uint16_t);
int lpfc_sli4_fcf_scan_read_fcf_rec(struct lpfc_hba *, uint16_t);
int lpfc_sli4_fcf_rr_read_fcf_rec(struct lpfc_hba *, uint16_t);
diff --git a/drivers/scsi/lpfc/lpfc_version.h b/drivers/scsi/lpfc/lpfc_version.h
index 3f4398ffb567..43fd693cf042 100644
--- a/drivers/scsi/lpfc/lpfc_version.h
+++ b/drivers/scsi/lpfc/lpfc_version.h
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term *
+ * Copyright (C) 2017-2019 Broadcom. All Rights Reserved. The term *
* “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. *
* Copyright (C) 2004-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
@@ -20,7 +20,7 @@
* included with this package. *
*******************************************************************/
-#define LPFC_DRIVER_VERSION "12.0.0.10"
+#define LPFC_DRIVER_VERSION "12.2.0.0"
#define LPFC_DRIVER_NAME "lpfc"
/* Used for SLI 2/3 */
diff --git a/drivers/scsi/lpfc/lpfc_vport.c b/drivers/scsi/lpfc/lpfc_vport.c
index 102a011ff6d4..343bc71d4615 100644
--- a/drivers/scsi/lpfc/lpfc_vport.c
+++ b/drivers/scsi/lpfc/lpfc_vport.c
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term *
+ * Copyright (C) 2017-2019 Broadcom. All Rights Reserved. The term *
* “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. *
* Copyright (C) 2004-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
@@ -313,11 +313,11 @@ lpfc_vport_create(struct fc_vport *fc_vport, bool disable)
goto error_out;
}
- /* NPIV is not supported if HBA has NVME enabled */
- if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) {
+ /* NPIV is not supported if HBA has NVME Target enabled */
+ if (phba->nvmet_support) {
lpfc_printf_log(phba, KERN_ERR, LOG_VPORT,
"3189 Create VPORT failed: "
- "NPIV is not supported on NVME\n");
+ "NPIV is not supported on NVME Target\n");
rc = VPORT_INVAL;
goto error_out;
}
@@ -403,6 +403,9 @@ lpfc_vport_create(struct fc_vport *fc_vport, bool disable)
/* Set the DFT_LUN_Q_DEPTH accordingly */
vport->cfg_lun_queue_depth = phba->pport->cfg_lun_queue_depth;
+ /* Only the physical port can support NVME for now */
+ vport->cfg_enable_fc4_type = LPFC_ENABLE_FCP;
+
*(struct lpfc_vport **)fc_vport->dd_data = vport;
vport->fc_vport = fc_vport;
@@ -415,22 +418,6 @@ lpfc_vport_create(struct fc_vport *fc_vport, bool disable)
vport->fdmi_port_mask = phba->pport->fdmi_port_mask;
}
- if ((phba->nvmet_support == 0) &&
- ((phba->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) ||
- (phba->cfg_enable_fc4_type == LPFC_ENABLE_NVME))) {
- /* Create NVME binding with nvme_fc_transport. This
- * ensures the vport is initialized.
- */
- rc = lpfc_nvme_create_localport(vport);
- if (rc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "6003 %s status x%x\n",
- "NVME registration failed, ",
- rc);
- goto error_out;
- }
- }
-
/*
* In SLI4, the vpi must be activated before it can be used
* by the port.
diff --git a/drivers/scsi/megaraid/megaraid_sas.h b/drivers/scsi/megaraid/megaraid_sas.h
index 16536c41f0c5..6fd57f7f0b1e 100644
--- a/drivers/scsi/megaraid/megaraid_sas.h
+++ b/drivers/scsi/megaraid/megaraid_sas.h
@@ -33,8 +33,8 @@
/*
* MegaRAID SAS Driver meta data
*/
-#define MEGASAS_VERSION "07.707.50.00-rc1"
-#define MEGASAS_RELDATE "December 18, 2018"
+#define MEGASAS_VERSION "07.707.51.00-rc1"
+#define MEGASAS_RELDATE "February 7, 2019"
/*
* Device IDs
@@ -790,6 +790,38 @@ struct MR_LD_TARGETID_LIST {
u8 targetId[MAX_LOGICAL_DRIVES_EXT];
};
+struct MR_HOST_DEVICE_LIST_ENTRY {
+ struct {
+ union {
+ struct {
+#if defined(__BIG_ENDIAN_BITFIELD)
+ u8 reserved:7;
+ u8 is_sys_pd:1;
+#else
+ u8 is_sys_pd:1;
+ u8 reserved:7;
+#endif
+ } bits;
+ u8 byte;
+ } u;
+ } flags;
+ u8 scsi_type;
+ __le16 target_id;
+ u8 reserved[4];
+ __le64 sas_addr[2];
+} __packed;
+
+struct MR_HOST_DEVICE_LIST {
+ __le32 size;
+ __le32 count;
+ __le32 reserved[2];
+ struct MR_HOST_DEVICE_LIST_ENTRY host_device_list[1];
+} __packed;
+
+#define HOST_DEVICE_LIST_SZ (sizeof(struct MR_HOST_DEVICE_LIST) + \
+ (sizeof(struct MR_HOST_DEVICE_LIST_ENTRY) * \
+ (MEGASAS_MAX_PD + MAX_LOGICAL_DRIVES_EXT - 1)))
+
/*
* SAS controller properties
@@ -870,13 +902,17 @@ struct megasas_ctrl_prop {
u8 viewSpace;
struct {
#if defined(__BIG_ENDIAN_BITFIELD)
- u16 reserved2:11;
+ u16 reserved3:9;
+ u16 enable_fw_dev_list:1;
+ u16 reserved2:1;
u16 enable_snap_dump:1;
u16 reserved1:4;
#else
u16 reserved1:4;
u16 enable_snap_dump:1;
- u16 reserved2:11;
+ u16 reserved2:1;
+ u16 enable_fw_dev_list:1;
+ u16 reserved3:9;
#endif
} on_off_properties2;
};
@@ -1685,7 +1721,8 @@ union megasas_sgl_frame {
typedef union _MFI_CAPABILITIES {
struct {
#if defined(__BIG_ENDIAN_BITFIELD)
- u32 reserved:17;
+ u32 reserved:16;
+ u32 support_fw_exposed_dev_list:1;
u32 support_nvme_passthru:1;
u32 support_64bit_mode:1;
u32 support_pd_map_target_id:1;
@@ -1717,7 +1754,8 @@ typedef union _MFI_CAPABILITIES {
u32 support_pd_map_target_id:1;
u32 support_64bit_mode:1;
u32 support_nvme_passthru:1;
- u32 reserved:17;
+ u32 support_fw_exposed_dev_list:1;
+ u32 reserved:16;
#endif
} mfi_capabilities;
__le32 reg;
@@ -2202,6 +2240,9 @@ struct megasas_instance {
struct MR_LD_TARGETID_LIST *ld_targetid_list_buf;
dma_addr_t ld_targetid_list_buf_h;
+ struct MR_HOST_DEVICE_LIST *host_device_list_buf;
+ dma_addr_t host_device_list_buf_h;
+
struct MR_SNAPDUMP_PROPERTIES *snapdump_prop;
dma_addr_t snapdump_prop_h;
@@ -2337,6 +2378,7 @@ struct megasas_instance {
u8 task_abort_tmo;
u8 max_reset_tmo;
u8 snapdump_wait_time;
+ u8 enable_fw_dev_list;
};
struct MR_LD_VF_MAP {
u32 size;
diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c
index fcbff83c0097..293f5cf524d7 100644
--- a/drivers/scsi/megaraid/megaraid_sas_base.c
+++ b/drivers/scsi/megaraid/megaraid_sas_base.c
@@ -3924,12 +3924,12 @@ megasas_transition_to_ready(struct megasas_instance *instance, int ocr)
/*
* The cur_state should not last for more than max_wait secs
*/
- for (i = 0; i < max_wait; i++) {
+ for (i = 0; i < max_wait * 50; i++) {
curr_abs_state = instance->instancet->
read_fw_status_reg(instance);
if (abs_state == curr_abs_state) {
- msleep(1000);
+ msleep(20);
} else
break;
}
@@ -4188,6 +4188,7 @@ int megasas_alloc_cmds(struct megasas_instance *instance)
if (megasas_create_frame_pool(instance)) {
dev_printk(KERN_DEBUG, &instance->pdev->dev, "Error creating frame DMA pool\n");
megasas_free_cmds(instance);
+ return -ENOMEM;
}
return 0;
@@ -4634,6 +4635,123 @@ megasas_ld_list_query(struct megasas_instance *instance, u8 query_type)
return ret;
}
+/**
+ * dcmd.opcode - MR_DCMD_CTRL_DEVICE_LIST_GET
+ * dcmd.mbox - reserved
+ * dcmd.sge IN - ptr to return MR_HOST_DEVICE_LIST structure
+ * Desc: This DCMD will return the combined device list
+ * Status: MFI_STAT_OK - List returned successfully
+ * MFI_STAT_INVALID_CMD - Firmware support for the feature has been
+ * disabled
+ * @instance: Adapter soft state
+ * @is_probe: Driver probe check
+ * Return: 0 if DCMD succeeded
+ * non-zero if failed
+ */
+int
+megasas_host_device_list_query(struct megasas_instance *instance,
+ bool is_probe)
+{
+ int ret, i, target_id;
+ struct megasas_cmd *cmd;
+ struct megasas_dcmd_frame *dcmd;
+ struct MR_HOST_DEVICE_LIST *ci;
+ u32 count;
+ dma_addr_t ci_h;
+
+ ci = instance->host_device_list_buf;
+ ci_h = instance->host_device_list_buf_h;
+
+ cmd = megasas_get_cmd(instance);
+
+ if (!cmd) {
+ dev_warn(&instance->pdev->dev,
+ "%s: failed to get cmd\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ dcmd = &cmd->frame->dcmd;
+
+ memset(ci, 0, sizeof(*ci));
+ memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE);
+
+ dcmd->mbox.b[0] = is_probe ? 0 : 1;
+ dcmd->cmd = MFI_CMD_DCMD;
+ dcmd->cmd_status = MFI_STAT_INVALID_STATUS;
+ dcmd->sge_count = 1;
+ dcmd->flags = MFI_FRAME_DIR_READ;
+ dcmd->timeout = 0;
+ dcmd->pad_0 = 0;
+ dcmd->data_xfer_len = cpu_to_le32(HOST_DEVICE_LIST_SZ);
+ dcmd->opcode = cpu_to_le32(MR_DCMD_CTRL_DEVICE_LIST_GET);
+
+ megasas_set_dma_settings(instance, dcmd, ci_h, HOST_DEVICE_LIST_SZ);
+
+ if (!instance->mask_interrupts) {
+ ret = megasas_issue_blocked_cmd(instance, cmd,
+ MFI_IO_TIMEOUT_SECS);
+ } else {
+ ret = megasas_issue_polled(instance, cmd);
+ cmd->flags |= DRV_DCMD_SKIP_REFIRE;
+ }
+
+ switch (ret) {
+ case DCMD_SUCCESS:
+ /* Fill the internal pd_list and ld_ids array based on
+ * targetIds returned by FW
+ */
+ count = le32_to_cpu(ci->count);
+
+ memset(instance->local_pd_list, 0,
+ MEGASAS_MAX_PD * sizeof(struct megasas_pd_list));
+ memset(instance->ld_ids, 0xff, MAX_LOGICAL_DRIVES_EXT);
+ for (i = 0; i < count; i++) {
+ target_id = le16_to_cpu(ci->host_device_list[i].target_id);
+ if (ci->host_device_list[i].flags.u.bits.is_sys_pd) {
+ instance->local_pd_list[target_id].tid = target_id;
+ instance->local_pd_list[target_id].driveType =
+ ci->host_device_list[i].scsi_type;
+ instance->local_pd_list[target_id].driveState =
+ MR_PD_STATE_SYSTEM;
+ } else {
+ instance->ld_ids[target_id] = target_id;
+ }
+ }
+
+ memcpy(instance->pd_list, instance->local_pd_list,
+ sizeof(instance->pd_list));
+ break;
+
+ case DCMD_TIMEOUT:
+ switch (dcmd_timeout_ocr_possible(instance)) {
+ case INITIATE_OCR:
+ cmd->flags |= DRV_DCMD_SKIP_REFIRE;
+ megasas_reset_fusion(instance->host,
+ MFI_IO_TIMEOUT_OCR);
+ break;
+ case KILL_ADAPTER:
+ megaraid_sas_kill_hba(instance);
+ break;
+ case IGNORE_TIMEOUT:
+ dev_info(&instance->pdev->dev, "Ignore DCMD timeout: %s %d\n",
+ __func__, __LINE__);
+ break;
+ }
+ break;
+ case DCMD_FAILED:
+ dev_err(&instance->pdev->dev,
+ "%s: MR_DCMD_CTRL_DEVICE_LIST_GET failed\n",
+ __func__);
+ break;
+ }
+
+ if (ret != DCMD_TIMEOUT)
+ megasas_return_cmd(instance, cmd);
+
+ return ret;
+}
+
/*
* megasas_update_ext_vd_details : Update details w.r.t Extended VD
* instance : Controller's instance
@@ -4861,6 +4979,9 @@ megasas_get_ctrl_info(struct megasas_instance *instance)
(ci->properties.on_off_properties2.enable_snap_dump ?
MEGASAS_DEFAULT_SNAP_DUMP_WAIT_TIME : 0);
+ instance->enable_fw_dev_list =
+ ci->properties.on_off_properties2.enable_fw_dev_list;
+
dev_info(&instance->pdev->dev,
"controller type\t: %s(%dMB)\n",
instance->is_imr ? "iMR" : "MR",
@@ -5320,6 +5441,40 @@ fallback:
}
/**
+ * megasas_get_device_list - Get the PD and LD device list from FW.
+ * @instance: Adapter soft state
+ * @return: Success or failure
+ *
+ * Issue DCMDs to Firmware to get the PD and LD list.
+ * Based on the FW support, driver sends the HOST_DEVICE_LIST or combination
+ * of PD_LIST/LD_LIST_QUERY DCMDs to get the device list.
+ */
+static
+int megasas_get_device_list(struct megasas_instance *instance)
+{
+ memset(instance->pd_list, 0,
+ (MEGASAS_MAX_PD * sizeof(struct megasas_pd_list)));
+ memset(instance->ld_ids, 0xff, MEGASAS_MAX_LD_IDS);
+
+ if (instance->enable_fw_dev_list) {
+ if (megasas_host_device_list_query(instance, true))
+ return FAILED;
+ } else {
+ if (megasas_get_pd_list(instance) < 0) {
+ dev_err(&instance->pdev->dev, "failed to get PD list\n");
+ return FAILED;
+ }
+
+ if (megasas_ld_list_query(instance,
+ MR_LD_QUERY_TYPE_EXPOSED_TO_HOST)) {
+ dev_err(&instance->pdev->dev, "failed to get LD list\n");
+ return FAILED;
+ }
+ }
+
+ return SUCCESS;
+}
+/**
* megasas_init_fw - Initializes the FW
* @instance: Adapter soft state
*
@@ -5571,18 +5726,13 @@ static int megasas_init_fw(struct megasas_instance *instance)
megasas_setup_jbod_map(instance);
- /** for passthrough
- * the following function will get the PD LIST.
- */
- memset(instance->pd_list, 0,
- (MEGASAS_MAX_PD * sizeof(struct megasas_pd_list)));
- if (megasas_get_pd_list(instance) < 0) {
- dev_err(&instance->pdev->dev, "failed to get PD list\n");
+ if (megasas_get_device_list(instance) != SUCCESS) {
+ dev_err(&instance->pdev->dev,
+ "%s: megasas_get_device_list failed\n",
+ __func__);
goto fail_get_ld_pd_list;
}
- memset(instance->ld_ids, 0xff, MEGASAS_MAX_LD_IDS);
-
/* stream detection initialization */
if (instance->adapter_type >= VENTURA_SERIES) {
fusion->stream_detect_by_ld =
@@ -5612,10 +5762,6 @@ static int megasas_init_fw(struct megasas_instance *instance)
}
}
- if (megasas_ld_list_query(instance,
- MR_LD_QUERY_TYPE_EXPOSED_TO_HOST))
- goto fail_get_ld_pd_list;
-
/*
* Compute the max allowed sectors per IO: The controller info has two
* limits on max sectors. Driver should use the minimum of these two.
@@ -6424,6 +6570,18 @@ int megasas_alloc_ctrl_dma_buffers(struct megasas_instance *instance)
if (!instance->snapdump_prop)
dev_err(&pdev->dev,
"Failed to allocate snapdump properties buffer\n");
+
+ instance->host_device_list_buf = dma_alloc_coherent(&pdev->dev,
+ HOST_DEVICE_LIST_SZ,
+ &instance->host_device_list_buf_h,
+ GFP_KERNEL);
+
+ if (!instance->host_device_list_buf) {
+ dev_err(&pdev->dev,
+ "Failed to allocate targetid list buffer\n");
+ return -ENOMEM;
+ }
+
}
instance->pd_list_buf =
@@ -6573,6 +6731,13 @@ void megasas_free_ctrl_dma_buffers(struct megasas_instance *instance)
sizeof(struct MR_SNAPDUMP_PROPERTIES),
instance->snapdump_prop,
instance->snapdump_prop_h);
+
+ if (instance->host_device_list_buf)
+ dma_free_coherent(&pdev->dev,
+ HOST_DEVICE_LIST_SZ,
+ instance->host_device_list_buf,
+ instance->host_device_list_buf_h);
+
}
/*
@@ -6746,7 +6911,9 @@ static int megasas_probe_one(struct pci_dev *pdev,
/*
* Trigger SCSI to scan our drives
*/
- scsi_scan_host(host);
+ if (!instance->enable_fw_dev_list ||
+ (instance->host_device_list_buf->count > 0))
+ scsi_scan_host(host);
/*
* Initiate AEN (Asynchronous Event Notification)
@@ -7866,6 +8033,139 @@ static inline void megasas_remove_scsi_device(struct scsi_device *sdev)
scsi_device_put(sdev);
}
+/**
+ * megasas_update_device_list - Update the PD and LD device list from FW
+ * after an AEN event notification
+ * @instance: Adapter soft state
+ * @event_type: Indicates type of event (PD or LD event)
+ *
+ * @return: Success or failure
+ *
+ * Issue DCMDs to Firmware to update the internal device list in driver.
+ * Based on the FW support, driver sends the HOST_DEVICE_LIST or combination
+ * of PD_LIST/LD_LIST_QUERY DCMDs to get the device list.
+ */
+static
+int megasas_update_device_list(struct megasas_instance *instance,
+ int event_type)
+{
+ int dcmd_ret = DCMD_SUCCESS;
+
+ if (instance->enable_fw_dev_list) {
+ dcmd_ret = megasas_host_device_list_query(instance, false);
+ if (dcmd_ret != DCMD_SUCCESS)
+ goto out;
+ } else {
+ if (event_type & SCAN_PD_CHANNEL) {
+ dcmd_ret = megasas_get_pd_list(instance);
+
+ if (dcmd_ret != DCMD_SUCCESS)
+ goto out;
+ }
+
+ if (event_type & SCAN_VD_CHANNEL) {
+ if (!instance->requestorId ||
+ (instance->requestorId &&
+ megasas_get_ld_vf_affiliation(instance, 0))) {
+ dcmd_ret = megasas_ld_list_query(instance,
+ MR_LD_QUERY_TYPE_EXPOSED_TO_HOST);
+ if (dcmd_ret != DCMD_SUCCESS)
+ goto out;
+ }
+ }
+ }
+
+out:
+ return dcmd_ret;
+}
+
+/**
+ * megasas_add_remove_devices - Add/remove devices to SCSI mid-layer
+ * after an AEN event notification
+ * @instance: Adapter soft state
+ * @scan_type: Indicates type of devices (PD/LD) to add
+ * @return void
+ */
+static
+void megasas_add_remove_devices(struct megasas_instance *instance,
+ int scan_type)
+{
+ int i, j;
+ u16 pd_index = 0;
+ u16 ld_index = 0;
+ u16 channel = 0, id = 0;
+ struct Scsi_Host *host;
+ struct scsi_device *sdev1;
+ struct MR_HOST_DEVICE_LIST *targetid_list = NULL;
+ struct MR_HOST_DEVICE_LIST_ENTRY *targetid_entry = NULL;
+
+ host = instance->host;
+
+ if (instance->enable_fw_dev_list) {
+ targetid_list = instance->host_device_list_buf;
+ for (i = 0; i < targetid_list->count; i++) {
+ targetid_entry = &targetid_list->host_device_list[i];
+ if (targetid_entry->flags.u.bits.is_sys_pd) {
+ channel = le16_to_cpu(targetid_entry->target_id) /
+ MEGASAS_MAX_DEV_PER_CHANNEL;
+ id = le16_to_cpu(targetid_entry->target_id) %
+ MEGASAS_MAX_DEV_PER_CHANNEL;
+ } else {
+ channel = MEGASAS_MAX_PD_CHANNELS +
+ (le16_to_cpu(targetid_entry->target_id) /
+ MEGASAS_MAX_DEV_PER_CHANNEL);
+ id = le16_to_cpu(targetid_entry->target_id) %
+ MEGASAS_MAX_DEV_PER_CHANNEL;
+ }
+ sdev1 = scsi_device_lookup(host, channel, id, 0);
+ if (!sdev1) {
+ scsi_add_device(host, channel, id, 0);
+ } else {
+ scsi_device_put(sdev1);
+ }
+ }
+ }
+
+ if (scan_type & SCAN_PD_CHANNEL) {
+ for (i = 0; i < MEGASAS_MAX_PD_CHANNELS; i++) {
+ for (j = 0; j < MEGASAS_MAX_DEV_PER_CHANNEL; j++) {
+ pd_index = i * MEGASAS_MAX_DEV_PER_CHANNEL + j;
+ sdev1 = scsi_device_lookup(host, i, j, 0);
+ if (instance->pd_list[pd_index].driveState ==
+ MR_PD_STATE_SYSTEM) {
+ if (!sdev1)
+ scsi_add_device(host, i, j, 0);
+ else
+ scsi_device_put(sdev1);
+ } else {
+ if (sdev1)
+ megasas_remove_scsi_device(sdev1);
+ }
+ }
+ }
+ }
+
+ if (scan_type & SCAN_VD_CHANNEL) {
+ for (i = 0; i < MEGASAS_MAX_LD_CHANNELS; i++) {
+ for (j = 0; j < MEGASAS_MAX_DEV_PER_CHANNEL; j++) {
+ ld_index = (i * MEGASAS_MAX_DEV_PER_CHANNEL) + j;
+ sdev1 = scsi_device_lookup(host,
+ MEGASAS_MAX_PD_CHANNELS + i, j, 0);
+ if (instance->ld_ids[ld_index] != 0xff) {
+ if (!sdev1)
+ scsi_add_device(host, MEGASAS_MAX_PD_CHANNELS + i, j, 0);
+ else
+ scsi_device_put(sdev1);
+ } else {
+ if (sdev1)
+ megasas_remove_scsi_device(sdev1);
+ }
+ }
+ }
+ }
+
+}
+
static void
megasas_aen_polling(struct work_struct *work)
{
@@ -7873,11 +8173,7 @@ megasas_aen_polling(struct work_struct *work)
container_of(work, struct megasas_aen_event, hotplug_work.work);
struct megasas_instance *instance = ev->instance;
union megasas_evt_class_locale class_locale;
- struct Scsi_Host *host;
- struct scsi_device *sdev1;
- u16 pd_index = 0;
- u16 ld_index = 0;
- int i, j, doscan = 0;
+ int event_type = 0;
u32 seq_num, wait_time = MEGASAS_RESET_WAIT_TIME;
int error;
u8 dcmd_ret = DCMD_SUCCESS;
@@ -7896,7 +8192,6 @@ megasas_aen_polling(struct work_struct *work)
mutex_lock(&instance->reset_mutex);
instance->ev = NULL;
- host = instance->host;
if (instance->evt_detail) {
megasas_decode_evt(instance);
@@ -7904,40 +8199,20 @@ megasas_aen_polling(struct work_struct *work)
case MR_EVT_PD_INSERTED:
case MR_EVT_PD_REMOVED:
- dcmd_ret = megasas_get_pd_list(instance);
- if (dcmd_ret == DCMD_SUCCESS)
- doscan = SCAN_PD_CHANNEL;
+ event_type = SCAN_PD_CHANNEL;
break;
case MR_EVT_LD_OFFLINE:
case MR_EVT_CFG_CLEARED:
case MR_EVT_LD_DELETED:
case MR_EVT_LD_CREATED:
- if (!instance->requestorId ||
- (instance->requestorId && megasas_get_ld_vf_affiliation(instance, 0)))
- dcmd_ret = megasas_ld_list_query(instance, MR_LD_QUERY_TYPE_EXPOSED_TO_HOST);
-
- if (dcmd_ret == DCMD_SUCCESS)
- doscan = SCAN_VD_CHANNEL;
-
+ event_type = SCAN_VD_CHANNEL;
break;
case MR_EVT_CTRL_HOST_BUS_SCAN_REQUESTED:
case MR_EVT_FOREIGN_CFG_IMPORTED:
case MR_EVT_LD_STATE_CHANGE:
- dcmd_ret = megasas_get_pd_list(instance);
-
- if (dcmd_ret != DCMD_SUCCESS)
- break;
-
- if (!instance->requestorId ||
- (instance->requestorId && megasas_get_ld_vf_affiliation(instance, 0)))
- dcmd_ret = megasas_ld_list_query(instance, MR_LD_QUERY_TYPE_EXPOSED_TO_HOST);
-
- if (dcmd_ret != DCMD_SUCCESS)
- break;
-
- doscan = SCAN_VD_CHANNEL | SCAN_PD_CHANNEL;
+ event_type = SCAN_PD_CHANNEL | SCAN_VD_CHANNEL;
dev_info(&instance->pdev->dev, "scanning for scsi%d...\n",
instance->host->host_no);
break;
@@ -7953,7 +8228,7 @@ megasas_aen_polling(struct work_struct *work)
}
break;
default:
- doscan = 0;
+ event_type = 0;
break;
}
} else {
@@ -7963,44 +8238,13 @@ megasas_aen_polling(struct work_struct *work)
return;
}
- mutex_unlock(&instance->reset_mutex);
+ if (event_type)
+ dcmd_ret = megasas_update_device_list(instance, event_type);
- if (doscan & SCAN_PD_CHANNEL) {
- for (i = 0; i < MEGASAS_MAX_PD_CHANNELS; i++) {
- for (j = 0; j < MEGASAS_MAX_DEV_PER_CHANNEL; j++) {
- pd_index = i*MEGASAS_MAX_DEV_PER_CHANNEL + j;
- sdev1 = scsi_device_lookup(host, i, j, 0);
- if (instance->pd_list[pd_index].driveState ==
- MR_PD_STATE_SYSTEM) {
- if (!sdev1)
- scsi_add_device(host, i, j, 0);
- else
- scsi_device_put(sdev1);
- } else {
- if (sdev1)
- megasas_remove_scsi_device(sdev1);
- }
- }
- }
- }
+ mutex_unlock(&instance->reset_mutex);
- if (doscan & SCAN_VD_CHANNEL) {
- for (i = 0; i < MEGASAS_MAX_LD_CHANNELS; i++) {
- for (j = 0; j < MEGASAS_MAX_DEV_PER_CHANNEL; j++) {
- ld_index = (i * MEGASAS_MAX_DEV_PER_CHANNEL) + j;
- sdev1 = scsi_device_lookup(host, MEGASAS_MAX_PD_CHANNELS + i, j, 0);
- if (instance->ld_ids[ld_index] != 0xff) {
- if (!sdev1)
- scsi_add_device(host, MEGASAS_MAX_PD_CHANNELS + i, j, 0);
- else
- scsi_device_put(sdev1);
- } else {
- if (sdev1)
- megasas_remove_scsi_device(sdev1);
- }
- }
- }
- }
+ if (event_type && dcmd_ret == DCMD_SUCCESS)
+ megasas_add_remove_devices(instance, event_type);
if (dcmd_ret == DCMD_SUCCESS)
seq_num = le32_to_cpu(instance->evt_detail->seq_num) + 1;
diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.c b/drivers/scsi/megaraid/megaraid_sas_fusion.c
index 647f48a28f85..1d17128030cd 100644
--- a/drivers/scsi/megaraid/megaraid_sas_fusion.c
+++ b/drivers/scsi/megaraid/megaraid_sas_fusion.c
@@ -937,11 +937,9 @@ wait_and_poll(struct megasas_instance *instance, struct megasas_cmd *cmd,
{
int i;
struct megasas_header *frame_hdr = &cmd->frame->hdr;
- struct fusion_context *fusion;
u32 msecs = seconds * 1000;
- fusion = instance->ctrl_context;
/*
* Wait for cmd_status to change
*/
@@ -1074,6 +1072,7 @@ megasas_ioc_init_fusion(struct megasas_instance *instance)
drv_ops->mfi_capabilities.support_qd_throttling = 1;
drv_ops->mfi_capabilities.support_pd_map_target_id = 1;
drv_ops->mfi_capabilities.support_nvme_passthru = 1;
+ drv_ops->mfi_capabilities.support_fw_exposed_dev_list = 1;
if (instance->consistent_mask_64bit)
drv_ops->mfi_capabilities.support_64bit_mode = 1;
@@ -1330,7 +1329,6 @@ megasas_sync_map_info(struct megasas_instance *instance)
struct megasas_cmd *cmd;
struct megasas_dcmd_frame *dcmd;
u16 num_lds;
- u32 size_sync_info;
struct fusion_context *fusion;
struct MR_LD_TARGET_SYNC *ci = NULL;
struct MR_DRV_RAID_MAP_ALL *map;
@@ -1359,8 +1357,6 @@ megasas_sync_map_info(struct megasas_instance *instance)
dcmd = &cmd->frame->dcmd;
- size_sync_info = sizeof(struct MR_LD_TARGET_SYNC) *num_lds;
-
memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE);
ci = (struct MR_LD_TARGET_SYNC *)
@@ -1639,15 +1635,12 @@ static inline void megasas_free_ioc_init_cmd(struct megasas_instance *instance)
u32
megasas_init_adapter_fusion(struct megasas_instance *instance)
{
- struct megasas_register_set __iomem *reg_set;
struct fusion_context *fusion;
u32 scratch_pad_1;
int i = 0, count;
fusion = instance->ctrl_context;
- reg_set = instance->reg_set;
-
megasas_fusion_update_can_queue(instance, PROBE_CONTEXT);
/*
@@ -1926,7 +1919,6 @@ static bool
megasas_is_prp_possible(struct megasas_instance *instance,
struct scsi_cmnd *scmd, int sge_count)
{
- struct fusion_context *fusion;
int i;
u32 data_length = 0;
struct scatterlist *sg_scmd;
@@ -1935,7 +1927,6 @@ megasas_is_prp_possible(struct megasas_instance *instance,
mr_nvme_pg_size = max_t(u32, instance->nvme_page_size,
MR_DEFAULT_NVME_PAGE_SIZE);
- fusion = instance->ctrl_context;
data_length = scsi_bufflen(scmd);
sg_scmd = scsi_sglist(scmd);
@@ -2048,12 +2039,9 @@ megasas_make_prp_nvme(struct megasas_instance *instance, struct scsi_cmnd *scmd,
u32 first_prp_len;
bool build_prp = false;
int data_len = scsi_bufflen(scmd);
- struct fusion_context *fusion;
u32 mr_nvme_pg_size = max_t(u32, instance->nvme_page_size,
MR_DEFAULT_NVME_PAGE_SIZE);
- fusion = instance->ctrl_context;
-
build_prp = megasas_is_prp_possible(instance, scmd, sge_count);
if (!build_prp)
@@ -2621,7 +2609,6 @@ megasas_build_ldio_fusion(struct megasas_instance *instance,
u32 start_lba_lo, start_lba_hi, device_id, datalength = 0;
u32 scsi_buff_len;
struct MPI2_RAID_SCSI_IO_REQUEST *io_request;
- union MEGASAS_REQUEST_DESCRIPTOR_UNION *req_desc;
struct IO_REQUEST_INFO io_info;
struct fusion_context *fusion;
struct MR_DRV_RAID_MAP_ALL *local_map_ptr;
@@ -2644,8 +2631,6 @@ megasas_build_ldio_fusion(struct megasas_instance *instance,
rctx->status = 0;
rctx->ex_status = 0;
- req_desc = (union MEGASAS_REQUEST_DESCRIPTOR_UNION *)cmd->request_desc;
-
start_lba_lo = 0;
start_lba_hi = 0;
fp_possible = false;
@@ -3246,9 +3231,6 @@ megasas_build_and_issue_cmd_fusion(struct megasas_instance *instance,
struct megasas_cmd_fusion *cmd, *r1_cmd = NULL;
union MEGASAS_REQUEST_DESCRIPTOR_UNION *req_desc;
u32 index;
- struct fusion_context *fusion;
-
- fusion = instance->ctrl_context;
if ((megasas_cmd_type(scmd) == READ_WRITE_LDIO) &&
instance->ldio_threshold &&
@@ -4401,14 +4383,11 @@ int megasas_task_abort_fusion(struct scsi_cmnd *scmd)
{
struct megasas_instance *instance;
u16 smid, devhandle;
- struct fusion_context *fusion;
int ret;
struct MR_PRIV_DEVICE *mr_device_priv_data;
mr_device_priv_data = scmd->device->hostdata;
-
instance = (struct megasas_instance *)scmd->device->host->hostdata;
- fusion = instance->ctrl_context;
scmd_printk(KERN_INFO, scmd, "task abort called for scmd(%p)\n", scmd);
scsi_print_command(scmd);
@@ -4428,7 +4407,6 @@ int megasas_task_abort_fusion(struct scsi_cmnd *scmd)
goto out;
}
-
if (!mr_device_priv_data->is_tm_capable) {
ret = FAILED;
goto out;
@@ -4487,12 +4465,10 @@ int megasas_reset_target_fusion(struct scsi_cmnd *scmd)
struct megasas_instance *instance;
int ret = FAILED;
u16 devhandle;
- struct fusion_context *fusion;
struct MR_PRIV_DEVICE *mr_device_priv_data;
mr_device_priv_data = scmd->device->hostdata;
instance = (struct megasas_instance *)scmd->device->host->hostdata;
- fusion = instance->ctrl_context;
sdev_printk(KERN_INFO, scmd->device,
"target reset called for scmd(%p)\n", scmd);
@@ -4512,7 +4488,6 @@ int megasas_reset_target_fusion(struct scsi_cmnd *scmd)
goto out;
}
-
if (!mr_device_priv_data->is_tm_capable) {
ret = FAILED;
goto out;
diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.h b/drivers/scsi/megaraid/megaraid_sas_fusion.h
index ca73c50fe723..1481bf029490 100644
--- a/drivers/scsi/megaraid/megaraid_sas_fusion.h
+++ b/drivers/scsi/megaraid/megaraid_sas_fusion.h
@@ -724,6 +724,7 @@ struct MPI2_IOC_INIT_REQUEST {
#define MR_DCMD_LD_VF_MAP_GET_ALL_LDS_111 0x03200200
#define MR_DCMD_LD_VF_MAP_GET_ALL_LDS 0x03150200
#define MR_DCMD_CTRL_SNAPDUMP_GET_PROPERTIES 0x01200100
+#define MR_DCMD_CTRL_DEVICE_LIST_GET 0x01190600
struct MR_DEV_HANDLE_INFO {
__le16 curDevHdl;
diff --git a/drivers/scsi/mpt3sas/mpi/mpi2_cnfg.h b/drivers/scsi/mpt3sas/mpi/mpi2_cnfg.h
index 398fa6fde960..a2f4a55c51be 100644
--- a/drivers/scsi/mpt3sas/mpi/mpi2_cnfg.h
+++ b/drivers/scsi/mpt3sas/mpi/mpi2_cnfg.h
@@ -548,7 +548,8 @@ typedef struct _MPI2_CONFIG_REPLY {
#define MPI2_MFGPAGE_DEVID_SAS2308_1 (0x0086)
#define MPI2_MFGPAGE_DEVID_SAS2308_2 (0x0087)
#define MPI2_MFGPAGE_DEVID_SAS2308_3 (0x006E)
-#define MPI2_MFGPAGE_DEVID_SAS2308_MPI_EP (0x02B0)
+#define MPI2_MFGPAGE_DEVID_SWITCH_MPI_EP (0x02B0)
+#define MPI2_MFGPAGE_DEVID_SWITCH_MPI_EP_1 (0x02B1)
/*MPI v2.5 SAS products */
#define MPI25_MFGPAGE_DEVID_SAS3004 (0x0096)
diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.c b/drivers/scsi/mpt3sas/mpt3sas_base.c
index 0a6cb8f0680c..1d8c584ec1e9 100644
--- a/drivers/scsi/mpt3sas/mpt3sas_base.c
+++ b/drivers/scsi/mpt3sas/mpt3sas_base.c
@@ -3281,12 +3281,18 @@ mpt3sas_base_free_smid(struct MPT3SAS_ADAPTER *ioc, u16 smid)
if (smid < ioc->hi_priority_smid) {
struct scsiio_tracker *st;
+ void *request;
st = _get_st_from_smid(ioc, smid);
if (!st) {
_base_recovery_check(ioc);
return;
}
+
+ /* Clear MPI request frame */
+ request = mpt3sas_base_get_msg_frame(ioc, smid);
+ memset(request, 0, ioc->request_sz);
+
mpt3sas_base_clear_st(ioc, st);
_base_recovery_check(ioc);
return;
@@ -3563,6 +3569,7 @@ _base_display_OEMs_branding(struct MPT3SAS_ADAPTER *ioc)
ioc->pdev->subsystem_device);
break;
}
+ break;
case MPI2_MFGPAGE_DEVID_SAS2308_2:
switch (ioc->pdev->subsystem_device) {
case MPT2SAS_INTEL_RS25GB008_SSDID:
@@ -3598,6 +3605,7 @@ _base_display_OEMs_branding(struct MPT3SAS_ADAPTER *ioc)
ioc->pdev->subsystem_device);
break;
}
+ break;
case MPI25_MFGPAGE_DEVID_SAS3008:
switch (ioc->pdev->subsystem_device) {
case MPT3SAS_INTEL_RMS3JC080_SSDID:
@@ -3742,6 +3750,7 @@ _base_display_OEMs_branding(struct MPT3SAS_ADAPTER *ioc)
ioc->pdev->subsystem_device);
break;
}
+ break;
case MPI2_MFGPAGE_DEVID_SAS2308_2:
switch (ioc->pdev->subsystem_device) {
case MPT2SAS_HP_2_4_INTERNAL_SSDID:
@@ -3765,6 +3774,7 @@ _base_display_OEMs_branding(struct MPT3SAS_ADAPTER *ioc)
ioc->pdev->subsystem_device);
break;
}
+ break;
default:
ioc_info(ioc, "HP SAS HBA: Subsystem ID: 0x%X\n",
ioc->pdev->subsystem_device);
diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.h b/drivers/scsi/mpt3sas/mpt3sas_base.h
index 800351932cc3..19158cb59101 100644
--- a/drivers/scsi/mpt3sas/mpt3sas_base.h
+++ b/drivers/scsi/mpt3sas/mpt3sas_base.h
@@ -75,9 +75,9 @@
#define MPT3SAS_DRIVER_NAME "mpt3sas"
#define MPT3SAS_AUTHOR "Avago Technologies <MPT-FusionLinux.pdl@avagotech.com>"
#define MPT3SAS_DESCRIPTION "LSI MPT Fusion SAS 3.0 Device Driver"
-#define MPT3SAS_DRIVER_VERSION "27.101.00.00"
+#define MPT3SAS_DRIVER_VERSION "27.102.00.00"
#define MPT3SAS_MAJOR_VERSION 27
-#define MPT3SAS_MINOR_VERSION 101
+#define MPT3SAS_MINOR_VERSION 102
#define MPT3SAS_BUILD_VERSION 0
#define MPT3SAS_RELEASE_VERSION 00
@@ -193,6 +193,9 @@ struct mpt3sas_nvme_cmd {
#define SAS2_PCI_DEVICE_B0_REVISION (0x01)
#define SAS3_PCI_DEVICE_C0_REVISION (0x02)
+/* Atlas PCIe Switch Management Port */
+#define MPI26_ATLAS_PCIe_SWITCH_DEVID (0x00B2)
+
/*
* Intel HBA branding
*/
diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c
index 6be39dc27103..1ccfbc7eebe0 100644
--- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c
+++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c
@@ -1462,11 +1462,23 @@ mpt3sas_scsih_scsi_lookup_get(struct MPT3SAS_ADAPTER *ioc, u16 smid)
{
struct scsi_cmnd *scmd = NULL;
struct scsiio_tracker *st;
+ Mpi25SCSIIORequest_t *mpi_request;
if (smid > 0 &&
smid <= ioc->scsiio_depth - INTERNAL_SCSIIO_CMDS_COUNT) {
u32 unique_tag = smid - 1;
+ mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
+
+ /*
+ * If SCSI IO request is outstanding at driver level then
+ * DevHandle filed must be non-zero. If DevHandle is zero
+ * then it means that this smid is free at driver level,
+ * so return NULL.
+ */
+ if (!mpi_request->DevHandle)
+ return scmd;
+
scmd = scsi_host_find_tag(ioc->shost, unique_tag);
if (scmd) {
st = scsi_cmd_priv(scmd);
@@ -10256,7 +10268,8 @@ _scsih_determine_hba_mpi_version(struct pci_dev *pdev)
case MPI2_MFGPAGE_DEVID_SAS2308_1:
case MPI2_MFGPAGE_DEVID_SAS2308_2:
case MPI2_MFGPAGE_DEVID_SAS2308_3:
- case MPI2_MFGPAGE_DEVID_SAS2308_MPI_EP:
+ case MPI2_MFGPAGE_DEVID_SWITCH_MPI_EP:
+ case MPI2_MFGPAGE_DEVID_SWITCH_MPI_EP_1:
return MPI2_VERSION;
case MPI25_MFGPAGE_DEVID_SAS3004:
case MPI25_MFGPAGE_DEVID_SAS3008:
@@ -10282,6 +10295,7 @@ _scsih_determine_hba_mpi_version(struct pci_dev *pdev)
case MPI26_MFGPAGE_DEVID_SAS3516_1:
case MPI26_MFGPAGE_DEVID_SAS3416:
case MPI26_MFGPAGE_DEVID_SAS3616:
+ case MPI26_ATLAS_PCIe_SWITCH_DEVID:
case MPI26_MFGPAGE_DEVID_CFG_SEC_3916:
case MPI26_MFGPAGE_DEVID_HARD_SEC_3916:
case MPI26_MFGPAGE_DEVID_CFG_SEC_3816:
@@ -10343,7 +10357,8 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id)
ioc->is_warpdrive = 1;
ioc->hide_ir_msg = 1;
break;
- case MPI2_MFGPAGE_DEVID_SAS2308_MPI_EP:
+ case MPI2_MFGPAGE_DEVID_SWITCH_MPI_EP:
+ case MPI2_MFGPAGE_DEVID_SWITCH_MPI_EP_1:
ioc->is_mcpu_endpoint = 1;
break;
default:
@@ -10371,6 +10386,7 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id)
case MPI26_MFGPAGE_DEVID_SAS3516_1:
case MPI26_MFGPAGE_DEVID_SAS3416:
case MPI26_MFGPAGE_DEVID_SAS3616:
+ case MPI26_ATLAS_PCIe_SWITCH_DEVID:
ioc->is_gen35_ioc = 1;
break;
case MPI26_MFGPAGE_DEVID_CFG_SEC_3816:
@@ -10783,7 +10799,9 @@ static const struct pci_device_id mpt3sas_pci_table[] = {
PCI_ANY_ID, PCI_ANY_ID },
{ MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2308_3,
PCI_ANY_ID, PCI_ANY_ID },
- { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2308_MPI_EP,
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SWITCH_MPI_EP,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SWITCH_MPI_EP_1,
PCI_ANY_ID, PCI_ANY_ID },
/* SSS6200 */
{ MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SSS6200,
@@ -10849,6 +10867,10 @@ static const struct pci_device_id mpt3sas_pci_table[] = {
{ MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_HARD_SEC_3916,
PCI_ANY_ID, PCI_ANY_ID },
+ /* Atlas PCIe Switch Management Port */
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI26_ATLAS_PCIe_SWITCH_DEVID,
+ PCI_ANY_ID, PCI_ANY_ID },
+
/* Sea SI 0x00E5 Configurable Secure
* 0x00E6 Hard Secure
*/
diff --git a/drivers/scsi/mvumi.c b/drivers/scsi/mvumi.c
index 36f64205ecfa..3df02691a092 100644
--- a/drivers/scsi/mvumi.c
+++ b/drivers/scsi/mvumi.c
@@ -718,8 +718,8 @@ static int mvumi_host_reset(struct scsi_cmnd *scmd)
mhba = (struct mvumi_hba *) scmd->device->host->hostdata;
- scmd_printk(KERN_NOTICE, scmd, "RESET -%ld cmd=%x retries=%x\n",
- scmd->serial_number, scmd->cmnd[0], scmd->retries);
+ scmd_printk(KERN_NOTICE, scmd, "RESET -%u cmd=%x retries=%x\n",
+ scmd->request->tag, scmd->cmnd[0], scmd->retries);
return mhba->instancet->reset_host(mhba);
}
@@ -2104,7 +2104,6 @@ static int mvumi_queue_command(struct Scsi_Host *shost,
unsigned long irq_flags;
spin_lock_irqsave(shost->host_lock, irq_flags);
- scsi_cmd_get_serial(shost, scmd);
mhba = (struct mvumi_hba *) shost->hostdata;
scmd->result = 0;
diff --git a/drivers/scsi/nsp32.c b/drivers/scsi/nsp32.c
index 00e3cbee55b8..da4d6e1106c4 100644
--- a/drivers/scsi/nsp32.c
+++ b/drivers/scsi/nsp32.c
@@ -2441,7 +2441,6 @@ static void nsp32_set_sync_entry(nsp32_hw_data *data,
period = data->synct[entry].period_num;
ackwidth = data->synct[entry].ackwidth;
- offset = offset;
sample_rate = data->synct[entry].sample_rate;
target->syncreg = TO_SYNCREG(period, offset);
diff --git a/drivers/scsi/osd/Kbuild b/drivers/scsi/osd/Kbuild
deleted file mode 100644
index 58cecd45b0f5..000000000000
--- a/drivers/scsi/osd/Kbuild
+++ /dev/null
@@ -1,20 +0,0 @@
-#
-# Kbuild for the OSD modules
-#
-# Copyright (C) 2008 Panasas Inc. All rights reserved.
-#
-# Authors:
-# Boaz Harrosh <ooo@electrozaur.com>
-# Benny Halevy <bhalevy@panasas.com>
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2
-#
-
-# libosd.ko - osd-initiator library
-libosd-y := osd_initiator.o
-obj-$(CONFIG_SCSI_OSD_INITIATOR) += libosd.o
-
-# osd.ko - SCSI ULD and char-device
-osd-y := osd_uld.o
-obj-$(CONFIG_SCSI_OSD_ULD) += osd.o
diff --git a/drivers/scsi/osd/Kconfig b/drivers/scsi/osd/Kconfig
deleted file mode 100644
index 347cc5e33749..000000000000
--- a/drivers/scsi/osd/Kconfig
+++ /dev/null
@@ -1,49 +0,0 @@
-#
-# Kernel configuration file for the OSD scsi protocol
-#
-# Copyright (C) 2008 Panasas Inc. All rights reserved.
-#
-# Authors:
-# Boaz Harrosh <ooo@electrozaur.com>
-# Benny Halevy <bhalevy@panasas.com>
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public version 2 License as
-# published by the Free Software Foundation
-#
-config SCSI_OSD_INITIATOR
- tristate "OSD-Initiator library"
- depends on SCSI
- help
- Enable the OSD-Initiator library (libosd.ko).
- NOTE: You must also select CRYPTO_SHA1 + CRYPTO_HMAC and their
- dependencies
-
-config SCSI_OSD_ULD
- tristate "OSD Upper Level driver"
- depends on SCSI_OSD_INITIATOR
- help
- Build a SCSI upper layer driver that exports /dev/osdX devices
- to user-mode for testing and controlling OSD devices. It is also
- needed by exofs, for mounting an OSD based file system.
-
-config SCSI_OSD_DPRINT_SENSE
- int "(0-2) When sense is returned, DEBUG print all sense descriptors"
- default 1
- depends on SCSI_OSD_INITIATOR
- help
- When a CHECK_CONDITION status is returned from a target, and a
- sense-buffer is retrieved, turning this on will dump a full
- sense-decoding message. Setting to 2 will also print recoverable
- errors that might be regularly returned for some filesystem
- operations.
-
-config SCSI_OSD_DEBUG
- bool "Compile All OSD modules with lots of DEBUG prints"
- default n
- depends on SCSI_OSD_INITIATOR
- help
- OSD Code is populated with lots of OSD_DEBUG(..) printouts to
- dmesg. Enable this if you found a bug and you want to help us
- track the problem (see also MAINTAINERS). Setting this will also
- force SCSI_OSD_DPRINT_SENSE=2.
diff --git a/drivers/scsi/osd/osd_debug.h b/drivers/scsi/osd/osd_debug.h
deleted file mode 100644
index 26341261bb5c..000000000000
--- a/drivers/scsi/osd/osd_debug.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * osd_debug.h - Some kprintf macros
- *
- * Copyright (C) 2008 Panasas Inc. All rights reserved.
- *
- * Authors:
- * Boaz Harrosh <ooo@electrozaur.com>
- * Benny Halevy <bhalevy@panasas.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2
- *
- */
-#ifndef __OSD_DEBUG_H__
-#define __OSD_DEBUG_H__
-
-#define OSD_ERR(fmt, a...) printk(KERN_ERR "osd: " fmt, ##a)
-#define OSD_INFO(fmt, a...) printk(KERN_NOTICE "osd: " fmt, ##a)
-
-#ifdef CONFIG_SCSI_OSD_DEBUG
-#define OSD_DEBUG(fmt, a...) \
- printk(KERN_NOTICE "osd @%s:%d: " fmt, __func__, __LINE__, ##a)
-#else
-#define OSD_DEBUG(fmt, a...) do {} while (0)
-#endif
-
-/* u64 has problems with printk this will cast it to unsigned long long */
-#define _LLU(x) (unsigned long long)(x)
-
-#endif /* ndef __OSD_DEBUG_H__ */
diff --git a/drivers/scsi/osd/osd_initiator.c b/drivers/scsi/osd/osd_initiator.c
deleted file mode 100644
index 60cf7c5eb880..000000000000
--- a/drivers/scsi/osd/osd_initiator.c
+++ /dev/null
@@ -1,2076 +0,0 @@
-/*
- * osd_initiator - Main body of the osd initiator library.
- *
- * Note: The file does not contain the advanced security functionality which
- * is only needed by the security_manager's initiators.
- *
- * Copyright (C) 2008 Panasas Inc. All rights reserved.
- *
- * Authors:
- * Boaz Harrosh <ooo@electrozaur.com>
- * Benny Halevy <bhalevy@panasas.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2
- *
- * 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.
- * 3. Neither the name of the Panasas company nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED ``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 REGENTS 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.
- */
-
-#include <linux/slab.h>
-#include <linux/module.h>
-
-#include <scsi/osd_initiator.h>
-#include <scsi/osd_sec.h>
-#include <scsi/osd_attributes.h>
-#include <scsi/osd_sense.h>
-
-#include <scsi/scsi_device.h>
-#include <scsi/scsi_request.h>
-
-#include "osd_debug.h"
-
-#ifndef __unused
-# define __unused __attribute__((unused))
-#endif
-
-enum { OSD_REQ_RETRIES = 1 };
-
-MODULE_AUTHOR("Boaz Harrosh <ooo@electrozaur.com>");
-MODULE_DESCRIPTION("open-osd initiator library libosd.ko");
-MODULE_LICENSE("GPL");
-
-static inline void build_test(void)
-{
- /* structures were not packed */
- BUILD_BUG_ON(sizeof(struct osd_capability) != OSD_CAP_LEN);
- BUILD_BUG_ON(sizeof(struct osdv2_cdb) != OSD_TOTAL_CDB_LEN);
- BUILD_BUG_ON(sizeof(struct osdv1_cdb) != OSDv1_TOTAL_CDB_LEN);
-}
-
-static const char *_osd_ver_desc(struct osd_request *or)
-{
- return osd_req_is_ver1(or) ? "OSD1" : "OSD2";
-}
-
-#define ATTR_DEF_RI(id, len) ATTR_DEF(OSD_APAGE_ROOT_INFORMATION, id, len)
-
-static int _osd_get_print_system_info(struct osd_dev *od,
- void *caps, struct osd_dev_info *odi)
-{
- struct osd_request *or;
- struct osd_attr get_attrs[] = {
- ATTR_DEF_RI(OSD_ATTR_RI_VENDOR_IDENTIFICATION, 8),
- ATTR_DEF_RI(OSD_ATTR_RI_PRODUCT_IDENTIFICATION, 16),
- ATTR_DEF_RI(OSD_ATTR_RI_PRODUCT_MODEL, 32),
- ATTR_DEF_RI(OSD_ATTR_RI_PRODUCT_REVISION_LEVEL, 4),
- ATTR_DEF_RI(OSD_ATTR_RI_PRODUCT_SERIAL_NUMBER, 64 /*variable*/),
- ATTR_DEF_RI(OSD_ATTR_RI_OSD_NAME, 64 /*variable*/),
- ATTR_DEF_RI(OSD_ATTR_RI_TOTAL_CAPACITY, 8),
- ATTR_DEF_RI(OSD_ATTR_RI_USED_CAPACITY, 8),
- ATTR_DEF_RI(OSD_ATTR_RI_NUMBER_OF_PARTITIONS, 8),
- ATTR_DEF_RI(OSD_ATTR_RI_CLOCK, 6),
- /* IBM-OSD-SIM Has a bug with this one put it last */
- ATTR_DEF_RI(OSD_ATTR_RI_OSD_SYSTEM_ID, 20),
- };
- void *iter = NULL, *pFirst;
- int nelem = ARRAY_SIZE(get_attrs), a = 0;
- int ret;
-
- or = osd_start_request(od);
- if (!or)
- return -ENOMEM;
-
- /* get attrs */
- osd_req_get_attributes(or, &osd_root_object);
- osd_req_add_get_attr_list(or, get_attrs, ARRAY_SIZE(get_attrs));
-
- ret = osd_finalize_request(or, 0, caps, NULL);
- if (ret)
- goto out;
-
- ret = osd_execute_request(or);
- if (ret) {
- OSD_ERR("Failed to detect %s => %d\n", _osd_ver_desc(or), ret);
- goto out;
- }
-
- osd_req_decode_get_attr_list(or, get_attrs, &nelem, &iter);
-
- OSD_INFO("Detected %s device\n",
- _osd_ver_desc(or));
-
- pFirst = get_attrs[a++].val_ptr;
- OSD_INFO("VENDOR_IDENTIFICATION [%s]\n",
- (char *)pFirst);
-
- pFirst = get_attrs[a++].val_ptr;
- OSD_INFO("PRODUCT_IDENTIFICATION [%s]\n",
- (char *)pFirst);
-
- pFirst = get_attrs[a++].val_ptr;
- OSD_INFO("PRODUCT_MODEL [%s]\n",
- (char *)pFirst);
-
- pFirst = get_attrs[a++].val_ptr;
- OSD_INFO("PRODUCT_REVISION_LEVEL [%u]\n",
- pFirst ? get_unaligned_be32(pFirst) : ~0U);
-
- pFirst = get_attrs[a++].val_ptr;
- OSD_INFO("PRODUCT_SERIAL_NUMBER [%s]\n",
- (char *)pFirst);
-
- odi->osdname_len = get_attrs[a].len;
- /* Avoid NULL for memcmp optimization 0-length is good enough */
- odi->osdname = kzalloc(odi->osdname_len + 1, GFP_KERNEL);
- if (!odi->osdname) {
- ret = -ENOMEM;
- goto out;
- }
- if (odi->osdname_len)
- memcpy(odi->osdname, get_attrs[a].val_ptr, odi->osdname_len);
- OSD_INFO("OSD_NAME [%s]\n", odi->osdname);
- a++;
-
- pFirst = get_attrs[a++].val_ptr;
- OSD_INFO("TOTAL_CAPACITY [0x%llx]\n",
- pFirst ? _LLU(get_unaligned_be64(pFirst)) : ~0ULL);
-
- pFirst = get_attrs[a++].val_ptr;
- OSD_INFO("USED_CAPACITY [0x%llx]\n",
- pFirst ? _LLU(get_unaligned_be64(pFirst)) : ~0ULL);
-
- pFirst = get_attrs[a++].val_ptr;
- OSD_INFO("NUMBER_OF_PARTITIONS [%llu]\n",
- pFirst ? _LLU(get_unaligned_be64(pFirst)) : ~0ULL);
-
- if (a >= nelem)
- goto out;
-
- /* FIXME: Where are the time utilities */
- pFirst = get_attrs[a++].val_ptr;
- OSD_INFO("CLOCK [0x%6phN]\n", pFirst);
-
- if (a < nelem) { /* IBM-OSD-SIM bug, Might not have it */
- unsigned len = get_attrs[a].len;
- char sid_dump[32*4 + 2]; /* 2nibbles+space+ASCII */
-
- hex_dump_to_buffer(get_attrs[a].val_ptr, len, 32, 1,
- sid_dump, sizeof(sid_dump), true);
- OSD_INFO("OSD_SYSTEM_ID(%d)\n"
- " [%s]\n", len, sid_dump);
-
- if (unlikely(len > sizeof(odi->systemid))) {
- OSD_ERR("OSD Target error: OSD_SYSTEM_ID too long(%d). "
- "device identification might not work\n", len);
- len = sizeof(odi->systemid);
- }
- odi->systemid_len = len;
- memcpy(odi->systemid, get_attrs[a].val_ptr, len);
- a++;
- }
-out:
- osd_end_request(or);
- return ret;
-}
-
-int osd_auto_detect_ver(struct osd_dev *od,
- void *caps, struct osd_dev_info *odi)
-{
- int ret;
-
- /* Auto-detect the osd version */
- ret = _osd_get_print_system_info(od, caps, odi);
- if (ret) {
- osd_dev_set_ver(od, OSD_VER1);
- OSD_DEBUG("converting to OSD1\n");
- ret = _osd_get_print_system_info(od, caps, odi);
- }
-
- return ret;
-}
-EXPORT_SYMBOL(osd_auto_detect_ver);
-
-static unsigned _osd_req_cdb_len(struct osd_request *or)
-{
- return osd_req_is_ver1(or) ? OSDv1_TOTAL_CDB_LEN : OSD_TOTAL_CDB_LEN;
-}
-
-static unsigned _osd_req_alist_elem_size(struct osd_request *or, unsigned len)
-{
- return osd_req_is_ver1(or) ?
- osdv1_attr_list_elem_size(len) :
- osdv2_attr_list_elem_size(len);
-}
-
-static void _osd_req_alist_elem_encode(struct osd_request *or,
- void *attr_last, const struct osd_attr *oa)
-{
- if (osd_req_is_ver1(or)) {
- struct osdv1_attributes_list_element *attr = attr_last;
-
- attr->attr_page = cpu_to_be32(oa->attr_page);
- attr->attr_id = cpu_to_be32(oa->attr_id);
- attr->attr_bytes = cpu_to_be16(oa->len);
- memcpy(attr->attr_val, oa->val_ptr, oa->len);
- } else {
- struct osdv2_attributes_list_element *attr = attr_last;
-
- attr->attr_page = cpu_to_be32(oa->attr_page);
- attr->attr_id = cpu_to_be32(oa->attr_id);
- attr->attr_bytes = cpu_to_be16(oa->len);
- memcpy(attr->attr_val, oa->val_ptr, oa->len);
- }
-}
-
-static int _osd_req_alist_elem_decode(struct osd_request *or,
- void *cur_p, struct osd_attr *oa, unsigned max_bytes)
-{
- unsigned inc;
- if (osd_req_is_ver1(or)) {
- struct osdv1_attributes_list_element *attr = cur_p;
-
- if (max_bytes < sizeof(*attr))
- return -1;
-
- oa->len = be16_to_cpu(attr->attr_bytes);
- inc = _osd_req_alist_elem_size(or, oa->len);
- if (inc > max_bytes)
- return -1;
-
- oa->attr_page = be32_to_cpu(attr->attr_page);
- oa->attr_id = be32_to_cpu(attr->attr_id);
-
- /* OSD1: On empty attributes we return a pointer to 2 bytes
- * of zeros. This keeps similar behaviour with OSD2.
- * (See below)
- */
- oa->val_ptr = likely(oa->len) ? attr->attr_val :
- (u8 *)&attr->attr_bytes;
- } else {
- struct osdv2_attributes_list_element *attr = cur_p;
-
- if (max_bytes < sizeof(*attr))
- return -1;
-
- oa->len = be16_to_cpu(attr->attr_bytes);
- inc = _osd_req_alist_elem_size(or, oa->len);
- if (inc > max_bytes)
- return -1;
-
- oa->attr_page = be32_to_cpu(attr->attr_page);
- oa->attr_id = be32_to_cpu(attr->attr_id);
-
- /* OSD2: For convenience, on empty attributes, we return 8 bytes
- * of zeros here. This keeps the same behaviour with OSD2r04,
- * and is nice with null terminating ASCII fields.
- * oa->val_ptr == NULL marks the end-of-list, or error.
- */
- oa->val_ptr = likely(oa->len) ? attr->attr_val : attr->reserved;
- }
- return inc;
-}
-
-static unsigned _osd_req_alist_size(struct osd_request *or, void *list_head)
-{
- return osd_req_is_ver1(or) ?
- osdv1_list_size(list_head) :
- osdv2_list_size(list_head);
-}
-
-static unsigned _osd_req_sizeof_alist_header(struct osd_request *or)
-{
- return osd_req_is_ver1(or) ?
- sizeof(struct osdv1_attributes_list_header) :
- sizeof(struct osdv2_attributes_list_header);
-}
-
-static void _osd_req_set_alist_type(struct osd_request *or,
- void *list, int list_type)
-{
- if (osd_req_is_ver1(or)) {
- struct osdv1_attributes_list_header *attr_list = list;
-
- memset(attr_list, 0, sizeof(*attr_list));
- attr_list->type = list_type;
- } else {
- struct osdv2_attributes_list_header *attr_list = list;
-
- memset(attr_list, 0, sizeof(*attr_list));
- attr_list->type = list_type;
- }
-}
-
-static bool _osd_req_is_alist_type(struct osd_request *or,
- void *list, int list_type)
-{
- if (!list)
- return false;
-
- if (osd_req_is_ver1(or)) {
- struct osdv1_attributes_list_header *attr_list = list;
-
- return attr_list->type == list_type;
- } else {
- struct osdv2_attributes_list_header *attr_list = list;
-
- return attr_list->type == list_type;
- }
-}
-
-/* This is for List-objects not Attributes-Lists */
-static void _osd_req_encode_olist(struct osd_request *or,
- struct osd_obj_id_list *list)
-{
- struct osd_cdb_head *cdbh = osd_cdb_head(&or->cdb);
-
- if (osd_req_is_ver1(or)) {
- cdbh->v1.list_identifier = list->list_identifier;
- cdbh->v1.start_address = list->continuation_id;
- } else {
- cdbh->v2.list_identifier = list->list_identifier;
- cdbh->v2.start_address = list->continuation_id;
- }
-}
-
-static osd_cdb_offset osd_req_encode_offset(struct osd_request *or,
- u64 offset, unsigned *padding)
-{
- return __osd_encode_offset(offset, padding,
- osd_req_is_ver1(or) ?
- OSDv1_OFFSET_MIN_SHIFT : OSD_OFFSET_MIN_SHIFT,
- OSD_OFFSET_MAX_SHIFT);
-}
-
-static struct osd_security_parameters *
-_osd_req_sec_params(struct osd_request *or)
-{
- struct osd_cdb *ocdb = &or->cdb;
-
- if (osd_req_is_ver1(or))
- return (struct osd_security_parameters *)&ocdb->v1.sec_params;
- else
- return (struct osd_security_parameters *)&ocdb->v2.sec_params;
-}
-
-void osd_dev_init(struct osd_dev *osdd, struct scsi_device *scsi_device)
-{
- memset(osdd, 0, sizeof(*osdd));
- osdd->scsi_device = scsi_device;
- osdd->def_timeout = BLK_DEFAULT_SG_TIMEOUT;
-#ifdef OSD_VER1_SUPPORT
- osdd->version = OSD_VER2;
-#endif
- /* TODO: Allocate pools for osd_request attributes ... */
-}
-EXPORT_SYMBOL(osd_dev_init);
-
-void osd_dev_fini(struct osd_dev *osdd)
-{
- /* TODO: De-allocate pools */
-
- osdd->scsi_device = NULL;
-}
-EXPORT_SYMBOL(osd_dev_fini);
-
-static struct osd_request *_osd_request_alloc(gfp_t gfp)
-{
- struct osd_request *or;
-
- /* TODO: Use mempool with one saved request */
- or = kzalloc(sizeof(*or), gfp);
- return or;
-}
-
-static void _osd_request_free(struct osd_request *or)
-{
- kfree(or);
-}
-
-struct osd_request *osd_start_request(struct osd_dev *dev)
-{
- struct osd_request *or;
-
- or = _osd_request_alloc(GFP_KERNEL);
- if (!or)
- return NULL;
-
- or->osd_dev = dev;
- or->timeout = dev->def_timeout;
- or->retries = OSD_REQ_RETRIES;
-
- return or;
-}
-EXPORT_SYMBOL(osd_start_request);
-
-static void _osd_free_seg(struct osd_request *or __unused,
- struct _osd_req_data_segment *seg)
-{
- if (!seg->buff || !seg->alloc_size)
- return;
-
- kfree(seg->buff);
- seg->buff = NULL;
- seg->alloc_size = 0;
-}
-
-static void _put_request(struct request *rq)
-{
- /*
- * If osd_finalize_request() was called but the request was not
- * executed through the block layer, then we must release BIOs.
- * TODO: Keep error code in or->async_error. Need to audit all
- * code paths.
- */
- if (unlikely(rq->bio))
- blk_mq_end_request(rq, BLK_STS_IOERR);
- else
- blk_put_request(rq);
-}
-
-void osd_end_request(struct osd_request *or)
-{
- struct request *rq = or->request;
-
- if (rq) {
- if (rq->next_rq) {
- _put_request(rq->next_rq);
- rq->next_rq = NULL;
- }
-
- _put_request(rq);
- }
-
- _osd_free_seg(or, &or->get_attr);
- _osd_free_seg(or, &or->enc_get_attr);
- _osd_free_seg(or, &or->set_attr);
- _osd_free_seg(or, &or->cdb_cont);
-
- _osd_request_free(or);
-}
-EXPORT_SYMBOL(osd_end_request);
-
-static void _set_error_resid(struct osd_request *or, struct request *req,
- blk_status_t error)
-{
- or->async_error = error;
- or->req_errors = scsi_req(req)->result;
- or->sense_len = scsi_req(req)->sense_len;
- if (or->sense_len)
- memcpy(or->sense, scsi_req(req)->sense, or->sense_len);
- if (or->out.req)
- or->out.residual = scsi_req(or->out.req)->resid_len;
- if (or->in.req)
- or->in.residual = scsi_req(or->in.req)->resid_len;
-}
-
-int osd_execute_request(struct osd_request *or)
-{
- blk_execute_rq(or->request->q, NULL, or->request, 0);
-
- if (scsi_req(or->request)->result) {
- _set_error_resid(or, or->request, BLK_STS_IOERR);
- return -EIO;
- }
-
- _set_error_resid(or, or->request, BLK_STS_OK);
- return 0;
-}
-EXPORT_SYMBOL(osd_execute_request);
-
-static void osd_request_async_done(struct request *req, blk_status_t error)
-{
- struct osd_request *or = req->end_io_data;
-
- _set_error_resid(or, req, error);
- if (req->next_rq) {
- blk_put_request(req->next_rq);
- req->next_rq = NULL;
- }
-
- blk_put_request(req);
- or->request = NULL;
- or->in.req = NULL;
- or->out.req = NULL;
-
- if (or->async_done)
- or->async_done(or, or->async_private);
- else
- osd_end_request(or);
-}
-
-int osd_execute_request_async(struct osd_request *or,
- osd_req_done_fn *done, void *private)
-{
- or->request->end_io_data = or;
- or->async_private = private;
- or->async_done = done;
-
- blk_execute_rq_nowait(or->request->q, NULL, or->request, 0,
- osd_request_async_done);
- return 0;
-}
-EXPORT_SYMBOL(osd_execute_request_async);
-
-u8 sg_out_pad_buffer[1 << OSDv1_OFFSET_MIN_SHIFT];
-u8 sg_in_pad_buffer[1 << OSDv1_OFFSET_MIN_SHIFT];
-
-static int _osd_realloc_seg(struct osd_request *or,
- struct _osd_req_data_segment *seg, unsigned max_bytes)
-{
- void *buff;
-
- if (seg->alloc_size >= max_bytes)
- return 0;
-
- buff = krealloc(seg->buff, max_bytes, GFP_KERNEL);
- if (!buff) {
- OSD_ERR("Failed to Realloc %d-bytes was-%d\n", max_bytes,
- seg->alloc_size);
- return -ENOMEM;
- }
-
- memset(buff + seg->alloc_size, 0, max_bytes - seg->alloc_size);
- seg->buff = buff;
- seg->alloc_size = max_bytes;
- return 0;
-}
-
-static int _alloc_cdb_cont(struct osd_request *or, unsigned total_bytes)
-{
- OSD_DEBUG("total_bytes=%d\n", total_bytes);
- return _osd_realloc_seg(or, &or->cdb_cont, total_bytes);
-}
-
-static int _alloc_set_attr_list(struct osd_request *or,
- const struct osd_attr *oa, unsigned nelem, unsigned add_bytes)
-{
- unsigned total_bytes = add_bytes;
-
- for (; nelem; --nelem, ++oa)
- total_bytes += _osd_req_alist_elem_size(or, oa->len);
-
- OSD_DEBUG("total_bytes=%d\n", total_bytes);
- return _osd_realloc_seg(or, &or->set_attr, total_bytes);
-}
-
-static int _alloc_get_attr_desc(struct osd_request *or, unsigned max_bytes)
-{
- OSD_DEBUG("total_bytes=%d\n", max_bytes);
- return _osd_realloc_seg(or, &or->enc_get_attr, max_bytes);
-}
-
-static int _alloc_get_attr_list(struct osd_request *or)
-{
- OSD_DEBUG("total_bytes=%d\n", or->get_attr.total_bytes);
- return _osd_realloc_seg(or, &or->get_attr, or->get_attr.total_bytes);
-}
-
-/*
- * Common to all OSD commands
- */
-
-static void _osdv1_req_encode_common(struct osd_request *or,
- __be16 act, const struct osd_obj_id *obj, u64 offset, u64 len)
-{
- struct osdv1_cdb *ocdb = &or->cdb.v1;
-
- /*
- * For speed, the commands
- * OSD_ACT_PERFORM_SCSI_COMMAND , V1 0x8F7E, V2 0x8F7C
- * OSD_ACT_SCSI_TASK_MANAGEMENT , V1 0x8F7F, V2 0x8F7D
- * are not supported here. Should pass zero and set after the call
- */
- act &= cpu_to_be16(~0x0080); /* V1 action code */
-
- OSD_DEBUG("OSDv1 execute opcode 0x%x\n", be16_to_cpu(act));
-
- ocdb->h.varlen_cdb.opcode = VARIABLE_LENGTH_CMD;
- ocdb->h.varlen_cdb.additional_cdb_length = OSD_ADDITIONAL_CDB_LENGTH;
- ocdb->h.varlen_cdb.service_action = act;
-
- ocdb->h.partition = cpu_to_be64(obj->partition);
- ocdb->h.object = cpu_to_be64(obj->id);
- ocdb->h.v1.length = cpu_to_be64(len);
- ocdb->h.v1.start_address = cpu_to_be64(offset);
-}
-
-static void _osdv2_req_encode_common(struct osd_request *or,
- __be16 act, const struct osd_obj_id *obj, u64 offset, u64 len)
-{
- struct osdv2_cdb *ocdb = &or->cdb.v2;
-
- OSD_DEBUG("OSDv2 execute opcode 0x%x\n", be16_to_cpu(act));
-
- ocdb->h.varlen_cdb.opcode = VARIABLE_LENGTH_CMD;
- ocdb->h.varlen_cdb.additional_cdb_length = OSD_ADDITIONAL_CDB_LENGTH;
- ocdb->h.varlen_cdb.service_action = act;
-
- ocdb->h.partition = cpu_to_be64(obj->partition);
- ocdb->h.object = cpu_to_be64(obj->id);
- ocdb->h.v2.length = cpu_to_be64(len);
- ocdb->h.v2.start_address = cpu_to_be64(offset);
-}
-
-static void _osd_req_encode_common(struct osd_request *or,
- __be16 act, const struct osd_obj_id *obj, u64 offset, u64 len)
-{
- if (osd_req_is_ver1(or))
- _osdv1_req_encode_common(or, act, obj, offset, len);
- else
- _osdv2_req_encode_common(or, act, obj, offset, len);
-}
-
-/*
- * Device commands
- */
-/*TODO: void osd_req_set_master_seed_xchg(struct osd_request *, ...); */
-/*TODO: void osd_req_set_master_key(struct osd_request *, ...); */
-
-void osd_req_format(struct osd_request *or, u64 tot_capacity)
-{
- _osd_req_encode_common(or, OSD_ACT_FORMAT_OSD, &osd_root_object, 0,
- tot_capacity);
-}
-EXPORT_SYMBOL(osd_req_format);
-
-int osd_req_list_dev_partitions(struct osd_request *or,
- osd_id initial_id, struct osd_obj_id_list *list, unsigned nelem)
-{
- return osd_req_list_partition_objects(or, 0, initial_id, list, nelem);
-}
-EXPORT_SYMBOL(osd_req_list_dev_partitions);
-
-static void _osd_req_encode_flush(struct osd_request *or,
- enum osd_options_flush_scope_values op)
-{
- struct osd_cdb_head *ocdb = osd_cdb_head(&or->cdb);
-
- ocdb->command_specific_options = op;
-}
-
-void osd_req_flush_obsd(struct osd_request *or,
- enum osd_options_flush_scope_values op)
-{
- _osd_req_encode_common(or, OSD_ACT_FLUSH_OSD, &osd_root_object, 0, 0);
- _osd_req_encode_flush(or, op);
-}
-EXPORT_SYMBOL(osd_req_flush_obsd);
-
-/*TODO: void osd_req_perform_scsi_command(struct osd_request *,
- const u8 *cdb, ...); */
-/*TODO: void osd_req_task_management(struct osd_request *, ...); */
-
-/*
- * Partition commands
- */
-static void _osd_req_encode_partition(struct osd_request *or,
- __be16 act, osd_id partition)
-{
- struct osd_obj_id par = {
- .partition = partition,
- .id = 0,
- };
-
- _osd_req_encode_common(or, act, &par, 0, 0);
-}
-
-void osd_req_create_partition(struct osd_request *or, osd_id partition)
-{
- _osd_req_encode_partition(or, OSD_ACT_CREATE_PARTITION, partition);
-}
-EXPORT_SYMBOL(osd_req_create_partition);
-
-void osd_req_remove_partition(struct osd_request *or, osd_id partition)
-{
- _osd_req_encode_partition(or, OSD_ACT_REMOVE_PARTITION, partition);
-}
-EXPORT_SYMBOL(osd_req_remove_partition);
-
-/*TODO: void osd_req_set_partition_key(struct osd_request *,
- osd_id partition, u8 new_key_id[OSD_CRYPTO_KEYID_SIZE],
- u8 seed[OSD_CRYPTO_SEED_SIZE]); */
-
-static int _osd_req_list_objects(struct osd_request *or,
- __be16 action, const struct osd_obj_id *obj, osd_id initial_id,
- struct osd_obj_id_list *list, unsigned nelem)
-{
- struct request_queue *q = osd_request_queue(or->osd_dev);
- u64 len = nelem * sizeof(osd_id) + sizeof(*list);
- struct bio *bio;
-
- _osd_req_encode_common(or, action, obj, (u64)initial_id, len);
-
- if (list->list_identifier)
- _osd_req_encode_olist(or, list);
-
- WARN_ON(or->in.bio);
- bio = bio_map_kern(q, list, len, GFP_KERNEL);
- if (IS_ERR(bio)) {
- OSD_ERR("!!! Failed to allocate list_objects BIO\n");
- return PTR_ERR(bio);
- }
-
- bio_set_op_attrs(bio, REQ_OP_READ, 0);
- or->in.bio = bio;
- or->in.total_bytes = bio->bi_iter.bi_size;
- return 0;
-}
-
-int osd_req_list_partition_collections(struct osd_request *or,
- osd_id partition, osd_id initial_id, struct osd_obj_id_list *list,
- unsigned nelem)
-{
- struct osd_obj_id par = {
- .partition = partition,
- .id = 0,
- };
-
- return osd_req_list_collection_objects(or, &par, initial_id, list,
- nelem);
-}
-EXPORT_SYMBOL(osd_req_list_partition_collections);
-
-int osd_req_list_partition_objects(struct osd_request *or,
- osd_id partition, osd_id initial_id, struct osd_obj_id_list *list,
- unsigned nelem)
-{
- struct osd_obj_id par = {
- .partition = partition,
- .id = 0,
- };
-
- return _osd_req_list_objects(or, OSD_ACT_LIST, &par, initial_id, list,
- nelem);
-}
-EXPORT_SYMBOL(osd_req_list_partition_objects);
-
-void osd_req_flush_partition(struct osd_request *or,
- osd_id partition, enum osd_options_flush_scope_values op)
-{
- _osd_req_encode_partition(or, OSD_ACT_FLUSH_PARTITION, partition);
- _osd_req_encode_flush(or, op);
-}
-EXPORT_SYMBOL(osd_req_flush_partition);
-
-/*
- * Collection commands
- */
-/*TODO: void osd_req_create_collection(struct osd_request *,
- const struct osd_obj_id *); */
-/*TODO: void osd_req_remove_collection(struct osd_request *,
- const struct osd_obj_id *); */
-
-int osd_req_list_collection_objects(struct osd_request *or,
- const struct osd_obj_id *obj, osd_id initial_id,
- struct osd_obj_id_list *list, unsigned nelem)
-{
- return _osd_req_list_objects(or, OSD_ACT_LIST_COLLECTION, obj,
- initial_id, list, nelem);
-}
-EXPORT_SYMBOL(osd_req_list_collection_objects);
-
-/*TODO: void query(struct osd_request *, ...); V2 */
-
-void osd_req_flush_collection(struct osd_request *or,
- const struct osd_obj_id *obj, enum osd_options_flush_scope_values op)
-{
- _osd_req_encode_common(or, OSD_ACT_FLUSH_PARTITION, obj, 0, 0);
- _osd_req_encode_flush(or, op);
-}
-EXPORT_SYMBOL(osd_req_flush_collection);
-
-/*TODO: void get_member_attrs(struct osd_request *, ...); V2 */
-/*TODO: void set_member_attrs(struct osd_request *, ...); V2 */
-
-/*
- * Object commands
- */
-void osd_req_create_object(struct osd_request *or, struct osd_obj_id *obj)
-{
- _osd_req_encode_common(or, OSD_ACT_CREATE, obj, 0, 0);
-}
-EXPORT_SYMBOL(osd_req_create_object);
-
-void osd_req_remove_object(struct osd_request *or, struct osd_obj_id *obj)
-{
- _osd_req_encode_common(or, OSD_ACT_REMOVE, obj, 0, 0);
-}
-EXPORT_SYMBOL(osd_req_remove_object);
-
-
-/*TODO: void osd_req_create_multi(struct osd_request *or,
- struct osd_obj_id *first, struct osd_obj_id_list *list, unsigned nelem);
-*/
-
-void osd_req_write(struct osd_request *or,
- const struct osd_obj_id *obj, u64 offset,
- struct bio *bio, u64 len)
-{
- _osd_req_encode_common(or, OSD_ACT_WRITE, obj, offset, len);
- WARN_ON(or->out.bio || or->out.total_bytes);
- WARN_ON(!op_is_write(bio_op(bio)));
- or->out.bio = bio;
- or->out.total_bytes = len;
-}
-EXPORT_SYMBOL(osd_req_write);
-
-int osd_req_write_kern(struct osd_request *or,
- const struct osd_obj_id *obj, u64 offset, void* buff, u64 len)
-{
- struct request_queue *req_q = osd_request_queue(or->osd_dev);
- struct bio *bio = bio_map_kern(req_q, buff, len, GFP_KERNEL);
-
- if (IS_ERR(bio))
- return PTR_ERR(bio);
-
- bio_set_op_attrs(bio, REQ_OP_WRITE, 0);
- osd_req_write(or, obj, offset, bio, len);
- return 0;
-}
-EXPORT_SYMBOL(osd_req_write_kern);
-
-/*TODO: void osd_req_append(struct osd_request *,
- const struct osd_obj_id *, struct bio *data_out); */
-/*TODO: void osd_req_create_write(struct osd_request *,
- const struct osd_obj_id *, struct bio *data_out, u64 offset); */
-/*TODO: void osd_req_clear(struct osd_request *,
- const struct osd_obj_id *, u64 offset, u64 len); */
-/*TODO: void osd_req_punch(struct osd_request *,
- const struct osd_obj_id *, u64 offset, u64 len); V2 */
-
-void osd_req_flush_object(struct osd_request *or,
- const struct osd_obj_id *obj, enum osd_options_flush_scope_values op,
- /*V2*/ u64 offset, /*V2*/ u64 len)
-{
- if (unlikely(osd_req_is_ver1(or) && (offset || len))) {
- OSD_DEBUG("OSD Ver1 flush on specific range ignored\n");
- offset = 0;
- len = 0;
- }
-
- _osd_req_encode_common(or, OSD_ACT_FLUSH, obj, offset, len);
- _osd_req_encode_flush(or, op);
-}
-EXPORT_SYMBOL(osd_req_flush_object);
-
-void osd_req_read(struct osd_request *or,
- const struct osd_obj_id *obj, u64 offset,
- struct bio *bio, u64 len)
-{
- _osd_req_encode_common(or, OSD_ACT_READ, obj, offset, len);
- WARN_ON(or->in.bio || or->in.total_bytes);
- WARN_ON(op_is_write(bio_op(bio)));
- or->in.bio = bio;
- or->in.total_bytes = len;
-}
-EXPORT_SYMBOL(osd_req_read);
-
-int osd_req_read_kern(struct osd_request *or,
- const struct osd_obj_id *obj, u64 offset, void* buff, u64 len)
-{
- struct request_queue *req_q = osd_request_queue(or->osd_dev);
- struct bio *bio = bio_map_kern(req_q, buff, len, GFP_KERNEL);
-
- if (IS_ERR(bio))
- return PTR_ERR(bio);
-
- osd_req_read(or, obj, offset, bio, len);
- return 0;
-}
-EXPORT_SYMBOL(osd_req_read_kern);
-
-static int _add_sg_continuation_descriptor(struct osd_request *or,
- const struct osd_sg_entry *sglist, unsigned numentries, u64 *len)
-{
- struct osd_sg_continuation_descriptor *oscd;
- u32 oscd_size;
- unsigned i;
- int ret;
-
- oscd_size = sizeof(*oscd) + numentries * sizeof(oscd->entries[0]);
-
- if (!or->cdb_cont.total_bytes) {
- /* First time, jump over the header, we will write to:
- * cdb_cont.buff + cdb_cont.total_bytes
- */
- or->cdb_cont.total_bytes =
- sizeof(struct osd_continuation_segment_header);
- }
-
- ret = _alloc_cdb_cont(or, or->cdb_cont.total_bytes + oscd_size);
- if (unlikely(ret))
- return ret;
-
- oscd = or->cdb_cont.buff + or->cdb_cont.total_bytes;
- oscd->hdr.type = cpu_to_be16(SCATTER_GATHER_LIST);
- oscd->hdr.pad_length = 0;
- oscd->hdr.length = cpu_to_be32(oscd_size - sizeof(*oscd));
-
- *len = 0;
- /* copy the sg entries and convert to network byte order */
- for (i = 0; i < numentries; i++) {
- oscd->entries[i].offset = cpu_to_be64(sglist[i].offset);
- oscd->entries[i].len = cpu_to_be64(sglist[i].len);
- *len += sglist[i].len;
- }
-
- or->cdb_cont.total_bytes += oscd_size;
- OSD_DEBUG("total_bytes=%d oscd_size=%d numentries=%d\n",
- or->cdb_cont.total_bytes, oscd_size, numentries);
- return 0;
-}
-
-static int _osd_req_finalize_cdb_cont(struct osd_request *or, const u8 *cap_key)
-{
- struct request_queue *req_q = osd_request_queue(or->osd_dev);
- struct bio *bio;
- struct osd_cdb_head *cdbh = osd_cdb_head(&or->cdb);
- struct osd_continuation_segment_header *cont_seg_hdr;
-
- if (!or->cdb_cont.total_bytes)
- return 0;
-
- cont_seg_hdr = or->cdb_cont.buff;
- cont_seg_hdr->format = CDB_CONTINUATION_FORMAT_V2;
- cont_seg_hdr->service_action = cdbh->varlen_cdb.service_action;
-
- /* create a bio for continuation segment */
- bio = bio_map_kern(req_q, or->cdb_cont.buff, or->cdb_cont.total_bytes,
- GFP_KERNEL);
- if (IS_ERR(bio))
- return PTR_ERR(bio);
-
- bio_set_op_attrs(bio, REQ_OP_WRITE, 0);
-
- /* integrity check the continuation before the bio is linked
- * with the other data segments since the continuation
- * integrity is separate from the other data segments.
- */
- osd_sec_sign_data(cont_seg_hdr->integrity_check, bio, cap_key);
-
- cdbh->v2.cdb_continuation_length = cpu_to_be32(or->cdb_cont.total_bytes);
-
- /* we can't use _req_append_segment, because we need to link in the
- * continuation bio to the head of the bio list - the
- * continuation segment (if it exists) is always the first segment in
- * the out data buffer.
- */
- bio->bi_next = or->out.bio;
- or->out.bio = bio;
- or->out.total_bytes += or->cdb_cont.total_bytes;
-
- return 0;
-}
-
-/* osd_req_write_sg: Takes a @bio that points to the data out buffer and an
- * @sglist that has the scatter gather entries. Scatter-gather enables a write
- * of multiple none-contiguous areas of an object, in a single call. The extents
- * may overlap and/or be in any order. The only constrain is that:
- * total_bytes(sglist) >= total_bytes(bio)
- */
-int osd_req_write_sg(struct osd_request *or,
- const struct osd_obj_id *obj, struct bio *bio,
- const struct osd_sg_entry *sglist, unsigned numentries)
-{
- u64 len;
- int ret = _add_sg_continuation_descriptor(or, sglist, numentries, &len);
-
- if (ret)
- return ret;
- osd_req_write(or, obj, 0, bio, len);
-
- return 0;
-}
-EXPORT_SYMBOL(osd_req_write_sg);
-
-/* osd_req_read_sg: Read multiple extents of an object into @bio
- * See osd_req_write_sg
- */
-int osd_req_read_sg(struct osd_request *or,
- const struct osd_obj_id *obj, struct bio *bio,
- const struct osd_sg_entry *sglist, unsigned numentries)
-{
- u64 len;
- u64 off;
- int ret;
-
- if (numentries > 1) {
- off = 0;
- ret = _add_sg_continuation_descriptor(or, sglist, numentries,
- &len);
- if (ret)
- return ret;
- } else {
- /* Optimize the case of single segment, read_sg is a
- * bidi operation.
- */
- len = sglist->len;
- off = sglist->offset;
- }
- osd_req_read(or, obj, off, bio, len);
-
- return 0;
-}
-EXPORT_SYMBOL(osd_req_read_sg);
-
-/* SG-list write/read Kern API
- *
- * osd_req_{write,read}_sg_kern takes an array of @buff pointers and an array
- * of sg_entries. @numentries indicates how many pointers and sg_entries there
- * are. By requiring an array of buff pointers. This allows a caller to do a
- * single write/read and scatter into multiple buffers.
- * NOTE: Each buffer + len should not cross a page boundary.
- */
-static struct bio *_create_sg_bios(struct osd_request *or,
- void **buff, const struct osd_sg_entry *sglist, unsigned numentries)
-{
- struct request_queue *q = osd_request_queue(or->osd_dev);
- struct bio *bio;
- unsigned i;
-
- bio = bio_kmalloc(GFP_KERNEL, numentries);
- if (unlikely(!bio)) {
- OSD_DEBUG("Failed to allocate BIO size=%u\n", numentries);
- return ERR_PTR(-ENOMEM);
- }
-
- for (i = 0; i < numentries; i++) {
- unsigned offset = offset_in_page(buff[i]);
- struct page *page = virt_to_page(buff[i]);
- unsigned len = sglist[i].len;
- unsigned added_len;
-
- BUG_ON(offset + len > PAGE_SIZE);
- added_len = bio_add_pc_page(q, bio, page, len, offset);
- if (unlikely(len != added_len)) {
- OSD_DEBUG("bio_add_pc_page len(%d) != added_len(%d)\n",
- len, added_len);
- bio_put(bio);
- return ERR_PTR(-ENOMEM);
- }
- }
-
- return bio;
-}
-
-int osd_req_write_sg_kern(struct osd_request *or,
- const struct osd_obj_id *obj, void **buff,
- const struct osd_sg_entry *sglist, unsigned numentries)
-{
- struct bio *bio = _create_sg_bios(or, buff, sglist, numentries);
- if (IS_ERR(bio))
- return PTR_ERR(bio);
-
- bio_set_op_attrs(bio, REQ_OP_WRITE, 0);
- osd_req_write_sg(or, obj, bio, sglist, numentries);
-
- return 0;
-}
-EXPORT_SYMBOL(osd_req_write_sg_kern);
-
-int osd_req_read_sg_kern(struct osd_request *or,
- const struct osd_obj_id *obj, void **buff,
- const struct osd_sg_entry *sglist, unsigned numentries)
-{
- struct bio *bio = _create_sg_bios(or, buff, sglist, numentries);
- if (IS_ERR(bio))
- return PTR_ERR(bio);
-
- osd_req_read_sg(or, obj, bio, sglist, numentries);
-
- return 0;
-}
-EXPORT_SYMBOL(osd_req_read_sg_kern);
-
-
-
-void osd_req_get_attributes(struct osd_request *or,
- const struct osd_obj_id *obj)
-{
- _osd_req_encode_common(or, OSD_ACT_GET_ATTRIBUTES, obj, 0, 0);
-}
-EXPORT_SYMBOL(osd_req_get_attributes);
-
-void osd_req_set_attributes(struct osd_request *or,
- const struct osd_obj_id *obj)
-{
- _osd_req_encode_common(or, OSD_ACT_SET_ATTRIBUTES, obj, 0, 0);
-}
-EXPORT_SYMBOL(osd_req_set_attributes);
-
-/*
- * Attributes List-mode
- */
-
-int osd_req_add_set_attr_list(struct osd_request *or,
- const struct osd_attr *oa, unsigned nelem)
-{
- unsigned total_bytes = or->set_attr.total_bytes;
- void *attr_last;
- int ret;
-
- if (or->attributes_mode &&
- or->attributes_mode != OSD_CDB_GET_SET_ATTR_LISTS) {
- WARN_ON(1);
- return -EINVAL;
- }
- or->attributes_mode = OSD_CDB_GET_SET_ATTR_LISTS;
-
- if (!total_bytes) { /* first-time: allocate and put list header */
- total_bytes = _osd_req_sizeof_alist_header(or);
- ret = _alloc_set_attr_list(or, oa, nelem, total_bytes);
- if (ret)
- return ret;
- _osd_req_set_alist_type(or, or->set_attr.buff,
- OSD_ATTR_LIST_SET_RETRIEVE);
- }
- attr_last = or->set_attr.buff + total_bytes;
-
- for (; nelem; --nelem) {
- unsigned elem_size = _osd_req_alist_elem_size(or, oa->len);
-
- total_bytes += elem_size;
- if (unlikely(or->set_attr.alloc_size < total_bytes)) {
- or->set_attr.total_bytes = total_bytes - elem_size;
- ret = _alloc_set_attr_list(or, oa, nelem, total_bytes);
- if (ret)
- return ret;
- attr_last =
- or->set_attr.buff + or->set_attr.total_bytes;
- }
-
- _osd_req_alist_elem_encode(or, attr_last, oa);
-
- attr_last += elem_size;
- ++oa;
- }
-
- or->set_attr.total_bytes = total_bytes;
- return 0;
-}
-EXPORT_SYMBOL(osd_req_add_set_attr_list);
-
-static int _req_append_segment(struct osd_request *or,
- unsigned padding, struct _osd_req_data_segment *seg,
- struct _osd_req_data_segment *last_seg, struct _osd_io_info *io)
-{
- void *pad_buff;
- int ret;
-
- if (padding) {
- /* check if we can just add it to last buffer */
- if (last_seg &&
- (padding <= last_seg->alloc_size - last_seg->total_bytes))
- pad_buff = last_seg->buff + last_seg->total_bytes;
- else
- pad_buff = io->pad_buff;
-
- ret = blk_rq_map_kern(io->req->q, io->req, pad_buff, padding,
- GFP_KERNEL);
- if (ret)
- return ret;
- io->total_bytes += padding;
- }
-
- ret = blk_rq_map_kern(io->req->q, io->req, seg->buff, seg->total_bytes,
- GFP_KERNEL);
- if (ret)
- return ret;
-
- io->total_bytes += seg->total_bytes;
- OSD_DEBUG("padding=%d buff=%p total_bytes=%d\n", padding, seg->buff,
- seg->total_bytes);
- return 0;
-}
-
-static int _osd_req_finalize_set_attr_list(struct osd_request *or)
-{
- struct osd_cdb_head *cdbh = osd_cdb_head(&or->cdb);
- unsigned padding;
- int ret;
-
- if (!or->set_attr.total_bytes) {
- cdbh->attrs_list.set_attr_offset = OSD_OFFSET_UNUSED;
- return 0;
- }
-
- cdbh->attrs_list.set_attr_bytes = cpu_to_be32(or->set_attr.total_bytes);
- cdbh->attrs_list.set_attr_offset =
- osd_req_encode_offset(or, or->out.total_bytes, &padding);
-
- ret = _req_append_segment(or, padding, &or->set_attr,
- or->out.last_seg, &or->out);
- if (ret)
- return ret;
-
- or->out.last_seg = &or->set_attr;
- return 0;
-}
-
-int osd_req_add_get_attr_list(struct osd_request *or,
- const struct osd_attr *oa, unsigned nelem)
-{
- unsigned total_bytes = or->enc_get_attr.total_bytes;
- void *attr_last;
- int ret;
-
- if (or->attributes_mode &&
- or->attributes_mode != OSD_CDB_GET_SET_ATTR_LISTS) {
- WARN_ON(1);
- return -EINVAL;
- }
- or->attributes_mode = OSD_CDB_GET_SET_ATTR_LISTS;
-
- /* first time calc data-in list header size */
- if (!or->get_attr.total_bytes)
- or->get_attr.total_bytes = _osd_req_sizeof_alist_header(or);
-
- /* calc data-out info */
- if (!total_bytes) { /* first-time: allocate and put list header */
- unsigned max_bytes;
-
- total_bytes = _osd_req_sizeof_alist_header(or);
- max_bytes = total_bytes +
- nelem * sizeof(struct osd_attributes_list_attrid);
- ret = _alloc_get_attr_desc(or, max_bytes);
- if (ret)
- return ret;
-
- _osd_req_set_alist_type(or, or->enc_get_attr.buff,
- OSD_ATTR_LIST_GET);
- }
- attr_last = or->enc_get_attr.buff + total_bytes;
-
- for (; nelem; --nelem) {
- struct osd_attributes_list_attrid *attrid;
- const unsigned cur_size = sizeof(*attrid);
-
- total_bytes += cur_size;
- if (unlikely(or->enc_get_attr.alloc_size < total_bytes)) {
- or->enc_get_attr.total_bytes = total_bytes - cur_size;
- ret = _alloc_get_attr_desc(or,
- total_bytes + nelem * sizeof(*attrid));
- if (ret)
- return ret;
- attr_last = or->enc_get_attr.buff +
- or->enc_get_attr.total_bytes;
- }
-
- attrid = attr_last;
- attrid->attr_page = cpu_to_be32(oa->attr_page);
- attrid->attr_id = cpu_to_be32(oa->attr_id);
-
- attr_last += cur_size;
-
- /* calc data-in size */
- or->get_attr.total_bytes +=
- _osd_req_alist_elem_size(or, oa->len);
- ++oa;
- }
-
- or->enc_get_attr.total_bytes = total_bytes;
-
- OSD_DEBUG(
- "get_attr.total_bytes=%u(%u) enc_get_attr.total_bytes=%u(%zu)\n",
- or->get_attr.total_bytes,
- or->get_attr.total_bytes - _osd_req_sizeof_alist_header(or),
- or->enc_get_attr.total_bytes,
- (or->enc_get_attr.total_bytes - _osd_req_sizeof_alist_header(or))
- / sizeof(struct osd_attributes_list_attrid));
-
- return 0;
-}
-EXPORT_SYMBOL(osd_req_add_get_attr_list);
-
-static int _osd_req_finalize_get_attr_list(struct osd_request *or)
-{
- struct osd_cdb_head *cdbh = osd_cdb_head(&or->cdb);
- unsigned out_padding;
- unsigned in_padding;
- int ret;
-
- if (!or->enc_get_attr.total_bytes) {
- cdbh->attrs_list.get_attr_desc_offset = OSD_OFFSET_UNUSED;
- cdbh->attrs_list.get_attr_offset = OSD_OFFSET_UNUSED;
- return 0;
- }
-
- ret = _alloc_get_attr_list(or);
- if (ret)
- return ret;
-
- /* The out-going buffer info update */
- OSD_DEBUG("out-going\n");
- cdbh->attrs_list.get_attr_desc_bytes =
- cpu_to_be32(or->enc_get_attr.total_bytes);
-
- cdbh->attrs_list.get_attr_desc_offset =
- osd_req_encode_offset(or, or->out.total_bytes, &out_padding);
-
- ret = _req_append_segment(or, out_padding, &or->enc_get_attr,
- or->out.last_seg, &or->out);
- if (ret)
- return ret;
- or->out.last_seg = &or->enc_get_attr;
-
- /* The incoming buffer info update */
- OSD_DEBUG("in-coming\n");
- cdbh->attrs_list.get_attr_alloc_length =
- cpu_to_be32(or->get_attr.total_bytes);
-
- cdbh->attrs_list.get_attr_offset =
- osd_req_encode_offset(or, or->in.total_bytes, &in_padding);
-
- ret = _req_append_segment(or, in_padding, &or->get_attr, NULL,
- &or->in);
- if (ret)
- return ret;
- or->in.last_seg = &or->get_attr;
-
- return 0;
-}
-
-int osd_req_decode_get_attr_list(struct osd_request *or,
- struct osd_attr *oa, int *nelem, void **iterator)
-{
- unsigned cur_bytes, returned_bytes;
- int n;
- const unsigned sizeof_attr_list = _osd_req_sizeof_alist_header(or);
- void *cur_p;
-
- if (!_osd_req_is_alist_type(or, or->get_attr.buff,
- OSD_ATTR_LIST_SET_RETRIEVE)) {
- oa->attr_page = 0;
- oa->attr_id = 0;
- oa->val_ptr = NULL;
- oa->len = 0;
- *iterator = NULL;
- return 0;
- }
-
- if (*iterator) {
- BUG_ON((*iterator < or->get_attr.buff) ||
- (or->get_attr.buff + or->get_attr.alloc_size < *iterator));
- cur_p = *iterator;
- cur_bytes = (*iterator - or->get_attr.buff) - sizeof_attr_list;
- returned_bytes = or->get_attr.total_bytes;
- } else { /* first time decode the list header */
- cur_bytes = sizeof_attr_list;
- returned_bytes = _osd_req_alist_size(or, or->get_attr.buff) +
- sizeof_attr_list;
-
- cur_p = or->get_attr.buff + sizeof_attr_list;
-
- if (returned_bytes > or->get_attr.alloc_size) {
- OSD_DEBUG("target report: space was not big enough! "
- "Allocate=%u Needed=%u\n",
- or->get_attr.alloc_size,
- returned_bytes + sizeof_attr_list);
-
- returned_bytes =
- or->get_attr.alloc_size - sizeof_attr_list;
- }
- or->get_attr.total_bytes = returned_bytes;
- }
-
- for (n = 0; (n < *nelem) && (cur_bytes < returned_bytes); ++n) {
- int inc = _osd_req_alist_elem_decode(or, cur_p, oa,
- returned_bytes - cur_bytes);
-
- if (inc < 0) {
- OSD_ERR("BAD FOOD from target. list not valid!"
- "c=%d r=%d n=%d\n",
- cur_bytes, returned_bytes, n);
- oa->val_ptr = NULL;
- cur_bytes = returned_bytes; /* break the caller loop */
- break;
- }
-
- cur_bytes += inc;
- cur_p += inc;
- ++oa;
- }
-
- *iterator = (returned_bytes - cur_bytes) ? cur_p : NULL;
- *nelem = n;
- return returned_bytes - cur_bytes;
-}
-EXPORT_SYMBOL(osd_req_decode_get_attr_list);
-
-/*
- * Attributes Page-mode
- */
-
-int osd_req_add_get_attr_page(struct osd_request *or,
- u32 page_id, void *attar_page, unsigned max_page_len,
- const struct osd_attr *set_one_attr)
-{
- struct osd_cdb_head *cdbh = osd_cdb_head(&or->cdb);
-
- if (or->attributes_mode &&
- or->attributes_mode != OSD_CDB_GET_ATTR_PAGE_SET_ONE) {
- WARN_ON(1);
- return -EINVAL;
- }
- or->attributes_mode = OSD_CDB_GET_ATTR_PAGE_SET_ONE;
-
- or->get_attr.buff = attar_page;
- or->get_attr.total_bytes = max_page_len;
-
- cdbh->attrs_page.get_attr_page = cpu_to_be32(page_id);
- cdbh->attrs_page.get_attr_alloc_length = cpu_to_be32(max_page_len);
-
- if (!set_one_attr || !set_one_attr->attr_page)
- return 0; /* The set is optional */
-
- or->set_attr.buff = set_one_attr->val_ptr;
- or->set_attr.total_bytes = set_one_attr->len;
-
- cdbh->attrs_page.set_attr_page = cpu_to_be32(set_one_attr->attr_page);
- cdbh->attrs_page.set_attr_id = cpu_to_be32(set_one_attr->attr_id);
- cdbh->attrs_page.set_attr_length = cpu_to_be32(set_one_attr->len);
- return 0;
-}
-EXPORT_SYMBOL(osd_req_add_get_attr_page);
-
-static int _osd_req_finalize_attr_page(struct osd_request *or)
-{
- struct osd_cdb_head *cdbh = osd_cdb_head(&or->cdb);
- unsigned in_padding, out_padding;
- int ret;
-
- /* returned page */
- cdbh->attrs_page.get_attr_offset =
- osd_req_encode_offset(or, or->in.total_bytes, &in_padding);
-
- ret = _req_append_segment(or, in_padding, &or->get_attr, NULL,
- &or->in);
- if (ret)
- return ret;
-
- if (or->set_attr.total_bytes == 0)
- return 0;
-
- /* set one value */
- cdbh->attrs_page.set_attr_offset =
- osd_req_encode_offset(or, or->out.total_bytes, &out_padding);
-
- ret = _req_append_segment(or, out_padding, &or->set_attr, NULL,
- &or->out);
- return ret;
-}
-
-static inline void osd_sec_parms_set_out_offset(bool is_v1,
- struct osd_security_parameters *sec_parms, osd_cdb_offset offset)
-{
- if (is_v1)
- sec_parms->v1.data_out_integrity_check_offset = offset;
- else
- sec_parms->v2.data_out_integrity_check_offset = offset;
-}
-
-static inline void osd_sec_parms_set_in_offset(bool is_v1,
- struct osd_security_parameters *sec_parms, osd_cdb_offset offset)
-{
- if (is_v1)
- sec_parms->v1.data_in_integrity_check_offset = offset;
- else
- sec_parms->v2.data_in_integrity_check_offset = offset;
-}
-
-static int _osd_req_finalize_data_integrity(struct osd_request *or,
- bool has_in, bool has_out, struct bio *out_data_bio, u64 out_data_bytes,
- const u8 *cap_key)
-{
- struct osd_security_parameters *sec_parms = _osd_req_sec_params(or);
- int ret;
-
- if (!osd_is_sec_alldata(sec_parms))
- return 0;
-
- if (has_out) {
- struct _osd_req_data_segment seg = {
- .buff = &or->out_data_integ,
- .total_bytes = sizeof(or->out_data_integ),
- };
- unsigned pad;
-
- or->out_data_integ.data_bytes = cpu_to_be64(out_data_bytes);
- or->out_data_integ.set_attributes_bytes = cpu_to_be64(
- or->set_attr.total_bytes);
- or->out_data_integ.get_attributes_bytes = cpu_to_be64(
- or->enc_get_attr.total_bytes);
-
- osd_sec_parms_set_out_offset(osd_req_is_ver1(or), sec_parms,
- osd_req_encode_offset(or, or->out.total_bytes, &pad));
-
- ret = _req_append_segment(or, pad, &seg, or->out.last_seg,
- &or->out);
- if (ret)
- return ret;
- or->out.last_seg = NULL;
-
- /* they are now all chained to request sign them all together */
- osd_sec_sign_data(&or->out_data_integ, out_data_bio,
- cap_key);
- }
-
- if (has_in) {
- struct _osd_req_data_segment seg = {
- .buff = &or->in_data_integ,
- .total_bytes = sizeof(or->in_data_integ),
- };
- unsigned pad;
-
- osd_sec_parms_set_in_offset(osd_req_is_ver1(or), sec_parms,
- osd_req_encode_offset(or, or->in.total_bytes, &pad));
-
- ret = _req_append_segment(or, pad, &seg, or->in.last_seg,
- &or->in);
- if (ret)
- return ret;
-
- or->in.last_seg = NULL;
- }
-
- return 0;
-}
-
-/*
- * osd_finalize_request and helpers
- */
-static struct request *_make_request(struct request_queue *q, bool has_write,
- struct _osd_io_info *oii)
-{
- struct request *req;
- struct bio *bio = oii->bio;
- int ret;
-
- req = blk_get_request(q, has_write ? REQ_OP_SCSI_OUT : REQ_OP_SCSI_IN,
- 0);
- if (IS_ERR(req))
- return req;
-
- for_each_bio(bio) {
- struct bio *bounce_bio = bio;
-
- ret = blk_rq_append_bio(req, &bounce_bio);
- if (ret)
- return ERR_PTR(ret);
- }
-
- return req;
-}
-
-static int _init_blk_request(struct osd_request *or,
- bool has_in, bool has_out)
-{
- struct scsi_device *scsi_device = or->osd_dev->scsi_device;
- struct request_queue *q = scsi_device->request_queue;
- struct request *req;
- int ret;
-
- req = _make_request(q, has_out, has_out ? &or->out : &or->in);
- if (IS_ERR(req)) {
- ret = PTR_ERR(req);
- goto out;
- }
-
- or->request = req;
- req->rq_flags |= RQF_QUIET;
-
- req->timeout = or->timeout;
- scsi_req(req)->retries = or->retries;
-
- if (has_out) {
- or->out.req = req;
- if (has_in) {
- /* allocate bidi request */
- req = _make_request(q, false, &or->in);
- if (IS_ERR(req)) {
- OSD_DEBUG("blk_get_request for bidi failed\n");
- ret = PTR_ERR(req);
- goto out;
- }
- or->in.req = or->request->next_rq = req;
- }
- } else if (has_in)
- or->in.req = req;
-
- ret = 0;
-out:
- OSD_DEBUG("or=%p has_in=%d has_out=%d => %d, %p\n",
- or, has_in, has_out, ret, or->request);
- return ret;
-}
-
-int osd_finalize_request(struct osd_request *or,
- u8 options, const void *cap, const u8 *cap_key)
-{
- struct osd_cdb_head *cdbh = osd_cdb_head(&or->cdb);
- bool has_in, has_out;
- /* Save for data_integrity without the cdb_continuation */
- struct bio *out_data_bio = or->out.bio;
- u64 out_data_bytes = or->out.total_bytes;
- int ret;
-
- if (options & OSD_REQ_FUA)
- cdbh->options |= OSD_CDB_FUA;
-
- if (options & OSD_REQ_DPO)
- cdbh->options |= OSD_CDB_DPO;
-
- if (options & OSD_REQ_BYPASS_TIMESTAMPS)
- cdbh->timestamp_control = OSD_CDB_BYPASS_TIMESTAMPS;
-
- osd_set_caps(&or->cdb, cap);
-
- has_in = or->in.bio || or->get_attr.total_bytes;
- has_out = or->out.bio || or->cdb_cont.total_bytes ||
- or->set_attr.total_bytes || or->enc_get_attr.total_bytes;
-
- ret = _osd_req_finalize_cdb_cont(or, cap_key);
- if (ret) {
- OSD_DEBUG("_osd_req_finalize_cdb_cont failed\n");
- return ret;
- }
- ret = _init_blk_request(or, has_in, has_out);
- if (ret) {
- OSD_DEBUG("_init_blk_request failed\n");
- return ret;
- }
-
- or->out.pad_buff = sg_out_pad_buffer;
- or->in.pad_buff = sg_in_pad_buffer;
-
- if (!or->attributes_mode)
- or->attributes_mode = OSD_CDB_GET_SET_ATTR_LISTS;
- cdbh->command_specific_options |= or->attributes_mode;
- if (or->attributes_mode == OSD_CDB_GET_ATTR_PAGE_SET_ONE) {
- ret = _osd_req_finalize_attr_page(or);
- if (ret) {
- OSD_DEBUG("_osd_req_finalize_attr_page failed\n");
- return ret;
- }
- } else {
- /* TODO: I think that for the GET_ATTR command these 2 should
- * be reversed to keep them in execution order (for embedded
- * targets with low memory footprint)
- */
- ret = _osd_req_finalize_set_attr_list(or);
- if (ret) {
- OSD_DEBUG("_osd_req_finalize_set_attr_list failed\n");
- return ret;
- }
-
- ret = _osd_req_finalize_get_attr_list(or);
- if (ret) {
- OSD_DEBUG("_osd_req_finalize_get_attr_list failed\n");
- return ret;
- }
- }
-
- ret = _osd_req_finalize_data_integrity(or, has_in, has_out,
- out_data_bio, out_data_bytes,
- cap_key);
- if (ret)
- return ret;
-
- osd_sec_sign_cdb(&or->cdb, cap_key);
-
- scsi_req(or->request)->cmd = or->cdb.buff;
- scsi_req(or->request)->cmd_len = _osd_req_cdb_len(or);
-
- return 0;
-}
-EXPORT_SYMBOL(osd_finalize_request);
-
-static bool _is_osd_security_code(int code)
-{
- return (code == osd_security_audit_value_frozen) ||
- (code == osd_security_working_key_frozen) ||
- (code == osd_nonce_not_unique) ||
- (code == osd_nonce_timestamp_out_of_range) ||
- (code == osd_invalid_dataout_buffer_integrity_check_value);
-}
-
-#define OSD_SENSE_PRINT1(fmt, a...) \
- do { \
- if (__cur_sense_need_output) \
- OSD_ERR(fmt, ##a); \
- } while (0)
-
-#define OSD_SENSE_PRINT2(fmt, a...) OSD_SENSE_PRINT1(" " fmt, ##a)
-
-int osd_req_decode_sense_full(struct osd_request *or,
- struct osd_sense_info *osi, bool silent,
- struct osd_obj_id *bad_obj_list __unused, int max_obj __unused,
- struct osd_attr *bad_attr_list, int max_attr)
-{
- int sense_len, original_sense_len;
- struct osd_sense_info local_osi;
- struct scsi_sense_descriptor_based *ssdb;
- void *cur_descriptor;
-#if (CONFIG_SCSI_OSD_DPRINT_SENSE == 0)
- const bool __cur_sense_need_output = false;
-#else
- bool __cur_sense_need_output = !silent;
-#endif
- int ret;
-
- if (likely(!or->req_errors))
- return 0;
-
- osi = osi ? : &local_osi;
- memset(osi, 0, sizeof(*osi));
-
- ssdb = (typeof(ssdb))or->sense;
- sense_len = or->sense_len;
- if ((sense_len < (int)sizeof(*ssdb) || !ssdb->sense_key)) {
- OSD_ERR("Block-layer returned error(0x%x) but "
- "sense_len(%u) || key(%d) is empty\n",
- or->req_errors, sense_len, ssdb->sense_key);
- goto analyze;
- }
-
- if ((ssdb->response_code != 0x72) && (ssdb->response_code != 0x73)) {
- OSD_ERR("Unrecognized scsi sense: rcode=%x length=%d\n",
- ssdb->response_code, sense_len);
- goto analyze;
- }
-
- osi->key = ssdb->sense_key;
- osi->additional_code = be16_to_cpu(ssdb->additional_sense_code);
- original_sense_len = ssdb->additional_sense_length + 8;
-
-#if (CONFIG_SCSI_OSD_DPRINT_SENSE == 1)
- if (__cur_sense_need_output)
- __cur_sense_need_output = (osi->key > scsi_sk_recovered_error);
-#endif
- OSD_SENSE_PRINT1("Main Sense information key=0x%x length(%d, %d) "
- "additional_code=0x%x async_error=%d errors=0x%x\n",
- osi->key, original_sense_len, sense_len,
- osi->additional_code, or->async_error,
- or->req_errors);
-
- if (original_sense_len < sense_len)
- sense_len = original_sense_len;
-
- cur_descriptor = ssdb->ssd;
- sense_len -= sizeof(*ssdb);
- while (sense_len > 0) {
- struct scsi_sense_descriptor *ssd = cur_descriptor;
- int cur_len = ssd->additional_length + 2;
-
- sense_len -= cur_len;
-
- if (sense_len < 0)
- break; /* sense was truncated */
-
- switch (ssd->descriptor_type) {
- case scsi_sense_information:
- case scsi_sense_command_specific_information:
- {
- struct scsi_sense_command_specific_data_descriptor
- *sscd = cur_descriptor;
-
- osi->command_info =
- get_unaligned_be64(&sscd->information) ;
- OSD_SENSE_PRINT2(
- "command_specific_information 0x%llx \n",
- _LLU(osi->command_info));
- break;
- }
- case scsi_sense_key_specific:
- {
- struct scsi_sense_key_specific_data_descriptor
- *ssks = cur_descriptor;
-
- osi->sense_info = get_unaligned_be16(&ssks->value);
- OSD_SENSE_PRINT2(
- "sense_key_specific_information %u"
- "sksv_cd_bpv_bp (0x%x)\n",
- osi->sense_info, ssks->sksv_cd_bpv_bp);
- break;
- }
- case osd_sense_object_identification:
- { /*FIXME: Keep first not last, Store in array*/
- struct osd_sense_identification_data_descriptor
- *osidd = cur_descriptor;
-
- osi->not_initiated_command_functions =
- le32_to_cpu(osidd->not_initiated_functions);
- osi->completed_command_functions =
- le32_to_cpu(osidd->completed_functions);
- osi->obj.partition = be64_to_cpu(osidd->partition_id);
- osi->obj.id = be64_to_cpu(osidd->object_id);
- OSD_SENSE_PRINT2(
- "object_identification pid=0x%llx oid=0x%llx\n",
- _LLU(osi->obj.partition), _LLU(osi->obj.id));
- OSD_SENSE_PRINT2(
- "not_initiated_bits(%x) "
- "completed_command_bits(%x)\n",
- osi->not_initiated_command_functions,
- osi->completed_command_functions);
- break;
- }
- case osd_sense_response_integrity_check:
- {
- struct osd_sense_response_integrity_check_descriptor
- *d = cur_descriptor;
- /* 2nibbles+space+ASCII */
- char dump[sizeof(d->integrity_check_value) * 4 + 2];
-
- hex_dump_to_buffer(d->integrity_check_value,
- sizeof(d->integrity_check_value),
- 32, 1, dump, sizeof(dump), true);
- OSD_SENSE_PRINT2("response_integrity [%s]\n", dump);
- }
- case osd_sense_attribute_identification:
- {
- struct osd_sense_attributes_data_descriptor
- *osadd = cur_descriptor;
- unsigned len = min(cur_len, sense_len);
- struct osd_sense_attr *pattr = osadd->sense_attrs;
-
- while (len >= sizeof(*pattr)) {
- u32 attr_page = be32_to_cpu(pattr->attr_page);
- u32 attr_id = be32_to_cpu(pattr->attr_id);
-
- if (!osi->attr.attr_page) {
- osi->attr.attr_page = attr_page;
- osi->attr.attr_id = attr_id;
- }
-
- if (bad_attr_list && max_attr) {
- bad_attr_list->attr_page = attr_page;
- bad_attr_list->attr_id = attr_id;
- bad_attr_list++;
- max_attr--;
- }
-
- len -= sizeof(*pattr);
- OSD_SENSE_PRINT2(
- "osd_sense_attribute_identification"
- "attr_page=0x%x attr_id=0x%x\n",
- attr_page, attr_id);
- }
- }
- /*These are not legal for OSD*/
- case scsi_sense_field_replaceable_unit:
- OSD_SENSE_PRINT2("scsi_sense_field_replaceable_unit\n");
- break;
- case scsi_sense_stream_commands:
- OSD_SENSE_PRINT2("scsi_sense_stream_commands\n");
- break;
- case scsi_sense_block_commands:
- OSD_SENSE_PRINT2("scsi_sense_block_commands\n");
- break;
- case scsi_sense_ata_return:
- OSD_SENSE_PRINT2("scsi_sense_ata_return\n");
- break;
- default:
- if (ssd->descriptor_type <= scsi_sense_Reserved_last)
- OSD_SENSE_PRINT2(
- "scsi_sense Reserved descriptor (0x%x)",
- ssd->descriptor_type);
- else
- OSD_SENSE_PRINT2(
- "scsi_sense Vendor descriptor (0x%x)",
- ssd->descriptor_type);
- }
-
- cur_descriptor += cur_len;
- }
-
-analyze:
- if (!osi->key) {
- /* scsi sense is Empty, the request was never issued to target
- * linux return code might tell us what happened.
- */
- if (or->async_error == BLK_STS_RESOURCE)
- osi->osd_err_pri = OSD_ERR_PRI_RESOURCE;
- else
- osi->osd_err_pri = OSD_ERR_PRI_UNREACHABLE;
- ret = or->async_error;
- } else if (osi->key <= scsi_sk_recovered_error) {
- osi->osd_err_pri = 0;
- ret = 0;
- } else if (osi->additional_code == scsi_invalid_field_in_cdb) {
- if (osi->cdb_field_offset == OSD_CFO_STARTING_BYTE) {
- osi->osd_err_pri = OSD_ERR_PRI_CLEAR_PAGES;
- ret = -EFAULT; /* caller should recover from this */
- } else if (osi->cdb_field_offset == OSD_CFO_OBJECT_ID) {
- osi->osd_err_pri = OSD_ERR_PRI_NOT_FOUND;
- ret = -ENOENT;
- } else if (osi->cdb_field_offset == OSD_CFO_PERMISSIONS) {
- osi->osd_err_pri = OSD_ERR_PRI_NO_ACCESS;
- ret = -EACCES;
- } else {
- osi->osd_err_pri = OSD_ERR_PRI_BAD_CRED;
- ret = -EINVAL;
- }
- } else if (osi->additional_code == osd_quota_error) {
- osi->osd_err_pri = OSD_ERR_PRI_NO_SPACE;
- ret = -ENOSPC;
- } else if (_is_osd_security_code(osi->additional_code)) {
- osi->osd_err_pri = OSD_ERR_PRI_BAD_CRED;
- ret = -EINVAL;
- } else {
- osi->osd_err_pri = OSD_ERR_PRI_EIO;
- ret = -EIO;
- }
-
- if (!or->out.residual)
- or->out.residual = or->out.total_bytes;
- if (!or->in.residual)
- or->in.residual = or->in.total_bytes;
-
- return ret;
-}
-EXPORT_SYMBOL(osd_req_decode_sense_full);
-
-/*
- * Implementation of osd_sec.h API
- * TODO: Move to a separate osd_sec.c file at a later stage.
- */
-
-enum { OSD_SEC_CAP_V1_ALL_CAPS =
- OSD_SEC_CAP_APPEND | OSD_SEC_CAP_OBJ_MGMT | OSD_SEC_CAP_REMOVE |
- OSD_SEC_CAP_CREATE | OSD_SEC_CAP_SET_ATTR | OSD_SEC_CAP_GET_ATTR |
- OSD_SEC_CAP_WRITE | OSD_SEC_CAP_READ | OSD_SEC_CAP_POL_SEC |
- OSD_SEC_CAP_GLOBAL | OSD_SEC_CAP_DEV_MGMT
-};
-
-enum { OSD_SEC_CAP_V2_ALL_CAPS =
- OSD_SEC_CAP_V1_ALL_CAPS | OSD_SEC_CAP_QUERY | OSD_SEC_CAP_M_OBJECT
-};
-
-void osd_sec_init_nosec_doall_caps(void *caps,
- const struct osd_obj_id *obj, bool is_collection, const bool is_v1)
-{
- struct osd_capability *cap = caps;
- u8 type;
- u8 descriptor_type;
-
- if (likely(obj->id)) {
- if (unlikely(is_collection)) {
- type = OSD_SEC_OBJ_COLLECTION;
- descriptor_type = is_v1 ? OSD_SEC_OBJ_DESC_OBJ :
- OSD_SEC_OBJ_DESC_COL;
- } else {
- type = OSD_SEC_OBJ_USER;
- descriptor_type = OSD_SEC_OBJ_DESC_OBJ;
- }
- WARN_ON(!obj->partition);
- } else {
- type = obj->partition ? OSD_SEC_OBJ_PARTITION :
- OSD_SEC_OBJ_ROOT;
- descriptor_type = OSD_SEC_OBJ_DESC_PAR;
- }
-
- memset(cap, 0, sizeof(*cap));
-
- cap->h.format = OSD_SEC_CAP_FORMAT_VER1;
- cap->h.integrity_algorithm__key_version = 0; /* MAKE_BYTE(0, 0); */
- cap->h.security_method = OSD_SEC_NOSEC;
-/* cap->expiration_time;
- cap->AUDIT[30-10];
- cap->discriminator[42-30];
- cap->object_created_time; */
- cap->h.object_type = type;
- osd_sec_set_caps(&cap->h, OSD_SEC_CAP_V1_ALL_CAPS);
- cap->h.object_descriptor_type = descriptor_type;
- cap->od.obj_desc.policy_access_tag = 0;
- cap->od.obj_desc.allowed_partition_id = cpu_to_be64(obj->partition);
- cap->od.obj_desc.allowed_object_id = cpu_to_be64(obj->id);
-}
-EXPORT_SYMBOL(osd_sec_init_nosec_doall_caps);
-
-/* FIXME: Extract version from caps pointer.
- * Also Pete's target only supports caps from OSDv1 for now
- */
-void osd_set_caps(struct osd_cdb *cdb, const void *caps)
-{
- /* NOTE: They start at same address */
- memcpy(&cdb->v1.caps, caps, OSDv1_CAP_LEN);
-}
-
-bool osd_is_sec_alldata(struct osd_security_parameters *sec_parms __unused)
-{
- return false;
-}
-
-void osd_sec_sign_cdb(struct osd_cdb *ocdb __unused, const u8 *cap_key __unused)
-{
-}
-
-void osd_sec_sign_data(void *data_integ __unused,
- struct bio *bio __unused, const u8 *cap_key __unused)
-{
-}
-
-/*
- * Declared in osd_protocol.h
- * 4.12.5 Data-In and Data-Out buffer offsets
- * byte offset = mantissa * (2^(exponent+8))
- * Returns the smallest allowed encoded offset that contains given @offset
- * The actual encoded offset returned is @offset + *@padding.
- */
-osd_cdb_offset __osd_encode_offset(
- u64 offset, unsigned *padding, int min_shift, int max_shift)
-{
- u64 try_offset = -1, mod, align;
- osd_cdb_offset be32_offset;
- int shift;
-
- *padding = 0;
- if (!offset)
- return 0;
-
- for (shift = min_shift; shift < max_shift; ++shift) {
- try_offset = offset >> shift;
- if (try_offset < (1 << OSD_OFFSET_MAX_BITS))
- break;
- }
-
- BUG_ON(shift == max_shift);
-
- align = 1 << shift;
- mod = offset & (align - 1);
- if (mod) {
- *padding = align - mod;
- try_offset += 1;
- }
-
- try_offset |= ((shift - 8) & 0xf) << 28;
- be32_offset = cpu_to_be32((u32)try_offset);
-
- OSD_DEBUG("offset=%llu mantissa=%llu exp=%d encoded=%x pad=%d\n",
- _LLU(offset), _LLU(try_offset & 0x0FFFFFFF), shift,
- be32_offset, *padding);
- return be32_offset;
-}
diff --git a/drivers/scsi/osd/osd_uld.c b/drivers/scsi/osd/osd_uld.c
deleted file mode 100644
index eaf36ccf58db..000000000000
--- a/drivers/scsi/osd/osd_uld.c
+++ /dev/null
@@ -1,571 +0,0 @@
-/*
- * osd_uld.c - OSD Upper Layer Driver
- *
- * A Linux driver module that registers as a SCSI ULD and probes
- * for OSD type SCSI devices.
- * It's main function is to export osd devices to in-kernel users like
- * osdfs and pNFS-objects-LD. It also provides one ioctl for running
- * in Kernel tests.
- *
- * Copyright (C) 2008 Panasas Inc. All rights reserved.
- *
- * Authors:
- * Boaz Harrosh <ooo@electrozaur.com>
- * Benny Halevy <bhalevy@panasas.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2
- *
- * 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.
- * 3. Neither the name of the Panasas company nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED ``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 REGENTS 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.
- */
-
-#include <linux/namei.h>
-#include <linux/cdev.h>
-#include <linux/fs.h>
-#include <linux/module.h>
-#include <linux/device.h>
-#include <linux/idr.h>
-#include <linux/major.h>
-#include <linux/file.h>
-#include <linux/slab.h>
-
-#include <scsi/scsi.h>
-#include <scsi/scsi_driver.h>
-#include <scsi/scsi_device.h>
-#include <scsi/scsi_ioctl.h>
-
-#include <scsi/osd_initiator.h>
-#include <scsi/osd_sec.h>
-
-#include "osd_debug.h"
-
-#ifndef TYPE_OSD
-# define TYPE_OSD 0x11
-#endif
-
-#ifndef SCSI_OSD_MAJOR
-# define SCSI_OSD_MAJOR 260
-#endif
-#define SCSI_OSD_MAX_MINOR MINORMASK
-
-static const char osd_name[] = "osd";
-static const char *osd_version_string = "open-osd 0.2.1";
-
-MODULE_AUTHOR("Boaz Harrosh <ooo@electrozaur.com>");
-MODULE_DESCRIPTION("open-osd Upper-Layer-Driver osd.ko");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS_CHARDEV_MAJOR(SCSI_OSD_MAJOR);
-MODULE_ALIAS_SCSI_DEVICE(TYPE_OSD);
-
-struct osd_uld_device {
- int minor;
- struct device class_dev;
- struct cdev cdev;
- struct osd_dev od;
- struct osd_dev_info odi;
- struct gendisk *disk;
-};
-
-struct osd_dev_handle {
- struct osd_dev od;
- struct file *file;
- struct osd_uld_device *oud;
-} ;
-
-static DEFINE_IDA(osd_minor_ida);
-
-/*
- * scsi sysfs attribute operations
- */
-static ssize_t osdname_show(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct osd_uld_device *ould = container_of(dev, struct osd_uld_device,
- class_dev);
- return sprintf(buf, "%s\n", ould->odi.osdname);
-}
-static DEVICE_ATTR_RO(osdname);
-
-static ssize_t systemid_show(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct osd_uld_device *ould = container_of(dev, struct osd_uld_device,
- class_dev);
-
- memcpy(buf, ould->odi.systemid, ould->odi.systemid_len);
- return ould->odi.systemid_len;
-}
-static DEVICE_ATTR_RO(systemid);
-
-static struct attribute *osd_uld_attrs[] = {
- &dev_attr_osdname.attr,
- &dev_attr_systemid.attr,
- NULL,
-};
-ATTRIBUTE_GROUPS(osd_uld);
-
-static struct class osd_uld_class = {
- .owner = THIS_MODULE,
- .name = "scsi_osd",
- .dev_groups = osd_uld_groups,
-};
-
-/*
- * Char Device operations
- */
-
-static int osd_uld_open(struct inode *inode, struct file *file)
-{
- struct osd_uld_device *oud = container_of(inode->i_cdev,
- struct osd_uld_device, cdev);
-
- get_device(&oud->class_dev);
- /* cache osd_uld_device on file handle */
- file->private_data = oud;
- OSD_DEBUG("osd_uld_open %p\n", oud);
- return 0;
-}
-
-static int osd_uld_release(struct inode *inode, struct file *file)
-{
- struct osd_uld_device *oud = file->private_data;
-
- OSD_DEBUG("osd_uld_release %p\n", file->private_data);
- file->private_data = NULL;
- put_device(&oud->class_dev);
- return 0;
-}
-
-/* FIXME: Only one vector for now */
-unsigned g_test_ioctl;
-do_test_fn *g_do_test;
-
-int osduld_register_test(unsigned ioctl, do_test_fn *do_test)
-{
- if (g_test_ioctl)
- return -EINVAL;
-
- g_test_ioctl = ioctl;
- g_do_test = do_test;
- return 0;
-}
-EXPORT_SYMBOL(osduld_register_test);
-
-void osduld_unregister_test(unsigned ioctl)
-{
- if (ioctl == g_test_ioctl) {
- g_test_ioctl = 0;
- g_do_test = NULL;
- }
-}
-EXPORT_SYMBOL(osduld_unregister_test);
-
-static do_test_fn *_find_ioctl(unsigned cmd)
-{
- if (g_test_ioctl == cmd)
- return g_do_test;
- else
- return NULL;
-}
-
-static long osd_uld_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
-{
- struct osd_uld_device *oud = file->private_data;
- int ret;
- do_test_fn *do_test;
-
- do_test = _find_ioctl(cmd);
- if (do_test)
- ret = do_test(&oud->od, cmd, arg);
- else {
- OSD_ERR("Unknown ioctl %d: osd_uld_device=%p\n", cmd, oud);
- ret = -ENOIOCTLCMD;
- }
- return ret;
-}
-
-static const struct file_operations osd_fops = {
- .owner = THIS_MODULE,
- .open = osd_uld_open,
- .release = osd_uld_release,
- .unlocked_ioctl = osd_uld_ioctl,
- .llseek = noop_llseek,
-};
-
-struct osd_dev *osduld_path_lookup(const char *name)
-{
- struct osd_uld_device *oud;
- struct osd_dev_handle *odh;
- struct file *file;
- int error;
-
- if (!name || !*name) {
- OSD_ERR("Mount with !path || !*path\n");
- return ERR_PTR(-EINVAL);
- }
-
- odh = kzalloc(sizeof(*odh), GFP_KERNEL);
- if (unlikely(!odh))
- return ERR_PTR(-ENOMEM);
-
- file = filp_open(name, O_RDWR, 0);
- if (IS_ERR(file)) {
- error = PTR_ERR(file);
- goto free_od;
- }
-
- if (file->f_op != &osd_fops){
- error = -EINVAL;
- goto close_file;
- }
-
- oud = file->private_data;
-
- odh->od = oud->od;
- odh->file = file;
- odh->oud = oud;
-
- return &odh->od;
-
-close_file:
- fput(file);
-free_od:
- kfree(odh);
- return ERR_PTR(error);
-}
-EXPORT_SYMBOL(osduld_path_lookup);
-
-static inline bool _the_same_or_null(const u8 *a1, unsigned a1_len,
- const u8 *a2, unsigned a2_len)
-{
- if (!a2_len) /* User string is Empty means don't care */
- return true;
-
- if (a1_len != a2_len)
- return false;
-
- return 0 == memcmp(a1, a2, a1_len);
-}
-
-static int _match_odi(struct device *dev, const void *find_data)
-{
- struct osd_uld_device *oud = container_of(dev, struct osd_uld_device,
- class_dev);
- const struct osd_dev_info *odi = find_data;
-
- if (_the_same_or_null(oud->odi.systemid, oud->odi.systemid_len,
- odi->systemid, odi->systemid_len) &&
- _the_same_or_null(oud->odi.osdname, oud->odi.osdname_len,
- odi->osdname, odi->osdname_len)) {
- OSD_DEBUG("found device sysid_len=%d osdname=%d\n",
- odi->systemid_len, odi->osdname_len);
- return 1;
- } else {
- return 0;
- }
-}
-
-/* osduld_info_lookup - Loop through all devices, return the requested osd_dev.
- *
- * if @odi->systemid_len and/or @odi->osdname_len are zero, they act as a don't
- * care. .e.g if they're both zero /dev/osd0 is returned.
- */
-struct osd_dev *osduld_info_lookup(const struct osd_dev_info *odi)
-{
- struct device *dev = class_find_device(&osd_uld_class, NULL, odi, _match_odi);
- if (likely(dev)) {
- struct osd_dev_handle *odh = kzalloc(sizeof(*odh), GFP_KERNEL);
- struct osd_uld_device *oud = container_of(dev,
- struct osd_uld_device, class_dev);
-
- if (unlikely(!odh)) {
- put_device(dev);
- return ERR_PTR(-ENOMEM);
- }
-
- odh->od = oud->od;
- odh->oud = oud;
-
- return &odh->od;
- }
-
- return ERR_PTR(-ENODEV);
-}
-EXPORT_SYMBOL(osduld_info_lookup);
-
-void osduld_put_device(struct osd_dev *od)
-{
- if (od && !IS_ERR(od)) {
- struct osd_dev_handle *odh =
- container_of(od, struct osd_dev_handle, od);
- struct osd_uld_device *oud = odh->oud;
-
- BUG_ON(od->scsi_device != oud->od.scsi_device);
-
- /* If scsi has released the device (logout), and exofs has last
- * reference on oud it will be freed by above osd_uld_release
- * within fput below. But this will oops in cdev_release which
- * is called after the fops->release. A get_/put_ pair makes
- * sure we have a cdev for the duration of fput
- */
- if (odh->file) {
- get_device(&oud->class_dev);
- fput(odh->file);
- }
- put_device(&oud->class_dev);
- kfree(odh);
- }
-}
-EXPORT_SYMBOL(osduld_put_device);
-
-const struct osd_dev_info *osduld_device_info(struct osd_dev *od)
-{
- struct osd_dev_handle *odh =
- container_of(od, struct osd_dev_handle, od);
- return &odh->oud->odi;
-}
-EXPORT_SYMBOL(osduld_device_info);
-
-bool osduld_device_same(struct osd_dev *od, const struct osd_dev_info *odi)
-{
- struct osd_dev_handle *odh =
- container_of(od, struct osd_dev_handle, od);
- struct osd_uld_device *oud = odh->oud;
-
- return (oud->odi.systemid_len == odi->systemid_len) &&
- _the_same_or_null(oud->odi.systemid, oud->odi.systemid_len,
- odi->systemid, odi->systemid_len) &&
- (oud->odi.osdname_len == odi->osdname_len) &&
- _the_same_or_null(oud->odi.osdname, oud->odi.osdname_len,
- odi->osdname, odi->osdname_len);
-}
-EXPORT_SYMBOL(osduld_device_same);
-
-/*
- * Scsi Device operations
- */
-
-static int __detect_osd(struct osd_uld_device *oud)
-{
- struct scsi_device *scsi_device = oud->od.scsi_device;
- struct scsi_sense_hdr sense_hdr;
- char caps[OSD_CAP_LEN];
- int error;
-
- /* sending a test_unit_ready as first command seems to be needed
- * by some targets
- */
- OSD_DEBUG("start scsi_test_unit_ready %p %p %p\n",
- oud, scsi_device, scsi_device->request_queue);
- error = scsi_test_unit_ready(scsi_device, 10*HZ, 5, &sense_hdr);
- if (error)
- OSD_ERR("warning: scsi_test_unit_ready failed\n");
-
- osd_sec_init_nosec_doall_caps(caps, &osd_root_object, false, true);
- if (osd_auto_detect_ver(&oud->od, caps, &oud->odi))
- return -ENODEV;
-
- return 0;
-}
-
-static void __remove(struct device *dev)
-{
- struct osd_uld_device *oud = container_of(dev, struct osd_uld_device,
- class_dev);
- struct scsi_device *scsi_device = oud->od.scsi_device;
-
- kfree(oud->odi.osdname);
-
- osd_dev_fini(&oud->od);
- scsi_device_put(scsi_device);
-
- OSD_INFO("osd_remove %s\n",
- oud->disk ? oud->disk->disk_name : NULL);
-
- if (oud->disk)
- put_disk(oud->disk);
-
- kfree(oud);
-}
-
-static int osd_probe(struct device *dev)
-{
- struct scsi_device *scsi_device = to_scsi_device(dev);
- struct gendisk *disk;
- struct osd_uld_device *oud;
- int minor;
- int error;
-
- if (scsi_device->type != TYPE_OSD)
- return -ENODEV;
-
- minor = ida_alloc_max(&osd_minor_ida, SCSI_OSD_MAX_MINOR, GFP_KERNEL);
- if (minor == -ENOSPC)
- return -EBUSY;
- if (minor < 0)
- return -ENODEV;
-
- error = -ENOMEM;
- oud = kzalloc(sizeof(*oud), GFP_KERNEL);
- if (NULL == oud)
- goto err_retract_minor;
-
- /* class device member */
- device_initialize(&oud->class_dev);
- dev_set_drvdata(dev, oud);
- oud->minor = minor;
- oud->class_dev.devt = MKDEV(SCSI_OSD_MAJOR, oud->minor);
- oud->class_dev.class = &osd_uld_class;
- oud->class_dev.parent = dev;
- oud->class_dev.release = __remove;
-
- /* hold one more reference to the scsi_device that will get released
- * in __release, in case a logout is happening while fs is mounted
- */
- if (scsi_device_get(scsi_device))
- goto err_retract_minor;
- osd_dev_init(&oud->od, scsi_device);
-
- /* allocate a disk and set it up */
- /* FIXME: do we need this since sg has already done that */
- disk = alloc_disk(1);
- if (!disk) {
- OSD_ERR("alloc_disk failed\n");
- goto err_free_osd;
- }
- disk->major = SCSI_OSD_MAJOR;
- disk->first_minor = oud->minor;
- sprintf(disk->disk_name, "osd%d", oud->minor);
- oud->disk = disk;
-
- /* Detect the OSD Version */
- error = __detect_osd(oud);
- if (error) {
- OSD_ERR("osd detection failed, non-compatible OSD device\n");
- goto err_free_osd;
- }
-
- /* init the char-device for communication with user-mode */
- cdev_init(&oud->cdev, &osd_fops);
- oud->cdev.owner = THIS_MODULE;
-
- error = dev_set_name(&oud->class_dev, "%s", disk->disk_name);
- if (error) {
- OSD_ERR("dev_set_name failed => %d\n", error);
- goto err_free_osd;
- }
-
- error = cdev_device_add(&oud->cdev, &oud->class_dev);
- if (error) {
- OSD_ERR("device_register failed => %d\n", error);
- goto err_free_osd;
- }
-
- OSD_INFO("osd_probe %s\n", disk->disk_name);
- return 0;
-
-err_free_osd:
- put_device(&oud->class_dev);
-err_retract_minor:
- ida_free(&osd_minor_ida, minor);
- return error;
-}
-
-static int osd_remove(struct device *dev)
-{
- struct scsi_device *scsi_device = to_scsi_device(dev);
- struct osd_uld_device *oud = dev_get_drvdata(dev);
-
- if (oud->od.scsi_device != scsi_device) {
- OSD_ERR("Half cooked osd-device %p, || %p!=%p",
- dev, oud->od.scsi_device, scsi_device);
- }
-
- cdev_device_del(&oud->cdev, &oud->class_dev);
- ida_free(&osd_minor_ida, oud->minor);
- put_device(&oud->class_dev);
-
- return 0;
-}
-
-/*
- * Global driver and scsi registration
- */
-
-static struct scsi_driver osd_driver = {
- .gendrv = {
- .name = osd_name,
- .owner = THIS_MODULE,
- .probe = osd_probe,
- .remove = osd_remove,
- }
-};
-
-static int __init osd_uld_init(void)
-{
- int err;
-
- err = class_register(&osd_uld_class);
- if (err) {
- OSD_ERR("Unable to register sysfs class => %d\n", err);
- return err;
- }
-
- err = register_chrdev_region(MKDEV(SCSI_OSD_MAJOR, 0),
- SCSI_OSD_MAX_MINOR, osd_name);
- if (err) {
- OSD_ERR("Unable to register major %d for osd ULD => %d\n",
- SCSI_OSD_MAJOR, err);
- goto err_out;
- }
-
- err = scsi_register_driver(&osd_driver.gendrv);
- if (err) {
- OSD_ERR("scsi_register_driver failed => %d\n", err);
- goto err_out_chrdev;
- }
-
- OSD_INFO("LOADED %s\n", osd_version_string);
- return 0;
-
-err_out_chrdev:
- unregister_chrdev_region(MKDEV(SCSI_OSD_MAJOR, 0), SCSI_OSD_MAX_MINOR);
-err_out:
- class_unregister(&osd_uld_class);
- return err;
-}
-
-static void __exit osd_uld_exit(void)
-{
- scsi_unregister_driver(&osd_driver.gendrv);
- unregister_chrdev_region(MKDEV(SCSI_OSD_MAJOR, 0), SCSI_OSD_MAX_MINOR);
- class_unregister(&osd_uld_class);
- OSD_INFO("UNLOADED %s\n", osd_version_string);
-}
-
-module_init(osd_uld_init);
-module_exit(osd_uld_exit);
diff --git a/drivers/scsi/osst.c b/drivers/scsi/osst.c
index 664c1238a87f..be3c73ebbfde 100644
--- a/drivers/scsi/osst.c
+++ b/drivers/scsi/osst.c
@@ -139,7 +139,7 @@ static int debugging = 1;
#define OSST_TIMEOUT (200 * HZ)
#define OSST_LONG_TIMEOUT (1800 * HZ)
-#define TAPE_NR(x) (iminor(x) & ~(-1 << ST_MODE_SHIFT))
+#define TAPE_NR(x) (iminor(x) & ((1 << ST_MODE_SHIFT)-1))
#define TAPE_MODE(x) ((iminor(x) & ST_MODE_MASK) >> ST_MODE_SHIFT)
#define TAPE_REWIND(x) ((iminor(x) & 0x80) == 0)
#define TAPE_IS_RAW(x) (TAPE_MODE(x) & (ST_NBR_MODES >> 1))
diff --git a/drivers/scsi/pcmcia/Makefile b/drivers/scsi/pcmcia/Makefile
index faa87a4b2d2b..a5a24dd44e7e 100644
--- a/drivers/scsi/pcmcia/Makefile
+++ b/drivers/scsi/pcmcia/Makefile
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
-ccflags-y := -Idrivers/scsi
+ccflags-y := -I $(srctree)/drivers/scsi
# 16-bit client drivers
obj-$(CONFIG_PCMCIA_QLOGIC) += qlogic_cs.o
diff --git a/drivers/scsi/pcmcia/nsp_cs.c b/drivers/scsi/pcmcia/nsp_cs.c
index 1bd6825a4f14..a81748e6e8fb 100644
--- a/drivers/scsi/pcmcia/nsp_cs.c
+++ b/drivers/scsi/pcmcia/nsp_cs.c
@@ -1134,7 +1134,8 @@ static irqreturn_t nspintr(int irq, void *dev_id)
//*sync_neg = SYNC_NOT_YET;
- if ((tmpSC->SCp.Message == MSG_COMMAND_COMPLETE)) { /* all command complete and return status */
+ /* all command complete and return status */
+ if (tmpSC->SCp.Message == MSG_COMMAND_COMPLETE) {
tmpSC->result = (DID_OK << 16) |
((tmpSC->SCp.Message & 0xff) << 8) |
((tmpSC->SCp.Status & 0xff) << 0);
diff --git a/drivers/scsi/qedf/qedf_debugfs.c b/drivers/scsi/qedf/qedf_debugfs.c
index c29c162a494f..a32d8ee4666e 100644
--- a/drivers/scsi/qedf/qedf_debugfs.c
+++ b/drivers/scsi/qedf/qedf_debugfs.c
@@ -27,30 +27,19 @@ qedf_dbg_host_init(struct qedf_dbg_ctx *qedf,
const struct file_operations *fops)
{
char host_dirname[32];
- struct dentry *file_dentry = NULL;
QEDF_INFO(qedf, QEDF_LOG_DEBUGFS, "Creating debugfs host node\n");
/* create pf dir */
sprintf(host_dirname, "host%u", qedf->host_no);
qedf->bdf_dentry = debugfs_create_dir(host_dirname, qedf_dbg_root);
- if (!qedf->bdf_dentry)
- return;
/* create debugfs files */
while (dops) {
if (!(dops->name))
break;
- file_dentry = debugfs_create_file(dops->name, 0600,
- qedf->bdf_dentry, qedf,
- fops);
- if (!file_dentry) {
- QEDF_INFO(qedf, QEDF_LOG_DEBUGFS,
- "Debugfs entry %s creation failed\n",
- dops->name);
- debugfs_remove_recursive(qedf->bdf_dentry);
- return;
- }
+ debugfs_create_file(dops->name, 0600, qedf->bdf_dentry, qedf,
+ fops);
dops++;
fops++;
}
@@ -80,9 +69,6 @@ qedf_dbg_init(char *drv_name)
/* create qed dir in root of debugfs. NULL means debugfs root */
qedf_dbg_root = debugfs_create_dir(drv_name, NULL);
- if (!qedf_dbg_root)
- QEDF_INFO(NULL, QEDF_LOG_DEBUGFS, "Init of debugfs "
- "failed\n");
}
/**
diff --git a/drivers/scsi/qedf/qedf_io.c b/drivers/scsi/qedf/qedf_io.c
index 6bbc38b1b465..6ca583bdde23 100644
--- a/drivers/scsi/qedf/qedf_io.c
+++ b/drivers/scsi/qedf/qedf_io.c
@@ -1128,12 +1128,6 @@ void qedf_scsi_completion(struct qedf_ctx *qedf, struct fcoe_cqe *cqe,
return;
}
- if (!sc_cmd->request->special) {
- QEDF_WARN(&(qedf->dbg_ctx), "request->special is NULL so "
- "request not valid, sc_cmd=%p.\n", sc_cmd);
- return;
- }
-
if (!sc_cmd->request->q) {
QEDF_WARN(&(qedf->dbg_ctx), "request->q is NULL so request "
"is not valid, sc_cmd=%p.\n", sc_cmd);
diff --git a/drivers/scsi/qedf/qedf_main.c b/drivers/scsi/qedf/qedf_main.c
index 9bbc19fc190b..9f9431a4cc0e 100644
--- a/drivers/scsi/qedf/qedf_main.c
+++ b/drivers/scsi/qedf/qedf_main.c
@@ -1418,7 +1418,7 @@ static struct libfc_function_template qedf_lport_template = {
static void qedf_fcoe_ctlr_setup(struct qedf_ctx *qedf)
{
- fcoe_ctlr_init(&qedf->ctlr, FIP_ST_AUTO);
+ fcoe_ctlr_init(&qedf->ctlr, FIP_MODE_AUTO);
qedf->ctlr.send = qedf_fip_send;
qedf->ctlr.get_src_addr = qedf_get_src_mac;
diff --git a/drivers/scsi/qedi/qedi_debugfs.c b/drivers/scsi/qedi/qedi_debugfs.c
index fd914ca4149a..5667e4752e2e 100644
--- a/drivers/scsi/qedi/qedi_debugfs.c
+++ b/drivers/scsi/qedi/qedi_debugfs.c
@@ -23,27 +23,16 @@ qedi_dbg_host_init(struct qedi_dbg_ctx *qedi,
const struct file_operations *fops)
{
char host_dirname[32];
- struct dentry *file_dentry = NULL;
sprintf(host_dirname, "host%u", qedi->host_no);
qedi->bdf_dentry = debugfs_create_dir(host_dirname, qedi_dbg_root);
- if (!qedi->bdf_dentry)
- return;
while (dops) {
if (!(dops->name))
break;
- file_dentry = debugfs_create_file(dops->name, 0600,
- qedi->bdf_dentry, qedi,
- fops);
- if (!file_dentry) {
- QEDI_INFO(qedi, QEDI_LOG_DEBUGFS,
- "Debugfs entry %s creation failed\n",
- dops->name);
- debugfs_remove_recursive(qedi->bdf_dentry);
- return;
- }
+ debugfs_create_file(dops->name, 0600, qedi->bdf_dentry, qedi,
+ fops);
dops++;
fops++;
}
@@ -60,8 +49,6 @@ void
qedi_dbg_init(char *drv_name)
{
qedi_dbg_root = debugfs_create_dir(drv_name, NULL);
- if (!qedi_dbg_root)
- QEDI_INFO(NULL, QEDI_LOG_DEBUGFS, "Init of debugfs failed\n");
}
void
diff --git a/drivers/scsi/qedi/qedi_fw.c b/drivers/scsi/qedi/qedi_fw.c
index 25d763ae5d5a..e2a995a6e8e7 100644
--- a/drivers/scsi/qedi/qedi_fw.c
+++ b/drivers/scsi/qedi/qedi_fw.c
@@ -616,13 +616,6 @@ static void qedi_scsi_completion(struct qedi_ctx *qedi,
goto error;
}
- if (!sc_cmd->request->special) {
- QEDI_WARN(&qedi->dbg_ctx,
- "request->special is NULL so request not valid, sc_cmd=%p.\n",
- sc_cmd);
- goto error;
- }
-
if (!sc_cmd->request->q) {
QEDI_WARN(&qedi->dbg_ctx,
"request->q is NULL so request is not valid, sc_cmd=%p.\n",
diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c
index ac504a1ff0ff..f928c4d3a1ef 100644
--- a/drivers/scsi/qla2xxx/qla_attr.c
+++ b/drivers/scsi/qla2xxx/qla_attr.c
@@ -543,6 +543,9 @@ qla2x00_sysfs_write_vpd(struct file *filp, struct kobject *kobj,
if (unlikely(pci_channel_offline(ha->pdev)))
return 0;
+ if (qla2x00_chip_is_down(vha))
+ return 0;
+
if (!capable(CAP_SYS_ADMIN) || off != 0 || count != ha->vpd_size ||
!ha->isp_ops->write_nvram)
return 0;
@@ -1002,7 +1005,7 @@ qla2x00_free_sysfs_attr(scsi_qla_host_t *vha, bool stop_beacon)
/* Scsi_Host attributes. */
static ssize_t
-qla2x00_drvr_version_show(struct device *dev,
+qla2x00_driver_version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return scnprintf(buf, PAGE_SIZE, "%s\n", qla2x00_version_str);
@@ -1632,6 +1635,94 @@ qla2x00_max_speed_sup_show(struct device *dev, struct device_attribute *attr,
ha->max_speed_sup ? "32Gps" : "16Gps");
}
+static ssize_t
+qla2x00_port_speed_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct scsi_qla_host *vha = shost_priv(dev_to_shost(dev));
+ ulong type, speed;
+ int oldspeed, rval;
+ int mode = QLA_SET_DATA_RATE_LR;
+ struct qla_hw_data *ha = vha->hw;
+
+ if (!IS_QLA27XX(vha->hw)) {
+ ql_log(ql_log_warn, vha, 0x70d8,
+ "Speed setting not supported \n");
+ return -EINVAL;
+ }
+
+ rval = kstrtol(buf, 10, &type);
+ if (rval)
+ return rval;
+ speed = type;
+ if (type == 40 || type == 80 || type == 160 ||
+ type == 320) {
+ ql_dbg(ql_dbg_user, vha, 0x70d9,
+ "Setting will be affected after a loss of sync\n");
+ type = type/10;
+ mode = QLA_SET_DATA_RATE_NOLR;
+ }
+
+ oldspeed = ha->set_data_rate;
+
+ switch (type) {
+ case 0:
+ ha->set_data_rate = PORT_SPEED_AUTO;
+ break;
+ case 4:
+ ha->set_data_rate = PORT_SPEED_4GB;
+ break;
+ case 8:
+ ha->set_data_rate = PORT_SPEED_8GB;
+ break;
+ case 16:
+ ha->set_data_rate = PORT_SPEED_16GB;
+ break;
+ case 32:
+ ha->set_data_rate = PORT_SPEED_32GB;
+ break;
+ default:
+ ql_log(ql_log_warn, vha, 0x1199,
+ "Unrecognized speed setting:%lx. Setting Autoneg\n",
+ speed);
+ ha->set_data_rate = PORT_SPEED_AUTO;
+ }
+
+ if (qla2x00_chip_is_down(vha) || (oldspeed == ha->set_data_rate))
+ return -EINVAL;
+
+ ql_log(ql_log_info, vha, 0x70da,
+ "Setting speed to %lx Gbps \n", type);
+
+ rval = qla2x00_set_data_rate(vha, mode);
+ if (rval != QLA_SUCCESS)
+ return -EIO;
+
+ return strlen(buf);
+}
+
+static ssize_t
+qla2x00_port_speed_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct scsi_qla_host *vha = shost_priv(dev_to_shost(dev));
+ struct qla_hw_data *ha = vha->hw;
+ ssize_t rval;
+ char *spd[7] = {"0", "0", "0", "4", "8", "16", "32"};
+
+ rval = qla2x00_get_data_rate(vha);
+ if (rval != QLA_SUCCESS) {
+ ql_log(ql_log_warn, vha, 0x70db,
+ "Unable to get port speed rval:%zd\n", rval);
+ return -EINVAL;
+ }
+
+ ql_log(ql_log_info, vha, 0x70d6,
+ "port speed:%d\n", ha->link_data_rate);
+
+ return scnprintf(buf, PAGE_SIZE, "%s\n", spd[ha->link_data_rate]);
+}
+
/* ----- */
static ssize_t
@@ -2059,7 +2150,21 @@ ql2xiniexchg_store(struct device *dev, struct device_attribute *attr,
return strlen(buf);
}
-static DEVICE_ATTR(driver_version, S_IRUGO, qla2x00_drvr_version_show, NULL);
+static ssize_t
+qla2x00_dif_bundle_statistics_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+ struct qla_hw_data *ha = vha->hw;
+
+ return scnprintf(buf, PAGE_SIZE,
+ "cross=%llu read=%llu write=%llu kalloc=%llu dma_alloc=%llu unusable=%u\n",
+ ha->dif_bundle_crossed_pages, ha->dif_bundle_reads,
+ ha->dif_bundle_writes, ha->dif_bundle_kallocs,
+ ha->dif_bundle_dma_allocs, ha->pool.unusable.count);
+}
+
+static DEVICE_ATTR(driver_version, S_IRUGO, qla2x00_driver_version_show, NULL);
static DEVICE_ATTR(fw_version, S_IRUGO, qla2x00_fw_version_show, NULL);
static DEVICE_ATTR(serial_num, S_IRUGO, qla2x00_serial_num_show, NULL);
static DEVICE_ATTR(isp_name, S_IRUGO, qla2x00_isp_name_show, NULL);
@@ -2112,6 +2217,10 @@ static DEVICE_ATTR(zio_threshold, 0644,
static DEVICE_ATTR_RW(qlini_mode);
static DEVICE_ATTR_RW(ql2xexchoffld);
static DEVICE_ATTR_RW(ql2xiniexchg);
+static DEVICE_ATTR(dif_bundle_statistics, 0444,
+ qla2x00_dif_bundle_statistics_show, NULL);
+static DEVICE_ATTR(port_speed, 0644, qla2x00_port_speed_show,
+ qla2x00_port_speed_store);
struct device_attribute *qla2x00_host_attrs[] = {
@@ -2150,6 +2259,8 @@ struct device_attribute *qla2x00_host_attrs[] = {
&dev_attr_min_link_speed,
&dev_attr_max_speed_sup,
&dev_attr_zio_threshold,
+ &dev_attr_dif_bundle_statistics,
+ &dev_attr_port_speed,
NULL, /* reserve for qlini_mode */
NULL, /* reserve for ql2xiniexchg */
NULL, /* reserve for ql2xexchoffld */
diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h
index d1fc4958222a..3d46975a5e5c 100644
--- a/drivers/scsi/qla2xxx/qla_def.h
+++ b/drivers/scsi/qla2xxx/qla_def.h
@@ -314,6 +314,7 @@ struct srb_cmd {
#define SRB_CRC_PROT_DMA_VALID BIT_4 /* DIF: prot DMA valid */
#define SRB_CRC_CTX_DSD_VALID BIT_5 /* DIF: dsd_list valid */
#define SRB_WAKEUP_ON_COMP BIT_6
+#define SRB_DIF_BUNDL_DMA_VALID BIT_7 /* DIF: DMA list valid */
/* To identify if a srb is of T10-CRC type. @sp => srb_t pointer */
#define IS_PROT_IO(sp) (sp->flags & SRB_CRC_CTX_DSD_VALID)
@@ -1892,6 +1893,13 @@ struct crc_context {
/* List of DMA context transfers */
struct list_head dsd_list;
+ /* List of DIF Bundling context DMA address */
+ struct list_head ldif_dsd_list;
+ u8 no_ldif_dsd;
+
+ struct list_head ldif_dma_hndl_list;
+ u32 dif_bundl_len;
+ u8 no_dif_bundl;
/* This structure should not exceed 512 bytes */
};
@@ -2359,7 +2367,9 @@ typedef struct fc_port {
#define NVME_PRLI_SP_INITIATOR BIT_5
#define NVME_PRLI_SP_TARGET BIT_4
#define NVME_PRLI_SP_DISCOVERY BIT_3
+#define NVME_PRLI_SP_FIRST_BURST BIT_0
uint8_t nvme_flag;
+ uint32_t nvme_first_burst_size;
#define NVME_FLAG_REGISTERED 4
#define NVME_FLAG_DELETING 2
#define NVME_FLAG_RESETTING 1
@@ -3688,12 +3698,14 @@ struct qla_hw_data {
#define PORT_SPEED_UNKNOWN 0xFFFF
#define PORT_SPEED_1GB 0x00
#define PORT_SPEED_2GB 0x01
+#define PORT_SPEED_AUTO 0x02
#define PORT_SPEED_4GB 0x03
#define PORT_SPEED_8GB 0x04
#define PORT_SPEED_16GB 0x05
#define PORT_SPEED_32GB 0x06
#define PORT_SPEED_10GB 0x13
uint16_t link_data_rate; /* F/W operating speed */
+ uint16_t set_data_rate; /* Set by user */
uint8_t current_topology;
uint8_t prev_topology;
@@ -3958,6 +3970,10 @@ struct qla_hw_data {
uint16_t fw_subminor_version;
uint16_t fw_attributes;
uint16_t fw_attributes_h;
+#define FW_ATTR_H_NVME_FBURST BIT_1
+#define FW_ATTR_H_NVME BIT_10
+#define FW_ATTR_H_NVME_UPDATED BIT_14
+
uint16_t fw_attributes_ext[2];
uint32_t fw_memory_size;
uint32_t fw_transfer_size;
@@ -4184,12 +4200,32 @@ struct qla_hw_data {
uint16_t min_link_speed;
uint16_t max_speed_sup;
+ /* DMA pool for the DIF bundling buffers */
+ struct dma_pool *dif_bundl_pool;
+ #define DIF_BUNDLING_DMA_POOL_SIZE 1024
+ struct {
+ struct {
+ struct list_head head;
+ uint count;
+ } good;
+ struct {
+ struct list_head head;
+ uint count;
+ } unusable;
+ } pool;
+
+ unsigned long long dif_bundle_crossed_pages;
+ unsigned long long dif_bundle_reads;
+ unsigned long long dif_bundle_writes;
+ unsigned long long dif_bundle_kallocs;
+ unsigned long long dif_bundle_dma_allocs;
+
atomic_t nvme_active_aen_cnt;
uint16_t nvme_last_rptd_aen; /* Last recorded aen count */
atomic_t zio_threshold;
uint16_t last_zio_threshold;
-#define DEFAULT_ZIO_THRESHOLD 64
+#define DEFAULT_ZIO_THRESHOLD 5
};
#define FW_ABILITY_MAX_SPEED_MASK 0xFUL
@@ -4198,6 +4234,10 @@ struct qla_hw_data {
#define FW_ABILITY_MAX_SPEED(ha) \
(ha->fw_ability_mask & FW_ABILITY_MAX_SPEED_MASK)
+#define QLA_GET_DATA_RATE 0
+#define QLA_SET_DATA_RATE_NOLR 1
+#define QLA_SET_DATA_RATE_LR 2 /* Set speed and initiate LR */
+
/*
* Qlogic scsi host structure
*/
@@ -4229,6 +4269,7 @@ typedef struct scsi_qla_host {
uint32_t qpairs_req_created:1;
uint32_t qpairs_rsp_created:1;
uint32_t nvme_enabled:1;
+ uint32_t nvme_first_burst:1;
} flags;
atomic_t loop_state;
diff --git a/drivers/scsi/qla2xxx/qla_dfs.c b/drivers/scsi/qla2xxx/qla_dfs.c
index 0b190082aa8d..5819a45ac5ef 100644
--- a/drivers/scsi/qla2xxx/qla_dfs.c
+++ b/drivers/scsi/qla2xxx/qla_dfs.c
@@ -193,6 +193,8 @@ qla_dfs_tgt_counters_show(struct seq_file *s, void *unused)
for (i = 0; i < vha->hw->max_qpairs; i++) {
qpair = vha->hw->queue_pair_map[i];
+ if (!qpair)
+ continue;
qla_core_sbt_cmd += qpair->tgt_counters.qla_core_sbt_cmd;
core_qla_que_buf += qpair->tgt_counters.core_qla_que_buf;
qla_core_ret_ctio += qpair->tgt_counters.qla_core_ret_ctio;
@@ -446,11 +448,6 @@ qla2x00_dfs_setup(scsi_qla_host_t *vha)
atomic_set(&qla2x00_dfs_root_count, 0);
qla2x00_dfs_root = debugfs_create_dir(QLA2XXX_DRIVER_NAME, NULL);
- if (!qla2x00_dfs_root) {
- ql_log(ql_log_warn, vha, 0x00f7,
- "Unable to create debugfs root directory.\n");
- goto out;
- }
create_dir:
if (ha->dfs_dir)
@@ -458,64 +455,28 @@ create_dir:
mutex_init(&ha->fce_mutex);
ha->dfs_dir = debugfs_create_dir(vha->host_str, qla2x00_dfs_root);
- if (!ha->dfs_dir) {
- ql_log(ql_log_warn, vha, 0x00f8,
- "Unable to create debugfs ha directory.\n");
- goto out;
- }
atomic_inc(&qla2x00_dfs_root_count);
create_nodes:
ha->dfs_fw_resource_cnt = debugfs_create_file("fw_resource_count",
S_IRUSR, ha->dfs_dir, vha, &dfs_fw_resource_cnt_ops);
- if (!ha->dfs_fw_resource_cnt) {
- ql_log(ql_log_warn, vha, 0x00fd,
- "Unable to create debugFS fw_resource_count node.\n");
- goto out;
- }
ha->dfs_tgt_counters = debugfs_create_file("tgt_counters", S_IRUSR,
ha->dfs_dir, vha, &dfs_tgt_counters_ops);
- if (!ha->dfs_tgt_counters) {
- ql_log(ql_log_warn, vha, 0xd301,
- "Unable to create debugFS tgt_counters node.\n");
- goto out;
- }
ha->tgt.dfs_tgt_port_database = debugfs_create_file("tgt_port_database",
S_IRUSR, ha->dfs_dir, vha, &dfs_tgt_port_database_ops);
- if (!ha->tgt.dfs_tgt_port_database) {
- ql_log(ql_log_warn, vha, 0xd03f,
- "Unable to create debugFS tgt_port_database node.\n");
- goto out;
- }
ha->dfs_fce = debugfs_create_file("fce", S_IRUSR, ha->dfs_dir, vha,
&dfs_fce_ops);
- if (!ha->dfs_fce) {
- ql_log(ql_log_warn, vha, 0x00f9,
- "Unable to create debugfs fce node.\n");
- goto out;
- }
ha->tgt.dfs_tgt_sess = debugfs_create_file("tgt_sess",
S_IRUSR, ha->dfs_dir, vha, &dfs_tgt_sess_ops);
- if (!ha->tgt.dfs_tgt_sess) {
- ql_log(ql_log_warn, vha, 0xd040,
- "Unable to create debugFS tgt_sess node.\n");
- goto out;
- }
- if (IS_QLA27XX(ha) || IS_QLA83XX(ha)) {
+ if (IS_QLA27XX(ha) || IS_QLA83XX(ha))
ha->tgt.dfs_naqp = debugfs_create_file("naqp",
0400, ha->dfs_dir, vha, &dfs_naqp_ops);
- if (!ha->tgt.dfs_naqp) {
- ql_log(ql_log_warn, vha, 0xd011,
- "Unable to create debugFS naqp node.\n");
- goto out;
- }
- }
out:
return 0;
}
diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h
index 3673fcdb033a..4eefe69ca807 100644
--- a/drivers/scsi/qla2xxx/qla_gbl.h
+++ b/drivers/scsi/qla2xxx/qla_gbl.h
@@ -160,6 +160,7 @@ extern int ql2xautodetectsfp;
extern int ql2xenablemsix;
extern int qla2xuseresexchforels;
extern int ql2xexlogins;
+extern int ql2xdifbundlinginternalbuffers;
extern int qla2x00_loop_reset(scsi_qla_host_t *);
extern void qla2x00_abort_all_cmds(scsi_qla_host_t *, int);
@@ -269,8 +270,8 @@ extern void qla24xx_build_scsi_iocbs(srb_t *, struct cmd_type_7 *,
uint16_t, struct req_que *);
extern int qla2x00_start_scsi(srb_t *sp);
extern int qla24xx_start_scsi(srb_t *sp);
-int qla2x00_marker(struct scsi_qla_host *, struct req_que *, struct rsp_que *,
- uint16_t, uint64_t, uint8_t);
+int qla2x00_marker(struct scsi_qla_host *, struct qla_qpair *,
+ uint16_t, uint64_t, uint8_t);
extern int qla2x00_start_sp(srb_t *);
extern int qla24xx_dif_start_scsi(srb_t *);
extern int qla2x00_start_bidir(srb_t *, struct scsi_qla_host *, uint32_t);
@@ -285,7 +286,7 @@ extern int qla24xx_walk_and_build_sglist_no_difb(struct qla_hw_data *, srb_t *,
extern int qla24xx_walk_and_build_sglist(struct qla_hw_data *, srb_t *,
uint32_t *, uint16_t, struct qla_tc_param *);
extern int qla24xx_walk_and_build_prot_sglist(struct qla_hw_data *, srb_t *,
- uint32_t *, uint16_t, struct qla_tc_param *);
+ uint32_t *, uint16_t, struct qla_tgt_cmd *);
extern int qla24xx_get_one_block_sg(uint32_t, struct qla2_sgx *, uint32_t *);
extern int qla24xx_configure_prot_mode(srb_t *, uint16_t *);
extern int qla24xx_build_scsi_crc_2_iocbs(srb_t *,
@@ -898,5 +899,6 @@ void qlt_update_host_map(struct scsi_qla_host *, port_id_t);
void qlt_remove_target_resources(struct qla_hw_data *);
void qlt_clr_qp_table(struct scsi_qla_host *vha);
void qlt_set_mode(struct scsi_qla_host *);
+int qla2x00_set_data_rate(scsi_qla_host_t *vha, uint16_t mode);
#endif /* _QLA_GBL_H */
diff --git a/drivers/scsi/qla2xxx/qla_gs.c b/drivers/scsi/qla2xxx/qla_gs.c
index cbc3bc49d4d1..c6fdad12428e 100644
--- a/drivers/scsi/qla2xxx/qla_gs.c
+++ b/drivers/scsi/qla2xxx/qla_gs.c
@@ -657,15 +657,16 @@ static int qla_async_rftid(scsi_qla_host_t *vha, port_id_t *d_id)
sp->u.iocb_cmd.timeout = qla2x00_async_iocb_timeout;
sp->done = qla2x00_async_sns_sp_done;
+ ql_dbg(ql_dbg_disc, vha, 0xffff,
+ "Async-%s - hdl=%x portid %06x.\n",
+ sp->name, sp->handle, d_id->b24);
+
rval = qla2x00_start_sp(sp);
if (rval != QLA_SUCCESS) {
ql_dbg(ql_dbg_disc, vha, 0x2043,
"RFT_ID issue IOCB failed (%d).\n", rval);
goto done_free_sp;
}
- ql_dbg(ql_dbg_disc, vha, 0xffff,
- "Async-%s - hdl=%x portid %06x.\n",
- sp->name, sp->handle, d_id->b24);
return rval;
done_free_sp:
sp->free(sp);
@@ -752,6 +753,10 @@ static int qla_async_rffid(scsi_qla_host_t *vha, port_id_t *d_id,
sp->u.iocb_cmd.timeout = qla2x00_async_iocb_timeout;
sp->done = qla2x00_async_sns_sp_done;
+ ql_dbg(ql_dbg_disc, vha, 0xffff,
+ "Async-%s - hdl=%x portid %06x feature %x type %x.\n",
+ sp->name, sp->handle, d_id->b24, fc4feature, fc4type);
+
rval = qla2x00_start_sp(sp);
if (rval != QLA_SUCCESS) {
ql_dbg(ql_dbg_disc, vha, 0x2047,
@@ -759,9 +764,6 @@ static int qla_async_rffid(scsi_qla_host_t *vha, port_id_t *d_id,
goto done_free_sp;
}
- ql_dbg(ql_dbg_disc, vha, 0xffff,
- "Async-%s - hdl=%x portid %06x feature %x type %x.\n",
- sp->name, sp->handle, d_id->b24, fc4feature, fc4type);
return rval;
done_free_sp:
@@ -844,15 +846,16 @@ static int qla_async_rnnid(scsi_qla_host_t *vha, port_id_t *d_id,
sp->u.iocb_cmd.timeout = qla2x00_async_iocb_timeout;
sp->done = qla2x00_async_sns_sp_done;
+ ql_dbg(ql_dbg_disc, vha, 0xffff,
+ "Async-%s - hdl=%x portid %06x\n",
+ sp->name, sp->handle, d_id->b24);
+
rval = qla2x00_start_sp(sp);
if (rval != QLA_SUCCESS) {
ql_dbg(ql_dbg_disc, vha, 0x204d,
"RNN_ID issue IOCB failed (%d).\n", rval);
goto done_free_sp;
}
- ql_dbg(ql_dbg_disc, vha, 0xffff,
- "Async-%s - hdl=%x portid %06x\n",
- sp->name, sp->handle, d_id->b24);
return rval;
@@ -957,15 +960,16 @@ static int qla_async_rsnn_nn(scsi_qla_host_t *vha)
sp->u.iocb_cmd.timeout = qla2x00_async_iocb_timeout;
sp->done = qla2x00_async_sns_sp_done;
+ ql_dbg(ql_dbg_disc, vha, 0xffff,
+ "Async-%s - hdl=%x.\n",
+ sp->name, sp->handle);
+
rval = qla2x00_start_sp(sp);
if (rval != QLA_SUCCESS) {
ql_dbg(ql_dbg_disc, vha, 0x2043,
"RFT_ID issue IOCB failed (%d).\n", rval);
goto done_free_sp;
}
- ql_dbg(ql_dbg_disc, vha, 0xffff,
- "Async-%s - hdl=%x.\n",
- sp->name, sp->handle);
return rval;
@@ -3578,14 +3582,14 @@ int qla24xx_async_gffid(scsi_qla_host_t *vha, fc_port_t *fcport)
sp->done = qla24xx_async_gffid_sp_done;
- rval = qla2x00_start_sp(sp);
- if (rval != QLA_SUCCESS)
- goto done_free_sp;
-
ql_dbg(ql_dbg_disc, vha, 0x2132,
"Async-%s hdl=%x %8phC.\n", sp->name,
sp->handle, fcport->port_name);
+ rval = qla2x00_start_sp(sp);
+ if (rval != QLA_SUCCESS)
+ goto done_free_sp;
+
return rval;
done_free_sp:
sp->free(sp);
@@ -4067,6 +4071,10 @@ static int qla24xx_async_gnnft(scsi_qla_host_t *vha, struct srb *sp,
sp->done = qla2x00_async_gpnft_gnnft_sp_done;
+ ql_dbg(ql_dbg_disc, vha, 0xffff,
+ "Async-%s hdl=%x FC4Type %x.\n", sp->name,
+ sp->handle, ct_req->req.gpn_ft.port_type);
+
rval = qla2x00_start_sp(sp);
if (rval != QLA_SUCCESS) {
spin_lock_irqsave(&vha->work_lock, flags);
@@ -4075,9 +4083,6 @@ static int qla24xx_async_gnnft(scsi_qla_host_t *vha, struct srb *sp,
goto done_free_sp;
}
- ql_dbg(ql_dbg_disc, vha, 0xffff,
- "Async-%s hdl=%x FC4Type %x.\n", sp->name,
- sp->handle, ct_req->req.gpn_ft.port_type);
return rval;
done_free_sp:
@@ -4158,7 +4163,8 @@ int qla24xx_async_gpnft(scsi_qla_host_t *vha, u8 fc4_type, srb_t *sp)
spin_lock_irqsave(&vha->work_lock, flags);
vha->scan.scan_flags &= ~SF_SCANNING;
spin_unlock_irqrestore(&vha->work_lock, flags);
- goto done_free_sp;
+ qla2x00_rel_sp(sp);
+ return rval;
}
sp->u.iocb_cmd.u.ctarg.req_size = GPN_FT_REQ_SIZE;
@@ -4177,7 +4183,13 @@ int qla24xx_async_gpnft(scsi_qla_host_t *vha, u8 fc4_type, srb_t *sp)
spin_lock_irqsave(&vha->work_lock, flags);
vha->scan.scan_flags &= ~SF_SCANNING;
spin_unlock_irqrestore(&vha->work_lock, flags);
- goto done_free_sp;
+ dma_free_coherent(&vha->hw->pdev->dev,
+ sp->u.iocb_cmd.u.ctarg.req_allocated_size,
+ sp->u.iocb_cmd.u.ctarg.req,
+ sp->u.iocb_cmd.u.ctarg.req_dma);
+ sp->u.iocb_cmd.u.ctarg.req = NULL;
+ qla2x00_rel_sp(sp);
+ return rval;
}
sp->u.iocb_cmd.u.ctarg.rsp_size = rspsz;
@@ -4214,6 +4226,10 @@ int qla24xx_async_gpnft(scsi_qla_host_t *vha, u8 fc4_type, srb_t *sp)
sp->done = qla2x00_async_gpnft_gnnft_sp_done;
+ ql_dbg(ql_dbg_disc, vha, 0xffff,
+ "Async-%s hdl=%x FC4Type %x.\n", sp->name,
+ sp->handle, ct_req->req.gpn_ft.port_type);
+
rval = qla2x00_start_sp(sp);
if (rval != QLA_SUCCESS) {
spin_lock_irqsave(&vha->work_lock, flags);
@@ -4222,9 +4238,6 @@ int qla24xx_async_gpnft(scsi_qla_host_t *vha, u8 fc4_type, srb_t *sp)
goto done_free_sp;
}
- ql_dbg(ql_dbg_disc, vha, 0xffff,
- "Async-%s hdl=%x FC4Type %x.\n", sp->name,
- sp->handle, ct_req->req.gpn_ft.port_type);
return rval;
done_free_sp:
@@ -4345,13 +4358,14 @@ int qla24xx_async_gnnid(scsi_qla_host_t *vha, fc_port_t *fcport)
sp->done = qla2x00_async_gnnid_sp_done;
- rval = qla2x00_start_sp(sp);
- if (rval != QLA_SUCCESS)
- goto done_free_sp;
ql_dbg(ql_dbg_disc, vha, 0xffff,
"Async-%s - %8phC hdl=%x loopid=%x portid %06x.\n",
sp->name, fcport->port_name,
sp->handle, fcport->loop_id, fcport->d_id.b24);
+
+ rval = qla2x00_start_sp(sp);
+ if (rval != QLA_SUCCESS)
+ goto done_free_sp;
return rval;
done_free_sp:
@@ -4475,14 +4489,15 @@ int qla24xx_async_gfpnid(scsi_qla_host_t *vha, fc_port_t *fcport)
sp->done = qla2x00_async_gfpnid_sp_done;
- rval = qla2x00_start_sp(sp);
- if (rval != QLA_SUCCESS)
- goto done_free_sp;
-
ql_dbg(ql_dbg_disc, vha, 0xffff,
"Async-%s - %8phC hdl=%x loopid=%x portid %06x.\n",
sp->name, fcport->port_name,
sp->handle, fcport->loop_id, fcport->d_id.b24);
+
+ rval = qla2x00_start_sp(sp);
+ if (rval != QLA_SUCCESS)
+ goto done_free_sp;
+
return rval;
done_free_sp:
diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
index 8d1acc802a67..0c700b140ce7 100644
--- a/drivers/scsi/qla2xxx/qla_init.c
+++ b/drivers/scsi/qla2xxx/qla_init.c
@@ -366,14 +366,16 @@ qla2x00_async_prlo(struct scsi_qla_host *vha, fc_port_t *fcport)
qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
sp->done = qla2x00_async_prlo_sp_done;
- rval = qla2x00_start_sp(sp);
- if (rval != QLA_SUCCESS)
- goto done_free_sp;
ql_dbg(ql_dbg_disc, vha, 0x2070,
"Async-prlo - hdl=%x loop-id=%x portid=%02x%02x%02x.\n",
sp->handle, fcport->loop_id, fcport->d_id.b.domain,
fcport->d_id.b.area, fcport->d_id.b.al_pa);
+
+ rval = qla2x00_start_sp(sp);
+ if (rval != QLA_SUCCESS)
+ goto done_free_sp;
+
return rval;
done_free_sp:
@@ -471,9 +473,11 @@ qla2x00_async_adisc(struct scsi_qla_host *vha, fc_port_t *fcport,
{
srb_t *sp;
struct srb_iocb *lio;
- int rval;
+ int rval = QLA_FUNCTION_FAILED;
+
+ if (!vha->flags.online || (fcport->flags & FCF_ASYNC_SENT))
+ return rval;
- rval = QLA_FUNCTION_FAILED;
fcport->flags |= FCF_ASYNC_SENT;
sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL);
if (!sp)
@@ -644,11 +648,14 @@ static void qla24xx_handle_gnl_done_event(scsi_qla_host_t *vha,
break;
case DSC_LS_PORT_UNAVAIL:
default:
- if (fcport->loop_id != FC_NO_LOOP_ID)
- qla2x00_clear_loop_id(fcport);
-
- fcport->loop_id = loop_id;
- fcport->fw_login_state = DSC_LS_PORT_UNAVAIL;
+ if (fcport->loop_id == FC_NO_LOOP_ID) {
+ qla2x00_find_new_loop_id(vha, fcport);
+ fcport->fw_login_state =
+ DSC_LS_PORT_UNAVAIL;
+ }
+ ql_dbg(ql_dbg_disc, vha, 0x20e5,
+ "%s %d %8phC\n", __func__, __LINE__,
+ fcport->port_name);
qla24xx_fcport_handle_login(vha, fcport);
break;
}
@@ -931,14 +938,14 @@ int qla24xx_async_gnl(struct scsi_qla_host *vha, fc_port_t *fcport)
sp->done = qla24xx_async_gnl_sp_done;
- rval = qla2x00_start_sp(sp);
- if (rval != QLA_SUCCESS)
- goto done_free_sp;
-
ql_dbg(ql_dbg_disc, vha, 0x20da,
"Async-%s - OUT WWPN %8phC hndl %x\n",
sp->name, fcport->port_name, sp->handle);
+ rval = qla2x00_start_sp(sp);
+ if (rval != QLA_SUCCESS)
+ goto done_free_sp;
+
return rval;
done_free_sp:
@@ -1072,6 +1079,11 @@ qla24xx_async_prli(struct scsi_qla_host *vha, fc_port_t *fcport)
if (fcport->fc4f_nvme)
lio->u.logio.flags |= SRB_LOGIN_NVME_PRLI;
+ ql_dbg(ql_dbg_disc, vha, 0x211b,
+ "Async-prli - %8phC hdl=%x, loopid=%x portid=%06x retries=%d %s.\n",
+ fcport->port_name, sp->handle, fcport->loop_id, fcport->d_id.b24,
+ fcport->login_retry, fcport->fc4f_nvme ? "nvme" : "fc");
+
rval = qla2x00_start_sp(sp);
if (rval != QLA_SUCCESS) {
fcport->flags |= FCF_LOGIN_NEEDED;
@@ -1079,11 +1091,6 @@ qla24xx_async_prli(struct scsi_qla_host *vha, fc_port_t *fcport)
goto done_free_sp;
}
- ql_dbg(ql_dbg_disc, vha, 0x211b,
- "Async-prli - %8phC hdl=%x, loopid=%x portid=%06x retries=%d %s.\n",
- fcport->port_name, sp->handle, fcport->loop_id, fcport->d_id.b24,
- fcport->login_retry, fcport->fc4f_nvme ? "nvme" : "fc");
-
return rval;
done_free_sp:
@@ -1471,29 +1478,6 @@ int qla24xx_fcport_handle_login(struct scsi_qla_host *vha, fc_port_t *fcport)
return 0;
}
-static
-void qla24xx_handle_rscn_event(fc_port_t *fcport, struct event_arg *ea)
-{
- fcport->rscn_gen++;
-
- ql_dbg(ql_dbg_disc, fcport->vha, 0x210c,
- "%s %8phC DS %d LS %d\n",
- __func__, fcport->port_name, fcport->disc_state,
- fcport->fw_login_state);
-
- if (fcport->flags & FCF_ASYNC_SENT)
- return;
-
- switch (fcport->disc_state) {
- case DSC_DELETED:
- case DSC_LOGIN_COMPLETE:
- qla24xx_post_gpnid_work(fcport->vha, &ea->id);
- break;
- default:
- break;
- }
-}
-
int qla24xx_post_newsess_work(struct scsi_qla_host *vha, port_id_t *id,
u8 *port_name, u8 *node_name, void *pla, u8 fc4_type)
{
@@ -1560,8 +1544,6 @@ static void qla_handle_els_plogi_done(scsi_qla_host_t *vha,
void qla2x00_fcport_event_handler(scsi_qla_host_t *vha, struct event_arg *ea)
{
- fc_port_t *f, *tf;
- uint32_t id = 0, mask, rid;
fc_port_t *fcport;
switch (ea->event) {
@@ -1574,10 +1556,6 @@ void qla2x00_fcport_event_handler(scsi_qla_host_t *vha, struct event_arg *ea)
case FCME_RSCN:
if (test_bit(UNLOADING, &vha->dpc_flags))
return;
- switch (ea->id.b.rsvd_1) {
- case RSCN_PORT_ADDR:
-#define BIGSCAN 1
-#if defined BIGSCAN & BIGSCAN > 0
{
unsigned long flags;
fcport = qla2x00_find_fcport_by_nportid
@@ -1596,59 +1574,6 @@ void qla2x00_fcport_event_handler(scsi_qla_host_t *vha, struct event_arg *ea)
}
spin_unlock_irqrestore(&vha->work_lock, flags);
}
-#else
- {
- int rc;
- fcport = qla2x00_find_fcport_by_nportid(vha, &ea->id, 1);
- if (!fcport) {
- /* cable moved */
- rc = qla24xx_post_gpnid_work(vha, &ea->id);
- if (rc) {
- ql_log(ql_log_warn, vha, 0xd044,
- "RSCN GPNID work failed %06x\n",
- ea->id.b24);
- }
- } else {
- ea->fcport = fcport;
- fcport->scan_needed = 1;
- qla24xx_handle_rscn_event(fcport, ea);
- }
- }
-#endif
- break;
- case RSCN_AREA_ADDR:
- case RSCN_DOM_ADDR:
- if (ea->id.b.rsvd_1 == RSCN_AREA_ADDR) {
- mask = 0xffff00;
- ql_dbg(ql_dbg_async, vha, 0x5044,
- "RSCN: Area 0x%06x was affected\n",
- ea->id.b24);
- } else {
- mask = 0xff0000;
- ql_dbg(ql_dbg_async, vha, 0x507a,
- "RSCN: Domain 0x%06x was affected\n",
- ea->id.b24);
- }
-
- rid = ea->id.b24 & mask;
- list_for_each_entry_safe(f, tf, &vha->vp_fcports,
- list) {
- id = f->d_id.b24 & mask;
- if (rid == id) {
- ea->fcport = f;
- qla24xx_handle_rscn_event(f, ea);
- }
- }
- break;
- case RSCN_FAB_ADDR:
- default:
- ql_log(ql_log_warn, vha, 0xd045,
- "RSCN: Fabric was affected. Addr format %d\n",
- ea->id.b.rsvd_1);
- qla2x00_mark_all_devices_lost(vha, 1);
- set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
- set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags);
- }
break;
case FCME_GNL_DONE:
qla24xx_handle_gnl_done_event(vha, ea);
@@ -1709,11 +1634,7 @@ void qla_rscn_replay(fc_port_t *fcport)
ea.event = FCME_RSCN;
ea.id = fcport->d_id;
ea.id.b.rsvd_1 = RSCN_PORT_ADDR;
-#if defined BIGSCAN & BIGSCAN > 0
qla2x00_fcport_event_handler(fcport->vha, &ea);
-#else
- qla24xx_post_gpnid_work(fcport->vha, &ea.id);
-#endif
}
}
@@ -1784,8 +1705,8 @@ qla2x00_async_tm_cmd(fc_port_t *fcport, uint32_t flags, uint32_t lun,
lun = (uint16_t)tm_iocb->u.tmf.lun;
/* Issue Marker IOCB */
- qla2x00_marker(vha, vha->hw->req_q_map[0],
- vha->hw->rsp_q_map[0], fcport->loop_id, lun,
+ qla2x00_marker(vha, vha->hw->base_qpair,
+ fcport->loop_id, lun,
flags == TCF_LUN_RESET ? MK_SYNC_ID_LUN : MK_SYNC_ID);
}
@@ -1829,7 +1750,7 @@ qla24xx_async_abort_cmd(srb_t *cmd_sp, bool wait)
int rval = QLA_FUNCTION_FAILED;
sp = qla2xxx_get_qpair_sp(cmd_sp->vha, cmd_sp->qpair, cmd_sp->fcport,
- GFP_KERNEL);
+ GFP_ATOMIC);
if (!sp)
goto done;
@@ -1912,6 +1833,12 @@ qla24xx_handle_prli_done_event(struct scsi_qla_host *vha, struct event_arg *ea)
ea->fcport->chip_reset = vha->hw->base_qpair->chip_reset;
ea->fcport->logout_on_delete = 1;
+ ea->fcport->nvme_prli_service_param = ea->iop[0];
+ if (ea->iop[0] & NVME_PRLI_SP_FIRST_BURST)
+ ea->fcport->nvme_first_burst_size =
+ (ea->iop[1] & 0xffff) * 512;
+ else
+ ea->fcport->nvme_first_burst_size = 0;
qla24xx_post_gpdb_work(vha, ea->fcport, 0);
break;
default:
@@ -3955,8 +3882,17 @@ qla24xx_config_rings(struct scsi_qla_host *vha)
WRT_REG_DWORD(&reg->isp24.rsp_q_in, 0);
WRT_REG_DWORD(&reg->isp24.rsp_q_out, 0);
}
+
qlt_24xx_config_rings(vha);
+ /* If the user has configured the speed, set it here */
+ if (ha->set_data_rate) {
+ ql_dbg(ql_dbg_init, vha, 0x00fd,
+ "Speed set by user : %s Gbps \n",
+ qla2x00_get_link_speed_str(ha, ha->set_data_rate));
+ icb->firmware_options_3 = (ha->set_data_rate << 13);
+ }
+
/* PCI posting */
RD_REG_DWORD(&ioreg->hccr);
}
@@ -4755,6 +4691,16 @@ qla2x00_alloc_fcport(scsi_qla_host_t *vha, gfp_t flags)
if (!fcport)
return NULL;
+ fcport->ct_desc.ct_sns = dma_alloc_coherent(&vha->hw->pdev->dev,
+ sizeof(struct ct_sns_pkt), &fcport->ct_desc.ct_sns_dma,
+ flags);
+ if (!fcport->ct_desc.ct_sns) {
+ ql_log(ql_log_warn, vha, 0xd049,
+ "Failed to allocate ct_sns request.\n");
+ kfree(fcport);
+ return NULL;
+ }
+
/* Setup fcport template structure. */
fcport->vha = vha;
fcport->port_type = FCT_UNKNOWN;
@@ -4763,13 +4709,11 @@ qla2x00_alloc_fcport(scsi_qla_host_t *vha, gfp_t flags)
fcport->supported_classes = FC_COS_UNSPECIFIED;
fcport->fp_speed = PORT_SPEED_UNKNOWN;
- fcport->ct_desc.ct_sns = dma_alloc_coherent(&vha->hw->pdev->dev,
- sizeof(struct ct_sns_pkt), &fcport->ct_desc.ct_sns_dma,
- flags);
fcport->disc_state = DSC_DELETED;
fcport->fw_login_state = DSC_LS_PORT_UNAVAIL;
fcport->deleted = QLA_SESS_DELETED;
fcport->login_retry = vha->hw->login_retry_count;
+ fcport->chip_reset = vha->hw->base_qpair->chip_reset;
fcport->logout_on_delete = 1;
if (!fcport->ct_desc.ct_sns) {
@@ -4778,6 +4722,7 @@ qla2x00_alloc_fcport(scsi_qla_host_t *vha, gfp_t flags)
kfree(fcport);
fcport = NULL;
}
+
INIT_WORK(&fcport->del_work, qla24xx_delete_sess_fn);
INIT_WORK(&fcport->reg_work, qla_register_fcport_fn);
INIT_LIST_HEAD(&fcport->gnl_entry);
@@ -5047,10 +4992,12 @@ qla2x00_configure_local_loop(scsi_qla_host_t *vha)
continue;
/* Bypass if not same domain and area of adapter. */
- if (area && domain &&
- (area != vha->d_id.b.area || domain != vha->d_id.b.domain))
+ if (area && domain && ((area != vha->d_id.b.area) ||
+ (domain != vha->d_id.b.domain)) &&
+ (ha->current_topology == ISP_CFG_NL))
continue;
+
/* Bypass invalid local loop ID. */
if (loop_id > LAST_LOCAL_LOOP_ID)
continue;
@@ -6101,11 +6048,6 @@ qla2x00_loop_resync(scsi_qla_host_t *vha)
{
int rval = QLA_SUCCESS;
uint32_t wait_time;
- struct req_que *req;
- struct rsp_que *rsp;
-
- req = vha->req;
- rsp = req->rsp;
clear_bit(ISP_ABORT_RETRY, &vha->dpc_flags);
if (vha->flags.online) {
@@ -6118,8 +6060,8 @@ qla2x00_loop_resync(scsi_qla_host_t *vha)
* Issue a marker after FW becomes
* ready.
*/
- qla2x00_marker(vha, req, rsp, 0, 0,
- MK_SYNC_ALL);
+ qla2x00_marker(vha, vha->hw->base_qpair,
+ 0, 0, MK_SYNC_ALL);
vha->marker_needed = 0;
}
@@ -6857,8 +6799,6 @@ qla2x00_restart_isp(scsi_qla_host_t *vha)
{
int status = 0;
struct qla_hw_data *ha = vha->hw;
- struct req_que *req = ha->req_q_map[0];
- struct rsp_que *rsp = ha->rsp_q_map[0];
/* If firmware needs to be loaded */
if (qla2x00_isp_firmware(vha)) {
@@ -6878,7 +6818,7 @@ qla2x00_restart_isp(scsi_qla_host_t *vha)
status = qla2x00_fw_ready(vha);
if (!status) {
/* Issue a marker after FW becomes ready. */
- qla2x00_marker(vha, req, rsp, 0, 0, MK_SYNC_ALL);
+ qla2x00_marker(vha, ha->base_qpair, 0, 0, MK_SYNC_ALL);
set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
}
@@ -7933,22 +7873,15 @@ qla24xx_configure_vhba(scsi_qla_host_t *vha)
uint16_t mb[MAILBOX_REGISTER_COUNT];
struct qla_hw_data *ha = vha->hw;
struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
- struct req_que *req;
- struct rsp_que *rsp;
if (!vha->vp_idx)
return -EINVAL;
rval = qla2x00_fw_ready(base_vha);
- if (vha->qpair)
- req = vha->qpair->req;
- else
- req = ha->req_q_map[0];
- rsp = req->rsp;
if (rval == QLA_SUCCESS) {
clear_bit(RESET_MARKER_NEEDED, &vha->dpc_flags);
- qla2x00_marker(vha, req, rsp, 0, 0, MK_SYNC_ALL);
+ qla2x00_marker(vha, ha->base_qpair, 0, 0, MK_SYNC_ALL);
}
vha->flags.management_server_logged_in = 0;
@@ -8340,8 +8273,6 @@ qla82xx_restart_isp(scsi_qla_host_t *vha)
{
int status, rval;
struct qla_hw_data *ha = vha->hw;
- struct req_que *req = ha->req_q_map[0];
- struct rsp_que *rsp = ha->rsp_q_map[0];
struct scsi_qla_host *vp;
unsigned long flags;
@@ -8353,7 +8284,7 @@ qla82xx_restart_isp(scsi_qla_host_t *vha)
status = qla2x00_fw_ready(vha);
if (!status) {
/* Issue a marker after FW becomes ready. */
- qla2x00_marker(vha, req, rsp, 0, 0, MK_SYNC_ALL);
+ qla2x00_marker(vha, ha->base_qpair, 0, 0, MK_SYNC_ALL);
vha->flags.online = 1;
set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
}
diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c
index 032635321ad6..456a41d2e2c6 100644
--- a/drivers/scsi/qla2xxx/qla_iocb.c
+++ b/drivers/scsi/qla2xxx/qla_iocb.c
@@ -336,7 +336,7 @@ qla2x00_start_scsi(srb_t *sp)
/* Send marker if required */
if (vha->marker_needed != 0) {
- if (qla2x00_marker(vha, req, rsp, 0, 0, MK_SYNC_ALL) !=
+ if (qla2x00_marker(vha, ha->base_qpair, 0, 0, MK_SYNC_ALL) !=
QLA_SUCCESS) {
return (QLA_FUNCTION_FAILED);
}
@@ -490,8 +490,7 @@ qla2x00_start_iocbs(struct scsi_qla_host *vha, struct req_que *req)
/**
* qla2x00_marker() - Send a marker IOCB to the firmware.
* @vha: HA context
- * @req: request queue
- * @rsp: response queue
+ * @qpair: queue pair pointer
* @loop_id: loop ID
* @lun: LUN
* @type: marker modifier
@@ -501,18 +500,16 @@ qla2x00_start_iocbs(struct scsi_qla_host *vha, struct req_que *req)
* Returns non-zero if a failure occurred, else zero.
*/
static int
-__qla2x00_marker(struct scsi_qla_host *vha, struct req_que *req,
- struct rsp_que *rsp, uint16_t loop_id,
- uint64_t lun, uint8_t type)
+__qla2x00_marker(struct scsi_qla_host *vha, struct qla_qpair *qpair,
+ uint16_t loop_id, uint64_t lun, uint8_t type)
{
mrk_entry_t *mrk;
struct mrk_entry_24xx *mrk24 = NULL;
-
+ struct req_que *req = qpair->req;
struct qla_hw_data *ha = vha->hw;
scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev);
- req = ha->req_q_map[0];
- mrk = (mrk_entry_t *)qla2x00_alloc_iocbs(vha, NULL);
+ mrk = (mrk_entry_t *)__qla2x00_alloc_iocbs(qpair, NULL);
if (mrk == NULL) {
ql_log(ql_log_warn, base_vha, 0x3026,
"Failed to allocate Marker IOCB.\n");
@@ -543,16 +540,15 @@ __qla2x00_marker(struct scsi_qla_host *vha, struct req_que *req,
}
int
-qla2x00_marker(struct scsi_qla_host *vha, struct req_que *req,
- struct rsp_que *rsp, uint16_t loop_id, uint64_t lun,
- uint8_t type)
+qla2x00_marker(struct scsi_qla_host *vha, struct qla_qpair *qpair,
+ uint16_t loop_id, uint64_t lun, uint8_t type)
{
int ret;
unsigned long flags = 0;
- spin_lock_irqsave(&vha->hw->hardware_lock, flags);
- ret = __qla2x00_marker(vha, req, rsp, loop_id, lun, type);
- spin_unlock_irqrestore(&vha->hw->hardware_lock, flags);
+ spin_lock_irqsave(qpair->qp_lock_ptr, flags);
+ ret = __qla2x00_marker(vha, qpair, loop_id, lun, type);
+ spin_unlock_irqrestore(qpair->qp_lock_ptr, flags);
return (ret);
}
@@ -567,11 +563,11 @@ qla2x00_marker(struct scsi_qla_host *vha, struct req_que *req,
int qla2x00_issue_marker(scsi_qla_host_t *vha, int ha_locked)
{
if (ha_locked) {
- if (__qla2x00_marker(vha, vha->req, vha->req->rsp, 0, 0,
+ if (__qla2x00_marker(vha, vha->hw->base_qpair, 0, 0,
MK_SYNC_ALL) != QLA_SUCCESS)
return QLA_FUNCTION_FAILED;
} else {
- if (qla2x00_marker(vha, vha->req, vha->req->rsp, 0, 0,
+ if (qla2x00_marker(vha, vha->hw->base_qpair, 0, 0,
MK_SYNC_ALL) != QLA_SUCCESS)
return QLA_FUNCTION_FAILED;
}
@@ -1098,88 +1094,300 @@ qla24xx_walk_and_build_sglist(struct qla_hw_data *ha, srb_t *sp, uint32_t *dsd,
int
qla24xx_walk_and_build_prot_sglist(struct qla_hw_data *ha, srb_t *sp,
- uint32_t *dsd, uint16_t tot_dsds, struct qla_tc_param *tc)
+ uint32_t *cur_dsd, uint16_t tot_dsds, struct qla_tgt_cmd *tc)
{
- void *next_dsd;
- uint8_t avail_dsds = 0;
- uint32_t dsd_list_len;
- struct dsd_dma *dsd_ptr;
+ struct dsd_dma *dsd_ptr = NULL, *dif_dsd, *nxt_dsd;
struct scatterlist *sg, *sgl;
- int i;
- struct scsi_cmnd *cmd;
- uint32_t *cur_dsd = dsd;
- uint16_t used_dsds = tot_dsds;
+ struct crc_context *difctx = NULL;
struct scsi_qla_host *vha;
+ uint dsd_list_len;
+ uint avail_dsds = 0;
+ uint used_dsds = tot_dsds;
+ bool dif_local_dma_alloc = false;
+ bool direction_to_device = false;
+ int i;
if (sp) {
- cmd = GET_CMD_SP(sp);
+ struct scsi_cmnd *cmd = GET_CMD_SP(sp);
sgl = scsi_prot_sglist(cmd);
vha = sp->vha;
+ difctx = sp->u.scmd.ctx;
+ direction_to_device = cmd->sc_data_direction == DMA_TO_DEVICE;
+ ql_dbg(ql_dbg_tgt + ql_dbg_verbose, vha, 0xe021,
+ "%s: scsi_cmnd: %p, crc_ctx: %p, sp: %p\n",
+ __func__, cmd, difctx, sp);
} else if (tc) {
vha = tc->vha;
sgl = tc->prot_sg;
+ difctx = tc->ctx;
+ direction_to_device = tc->dma_data_direction == DMA_TO_DEVICE;
} else {
BUG();
return 1;
}
- ql_dbg(ql_dbg_tgt, vha, 0xe021,
- "%s: enter\n", __func__);
-
- for_each_sg(sgl, sg, tot_dsds, i) {
- dma_addr_t sle_dma;
-
- /* Allocate additional continuation packets? */
- if (avail_dsds == 0) {
- avail_dsds = (used_dsds > QLA_DSDS_PER_IOCB) ?
- QLA_DSDS_PER_IOCB : used_dsds;
- dsd_list_len = (avail_dsds + 1) * 12;
- used_dsds -= avail_dsds;
-
- /* allocate tracking DS */
- dsd_ptr = kzalloc(sizeof(struct dsd_dma), GFP_ATOMIC);
- if (!dsd_ptr)
- return 1;
-
- /* allocate new list */
- dsd_ptr->dsd_addr = next_dsd =
- dma_pool_alloc(ha->dl_dma_pool, GFP_ATOMIC,
- &dsd_ptr->dsd_list_dma);
-
- if (!next_dsd) {
- /*
- * Need to cleanup only this dsd_ptr, rest
- * will be done by sp_free_dma()
- */
- kfree(dsd_ptr);
- return 1;
+ ql_dbg(ql_dbg_tgt + ql_dbg_verbose, vha, 0xe021,
+ "%s: enter (write=%u)\n", __func__, direction_to_device);
+
+ /* if initiator doing write or target doing read */
+ if (direction_to_device) {
+ for_each_sg(sgl, sg, tot_dsds, i) {
+ u64 sle_phys = sg_phys(sg);
+
+ /* If SGE addr + len flips bits in upper 32-bits */
+ if (MSD(sle_phys + sg->length) ^ MSD(sle_phys)) {
+ ql_dbg(ql_dbg_tgt + ql_dbg_verbose, vha, 0xe022,
+ "%s: page boundary crossing (phys=%llx len=%x)\n",
+ __func__, sle_phys, sg->length);
+
+ if (difctx) {
+ ha->dif_bundle_crossed_pages++;
+ dif_local_dma_alloc = true;
+ } else {
+ ql_dbg(ql_dbg_tgt + ql_dbg_verbose,
+ vha, 0xe022,
+ "%s: difctx pointer is NULL\n",
+ __func__);
+ }
+ break;
}
+ }
+ ha->dif_bundle_writes++;
+ } else {
+ ha->dif_bundle_reads++;
+ }
+
+ if (ql2xdifbundlinginternalbuffers)
+ dif_local_dma_alloc = direction_to_device;
+
+ if (dif_local_dma_alloc) {
+ u32 track_difbundl_buf = 0;
+ u32 ldma_sg_len = 0;
+ u8 ldma_needed = 1;
+
+ difctx->no_dif_bundl = 0;
+ difctx->dif_bundl_len = 0;
+
+ /* Track DSD buffers */
+ INIT_LIST_HEAD(&difctx->ldif_dsd_list);
+ /* Track local DMA buffers */
+ INIT_LIST_HEAD(&difctx->ldif_dma_hndl_list);
+
+ for_each_sg(sgl, sg, tot_dsds, i) {
+ u32 sglen = sg_dma_len(sg);
+
+ ql_dbg(ql_dbg_tgt + ql_dbg_verbose, vha, 0xe023,
+ "%s: sg[%x] (phys=%llx sglen=%x) ldma_sg_len: %x dif_bundl_len: %x ldma_needed: %x\n",
+ __func__, i, (u64)sg_phys(sg), sglen, ldma_sg_len,
+ difctx->dif_bundl_len, ldma_needed);
+
+ while (sglen) {
+ u32 xfrlen = 0;
+
+ if (ldma_needed) {
+ /*
+ * Allocate list item to store
+ * the DMA buffers
+ */
+ dsd_ptr = kzalloc(sizeof(*dsd_ptr),
+ GFP_ATOMIC);
+ if (!dsd_ptr) {
+ ql_dbg(ql_dbg_tgt, vha, 0xe024,
+ "%s: failed alloc dsd_ptr\n",
+ __func__);
+ return 1;
+ }
+ ha->dif_bundle_kallocs++;
+
+ /* allocate dma buffer */
+ dsd_ptr->dsd_addr = dma_pool_alloc
+ (ha->dif_bundl_pool, GFP_ATOMIC,
+ &dsd_ptr->dsd_list_dma);
+ if (!dsd_ptr->dsd_addr) {
+ ql_dbg(ql_dbg_tgt, vha, 0xe024,
+ "%s: failed alloc ->dsd_ptr\n",
+ __func__);
+ /*
+ * need to cleanup only this
+ * dsd_ptr rest will be done
+ * by sp_free_dma()
+ */
+ kfree(dsd_ptr);
+ ha->dif_bundle_kallocs--;
+ return 1;
+ }
+ ha->dif_bundle_dma_allocs++;
+ ldma_needed = 0;
+ difctx->no_dif_bundl++;
+ list_add_tail(&dsd_ptr->list,
+ &difctx->ldif_dma_hndl_list);
+ }
+
+ /* xfrlen is min of dma pool size and sglen */
+ xfrlen = (sglen >
+ (DIF_BUNDLING_DMA_POOL_SIZE - ldma_sg_len)) ?
+ DIF_BUNDLING_DMA_POOL_SIZE - ldma_sg_len :
+ sglen;
+
+ /* replace with local allocated dma buffer */
+ sg_pcopy_to_buffer(sgl, sg_nents(sgl),
+ dsd_ptr->dsd_addr + ldma_sg_len, xfrlen,
+ difctx->dif_bundl_len);
+ difctx->dif_bundl_len += xfrlen;
+ sglen -= xfrlen;
+ ldma_sg_len += xfrlen;
+ if (ldma_sg_len == DIF_BUNDLING_DMA_POOL_SIZE ||
+ sg_is_last(sg)) {
+ ldma_needed = 1;
+ ldma_sg_len = 0;
+ }
+ }
+ }
- if (sp) {
- list_add_tail(&dsd_ptr->list,
- &((struct crc_context *)
- sp->u.scmd.ctx)->dsd_list);
+ track_difbundl_buf = used_dsds = difctx->no_dif_bundl;
+ ql_dbg(ql_dbg_tgt + ql_dbg_verbose, vha, 0xe025,
+ "dif_bundl_len=%x, no_dif_bundl=%x track_difbundl_buf: %x\n",
+ difctx->dif_bundl_len, difctx->no_dif_bundl,
+ track_difbundl_buf);
- sp->flags |= SRB_CRC_CTX_DSD_VALID;
- } else {
- list_add_tail(&dsd_ptr->list,
- &(tc->ctx->dsd_list));
- *tc->ctx_dsd_alloced = 1;
+ if (sp)
+ sp->flags |= SRB_DIF_BUNDL_DMA_VALID;
+ else
+ tc->prot_flags = DIF_BUNDL_DMA_VALID;
+
+ list_for_each_entry_safe(dif_dsd, nxt_dsd,
+ &difctx->ldif_dma_hndl_list, list) {
+ u32 sglen = (difctx->dif_bundl_len >
+ DIF_BUNDLING_DMA_POOL_SIZE) ?
+ DIF_BUNDLING_DMA_POOL_SIZE : difctx->dif_bundl_len;
+
+ BUG_ON(track_difbundl_buf == 0);
+
+ /* Allocate additional continuation packets? */
+ if (avail_dsds == 0) {
+ ql_dbg(ql_dbg_tgt + ql_dbg_verbose, vha,
+ 0xe024,
+ "%s: adding continuation iocb's\n",
+ __func__);
+ avail_dsds = (used_dsds > QLA_DSDS_PER_IOCB) ?
+ QLA_DSDS_PER_IOCB : used_dsds;
+ dsd_list_len = (avail_dsds + 1) * 12;
+ used_dsds -= avail_dsds;
+
+ /* allocate tracking DS */
+ dsd_ptr = kzalloc(sizeof(*dsd_ptr), GFP_ATOMIC);
+ if (!dsd_ptr) {
+ ql_dbg(ql_dbg_tgt, vha, 0xe026,
+ "%s: failed alloc dsd_ptr\n",
+ __func__);
+ return 1;
+ }
+ ha->dif_bundle_kallocs++;
+
+ difctx->no_ldif_dsd++;
+ /* allocate new list */
+ dsd_ptr->dsd_addr =
+ dma_pool_alloc(ha->dl_dma_pool, GFP_ATOMIC,
+ &dsd_ptr->dsd_list_dma);
+ if (!dsd_ptr->dsd_addr) {
+ ql_dbg(ql_dbg_tgt, vha, 0xe026,
+ "%s: failed alloc ->dsd_addr\n",
+ __func__);
+ /*
+ * need to cleanup only this dsd_ptr
+ * rest will be done by sp_free_dma()
+ */
+ kfree(dsd_ptr);
+ ha->dif_bundle_kallocs--;
+ return 1;
+ }
+ ha->dif_bundle_dma_allocs++;
+
+ if (sp) {
+ list_add_tail(&dsd_ptr->list,
+ &difctx->ldif_dsd_list);
+ sp->flags |= SRB_CRC_CTX_DSD_VALID;
+ } else {
+ list_add_tail(&dsd_ptr->list,
+ &difctx->ldif_dsd_list);
+ tc->ctx_dsd_alloced = 1;
+ }
+
+ /* add new list to cmd iocb or last list */
+ *cur_dsd++ =
+ cpu_to_le32(LSD(dsd_ptr->dsd_list_dma));
+ *cur_dsd++ =
+ cpu_to_le32(MSD(dsd_ptr->dsd_list_dma));
+ *cur_dsd++ = dsd_list_len;
+ cur_dsd = dsd_ptr->dsd_addr;
}
-
- /* add new list to cmd iocb or last list */
- *cur_dsd++ = cpu_to_le32(LSD(dsd_ptr->dsd_list_dma));
- *cur_dsd++ = cpu_to_le32(MSD(dsd_ptr->dsd_list_dma));
- *cur_dsd++ = dsd_list_len;
- cur_dsd = (uint32_t *)next_dsd;
+ *cur_dsd++ = cpu_to_le32(LSD(dif_dsd->dsd_list_dma));
+ *cur_dsd++ = cpu_to_le32(MSD(dif_dsd->dsd_list_dma));
+ *cur_dsd++ = cpu_to_le32(sglen);
+ avail_dsds--;
+ difctx->dif_bundl_len -= sglen;
+ track_difbundl_buf--;
}
- sle_dma = sg_dma_address(sg);
-
- *cur_dsd++ = cpu_to_le32(LSD(sle_dma));
- *cur_dsd++ = cpu_to_le32(MSD(sle_dma));
- *cur_dsd++ = cpu_to_le32(sg_dma_len(sg));
- avail_dsds--;
+ ql_dbg(ql_dbg_tgt + ql_dbg_verbose, vha, 0xe026,
+ "%s: no_ldif_dsd:%x, no_dif_bundl:%x\n", __func__,
+ difctx->no_ldif_dsd, difctx->no_dif_bundl);
+ } else {
+ for_each_sg(sgl, sg, tot_dsds, i) {
+ dma_addr_t sle_dma;
+
+ /* Allocate additional continuation packets? */
+ if (avail_dsds == 0) {
+ avail_dsds = (used_dsds > QLA_DSDS_PER_IOCB) ?
+ QLA_DSDS_PER_IOCB : used_dsds;
+ dsd_list_len = (avail_dsds + 1) * 12;
+ used_dsds -= avail_dsds;
+
+ /* allocate tracking DS */
+ dsd_ptr = kzalloc(sizeof(*dsd_ptr), GFP_ATOMIC);
+ if (!dsd_ptr) {
+ ql_dbg(ql_dbg_tgt + ql_dbg_verbose,
+ vha, 0xe027,
+ "%s: failed alloc dsd_dma...\n",
+ __func__);
+ return 1;
+ }
+
+ /* allocate new list */
+ dsd_ptr->dsd_addr =
+ dma_pool_alloc(ha->dl_dma_pool, GFP_ATOMIC,
+ &dsd_ptr->dsd_list_dma);
+ if (!dsd_ptr->dsd_addr) {
+ /* need to cleanup only this dsd_ptr */
+ /* rest will be done by sp_free_dma() */
+ kfree(dsd_ptr);
+ return 1;
+ }
+
+ if (sp) {
+ list_add_tail(&dsd_ptr->list,
+ &difctx->dsd_list);
+ sp->flags |= SRB_CRC_CTX_DSD_VALID;
+ } else {
+ list_add_tail(&dsd_ptr->list,
+ &difctx->dsd_list);
+ tc->ctx_dsd_alloced = 1;
+ }
+
+ /* add new list to cmd iocb or last list */
+ *cur_dsd++ =
+ cpu_to_le32(LSD(dsd_ptr->dsd_list_dma));
+ *cur_dsd++ =
+ cpu_to_le32(MSD(dsd_ptr->dsd_list_dma));
+ *cur_dsd++ = dsd_list_len;
+ cur_dsd = dsd_ptr->dsd_addr;
+ }
+ sle_dma = sg_dma_address(sg);
+ *cur_dsd++ = cpu_to_le32(LSD(sle_dma));
+ *cur_dsd++ = cpu_to_le32(MSD(sle_dma));
+ *cur_dsd++ = cpu_to_le32(sg_dma_len(sg));
+ avail_dsds--;
+ }
}
/* Null termination */
*cur_dsd++ = 0;
@@ -1187,7 +1395,6 @@ qla24xx_walk_and_build_prot_sglist(struct qla_hw_data *ha, srb_t *sp,
*cur_dsd++ = 0;
return 0;
}
-
/**
* qla24xx_build_scsi_crc_2_iocbs() - Build IOCB command utilizing Command
* Type 6 IOCB types.
@@ -1416,21 +1623,19 @@ qla24xx_start_scsi(srb_t *sp)
uint16_t req_cnt;
uint16_t tot_dsds;
struct req_que *req = NULL;
- struct rsp_que *rsp = NULL;
struct scsi_cmnd *cmd = GET_CMD_SP(sp);
struct scsi_qla_host *vha = sp->vha;
struct qla_hw_data *ha = vha->hw;
/* Setup device pointers. */
req = vha->req;
- rsp = req->rsp;
/* So we know we haven't pci_map'ed anything yet */
tot_dsds = 0;
/* Send marker if required */
if (vha->marker_needed != 0) {
- if (qla2x00_marker(vha, req, rsp, 0, 0, MK_SYNC_ALL) !=
+ if (qla2x00_marker(vha, ha->base_qpair, 0, 0, MK_SYNC_ALL) !=
QLA_SUCCESS)
return QLA_FUNCTION_FAILED;
vha->marker_needed = 0;
@@ -1583,7 +1788,7 @@ qla24xx_dif_start_scsi(srb_t *sp)
/* Send marker if required */
if (vha->marker_needed != 0) {
- if (qla2x00_marker(vha, req, rsp, 0, 0, MK_SYNC_ALL) !=
+ if (qla2x00_marker(vha, ha->base_qpair, 0, 0, MK_SYNC_ALL) !=
QLA_SUCCESS)
return QLA_FUNCTION_FAILED;
vha->marker_needed = 0;
@@ -1754,7 +1959,6 @@ qla2xxx_start_scsi_mq(srb_t *sp)
uint16_t req_cnt;
uint16_t tot_dsds;
struct req_que *req = NULL;
- struct rsp_que *rsp = NULL;
struct scsi_cmnd *cmd = GET_CMD_SP(sp);
struct scsi_qla_host *vha = sp->fcport->vha;
struct qla_hw_data *ha = vha->hw;
@@ -1764,7 +1968,6 @@ qla2xxx_start_scsi_mq(srb_t *sp)
spin_lock_irqsave(&qpair->qp_lock, flags);
/* Setup qpair pointers */
- rsp = qpair->rsp;
req = qpair->req;
/* So we know we haven't pci_map'ed anything yet */
@@ -1772,7 +1975,7 @@ qla2xxx_start_scsi_mq(srb_t *sp)
/* Send marker if required */
if (vha->marker_needed != 0) {
- if (__qla2x00_marker(vha, req, rsp, 0, 0, MK_SYNC_ALL) !=
+ if (__qla2x00_marker(vha, qpair, 0, 0, MK_SYNC_ALL) !=
QLA_SUCCESS) {
spin_unlock_irqrestore(&qpair->qp_lock, flags);
return QLA_FUNCTION_FAILED;
@@ -1940,7 +2143,7 @@ qla2xxx_dif_start_scsi_mq(srb_t *sp)
/* Send marker if required */
if (vha->marker_needed != 0) {
- if (__qla2x00_marker(vha, req, rsp, 0, 0, MK_SYNC_ALL) !=
+ if (__qla2x00_marker(vha, qpair, 0, 0, MK_SYNC_ALL) !=
QLA_SUCCESS) {
spin_unlock_irqrestore(&qpair->qp_lock, flags);
return QLA_FUNCTION_FAILED;
@@ -2208,8 +2411,11 @@ qla24xx_prli_iocb(srb_t *sp, struct logio_entry_24xx *logio)
logio->entry_type = LOGINOUT_PORT_IOCB_TYPE;
logio->control_flags = cpu_to_le16(LCF_COMMAND_PRLI);
- if (lio->u.logio.flags & SRB_LOGIN_NVME_PRLI)
+ if (lio->u.logio.flags & SRB_LOGIN_NVME_PRLI) {
logio->control_flags |= LCF_NVME_PRLI;
+ if (sp->vha->flags.nvme_first_burst)
+ logio->io_parameter[0] = NVME_PRLI_SP_FIRST_BURST;
+ }
logio->nport_handle = cpu_to_le16(sp->fcport->loop_id);
logio->port_id[0] = sp->fcport->d_id.b.al_pa;
@@ -2991,8 +3197,8 @@ qla82xx_start_scsi(srb_t *sp)
/* Send marker if required */
if (vha->marker_needed != 0) {
- if (qla2x00_marker(vha, req,
- rsp, 0, 0, MK_SYNC_ALL) != QLA_SUCCESS) {
+ if (qla2x00_marker(vha, ha->base_qpair,
+ 0, 0, MK_SYNC_ALL) != QLA_SUCCESS) {
ql_log(ql_log_warn, vha, 0x300c,
"qla2x00_marker failed for cmd=%p.\n", cmd);
return QLA_FUNCTION_FAILED;
@@ -3434,23 +3640,22 @@ qla24xx_prlo_iocb(srb_t *sp, struct logio_entry_24xx *logio)
int
qla2x00_start_sp(srb_t *sp)
{
- int rval;
+ int rval = QLA_SUCCESS;
scsi_qla_host_t *vha = sp->vha;
struct qla_hw_data *ha = vha->hw;
struct qla_qpair *qp = sp->qpair;
void *pkt;
unsigned long flags;
- rval = QLA_FUNCTION_FAILED;
spin_lock_irqsave(qp->qp_lock_ptr, flags);
pkt = __qla2x00_alloc_iocbs(sp->qpair, sp);
if (!pkt) {
+ rval = EAGAIN;
ql_log(ql_log_warn, vha, 0x700c,
"qla2x00_alloc_iocbs failed.\n");
goto done;
}
- rval = QLA_SUCCESS;
switch (sp->type) {
case SRB_LOGIN_CMD:
IS_FWI2_CAPABLE(ha) ?
@@ -3646,8 +3851,8 @@ qla2x00_start_bidir(srb_t *sp, struct scsi_qla_host *vha, uint32_t tot_dsds)
/* Send marker if required */
if (vha->marker_needed != 0) {
- if (qla2x00_marker(vha, req,
- rsp, 0, 0, MK_SYNC_ALL) != QLA_SUCCESS)
+ if (qla2x00_marker(vha, ha->base_qpair,
+ 0, 0, MK_SYNC_ALL) != QLA_SUCCESS)
return EXT_STATUS_MAILBOX;
vha->marker_needed = 0;
}
diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
index 8507c43b918c..69bbea9239cc 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -834,7 +834,8 @@ skip_rio:
* Restore for Physical Port only
*/
if (!vha->vp_idx) {
- if (ha->flags.fawwpn_enabled) {
+ if (ha->flags.fawwpn_enabled &&
+ (ha->current_topology == ISP_CFG_F)) {
void *wwpn = ha->init_cb->port_name;
memcpy(vha->port_name, wwpn, WWN_SIZE);
fc_host_port_name(vha->host) =
@@ -1714,6 +1715,15 @@ qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req,
vha->hw->exch_starvation = 0;
data[0] = MBS_COMMAND_COMPLETE;
+
+ if (sp->type == SRB_PRLI_CMD) {
+ lio->u.logio.iop[0] =
+ le32_to_cpu(logio->io_parameter[0]);
+ lio->u.logio.iop[1] =
+ le32_to_cpu(logio->io_parameter[1]);
+ goto logio_done;
+ }
+
if (sp->type != SRB_LOGIN_CMD)
goto logio_done;
@@ -2725,6 +2735,17 @@ check_scsi_status:
cp->device->vendor);
break;
+ case CS_DMA:
+ ql_log(ql_log_info, fcport->vha, 0x3022,
+ "CS_DMA error: 0x%x-0x%x (0x%x) nexus=%ld:%d:%llu portid=%06x oxid=0x%x cdb=%10phN len=0x%x rsp_info=0x%x resid=0x%x fw_resid=0x%x sp=%p cp=%p.\n",
+ comp_status, scsi_status, res, vha->host_no,
+ cp->device->id, cp->device->lun, fcport->d_id.b24,
+ ox_id, cp->cmnd, scsi_bufflen(cp), rsp_info_len,
+ resid_len, fw_resid_len, sp, cp);
+ ql_dump_buffer(ql_dbg_tgt + ql_dbg_verbose, vha, 0xe0ee,
+ pkt, sizeof(*sts24));
+ res = DID_ERROR << 16;
+ break;
default:
res = DID_ERROR << 16;
break;
@@ -3410,7 +3431,7 @@ qla24xx_enable_msix(struct qla_hw_data *ha, struct rsp_que *rsp)
min_vecs++;
}
- if (USER_CTRL_IRQ(ha)) {
+ if (USER_CTRL_IRQ(ha) || !ha->mqiobase) {
/* user wants to control IRQ setting for target mode */
ret = pci_alloc_irq_vectors(ha->pdev, min_vecs,
ha->msix_count, PCI_IRQ_MSIX);
diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index 191b6b7c8747..5400696e1f6b 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -1109,7 +1109,12 @@ qla2x00_get_fw_version(scsi_qla_host_t *vha)
* FW supports nvme and driver load parameter requested nvme.
* BIT 26 of fw_attributes indicates NVMe support.
*/
- if ((ha->fw_attributes_h & 0x400) && ql2xnvmeenable) {
+ if ((ha->fw_attributes_h &
+ (FW_ATTR_H_NVME | FW_ATTR_H_NVME_UPDATED)) &&
+ ql2xnvmeenable) {
+ if (ha->fw_attributes_h & FW_ATTR_H_NVME_FBURST)
+ vha->flags.nvme_first_burst = 1;
+
vha->flags.nvme_enabled = 1;
ql_log(ql_log_info, vha, 0xd302,
"%s: FC-NVMe is Enabled (0x%x)\n",
@@ -1508,16 +1513,12 @@ qla2x00_abort_target(struct fc_port *fcport, uint64_t l, int tag)
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
scsi_qla_host_t *vha;
- struct req_que *req;
- struct rsp_que *rsp;
vha = fcport->vha;
ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x103e,
"Entered %s.\n", __func__);
- req = vha->hw->req_q_map[0];
- rsp = req->rsp;
mcp->mb[0] = MBC_ABORT_TARGET;
mcp->out_mb = MBX_9|MBX_2|MBX_1|MBX_0;
if (HAS_EXTENDED_IDS(vha->hw)) {
@@ -1540,7 +1541,7 @@ qla2x00_abort_target(struct fc_port *fcport, uint64_t l, int tag)
}
/* Issue marker IOCB. */
- rval2 = qla2x00_marker(vha, req, rsp, fcport->loop_id, 0,
+ rval2 = qla2x00_marker(vha, vha->hw->base_qpair, fcport->loop_id, 0,
MK_SYNC_ID);
if (rval2 != QLA_SUCCESS) {
ql_dbg(ql_dbg_mbx, vha, 0x1040,
@@ -1560,16 +1561,12 @@ qla2x00_lun_reset(struct fc_port *fcport, uint64_t l, int tag)
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
scsi_qla_host_t *vha;
- struct req_que *req;
- struct rsp_que *rsp;
vha = fcport->vha;
ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1042,
"Entered %s.\n", __func__);
- req = vha->hw->req_q_map[0];
- rsp = req->rsp;
mcp->mb[0] = MBC_LUN_RESET;
mcp->out_mb = MBX_9|MBX_3|MBX_2|MBX_1|MBX_0;
if (HAS_EXTENDED_IDS(vha->hw))
@@ -1589,7 +1586,7 @@ qla2x00_lun_reset(struct fc_port *fcport, uint64_t l, int tag)
}
/* Issue marker IOCB. */
- rval2 = qla2x00_marker(vha, req, rsp, fcport->loop_id, l,
+ rval2 = qla2x00_marker(vha, vha->hw->base_qpair, fcport->loop_id, l,
MK_SYNC_ID_LUN);
if (rval2 != QLA_SUCCESS) {
ql_dbg(ql_dbg_mbx, vha, 0x1044,
@@ -2244,10 +2241,7 @@ qla2x00_lip_reset(scsi_qla_host_t *vha)
mcp->out_mb = MBX_2|MBX_1|MBX_0;
} else if (IS_FWI2_CAPABLE(vha->hw)) {
mcp->mb[0] = MBC_LIP_FULL_LOGIN;
- if (N2N_TOPO(vha->hw))
- mcp->mb[1] = BIT_4; /* re-init */
- else
- mcp->mb[1] = BIT_6; /* LIP */
+ mcp->mb[1] = BIT_4;
mcp->mb[2] = 0;
mcp->mb[3] = vha->hw->loop_reset_delay;
mcp->out_mb = MBX_3|MBX_2|MBX_1|MBX_0;
@@ -2757,7 +2751,7 @@ qla2x00_full_login_lip(scsi_qla_host_t *vha)
"Entered %s.\n", __func__);
mcp->mb[0] = MBC_LIP_FULL_LOGIN;
- mcp->mb[1] = IS_FWI2_CAPABLE(vha->hw) ? BIT_3 : 0;
+ mcp->mb[1] = IS_FWI2_CAPABLE(vha->hw) ? BIT_4 : 0;
mcp->mb[2] = 0;
mcp->mb[3] = 0;
mcp->out_mb = MBX_3|MBX_2|MBX_1|MBX_0;
@@ -3184,7 +3178,6 @@ __qla24xx_issue_tmf(char *name, uint32_t type, struct fc_port *fcport,
scsi_qla_host_t *vha;
struct qla_hw_data *ha;
struct req_que *req;
- struct rsp_que *rsp;
struct qla_qpair *qpair;
vha = fcport->vha;
@@ -3197,10 +3190,7 @@ __qla24xx_issue_tmf(char *name, uint32_t type, struct fc_port *fcport,
if (vha->vp_idx && vha->qpair) {
/* NPIV port */
qpair = vha->qpair;
- rsp = qpair->rsp;
req = qpair->req;
- } else {
- rsp = req->rsp;
}
tsk = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &tsk_dma);
@@ -3257,7 +3247,7 @@ __qla24xx_issue_tmf(char *name, uint32_t type, struct fc_port *fcport,
}
/* Issue marker IOCB. */
- rval2 = qla2x00_marker(vha, req, rsp, fcport->loop_id, l,
+ rval2 = qla2x00_marker(vha, ha->base_qpair, fcport->loop_id, l,
type == TCF_LUN_RESET ? MK_SYNC_ID_LUN: MK_SYNC_ID);
if (rval2 != QLA_SUCCESS) {
ql_dbg(ql_dbg_mbx, vha, 0x1099,
@@ -5248,6 +5238,66 @@ qla81xx_write_mpi_register(scsi_qla_host_t *vha, uint16_t *mb)
return rval;
}
+/* Set the specified data rate */
+int
+qla2x00_set_data_rate(scsi_qla_host_t *vha, uint16_t mode)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+ struct qla_hw_data *ha = vha->hw;
+ uint16_t val;
+
+ ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1106,
+ "Entered %s speed:0x%x mode:0x%x.\n", __func__, ha->set_data_rate,
+ mode);
+
+ if (!IS_FWI2_CAPABLE(ha))
+ return QLA_FUNCTION_FAILED;
+
+ memset(mcp, 0, sizeof(*mcp));
+ switch (ha->set_data_rate) {
+ case PORT_SPEED_AUTO:
+ case PORT_SPEED_4GB:
+ case PORT_SPEED_8GB:
+ case PORT_SPEED_16GB:
+ case PORT_SPEED_32GB:
+ val = ha->set_data_rate;
+ break;
+ default:
+ ql_log(ql_log_warn, vha, 0x1199,
+ "Unrecognized speed setting:%d. Setting Autoneg\n",
+ ha->set_data_rate);
+ val = ha->set_data_rate = PORT_SPEED_AUTO;
+ break;
+ }
+
+ mcp->mb[0] = MBC_DATA_RATE;
+ mcp->mb[1] = mode;
+ mcp->mb[2] = val;
+
+ mcp->out_mb = MBX_2|MBX_1|MBX_0;
+ mcp->in_mb = MBX_2|MBX_1|MBX_0;
+ if (IS_QLA83XX(ha) || IS_QLA27XX(ha))
+ mcp->in_mb |= MBX_4|MBX_3;
+ mcp->tov = MBX_TOV_SECONDS;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(vha, mcp);
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x1107,
+ "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
+ } else {
+ if (mcp->mb[1] != 0x7)
+ ql_dbg(ql_dbg_mbx, vha, 0x1179,
+ "Speed set:0x%x\n", mcp->mb[1]);
+
+ ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1108,
+ "Done %s.\n", __func__);
+ }
+
+ return rval;
+}
+
int
qla2x00_get_data_rate(scsi_qla_host_t *vha)
{
@@ -5263,7 +5313,7 @@ qla2x00_get_data_rate(scsi_qla_host_t *vha)
return QLA_FUNCTION_FAILED;
mcp->mb[0] = MBC_DATA_RATE;
- mcp->mb[1] = 0;
+ mcp->mb[1] = QLA_GET_DATA_RATE;
mcp->out_mb = MBX_1|MBX_0;
mcp->in_mb = MBX_2|MBX_1|MBX_0;
if (IS_QLA83XX(ha) || IS_QLA27XX(ha))
@@ -6268,8 +6318,6 @@ int __qla24xx_parse_gpdb(struct scsi_qla_host *vha, fc_port_t *fcport,
fcport->d_id.b.rsvd_1 = 0;
if (fcport->fc4f_nvme) {
- fcport->nvme_prli_service_param =
- pd->prli_nvme_svc_param_word_3;
fcport->port_type = FCT_NVME;
} else {
/* If not target must be initiator or unknown type. */
diff --git a/drivers/scsi/qla2xxx/qla_nvme.c b/drivers/scsi/qla2xxx/qla_nvme.c
index 39d892bbd219..41c85da3ab32 100644
--- a/drivers/scsi/qla2xxx/qla_nvme.c
+++ b/drivers/scsi/qla2xxx/qla_nvme.c
@@ -185,6 +185,14 @@ static void qla_nvme_abort_work(struct work_struct *work)
struct qla_hw_data *ha = fcport->vha->hw;
int rval;
+ if (fcport)
+ ql_dbg(ql_dbg_io, fcport->vha, 0xffff,
+ "%s called for sp=%p, hndl=%x on fcport=%p deleted=%d\n",
+ __func__, sp, sp->handle, fcport, fcport->deleted);
+
+ if (!ha->flags.fw_started && (fcport && fcport->deleted))
+ return;
+
rval = ha->isp_ops->abort_command(sp);
ql_dbg(ql_dbg_io, fcport->vha, 0x212b,
@@ -358,17 +366,24 @@ static inline int qla2x00_start_nvme_mq(srb_t *sp)
/* No data transfer how do we check buffer len == 0?? */
if (fd->io_dir == NVMEFC_FCP_READ) {
- cmd_pkt->control_flags =
- cpu_to_le16(CF_READ_DATA | CF_NVME_ENABLE);
+ cmd_pkt->control_flags = CF_READ_DATA;
vha->qla_stats.input_bytes += fd->payload_length;
vha->qla_stats.input_requests++;
} else if (fd->io_dir == NVMEFC_FCP_WRITE) {
- cmd_pkt->control_flags =
- cpu_to_le16(CF_WRITE_DATA | CF_NVME_ENABLE);
+ cmd_pkt->control_flags = CF_WRITE_DATA;
+ if ((vha->flags.nvme_first_burst) &&
+ (sp->fcport->nvme_prli_service_param &
+ NVME_PRLI_SP_FIRST_BURST)) {
+ if ((fd->payload_length <=
+ sp->fcport->nvme_first_burst_size) ||
+ (sp->fcport->nvme_first_burst_size == 0))
+ cmd_pkt->control_flags |=
+ CF_NVME_FIRST_BURST_ENABLE;
+ }
vha->qla_stats.output_bytes += fd->payload_length;
vha->qla_stats.output_requests++;
} else if (fd->io_dir == 0) {
- cmd_pkt->control_flags = cpu_to_le16(CF_NVME_ENABLE);
+ cmd_pkt->control_flags = 0;
}
/* Set NPORT-ID */
@@ -600,6 +615,7 @@ static void qla_nvme_unregister_remote_port(struct work_struct *work)
struct fc_port *fcport = container_of(work, struct fc_port,
nvme_del_work);
struct qla_nvme_rport *qla_rport, *trport;
+ scsi_qla_host_t *base_vha;
if (!IS_ENABLED(CONFIG_NVME_FC))
return;
@@ -607,6 +623,15 @@ static void qla_nvme_unregister_remote_port(struct work_struct *work)
ql_log(ql_log_warn, NULL, 0x2112,
"%s: unregister remoteport on %p\n",__func__, fcport);
+ base_vha = pci_get_drvdata(fcport->vha->hw->pdev);
+ if (test_bit(PFLG_DRIVER_REMOVING, &base_vha->pci_flags)) {
+ ql_dbg(ql_dbg_disc, fcport->vha, 0x2114,
+ "%s: Notify FC-NVMe transport, set devloss=0\n",
+ __func__);
+
+ nvme_fc_set_remoteport_devloss(fcport->nvme_remote_port, 0);
+ }
+
list_for_each_entry_safe(qla_rport, trport,
&fcport->vha->nvme_rport_list, list) {
if (qla_rport->fcport == fcport) {
@@ -623,23 +648,11 @@ static void qla_nvme_unregister_remote_port(struct work_struct *work)
void qla_nvme_delete(struct scsi_qla_host *vha)
{
- struct qla_nvme_rport *qla_rport, *trport;
- fc_port_t *fcport;
int nv_ret;
if (!IS_ENABLED(CONFIG_NVME_FC))
return;
- list_for_each_entry_safe(qla_rport, trport,
- &vha->nvme_rport_list, list) {
- fcport = qla_rport->fcport;
-
- ql_log(ql_log_info, fcport->vha, 0x2114, "%s: fcport=%p\n",
- __func__, fcport);
-
- nvme_fc_set_remoteport_devloss(fcport->nvme_remote_port, 0);
- }
-
if (vha->nvme_local_port) {
init_completion(&vha->nvme_del_done);
ql_log(ql_log_info, vha, 0x2116,
diff --git a/drivers/scsi/qla2xxx/qla_nvme.h b/drivers/scsi/qla2xxx/qla_nvme.h
index 4941d107fb1c..da8dad5ad693 100644
--- a/drivers/scsi/qla2xxx/qla_nvme.h
+++ b/drivers/scsi/qla2xxx/qla_nvme.h
@@ -57,7 +57,7 @@ struct cmd_nvme {
uint64_t rsvd;
uint16_t control_flags; /* Control Flags */
-#define CF_NVME_ENABLE BIT_9
+#define CF_NVME_FIRST_BURST_ENABLE BIT_11
#define CF_DIF_SEG_DESCR_ENABLE BIT_3
#define CF_DATA_SEG_DESCR_ENABLE BIT_2
#define CF_READ_DATA BIT_1
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index c6ef83d0d99b..91f576d743fe 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -285,6 +285,27 @@ MODULE_PARM_DESC(qla2xuseresexchforels,
"Reserve 1/2 of emergency exchanges for ELS.\n"
" 0 (default): disabled");
+int ql2xprotmask;
+module_param(ql2xprotmask, int, 0644);
+MODULE_PARM_DESC(ql2xprotmask,
+ "Override DIF/DIX protection capabilities mask\n"
+ "Default is 0 which sets protection mask based on "
+ "capabilities reported by HBA firmware.\n");
+
+int ql2xprotguard;
+module_param(ql2xprotguard, int, 0644);
+MODULE_PARM_DESC(ql2xprotguard, "Override choice of DIX checksum\n"
+ " 0 -- Let HBA firmware decide\n"
+ " 1 -- Force T10 CRC\n"
+ " 2 -- Force IP checksum\n");
+
+int ql2xdifbundlinginternalbuffers;
+module_param(ql2xdifbundlinginternalbuffers, int, 0644);
+MODULE_PARM_DESC(ql2xdifbundlinginternalbuffers,
+ "Force using internal buffers for DIF information\n"
+ "0 (Default). Based on check.\n"
+ "1 Force using internal buffers\n");
+
/*
* SCSI host template entry points
*/
@@ -804,7 +825,44 @@ qla2xxx_qpair_sp_free_dma(void *ptr)
ha->gbl_dsd_inuse -= ctx1->dsd_use_cnt;
ha->gbl_dsd_avail += ctx1->dsd_use_cnt;
mempool_free(ctx1, ha->ctx_mempool);
+ sp->flags &= ~SRB_FCP_CMND_DMA_VALID;
+ }
+ if (sp->flags & SRB_DIF_BUNDL_DMA_VALID) {
+ struct crc_context *difctx = sp->u.scmd.ctx;
+ struct dsd_dma *dif_dsd, *nxt_dsd;
+
+ list_for_each_entry_safe(dif_dsd, nxt_dsd,
+ &difctx->ldif_dma_hndl_list, list) {
+ list_del(&dif_dsd->list);
+ dma_pool_free(ha->dif_bundl_pool, dif_dsd->dsd_addr,
+ dif_dsd->dsd_list_dma);
+ kfree(dif_dsd);
+ difctx->no_dif_bundl--;
+ }
+
+ list_for_each_entry_safe(dif_dsd, nxt_dsd,
+ &difctx->ldif_dsd_list, list) {
+ list_del(&dif_dsd->list);
+ dma_pool_free(ha->dl_dma_pool, dif_dsd->dsd_addr,
+ dif_dsd->dsd_list_dma);
+ kfree(dif_dsd);
+ difctx->no_ldif_dsd--;
+ }
+
+ if (difctx->no_ldif_dsd) {
+ ql_dbg(ql_dbg_tgt+ql_dbg_verbose, sp->vha, 0xe022,
+ "%s: difctx->no_ldif_dsd=%x\n",
+ __func__, difctx->no_ldif_dsd);
+ }
+
+ if (difctx->no_dif_bundl) {
+ ql_dbg(ql_dbg_tgt+ql_dbg_verbose, sp->vha, 0xe022,
+ "%s: difctx->no_dif_bundl=%x\n",
+ __func__, difctx->no_dif_bundl);
+ }
+ sp->flags &= ~SRB_DIF_BUNDL_DMA_VALID;
}
+
end:
CMD_SP(cmd) = NULL;
qla2xxx_rel_qpair_sp(sp->qpair, sp);
@@ -1459,7 +1517,7 @@ __qla2xxx_eh_generic_reset(char *name, enum nexus_wait_type type,
goto eh_reset_failed;
}
err = 2;
- if (do_reset(fcport, cmd->device->lun, blk_mq_rq_cpu(cmd->request) + 1)
+ if (do_reset(fcport, cmd->device->lun, 1)
!= QLA_SUCCESS) {
ql_log(ql_log_warn, vha, 0x800c,
"do_reset failed for cmd=%p.\n", cmd);
@@ -3342,13 +3400,16 @@ skip_dpc:
"Registering for DIF/DIX type 1 and 3 protection.\n");
if (ql2xenabledif == 1)
prot = SHOST_DIX_TYPE0_PROTECTION;
- scsi_host_set_prot(host,
- prot | SHOST_DIF_TYPE1_PROTECTION
- | SHOST_DIF_TYPE2_PROTECTION
- | SHOST_DIF_TYPE3_PROTECTION
- | SHOST_DIX_TYPE1_PROTECTION
- | SHOST_DIX_TYPE2_PROTECTION
- | SHOST_DIX_TYPE3_PROTECTION);
+ if (ql2xprotmask)
+ scsi_host_set_prot(host, ql2xprotmask);
+ else
+ scsi_host_set_prot(host,
+ prot | SHOST_DIF_TYPE1_PROTECTION
+ | SHOST_DIF_TYPE2_PROTECTION
+ | SHOST_DIF_TYPE3_PROTECTION
+ | SHOST_DIX_TYPE1_PROTECTION
+ | SHOST_DIX_TYPE2_PROTECTION
+ | SHOST_DIX_TYPE3_PROTECTION);
guard = SHOST_DIX_GUARD_CRC;
@@ -3356,7 +3417,10 @@ skip_dpc:
(ql2xenabledif > 1 || IS_PI_DIFB_DIX0_CAPABLE(ha)))
guard |= SHOST_DIX_GUARD_IP;
- scsi_host_set_guard(host, guard);
+ if (ql2xprotguard)
+ scsi_host_set_guard(host, ql2xprotguard);
+ else
+ scsi_host_set_guard(host, guard);
} else
base_vha->flags.difdix_supported = 0;
}
@@ -3997,9 +4061,86 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
"Failed to allocate memory for fcp_cmnd_dma_pool.\n");
goto fail_dl_dma_pool;
}
+
+ if (ql2xenabledif) {
+ u64 bufsize = DIF_BUNDLING_DMA_POOL_SIZE;
+ struct dsd_dma *dsd, *nxt;
+ uint i;
+ /* Creata a DMA pool of buffers for DIF bundling */
+ ha->dif_bundl_pool = dma_pool_create(name,
+ &ha->pdev->dev, DIF_BUNDLING_DMA_POOL_SIZE, 8, 0);
+ if (!ha->dif_bundl_pool) {
+ ql_dbg_pci(ql_dbg_init, ha->pdev, 0x0024,
+ "%s: failed create dif_bundl_pool\n",
+ __func__);
+ goto fail_dif_bundl_dma_pool;
+ }
+
+ INIT_LIST_HEAD(&ha->pool.good.head);
+ INIT_LIST_HEAD(&ha->pool.unusable.head);
+ ha->pool.good.count = 0;
+ ha->pool.unusable.count = 0;
+ for (i = 0; i < 128; i++) {
+ dsd = kzalloc(sizeof(*dsd), GFP_ATOMIC);
+ if (!dsd) {
+ ql_dbg_pci(ql_dbg_init, ha->pdev,
+ 0xe0ee, "%s: failed alloc dsd\n",
+ __func__);
+ return 1;
+ }
+ ha->dif_bundle_kallocs++;
+
+ dsd->dsd_addr = dma_pool_alloc(
+ ha->dif_bundl_pool, GFP_ATOMIC,
+ &dsd->dsd_list_dma);
+ if (!dsd->dsd_addr) {
+ ql_dbg_pci(ql_dbg_init, ha->pdev,
+ 0xe0ee,
+ "%s: failed alloc ->dsd_addr\n",
+ __func__);
+ kfree(dsd);
+ ha->dif_bundle_kallocs--;
+ continue;
+ }
+ ha->dif_bundle_dma_allocs++;
+
+ /*
+ * if DMA buffer crosses 4G boundary,
+ * put it on bad list
+ */
+ if (MSD(dsd->dsd_list_dma) ^
+ MSD(dsd->dsd_list_dma + bufsize)) {
+ list_add_tail(&dsd->list,
+ &ha->pool.unusable.head);
+ ha->pool.unusable.count++;
+ } else {
+ list_add_tail(&dsd->list,
+ &ha->pool.good.head);
+ ha->pool.good.count++;
+ }
+ }
+
+ /* return the good ones back to the pool */
+ list_for_each_entry_safe(dsd, nxt,
+ &ha->pool.good.head, list) {
+ list_del(&dsd->list);
+ dma_pool_free(ha->dif_bundl_pool,
+ dsd->dsd_addr, dsd->dsd_list_dma);
+ ha->dif_bundle_dma_allocs--;
+ kfree(dsd);
+ ha->dif_bundle_kallocs--;
+ }
+
+ ql_dbg_pci(ql_dbg_init, ha->pdev, 0x0024,
+ "%s: dif dma pool (good=%u unusable=%u)\n",
+ __func__, ha->pool.good.count,
+ ha->pool.unusable.count);
+ }
+
ql_dbg_pci(ql_dbg_init, ha->pdev, 0x0025,
- "dl_dma_pool=%p fcp_cmnd_dma_pool=%p.\n",
- ha->dl_dma_pool, ha->fcp_cmnd_dma_pool);
+ "dl_dma_pool=%p fcp_cmnd_dma_pool=%p dif_bundl_pool=%p.\n",
+ ha->dl_dma_pool, ha->fcp_cmnd_dma_pool,
+ ha->dif_bundl_pool);
}
/* Allocate memory for SNS commands */
@@ -4164,6 +4305,24 @@ fail_free_ms_iocb:
dma_free_coherent(&ha->pdev->dev, sizeof(struct sns_cmd_pkt),
ha->sns_cmd, ha->sns_cmd_dma);
fail_dma_pool:
+ if (ql2xenabledif) {
+ struct dsd_dma *dsd, *nxt;
+
+ list_for_each_entry_safe(dsd, nxt, &ha->pool.unusable.head,
+ list) {
+ list_del(&dsd->list);
+ dma_pool_free(ha->dif_bundl_pool, dsd->dsd_addr,
+ dsd->dsd_list_dma);
+ ha->dif_bundle_dma_allocs--;
+ kfree(dsd);
+ ha->dif_bundle_kallocs--;
+ ha->pool.unusable.count--;
+ }
+ dma_pool_destroy(ha->dif_bundl_pool);
+ ha->dif_bundl_pool = NULL;
+ }
+
+fail_dif_bundl_dma_pool:
if (IS_QLA82XX(ha) || ql2xenabledif) {
dma_pool_destroy(ha->fcp_cmnd_dma_pool);
ha->fcp_cmnd_dma_pool = NULL;
@@ -4544,6 +4703,32 @@ qla2x00_mem_free(struct qla_hw_data *ha)
mempool_destroy(ha->ctx_mempool);
+ if (ql2xenabledif) {
+ struct dsd_dma *dsd, *nxt;
+
+ list_for_each_entry_safe(dsd, nxt, &ha->pool.unusable.head,
+ list) {
+ list_del(&dsd->list);
+ dma_pool_free(ha->dif_bundl_pool, dsd->dsd_addr,
+ dsd->dsd_list_dma);
+ ha->dif_bundle_dma_allocs--;
+ kfree(dsd);
+ ha->dif_bundle_kallocs--;
+ ha->pool.unusable.count--;
+ }
+ list_for_each_entry_safe(dsd, nxt, &ha->pool.good.head, list) {
+ list_del(&dsd->list);
+ dma_pool_free(ha->dif_bundl_pool, dsd->dsd_addr,
+ dsd->dsd_list_dma);
+ ha->dif_bundle_dma_allocs--;
+ kfree(dsd);
+ ha->dif_bundle_kallocs--;
+ }
+ }
+
+ if (ha->dif_bundl_pool)
+ dma_pool_destroy(ha->dif_bundl_pool);
+
qlt_mem_free(ha);
if (ha->init_cb)
@@ -5019,14 +5204,14 @@ qla2x00_do_work(struct scsi_qla_host *vha)
struct qla_work_evt *e, *tmp;
unsigned long flags;
LIST_HEAD(work);
+ int rc;
spin_lock_irqsave(&vha->work_lock, flags);
list_splice_init(&vha->work_list, &work);
spin_unlock_irqrestore(&vha->work_lock, flags);
list_for_each_entry_safe(e, tmp, &work, list) {
- list_del_init(&e->list);
-
+ rc = QLA_SUCCESS;
switch (e->type) {
case QLA_EVT_AEN:
fc_host_post_event(vha->host, fc_get_event_number(),
@@ -5040,7 +5225,7 @@ qla2x00_do_work(struct scsi_qla_host *vha)
e->u.logio.data);
break;
case QLA_EVT_ASYNC_LOGOUT:
- qla2x00_async_logout(vha, e->u.logio.fcport);
+ rc = qla2x00_async_logout(vha, e->u.logio.fcport);
break;
case QLA_EVT_ASYNC_LOGOUT_DONE:
qla2x00_async_logout_done(vha, e->u.logio.fcport,
@@ -5085,7 +5270,7 @@ qla2x00_do_work(struct scsi_qla_host *vha)
qla24xx_do_nack_work(vha, e);
break;
case QLA_EVT_ASYNC_PRLO:
- qla2x00_async_prlo(vha, e->u.logio.fcport);
+ rc = qla2x00_async_prlo(vha, e->u.logio.fcport);
break;
case QLA_EVT_ASYNC_PRLO_DONE:
qla2x00_async_prlo_done(vha, e->u.logio.fcport,
@@ -5118,6 +5303,15 @@ qla2x00_do_work(struct scsi_qla_host *vha)
e->u.fcport.fcport, false);
break;
}
+
+ if (rc == EAGAIN) {
+ /* put 'work' at head of 'vha->work_list' */
+ spin_lock_irqsave(&vha->work_lock, flags);
+ list_splice(&work, &vha->work_list);
+ spin_unlock_irqrestore(&vha->work_lock, flags);
+ break;
+ }
+ list_del_init(&e->list);
if (e->flags & QLA_EVT_FLAG_FREE)
kfree(e);
@@ -6930,13 +7124,64 @@ qla2xxx_pci_resume(struct pci_dev *pdev)
ha->flags.eeh_busy = 0;
}
+static void
+qla_pci_reset_prepare(struct pci_dev *pdev)
+{
+ scsi_qla_host_t *base_vha = pci_get_drvdata(pdev);
+ struct qla_hw_data *ha = base_vha->hw;
+ struct qla_qpair *qpair;
+
+ ql_log(ql_log_warn, base_vha, 0xffff,
+ "%s.\n", __func__);
+
+ /*
+ * PCI FLR/function reset is about to reset the
+ * slot. Stop the chip to stop all DMA access.
+ * It is assumed that pci_reset_done will be called
+ * after FLR to resume Chip operation.
+ */
+ ha->flags.eeh_busy = 1;
+ mutex_lock(&ha->mq_lock);
+ list_for_each_entry(qpair, &base_vha->qp_list, qp_list_elem)
+ qpair->online = 0;
+ mutex_unlock(&ha->mq_lock);
+
+ set_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags);
+ qla2x00_abort_isp_cleanup(base_vha);
+ qla2x00_abort_all_cmds(base_vha, DID_RESET << 16);
+}
+
+static void
+qla_pci_reset_done(struct pci_dev *pdev)
+{
+ scsi_qla_host_t *base_vha = pci_get_drvdata(pdev);
+ struct qla_hw_data *ha = base_vha->hw;
+ struct qla_qpair *qpair;
+
+ ql_log(ql_log_warn, base_vha, 0xffff,
+ "%s.\n", __func__);
+
+ /*
+ * FLR just completed by PCI layer. Resume adapter
+ */
+ ha->flags.eeh_busy = 0;
+ mutex_lock(&ha->mq_lock);
+ list_for_each_entry(qpair, &base_vha->qp_list, qp_list_elem)
+ qpair->online = 1;
+ mutex_unlock(&ha->mq_lock);
+
+ base_vha->flags.online = 1;
+ ha->isp_ops->abort_isp(base_vha);
+ clear_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags);
+}
+
static int qla2xxx_map_queues(struct Scsi_Host *shost)
{
int rc;
scsi_qla_host_t *vha = (scsi_qla_host_t *)shost->hostdata;
struct blk_mq_queue_map *qmap = &shost->tag_set.map[0];
- if (USER_CTRL_IRQ(vha->hw))
+ if (USER_CTRL_IRQ(vha->hw) || !vha->hw->mqiobase)
rc = blk_mq_map_queues(qmap);
else
rc = blk_mq_pci_map_queues(qmap, vha->hw->pdev, vha->irq_offset);
@@ -6948,6 +7193,8 @@ static const struct pci_error_handlers qla2xxx_err_handler = {
.mmio_enabled = qla2xxx_pci_mmio_enabled,
.slot_reset = qla2xxx_pci_slot_reset,
.resume = qla2xxx_pci_resume,
+ .reset_prepare = qla_pci_reset_prepare,
+ .reset_done = qla_pci_reset_done,
};
static struct pci_device_id qla2xxx_pci_tbl[] = {
diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c
index 510337eac106..582d1663f971 100644
--- a/drivers/scsi/qla2xxx/qla_target.c
+++ b/drivers/scsi/qla2xxx/qla_target.c
@@ -660,14 +660,14 @@ int qla24xx_async_notify_ack(scsi_qla_host_t *vha, fc_port_t *fcport,
sp->u.iocb_cmd.u.nack.ntfy = ntfy;
sp->done = qla2x00_async_nack_sp_done;
- rval = qla2x00_start_sp(sp);
- if (rval != QLA_SUCCESS)
- goto done_free_sp;
-
ql_dbg(ql_dbg_disc, vha, 0x20f4,
"Async-%s %8phC hndl %x %s\n",
sp->name, fcport->port_name, sp->handle, c);
+ rval = qla2x00_start_sp(sp);
+ if (rval != QLA_SUCCESS)
+ goto done_free_sp;
+
return rval;
done_free_sp:
@@ -684,6 +684,9 @@ void qla24xx_do_nack_work(struct scsi_qla_host *vha, struct qla_work_evt *e)
switch (e->u.nack.type) {
case SRB_NACK_PRLI:
+ t = e->u.nack.fcport;
+ flush_work(&t->del_work);
+ flush_work(&t->free_work);
mutex_lock(&vha->vha_tgt.tgt_mutex);
t = qlt_create_sess(vha, e->u.nack.fcport, 0);
mutex_unlock(&vha->vha_tgt.tgt_mutex);
@@ -3230,7 +3233,7 @@ qlt_build_ctio_crc2_pkt(struct qla_qpair *qpair, struct qla_tgt_prm *prm)
cur_dsd = (uint32_t *) &crc_ctx_pkt->u.bundling.dif_address;
if (qla24xx_walk_and_build_prot_sglist(ha, NULL, cur_dsd,
- prm->prot_seg_cnt, &tc))
+ prm->prot_seg_cnt, cmd))
goto crc_queuing_error;
}
return QLA_SUCCESS;
@@ -3257,13 +3260,10 @@ int qlt_xmit_response(struct qla_tgt_cmd *cmd, int xmit_type,
unsigned long flags = 0;
int res;
- if (cmd->sess && cmd->sess->deleted) {
+ if (!qpair->fw_started || (cmd->reset_count != qpair->chip_reset) ||
+ (cmd->sess && cmd->sess->deleted)) {
cmd->state = QLA_TGT_STATE_PROCESSED;
- if (cmd->sess->logout_completed)
- /* no need to terminate. FW already freed exchange. */
- qlt_abort_cmd_on_host_reset(cmd->vha, cmd);
- else
- qlt_send_term_exchange(qpair, cmd, &cmd->atio, 0, 0);
+ qlt_abort_cmd_on_host_reset(cmd->vha, cmd);
return 0;
}
@@ -6343,7 +6343,7 @@ static void qlt_tmr_work(struct qla_tgt *tgt,
struct atio_from_isp *a = &prm->tm_iocb2;
struct scsi_qla_host *vha = tgt->vha;
struct qla_hw_data *ha = vha->hw;
- struct fc_port *sess = NULL;
+ struct fc_port *sess;
unsigned long flags;
uint8_t *s_id = NULL; /* to hide compiler warnings */
int rc;
@@ -6369,7 +6369,6 @@ static void qlt_tmr_work(struct qla_tgt *tgt,
goto out_term2;
} else {
if (sess->deleted) {
- sess = NULL;
goto out_term2;
}
@@ -6377,7 +6376,6 @@ static void qlt_tmr_work(struct qla_tgt *tgt,
ql_dbg(ql_dbg_tgt_tmr, vha, 0xf020,
"%s: kref_get fail %8phC\n",
__func__, sess->port_name);
- sess = NULL;
goto out_term2;
}
}
@@ -6396,8 +6394,6 @@ static void qlt_tmr_work(struct qla_tgt *tgt,
return;
out_term2:
- if (sess)
- ha->tgt.tgt_ops->put_sess(sess);
spin_unlock_irqrestore(&ha->tgt.sess_lock, flags);
out_term:
qlt_send_term_exchange(ha->base_qpair, NULL, &prm->tm_iocb2, 1, 0);
diff --git a/drivers/scsi/qla2xxx/qla_target.h b/drivers/scsi/qla2xxx/qla_target.h
index 577e1786a3f1..f3de75000a08 100644
--- a/drivers/scsi/qla2xxx/qla_target.h
+++ b/drivers/scsi/qla2xxx/qla_target.h
@@ -928,6 +928,8 @@ struct qla_tgt_cmd {
uint64_t lba;
uint16_t a_guard, e_guard, a_app_tag, e_app_tag;
uint32_t a_ref_tag, e_ref_tag;
+#define DIF_BUNDL_DMA_VALID 1
+ uint16_t prot_flags;
uint64_t jiffies_at_alloc;
uint64_t jiffies_at_free;
diff --git a/drivers/scsi/qla2xxx/qla_tmpl.c b/drivers/scsi/qla2xxx/qla_tmpl.c
index 0ccd06f11f12..9e52500caff0 100644
--- a/drivers/scsi/qla2xxx/qla_tmpl.c
+++ b/drivers/scsi/qla2xxx/qla_tmpl.c
@@ -221,7 +221,13 @@ qla27xx_skip_entry(struct qla27xx_fwdt_entry *ent, void *buf)
ent->hdr.driver_flags |= DRIVER_FLAG_SKIP_ENTRY;
}
-static int
+static inline struct qla27xx_fwdt_entry *
+qla27xx_next_entry(struct qla27xx_fwdt_entry *ent)
+{
+ return (void *)ent + ent->hdr.size;
+}
+
+static struct qla27xx_fwdt_entry *
qla27xx_fwdt_entry_t0(struct scsi_qla_host *vha,
struct qla27xx_fwdt_entry *ent, void *buf, ulong *len)
{
@@ -229,10 +235,10 @@ qla27xx_fwdt_entry_t0(struct scsi_qla_host *vha,
"%s: nop [%lx]\n", __func__, *len);
qla27xx_skip_entry(ent, buf);
- return false;
+ return qla27xx_next_entry(ent);
}
-static int
+static struct qla27xx_fwdt_entry *
qla27xx_fwdt_entry_t255(struct scsi_qla_host *vha,
struct qla27xx_fwdt_entry *ent, void *buf, ulong *len)
{
@@ -241,10 +247,10 @@ qla27xx_fwdt_entry_t255(struct scsi_qla_host *vha,
qla27xx_skip_entry(ent, buf);
/* terminate */
- return true;
+ return NULL;
}
-static int
+static struct qla27xx_fwdt_entry *
qla27xx_fwdt_entry_t256(struct scsi_qla_host *vha,
struct qla27xx_fwdt_entry *ent, void *buf, ulong *len)
{
@@ -255,10 +261,10 @@ qla27xx_fwdt_entry_t256(struct scsi_qla_host *vha,
qla27xx_read_window(reg, ent->t256.base_addr, ent->t256.pci_offset,
ent->t256.reg_count, ent->t256.reg_width, buf, len);
- return false;
+ return qla27xx_next_entry(ent);
}
-static int
+static struct qla27xx_fwdt_entry *
qla27xx_fwdt_entry_t257(struct scsi_qla_host *vha,
struct qla27xx_fwdt_entry *ent, void *buf, ulong *len)
{
@@ -269,10 +275,10 @@ qla27xx_fwdt_entry_t257(struct scsi_qla_host *vha,
qla27xx_write_reg(reg, IOBASE_ADDR, ent->t257.base_addr, buf);
qla27xx_write_reg(reg, ent->t257.pci_offset, ent->t257.write_data, buf);
- return false;
+ return qla27xx_next_entry(ent);
}
-static int
+static struct qla27xx_fwdt_entry *
qla27xx_fwdt_entry_t258(struct scsi_qla_host *vha,
struct qla27xx_fwdt_entry *ent, void *buf, ulong *len)
{
@@ -284,10 +290,10 @@ qla27xx_fwdt_entry_t258(struct scsi_qla_host *vha,
qla27xx_read_window(reg, ent->t258.base_addr, ent->t258.pci_offset,
ent->t258.reg_count, ent->t258.reg_width, buf, len);
- return false;
+ return qla27xx_next_entry(ent);
}
-static int
+static struct qla27xx_fwdt_entry *
qla27xx_fwdt_entry_t259(struct scsi_qla_host *vha,
struct qla27xx_fwdt_entry *ent, void *buf, ulong *len)
{
@@ -299,10 +305,10 @@ qla27xx_fwdt_entry_t259(struct scsi_qla_host *vha,
qla27xx_write_reg(reg, ent->t259.banksel_offset, ent->t259.bank, buf);
qla27xx_write_reg(reg, ent->t259.pci_offset, ent->t259.write_data, buf);
- return false;
+ return qla27xx_next_entry(ent);
}
-static int
+static struct qla27xx_fwdt_entry *
qla27xx_fwdt_entry_t260(struct scsi_qla_host *vha,
struct qla27xx_fwdt_entry *ent, void *buf, ulong *len)
{
@@ -313,10 +319,10 @@ qla27xx_fwdt_entry_t260(struct scsi_qla_host *vha,
qla27xx_insert32(ent->t260.pci_offset, buf, len);
qla27xx_read_reg(reg, ent->t260.pci_offset, buf, len);
- return false;
+ return qla27xx_next_entry(ent);
}
-static int
+static struct qla27xx_fwdt_entry *
qla27xx_fwdt_entry_t261(struct scsi_qla_host *vha,
struct qla27xx_fwdt_entry *ent, void *buf, ulong *len)
{
@@ -326,10 +332,10 @@ qla27xx_fwdt_entry_t261(struct scsi_qla_host *vha,
"%s: wrpci [%lx]\n", __func__, *len);
qla27xx_write_reg(reg, ent->t261.pci_offset, ent->t261.write_data, buf);
- return false;
+ return qla27xx_next_entry(ent);
}
-static int
+static struct qla27xx_fwdt_entry *
qla27xx_fwdt_entry_t262(struct scsi_qla_host *vha,
struct qla27xx_fwdt_entry *ent, void *buf, ulong *len)
{
@@ -362,6 +368,11 @@ qla27xx_fwdt_entry_t262(struct scsi_qla_host *vha,
ent->t262.start_addr = start;
ent->t262.end_addr = end;
}
+ } else if (ent->t262.ram_area == T262_RAM_AREA_MISC) {
+ if (buf) {
+ ent->t262.start_addr = start;
+ ent->t262.end_addr = end;
+ }
} else {
ql_dbg(ql_dbg_misc, vha, 0xd022,
"%s: unknown area %x\n", __func__, ent->t262.ram_area);
@@ -384,10 +395,10 @@ qla27xx_fwdt_entry_t262(struct scsi_qla_host *vha,
}
*len += dwords * sizeof(uint32_t);
done:
- return false;
+ return qla27xx_next_entry(ent);
}
-static int
+static struct qla27xx_fwdt_entry *
qla27xx_fwdt_entry_t263(struct scsi_qla_host *vha,
struct qla27xx_fwdt_entry *ent, void *buf, ulong *len)
{
@@ -450,10 +461,10 @@ qla27xx_fwdt_entry_t263(struct scsi_qla_host *vha,
qla27xx_skip_entry(ent, buf);
}
- return false;
+ return qla27xx_next_entry(ent);
}
-static int
+static struct qla27xx_fwdt_entry *
qla27xx_fwdt_entry_t264(struct scsi_qla_host *vha,
struct qla27xx_fwdt_entry *ent, void *buf, ulong *len)
{
@@ -478,10 +489,10 @@ qla27xx_fwdt_entry_t264(struct scsi_qla_host *vha,
qla27xx_skip_entry(ent, buf);
}
- return false;
+ return qla27xx_next_entry(ent);
}
-static int
+static struct qla27xx_fwdt_entry *
qla27xx_fwdt_entry_t265(struct scsi_qla_host *vha,
struct qla27xx_fwdt_entry *ent, void *buf, ulong *len)
{
@@ -492,10 +503,10 @@ qla27xx_fwdt_entry_t265(struct scsi_qla_host *vha,
if (buf)
qla24xx_pause_risc(reg, vha->hw);
- return false;
+ return qla27xx_next_entry(ent);
}
-static int
+static struct qla27xx_fwdt_entry *
qla27xx_fwdt_entry_t266(struct scsi_qla_host *vha,
struct qla27xx_fwdt_entry *ent, void *buf, ulong *len)
{
@@ -504,10 +515,10 @@ qla27xx_fwdt_entry_t266(struct scsi_qla_host *vha,
if (buf)
qla24xx_soft_reset(vha->hw);
- return false;
+ return qla27xx_next_entry(ent);
}
-static int
+static struct qla27xx_fwdt_entry *
qla27xx_fwdt_entry_t267(struct scsi_qla_host *vha,
struct qla27xx_fwdt_entry *ent, void *buf, ulong *len)
{
@@ -517,10 +528,10 @@ qla27xx_fwdt_entry_t267(struct scsi_qla_host *vha,
"%s: dis intr [%lx]\n", __func__, *len);
qla27xx_write_reg(reg, ent->t267.pci_offset, ent->t267.data, buf);
- return false;
+ return qla27xx_next_entry(ent);
}
-static int
+static struct qla27xx_fwdt_entry *
qla27xx_fwdt_entry_t268(struct scsi_qla_host *vha,
struct qla27xx_fwdt_entry *ent, void *buf, ulong *len)
{
@@ -587,10 +598,10 @@ qla27xx_fwdt_entry_t268(struct scsi_qla_host *vha,
break;
}
- return false;
+ return qla27xx_next_entry(ent);
}
-static int
+static struct qla27xx_fwdt_entry *
qla27xx_fwdt_entry_t269(struct scsi_qla_host *vha,
struct qla27xx_fwdt_entry *ent, void *buf, ulong *len)
{
@@ -604,10 +615,10 @@ qla27xx_fwdt_entry_t269(struct scsi_qla_host *vha,
if (buf)
ent->t269.scratch_size = 5 * sizeof(uint32_t);
- return false;
+ return qla27xx_next_entry(ent);
}
-static int
+static struct qla27xx_fwdt_entry *
qla27xx_fwdt_entry_t270(struct scsi_qla_host *vha,
struct qla27xx_fwdt_entry *ent, void *buf, ulong *len)
{
@@ -625,10 +636,10 @@ qla27xx_fwdt_entry_t270(struct scsi_qla_host *vha,
addr += sizeof(uint32_t);
}
- return false;
+ return qla27xx_next_entry(ent);
}
-static int
+static struct qla27xx_fwdt_entry *
qla27xx_fwdt_entry_t271(struct scsi_qla_host *vha,
struct qla27xx_fwdt_entry *ent, void *buf, ulong *len)
{
@@ -642,10 +653,10 @@ qla27xx_fwdt_entry_t271(struct scsi_qla_host *vha,
qla27xx_write_reg(reg, 0xc4, data, buf);
qla27xx_write_reg(reg, 0xc0, addr, buf);
- return false;
+ return qla27xx_next_entry(ent);
}
-static int
+static struct qla27xx_fwdt_entry *
qla27xx_fwdt_entry_t272(struct scsi_qla_host *vha,
struct qla27xx_fwdt_entry *ent, void *buf, ulong *len)
{
@@ -662,10 +673,10 @@ qla27xx_fwdt_entry_t272(struct scsi_qla_host *vha,
}
*len += dwords * sizeof(uint32_t);
- return false;
+ return qla27xx_next_entry(ent);
}
-static int
+static struct qla27xx_fwdt_entry *
qla27xx_fwdt_entry_t273(struct scsi_qla_host *vha,
struct qla27xx_fwdt_entry *ent, void *buf, ulong *len)
{
@@ -685,10 +696,10 @@ qla27xx_fwdt_entry_t273(struct scsi_qla_host *vha,
addr += sizeof(uint32_t);
}
- return false;
+ return qla27xx_next_entry(ent);
}
-static int
+static struct qla27xx_fwdt_entry *
qla27xx_fwdt_entry_t274(struct scsi_qla_host *vha,
struct qla27xx_fwdt_entry *ent, void *buf, ulong *len)
{
@@ -746,10 +757,10 @@ qla27xx_fwdt_entry_t274(struct scsi_qla_host *vha,
qla27xx_skip_entry(ent, buf);
}
- return false;
+ return qla27xx_next_entry(ent);
}
-static int
+static struct qla27xx_fwdt_entry *
qla27xx_fwdt_entry_t275(struct scsi_qla_host *vha,
struct qla27xx_fwdt_entry *ent, void *buf, ulong *len)
{
@@ -763,7 +774,7 @@ qla27xx_fwdt_entry_t275(struct scsi_qla_host *vha,
qla27xx_skip_entry(ent, buf);
goto done;
}
- if (offset + ent->t275.length > ent->hdr.entry_size) {
+ if (offset + ent->t275.length > ent->hdr.size) {
ql_dbg(ql_dbg_misc, vha, 0xd030,
"%s: buffer overflow\n", __func__);
qla27xx_skip_entry(ent, buf);
@@ -772,59 +783,103 @@ qla27xx_fwdt_entry_t275(struct scsi_qla_host *vha,
qla27xx_insertbuf(ent->t275.buffer, ent->t275.length, buf, len);
done:
- return false;
+ return qla27xx_next_entry(ent);
}
-static int
+static struct qla27xx_fwdt_entry *
+qla27xx_fwdt_entry_t276(struct scsi_qla_host *vha,
+ struct qla27xx_fwdt_entry *ent, void *buf, ulong *len)
+{
+ uint type = vha->hw->pdev->device >> 4 & 0xf;
+ uint func = vha->hw->port_no & 0x3;
+
+ ql_dbg(ql_dbg_misc + ql_dbg_verbose, vha, 0xd214,
+ "%s: cond [%lx]\n", __func__, *len);
+
+ if (type != ent->t276.cond1 || func != ent->t276.cond2) {
+ ent = qla27xx_next_entry(ent);
+ qla27xx_skip_entry(ent, buf);
+ }
+
+ return qla27xx_next_entry(ent);
+}
+
+static struct qla27xx_fwdt_entry *
+qla27xx_fwdt_entry_t277(struct scsi_qla_host *vha,
+ struct qla27xx_fwdt_entry *ent, void *buf, ulong *len)
+{
+ struct device_reg_24xx __iomem *reg = qla27xx_isp_reg(vha);
+
+ ql_dbg(ql_dbg_misc + ql_dbg_verbose, vha, 0xd215,
+ "%s: rdpep [%lx]\n", __func__, *len);
+ qla27xx_insert32(ent->t277.wr_cmd_data, buf, len);
+ qla27xx_write_reg(reg, ent->t277.cmd_addr, ent->t277.wr_cmd_data, buf);
+ qla27xx_read_reg(reg, ent->t277.data_addr, buf, len);
+
+ return qla27xx_next_entry(ent);
+}
+
+static struct qla27xx_fwdt_entry *
+qla27xx_fwdt_entry_t278(struct scsi_qla_host *vha,
+ struct qla27xx_fwdt_entry *ent, void *buf, ulong *len)
+{
+ struct device_reg_24xx __iomem *reg = qla27xx_isp_reg(vha);
+
+ ql_dbg(ql_dbg_misc + ql_dbg_verbose, vha, 0xd216,
+ "%s: wrpep [%lx]\n", __func__, *len);
+ qla27xx_write_reg(reg, ent->t278.data_addr, ent->t278.wr_data, buf);
+ qla27xx_write_reg(reg, ent->t278.cmd_addr, ent->t278.wr_cmd_data, buf);
+
+ return qla27xx_next_entry(ent);
+}
+
+static struct qla27xx_fwdt_entry *
qla27xx_fwdt_entry_other(struct scsi_qla_host *vha,
struct qla27xx_fwdt_entry *ent, void *buf, ulong *len)
{
ql_dbg(ql_dbg_misc, vha, 0xd2ff,
- "%s: type %x [%lx]\n", __func__, ent->hdr.entry_type, *len);
+ "%s: type %x [%lx]\n", __func__, ent->hdr.type, *len);
qla27xx_skip_entry(ent, buf);
- return false;
+ return qla27xx_next_entry(ent);
}
-struct qla27xx_fwdt_entry_call {
+static struct {
uint type;
- int (*call)(
- struct scsi_qla_host *,
- struct qla27xx_fwdt_entry *,
- void *,
- ulong *);
-};
-
-static struct qla27xx_fwdt_entry_call ql27xx_fwdt_entry_call_list[] = {
- { ENTRY_TYPE_NOP , qla27xx_fwdt_entry_t0 } ,
- { ENTRY_TYPE_TMP_END , qla27xx_fwdt_entry_t255 } ,
- { ENTRY_TYPE_RD_IOB_T1 , qla27xx_fwdt_entry_t256 } ,
- { ENTRY_TYPE_WR_IOB_T1 , qla27xx_fwdt_entry_t257 } ,
- { ENTRY_TYPE_RD_IOB_T2 , qla27xx_fwdt_entry_t258 } ,
- { ENTRY_TYPE_WR_IOB_T2 , qla27xx_fwdt_entry_t259 } ,
- { ENTRY_TYPE_RD_PCI , qla27xx_fwdt_entry_t260 } ,
- { ENTRY_TYPE_WR_PCI , qla27xx_fwdt_entry_t261 } ,
- { ENTRY_TYPE_RD_RAM , qla27xx_fwdt_entry_t262 } ,
- { ENTRY_TYPE_GET_QUEUE , qla27xx_fwdt_entry_t263 } ,
- { ENTRY_TYPE_GET_FCE , qla27xx_fwdt_entry_t264 } ,
- { ENTRY_TYPE_PSE_RISC , qla27xx_fwdt_entry_t265 } ,
- { ENTRY_TYPE_RST_RISC , qla27xx_fwdt_entry_t266 } ,
- { ENTRY_TYPE_DIS_INTR , qla27xx_fwdt_entry_t267 } ,
- { ENTRY_TYPE_GET_HBUF , qla27xx_fwdt_entry_t268 } ,
- { ENTRY_TYPE_SCRATCH , qla27xx_fwdt_entry_t269 } ,
- { ENTRY_TYPE_RDREMREG , qla27xx_fwdt_entry_t270 } ,
- { ENTRY_TYPE_WRREMREG , qla27xx_fwdt_entry_t271 } ,
- { ENTRY_TYPE_RDREMRAM , qla27xx_fwdt_entry_t272 } ,
- { ENTRY_TYPE_PCICFG , qla27xx_fwdt_entry_t273 } ,
- { ENTRY_TYPE_GET_SHADOW , qla27xx_fwdt_entry_t274 } ,
- { ENTRY_TYPE_WRITE_BUF , qla27xx_fwdt_entry_t275 } ,
- { -1 , qla27xx_fwdt_entry_other }
+ typeof(qla27xx_fwdt_entry_other)(*call);
+} qla27xx_fwdt_entry_call[] = {
+ { ENTRY_TYPE_NOP, qla27xx_fwdt_entry_t0 },
+ { ENTRY_TYPE_TMP_END, qla27xx_fwdt_entry_t255 },
+ { ENTRY_TYPE_RD_IOB_T1, qla27xx_fwdt_entry_t256 },
+ { ENTRY_TYPE_WR_IOB_T1, qla27xx_fwdt_entry_t257 },
+ { ENTRY_TYPE_RD_IOB_T2, qla27xx_fwdt_entry_t258 },
+ { ENTRY_TYPE_WR_IOB_T2, qla27xx_fwdt_entry_t259 },
+ { ENTRY_TYPE_RD_PCI, qla27xx_fwdt_entry_t260 },
+ { ENTRY_TYPE_WR_PCI, qla27xx_fwdt_entry_t261 },
+ { ENTRY_TYPE_RD_RAM, qla27xx_fwdt_entry_t262 },
+ { ENTRY_TYPE_GET_QUEUE, qla27xx_fwdt_entry_t263 },
+ { ENTRY_TYPE_GET_FCE, qla27xx_fwdt_entry_t264 },
+ { ENTRY_TYPE_PSE_RISC, qla27xx_fwdt_entry_t265 },
+ { ENTRY_TYPE_RST_RISC, qla27xx_fwdt_entry_t266 },
+ { ENTRY_TYPE_DIS_INTR, qla27xx_fwdt_entry_t267 },
+ { ENTRY_TYPE_GET_HBUF, qla27xx_fwdt_entry_t268 },
+ { ENTRY_TYPE_SCRATCH, qla27xx_fwdt_entry_t269 },
+ { ENTRY_TYPE_RDREMREG, qla27xx_fwdt_entry_t270 },
+ { ENTRY_TYPE_WRREMREG, qla27xx_fwdt_entry_t271 },
+ { ENTRY_TYPE_RDREMRAM, qla27xx_fwdt_entry_t272 },
+ { ENTRY_TYPE_PCICFG, qla27xx_fwdt_entry_t273 },
+ { ENTRY_TYPE_GET_SHADOW, qla27xx_fwdt_entry_t274 },
+ { ENTRY_TYPE_WRITE_BUF, qla27xx_fwdt_entry_t275 },
+ { ENTRY_TYPE_CONDITIONAL, qla27xx_fwdt_entry_t276 },
+ { ENTRY_TYPE_RDPEPREG, qla27xx_fwdt_entry_t277 },
+ { ENTRY_TYPE_WRPEPREG, qla27xx_fwdt_entry_t278 },
+ { -1, qla27xx_fwdt_entry_other }
};
-static inline int (*qla27xx_find_entry(uint type))
- (struct scsi_qla_host *, struct qla27xx_fwdt_entry *, void *, ulong *)
+static inline
+typeof(qla27xx_fwdt_entry_call->call)(qla27xx_find_entry(uint type))
{
- struct qla27xx_fwdt_entry_call *list = ql27xx_fwdt_entry_call_list;
+ typeof(*qla27xx_fwdt_entry_call) *list = qla27xx_fwdt_entry_call;
while (list->type < type)
list++;
@@ -834,14 +889,6 @@ static inline int (*qla27xx_find_entry(uint type))
return qla27xx_fwdt_entry_other;
}
-static inline void *
-qla27xx_next_entry(void *p)
-{
- struct qla27xx_fwdt_entry *ent = p;
-
- return p + ent->hdr.entry_size;
-}
-
static void
qla27xx_walk_template(struct scsi_qla_host *vha,
struct qla27xx_fwdt_template *tmp, void *buf, ulong *len)
@@ -852,18 +899,16 @@ qla27xx_walk_template(struct scsi_qla_host *vha,
ql_dbg(ql_dbg_misc, vha, 0xd01a,
"%s: entry count %lx\n", __func__, count);
while (count--) {
- if (buf && *len >= vha->hw->fw_dump_len)
+ ent = qla27xx_find_entry(ent->hdr.type)(vha, ent, buf, len);
+ if (!ent)
break;
- if (qla27xx_find_entry(ent->hdr.entry_type)(vha, ent, buf, len))
- break;
- ent = qla27xx_next_entry(ent);
}
if (count)
ql_dbg(ql_dbg_misc, vha, 0xd018,
"%s: entry residual count (%lx)\n", __func__, count);
- if (ent->hdr.entry_type != ENTRY_TYPE_TMP_END)
+ if (ent)
ql_dbg(ql_dbg_misc, vha, 0xd019,
"%s: missing end entry (%lx)\n", __func__, count);
diff --git a/drivers/scsi/qla2xxx/qla_tmpl.h b/drivers/scsi/qla2xxx/qla_tmpl.h
index 141c1c5e73f4..5c2c2a8a19c4 100644
--- a/drivers/scsi/qla2xxx/qla_tmpl.h
+++ b/drivers/scsi/qla2xxx/qla_tmpl.h
@@ -54,6 +54,9 @@ struct __packed qla27xx_fwdt_template {
#define ENTRY_TYPE_PCICFG 273
#define ENTRY_TYPE_GET_SHADOW 274
#define ENTRY_TYPE_WRITE_BUF 275
+#define ENTRY_TYPE_CONDITIONAL 276
+#define ENTRY_TYPE_RDPEPREG 277
+#define ENTRY_TYPE_WRPEPREG 278
#define CAPTURE_FLAG_PHYS_ONLY BIT_0
#define CAPTURE_FLAG_PHYS_VIRT BIT_1
@@ -62,8 +65,8 @@ struct __packed qla27xx_fwdt_template {
struct __packed qla27xx_fwdt_entry {
struct __packed {
- uint32_t entry_type;
- uint32_t entry_size;
+ uint32_t type;
+ uint32_t size;
uint32_t reserved_1;
uint8_t capture_flags;
@@ -199,6 +202,24 @@ struct __packed qla27xx_fwdt_entry {
uint32_t length;
uint8_t buffer[];
} t275;
+
+ struct __packed {
+ uint32_t cond1;
+ uint32_t cond2;
+ } t276;
+
+ struct __packed {
+ uint32_t cmd_addr;
+ uint32_t wr_cmd_data;
+ uint32_t data_addr;
+ } t277;
+
+ struct __packed {
+ uint32_t cmd_addr;
+ uint32_t wr_cmd_data;
+ uint32_t data_addr;
+ uint32_t wr_data;
+ } t278;
};
};
@@ -206,6 +227,7 @@ struct __packed qla27xx_fwdt_entry {
#define T262_RAM_AREA_EXTERNAL_RAM 2
#define T262_RAM_AREA_SHARED_RAM 3
#define T262_RAM_AREA_DDR_RAM 4
+#define T262_RAM_AREA_MISC 5
#define T263_QUEUE_TYPE_REQ 1
#define T263_QUEUE_TYPE_RSP 2
diff --git a/drivers/scsi/qla2xxx/qla_version.h b/drivers/scsi/qla2xxx/qla_version.h
index ca7945cb959b..0690dac24081 100644
--- a/drivers/scsi/qla2xxx/qla_version.h
+++ b/drivers/scsi/qla2xxx/qla_version.h
@@ -7,7 +7,7 @@
/*
* Driver version
*/
-#define QLA2XXX_VERSION "10.00.00.12-k"
+#define QLA2XXX_VERSION "10.00.00.14-k"
#define QLA_DRIVER_MAJOR_VER 10
#define QLA_DRIVER_MINOR_VER 0
diff --git a/drivers/scsi/qla2xxx/tcm_qla2xxx.c b/drivers/scsi/qla2xxx/tcm_qla2xxx.c
index 283e6b80abb5..8a3075d17c63 100644
--- a/drivers/scsi/qla2xxx/tcm_qla2xxx.c
+++ b/drivers/scsi/qla2xxx/tcm_qla2xxx.c
@@ -420,26 +420,6 @@ static int tcm_qla2xxx_write_pending(struct se_cmd *se_cmd)
return qlt_rdy_to_xfer(cmd);
}
-static int tcm_qla2xxx_write_pending_status(struct se_cmd *se_cmd)
-{
- unsigned long flags;
- /*
- * Check for WRITE_PENDING status to determine if we need to wait for
- * CTIO aborts to be posted via hardware in tcm_qla2xxx_handle_data().
- */
- spin_lock_irqsave(&se_cmd->t_state_lock, flags);
- if (se_cmd->t_state == TRANSPORT_WRITE_PENDING ||
- se_cmd->t_state == TRANSPORT_COMPLETE_QF_WP) {
- spin_unlock_irqrestore(&se_cmd->t_state_lock, flags);
- wait_for_completion_timeout(&se_cmd->t_transport_stop_comp,
- 50);
- return 0;
- }
- spin_unlock_irqrestore(&se_cmd->t_state_lock, flags);
-
- return 0;
-}
-
static void tcm_qla2xxx_set_default_node_attrs(struct se_node_acl *nacl)
{
return;
@@ -537,15 +517,6 @@ static void tcm_qla2xxx_handle_data_work(struct work_struct *work)
cmd->qpair->tgt_counters.qla_core_ret_ctio++;
if (!cmd->write_data_transferred) {
- /*
- * Check if se_cmd has already been aborted via LUN_RESET, and
- * waiting upon completion in tcm_qla2xxx_write_pending_status()
- */
- if (cmd->se_cmd.transport_state & CMD_T_ABORTED) {
- complete(&cmd->se_cmd.t_transport_stop_comp);
- return;
- }
-
switch (cmd->dif_err_code) {
case DIF_ERR_GRD:
cmd->se_cmd.pi_err =
@@ -1902,7 +1873,6 @@ static const struct target_core_fabric_ops tcm_qla2xxx_ops = {
.sess_get_index = tcm_qla2xxx_sess_get_index,
.sess_get_initiator_sid = NULL,
.write_pending = tcm_qla2xxx_write_pending,
- .write_pending_status = tcm_qla2xxx_write_pending_status,
.set_default_node_attributes = tcm_qla2xxx_set_default_node_attrs,
.get_cmd_state = tcm_qla2xxx_get_cmd_state,
.queue_data_in = tcm_qla2xxx_queue_data_in,
@@ -1943,7 +1913,6 @@ static const struct target_core_fabric_ops tcm_qla2xxx_npiv_ops = {
.sess_get_index = tcm_qla2xxx_sess_get_index,
.sess_get_initiator_sid = NULL,
.write_pending = tcm_qla2xxx_write_pending,
- .write_pending_status = tcm_qla2xxx_write_pending_status,
.set_default_node_attributes = tcm_qla2xxx_set_default_node_attrs,
.get_cmd_state = tcm_qla2xxx_get_cmd_state,
.queue_data_in = tcm_qla2xxx_queue_data_in,
diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c
index a77bfb224248..6e4f4931ae17 100644
--- a/drivers/scsi/qla4xxx/ql4_os.c
+++ b/drivers/scsi/qla4xxx/ql4_os.c
@@ -2875,7 +2875,7 @@ static int qla4xxx_session_get_param(struct iscsi_cls_session *cls_sess,
chap_tbl.secret_len);
}
}
- /* allow fall-through */
+ /* fall through */
default:
return iscsi_session_get_param(cls_sess, param, buf);
}
@@ -3203,6 +3203,8 @@ static int qla4xxx_conn_bind(struct iscsi_cls_session *cls_session,
if (iscsi_conn_bind(cls_session, cls_conn, is_leading))
return -EINVAL;
ep = iscsi_lookup_endpoint(transport_fd);
+ if (!ep)
+ return -EINVAL;
conn = cls_conn->dd_data;
qla_conn = conn->dd_data;
qla_conn->qla_ep = ep->dd_data;
diff --git a/drivers/scsi/qlogicpti.c b/drivers/scsi/qlogicpti.c
index e35ce762d454..0e22512bd3e4 100644
--- a/drivers/scsi/qlogicpti.c
+++ b/drivers/scsi/qlogicpti.c
@@ -1314,8 +1314,7 @@ static int qpti_sbus_probe(struct platform_device *op)
qpti->qhost = host;
qpti->op = op;
qpti->qpti_id = nqptis;
- strcpy(qpti->prom_name, op->dev.of_node->name);
- qpti->is_pti = strcmp(qpti->prom_name, "QLGC,isp");
+ qpti->is_pti = !of_node_name_eq(op->dev.of_node, "QLGC,isp");
if (qpti_map_regs(qpti) < 0)
goto fail_unlink;
diff --git a/drivers/scsi/qlogicpti.h b/drivers/scsi/qlogicpti.h
index 884ad72ade57..2b6374e08a7d 100644
--- a/drivers/scsi/qlogicpti.h
+++ b/drivers/scsi/qlogicpti.h
@@ -364,7 +364,6 @@ struct qlogicpti {
int qpti_id;
int scsi_id;
int prom_node;
- char prom_name[64];
int irq;
char differential, ultra, clock;
unsigned char bursts;
@@ -379,7 +378,7 @@ struct qlogicpti {
#define SREG_IMASK 0x0c /* Interrupt level */
#define SREG_SPMASK 0x03 /* Mask for switch pack */
unsigned char swsreg;
- unsigned int
+ unsigned int
gotirq : 1, /* this instance got an irq */
is_pti : 1; /* Non-zero if this is a PTI board. */
};
diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c
index 7675ff0ca2ea..99a7b9f520ae 100644
--- a/drivers/scsi/scsi.c
+++ b/drivers/scsi/scsi.c
@@ -175,22 +175,6 @@ void scsi_log_completion(struct scsi_cmnd *cmd, int disposition)
#endif
/**
- * scsi_cmd_get_serial - Assign a serial number to a command
- * @host: the scsi host
- * @cmd: command to assign serial number to
- *
- * Description: a serial number identifies a request for error recovery
- * and debugging purposes. Protected by the Host_Lock of host.
- */
-void scsi_cmd_get_serial(struct Scsi_Host *host, struct scsi_cmnd *cmd)
-{
- cmd->serial_number = host->cmd_serial_number++;
- if (cmd->serial_number == 0)
- cmd->serial_number = host->cmd_serial_number++;
-}
-EXPORT_SYMBOL(scsi_cmd_get_serial);
-
-/**
* scsi_finish_command - cleanup and pass command back to upper layer
* @cmd: the command
*
diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index e27f4df24021..2740a90501a0 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -76,6 +76,7 @@ static const char *sdebug_version_date = "20190125";
#define LBA_OUT_OF_RANGE 0x21
#define INVALID_FIELD_IN_CDB 0x24
#define INVALID_FIELD_IN_PARAM_LIST 0x26
+#define WRITE_PROTECTED 0x27
#define UA_RESET_ASC 0x29
#define UA_CHANGED_ASC 0x2a
#define TARGET_CHANGED_ASC 0x3f
@@ -351,12 +352,11 @@ enum sdeb_opcode_index {
SDEB_I_ATA_PT = 22, /* 12, 16 */
SDEB_I_SEND_DIAG = 23,
SDEB_I_UNMAP = 24,
- SDEB_I_XDWRITEREAD = 25, /* 10 only */
- SDEB_I_WRITE_BUFFER = 26,
- SDEB_I_WRITE_SAME = 27, /* 10, 16 */
- SDEB_I_SYNC_CACHE = 28, /* 10, 16 */
- SDEB_I_COMP_WRITE = 29,
- SDEB_I_LAST_ELEMENT = 30, /* keep this last (previous + 1) */
+ SDEB_I_WRITE_BUFFER = 25,
+ SDEB_I_WRITE_SAME = 26, /* 10, 16 */
+ SDEB_I_SYNC_CACHE = 27, /* 10, 16 */
+ SDEB_I_COMP_WRITE = 28,
+ SDEB_I_LAST_ELEMENT = 29, /* keep this last (previous + 1) */
};
@@ -377,7 +377,7 @@ static const unsigned char opcode_ind_arr[256] = {
/* 0x40; 0x40->0x5f: 10 byte cdbs */
0, SDEB_I_WRITE_SAME, SDEB_I_UNMAP, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, SDEB_I_LOG_SENSE, 0, 0,
- 0, 0, 0, SDEB_I_XDWRITEREAD, 0, SDEB_I_MODE_SELECT, SDEB_I_RESERVE,
+ 0, 0, 0, 0, 0, SDEB_I_MODE_SELECT, SDEB_I_RESERVE,
SDEB_I_RELEASE,
0, 0, SDEB_I_MODE_SENSE, 0, 0, 0, 0, 0,
/* 0x60; 0x60->0x7d are reserved, 0x7e is "extended cdb" */
@@ -430,7 +430,6 @@ static int resp_rsup_opcodes(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_rsup_tmfs(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_write_same_10(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_write_same_16(struct scsi_cmnd *, struct sdebug_dev_info *);
-static int resp_xdwriteread_10(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_comp_write(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_write_buffer(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_sync_cache(struct scsi_cmnd *, struct sdebug_dev_info *);
@@ -600,9 +599,6 @@ static const struct opcode_info_t opcode_info_arr[SDEB_I_LAST_ELEMENT + 1] = {
{0, 0x42, 0, F_D_OUT | FF_MEDIA_IO, resp_unmap, NULL, /* UNMAP */
{10, 0x1, 0, 0, 0, 0, 0x3f, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0} },
/* 25 */
- {0, 0x53, 0, F_D_IN | F_D_OUT | FF_MEDIA_IO, resp_xdwriteread_10,
- NULL, {10, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xc7,
- 0, 0, 0, 0, 0, 0} }, /* XDWRITEREAD(10) */
{0, 0x3b, 0, F_D_OUT_MAYBE, resp_write_buffer, NULL,
{10, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc7, 0, 0,
0, 0, 0, 0} }, /* WRITE_BUFFER */
@@ -618,7 +614,7 @@ static const struct opcode_info_t opcode_info_arr[SDEB_I_LAST_ELEMENT + 1] = {
{16, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0,
0, 0xff, 0x3f, 0xc7} }, /* COMPARE AND WRITE */
-/* 30 */
+/* 29 */
{0xff, 0, 0, 0, NULL, NULL, /* terminating element */
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
};
@@ -673,6 +669,7 @@ static bool sdebug_verbose;
static bool have_dif_prot;
static bool write_since_sync;
static bool sdebug_statistics = DEF_STATISTICS;
+static bool sdebug_wp;
static unsigned int sdebug_store_sectors;
static sector_t sdebug_capacity; /* in sectors */
@@ -836,7 +833,8 @@ static void mk_sense_invalid_opcode(struct scsi_cmnd *scp)
mk_sense_buffer(scp, ILLEGAL_REQUEST, INVALID_OPCODE, 0);
}
-static int scsi_debug_ioctl(struct scsi_device *dev, int cmd, void __user *arg)
+static int scsi_debug_ioctl(struct scsi_device *dev, unsigned int cmd,
+ void __user *arg)
{
if (sdebug_verbose) {
if (0x1261 == cmd)
@@ -1010,16 +1008,16 @@ static int fill_from_dev_buffer(struct scsi_cmnd *scp, unsigned char *arr,
int arr_len)
{
int act_len;
- struct scsi_data_buffer *sdb = scsi_in(scp);
+ struct scsi_data_buffer *sdb = &scp->sdb;
if (!sdb->length)
return 0;
- if (!(scsi_bidi_cmnd(scp) || scp->sc_data_direction == DMA_FROM_DEVICE))
+ if (scp->sc_data_direction != DMA_FROM_DEVICE)
return DID_ERROR << 16;
act_len = sg_copy_from_buffer(sdb->table.sgl, sdb->table.nents,
arr, arr_len);
- sdb->resid = scsi_bufflen(scp) - act_len;
+ scsi_set_resid(scp, scsi_bufflen(scp) - act_len);
return 0;
}
@@ -1033,20 +1031,21 @@ static int p_fill_from_dev_buffer(struct scsi_cmnd *scp, const void *arr,
int arr_len, unsigned int off_dst)
{
int act_len, n;
- struct scsi_data_buffer *sdb = scsi_in(scp);
+ struct scsi_data_buffer *sdb = &scp->sdb;
off_t skip = off_dst;
if (sdb->length <= off_dst)
return 0;
- if (!(scsi_bidi_cmnd(scp) || scp->sc_data_direction == DMA_FROM_DEVICE))
+ if (scp->sc_data_direction != DMA_FROM_DEVICE)
return DID_ERROR << 16;
act_len = sg_pcopy_from_buffer(sdb->table.sgl, sdb->table.nents,
arr, arr_len, skip);
pr_debug("%s: off_dst=%u, scsi_bufflen=%u, act_len=%u, resid=%d\n",
- __func__, off_dst, scsi_bufflen(scp), act_len, sdb->resid);
+ __func__, off_dst, scsi_bufflen(scp), act_len,
+ scsi_get_resid(scp));
n = (int)scsi_bufflen(scp) - ((int)off_dst + act_len);
- sdb->resid = min(sdb->resid, n);
+ scsi_set_resid(scp, min(scsi_get_resid(scp), n));
return 0;
}
@@ -1058,7 +1057,7 @@ static int fetch_to_dev_buffer(struct scsi_cmnd *scp, unsigned char *arr,
{
if (!scsi_bufflen(scp))
return 0;
- if (!(scsi_bidi_cmnd(scp) || scp->sc_data_direction == DMA_TO_DEVICE))
+ if (scp->sc_data_direction != DMA_TO_DEVICE)
return -1;
return scsi_sg_copy_to_buffer(scp, arr, arr_len);
@@ -2146,9 +2145,11 @@ static int resp_mode_sense(struct scsi_cmnd *scp,
target_dev_id = ((devip->sdbg_host->shost->host_no + 1) * 2000) +
(devip->target * 1000) - 3;
/* for disks set DPOFUA bit and clear write protect (WP) bit */
- if (is_disk)
+ if (is_disk) {
dev_spec = 0x10; /* =0x90 if WP=1 implies read-only */
- else
+ if (sdebug_wp)
+ dev_spec |= 0x80;
+ } else
dev_spec = 0x0;
if (msense_6) {
arr[2] = dev_spec;
@@ -2331,6 +2332,10 @@ static int resp_mode_select(struct scsi_cmnd *scp,
if (ctrl_m_pg[1] == arr[off + 1]) {
memcpy(ctrl_m_pg + 2, arr + off + 2,
sizeof(ctrl_m_pg) - 2);
+ if (ctrl_m_pg[4] & 0x8)
+ sdebug_wp = true;
+ else
+ sdebug_wp = false;
sdebug_dsense = !!(ctrl_m_pg[2] & 0x4);
goto set_mode_changed_ua;
}
@@ -2455,8 +2460,8 @@ static int resp_log_sense(struct scsi_cmnd *scp,
min(len, SDEBUG_MAX_INQ_ARR_SZ));
}
-static int check_device_access_params(struct scsi_cmnd *scp,
- unsigned long long lba, unsigned int num)
+static inline int check_device_access_params(struct scsi_cmnd *scp,
+ unsigned long long lba, unsigned int num, bool write)
{
if (lba + num > sdebug_capacity) {
mk_sense_buffer(scp, ILLEGAL_REQUEST, LBA_OUT_OF_RANGE, 0);
@@ -2468,6 +2473,10 @@ static int check_device_access_params(struct scsi_cmnd *scp,
mk_sense_buffer(scp, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, 0);
return check_condition_result;
}
+ if (write && unlikely(sdebug_wp)) {
+ mk_sense_buffer(scp, DATA_PROTECT, WRITE_PROTECTED, 0x2);
+ return check_condition_result;
+ }
return 0;
}
@@ -2477,21 +2486,19 @@ static int do_device_access(struct scsi_cmnd *scmd, u32 sg_skip, u64 lba,
{
int ret;
u64 block, rest = 0;
- struct scsi_data_buffer *sdb;
+ struct scsi_data_buffer *sdb = &scmd->sdb;
enum dma_data_direction dir;
if (do_write) {
- sdb = scsi_out(scmd);
dir = DMA_TO_DEVICE;
write_since_sync = true;
} else {
- sdb = scsi_in(scmd);
dir = DMA_FROM_DEVICE;
}
if (!sdb->length)
return 0;
- if (!(scsi_bidi_cmnd(scmd) || scmd->sc_data_direction == dir))
+ if (scmd->sc_data_direction != dir)
return -1;
block = do_div(lba, sdebug_store_sectors);
@@ -2728,18 +2735,9 @@ static int resp_read_dt0(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
} else
sqcp = NULL;
- /* inline check_device_access_params() */
- if (unlikely(lba + num > sdebug_capacity)) {
- mk_sense_buffer(scp, ILLEGAL_REQUEST, LBA_OUT_OF_RANGE, 0);
- return check_condition_result;
- }
- /* transfer length excessive (tie in to block limits VPD page) */
- if (unlikely(num > sdebug_store_sectors)) {
- /* needs work to find which cdb byte 'num' comes from */
- mk_sense_buffer(scp, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, 0);
- return check_condition_result;
- }
-
+ ret = check_device_access_params(scp, lba, num, false);
+ if (ret)
+ return ret;
if (unlikely((SDEBUG_OPT_MEDIUM_ERR & sdebug_opts) &&
(lba <= (sdebug_medium_error_start + sdebug_medium_error_count - 1)) &&
((lba + num) > sdebug_medium_error_start))) {
@@ -2774,7 +2772,7 @@ static int resp_read_dt0(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
if (unlikely(ret == -1))
return DID_ERROR << 16;
- scsi_in(scp)->resid = scsi_bufflen(scp) - ret;
+ scsi_set_resid(scp, scsi_bufflen(scp) - ret);
if (unlikely(sqcp)) {
if (sqcp->inj_recovered) {
@@ -3031,19 +3029,9 @@ static int resp_write_dt0(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
sdev_printk(KERN_ERR, scp->device, "Unprotected WR "
"to DIF device\n");
}
-
- /* inline check_device_access_params() */
- if (unlikely(lba + num > sdebug_capacity)) {
- mk_sense_buffer(scp, ILLEGAL_REQUEST, LBA_OUT_OF_RANGE, 0);
- return check_condition_result;
- }
- /* transfer length excessive (tie in to block limits VPD page) */
- if (unlikely(num > sdebug_store_sectors)) {
- /* needs work to find which cdb byte 'num' comes from */
- mk_sense_buffer(scp, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, 0);
- return check_condition_result;
- }
-
+ ret = check_device_access_params(scp, lba, num, true);
+ if (ret)
+ return ret;
write_lock_irqsave(&atomic_rw, iflags);
/* DIX + T10 DIF */
@@ -3182,7 +3170,7 @@ static int resp_write_scat(struct scsi_cmnd *scp,
my_name, __func__, k, lba, num, sg_off);
if (num == 0)
continue;
- ret = check_device_access_params(scp, lba, num);
+ ret = check_device_access_params(scp, lba, num, true);
if (ret)
goto err_out_unlock;
num_by = num * lb_size;
@@ -3268,7 +3256,7 @@ static int resp_write_same(struct scsi_cmnd *scp, u64 lba, u32 num,
u64 block, lbaa;
u8 *fs1p;
- ret = check_device_access_params(scp, lba, num);
+ ret = check_device_access_params(scp, lba, num, true);
if (ret)
return ret;
@@ -3440,18 +3428,9 @@ static int resp_comp_write(struct scsi_cmnd *scp,
(cmd[1] & 0xe0) == 0)
sdev_printk(KERN_ERR, scp->device, "Unprotected WR "
"to DIF device\n");
-
- /* inline check_device_access_params() */
- if (lba + num > sdebug_capacity) {
- mk_sense_buffer(scp, ILLEGAL_REQUEST, LBA_OUT_OF_RANGE, 0);
- return check_condition_result;
- }
- /* transfer length excessive (tie in to block limits VPD page) */
- if (num > sdebug_store_sectors) {
- /* needs work to find which cdb byte 'num' comes from */
- mk_sense_buffer(scp, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, 0);
- return check_condition_result;
- }
+ ret = check_device_access_params(scp, lba, num, false);
+ if (ret)
+ return ret;
dnum = 2 * num;
arr = kcalloc(lb_size, dnum, GFP_ATOMIC);
if (NULL == arr) {
@@ -3534,7 +3513,7 @@ static int resp_unmap(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
unsigned long long lba = get_unaligned_be64(&desc[i].lba);
unsigned int num = get_unaligned_be32(&desc[i].blocks);
- ret = check_device_access_params(scp, lba, num);
+ ret = check_device_access_params(scp, lba, num, true);
if (ret)
goto out;
@@ -3567,7 +3546,7 @@ static int resp_get_lba_status(struct scsi_cmnd *scp,
if (alloc_len < 24)
return 0;
- ret = check_device_access_params(scp, lba, 1);
+ ret = check_device_access_params(scp, lba, 1, false);
if (ret)
return ret;
@@ -3719,68 +3698,6 @@ static int resp_report_luns(struct scsi_cmnd *scp,
return res;
}
-static int resp_xdwriteread(struct scsi_cmnd *scp, unsigned long long lba,
- unsigned int num, struct sdebug_dev_info *devip)
-{
- int j;
- unsigned char *kaddr, *buf;
- unsigned int offset;
- struct scsi_data_buffer *sdb = scsi_in(scp);
- struct sg_mapping_iter miter;
-
- /* better not to use temporary buffer. */
- buf = kzalloc(scsi_bufflen(scp), GFP_ATOMIC);
- if (!buf) {
- mk_sense_buffer(scp, ILLEGAL_REQUEST, INSUFF_RES_ASC,
- INSUFF_RES_ASCQ);
- return check_condition_result;
- }
-
- scsi_sg_copy_to_buffer(scp, buf, scsi_bufflen(scp));
-
- offset = 0;
- sg_miter_start(&miter, sdb->table.sgl, sdb->table.nents,
- SG_MITER_ATOMIC | SG_MITER_TO_SG);
-
- while (sg_miter_next(&miter)) {
- kaddr = miter.addr;
- for (j = 0; j < miter.length; j++)
- *(kaddr + j) ^= *(buf + offset + j);
-
- offset += miter.length;
- }
- sg_miter_stop(&miter);
- kfree(buf);
-
- return 0;
-}
-
-static int resp_xdwriteread_10(struct scsi_cmnd *scp,
- struct sdebug_dev_info *devip)
-{
- u8 *cmd = scp->cmnd;
- u64 lba;
- u32 num;
- int errsts;
-
- if (!scsi_bidi_cmnd(scp)) {
- mk_sense_buffer(scp, ILLEGAL_REQUEST, INSUFF_RES_ASC,
- INSUFF_RES_ASCQ);
- return check_condition_result;
- }
- errsts = resp_read_dt0(scp, devip);
- if (errsts)
- return errsts;
- if (!(cmd[1] & 0x4)) { /* DISABLE_WRITE is not set */
- errsts = resp_write_dt0(scp, devip);
- if (errsts)
- return errsts;
- }
- lba = get_unaligned_be32(cmd + 2);
- num = get_unaligned_be16(cmd + 7);
- return resp_xdwriteread(scp, lba, num, devip);
-}
-
static struct sdebug_queue *get_queue(struct scsi_cmnd *cmnd)
{
u32 tag = blk_mq_unique_tag(cmnd->request);
@@ -3954,7 +3871,6 @@ static int scsi_debug_slave_alloc(struct scsi_device *sdp)
if (sdebug_verbose)
pr_info("slave_alloc <%u %u %u %llu>\n",
sdp->host->host_no, sdp->channel, sdp->id, sdp->lun);
- blk_queue_flag_set(QUEUE_FLAG_BIDI, sdp->request_queue);
return 0;
}
@@ -4554,6 +4470,7 @@ module_param_named(virtual_gb, sdebug_virtual_gb, int, S_IRUGO | S_IWUSR);
module_param_named(uuid_ctl, sdebug_uuid_ctl, int, S_IRUGO);
module_param_named(vpd_use_hostno, sdebug_vpd_use_hostno, int,
S_IRUGO | S_IWUSR);
+module_param_named(wp, sdebug_wp, bool, S_IRUGO | S_IWUSR);
module_param_named(write_same_length, sdebug_write_same_length, int,
S_IRUGO | S_IWUSR);
@@ -4613,6 +4530,7 @@ MODULE_PARM_DESC(uuid_ctl,
"1->use uuid for lu name, 0->don't, 2->all use same (def=0)");
MODULE_PARM_DESC(virtual_gb, "virtual gigabyte (GiB) size (def=0 -> use dev_size_mb)");
MODULE_PARM_DESC(vpd_use_hostno, "0 -> dev ids ignore hostno (def=1 -> unique dev ids)");
+MODULE_PARM_DESC(wp, "Write Protect (def=0)");
MODULE_PARM_DESC(write_same_length, "Maximum blocks per WRITE SAME cmd (def=0xffff)");
#define SDEBUG_INFO_LEN 256
diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c
index 16eef068e9e9..1b8378f36139 100644
--- a/drivers/scsi/scsi_error.c
+++ b/drivers/scsi/scsi_error.c
@@ -965,7 +965,6 @@ void scsi_eh_prep_cmnd(struct scsi_cmnd *scmd, struct scsi_eh_save *ses,
ses->cmnd = scmd->cmnd;
ses->data_direction = scmd->sc_data_direction;
ses->sdb = scmd->sdb;
- ses->next_rq = scmd->request->next_rq;
ses->result = scmd->result;
ses->underflow = scmd->underflow;
ses->prot_op = scmd->prot_op;
@@ -976,7 +975,6 @@ void scsi_eh_prep_cmnd(struct scsi_cmnd *scmd, struct scsi_eh_save *ses,
scmd->cmnd = ses->eh_cmnd;
memset(scmd->cmnd, 0, BLK_MAX_CDB);
memset(&scmd->sdb, 0, sizeof(scmd->sdb));
- scmd->request->next_rq = NULL;
scmd->result = 0;
if (sense_bytes) {
@@ -1029,7 +1027,6 @@ void scsi_eh_restore_cmnd(struct scsi_cmnd* scmd, struct scsi_eh_save *ses)
scmd->cmnd = ses->cmnd;
scmd->sc_data_direction = ses->data_direction;
scmd->sdb = ses->sdb;
- scmd->request->next_rq = ses->next_rq;
scmd->result = ses->result;
scmd->underflow = ses->underflow;
scmd->prot_op = ses->prot_op;
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index a6828391d6b3..601b9f1de267 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -316,7 +316,6 @@ EXPORT_SYMBOL(__scsi_execute);
*/
static void scsi_init_cmd_errh(struct scsi_cmnd *cmd)
{
- cmd->serial_number = 0;
scsi_set_resid(cmd, 0);
memset(cmd->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE);
if (cmd->cmd_len == 0)
@@ -556,15 +555,8 @@ static void scsi_uninit_cmd(struct scsi_cmnd *cmd)
static void scsi_mq_free_sgtables(struct scsi_cmnd *cmd)
{
- struct scsi_data_buffer *sdb;
-
if (cmd->sdb.table.nents)
sg_free_table_chained(&cmd->sdb.table, true);
- if (cmd->request->next_rq) {
- sdb = cmd->request->next_rq->special;
- if (sdb)
- sg_free_table_chained(&sdb->table, true);
- }
if (scsi_prot_sg_count(cmd))
sg_free_table_chained(&cmd->prot_sdb->table, true);
}
@@ -578,7 +570,7 @@ static void scsi_mq_uninit_cmd(struct scsi_cmnd *cmd)
/* Returns false when no more bytes to process, true if there are more */
static bool scsi_end_request(struct request *req, blk_status_t error,
- unsigned int bytes, unsigned int bidi_bytes)
+ unsigned int bytes)
{
struct scsi_cmnd *cmd = blk_mq_rq_to_pdu(req);
struct scsi_device *sdev = cmd->device;
@@ -587,21 +579,23 @@ static bool scsi_end_request(struct request *req, blk_status_t error,
if (blk_update_request(req, error, bytes))
return true;
- /* Bidi request must be completed as a whole */
- if (unlikely(bidi_bytes) &&
- blk_update_request(req->next_rq, error, bidi_bytes))
- return true;
-
if (blk_queue_add_random(q))
add_disk_randomness(req->rq_disk);
if (!blk_rq_is_scsi(req)) {
WARN_ON_ONCE(!(cmd->flags & SCMD_INITIALIZED));
cmd->flags &= ~SCMD_INITIALIZED;
- destroy_rcu_head(&cmd->rcu);
}
/*
+ * Calling rcu_barrier() is not necessary here because the
+ * SCSI error handler guarantees that the function called by
+ * call_rcu() has been called before scsi_end_request() is
+ * called.
+ */
+ destroy_rcu_head(&cmd->rcu);
+
+ /*
* In the MQ case the command gets freed by __blk_mq_end_request,
* so we have to do all cleanup that depends on it earlier.
*
@@ -817,7 +811,7 @@ static void scsi_io_completion_action(struct scsi_cmnd *cmd, int result)
scsi_print_command(cmd);
}
}
- if (!scsi_end_request(req, blk_stat, blk_rq_err_bytes(req), 0))
+ if (!scsi_end_request(req, blk_stat, blk_rq_err_bytes(req)))
return;
/*FALLTHRU*/
case ACTION_REPREP:
@@ -951,30 +945,6 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes)
* scsi_result_to_blk_status may have reset the host_byte
*/
scsi_req(req)->result = cmd->result;
- scsi_req(req)->resid_len = scsi_get_resid(cmd);
-
- if (unlikely(scsi_bidi_cmnd(cmd))) {
- /*
- * Bidi commands Must be complete as a whole,
- * both sides at once.
- */
- scsi_req(req->next_rq)->resid_len = scsi_in(cmd)->resid;
- if (scsi_end_request(req, BLK_STS_OK, blk_rq_bytes(req),
- blk_rq_bytes(req->next_rq)))
- WARN_ONCE(true,
- "Bidi command with remaining bytes");
- return;
- }
- }
-
- /* no bidi support yet, other than in pass-through */
- if (unlikely(blk_bidi_rq(req))) {
- WARN_ONCE(true, "Only support bidi command in passthrough");
- scmd_printk(KERN_ERR, cmd, "Killing bidi command\n");
- if (scsi_end_request(req, BLK_STS_IOERR, blk_rq_bytes(req),
- blk_rq_bytes(req->next_rq)))
- WARN_ONCE(true, "Bidi command with remaining bytes");
- return;
}
/*
@@ -991,13 +961,13 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes)
* to retry code. Fast path should return in this block.
*/
if (likely(blk_rq_bytes(req) > 0 || blk_stat == BLK_STS_OK)) {
- if (likely(!scsi_end_request(req, blk_stat, good_bytes, 0)))
+ if (likely(!scsi_end_request(req, blk_stat, good_bytes)))
return; /* no bytes remaining */
}
/* Kill remainder if no retries. */
if (unlikely(blk_stat && scsi_noretry_cmd(cmd))) {
- if (scsi_end_request(req, blk_stat, blk_rq_bytes(req), 0))
+ if (scsi_end_request(req, blk_stat, blk_rq_bytes(req)))
WARN_ONCE(true,
"Bytes remaining after failed, no-retry command");
return;
@@ -1059,12 +1029,6 @@ blk_status_t scsi_init_io(struct scsi_cmnd *cmd)
if (ret)
return ret;
- if (blk_bidi_rq(rq)) {
- ret = scsi_init_sgtable(rq->next_rq, rq->next_rq->special);
- if (ret)
- goto out_free_sgtables;
- }
-
if (blk_integrity_rq(rq)) {
struct scsi_data_buffer *prot_sdb = cmd->prot_sdb;
int ivecs, count;
@@ -1608,10 +1572,7 @@ static blk_status_t scsi_mq_prep_fn(struct request *req)
scsi_init_command(sdev, cmd);
- req->special = cmd;
-
cmd->request = req;
-
cmd->tag = req->tag;
cmd->prot_op = SCSI_PROT_NORMAL;
@@ -1625,17 +1586,6 @@ static blk_status_t scsi_mq_prep_fn(struct request *req)
(struct scatterlist *)(cmd->prot_sdb + 1);
}
- if (blk_bidi_rq(req)) {
- struct request *next_rq = req->next_rq;
- struct scsi_data_buffer *bidi_sdb = blk_mq_rq_to_pdu(next_rq);
-
- memset(bidi_sdb, 0, sizeof(struct scsi_data_buffer));
- bidi_sdb->table.sgl =
- (struct scatterlist *)(bidi_sdb + 1);
-
- next_rq->special = bidi_sdb;
- }
-
blk_mq_start_request(req);
return scsi_setup_cmnd(sdev, req);
@@ -1713,13 +1663,13 @@ static blk_status_t scsi_queue_rq(struct blk_mq_hw_ctx *hctx,
if (!scsi_host_queue_ready(q, shost, sdev))
goto out_dec_target_busy;
- clear_bit(SCMD_STATE_COMPLETE, &cmd->state);
if (!(req->rq_flags & RQF_DONTPREP)) {
ret = scsi_mq_prep_fn(req);
if (ret != BLK_STS_OK)
goto out_dec_host_busy;
req->rq_flags |= RQF_DONTPREP;
} else {
+ clear_bit(SCMD_STATE_COMPLETE, &cmd->state);
blk_mq_start_request(req);
}
@@ -1900,7 +1850,7 @@ int scsi_mq_setup_tags(struct Scsi_Host *shost)
shost->tag_set.queue_depth = shost->can_queue;
shost->tag_set.cmd_size = cmd_size;
shost->tag_set.numa_node = NUMA_NO_NODE;
- shost->tag_set.flags = BLK_MQ_F_SHOULD_MERGE | BLK_MQ_F_SG_MERGE;
+ shost->tag_set.flags = BLK_MQ_F_SHOULD_MERGE;
shost->tag_set.flags |=
BLK_ALLOC_POLICY_TO_MQ_FLAG(shost->hostt->tag_alloc_policy);
shost->tag_set.driver_data = shost;
@@ -2598,8 +2548,10 @@ void scsi_device_resume(struct scsi_device *sdev)
* device deleted during suspend)
*/
mutex_lock(&sdev->state_mutex);
- sdev->quiesced_by = NULL;
- blk_clear_pm_only(sdev->request_queue);
+ if (sdev->quiesced_by) {
+ sdev->quiesced_by = NULL;
+ blk_clear_pm_only(sdev->request_queue);
+ }
if (sdev->sdev_state == SDEV_QUIESCE)
scsi_device_set_state(sdev, SDEV_RUNNING);
mutex_unlock(&sdev->state_mutex);
diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
index dd0d516f65e2..53380e07b40e 100644
--- a/drivers/scsi/scsi_scan.c
+++ b/drivers/scsi/scsi_scan.c
@@ -220,7 +220,7 @@ static struct scsi_device *scsi_alloc_sdev(struct scsi_target *starget,
struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
sdev = kzalloc(sizeof(*sdev) + shost->transportt->device_size,
- GFP_ATOMIC);
+ GFP_KERNEL);
if (!sdev)
goto out;
@@ -788,7 +788,7 @@ static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result,
*/
sdev->inquiry = kmemdup(inq_result,
max_t(size_t, sdev->inquiry_len, 36),
- GFP_ATOMIC);
+ GFP_KERNEL);
if (sdev->inquiry == NULL)
return SCSI_SCAN_NO_RESPONSE;
@@ -1079,7 +1079,7 @@ static int scsi_probe_and_add_lun(struct scsi_target *starget,
if (!sdev)
goto out;
- result = kmalloc(result_len, GFP_ATOMIC |
+ result = kmalloc(result_len, GFP_KERNEL |
((shost->unchecked_isa_dma) ? __GFP_DMA : 0));
if (!result)
goto out_free_sdev;
diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c
index 6a9040faed00..3b119ca0cc0c 100644
--- a/drivers/scsi/scsi_sysfs.c
+++ b/drivers/scsi/scsi_sysfs.c
@@ -771,6 +771,12 @@ store_state_field(struct device *dev, struct device_attribute *attr,
mutex_lock(&sdev->state_mutex);
ret = scsi_device_set_state(sdev, state);
+ /*
+ * If the device state changes to SDEV_RUNNING, we need to run
+ * the queue to avoid I/O hang.
+ */
+ if (ret == 0 && state == SDEV_RUNNING)
+ blk_mq_run_hw_queues(sdev->request_queue, true);
mutex_unlock(&sdev->state_mutex);
return ret == 0 ? count : -EINVAL;
diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c
index 0508831d6fb9..0a82e93566dc 100644
--- a/drivers/scsi/scsi_transport_iscsi.c
+++ b/drivers/scsi/scsi_transport_iscsi.c
@@ -2200,6 +2200,8 @@ void iscsi_remove_session(struct iscsi_cls_session *session)
scsi_target_unblock(&session->dev, SDEV_TRANSPORT_OFFLINE);
/* flush running scans then delete devices */
flush_work(&session->scan_work);
+ /* flush running unbind operations */
+ flush_work(&session->unbind_work);
__iscsi_unbind_session(&session->unbind_work);
/* hw iscsi may not have removed all connections from session */
diff --git a/drivers/scsi/scsi_transport_sas.c b/drivers/scsi/scsi_transport_sas.c
index 692b46937e52..60f1a81d2034 100644
--- a/drivers/scsi/scsi_transport_sas.c
+++ b/drivers/scsi/scsi_transport_sas.c
@@ -213,7 +213,6 @@ static int sas_bsg_initialize(struct Scsi_Host *shost, struct sas_rphy *rphy)
to_sas_host_attrs(shost)->q = q;
}
- blk_queue_flag_set(QUEUE_FLAG_BIDI, q);
return 0;
}
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index 5464d467e23e..2b2bc4b49d78 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -665,6 +665,68 @@ static int sd_sec_submit(void *data, u16 spsp, u8 secp, void *buffer,
}
#endif /* CONFIG_BLK_SED_OPAL */
+/*
+ * Look up the DIX operation based on whether the command is read or
+ * write and whether dix and dif are enabled.
+ */
+static unsigned int sd_prot_op(bool write, bool dix, bool dif)
+{
+ /* Lookup table: bit 2 (write), bit 1 (dix), bit 0 (dif) */
+ static const unsigned int ops[] = { /* wrt dix dif */
+ SCSI_PROT_NORMAL, /* 0 0 0 */
+ SCSI_PROT_READ_STRIP, /* 0 0 1 */
+ SCSI_PROT_READ_INSERT, /* 0 1 0 */
+ SCSI_PROT_READ_PASS, /* 0 1 1 */
+ SCSI_PROT_NORMAL, /* 1 0 0 */
+ SCSI_PROT_WRITE_INSERT, /* 1 0 1 */
+ SCSI_PROT_WRITE_STRIP, /* 1 1 0 */
+ SCSI_PROT_WRITE_PASS, /* 1 1 1 */
+ };
+
+ return ops[write << 2 | dix << 1 | dif];
+}
+
+/*
+ * Returns a mask of the protection flags that are valid for a given DIX
+ * operation.
+ */
+static unsigned int sd_prot_flag_mask(unsigned int prot_op)
+{
+ static const unsigned int flag_mask[] = {
+ [SCSI_PROT_NORMAL] = 0,
+
+ [SCSI_PROT_READ_STRIP] = SCSI_PROT_TRANSFER_PI |
+ SCSI_PROT_GUARD_CHECK |
+ SCSI_PROT_REF_CHECK |
+ SCSI_PROT_REF_INCREMENT,
+
+ [SCSI_PROT_READ_INSERT] = SCSI_PROT_REF_INCREMENT |
+ SCSI_PROT_IP_CHECKSUM,
+
+ [SCSI_PROT_READ_PASS] = SCSI_PROT_TRANSFER_PI |
+ SCSI_PROT_GUARD_CHECK |
+ SCSI_PROT_REF_CHECK |
+ SCSI_PROT_REF_INCREMENT |
+ SCSI_PROT_IP_CHECKSUM,
+
+ [SCSI_PROT_WRITE_INSERT] = SCSI_PROT_TRANSFER_PI |
+ SCSI_PROT_REF_INCREMENT,
+
+ [SCSI_PROT_WRITE_STRIP] = SCSI_PROT_GUARD_CHECK |
+ SCSI_PROT_REF_CHECK |
+ SCSI_PROT_REF_INCREMENT |
+ SCSI_PROT_IP_CHECKSUM,
+
+ [SCSI_PROT_WRITE_PASS] = SCSI_PROT_TRANSFER_PI |
+ SCSI_PROT_GUARD_CHECK |
+ SCSI_PROT_REF_CHECK |
+ SCSI_PROT_REF_INCREMENT |
+ SCSI_PROT_IP_CHECKSUM,
+ };
+
+ return flag_mask[prot_op];
+}
+
static unsigned char sd_setup_protect_cmnd(struct scsi_cmnd *scmd,
unsigned int dix, unsigned int dif)
{
@@ -761,8 +823,8 @@ static blk_status_t sd_setup_unmap_cmnd(struct scsi_cmnd *cmd)
{
struct scsi_device *sdp = cmd->device;
struct request *rq = cmd->request;
- u64 sector = blk_rq_pos(rq) >> (ilog2(sdp->sector_size) - 9);
- u32 nr_sectors = blk_rq_sectors(rq) >> (ilog2(sdp->sector_size) - 9);
+ u64 lba = sectors_to_logical(sdp, blk_rq_pos(rq));
+ u32 nr_blocks = sectors_to_logical(sdp, blk_rq_sectors(rq));
unsigned int data_len = 24;
char *buf;
@@ -781,13 +843,12 @@ static blk_status_t sd_setup_unmap_cmnd(struct scsi_cmnd *cmd)
buf = page_address(rq->special_vec.bv_page);
put_unaligned_be16(6 + 16, &buf[0]);
put_unaligned_be16(16, &buf[2]);
- put_unaligned_be64(sector, &buf[8]);
- put_unaligned_be32(nr_sectors, &buf[16]);
+ put_unaligned_be64(lba, &buf[8]);
+ put_unaligned_be32(nr_blocks, &buf[16]);
cmd->allowed = SD_MAX_RETRIES;
cmd->transfersize = data_len;
rq->timeout = SD_TIMEOUT;
- scsi_req(rq)->resid_len = data_len;
return scsi_init_io(cmd);
}
@@ -797,8 +858,8 @@ static blk_status_t sd_setup_write_same16_cmnd(struct scsi_cmnd *cmd,
{
struct scsi_device *sdp = cmd->device;
struct request *rq = cmd->request;
- u64 sector = blk_rq_pos(rq) >> (ilog2(sdp->sector_size) - 9);
- u32 nr_sectors = blk_rq_sectors(rq) >> (ilog2(sdp->sector_size) - 9);
+ u64 lba = sectors_to_logical(sdp, blk_rq_pos(rq));
+ u32 nr_blocks = sectors_to_logical(sdp, blk_rq_sectors(rq));
u32 data_len = sdp->sector_size;
rq->special_vec.bv_page = mempool_alloc(sd_page_pool, GFP_ATOMIC);
@@ -813,13 +874,12 @@ static blk_status_t sd_setup_write_same16_cmnd(struct scsi_cmnd *cmd,
cmd->cmnd[0] = WRITE_SAME_16;
if (unmap)
cmd->cmnd[1] = 0x8; /* UNMAP */
- put_unaligned_be64(sector, &cmd->cmnd[2]);
- put_unaligned_be32(nr_sectors, &cmd->cmnd[10]);
+ put_unaligned_be64(lba, &cmd->cmnd[2]);
+ put_unaligned_be32(nr_blocks, &cmd->cmnd[10]);
cmd->allowed = SD_MAX_RETRIES;
cmd->transfersize = data_len;
rq->timeout = unmap ? SD_TIMEOUT : SD_WRITE_SAME_TIMEOUT;
- scsi_req(rq)->resid_len = data_len;
return scsi_init_io(cmd);
}
@@ -829,8 +889,8 @@ static blk_status_t sd_setup_write_same10_cmnd(struct scsi_cmnd *cmd,
{
struct scsi_device *sdp = cmd->device;
struct request *rq = cmd->request;
- u64 sector = blk_rq_pos(rq) >> (ilog2(sdp->sector_size) - 9);
- u32 nr_sectors = blk_rq_sectors(rq) >> (ilog2(sdp->sector_size) - 9);
+ u64 lba = sectors_to_logical(sdp, blk_rq_pos(rq));
+ u32 nr_blocks = sectors_to_logical(sdp, blk_rq_sectors(rq));
u32 data_len = sdp->sector_size;
rq->special_vec.bv_page = mempool_alloc(sd_page_pool, GFP_ATOMIC);
@@ -845,13 +905,12 @@ static blk_status_t sd_setup_write_same10_cmnd(struct scsi_cmnd *cmd,
cmd->cmnd[0] = WRITE_SAME;
if (unmap)
cmd->cmnd[1] = 0x8; /* UNMAP */
- put_unaligned_be32(sector, &cmd->cmnd[2]);
- put_unaligned_be16(nr_sectors, &cmd->cmnd[7]);
+ put_unaligned_be32(lba, &cmd->cmnd[2]);
+ put_unaligned_be16(nr_blocks, &cmd->cmnd[7]);
cmd->allowed = SD_MAX_RETRIES;
cmd->transfersize = data_len;
rq->timeout = unmap ? SD_TIMEOUT : SD_WRITE_SAME_TIMEOUT;
- scsi_req(rq)->resid_len = data_len;
return scsi_init_io(cmd);
}
@@ -861,8 +920,8 @@ static blk_status_t sd_setup_write_zeroes_cmnd(struct scsi_cmnd *cmd)
struct request *rq = cmd->request;
struct scsi_device *sdp = cmd->device;
struct scsi_disk *sdkp = scsi_disk(rq->rq_disk);
- u64 sector = blk_rq_pos(rq) >> (ilog2(sdp->sector_size) - 9);
- u32 nr_sectors = blk_rq_sectors(rq) >> (ilog2(sdp->sector_size) - 9);
+ u64 lba = sectors_to_logical(sdp, blk_rq_pos(rq));
+ u32 nr_blocks = sectors_to_logical(sdp, blk_rq_sectors(rq));
if (!(rq->cmd_flags & REQ_NOUNMAP)) {
switch (sdkp->zeroing_mode) {
@@ -876,7 +935,7 @@ static blk_status_t sd_setup_write_zeroes_cmnd(struct scsi_cmnd *cmd)
if (sdp->no_write_same)
return BLK_STS_TARGET;
- if (sdkp->ws16 || sector > 0xffffffff || nr_sectors > 0xffff)
+ if (sdkp->ws16 || lba > 0xffffffff || nr_blocks > 0xffff)
return sd_setup_write_same16_cmnd(cmd, false);
return sd_setup_write_same10_cmnd(cmd, false);
@@ -957,9 +1016,8 @@ static blk_status_t sd_setup_write_same_cmnd(struct scsi_cmnd *cmd)
struct scsi_device *sdp = cmd->device;
struct scsi_disk *sdkp = scsi_disk(rq->rq_disk);
struct bio *bio = rq->bio;
- sector_t sector = blk_rq_pos(rq);
- unsigned int nr_sectors = blk_rq_sectors(rq);
- unsigned int nr_bytes = blk_rq_bytes(rq);
+ u64 lba = sectors_to_logical(sdp, blk_rq_pos(rq));
+ u32 nr_blocks = sectors_to_logical(sdp, blk_rq_sectors(rq));
blk_status_t ret;
if (sdkp->device->no_write_same)
@@ -967,21 +1025,18 @@ static blk_status_t sd_setup_write_same_cmnd(struct scsi_cmnd *cmd)
BUG_ON(bio_offset(bio) || bio_iovec(bio).bv_len != sdp->sector_size);
- sector >>= ilog2(sdp->sector_size) - 9;
- nr_sectors >>= ilog2(sdp->sector_size) - 9;
-
rq->timeout = SD_WRITE_SAME_TIMEOUT;
- if (sdkp->ws16 || sector > 0xffffffff || nr_sectors > 0xffff) {
+ if (sdkp->ws16 || lba > 0xffffffff || nr_blocks > 0xffff) {
cmd->cmd_len = 16;
cmd->cmnd[0] = WRITE_SAME_16;
- put_unaligned_be64(sector, &cmd->cmnd[2]);
- put_unaligned_be32(nr_sectors, &cmd->cmnd[10]);
+ put_unaligned_be64(lba, &cmd->cmnd[2]);
+ put_unaligned_be32(nr_blocks, &cmd->cmnd[10]);
} else {
cmd->cmd_len = 10;
cmd->cmnd[0] = WRITE_SAME;
- put_unaligned_be32(sector, &cmd->cmnd[2]);
- put_unaligned_be16(nr_sectors, &cmd->cmnd[7]);
+ put_unaligned_be32(lba, &cmd->cmnd[2]);
+ put_unaligned_be16(nr_blocks, &cmd->cmnd[7]);
}
cmd->transfersize = sdp->sector_size;
@@ -999,7 +1054,7 @@ static blk_status_t sd_setup_write_same_cmnd(struct scsi_cmnd *cmd)
*/
rq->__data_len = sdp->sector_size;
ret = scsi_init_io(cmd);
- rq->__data_len = nr_bytes;
+ rq->__data_len = blk_rq_bytes(rq);
return ret;
}
@@ -1020,224 +1075,186 @@ static blk_status_t sd_setup_flush_cmnd(struct scsi_cmnd *cmd)
return BLK_STS_OK;
}
-static blk_status_t sd_setup_read_write_cmnd(struct scsi_cmnd *SCpnt)
+static blk_status_t sd_setup_rw32_cmnd(struct scsi_cmnd *cmd, bool write,
+ sector_t lba, unsigned int nr_blocks,
+ unsigned char flags)
{
- struct request *rq = SCpnt->request;
- struct scsi_device *sdp = SCpnt->device;
- struct gendisk *disk = rq->rq_disk;
- struct scsi_disk *sdkp = scsi_disk(disk);
- sector_t block = blk_rq_pos(rq);
+ cmd->cmnd = mempool_alloc(sd_cdb_pool, GFP_ATOMIC);
+ if (unlikely(cmd->cmnd == NULL))
+ return BLK_STS_RESOURCE;
+
+ cmd->cmd_len = SD_EXT_CDB_SIZE;
+ memset(cmd->cmnd, 0, cmd->cmd_len);
+
+ cmd->cmnd[0] = VARIABLE_LENGTH_CMD;
+ cmd->cmnd[7] = 0x18; /* Additional CDB len */
+ cmd->cmnd[9] = write ? WRITE_32 : READ_32;
+ cmd->cmnd[10] = flags;
+ put_unaligned_be64(lba, &cmd->cmnd[12]);
+ put_unaligned_be32(lba, &cmd->cmnd[20]); /* Expected Indirect LBA */
+ put_unaligned_be32(nr_blocks, &cmd->cmnd[28]);
+
+ return BLK_STS_OK;
+}
+
+static blk_status_t sd_setup_rw16_cmnd(struct scsi_cmnd *cmd, bool write,
+ sector_t lba, unsigned int nr_blocks,
+ unsigned char flags)
+{
+ cmd->cmd_len = 16;
+ cmd->cmnd[0] = write ? WRITE_16 : READ_16;
+ cmd->cmnd[1] = flags;
+ cmd->cmnd[14] = 0;
+ cmd->cmnd[15] = 0;
+ put_unaligned_be64(lba, &cmd->cmnd[2]);
+ put_unaligned_be32(nr_blocks, &cmd->cmnd[10]);
+
+ return BLK_STS_OK;
+}
+
+static blk_status_t sd_setup_rw10_cmnd(struct scsi_cmnd *cmd, bool write,
+ sector_t lba, unsigned int nr_blocks,
+ unsigned char flags)
+{
+ cmd->cmd_len = 10;
+ cmd->cmnd[0] = write ? WRITE_10 : READ_10;
+ cmd->cmnd[1] = flags;
+ cmd->cmnd[6] = 0;
+ cmd->cmnd[9] = 0;
+ put_unaligned_be32(lba, &cmd->cmnd[2]);
+ put_unaligned_be16(nr_blocks, &cmd->cmnd[7]);
+
+ return BLK_STS_OK;
+}
+
+static blk_status_t sd_setup_rw6_cmnd(struct scsi_cmnd *cmd, bool write,
+ sector_t lba, unsigned int nr_blocks,
+ unsigned char flags)
+{
+ /* Avoid that 0 blocks gets translated into 256 blocks. */
+ if (WARN_ON_ONCE(nr_blocks == 0))
+ return BLK_STS_IOERR;
+
+ if (unlikely(flags & 0x8)) {
+ /*
+ * This happens only if this drive failed 10byte rw
+ * command with ILLEGAL_REQUEST during operation and
+ * thus turned off use_10_for_rw.
+ */
+ scmd_printk(KERN_ERR, cmd, "FUA write on READ/WRITE(6) drive\n");
+ return BLK_STS_IOERR;
+ }
+
+ cmd->cmd_len = 6;
+ cmd->cmnd[0] = write ? WRITE_6 : READ_6;
+ cmd->cmnd[1] = (lba >> 16) & 0x1f;
+ cmd->cmnd[2] = (lba >> 8) & 0xff;
+ cmd->cmnd[3] = lba & 0xff;
+ cmd->cmnd[4] = nr_blocks;
+ cmd->cmnd[5] = 0;
+
+ return BLK_STS_OK;
+}
+
+static blk_status_t sd_setup_read_write_cmnd(struct scsi_cmnd *cmd)
+{
+ struct request *rq = cmd->request;
+ struct scsi_device *sdp = cmd->device;
+ struct scsi_disk *sdkp = scsi_disk(rq->rq_disk);
+ sector_t lba = sectors_to_logical(sdp, blk_rq_pos(rq));
sector_t threshold;
- unsigned int this_count = blk_rq_sectors(rq);
- unsigned int dif, dix;
- unsigned char protect;
+ unsigned int nr_blocks = sectors_to_logical(sdp, blk_rq_sectors(rq));
+ bool dif, dix;
+ unsigned int mask = logical_to_sectors(sdp, 1) - 1;
+ bool write = rq_data_dir(rq) == WRITE;
+ unsigned char protect, fua;
blk_status_t ret;
- ret = scsi_init_io(SCpnt);
+ ret = scsi_init_io(cmd);
if (ret != BLK_STS_OK)
return ret;
- WARN_ON_ONCE(SCpnt != rq->special);
- SCSI_LOG_HLQUEUE(1,
- scmd_printk(KERN_INFO, SCpnt,
- "%s: block=%llu, count=%d\n",
- __func__, (unsigned long long)block, this_count));
-
- if (!sdp || !scsi_device_online(sdp) ||
- block + blk_rq_sectors(rq) > get_capacity(disk)) {
- SCSI_LOG_HLQUEUE(2, scmd_printk(KERN_INFO, SCpnt,
- "Finishing %u sectors\n",
- blk_rq_sectors(rq)));
- SCSI_LOG_HLQUEUE(2, scmd_printk(KERN_INFO, SCpnt,
- "Retry with 0x%p\n", SCpnt));
+ if (!scsi_device_online(sdp) || sdp->changed) {
+ scmd_printk(KERN_ERR, cmd, "device offline or changed\n");
return BLK_STS_IOERR;
}
- if (sdp->changed) {
- /*
- * quietly refuse to do anything to a changed disc until
- * the changed bit has been reset
- */
- /* printk("SCSI disk has been changed or is not present. Prohibiting further I/O.\n"); */
+ if (blk_rq_pos(rq) + blk_rq_sectors(rq) > get_capacity(rq->rq_disk)) {
+ scmd_printk(KERN_ERR, cmd, "access beyond end of device\n");
+ return BLK_STS_IOERR;
+ }
+
+ if ((blk_rq_pos(rq) & mask) || (blk_rq_sectors(rq) & mask)) {
+ scmd_printk(KERN_ERR, cmd, "request not aligned to the logical block size\n");
return BLK_STS_IOERR;
}
/*
- * Some SD card readers can't handle multi-sector accesses which touch
- * the last one or two hardware sectors. Split accesses as needed.
+ * Some SD card readers can't handle accesses which touch the
+ * last one or two logical blocks. Split accesses as needed.
*/
- threshold = get_capacity(disk) - SD_LAST_BUGGY_SECTORS *
- (sdp->sector_size / 512);
+ threshold = sdkp->capacity - SD_LAST_BUGGY_SECTORS;
- if (unlikely(sdp->last_sector_bug && block + this_count > threshold)) {
- if (block < threshold) {
+ if (unlikely(sdp->last_sector_bug && lba + nr_blocks > threshold)) {
+ if (lba < threshold) {
/* Access up to the threshold but not beyond */
- this_count = threshold - block;
+ nr_blocks = threshold - lba;
} else {
- /* Access only a single hardware sector */
- this_count = sdp->sector_size / 512;
+ /* Access only a single logical block */
+ nr_blocks = 1;
}
}
- SCSI_LOG_HLQUEUE(2, scmd_printk(KERN_INFO, SCpnt, "block=%llu\n",
- (unsigned long long)block));
+ fua = rq->cmd_flags & REQ_FUA ? 0x8 : 0;
+ dix = scsi_prot_sg_count(cmd);
+ dif = scsi_host_dif_capable(cmd->device->host, sdkp->protection_type);
- /*
- * If we have a 1K hardware sectorsize, prevent access to single
- * 512 byte sectors. In theory we could handle this - in fact
- * the scsi cdrom driver must be able to handle this because
- * we typically use 1K blocksizes, and cdroms typically have
- * 2K hardware sectorsizes. Of course, things are simpler
- * with the cdrom, since it is read-only. For performance
- * reasons, the filesystems should be able to handle this
- * and not force the scsi disk driver to use bounce buffers
- * for this.
- */
- if (sdp->sector_size == 1024) {
- if ((block & 1) || (blk_rq_sectors(rq) & 1)) {
- scmd_printk(KERN_ERR, SCpnt,
- "Bad block number requested\n");
- return BLK_STS_IOERR;
- }
- block = block >> 1;
- this_count = this_count >> 1;
- }
- if (sdp->sector_size == 2048) {
- if ((block & 3) || (blk_rq_sectors(rq) & 3)) {
- scmd_printk(KERN_ERR, SCpnt,
- "Bad block number requested\n");
- return BLK_STS_IOERR;
- }
- block = block >> 2;
- this_count = this_count >> 2;
- }
- if (sdp->sector_size == 4096) {
- if ((block & 7) || (blk_rq_sectors(rq) & 7)) {
- scmd_printk(KERN_ERR, SCpnt,
- "Bad block number requested\n");
- return BLK_STS_IOERR;
- }
- block = block >> 3;
- this_count = this_count >> 3;
- }
- if (rq_data_dir(rq) == WRITE) {
- SCpnt->cmnd[0] = WRITE_6;
-
- if (blk_integrity_rq(rq))
- t10_pi_prepare(SCpnt->request, sdkp->protection_type);
-
- } else if (rq_data_dir(rq) == READ) {
- SCpnt->cmnd[0] = READ_6;
- } else {
- scmd_printk(KERN_ERR, SCpnt, "Unknown command %d\n", req_op(rq));
- return BLK_STS_IOERR;
- }
-
- SCSI_LOG_HLQUEUE(2, scmd_printk(KERN_INFO, SCpnt,
- "%s %d/%u 512 byte blocks.\n",
- (rq_data_dir(rq) == WRITE) ?
- "writing" : "reading", this_count,
- blk_rq_sectors(rq)));
-
- dix = scsi_prot_sg_count(SCpnt);
- dif = scsi_host_dif_capable(SCpnt->device->host, sdkp->protection_type);
+ if (write && dix)
+ t10_pi_prepare(cmd->request, sdkp->protection_type);
if (dif || dix)
- protect = sd_setup_protect_cmnd(SCpnt, dix, dif);
+ protect = sd_setup_protect_cmnd(cmd, dix, dif);
else
protect = 0;
if (protect && sdkp->protection_type == T10_PI_TYPE2_PROTECTION) {
- SCpnt->cmnd = mempool_alloc(sd_cdb_pool, GFP_ATOMIC);
-
- if (unlikely(!SCpnt->cmnd))
- return BLK_STS_RESOURCE;
-
- SCpnt->cmd_len = SD_EXT_CDB_SIZE;
- memset(SCpnt->cmnd, 0, SCpnt->cmd_len);
- SCpnt->cmnd[0] = VARIABLE_LENGTH_CMD;
- SCpnt->cmnd[7] = 0x18;
- SCpnt->cmnd[9] = (rq_data_dir(rq) == READ) ? READ_32 : WRITE_32;
- SCpnt->cmnd[10] = protect | ((rq->cmd_flags & REQ_FUA) ? 0x8 : 0);
-
- /* LBA */
- SCpnt->cmnd[12] = sizeof(block) > 4 ? (unsigned char) (block >> 56) & 0xff : 0;
- SCpnt->cmnd[13] = sizeof(block) > 4 ? (unsigned char) (block >> 48) & 0xff : 0;
- SCpnt->cmnd[14] = sizeof(block) > 4 ? (unsigned char) (block >> 40) & 0xff : 0;
- SCpnt->cmnd[15] = sizeof(block) > 4 ? (unsigned char) (block >> 32) & 0xff : 0;
- SCpnt->cmnd[16] = (unsigned char) (block >> 24) & 0xff;
- SCpnt->cmnd[17] = (unsigned char) (block >> 16) & 0xff;
- SCpnt->cmnd[18] = (unsigned char) (block >> 8) & 0xff;
- SCpnt->cmnd[19] = (unsigned char) block & 0xff;
-
- /* Expected Indirect LBA */
- SCpnt->cmnd[20] = (unsigned char) (block >> 24) & 0xff;
- SCpnt->cmnd[21] = (unsigned char) (block >> 16) & 0xff;
- SCpnt->cmnd[22] = (unsigned char) (block >> 8) & 0xff;
- SCpnt->cmnd[23] = (unsigned char) block & 0xff;
-
- /* Transfer length */
- SCpnt->cmnd[28] = (unsigned char) (this_count >> 24) & 0xff;
- SCpnt->cmnd[29] = (unsigned char) (this_count >> 16) & 0xff;
- SCpnt->cmnd[30] = (unsigned char) (this_count >> 8) & 0xff;
- SCpnt->cmnd[31] = (unsigned char) this_count & 0xff;
- } else if (sdp->use_16_for_rw || (this_count > 0xffff)) {
- SCpnt->cmnd[0] += READ_16 - READ_6;
- SCpnt->cmnd[1] = protect | ((rq->cmd_flags & REQ_FUA) ? 0x8 : 0);
- SCpnt->cmnd[2] = sizeof(block) > 4 ? (unsigned char) (block >> 56) & 0xff : 0;
- SCpnt->cmnd[3] = sizeof(block) > 4 ? (unsigned char) (block >> 48) & 0xff : 0;
- SCpnt->cmnd[4] = sizeof(block) > 4 ? (unsigned char) (block >> 40) & 0xff : 0;
- SCpnt->cmnd[5] = sizeof(block) > 4 ? (unsigned char) (block >> 32) & 0xff : 0;
- SCpnt->cmnd[6] = (unsigned char) (block >> 24) & 0xff;
- SCpnt->cmnd[7] = (unsigned char) (block >> 16) & 0xff;
- SCpnt->cmnd[8] = (unsigned char) (block >> 8) & 0xff;
- SCpnt->cmnd[9] = (unsigned char) block & 0xff;
- SCpnt->cmnd[10] = (unsigned char) (this_count >> 24) & 0xff;
- SCpnt->cmnd[11] = (unsigned char) (this_count >> 16) & 0xff;
- SCpnt->cmnd[12] = (unsigned char) (this_count >> 8) & 0xff;
- SCpnt->cmnd[13] = (unsigned char) this_count & 0xff;
- SCpnt->cmnd[14] = SCpnt->cmnd[15] = 0;
- } else if ((this_count > 0xff) || (block > 0x1fffff) ||
- scsi_device_protection(SCpnt->device) ||
- SCpnt->device->use_10_for_rw) {
- SCpnt->cmnd[0] += READ_10 - READ_6;
- SCpnt->cmnd[1] = protect | ((rq->cmd_flags & REQ_FUA) ? 0x8 : 0);
- SCpnt->cmnd[2] = (unsigned char) (block >> 24) & 0xff;
- SCpnt->cmnd[3] = (unsigned char) (block >> 16) & 0xff;
- SCpnt->cmnd[4] = (unsigned char) (block >> 8) & 0xff;
- SCpnt->cmnd[5] = (unsigned char) block & 0xff;
- SCpnt->cmnd[6] = SCpnt->cmnd[9] = 0;
- SCpnt->cmnd[7] = (unsigned char) (this_count >> 8) & 0xff;
- SCpnt->cmnd[8] = (unsigned char) this_count & 0xff;
+ ret = sd_setup_rw32_cmnd(cmd, write, lba, nr_blocks,
+ protect | fua);
+ } else if (sdp->use_16_for_rw || (nr_blocks > 0xffff)) {
+ ret = sd_setup_rw16_cmnd(cmd, write, lba, nr_blocks,
+ protect | fua);
+ } else if ((nr_blocks > 0xff) || (lba > 0x1fffff) ||
+ sdp->use_10_for_rw || protect) {
+ ret = sd_setup_rw10_cmnd(cmd, write, lba, nr_blocks,
+ protect | fua);
} else {
- if (unlikely(rq->cmd_flags & REQ_FUA)) {
- /*
- * This happens only if this drive failed
- * 10byte rw command with ILLEGAL_REQUEST
- * during operation and thus turned off
- * use_10_for_rw.
- */
- scmd_printk(KERN_ERR, SCpnt,
- "FUA write on READ/WRITE(6) drive\n");
- return BLK_STS_IOERR;
- }
-
- SCpnt->cmnd[1] |= (unsigned char) ((block >> 16) & 0x1f);
- SCpnt->cmnd[2] = (unsigned char) ((block >> 8) & 0xff);
- SCpnt->cmnd[3] = (unsigned char) block & 0xff;
- SCpnt->cmnd[4] = (unsigned char) this_count;
- SCpnt->cmnd[5] = 0;
+ ret = sd_setup_rw6_cmnd(cmd, write, lba, nr_blocks,
+ protect | fua);
}
- SCpnt->sdb.length = this_count * sdp->sector_size;
+
+ if (unlikely(ret != BLK_STS_OK))
+ return ret;
/*
* We shouldn't disconnect in the middle of a sector, so with a dumb
* host adapter, it's safe to assume that we can at least transfer
* this many bytes between each connect / disconnect.
*/
- SCpnt->transfersize = sdp->sector_size;
- SCpnt->underflow = this_count << 9;
- SCpnt->allowed = SD_MAX_RETRIES;
+ cmd->transfersize = sdp->sector_size;
+ cmd->underflow = nr_blocks << 9;
+ cmd->allowed = SD_MAX_RETRIES;
+ cmd->sdb.length = nr_blocks * sdp->sector_size;
+
+ SCSI_LOG_HLQUEUE(1,
+ scmd_printk(KERN_INFO, cmd,
+ "%s: block=%llu, count=%d\n", __func__,
+ (unsigned long long)blk_rq_pos(rq),
+ blk_rq_sectors(rq)));
+ SCSI_LOG_HLQUEUE(2,
+ scmd_printk(KERN_INFO, cmd,
+ "%s %d/%u 512 byte blocks.\n",
+ write ? "writing" : "reading", nr_blocks,
+ blk_rq_sectors(rq)));
/*
* This indicates that the command is ready from our end to be
@@ -1398,11 +1415,6 @@ static void sd_release(struct gendisk *disk, fmode_t mode)
scsi_set_medium_removal(sdev, SCSI_REMOVAL_ALLOW);
}
- /*
- * XXX and what if there are packets in flight and this close()
- * XXX is followed by a "rmmod sd_mod"?
- */
-
scsi_disk_put(sdkp);
}
@@ -2549,25 +2561,25 @@ sd_print_capacity(struct scsi_disk *sdkp,
int sector_size = sdkp->device->sector_size;
char cap_str_2[10], cap_str_10[10];
+ if (!sdkp->first_scan && old_capacity == sdkp->capacity)
+ return;
+
string_get_size(sdkp->capacity, sector_size,
STRING_UNITS_2, cap_str_2, sizeof(cap_str_2));
string_get_size(sdkp->capacity, sector_size,
- STRING_UNITS_10, cap_str_10,
- sizeof(cap_str_10));
+ STRING_UNITS_10, cap_str_10, sizeof(cap_str_10));
- if (sdkp->first_scan || old_capacity != sdkp->capacity) {
- sd_printk(KERN_NOTICE, sdkp,
- "%llu %d-byte logical blocks: (%s/%s)\n",
- (unsigned long long)sdkp->capacity,
- sector_size, cap_str_10, cap_str_2);
+ sd_printk(KERN_NOTICE, sdkp,
+ "%llu %d-byte logical blocks: (%s/%s)\n",
+ (unsigned long long)sdkp->capacity,
+ sector_size, cap_str_10, cap_str_2);
- if (sdkp->physical_block_size != sector_size)
- sd_printk(KERN_NOTICE, sdkp,
- "%u-byte physical blocks\n",
- sdkp->physical_block_size);
+ if (sdkp->physical_block_size != sector_size)
+ sd_printk(KERN_NOTICE, sdkp,
+ "%u-byte physical blocks\n",
+ sdkp->physical_block_size);
- sd_zbc_print_zones(sdkp);
- }
+ sd_zbc_print_zones(sdkp);
}
/* called with buffer of length 512 */
@@ -3047,6 +3059,58 @@ static void sd_read_security(struct scsi_disk *sdkp, unsigned char *buffer)
sdkp->security = 1;
}
+/*
+ * Determine the device's preferred I/O size for reads and writes
+ * unless the reported value is unreasonably small, large, not a
+ * multiple of the physical block size, or simply garbage.
+ */
+static bool sd_validate_opt_xfer_size(struct scsi_disk *sdkp,
+ unsigned int dev_max)
+{
+ struct scsi_device *sdp = sdkp->device;
+ unsigned int opt_xfer_bytes =
+ logical_to_bytes(sdp, sdkp->opt_xfer_blocks);
+
+ if (sdkp->opt_xfer_blocks == 0)
+ return false;
+
+ if (sdkp->opt_xfer_blocks > dev_max) {
+ sd_first_printk(KERN_WARNING, sdkp,
+ "Optimal transfer size %u logical blocks " \
+ "> dev_max (%u logical blocks)\n",
+ sdkp->opt_xfer_blocks, dev_max);
+ return false;
+ }
+
+ if (sdkp->opt_xfer_blocks > SD_DEF_XFER_BLOCKS) {
+ sd_first_printk(KERN_WARNING, sdkp,
+ "Optimal transfer size %u logical blocks " \
+ "> sd driver limit (%u logical blocks)\n",
+ sdkp->opt_xfer_blocks, SD_DEF_XFER_BLOCKS);
+ return false;
+ }
+
+ if (opt_xfer_bytes < PAGE_SIZE) {
+ sd_first_printk(KERN_WARNING, sdkp,
+ "Optimal transfer size %u bytes < " \
+ "PAGE_SIZE (%u bytes)\n",
+ opt_xfer_bytes, (unsigned int)PAGE_SIZE);
+ return false;
+ }
+
+ if (opt_xfer_bytes & (sdkp->physical_block_size - 1)) {
+ sd_first_printk(KERN_WARNING, sdkp,
+ "Optimal transfer size %u bytes not a " \
+ "multiple of physical block size (%u bytes)\n",
+ opt_xfer_bytes, sdkp->physical_block_size);
+ return false;
+ }
+
+ sd_first_printk(KERN_INFO, sdkp, "Optimal transfer size %u bytes\n",
+ opt_xfer_bytes);
+ return true;
+}
+
/**
* sd_revalidate_disk - called the first time a new disk is seen,
* performs disk spin up, read_capacity, etc.
@@ -3125,15 +3189,7 @@ static int sd_revalidate_disk(struct gendisk *disk)
dev_max = min_not_zero(dev_max, sdkp->max_xfer_blocks);
q->limits.max_dev_sectors = logical_to_sectors(sdp, dev_max);
- /*
- * Determine the device's preferred I/O size for reads and writes
- * unless the reported value is unreasonably small, large, or
- * garbage.
- */
- if (sdkp->opt_xfer_blocks &&
- sdkp->opt_xfer_blocks <= dev_max &&
- sdkp->opt_xfer_blocks <= SD_DEF_XFER_BLOCKS &&
- logical_to_bytes(sdp, sdkp->opt_xfer_blocks) >= PAGE_SIZE) {
+ if (sd_validate_opt_xfer_size(sdkp, dev_max)) {
q->limits.io_opt = logical_to_bytes(sdp, sdkp->opt_xfer_blocks);
rw_max = logical_to_sectors(sdp, sdkp->opt_xfer_blocks);
} else
@@ -3447,9 +3503,21 @@ static void scsi_disk_release(struct device *dev)
{
struct scsi_disk *sdkp = to_scsi_disk(dev);
struct gendisk *disk = sdkp->disk;
-
+ struct request_queue *q = disk->queue;
+
ida_free(&sd_index_ida, sdkp->index);
+ /*
+ * Wait until all requests that are in progress have completed.
+ * This is necessary to avoid that e.g. scsi_end_request() crashes
+ * due to clearing the disk->private_data pointer. Wait from inside
+ * scsi_disk_release() instead of from sd_release() to avoid that
+ * freezing and unfreezing the request queue affects user space I/O
+ * in case multiple processes open a /dev/sd... node concurrently.
+ */
+ blk_mq_freeze_queue(q);
+ blk_mq_unfreeze_queue(q);
+
disk->private_data = NULL;
put_disk(disk);
put_device(&sdkp->device->sdev_gendev);
diff --git a/drivers/scsi/sd.h b/drivers/scsi/sd.h
index 7f43e6839bce..5796ace76225 100644
--- a/drivers/scsi/sd.h
+++ b/drivers/scsi/sd.h
@@ -132,7 +132,7 @@ static inline struct scsi_disk *scsi_disk(struct gendisk *disk)
#define sd_first_printk(prefix, sdsk, fmt, a...) \
do { \
- if ((sdkp)->first_scan) \
+ if ((sdsk)->first_scan) \
sd_printk(prefix, sdsk, fmt, ##a); \
} while (0)
@@ -188,68 +188,6 @@ static inline sector_t sectors_to_logical(struct scsi_device *sdev, sector_t sec
return sector >> (ilog2(sdev->sector_size) - 9);
}
-/*
- * Look up the DIX operation based on whether the command is read or
- * write and whether dix and dif are enabled.
- */
-static inline unsigned int sd_prot_op(bool write, bool dix, bool dif)
-{
- /* Lookup table: bit 2 (write), bit 1 (dix), bit 0 (dif) */
- const unsigned int ops[] = { /* wrt dix dif */
- SCSI_PROT_NORMAL, /* 0 0 0 */
- SCSI_PROT_READ_STRIP, /* 0 0 1 */
- SCSI_PROT_READ_INSERT, /* 0 1 0 */
- SCSI_PROT_READ_PASS, /* 0 1 1 */
- SCSI_PROT_NORMAL, /* 1 0 0 */
- SCSI_PROT_WRITE_INSERT, /* 1 0 1 */
- SCSI_PROT_WRITE_STRIP, /* 1 1 0 */
- SCSI_PROT_WRITE_PASS, /* 1 1 1 */
- };
-
- return ops[write << 2 | dix << 1 | dif];
-}
-
-/*
- * Returns a mask of the protection flags that are valid for a given DIX
- * operation.
- */
-static inline unsigned int sd_prot_flag_mask(unsigned int prot_op)
-{
- const unsigned int flag_mask[] = {
- [SCSI_PROT_NORMAL] = 0,
-
- [SCSI_PROT_READ_STRIP] = SCSI_PROT_TRANSFER_PI |
- SCSI_PROT_GUARD_CHECK |
- SCSI_PROT_REF_CHECK |
- SCSI_PROT_REF_INCREMENT,
-
- [SCSI_PROT_READ_INSERT] = SCSI_PROT_REF_INCREMENT |
- SCSI_PROT_IP_CHECKSUM,
-
- [SCSI_PROT_READ_PASS] = SCSI_PROT_TRANSFER_PI |
- SCSI_PROT_GUARD_CHECK |
- SCSI_PROT_REF_CHECK |
- SCSI_PROT_REF_INCREMENT |
- SCSI_PROT_IP_CHECKSUM,
-
- [SCSI_PROT_WRITE_INSERT] = SCSI_PROT_TRANSFER_PI |
- SCSI_PROT_REF_INCREMENT,
-
- [SCSI_PROT_WRITE_STRIP] = SCSI_PROT_GUARD_CHECK |
- SCSI_PROT_REF_CHECK |
- SCSI_PROT_REF_INCREMENT |
- SCSI_PROT_IP_CHECKSUM,
-
- [SCSI_PROT_WRITE_PASS] = SCSI_PROT_TRANSFER_PI |
- SCSI_PROT_GUARD_CHECK |
- SCSI_PROT_REF_CHECK |
- SCSI_PROT_REF_INCREMENT |
- SCSI_PROT_IP_CHECKSUM,
- };
-
- return flag_mask[prot_op];
-}
-
#ifdef CONFIG_BLK_DEV_INTEGRITY
extern void sd_dif_config_host(struct scsi_disk *);
diff --git a/drivers/scsi/smartpqi/Makefile b/drivers/scsi/smartpqi/Makefile
index e6b779930230..a03a6edb0060 100644
--- a/drivers/scsi/smartpqi/Makefile
+++ b/drivers/scsi/smartpqi/Makefile
@@ -1,3 +1,2 @@
-ccflags-y += -I.
obj-$(CONFIG_SCSI_SMARTPQI) += smartpqi.o
smartpqi-objs := smartpqi_init.o smartpqi_sis.o smartpqi_sas_transport.o
diff --git a/drivers/scsi/smartpqi/smartpqi_init.c b/drivers/scsi/smartpqi/smartpqi_init.c
index f564af8949e8..75ec43aa8df3 100644
--- a/drivers/scsi/smartpqi/smartpqi_init.c
+++ b/drivers/scsi/smartpqi/smartpqi_init.c
@@ -2764,6 +2764,12 @@ static void pqi_process_raid_io_error(struct pqi_io_request *io_request)
sshdr.sense_key == HARDWARE_ERROR &&
sshdr.asc == 0x3e &&
sshdr.ascq == 0x1) {
+ struct pqi_ctrl_info *ctrl_info = shost_to_hba(scmd->device->host);
+ struct pqi_scsi_dev *device = scmd->device->hostdata;
+
+ if (printk_ratelimit())
+ scmd_printk(KERN_ERR, scmd, "received 'logical unit failure' from controller for scsi %d:%d:%d:%d\n",
+ ctrl_info->scsi_host->host_no, device->bus, device->target, device->lun);
pqi_take_device_offline(scmd->device, "RAID");
host_byte = DID_NO_CONNECT;
}
@@ -6043,7 +6049,8 @@ out:
return rc;
}
-static int pqi_ioctl(struct scsi_device *sdev, int cmd, void __user *arg)
+static int pqi_ioctl(struct scsi_device *sdev, unsigned int cmd,
+ void __user *arg)
{
int rc;
struct pqi_ctrl_info *ctrl_info;
diff --git a/drivers/scsi/snic/snic_debugfs.c b/drivers/scsi/snic/snic_debugfs.c
index 0abe17c1a73b..2b349365592f 100644
--- a/drivers/scsi/snic/snic_debugfs.c
+++ b/drivers/scsi/snic/snic_debugfs.c
@@ -30,33 +30,13 @@
* fnic directory and statistics directory for trace buffer and
* stats logging
*/
-
-int
-snic_debugfs_init(void)
+void snic_debugfs_init(void)
{
- int rc = -1;
- struct dentry *de = NULL;
-
- de = debugfs_create_dir("snic", NULL);
- if (!de) {
- SNIC_DBG("Cannot create debugfs root\n");
-
- return rc;
- }
- snic_glob->trc_root = de;
-
- de = debugfs_create_dir("statistics", snic_glob->trc_root);
- if (!de) {
- SNIC_DBG("Cannot create Statistics directory\n");
+ snic_glob->trc_root = debugfs_create_dir("snic", NULL);
- return rc;
- }
- snic_glob->stats_root = de;
-
- rc = 0;
-
- return rc;
-} /* end of snic_debugfs_init */
+ snic_glob->stats_root = debugfs_create_dir("statistics",
+ snic_glob->trc_root);
+}
/*
* snic_debugfs_term - Tear down debugfs intrastructure
@@ -391,56 +371,23 @@ static const struct file_operations snic_reset_stats_fops = {
* It will create file stats and reset_stats under statistics/host# directory
* to log per snic stats
*/
-int
-snic_stats_debugfs_init(struct snic *snic)
+void snic_stats_debugfs_init(struct snic *snic)
{
- int rc = -1;
char name[16];
- struct dentry *de = NULL;
snprintf(name, sizeof(name), "host%d", snic->shost->host_no);
- if (!snic_glob->stats_root) {
- SNIC_DBG("snic_stats root doesn't exist\n");
-
- return rc;
- }
-
- de = debugfs_create_dir(name, snic_glob->stats_root);
- if (!de) {
- SNIC_DBG("Cannot create host directory\n");
-
- return rc;
- }
- snic->stats_host = de;
-
- de = debugfs_create_file("stats",
- S_IFREG|S_IRUGO,
- snic->stats_host,
- snic,
- &snic_stats_fops);
- if (!de) {
- SNIC_DBG("Cannot create host's stats file\n");
-
- return rc;
- }
- snic->stats_file = de;
-
- de = debugfs_create_file("reset_stats",
- S_IFREG|S_IRUGO|S_IWUSR,
- snic->stats_host,
- snic,
- &snic_reset_stats_fops);
- if (!de) {
- SNIC_DBG("Cannot create host's reset_stats file\n");
+ snic->stats_host = debugfs_create_dir(name, snic_glob->stats_root);
- return rc;
- }
- snic->reset_stats_file = de;
- rc = 0;
+ snic->stats_file = debugfs_create_file("stats", S_IFREG|S_IRUGO,
+ snic->stats_host, snic,
+ &snic_stats_fops);
- return rc;
-} /* end of snic_stats_debugfs_init */
+ snic->reset_stats_file = debugfs_create_file("reset_stats",
+ S_IFREG|S_IRUGO|S_IWUSR,
+ snic->stats_host, snic,
+ &snic_reset_stats_fops);
+}
/*
* snic_stats_debugfs_remove - Tear down debugfs infrastructure of stats
@@ -517,46 +464,18 @@ static const struct file_operations snic_trc_fops = {
* snic_trc_debugfs_init : creates trace/tracing_enable files for trace
* under debugfs
*/
-int
-snic_trc_debugfs_init(void)
+void snic_trc_debugfs_init(void)
{
- struct dentry *de = NULL;
- int ret = -1;
-
- if (!snic_glob->trc_root) {
- SNIC_ERR("Debugfs root directory for snic doesn't exist.\n");
-
- return ret;
- }
-
- de = debugfs_create_bool("tracing_enable",
- S_IFREG | S_IRUGO | S_IWUSR,
- snic_glob->trc_root,
- &snic_glob->trc.enable);
-
- if (!de) {
- SNIC_ERR("Can't create trace_enable file.\n");
-
- return ret;
- }
- snic_glob->trc.trc_enable = de;
-
- de = debugfs_create_file("trace",
- S_IFREG | S_IRUGO | S_IWUSR,
- snic_glob->trc_root,
- NULL,
- &snic_trc_fops);
-
- if (!de) {
- SNIC_ERR("Cannot create trace file.\n");
-
- return ret;
- }
- snic_glob->trc.trc_file = de;
- ret = 0;
-
- return ret;
-} /* end of snic_trc_debugfs_init */
+ snic_glob->trc.trc_enable = debugfs_create_bool("tracing_enable",
+ S_IFREG | S_IRUGO | S_IWUSR,
+ snic_glob->trc_root,
+ &snic_glob->trc.enable);
+
+ snic_glob->trc.trc_file = debugfs_create_file("trace",
+ S_IFREG | S_IRUGO | S_IWUSR,
+ snic_glob->trc_root, NULL,
+ &snic_trc_fops);
+}
/*
* snic_trc_debugfs_term : cleans up the files created for trace under debugfs
diff --git a/drivers/scsi/snic/snic_main.c b/drivers/scsi/snic/snic_main.c
index 5e824fd6047a..14f4ce665e58 100644
--- a/drivers/scsi/snic/snic_main.c
+++ b/drivers/scsi/snic/snic_main.c
@@ -397,12 +397,7 @@ snic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
#ifdef CONFIG_SCSI_SNIC_DEBUG_FS
/* Per snic debugfs init */
- ret = snic_stats_debugfs_init(snic);
- if (ret) {
- SNIC_HOST_ERR(snic->shost,
- "Failed to initialize debugfs stats\n");
- snic_stats_debugfs_remove(snic);
- }
+ snic_stats_debugfs_init(snic);
#endif
/* Setup PCI Resources */
@@ -850,12 +845,7 @@ snic_global_data_init(void)
#ifdef CONFIG_SCSI_SNIC_DEBUG_FS
/* Debugfs related Initialization */
/* Create debugfs entries for snic */
- ret = snic_debugfs_init();
- if (ret < 0) {
- SNIC_ERR("Failed to create sysfs dir for tracing and stats.\n");
- snic_debugfs_term();
- /* continue even if it fails */
- }
+ snic_debugfs_init();
/* Trace related Initialization */
/* Allocate memory for trace buffer */
diff --git a/drivers/scsi/snic/snic_stats.h b/drivers/scsi/snic/snic_stats.h
index fd1066b1cad5..faf0cb601954 100644
--- a/drivers/scsi/snic/snic_stats.h
+++ b/drivers/scsi/snic/snic_stats.h
@@ -99,7 +99,7 @@ struct snic_stats {
atomic64_t io_cmpl_skip;
};
-int snic_stats_debugfs_init(struct snic *);
+void snic_stats_debugfs_init(struct snic *);
void snic_stats_debugfs_remove(struct snic *);
/* Auxillary function to update active IO counter */
diff --git a/drivers/scsi/snic/snic_trc.c b/drivers/scsi/snic/snic_trc.c
index 458eaba24c78..f23fe2f88438 100644
--- a/drivers/scsi/snic/snic_trc.c
+++ b/drivers/scsi/snic/snic_trc.c
@@ -138,12 +138,7 @@ snic_trc_init(void)
trc->buf = (struct snic_trc_data *) tbuf;
spin_lock_init(&trc->lock);
- ret = snic_trc_debugfs_init();
- if (ret) {
- SNIC_ERR("Failed to create Debugfs Files.\n");
-
- goto error;
- }
+ snic_trc_debugfs_init();
trc->max_idx = (tbuf_sz / SNIC_TRC_ENTRY_SZ);
trc->rd_idx = trc->wr_idx = 0;
@@ -153,11 +148,6 @@ snic_trc_init(void)
ret = 0;
return ret;
-
-error:
- snic_trc_free();
-
- return ret;
} /* end of snic_trc_init */
/*
diff --git a/drivers/scsi/snic/snic_trc.h b/drivers/scsi/snic/snic_trc.h
index b37f8867bfde..87dcc7457d15 100644
--- a/drivers/scsi/snic/snic_trc.h
+++ b/drivers/scsi/snic/snic_trc.h
@@ -53,12 +53,12 @@ struct snic_trc {
int snic_trc_init(void);
void snic_trc_free(void);
-int snic_trc_debugfs_init(void);
+void snic_trc_debugfs_init(void);
void snic_trc_debugfs_term(void);
struct snic_trc_data *snic_get_trc_buf(void);
int snic_get_trc_data(char *buf, int buf_sz);
-int snic_debugfs_init(void);
+void snic_debugfs_init(void);
void snic_debugfs_term(void);
static inline void
diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c
index 38ddbbfe5f3c..039c27c2d7b3 100644
--- a/drivers/scsi/sr.c
+++ b/drivers/scsi/sr.c
@@ -394,7 +394,6 @@ static blk_status_t sr_init_command(struct scsi_cmnd *SCpnt)
ret = scsi_init_io(SCpnt);
if (ret != BLK_STS_OK)
goto out;
- WARN_ON_ONCE(SCpnt != rq->special);
cd = scsi_cd(rq->rq_disk);
/* from here on until we're complete, any goto out
diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c
index 7ff22d3f03e3..19c022e66d63 100644
--- a/drivers/scsi/st.c
+++ b/drivers/scsi/st.c
@@ -169,7 +169,7 @@ static int debugging = DEBUG;
/* Remove mode bits and auto-rewind bit (7) */
#define TAPE_NR(x) ( ((iminor(x) & ~255) >> (ST_NBR_MODE_BITS + 1)) | \
- (iminor(x) & ~(-1 << ST_MODE_SHIFT)) )
+ (iminor(x) & ((1 << ST_MODE_SHIFT)-1)))
#define TAPE_MODE(x) ((iminor(x) & ST_MODE_MASK) >> ST_MODE_SHIFT)
/* Construct the minor number from the device (d), mode (m), and non-rewind (n) data */
@@ -337,12 +337,14 @@ static void st_analyze_sense(struct st_request *SRpnt, struct st_cmdstatus *s)
switch (sense[0] & 0x7f) {
case 0x71:
s->deferred = 1;
+ /* fall through */
case 0x70:
s->fixed_format = 1;
s->flags = sense[2] & 0xe0;
break;
case 0x73:
s->deferred = 1;
+ /* fall through */
case 0x72:
s->fixed_format = 0;
ucp = scsi_sense_desc_find(sense, SCSI_SENSE_BUFFERSIZE, 4);
@@ -2721,6 +2723,7 @@ static int st_int_ioctl(struct scsi_tape *STp, unsigned int cmd_in, unsigned lon
switch (cmd_in) {
case MTFSFM:
chg_eof = 0; /* Changed from the FSF after this */
+ /* fall through */
case MTFSF:
cmd[0] = SPACE;
cmd[1] = 0x01; /* Space FileMarks */
@@ -2735,6 +2738,7 @@ static int st_int_ioctl(struct scsi_tape *STp, unsigned int cmd_in, unsigned lon
break;
case MTBSFM:
chg_eof = 0; /* Changed from the FSF after this */
+ /* fall through */
case MTBSF:
cmd[0] = SPACE;
cmd[1] = 0x01; /* Space FileMarks */
diff --git a/drivers/scsi/ufs/Kconfig b/drivers/scsi/ufs/Kconfig
index 2ddbb26d9c26..6db37cf306b0 100644
--- a/drivers/scsi/ufs/Kconfig
+++ b/drivers/scsi/ufs/Kconfig
@@ -99,7 +99,6 @@ config SCSI_UFS_DWC_TC_PLATFORM
config SCSI_UFS_QCOM
tristate "QCOM specific hooks to UFS controller platform driver"
depends on SCSI_UFSHCD_PLATFORM && ARCH_QCOM
- select PHY_QCOM_UFS
help
This selects the QCOM specific additions to UFSHCD platform driver.
UFS host on QCOM needs some vendor specific configuration before
diff --git a/drivers/scsi/ufs/ufs-hisi.c b/drivers/scsi/ufs/ufs-hisi.c
index 452e19f8fb47..0e855b5afe82 100644
--- a/drivers/scsi/ufs/ufs-hisi.c
+++ b/drivers/scsi/ufs/ufs-hisi.c
@@ -66,7 +66,7 @@ static int ufs_hisi_check_hibern8(struct ufs_hba *hba)
return err;
}
-static void ufs_hi3660_clk_init(struct ufs_hba *hba)
+static void ufs_hisi_clk_init(struct ufs_hba *hba)
{
struct ufs_hisi_host *host = ufshcd_get_variant(hba);
@@ -80,7 +80,7 @@ static void ufs_hi3660_clk_init(struct ufs_hba *hba)
ufs_sys_ctrl_set_bits(host, BIT_SYSCTRL_REF_CLOCK_EN, PHY_CLK_CTRL);
}
-static void ufs_hi3660_soc_init(struct ufs_hba *hba)
+static void ufs_hisi_soc_init(struct ufs_hba *hba)
{
struct ufs_hisi_host *host = ufshcd_get_variant(hba);
u32 reg;
@@ -139,6 +139,7 @@ static void ufs_hi3660_soc_init(struct ufs_hba *hba)
static int ufs_hisi_link_startup_pre_change(struct ufs_hba *hba)
{
+ struct ufs_hisi_host *host = ufshcd_get_variant(hba);
int err;
uint32_t value;
uint32_t reg;
@@ -153,6 +154,14 @@ static int ufs_hisi_link_startup_pre_change(struct ufs_hba *hba)
ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x8121, 0x0), 0x2D);
/* MPHY CBOVRCTRL3 */
ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x8122, 0x0), 0x1);
+
+ if (host->caps & UFS_HISI_CAP_PHY10nm) {
+ /* MPHY CBOVRCTRL4 */
+ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x8127, 0x0), 0x98);
+ /* MPHY CBOVRCTRL5 */
+ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x8128, 0x0), 0x1);
+ }
+
/* Unipro VS_MphyCfgUpdt */
ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0xD085, 0x0), 0x1);
/* MPHY RXOVRCTRL4 rx0 */
@@ -173,10 +182,21 @@ static int ufs_hisi_link_startup_pre_change(struct ufs_hba *hba)
ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x8113, 0x0), 0x1);
ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0xD085, 0x0), 0x1);
- /* Tactive RX */
- ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x008F, 0x4), 0x7);
- /* Tactive RX */
- ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x008F, 0x5), 0x7);
+ if (host->caps & UFS_HISI_CAP_PHY10nm) {
+ /* RX_Hibern8Time_Capability*/
+ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x0092, 0x4), 0xA);
+ /* RX_Hibern8Time_Capability*/
+ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x0092, 0x5), 0xA);
+ /* RX_Min_ActivateTime */
+ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x008f, 0x4), 0xA);
+ /* RX_Min_ActivateTime*/
+ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x008f, 0x5), 0xA);
+ } else {
+ /* Tactive RX */
+ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x008F, 0x4), 0x7);
+ /* Tactive RX */
+ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x008F, 0x5), 0x7);
+ }
/* Gear3 Synclength */
ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x0095, 0x4), 0x4F);
@@ -208,7 +228,8 @@ static int ufs_hisi_link_startup_pre_change(struct ufs_hba *hba)
if (err)
dev_err(hba->dev, "ufs_hisi_check_hibern8 error\n");
- ufshcd_writel(hba, UFS_HCLKDIV_NORMAL_VALUE, UFS_REG_HCLKDIV);
+ if (!(host->caps & UFS_HISI_CAP_PHY10nm))
+ ufshcd_writel(hba, UFS_HCLKDIV_NORMAL_VALUE, UFS_REG_HCLKDIV);
/* disable auto H8 */
reg = ufshcd_readl(hba, REG_AUTO_HIBERNATE_IDLE_TIMER);
@@ -253,7 +274,7 @@ static int ufs_hisi_link_startup_post_change(struct ufs_hba *hba)
return 0;
}
-static int ufs_hi3660_link_startup_notify(struct ufs_hba *hba,
+static int ufs_hisi_link_startup_notify(struct ufs_hba *hba,
enum ufs_notify_change_status status)
{
int err = 0;
@@ -391,6 +412,28 @@ static void ufs_hisi_set_dev_cap(struct ufs_hisi_dev_params *hisi_param)
static void ufs_hisi_pwr_change_pre_change(struct ufs_hba *hba)
{
+ struct ufs_hisi_host *host = ufshcd_get_variant(hba);
+
+ if (host->caps & UFS_HISI_CAP_PHY10nm) {
+ /*
+ * Boston platform need to set SaveConfigTime to 0x13,
+ * and change sync length to maximum value
+ */
+ /* VS_DebugSaveConfigTime */
+ ufshcd_dme_set(hba, UIC_ARG_MIB((u32)0xD0A0), 0x13);
+ /* g1 sync length */
+ ufshcd_dme_set(hba, UIC_ARG_MIB((u32)0x1552), 0x4f);
+ /* g2 sync length */
+ ufshcd_dme_set(hba, UIC_ARG_MIB((u32)0x1554), 0x4f);
+ /* g3 sync length */
+ ufshcd_dme_set(hba, UIC_ARG_MIB((u32)0x1556), 0x4f);
+ /* PA_Hibern8Time */
+ ufshcd_dme_set(hba, UIC_ARG_MIB((u32)0x15a7), 0xA);
+ /* PA_Tactivate */
+ ufshcd_dme_set(hba, UIC_ARG_MIB((u32)0x15a8), 0xA);
+ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0xd085, 0x0), 0x01);
+ }
+
if (hba->dev_quirks & UFS_DEVICE_QUIRK_HOST_VS_DEBUGSAVECONFIGTIME) {
pr_info("ufs flash device must set VS_DebugSaveConfigTime 0x10\n");
/* VS_DebugSaveConfigTime */
@@ -429,7 +472,7 @@ static void ufs_hisi_pwr_change_pre_change(struct ufs_hba *hba)
ufshcd_dme_set(hba, UIC_ARG_MIB(0xd046), 32767);
}
-static int ufs_hi3660_pwr_change_notify(struct ufs_hba *hba,
+static int ufs_hisi_pwr_change_notify(struct ufs_hba *hba,
enum ufs_notify_change_status status,
struct ufs_pa_layer_attr *dev_max_params,
struct ufs_pa_layer_attr *dev_req_params)
@@ -567,25 +610,69 @@ static int ufs_hi3660_init(struct ufs_hba *hba)
return ret;
}
- ufs_hi3660_clk_init(hba);
+ ufs_hisi_clk_init(hba);
+
+ ufs_hisi_soc_init(hba);
+
+ return 0;
+}
+
+static int ufs_hi3670_init(struct ufs_hba *hba)
+{
+ int ret = 0;
+ struct device *dev = hba->dev;
+ struct ufs_hisi_host *host;
+
+ ret = ufs_hisi_init_common(hba);
+ if (ret) {
+ dev_err(dev, "%s: ufs common init fail\n", __func__);
+ return ret;
+ }
+
+ ufs_hisi_clk_init(hba);
+
+ ufs_hisi_soc_init(hba);
- ufs_hi3660_soc_init(hba);
+ /* Add cap for 10nm PHY variant on HI3670 SoC */
+ host = ufshcd_get_variant(hba);
+ host->caps |= UFS_HISI_CAP_PHY10nm;
return 0;
}
-static struct ufs_hba_variant_ops ufs_hba_hisi_vops = {
+static const struct ufs_hba_variant_ops ufs_hba_hi3660_vops = {
.name = "hi3660",
.init = ufs_hi3660_init,
- .link_startup_notify = ufs_hi3660_link_startup_notify,
- .pwr_change_notify = ufs_hi3660_pwr_change_notify,
+ .link_startup_notify = ufs_hisi_link_startup_notify,
+ .pwr_change_notify = ufs_hisi_pwr_change_notify,
.suspend = ufs_hisi_suspend,
.resume = ufs_hisi_resume,
};
+static const struct ufs_hba_variant_ops ufs_hba_hi3670_vops = {
+ .name = "hi3670",
+ .init = ufs_hi3670_init,
+ .link_startup_notify = ufs_hisi_link_startup_notify,
+ .pwr_change_notify = ufs_hisi_pwr_change_notify,
+ .suspend = ufs_hisi_suspend,
+ .resume = ufs_hisi_resume,
+};
+
+static const struct of_device_id ufs_hisi_of_match[] = {
+ { .compatible = "hisilicon,hi3660-ufs", .data = &ufs_hba_hi3660_vops },
+ { .compatible = "hisilicon,hi3670-ufs", .data = &ufs_hba_hi3670_vops },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, ufs_hisi_of_match);
+
static int ufs_hisi_probe(struct platform_device *pdev)
{
- return ufshcd_pltfrm_init(pdev, &ufs_hba_hisi_vops);
+ const struct of_device_id *of_id;
+
+ of_id = of_match_node(ufs_hisi_of_match, pdev->dev.of_node);
+
+ return ufshcd_pltfrm_init(pdev, of_id->data);
}
static int ufs_hisi_remove(struct platform_device *pdev)
@@ -596,13 +683,6 @@ static int ufs_hisi_remove(struct platform_device *pdev)
return 0;
}
-static const struct of_device_id ufs_hisi_of_match[] = {
- { .compatible = "hisilicon,hi3660-ufs" },
- {},
-};
-
-MODULE_DEVICE_TABLE(of, ufs_hisi_of_match);
-
static const struct dev_pm_ops ufs_hisi_pm_ops = {
.suspend = ufshcd_pltfrm_suspend,
.resume = ufshcd_pltfrm_resume,
diff --git a/drivers/scsi/ufs/ufs-hisi.h b/drivers/scsi/ufs/ufs-hisi.h
index 3df9cd7acc29..667dfe39b57e 100644
--- a/drivers/scsi/ufs/ufs-hisi.h
+++ b/drivers/scsi/ufs/ufs-hisi.h
@@ -91,6 +91,9 @@ enum {
#define UFS_HISI_LIMIT_HS_RATE PA_HS_MODE_B
#define UFS_HISI_LIMIT_DESIRED_MODE FAST
+#define UFS_HISI_CAP_RESERVED BIT(0)
+#define UFS_HISI_CAP_PHY10nm BIT(1)
+
struct ufs_hisi_host {
struct ufs_hba *hba;
void __iomem *ufs_sys_ctrl;
@@ -112,4 +115,5 @@ struct ufs_hisi_host {
ufs_sys_ctrl_writel((host), \
((~(mask)) & (ufs_sys_ctrl_readl((host), (reg)))), \
(reg))
+
#endif /* UFS_HISI_H_ */
diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
index 6d176815e6ce..21e4ccb5ba6e 100644
--- a/drivers/scsi/ufs/ufs.h
+++ b/drivers/scsi/ufs/ufs.h
@@ -514,7 +514,6 @@ struct ufs_vreg {
struct regulator *reg;
const char *name;
bool enabled;
- bool unused;
int min_uV;
int max_uV;
int min_uA;
diff --git a/drivers/scsi/ufs/ufs_bsg.c b/drivers/scsi/ufs/ufs_bsg.c
index 775bb4e5e36e..869e71f861d6 100644
--- a/drivers/scsi/ufs/ufs_bsg.c
+++ b/drivers/scsi/ufs/ufs_bsg.c
@@ -27,15 +27,11 @@ static int ufs_bsg_get_query_desc_size(struct ufs_hba *hba, int *desc_len,
static int ufs_bsg_verify_query_size(struct ufs_hba *hba,
unsigned int request_len,
- unsigned int reply_len,
- int desc_len, enum query_opcode desc_op)
+ unsigned int reply_len)
{
int min_req_len = sizeof(struct ufs_bsg_request);
int min_rsp_len = sizeof(struct ufs_bsg_reply);
- if (desc_op == UPIU_QUERY_OPCODE_WRITE_DESC)
- min_req_len += desc_len;
-
if (min_req_len > request_len || min_rsp_len > reply_len) {
dev_err(hba->dev, "not enough space assigned\n");
return -EINVAL;
@@ -44,21 +40,16 @@ static int ufs_bsg_verify_query_size(struct ufs_hba *hba,
return 0;
}
-static int ufs_bsg_verify_query_params(struct ufs_hba *hba,
- struct ufs_bsg_request *bsg_request,
- unsigned int request_len,
- unsigned int reply_len,
- uint8_t *desc_buff, int *desc_len,
- enum query_opcode desc_op)
+static int ufs_bsg_alloc_desc_buffer(struct ufs_hba *hba, struct bsg_job *job,
+ uint8_t **desc_buff, int *desc_len,
+ enum query_opcode desc_op)
{
+ struct ufs_bsg_request *bsg_request = job->request;
struct utp_upiu_query *qr;
+ u8 *descp;
- if (desc_op == UPIU_QUERY_OPCODE_READ_DESC) {
- dev_err(hba->dev, "unsupported opcode %d\n", desc_op);
- return -ENOTSUPP;
- }
-
- if (desc_op != UPIU_QUERY_OPCODE_WRITE_DESC)
+ if (desc_op != UPIU_QUERY_OPCODE_WRITE_DESC &&
+ desc_op != UPIU_QUERY_OPCODE_READ_DESC)
goto out;
qr = &bsg_request->upiu_req.qr;
@@ -67,11 +58,21 @@ static int ufs_bsg_verify_query_params(struct ufs_hba *hba,
return -EINVAL;
}
- if (ufs_bsg_verify_query_size(hba, request_len, reply_len, *desc_len,
- desc_op))
+ if (*desc_len > job->request_payload.payload_len) {
+ dev_err(hba->dev, "Illegal desc size\n");
return -EINVAL;
+ }
- desc_buff = (uint8_t *)(bsg_request + 1);
+ descp = kzalloc(*desc_len, GFP_KERNEL);
+ if (!descp)
+ return -ENOMEM;
+
+ if (desc_op == UPIU_QUERY_OPCODE_WRITE_DESC)
+ sg_copy_to_buffer(job->request_payload.sg_list,
+ job->request_payload.sg_cnt, descp,
+ *desc_len);
+
+ *desc_buff = descp;
out:
return 0;
@@ -91,7 +92,7 @@ static int ufs_bsg_request(struct bsg_job *job)
enum query_opcode desc_op = UPIU_QUERY_OPCODE_NOP;
int ret;
- ret = ufs_bsg_verify_query_size(hba, req_len, reply_len, 0, desc_op);
+ ret = ufs_bsg_verify_query_size(hba, req_len, reply_len);
if (ret)
goto out;
@@ -101,9 +102,8 @@ static int ufs_bsg_request(struct bsg_job *job)
switch (msgcode) {
case UPIU_TRANSACTION_QUERY_REQ:
desc_op = bsg_request->upiu_req.qr.opcode;
- ret = ufs_bsg_verify_query_params(hba, bsg_request, req_len,
- reply_len, desc_buff,
- &desc_len, desc_op);
+ ret = ufs_bsg_alloc_desc_buffer(hba, job, &desc_buff,
+ &desc_len, desc_op);
if (ret)
goto out;
@@ -135,11 +135,20 @@ static int ufs_bsg_request(struct bsg_job *job)
break;
}
+ if (!desc_buff)
+ goto out;
+
+ if (desc_op == UPIU_QUERY_OPCODE_READ_DESC && desc_len)
+ bsg_reply->reply_payload_rcv_len =
+ sg_copy_from_buffer(job->request_payload.sg_list,
+ job->request_payload.sg_cnt,
+ desc_buff, desc_len);
+
+ kfree(desc_buff);
+
out:
bsg_reply->result = ret;
- job->reply_len = sizeof(struct ufs_bsg_reply) +
- bsg_reply->reply_payload_rcv_len;
-
+ job->reply_len = sizeof(struct ufs_bsg_reply);
bsg_job_done(job, ret, bsg_reply->reply_payload_rcv_len);
return ret;
diff --git a/drivers/scsi/ufs/ufs_quirks.h b/drivers/scsi/ufs/ufs_quirks.h
index 5d2dfdb41a6f..a9bbd34d6b16 100644
--- a/drivers/scsi/ufs/ufs_quirks.h
+++ b/drivers/scsi/ufs/ufs_quirks.h
@@ -45,21 +45,6 @@ struct ufs_dev_fix {
}
/*
- * If UFS device is having issue in processing LCC (Line Control
- * Command) coming from UFS host controller then enable this quirk.
- * When this quirk is enabled, host controller driver should disable
- * the LCC transmission on UFS host controller (by clearing
- * TX_LCC_ENABLE attribute of host to 0).
- */
-#define UFS_DEVICE_QUIRK_BROKEN_LCC (1 << 0)
-
-/*
- * Some UFS devices don't need VCCQ rail for device operations. Enabling this
- * quirk for such devices will make sure that VCCQ rail is not voted.
- */
-#define UFS_DEVICE_NO_VCCQ (1 << 1)
-
-/*
* Some vendor's UFS device sends back to back NACs for the DL data frames
* causing the host controller to raise the DFES error status. Sometimes
* such UFS devices send back to back NAC without waiting for new
@@ -85,13 +70,6 @@ struct ufs_dev_fix {
#define UFS_DEVICE_QUIRK_RECOVERY_FROM_DL_NAC_ERRORS (1 << 2)
/*
- * Some UFS devices may not work properly after resume if the link was kept
- * in off state during suspend. Enabling this quirk will not allow the
- * link to be kept in off state during suspend.
- */
-#define UFS_DEVICE_QUIRK_NO_LINK_OFF (1 << 3)
-
-/*
* Few Toshiba UFS device models advertise RX_MIN_ACTIVATETIME_CAPABILITY as
* 600us which may not be enough for reliable hibern8 exit hardware sequence
* from UFS device.
@@ -101,13 +79,6 @@ struct ufs_dev_fix {
#define UFS_DEVICE_QUIRK_PA_TACTIVATE (1 << 4)
/*
- * Some UFS memory devices may have really low read/write throughput in
- * FAST AUTO mode, enable this quirk to make sure that FAST AUTO mode is
- * never enabled for such devices.
- */
-#define UFS_DEVICE_NO_FASTAUTO (1 << 5)
-
-/*
* It seems some UFS devices may keep drawing more than sleep current
* (atleast for 500us) from UFS rails (especially from VCCQ rail).
* To avoid this situation, add 2ms delay before putting these UFS
diff --git a/drivers/scsi/ufs/ufshcd-dwc.c b/drivers/scsi/ufs/ufshcd-dwc.c
index 5fd16c72207f..977b21871a5d 100644
--- a/drivers/scsi/ufs/ufshcd-dwc.c
+++ b/drivers/scsi/ufs/ufshcd-dwc.c
@@ -50,7 +50,7 @@ static void ufshcd_dwc_program_clk_div(struct ufs_hba *hba, u32 divider_val)
/**
* ufshcd_dwc_link_is_up()
* Check if link is up
- * @hba: private structure poitner
+ * @hba: private structure pointer
*
* Returns 0 on success, non-zero value on failure
*/
@@ -110,7 +110,7 @@ static int ufshcd_dwc_connection_setup(struct ufs_hba *hba)
/**
* ufshcd_dwc_link_startup_notify()
* UFS Host DWC specific link startup sequence
- * @hba: private structure poitner
+ * @hba: private structure pointer
* @status: Callback notify status
*
* Returns 0 on success, non-zero value on failure
diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.c b/drivers/scsi/ufs/ufshcd-pltfrm.c
index 895a9b5ac989..27213676329c 100644
--- a/drivers/scsi/ufs/ufshcd-pltfrm.c
+++ b/drivers/scsi/ufs/ufshcd-pltfrm.c
@@ -297,7 +297,7 @@ static void ufshcd_init_lanes_per_dir(struct ufs_hba *hba)
* Returns 0 on success, non-zero value on failure
*/
int ufshcd_pltfrm_init(struct platform_device *pdev,
- struct ufs_hba_variant_ops *vops)
+ const struct ufs_hba_variant_ops *vops)
{
struct ufs_hba *hba;
void __iomem *mmio_base;
diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.h b/drivers/scsi/ufs/ufshcd-pltfrm.h
index df64c4180340..1f29e1fd6d52 100644
--- a/drivers/scsi/ufs/ufshcd-pltfrm.h
+++ b/drivers/scsi/ufs/ufshcd-pltfrm.h
@@ -17,7 +17,7 @@
#include "ufshcd.h"
int ufshcd_pltfrm_init(struct platform_device *pdev,
- struct ufs_hba_variant_ops *vops);
+ const struct ufs_hba_variant_ops *vops);
void ufshcd_pltfrm_shutdown(struct platform_device *pdev);
#ifdef CONFIG_PM
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 2ddf24466a62..e040f9dd9ff3 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -219,12 +219,9 @@ static struct ufs_dev_fix ufs_fixups[] = {
/* UFS cards deviations table */
UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL,
UFS_DEVICE_QUIRK_DELAY_BEFORE_LPM),
- UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL, UFS_DEVICE_NO_VCCQ),
UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL,
UFS_DEVICE_QUIRK_RECOVERY_FROM_DL_NAC_ERRORS),
UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL,
- UFS_DEVICE_NO_FASTAUTO),
- UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL,
UFS_DEVICE_QUIRK_HOST_PA_TACTIVATE),
UFS_FIX(UFS_VENDOR_TOSHIBA, UFS_ANY_MODEL,
UFS_DEVICE_QUIRK_DELAY_BEFORE_LPM),
@@ -232,7 +229,6 @@ static struct ufs_dev_fix ufs_fixups[] = {
UFS_DEVICE_QUIRK_PA_TACTIVATE),
UFS_FIX(UFS_VENDOR_TOSHIBA, "THGLF2G9D8KBADG",
UFS_DEVICE_QUIRK_PA_TACTIVATE),
- UFS_FIX(UFS_VENDOR_SKHYNIX, UFS_ANY_MODEL, UFS_DEVICE_NO_VCCQ),
UFS_FIX(UFS_VENDOR_SKHYNIX, UFS_ANY_MODEL,
UFS_DEVICE_QUIRK_HOST_PA_SAVECONFIGTIME),
UFS_FIX(UFS_VENDOR_SKHYNIX, "hB8aL1" /*H28U62301AMR*/,
@@ -251,7 +247,6 @@ static int ufshcd_probe_hba(struct ufs_hba *hba);
static int __ufshcd_setup_clocks(struct ufs_hba *hba, bool on,
bool skip_ref_clk);
static int ufshcd_setup_clocks(struct ufs_hba *hba, bool on);
-static int ufshcd_set_vccq_rail_unused(struct ufs_hba *hba, bool unused);
static int ufshcd_uic_hibern8_exit(struct ufs_hba *hba);
static int ufshcd_uic_hibern8_enter(struct ufs_hba *hba);
static inline void ufshcd_add_delay_before_dme_cmd(struct ufs_hba *hba);
@@ -399,15 +394,20 @@ static void ufshcd_print_uic_err_hist(struct ufs_hba *hba,
struct ufs_uic_err_reg_hist *err_hist, char *err_name)
{
int i;
+ bool found = false;
for (i = 0; i < UIC_ERR_REG_HIST_LENGTH; i++) {
- int p = (i + err_hist->pos - 1) % UIC_ERR_REG_HIST_LENGTH;
+ int p = (i + err_hist->pos) % UIC_ERR_REG_HIST_LENGTH;
if (err_hist->reg[p] == 0)
continue;
dev_err(hba->dev, "%s[%d] = 0x%x at %lld us\n", err_name, i,
err_hist->reg[p], ktime_to_us(err_hist->tstamp[p]));
+ found = true;
}
+
+ if (!found)
+ dev_err(hba->dev, "No record of %s uic errors\n", err_name);
}
static void ufshcd_print_host_regs(struct ufs_hba *hba)
@@ -5775,6 +5775,20 @@ static int ufshcd_issue_devman_upiu_cmd(struct ufs_hba *hba,
/* just copy the upiu response as it is */
memcpy(rsp_upiu, lrbp->ucd_rsp_ptr, sizeof(*rsp_upiu));
+ if (desc_buff && desc_op == UPIU_QUERY_OPCODE_READ_DESC) {
+ u8 *descp = (u8 *)lrbp->ucd_rsp_ptr + sizeof(*rsp_upiu);
+ u16 resp_len = be32_to_cpu(lrbp->ucd_rsp_ptr->header.dword_2) &
+ MASK_QUERY_DATA_SEG_LEN;
+
+ if (*buff_len >= resp_len) {
+ memcpy(desc_buff, descp, resp_len);
+ *buff_len = resp_len;
+ } else {
+ dev_warn(hba->dev, "rsp size is bigger than buffer");
+ *buff_len = 0;
+ err = -EINVAL;
+ }
+ }
ufshcd_put_dev_cmd_tag(hba, tag);
wake_up(&hba->dev_cmd.tag_wq);
@@ -5810,11 +5824,6 @@ int ufshcd_exec_raw_upiu_cmd(struct ufs_hba *hba,
int ocs_value;
u8 tm_f = be32_to_cpu(req_upiu->header.dword_1) >> 16 & MASK_TM_FUNC;
- if (desc_buff && desc_op != UPIU_QUERY_OPCODE_WRITE_DESC) {
- err = -ENOTSUPP;
- goto out;
- }
-
switch (msgcode) {
case UPIU_TRANSACTION_NOP_OUT:
cmd_type = DEV_CMD_TYPE_NOP;
@@ -5855,7 +5864,6 @@ int ufshcd_exec_raw_upiu_cmd(struct ufs_hba *hba,
break;
}
-out:
return err;
}
@@ -6825,11 +6833,6 @@ static int ufshcd_probe_hba(struct ufs_hba *hba)
ufs_fixup_device_setup(hba, &card);
ufshcd_tune_unipro_params(hba);
- ret = ufshcd_set_vccq_rail_unused(hba,
- (hba->dev_quirks & UFS_DEVICE_NO_VCCQ) ? true : false);
- if (ret)
- goto out;
-
/* UFS device is also active now */
ufshcd_set_ufs_dev_active(hba);
ufshcd_force_reset_auto_bkops(hba);
@@ -7013,24 +7016,13 @@ static int ufshcd_config_vreg_load(struct device *dev, struct ufs_vreg *vreg,
static inline int ufshcd_config_vreg_lpm(struct ufs_hba *hba,
struct ufs_vreg *vreg)
{
- if (!vreg)
- return 0;
- else if (vreg->unused)
- return 0;
- else
- return ufshcd_config_vreg_load(hba->dev, vreg,
- UFS_VREG_LPM_LOAD_UA);
+ return ufshcd_config_vreg_load(hba->dev, vreg, UFS_VREG_LPM_LOAD_UA);
}
static inline int ufshcd_config_vreg_hpm(struct ufs_hba *hba,
struct ufs_vreg *vreg)
{
- if (!vreg)
- return 0;
- else if (vreg->unused)
- return 0;
- else
- return ufshcd_config_vreg_load(hba->dev, vreg, vreg->max_uA);
+ return ufshcd_config_vreg_load(hba->dev, vreg, vreg->max_uA);
}
static int ufshcd_config_vreg(struct device *dev,
@@ -7068,9 +7060,7 @@ static int ufshcd_enable_vreg(struct device *dev, struct ufs_vreg *vreg)
{
int ret = 0;
- if (!vreg)
- goto out;
- else if (vreg->enabled || vreg->unused)
+ if (!vreg || vreg->enabled)
goto out;
ret = ufshcd_config_vreg(dev, vreg, true);
@@ -7090,9 +7080,7 @@ static int ufshcd_disable_vreg(struct device *dev, struct ufs_vreg *vreg)
{
int ret = 0;
- if (!vreg)
- goto out;
- else if (!vreg->enabled || vreg->unused)
+ if (!vreg || !vreg->enabled)
goto out;
ret = regulator_disable(vreg->reg);
@@ -7198,36 +7186,6 @@ static int ufshcd_init_hba_vreg(struct ufs_hba *hba)
return 0;
}
-static int ufshcd_set_vccq_rail_unused(struct ufs_hba *hba, bool unused)
-{
- int ret = 0;
- struct ufs_vreg_info *info = &hba->vreg_info;
-
- if (!info)
- goto out;
- else if (!info->vccq)
- goto out;
-
- if (unused) {
- /* shut off the rail here */
- ret = ufshcd_toggle_vreg(hba->dev, info->vccq, false);
- /*
- * Mark this rail as no longer used, so it doesn't get enabled
- * later by mistake
- */
- if (!ret)
- info->vccq->unused = true;
- } else {
- /*
- * rail should have been already enabled hence just make sure
- * that unused flag is cleared.
- */
- info->vccq->unused = false;
- }
-out:
- return ret;
-}
-
static int __ufshcd_setup_clocks(struct ufs_hba *hba, bool on,
bool skip_ref_clk)
{
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index 69ba7445d2b3..ecfa898b9ccc 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -546,7 +546,7 @@ struct ufs_hba {
int nutrs;
int nutmrs;
u32 ufs_version;
- struct ufs_hba_variant_ops *vops;
+ const struct ufs_hba_variant_ops *vops;
void *priv;
unsigned int irq;
bool is_irq_enabled;
diff --git a/drivers/scsi/virtio_scsi.c b/drivers/scsi/virtio_scsi.c
index 772b976e4ee4..8af01777d09c 100644
--- a/drivers/scsi/virtio_scsi.c
+++ b/drivers/scsi/virtio_scsi.c
@@ -100,16 +100,8 @@ static inline struct Scsi_Host *virtio_scsi_host(struct virtio_device *vdev)
static void virtscsi_compute_resid(struct scsi_cmnd *sc, u32 resid)
{
- if (!resid)
- return;
-
- if (!scsi_bidi_cmnd(sc)) {
+ if (resid)
scsi_set_resid(sc, resid);
- return;
- }
-
- scsi_in(sc)->resid = min(resid, scsi_in(sc)->length);
- scsi_out(sc)->resid = resid - scsi_in(sc)->resid;
}
/**
@@ -403,9 +395,9 @@ static int virtscsi_add_cmd(struct virtqueue *vq,
if (sc && sc->sc_data_direction != DMA_NONE) {
if (sc->sc_data_direction != DMA_FROM_DEVICE)
- out = &scsi_out(sc)->table;
+ out = &sc->sdb.table;
if (sc->sc_data_direction != DMA_TO_DEVICE)
- in = &scsi_in(sc)->table;
+ in = &sc->sdb.table;
}
/* Request header. */
@@ -594,7 +586,6 @@ static int virtscsi_device_reset(struct scsi_cmnd *sc)
return FAILED;
memset(cmd, 0, sizeof(*cmd));
- cmd->sc = sc;
cmd->req.tmf = (struct virtio_scsi_ctrl_tmf_req){
.type = VIRTIO_SCSI_T_TMF,
.subtype = cpu_to_virtio32(vscsi->vdev,
@@ -653,7 +644,6 @@ static int virtscsi_abort(struct scsi_cmnd *sc)
return FAILED;
memset(cmd, 0, sizeof(*cmd));
- cmd->sc = sc;
cmd->req.tmf = (struct virtio_scsi_ctrl_tmf_req){
.type = VIRTIO_SCSI_T_TMF,
.subtype = VIRTIO_SCSI_T_TMF_ABORT_TASK,
diff --git a/drivers/soc/bcm/bcm2835-power.c b/drivers/soc/bcm/bcm2835-power.c
index 9351349cf0a9..1e0041ec8132 100644
--- a/drivers/soc/bcm/bcm2835-power.c
+++ b/drivers/soc/bcm/bcm2835-power.c
@@ -150,7 +150,12 @@ struct bcm2835_power {
static int bcm2835_asb_enable(struct bcm2835_power *power, u32 reg)
{
- u64 start = ktime_get_ns();
+ u64 start;
+
+ if (!reg)
+ return 0;
+
+ start = ktime_get_ns();
/* Enable the module's async AXI bridges. */
ASB_WRITE(reg, ASB_READ(reg) & ~ASB_REQ_STOP);
@@ -165,7 +170,12 @@ static int bcm2835_asb_enable(struct bcm2835_power *power, u32 reg)
static int bcm2835_asb_disable(struct bcm2835_power *power, u32 reg)
{
- u64 start = ktime_get_ns();
+ u64 start;
+
+ if (!reg)
+ return 0;
+
+ start = ktime_get_ns();
/* Enable the module's async AXI bridges. */
ASB_WRITE(reg, ASB_READ(reg) | ASB_REQ_STOP);
@@ -475,7 +485,7 @@ static int bcm2835_power_pd_power_off(struct generic_pm_domain *domain)
}
}
-static void
+static int
bcm2835_init_power_domain(struct bcm2835_power *power,
int pd_xlate_index, const char *name)
{
@@ -483,6 +493,17 @@ bcm2835_init_power_domain(struct bcm2835_power *power,
struct bcm2835_power_domain *dom = &power->domains[pd_xlate_index];
dom->clk = devm_clk_get(dev->parent, name);
+ if (IS_ERR(dom->clk)) {
+ int ret = PTR_ERR(dom->clk);
+
+ if (ret == -EPROBE_DEFER)
+ return ret;
+
+ /* Some domains don't have a clk, so make sure that we
+ * don't deref an error pointer later.
+ */
+ dom->clk = NULL;
+ }
dom->base.name = name;
dom->base.power_on = bcm2835_power_pd_power_on;
@@ -495,6 +516,8 @@ bcm2835_init_power_domain(struct bcm2835_power *power,
pm_genpd_init(&dom->base, NULL, true);
power->pd_xlate.domains[pd_xlate_index] = &dom->base;
+
+ return 0;
}
/** bcm2835_reset_reset - Resets a block that has a reset line in the
@@ -592,7 +615,7 @@ static int bcm2835_power_probe(struct platform_device *pdev)
{ BCM2835_POWER_DOMAIN_IMAGE_PERI, BCM2835_POWER_DOMAIN_CAM0 },
{ BCM2835_POWER_DOMAIN_IMAGE_PERI, BCM2835_POWER_DOMAIN_CAM1 },
};
- int ret, i;
+ int ret = 0, i;
u32 id;
power = devm_kzalloc(dev, sizeof(*power), GFP_KERNEL);
@@ -619,8 +642,11 @@ static int bcm2835_power_probe(struct platform_device *pdev)
power->pd_xlate.num_domains = ARRAY_SIZE(power_domain_names);
- for (i = 0; i < ARRAY_SIZE(power_domain_names); i++)
- bcm2835_init_power_domain(power, i, power_domain_names[i]);
+ for (i = 0; i < ARRAY_SIZE(power_domain_names); i++) {
+ ret = bcm2835_init_power_domain(power, i, power_domain_names[i]);
+ if (ret)
+ goto fail;
+ }
for (i = 0; i < ARRAY_SIZE(domain_deps); i++) {
pm_genpd_add_subdomain(&power->domains[domain_deps[i].parent].base,
@@ -634,12 +660,21 @@ static int bcm2835_power_probe(struct platform_device *pdev)
ret = devm_reset_controller_register(dev, &power->reset);
if (ret)
- return ret;
+ goto fail;
of_genpd_add_provider_onecell(dev->parent->of_node, &power->pd_xlate);
dev_info(dev, "Broadcom BCM2835 power domains driver");
return 0;
+
+fail:
+ for (i = 0; i < ARRAY_SIZE(power_domain_names); i++) {
+ struct generic_pm_domain *dom = &power->domains[i].base;
+
+ if (dom->name)
+ pm_genpd_remove(dom);
+ }
+ return ret;
}
static int bcm2835_power_remove(struct platform_device *pdev)
diff --git a/drivers/spi/spi-topcliff-pch.c b/drivers/spi/spi-topcliff-pch.c
index e7e8ea1edcce..fba3f180f233 100644
--- a/drivers/spi/spi-topcliff-pch.c
+++ b/drivers/spi/spi-topcliff-pch.c
@@ -92,7 +92,6 @@
#define PCH_MAX_SPBR 1023
/* Definition for ML7213/ML7223/ML7831 by LAPIS Semiconductor */
-#define PCI_VENDOR_ID_ROHM 0x10DB
#define PCI_DEVICE_ID_ML7213_SPI 0x802c
#define PCI_DEVICE_ID_ML7223_SPI 0x800F
#define PCI_DEVICE_ID_ML7831_SPI 0x8816
diff --git a/drivers/spmi/Kconfig b/drivers/spmi/Kconfig
index 0d3b70b3bda8..d48ed7c2c6c4 100644
--- a/drivers/spmi/Kconfig
+++ b/drivers/spmi/Kconfig
@@ -12,7 +12,7 @@ if SPMI
config SPMI_MSM_PMIC_ARB
tristate "Qualcomm MSM SPMI Controller (PMIC Arbiter)"
- select IRQ_DOMAIN
+ select IRQ_DOMAIN_HIERARCHY
depends on ARCH_QCOM || COMPILE_TEST
depends on HAS_IOMEM
default ARCH_QCOM
diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c
index 360b8218f322..928759242e42 100644
--- a/drivers/spmi/spmi-pmic-arb.c
+++ b/drivers/spmi/spmi-pmic-arb.c
@@ -666,7 +666,8 @@ static int qpnpint_get_irqchip_state(struct irq_data *d,
return 0;
}
-static int qpnpint_irq_request_resources(struct irq_data *d)
+static int qpnpint_irq_domain_activate(struct irq_domain *domain,
+ struct irq_data *d, bool reserve)
{
struct spmi_pmic_arb *pmic_arb = irq_data_get_irq_chip_data(d);
u16 periph = hwirq_to_per(d->hwirq);
@@ -692,27 +693,25 @@ static struct irq_chip pmic_arb_irqchip = {
.irq_set_type = qpnpint_irq_set_type,
.irq_set_wake = qpnpint_irq_set_wake,
.irq_get_irqchip_state = qpnpint_get_irqchip_state,
- .irq_request_resources = qpnpint_irq_request_resources,
.flags = IRQCHIP_MASK_ON_SUSPEND,
};
-static int qpnpint_irq_domain_dt_translate(struct irq_domain *d,
- struct device_node *controller,
- const u32 *intspec,
- unsigned int intsize,
- unsigned long *out_hwirq,
- unsigned int *out_type)
+static int qpnpint_irq_domain_translate(struct irq_domain *d,
+ struct irq_fwspec *fwspec,
+ unsigned long *out_hwirq,
+ unsigned int *out_type)
{
struct spmi_pmic_arb *pmic_arb = d->host_data;
+ u32 *intspec = fwspec->param;
u16 apid, ppid;
int rc;
dev_dbg(&pmic_arb->spmic->dev, "intspec[0] 0x%1x intspec[1] 0x%02x intspec[2] 0x%02x\n",
intspec[0], intspec[1], intspec[2]);
- if (irq_domain_get_of_node(d) != controller)
+ if (irq_domain_get_of_node(d) != pmic_arb->spmic->dev.of_node)
return -EINVAL;
- if (intsize != 4)
+ if (fwspec->param_count != 4)
return -EINVAL;
if (intspec[0] > 0xF || intspec[1] > 0xFF || intspec[2] > 0x7)
return -EINVAL;
@@ -740,17 +739,43 @@ static int qpnpint_irq_domain_dt_translate(struct irq_domain *d,
return 0;
}
-static int qpnpint_irq_domain_map(struct irq_domain *d,
- unsigned int virq,
- irq_hw_number_t hwirq)
+
+static void qpnpint_irq_domain_map(struct spmi_pmic_arb *pmic_arb,
+ struct irq_domain *domain, unsigned int virq,
+ irq_hw_number_t hwirq, unsigned int type)
{
- struct spmi_pmic_arb *pmic_arb = d->host_data;
+ irq_flow_handler_t handler;
+
+ dev_dbg(&pmic_arb->spmic->dev, "virq = %u, hwirq = %lu, type = %u\n",
+ virq, hwirq, type);
+
+ if (type & IRQ_TYPE_EDGE_BOTH)
+ handler = handle_edge_irq;
+ else
+ handler = handle_level_irq;
+
+ irq_domain_set_info(domain, virq, hwirq, &pmic_arb_irqchip, pmic_arb,
+ handler, NULL, NULL);
+}
+
+static int qpnpint_irq_domain_alloc(struct irq_domain *domain,
+ unsigned int virq, unsigned int nr_irqs,
+ void *data)
+{
+ struct spmi_pmic_arb *pmic_arb = domain->host_data;
+ struct irq_fwspec *fwspec = data;
+ irq_hw_number_t hwirq;
+ unsigned int type;
+ int ret, i;
+
+ ret = qpnpint_irq_domain_translate(domain, fwspec, &hwirq, &type);
+ if (ret)
+ return ret;
- dev_dbg(&pmic_arb->spmic->dev, "virq = %u, hwirq = %lu\n", virq, hwirq);
+ for (i = 0; i < nr_irqs; i++)
+ qpnpint_irq_domain_map(pmic_arb, domain, virq + i, hwirq + i,
+ type);
- irq_set_chip_and_handler(virq, &pmic_arb_irqchip, handle_level_irq);
- irq_set_chip_data(virq, d->host_data);
- irq_set_noprobe(virq);
return 0;
}
@@ -1126,8 +1151,10 @@ static const struct pmic_arb_ver_ops pmic_arb_v5 = {
};
static const struct irq_domain_ops pmic_arb_irq_domain_ops = {
- .map = qpnpint_irq_domain_map,
- .xlate = qpnpint_irq_domain_dt_translate,
+ .activate = qpnpint_irq_domain_activate,
+ .alloc = qpnpint_irq_domain_alloc,
+ .free = irq_domain_free_irqs_common,
+ .translate = qpnpint_irq_domain_translate,
};
static int spmi_pmic_arb_probe(struct platform_device *pdev)
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index c0901b96cfe4..62951e836cbc 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -114,8 +114,6 @@ source "drivers/staging/ralink-gdma/Kconfig"
source "drivers/staging/mt7621-mmc/Kconfig"
-source "drivers/staging/mt7621-eth/Kconfig"
-
source "drivers/staging/mt7621-dts/Kconfig"
source "drivers/staging/gasket/Kconfig"
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index 57c6bce13ff4..d1b17ddcd354 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -47,7 +47,6 @@ obj-$(CONFIG_SPI_MT7621) += mt7621-spi/
obj-$(CONFIG_SOC_MT7621) += mt7621-dma/
obj-$(CONFIG_DMA_RALINK) += ralink-gdma/
obj-$(CONFIG_MTK_MMC) += mt7621-mmc/
-obj-$(CONFIG_NET_MEDIATEK_SOC_STAGING) += mt7621-eth/
obj-$(CONFIG_SOC_MT7621) += mt7621-dts/
obj-$(CONFIG_STAGING_GASKET_FRAMEWORK) += gasket/
obj-$(CONFIG_XIL_AXIS_FIFO) += axis-fifo/
diff --git a/drivers/staging/axis-fifo/Kconfig b/drivers/staging/axis-fifo/Kconfig
index 687537203d9c..d9725888af6f 100644
--- a/drivers/staging/axis-fifo/Kconfig
+++ b/drivers/staging/axis-fifo/Kconfig
@@ -3,6 +3,7 @@
#
config XIL_AXIS_FIFO
tristate "Xilinx AXI-Stream FIFO IP core driver"
+ depends on OF
default n
help
This adds support for the Xilinx AXI-Stream
diff --git a/drivers/staging/comedi/comedidev.h b/drivers/staging/comedi/comedidev.h
index a7d569cfca5d..0dff1ac057cd 100644
--- a/drivers/staging/comedi/comedidev.h
+++ b/drivers/staging/comedi/comedidev.h
@@ -1001,6 +1001,8 @@ int comedi_dio_insn_config(struct comedi_device *dev,
unsigned int mask);
unsigned int comedi_dio_update_state(struct comedi_subdevice *s,
unsigned int *data);
+unsigned int comedi_bytes_per_scan_cmd(struct comedi_subdevice *s,
+ struct comedi_cmd *cmd);
unsigned int comedi_bytes_per_scan(struct comedi_subdevice *s);
unsigned int comedi_nscans_left(struct comedi_subdevice *s,
unsigned int nscans);
diff --git a/drivers/staging/comedi/drivers.c b/drivers/staging/comedi/drivers.c
index eefa62f42c0f..5a32b8fc000e 100644
--- a/drivers/staging/comedi/drivers.c
+++ b/drivers/staging/comedi/drivers.c
@@ -394,11 +394,13 @@ unsigned int comedi_dio_update_state(struct comedi_subdevice *s,
EXPORT_SYMBOL_GPL(comedi_dio_update_state);
/**
- * comedi_bytes_per_scan() - Get length of asynchronous command "scan" in bytes
+ * comedi_bytes_per_scan_cmd() - Get length of asynchronous command "scan" in
+ * bytes
* @s: COMEDI subdevice.
+ * @cmd: COMEDI command.
*
* Determines the overall scan length according to the subdevice type and the
- * number of channels in the scan.
+ * number of channels in the scan for the specified command.
*
* For digital input, output or input/output subdevices, samples for
* multiple channels are assumed to be packed into one or more unsigned
@@ -408,9 +410,9 @@ EXPORT_SYMBOL_GPL(comedi_dio_update_state);
*
* Returns the overall scan length in bytes.
*/
-unsigned int comedi_bytes_per_scan(struct comedi_subdevice *s)
+unsigned int comedi_bytes_per_scan_cmd(struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
{
- struct comedi_cmd *cmd = &s->async->cmd;
unsigned int num_samples;
unsigned int bits_per_sample;
@@ -427,6 +429,29 @@ unsigned int comedi_bytes_per_scan(struct comedi_subdevice *s)
}
return comedi_samples_to_bytes(s, num_samples);
}
+EXPORT_SYMBOL_GPL(comedi_bytes_per_scan_cmd);
+
+/**
+ * comedi_bytes_per_scan() - Get length of asynchronous command "scan" in bytes
+ * @s: COMEDI subdevice.
+ *
+ * Determines the overall scan length according to the subdevice type and the
+ * number of channels in the scan for the current command.
+ *
+ * For digital input, output or input/output subdevices, samples for
+ * multiple channels are assumed to be packed into one or more unsigned
+ * short or unsigned int values according to the subdevice's %SDF_LSAMPL
+ * flag. For other types of subdevice, samples are assumed to occupy a
+ * whole unsigned short or unsigned int according to the %SDF_LSAMPL flag.
+ *
+ * Returns the overall scan length in bytes.
+ */
+unsigned int comedi_bytes_per_scan(struct comedi_subdevice *s)
+{
+ struct comedi_cmd *cmd = &s->async->cmd;
+
+ return comedi_bytes_per_scan_cmd(s, cmd);
+}
EXPORT_SYMBOL_GPL(comedi_bytes_per_scan);
static unsigned int __comedi_nscans_left(struct comedi_subdevice *s,
diff --git a/drivers/staging/comedi/drivers/ni_mio_common.c b/drivers/staging/comedi/drivers/ni_mio_common.c
index 5edf59ac6706..b04dad8c7092 100644
--- a/drivers/staging/comedi/drivers/ni_mio_common.c
+++ b/drivers/staging/comedi/drivers/ni_mio_common.c
@@ -3545,6 +3545,7 @@ static int ni_cdio_cmdtest(struct comedi_device *dev,
struct comedi_subdevice *s, struct comedi_cmd *cmd)
{
struct ni_private *devpriv = dev->private;
+ unsigned int bytes_per_scan;
int err = 0;
/* Step 1 : check if triggers are trivially valid */
@@ -3579,9 +3580,12 @@ static int ni_cdio_cmdtest(struct comedi_device *dev,
err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
cmd->chanlist_len);
- err |= comedi_check_trigger_arg_max(&cmd->stop_arg,
- s->async->prealloc_bufsz /
- comedi_bytes_per_scan(s));
+ bytes_per_scan = comedi_bytes_per_scan_cmd(s, cmd);
+ if (bytes_per_scan) {
+ err |= comedi_check_trigger_arg_max(&cmd->stop_arg,
+ s->async->prealloc_bufsz /
+ bytes_per_scan);
+ }
if (err)
return 3;
diff --git a/drivers/staging/erofs/data.c b/drivers/staging/erofs/data.c
index 9c471f08ffd4..526e0dbea5b5 100644
--- a/drivers/staging/erofs/data.c
+++ b/drivers/staging/erofs/data.c
@@ -20,8 +20,9 @@ static inline void read_endio(struct bio *bio)
int i;
struct bio_vec *bvec;
const blk_status_t err = bio->bi_status;
+ struct bvec_iter_all iter_all;
- bio_for_each_segment_all(bvec, bio, i) {
+ bio_for_each_segment_all(bvec, bio, i, iter_all) {
struct page *page = bvec->bv_page;
/* page is already locked */
diff --git a/drivers/staging/erofs/dir.c b/drivers/staging/erofs/dir.c
index 829f7b12e0dc..9bbc68729c11 100644
--- a/drivers/staging/erofs/dir.c
+++ b/drivers/staging/erofs/dir.c
@@ -23,6 +23,21 @@ static const unsigned char erofs_filetype_table[EROFS_FT_MAX] = {
[EROFS_FT_SYMLINK] = DT_LNK,
};
+static void debug_one_dentry(unsigned char d_type, const char *de_name,
+ unsigned int de_namelen)
+{
+#ifdef CONFIG_EROFS_FS_DEBUG
+ /* since the on-disk name could not have the trailing '\0' */
+ unsigned char dbg_namebuf[EROFS_NAME_LEN + 1];
+
+ memcpy(dbg_namebuf, de_name, de_namelen);
+ dbg_namebuf[de_namelen] = '\0';
+
+ debugln("found dirent %s de_len %u d_type %d", dbg_namebuf,
+ de_namelen, d_type);
+#endif
+}
+
static int erofs_fill_dentries(struct dir_context *ctx,
void *dentry_blk, unsigned int *ofs,
unsigned int nameoff, unsigned int maxsize)
@@ -33,14 +48,10 @@ static int erofs_fill_dentries(struct dir_context *ctx,
de = dentry_blk + *ofs;
while (de < end) {
const char *de_name;
- int de_namelen;
+ unsigned int de_namelen;
unsigned char d_type;
-#ifdef CONFIG_EROFS_FS_DEBUG
- unsigned int dbg_namelen;
- unsigned char dbg_namebuf[EROFS_NAME_LEN];
-#endif
- if (unlikely(de->file_type < EROFS_FT_MAX))
+ if (de->file_type < EROFS_FT_MAX)
d_type = erofs_filetype_table[de->file_type];
else
d_type = DT_UNKNOWN;
@@ -48,26 +59,20 @@ static int erofs_fill_dentries(struct dir_context *ctx,
nameoff = le16_to_cpu(de->nameoff);
de_name = (char *)dentry_blk + nameoff;
- de_namelen = unlikely(de + 1 >= end) ?
- /* last directory entry */
- strnlen(de_name, maxsize - nameoff) :
- le16_to_cpu(de[1].nameoff) - nameoff;
+ /* the last dirent in the block? */
+ if (de + 1 >= end)
+ de_namelen = strnlen(de_name, maxsize - nameoff);
+ else
+ de_namelen = le16_to_cpu(de[1].nameoff) - nameoff;
/* a corrupted entry is found */
- if (unlikely(de_namelen < 0)) {
+ if (unlikely(nameoff + de_namelen > maxsize ||
+ de_namelen > EROFS_NAME_LEN)) {
DBG_BUGON(1);
return -EIO;
}
-#ifdef CONFIG_EROFS_FS_DEBUG
- dbg_namelen = min(EROFS_NAME_LEN - 1, de_namelen);
- memcpy(dbg_namebuf, de_name, dbg_namelen);
- dbg_namebuf[dbg_namelen] = '\0';
-
- debugln("%s, found de_name %s de_len %d d_type %d", __func__,
- dbg_namebuf, de_namelen, d_type);
-#endif
-
+ debug_one_dentry(d_type, de_name, de_namelen);
if (!dir_emit(ctx, de_name, de_namelen,
le64_to_cpu(de->nid), d_type))
/* stopped by some reason */
diff --git a/drivers/staging/erofs/unzip_vle.c b/drivers/staging/erofs/unzip_vle.c
index 02f34a83147d..31eef8395774 100644
--- a/drivers/staging/erofs/unzip_vle.c
+++ b/drivers/staging/erofs/unzip_vle.c
@@ -849,8 +849,9 @@ static inline void z_erofs_vle_read_endio(struct bio *bio)
#ifdef EROFS_FS_HAS_MANAGED_CACHE
struct address_space *mc = NULL;
#endif
+ struct bvec_iter_all iter_all;
- bio_for_each_segment_all(bvec, bio, i) {
+ bio_for_each_segment_all(bvec, bio, i, iter_all) {
struct page *page = bvec->bv_page;
bool cachemngd = false;
@@ -971,6 +972,7 @@ repeat:
overlapped = false;
compressed_pages = grp->compressed_pages;
+ err = 0;
for (i = 0; i < clusterpages; ++i) {
unsigned int pagenr;
@@ -980,26 +982,39 @@ repeat:
DBG_BUGON(!page);
DBG_BUGON(!page->mapping);
- if (z_erofs_is_stagingpage(page))
- continue;
+ if (!z_erofs_is_stagingpage(page)) {
#ifdef EROFS_FS_HAS_MANAGED_CACHE
- if (page->mapping == MNGD_MAPPING(sbi)) {
- DBG_BUGON(!PageUptodate(page));
- continue;
- }
+ if (page->mapping == MNGD_MAPPING(sbi)) {
+ if (unlikely(!PageUptodate(page)))
+ err = -EIO;
+ continue;
+ }
#endif
- /* only non-head page could be reused as a compressed page */
- pagenr = z_erofs_onlinepage_index(page);
+ /*
+ * only if non-head page can be selected
+ * for inplace decompression
+ */
+ pagenr = z_erofs_onlinepage_index(page);
- DBG_BUGON(pagenr >= nr_pages);
- DBG_BUGON(pages[pagenr]);
- ++sparsemem_pages;
- pages[pagenr] = page;
+ DBG_BUGON(pagenr >= nr_pages);
+ DBG_BUGON(pages[pagenr]);
+ ++sparsemem_pages;
+ pages[pagenr] = page;
- overlapped = true;
+ overlapped = true;
+ }
+
+ /* PG_error needs checking for inplaced and staging pages */
+ if (unlikely(PageError(page))) {
+ DBG_BUGON(PageUptodate(page));
+ err = -EIO;
+ }
}
+ if (unlikely(err))
+ goto out;
+
llen = (nr_pages << PAGE_SHIFT) - work->pageofs;
if (z_erofs_vle_workgrp_fmt(grp) == Z_EROFS_VLE_WORKGRP_FMT_PLAIN) {
@@ -1028,6 +1043,10 @@ repeat:
skip_allocpage:
vout = erofs_vmap(pages, nr_pages);
+ if (!vout) {
+ err = -ENOMEM;
+ goto out;
+ }
err = z_erofs_vle_unzip_vmap(compressed_pages,
clusterpages, vout, llen, work->pageofs, overlapped);
@@ -1193,6 +1212,7 @@ repeat:
if (page->mapping == mc) {
WRITE_ONCE(grp->compressed_pages[nr], page);
+ ClearPageError(page);
if (!PagePrivate(page)) {
/*
* impossible to be !PagePrivate(page) for
diff --git a/drivers/staging/erofs/unzip_vle_lz4.c b/drivers/staging/erofs/unzip_vle_lz4.c
index 48b263a2731a..0daac9b984a8 100644
--- a/drivers/staging/erofs/unzip_vle_lz4.c
+++ b/drivers/staging/erofs/unzip_vle_lz4.c
@@ -136,10 +136,13 @@ int z_erofs_vle_unzip_fast_percpu(struct page **compressed_pages,
nr_pages = DIV_ROUND_UP(outlen + pageofs, PAGE_SIZE);
- if (clusterpages == 1)
+ if (clusterpages == 1) {
vin = kmap_atomic(compressed_pages[0]);
- else
+ } else {
vin = erofs_vmap(compressed_pages, clusterpages);
+ if (!vin)
+ return -ENOMEM;
+ }
preempt_disable();
vout = erofs_pcpubuf[smp_processor_id()].data;
diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig
index 19cadd17e542..1da5c20d65c0 100644
--- a/drivers/staging/media/Kconfig
+++ b/drivers/staging/media/Kconfig
@@ -25,10 +25,6 @@ source "drivers/staging/media/davinci_vpfe/Kconfig"
source "drivers/staging/media/imx/Kconfig"
-source "drivers/staging/media/imx074/Kconfig"
-
-source "drivers/staging/media/mt9t031/Kconfig"
-
source "drivers/staging/media/omap4iss/Kconfig"
source "drivers/staging/media/rockchip/vpu/Kconfig"
@@ -41,4 +37,6 @@ source "drivers/staging/media/zoran/Kconfig"
source "drivers/staging/media/ipu3/Kconfig"
+source "drivers/staging/media/soc_camera/Kconfig"
+
endif
diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile
index edde1960b030..0355e3030504 100644
--- a/drivers/staging/media/Makefile
+++ b/drivers/staging/media/Makefile
@@ -1,8 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_I2C_BCM2048) += bcm2048/
obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx/
-obj-$(CONFIG_SOC_CAMERA_IMX074) += imx074/
-obj-$(CONFIG_SOC_CAMERA_MT9T031) += mt9t031/
obj-$(CONFIG_VIDEO_DM365_VPFE) += davinci_vpfe/
obj-$(CONFIG_VIDEO_OMAP4) += omap4iss/
obj-$(CONFIG_VIDEO_SUNXI) += sunxi/
@@ -10,3 +8,4 @@ obj-$(CONFIG_TEGRA_VDE) += tegra-vde/
obj-$(CONFIG_VIDEO_ZORAN) += zoran/
obj-$(CONFIG_VIDEO_ROCKCHIP_VPU) += rockchip/vpu/
obj-$(CONFIG_VIDEO_IPU3_IMGU) += ipu3/
+obj-$(CONFIG_SOC_CAMERA) += soc_camera/
diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.c b/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.c
index 5618c804c7e4..565a3dc5bed1 100644
--- a/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.c
+++ b/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.c
@@ -781,7 +781,7 @@ ipipe_set_3d_lut_regs(void __iomem *base_addr, void __iomem *isp5_base_addr,
if (!lut_3d->en)
return;
- /* valied table */
+ /* valid table */
tbl = lut_3d->table;
for (i = 0; i < VPFE_IPIPE_MAX_SIZE_3D_LUT; i++) {
/*
diff --git a/drivers/staging/media/davinci_vpfe/dm365_isif.c b/drivers/staging/media/davinci_vpfe/dm365_isif.c
index 625d0aa8367f..0a6d038fcec9 100644
--- a/drivers/staging/media/davinci_vpfe/dm365_isif.c
+++ b/drivers/staging/media/davinci_vpfe/dm365_isif.c
@@ -675,7 +675,7 @@ static void isif_config_bclamp(struct vpfe_isif_device *isif,
val = (bc->bc_mode_color & ISIF_BC_MODE_COLOR_MASK) <<
ISIF_BC_MODE_COLOR_SHIFT;
- /* Enable BC and horizontal clamp calculation paramaters */
+ /* Enable BC and horizontal clamp calculation parameters */
val = val | 1 | ((bc->horz.mode & ISIF_HORZ_BC_MODE_MASK) <<
ISIF_HORZ_BC_MODE_SHIFT);
@@ -712,7 +712,7 @@ static void isif_config_bclamp(struct vpfe_isif_device *isif,
isif_write(isif->isif_cfg.base_addr, val, CLHWIN2);
}
- /* vertical clamp calculation paramaters */
+ /* vertical clamp calculation parameters */
/* OB H Valid */
val = bc->vert.ob_h_sz_calc & ISIF_VERT_BC_OB_H_SZ_MASK;
diff --git a/drivers/staging/media/davinci_vpfe/dm365_resizer.c b/drivers/staging/media/davinci_vpfe/dm365_resizer.c
index 6098f43ac51b..9d726298b406 100644
--- a/drivers/staging/media/davinci_vpfe/dm365_resizer.c
+++ b/drivers/staging/media/davinci_vpfe/dm365_resizer.c
@@ -1284,7 +1284,7 @@ static int resizer_set_stream(struct v4l2_subdev *sd, int enable)
* @cfg: V4L2 subdev pad config
* @pad: pad number.
* @which: wanted subdev format.
- * Retun wanted mbus frame format.
+ * Return wanted mbus frame format.
*/
static struct v4l2_mbus_framefmt *
__resizer_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
@@ -1785,7 +1785,7 @@ void vpfe_resizer_unregister_entities(struct vpfe_resizer_device *vpfe_rsz)
/*
* vpfe_resizer_register_entities() - Register entity
- * @resizer - pointer to resizer devive.
+ * @resizer - pointer to resizer device.
* @vdev: pointer to v4l2 device structure.
*/
int vpfe_resizer_register_entities(struct vpfe_resizer_device *resizer,
diff --git a/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c b/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c
index 34d63c2e9199..57b93605bc58 100644
--- a/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c
+++ b/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c
@@ -528,7 +528,7 @@ static void vpfe_cleanup_modules(struct vpfe_device *vpfe_dev,
* @vpfe_dev - ptr to vpfe capture device
* @pdev - pointer to platform device
*
- * intialize all v4l2 subdevs and media entities
+ * initialize all v4l2 subdevs and media entities
*/
static int vpfe_initialize_modules(struct vpfe_device *vpfe_dev,
struct platform_device *pdev)
diff --git a/drivers/staging/media/imx/Kconfig b/drivers/staging/media/imx/Kconfig
index bfc17de56b17..36b276ea2ecc 100644
--- a/drivers/staging/media/imx/Kconfig
+++ b/drivers/staging/media/imx/Kconfig
@@ -11,7 +11,7 @@ config VIDEO_IMX_MEDIA
driver for the i.MX5/6 SOC.
if VIDEO_IMX_MEDIA
-menu "i.MX5/6 Media Sub devices"
+menu "i.MX5/6/7 Media Sub devices"
config VIDEO_IMX_CSI
tristate "i.MX5/6 Camera Sensor Interface driver"
@@ -20,5 +20,12 @@ config VIDEO_IMX_CSI
---help---
A video4linux camera sensor interface driver for i.MX5/6.
+config VIDEO_IMX7_CSI
+ tristate "i.MX7 Camera Sensor Interface driver"
+ depends on VIDEO_IMX_MEDIA && VIDEO_DEV && I2C
+ default y
+ help
+ Enable support for video4linux camera sensor interface driver for
+ i.MX7.
endmenu
endif
diff --git a/drivers/staging/media/imx/Makefile b/drivers/staging/media/imx/Makefile
index 698a4210316e..d2d909a36239 100644
--- a/drivers/staging/media/imx/Makefile
+++ b/drivers/staging/media/imx/Makefile
@@ -1,5 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
imx-media-objs := imx-media-dev.o imx-media-internal-sd.o imx-media-of.o
+imx-media-objs += imx-media-dev-common.o
imx-media-common-objs := imx-media-utils.o imx-media-fim.o
imx-media-ic-objs := imx-ic-common.o imx-ic-prp.o imx-ic-prpencvf.o
@@ -11,3 +12,6 @@ obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx-media-ic.o
obj-$(CONFIG_VIDEO_IMX_CSI) += imx-media-csi.o
obj-$(CONFIG_VIDEO_IMX_CSI) += imx6-mipi-csi2.o
+
+obj-$(CONFIG_VIDEO_IMX7_CSI) += imx7-media-csi.o
+obj-$(CONFIG_VIDEO_IMX7_CSI) += imx7-mipi-csis.o
diff --git a/drivers/staging/media/imx/TODO b/drivers/staging/media/imx/TODO
index aeeb15494a49..6f29b5ca5324 100644
--- a/drivers/staging/media/imx/TODO
+++ b/drivers/staging/media/imx/TODO
@@ -45,3 +45,12 @@
Which means a port must not contain mixed-use endpoints, they
must all refer to media links between V4L2 subdevices.
+
+- i.MX7: all of the above, since it uses the imx media core
+
+- i.MX7: use Frame Interval Monitor
+
+- i.MX7: runtime testing with parallel sensor, links setup and streaming
+
+- i.MX7: runtime testing with different formats, for the time only 10-bit bayer
+ is tested
diff --git a/drivers/staging/media/imx/imx-ic-common.c b/drivers/staging/media/imx/imx-ic-common.c
index cfdd4900a3be..765919487a73 100644
--- a/drivers/staging/media/imx/imx-ic-common.c
+++ b/drivers/staging/media/imx/imx-ic-common.c
@@ -41,13 +41,13 @@ static int imx_ic_probe(struct platform_device *pdev)
pdata = priv->dev->platform_data;
priv->ipu_id = pdata->ipu_id;
switch (pdata->grp_id) {
- case IMX_MEDIA_GRP_ID_IC_PRP:
+ case IMX_MEDIA_GRP_ID_IPU_IC_PRP:
priv->task_id = IC_TASK_PRP;
break;
- case IMX_MEDIA_GRP_ID_IC_PRPENC:
+ case IMX_MEDIA_GRP_ID_IPU_IC_PRPENC:
priv->task_id = IC_TASK_ENCODER;
break;
- case IMX_MEDIA_GRP_ID_IC_PRPVF:
+ case IMX_MEDIA_GRP_ID_IPU_IC_PRPVF:
priv->task_id = IC_TASK_VIEWFINDER;
break;
default:
diff --git a/drivers/staging/media/imx/imx-ic-prp.c b/drivers/staging/media/imx/imx-ic-prp.c
index 98923fc844ce..3d43cdcb4bb9 100644
--- a/drivers/staging/media/imx/imx-ic-prp.c
+++ b/drivers/staging/media/imx/imx-ic-prp.c
@@ -77,7 +77,7 @@ static int prp_start(struct prp_priv *priv)
priv->ipu = priv->md->ipu[ic_priv->ipu_id];
/* set IC to receive from CSI or VDI depending on source */
- src_is_vdic = !!(priv->src_sd->grp_id & IMX_MEDIA_GRP_ID_VDIC);
+ src_is_vdic = !!(priv->src_sd->grp_id & IMX_MEDIA_GRP_ID_IPU_VDIC);
ipu_set_ic_src_mux(priv->ipu, priv->csi_id, src_is_vdic);
@@ -237,8 +237,8 @@ static int prp_link_setup(struct media_entity *entity,
ret = -EBUSY;
goto out;
}
- if (priv->sink_sd_prpenc && (remote_sd->grp_id &
- IMX_MEDIA_GRP_ID_VDIC)) {
+ if (priv->sink_sd_prpenc &&
+ (remote_sd->grp_id & IMX_MEDIA_GRP_ID_IPU_VDIC)) {
ret = -EINVAL;
goto out;
}
@@ -259,7 +259,7 @@ static int prp_link_setup(struct media_entity *entity,
goto out;
}
if (priv->src_sd && (priv->src_sd->grp_id &
- IMX_MEDIA_GRP_ID_VDIC)) {
+ IMX_MEDIA_GRP_ID_IPU_VDIC)) {
ret = -EINVAL;
goto out;
}
@@ -309,13 +309,13 @@ static int prp_link_validate(struct v4l2_subdev *sd,
return ret;
csi = imx_media_find_upstream_subdev(priv->md, &ic_priv->sd.entity,
- IMX_MEDIA_GRP_ID_CSI);
+ IMX_MEDIA_GRP_ID_IPU_CSI);
if (IS_ERR(csi))
csi = NULL;
mutex_lock(&priv->lock);
- if (priv->src_sd->grp_id & IMX_MEDIA_GRP_ID_VDIC) {
+ if (priv->src_sd->grp_id & IMX_MEDIA_GRP_ID_IPU_VDIC) {
/*
* the ->PRPENC link cannot be enabled if the source
* is the VDIC
@@ -334,10 +334,10 @@ static int prp_link_validate(struct v4l2_subdev *sd,
if (csi) {
switch (csi->grp_id) {
- case IMX_MEDIA_GRP_ID_CSI0:
+ case IMX_MEDIA_GRP_ID_IPU_CSI0:
priv->csi_id = 0;
break;
- case IMX_MEDIA_GRP_ID_CSI1:
+ case IMX_MEDIA_GRP_ID_IPU_CSI1:
priv->csi_id = 1;
break;
default:
@@ -422,9 +422,14 @@ static int prp_s_frame_interval(struct v4l2_subdev *sd,
if (fi->pad >= PRP_NUM_PADS)
return -EINVAL;
- /* No limits on frame interval */
mutex_lock(&priv->lock);
- priv->frame_interval = fi->interval;
+
+ /* No limits on valid frame intervals */
+ if (fi->interval.numerator == 0 || fi->interval.denominator == 0)
+ fi->interval = priv->frame_interval;
+ else
+ priv->frame_interval = fi->interval;
+
mutex_unlock(&priv->lock);
return 0;
diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c
index 28f41caba05d..5c8e6ad8c025 100644
--- a/drivers/staging/media/imx/imx-ic-prpencvf.c
+++ b/drivers/staging/media/imx/imx-ic-prpencvf.c
@@ -48,7 +48,7 @@
#define MAX_W_SRC 1024
#define MAX_H_SRC 1024
-#define W_ALIGN_SRC 4 /* multiple of 16 pixels */
+#define W_ALIGN_SRC 1 /* multiple of 2 pixels */
#define H_ALIGN_SRC 1 /* multiple of 2 lines */
#define S_ALIGN 1 /* multiple of 2 */
@@ -106,6 +106,7 @@ struct prp_priv {
u32 frame_sequence; /* frame sequence counter */
bool last_eof; /* waiting for last EOF at stream off */
bool nfb4eof; /* NFB4EOF encountered during streaming */
+ bool interweave_swap; /* swap top/bottom lines when interweaving */
struct completion last_eof_comp;
};
@@ -235,6 +236,9 @@ static void prp_vb2_buf_done(struct prp_priv *priv, struct ipuv3_channel *ch)
if (ipu_idmac_buffer_is_ready(ch, priv->ipu_buf_num))
ipu_idmac_clear_buffer(ch, priv->ipu_buf_num);
+ if (priv->interweave_swap && ch == priv->out_ch)
+ phys += vdev->fmt.fmt.pix.bytesperline;
+
ipu_cpmem_set_buffer(ch, priv->ipu_buf_num, phys);
}
@@ -354,20 +358,30 @@ static int prp_setup_channel(struct prp_priv *priv,
{
struct imx_media_video_dev *vdev = priv->vdev;
const struct imx_media_pixfmt *outcc;
- struct v4l2_mbus_framefmt *infmt;
+ struct v4l2_mbus_framefmt *outfmt;
unsigned int burst_size;
struct ipu_image image;
+ bool interweave;
int ret;
- infmt = &priv->format_mbus[PRPENCVF_SINK_PAD];
+ outfmt = &priv->format_mbus[PRPENCVF_SRC_PAD];
outcc = vdev->cc;
ipu_cpmem_zero(channel);
memset(&image, 0, sizeof(image));
image.pix = vdev->fmt.fmt.pix;
- image.rect.width = image.pix.width;
- image.rect.height = image.pix.height;
+ image.rect = vdev->compose;
+
+ /*
+ * If the field type at capture interface is interlaced, and
+ * the output IDMAC pad is sequential, enable interweave at
+ * the IDMAC output channel.
+ */
+ interweave = V4L2_FIELD_IS_INTERLACED(image.pix.field) &&
+ V4L2_FIELD_IS_SEQUENTIAL(outfmt->field);
+ priv->interweave_swap = interweave &&
+ image.pix.field == V4L2_FIELD_INTERLACED_BT;
if (rot_swap_width_height) {
swap(image.pix.width, image.pix.height);
@@ -378,15 +392,25 @@ static int prp_setup_channel(struct prp_priv *priv,
(image.pix.width * outcc->bpp) >> 3;
}
+ if (priv->interweave_swap && channel == priv->out_ch) {
+ /* start interweave scan at 1st top line (2nd line) */
+ image.rect.top = 1;
+ }
+
image.phys0 = addr0;
image.phys1 = addr1;
- if (channel == priv->out_ch || channel == priv->rot_out_ch) {
+ /*
+ * Skip writing U and V components to odd rows in the output
+ * channels for planar 4:2:0 (but not when enabling IDMAC
+ * interweaving, they are incompatible).
+ */
+ if ((channel == priv->out_ch && !interweave) ||
+ channel == priv->rot_out_ch) {
switch (image.pix.pixelformat) {
case V4L2_PIX_FMT_YUV420:
case V4L2_PIX_FMT_YVU420:
case V4L2_PIX_FMT_NV12:
- /* Skip writing U and V components to odd rows */
ipu_cpmem_skip_odd_chroma_rows(channel);
break;
}
@@ -409,10 +433,12 @@ static int prp_setup_channel(struct prp_priv *priv,
if (rot_mode)
ipu_cpmem_set_rotation(channel, rot_mode);
- if (image.pix.field == V4L2_FIELD_NONE &&
- V4L2_FIELD_HAS_BOTH(infmt->field) &&
- channel == priv->out_ch)
- ipu_cpmem_interlaced_scan(channel, image.pix.bytesperline);
+ if (interweave && channel == priv->out_ch)
+ ipu_cpmem_interlaced_scan(channel,
+ priv->interweave_swap ?
+ -image.pix.bytesperline :
+ image.pix.bytesperline,
+ image.pix.pixelformat);
ret = ipu_ic_task_idma_init(priv->ic, channel,
image.pix.width, image.pix.height,
@@ -680,12 +706,23 @@ static int prp_start(struct prp_priv *priv)
goto out_free_nfb4eof_irq;
}
+ /* start upstream */
+ ret = v4l2_subdev_call(priv->src_sd, video, s_stream, 1);
+ ret = (ret && ret != -ENOIOCTLCMD) ? ret : 0;
+ if (ret) {
+ v4l2_err(&ic_priv->sd,
+ "upstream stream on failed: %d\n", ret);
+ goto out_free_eof_irq;
+ }
+
/* start the EOF timeout timer */
mod_timer(&priv->eof_timeout_timer,
jiffies + msecs_to_jiffies(IMX_MEDIA_EOF_TIMEOUT));
return 0;
+out_free_eof_irq:
+ devm_free_irq(ic_priv->dev, priv->eof_irq, priv);
out_free_nfb4eof_irq:
devm_free_irq(ic_priv->dev, priv->nfb4eof_irq, priv);
out_unsetup:
@@ -717,6 +754,12 @@ static void prp_stop(struct prp_priv *priv)
if (ret == 0)
v4l2_warn(&ic_priv->sd, "wait last EOF timeout\n");
+ /* stop upstream */
+ ret = v4l2_subdev_call(priv->src_sd, video, s_stream, 0);
+ if (ret && ret != -ENOIOCTLCMD)
+ v4l2_warn(&ic_priv->sd,
+ "upstream stream off failed: %d\n", ret);
+
devm_free_irq(ic_priv->dev, priv->eof_irq, priv);
devm_free_irq(ic_priv->dev, priv->nfb4eof_irq, priv);
@@ -838,8 +881,7 @@ static void prp_try_fmt(struct prp_priv *priv,
infmt = __prp_get_fmt(priv, cfg, PRPENCVF_SINK_PAD, sdformat->which);
if (sdformat->pad == PRPENCVF_SRC_PAD) {
- if (sdformat->format.field != V4L2_FIELD_NONE)
- sdformat->format.field = infmt->field;
+ sdformat->format.field = infmt->field;
prp_bound_align_output(&sdformat->format, infmt,
priv->rot_mode);
@@ -870,6 +912,7 @@ static int prp_set_fmt(struct v4l2_subdev *sd,
const struct imx_media_pixfmt *cc;
struct v4l2_pix_format vdev_fmt;
struct v4l2_mbus_framefmt *fmt;
+ struct v4l2_rect vdev_compose;
int ret = 0;
if (sdformat->pad >= PRPENCVF_NUM_PADS)
@@ -911,11 +954,11 @@ static int prp_set_fmt(struct v4l2_subdev *sd,
priv->cc[sdformat->pad] = cc;
/* propagate output pad format to capture device */
- imx_media_mbus_fmt_to_pix_fmt(&vdev_fmt,
+ imx_media_mbus_fmt_to_pix_fmt(&vdev_fmt, &vdev_compose,
&priv->format_mbus[PRPENCVF_SRC_PAD],
priv->cc[PRPENCVF_SRC_PAD]);
mutex_unlock(&priv->lock);
- imx_media_capture_device_set_format(vdev, &vdev_fmt);
+ imx_media_capture_device_set_format(vdev, &vdev_fmt, &vdev_compose);
return 0;
out:
@@ -1148,15 +1191,6 @@ static int prp_s_stream(struct v4l2_subdev *sd, int enable)
if (ret)
goto out;
- /* start/stop upstream */
- ret = v4l2_subdev_call(priv->src_sd, video, s_stream, enable);
- ret = (ret && ret != -ENOIOCTLCMD) ? ret : 0;
- if (ret) {
- if (enable)
- prp_stop(priv);
- goto out;
- }
-
update_count:
priv->stream_count += enable ? 1 : -1;
if (priv->stream_count < 0)
@@ -1189,9 +1223,14 @@ static int prp_s_frame_interval(struct v4l2_subdev *sd,
if (fi->pad >= PRPENCVF_NUM_PADS)
return -EINVAL;
- /* No limits on frame interval */
mutex_lock(&priv->lock);
- priv->frame_interval = fi->interval;
+
+ /* No limits on valid frame intervals */
+ if (fi->interval.numerator == 0 || fi->interval.denominator == 0)
+ fi->interval = priv->frame_interval;
+ else
+ priv->frame_interval = fi->interval;
+
mutex_unlock(&priv->lock);
return 0;
diff --git a/drivers/staging/media/imx/imx-media-capture.c b/drivers/staging/media/imx/imx-media-capture.c
index b37e1186eb2f..9703c85b19c4 100644
--- a/drivers/staging/media/imx/imx-media-capture.c
+++ b/drivers/staging/media/imx/imx-media-capture.c
@@ -203,21 +203,14 @@ static int capture_g_fmt_vid_cap(struct file *file, void *fh,
return 0;
}
-static int capture_try_fmt_vid_cap(struct file *file, void *fh,
- struct v4l2_format *f)
+static int __capture_try_fmt_vid_cap(struct capture_priv *priv,
+ struct v4l2_subdev_format *fmt_src,
+ struct v4l2_format *f,
+ struct v4l2_rect *compose)
{
- struct capture_priv *priv = video_drvdata(file);
- struct v4l2_subdev_format fmt_src;
const struct imx_media_pixfmt *cc, *cc_src;
- int ret;
-
- fmt_src.pad = priv->src_sd_pad;
- fmt_src.which = V4L2_SUBDEV_FORMAT_ACTIVE;
- ret = v4l2_subdev_call(priv->src_sd, pad, get_fmt, NULL, &fmt_src);
- if (ret)
- return ret;
- cc_src = imx_media_find_ipu_format(fmt_src.format.code, CS_SEL_ANY);
+ cc_src = imx_media_find_ipu_format(fmt_src->format.code, CS_SEL_ANY);
if (cc_src) {
u32 fourcc, cs_sel;
@@ -231,7 +224,7 @@ static int capture_try_fmt_vid_cap(struct file *file, void *fh,
cc = imx_media_find_format(fourcc, cs_sel, false);
}
} else {
- cc_src = imx_media_find_mbus_format(fmt_src.format.code,
+ cc_src = imx_media_find_mbus_format(fmt_src->format.code,
CS_SEL_ANY, true);
if (WARN_ON(!cc_src))
return -EINVAL;
@@ -239,15 +232,48 @@ static int capture_try_fmt_vid_cap(struct file *file, void *fh,
cc = cc_src;
}
- imx_media_mbus_fmt_to_pix_fmt(&f->fmt.pix, &fmt_src.format, cc);
+ /* allow IDMAC interweave but enforce field order from source */
+ if (V4L2_FIELD_IS_INTERLACED(f->fmt.pix.field)) {
+ switch (fmt_src->format.field) {
+ case V4L2_FIELD_SEQ_TB:
+ fmt_src->format.field = V4L2_FIELD_INTERLACED_TB;
+ break;
+ case V4L2_FIELD_SEQ_BT:
+ fmt_src->format.field = V4L2_FIELD_INTERLACED_BT;
+ break;
+ default:
+ break;
+ }
+ }
+
+ imx_media_mbus_fmt_to_pix_fmt(&f->fmt.pix, compose,
+ &fmt_src->format, cc);
return 0;
}
+static int capture_try_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct capture_priv *priv = video_drvdata(file);
+ struct v4l2_subdev_format fmt_src;
+ int ret;
+
+ fmt_src.pad = priv->src_sd_pad;
+ fmt_src.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ ret = v4l2_subdev_call(priv->src_sd, pad, get_fmt, NULL, &fmt_src);
+ if (ret)
+ return ret;
+
+ return __capture_try_fmt_vid_cap(priv, &fmt_src, f, NULL);
+}
+
static int capture_s_fmt_vid_cap(struct file *file, void *fh,
struct v4l2_format *f)
{
struct capture_priv *priv = video_drvdata(file);
+ struct v4l2_subdev_format fmt_src;
+ struct v4l2_rect compose;
int ret;
if (vb2_is_busy(&priv->q)) {
@@ -255,13 +281,20 @@ static int capture_s_fmt_vid_cap(struct file *file, void *fh,
return -EBUSY;
}
- ret = capture_try_fmt_vid_cap(file, priv, f);
+ fmt_src.pad = priv->src_sd_pad;
+ fmt_src.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ ret = v4l2_subdev_call(priv->src_sd, pad, get_fmt, NULL, &fmt_src);
+ if (ret)
+ return ret;
+
+ ret = __capture_try_fmt_vid_cap(priv, &fmt_src, f, &compose);
if (ret)
return ret;
priv->vdev.fmt.fmt.pix = f->fmt.pix;
priv->vdev.cc = imx_media_find_format(f->fmt.pix.pixelformat,
CS_SEL_ANY, true);
+ priv->vdev.compose = compose;
return 0;
}
@@ -290,6 +323,36 @@ static int capture_s_std(struct file *file, void *fh, v4l2_std_id std)
return v4l2_subdev_call(priv->src_sd, video, s_std, std);
}
+static int capture_g_selection(struct file *file, void *fh,
+ struct v4l2_selection *s)
+{
+ struct capture_priv *priv = video_drvdata(file);
+
+ switch (s->target) {
+ case V4L2_SEL_TGT_COMPOSE:
+ case V4L2_SEL_TGT_COMPOSE_DEFAULT:
+ case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+ /* The compose rectangle is fixed to the source format. */
+ s->r = priv->vdev.compose;
+ break;
+ case V4L2_SEL_TGT_COMPOSE_PADDED:
+ /*
+ * The hardware writes with a configurable but fixed DMA burst
+ * size. If the source format width is not burst size aligned,
+ * the written frame contains padding to the right.
+ */
+ s->r.left = 0;
+ s->r.top = 0;
+ s->r.width = priv->vdev.fmt.fmt.pix.width;
+ s->r.height = priv->vdev.fmt.fmt.pix.height;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int capture_g_parm(struct file *file, void *fh,
struct v4l2_streamparm *a)
{
@@ -335,6 +398,21 @@ static int capture_s_parm(struct file *file, void *fh,
return 0;
}
+static int capture_subscribe_event(struct v4l2_fh *fh,
+ const struct v4l2_event_subscription *sub)
+{
+ switch (sub->type) {
+ case V4L2_EVENT_IMX_FRAME_INTERVAL_ERROR:
+ return v4l2_event_subscribe(fh, sub, 0, NULL);
+ case V4L2_EVENT_SOURCE_CHANGE:
+ return v4l2_src_change_event_subscribe(fh, sub);
+ case V4L2_EVENT_CTRL:
+ return v4l2_ctrl_subscribe_event(fh, sub);
+ default:
+ return -EINVAL;
+ }
+}
+
static const struct v4l2_ioctl_ops capture_ioctl_ops = {
.vidioc_querycap = vidioc_querycap,
@@ -350,6 +428,8 @@ static const struct v4l2_ioctl_ops capture_ioctl_ops = {
.vidioc_g_std = capture_g_std,
.vidioc_s_std = capture_s_std,
+ .vidioc_g_selection = capture_g_selection,
+
.vidioc_g_parm = capture_g_parm,
.vidioc_s_parm = capture_s_parm,
@@ -362,6 +442,9 @@ static const struct v4l2_ioctl_ops capture_ioctl_ops = {
.vidioc_expbuf = vb2_ioctl_expbuf,
.vidioc_streamon = vb2_ioctl_streamon,
.vidioc_streamoff = vb2_ioctl_streamoff,
+
+ .vidioc_subscribe_event = capture_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};
/*
@@ -572,7 +655,8 @@ static struct video_device capture_videodev = {
};
void imx_media_capture_device_set_format(struct imx_media_video_dev *vdev,
- struct v4l2_pix_format *pix)
+ const struct v4l2_pix_format *pix,
+ const struct v4l2_rect *compose)
{
struct capture_priv *priv = to_capture_priv(vdev);
@@ -580,6 +664,7 @@ void imx_media_capture_device_set_format(struct imx_media_video_dev *vdev,
priv->vdev.fmt.fmt.pix = *pix;
priv->vdev.cc = imx_media_find_format(pix->pixelformat, CS_SEL_ANY,
true);
+ priv->vdev.compose = *compose;
mutex_unlock(&priv->mutex);
}
EXPORT_SYMBOL_GPL(imx_media_capture_device_set_format);
@@ -685,7 +770,7 @@ int imx_media_capture_device_register(struct imx_media_video_dev *vdev)
}
vdev->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- imx_media_mbus_fmt_to_pix_fmt(&vdev->fmt.fmt.pix,
+ imx_media_mbus_fmt_to_pix_fmt(&vdev->fmt.fmt.pix, &vdev->compose,
&fmt_src.format, NULL);
vdev->cc = imx_media_find_format(vdev->fmt.fmt.pix.pixelformat,
CS_SEL_ANY, false);
diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c
index 4223f8d418ae..3b7517348666 100644
--- a/drivers/staging/media/imx/imx-media-csi.c
+++ b/drivers/staging/media/imx/imx-media-csi.c
@@ -41,7 +41,7 @@
#define MIN_H 144
#define MAX_W 4096
#define MAX_H 4096
-#define W_ALIGN 4 /* multiple of 16 pixels */
+#define W_ALIGN 1 /* multiple of 2 pixels */
#define H_ALIGN 1 /* multiple of 2 lines */
#define S_ALIGN 1 /* multiple of 2 */
@@ -114,6 +114,7 @@ struct csi_priv {
u32 frame_sequence; /* frame sequence counter */
bool last_eof; /* waiting for last EOF at stream off */
bool nfb4eof; /* NFB4EOF encountered during streaming */
+ bool interweave_swap; /* swap top/bottom lines when interweaving */
struct completion last_eof_comp;
};
@@ -286,6 +287,9 @@ static void csi_vb2_buf_done(struct csi_priv *priv)
if (ipu_idmac_buffer_is_ready(priv->idmac_ch, priv->ipu_buf_num))
ipu_idmac_clear_buffer(priv->idmac_ch, priv->ipu_buf_num);
+ if (priv->interweave_swap)
+ phys += vdev->fmt.fmt.pix.bytesperline;
+
ipu_cpmem_set_buffer(priv->idmac_ch, priv->ipu_buf_num, phys);
}
@@ -398,23 +402,24 @@ static int csi_idmac_setup_channel(struct csi_priv *priv)
struct imx_media_video_dev *vdev = priv->vdev;
const struct imx_media_pixfmt *incc;
struct v4l2_mbus_framefmt *infmt;
+ struct v4l2_mbus_framefmt *outfmt;
+ bool passthrough, interweave;
struct ipu_image image;
u32 passthrough_bits;
u32 passthrough_cycles;
dma_addr_t phys[2];
- bool passthrough;
u32 burst_size;
int ret;
infmt = &priv->format_mbus[CSI_SINK_PAD];
incc = priv->cc[CSI_SINK_PAD];
+ outfmt = &priv->format_mbus[CSI_SRC_PAD_IDMAC];
ipu_cpmem_zero(priv->idmac_ch);
memset(&image, 0, sizeof(image));
image.pix = vdev->fmt.fmt.pix;
- image.rect.width = image.pix.width;
- image.rect.height = image.pix.height;
+ image.rect = vdev->compose;
csi_idmac_setup_vb2_buf(priv, phys);
@@ -424,6 +429,16 @@ static int csi_idmac_setup_channel(struct csi_priv *priv)
passthrough = requires_passthrough(&priv->upstream_ep, infmt, incc);
passthrough_cycles = 1;
+ /*
+ * If the field type at capture interface is interlaced, and
+ * the output IDMAC pad is sequential, enable interweave at
+ * the IDMAC output channel.
+ */
+ interweave = V4L2_FIELD_IS_INTERLACED(image.pix.field) &&
+ V4L2_FIELD_IS_SEQUENTIAL(outfmt->field);
+ priv->interweave_swap = interweave &&
+ image.pix.field == V4L2_FIELD_INTERLACED_BT;
+
switch (image.pix.pixelformat) {
case V4L2_PIX_FMT_SBGGR8:
case V4L2_PIX_FMT_SGBRG8:
@@ -442,13 +457,18 @@ static int csi_idmac_setup_channel(struct csi_priv *priv)
passthrough_bits = 16;
break;
case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_YVU420:
case V4L2_PIX_FMT_NV12:
burst_size = (image.pix.width & 0x3f) ?
((image.pix.width & 0x1f) ?
((image.pix.width & 0xf) ? 8 : 16) : 32) : 64;
passthrough_bits = 16;
- /* Skip writing U and V components to odd rows */
- ipu_cpmem_skip_odd_chroma_rows(priv->idmac_ch);
+ /*
+ * Skip writing U and V components to odd rows (but not
+ * when enabling IDMAC interweaving, they are incompatible).
+ */
+ if (!interweave)
+ ipu_cpmem_skip_odd_chroma_rows(priv->idmac_ch);
break;
case V4L2_PIX_FMT_YUYV:
case V4L2_PIX_FMT_UYVY:
@@ -471,6 +491,12 @@ static int csi_idmac_setup_channel(struct csi_priv *priv)
}
if (passthrough) {
+ if (priv->interweave_swap) {
+ /* start interweave scan at 1st top line (2nd line) */
+ image.phys0 += image.pix.bytesperline;
+ image.phys1 += image.pix.bytesperline;
+ }
+
ipu_cpmem_set_resolution(priv->idmac_ch,
image.rect.width * passthrough_cycles,
image.rect.height);
@@ -480,6 +506,11 @@ static int csi_idmac_setup_channel(struct csi_priv *priv)
ipu_cpmem_set_format_passthrough(priv->idmac_ch,
passthrough_bits);
} else {
+ if (priv->interweave_swap) {
+ /* start interweave scan at 1st top line (2nd line) */
+ image.rect.top = 1;
+ }
+
ret = ipu_cpmem_set_image(priv->idmac_ch, &image);
if (ret)
goto unsetup_vb2;
@@ -509,10 +540,12 @@ static int csi_idmac_setup_channel(struct csi_priv *priv)
ipu_smfc_set_burstsize(priv->smfc, burst_size);
- if (image.pix.field == V4L2_FIELD_NONE &&
- V4L2_FIELD_HAS_BOTH(infmt->field))
+ if (interweave)
ipu_cpmem_interlaced_scan(priv->idmac_ch,
- image.pix.bytesperline);
+ priv->interweave_swap ?
+ -image.pix.bytesperline :
+ image.pix.bytesperline,
+ image.pix.pixelformat);
ipu_idmac_set_double_buffer(priv->idmac_ch, true);
@@ -629,7 +662,7 @@ out_put_ipu:
return ret;
}
-static void csi_idmac_stop(struct csi_priv *priv)
+static void csi_idmac_wait_last_eof(struct csi_priv *priv)
{
unsigned long flags;
int ret;
@@ -646,7 +679,10 @@ static void csi_idmac_stop(struct csi_priv *priv)
&priv->last_eof_comp, msecs_to_jiffies(IMX_MEDIA_EOF_TIMEOUT));
if (ret == 0)
v4l2_warn(&priv->sd, "wait last EOF timeout\n");
+}
+static void csi_idmac_stop(struct csi_priv *priv)
+{
devm_free_irq(priv->dev, priv->eof_irq, priv);
devm_free_irq(priv->dev, priv->nfb4eof_irq, priv);
@@ -679,12 +715,7 @@ static int csi_setup(struct csi_priv *priv)
priv->upstream_ep.bus.parallel.flags :
priv->upstream_ep.bus.mipi_csi2.flags;
- /*
- * we need to pass input frame to CSI interface, but
- * with translated field type from output format
- */
if_fmt = *infmt;
- if_fmt.field = outfmt->field;
crop = priv->crop;
/*
@@ -702,7 +733,7 @@ static int csi_setup(struct csi_priv *priv)
priv->crop.width == 2 * priv->compose.width,
priv->crop.height == 2 * priv->compose.height);
- ipu_csi_init_interface(priv->csi, &mbus_cfg, &if_fmt);
+ ipu_csi_init_interface(priv->csi, &mbus_cfg, &if_fmt, outfmt);
ipu_csi_set_dest(priv->csi, priv->dest);
@@ -722,10 +753,16 @@ static int csi_start(struct csi_priv *priv)
output_fi = &priv->frame_interval[priv->active_output_pad];
+ /* start upstream */
+ ret = v4l2_subdev_call(priv->src_sd, video, s_stream, 1);
+ ret = (ret && ret != -ENOIOCTLCMD) ? ret : 0;
+ if (ret)
+ return ret;
+
if (priv->dest == IPU_CSI_DEST_IDMAC) {
ret = csi_idmac_start(priv);
if (ret)
- return ret;
+ goto stop_upstream;
}
ret = csi_setup(priv);
@@ -753,11 +790,26 @@ fim_off:
idmac_stop:
if (priv->dest == IPU_CSI_DEST_IDMAC)
csi_idmac_stop(priv);
+stop_upstream:
+ v4l2_subdev_call(priv->src_sd, video, s_stream, 0);
return ret;
}
static void csi_stop(struct csi_priv *priv)
{
+ if (priv->dest == IPU_CSI_DEST_IDMAC)
+ csi_idmac_wait_last_eof(priv);
+
+ /*
+ * Disable the CSI asap, after syncing with the last EOF.
+ * Doing so after the IDMA channel is disabled has shown to
+ * create hard system-wide hangs.
+ */
+ ipu_csi_disable(priv->csi);
+
+ /* stop upstream */
+ v4l2_subdev_call(priv->src_sd, video, s_stream, 0);
+
if (priv->dest == IPU_CSI_DEST_IDMAC) {
csi_idmac_stop(priv);
@@ -765,8 +817,6 @@ static void csi_stop(struct csi_priv *priv)
if (priv->fim)
imx_media_fim_set_stream(priv->fim, NULL, false);
}
-
- ipu_csi_disable(priv->csi);
}
static const struct csi_skip_desc csi_skip[12] = {
@@ -876,7 +926,10 @@ static int csi_s_frame_interval(struct v4l2_subdev *sd,
switch (fi->pad) {
case CSI_SINK_PAD:
- /* No limits on input frame interval */
+ /* No limits on valid input frame intervals */
+ if (fi->interval.numerator == 0 ||
+ fi->interval.denominator == 0)
+ fi->interval = *input_fi;
/* Reset output intervals and frame skipping ratio to 1:1 */
priv->frame_interval[CSI_SRC_PAD_IDMAC] = fi->interval;
priv->frame_interval[CSI_SRC_PAD_DIRECT] = fi->interval;
@@ -927,23 +980,13 @@ static int csi_s_stream(struct v4l2_subdev *sd, int enable)
goto update_count;
if (enable) {
- /* upstream must be started first, before starting CSI */
- ret = v4l2_subdev_call(priv->src_sd, video, s_stream, 1);
- ret = (ret && ret != -ENOIOCTLCMD) ? ret : 0;
- if (ret)
- goto out;
-
dev_dbg(priv->dev, "stream ON\n");
ret = csi_start(priv);
- if (ret) {
- v4l2_subdev_call(priv->src_sd, video, s_stream, 0);
+ if (ret)
goto out;
- }
} else {
dev_dbg(priv->dev, "stream OFF\n");
- /* CSI must be stopped first, then stop upstream */
csi_stop(priv);
- v4l2_subdev_call(priv->src_sd, video, s_stream, 0);
}
update_count:
@@ -1001,6 +1044,8 @@ static int csi_link_setup(struct media_entity *entity,
v4l2_ctrl_handler_free(&priv->ctrl_hdlr);
v4l2_ctrl_handler_init(&priv->ctrl_hdlr, 0);
priv->sink = NULL;
+ /* do not apply IC burst alignment in csi_try_crop */
+ priv->active_output_pad = CSI_SRC_PAD_IDMAC;
goto out;
}
@@ -1029,10 +1074,10 @@ static int csi_link_setup(struct media_entity *entity,
remote_sd = media_entity_to_v4l2_subdev(remote->entity);
switch (remote_sd->grp_id) {
- case IMX_MEDIA_GRP_ID_VDIC:
+ case IMX_MEDIA_GRP_ID_IPU_VDIC:
priv->dest = IPU_CSI_DEST_VDIC;
break;
- case IMX_MEDIA_GRP_ID_IC_PRP:
+ case IMX_MEDIA_GRP_ID_IPU_IC_PRP:
priv->dest = IPU_CSI_DEST_IC;
break;
default:
@@ -1137,12 +1182,21 @@ static void csi_try_crop(struct csi_priv *priv,
struct v4l2_mbus_framefmt *infmt,
struct v4l2_fwnode_endpoint *upstream_ep)
{
+ u32 in_height;
+
crop->width = min_t(__u32, infmt->width, crop->width);
if (crop->left + crop->width > infmt->width)
crop->left = infmt->width - crop->width;
/* adjust crop left/width to h/w alignment restrictions */
crop->left &= ~0x3;
- crop->width &= ~0x7;
+ if (priv->active_output_pad == CSI_SRC_PAD_DIRECT)
+ crop->width &= ~0x7; /* multiple of 8 pixels (IC burst) */
+ else
+ crop->width &= ~0x1; /* multiple of 2 pixels */
+
+ in_height = infmt->height;
+ if (infmt->field == V4L2_FIELD_ALTERNATE)
+ in_height *= 2;
/*
* FIXME: not sure why yet, but on interlaced bt.656,
@@ -1153,12 +1207,12 @@ static void csi_try_crop(struct csi_priv *priv,
if (upstream_ep->bus_type == V4L2_MBUS_BT656 &&
(V4L2_FIELD_HAS_BOTH(infmt->field) ||
infmt->field == V4L2_FIELD_ALTERNATE)) {
- crop->height = infmt->height;
- crop->top = (infmt->height == 480) ? 2 : 0;
+ crop->height = in_height;
+ crop->top = (in_height == 480) ? 2 : 0;
} else {
- crop->height = min_t(__u32, infmt->height, crop->height);
- if (crop->top + crop->height > infmt->height)
- crop->top = infmt->height - crop->height;
+ crop->height = min_t(__u32, in_height, crop->height);
+ if (crop->top + crop->height > in_height)
+ crop->top = in_height - crop->height;
}
}
@@ -1308,6 +1362,49 @@ out:
return ret;
}
+static void csi_try_field(struct csi_priv *priv,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *sdformat)
+{
+ struct v4l2_mbus_framefmt *infmt =
+ __csi_get_fmt(priv, cfg, CSI_SINK_PAD, sdformat->which);
+
+ /* no restrictions on sink pad field type */
+ if (sdformat->pad == CSI_SINK_PAD)
+ return;
+
+ switch (infmt->field) {
+ case V4L2_FIELD_SEQ_TB:
+ case V4L2_FIELD_SEQ_BT:
+ /*
+ * If the user requests sequential at the source pad,
+ * allow it (along with possibly inverting field order).
+ * Otherwise passthrough the field type.
+ */
+ if (!V4L2_FIELD_IS_SEQUENTIAL(sdformat->format.field))
+ sdformat->format.field = infmt->field;
+ break;
+ case V4L2_FIELD_ALTERNATE:
+ /*
+ * This driver does not support alternate field mode, and
+ * the CSI captures a whole frame, so the CSI never presents
+ * alternate mode at its source pads. If user has not
+ * already requested sequential, translate ALTERNATE at
+ * sink pad to SEQ_TB or SEQ_BT at the source pad depending
+ * on input height (assume NTSC BT order if 480 total active
+ * frame lines, otherwise PAL TB order).
+ */
+ if (!V4L2_FIELD_IS_SEQUENTIAL(sdformat->format.field))
+ sdformat->format.field = (infmt->height == 480 / 2) ?
+ V4L2_FIELD_SEQ_BT : V4L2_FIELD_SEQ_TB;
+ break;
+ default:
+ /* Passthrough for all other input field types */
+ sdformat->format.field = infmt->field;
+ break;
+ }
+}
+
static void csi_try_fmt(struct csi_priv *priv,
struct v4l2_fwnode_endpoint *upstream_ep,
struct v4l2_subdev_pad_config *cfg,
@@ -1347,42 +1444,20 @@ static void csi_try_fmt(struct csi_priv *priv,
}
}
- if (sdformat->pad == CSI_SRC_PAD_DIRECT ||
- sdformat->format.field != V4L2_FIELD_NONE)
- sdformat->format.field = infmt->field;
-
- /*
- * translate V4L2_FIELD_ALTERNATE to SEQ_TB or SEQ_BT
- * depending on input height (assume NTSC top-bottom
- * order if 480 lines, otherwise PAL bottom-top order).
- */
- if (sdformat->format.field == V4L2_FIELD_ALTERNATE) {
- sdformat->format.field = (infmt->height == 480) ?
- V4L2_FIELD_SEQ_TB : V4L2_FIELD_SEQ_BT;
- }
+ csi_try_field(priv, cfg, sdformat);
/* propagate colorimetry from sink */
sdformat->format.colorspace = infmt->colorspace;
sdformat->format.xfer_func = infmt->xfer_func;
sdformat->format.quantization = infmt->quantization;
sdformat->format.ycbcr_enc = infmt->ycbcr_enc;
+
break;
case CSI_SINK_PAD:
v4l_bound_align_image(&sdformat->format.width, MIN_W, MAX_W,
W_ALIGN, &sdformat->format.height,
MIN_H, MAX_H, H_ALIGN, S_ALIGN);
- /* Reset crop and compose rectangles */
- crop->left = 0;
- crop->top = 0;
- crop->width = sdformat->format.width;
- crop->height = sdformat->format.height;
- csi_try_crop(priv, crop, cfg, &sdformat->format, upstream_ep);
- compose->left = 0;
- compose->top = 0;
- compose->width = crop->width;
- compose->height = crop->height;
-
*cc = imx_media_find_mbus_format(sdformat->format.code,
CS_SEL_ANY, true);
if (!*cc) {
@@ -1393,9 +1468,25 @@ static void csi_try_fmt(struct csi_priv *priv,
sdformat->format.code = (*cc)->codes[0];
}
+ csi_try_field(priv, cfg, sdformat);
+
imx_media_fill_default_mbus_fields(
&sdformat->format, infmt,
priv->active_output_pad == CSI_SRC_PAD_DIRECT);
+
+ /* Reset crop and compose rectangles */
+ crop->left = 0;
+ crop->top = 0;
+ crop->width = sdformat->format.width;
+ crop->height = sdformat->format.height;
+ if (sdformat->format.field == V4L2_FIELD_ALTERNATE)
+ crop->height *= 2;
+ csi_try_crop(priv, crop, cfg, &sdformat->format, upstream_ep);
+ compose->left = 0;
+ compose->top = 0;
+ compose->width = crop->width;
+ compose->height = crop->height;
+
break;
}
}
@@ -1411,6 +1502,7 @@ static int csi_set_fmt(struct v4l2_subdev *sd,
struct v4l2_pix_format vdev_fmt;
struct v4l2_mbus_framefmt *fmt;
struct v4l2_rect *crop, *compose;
+ struct v4l2_rect vdev_compose;
int ret;
if (sdformat->pad >= CSI_NUM_PADS)
@@ -1466,11 +1558,11 @@ static int csi_set_fmt(struct v4l2_subdev *sd,
priv->cc[sdformat->pad] = cc;
/* propagate IDMAC output pad format to capture device */
- imx_media_mbus_fmt_to_pix_fmt(&vdev_fmt,
+ imx_media_mbus_fmt_to_pix_fmt(&vdev_fmt, &vdev_compose,
&priv->format_mbus[CSI_SRC_PAD_IDMAC],
priv->cc[CSI_SRC_PAD_IDMAC]);
mutex_unlock(&priv->lock);
- imx_media_capture_device_set_format(vdev, &vdev_fmt);
+ imx_media_capture_device_set_format(vdev, &vdev_fmt, &vdev_compose);
return 0;
out:
@@ -1502,6 +1594,8 @@ static int csi_get_selection(struct v4l2_subdev *sd,
sel->r.top = 0;
sel->r.width = infmt->width;
sel->r.height = infmt->height;
+ if (infmt->field == V4L2_FIELD_ALTERNATE)
+ sel->r.height *= 2;
break;
case V4L2_SEL_TGT_CROP:
sel->r = *crop;
@@ -1787,7 +1881,7 @@ static int imx_csi_parse_endpoint(struct device *dev,
struct v4l2_fwnode_endpoint *vep,
struct v4l2_async_subdev *asd)
{
- return fwnode_device_is_available(asd->match.fwnode) ? 0 : -EINVAL;
+ return fwnode_device_is_available(asd->match.fwnode) ? 0 : -ENOTCONN;
}
static int imx_csi_async_register(struct csi_priv *priv)
@@ -1864,6 +1958,8 @@ static int imx_csi_probe(struct platform_device *pdev)
priv->csi_id = pdata->csi;
priv->smfc_id = (priv->csi_id == 0) ? 0 : 2;
+ priv->active_output_pad = CSI_SRC_PAD_IDMAC;
+
timer_setup(&priv->eof_timeout_timer, csi_idmac_eof_timeout, 0);
spin_lock_init(&priv->irqlock);
@@ -1877,7 +1973,7 @@ static int imx_csi_probe(struct platform_device *pdev)
priv->sd.owner = THIS_MODULE;
priv->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
priv->sd.grp_id = priv->csi_id ?
- IMX_MEDIA_GRP_ID_CSI1 : IMX_MEDIA_GRP_ID_CSI0;
+ IMX_MEDIA_GRP_ID_IPU_CSI1 : IMX_MEDIA_GRP_ID_IPU_CSI0;
imx_media_grp_id_to_sd_name(priv->sd.name, sizeof(priv->sd.name),
priv->sd.grp_id, ipu_get_num(priv->ipu));
diff --git a/drivers/staging/media/imx/imx-media-dev-common.c b/drivers/staging/media/imx/imx-media-dev-common.c
new file mode 100644
index 000000000000..910594125889
--- /dev/null
+++ b/drivers/staging/media/imx/imx-media-dev-common.c
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * V4L2 Media Controller Driver for Freescale common i.MX5/6/7 SOC
+ *
+ * Copyright (c) 2019 Linaro Ltd
+ * Copyright (c) 2016 Mentor Graphics Inc.
+ */
+
+#include <linux/of_graph.h>
+#include <linux/of_platform.h>
+#include "imx-media.h"
+
+static const struct v4l2_async_notifier_operations imx_media_subdev_ops = {
+ .bound = imx_media_subdev_bound,
+ .complete = imx_media_probe_complete,
+};
+
+static const struct media_device_ops imx_media_md_ops = {
+ .link_notify = imx_media_link_notify,
+};
+
+struct imx_media_dev *imx_media_dev_init(struct device *dev)
+{
+ struct imx_media_dev *imxmd;
+ int ret;
+
+ imxmd = devm_kzalloc(dev, sizeof(*imxmd), GFP_KERNEL);
+ if (!imxmd)
+ return ERR_PTR(-ENOMEM);
+
+ dev_set_drvdata(dev, imxmd);
+
+ strlcpy(imxmd->md.model, "imx-media", sizeof(imxmd->md.model));
+ imxmd->md.ops = &imx_media_md_ops;
+ imxmd->md.dev = dev;
+
+ mutex_init(&imxmd->mutex);
+
+ imxmd->v4l2_dev.mdev = &imxmd->md;
+ imxmd->v4l2_dev.notify = imx_media_notify;
+ strlcpy(imxmd->v4l2_dev.name, "imx-media",
+ sizeof(imxmd->v4l2_dev.name));
+
+ media_device_init(&imxmd->md);
+
+ ret = v4l2_device_register(dev, &imxmd->v4l2_dev);
+ if (ret < 0) {
+ v4l2_err(&imxmd->v4l2_dev,
+ "Failed to register v4l2_device: %d\n", ret);
+ goto cleanup;
+ }
+
+ dev_set_drvdata(imxmd->v4l2_dev.dev, imxmd);
+
+ INIT_LIST_HEAD(&imxmd->vdev_list);
+
+ v4l2_async_notifier_init(&imxmd->notifier);
+
+ return imxmd;
+
+cleanup:
+ media_device_cleanup(&imxmd->md);
+
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(imx_media_dev_init);
+
+int imx_media_dev_notifier_register(struct imx_media_dev *imxmd)
+{
+ int ret;
+
+ /* no subdevs? just bail */
+ if (list_empty(&imxmd->notifier.asd_list)) {
+ v4l2_err(&imxmd->v4l2_dev, "no subdevs\n");
+ return -ENODEV;
+ }
+
+ /* prepare the async subdev notifier and register it */
+ imxmd->notifier.ops = &imx_media_subdev_ops;
+ ret = v4l2_async_notifier_register(&imxmd->v4l2_dev,
+ &imxmd->notifier);
+ if (ret) {
+ v4l2_err(&imxmd->v4l2_dev,
+ "v4l2_async_notifier_register failed with %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(imx_media_dev_notifier_register);
diff --git a/drivers/staging/media/imx/imx-media-dev.c b/drivers/staging/media/imx/imx-media-dev.c
index 4b344a4a3706..28a3d23aad5b 100644
--- a/drivers/staging/media/imx/imx-media-dev.c
+++ b/drivers/staging/media/imx/imx-media-dev.c
@@ -116,16 +116,16 @@ static int imx_media_get_ipu(struct imx_media_dev *imxmd,
}
/* async subdev bound notifier */
-static int imx_media_subdev_bound(struct v4l2_async_notifier *notifier,
- struct v4l2_subdev *sd,
- struct v4l2_async_subdev *asd)
+int imx_media_subdev_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *sd,
+ struct v4l2_async_subdev *asd)
{
struct imx_media_dev *imxmd = notifier2dev(notifier);
int ret = 0;
mutex_lock(&imxmd->mutex);
- if (sd->grp_id & IMX_MEDIA_GRP_ID_CSI) {
+ if (sd->grp_id & IMX_MEDIA_GRP_ID_IPU_CSI) {
ret = imx_media_get_ipu(imxmd, sd);
if (ret)
goto out;
@@ -149,13 +149,13 @@ static int imx_media_create_links(struct v4l2_async_notifier *notifier)
list_for_each_entry(sd, &imxmd->v4l2_dev.subdevs, list) {
switch (sd->grp_id) {
- case IMX_MEDIA_GRP_ID_VDIC:
- case IMX_MEDIA_GRP_ID_IC_PRP:
- case IMX_MEDIA_GRP_ID_IC_PRPENC:
- case IMX_MEDIA_GRP_ID_IC_PRPVF:
- case IMX_MEDIA_GRP_ID_CSI0:
- case IMX_MEDIA_GRP_ID_CSI1:
- ret = imx_media_create_internal_links(imxmd, sd);
+ case IMX_MEDIA_GRP_ID_IPU_VDIC:
+ case IMX_MEDIA_GRP_ID_IPU_IC_PRP:
+ case IMX_MEDIA_GRP_ID_IPU_IC_PRPENC:
+ case IMX_MEDIA_GRP_ID_IPU_IC_PRPVF:
+ case IMX_MEDIA_GRP_ID_IPU_CSI0:
+ case IMX_MEDIA_GRP_ID_IPU_CSI1:
+ ret = imx_media_create_ipu_internal_links(imxmd, sd);
if (ret)
return ret;
/*
@@ -163,9 +163,13 @@ static int imx_media_create_links(struct v4l2_async_notifier *notifier)
* internal entities, so create the external links
* to the CSI sink pads.
*/
- if (sd->grp_id & IMX_MEDIA_GRP_ID_CSI)
+ if (sd->grp_id & IMX_MEDIA_GRP_ID_IPU_CSI)
imx_media_create_csi_of_links(imxmd, sd);
break;
+ case IMX_MEDIA_GRP_ID_CSI:
+ imx_media_create_csi_of_links(imxmd, sd);
+
+ break;
default:
/*
* if this subdev has fwnode links, create media
@@ -302,7 +306,7 @@ static int imx_media_create_pad_vdev_lists(struct imx_media_dev *imxmd)
}
/* async subdev complete notifier */
-static int imx_media_probe_complete(struct v4l2_async_notifier *notifier)
+int imx_media_probe_complete(struct v4l2_async_notifier *notifier)
{
struct imx_media_dev *imxmd = notifier2dev(notifier);
int ret;
@@ -326,11 +330,6 @@ unlock:
return media_device_register(&imxmd->md);
}
-static const struct v4l2_async_notifier_operations imx_media_subdev_ops = {
- .bound = imx_media_subdev_bound,
- .complete = imx_media_probe_complete,
-};
-
/*
* adds controls to a video device from an entity subdevice.
* Continues upstream from the entity's sink pads.
@@ -374,8 +373,8 @@ static int imx_media_inherit_controls(struct imx_media_dev *imxmd,
return ret;
}
-static int imx_media_link_notify(struct media_link *link, u32 flags,
- unsigned int notification)
+int imx_media_link_notify(struct media_link *link, u32 flags,
+ unsigned int notification)
{
struct media_entity *source = link->source->entity;
struct imx_media_pad_vdev *pad_vdev;
@@ -438,9 +437,27 @@ static int imx_media_link_notify(struct media_link *link, u32 flags,
return ret;
}
-static const struct media_device_ops imx_media_md_ops = {
- .link_notify = imx_media_link_notify,
-};
+void imx_media_notify(struct v4l2_subdev *sd, unsigned int notification,
+ void *arg)
+{
+ struct media_entity *entity = &sd->entity;
+ int i;
+
+ if (notification != V4L2_DEVICE_NOTIFY_EVENT)
+ return;
+
+ for (i = 0; i < entity->num_pads; i++) {
+ struct media_pad *pad = &entity->pads[i];
+ struct imx_media_pad_vdev *pad_vdev;
+ struct list_head *pad_vdev_list;
+
+ pad_vdev_list = to_pad_vdev_list(sd, pad->index);
+ if (!pad_vdev_list)
+ continue;
+ list_for_each_entry(pad_vdev, pad_vdev_list, list)
+ v4l2_event_queue(pad_vdev->vdev->vfd, arg);
+ }
+}
static int imx_media_probe(struct platform_device *pdev)
{
@@ -449,76 +466,37 @@ static int imx_media_probe(struct platform_device *pdev)
struct imx_media_dev *imxmd;
int ret;
- imxmd = devm_kzalloc(dev, sizeof(*imxmd), GFP_KERNEL);
- if (!imxmd)
- return -ENOMEM;
-
- dev_set_drvdata(dev, imxmd);
-
- strscpy(imxmd->md.model, "imx-media", sizeof(imxmd->md.model));
- imxmd->md.ops = &imx_media_md_ops;
- imxmd->md.dev = dev;
-
- mutex_init(&imxmd->mutex);
-
- imxmd->v4l2_dev.mdev = &imxmd->md;
- strscpy(imxmd->v4l2_dev.name, "imx-media",
- sizeof(imxmd->v4l2_dev.name));
-
- media_device_init(&imxmd->md);
-
- ret = v4l2_device_register(dev, &imxmd->v4l2_dev);
- if (ret < 0) {
- v4l2_err(&imxmd->v4l2_dev,
- "Failed to register v4l2_device: %d\n", ret);
- goto cleanup;
- }
-
- dev_set_drvdata(imxmd->v4l2_dev.dev, imxmd);
-
- INIT_LIST_HEAD(&imxmd->vdev_list);
-
- v4l2_async_notifier_init(&imxmd->notifier);
+ imxmd = imx_media_dev_init(dev);
+ if (IS_ERR(imxmd))
+ return PTR_ERR(imxmd);
ret = imx_media_add_of_subdevs(imxmd, node);
if (ret) {
v4l2_err(&imxmd->v4l2_dev,
"add_of_subdevs failed with %d\n", ret);
- goto notifier_cleanup;
+ goto cleanup;
}
ret = imx_media_add_internal_subdevs(imxmd);
if (ret) {
v4l2_err(&imxmd->v4l2_dev,
"add_internal_subdevs failed with %d\n", ret);
- goto notifier_cleanup;
- }
-
- /* no subdevs? just bail */
- if (list_empty(&imxmd->notifier.asd_list)) {
- ret = -ENODEV;
- goto notifier_cleanup;
+ goto cleanup;
}
- /* prepare the async subdev notifier and register it */
- imxmd->notifier.ops = &imx_media_subdev_ops;
- ret = v4l2_async_notifier_register(&imxmd->v4l2_dev,
- &imxmd->notifier);
- if (ret) {
- v4l2_err(&imxmd->v4l2_dev,
- "v4l2_async_notifier_register failed with %d\n", ret);
+ ret = imx_media_dev_notifier_register(imxmd);
+ if (ret)
goto del_int;
- }
return 0;
del_int:
imx_media_remove_internal_subdevs(imxmd);
-notifier_cleanup:
+cleanup:
v4l2_async_notifier_cleanup(&imxmd->notifier);
v4l2_device_unregister(&imxmd->v4l2_dev);
-cleanup:
media_device_cleanup(&imxmd->md);
+
return ret;
}
@@ -532,8 +510,8 @@ static int imx_media_remove(struct platform_device *pdev)
v4l2_async_notifier_unregister(&imxmd->notifier);
imx_media_remove_internal_subdevs(imxmd);
v4l2_async_notifier_cleanup(&imxmd->notifier);
- v4l2_device_unregister(&imxmd->v4l2_dev);
media_device_unregister(&imxmd->md);
+ v4l2_device_unregister(&imxmd->v4l2_dev);
media_device_cleanup(&imxmd->md);
return 0;
diff --git a/drivers/staging/media/imx/imx-media-internal-sd.c b/drivers/staging/media/imx/imx-media-internal-sd.c
index 0fdc45dbfb76..5e10d95e5529 100644
--- a/drivers/staging/media/imx/imx-media-internal-sd.c
+++ b/drivers/staging/media/imx/imx-media-internal-sd.c
@@ -30,32 +30,32 @@ static const struct internal_subdev_id {
} isd_id[num_isd] = {
[isd_csi0] = {
.index = isd_csi0,
- .grp_id = IMX_MEDIA_GRP_ID_CSI0,
+ .grp_id = IMX_MEDIA_GRP_ID_IPU_CSI0,
.name = "imx-ipuv3-csi",
},
[isd_csi1] = {
.index = isd_csi1,
- .grp_id = IMX_MEDIA_GRP_ID_CSI1,
+ .grp_id = IMX_MEDIA_GRP_ID_IPU_CSI1,
.name = "imx-ipuv3-csi",
},
[isd_vdic] = {
.index = isd_vdic,
- .grp_id = IMX_MEDIA_GRP_ID_VDIC,
+ .grp_id = IMX_MEDIA_GRP_ID_IPU_VDIC,
.name = "imx-ipuv3-vdic",
},
[isd_ic_prp] = {
.index = isd_ic_prp,
- .grp_id = IMX_MEDIA_GRP_ID_IC_PRP,
+ .grp_id = IMX_MEDIA_GRP_ID_IPU_IC_PRP,
.name = "imx-ipuv3-ic",
},
[isd_ic_prpenc] = {
.index = isd_ic_prpenc,
- .grp_id = IMX_MEDIA_GRP_ID_IC_PRPENC,
+ .grp_id = IMX_MEDIA_GRP_ID_IPU_IC_PRPENC,
.name = "imx-ipuv3-ic",
},
[isd_ic_prpvf] = {
.index = isd_ic_prpvf,
- .grp_id = IMX_MEDIA_GRP_ID_IC_PRPVF,
+ .grp_id = IMX_MEDIA_GRP_ID_IPU_IC_PRPVF,
.name = "imx-ipuv3-ic",
},
};
@@ -229,8 +229,8 @@ static int create_ipu_internal_link(struct imx_media_dev *imxmd,
return ret;
}
-int imx_media_create_internal_links(struct imx_media_dev *imxmd,
- struct v4l2_subdev *sd)
+int imx_media_create_ipu_internal_links(struct imx_media_dev *imxmd,
+ struct v4l2_subdev *sd)
{
const struct internal_subdev *intsd;
const struct internal_pad *intpad;
@@ -312,8 +312,8 @@ static int add_ipu_internal_subdevs(struct imx_media_dev *imxmd, int ipu_id)
* of_parse_subdev().
*/
switch (isd->id->grp_id) {
- case IMX_MEDIA_GRP_ID_CSI0:
- case IMX_MEDIA_GRP_ID_CSI1:
+ case IMX_MEDIA_GRP_ID_IPU_CSI0:
+ case IMX_MEDIA_GRP_ID_IPU_CSI1:
ret = 0;
break;
default:
diff --git a/drivers/staging/media/imx/imx-media-of.c b/drivers/staging/media/imx/imx-media-of.c
index a01327f6e045..03446335ac03 100644
--- a/drivers/staging/media/imx/imx-media-of.c
+++ b/drivers/staging/media/imx/imx-media-of.c
@@ -20,7 +20,8 @@
#include <video/imx-ipu-v3.h>
#include "imx-media.h"
-static int of_add_csi(struct imx_media_dev *imxmd, struct device_node *csi_np)
+int imx_media_of_add_csi(struct imx_media_dev *imxmd,
+ struct device_node *csi_np)
{
int ret;
@@ -45,6 +46,7 @@ static int of_add_csi(struct imx_media_dev *imxmd, struct device_node *csi_np)
return 0;
}
+EXPORT_SYMBOL_GPL(imx_media_of_add_csi);
int imx_media_add_of_subdevs(struct imx_media_dev *imxmd,
struct device_node *np)
@@ -57,7 +59,7 @@ int imx_media_add_of_subdevs(struct imx_media_dev *imxmd,
if (!csi_np)
break;
- ret = of_add_csi(imxmd, csi_np);
+ ret = imx_media_of_add_csi(imxmd, csi_np);
of_node_put(csi_np);
if (ret)
return ret;
diff --git a/drivers/staging/media/imx/imx-media-utils.c b/drivers/staging/media/imx/imx-media-utils.c
index 0eaa353d5cb3..1c63a2765a81 100644
--- a/drivers/staging/media/imx/imx-media-utils.c
+++ b/drivers/staging/media/imx/imx-media-utils.c
@@ -577,9 +577,11 @@ void imx_media_fill_default_mbus_fields(struct v4l2_mbus_framefmt *tryfmt,
EXPORT_SYMBOL_GPL(imx_media_fill_default_mbus_fields);
int imx_media_mbus_fmt_to_pix_fmt(struct v4l2_pix_format *pix,
- struct v4l2_mbus_framefmt *mbus,
+ struct v4l2_rect *compose,
+ const struct v4l2_mbus_framefmt *mbus,
const struct imx_media_pixfmt *cc)
{
+ u32 width;
u32 stride;
if (!cc) {
@@ -602,9 +604,16 @@ int imx_media_mbus_fmt_to_pix_fmt(struct v4l2_pix_format *pix,
cc = imx_media_find_mbus_format(code, CS_SEL_YUV, false);
}
- stride = cc->planar ? mbus->width : (mbus->width * cc->bpp) >> 3;
+ /* Round up width for minimum burst size */
+ width = round_up(mbus->width, 8);
- pix->width = mbus->width;
+ /* Round up stride for IDMAC line start address alignment */
+ if (cc->planar)
+ stride = round_up(width, 16);
+ else
+ stride = round_up((width * cc->bpp) >> 3, 8);
+
+ pix->width = width;
pix->height = mbus->height;
pix->pixelformat = cc->fourcc;
pix->colorspace = mbus->colorspace;
@@ -613,7 +622,19 @@ int imx_media_mbus_fmt_to_pix_fmt(struct v4l2_pix_format *pix,
pix->quantization = mbus->quantization;
pix->field = mbus->field;
pix->bytesperline = stride;
- pix->sizeimage = (pix->width * pix->height * cc->bpp) >> 3;
+ pix->sizeimage = cc->planar ? ((stride * pix->height * cc->bpp) >> 3) :
+ stride * pix->height;
+
+ /*
+ * set capture compose rectangle, which is fixed to the
+ * source subdevice mbus format.
+ */
+ if (compose) {
+ compose->left = 0;
+ compose->top = 0;
+ compose->width = mbus->width;
+ compose->height = mbus->height;
+ }
return 0;
}
@@ -626,13 +647,11 @@ int imx_media_mbus_fmt_to_ipu_image(struct ipu_image *image,
memset(image, 0, sizeof(*image));
- ret = imx_media_mbus_fmt_to_pix_fmt(&image->pix, mbus, NULL);
+ ret = imx_media_mbus_fmt_to_pix_fmt(&image->pix, &image->rect,
+ mbus, NULL);
if (ret)
return ret;
- image->rect.width = mbus->width;
- image->rect.height = mbus->height;
-
return 0;
}
EXPORT_SYMBOL_GPL(imx_media_mbus_fmt_to_ipu_image);
@@ -696,20 +715,20 @@ void imx_media_grp_id_to_sd_name(char *sd_name, int sz, u32 grp_id, int ipu_id)
int id;
switch (grp_id) {
- case IMX_MEDIA_GRP_ID_CSI0...IMX_MEDIA_GRP_ID_CSI1:
- id = (grp_id >> IMX_MEDIA_GRP_ID_CSI_BIT) - 1;
+ case IMX_MEDIA_GRP_ID_IPU_CSI0...IMX_MEDIA_GRP_ID_IPU_CSI1:
+ id = (grp_id >> IMX_MEDIA_GRP_ID_IPU_CSI_BIT) - 1;
snprintf(sd_name, sz, "ipu%d_csi%d", ipu_id + 1, id);
break;
- case IMX_MEDIA_GRP_ID_VDIC:
+ case IMX_MEDIA_GRP_ID_IPU_VDIC:
snprintf(sd_name, sz, "ipu%d_vdic", ipu_id + 1);
break;
- case IMX_MEDIA_GRP_ID_IC_PRP:
+ case IMX_MEDIA_GRP_ID_IPU_IC_PRP:
snprintf(sd_name, sz, "ipu%d_ic_prp", ipu_id + 1);
break;
- case IMX_MEDIA_GRP_ID_IC_PRPENC:
+ case IMX_MEDIA_GRP_ID_IPU_IC_PRPENC:
snprintf(sd_name, sz, "ipu%d_ic_prpenc", ipu_id + 1);
break;
- case IMX_MEDIA_GRP_ID_IC_PRPVF:
+ case IMX_MEDIA_GRP_ID_IPU_IC_PRPVF:
snprintf(sd_name, sz, "ipu%d_ic_prpvf", ipu_id + 1);
break;
default:
diff --git a/drivers/staging/media/imx/imx-media-vdic.c b/drivers/staging/media/imx/imx-media-vdic.c
index 482250d47e7c..2808662e2597 100644
--- a/drivers/staging/media/imx/imx-media-vdic.c
+++ b/drivers/staging/media/imx/imx-media-vdic.c
@@ -219,26 +219,18 @@ static void __maybe_unused prepare_vdi_in_buffers(struct vdic_priv *priv,
switch (priv->fieldtype) {
case V4L2_FIELD_SEQ_TB:
- prev_phys = vb2_dma_contig_plane_dma_addr(prev_vb, 0);
- curr_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0) + fs;
- next_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0);
- break;
case V4L2_FIELD_SEQ_BT:
prev_phys = vb2_dma_contig_plane_dma_addr(prev_vb, 0) + fs;
curr_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0);
next_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0) + fs;
break;
+ case V4L2_FIELD_INTERLACED_TB:
case V4L2_FIELD_INTERLACED_BT:
+ case V4L2_FIELD_INTERLACED:
prev_phys = vb2_dma_contig_plane_dma_addr(prev_vb, 0) + is;
curr_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0);
next_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0) + is;
break;
- default:
- /* assume V4L2_FIELD_INTERLACED_TB */
- prev_phys = vb2_dma_contig_plane_dma_addr(prev_vb, 0);
- curr_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0) + is;
- next_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0);
- break;
}
ipu_cpmem_set_buffer(priv->vdi_in_ch_p, 0, prev_phys);
@@ -263,10 +255,10 @@ static int setup_vdi_channel(struct vdic_priv *priv,
memset(&image, 0, sizeof(image));
image.pix = vdev->fmt.fmt.pix;
+ image.rect = vdev->compose;
/* one field to VDIC channels */
image.pix.height /= 2;
- image.rect.width = image.pix.width;
- image.rect.height = image.pix.height;
+ image.rect.height /= 2;
image.phys0 = phys0;
image.phys1 = phys1;
@@ -826,7 +818,10 @@ static int vdic_s_frame_interval(struct v4l2_subdev *sd,
switch (fi->pad) {
case VDIC_SINK_PAD_DIRECT:
case VDIC_SINK_PAD_IDMAC:
- /* No limits on input frame interval */
+ /* No limits on valid input frame intervals */
+ if (fi->interval.numerator == 0 ||
+ fi->interval.denominator == 0)
+ fi->interval = priv->frame_interval[fi->pad];
/* Reset output interval */
*output_fi = fi->interval;
if (priv->csi_direct)
diff --git a/drivers/staging/media/imx/imx-media.h b/drivers/staging/media/imx/imx-media.h
index bc7feb81937c..ae964c8d5be1 100644
--- a/drivers/staging/media/imx/imx-media.h
+++ b/drivers/staging/media/imx/imx-media.h
@@ -80,6 +80,8 @@ struct imx_media_video_dev {
/* the user format */
struct v4l2_format fmt;
+ /* the compose rectangle */
+ struct v4l2_rect compose;
const struct imx_media_pixfmt *cc;
/* links this vdev to master list */
@@ -178,7 +180,8 @@ void imx_media_fill_default_mbus_fields(struct v4l2_mbus_framefmt *tryfmt,
struct v4l2_mbus_framefmt *fmt,
bool ic_route);
int imx_media_mbus_fmt_to_pix_fmt(struct v4l2_pix_format *pix,
- struct v4l2_mbus_framefmt *mbus,
+ struct v4l2_rect *compose,
+ const struct v4l2_mbus_framefmt *mbus,
const struct imx_media_pixfmt *cc);
int imx_media_mbus_fmt_to_ipu_image(struct ipu_image *image,
struct v4l2_mbus_framefmt *mbus);
@@ -226,6 +229,18 @@ int imx_media_add_async_subdev(struct imx_media_dev *imxmd,
struct fwnode_handle *fwnode,
struct platform_device *pdev);
+int imx_media_subdev_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *sd,
+ struct v4l2_async_subdev *asd);
+int imx_media_link_notify(struct media_link *link, u32 flags,
+ unsigned int notification);
+void imx_media_notify(struct v4l2_subdev *sd, unsigned int notification,
+ void *arg);
+int imx_media_probe_complete(struct v4l2_async_notifier *notifier);
+
+struct imx_media_dev *imx_media_dev_init(struct device *dev);
+int imx_media_dev_notifier_register(struct imx_media_dev *imxmd);
+
/* imx-media-fim.c */
struct imx_media_fim;
void imx_media_fim_eof_monitor(struct imx_media_fim *fim, ktime_t timestamp);
@@ -238,8 +253,8 @@ void imx_media_fim_free(struct imx_media_fim *fim);
/* imx-media-internal-sd.c */
int imx_media_add_internal_subdevs(struct imx_media_dev *imxmd);
-int imx_media_create_internal_links(struct imx_media_dev *imxmd,
- struct v4l2_subdev *sd);
+int imx_media_create_ipu_internal_links(struct imx_media_dev *imxmd,
+ struct v4l2_subdev *sd);
void imx_media_remove_internal_subdevs(struct imx_media_dev *imxmd);
/* imx-media-of.c */
@@ -249,6 +264,8 @@ int imx_media_create_of_links(struct imx_media_dev *imxmd,
struct v4l2_subdev *sd);
int imx_media_create_csi_of_links(struct imx_media_dev *imxmd,
struct v4l2_subdev *csi);
+int imx_media_of_add_csi(struct imx_media_dev *imxmd,
+ struct device_node *csi_np);
/* imx-media-capture.c */
struct imx_media_video_dev *
@@ -259,18 +276,20 @@ void imx_media_capture_device_unregister(struct imx_media_video_dev *vdev);
struct imx_media_buffer *
imx_media_capture_device_next_buf(struct imx_media_video_dev *vdev);
void imx_media_capture_device_set_format(struct imx_media_video_dev *vdev,
- struct v4l2_pix_format *pix);
+ const struct v4l2_pix_format *pix,
+ const struct v4l2_rect *compose);
void imx_media_capture_device_error(struct imx_media_video_dev *vdev);
/* subdev group ids */
-#define IMX_MEDIA_GRP_ID_CSI2 BIT(8)
-#define IMX_MEDIA_GRP_ID_CSI_BIT 9
-#define IMX_MEDIA_GRP_ID_CSI (0x3 << IMX_MEDIA_GRP_ID_CSI_BIT)
-#define IMX_MEDIA_GRP_ID_CSI0 BIT(IMX_MEDIA_GRP_ID_CSI_BIT)
-#define IMX_MEDIA_GRP_ID_CSI1 (2 << IMX_MEDIA_GRP_ID_CSI_BIT)
-#define IMX_MEDIA_GRP_ID_VDIC BIT(11)
-#define IMX_MEDIA_GRP_ID_IC_PRP BIT(12)
-#define IMX_MEDIA_GRP_ID_IC_PRPENC BIT(13)
-#define IMX_MEDIA_GRP_ID_IC_PRPVF BIT(14)
+#define IMX_MEDIA_GRP_ID_CSI2 BIT(8)
+#define IMX_MEDIA_GRP_ID_CSI BIT(9)
+#define IMX_MEDIA_GRP_ID_IPU_CSI_BIT 10
+#define IMX_MEDIA_GRP_ID_IPU_CSI (0x3 << IMX_MEDIA_GRP_ID_IPU_CSI_BIT)
+#define IMX_MEDIA_GRP_ID_IPU_CSI0 BIT(IMX_MEDIA_GRP_ID_IPU_CSI_BIT)
+#define IMX_MEDIA_GRP_ID_IPU_CSI1 (2 << IMX_MEDIA_GRP_ID_IPU_CSI_BIT)
+#define IMX_MEDIA_GRP_ID_IPU_VDIC BIT(12)
+#define IMX_MEDIA_GRP_ID_IPU_IC_PRP BIT(13)
+#define IMX_MEDIA_GRP_ID_IPU_IC_PRPENC BIT(14)
+#define IMX_MEDIA_GRP_ID_IPU_IC_PRPVF BIT(15)
#endif
diff --git a/drivers/staging/media/imx/imx7-media-csi.c b/drivers/staging/media/imx/imx7-media-csi.c
new file mode 100644
index 000000000000..3fba7c27c0ec
--- /dev/null
+++ b/drivers/staging/media/imx/imx7-media-csi.c
@@ -0,0 +1,1369 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * V4L2 Capture CSI Subdev for Freescale i.MX7 SOC
+ *
+ * Copyright (c) 2019 Linaro Ltd
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gcd.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of_graph.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-mc.h>
+#include <media/v4l2-subdev.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include <media/imx.h>
+#include "imx-media.h"
+
+#define IMX7_CSI_PAD_SINK 0
+#define IMX7_CSI_PAD_SRC 1
+#define IMX7_CSI_PADS_NUM 2
+
+/* reset values */
+#define CSICR1_RESET_VAL 0x40000800
+#define CSICR2_RESET_VAL 0x0
+#define CSICR3_RESET_VAL 0x0
+
+/* csi control reg 1 */
+#define BIT_SWAP16_EN BIT(31)
+#define BIT_EXT_VSYNC BIT(30)
+#define BIT_EOF_INT_EN BIT(29)
+#define BIT_PRP_IF_EN BIT(28)
+#define BIT_CCIR_MODE BIT(27)
+#define BIT_COF_INT_EN BIT(26)
+#define BIT_SF_OR_INTEN BIT(25)
+#define BIT_RF_OR_INTEN BIT(24)
+#define BIT_SFF_DMA_DONE_INTEN BIT(22)
+#define BIT_STATFF_INTEN BIT(21)
+#define BIT_FB2_DMA_DONE_INTEN BIT(20)
+#define BIT_FB1_DMA_DONE_INTEN BIT(19)
+#define BIT_RXFF_INTEN BIT(18)
+#define BIT_SOF_POL BIT(17)
+#define BIT_SOF_INTEN BIT(16)
+#define BIT_MCLKDIV (0xF << 12)
+#define BIT_HSYNC_POL BIT(11)
+#define BIT_CCIR_EN BIT(10)
+#define BIT_MCLKEN BIT(9)
+#define BIT_FCC BIT(8)
+#define BIT_PACK_DIR BIT(7)
+#define BIT_CLR_STATFIFO BIT(6)
+#define BIT_CLR_RXFIFO BIT(5)
+#define BIT_GCLK_MODE BIT(4)
+#define BIT_INV_DATA BIT(3)
+#define BIT_INV_PCLK BIT(2)
+#define BIT_REDGE BIT(1)
+#define BIT_PIXEL_BIT BIT(0)
+
+#define SHIFT_MCLKDIV 12
+
+/* control reg 3 */
+#define BIT_FRMCNT (0xFFFF << 16)
+#define BIT_FRMCNT_RST BIT(15)
+#define BIT_DMA_REFLASH_RFF BIT(14)
+#define BIT_DMA_REFLASH_SFF BIT(13)
+#define BIT_DMA_REQ_EN_RFF BIT(12)
+#define BIT_DMA_REQ_EN_SFF BIT(11)
+#define BIT_STATFF_LEVEL (0x7 << 8)
+#define BIT_HRESP_ERR_EN BIT(7)
+#define BIT_RXFF_LEVEL (0x7 << 4)
+#define BIT_TWO_8BIT_SENSOR BIT(3)
+#define BIT_ZERO_PACK_EN BIT(2)
+#define BIT_ECC_INT_EN BIT(1)
+#define BIT_ECC_AUTO_EN BIT(0)
+
+#define SHIFT_FRMCNT 16
+#define SHIFT_RXFIFO_LEVEL 4
+
+/* csi status reg */
+#define BIT_ADDR_CH_ERR_INT BIT(28)
+#define BIT_FIELD0_INT BIT(27)
+#define BIT_FIELD1_INT BIT(26)
+#define BIT_SFF_OR_INT BIT(25)
+#define BIT_RFF_OR_INT BIT(24)
+#define BIT_DMA_TSF_DONE_SFF BIT(22)
+#define BIT_STATFF_INT BIT(21)
+#define BIT_DMA_TSF_DONE_FB2 BIT(20)
+#define BIT_DMA_TSF_DONE_FB1 BIT(19)
+#define BIT_RXFF_INT BIT(18)
+#define BIT_EOF_INT BIT(17)
+#define BIT_SOF_INT BIT(16)
+#define BIT_F2_INT BIT(15)
+#define BIT_F1_INT BIT(14)
+#define BIT_COF_INT BIT(13)
+#define BIT_HRESP_ERR_INT BIT(7)
+#define BIT_ECC_INT BIT(1)
+#define BIT_DRDY BIT(0)
+
+/* csi control reg 18 */
+#define BIT_CSI_HW_ENABLE BIT(31)
+#define BIT_MIPI_DATA_FORMAT_RAW8 (0x2a << 25)
+#define BIT_MIPI_DATA_FORMAT_RAW10 (0x2b << 25)
+#define BIT_MIPI_DATA_FORMAT_RAW12 (0x2c << 25)
+#define BIT_MIPI_DATA_FORMAT_RAW14 (0x2d << 25)
+#define BIT_MIPI_DATA_FORMAT_YUV422_8B (0x1e << 25)
+#define BIT_MIPI_DATA_FORMAT_MASK (0x3F << 25)
+#define BIT_MIPI_DATA_FORMAT_OFFSET 25
+#define BIT_DATA_FROM_MIPI BIT(22)
+#define BIT_MIPI_YU_SWAP BIT(21)
+#define BIT_MIPI_DOUBLE_CMPNT BIT(20)
+#define BIT_BASEADDR_CHG_ERR_EN BIT(9)
+#define BIT_BASEADDR_SWITCH_SEL BIT(5)
+#define BIT_BASEADDR_SWITCH_EN BIT(4)
+#define BIT_PARALLEL24_EN BIT(3)
+#define BIT_DEINTERLACE_EN BIT(2)
+#define BIT_TVDECODER_IN_EN BIT(1)
+#define BIT_NTSC_EN BIT(0)
+
+#define CSI_MCLK_VF 1
+#define CSI_MCLK_ENC 2
+#define CSI_MCLK_RAW 4
+#define CSI_MCLK_I2C 8
+
+#define CSI_CSICR1 0x0
+#define CSI_CSICR2 0x4
+#define CSI_CSICR3 0x8
+#define CSI_STATFIFO 0xC
+#define CSI_CSIRXFIFO 0x10
+#define CSI_CSIRXCNT 0x14
+#define CSI_CSISR 0x18
+
+#define CSI_CSIDBG 0x1C
+#define CSI_CSIDMASA_STATFIFO 0x20
+#define CSI_CSIDMATS_STATFIFO 0x24
+#define CSI_CSIDMASA_FB1 0x28
+#define CSI_CSIDMASA_FB2 0x2C
+#define CSI_CSIFBUF_PARA 0x30
+#define CSI_CSIIMAG_PARA 0x34
+
+#define CSI_CSICR18 0x48
+#define CSI_CSICR19 0x4c
+
+static const char * const imx7_csi_clk_id[] = {"axi", "dcic", "mclk"};
+
+struct imx7_csi {
+ struct device *dev;
+ struct v4l2_subdev sd;
+ struct imx_media_video_dev *vdev;
+ struct imx_media_dev *imxmd;
+ struct media_pad pad[IMX7_CSI_PADS_NUM];
+
+ /* lock to protect members below */
+ struct mutex lock;
+ /* lock to protect irq handler when stop streaming */
+ spinlock_t irqlock;
+
+ struct v4l2_subdev *src_sd;
+
+ struct media_entity *sink;
+
+ struct v4l2_fwnode_endpoint upstream_ep;
+
+ struct v4l2_mbus_framefmt format_mbus[IMX7_CSI_PADS_NUM];
+ const struct imx_media_pixfmt *cc[IMX7_CSI_PADS_NUM];
+ struct v4l2_fract frame_interval[IMX7_CSI_PADS_NUM];
+
+ struct v4l2_ctrl_handler ctrl_hdlr;
+
+ void __iomem *regbase;
+ int irq;
+
+ int num_clks;
+ struct clk_bulk_data *clks;
+
+ /* active vb2 buffers to send to video dev sink */
+ struct imx_media_buffer *active_vb2_buf[2];
+ struct imx_media_dma_buf underrun_buf;
+
+ int buf_num;
+ u32 frame_sequence;
+
+ bool last_eof;
+ bool is_init;
+ bool is_streaming;
+ bool is_csi2;
+
+ struct completion last_eof_completion;
+};
+
+#define imx7_csi_reg_read(_csi, _offset) \
+ __raw_readl((_csi)->regbase + (_offset))
+#define imx7_csi_reg_write(_csi, _val, _offset) \
+ __raw_writel(_val, (_csi)->regbase + (_offset))
+
+static void imx7_csi_clk_enable(struct imx7_csi *csi)
+{
+ int ret;
+
+ ret = clk_bulk_prepare_enable(csi->num_clks, csi->clks);
+ if (ret < 0)
+ dev_err(csi->dev, "failed to enable clocks\n");
+}
+
+static void imx7_csi_clk_disable(struct imx7_csi *csi)
+{
+ clk_bulk_disable_unprepare(csi->num_clks, csi->clks);
+}
+
+static void imx7_csi_hw_reset(struct imx7_csi *csi)
+{
+ imx7_csi_reg_write(csi,
+ imx7_csi_reg_read(csi, CSI_CSICR3) | BIT_FRMCNT_RST,
+ CSI_CSICR3);
+
+ imx7_csi_reg_write(csi, CSICR1_RESET_VAL, CSI_CSICR1);
+ imx7_csi_reg_write(csi, CSICR2_RESET_VAL, CSI_CSICR2);
+ imx7_csi_reg_write(csi, CSICR3_RESET_VAL, CSI_CSICR3);
+}
+
+static unsigned long imx7_csi_irq_clear(struct imx7_csi *csi)
+{
+ unsigned long isr;
+
+ isr = imx7_csi_reg_read(csi, CSI_CSISR);
+ imx7_csi_reg_write(csi, isr, CSI_CSISR);
+
+ return isr;
+}
+
+static void imx7_csi_init_interface(struct imx7_csi *csi)
+{
+ unsigned int val = 0;
+ unsigned int imag_para;
+
+ val = BIT_SOF_POL | BIT_REDGE | BIT_GCLK_MODE | BIT_HSYNC_POL |
+ BIT_FCC | 1 << SHIFT_MCLKDIV | BIT_MCLKEN;
+ imx7_csi_reg_write(csi, val, CSI_CSICR1);
+
+ imag_para = (800 << 16) | 600;
+ imx7_csi_reg_write(csi, imag_para, CSI_CSIIMAG_PARA);
+
+ val = BIT_DMA_REFLASH_RFF;
+ imx7_csi_reg_write(csi, val, CSI_CSICR3);
+}
+
+static void imx7_csi_hw_enable_irq(struct imx7_csi *csi)
+{
+ unsigned long cr1 = imx7_csi_reg_read(csi, CSI_CSICR1);
+
+ cr1 |= BIT_SOF_INTEN;
+ cr1 |= BIT_RFF_OR_INT;
+
+ /* still capture needs DMA interrupt */
+ cr1 |= BIT_FB1_DMA_DONE_INTEN;
+ cr1 |= BIT_FB2_DMA_DONE_INTEN;
+
+ cr1 |= BIT_EOF_INT_EN;
+
+ imx7_csi_reg_write(csi, cr1, CSI_CSICR1);
+}
+
+static void imx7_csi_hw_disable_irq(struct imx7_csi *csi)
+{
+ unsigned long cr1 = imx7_csi_reg_read(csi, CSI_CSICR1);
+
+ cr1 &= ~BIT_SOF_INTEN;
+ cr1 &= ~BIT_RFF_OR_INT;
+ cr1 &= ~BIT_FB1_DMA_DONE_INTEN;
+ cr1 &= ~BIT_FB2_DMA_DONE_INTEN;
+ cr1 &= ~BIT_EOF_INT_EN;
+
+ imx7_csi_reg_write(csi, cr1, CSI_CSICR1);
+}
+
+static void imx7_csi_hw_enable(struct imx7_csi *csi)
+{
+ unsigned long cr = imx7_csi_reg_read(csi, CSI_CSICR18);
+
+ cr |= BIT_CSI_HW_ENABLE;
+
+ imx7_csi_reg_write(csi, cr, CSI_CSICR18);
+}
+
+static void imx7_csi_hw_disable(struct imx7_csi *csi)
+{
+ unsigned long cr = imx7_csi_reg_read(csi, CSI_CSICR18);
+
+ cr &= ~BIT_CSI_HW_ENABLE;
+
+ imx7_csi_reg_write(csi, cr, CSI_CSICR18);
+}
+
+static void imx7_csi_dma_reflash(struct imx7_csi *csi)
+{
+ unsigned long cr3 = imx7_csi_reg_read(csi, CSI_CSICR18);
+
+ cr3 = imx7_csi_reg_read(csi, CSI_CSICR3);
+ cr3 |= BIT_DMA_REFLASH_RFF;
+ imx7_csi_reg_write(csi, cr3, CSI_CSICR3);
+}
+
+static void imx7_csi_rx_fifo_clear(struct imx7_csi *csi)
+{
+ unsigned long cr1;
+
+ cr1 = imx7_csi_reg_read(csi, CSI_CSICR1);
+ imx7_csi_reg_write(csi, cr1 & ~BIT_FCC, CSI_CSICR1);
+ cr1 = imx7_csi_reg_read(csi, CSI_CSICR1);
+ imx7_csi_reg_write(csi, cr1 | BIT_CLR_RXFIFO, CSI_CSICR1);
+
+ cr1 = imx7_csi_reg_read(csi, CSI_CSICR1);
+ imx7_csi_reg_write(csi, cr1 | BIT_FCC, CSI_CSICR1);
+}
+
+static void imx7_csi_buf_stride_set(struct imx7_csi *csi, u32 stride)
+{
+ imx7_csi_reg_write(csi, stride, CSI_CSIFBUF_PARA);
+}
+
+static void imx7_csi_deinterlace_enable(struct imx7_csi *csi, bool enable)
+{
+ unsigned long cr18 = imx7_csi_reg_read(csi, CSI_CSICR18);
+
+ if (enable)
+ cr18 |= BIT_DEINTERLACE_EN;
+ else
+ cr18 &= ~BIT_DEINTERLACE_EN;
+
+ imx7_csi_reg_write(csi, cr18, CSI_CSICR18);
+}
+
+static void imx7_csi_dmareq_rff_enable(struct imx7_csi *csi)
+{
+ unsigned long cr3 = imx7_csi_reg_read(csi, CSI_CSICR3);
+ unsigned long cr2 = imx7_csi_reg_read(csi, CSI_CSICR2);
+
+ /* Burst Type of DMA Transfer from RxFIFO. INCR16 */
+ cr2 |= 0xC0000000;
+
+ cr3 |= BIT_DMA_REQ_EN_RFF;
+ cr3 |= BIT_HRESP_ERR_EN;
+ cr3 &= ~BIT_RXFF_LEVEL;
+ cr3 |= 0x2 << 4;
+
+ imx7_csi_reg_write(csi, cr3, CSI_CSICR3);
+ imx7_csi_reg_write(csi, cr2, CSI_CSICR2);
+}
+
+static void imx7_csi_dmareq_rff_disable(struct imx7_csi *csi)
+{
+ unsigned long cr3 = imx7_csi_reg_read(csi, CSI_CSICR3);
+
+ cr3 &= ~BIT_DMA_REQ_EN_RFF;
+ cr3 &= ~BIT_HRESP_ERR_EN;
+ imx7_csi_reg_write(csi, cr3, CSI_CSICR3);
+}
+
+static void imx7_csi_set_imagpara(struct imx7_csi *csi, int width, int height)
+{
+ int imag_para;
+ int rx_count;
+
+ rx_count = (width * height) >> 2;
+ imx7_csi_reg_write(csi, rx_count, CSI_CSIRXCNT);
+
+ imag_para = (width << 16) | height;
+ imx7_csi_reg_write(csi, imag_para, CSI_CSIIMAG_PARA);
+
+ /* reflash the embedded DMA controller */
+ imx7_csi_dma_reflash(csi);
+}
+
+static void imx7_csi_sw_reset(struct imx7_csi *csi)
+{
+ imx7_csi_hw_disable(csi);
+
+ imx7_csi_rx_fifo_clear(csi);
+
+ imx7_csi_dma_reflash(csi);
+
+ usleep_range(2000, 3000);
+
+ imx7_csi_irq_clear(csi);
+
+ imx7_csi_hw_enable(csi);
+}
+
+static void imx7_csi_error_recovery(struct imx7_csi *csi)
+{
+ imx7_csi_hw_disable(csi);
+
+ imx7_csi_rx_fifo_clear(csi);
+
+ imx7_csi_dma_reflash(csi);
+
+ imx7_csi_hw_enable(csi);
+}
+
+static void imx7_csi_init(struct imx7_csi *csi)
+{
+ if (csi->is_init)
+ return;
+
+ imx7_csi_clk_enable(csi);
+ imx7_csi_hw_reset(csi);
+ imx7_csi_init_interface(csi);
+ imx7_csi_dmareq_rff_enable(csi);
+
+ csi->is_init = true;
+}
+
+static void imx7_csi_deinit(struct imx7_csi *csi)
+{
+ if (!csi->is_init)
+ return;
+
+ imx7_csi_hw_reset(csi);
+ imx7_csi_init_interface(csi);
+ imx7_csi_dmareq_rff_disable(csi);
+ imx7_csi_clk_disable(csi);
+
+ csi->is_init = false;
+}
+
+static int imx7_csi_get_upstream_endpoint(struct imx7_csi *csi,
+ struct v4l2_fwnode_endpoint *ep,
+ bool skip_mux)
+{
+ struct device_node *endpoint, *port;
+ struct media_entity *src;
+ struct v4l2_subdev *sd;
+ struct media_pad *pad;
+
+ if (!csi->src_sd)
+ return -EPIPE;
+
+ src = &csi->src_sd->entity;
+
+skip_video_mux:
+ /* get source pad of entity directly upstream from src */
+ pad = imx_media_find_upstream_pad(csi->imxmd, src, 0);
+ if (IS_ERR(pad))
+ return PTR_ERR(pad);
+
+ sd = media_entity_to_v4l2_subdev(pad->entity);
+
+ /* To get bus type we may need to skip video mux */
+ if (skip_mux && src->function == MEDIA_ENT_F_VID_MUX) {
+ src = &sd->entity;
+ goto skip_video_mux;
+ }
+
+ /*
+ * NOTE: this assumes an OF-graph port id is the same as a
+ * media pad index.
+ */
+ port = of_graph_get_port_by_id(sd->dev->of_node, pad->index);
+ if (!port)
+ return -ENODEV;
+
+ endpoint = of_get_next_child(port, NULL);
+ of_node_put(port);
+ if (!endpoint)
+ return -ENODEV;
+
+ v4l2_fwnode_endpoint_parse(of_fwnode_handle(endpoint), ep);
+ of_node_put(endpoint);
+
+ return 0;
+}
+
+static int imx7_csi_link_setup(struct media_entity *entity,
+ const struct media_pad *local,
+ const struct media_pad *remote, u32 flags)
+{
+ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
+ struct imx7_csi *csi = v4l2_get_subdevdata(sd);
+ struct v4l2_subdev *remote_sd;
+ int ret = 0;
+
+ dev_dbg(csi->dev, "link setup %s -> %s\n", remote->entity->name,
+ local->entity->name);
+
+ mutex_lock(&csi->lock);
+
+ if (local->flags & MEDIA_PAD_FL_SINK) {
+ if (!is_media_entity_v4l2_subdev(remote->entity)) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ remote_sd = media_entity_to_v4l2_subdev(remote->entity);
+
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (csi->src_sd) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+ csi->src_sd = remote_sd;
+ } else {
+ csi->src_sd = NULL;
+ }
+
+ goto init;
+ }
+
+ /* source pad */
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (csi->sink) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+ csi->sink = remote->entity;
+ } else {
+ v4l2_ctrl_handler_free(&csi->ctrl_hdlr);
+ v4l2_ctrl_handler_init(&csi->ctrl_hdlr, 0);
+ csi->sink = NULL;
+ }
+
+init:
+ if (csi->sink || csi->src_sd)
+ imx7_csi_init(csi);
+ else
+ imx7_csi_deinit(csi);
+
+unlock:
+ mutex_unlock(&csi->lock);
+
+ return ret;
+}
+
+static int imx7_csi_pad_link_validate(struct v4l2_subdev *sd,
+ struct media_link *link,
+ struct v4l2_subdev_format *source_fmt,
+ struct v4l2_subdev_format *sink_fmt)
+{
+ struct imx7_csi *csi = v4l2_get_subdevdata(sd);
+ struct v4l2_fwnode_endpoint upstream_ep = {};
+ int ret;
+
+ ret = v4l2_subdev_link_validate_default(sd, link, source_fmt, sink_fmt);
+ if (ret)
+ return ret;
+
+ ret = imx7_csi_get_upstream_endpoint(csi, &upstream_ep, true);
+ if (ret) {
+ v4l2_err(&csi->sd, "failed to find upstream endpoint\n");
+ return ret;
+ }
+
+ mutex_lock(&csi->lock);
+
+ csi->upstream_ep = upstream_ep;
+ csi->is_csi2 = (upstream_ep.bus_type == V4L2_MBUS_CSI2_DPHY);
+
+ mutex_unlock(&csi->lock);
+
+ return 0;
+}
+
+static void imx7_csi_update_buf(struct imx7_csi *csi, dma_addr_t phys,
+ int buf_num)
+{
+ if (buf_num == 1)
+ imx7_csi_reg_write(csi, phys, CSI_CSIDMASA_FB2);
+ else
+ imx7_csi_reg_write(csi, phys, CSI_CSIDMASA_FB1);
+}
+
+static void imx7_csi_setup_vb2_buf(struct imx7_csi *csi)
+{
+ struct imx_media_video_dev *vdev = csi->vdev;
+ struct imx_media_buffer *buf;
+ struct vb2_buffer *vb2_buf;
+ dma_addr_t phys[2];
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ buf = imx_media_capture_device_next_buf(vdev);
+ if (buf) {
+ csi->active_vb2_buf[i] = buf;
+ vb2_buf = &buf->vbuf.vb2_buf;
+ phys[i] = vb2_dma_contig_plane_dma_addr(vb2_buf, 0);
+ } else {
+ csi->active_vb2_buf[i] = NULL;
+ phys[i] = csi->underrun_buf.phys;
+ }
+
+ imx7_csi_update_buf(csi, phys[i], i);
+ }
+}
+
+static void imx7_csi_dma_unsetup_vb2_buf(struct imx7_csi *csi,
+ enum vb2_buffer_state return_status)
+{
+ struct imx_media_buffer *buf;
+ int i;
+
+ /* return any remaining active frames with return_status */
+ for (i = 0; i < 2; i++) {
+ buf = csi->active_vb2_buf[i];
+ if (buf) {
+ struct vb2_buffer *vb = &buf->vbuf.vb2_buf;
+
+ vb->timestamp = ktime_get_ns();
+ vb2_buffer_done(vb, return_status);
+ }
+ }
+}
+
+static void imx7_csi_vb2_buf_done(struct imx7_csi *csi)
+{
+ struct imx_media_video_dev *vdev = csi->vdev;
+ struct imx_media_buffer *done, *next;
+ struct vb2_buffer *vb;
+ dma_addr_t phys;
+
+ done = csi->active_vb2_buf[csi->buf_num];
+ if (done) {
+ done->vbuf.field = vdev->fmt.fmt.pix.field;
+ done->vbuf.sequence = csi->frame_sequence;
+ vb = &done->vbuf.vb2_buf;
+ vb->timestamp = ktime_get_ns();
+ vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+ }
+ csi->frame_sequence++;
+
+ /* get next queued buffer */
+ next = imx_media_capture_device_next_buf(vdev);
+ if (next) {
+ phys = vb2_dma_contig_plane_dma_addr(&next->vbuf.vb2_buf, 0);
+ csi->active_vb2_buf[csi->buf_num] = next;
+ } else {
+ phys = csi->underrun_buf.phys;
+ csi->active_vb2_buf[csi->buf_num] = NULL;
+ }
+
+ imx7_csi_update_buf(csi, phys, csi->buf_num);
+}
+
+static irqreturn_t imx7_csi_irq_handler(int irq, void *data)
+{
+ struct imx7_csi *csi = data;
+ unsigned long status;
+
+ spin_lock(&csi->irqlock);
+
+ status = imx7_csi_irq_clear(csi);
+
+ if (status & BIT_RFF_OR_INT) {
+ dev_warn(csi->dev, "Rx fifo overflow\n");
+ imx7_csi_error_recovery(csi);
+ }
+
+ if (status & BIT_HRESP_ERR_INT) {
+ dev_warn(csi->dev, "Hresponse error detected\n");
+ imx7_csi_error_recovery(csi);
+ }
+
+ if (status & BIT_ADDR_CH_ERR_INT) {
+ imx7_csi_hw_disable(csi);
+
+ imx7_csi_dma_reflash(csi);
+
+ imx7_csi_hw_enable(csi);
+ }
+
+ if ((status & BIT_DMA_TSF_DONE_FB1) &&
+ (status & BIT_DMA_TSF_DONE_FB2)) {
+ /*
+ * For both FB1 and FB2 interrupter bits set case,
+ * CSI DMA is work in one of FB1 and FB2 buffer,
+ * but software can not know the state.
+ * Skip it to avoid base address updated
+ * when csi work in field0 and field1 will write to
+ * new base address.
+ */
+ } else if (status & BIT_DMA_TSF_DONE_FB1) {
+ csi->buf_num = 0;
+ } else if (status & BIT_DMA_TSF_DONE_FB2) {
+ csi->buf_num = 1;
+ }
+
+ if ((status & BIT_DMA_TSF_DONE_FB1) ||
+ (status & BIT_DMA_TSF_DONE_FB2)) {
+ imx7_csi_vb2_buf_done(csi);
+
+ if (csi->last_eof) {
+ complete(&csi->last_eof_completion);
+ csi->last_eof = false;
+ }
+ }
+
+ spin_unlock(&csi->irqlock);
+
+ return IRQ_HANDLED;
+}
+
+static int imx7_csi_dma_start(struct imx7_csi *csi)
+{
+ struct imx_media_video_dev *vdev = csi->vdev;
+ struct v4l2_pix_format *out_pix = &vdev->fmt.fmt.pix;
+ int ret;
+
+ ret = imx_media_alloc_dma_buf(csi->imxmd, &csi->underrun_buf,
+ out_pix->sizeimage);
+ if (ret < 0) {
+ v4l2_warn(&csi->sd, "consider increasing the CMA area\n");
+ return ret;
+ }
+
+ csi->frame_sequence = 0;
+ csi->last_eof = false;
+ init_completion(&csi->last_eof_completion);
+
+ imx7_csi_setup_vb2_buf(csi);
+
+ return 0;
+}
+
+static void imx7_csi_dma_stop(struct imx7_csi *csi)
+{
+ unsigned long timeout_jiffies;
+ unsigned long flags;
+ int ret;
+
+ /* mark next EOF interrupt as the last before stream off */
+ spin_lock_irqsave(&csi->irqlock, flags);
+ csi->last_eof = true;
+ spin_unlock_irqrestore(&csi->irqlock, flags);
+
+ /*
+ * and then wait for interrupt handler to mark completion.
+ */
+ timeout_jiffies = msecs_to_jiffies(IMX_MEDIA_EOF_TIMEOUT);
+ ret = wait_for_completion_timeout(&csi->last_eof_completion,
+ timeout_jiffies);
+ if (ret == 0)
+ v4l2_warn(&csi->sd, "wait last EOF timeout\n");
+
+ imx7_csi_hw_disable_irq(csi);
+
+ imx7_csi_dma_unsetup_vb2_buf(csi, VB2_BUF_STATE_ERROR);
+
+ imx_media_free_dma_buf(csi->imxmd, &csi->underrun_buf);
+}
+
+static int imx7_csi_configure(struct imx7_csi *csi)
+{
+ struct imx_media_video_dev *vdev = csi->vdev;
+ struct v4l2_pix_format *out_pix = &vdev->fmt.fmt.pix;
+ __u32 in_code = csi->format_mbus[IMX7_CSI_PAD_SINK].code;
+ u32 cr1, cr18;
+
+ if (out_pix->field == V4L2_FIELD_INTERLACED) {
+ imx7_csi_deinterlace_enable(csi, true);
+ imx7_csi_buf_stride_set(csi, out_pix->width);
+ } else {
+ imx7_csi_deinterlace_enable(csi, false);
+ imx7_csi_buf_stride_set(csi, 0);
+ }
+
+ imx7_csi_set_imagpara(csi, out_pix->width, out_pix->height);
+
+ if (!csi->is_csi2)
+ return 0;
+
+ cr1 = imx7_csi_reg_read(csi, CSI_CSICR1);
+ cr1 &= ~BIT_GCLK_MODE;
+
+ cr18 = imx7_csi_reg_read(csi, CSI_CSICR18);
+ cr18 &= BIT_MIPI_DATA_FORMAT_MASK;
+ cr18 |= BIT_DATA_FROM_MIPI;
+
+ switch (out_pix->pixelformat) {
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_YUYV:
+ cr18 |= BIT_MIPI_DATA_FORMAT_YUV422_8B;
+ break;
+ case V4L2_PIX_FMT_SBGGR8:
+ cr18 |= BIT_MIPI_DATA_FORMAT_RAW8;
+ break;
+ case V4L2_PIX_FMT_SBGGR16:
+ if (in_code == MEDIA_BUS_FMT_SBGGR10_1X10)
+ cr18 |= BIT_MIPI_DATA_FORMAT_RAW10;
+ else if (in_code == MEDIA_BUS_FMT_SBGGR12_1X12)
+ cr18 |= BIT_MIPI_DATA_FORMAT_RAW12;
+ else if (in_code == MEDIA_BUS_FMT_SBGGR14_1X14)
+ cr18 |= BIT_MIPI_DATA_FORMAT_RAW14;
+ cr1 |= BIT_PIXEL_BIT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ imx7_csi_reg_write(csi, cr1, CSI_CSICR1);
+ imx7_csi_reg_write(csi, cr18, CSI_CSICR18);
+
+ return 0;
+}
+
+static int imx7_csi_enable(struct imx7_csi *csi)
+{
+ imx7_csi_sw_reset(csi);
+
+ if (csi->is_csi2) {
+ imx7_csi_dmareq_rff_enable(csi);
+ imx7_csi_hw_enable_irq(csi);
+ imx7_csi_hw_enable(csi);
+ return 0;
+ }
+
+ return 0;
+}
+
+static void imx7_csi_disable(struct imx7_csi *csi)
+{
+ imx7_csi_dmareq_rff_disable(csi);
+
+ imx7_csi_hw_disable_irq(csi);
+
+ imx7_csi_buf_stride_set(csi, 0);
+
+ imx7_csi_hw_disable(csi);
+}
+
+static int imx7_csi_streaming_start(struct imx7_csi *csi)
+{
+ int ret;
+
+ ret = imx7_csi_dma_start(csi);
+ if (ret < 0)
+ return ret;
+
+ ret = imx7_csi_configure(csi);
+ if (ret < 0)
+ goto dma_stop;
+
+ imx7_csi_enable(csi);
+
+ return 0;
+
+dma_stop:
+ imx7_csi_dma_stop(csi);
+
+ return ret;
+}
+
+static int imx7_csi_streaming_stop(struct imx7_csi *csi)
+{
+ imx7_csi_dma_stop(csi);
+
+ imx7_csi_disable(csi);
+
+ return 0;
+}
+
+static int imx7_csi_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct imx7_csi *csi = v4l2_get_subdevdata(sd);
+ int ret = 0;
+
+ mutex_lock(&csi->lock);
+
+ if (!csi->src_sd || !csi->sink) {
+ ret = -EPIPE;
+ goto out_unlock;
+ }
+
+ if (csi->is_streaming == !!enable)
+ goto out_unlock;
+
+ if (enable) {
+ ret = v4l2_subdev_call(csi->src_sd, video, s_stream, 1);
+ if (ret < 0)
+ goto out_unlock;
+
+ ret = imx7_csi_streaming_start(csi);
+ if (ret < 0) {
+ v4l2_subdev_call(csi->src_sd, video, s_stream, 0);
+ goto out_unlock;
+ }
+ } else {
+ imx7_csi_streaming_stop(csi);
+
+ v4l2_subdev_call(csi->src_sd, video, s_stream, 0);
+ }
+
+ csi->is_streaming = !!enable;
+
+out_unlock:
+ mutex_unlock(&csi->lock);
+
+ return ret;
+}
+
+static struct v4l2_mbus_framefmt *
+imx7_csi_get_format(struct imx7_csi *csi,
+ struct v4l2_subdev_pad_config *cfg,
+ unsigned int pad,
+ enum v4l2_subdev_format_whence which)
+{
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
+ return v4l2_subdev_get_try_format(&csi->sd, cfg, pad);
+
+ return &csi->format_mbus[pad];
+}
+
+static int imx7_csi_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ struct imx7_csi *csi = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *in_fmt;
+ int ret = 0;
+
+ mutex_lock(&csi->lock);
+
+ in_fmt = imx7_csi_get_format(csi, cfg, IMX7_CSI_PAD_SINK, code->which);
+
+ switch (code->pad) {
+ case IMX7_CSI_PAD_SINK:
+ ret = imx_media_enum_mbus_format(&code->code, code->index,
+ CS_SEL_ANY, true);
+ break;
+ case IMX7_CSI_PAD_SRC:
+ if (code->index != 0) {
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+
+ code->code = in_fmt->code;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+out_unlock:
+ mutex_unlock(&csi->lock);
+
+ return ret;
+}
+
+static int imx7_csi_get_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *sdformat)
+{
+ struct imx7_csi *csi = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *fmt;
+ int ret = 0;
+
+ mutex_lock(&csi->lock);
+
+ fmt = imx7_csi_get_format(csi, cfg, sdformat->pad, sdformat->which);
+ if (!fmt) {
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+
+ sdformat->format = *fmt;
+
+out_unlock:
+ mutex_unlock(&csi->lock);
+
+ return ret;
+}
+
+static int imx7_csi_try_fmt(struct imx7_csi *csi,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *sdformat,
+ const struct imx_media_pixfmt **cc)
+{
+ const struct imx_media_pixfmt *in_cc;
+ struct v4l2_mbus_framefmt *in_fmt;
+ u32 code;
+
+ in_fmt = imx7_csi_get_format(csi, cfg, IMX7_CSI_PAD_SINK,
+ sdformat->which);
+ if (!in_fmt)
+ return -EINVAL;
+
+ switch (sdformat->pad) {
+ case IMX7_CSI_PAD_SRC:
+ in_cc = imx_media_find_mbus_format(in_fmt->code, CS_SEL_ANY,
+ true);
+
+ sdformat->format.width = in_fmt->width;
+ sdformat->format.height = in_fmt->height;
+ sdformat->format.code = in_fmt->code;
+ *cc = in_cc;
+
+ sdformat->format.colorspace = in_fmt->colorspace;
+ sdformat->format.xfer_func = in_fmt->xfer_func;
+ sdformat->format.quantization = in_fmt->quantization;
+ sdformat->format.ycbcr_enc = in_fmt->ycbcr_enc;
+ break;
+ case IMX7_CSI_PAD_SINK:
+ *cc = imx_media_find_mbus_format(sdformat->format.code,
+ CS_SEL_ANY, true);
+ if (!*cc) {
+ imx_media_enum_mbus_format(&code, 0, CS_SEL_ANY, false);
+ *cc = imx_media_find_mbus_format(code, CS_SEL_ANY,
+ false);
+ sdformat->format.code = (*cc)->codes[0];
+ }
+
+ imx_media_fill_default_mbus_fields(&sdformat->format, in_fmt,
+ false);
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+ return 0;
+}
+
+static int imx7_csi_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *sdformat)
+{
+ struct imx7_csi *csi = v4l2_get_subdevdata(sd);
+ struct imx_media_video_dev *vdev = csi->vdev;
+ const struct imx_media_pixfmt *outcc;
+ struct v4l2_mbus_framefmt *outfmt;
+ struct v4l2_pix_format vdev_fmt;
+ struct v4l2_rect vdev_compose;
+ const struct imx_media_pixfmt *cc;
+ struct v4l2_mbus_framefmt *fmt;
+ struct v4l2_subdev_format format;
+ int ret = 0;
+
+ if (sdformat->pad >= IMX7_CSI_PADS_NUM)
+ return -EINVAL;
+
+ mutex_lock(&csi->lock);
+
+ if (csi->is_streaming) {
+ ret = -EBUSY;
+ goto out_unlock;
+ }
+
+ imx7_csi_try_fmt(csi, cfg, sdformat, &cc);
+
+ fmt = imx7_csi_get_format(csi, cfg, sdformat->pad, sdformat->which);
+ if (!fmt) {
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+
+ *fmt = sdformat->format;
+
+ if (sdformat->pad == IMX7_CSI_PAD_SINK) {
+ /* propagate format to source pads */
+ format.pad = IMX7_CSI_PAD_SRC;
+ format.which = sdformat->which;
+ format.format = sdformat->format;
+ if (imx7_csi_try_fmt(csi, cfg, &format, &outcc)) {
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+ outfmt = imx7_csi_get_format(csi, cfg, IMX7_CSI_PAD_SRC,
+ sdformat->which);
+ *outfmt = format.format;
+
+ if (sdformat->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+ csi->cc[IMX7_CSI_PAD_SRC] = outcc;
+ }
+
+ if (sdformat->which == V4L2_SUBDEV_FORMAT_TRY)
+ goto out_unlock;
+
+ csi->cc[sdformat->pad] = cc;
+
+ /* propagate output pad format to capture device */
+ imx_media_mbus_fmt_to_pix_fmt(&vdev_fmt, &vdev_compose,
+ &csi->format_mbus[IMX7_CSI_PAD_SRC],
+ csi->cc[IMX7_CSI_PAD_SRC]);
+ mutex_unlock(&csi->lock);
+ imx_media_capture_device_set_format(vdev, &vdev_fmt, &vdev_compose);
+
+ return 0;
+
+out_unlock:
+ mutex_unlock(&csi->lock);
+
+ return ret;
+}
+
+static int imx7_csi_registered(struct v4l2_subdev *sd)
+{
+ struct imx7_csi *csi = v4l2_get_subdevdata(sd);
+ int ret;
+ int i;
+
+ for (i = 0; i < IMX7_CSI_PADS_NUM; i++) {
+ csi->pad[i].flags = (i == IMX7_CSI_PAD_SINK) ?
+ MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE;
+
+ /* set a default mbus format */
+ ret = imx_media_init_mbus_fmt(&csi->format_mbus[i],
+ 800, 600, 0, V4L2_FIELD_NONE,
+ &csi->cc[i]);
+ if (ret < 0)
+ return ret;
+
+ /* init default frame interval */
+ csi->frame_interval[i].numerator = 1;
+ csi->frame_interval[i].denominator = 30;
+ }
+
+ ret = media_entity_pads_init(&sd->entity, IMX7_CSI_PADS_NUM, csi->pad);
+ if (ret < 0)
+ return ret;
+
+ ret = imx_media_capture_device_register(csi->vdev);
+ if (ret < 0)
+ return ret;
+
+ ret = imx_media_add_video_device(csi->imxmd, csi->vdev);
+ if (ret < 0) {
+ imx_media_capture_device_unregister(csi->vdev);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void imx7_csi_unregistered(struct v4l2_subdev *sd)
+{
+ struct imx7_csi *csi = v4l2_get_subdevdata(sd);
+
+ imx_media_capture_device_unregister(csi->vdev);
+}
+
+static int imx7_csi_init_cfg(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg)
+{
+ struct imx7_csi *csi = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *mf;
+ int ret;
+ int i;
+
+ for (i = 0; i < IMX7_CSI_PADS_NUM; i++) {
+ mf = v4l2_subdev_get_try_format(sd, cfg, i);
+
+ ret = imx_media_init_mbus_fmt(mf, 800, 600, 0, V4L2_FIELD_NONE,
+ &csi->cc[i]);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct media_entity_operations imx7_csi_entity_ops = {
+ .link_setup = imx7_csi_link_setup,
+ .link_validate = v4l2_subdev_link_validate,
+};
+
+static const struct v4l2_subdev_video_ops imx7_csi_video_ops = {
+ .s_stream = imx7_csi_s_stream,
+};
+
+static const struct v4l2_subdev_pad_ops imx7_csi_pad_ops = {
+ .init_cfg = imx7_csi_init_cfg,
+ .enum_mbus_code = imx7_csi_enum_mbus_code,
+ .get_fmt = imx7_csi_get_fmt,
+ .set_fmt = imx7_csi_set_fmt,
+ .link_validate = imx7_csi_pad_link_validate,
+};
+
+static const struct v4l2_subdev_ops imx7_csi_subdev_ops = {
+ .video = &imx7_csi_video_ops,
+ .pad = &imx7_csi_pad_ops,
+};
+
+static const struct v4l2_subdev_internal_ops imx7_csi_internal_ops = {
+ .registered = imx7_csi_registered,
+ .unregistered = imx7_csi_unregistered,
+};
+
+static int imx7_csi_parse_endpoint(struct device *dev,
+ struct v4l2_fwnode_endpoint *vep,
+ struct v4l2_async_subdev *asd)
+{
+ return fwnode_device_is_available(asd->match.fwnode) ? 0 : -EINVAL;
+}
+
+static int imx7_csi_clocks_get(struct imx7_csi *csi)
+{
+ struct device *dev = csi->dev;
+ int i;
+
+ csi->num_clks = ARRAY_SIZE(imx7_csi_clk_id);
+ csi->clks = devm_kcalloc(dev, csi->num_clks, sizeof(*csi->clks),
+ GFP_KERNEL);
+
+ if (!csi->clks)
+ return -ENOMEM;
+
+ for (i = 0; i < csi->num_clks; i++)
+ csi->clks[i].id = imx7_csi_clk_id[i];
+
+ return devm_clk_bulk_get(dev, csi->num_clks, csi->clks);
+}
+
+static int imx7_csi_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *node = dev->of_node;
+ struct imx_media_dev *imxmd;
+ struct imx7_csi *csi;
+ struct resource *res;
+ int ret;
+
+ csi = devm_kzalloc(&pdev->dev, sizeof(*csi), GFP_KERNEL);
+ if (!csi)
+ return -ENOMEM;
+
+ csi->dev = dev;
+
+ ret = imx7_csi_clocks_get(csi);
+ if (ret < 0) {
+ dev_err(dev, "Failed to get clocks");
+ return -ENODEV;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ csi->irq = platform_get_irq(pdev, 0);
+ if (!res || csi->irq < 0) {
+ dev_err(dev, "Missing platform resources data\n");
+ return -ENODEV;
+ }
+
+ csi->regbase = devm_ioremap_resource(dev, res);
+ if (IS_ERR(csi->regbase)) {
+ dev_err(dev, "Failed platform resources map\n");
+ return -ENODEV;
+ }
+
+ spin_lock_init(&csi->irqlock);
+ mutex_init(&csi->lock);
+
+ /* install interrupt handler */
+ ret = devm_request_irq(dev, csi->irq, imx7_csi_irq_handler, 0, "csi",
+ (void *)csi);
+ if (ret < 0) {
+ dev_err(dev, "Request CSI IRQ failed.\n");
+ ret = -ENODEV;
+ goto destroy_mutex;
+ }
+
+ /* add media device */
+ imxmd = imx_media_dev_init(dev);
+ if (IS_ERR(imxmd)) {
+ ret = PTR_ERR(imxmd);
+ goto destroy_mutex;
+ }
+ platform_set_drvdata(pdev, &csi->sd);
+
+ ret = imx_media_of_add_csi(imxmd, node);
+ if (ret < 0)
+ goto cleanup;
+
+ ret = imx_media_dev_notifier_register(imxmd);
+ if (ret < 0)
+ goto cleanup;
+
+ csi->imxmd = imxmd;
+ v4l2_subdev_init(&csi->sd, &imx7_csi_subdev_ops);
+ v4l2_set_subdevdata(&csi->sd, csi);
+ csi->sd.internal_ops = &imx7_csi_internal_ops;
+ csi->sd.entity.ops = &imx7_csi_entity_ops;
+ csi->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
+ csi->sd.dev = &pdev->dev;
+ csi->sd.owner = THIS_MODULE;
+ csi->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
+ csi->sd.grp_id = IMX_MEDIA_GRP_ID_CSI;
+ snprintf(csi->sd.name, sizeof(csi->sd.name), "csi");
+
+ csi->vdev = imx_media_capture_device_init(&csi->sd, IMX7_CSI_PAD_SRC);
+ if (IS_ERR(csi->vdev))
+ return PTR_ERR(csi->vdev);
+
+ v4l2_ctrl_handler_init(&csi->ctrl_hdlr, 0);
+ csi->sd.ctrl_handler = &csi->ctrl_hdlr;
+
+ ret = v4l2_async_register_fwnode_subdev(&csi->sd,
+ sizeof(struct v4l2_async_subdev),
+ NULL, 0,
+ imx7_csi_parse_endpoint);
+ if (ret)
+ goto free;
+
+ return 0;
+
+free:
+ imx_media_capture_device_unregister(csi->vdev);
+ imx_media_capture_device_remove(csi->vdev);
+ v4l2_ctrl_handler_free(&csi->ctrl_hdlr);
+
+cleanup:
+ v4l2_async_notifier_cleanup(&imxmd->notifier);
+ v4l2_device_unregister(&imxmd->v4l2_dev);
+ media_device_unregister(&imxmd->md);
+ media_device_cleanup(&imxmd->md);
+
+destroy_mutex:
+ mutex_destroy(&csi->lock);
+
+ return ret;
+}
+
+static int imx7_csi_remove(struct platform_device *pdev)
+{
+ struct v4l2_subdev *sd = platform_get_drvdata(pdev);
+ struct imx7_csi *csi = v4l2_get_subdevdata(sd);
+ struct imx_media_dev *imxmd = csi->imxmd;
+
+ v4l2_async_notifier_unregister(&imxmd->notifier);
+ v4l2_async_notifier_cleanup(&imxmd->notifier);
+
+ media_device_unregister(&imxmd->md);
+ v4l2_device_unregister(&imxmd->v4l2_dev);
+ media_device_cleanup(&imxmd->md);
+
+ imx_media_capture_device_unregister(csi->vdev);
+ imx_media_capture_device_remove(csi->vdev);
+
+ v4l2_async_unregister_subdev(sd);
+ v4l2_ctrl_handler_free(&csi->ctrl_hdlr);
+
+ mutex_destroy(&csi->lock);
+
+ return 0;
+}
+
+static const struct of_device_id imx7_csi_of_match[] = {
+ { .compatible = "fsl,imx7-csi" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, imx7_csi_of_match);
+
+static struct platform_driver imx7_csi_driver = {
+ .probe = imx7_csi_probe,
+ .remove = imx7_csi_remove,
+ .driver = {
+ .of_match_table = imx7_csi_of_match,
+ .name = "imx7-csi",
+ },
+};
+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/staging/media/imx/imx7-mipi-csis.c b/drivers/staging/media/imx/imx7-mipi-csis.c
new file mode 100644
index 000000000000..2ddcc42ab8ff
--- /dev/null
+++ b/drivers/staging/media/imx/imx7-mipi-csis.c
@@ -0,0 +1,1160 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Freescale i.MX7 SoC series MIPI-CSI V3.3 receiver driver
+ *
+ * Copyright (C) 2019 Linaro Ltd
+ * Copyright (C) 2015-2016 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright (C) 2011 - 2013 Samsung Electronics Co., Ltd.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spinlock.h>
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-subdev.h>
+
+#include "imx-media.h"
+
+#define CSIS_DRIVER_NAME "imx7-mipi-csis"
+#define CSIS_SUBDEV_NAME CSIS_DRIVER_NAME
+
+#define CSIS_PAD_SINK 0
+#define CSIS_PAD_SOURCE 1
+#define CSIS_PADS_NUM 2
+
+#define MIPI_CSIS_DEF_PIX_WIDTH 640
+#define MIPI_CSIS_DEF_PIX_HEIGHT 480
+
+/* Register map definition */
+
+/* CSIS common control */
+#define MIPI_CSIS_CMN_CTRL 0x04
+#define MIPI_CSIS_CMN_CTRL_UPDATE_SHADOW BIT(16)
+#define MIPI_CSIS_CMN_CTRL_INTER_MODE BIT(10)
+#define MIPI_CSIS_CMN_CTRL_UPDATE_SHADOW_CTRL BIT(2)
+#define MIPI_CSIS_CMN_CTRL_RESET BIT(1)
+#define MIPI_CSIS_CMN_CTRL_ENABLE BIT(0)
+
+#define MIPI_CSIS_CMN_CTRL_LANE_NR_OFFSET 8
+#define MIPI_CSIS_CMN_CTRL_LANE_NR_MASK (3 << 8)
+
+/* CSIS clock control */
+#define MIPI_CSIS_CLK_CTRL 0x08
+#define MIPI_CSIS_CLK_CTRL_CLKGATE_TRAIL_CH3(x) ((x) << 28)
+#define MIPI_CSIS_CLK_CTRL_CLKGATE_TRAIL_CH2(x) ((x) << 24)
+#define MIPI_CSIS_CLK_CTRL_CLKGATE_TRAIL_CH1(x) ((x) << 20)
+#define MIPI_CSIS_CLK_CTRL_CLKGATE_TRAIL_CH0(x) ((x) << 16)
+#define MIPI_CSIS_CLK_CTRL_CLKGATE_EN_MSK (0xf << 4)
+#define MIPI_CSIS_CLK_CTRL_WCLK_SRC BIT(0)
+
+/* CSIS Interrupt mask */
+#define MIPI_CSIS_INTMSK 0x10
+#define MIPI_CSIS_INTMSK_EVEN_BEFORE BIT(31)
+#define MIPI_CSIS_INTMSK_EVEN_AFTER BIT(30)
+#define MIPI_CSIS_INTMSK_ODD_BEFORE BIT(29)
+#define MIPI_CSIS_INTMSK_ODD_AFTER BIT(28)
+#define MIPI_CSIS_INTMSK_FRAME_START BIT(24)
+#define MIPI_CSIS_INTMSK_FRAME_END BIT(20)
+#define MIPI_CSIS_INTMSK_ERR_SOT_HS BIT(16)
+#define MIPI_CSIS_INTMSK_ERR_LOST_FS BIT(12)
+#define MIPI_CSIS_INTMSK_ERR_LOST_FE BIT(8)
+#define MIPI_CSIS_INTMSK_ERR_OVER BIT(4)
+#define MIPI_CSIS_INTMSK_ERR_WRONG_CFG BIT(3)
+#define MIPI_CSIS_INTMSK_ERR_ECC BIT(2)
+#define MIPI_CSIS_INTMSK_ERR_CRC BIT(1)
+#define MIPI_CSIS_INTMSK_ERR_UNKNOWN BIT(0)
+
+/* CSIS Interrupt source */
+#define MIPI_CSIS_INTSRC 0x14
+#define MIPI_CSIS_INTSRC_EVEN_BEFORE BIT(31)
+#define MIPI_CSIS_INTSRC_EVEN_AFTER BIT(30)
+#define MIPI_CSIS_INTSRC_EVEN BIT(30)
+#define MIPI_CSIS_INTSRC_ODD_BEFORE BIT(29)
+#define MIPI_CSIS_INTSRC_ODD_AFTER BIT(28)
+#define MIPI_CSIS_INTSRC_ODD (0x3 << 28)
+#define MIPI_CSIS_INTSRC_NON_IMAGE_DATA (0xf << 28)
+#define MIPI_CSIS_INTSRC_FRAME_START BIT(24)
+#define MIPI_CSIS_INTSRC_FRAME_END BIT(20)
+#define MIPI_CSIS_INTSRC_ERR_SOT_HS BIT(16)
+#define MIPI_CSIS_INTSRC_ERR_LOST_FS BIT(12)
+#define MIPI_CSIS_INTSRC_ERR_LOST_FE BIT(8)
+#define MIPI_CSIS_INTSRC_ERR_OVER BIT(4)
+#define MIPI_CSIS_INTSRC_ERR_WRONG_CFG BIT(3)
+#define MIPI_CSIS_INTSRC_ERR_ECC BIT(2)
+#define MIPI_CSIS_INTSRC_ERR_CRC BIT(1)
+#define MIPI_CSIS_INTSRC_ERR_UNKNOWN BIT(0)
+#define MIPI_CSIS_INTSRC_ERRORS 0xfffff
+
+/* D-PHY status control */
+#define MIPI_CSIS_DPHYSTATUS 0x20
+#define MIPI_CSIS_DPHYSTATUS_ULPS_DAT BIT(8)
+#define MIPI_CSIS_DPHYSTATUS_STOPSTATE_DAT BIT(4)
+#define MIPI_CSIS_DPHYSTATUS_ULPS_CLK BIT(1)
+#define MIPI_CSIS_DPHYSTATUS_STOPSTATE_CLK BIT(0)
+
+/* D-PHY common control */
+#define MIPI_CSIS_DPHYCTRL 0x24
+#define MIPI_CSIS_DPHYCTRL_HSS_MASK (0xff << 24)
+#define MIPI_CSIS_DPHYCTRL_HSS_OFFSET 24
+#define MIPI_CSIS_DPHYCTRL_SCLKS_MASK (0x3 << 22)
+#define MIPI_CSIS_DPHYCTRL_SCLKS_OFFSET 22
+#define MIPI_CSIS_DPHYCTRL_DPDN_SWAP_CLK BIT(6)
+#define MIPI_CSIS_DPHYCTRL_DPDN_SWAP_DAT BIT(5)
+#define MIPI_CSIS_DPHYCTRL_ENABLE_DAT BIT(1)
+#define MIPI_CSIS_DPHYCTRL_ENABLE_CLK BIT(0)
+#define MIPI_CSIS_DPHYCTRL_ENABLE (0x1f << 0)
+
+/* D-PHY Master and Slave Control register Low */
+#define MIPI_CSIS_DPHYBCTRL_L 0x30
+/* D-PHY Master and Slave Control register High */
+#define MIPI_CSIS_DPHYBCTRL_H 0x34
+/* D-PHY Slave Control register Low */
+#define MIPI_CSIS_DPHYSCTRL_L 0x38
+/* D-PHY Slave Control register High */
+#define MIPI_CSIS_DPHYSCTRL_H 0x3c
+
+/* ISP Configuration register */
+#define MIPI_CSIS_ISPCONFIG_CH0 0x40
+#define MIPI_CSIS_ISPCONFIG_CH1 0x50
+#define MIPI_CSIS_ISPCONFIG_CH2 0x60
+#define MIPI_CSIS_ISPCONFIG_CH3 0x70
+
+#define MIPI_CSIS_ISPCFG_MEM_FULL_GAP_MSK (0xff << 24)
+#define MIPI_CSIS_ISPCFG_MEM_FULL_GAP(x) ((x) << 24)
+#define MIPI_CSIS_ISPCFG_DOUBLE_CMPNT BIT(12)
+#define MIPI_CSIS_ISPCFG_ALIGN_32BIT BIT(11)
+#define MIPI_CSIS_ISPCFG_FMT_YCBCR422_8BIT (0x1e << 2)
+#define MIPI_CSIS_ISPCFG_FMT_RAW8 (0x2a << 2)
+#define MIPI_CSIS_ISPCFG_FMT_RAW10 (0x2b << 2)
+#define MIPI_CSIS_ISPCFG_FMT_RAW12 (0x2c << 2)
+
+/* User defined formats, x = 1...4 */
+#define MIPI_CSIS_ISPCFG_FMT_USER(x) ((0x30 + (x) - 1) << 2)
+#define MIPI_CSIS_ISPCFG_FMT_MASK (0x3f << 2)
+
+/* ISP Image Resolution register */
+#define MIPI_CSIS_ISPRESOL_CH0 0x44
+#define MIPI_CSIS_ISPRESOL_CH1 0x54
+#define MIPI_CSIS_ISPRESOL_CH2 0x64
+#define MIPI_CSIS_ISPRESOL_CH3 0x74
+#define CSIS_MAX_PIX_WIDTH 0xffff
+#define CSIS_MAX_PIX_HEIGHT 0xffff
+
+/* ISP SYNC register */
+#define MIPI_CSIS_ISPSYNC_CH0 0x48
+#define MIPI_CSIS_ISPSYNC_CH1 0x58
+#define MIPI_CSIS_ISPSYNC_CH2 0x68
+#define MIPI_CSIS_ISPSYNC_CH3 0x78
+
+#define MIPI_CSIS_ISPSYNC_HSYNC_LINTV_OFFSET 18
+#define MIPI_CSIS_ISPSYNC_VSYNC_SINTV_OFFSET 12
+#define MIPI_CSIS_ISPSYNC_VSYNC_EINTV_OFFSET 0
+
+/* Non-image packet data buffers */
+#define MIPI_CSIS_PKTDATA_ODD 0x2000
+#define MIPI_CSIS_PKTDATA_EVEN 0x3000
+#define MIPI_CSIS_PKTDATA_SIZE SZ_4K
+
+#define DEFAULT_SCLK_CSIS_FREQ 166000000UL
+
+enum {
+ ST_POWERED = 1,
+ ST_STREAMING = 2,
+ ST_SUSPENDED = 4,
+};
+
+struct mipi_csis_event {
+ u32 mask;
+ const char * const name;
+ unsigned int counter;
+};
+
+static const struct mipi_csis_event mipi_csis_events[] = {
+ /* Errors */
+ { MIPI_CSIS_INTSRC_ERR_SOT_HS, "SOT Error" },
+ { MIPI_CSIS_INTSRC_ERR_LOST_FS, "Lost Frame Start Error" },
+ { MIPI_CSIS_INTSRC_ERR_LOST_FE, "Lost Frame End Error" },
+ { MIPI_CSIS_INTSRC_ERR_OVER, "FIFO Overflow Error" },
+ { MIPI_CSIS_INTSRC_ERR_WRONG_CFG, "Wrong Configuration Error" },
+ { MIPI_CSIS_INTSRC_ERR_ECC, "ECC Error" },
+ { MIPI_CSIS_INTSRC_ERR_CRC, "CRC Error" },
+ { MIPI_CSIS_INTSRC_ERR_UNKNOWN, "Unknown Error" },
+ /* Non-image data receive events */
+ { MIPI_CSIS_INTSRC_EVEN_BEFORE, "Non-image data before even frame" },
+ { MIPI_CSIS_INTSRC_EVEN_AFTER, "Non-image data after even frame" },
+ { MIPI_CSIS_INTSRC_ODD_BEFORE, "Non-image data before odd frame" },
+ { MIPI_CSIS_INTSRC_ODD_AFTER, "Non-image data after odd frame" },
+ /* Frame start/end */
+ { MIPI_CSIS_INTSRC_FRAME_START, "Frame Start" },
+ { MIPI_CSIS_INTSRC_FRAME_END, "Frame End" },
+};
+
+#define MIPI_CSIS_NUM_EVENTS ARRAY_SIZE(mipi_csis_events)
+
+static const char * const mipi_csis_clk_id[] = {"pclk", "wrap", "phy"};
+
+struct csis_hw_reset {
+ struct regmap *src;
+ u8 req_src;
+ u8 rst_bit;
+};
+
+struct csi_state {
+ /* lock elements below */
+ struct mutex lock;
+ /* lock for event handler */
+ spinlock_t slock;
+ struct device *dev;
+ struct media_pad pads[CSIS_PADS_NUM];
+ struct v4l2_subdev mipi_sd;
+ struct v4l2_subdev *src_sd;
+
+ u8 index;
+ struct platform_device *pdev;
+ struct phy *phy;
+ void __iomem *regs;
+ struct clk *wrap_clk;
+ int irq;
+ u32 flags;
+
+ struct dentry *debugfs_root;
+ bool debug;
+
+ int num_clks;
+ struct clk_bulk_data *clks;
+
+ u32 clk_frequency;
+ u32 hs_settle;
+
+ struct reset_control *mrst;
+
+ const struct csis_pix_format *csis_fmt;
+ struct v4l2_mbus_framefmt format_mbus;
+
+ struct v4l2_fwnode_bus_mipi_csi2 bus;
+
+ struct mipi_csis_event events[MIPI_CSIS_NUM_EVENTS];
+
+ struct v4l2_async_notifier subdev_notifier;
+
+ struct csis_hw_reset hw_reset;
+ struct regulator *mipi_phy_regulator;
+ bool sink_linked;
+};
+
+struct csis_pix_format {
+ unsigned int pix_width_alignment;
+ u32 code;
+ u32 fmt_reg;
+ u8 data_alignment;
+};
+
+static const struct csis_pix_format mipi_csis_formats[] = {
+ {
+ .code = MEDIA_BUS_FMT_SBGGR10_1X10,
+ .fmt_reg = MIPI_CSIS_ISPCFG_FMT_RAW10,
+ .data_alignment = 16,
+ }, {
+ .code = MEDIA_BUS_FMT_VYUY8_2X8,
+ .fmt_reg = MIPI_CSIS_ISPCFG_FMT_YCBCR422_8BIT,
+ .data_alignment = 16,
+ }, {
+ .code = MEDIA_BUS_FMT_SBGGR8_1X8,
+ .fmt_reg = MIPI_CSIS_ISPCFG_FMT_RAW8,
+ .data_alignment = 8,
+ }, {
+ .code = MEDIA_BUS_FMT_YUYV8_2X8,
+ .fmt_reg = MIPI_CSIS_ISPCFG_FMT_YCBCR422_8BIT,
+ .data_alignment = 16,
+ }
+};
+
+#define mipi_csis_write(__csis, __r, __v) writel(__v, (__csis)->regs + (__r))
+#define mipi_csis_read(__csis, __r) readl((__csis)->regs + (__r))
+
+static int mipi_csis_dump_regs(struct csi_state *state)
+{
+ struct device *dev = &state->pdev->dev;
+ unsigned int i;
+ u32 cfg;
+ struct {
+ u32 offset;
+ const char * const name;
+ } registers[] = {
+ { 0x04, "CTRL" },
+ { 0x24, "DPHYCTRL" },
+ { 0x08, "CLKCTRL" },
+ { 0x20, "DPHYSTS" },
+ { 0x10, "INTMSK" },
+ { 0x40, "CONFIG_CH0" },
+ { 0xC0, "DBG_CONFIG" },
+ { 0x38, "DPHYSLAVE_L" },
+ { 0x3C, "DPHYSLAVE_H" },
+ };
+
+ dev_info(dev, "--- REGISTERS ---\n");
+
+ for (i = 0; i < ARRAY_SIZE(registers); i++) {
+ cfg = mipi_csis_read(state, registers[i].offset);
+ dev_info(dev, "%12s: 0x%08x\n", registers[i].name, cfg);
+ }
+
+ return 0;
+}
+
+static struct csi_state *mipi_sd_to_csis_state(struct v4l2_subdev *sdev)
+{
+ return container_of(sdev, struct csi_state, mipi_sd);
+}
+
+static const struct csis_pix_format *find_csis_format(u32 code)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(mipi_csis_formats); i++)
+ if (code == mipi_csis_formats[i].code)
+ return &mipi_csis_formats[i];
+ return NULL;
+}
+
+static void mipi_csis_enable_interrupts(struct csi_state *state, bool on)
+{
+ mipi_csis_write(state, MIPI_CSIS_INTMSK, on ? 0xffffffff : 0);
+}
+
+static void mipi_csis_sw_reset(struct csi_state *state)
+{
+ u32 val = mipi_csis_read(state, MIPI_CSIS_CMN_CTRL);
+
+ mipi_csis_write(state, MIPI_CSIS_CMN_CTRL,
+ val | MIPI_CSIS_CMN_CTRL_RESET);
+ usleep_range(10, 20);
+}
+
+static int mipi_csis_phy_init(struct csi_state *state)
+{
+ state->mipi_phy_regulator = devm_regulator_get(state->dev, "phy");
+
+ return regulator_set_voltage(state->mipi_phy_regulator, 1000000,
+ 1000000);
+}
+
+static void mipi_csis_phy_reset(struct csi_state *state)
+{
+ reset_control_assert(state->mrst);
+
+ msleep(20);
+
+ reset_control_deassert(state->mrst);
+}
+
+static void mipi_csis_system_enable(struct csi_state *state, int on)
+{
+ u32 val, mask;
+
+ val = mipi_csis_read(state, MIPI_CSIS_CMN_CTRL);
+ if (on)
+ val |= MIPI_CSIS_CMN_CTRL_ENABLE;
+ else
+ val &= ~MIPI_CSIS_CMN_CTRL_ENABLE;
+ mipi_csis_write(state, MIPI_CSIS_CMN_CTRL, val);
+
+ val = mipi_csis_read(state, MIPI_CSIS_DPHYCTRL);
+ val &= ~MIPI_CSIS_DPHYCTRL_ENABLE;
+ if (on) {
+ mask = (1 << (state->bus.num_data_lanes + 1)) - 1;
+ val |= (mask & MIPI_CSIS_DPHYCTRL_ENABLE);
+ }
+ mipi_csis_write(state, MIPI_CSIS_DPHYCTRL, val);
+}
+
+/* Called with the state.lock mutex held */
+static void __mipi_csis_set_format(struct csi_state *state)
+{
+ struct v4l2_mbus_framefmt *mf = &state->format_mbus;
+ u32 val;
+
+ /* Color format */
+ val = mipi_csis_read(state, MIPI_CSIS_ISPCONFIG_CH0);
+ val = (val & ~MIPI_CSIS_ISPCFG_FMT_MASK) | state->csis_fmt->fmt_reg;
+ mipi_csis_write(state, MIPI_CSIS_ISPCONFIG_CH0, val);
+
+ /* Pixel resolution */
+ val = mf->width | (mf->height << 16);
+ mipi_csis_write(state, MIPI_CSIS_ISPRESOL_CH0, val);
+}
+
+static void mipi_csis_set_hsync_settle(struct csi_state *state, int hs_settle)
+{
+ u32 val = mipi_csis_read(state, MIPI_CSIS_DPHYCTRL);
+
+ val = ((val & ~MIPI_CSIS_DPHYCTRL_HSS_MASK) | (hs_settle << 24));
+
+ mipi_csis_write(state, MIPI_CSIS_DPHYCTRL, val);
+}
+
+static void mipi_csis_set_params(struct csi_state *state)
+{
+ int lanes = state->bus.num_data_lanes;
+ u32 val;
+
+ val = mipi_csis_read(state, MIPI_CSIS_CMN_CTRL);
+ val &= ~MIPI_CSIS_CMN_CTRL_LANE_NR_MASK;
+ val |= (lanes - 1) << MIPI_CSIS_CMN_CTRL_LANE_NR_OFFSET;
+ mipi_csis_write(state, MIPI_CSIS_CMN_CTRL, val);
+
+ __mipi_csis_set_format(state);
+
+ mipi_csis_set_hsync_settle(state, state->hs_settle);
+
+ val = mipi_csis_read(state, MIPI_CSIS_ISPCONFIG_CH0);
+ if (state->csis_fmt->data_alignment == 32)
+ val |= MIPI_CSIS_ISPCFG_ALIGN_32BIT;
+ else
+ val &= ~MIPI_CSIS_ISPCFG_ALIGN_32BIT;
+ mipi_csis_write(state, MIPI_CSIS_ISPCONFIG_CH0, val);
+
+ val = (0 << MIPI_CSIS_ISPSYNC_HSYNC_LINTV_OFFSET) |
+ (0 << MIPI_CSIS_ISPSYNC_VSYNC_SINTV_OFFSET) |
+ (0 << MIPI_CSIS_ISPSYNC_VSYNC_EINTV_OFFSET);
+ mipi_csis_write(state, MIPI_CSIS_ISPSYNC_CH0, val);
+
+ val = mipi_csis_read(state, MIPI_CSIS_CLK_CTRL);
+ val &= ~MIPI_CSIS_CLK_CTRL_WCLK_SRC;
+ if (state->wrap_clk)
+ val |= MIPI_CSIS_CLK_CTRL_WCLK_SRC;
+ else
+ val &= ~MIPI_CSIS_CLK_CTRL_WCLK_SRC;
+
+ val |= MIPI_CSIS_CLK_CTRL_CLKGATE_TRAIL_CH0(15);
+ val &= ~MIPI_CSIS_CLK_CTRL_CLKGATE_EN_MSK;
+ mipi_csis_write(state, MIPI_CSIS_CLK_CTRL, val);
+
+ mipi_csis_write(state, MIPI_CSIS_DPHYBCTRL_L, 0x1f4);
+ mipi_csis_write(state, MIPI_CSIS_DPHYBCTRL_H, 0);
+
+ /* Update the shadow register. */
+ val = mipi_csis_read(state, MIPI_CSIS_CMN_CTRL);
+ mipi_csis_write(state, MIPI_CSIS_CMN_CTRL,
+ val | MIPI_CSIS_CMN_CTRL_UPDATE_SHADOW |
+ MIPI_CSIS_CMN_CTRL_UPDATE_SHADOW_CTRL);
+}
+
+static void mipi_csis_clk_enable(struct csi_state *state)
+{
+ int ret;
+
+ ret = clk_bulk_prepare_enable(state->num_clks, state->clks);
+ if (ret < 0)
+ dev_err(state->dev, "failed to enable clocks\n");
+}
+
+static void mipi_csis_clk_disable(struct csi_state *state)
+{
+ clk_bulk_disable_unprepare(state->num_clks, state->clks);
+}
+
+static int mipi_csis_clk_get(struct csi_state *state)
+{
+ struct device *dev = &state->pdev->dev;
+ unsigned int i;
+ int ret;
+
+ state->num_clks = ARRAY_SIZE(mipi_csis_clk_id);
+ state->clks = devm_kcalloc(dev, state->num_clks, sizeof(*state->clks),
+ GFP_KERNEL);
+
+ if (!state->clks)
+ return -ENOMEM;
+
+ for (i = 0; i < state->num_clks; i++)
+ state->clks[i].id = mipi_csis_clk_id[i];
+
+ ret = devm_clk_bulk_get(dev, state->num_clks, state->clks);
+ if (ret < 0)
+ return ret;
+
+ state->wrap_clk = devm_clk_get(dev, "wrap");
+ if (IS_ERR(state->wrap_clk))
+ return IS_ERR(state->wrap_clk);
+
+ /* Set clock rate */
+ ret = clk_set_rate(state->wrap_clk, state->clk_frequency);
+ if (ret < 0)
+ dev_err(dev, "set rate=%d failed: %d\n", state->clk_frequency,
+ ret);
+
+ return ret;
+}
+
+static void mipi_csis_start_stream(struct csi_state *state)
+{
+ mipi_csis_sw_reset(state);
+ mipi_csis_set_params(state);
+ mipi_csis_system_enable(state, true);
+ mipi_csis_enable_interrupts(state, true);
+}
+
+static void mipi_csis_stop_stream(struct csi_state *state)
+{
+ mipi_csis_enable_interrupts(state, false);
+ mipi_csis_system_enable(state, false);
+}
+
+static void mipi_csis_clear_counters(struct csi_state *state)
+{
+ unsigned long flags;
+ unsigned int i;
+
+ spin_lock_irqsave(&state->slock, flags);
+ for (i = 0; i < MIPI_CSIS_NUM_EVENTS; i++)
+ state->events[i].counter = 0;
+ spin_unlock_irqrestore(&state->slock, flags);
+}
+
+static void mipi_csis_log_counters(struct csi_state *state, bool non_errors)
+{
+ int i = non_errors ? MIPI_CSIS_NUM_EVENTS : MIPI_CSIS_NUM_EVENTS - 4;
+ struct device *dev = &state->pdev->dev;
+ unsigned long flags;
+
+ spin_lock_irqsave(&state->slock, flags);
+
+ for (i--; i >= 0; i--) {
+ if (state->events[i].counter > 0 || state->debug)
+ dev_info(dev, "%s events: %d\n", state->events[i].name,
+ state->events[i].counter);
+ }
+ spin_unlock_irqrestore(&state->slock, flags);
+}
+
+/*
+ * V4L2 subdev operations
+ */
+static int mipi_csis_s_stream(struct v4l2_subdev *mipi_sd, int enable)
+{
+ struct csi_state *state = mipi_sd_to_csis_state(mipi_sd);
+ int ret = 0;
+
+ if (enable) {
+ mipi_csis_clear_counters(state);
+ ret = pm_runtime_get_sync(&state->pdev->dev);
+ if (ret < 0) {
+ pm_runtime_put_noidle(&state->pdev->dev);
+ return ret;
+ }
+ ret = v4l2_subdev_call(state->src_sd, core, s_power, 1);
+ if (ret < 0)
+ return ret;
+ }
+
+ mutex_lock(&state->lock);
+ if (enable) {
+ if (state->flags & ST_SUSPENDED) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+
+ mipi_csis_start_stream(state);
+ ret = v4l2_subdev_call(state->src_sd, video, s_stream, 1);
+ if (ret < 0)
+ goto unlock;
+
+ mipi_csis_log_counters(state, true);
+
+ state->flags |= ST_STREAMING;
+ } else {
+ v4l2_subdev_call(state->src_sd, video, s_stream, 0);
+ ret = v4l2_subdev_call(state->src_sd, core, s_power, 1);
+ mipi_csis_stop_stream(state);
+ state->flags &= ~ST_STREAMING;
+ if (state->debug)
+ mipi_csis_log_counters(state, true);
+ }
+
+unlock:
+ mutex_unlock(&state->lock);
+ if (!enable)
+ pm_runtime_put(&state->pdev->dev);
+
+ return ret;
+}
+
+static int mipi_csis_link_setup(struct media_entity *entity,
+ const struct media_pad *local_pad,
+ const struct media_pad *remote_pad, u32 flags)
+{
+ struct v4l2_subdev *mipi_sd = media_entity_to_v4l2_subdev(entity);
+ struct csi_state *state = mipi_sd_to_csis_state(mipi_sd);
+ struct v4l2_subdev *remote_sd;
+ int ret = 0;
+
+ dev_dbg(state->dev, "link setup %s -> %s", remote_pad->entity->name,
+ local_pad->entity->name);
+
+ remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity);
+
+ mutex_lock(&state->lock);
+
+ if (local_pad->flags & MEDIA_PAD_FL_SOURCE) {
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (state->sink_linked) {
+ ret = -EBUSY;
+ goto out;
+ }
+ state->sink_linked = true;
+ } else {
+ state->sink_linked = false;
+ }
+ } else {
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (state->src_sd) {
+ ret = -EBUSY;
+ goto out;
+ }
+ state->src_sd = remote_sd;
+ } else {
+ state->src_sd = NULL;
+ }
+ }
+
+out:
+ mutex_unlock(&state->lock);
+ return ret;
+}
+
+static int mipi_csis_init_cfg(struct v4l2_subdev *mipi_sd,
+ struct v4l2_subdev_pad_config *cfg)
+{
+ struct v4l2_mbus_framefmt *mf;
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < CSIS_PADS_NUM; i++) {
+ mf = v4l2_subdev_get_try_format(mipi_sd, cfg, i);
+
+ ret = imx_media_init_mbus_fmt(mf, MIPI_CSIS_DEF_PIX_HEIGHT,
+ MIPI_CSIS_DEF_PIX_WIDTH, 0,
+ V4L2_FIELD_NONE, NULL);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct csis_pix_format const *
+mipi_csis_try_format(struct v4l2_subdev *mipi_sd, struct v4l2_mbus_framefmt *mf)
+{
+ struct csi_state *state = mipi_sd_to_csis_state(mipi_sd);
+ struct csis_pix_format const *csis_fmt;
+
+ csis_fmt = find_csis_format(mf->code);
+ if (!csis_fmt)
+ csis_fmt = &mipi_csis_formats[0];
+
+ v4l_bound_align_image(&mf->width, 1, CSIS_MAX_PIX_WIDTH,
+ csis_fmt->pix_width_alignment,
+ &mf->height, 1, CSIS_MAX_PIX_HEIGHT, 1,
+ 0);
+
+ state->format_mbus.code = csis_fmt->code;
+ state->format_mbus.width = mf->width;
+ state->format_mbus.height = mf->height;
+
+ return csis_fmt;
+}
+
+static struct v4l2_mbus_framefmt *
+mipi_csis_get_format(struct csi_state *state,
+ struct v4l2_subdev_pad_config *cfg,
+ enum v4l2_subdev_format_whence which,
+ unsigned int pad)
+{
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
+ return v4l2_subdev_get_try_format(&state->mipi_sd, cfg, pad);
+
+ return &state->format_mbus;
+}
+
+static int mipi_csis_set_fmt(struct v4l2_subdev *mipi_sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *sdformat)
+{
+ struct csi_state *state = mipi_sd_to_csis_state(mipi_sd);
+ struct csis_pix_format const *csis_fmt;
+ struct v4l2_mbus_framefmt *fmt;
+
+ if (sdformat->pad >= CSIS_PADS_NUM)
+ return -EINVAL;
+
+ fmt = mipi_csis_get_format(state, cfg, sdformat->which, sdformat->pad);
+
+ mutex_lock(&state->lock);
+ if (fmt && sdformat->pad == CSIS_PAD_SOURCE) {
+ sdformat->format = *fmt;
+ goto unlock;
+ }
+
+ csis_fmt = mipi_csis_try_format(mipi_sd, &sdformat->format);
+
+ sdformat->format = *fmt;
+
+ if (csis_fmt && sdformat->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+ state->csis_fmt = csis_fmt;
+ else
+ cfg->try_fmt = sdformat->format;
+
+unlock:
+ mutex_unlock(&state->lock);
+
+ return 0;
+}
+
+static int mipi_csis_get_fmt(struct v4l2_subdev *mipi_sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *sdformat)
+{
+ struct csi_state *state = mipi_sd_to_csis_state(mipi_sd);
+ struct v4l2_mbus_framefmt *fmt;
+
+ mutex_lock(&state->lock);
+
+ fmt = mipi_csis_get_format(state, cfg, sdformat->which, sdformat->pad);
+
+ sdformat->format = *fmt;
+
+ mutex_unlock(&state->lock);
+
+ return 0;
+}
+
+static int mipi_csis_log_status(struct v4l2_subdev *mipi_sd)
+{
+ struct csi_state *state = mipi_sd_to_csis_state(mipi_sd);
+
+ mutex_lock(&state->lock);
+ mipi_csis_log_counters(state, true);
+ if (state->debug && (state->flags & ST_POWERED))
+ mipi_csis_dump_regs(state);
+ mutex_unlock(&state->lock);
+
+ return 0;
+}
+
+static irqreturn_t mipi_csis_irq_handler(int irq, void *dev_id)
+{
+ struct csi_state *state = dev_id;
+ unsigned long flags;
+ unsigned int i;
+ u32 status;
+
+ status = mipi_csis_read(state, MIPI_CSIS_INTSRC);
+
+ spin_lock_irqsave(&state->slock, flags);
+
+ /* Update the event/error counters */
+ if ((status & MIPI_CSIS_INTSRC_ERRORS) || state->debug) {
+ for (i = 0; i < MIPI_CSIS_NUM_EVENTS; i++) {
+ if (!(status & state->events[i].mask))
+ continue;
+ state->events[i].counter++;
+ }
+ }
+ spin_unlock_irqrestore(&state->slock, flags);
+
+ mipi_csis_write(state, MIPI_CSIS_INTSRC, status);
+
+ return IRQ_HANDLED;
+}
+
+static const struct v4l2_subdev_core_ops mipi_csis_core_ops = {
+ .log_status = mipi_csis_log_status,
+};
+
+static const struct media_entity_operations mipi_csis_entity_ops = {
+ .link_setup = mipi_csis_link_setup,
+ .link_validate = v4l2_subdev_link_validate,
+};
+
+static const struct v4l2_subdev_video_ops mipi_csis_video_ops = {
+ .s_stream = mipi_csis_s_stream,
+};
+
+static const struct v4l2_subdev_pad_ops mipi_csis_pad_ops = {
+ .init_cfg = mipi_csis_init_cfg,
+ .get_fmt = mipi_csis_get_fmt,
+ .set_fmt = mipi_csis_set_fmt,
+};
+
+static const struct v4l2_subdev_ops mipi_csis_subdev_ops = {
+ .core = &mipi_csis_core_ops,
+ .video = &mipi_csis_video_ops,
+ .pad = &mipi_csis_pad_ops,
+};
+
+static int mipi_csis_parse_dt(struct platform_device *pdev,
+ struct csi_state *state)
+{
+ struct device_node *node = pdev->dev.of_node;
+
+ if (of_property_read_u32(node, "clock-frequency",
+ &state->clk_frequency))
+ state->clk_frequency = DEFAULT_SCLK_CSIS_FREQ;
+
+ /* Get MIPI PHY resets */
+ state->mrst = devm_reset_control_get_exclusive(&pdev->dev, "mrst");
+ if (IS_ERR(state->mrst))
+ return PTR_ERR(state->mrst);
+
+ /* Get MIPI CSI-2 bus configuration from the endpoint node. */
+ of_property_read_u32(node, "fsl,csis-hs-settle", &state->hs_settle);
+
+ return 0;
+}
+
+static int mipi_csis_pm_resume(struct device *dev, bool runtime);
+
+static int mipi_csis_parse_endpoint(struct device *dev,
+ struct v4l2_fwnode_endpoint *ep,
+ struct v4l2_async_subdev *asd)
+{
+ struct v4l2_subdev *mipi_sd = dev_get_drvdata(dev);
+ struct csi_state *state = mipi_sd_to_csis_state(mipi_sd);
+
+ if (ep->bus_type != V4L2_MBUS_CSI2_DPHY) {
+ dev_err(dev, "invalid bus type, must be MIPI CSI2\n");
+ return -EINVAL;
+ }
+
+ state->bus = ep->bus.mipi_csi2;
+
+ dev_dbg(state->dev, "data lanes: %d\n", state->bus.num_data_lanes);
+ dev_dbg(state->dev, "flags: 0x%08x\n", state->bus.flags);
+
+ return 0;
+}
+
+static int mipi_csis_subdev_init(struct v4l2_subdev *mipi_sd,
+ struct platform_device *pdev,
+ const struct v4l2_subdev_ops *ops)
+{
+ struct csi_state *state = mipi_sd_to_csis_state(mipi_sd);
+ unsigned int sink_port = 0;
+ int ret;
+
+ v4l2_subdev_init(mipi_sd, ops);
+ mipi_sd->owner = THIS_MODULE;
+ snprintf(mipi_sd->name, sizeof(mipi_sd->name), "%s.%d",
+ CSIS_SUBDEV_NAME, state->index);
+
+ mipi_sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ mipi_sd->ctrl_handler = NULL;
+
+ mipi_sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
+ mipi_sd->entity.ops = &mipi_csis_entity_ops;
+
+ mipi_sd->dev = &pdev->dev;
+
+ state->csis_fmt = &mipi_csis_formats[0];
+ state->format_mbus.code = mipi_csis_formats[0].code;
+ state->format_mbus.width = MIPI_CSIS_DEF_PIX_WIDTH;
+ state->format_mbus.height = MIPI_CSIS_DEF_PIX_HEIGHT;
+ state->format_mbus.field = V4L2_FIELD_NONE;
+
+ v4l2_set_subdevdata(mipi_sd, &pdev->dev);
+
+ ret = v4l2_async_register_fwnode_subdev(mipi_sd,
+ sizeof(struct v4l2_async_subdev),
+ &sink_port, 1,
+ mipi_csis_parse_endpoint);
+ if (ret < 0)
+ dev_err(&pdev->dev, "async fwnode register failed: %d\n", ret);
+
+ return ret;
+}
+
+#ifdef CONFIG_DEBUG_FS
+#include <linux/debugfs.h>
+
+static int mipi_csis_dump_regs_show(struct seq_file *m, void *private)
+{
+ struct csi_state *state = m->private;
+
+ return mipi_csis_dump_regs(state);
+}
+DEFINE_SHOW_ATTRIBUTE(mipi_csis_dump_regs);
+
+static int __init_or_module mipi_csis_debugfs_init(struct csi_state *state)
+{
+ struct dentry *d;
+
+ if (!debugfs_initialized())
+ return -ENODEV;
+
+ state->debugfs_root = debugfs_create_dir(dev_name(state->dev), NULL);
+ if (!state->debugfs_root)
+ return -ENOMEM;
+
+ d = debugfs_create_bool("debug_enable", 0600, state->debugfs_root,
+ &state->debug);
+ if (!d)
+ goto remove_debugfs;
+
+ d = debugfs_create_file("dump_regs", 0600, state->debugfs_root,
+ state, &mipi_csis_dump_regs_fops);
+ if (!d)
+ goto remove_debugfs;
+
+ return 0;
+
+remove_debugfs:
+ debugfs_remove_recursive(state->debugfs_root);
+
+ return -ENOMEM;
+}
+
+static void mipi_csis_debugfs_exit(struct csi_state *state)
+{
+ debugfs_remove_recursive(state->debugfs_root);
+}
+
+#else
+static int mipi_csis_debugfs_init(struct csi_state *state __maybe_unused)
+{
+ return 0;
+}
+
+static void mipi_csis_debugfs_exit(struct csi_state *state __maybe_unused)
+{
+}
+#endif
+
+static int mipi_csis_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct resource *mem_res;
+ struct csi_state *state;
+ int ret = -ENOMEM;
+
+ state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
+ if (!state)
+ return -ENOMEM;
+
+ spin_lock_init(&state->slock);
+
+ state->pdev = pdev;
+ state->dev = dev;
+
+ ret = mipi_csis_parse_dt(pdev, state);
+ if (ret < 0) {
+ dev_err(dev, "Failed to parse device tree: %d\n", ret);
+ return ret;
+ }
+
+ mipi_csis_phy_init(state);
+ mipi_csis_phy_reset(state);
+
+ mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ state->regs = devm_ioremap_resource(dev, mem_res);
+ if (IS_ERR(state->regs))
+ return PTR_ERR(state->regs);
+
+ state->irq = platform_get_irq(pdev, 0);
+ if (state->irq < 0) {
+ dev_err(dev, "Failed to get irq\n");
+ return state->irq;
+ }
+
+ ret = mipi_csis_clk_get(state);
+ if (ret < 0)
+ return ret;
+
+ mipi_csis_clk_enable(state);
+
+ ret = devm_request_irq(dev, state->irq, mipi_csis_irq_handler,
+ 0, dev_name(dev), state);
+ if (ret) {
+ dev_err(dev, "Interrupt request failed\n");
+ goto disable_clock;
+ }
+
+ platform_set_drvdata(pdev, &state->mipi_sd);
+
+ mutex_init(&state->lock);
+ ret = mipi_csis_subdev_init(&state->mipi_sd, pdev,
+ &mipi_csis_subdev_ops);
+ if (ret < 0)
+ goto disable_clock;
+
+ state->pads[CSIS_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ state->pads[CSIS_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_pads_init(&state->mipi_sd.entity, CSIS_PADS_NUM,
+ state->pads);
+ if (ret < 0)
+ goto unregister_subdev;
+
+ memcpy(state->events, mipi_csis_events, sizeof(state->events));
+
+ mipi_csis_debugfs_init(state);
+ pm_runtime_enable(dev);
+ if (!pm_runtime_enabled(dev)) {
+ ret = mipi_csis_pm_resume(dev, true);
+ if (ret < 0)
+ goto unregister_all;
+ }
+
+ dev_info(&pdev->dev, "lanes: %d, hs_settle: %d, wclk: %d, freq: %u\n",
+ state->bus.num_data_lanes, state->hs_settle,
+ state->wrap_clk ? 1 : 0, state->clk_frequency);
+
+ return 0;
+
+unregister_all:
+ mipi_csis_debugfs_exit(state);
+ media_entity_cleanup(&state->mipi_sd.entity);
+unregister_subdev:
+ v4l2_async_unregister_subdev(&state->mipi_sd);
+disable_clock:
+ mipi_csis_clk_disable(state);
+ mutex_destroy(&state->lock);
+
+ return ret;
+}
+
+static int mipi_csis_pm_suspend(struct device *dev, bool runtime)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct v4l2_subdev *mipi_sd = platform_get_drvdata(pdev);
+ struct csi_state *state = mipi_sd_to_csis_state(mipi_sd);
+ int ret = 0;
+
+ mutex_lock(&state->lock);
+ if (state->flags & ST_POWERED) {
+ mipi_csis_stop_stream(state);
+ ret = regulator_disable(state->mipi_phy_regulator);
+ if (ret)
+ goto unlock;
+ mipi_csis_clk_disable(state);
+ state->flags &= ~ST_POWERED;
+ if (!runtime)
+ state->flags |= ST_SUSPENDED;
+ }
+
+unlock:
+ mutex_unlock(&state->lock);
+
+ return ret ? -EAGAIN : 0;
+}
+
+static int mipi_csis_pm_resume(struct device *dev, bool runtime)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct v4l2_subdev *mipi_sd = platform_get_drvdata(pdev);
+ struct csi_state *state = mipi_sd_to_csis_state(mipi_sd);
+ int ret = 0;
+
+ mutex_lock(&state->lock);
+ if (!runtime && !(state->flags & ST_SUSPENDED))
+ goto unlock;
+
+ if (!(state->flags & ST_POWERED)) {
+ ret = regulator_enable(state->mipi_phy_regulator);
+ if (ret)
+ goto unlock;
+
+ state->flags |= ST_POWERED;
+ mipi_csis_clk_enable(state);
+ }
+ if (state->flags & ST_STREAMING)
+ mipi_csis_start_stream(state);
+
+ state->flags &= ~ST_SUSPENDED;
+
+unlock:
+ mutex_unlock(&state->lock);
+
+ return ret ? -EAGAIN : 0;
+}
+
+static int __maybe_unused mipi_csis_suspend(struct device *dev)
+{
+ return mipi_csis_pm_suspend(dev, false);
+}
+
+static int __maybe_unused mipi_csis_resume(struct device *dev)
+{
+ return mipi_csis_pm_resume(dev, false);
+}
+
+static int __maybe_unused mipi_csis_runtime_suspend(struct device *dev)
+{
+ return mipi_csis_pm_suspend(dev, true);
+}
+
+static int __maybe_unused mipi_csis_runtime_resume(struct device *dev)
+{
+ return mipi_csis_pm_resume(dev, true);
+}
+
+static int mipi_csis_remove(struct platform_device *pdev)
+{
+ struct v4l2_subdev *mipi_sd = platform_get_drvdata(pdev);
+ struct csi_state *state = mipi_sd_to_csis_state(mipi_sd);
+
+ mipi_csis_debugfs_exit(state);
+ v4l2_async_unregister_subdev(&state->mipi_sd);
+ v4l2_async_notifier_unregister(&state->subdev_notifier);
+
+ pm_runtime_disable(&pdev->dev);
+ mipi_csis_pm_suspend(&pdev->dev, true);
+ mipi_csis_clk_disable(state);
+ media_entity_cleanup(&state->mipi_sd.entity);
+ mutex_destroy(&state->lock);
+ pm_runtime_set_suspended(&pdev->dev);
+
+ return 0;
+}
+
+static const struct dev_pm_ops mipi_csis_pm_ops = {
+ SET_RUNTIME_PM_OPS(mipi_csis_runtime_suspend, mipi_csis_runtime_resume,
+ NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(mipi_csis_suspend, mipi_csis_resume)
+};
+
+static const struct of_device_id mipi_csis_of_match[] = {
+ { .compatible = "fsl,imx7-mipi-csi2", },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, mipi_csis_of_match);
+
+static struct platform_driver mipi_csis_driver = {
+ .probe = mipi_csis_probe,
+ .remove = mipi_csis_remove,
+ .driver = {
+ .of_match_table = mipi_csis_of_match,
+ .name = CSIS_DRIVER_NAME,
+ .pm = &mipi_csis_pm_ops,
+ },
+};
+
+module_platform_driver(mipi_csis_driver);
+
+MODULE_DESCRIPTION("i.MX7 MIPI CSI-2 Receiver driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:imx7-mipi-csi2");
diff --git a/drivers/staging/media/imx074/Kconfig b/drivers/staging/media/imx074/Kconfig
deleted file mode 100644
index 229cbeea580b..000000000000
--- a/drivers/staging/media/imx074/Kconfig
+++ /dev/null
@@ -1,5 +0,0 @@
-config SOC_CAMERA_IMX074
- tristate "imx074 support (DEPRECATED)"
- depends on SOC_CAMERA && I2C
- help
- This driver supports IMX074 cameras from Sony
diff --git a/drivers/staging/media/imx074/Makefile b/drivers/staging/media/imx074/Makefile
deleted file mode 100644
index 7d183574aa84..000000000000
--- a/drivers/staging/media/imx074/Makefile
+++ /dev/null
@@ -1 +0,0 @@
-obj-$(CONFIG_SOC_CAMERA_IMX074) += imx074.o
diff --git a/drivers/staging/media/imx074/TODO b/drivers/staging/media/imx074/TODO
deleted file mode 100644
index 15580a4f950c..000000000000
--- a/drivers/staging/media/imx074/TODO
+++ /dev/null
@@ -1,5 +0,0 @@
-This sensor driver needs to be converted to a regular
-v4l2 subdev driver. The soc_camera framework is deprecated and
-will be removed in the future. Unless someone does this work this
-sensor driver will be deleted when the soc_camera framework is
-deleted.
diff --git a/drivers/staging/media/ipu3/Makefile b/drivers/staging/media/ipu3/Makefile
index fb146d178bd4..fa7fa3372bcb 100644
--- a/drivers/staging/media/ipu3/Makefile
+++ b/drivers/staging/media/ipu3/Makefile
@@ -9,3 +9,9 @@ ipu3-imgu-objs += \
ipu3-css.o ipu3-v4l2.o ipu3.o
obj-$(CONFIG_VIDEO_IPU3_IMGU) += ipu3-imgu.o
+
+# HACK! While this driver is in bad shape, don't enable several warnings
+# that would be otherwise enabled with W=1
+ccflags-y += $(call cc-disable-warning, packed-not-aligned)
+ccflags-y += $(call cc-disable-warning, type-limits)
+ccflags-y += $(call cc-disable-warning, unused-const-variable)
diff --git a/drivers/staging/media/ipu3/TODO b/drivers/staging/media/ipu3/TODO
index 905bbb190217..5e55baeaea1a 100644
--- a/drivers/staging/media/ipu3/TODO
+++ b/drivers/staging/media/ipu3/TODO
@@ -8,11 +8,6 @@ staging directory.
- Using ENABLED and IMMUTABLE link flags for the links where those are
relevant. (Sakari)
-- Prefix imgu for all public APIs, i.e. change ipu3_v4l2_register() to
- imgu_v4l2_register(). (Sakari)
-
-- Use V4L2_CTRL_TYPE_MENU for dual-pipe mode control. (Sakari)
-
- IPU3 driver documentation (Laurent)
Add diagram in driver rst to describe output capability.
Comments on configuring v4l2 subdevs for CIO2 and ImgU.
@@ -32,3 +27,5 @@ staging directory.
- Document different operation modes, and which buffer queues are relevant
in each mode. To process an image, which queues require a buffer an in
which ones is it optional?
+
+- Make sure it builds fine with no warnings with W=1
diff --git a/drivers/staging/media/ipu3/include/intel-ipu3.h b/drivers/staging/media/ipu3/include/intel-ipu3.h
index ec0b74829351..1e7184e4311d 100644
--- a/drivers/staging/media/ipu3/include/intel-ipu3.h
+++ b/drivers/staging/media/ipu3/include/intel-ipu3.h
@@ -16,12 +16,6 @@
#define V4L2_CID_INTEL_IPU3_BASE (V4L2_CID_USER_BASE + 0x10c0)
#define V4L2_CID_INTEL_IPU3_MODE (V4L2_CID_INTEL_IPU3_BASE + 1)
-/* custom ctrl to set pipe mode */
-enum ipu3_running_mode {
- IPU3_RUNNING_MODE_VIDEO = 0,
- IPU3_RUNNING_MODE_STILL = 1,
-};
-
/******************* ipu3_uapi_stats_3a *******************/
#define IPU3_UAPI_MAX_STRIPES 2
@@ -438,11 +432,11 @@ struct ipu3_uapi_awb_fr_raw_buffer {
*
* @grid_cfg: grid config, default 16x16.
* @bayer_coeff: 1D Filter 1x11 center symmetry/anti-symmetry.
- * coeffcients defaults { 0, 0, 0, 0, 0, 128 }.
+ * coefficients defaults { 0, 0, 0, 0, 0, 128 }.
* Applied on whole image for each Bayer channel separately
* by a weighted sum of its 11x1 neighbors.
* @reserved1: reserved
- * @bayer_sign: sign of filter coeffcients, default 0.
+ * @bayer_sign: sign of filter coefficients, default 0.
* @bayer_nf: normalization factor for the convolution coeffs, to make sure
* total memory needed is within pre-determined range.
* NF should be the log2 of the sum of the abs values of the
diff --git a/drivers/staging/media/ipu3/ipu3-abi.h b/drivers/staging/media/ipu3/ipu3-abi.h
index 25be56ff01c8..e1185602c7fd 100644
--- a/drivers/staging/media/ipu3/ipu3-abi.h
+++ b/drivers/staging/media/ipu3/ipu3-abi.h
@@ -1510,7 +1510,7 @@ struct imgu_abi_blob_info {
/* offset wrt hdr in bytes */
u32 prog_name_offset; /* offset wrt hdr in bytes */
u32 size; /* Size of blob */
- u32 padding_size; /* total cummulative of bytes added
+ u32 padding_size; /* total cumulative of bytes added
* due to section alignment
*/
u32 icache_source; /* Position of icache in blob */
diff --git a/drivers/staging/media/ipu3/ipu3-css-fw.c b/drivers/staging/media/ipu3/ipu3-css-fw.c
index 55861aa8fb03..4122d4e42db6 100644
--- a/drivers/staging/media/ipu3/ipu3-css-fw.c
+++ b/drivers/staging/media/ipu3/ipu3-css-fw.c
@@ -10,7 +10,7 @@
#include "ipu3-css-fw.h"
#include "ipu3-dmamap.h"
-static void ipu3_css_fw_show_binary(struct device *dev, struct imgu_fw_info *bi,
+static void imgu_css_fw_show_binary(struct device *dev, struct imgu_fw_info *bi,
const char *name)
{
unsigned int i;
@@ -54,7 +54,7 @@ static void ipu3_css_fw_show_binary(struct device *dev, struct imgu_fw_info *bi,
dev_dbg(dev, "\n");
}
-unsigned int ipu3_css_fw_obgrid_size(const struct imgu_fw_info *bi)
+unsigned int imgu_css_fw_obgrid_size(const struct imgu_fw_info *bi)
{
unsigned int width = DIV_ROUND_UP(bi->info.isp.sp.internal.max_width,
IMGU_OBGRID_TILE_SIZE * 2) + 1;
@@ -69,7 +69,7 @@ unsigned int ipu3_css_fw_obgrid_size(const struct imgu_fw_info *bi)
return obgrid_size;
}
-void *ipu3_css_fw_pipeline_params(struct ipu3_css *css, unsigned int pipe,
+void *imgu_css_fw_pipeline_params(struct imgu_css *css, unsigned int pipe,
enum imgu_abi_param_class cls,
enum imgu_abi_memories mem,
struct imgu_fw_isp_parameter *par,
@@ -91,7 +91,7 @@ void *ipu3_css_fw_pipeline_params(struct ipu3_css *css, unsigned int pipe,
return binary_params + par->offset;
}
-void ipu3_css_fw_cleanup(struct ipu3_css *css)
+void imgu_css_fw_cleanup(struct imgu_css *css)
{
struct imgu_device *imgu = dev_get_drvdata(css->dev);
@@ -99,7 +99,7 @@ void ipu3_css_fw_cleanup(struct ipu3_css *css)
unsigned int i;
for (i = 0; i < css->fwp->file_header.binary_nr; i++)
- ipu3_dmamap_free(imgu, &css->binary[i]);
+ imgu_dmamap_free(imgu, &css->binary[i]);
kfree(css->binary);
}
if (css->fw)
@@ -109,7 +109,7 @@ void ipu3_css_fw_cleanup(struct ipu3_css *css)
css->fw = NULL;
}
-int ipu3_css_fw_init(struct ipu3_css *css)
+int imgu_css_fw_init(struct imgu_css *css)
{
static const u32 BLOCK_MAX = 65536;
struct imgu_device *imgu = dev_get_drvdata(css->dev);
@@ -227,7 +227,7 @@ int ipu3_css_fw_init(struct ipu3_css *css)
css->fw->size)
goto bad_fw;
- ipu3_css_fw_show_binary(dev, bi, name);
+ imgu_css_fw_show_binary(dev, bi, name);
}
if (css->fw_bl == -1 || css->fw_sp[0] == -1 || css->fw_sp[1] == -1)
@@ -246,7 +246,7 @@ int ipu3_css_fw_init(struct ipu3_css *css)
void *blob = (void *)css->fwp + bi->blob.offset;
size_t size = bi->blob.size;
- if (!ipu3_dmamap_alloc(imgu, &css->binary[i], size)) {
+ if (!imgu_dmamap_alloc(imgu, &css->binary[i], size)) {
r = -ENOMEM;
goto error_out;
}
@@ -260,6 +260,6 @@ bad_fw:
r = -ENODEV;
error_out:
- ipu3_css_fw_cleanup(css);
+ imgu_css_fw_cleanup(css);
return r;
}
diff --git a/drivers/staging/media/ipu3/ipu3-css-fw.h b/drivers/staging/media/ipu3/ipu3-css-fw.h
index 07d8bb8b25f3..79ffa7045139 100644
--- a/drivers/staging/media/ipu3/ipu3-css-fw.h
+++ b/drivers/staging/media/ipu3/ipu3-css-fw.h
@@ -175,11 +175,11 @@ struct imgu_fw_header {
/******************* Firmware functions *******************/
-int ipu3_css_fw_init(struct ipu3_css *css);
-void ipu3_css_fw_cleanup(struct ipu3_css *css);
+int imgu_css_fw_init(struct imgu_css *css);
+void imgu_css_fw_cleanup(struct imgu_css *css);
-unsigned int ipu3_css_fw_obgrid_size(const struct imgu_fw_info *bi);
-void *ipu3_css_fw_pipeline_params(struct ipu3_css *css, unsigned int pipe,
+unsigned int imgu_css_fw_obgrid_size(const struct imgu_fw_info *bi);
+void *imgu_css_fw_pipeline_params(struct imgu_css *css, unsigned int pipe,
enum imgu_abi_param_class cls,
enum imgu_abi_memories mem,
struct imgu_fw_isp_parameter *par,
diff --git a/drivers/staging/media/ipu3/ipu3-css-params.c b/drivers/staging/media/ipu3/ipu3-css-params.c
index 776206ded83b..4533dacad4be 100644
--- a/drivers/staging/media/ipu3/ipu3-css-params.c
+++ b/drivers/staging/media/ipu3/ipu3-css-params.c
@@ -6,6 +6,7 @@
#include "ipu3-css.h"
#include "ipu3-css-fw.h"
#include "ipu3-tables.h"
+#include "ipu3-css-params.h"
#define DIV_ROUND_CLOSEST_DOWN(a, b) (((a) + ((b) / 2) - 1) / (b))
#define roundclosest_down(a, b) (DIV_ROUND_CLOSEST_DOWN(a, b) * (b))
@@ -13,7 +14,7 @@
#define IPU3_UAPI_ANR_MAX_RESET ((1 << 12) - 1)
#define IPU3_UAPI_ANR_MIN_RESET (((-1) << 12) + 1)
-struct ipu3_css_scaler_info {
+struct imgu_css_scaler_info {
unsigned int phase_step; /* Same for luma/chroma */
int exp_shift;
@@ -24,7 +25,7 @@ struct ipu3_css_scaler_info {
int crop_top;
};
-static unsigned int ipu3_css_scaler_get_exp(unsigned int counter,
+static unsigned int imgu_css_scaler_get_exp(unsigned int counter,
unsigned int divider)
{
int i = fls(divider) - fls(counter);
@@ -40,13 +41,13 @@ static unsigned int ipu3_css_scaler_get_exp(unsigned int counter,
/* Set up the CSS scaler look up table */
static void
-ipu3_css_scaler_setup_lut(unsigned int taps, unsigned int input_width,
+imgu_css_scaler_setup_lut(unsigned int taps, unsigned int input_width,
unsigned int output_width, int phase_step_correction,
const int *coeffs, unsigned int coeffs_size,
- s8 coeff_lut[], struct ipu3_css_scaler_info *info)
+ s8 coeff_lut[], struct imgu_css_scaler_info *info)
{
int tap, phase, phase_sum_left, phase_sum_right;
- int exponent = ipu3_css_scaler_get_exp(output_width, input_width);
+ int exponent = imgu_css_scaler_get_exp(output_width, input_width);
int mantissa = (1 << exponent) * output_width;
unsigned int phase_step;
@@ -113,8 +114,8 @@ ipu3_css_scaler_setup_lut(unsigned int taps, unsigned int input_width,
* (must be perfectly aligned with hardware).
*/
static unsigned int
-ipu3_css_scaler_calc_scaled_output(unsigned int input,
- struct ipu3_css_scaler_info *info)
+imgu_css_scaler_calc_scaled_output(unsigned int input,
+ struct imgu_css_scaler_info *info)
{
unsigned int arg1 = input * info->phase_step +
(1 - IMGU_SCALER_TAPS_Y / 2) * IMGU_SCALER_FIR_PHASES -
@@ -132,10 +133,10 @@ ipu3_css_scaler_calc_scaled_output(unsigned int input,
* and chroma details of a scaler
*/
static void
-ipu3_css_scaler_calc(u32 input_width, u32 input_height, u32 target_width,
+imgu_css_scaler_calc(u32 input_width, u32 input_height, u32 target_width,
u32 target_height, struct imgu_abi_osys_config *cfg,
- struct ipu3_css_scaler_info *info_luma,
- struct ipu3_css_scaler_info *info_chroma,
+ struct imgu_css_scaler_info *info_luma,
+ struct imgu_css_scaler_info *info_chroma,
unsigned int *output_width, unsigned int *output_height,
unsigned int *procmode)
{
@@ -164,24 +165,24 @@ ipu3_css_scaler_calc(u32 input_width, u32 input_height, u32 target_width,
do {
phase_step_correction++;
- ipu3_css_scaler_setup_lut(IMGU_SCALER_TAPS_Y,
+ imgu_css_scaler_setup_lut(IMGU_SCALER_TAPS_Y,
input_width, target_width,
phase_step_correction,
- ipu3_css_downscale_4taps,
+ imgu_css_downscale_4taps,
IMGU_SCALER_DOWNSCALE_4TAPS_LEN,
cfg->scaler_coeffs_luma, info_luma);
- ipu3_css_scaler_setup_lut(IMGU_SCALER_TAPS_UV,
+ imgu_css_scaler_setup_lut(IMGU_SCALER_TAPS_UV,
input_width, target_width,
phase_step_correction,
- ipu3_css_downscale_2taps,
+ imgu_css_downscale_2taps,
IMGU_SCALER_DOWNSCALE_2TAPS_LEN,
cfg->scaler_coeffs_chroma,
info_chroma);
- out_width = ipu3_css_scaler_calc_scaled_output(input_width,
+ out_width = imgu_css_scaler_calc_scaled_output(input_width,
info_luma);
- out_height = ipu3_css_scaler_calc_scaled_output(input_height,
+ out_height = imgu_css_scaler_calc_scaled_output(input_height,
info_luma);
} while ((out_width < target_width || out_height < target_height ||
!IS_ALIGNED(out_height, height_alignment)) &&
@@ -193,7 +194,7 @@ ipu3_css_scaler_calc(u32 input_width, u32 input_height, u32 target_width,
/********************** Osys routines for scaler****************************/
-static void ipu3_css_osys_set_format(enum imgu_abi_frame_format host_format,
+static void imgu_css_osys_set_format(enum imgu_abi_frame_format host_format,
unsigned int *osys_format,
unsigned int *osys_tiling)
{
@@ -230,7 +231,7 @@ static void ipu3_css_osys_set_format(enum imgu_abi_frame_format host_format,
* Function calculates input frame stripe offset, based
* on output frame stripe offset and filter parameters.
*/
-static int ipu3_css_osys_calc_stripe_offset(int stripe_offset_out,
+static int imgu_css_osys_calc_stripe_offset(int stripe_offset_out,
int fir_phases, int phase_init,
int phase_step, int pad_left)
{
@@ -244,12 +245,12 @@ static int ipu3_css_osys_calc_stripe_offset(int stripe_offset_out,
* Calculate input frame phase, given the output frame
* stripe offset and filter parameters
*/
-static int ipu3_css_osys_calc_stripe_phase_init(int stripe_offset_out,
+static int imgu_css_osys_calc_stripe_phase_init(int stripe_offset_out,
int fir_phases, int phase_init,
int phase_step, int pad_left)
{
int stripe_offset_inp =
- ipu3_css_osys_calc_stripe_offset(stripe_offset_out,
+ imgu_css_osys_calc_stripe_offset(stripe_offset_out,
fir_phases, phase_init,
phase_step, pad_left);
@@ -261,7 +262,7 @@ static int ipu3_css_osys_calc_stripe_phase_init(int stripe_offset_out,
* This function calculates input frame stripe width,
* based on output frame stripe offset and filter parameters
*/
-static int ipu3_css_osys_calc_inp_stripe_width(int stripe_width_out,
+static int imgu_css_osys_calc_inp_stripe_width(int stripe_width_out,
int fir_phases, int phase_init,
int phase_step, int fir_taps,
int pad_left, int pad_right)
@@ -278,7 +279,7 @@ static int ipu3_css_osys_calc_inp_stripe_width(int stripe_width_out,
* This function calculates output frame stripe width, basedi
* on output frame stripe offset and filter parameters
*/
-static int ipu3_css_osys_out_stripe_width(int stripe_width_inp, int fir_phases,
+static int imgu_css_osys_out_stripe_width(int stripe_width_inp, int fir_phases,
int phase_init, int phase_step,
int fir_taps, int pad_left,
int pad_right, int column_offset)
@@ -291,7 +292,7 @@ static int ipu3_css_osys_out_stripe_width(int stripe_width_inp, int fir_phases,
return stripe_width_out - (fir_taps - 1);
}
-struct ipu3_css_reso {
+struct imgu_css_reso {
unsigned int input_width;
unsigned int input_height;
enum imgu_abi_frame_format input_format;
@@ -305,7 +306,7 @@ struct ipu3_css_reso {
int block_width;
};
-struct ipu3_css_frame_params {
+struct imgu_css_frame_params {
/* Output pins */
unsigned int enable;
unsigned int format;
@@ -321,7 +322,7 @@ struct ipu3_css_frame_params {
unsigned int crop_top;
};
-struct ipu3_css_stripe_params {
+struct imgu_css_stripe_params {
unsigned int processing_mode;
unsigned int phase_step;
unsigned int exp_shift;
@@ -358,20 +359,20 @@ struct ipu3_css_stripe_params {
* frame_params - size IMGU_ABI_OSYS_PINS
* stripe_params - size IPU3_UAPI_MAX_STRIPES
*/
-static int ipu3_css_osys_calc_frame_and_stripe_params(
- struct ipu3_css *css, unsigned int stripes,
+static int imgu_css_osys_calc_frame_and_stripe_params(
+ struct imgu_css *css, unsigned int stripes,
struct imgu_abi_osys_config *osys,
- struct ipu3_css_scaler_info *scaler_luma,
- struct ipu3_css_scaler_info *scaler_chroma,
- struct ipu3_css_frame_params frame_params[],
- struct ipu3_css_stripe_params stripe_params[],
+ struct imgu_css_scaler_info *scaler_luma,
+ struct imgu_css_scaler_info *scaler_chroma,
+ struct imgu_css_frame_params frame_params[],
+ struct imgu_css_stripe_params stripe_params[],
unsigned int pipe)
{
- struct ipu3_css_reso reso;
+ struct imgu_css_reso reso;
unsigned int output_width, pin, s;
u32 input_width, input_height, target_width, target_height;
unsigned int procmode = 0;
- struct ipu3_css_pipe *css_pipe = &css->pipes[pipe];
+ struct imgu_css_pipe *css_pipe = &css->pipes[pipe];
input_width = css_pipe->rect[IPU3_CSS_RECT_GDC].width;
input_height = css_pipe->rect[IPU3_CSS_RECT_GDC].height;
@@ -463,7 +464,7 @@ static int ipu3_css_osys_calc_frame_and_stripe_params(
scaled = 1;
}
}
- ipu3_css_osys_set_format(reso.pin_format[pin], &format,
+ imgu_css_osys_set_format(reso.pin_format[pin], &format,
&tiling);
} else {
enable = 0;
@@ -475,7 +476,7 @@ static int ipu3_css_osys_calc_frame_and_stripe_params(
frame_params[pin].scaled = scaled;
}
- ipu3_css_scaler_calc(input_width, input_height, target_width,
+ imgu_css_scaler_calc(input_width, input_height, target_width,
target_height, osys, scaler_luma, scaler_chroma,
&reso.pin_width[IMGU_ABI_OSYS_PIN_VF],
&reso.pin_height[IMGU_ABI_OSYS_PIN_VF], &procmode);
@@ -580,14 +581,14 @@ static int ipu3_css_osys_calc_frame_and_stripe_params(
stripe_offset_out_uv = stripe_offset_out_y /
IMGU_LUMA_TO_CHROMA_RATIO;
stripe_offset_inp_y =
- ipu3_css_osys_calc_stripe_offset(
+ imgu_css_osys_calc_stripe_offset(
stripe_offset_out_y,
IMGU_OSYS_FIR_PHASES,
scaler_luma->phase_init,
scaler_luma->phase_step,
scaler_luma->pad_left);
stripe_offset_inp_uv =
- ipu3_css_osys_calc_stripe_offset(
+ imgu_css_osys_calc_stripe_offset(
stripe_offset_out_uv,
IMGU_OSYS_FIR_PHASES,
scaler_chroma->phase_init,
@@ -596,14 +597,14 @@ static int ipu3_css_osys_calc_frame_and_stripe_params(
/* Calculate stripe phase init */
stripe_phase_init_y =
- ipu3_css_osys_calc_stripe_phase_init(
+ imgu_css_osys_calc_stripe_phase_init(
stripe_offset_out_y,
IMGU_OSYS_FIR_PHASES,
scaler_luma->phase_init,
scaler_luma->phase_step,
scaler_luma->pad_left);
stripe_phase_init_uv =
- ipu3_css_osys_calc_stripe_phase_init(
+ imgu_css_osys_calc_stripe_phase_init(
stripe_offset_out_uv,
IMGU_OSYS_FIR_PHASES,
scaler_chroma->phase_init,
@@ -707,7 +708,7 @@ static int ipu3_css_osys_calc_frame_and_stripe_params(
IMGU_LUMA_TO_CHROMA_RATIO;
/* Calculate input stripe width */
stripe_input_width_y = stripe_offset_col_y +
- ipu3_css_osys_calc_inp_stripe_width(
+ imgu_css_osys_calc_inp_stripe_width(
stripe_output_width_y,
IMGU_OSYS_FIR_PHASES,
stripe_phase_init_y,
@@ -717,7 +718,7 @@ static int ipu3_css_osys_calc_frame_and_stripe_params(
stripe_pad_right_y);
stripe_input_width_uv = stripe_offset_col_uv +
- ipu3_css_osys_calc_inp_stripe_width(
+ imgu_css_osys_calc_inp_stripe_width(
stripe_output_width_uv,
IMGU_OSYS_FIR_PHASES,
stripe_phase_init_uv,
@@ -752,7 +753,7 @@ static int ipu3_css_osys_calc_frame_and_stripe_params(
*/
stripe_input_width_y = ALIGN(stripe_input_width_y, 8);
stripe_output_width_y =
- ipu3_css_osys_out_stripe_width(
+ imgu_css_osys_out_stripe_width(
stripe_input_width_y,
IMGU_OSYS_FIR_PHASES,
stripe_phase_init_y,
@@ -846,23 +847,23 @@ static int ipu3_css_osys_calc_frame_and_stripe_params(
* This function configures the Output Formatter System, given the number of
* stripes, scaler luma and chrome parameters
*/
-static int ipu3_css_osys_calc(struct ipu3_css *css, unsigned int pipe,
+static int imgu_css_osys_calc(struct imgu_css *css, unsigned int pipe,
unsigned int stripes,
struct imgu_abi_osys_config *osys,
- struct ipu3_css_scaler_info *scaler_luma,
- struct ipu3_css_scaler_info *scaler_chroma,
+ struct imgu_css_scaler_info *scaler_luma,
+ struct imgu_css_scaler_info *scaler_chroma,
struct imgu_abi_stripes block_stripes[])
{
- struct ipu3_css_frame_params frame_params[IMGU_ABI_OSYS_PINS];
- struct ipu3_css_stripe_params stripe_params[IPU3_UAPI_MAX_STRIPES];
+ struct imgu_css_frame_params frame_params[IMGU_ABI_OSYS_PINS];
+ struct imgu_css_stripe_params stripe_params[IPU3_UAPI_MAX_STRIPES];
struct imgu_abi_osys_formatter_params *param;
unsigned int pin, s;
- struct ipu3_css_pipe *css_pipe = &css->pipes[pipe];
+ struct imgu_css_pipe *css_pipe = &css->pipes[pipe];
memset(osys, 0, sizeof(*osys));
/* Compute the frame and stripe params */
- if (ipu3_css_osys_calc_frame_and_stripe_params(css, stripes, osys,
+ if (imgu_css_osys_calc_frame_and_stripe_params(css, stripes, osys,
scaler_luma,
scaler_chroma,
frame_params,
@@ -1251,7 +1252,7 @@ static int ipu3_css_osys_calc(struct ipu3_css *css, unsigned int pipe,
*/
static int
-ipu3_css_shd_ops_calc(struct imgu_abi_shd_intra_frame_operations_data *ops,
+imgu_css_shd_ops_calc(struct imgu_abi_shd_intra_frame_operations_data *ops,
const struct ipu3_uapi_shd_grid_config *grid,
unsigned int image_height)
{
@@ -1495,7 +1496,7 @@ struct process_lines {
/* Helper to config intra_frame_operations_data. */
static int
-ipu3_css_acc_process_lines(const struct process_lines *pl,
+imgu_css_acc_process_lines(const struct process_lines *pl,
struct imgu_abi_acc_operation *p_op,
struct imgu_abi_acc_process_lines_cmd_data *p_pl,
struct imgu_abi_acc_transfer_op_data *p_tr)
@@ -1632,12 +1633,12 @@ ipu3_css_acc_process_lines(const struct process_lines *pl,
return 0;
}
-static int ipu3_css_af_ops_calc(struct ipu3_css *css, unsigned int pipe,
+static int imgu_css_af_ops_calc(struct imgu_css *css, unsigned int pipe,
struct imgu_abi_af_config *af_config)
{
struct imgu_abi_af_intra_frame_operations_data *to =
&af_config->operations_data;
- struct ipu3_css_pipe *css_pipe = &css->pipes[pipe];
+ struct imgu_css_pipe *css_pipe = &css->pipes[pipe];
struct imgu_fw_info *bi =
&css->fwp->binary_header[css_pipe->bindex];
@@ -1655,17 +1656,17 @@ static int ipu3_css_af_ops_calc(struct ipu3_css *css, unsigned int pipe,
.acc_enable = bi->info.isp.sp.enable.af,
};
- return ipu3_css_acc_process_lines(&pl, to->ops, to->process_lines_data,
+ return imgu_css_acc_process_lines(&pl, to->ops, to->process_lines_data,
NULL);
}
static int
-ipu3_css_awb_fr_ops_calc(struct ipu3_css *css, unsigned int pipe,
+imgu_css_awb_fr_ops_calc(struct imgu_css *css, unsigned int pipe,
struct imgu_abi_awb_fr_config *awb_fr_config)
{
struct imgu_abi_awb_fr_intra_frame_operations_data *to =
&awb_fr_config->operations_data;
- struct ipu3_css_pipe *css_pipe = &css->pipes[pipe];
+ struct imgu_css_pipe *css_pipe = &css->pipes[pipe];
struct imgu_fw_info *bi =
&css->fwp->binary_header[css_pipe->bindex];
struct process_lines pl = {
@@ -1682,16 +1683,16 @@ ipu3_css_awb_fr_ops_calc(struct ipu3_css *css, unsigned int pipe,
.acc_enable = bi->info.isp.sp.enable.awb_fr_acc,
};
- return ipu3_css_acc_process_lines(&pl, to->ops, to->process_lines_data,
+ return imgu_css_acc_process_lines(&pl, to->ops, to->process_lines_data,
NULL);
}
-static int ipu3_css_awb_ops_calc(struct ipu3_css *css, unsigned int pipe,
+static int imgu_css_awb_ops_calc(struct imgu_css *css, unsigned int pipe,
struct imgu_abi_awb_config *awb_config)
{
struct imgu_abi_awb_intra_frame_operations_data *to =
&awb_config->operations_data;
- struct ipu3_css_pipe *css_pipe = &css->pipes[pipe];
+ struct imgu_css_pipe *css_pipe = &css->pipes[pipe];
struct imgu_fw_info *bi =
&css->fwp->binary_header[css_pipe->bindex];
@@ -1708,33 +1709,33 @@ static int ipu3_css_awb_ops_calc(struct ipu3_css *css, unsigned int pipe,
.acc_enable = bi->info.isp.sp.enable.awb_acc,
};
- return ipu3_css_acc_process_lines(&pl, to->ops, to->process_lines_data,
+ return imgu_css_acc_process_lines(&pl, to->ops, to->process_lines_data,
to->transfer_data);
}
-static u16 ipu3_css_grid_end(u16 start, u8 width, u8 block_width_log2)
+static u16 imgu_css_grid_end(u16 start, u8 width, u8 block_width_log2)
{
return (start & IPU3_UAPI_GRID_START_MASK) +
(width << block_width_log2) - 1;
}
-static void ipu3_css_grid_end_calc(struct ipu3_uapi_grid_config *grid_cfg)
+static void imgu_css_grid_end_calc(struct ipu3_uapi_grid_config *grid_cfg)
{
- grid_cfg->x_end = ipu3_css_grid_end(grid_cfg->x_start, grid_cfg->width,
+ grid_cfg->x_end = imgu_css_grid_end(grid_cfg->x_start, grid_cfg->width,
grid_cfg->block_width_log2);
- grid_cfg->y_end = ipu3_css_grid_end(grid_cfg->y_start, grid_cfg->height,
+ grid_cfg->y_end = imgu_css_grid_end(grid_cfg->y_start, grid_cfg->height,
grid_cfg->block_height_log2);
}
/****************** config computation *****************************/
-static int ipu3_css_cfg_acc_stripe(struct ipu3_css *css, unsigned int pipe,
+static int imgu_css_cfg_acc_stripe(struct imgu_css *css, unsigned int pipe,
struct imgu_abi_acc_param *acc)
{
- struct ipu3_css_pipe *css_pipe = &css->pipes[pipe];
+ struct imgu_css_pipe *css_pipe = &css->pipes[pipe];
const struct imgu_fw_info *bi =
&css->fwp->binary_header[css_pipe->bindex];
- struct ipu3_css_scaler_info scaler_luma, scaler_chroma;
+ struct imgu_css_scaler_info scaler_luma, scaler_chroma;
const unsigned int stripes = bi->info.isp.sp.iterator.num_stripes;
const unsigned int f = IPU3_UAPI_ISP_VEC_ELEMS * 2;
unsigned int bds_ds, i;
@@ -1743,7 +1744,7 @@ static int ipu3_css_cfg_acc_stripe(struct ipu3_css *css, unsigned int pipe,
/* acc_param: osys_config */
- if (ipu3_css_osys_calc(css, pipe, stripes, &acc->osys, &scaler_luma,
+ if (imgu_css_osys_calc(css, pipe, stripes, &acc->osys, &scaler_luma,
&scaler_chroma, acc->stripe.block_stripes))
return -EINVAL;
@@ -1900,12 +1901,12 @@ static int ipu3_css_cfg_acc_stripe(struct ipu3_css *css, unsigned int pipe,
return 0;
}
-static void ipu3_css_cfg_acc_dvs(struct ipu3_css *css,
+static void imgu_css_cfg_acc_dvs(struct imgu_css *css,
struct imgu_abi_acc_param *acc,
unsigned int pipe)
{
unsigned int i;
- struct ipu3_css_pipe *css_pipe = &css->pipes[pipe];
+ struct imgu_css_pipe *css_pipe = &css->pipes[pipe];
/* Disable DVS statistics */
acc->dvs_stat.operations_data.process_lines_data[0].lines =
@@ -1919,11 +1920,11 @@ static void ipu3_css_cfg_acc_dvs(struct ipu3_css *css,
acc->dvs_stat.cfg.grd_config[i].enable = 0;
}
-static void acc_bds_per_stripe_data(struct ipu3_css *css,
+static void acc_bds_per_stripe_data(struct imgu_css *css,
struct imgu_abi_acc_param *acc,
const int i, unsigned int pipe)
{
- struct ipu3_css_pipe *css_pipe = &css->pipes[pipe];
+ struct imgu_css_pipe *css_pipe = &css->pipes[pipe];
acc->bds.per_stripe.aligned_data[i].data.crop.hor_crop_en = 0;
acc->bds.per_stripe.aligned_data[i].data.crop.hor_crop_start = 0;
@@ -1944,13 +1945,13 @@ static void acc_bds_per_stripe_data(struct ipu3_css *css,
* telling which fields to take from the old values (or generate if it is NULL)
* and which to take from the new user values.
*/
-int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe,
+int imgu_css_cfg_acc(struct imgu_css *css, unsigned int pipe,
struct ipu3_uapi_flags *use,
struct imgu_abi_acc_param *acc,
struct imgu_abi_acc_param *acc_old,
struct ipu3_uapi_acc_param *acc_user)
{
- struct ipu3_css_pipe *css_pipe = &css->pipes[pipe];
+ struct imgu_css_pipe *css_pipe = &css->pipes[pipe];
const struct imgu_fw_info *bi =
&css->fwp->binary_header[css_pipe->bindex];
const unsigned int stripes = bi->info.isp.sp.iterator.num_stripes;
@@ -1959,7 +1960,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe,
const unsigned int min_overlap = 10;
const struct v4l2_pix_format_mplane *pixm =
&css_pipe->queue[IPU3_CSS_QUEUE_IN].fmt.mpix;
- const struct ipu3_css_bds_config *cfg_bds;
+ const struct imgu_css_bds_config *cfg_bds;
struct imgu_abi_input_feeder_data *feeder_data;
unsigned int bds_ds, ofs_x, ofs_y, i, width, height;
@@ -1967,7 +1968,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe,
/* Update stripe using chroma and luma */
- if (ipu3_css_cfg_acc_stripe(css, pipe, acc))
+ if (imgu_css_cfg_acc_stripe(css, pipe, acc))
return -EINVAL;
/* acc_param: input_feeder_config */
@@ -2021,7 +2022,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe,
acc->bnr = acc_old->bnr;
} else {
/* Calculate from scratch */
- acc->bnr = ipu3_css_bnr_defaults;
+ acc->bnr = imgu_css_bnr_defaults;
}
acc->bnr.column_size = tnr_frame_width;
@@ -2049,7 +2050,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe,
acc->dm = acc_old->dm;
} else {
/* Calculate from scratch */
- acc->dm = ipu3_css_dm_defaults;
+ acc->dm = imgu_css_dm_defaults;
}
acc->dm.frame_width = tnr_frame_width;
@@ -2064,7 +2065,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe,
acc->ccm = acc_old->ccm;
} else {
/* Calculate from scratch */
- acc->ccm = ipu3_css_ccm_defaults;
+ acc->ccm = imgu_css_ccm_defaults;
}
/* acc_param: gamma_config */
@@ -2078,7 +2079,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe,
} else {
/* Calculate from scratch */
acc->gamma.gc_ctrl.enable = 1;
- acc->gamma.gc_lut = ipu3_css_gamma_lut;
+ acc->gamma.gc_lut = imgu_css_gamma_lut;
}
/* acc_param: csc_mat_config */
@@ -2091,7 +2092,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe,
acc->csc = acc_old->csc;
} else {
/* Calculate from scratch */
- acc->csc = ipu3_css_csc_defaults;
+ acc->csc = imgu_css_csc_defaults;
}
/* acc_param: cds_params */
@@ -2104,7 +2105,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe,
acc->cds = acc_old->cds;
} else {
/* Calculate from scratch */
- acc->cds = ipu3_css_cds_defaults;
+ acc->cds = imgu_css_cds_defaults;
}
/* acc_param: shd_config */
@@ -2119,7 +2120,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe,
acc->shd.shd_lut = acc_old->shd.shd_lut;
} else {
/* Calculate from scratch */
- acc->shd.shd = ipu3_css_shd_defaults;
+ acc->shd.shd = imgu_css_shd_defaults;
memset(&acc->shd.shd_lut, 0, sizeof(acc->shd.shd_lut));
}
@@ -2137,12 +2138,12 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe,
acc->shd.shd.grid.block_height_log2) %
acc->shd.shd.grid.grid_height_per_slice;
- if (ipu3_css_shd_ops_calc(&acc->shd.shd_ops, &acc->shd.shd.grid,
+ if (imgu_css_shd_ops_calc(&acc->shd.shd_ops, &acc->shd.shd.grid,
css_pipe->rect[IPU3_CSS_RECT_BDS].height))
return -EINVAL;
/* acc_param: dvs_stat_config */
- ipu3_css_cfg_acc_dvs(css, acc, pipe);
+ imgu_css_cfg_acc_dvs(css, acc, pipe);
/* acc_param: yuvp1_iefd_config */
@@ -2154,7 +2155,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe,
acc->iefd = acc_old->iefd;
} else {
/* Calculate from scratch */
- acc->iefd = ipu3_css_iefd_defaults;
+ acc->iefd = imgu_css_iefd_defaults;
}
/* acc_param: yuvp1_yds_config yds_c0 */
@@ -2167,7 +2168,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe,
acc->yds_c0 = acc_old->yds_c0;
} else {
/* Calculate from scratch */
- acc->yds_c0 = ipu3_css_yds_defaults;
+ acc->yds_c0 = imgu_css_yds_defaults;
}
/* acc_param: yuvp1_chnr_config chnr_c0 */
@@ -2180,7 +2181,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe,
acc->chnr_c0 = acc_old->chnr_c0;
} else {
/* Calculate from scratch */
- acc->chnr_c0 = ipu3_css_chnr_defaults;
+ acc->chnr_c0 = imgu_css_chnr_defaults;
}
/* acc_param: yuvp1_y_ee_nr_config */
@@ -2193,7 +2194,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe,
acc->y_ee_nr = acc_old->y_ee_nr;
} else {
/* Calculate from scratch */
- acc->y_ee_nr = ipu3_css_y_ee_nr_defaults;
+ acc->y_ee_nr = imgu_css_y_ee_nr_defaults;
}
/* acc_param: yuvp1_yds_config yds */
@@ -2206,7 +2207,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe,
acc->yds = acc_old->yds;
} else {
/* Calculate from scratch */
- acc->yds = ipu3_css_yds_defaults;
+ acc->yds = imgu_css_yds_defaults;
}
/* acc_param: yuvp1_chnr_config chnr */
@@ -2219,7 +2220,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe,
acc->chnr = acc_old->chnr;
} else {
/* Calculate from scratch */
- acc->chnr = ipu3_css_chnr_defaults;
+ acc->chnr = imgu_css_chnr_defaults;
}
/* acc_param: yuvp2_y_tm_lut_static_config */
@@ -2238,7 +2239,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe,
acc->yds2 = acc_old->yds2;
} else {
/* Calculate from scratch */
- acc->yds2 = ipu3_css_yds_defaults;
+ acc->yds2 = imgu_css_yds_defaults;
}
/* acc_param: yuvp2_tcc_static_config */
@@ -2270,8 +2271,8 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe,
for (i = 7; i < IPU3_UAPI_YUVP2_TCC_INV_Y_LUT_ELEMENTS; i++)
acc->tcc.inv_y_lut.entries[i] = 1024 >> (i - 6);
- acc->tcc.gain_pcwl = ipu3_css_tcc_gain_pcwl_lut;
- acc->tcc.r_sqr_lut = ipu3_css_tcc_r_sqr_lut;
+ acc->tcc.gain_pcwl = imgu_css_tcc_gain_pcwl_lut;
+ acc->tcc.r_sqr_lut = imgu_css_tcc_r_sqr_lut;
}
/* acc_param: dpc_config */
@@ -2287,10 +2288,10 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe,
bds_ds = (css_pipe->rect[IPU3_CSS_RECT_EFFECTIVE].height *
IMGU_BDS_GRANULARITY) / css_pipe->rect[IPU3_CSS_RECT_BDS].height;
if (bds_ds < IMGU_BDS_MIN_SF_INV ||
- bds_ds - IMGU_BDS_MIN_SF_INV >= ARRAY_SIZE(ipu3_css_bds_configs))
+ bds_ds - IMGU_BDS_MIN_SF_INV >= ARRAY_SIZE(imgu_css_bds_configs))
return -EINVAL;
- cfg_bds = &ipu3_css_bds_configs[bds_ds - IMGU_BDS_MIN_SF_INV];
+ cfg_bds = &imgu_css_bds_configs[bds_ds - IMGU_BDS_MIN_SF_INV];
acc->bds.hor.hor_ctrl1.hor_crop_en = 0;
acc->bds.hor.hor_ctrl1.hor_crop_start = 0;
acc->bds.hor.hor_ctrl1.hor_crop_end = 0;
@@ -2339,7 +2340,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe,
sizeof(acc->anr.stitch.pyramid));
} else {
/* Calculate from scratch */
- acc->anr = ipu3_css_anr_defaults;
+ acc->anr = imgu_css_anr_defaults;
}
/* Always enabled */
@@ -2377,10 +2378,10 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe,
acc->awb_fr.config = acc_old->awb_fr.config;
} else {
/* Set from scratch */
- acc->awb_fr.config = ipu3_css_awb_fr_defaults;
+ acc->awb_fr.config = imgu_css_awb_fr_defaults;
}
- ipu3_css_grid_end_calc(&acc->awb_fr.config.grid_cfg);
+ imgu_css_grid_end_calc(&acc->awb_fr.config.grid_cfg);
if (acc->awb_fr.config.grid_cfg.width <= 0)
return -EINVAL;
@@ -2415,7 +2416,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe,
acc->awb_fr.stripes[0].grid_cfg.width;
b_w_log2 = acc->awb_fr.stripes[0].grid_cfg.block_width_log2;
- end = ipu3_css_grid_end(acc->awb_fr.stripes[0].grid_cfg.x_start,
+ end = imgu_css_grid_end(acc->awb_fr.stripes[0].grid_cfg.x_start,
acc->awb_fr.stripes[0].grid_cfg.width,
b_w_log2);
acc->awb_fr.stripes[0].grid_cfg.x_end = end;
@@ -2425,7 +2426,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe,
acc->stripe.down_scaled_stripes[1].offset) &
IPU3_UAPI_GRID_START_MASK;
b_w_log2 = acc->awb_fr.stripes[1].grid_cfg.block_width_log2;
- end = ipu3_css_grid_end(acc->awb_fr.stripes[1].grid_cfg.x_start,
+ end = imgu_css_grid_end(acc->awb_fr.stripes[1].grid_cfg.x_start,
acc->awb_fr.stripes[1].grid_cfg.width,
b_w_log2);
acc->awb_fr.stripes[1].grid_cfg.x_end = end;
@@ -2439,7 +2440,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe,
acc->awb_fr.stripes[i].grid_cfg.height_per_slice = 1;
}
- if (ipu3_css_awb_fr_ops_calc(css, pipe, &acc->awb_fr))
+ if (imgu_css_awb_fr_ops_calc(css, pipe, &acc->awb_fr))
return -EINVAL;
/* acc_param: ae_config */
@@ -2461,18 +2462,18 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe,
static const struct ipu3_uapi_ae_weight_elem
weight_def = { 1, 1, 1, 1, 1, 1, 1, 1 };
- acc->ae.grid_cfg = ipu3_css_ae_grid_defaults;
- acc->ae.ae_ccm = ipu3_css_ae_ccm_defaults;
+ acc->ae.grid_cfg = imgu_css_ae_grid_defaults;
+ acc->ae.ae_ccm = imgu_css_ae_ccm_defaults;
for (i = 0; i < IPU3_UAPI_AE_WEIGHTS; i++)
acc->ae.weights[i] = weight_def;
}
b_w_log2 = acc->ae.grid_cfg.block_width_log2;
- acc->ae.grid_cfg.x_end = ipu3_css_grid_end(acc->ae.grid_cfg.x_start,
+ acc->ae.grid_cfg.x_end = imgu_css_grid_end(acc->ae.grid_cfg.x_start,
acc->ae.grid_cfg.width,
b_w_log2);
b_w_log2 = acc->ae.grid_cfg.block_height_log2;
- acc->ae.grid_cfg.y_end = ipu3_css_grid_end(acc->ae.grid_cfg.y_start,
+ acc->ae.grid_cfg.y_end = imgu_css_grid_end(acc->ae.grid_cfg.y_start,
acc->ae.grid_cfg.height,
b_w_log2);
@@ -2501,7 +2502,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe,
b_w_log2 = acc->ae.stripes[0].grid.block_width_log2;
acc->ae.stripes[0].grid.x_end =
- ipu3_css_grid_end(acc->ae.stripes[0].grid.x_start,
+ imgu_css_grid_end(acc->ae.stripes[0].grid.x_start,
acc->ae.stripes[0].grid.width,
b_w_log2);
@@ -2511,7 +2512,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe,
IPU3_UAPI_GRID_START_MASK;
b_w_log2 = acc->ae.stripes[1].grid.block_width_log2;
acc->ae.stripes[1].grid.x_end =
- ipu3_css_grid_end(acc->ae.stripes[1].grid.x_start,
+ imgu_css_grid_end(acc->ae.stripes[1].grid.x_start,
acc->ae.stripes[1].grid.width,
b_w_log2);
}
@@ -2528,11 +2529,11 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe,
} else {
/* Set from scratch */
acc->af.config.filter_config =
- ipu3_css_af_defaults.filter_config;
- acc->af.config.grid_cfg = ipu3_css_af_defaults.grid_cfg;
+ imgu_css_af_defaults.filter_config;
+ acc->af.config.grid_cfg = imgu_css_af_defaults.grid_cfg;
}
- ipu3_css_grid_end_calc(&acc->af.config.grid_cfg);
+ imgu_css_grid_end_calc(&acc->af.config.grid_cfg);
if (acc->af.config.grid_cfg.width <= 0)
return -EINVAL;
@@ -2578,7 +2579,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe,
b_w_log2 = acc->af.stripes[0].grid_cfg.block_width_log2;
acc->af.stripes[0].grid_cfg.x_end =
- ipu3_css_grid_end(acc->af.stripes[0].grid_cfg.x_start,
+ imgu_css_grid_end(acc->af.stripes[0].grid_cfg.x_start,
acc->af.stripes[0].grid_cfg.width,
b_w_log2);
@@ -2589,7 +2590,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe,
b_w_log2 = acc->af.stripes[1].grid_cfg.block_width_log2;
acc->af.stripes[1].grid_cfg.x_end =
- ipu3_css_grid_end(acc->af.stripes[1].grid_cfg.x_start,
+ imgu_css_grid_end(acc->af.stripes[1].grid_cfg.x_start,
acc->af.stripes[1].grid_cfg.width,
b_w_log2);
@@ -2601,7 +2602,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe,
acc->af.stripes[i].grid_cfg.height_per_slice = 1;
}
- if (ipu3_css_af_ops_calc(css, pipe, &acc->af))
+ if (imgu_css_af_ops_calc(css, pipe, &acc->af))
return -EINVAL;
/* acc_param: awb_config */
@@ -2614,7 +2615,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe,
acc->awb.config = acc_old->awb.config;
} else {
/* Set from scratch */
- acc->awb.config = ipu3_css_awb_defaults;
+ acc->awb.config = imgu_css_awb_defaults;
}
if (acc->awb.config.grid.width <= 0)
@@ -2622,7 +2623,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe,
acc->awb.config.grid.height_per_slice =
IMGU_ABI_AWB_MAX_CELLS_PER_SET / acc->awb.config.grid.width,
- ipu3_css_grid_end_calc(&acc->awb.config.grid);
+ imgu_css_grid_end_calc(&acc->awb.config.grid);
for (i = 0; i < stripes; i++)
acc->awb.stripes[i] = acc->awb.config;
@@ -2647,7 +2648,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe,
b_w_log2 = acc->awb.stripes[0].grid.block_width_log2;
acc->awb.stripes[0].grid.x_end =
- ipu3_css_grid_end(acc->awb.stripes[0].grid.x_start,
+ imgu_css_grid_end(acc->awb.stripes[0].grid.x_start,
acc->awb.stripes[0].grid.width,
b_w_log2);
@@ -2658,7 +2659,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe,
b_w_log2 = acc->awb.stripes[1].grid.block_width_log2;
acc->awb.stripes[1].grid.x_end =
- ipu3_css_grid_end(acc->awb.stripes[1].grid.x_start,
+ imgu_css_grid_end(acc->awb.stripes[1].grid.x_start,
acc->awb.stripes[1].grid.width,
b_w_log2);
@@ -2670,7 +2671,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe,
acc->awb.stripes[i].grid.height_per_slice = 1;
}
- if (ipu3_css_awb_ops_calc(css, pipe, &acc->awb))
+ if (imgu_css_awb_ops_calc(css, pipe, &acc->awb))
return -EINVAL;
return 0;
@@ -2685,7 +2686,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe,
* to the structure inside `new_binary_params'. In that case the caller
* should calculate and fill the structure from scratch.
*/
-static void *ipu3_css_cfg_copy(struct ipu3_css *css,
+static void *imgu_css_cfg_copy(struct imgu_css *css,
unsigned int pipe, bool use_user,
void *user_setting, void *old_binary_params,
void *new_binary_params,
@@ -2696,7 +2697,7 @@ static void *ipu3_css_cfg_copy(struct ipu3_css *css,
const enum imgu_abi_param_class c = IMGU_ABI_PARAM_CLASS_PARAM;
void *new_setting, *old_setting;
- new_setting = ipu3_css_fw_pipeline_params(css, pipe, c, m, par,
+ new_setting = imgu_css_fw_pipeline_params(css, pipe, c, m, par,
par_size, new_binary_params);
if (!new_setting)
return ERR_PTR(-EPROTO); /* Corrupted firmware */
@@ -2706,7 +2707,7 @@ static void *ipu3_css_cfg_copy(struct ipu3_css *css,
memcpy(new_setting, user_setting, par_size);
} else if (old_binary_params) {
/* Take previous value */
- old_setting = ipu3_css_fw_pipeline_params(css, pipe, c, m, par,
+ old_setting = imgu_css_fw_pipeline_params(css, pipe, c, m, par,
par_size,
old_binary_params);
if (!old_setting)
@@ -2722,7 +2723,7 @@ static void *ipu3_css_cfg_copy(struct ipu3_css *css,
/*
* Configure VMEM0 parameters (late binding parameters).
*/
-int ipu3_css_cfg_vmem0(struct ipu3_css *css, unsigned int pipe,
+int imgu_css_cfg_vmem0(struct imgu_css *css, unsigned int pipe,
struct ipu3_uapi_flags *use,
void *vmem0, void *vmem0_old,
struct ipu3_uapi_params *user)
@@ -2744,7 +2745,7 @@ int ipu3_css_cfg_vmem0(struct ipu3_css *css, unsigned int pipe,
/* Configure Linearization VMEM0 parameters */
- lin_vmem = ipu3_css_cfg_copy(css, pipe, use && use->lin_vmem_params,
+ lin_vmem = imgu_css_cfg_copy(css, pipe, use && use->lin_vmem_params,
&user->lin_vmem_params, vmem0_old, vmem0,
m, &pofs->vmem.lin, sizeof(*lin_vmem));
if (!IS_ERR_OR_NULL(lin_vmem)) {
@@ -2764,7 +2765,7 @@ int ipu3_css_cfg_vmem0(struct ipu3_css *css, unsigned int pipe,
/* Configure TNR3 VMEM parameters */
if (css->pipes[pipe].pipe_id == IPU3_CSS_PIPE_ID_VIDEO) {
- tnr_vmem = ipu3_css_cfg_copy(css, pipe,
+ tnr_vmem = imgu_css_cfg_copy(css, pipe,
use && use->tnr3_vmem_params,
&user->tnr3_vmem_params,
vmem0_old, vmem0, m,
@@ -2780,17 +2781,17 @@ int ipu3_css_cfg_vmem0(struct ipu3_css *css, unsigned int pipe,
/* Configure XNR3 VMEM parameters */
- xnr_vmem = ipu3_css_cfg_copy(css, pipe, use && use->xnr3_vmem_params,
+ xnr_vmem = imgu_css_cfg_copy(css, pipe, use && use->xnr3_vmem_params,
&user->xnr3_vmem_params, vmem0_old, vmem0,
m, &pofs->vmem.xnr3, sizeof(*xnr_vmem));
if (!IS_ERR_OR_NULL(xnr_vmem)) {
- xnr_vmem->x[i] = ipu3_css_xnr3_vmem_defaults.x
+ xnr_vmem->x[i] = imgu_css_xnr3_vmem_defaults.x
[i % IMGU_XNR3_VMEM_LUT_LEN];
- xnr_vmem->a[i] = ipu3_css_xnr3_vmem_defaults.a
+ xnr_vmem->a[i] = imgu_css_xnr3_vmem_defaults.a
[i % IMGU_XNR3_VMEM_LUT_LEN];
- xnr_vmem->b[i] = ipu3_css_xnr3_vmem_defaults.b
+ xnr_vmem->b[i] = imgu_css_xnr3_vmem_defaults.b
[i % IMGU_XNR3_VMEM_LUT_LEN];
- xnr_vmem->c[i] = ipu3_css_xnr3_vmem_defaults.c
+ xnr_vmem->c[i] = imgu_css_xnr3_vmem_defaults.c
[i % IMGU_XNR3_VMEM_LUT_LEN];
}
@@ -2801,12 +2802,12 @@ int ipu3_css_cfg_vmem0(struct ipu3_css *css, unsigned int pipe,
/*
* Configure DMEM0 parameters (late binding parameters).
*/
-int ipu3_css_cfg_dmem0(struct ipu3_css *css, unsigned int pipe,
+int imgu_css_cfg_dmem0(struct imgu_css *css, unsigned int pipe,
struct ipu3_uapi_flags *use,
void *dmem0, void *dmem0_old,
struct ipu3_uapi_params *user)
{
- struct ipu3_css_pipe *css_pipe = &css->pipes[pipe];
+ struct imgu_css_pipe *css_pipe = &css->pipes[pipe];
const struct imgu_fw_info *bi =
&css->fwp->binary_header[css_pipe->bindex];
struct imgu_fw_param_memory_offsets *pofs = (void *)css->fwp +
@@ -2824,7 +2825,7 @@ int ipu3_css_cfg_dmem0(struct ipu3_css *css, unsigned int pipe,
/* Configure TNR3 DMEM0 parameters */
if (css_pipe->pipe_id == IPU3_CSS_PIPE_ID_VIDEO) {
- tnr_dmem = ipu3_css_cfg_copy(css, pipe,
+ tnr_dmem = imgu_css_cfg_copy(css, pipe,
use && use->tnr3_dmem_params,
&user->tnr3_dmem_params,
dmem0_old, dmem0, m,
@@ -2839,7 +2840,7 @@ int ipu3_css_cfg_dmem0(struct ipu3_css *css, unsigned int pipe,
/* Configure XNR3 DMEM0 parameters */
- xnr_dmem = ipu3_css_cfg_copy(css, pipe, use && use->xnr3_dmem_params,
+ xnr_dmem = imgu_css_cfg_copy(css, pipe, use && use->xnr3_dmem_params,
&user->xnr3_dmem_params, dmem0_old, dmem0,
m, &pofs->dmem.xnr3, sizeof(*xnr_dmem));
if (!IS_ERR_OR_NULL(xnr_dmem)) {
@@ -2853,7 +2854,7 @@ int ipu3_css_cfg_dmem0(struct ipu3_css *css, unsigned int pipe,
}
/* Generate unity morphing table without morphing effect */
-void ipu3_css_cfg_gdc_table(struct imgu_abi_gdc_warp_param *gdc,
+void imgu_css_cfg_gdc_table(struct imgu_abi_gdc_warp_param *gdc,
int frame_in_x, int frame_in_y,
int frame_out_x, int frame_out_y,
int env_w, int env_h)
diff --git a/drivers/staging/media/ipu3/ipu3-css-params.h b/drivers/staging/media/ipu3/ipu3-css-params.h
index f3a0a47117a4..ffaec6b7d5cc 100644
--- a/drivers/staging/media/ipu3/ipu3-css-params.h
+++ b/drivers/staging/media/ipu3/ipu3-css-params.h
@@ -4,23 +4,23 @@
#ifndef __IPU3_PARAMS_H
#define __IPU3_PARAMS_H
-int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe,
+int imgu_css_cfg_acc(struct imgu_css *css, unsigned int pipe,
struct ipu3_uapi_flags *use,
struct imgu_abi_acc_param *acc,
struct imgu_abi_acc_param *acc_old,
struct ipu3_uapi_acc_param *acc_user);
-int ipu3_css_cfg_vmem0(struct ipu3_css *css, unsigned int pipe,
+int imgu_css_cfg_vmem0(struct imgu_css *css, unsigned int pipe,
struct ipu3_uapi_flags *use,
void *vmem0, void *vmem0_old,
struct ipu3_uapi_params *user);
-int ipu3_css_cfg_dmem0(struct ipu3_css *css, unsigned int pipe,
+int imgu_css_cfg_dmem0(struct imgu_css *css, unsigned int pipe,
struct ipu3_uapi_flags *use,
void *dmem0, void *dmem0_old,
struct ipu3_uapi_params *user);
-void ipu3_css_cfg_gdc_table(struct imgu_abi_gdc_warp_param *gdc,
+void imgu_css_cfg_gdc_table(struct imgu_abi_gdc_warp_param *gdc,
int frame_in_x, int frame_in_y,
int frame_out_x, int frame_out_y,
int env_w, int env_h);
diff --git a/drivers/staging/media/ipu3/ipu3-css-pool.c b/drivers/staging/media/ipu3/ipu3-css-pool.c
index 6f271f81669b..fa5b7d3acef2 100644
--- a/drivers/staging/media/ipu3/ipu3-css-pool.c
+++ b/drivers/staging/media/ipu3/ipu3-css-pool.c
@@ -7,30 +7,30 @@
#include "ipu3-css-pool.h"
#include "ipu3-dmamap.h"
-int ipu3_css_dma_buffer_resize(struct imgu_device *imgu,
- struct ipu3_css_map *map, size_t size)
+int imgu_css_dma_buffer_resize(struct imgu_device *imgu,
+ struct imgu_css_map *map, size_t size)
{
if (map->size < size && map->vaddr) {
dev_warn(&imgu->pci_dev->dev, "dma buf resized from %zu to %zu",
map->size, size);
- ipu3_dmamap_free(imgu, map);
- if (!ipu3_dmamap_alloc(imgu, map, size))
+ imgu_dmamap_free(imgu, map);
+ if (!imgu_dmamap_alloc(imgu, map, size))
return -ENOMEM;
}
return 0;
}
-void ipu3_css_pool_cleanup(struct imgu_device *imgu, struct ipu3_css_pool *pool)
+void imgu_css_pool_cleanup(struct imgu_device *imgu, struct imgu_css_pool *pool)
{
unsigned int i;
for (i = 0; i < IPU3_CSS_POOL_SIZE; i++)
- ipu3_dmamap_free(imgu, &pool->entry[i].param);
+ imgu_dmamap_free(imgu, &pool->entry[i].param);
}
-int ipu3_css_pool_init(struct imgu_device *imgu, struct ipu3_css_pool *pool,
+int imgu_css_pool_init(struct imgu_device *imgu, struct imgu_css_pool *pool,
size_t size)
{
unsigned int i;
@@ -42,7 +42,7 @@ int ipu3_css_pool_init(struct imgu_device *imgu, struct ipu3_css_pool *pool,
continue;
}
- if (!ipu3_dmamap_alloc(imgu, &pool->entry[i].param, size))
+ if (!imgu_dmamap_alloc(imgu, &pool->entry[i].param, size))
goto fail;
}
@@ -51,14 +51,14 @@ int ipu3_css_pool_init(struct imgu_device *imgu, struct ipu3_css_pool *pool,
return 0;
fail:
- ipu3_css_pool_cleanup(imgu, pool);
+ imgu_css_pool_cleanup(imgu, pool);
return -ENOMEM;
}
/*
* Allocate a new parameter via recycling the oldest entry in the pool.
*/
-void ipu3_css_pool_get(struct ipu3_css_pool *pool)
+void imgu_css_pool_get(struct imgu_css_pool *pool)
{
/* Get the oldest entry */
u32 n = (pool->last + 1) % IPU3_CSS_POOL_SIZE;
@@ -70,25 +70,25 @@ void ipu3_css_pool_get(struct ipu3_css_pool *pool)
/*
* Undo, for all practical purposes, the effect of pool_get().
*/
-void ipu3_css_pool_put(struct ipu3_css_pool *pool)
+void imgu_css_pool_put(struct imgu_css_pool *pool)
{
pool->entry[pool->last].valid = false;
pool->last = (pool->last + IPU3_CSS_POOL_SIZE - 1) % IPU3_CSS_POOL_SIZE;
}
/**
- * ipu3_css_pool_last - Retrieve the nth pool entry from last
+ * imgu_css_pool_last - Retrieve the nth pool entry from last
*
- * @pool: a pointer to &struct ipu3_css_pool.
+ * @pool: a pointer to &struct imgu_css_pool.
* @n: the distance to the last index.
*
* Returns:
* The nth entry from last or null map to indicate no frame stored.
*/
-const struct ipu3_css_map *
-ipu3_css_pool_last(struct ipu3_css_pool *pool, unsigned int n)
+const struct imgu_css_map *
+imgu_css_pool_last(struct imgu_css_pool *pool, unsigned int n)
{
- static const struct ipu3_css_map null_map = { 0 };
+ static const struct imgu_css_map null_map = { 0 };
int i = (pool->last + IPU3_CSS_POOL_SIZE - n) % IPU3_CSS_POOL_SIZE;
WARN_ON(n >= IPU3_CSS_POOL_SIZE);
diff --git a/drivers/staging/media/ipu3/ipu3-css-pool.h b/drivers/staging/media/ipu3/ipu3-css-pool.h
index 2657c39a4d71..f4a60b41401b 100644
--- a/drivers/staging/media/ipu3/ipu3-css-pool.h
+++ b/drivers/staging/media/ipu3/ipu3-css-pool.h
@@ -10,15 +10,15 @@ struct imgu_device;
#define IPU3_CSS_POOL_SIZE 4
/**
- * ipu3_css_map - store DMA mapping info for buffer
+ * imgu_css_map - store DMA mapping info for buffer
*
* @size: size of the buffer in bytes.
* @vaddr: kernel virtual address.
* @daddr: iova dma address to access IPU3.
* @vma: private, a pointer to &struct vm_struct,
- * used for ipu3_dmamap_free.
+ * used for imgu_dmamap_free.
*/
-struct ipu3_css_map {
+struct imgu_css_map {
size_t size;
void *vaddr;
dma_addr_t daddr;
@@ -26,30 +26,30 @@ struct ipu3_css_map {
};
/**
- * ipu3_css_pool - circular buffer pool definition
+ * imgu_css_pool - circular buffer pool definition
*
* @entry: array with IPU3_CSS_POOL_SIZE elements.
- * @entry.param: a &struct ipu3_css_map for storing the mem mapping.
+ * @entry.param: a &struct imgu_css_map for storing the mem mapping.
* @entry.valid: used to mark if the entry has valid data.
* @last: write pointer, initialized to IPU3_CSS_POOL_SIZE.
*/
-struct ipu3_css_pool {
+struct imgu_css_pool {
struct {
- struct ipu3_css_map param;
+ struct imgu_css_map param;
bool valid;
} entry[IPU3_CSS_POOL_SIZE];
u32 last;
};
-int ipu3_css_dma_buffer_resize(struct imgu_device *imgu,
- struct ipu3_css_map *map, size_t size);
-void ipu3_css_pool_cleanup(struct imgu_device *imgu,
- struct ipu3_css_pool *pool);
-int ipu3_css_pool_init(struct imgu_device *imgu, struct ipu3_css_pool *pool,
+int imgu_css_dma_buffer_resize(struct imgu_device *imgu,
+ struct imgu_css_map *map, size_t size);
+void imgu_css_pool_cleanup(struct imgu_device *imgu,
+ struct imgu_css_pool *pool);
+int imgu_css_pool_init(struct imgu_device *imgu, struct imgu_css_pool *pool,
size_t size);
-void ipu3_css_pool_get(struct ipu3_css_pool *pool);
-void ipu3_css_pool_put(struct ipu3_css_pool *pool);
-const struct ipu3_css_map *ipu3_css_pool_last(struct ipu3_css_pool *pool,
+void imgu_css_pool_get(struct imgu_css_pool *pool);
+void imgu_css_pool_put(struct imgu_css_pool *pool);
+const struct imgu_css_map *imgu_css_pool_last(struct imgu_css_pool *pool,
u32 last);
#endif
diff --git a/drivers/staging/media/ipu3/ipu3-css.c b/drivers/staging/media/ipu3/ipu3-css.c
index 44c55639389a..15ab77e4b766 100644
--- a/drivers/staging/media/ipu3/ipu3-css.c
+++ b/drivers/staging/media/ipu3/ipu3-css.c
@@ -46,7 +46,7 @@
IPU3_CSS_QUEUE_TO_FLAGS(IPU3_CSS_QUEUE_VF)
/* Formats supported by IPU3 Camera Sub System */
-static const struct ipu3_css_format ipu3_css_formats[] = {
+static const struct imgu_css_format imgu_css_formats[] = {
{
.pixelformat = V4L2_PIX_FMT_NV12,
.colorspace = V4L2_COLORSPACE_SRGB,
@@ -100,7 +100,7 @@ static const struct ipu3_css_format ipu3_css_formats[] = {
static const struct {
enum imgu_abi_queue_id qid;
size_t ptr_ofs;
-} ipu3_css_queues[IPU3_CSS_QUEUES] = {
+} imgu_css_queues[IPU3_CSS_QUEUES] = {
[IPU3_CSS_QUEUE_IN] = {
IMGU_ABI_QUEUE_C_ID,
offsetof(struct imgu_abi_buffer, payload.frame.frame_data)
@@ -120,7 +120,7 @@ static const struct {
};
/* Initialize queue based on given format, adjust format as needed */
-static int ipu3_css_queue_init(struct ipu3_css_queue *queue,
+static int imgu_css_queue_init(struct imgu_css_queue *queue,
struct v4l2_pix_format_mplane *fmt, u32 flags)
{
struct v4l2_pix_format_mplane *const f = &queue->fmt.mpix;
@@ -133,11 +133,11 @@ static int ipu3_css_queue_init(struct ipu3_css_queue *queue,
if (!fmt)
return 0;
- for (i = 0; i < ARRAY_SIZE(ipu3_css_formats); i++) {
- if (!(ipu3_css_formats[i].flags & flags))
+ for (i = 0; i < ARRAY_SIZE(imgu_css_formats); i++) {
+ if (!(imgu_css_formats[i].flags & flags))
continue;
- queue->css_fmt = &ipu3_css_formats[i];
- if (ipu3_css_formats[i].pixelformat == fmt->pixelformat)
+ queue->css_fmt = &imgu_css_formats[i];
+ if (imgu_css_formats[i].pixelformat == fmt->pixelformat)
break;
}
if (!queue->css_fmt)
@@ -178,7 +178,7 @@ static int ipu3_css_queue_init(struct ipu3_css_queue *queue,
return 0;
}
-static bool ipu3_css_queue_enabled(struct ipu3_css_queue *q)
+static bool imgu_css_queue_enabled(struct imgu_css_queue *q)
{
return q->css_fmt;
}
@@ -200,7 +200,7 @@ static inline void writes(const void *mem, ssize_t count, void __iomem *addr)
}
/* Wait until register `reg', masked with `mask', becomes `cmp' */
-static int ipu3_hw_wait(void __iomem *base, int reg, u32 mask, u32 cmp)
+static int imgu_hw_wait(void __iomem *base, int reg, u32 mask, u32 cmp)
{
u32 val;
@@ -210,7 +210,7 @@ static int ipu3_hw_wait(void __iomem *base, int reg, u32 mask, u32 cmp)
/* Initialize the IPU3 CSS hardware and associated h/w blocks */
-int ipu3_css_set_powerup(struct device *dev, void __iomem *base)
+int imgu_css_set_powerup(struct device *dev, void __iomem *base)
{
static const unsigned int freq = 450;
u32 pm_ctrl, state, val;
@@ -221,7 +221,7 @@ int ipu3_css_set_powerup(struct device *dev, void __iomem *base)
writel(0, base + IMGU_REG_GP_BUSY);
/* Wait for idle signal */
- if (ipu3_hw_wait(base, IMGU_REG_STATE, IMGU_STATE_IDLE_STS,
+ if (imgu_hw_wait(base, IMGU_REG_STATE, IMGU_STATE_IDLE_STS,
IMGU_STATE_IDLE_STS)) {
dev_err(dev, "failed to set CSS idle\n");
goto fail;
@@ -245,7 +245,7 @@ int ipu3_css_set_powerup(struct device *dev, void __iomem *base)
if (state & IMGU_STATE_POWER_DOWN) {
writel(IMGU_PM_CTRL_RACE_TO_HALT | IMGU_PM_CTRL_START,
base + IMGU_REG_PM_CTRL);
- if (ipu3_hw_wait(base, IMGU_REG_PM_CTRL,
+ if (imgu_hw_wait(base, IMGU_REG_PM_CTRL,
IMGU_PM_CTRL_START, 0)) {
dev_err(dev, "failed to power up CSS\n");
goto fail;
@@ -263,7 +263,7 @@ int ipu3_css_set_powerup(struct device *dev, void __iomem *base)
val = pm_ctrl & ~(IMGU_PM_CTRL_CSS_PWRDN | IMGU_PM_CTRL_RST_AT_EOF);
writel(val, base + IMGU_REG_PM_CTRL);
writel(0, base + IMGU_REG_GP_BUSY);
- if (ipu3_hw_wait(base, IMGU_REG_STATE,
+ if (imgu_hw_wait(base, IMGU_REG_STATE,
IMGU_STATE_PWRDNM_FSM_MASK, 0)) {
dev_err(dev, "failed to pwrdn CSS\n");
goto fail;
@@ -273,7 +273,7 @@ int ipu3_css_set_powerup(struct device *dev, void __iomem *base)
writel(1, base + IMGU_REG_GP_BUSY);
writel(readl(base + IMGU_REG_PM_CTRL) | IMGU_PM_CTRL_FORCE_HALT,
base + IMGU_REG_PM_CTRL);
- if (ipu3_hw_wait(base, IMGU_REG_STATE, IMGU_STATE_HALT_STS,
+ if (imgu_hw_wait(base, IMGU_REG_STATE, IMGU_STATE_HALT_STS,
IMGU_STATE_HALT_STS)) {
dev_err(dev, "failed to halt CSS\n");
goto fail;
@@ -281,7 +281,7 @@ int ipu3_css_set_powerup(struct device *dev, void __iomem *base)
writel(readl(base + IMGU_REG_PM_CTRL) | IMGU_PM_CTRL_START,
base + IMGU_REG_PM_CTRL);
- if (ipu3_hw_wait(base, IMGU_REG_PM_CTRL, IMGU_PM_CTRL_START, 0)) {
+ if (imgu_hw_wait(base, IMGU_REG_PM_CTRL, IMGU_PM_CTRL_START, 0)) {
dev_err(dev, "failed to start CSS\n");
goto fail;
}
@@ -296,26 +296,26 @@ int ipu3_css_set_powerup(struct device *dev, void __iomem *base)
return 0;
fail:
- ipu3_css_set_powerdown(dev, base);
+ imgu_css_set_powerdown(dev, base);
return -EIO;
}
-void ipu3_css_set_powerdown(struct device *dev, void __iomem *base)
+void imgu_css_set_powerdown(struct device *dev, void __iomem *base)
{
dev_dbg(dev, "%s\n", __func__);
/* wait for cio idle signal */
- if (ipu3_hw_wait(base, IMGU_REG_CIO_GATE_BURST_STATE,
+ if (imgu_hw_wait(base, IMGU_REG_CIO_GATE_BURST_STATE,
IMGU_CIO_GATE_BURST_MASK, 0))
dev_warn(dev, "wait cio gate idle timeout");
/* wait for css idle signal */
- if (ipu3_hw_wait(base, IMGU_REG_STATE, IMGU_STATE_IDLE_STS,
+ if (imgu_hw_wait(base, IMGU_REG_STATE, IMGU_STATE_IDLE_STS,
IMGU_STATE_IDLE_STS))
dev_warn(dev, "wait css idle timeout\n");
/* do halt-halted handshake with css */
writel(1, base + IMGU_REG_GP_HALT);
- if (ipu3_hw_wait(base, IMGU_REG_STATE, IMGU_STATE_HALT_STS,
+ if (imgu_hw_wait(base, IMGU_REG_STATE, IMGU_STATE_HALT_STS,
IMGU_STATE_HALT_STS))
dev_warn(dev, "failed to halt css");
@@ -323,7 +323,7 @@ void ipu3_css_set_powerdown(struct device *dev, void __iomem *base)
writel(0, base + IMGU_REG_GP_BUSY);
}
-static void ipu3_css_hw_enable_irq(struct ipu3_css *css)
+static void imgu_css_hw_enable_irq(struct imgu_css *css)
{
void __iomem *const base = css->base;
u32 val, i;
@@ -371,7 +371,7 @@ static void ipu3_css_hw_enable_irq(struct ipu3_css *css)
}
}
-static int ipu3_css_hw_init(struct ipu3_css *css)
+static int imgu_css_hw_init(struct imgu_css *css)
{
/* For checking that streaming monitor statuses are valid */
static const struct {
@@ -463,11 +463,11 @@ static int ipu3_css_hw_init(struct ipu3_css *css)
/* Initialize GDC with default values */
- for (i = 0; i < ARRAY_SIZE(ipu3_css_gdc_lut[0]); i++) {
- u32 val0 = ipu3_css_gdc_lut[0][i] & IMGU_GDC_LUT_MASK;
- u32 val1 = ipu3_css_gdc_lut[1][i] & IMGU_GDC_LUT_MASK;
- u32 val2 = ipu3_css_gdc_lut[2][i] & IMGU_GDC_LUT_MASK;
- u32 val3 = ipu3_css_gdc_lut[3][i] & IMGU_GDC_LUT_MASK;
+ for (i = 0; i < ARRAY_SIZE(imgu_css_gdc_lut[0]); i++) {
+ u32 val0 = imgu_css_gdc_lut[0][i] & IMGU_GDC_LUT_MASK;
+ u32 val1 = imgu_css_gdc_lut[1][i] & IMGU_GDC_LUT_MASK;
+ u32 val2 = imgu_css_gdc_lut[2][i] & IMGU_GDC_LUT_MASK;
+ u32 val3 = imgu_css_gdc_lut[3][i] & IMGU_GDC_LUT_MASK;
writel(val0 | (val1 << 16),
base + IMGU_REG_GDC_LUT_BASE + i * 8);
@@ -479,7 +479,7 @@ static int ipu3_css_hw_init(struct ipu3_css *css)
}
/* Boot the given IPU3 CSS SP */
-static int ipu3_css_hw_start_sp(struct ipu3_css *css, int sp)
+static int imgu_css_hw_start_sp(struct imgu_css *css, int sp)
{
void __iomem *const base = css->base;
struct imgu_fw_info *bi = &css->fwp->binary_header[css->fw_sp[sp]];
@@ -501,7 +501,7 @@ static int ipu3_css_hw_start_sp(struct ipu3_css *css, int sp)
writel(readl(base + IMGU_REG_SP_CTRL(sp))
| IMGU_CTRL_START | IMGU_CTRL_RUN, base + IMGU_REG_SP_CTRL(sp));
- if (ipu3_hw_wait(css->base, IMGU_REG_SP_DMEM_BASE(sp)
+ if (imgu_hw_wait(css->base, IMGU_REG_SP_DMEM_BASE(sp)
+ bi->info.sp.sw_state,
~0, IMGU_ABI_SP_SWSTATE_INITIALIZED))
return -EIO;
@@ -510,7 +510,7 @@ static int ipu3_css_hw_start_sp(struct ipu3_css *css, int sp)
}
/* Start the IPU3 CSS ImgU (Imaging Unit) and all the SPs */
-static int ipu3_css_hw_start(struct ipu3_css *css)
+static int imgu_css_hw_start(struct imgu_css *css)
{
static const u32 event_mask =
((1 << IMGU_ABI_EVTTYPE_OUT_FRAME_DONE) |
@@ -560,7 +560,7 @@ static int ipu3_css_hw_start(struct ipu3_css *css)
writel(readl(base + IMGU_REG_ISP_CTRL)
| IMGU_CTRL_START | IMGU_CTRL_RUN, base + IMGU_REG_ISP_CTRL);
- if (ipu3_hw_wait(css->base, IMGU_REG_ISP_DMEM_BASE
+ if (imgu_hw_wait(css->base, IMGU_REG_ISP_DMEM_BASE
+ bl->info.bl.sw_state, ~0,
IMGU_ABI_BL_SWSTATE_OK)) {
dev_err(css->dev, "failed to start bootloader\n");
@@ -581,7 +581,7 @@ static int ipu3_css_hw_start(struct ipu3_css *css)
base + IMGU_REG_SP_DMEM_BASE(0) + bi->info.sp.sw_state);
writel(1, base + IMGU_REG_SP_DMEM_BASE(0) + bi->info.sp.invalidate_tlb);
- if (ipu3_css_hw_start_sp(css, 0))
+ if (imgu_css_hw_start_sp(css, 0))
return -EIO;
writel(0, base + IMGU_REG_SP_DMEM_BASE(0) + bi->info.sp.isp_started);
@@ -608,7 +608,7 @@ static int ipu3_css_hw_start(struct ipu3_css *css)
writel(IMGU_ABI_SP_SWSTATE_TERMINATED,
base + IMGU_REG_SP_DMEM_BASE(1) + bi->info.sp.sw_state);
- if (ipu3_css_hw_start_sp(css, 1))
+ if (imgu_css_hw_start_sp(css, 1))
return -EIO;
writel(IMGU_ABI_SP_COMM_COMMAND_READY, base + IMGU_REG_SP_DMEM_BASE(1)
@@ -617,7 +617,7 @@ static int ipu3_css_hw_start(struct ipu3_css *css)
return 0;
}
-static void ipu3_css_hw_stop(struct ipu3_css *css)
+static void imgu_css_hw_stop(struct imgu_css *css)
{
void __iomem *const base = css->base;
struct imgu_fw_info *bi = &css->fwp->binary_header[css->fw_sp[0]];
@@ -626,18 +626,18 @@ static void ipu3_css_hw_stop(struct ipu3_css *css)
writel(IMGU_ABI_SP_COMM_COMMAND_TERMINATE,
base + IMGU_REG_SP_DMEM_BASE(0) +
bi->info.sp.host_sp_com + IMGU_ABI_SP_COMM_COMMAND);
- if (ipu3_hw_wait(css->base, IMGU_REG_SP_CTRL(0),
+ if (imgu_hw_wait(css->base, IMGU_REG_SP_CTRL(0),
IMGU_CTRL_IDLE, IMGU_CTRL_IDLE))
dev_err(css->dev, "wait sp0 idle timeout.\n");
if (readl(base + IMGU_REG_SP_DMEM_BASE(0) + bi->info.sp.sw_state) !=
IMGU_ABI_SP_SWSTATE_TERMINATED)
dev_err(css->dev, "sp0 is not terminated.\n");
- if (ipu3_hw_wait(css->base, IMGU_REG_ISP_CTRL,
+ if (imgu_hw_wait(css->base, IMGU_REG_ISP_CTRL,
IMGU_CTRL_IDLE, IMGU_CTRL_IDLE))
dev_err(css->dev, "wait isp idle timeout\n");
}
-static void ipu3_css_hw_cleanup(struct ipu3_css *css)
+static void imgu_css_hw_cleanup(struct imgu_css *css)
{
void __iomem *const base = css->base;
@@ -648,7 +648,7 @@ static void ipu3_css_hw_cleanup(struct ipu3_css *css)
writel(0, base + IMGU_REG_GP_BUSY);
/* Wait for idle signal */
- if (ipu3_hw_wait(css->base, IMGU_REG_STATE, IMGU_STATE_IDLE_STS,
+ if (imgu_hw_wait(css->base, IMGU_REG_STATE, IMGU_STATE_IDLE_STS,
IMGU_STATE_IDLE_STS))
dev_err(css->dev, "failed to shut down hw cleanly\n");
@@ -659,19 +659,19 @@ static void ipu3_css_hw_cleanup(struct ipu3_css *css)
usleep_range(200, 300);
}
-static void ipu3_css_pipeline_cleanup(struct ipu3_css *css, unsigned int pipe)
+static void imgu_css_pipeline_cleanup(struct imgu_css *css, unsigned int pipe)
{
struct imgu_device *imgu = dev_get_drvdata(css->dev);
unsigned int i;
- ipu3_css_pool_cleanup(imgu,
+ imgu_css_pool_cleanup(imgu,
&css->pipes[pipe].pool.parameter_set_info);
- ipu3_css_pool_cleanup(imgu, &css->pipes[pipe].pool.acc);
- ipu3_css_pool_cleanup(imgu, &css->pipes[pipe].pool.gdc);
- ipu3_css_pool_cleanup(imgu, &css->pipes[pipe].pool.obgrid);
+ imgu_css_pool_cleanup(imgu, &css->pipes[pipe].pool.acc);
+ imgu_css_pool_cleanup(imgu, &css->pipes[pipe].pool.gdc);
+ imgu_css_pool_cleanup(imgu, &css->pipes[pipe].pool.obgrid);
for (i = 0; i < IMGU_ABI_NUM_MEMORIES; i++)
- ipu3_css_pool_cleanup(imgu,
+ imgu_css_pool_cleanup(imgu,
&css->pipes[pipe].pool.binary_params_p[i]);
}
@@ -679,7 +679,7 @@ static void ipu3_css_pipeline_cleanup(struct ipu3_css *css, unsigned int pipe)
* This function initializes various stages of the
* IPU3 CSS ISP pipeline
*/
-static int ipu3_css_pipeline_init(struct ipu3_css *css, unsigned int pipe)
+static int imgu_css_pipeline_init(struct imgu_css *css, unsigned int pipe)
{
static const int BYPC = 2; /* Bytes per component */
static const struct imgu_abi_buffer_sp buffer_sp_init = {
@@ -697,7 +697,7 @@ static int ipu3_css_pipeline_init(struct ipu3_css *css, unsigned int pipe)
const int stage = 0;
unsigned int i, j;
- struct ipu3_css_pipe *css_pipe = &css->pipes[pipe];
+ struct imgu_css_pipe *css_pipe = &css->pipes[pipe];
const struct imgu_fw_info *bi =
&css->fwp->binary_header[css_pipe->bindex];
const unsigned int stripes = bi->info.isp.sp.iterator.num_stripes;
@@ -725,7 +725,7 @@ static int ipu3_css_pipeline_init(struct ipu3_css *css, unsigned int pipe)
/* Configure iterator */
- cfg_iter = ipu3_css_fw_pipeline_params(css, pipe, cfg, m0,
+ cfg_iter = imgu_css_fw_pipeline_params(css, pipe, cfg, m0,
&cofs->dmem.iterator,
sizeof(*cfg_iter), vaddr);
if (!cfg_iter)
@@ -791,7 +791,7 @@ static int ipu3_css_pipeline_init(struct ipu3_css *css, unsigned int pipe)
/* Configure reference (delay) frames */
- cfg_ref = ipu3_css_fw_pipeline_params(css, pipe, cfg, m0,
+ cfg_ref = imgu_css_fw_pipeline_params(css, pipe, cfg, m0,
&cofs->dmem.ref,
sizeof(*cfg_ref), vaddr);
if (!cfg_ref)
@@ -821,7 +821,7 @@ static int ipu3_css_pipeline_init(struct ipu3_css *css, unsigned int pipe)
/* Configure DVS (digital video stabilization) */
- cfg_dvs = ipu3_css_fw_pipeline_params(css, pipe, cfg, m0,
+ cfg_dvs = imgu_css_fw_pipeline_params(css, pipe, cfg, m0,
&cofs->dmem.dvs, sizeof(*cfg_dvs),
vaddr);
if (!cfg_dvs)
@@ -837,7 +837,7 @@ static int ipu3_css_pipeline_init(struct ipu3_css *css, unsigned int pipe)
/* Configure TNR (temporal noise reduction) */
if (css_pipe->pipe_id == IPU3_CSS_PIPE_ID_VIDEO) {
- cfg_tnr = ipu3_css_fw_pipeline_params(css, pipe, cfg, m0,
+ cfg_tnr = imgu_css_fw_pipeline_params(css, pipe, cfg, m0,
&cofs->dmem.tnr3,
sizeof(*cfg_tnr),
vaddr);
@@ -868,7 +868,7 @@ static int ipu3_css_pipeline_init(struct ipu3_css *css, unsigned int pipe)
cfg = IMGU_ABI_PARAM_CLASS_STATE;
vaddr = css_pipe->binary_params_cs[cfg - 1][m0].vaddr;
- cfg_ref_state = ipu3_css_fw_pipeline_params(css, pipe, cfg, m0,
+ cfg_ref_state = imgu_css_fw_pipeline_params(css, pipe, cfg, m0,
&sofs->dmem.ref,
sizeof(*cfg_ref_state),
vaddr);
@@ -881,7 +881,7 @@ static int ipu3_css_pipeline_init(struct ipu3_css *css, unsigned int pipe)
/* Configure tnr dmem state parameters */
if (css_pipe->pipe_id == IPU3_CSS_PIPE_ID_VIDEO) {
cfg_tnr_state =
- ipu3_css_fw_pipeline_params(css, pipe, cfg, m0,
+ imgu_css_fw_pipeline_params(css, pipe, cfg, m0,
&sofs->dmem.tnr3,
sizeof(*cfg_tnr_state),
vaddr);
@@ -1068,21 +1068,21 @@ static int ipu3_css_pipeline_init(struct ipu3_css *css, unsigned int pipe)
/* Initialize parameter pools */
- if (ipu3_css_pool_init(imgu, &css_pipe->pool.parameter_set_info,
+ if (imgu_css_pool_init(imgu, &css_pipe->pool.parameter_set_info,
sizeof(struct imgu_abi_parameter_set_info)) ||
- ipu3_css_pool_init(imgu, &css_pipe->pool.acc,
+ imgu_css_pool_init(imgu, &css_pipe->pool.acc,
sizeof(struct imgu_abi_acc_param)) ||
- ipu3_css_pool_init(imgu, &css_pipe->pool.gdc,
+ imgu_css_pool_init(imgu, &css_pipe->pool.gdc,
sizeof(struct imgu_abi_gdc_warp_param) *
3 * cfg_dvs->num_horizontal_blocks / 2 *
cfg_dvs->num_vertical_blocks) ||
- ipu3_css_pool_init(imgu, &css_pipe->pool.obgrid,
- ipu3_css_fw_obgrid_size(
+ imgu_css_pool_init(imgu, &css_pipe->pool.obgrid,
+ imgu_css_fw_obgrid_size(
&css->fwp->binary_header[css_pipe->bindex])))
goto out_of_memory;
for (i = 0; i < IMGU_ABI_NUM_MEMORIES; i++)
- if (ipu3_css_pool_init(imgu,
+ if (imgu_css_pool_init(imgu,
&css_pipe->pool.binary_params_p[i],
bi->info.isp.sp.mem_initializers.params
[IMGU_ABI_PARAM_CLASS_PARAM][i].size))
@@ -1091,15 +1091,15 @@ static int ipu3_css_pipeline_init(struct ipu3_css *css, unsigned int pipe)
return 0;
bad_firmware:
- ipu3_css_pipeline_cleanup(css, pipe);
+ imgu_css_pipeline_cleanup(css, pipe);
return -EPROTO;
out_of_memory:
- ipu3_css_pipeline_cleanup(css, pipe);
+ imgu_css_pipeline_cleanup(css, pipe);
return -ENOMEM;
}
-static u8 ipu3_css_queue_pos(struct ipu3_css *css, int queue, int thread)
+static u8 imgu_css_queue_pos(struct imgu_css *css, int queue, int thread)
{
static const unsigned int sp;
void __iomem *const base = css->base;
@@ -1112,7 +1112,7 @@ static u8 ipu3_css_queue_pos(struct ipu3_css *css, int queue, int thread)
}
/* Sent data to sp using given buffer queue, or if queue < 0, event queue. */
-static int ipu3_css_queue_data(struct ipu3_css *css,
+static int imgu_css_queue_data(struct imgu_css *css,
int queue, int thread, u32 data)
{
static const unsigned int sp;
@@ -1151,7 +1151,7 @@ static int ipu3_css_queue_data(struct ipu3_css *css,
}
/* Receive data using given buffer queue, or if queue < 0, event queue. */
-static int ipu3_css_dequeue_data(struct ipu3_css *css, int queue, u32 *data)
+static int imgu_css_dequeue_data(struct imgu_css *css, int queue, u32 *data)
{
static const unsigned int sp;
void __iomem *const base = css->base;
@@ -1188,7 +1188,7 @@ static int ipu3_css_dequeue_data(struct ipu3_css *css, int queue, u32 *data)
writeb(start2, &q->sp2host_evtq_info.start);
/* Acknowledge events dequeued from event queue */
- r = ipu3_css_queue_data(css, queue, 0,
+ r = imgu_css_queue_data(css, queue, 0,
IMGU_ABI_EVENT_EVENT_DEQUEUED);
if (r < 0)
return r;
@@ -1198,52 +1198,52 @@ static int ipu3_css_dequeue_data(struct ipu3_css *css, int queue, u32 *data)
}
/* Free binary-specific resources */
-static void ipu3_css_binary_cleanup(struct ipu3_css *css, unsigned int pipe)
+static void imgu_css_binary_cleanup(struct imgu_css *css, unsigned int pipe)
{
struct imgu_device *imgu = dev_get_drvdata(css->dev);
unsigned int i, j;
- struct ipu3_css_pipe *css_pipe = &css->pipes[pipe];
+ struct imgu_css_pipe *css_pipe = &css->pipes[pipe];
for (j = 0; j < IMGU_ABI_PARAM_CLASS_NUM - 1; j++)
for (i = 0; i < IMGU_ABI_NUM_MEMORIES; i++)
- ipu3_dmamap_free(imgu,
+ imgu_dmamap_free(imgu,
&css_pipe->binary_params_cs[j][i]);
j = IPU3_CSS_AUX_FRAME_REF;
for (i = 0; i < IPU3_CSS_AUX_FRAMES; i++)
- ipu3_dmamap_free(imgu,
+ imgu_dmamap_free(imgu,
&css_pipe->aux_frames[j].mem[i]);
j = IPU3_CSS_AUX_FRAME_TNR;
for (i = 0; i < IPU3_CSS_AUX_FRAMES; i++)
- ipu3_dmamap_free(imgu,
+ imgu_dmamap_free(imgu,
&css_pipe->aux_frames[j].mem[i]);
}
-static int ipu3_css_binary_preallocate(struct ipu3_css *css, unsigned int pipe)
+static int imgu_css_binary_preallocate(struct imgu_css *css, unsigned int pipe)
{
struct imgu_device *imgu = dev_get_drvdata(css->dev);
unsigned int i, j;
- struct ipu3_css_pipe *css_pipe = &css->pipes[pipe];
+ struct imgu_css_pipe *css_pipe = &css->pipes[pipe];
for (j = IMGU_ABI_PARAM_CLASS_CONFIG;
j < IMGU_ABI_PARAM_CLASS_NUM; j++)
for (i = 0; i < IMGU_ABI_NUM_MEMORIES; i++)
- if (!ipu3_dmamap_alloc(imgu,
+ if (!imgu_dmamap_alloc(imgu,
&css_pipe->binary_params_cs[j - 1][i],
CSS_ABI_SIZE))
goto out_of_memory;
for (i = 0; i < IPU3_CSS_AUX_FRAMES; i++)
- if (!ipu3_dmamap_alloc(imgu,
+ if (!imgu_dmamap_alloc(imgu,
&css_pipe->aux_frames[IPU3_CSS_AUX_FRAME_REF].
mem[i], CSS_BDS_SIZE))
goto out_of_memory;
for (i = 0; i < IPU3_CSS_AUX_FRAMES; i++)
- if (!ipu3_dmamap_alloc(imgu,
+ if (!imgu_dmamap_alloc(imgu,
&css_pipe->aux_frames[IPU3_CSS_AUX_FRAME_TNR].
mem[i], CSS_GDC_SIZE))
goto out_of_memory;
@@ -1251,14 +1251,14 @@ static int ipu3_css_binary_preallocate(struct ipu3_css *css, unsigned int pipe)
return 0;
out_of_memory:
- ipu3_css_binary_cleanup(css, pipe);
+ imgu_css_binary_cleanup(css, pipe);
return -ENOMEM;
}
/* allocate binary-specific resources */
-static int ipu3_css_binary_setup(struct ipu3_css *css, unsigned int pipe)
+static int imgu_css_binary_setup(struct imgu_css *css, unsigned int pipe)
{
- struct ipu3_css_pipe *css_pipe = &css->pipes[pipe];
+ struct imgu_css_pipe *css_pipe = &css->pipes[pipe];
struct imgu_fw_info *bi = &css->fwp->binary_header[css_pipe->bindex];
struct imgu_device *imgu = dev_get_drvdata(css->dev);
int i, j, size;
@@ -1269,7 +1269,7 @@ static int ipu3_css_binary_setup(struct ipu3_css *css, unsigned int pipe)
for (j = IMGU_ABI_PARAM_CLASS_CONFIG; j < IMGU_ABI_PARAM_CLASS_NUM; j++)
for (i = 0; i < IMGU_ABI_NUM_MEMORIES; i++) {
- if (ipu3_css_dma_buffer_resize(
+ if (imgu_css_dma_buffer_resize(
imgu,
&css_pipe->binary_params_cs[j - 1][i],
bi->info.isp.sp.mem_initializers.params[j][i].size))
@@ -1292,7 +1292,7 @@ static int ipu3_css_binary_setup(struct ipu3_css *css, unsigned int pipe)
css_pipe->aux_frames[IPU3_CSS_AUX_FRAME_REF].bytesperpixel * w;
size = w * h * BYPC + (w / 2) * (h / 2) * BYPC * 2;
for (i = 0; i < IPU3_CSS_AUX_FRAMES; i++)
- if (ipu3_css_dma_buffer_resize(
+ if (imgu_css_dma_buffer_resize(
imgu,
&css_pipe->aux_frames[IPU3_CSS_AUX_FRAME_REF].mem[i],
size))
@@ -1313,7 +1313,7 @@ static int ipu3_css_binary_setup(struct ipu3_css *css, unsigned int pipe)
h = css_pipe->aux_frames[IPU3_CSS_AUX_FRAME_TNR].height;
size = w * ALIGN(h * 3 / 2 + 3, 2); /* +3 for vf_pp prefetch */
for (i = 0; i < IPU3_CSS_AUX_FRAMES; i++)
- if (ipu3_css_dma_buffer_resize(
+ if (imgu_css_dma_buffer_resize(
imgu,
&css_pipe->aux_frames[IPU3_CSS_AUX_FRAME_TNR].mem[i],
size))
@@ -1322,11 +1322,11 @@ static int ipu3_css_binary_setup(struct ipu3_css *css, unsigned int pipe)
return 0;
out_of_memory:
- ipu3_css_binary_cleanup(css, pipe);
+ imgu_css_binary_cleanup(css, pipe);
return -ENOMEM;
}
-int ipu3_css_start_streaming(struct ipu3_css *css)
+int imgu_css_start_streaming(struct imgu_css *css)
{
u32 data;
int r, pipe;
@@ -1335,48 +1335,48 @@ int ipu3_css_start_streaming(struct ipu3_css *css)
return -EPROTO;
for_each_set_bit(pipe, css->enabled_pipes, IMGU_MAX_PIPE_NUM) {
- r = ipu3_css_binary_setup(css, pipe);
+ r = imgu_css_binary_setup(css, pipe);
if (r < 0)
return r;
}
- r = ipu3_css_hw_init(css);
+ r = imgu_css_hw_init(css);
if (r < 0)
return r;
- r = ipu3_css_hw_start(css);
+ r = imgu_css_hw_start(css);
if (r < 0)
goto fail;
for_each_set_bit(pipe, css->enabled_pipes, IMGU_MAX_PIPE_NUM) {
- r = ipu3_css_pipeline_init(css, pipe);
+ r = imgu_css_pipeline_init(css, pipe);
if (r < 0)
goto fail;
}
css->streaming = true;
- ipu3_css_hw_enable_irq(css);
+ imgu_css_hw_enable_irq(css);
/* Initialize parameters to default */
for_each_set_bit(pipe, css->enabled_pipes, IMGU_MAX_PIPE_NUM) {
- r = ipu3_css_set_parameters(css, pipe, NULL);
+ r = imgu_css_set_parameters(css, pipe, NULL);
if (r < 0)
goto fail;
}
- while (!(r = ipu3_css_dequeue_data(css, IMGU_ABI_QUEUE_A_ID, &data)))
+ while (!(r = imgu_css_dequeue_data(css, IMGU_ABI_QUEUE_A_ID, &data)))
;
if (r != -EBUSY)
goto fail;
- while (!(r = ipu3_css_dequeue_data(css, IMGU_ABI_QUEUE_B_ID, &data)))
+ while (!(r = imgu_css_dequeue_data(css, IMGU_ABI_QUEUE_B_ID, &data)))
;
if (r != -EBUSY)
goto fail;
for_each_set_bit(pipe, css->enabled_pipes, IMGU_MAX_PIPE_NUM) {
- r = ipu3_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, pipe,
+ r = imgu_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, pipe,
IMGU_ABI_EVENT_START_STREAM |
pipe << 16);
if (r < 0)
@@ -1387,22 +1387,22 @@ int ipu3_css_start_streaming(struct ipu3_css *css)
fail:
css->streaming = false;
- ipu3_css_hw_cleanup(css);
+ imgu_css_hw_cleanup(css);
for_each_set_bit(pipe, css->enabled_pipes, IMGU_MAX_PIPE_NUM) {
- ipu3_css_pipeline_cleanup(css, pipe);
- ipu3_css_binary_cleanup(css, pipe);
+ imgu_css_pipeline_cleanup(css, pipe);
+ imgu_css_binary_cleanup(css, pipe);
}
return r;
}
-void ipu3_css_stop_streaming(struct ipu3_css *css)
+void imgu_css_stop_streaming(struct imgu_css *css)
{
- struct ipu3_css_buffer *b, *b0;
+ struct imgu_css_buffer *b, *b0;
int q, r, pipe;
for_each_set_bit(pipe, css->enabled_pipes, IMGU_MAX_PIPE_NUM) {
- r = ipu3_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, pipe,
+ r = imgu_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, pipe,
IMGU_ABI_EVENT_STOP_STREAM);
if (r < 0)
dev_warn(css->dev, "failed on stop stream event\n");
@@ -1411,14 +1411,14 @@ void ipu3_css_stop_streaming(struct ipu3_css *css)
if (!css->streaming)
return;
- ipu3_css_hw_stop(css);
+ imgu_css_hw_stop(css);
- ipu3_css_hw_cleanup(css);
+ imgu_css_hw_cleanup(css);
for_each_set_bit(pipe, css->enabled_pipes, IMGU_MAX_PIPE_NUM) {
- struct ipu3_css_pipe *css_pipe = &css->pipes[pipe];
+ struct imgu_css_pipe *css_pipe = &css->pipes[pipe];
- ipu3_css_pipeline_cleanup(css, pipe);
+ imgu_css_pipeline_cleanup(css, pipe);
spin_lock(&css_pipe->qlock);
for (q = 0; q < IPU3_CSS_QUEUES; q++)
@@ -1434,10 +1434,10 @@ void ipu3_css_stop_streaming(struct ipu3_css *css)
css->streaming = false;
}
-bool ipu3_css_pipe_queue_empty(struct ipu3_css *css, unsigned int pipe)
+bool imgu_css_pipe_queue_empty(struct imgu_css *css, unsigned int pipe)
{
int q;
- struct ipu3_css_pipe *css_pipe = &css->pipes[pipe];
+ struct imgu_css_pipe *css_pipe = &css->pipes[pipe];
spin_lock(&css_pipe->qlock);
for (q = 0; q < IPU3_CSS_QUEUES; q++)
@@ -1447,44 +1447,44 @@ bool ipu3_css_pipe_queue_empty(struct ipu3_css *css, unsigned int pipe)
return (q == IPU3_CSS_QUEUES);
}
-bool ipu3_css_queue_empty(struct ipu3_css *css)
+bool imgu_css_queue_empty(struct imgu_css *css)
{
unsigned int pipe;
bool ret = 0;
for (pipe = 0; pipe < IMGU_MAX_PIPE_NUM; pipe++)
- ret &= ipu3_css_pipe_queue_empty(css, pipe);
+ ret &= imgu_css_pipe_queue_empty(css, pipe);
return ret;
}
-bool ipu3_css_is_streaming(struct ipu3_css *css)
+bool imgu_css_is_streaming(struct imgu_css *css)
{
return css->streaming;
}
-static int ipu3_css_map_init(struct ipu3_css *css, unsigned int pipe)
+static int imgu_css_map_init(struct imgu_css *css, unsigned int pipe)
{
struct imgu_device *imgu = dev_get_drvdata(css->dev);
- struct ipu3_css_pipe *css_pipe = &css->pipes[pipe];
+ struct imgu_css_pipe *css_pipe = &css->pipes[pipe];
unsigned int p, q, i;
/* Allocate and map common structures with imgu hardware */
for (p = 0; p < IPU3_CSS_PIPE_ID_NUM; p++)
for (i = 0; i < IMGU_ABI_MAX_STAGES; i++) {
- if (!ipu3_dmamap_alloc(imgu,
+ if (!imgu_dmamap_alloc(imgu,
&css_pipe->
xmem_sp_stage_ptrs[p][i],
sizeof(struct imgu_abi_sp_stage)))
return -ENOMEM;
- if (!ipu3_dmamap_alloc(imgu,
+ if (!imgu_dmamap_alloc(imgu,
&css_pipe->
xmem_isp_stage_ptrs[p][i],
sizeof(struct imgu_abi_isp_stage)))
return -ENOMEM;
}
- if (!ipu3_dmamap_alloc(imgu, &css_pipe->sp_ddr_ptrs,
+ if (!imgu_dmamap_alloc(imgu, &css_pipe->sp_ddr_ptrs,
ALIGN(sizeof(struct imgu_abi_ddr_address_map),
IMGU_ABI_ISP_DDR_WORD_BYTES)))
return -ENOMEM;
@@ -1493,58 +1493,58 @@ static int ipu3_css_map_init(struct ipu3_css *css, unsigned int pipe)
unsigned int abi_buf_num = ARRAY_SIZE(css_pipe->abi_buffers[q]);
for (i = 0; i < abi_buf_num; i++)
- if (!ipu3_dmamap_alloc(imgu,
+ if (!imgu_dmamap_alloc(imgu,
&css_pipe->abi_buffers[q][i],
sizeof(struct imgu_abi_buffer)))
return -ENOMEM;
}
- if (ipu3_css_binary_preallocate(css, pipe)) {
- ipu3_css_binary_cleanup(css, pipe);
+ if (imgu_css_binary_preallocate(css, pipe)) {
+ imgu_css_binary_cleanup(css, pipe);
return -ENOMEM;
}
return 0;
}
-static void ipu3_css_pipe_cleanup(struct ipu3_css *css, unsigned int pipe)
+static void imgu_css_pipe_cleanup(struct imgu_css *css, unsigned int pipe)
{
struct imgu_device *imgu = dev_get_drvdata(css->dev);
- struct ipu3_css_pipe *css_pipe = &css->pipes[pipe];
+ struct imgu_css_pipe *css_pipe = &css->pipes[pipe];
unsigned int p, q, i, abi_buf_num;
- ipu3_css_binary_cleanup(css, pipe);
+ imgu_css_binary_cleanup(css, pipe);
for (q = 0; q < IPU3_CSS_QUEUES; q++) {
abi_buf_num = ARRAY_SIZE(css_pipe->abi_buffers[q]);
for (i = 0; i < abi_buf_num; i++)
- ipu3_dmamap_free(imgu, &css_pipe->abi_buffers[q][i]);
+ imgu_dmamap_free(imgu, &css_pipe->abi_buffers[q][i]);
}
for (p = 0; p < IPU3_CSS_PIPE_ID_NUM; p++)
for (i = 0; i < IMGU_ABI_MAX_STAGES; i++) {
- ipu3_dmamap_free(imgu,
+ imgu_dmamap_free(imgu,
&css_pipe->xmem_sp_stage_ptrs[p][i]);
- ipu3_dmamap_free(imgu,
+ imgu_dmamap_free(imgu,
&css_pipe->xmem_isp_stage_ptrs[p][i]);
}
- ipu3_dmamap_free(imgu, &css_pipe->sp_ddr_ptrs);
+ imgu_dmamap_free(imgu, &css_pipe->sp_ddr_ptrs);
}
-void ipu3_css_cleanup(struct ipu3_css *css)
+void imgu_css_cleanup(struct imgu_css *css)
{
struct imgu_device *imgu = dev_get_drvdata(css->dev);
unsigned int pipe;
- ipu3_css_stop_streaming(css);
+ imgu_css_stop_streaming(css);
for (pipe = 0; pipe < IMGU_MAX_PIPE_NUM; pipe++)
- ipu3_css_pipe_cleanup(css, pipe);
- ipu3_dmamap_free(imgu, &css->xmem_sp_group_ptrs);
- ipu3_css_fw_cleanup(css);
+ imgu_css_pipe_cleanup(css, pipe);
+ imgu_dmamap_free(imgu, &css->xmem_sp_group_ptrs);
+ imgu_css_fw_cleanup(css);
}
-int ipu3_css_init(struct device *dev, struct ipu3_css *css,
+int imgu_css_init(struct device *dev, struct imgu_css *css,
void __iomem *base, int length)
{
struct imgu_device *imgu = dev_get_drvdata(dev);
@@ -1556,35 +1556,35 @@ int ipu3_css_init(struct device *dev, struct ipu3_css *css,
css->iomem_length = length;
for (pipe = 0; pipe < IMGU_MAX_PIPE_NUM; pipe++) {
- struct ipu3_css_pipe *css_pipe = &css->pipes[pipe];
+ struct imgu_css_pipe *css_pipe = &css->pipes[pipe];
css_pipe->vf_output_en = false;
spin_lock_init(&css_pipe->qlock);
css_pipe->bindex = IPU3_CSS_DEFAULT_BINARY;
css_pipe->pipe_id = IPU3_CSS_PIPE_ID_VIDEO;
for (q = 0; q < IPU3_CSS_QUEUES; q++) {
- r = ipu3_css_queue_init(&css_pipe->queue[q], NULL, 0);
+ r = imgu_css_queue_init(&css_pipe->queue[q], NULL, 0);
if (r)
return r;
}
- r = ipu3_css_map_init(css, pipe);
+ r = imgu_css_map_init(css, pipe);
if (r) {
- ipu3_css_cleanup(css);
+ imgu_css_cleanup(css);
return r;
}
}
- if (!ipu3_dmamap_alloc(imgu, &css->xmem_sp_group_ptrs,
+ if (!imgu_dmamap_alloc(imgu, &css->xmem_sp_group_ptrs,
sizeof(struct imgu_abi_sp_group)))
return -ENOMEM;
- r = ipu3_css_fw_init(css);
+ r = imgu_css_fw_init(css);
if (r)
return r;
return 0;
}
-static u32 ipu3_css_adjust(u32 res, u32 align)
+static u32 imgu_css_adjust(u32 res, u32 align)
{
u32 val = max_t(u32, IPU3_CSS_MIN_RES, res);
@@ -1592,9 +1592,9 @@ static u32 ipu3_css_adjust(u32 res, u32 align)
}
/* Select a binary matching the required resolutions and formats */
-static int ipu3_css_find_binary(struct ipu3_css *css,
+static int imgu_css_find_binary(struct imgu_css *css,
unsigned int pipe,
- struct ipu3_css_queue queue[IPU3_CSS_QUEUES],
+ struct imgu_css_queue queue[IPU3_CSS_QUEUES],
struct v4l2_rect rects[IPU3_CSS_RECTS])
{
const int binary_nr = css->fwp->file_header.binary_nr;
@@ -1611,7 +1611,7 @@ static int ipu3_css_find_binary(struct ipu3_css *css,
const char *name;
int i, j;
- if (!ipu3_css_queue_enabled(&queue[IPU3_CSS_QUEUE_IN]))
+ if (!imgu_css_queue_enabled(&queue[IPU3_CSS_QUEUE_IN]))
return -EINVAL;
/* Find out the strip size boundary */
@@ -1659,7 +1659,7 @@ static int ipu3_css_find_binary(struct ipu3_css *css,
in->height > bi->info.isp.sp.input.max_height)
continue;
- if (ipu3_css_queue_enabled(&queue[IPU3_CSS_QUEUE_OUT])) {
+ if (imgu_css_queue_enabled(&queue[IPU3_CSS_QUEUE_OUT])) {
if (bi->info.isp.num_output_pins <= 0)
continue;
@@ -1681,7 +1681,7 @@ static int ipu3_css_find_binary(struct ipu3_css *css,
continue;
}
- if (ipu3_css_queue_enabled(&queue[IPU3_CSS_QUEUE_VF])) {
+ if (imgu_css_queue_enabled(&queue[IPU3_CSS_QUEUE_VF])) {
if (bi->info.isp.num_output_pins <= 1)
continue;
@@ -1716,7 +1716,7 @@ static int ipu3_css_find_binary(struct ipu3_css *css,
* found binary number. May modify the given parameters if not exact match
* is found.
*/
-int ipu3_css_fmt_try(struct ipu3_css *css,
+int imgu_css_fmt_try(struct imgu_css *css,
struct v4l2_pix_format_mplane *fmts[IPU3_CSS_QUEUES],
struct v4l2_rect *rects[IPU3_CSS_RECTS],
unsigned int pipe)
@@ -1744,14 +1744,14 @@ int ipu3_css_fmt_try(struct ipu3_css *css,
struct v4l2_rect *const bds = &r[IPU3_CSS_RECT_BDS];
struct v4l2_rect *const env = &r[IPU3_CSS_RECT_ENVELOPE];
struct v4l2_rect *const gdc = &r[IPU3_CSS_RECT_GDC];
- struct ipu3_css_queue q[IPU3_CSS_QUEUES];
+ struct imgu_css_queue q[IPU3_CSS_QUEUES];
struct v4l2_pix_format_mplane *const in =
&q[IPU3_CSS_QUEUE_IN].fmt.mpix;
struct v4l2_pix_format_mplane *const out =
&q[IPU3_CSS_QUEUE_OUT].fmt.mpix;
struct v4l2_pix_format_mplane *const vf =
&q[IPU3_CSS_QUEUE_VF].fmt.mpix;
- int i, s;
+ int i, s, ret;
/* Adjust all formats, get statistics buffer sizes and formats */
for (i = 0; i < IPU3_CSS_QUEUES; i++) {
@@ -1762,7 +1762,7 @@ int ipu3_css_fmt_try(struct ipu3_css *css,
else
dev_dbg(css->dev, "%s %s: (not set)\n", __func__,
qnames[i]);
- if (ipu3_css_queue_init(&q[i], fmts[i],
+ if (imgu_css_queue_init(&q[i], fmts[i],
IPU3_CSS_QUEUE_TO_FLAGS(i))) {
dev_notice(css->dev, "can not initialize queue %s\n",
qnames[i]);
@@ -1785,13 +1785,13 @@ int ipu3_css_fmt_try(struct ipu3_css *css,
}
/* Always require one input and vf only if out is also enabled */
- if (!ipu3_css_queue_enabled(&q[IPU3_CSS_QUEUE_IN]) ||
- !ipu3_css_queue_enabled(&q[IPU3_CSS_QUEUE_OUT])) {
+ if (!imgu_css_queue_enabled(&q[IPU3_CSS_QUEUE_IN]) ||
+ !imgu_css_queue_enabled(&q[IPU3_CSS_QUEUE_OUT])) {
dev_warn(css->dev, "required queues are disabled\n");
return -EINVAL;
}
- if (!ipu3_css_queue_enabled(&q[IPU3_CSS_QUEUE_OUT])) {
+ if (!imgu_css_queue_enabled(&q[IPU3_CSS_QUEUE_OUT])) {
out->width = in->width;
out->height = in->height;
}
@@ -1808,30 +1808,30 @@ int ipu3_css_fmt_try(struct ipu3_css *css,
gdc->height = out->height;
}
- in->width = ipu3_css_adjust(in->width, 1);
- in->height = ipu3_css_adjust(in->height, 1);
- eff->width = ipu3_css_adjust(eff->width, EFF_ALIGN_W);
- eff->height = ipu3_css_adjust(eff->height, 1);
- bds->width = ipu3_css_adjust(bds->width, BDS_ALIGN_W);
- bds->height = ipu3_css_adjust(bds->height, 1);
- gdc->width = ipu3_css_adjust(gdc->width, OUT_ALIGN_W);
- gdc->height = ipu3_css_adjust(gdc->height, OUT_ALIGN_H);
- out->width = ipu3_css_adjust(out->width, OUT_ALIGN_W);
- out->height = ipu3_css_adjust(out->height, OUT_ALIGN_H);
- vf->width = ipu3_css_adjust(vf->width, VF_ALIGN_W);
- vf->height = ipu3_css_adjust(vf->height, 1);
+ in->width = imgu_css_adjust(in->width, 1);
+ in->height = imgu_css_adjust(in->height, 1);
+ eff->width = imgu_css_adjust(eff->width, EFF_ALIGN_W);
+ eff->height = imgu_css_adjust(eff->height, 1);
+ bds->width = imgu_css_adjust(bds->width, BDS_ALIGN_W);
+ bds->height = imgu_css_adjust(bds->height, 1);
+ gdc->width = imgu_css_adjust(gdc->width, OUT_ALIGN_W);
+ gdc->height = imgu_css_adjust(gdc->height, OUT_ALIGN_H);
+ out->width = imgu_css_adjust(out->width, OUT_ALIGN_W);
+ out->height = imgu_css_adjust(out->height, OUT_ALIGN_H);
+ vf->width = imgu_css_adjust(vf->width, VF_ALIGN_W);
+ vf->height = imgu_css_adjust(vf->height, 1);
s = (bds->width - gdc->width) / 2 - FILTER_SIZE;
env->width = s < MIN_ENVELOPE ? MIN_ENVELOPE : s;
s = (bds->height - gdc->height) / 2 - FILTER_SIZE;
env->height = s < MIN_ENVELOPE ? MIN_ENVELOPE : s;
- css->pipes[pipe].bindex =
- ipu3_css_find_binary(css, pipe, q, r);
- if (css->pipes[pipe].bindex < 0) {
+ ret = imgu_css_find_binary(css, pipe, q, r);
+ if (ret < 0) {
dev_err(css->dev, "failed to find suitable binary\n");
return -EINVAL;
}
+ css->pipes[pipe].bindex = ret;
dev_dbg(css->dev, "Binary index %d for pipe %d found.",
css->pipes[pipe].bindex, pipe);
@@ -1839,7 +1839,7 @@ int ipu3_css_fmt_try(struct ipu3_css *css,
/* Final adjustment and set back the queried formats */
for (i = 0; i < IPU3_CSS_QUEUES; i++) {
if (fmts[i]) {
- if (ipu3_css_queue_init(&q[i], &q[i].fmt.mpix,
+ if (imgu_css_queue_init(&q[i], &q[i].fmt.mpix,
IPU3_CSS_QUEUE_TO_FLAGS(i))) {
dev_err(css->dev,
"final resolution adjustment failed\n");
@@ -1862,7 +1862,7 @@ int ipu3_css_fmt_try(struct ipu3_css *css,
return 0;
}
-int ipu3_css_fmt_set(struct ipu3_css *css,
+int imgu_css_fmt_set(struct imgu_css *css,
struct v4l2_pix_format_mplane *fmts[IPU3_CSS_QUEUES],
struct v4l2_rect *rects[IPU3_CSS_RECTS],
unsigned int pipe)
@@ -1870,7 +1870,7 @@ int ipu3_css_fmt_set(struct ipu3_css *css,
struct v4l2_rect rect_data[IPU3_CSS_RECTS];
struct v4l2_rect *all_rects[IPU3_CSS_RECTS];
int i, r;
- struct ipu3_css_pipe *css_pipe = &css->pipes[pipe];
+ struct imgu_css_pipe *css_pipe = &css->pipes[pipe];
for (i = 0; i < IPU3_CSS_RECTS; i++) {
if (rects[i])
@@ -1879,12 +1879,12 @@ int ipu3_css_fmt_set(struct ipu3_css *css,
memset(&rect_data[i], 0, sizeof(rect_data[i]));
all_rects[i] = &rect_data[i];
}
- r = ipu3_css_fmt_try(css, fmts, all_rects, pipe);
+ r = imgu_css_fmt_try(css, fmts, all_rects, pipe);
if (r < 0)
return r;
for (i = 0; i < IPU3_CSS_QUEUES; i++)
- if (ipu3_css_queue_init(&css_pipe->queue[i], fmts[i],
+ if (imgu_css_queue_init(&css_pipe->queue[i], fmts[i],
IPU3_CSS_QUEUE_TO_FLAGS(i)))
return -EINVAL;
for (i = 0; i < IPU3_CSS_RECTS; i++) {
@@ -1896,7 +1896,7 @@ int ipu3_css_fmt_set(struct ipu3_css *css,
return 0;
}
-int ipu3_css_meta_fmt_set(struct v4l2_meta_format *fmt)
+int imgu_css_meta_fmt_set(struct v4l2_meta_format *fmt)
{
switch (fmt->dataformat) {
case V4L2_META_FMT_IPU3_PARAMS:
@@ -1913,27 +1913,27 @@ int ipu3_css_meta_fmt_set(struct v4l2_meta_format *fmt)
}
/*
- * Queue given buffer to CSS. ipu3_css_buf_prepare() must have been first
+ * Queue given buffer to CSS. imgu_css_buf_prepare() must have been first
* called for the buffer. May be called from interrupt context.
* Returns 0 on success, -EBUSY if the buffer queue is full, or some other
* code on error conditions.
*/
-int ipu3_css_buf_queue(struct ipu3_css *css, unsigned int pipe,
- struct ipu3_css_buffer *b)
+int imgu_css_buf_queue(struct imgu_css *css, unsigned int pipe,
+ struct imgu_css_buffer *b)
{
struct imgu_abi_buffer *abi_buf;
struct imgu_addr_t *buf_addr;
u32 data;
int r;
- struct ipu3_css_pipe *css_pipe = &css->pipes[pipe];
+ struct imgu_css_pipe *css_pipe = &css->pipes[pipe];
if (!css->streaming)
return -EPROTO; /* CSS or buffer in wrong state */
- if (b->queue >= IPU3_CSS_QUEUES || !ipu3_css_queues[b->queue].qid)
+ if (b->queue >= IPU3_CSS_QUEUES || !imgu_css_queues[b->queue].qid)
return -EINVAL;
- b->queue_pos = ipu3_css_queue_pos(css, ipu3_css_queues[b->queue].qid,
+ b->queue_pos = imgu_css_queue_pos(css, imgu_css_queues[b->queue].qid,
pipe);
if (b->queue_pos >= ARRAY_SIZE(css->pipes[pipe].abi_buffers[b->queue]))
@@ -1943,7 +1943,7 @@ int ipu3_css_buf_queue(struct ipu3_css *css, unsigned int pipe,
/* Fill struct abi_buffer for firmware */
memset(abi_buf, 0, sizeof(*abi_buf));
- buf_addr = (void *)abi_buf + ipu3_css_queues[b->queue].ptr_ofs;
+ buf_addr = (void *)abi_buf + imgu_css_queues[b->queue].ptr_ofs;
*(imgu_addr_t *)buf_addr = b->daddr;
if (b->queue == IPU3_CSS_QUEUE_STAT_3A)
@@ -1963,14 +1963,14 @@ int ipu3_css_buf_queue(struct ipu3_css *css, unsigned int pipe,
b->state = IPU3_CSS_BUFFER_QUEUED;
data = css->pipes[pipe].abi_buffers[b->queue][b->queue_pos].daddr;
- r = ipu3_css_queue_data(css, ipu3_css_queues[b->queue].qid,
+ r = imgu_css_queue_data(css, imgu_css_queues[b->queue].qid,
pipe, data);
if (r < 0)
goto queueing_failed;
data = IMGU_ABI_EVENT_BUFFER_ENQUEUED(pipe,
- ipu3_css_queues[b->queue].qid);
- r = ipu3_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, pipe, data);
+ imgu_css_queues[b->queue].qid);
+ r = imgu_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, pipe, data);
if (r < 0)
goto queueing_failed;
@@ -1992,7 +1992,7 @@ queueing_failed:
* should be called again, or -EBUSY which means that there are no more
* buffers available. May be called from interrupt context.
*/
-struct ipu3_css_buffer *ipu3_css_buf_dequeue(struct ipu3_css *css)
+struct imgu_css_buffer *imgu_css_buf_dequeue(struct imgu_css *css)
{
static const unsigned char evtype_to_queue[] = {
[IMGU_ABI_EVTTYPE_INPUT_FRAME_DONE] = IPU3_CSS_QUEUE_IN,
@@ -2000,15 +2000,15 @@ struct ipu3_css_buffer *ipu3_css_buf_dequeue(struct ipu3_css *css)
[IMGU_ABI_EVTTYPE_VF_OUT_FRAME_DONE] = IPU3_CSS_QUEUE_VF,
[IMGU_ABI_EVTTYPE_3A_STATS_DONE] = IPU3_CSS_QUEUE_STAT_3A,
};
- struct ipu3_css_buffer *b = ERR_PTR(-EAGAIN);
+ struct imgu_css_buffer *b = ERR_PTR(-EAGAIN);
u32 event, daddr;
int evtype, pipe, pipeid, queue, qid, r;
- struct ipu3_css_pipe *css_pipe;
+ struct imgu_css_pipe *css_pipe;
if (!css->streaming)
return ERR_PTR(-EPROTO);
- r = ipu3_css_dequeue_data(css, IMGU_ABI_QUEUE_EVENT_ID, &event);
+ r = imgu_css_dequeue_data(css, IMGU_ABI_QUEUE_EVENT_ID, &event);
if (r < 0)
return ERR_PTR(r);
@@ -2025,7 +2025,7 @@ struct ipu3_css_buffer *ipu3_css_buf_dequeue(struct ipu3_css *css)
pipeid = (event & IMGU_ABI_EVTTYPE_PIPEID_MASK) >>
IMGU_ABI_EVTTYPE_PIPEID_SHIFT;
queue = evtype_to_queue[evtype];
- qid = ipu3_css_queues[queue].qid;
+ qid = imgu_css_queues[queue].qid;
if (pipe >= IMGU_MAX_PIPE_NUM) {
dev_err(css->dev, "Invalid pipe: %i\n", pipe);
@@ -2041,14 +2041,14 @@ struct ipu3_css_buffer *ipu3_css_buf_dequeue(struct ipu3_css *css)
"event: buffer done 0x%x queue %i pipe %i pipeid %i\n",
event, queue, pipe, pipeid);
- r = ipu3_css_dequeue_data(css, qid, &daddr);
+ r = imgu_css_dequeue_data(css, qid, &daddr);
if (r < 0) {
dev_err(css->dev, "failed to dequeue buffer\n");
/* Force real error, not -EBUSY */
return ERR_PTR(-EIO);
}
- r = ipu3_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, pipe,
+ r = imgu_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, pipe,
IMGU_ABI_EVENT_BUFFER_DEQUEUED(qid));
if (r < 0) {
dev_err(css->dev, "failed to queue event\n");
@@ -2062,7 +2062,7 @@ struct ipu3_css_buffer *ipu3_css_buf_dequeue(struct ipu3_css *css)
return ERR_PTR(-EIO);
}
b = list_first_entry(&css_pipe->queue[queue].bufs,
- struct ipu3_css_buffer, list);
+ struct imgu_css_buffer, list);
if (queue != b->queue ||
daddr != css_pipe->abi_buffers
[b->queue][b->queue_pos].daddr) {
@@ -2090,7 +2090,7 @@ struct ipu3_css_buffer *ipu3_css_buf_dequeue(struct ipu3_css *css)
event, pipe);
break;
case IMGU_ABI_EVTTYPE_TIMER:
- r = ipu3_css_dequeue_data(css, IMGU_ABI_QUEUE_EVENT_ID, &event);
+ r = imgu_css_dequeue_data(css, IMGU_ABI_QUEUE_EVENT_ID, &event);
if (r < 0)
return ERR_PTR(r);
@@ -2128,11 +2128,11 @@ struct ipu3_css_buffer *ipu3_css_buf_dequeue(struct ipu3_css *css)
* Return index to css->parameter_set_info which has the newly created
* parameters or negative value on error.
*/
-int ipu3_css_set_parameters(struct ipu3_css *css, unsigned int pipe,
+int imgu_css_set_parameters(struct imgu_css *css, unsigned int pipe,
struct ipu3_uapi_params *set_params)
{
static const unsigned int queue_id = IMGU_ABI_QUEUE_A_ID;
- struct ipu3_css_pipe *css_pipe = &css->pipes[pipe];
+ struct imgu_css_pipe *css_pipe = &css->pipes[pipe];
const int stage = 0;
const struct imgu_fw_info *bi;
int obgrid_size;
@@ -2144,7 +2144,7 @@ int ipu3_css_set_parameters(struct ipu3_css *css, unsigned int pipe,
struct imgu_abi_acc_param *acc = NULL;
struct imgu_abi_gdc_warp_param *gdc = NULL;
struct ipu3_uapi_obgrid_param *obgrid = NULL;
- const struct ipu3_css_map *map;
+ const struct imgu_css_map *map;
void *vmem0 = NULL;
void *dmem0 = NULL;
@@ -2157,7 +2157,7 @@ int ipu3_css_set_parameters(struct ipu3_css *css, unsigned int pipe,
dev_dbg(css->dev, "%s for pipe %d", __func__, pipe);
bi = &css->fwp->binary_header[css_pipe->bindex];
- obgrid_size = ipu3_css_fw_obgrid_size(bi);
+ obgrid_size = imgu_css_fw_obgrid_size(bi);
stripes = bi->info.isp.sp.iterator.num_stripes ? : 1;
/*
@@ -2165,45 +2165,45 @@ int ipu3_css_set_parameters(struct ipu3_css *css, unsigned int pipe,
* parameters from previous buffers will be overwritten. Fix the driver
* not to allow this.
*/
- ipu3_css_pool_get(&css_pipe->pool.parameter_set_info);
- param_set = ipu3_css_pool_last(&css_pipe->pool.parameter_set_info,
+ imgu_css_pool_get(&css_pipe->pool.parameter_set_info);
+ param_set = imgu_css_pool_last(&css_pipe->pool.parameter_set_info,
0)->vaddr;
/* Get a new acc only if new parameters given, or none yet */
- map = ipu3_css_pool_last(&css_pipe->pool.acc, 0);
+ map = imgu_css_pool_last(&css_pipe->pool.acc, 0);
if (set_params || !map->vaddr) {
- ipu3_css_pool_get(&css_pipe->pool.acc);
- map = ipu3_css_pool_last(&css_pipe->pool.acc, 0);
+ imgu_css_pool_get(&css_pipe->pool.acc);
+ map = imgu_css_pool_last(&css_pipe->pool.acc, 0);
acc = map->vaddr;
}
/* Get new VMEM0 only if needed, or none yet */
m = IMGU_ABI_MEM_ISP_VMEM0;
- map = ipu3_css_pool_last(&css_pipe->pool.binary_params_p[m], 0);
+ map = imgu_css_pool_last(&css_pipe->pool.binary_params_p[m], 0);
if (!map->vaddr || (set_params && (set_params->use.lin_vmem_params ||
set_params->use.tnr3_vmem_params ||
set_params->use.xnr3_vmem_params))) {
- ipu3_css_pool_get(&css_pipe->pool.binary_params_p[m]);
- map = ipu3_css_pool_last(&css_pipe->pool.binary_params_p[m], 0);
+ imgu_css_pool_get(&css_pipe->pool.binary_params_p[m]);
+ map = imgu_css_pool_last(&css_pipe->pool.binary_params_p[m], 0);
vmem0 = map->vaddr;
}
/* Get new DMEM0 only if needed, or none yet */
m = IMGU_ABI_MEM_ISP_DMEM0;
- map = ipu3_css_pool_last(&css_pipe->pool.binary_params_p[m], 0);
+ map = imgu_css_pool_last(&css_pipe->pool.binary_params_p[m], 0);
if (!map->vaddr || (set_params && (set_params->use.tnr3_dmem_params ||
set_params->use.xnr3_dmem_params))) {
- ipu3_css_pool_get(&css_pipe->pool.binary_params_p[m]);
- map = ipu3_css_pool_last(&css_pipe->pool.binary_params_p[m], 0);
+ imgu_css_pool_get(&css_pipe->pool.binary_params_p[m]);
+ map = imgu_css_pool_last(&css_pipe->pool.binary_params_p[m], 0);
dmem0 = map->vaddr;
}
/* Configure acc parameter cluster */
if (acc) {
/* get acc_old */
- map = ipu3_css_pool_last(&css_pipe->pool.acc, 1);
+ map = imgu_css_pool_last(&css_pipe->pool.acc, 1);
/* user acc */
- r = ipu3_css_cfg_acc(css, pipe, use, acc, map->vaddr,
+ r = imgu_css_cfg_acc(css, pipe, use, acc, map->vaddr,
set_params ? &set_params->acc_param : NULL);
if (r < 0)
goto fail;
@@ -2212,8 +2212,8 @@ int ipu3_css_set_parameters(struct ipu3_css *css, unsigned int pipe,
/* Configure late binding parameters */
if (vmem0) {
m = IMGU_ABI_MEM_ISP_VMEM0;
- map = ipu3_css_pool_last(&css_pipe->pool.binary_params_p[m], 1);
- r = ipu3_css_cfg_vmem0(css, pipe, use, vmem0,
+ map = imgu_css_pool_last(&css_pipe->pool.binary_params_p[m], 1);
+ r = imgu_css_cfg_vmem0(css, pipe, use, vmem0,
map->vaddr, set_params);
if (r < 0)
goto fail;
@@ -2221,8 +2221,8 @@ int ipu3_css_set_parameters(struct ipu3_css *css, unsigned int pipe,
if (dmem0) {
m = IMGU_ABI_MEM_ISP_DMEM0;
- map = ipu3_css_pool_last(&css_pipe->pool.binary_params_p[m], 1);
- r = ipu3_css_cfg_dmem0(css, pipe, use, dmem0,
+ map = imgu_css_pool_last(&css_pipe->pool.binary_params_p[m], 1);
+ r = imgu_css_cfg_dmem0(css, pipe, use, dmem0,
map->vaddr, set_params);
if (r < 0)
goto fail;
@@ -2234,12 +2234,12 @@ int ipu3_css_set_parameters(struct ipu3_css *css, unsigned int pipe,
unsigned int g = IPU3_CSS_RECT_GDC;
unsigned int e = IPU3_CSS_RECT_ENVELOPE;
- map = ipu3_css_pool_last(&css_pipe->pool.gdc, 0);
+ map = imgu_css_pool_last(&css_pipe->pool.gdc, 0);
if (!map->vaddr) {
- ipu3_css_pool_get(&css_pipe->pool.gdc);
- map = ipu3_css_pool_last(&css_pipe->pool.gdc, 0);
+ imgu_css_pool_get(&css_pipe->pool.gdc);
+ map = imgu_css_pool_last(&css_pipe->pool.gdc, 0);
gdc = map->vaddr;
- ipu3_css_cfg_gdc_table(map->vaddr,
+ imgu_css_cfg_gdc_table(map->vaddr,
css_pipe->aux_frames[a].bytesperline /
css_pipe->aux_frames[a].bytesperpixel,
css_pipe->aux_frames[a].height,
@@ -2252,10 +2252,10 @@ int ipu3_css_set_parameters(struct ipu3_css *css, unsigned int pipe,
}
/* Get a new obgrid only if a new obgrid is given, or none yet */
- map = ipu3_css_pool_last(&css_pipe->pool.obgrid, 0);
+ map = imgu_css_pool_last(&css_pipe->pool.obgrid, 0);
if (!map->vaddr || (set_params && set_params->use.obgrid_param)) {
- ipu3_css_pool_get(&css_pipe->pool.obgrid);
- map = ipu3_css_pool_last(&css_pipe->pool.obgrid, 0);
+ imgu_css_pool_get(&css_pipe->pool.obgrid);
+ map = imgu_css_pool_last(&css_pipe->pool.obgrid, 0);
obgrid = map->vaddr;
/* Configure optical black level grid (obgrid) */
@@ -2269,30 +2269,30 @@ int ipu3_css_set_parameters(struct ipu3_css *css, unsigned int pipe,
/* Configure parameter set info, queued to `queue_id' */
memset(param_set, 0, sizeof(*param_set));
- map = ipu3_css_pool_last(&css_pipe->pool.acc, 0);
+ map = imgu_css_pool_last(&css_pipe->pool.acc, 0);
param_set->mem_map.acc_cluster_params_for_sp = map->daddr;
- map = ipu3_css_pool_last(&css_pipe->pool.gdc, 0);
+ map = imgu_css_pool_last(&css_pipe->pool.gdc, 0);
param_set->mem_map.dvs_6axis_params_y = map->daddr;
for (i = 0; i < stripes; i++) {
- map = ipu3_css_pool_last(&css_pipe->pool.obgrid, 0);
+ map = imgu_css_pool_last(&css_pipe->pool.obgrid, 0);
param_set->mem_map.obgrid_tbl[i] =
map->daddr + (obgrid_size / stripes) * i;
}
for (m = 0; m < IMGU_ABI_NUM_MEMORIES; m++) {
- map = ipu3_css_pool_last(&css_pipe->pool.binary_params_p[m], 0);
+ map = imgu_css_pool_last(&css_pipe->pool.binary_params_p[m], 0);
param_set->mem_map.isp_mem_param[stage][m] = map->daddr;
}
/* Then queue the new parameter buffer */
- map = ipu3_css_pool_last(&css_pipe->pool.parameter_set_info, 0);
- r = ipu3_css_queue_data(css, queue_id, pipe, map->daddr);
+ map = imgu_css_pool_last(&css_pipe->pool.parameter_set_info, 0);
+ r = imgu_css_queue_data(css, queue_id, pipe, map->daddr);
if (r < 0)
goto fail;
- r = ipu3_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, pipe,
+ r = imgu_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, pipe,
IMGU_ABI_EVENT_BUFFER_ENQUEUED(pipe,
queue_id));
if (r < 0)
@@ -2303,12 +2303,12 @@ int ipu3_css_set_parameters(struct ipu3_css *css, unsigned int pipe,
do {
u32 daddr;
- r = ipu3_css_dequeue_data(css, queue_id, &daddr);
+ r = imgu_css_dequeue_data(css, queue_id, &daddr);
if (r == -EBUSY)
break;
if (r)
goto fail_no_put;
- r = ipu3_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, pipe,
+ r = imgu_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, pipe,
IMGU_ABI_EVENT_BUFFER_DEQUEUED
(queue_id));
if (r < 0) {
@@ -2326,19 +2326,19 @@ fail:
* parameters again later.
*/
- ipu3_css_pool_put(&css_pipe->pool.parameter_set_info);
+ imgu_css_pool_put(&css_pipe->pool.parameter_set_info);
if (acc)
- ipu3_css_pool_put(&css_pipe->pool.acc);
+ imgu_css_pool_put(&css_pipe->pool.acc);
if (gdc)
- ipu3_css_pool_put(&css_pipe->pool.gdc);
+ imgu_css_pool_put(&css_pipe->pool.gdc);
if (obgrid)
- ipu3_css_pool_put(&css_pipe->pool.obgrid);
+ imgu_css_pool_put(&css_pipe->pool.obgrid);
if (vmem0)
- ipu3_css_pool_put(
+ imgu_css_pool_put(
&css_pipe->pool.binary_params_p
[IMGU_ABI_MEM_ISP_VMEM0]);
if (dmem0)
- ipu3_css_pool_put(
+ imgu_css_pool_put(
&css_pipe->pool.binary_params_p
[IMGU_ABI_MEM_ISP_DMEM0]);
@@ -2346,7 +2346,7 @@ fail_no_put:
return r;
}
-int ipu3_css_irq_ack(struct ipu3_css *css)
+int imgu_css_irq_ack(struct imgu_css *css)
{
static const int NUM_SWIRQS = 3;
struct imgu_fw_info *bi = &css->fwp->binary_header[css->fw_sp[0]];
diff --git a/drivers/staging/media/ipu3/ipu3-css.h b/drivers/staging/media/ipu3/ipu3-css.h
index e88d60f1a0c3..6b8bab27ab1f 100644
--- a/drivers/staging/media/ipu3/ipu3-css.h
+++ b/drivers/staging/media/ipu3/ipu3-css.h
@@ -43,7 +43,7 @@
* The pipe id type, distinguishes the kind of pipes that
* can be run in parallel.
*/
-enum ipu3_css_pipe_id {
+enum imgu_css_pipe_id {
IPU3_CSS_PIPE_ID_PREVIEW,
IPU3_CSS_PIPE_ID_COPY,
IPU3_CSS_PIPE_ID_VIDEO,
@@ -53,29 +53,29 @@ enum ipu3_css_pipe_id {
IPU3_CSS_PIPE_ID_NUM
};
-struct ipu3_css_resolution {
+struct imgu_css_resolution {
u32 w;
u32 h;
};
-enum ipu3_css_buffer_state {
+enum imgu_css_buffer_state {
IPU3_CSS_BUFFER_NEW, /* Not yet queued */
IPU3_CSS_BUFFER_QUEUED, /* Queued, waiting to be filled */
IPU3_CSS_BUFFER_DONE, /* Finished processing, removed from queue */
IPU3_CSS_BUFFER_FAILED, /* Was not processed, removed from queue */
};
-struct ipu3_css_buffer {
+struct imgu_css_buffer {
/* Private fields: user doesn't touch */
dma_addr_t daddr;
unsigned int queue;
- enum ipu3_css_buffer_state state;
+ enum imgu_css_buffer_state state;
struct list_head list;
u8 queue_pos;
unsigned int pipe;
};
-struct ipu3_css_format {
+struct imgu_css_format {
u32 pixelformat;
enum v4l2_colorspace colorspace;
enum imgu_abi_frame_format frame_format;
@@ -89,22 +89,22 @@ struct ipu3_css_format {
u8 flags;
};
-struct ipu3_css_queue {
+struct imgu_css_queue {
union {
struct v4l2_pix_format_mplane mpix;
struct v4l2_meta_format meta;
} fmt;
- const struct ipu3_css_format *css_fmt;
+ const struct imgu_css_format *css_fmt;
unsigned int width_pad;
struct list_head bufs;
};
-struct ipu3_css_pipe {
- enum ipu3_css_pipe_id pipe_id;
+struct imgu_css_pipe {
+ enum imgu_css_pipe_id pipe_id;
unsigned int bindex;
- struct ipu3_css_queue queue[IPU3_CSS_QUEUES];
+ struct imgu_css_queue queue[IPU3_CSS_QUEUES];
struct v4l2_rect rect[IPU3_CSS_RECTS];
bool vf_output_en;
@@ -112,21 +112,21 @@ struct ipu3_css_pipe {
spinlock_t qlock;
/* Data structures shared with IMGU and driver, always allocated */
- struct ipu3_css_map sp_ddr_ptrs;
- struct ipu3_css_map xmem_sp_stage_ptrs[IPU3_CSS_PIPE_ID_NUM]
+ struct imgu_css_map sp_ddr_ptrs;
+ struct imgu_css_map xmem_sp_stage_ptrs[IPU3_CSS_PIPE_ID_NUM]
[IMGU_ABI_MAX_STAGES];
- struct ipu3_css_map xmem_isp_stage_ptrs[IPU3_CSS_PIPE_ID_NUM]
+ struct imgu_css_map xmem_isp_stage_ptrs[IPU3_CSS_PIPE_ID_NUM]
[IMGU_ABI_MAX_STAGES];
/*
* Data structures shared with IMGU and driver, binary specific.
* PARAM_CLASS_CONFIG and PARAM_CLASS_STATE parameters.
*/
- struct ipu3_css_map binary_params_cs[IMGU_ABI_PARAM_CLASS_NUM - 1]
+ struct imgu_css_map binary_params_cs[IMGU_ABI_PARAM_CLASS_NUM - 1]
[IMGU_ABI_NUM_MEMORIES];
struct {
- struct ipu3_css_map mem[IPU3_CSS_AUX_FRAMES];
+ struct imgu_css_map mem[IPU3_CSS_AUX_FRAMES];
unsigned int width;
unsigned int height;
unsigned int bytesperline;
@@ -134,76 +134,76 @@ struct ipu3_css_pipe {
} aux_frames[IPU3_CSS_AUX_FRAME_TYPES];
struct {
- struct ipu3_css_pool parameter_set_info;
- struct ipu3_css_pool acc;
- struct ipu3_css_pool gdc;
- struct ipu3_css_pool obgrid;
+ struct imgu_css_pool parameter_set_info;
+ struct imgu_css_pool acc;
+ struct imgu_css_pool gdc;
+ struct imgu_css_pool obgrid;
/* PARAM_CLASS_PARAM parameters for binding while streaming */
- struct ipu3_css_pool binary_params_p[IMGU_ABI_NUM_MEMORIES];
+ struct imgu_css_pool binary_params_p[IMGU_ABI_NUM_MEMORIES];
} pool;
- struct ipu3_css_map abi_buffers[IPU3_CSS_QUEUES]
+ struct imgu_css_map abi_buffers[IPU3_CSS_QUEUES]
[IMGU_ABI_HOST2SP_BUFQ_SIZE];
};
/* IPU3 Camera Sub System structure */
-struct ipu3_css {
+struct imgu_css {
struct device *dev;
void __iomem *base;
const struct firmware *fw;
struct imgu_fw_header *fwp;
int iomem_length;
int fw_bl, fw_sp[IMGU_NUM_SP]; /* Indices of bl and SP binaries */
- struct ipu3_css_map *binary; /* fw binaries mapped to device */
+ struct imgu_css_map *binary; /* fw binaries mapped to device */
bool streaming; /* true when streaming is enabled */
- struct ipu3_css_pipe pipes[IMGU_MAX_PIPE_NUM];
- struct ipu3_css_map xmem_sp_group_ptrs;
+ struct imgu_css_pipe pipes[IMGU_MAX_PIPE_NUM];
+ struct imgu_css_map xmem_sp_group_ptrs;
/* enabled pipe(s) */
DECLARE_BITMAP(enabled_pipes, IMGU_MAX_PIPE_NUM);
};
/******************* css v4l *******************/
-int ipu3_css_init(struct device *dev, struct ipu3_css *css,
+int imgu_css_init(struct device *dev, struct imgu_css *css,
void __iomem *base, int length);
-void ipu3_css_cleanup(struct ipu3_css *css);
-int ipu3_css_fmt_try(struct ipu3_css *css,
+void imgu_css_cleanup(struct imgu_css *css);
+int imgu_css_fmt_try(struct imgu_css *css,
struct v4l2_pix_format_mplane *fmts[IPU3_CSS_QUEUES],
struct v4l2_rect *rects[IPU3_CSS_RECTS],
unsigned int pipe);
-int ipu3_css_fmt_set(struct ipu3_css *css,
+int imgu_css_fmt_set(struct imgu_css *css,
struct v4l2_pix_format_mplane *fmts[IPU3_CSS_QUEUES],
struct v4l2_rect *rects[IPU3_CSS_RECTS],
unsigned int pipe);
-int ipu3_css_meta_fmt_set(struct v4l2_meta_format *fmt);
-int ipu3_css_buf_queue(struct ipu3_css *css, unsigned int pipe,
- struct ipu3_css_buffer *b);
-struct ipu3_css_buffer *ipu3_css_buf_dequeue(struct ipu3_css *css);
-int ipu3_css_start_streaming(struct ipu3_css *css);
-void ipu3_css_stop_streaming(struct ipu3_css *css);
-bool ipu3_css_queue_empty(struct ipu3_css *css);
-bool ipu3_css_is_streaming(struct ipu3_css *css);
-bool ipu3_css_pipe_queue_empty(struct ipu3_css *css, unsigned int pipe);
+int imgu_css_meta_fmt_set(struct v4l2_meta_format *fmt);
+int imgu_css_buf_queue(struct imgu_css *css, unsigned int pipe,
+ struct imgu_css_buffer *b);
+struct imgu_css_buffer *imgu_css_buf_dequeue(struct imgu_css *css);
+int imgu_css_start_streaming(struct imgu_css *css);
+void imgu_css_stop_streaming(struct imgu_css *css);
+bool imgu_css_queue_empty(struct imgu_css *css);
+bool imgu_css_is_streaming(struct imgu_css *css);
+bool imgu_css_pipe_queue_empty(struct imgu_css *css, unsigned int pipe);
/******************* css hw *******************/
-int ipu3_css_set_powerup(struct device *dev, void __iomem *base);
-void ipu3_css_set_powerdown(struct device *dev, void __iomem *base);
-int ipu3_css_irq_ack(struct ipu3_css *css);
+int imgu_css_set_powerup(struct device *dev, void __iomem *base);
+void imgu_css_set_powerdown(struct device *dev, void __iomem *base);
+int imgu_css_irq_ack(struct imgu_css *css);
/******************* set parameters ************/
-int ipu3_css_set_parameters(struct ipu3_css *css, unsigned int pipe,
+int imgu_css_set_parameters(struct imgu_css *css, unsigned int pipe,
struct ipu3_uapi_params *set_params);
/******************* auxiliary helpers *******************/
-static inline enum ipu3_css_buffer_state
-ipu3_css_buf_state(struct ipu3_css_buffer *b)
+static inline enum imgu_css_buffer_state
+imgu_css_buf_state(struct imgu_css_buffer *b)
{
return b->state;
}
/* Initialize given buffer. May be called several times. */
-static inline void ipu3_css_buf_init(struct ipu3_css_buffer *b,
+static inline void imgu_css_buf_init(struct imgu_css_buffer *b,
unsigned int queue, dma_addr_t daddr)
{
b->state = IPU3_CSS_BUFFER_NEW;
diff --git a/drivers/staging/media/ipu3/ipu3-dmamap.c b/drivers/staging/media/ipu3/ipu3-dmamap.c
index 93a393d4e15e..d978a00e1e0b 100644
--- a/drivers/staging/media/ipu3/ipu3-dmamap.c
+++ b/drivers/staging/media/ipu3/ipu3-dmamap.c
@@ -12,11 +12,12 @@
#include "ipu3.h"
#include "ipu3-css-pool.h"
#include "ipu3-mmu.h"
+#include "ipu3-dmamap.h"
/*
- * Free a buffer allocated by ipu3_dmamap_alloc_buffer()
+ * Free a buffer allocated by imgu_dmamap_alloc_buffer()
*/
-static void ipu3_dmamap_free_buffer(struct page **pages,
+static void imgu_dmamap_free_buffer(struct page **pages,
size_t size)
{
int count = size >> PAGE_SHIFT;
@@ -30,7 +31,7 @@ static void ipu3_dmamap_free_buffer(struct page **pages,
* Based on the implementation of __iommu_dma_alloc_pages()
* defined in drivers/iommu/dma-iommu.c
*/
-static struct page **ipu3_dmamap_alloc_buffer(size_t size,
+static struct page **imgu_dmamap_alloc_buffer(size_t size,
unsigned long order_mask,
gfp_t gfp)
{
@@ -73,7 +74,7 @@ static struct page **ipu3_dmamap_alloc_buffer(size_t size,
__free_pages(page, order);
}
if (!page) {
- ipu3_dmamap_free_buffer(pages, i << PAGE_SHIFT);
+ imgu_dmamap_free_buffer(pages, i << PAGE_SHIFT);
return NULL;
}
count -= order_size;
@@ -85,7 +86,7 @@ static struct page **ipu3_dmamap_alloc_buffer(size_t size,
}
/**
- * ipu3_dmamap_alloc - allocate and map a buffer into KVA
+ * imgu_dmamap_alloc - allocate and map a buffer into KVA
* @imgu: struct device pointer
* @map: struct to store mapping variables
* @len: size required
@@ -94,7 +95,7 @@ static struct page **ipu3_dmamap_alloc_buffer(size_t size,
* KVA on success
* %NULL on failure
*/
-void *ipu3_dmamap_alloc(struct imgu_device *imgu, struct ipu3_css_map *map,
+void *imgu_dmamap_alloc(struct imgu_device *imgu, struct imgu_css_map *map,
size_t len)
{
unsigned long shift = iova_shift(&imgu->iova_domain);
@@ -113,7 +114,7 @@ void *ipu3_dmamap_alloc(struct imgu_device *imgu, struct ipu3_css_map *map,
if (!iova)
return NULL;
- pages = ipu3_dmamap_alloc_buffer(size, alloc_sizes >> PAGE_SHIFT,
+ pages = imgu_dmamap_alloc_buffer(size, alloc_sizes >> PAGE_SHIFT,
GFP_KERNEL);
if (!pages)
goto out_free_iova;
@@ -121,7 +122,7 @@ void *ipu3_dmamap_alloc(struct imgu_device *imgu, struct ipu3_css_map *map,
/* Call IOMMU driver to setup pgt */
iovaddr = iova_dma_addr(&imgu->iova_domain, iova);
for (i = 0; i < size / PAGE_SIZE; ++i) {
- rval = ipu3_mmu_map(imgu->mmu, iovaddr,
+ rval = imgu_mmu_map(imgu->mmu, iovaddr,
page_to_phys(pages[i]), PAGE_SIZE);
if (rval)
goto out_unmap;
@@ -152,8 +153,8 @@ out_vunmap:
vunmap(map->vma->addr);
out_unmap:
- ipu3_dmamap_free_buffer(pages, size);
- ipu3_mmu_unmap(imgu->mmu, iova_dma_addr(&imgu->iova_domain, iova),
+ imgu_dmamap_free_buffer(pages, size);
+ imgu_mmu_unmap(imgu->mmu, iova_dma_addr(&imgu->iova_domain, iova),
i * PAGE_SIZE);
map->vma = NULL;
@@ -163,7 +164,7 @@ out_free_iova:
return NULL;
}
-void ipu3_dmamap_unmap(struct imgu_device *imgu, struct ipu3_css_map *map)
+void imgu_dmamap_unmap(struct imgu_device *imgu, struct imgu_css_map *map)
{
struct iova *iova;
@@ -172,16 +173,16 @@ void ipu3_dmamap_unmap(struct imgu_device *imgu, struct ipu3_css_map *map)
if (WARN_ON(!iova))
return;
- ipu3_mmu_unmap(imgu->mmu, iova_dma_addr(&imgu->iova_domain, iova),
+ imgu_mmu_unmap(imgu->mmu, iova_dma_addr(&imgu->iova_domain, iova),
iova_size(iova) << iova_shift(&imgu->iova_domain));
__free_iova(&imgu->iova_domain, iova);
}
/*
- * Counterpart of ipu3_dmamap_alloc
+ * Counterpart of imgu_dmamap_alloc
*/
-void ipu3_dmamap_free(struct imgu_device *imgu, struct ipu3_css_map *map)
+void imgu_dmamap_free(struct imgu_device *imgu, struct imgu_css_map *map)
{
struct vm_struct *area = map->vma;
@@ -191,18 +192,18 @@ void ipu3_dmamap_free(struct imgu_device *imgu, struct ipu3_css_map *map)
if (!map->vaddr)
return;
- ipu3_dmamap_unmap(imgu, map);
+ imgu_dmamap_unmap(imgu, map);
if (WARN_ON(!area) || WARN_ON(!area->pages))
return;
- ipu3_dmamap_free_buffer(area->pages, map->size);
+ imgu_dmamap_free_buffer(area->pages, map->size);
vunmap(map->vaddr);
map->vaddr = NULL;
}
-int ipu3_dmamap_map_sg(struct imgu_device *imgu, struct scatterlist *sglist,
- int nents, struct ipu3_css_map *map)
+int imgu_dmamap_map_sg(struct imgu_device *imgu, struct scatterlist *sglist,
+ int nents, struct imgu_css_map *map)
{
unsigned long shift = iova_shift(&imgu->iova_domain);
struct scatterlist *sg;
@@ -232,7 +233,7 @@ int ipu3_dmamap_map_sg(struct imgu_device *imgu, struct scatterlist *sglist,
dev_dbg(&imgu->pci_dev->dev, "dmamap: iova low pfn %lu, high pfn %lu\n",
iova->pfn_lo, iova->pfn_hi);
- if (ipu3_mmu_map_sg(imgu->mmu, iova_dma_addr(&imgu->iova_domain, iova),
+ if (imgu_mmu_map_sg(imgu->mmu, iova_dma_addr(&imgu->iova_domain, iova),
sglist, nents) < size)
goto out_fail;
@@ -248,7 +249,7 @@ out_fail:
return -EFAULT;
}
-int ipu3_dmamap_init(struct imgu_device *imgu)
+int imgu_dmamap_init(struct imgu_device *imgu)
{
unsigned long order, base_pfn;
int ret = iova_cache_get();
@@ -263,7 +264,7 @@ int ipu3_dmamap_init(struct imgu_device *imgu)
return 0;
}
-void ipu3_dmamap_exit(struct imgu_device *imgu)
+void imgu_dmamap_exit(struct imgu_device *imgu)
{
put_iova_domain(&imgu->iova_domain);
iova_cache_put();
diff --git a/drivers/staging/media/ipu3/ipu3-dmamap.h b/drivers/staging/media/ipu3/ipu3-dmamap.h
index b9d224a33273..9db513b3d603 100644
--- a/drivers/staging/media/ipu3/ipu3-dmamap.h
+++ b/drivers/staging/media/ipu3/ipu3-dmamap.h
@@ -8,15 +8,15 @@
struct imgu_device;
struct scatterlist;
-void *ipu3_dmamap_alloc(struct imgu_device *imgu, struct ipu3_css_map *map,
+void *imgu_dmamap_alloc(struct imgu_device *imgu, struct imgu_css_map *map,
size_t len);
-void ipu3_dmamap_free(struct imgu_device *imgu, struct ipu3_css_map *map);
+void imgu_dmamap_free(struct imgu_device *imgu, struct imgu_css_map *map);
-int ipu3_dmamap_map_sg(struct imgu_device *imgu, struct scatterlist *sglist,
- int nents, struct ipu3_css_map *map);
-void ipu3_dmamap_unmap(struct imgu_device *imgu, struct ipu3_css_map *map);
+int imgu_dmamap_map_sg(struct imgu_device *imgu, struct scatterlist *sglist,
+ int nents, struct imgu_css_map *map);
+void imgu_dmamap_unmap(struct imgu_device *imgu, struct imgu_css_map *map);
-int ipu3_dmamap_init(struct imgu_device *imgu);
-void ipu3_dmamap_exit(struct imgu_device *imgu);
+int imgu_dmamap_init(struct imgu_device *imgu);
+void imgu_dmamap_exit(struct imgu_device *imgu);
#endif
diff --git a/drivers/staging/media/ipu3/ipu3-mmu.c b/drivers/staging/media/ipu3/ipu3-mmu.c
index b9f209541f78..cfc2bdfb14b3 100644
--- a/drivers/staging/media/ipu3/ipu3-mmu.c
+++ b/drivers/staging/media/ipu3/ipu3-mmu.c
@@ -48,7 +48,7 @@
#define REG_GP_HALT (IMGU_REG_BASE + 0x5dc)
#define REG_GP_HALTED (IMGU_REG_BASE + 0x5e0)
-struct ipu3_mmu {
+struct imgu_mmu {
struct device *dev;
void __iomem *base;
/* protect access to l2pts, l1pt */
@@ -63,28 +63,28 @@ struct ipu3_mmu {
u32 **l2pts;
u32 *l1pt;
- struct ipu3_mmu_info geometry;
+ struct imgu_mmu_info geometry;
};
-static inline struct ipu3_mmu *to_ipu3_mmu(struct ipu3_mmu_info *info)
+static inline struct imgu_mmu *to_imgu_mmu(struct imgu_mmu_info *info)
{
- return container_of(info, struct ipu3_mmu, geometry);
+ return container_of(info, struct imgu_mmu, geometry);
}
/**
- * ipu3_mmu_tlb_invalidate - invalidate translation look-aside buffer
+ * imgu_mmu_tlb_invalidate - invalidate translation look-aside buffer
* @mmu: MMU to perform the invalidate operation on
*
* This function invalidates the whole TLB. Must be called when the hardware
* is powered on.
*/
-static void ipu3_mmu_tlb_invalidate(struct ipu3_mmu *mmu)
+static void imgu_mmu_tlb_invalidate(struct imgu_mmu *mmu)
{
writel(TLB_INVALIDATE, mmu->base + REG_TLB_INVALIDATE);
}
-static void call_if_ipu3_is_powered(struct ipu3_mmu *mmu,
- void (*func)(struct ipu3_mmu *mmu))
+static void call_if_imgu_is_powered(struct imgu_mmu *mmu,
+ void (*func)(struct imgu_mmu *mmu))
{
if (!pm_runtime_get_if_in_use(mmu->dev))
return;
@@ -94,14 +94,14 @@ static void call_if_ipu3_is_powered(struct ipu3_mmu *mmu,
}
/**
- * ipu3_mmu_set_halt - set CIO gate halt bit
+ * imgu_mmu_set_halt - set CIO gate halt bit
* @mmu: MMU to set the CIO gate bit in.
* @halt: Desired state of the gate bit.
*
* This function sets the CIO gate bit that controls whether external memory
* accesses are allowed. Must be called when the hardware is powered on.
*/
-static void ipu3_mmu_set_halt(struct ipu3_mmu *mmu, bool halt)
+static void imgu_mmu_set_halt(struct imgu_mmu *mmu, bool halt)
{
int ret;
u32 val;
@@ -116,12 +116,12 @@ static void ipu3_mmu_set_halt(struct ipu3_mmu *mmu, bool halt)
}
/**
- * ipu3_mmu_alloc_page_table - allocate a pre-filled page table
+ * imgu_mmu_alloc_page_table - allocate a pre-filled page table
* @pteval: Value to initialize for page table entries with.
*
* Return: Pointer to allocated page table or NULL on failure.
*/
-static u32 *ipu3_mmu_alloc_page_table(u32 pteval)
+static u32 *imgu_mmu_alloc_page_table(u32 pteval)
{
u32 *pt;
int pte;
@@ -139,10 +139,10 @@ static u32 *ipu3_mmu_alloc_page_table(u32 pteval)
}
/**
- * ipu3_mmu_free_page_table - free page table
+ * imgu_mmu_free_page_table - free page table
* @pt: Page table to free.
*/
-static void ipu3_mmu_free_page_table(u32 *pt)
+static void imgu_mmu_free_page_table(u32 *pt)
{
set_memory_wb((unsigned long int)pt, IPU3_PT_ORDER);
free_page((unsigned long)pt);
@@ -168,7 +168,7 @@ static inline void address_to_pte_idx(unsigned long iova, u32 *l1pt_idx,
*l1pt_idx = iova & IPU3_L1PT_MASK;
}
-static u32 *ipu3_mmu_get_l2pt(struct ipu3_mmu *mmu, u32 l1pt_idx)
+static u32 *imgu_mmu_get_l2pt(struct imgu_mmu *mmu, u32 l1pt_idx)
{
unsigned long flags;
u32 *l2pt, *new_l2pt;
@@ -182,7 +182,7 @@ static u32 *ipu3_mmu_get_l2pt(struct ipu3_mmu *mmu, u32 l1pt_idx)
spin_unlock_irqrestore(&mmu->lock, flags);
- new_l2pt = ipu3_mmu_alloc_page_table(mmu->dummy_page_pteval);
+ new_l2pt = imgu_mmu_alloc_page_table(mmu->dummy_page_pteval);
if (!new_l2pt)
return NULL;
@@ -193,7 +193,7 @@ static u32 *ipu3_mmu_get_l2pt(struct ipu3_mmu *mmu, u32 l1pt_idx)
l2pt = mmu->l2pts[l1pt_idx];
if (l2pt) {
- ipu3_mmu_free_page_table(new_l2pt);
+ imgu_mmu_free_page_table(new_l2pt);
goto done;
}
@@ -208,7 +208,7 @@ done:
return l2pt;
}
-static int __ipu3_mmu_map(struct ipu3_mmu *mmu, unsigned long iova,
+static int __imgu_mmu_map(struct imgu_mmu *mmu, unsigned long iova,
phys_addr_t paddr)
{
u32 l1pt_idx, l2pt_idx;
@@ -220,7 +220,7 @@ static int __ipu3_mmu_map(struct ipu3_mmu *mmu, unsigned long iova,
address_to_pte_idx(iova, &l1pt_idx, &l2pt_idx);
- l2pt = ipu3_mmu_get_l2pt(mmu, l1pt_idx);
+ l2pt = imgu_mmu_get_l2pt(mmu, l1pt_idx);
if (!l2pt)
return -ENOMEM;
@@ -238,11 +238,11 @@ static int __ipu3_mmu_map(struct ipu3_mmu *mmu, unsigned long iova,
return 0;
}
-/**
+/*
* The following four functions are implemented based on iommu.c
* drivers/iommu/iommu.c/iommu_pgsize().
*/
-static size_t ipu3_mmu_pgsize(unsigned long pgsize_bitmap,
+static size_t imgu_mmu_pgsize(unsigned long pgsize_bitmap,
unsigned long addr_merge, size_t size)
{
unsigned int pgsize_idx;
@@ -276,10 +276,10 @@ static size_t ipu3_mmu_pgsize(unsigned long pgsize_bitmap,
}
/* drivers/iommu/iommu.c/iommu_map() */
-int ipu3_mmu_map(struct ipu3_mmu_info *info, unsigned long iova,
+int imgu_mmu_map(struct imgu_mmu_info *info, unsigned long iova,
phys_addr_t paddr, size_t size)
{
- struct ipu3_mmu *mmu = to_ipu3_mmu(info);
+ struct imgu_mmu *mmu = to_imgu_mmu(info);
unsigned int min_pagesz;
int ret = 0;
@@ -301,13 +301,13 @@ int ipu3_mmu_map(struct ipu3_mmu_info *info, unsigned long iova,
iova, &paddr, size);
while (size) {
- size_t pgsize = ipu3_mmu_pgsize(mmu->geometry.pgsize_bitmap,
+ size_t pgsize = imgu_mmu_pgsize(mmu->geometry.pgsize_bitmap,
iova | paddr, size);
dev_dbg(mmu->dev, "mapping: iova 0x%lx pa %pa pgsize 0x%zx\n",
iova, &paddr, pgsize);
- ret = __ipu3_mmu_map(mmu, iova, paddr);
+ ret = __imgu_mmu_map(mmu, iova, paddr);
if (ret)
break;
@@ -316,16 +316,16 @@ int ipu3_mmu_map(struct ipu3_mmu_info *info, unsigned long iova,
size -= pgsize;
}
- call_if_ipu3_is_powered(mmu, ipu3_mmu_tlb_invalidate);
+ call_if_imgu_is_powered(mmu, imgu_mmu_tlb_invalidate);
return ret;
}
/* drivers/iommu/iommu.c/default_iommu_map_sg() */
-size_t ipu3_mmu_map_sg(struct ipu3_mmu_info *info, unsigned long iova,
+size_t imgu_mmu_map_sg(struct imgu_mmu_info *info, unsigned long iova,
struct scatterlist *sg, unsigned int nents)
{
- struct ipu3_mmu *mmu = to_ipu3_mmu(info);
+ struct imgu_mmu *mmu = to_imgu_mmu(info);
struct scatterlist *s;
size_t s_length, mapped = 0;
unsigned int i, min_pagesz;
@@ -345,25 +345,25 @@ size_t ipu3_mmu_map_sg(struct ipu3_mmu_info *info, unsigned long iova,
if (i == nents - 1 && !IS_ALIGNED(s->length, min_pagesz))
s_length = PAGE_ALIGN(s->length);
- ret = ipu3_mmu_map(info, iova + mapped, phys, s_length);
+ ret = imgu_mmu_map(info, iova + mapped, phys, s_length);
if (ret)
goto out_err;
mapped += s_length;
}
- call_if_ipu3_is_powered(mmu, ipu3_mmu_tlb_invalidate);
+ call_if_imgu_is_powered(mmu, imgu_mmu_tlb_invalidate);
return mapped;
out_err:
/* undo mappings already done */
- ipu3_mmu_unmap(info, iova, mapped);
+ imgu_mmu_unmap(info, iova, mapped);
return 0;
}
-static size_t __ipu3_mmu_unmap(struct ipu3_mmu *mmu,
+static size_t __imgu_mmu_unmap(struct imgu_mmu *mmu,
unsigned long iova, size_t size)
{
u32 l1pt_idx, l2pt_idx;
@@ -395,10 +395,10 @@ static size_t __ipu3_mmu_unmap(struct ipu3_mmu *mmu,
}
/* drivers/iommu/iommu.c/iommu_unmap() */
-size_t ipu3_mmu_unmap(struct ipu3_mmu_info *info, unsigned long iova,
+size_t imgu_mmu_unmap(struct imgu_mmu_info *info, unsigned long iova,
size_t size)
{
- struct ipu3_mmu *mmu = to_ipu3_mmu(info);
+ struct imgu_mmu *mmu = to_imgu_mmu(info);
size_t unmapped_page, unmapped = 0;
unsigned int min_pagesz;
@@ -423,10 +423,10 @@ size_t ipu3_mmu_unmap(struct ipu3_mmu_info *info, unsigned long iova,
* or we hit an area that isn't mapped.
*/
while (unmapped < size) {
- size_t pgsize = ipu3_mmu_pgsize(mmu->geometry.pgsize_bitmap,
+ size_t pgsize = imgu_mmu_pgsize(mmu->geometry.pgsize_bitmap,
iova, size - unmapped);
- unmapped_page = __ipu3_mmu_unmap(mmu, iova, pgsize);
+ unmapped_page = __imgu_mmu_unmap(mmu, iova, pgsize);
if (!unmapped_page)
break;
@@ -437,20 +437,21 @@ size_t ipu3_mmu_unmap(struct ipu3_mmu_info *info, unsigned long iova,
unmapped += unmapped_page;
}
- call_if_ipu3_is_powered(mmu, ipu3_mmu_tlb_invalidate);
+ call_if_imgu_is_powered(mmu, imgu_mmu_tlb_invalidate);
return unmapped;
}
/**
- * ipu3_mmu_init() - initialize IPU3 MMU block
+ * imgu_mmu_init() - initialize IPU3 MMU block
+ * @parent: struct device parent
* @base: IOMEM base of hardware registers.
*
* Return: Pointer to IPU3 MMU private data pointer or ERR_PTR() on error.
*/
-struct ipu3_mmu_info *ipu3_mmu_init(struct device *parent, void __iomem *base)
+struct imgu_mmu_info *imgu_mmu_init(struct device *parent, void __iomem *base)
{
- struct ipu3_mmu *mmu;
+ struct imgu_mmu *mmu;
u32 pteval;
mmu = kzalloc(sizeof(*mmu), GFP_KERNEL);
@@ -462,7 +463,7 @@ struct ipu3_mmu_info *ipu3_mmu_init(struct device *parent, void __iomem *base)
spin_lock_init(&mmu->lock);
/* Disallow external memory access when having no valid page tables. */
- ipu3_mmu_set_halt(mmu, true);
+ imgu_mmu_set_halt(mmu, true);
/*
* The MMU does not have a "valid" bit, so we have to use a dummy
@@ -478,7 +479,7 @@ struct ipu3_mmu_info *ipu3_mmu_init(struct device *parent, void __iomem *base)
* Allocate a dummy L2 page table with all entries pointing to
* the dummy page.
*/
- mmu->dummy_l2pt = ipu3_mmu_alloc_page_table(pteval);
+ mmu->dummy_l2pt = imgu_mmu_alloc_page_table(pteval);
if (!mmu->dummy_l2pt)
goto fail_dummy_page;
pteval = IPU3_ADDR2PTE(virt_to_phys(mmu->dummy_l2pt));
@@ -493,14 +494,14 @@ struct ipu3_mmu_info *ipu3_mmu_init(struct device *parent, void __iomem *base)
goto fail_l2pt;
/* Allocate the L1 page table. */
- mmu->l1pt = ipu3_mmu_alloc_page_table(mmu->dummy_l2pt_pteval);
+ mmu->l1pt = imgu_mmu_alloc_page_table(mmu->dummy_l2pt_pteval);
if (!mmu->l1pt)
goto fail_l2pts;
pteval = IPU3_ADDR2PTE(virt_to_phys(mmu->l1pt));
writel(pteval, mmu->base + REG_L1_PHYS);
- ipu3_mmu_tlb_invalidate(mmu);
- ipu3_mmu_set_halt(mmu, false);
+ imgu_mmu_tlb_invalidate(mmu);
+ imgu_mmu_set_halt(mmu, false);
mmu->geometry.aperture_start = 0;
mmu->geometry.aperture_end = DMA_BIT_MASK(IPU3_MMU_ADDRESS_BITS);
@@ -511,7 +512,7 @@ struct ipu3_mmu_info *ipu3_mmu_init(struct device *parent, void __iomem *base)
fail_l2pts:
vfree(mmu->l2pts);
fail_l2pt:
- ipu3_mmu_free_page_table(mmu->dummy_l2pt);
+ imgu_mmu_free_page_table(mmu->dummy_l2pt);
fail_dummy_page:
free_page((unsigned long)mmu->dummy_page);
fail_group:
@@ -521,41 +522,41 @@ fail_group:
}
/**
- * ipu3_mmu_exit() - clean up IPU3 MMU block
- * @mmu: IPU3 MMU private data
+ * imgu_mmu_exit() - clean up IPU3 MMU block
+ * @info: IPU3 MMU private data
*/
-void ipu3_mmu_exit(struct ipu3_mmu_info *info)
+void imgu_mmu_exit(struct imgu_mmu_info *info)
{
- struct ipu3_mmu *mmu = to_ipu3_mmu(info);
+ struct imgu_mmu *mmu = to_imgu_mmu(info);
/* We are going to free our page tables, no more memory access. */
- ipu3_mmu_set_halt(mmu, true);
- ipu3_mmu_tlb_invalidate(mmu);
+ imgu_mmu_set_halt(mmu, true);
+ imgu_mmu_tlb_invalidate(mmu);
- ipu3_mmu_free_page_table(mmu->l1pt);
+ imgu_mmu_free_page_table(mmu->l1pt);
vfree(mmu->l2pts);
- ipu3_mmu_free_page_table(mmu->dummy_l2pt);
+ imgu_mmu_free_page_table(mmu->dummy_l2pt);
free_page((unsigned long)mmu->dummy_page);
kfree(mmu);
}
-void ipu3_mmu_suspend(struct ipu3_mmu_info *info)
+void imgu_mmu_suspend(struct imgu_mmu_info *info)
{
- struct ipu3_mmu *mmu = to_ipu3_mmu(info);
+ struct imgu_mmu *mmu = to_imgu_mmu(info);
- ipu3_mmu_set_halt(mmu, true);
+ imgu_mmu_set_halt(mmu, true);
}
-void ipu3_mmu_resume(struct ipu3_mmu_info *info)
+void imgu_mmu_resume(struct imgu_mmu_info *info)
{
- struct ipu3_mmu *mmu = to_ipu3_mmu(info);
+ struct imgu_mmu *mmu = to_imgu_mmu(info);
u32 pteval;
- ipu3_mmu_set_halt(mmu, true);
+ imgu_mmu_set_halt(mmu, true);
pteval = IPU3_ADDR2PTE(virt_to_phys(mmu->l1pt));
writel(pteval, mmu->base + REG_L1_PHYS);
- ipu3_mmu_tlb_invalidate(mmu);
- ipu3_mmu_set_halt(mmu, false);
+ imgu_mmu_tlb_invalidate(mmu);
+ imgu_mmu_set_halt(mmu, false);
}
diff --git a/drivers/staging/media/ipu3/ipu3-mmu.h b/drivers/staging/media/ipu3/ipu3-mmu.h
index 8fe63b4c6e1c..fa58827eb19c 100644
--- a/drivers/staging/media/ipu3/ipu3-mmu.h
+++ b/drivers/staging/media/ipu3/ipu3-mmu.h
@@ -6,13 +6,13 @@
#define __IPU3_MMU_H
/**
- * struct ipu3_mmu_info - Describes mmu geometry
+ * struct imgu_mmu_info - Describes mmu geometry
*
* @aperture_start: First address that can be mapped
* @aperture_end: Last address that can be mapped
* @pgsize_bitmap: Bitmap of page sizes in use
*/
-struct ipu3_mmu_info {
+struct imgu_mmu_info {
dma_addr_t aperture_start;
dma_addr_t aperture_end;
unsigned long pgsize_bitmap;
@@ -21,15 +21,15 @@ struct ipu3_mmu_info {
struct device;
struct scatterlist;
-struct ipu3_mmu_info *ipu3_mmu_init(struct device *parent, void __iomem *base);
-void ipu3_mmu_exit(struct ipu3_mmu_info *info);
-void ipu3_mmu_suspend(struct ipu3_mmu_info *info);
-void ipu3_mmu_resume(struct ipu3_mmu_info *info);
+struct imgu_mmu_info *imgu_mmu_init(struct device *parent, void __iomem *base);
+void imgu_mmu_exit(struct imgu_mmu_info *info);
+void imgu_mmu_suspend(struct imgu_mmu_info *info);
+void imgu_mmu_resume(struct imgu_mmu_info *info);
-int ipu3_mmu_map(struct ipu3_mmu_info *info, unsigned long iova,
+int imgu_mmu_map(struct imgu_mmu_info *info, unsigned long iova,
phys_addr_t paddr, size_t size);
-size_t ipu3_mmu_unmap(struct ipu3_mmu_info *info, unsigned long iova,
+size_t imgu_mmu_unmap(struct imgu_mmu_info *info, unsigned long iova,
size_t size);
-size_t ipu3_mmu_map_sg(struct ipu3_mmu_info *info, unsigned long iova,
+size_t imgu_mmu_map_sg(struct imgu_mmu_info *info, unsigned long iova,
struct scatterlist *sg, unsigned int nents);
#endif
diff --git a/drivers/staging/media/ipu3/ipu3-tables.c b/drivers/staging/media/ipu3/ipu3-tables.c
index 334517987eba..3a3730bd4395 100644
--- a/drivers/staging/media/ipu3/ipu3-tables.c
+++ b/drivers/staging/media/ipu3/ipu3-tables.c
@@ -5,8 +5,8 @@
#define X 0 /* Don't care value */
-const struct ipu3_css_bds_config
- ipu3_css_bds_configs[IMGU_BDS_CONFIG_LEN] = { {
+const struct imgu_css_bds_config
+ imgu_css_bds_configs[IMGU_BDS_CONFIG_LEN] = { {
/* Scale factor 32 / (32 + 0) = 1 */
.hor_phase_arr = {
.even = { { 0, 0, 64, 6, 0, 0, 0 } },
@@ -9015,7 +9015,7 @@ const struct ipu3_css_bds_config
.ver_ds_en = 1
} };
-const s32 ipu3_css_downscale_4taps[IMGU_SCALER_DOWNSCALE_4TAPS_LEN] = {
+const s32 imgu_css_downscale_4taps[IMGU_SCALER_DOWNSCALE_4TAPS_LEN] = {
IMGU_SCALER_FP * -0.000000000000000,
IMGU_SCALER_FP * -0.000249009327023,
IMGU_SCALER_FP * -0.001022241683322,
@@ -9146,7 +9146,7 @@ const s32 ipu3_css_downscale_4taps[IMGU_SCALER_DOWNSCALE_4TAPS_LEN] = {
IMGU_SCALER_FP * -0.000249009327023
};
-const s32 ipu3_css_downscale_2taps[IMGU_SCALER_DOWNSCALE_2TAPS_LEN] = {
+const s32 imgu_css_downscale_2taps[IMGU_SCALER_DOWNSCALE_2TAPS_LEN] = {
IMGU_SCALER_FP * 0.074300676367033,
IMGU_SCALER_FP * 0.094030234498392,
IMGU_SCALER_FP * 0.115522859526596,
@@ -9214,7 +9214,7 @@ const s32 ipu3_css_downscale_2taps[IMGU_SCALER_DOWNSCALE_2TAPS_LEN] = {
};
/* settings for Geometric Distortion Correction */
-const s16 ipu3_css_gdc_lut[4][256] = { {
+const s16 imgu_css_gdc_lut[4][256] = { {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -2, -2, -2,
-2, -3, -3, -3, -4, -4, -4, -5, -5, -5, -6, -6, -7, -7, -7, -8, -8,
-9, -9, -10, -10, -11, -11, -12, -12, -13, -13, -14, -14, -15, -15,
@@ -9292,7 +9292,7 @@ const s16 ipu3_css_gdc_lut[4][256] = { {
-1, 0, 1, 0, 0, 0, 0, 0, 0, 0
} };
-const struct ipu3_css_xnr3_vmem_defaults ipu3_css_xnr3_vmem_defaults = {
+const struct imgu_css_xnr3_vmem_defaults imgu_css_xnr3_vmem_defaults = {
.x = {
1024, 1164, 1320, 1492, 1680, 1884, 2108, 2352,
2616, 2900, 3208, 3540, 3896, 4276, 4684, 5120
@@ -9311,7 +9311,7 @@ const struct ipu3_css_xnr3_vmem_defaults ipu3_css_xnr3_vmem_defaults = {
};
/* settings for Bayer Noise Reduction */
-const struct ipu3_uapi_bnr_static_config ipu3_css_bnr_defaults = {
+const struct ipu3_uapi_bnr_static_config imgu_css_bnr_defaults = {
{ 16, 16, 16, 16 }, /* wb_gains */
{ 16, 16, 16, 16 }, /* wb_gains_thr */
{ 0, X, 8, 6, X, 14 }, /* thr_coeffs */
@@ -9327,18 +9327,18 @@ const struct ipu3_uapi_bnr_static_config ipu3_css_bnr_defaults = {
{ 8, 4, 4, X, 8, X, 1, 1, 1, 1 }, /* dn_detect_ctrl */
};
-const struct ipu3_uapi_dm_config ipu3_css_dm_defaults = {
+const struct ipu3_uapi_dm_config imgu_css_dm_defaults = {
1, 1, 1, X, X, 8, X, 7, X, 8, X, 8, X, 4, X
};
-const struct ipu3_uapi_ccm_mat_config ipu3_css_ccm_defaults = {
+const struct ipu3_uapi_ccm_mat_config imgu_css_ccm_defaults = {
9775, -2671, 1087, 0,
-1071, 8303, 815, 0,
-23, -7887, 16103, 0
};
/* settings for Gamma correction */
-const struct ipu3_uapi_gamma_corr_lut ipu3_css_gamma_lut = { {
+const struct ipu3_uapi_gamma_corr_lut imgu_css_gamma_lut = { {
63, 79, 95, 111, 127, 143, 159, 175, 191, 207, 223, 239, 255, 271, 287,
303, 319, 335, 351, 367, 383, 399, 415, 431, 447, 463, 479, 495, 511,
527, 543, 559, 575, 591, 607, 623, 639, 655, 671, 687, 703, 719, 735,
@@ -9362,13 +9362,13 @@ const struct ipu3_uapi_gamma_corr_lut ipu3_css_gamma_lut = { {
7807, 7871, 7935, 7999, 8063, 8127, 8191
} };
-const struct ipu3_uapi_csc_mat_config ipu3_css_csc_defaults = {
+const struct ipu3_uapi_csc_mat_config imgu_css_csc_defaults = {
4898, 9617, 1867, 0,
-2410, -4732, 7143, 0,
10076, -8437, -1638, 0
};
-const struct ipu3_uapi_cds_params ipu3_css_cds_defaults = {
+const struct ipu3_uapi_cds_params imgu_css_cds_defaults = {
1, 3, 3, 1,
1, 3, 3, 1,
4, X, /* ds_nf */
@@ -9376,7 +9376,7 @@ const struct ipu3_uapi_cds_params ipu3_css_cds_defaults = {
0, X /* uv_bin_output */
};
-const struct ipu3_uapi_shd_config_static ipu3_css_shd_defaults = {
+const struct ipu3_uapi_shd_config_static imgu_css_shd_defaults = {
.grid = {
.width = 73,
.height = 55,
@@ -9397,7 +9397,7 @@ const struct ipu3_uapi_shd_config_static ipu3_css_shd_defaults = {
},
};
-const struct ipu3_uapi_yuvp1_iefd_config ipu3_css_iefd_defaults = {
+const struct ipu3_uapi_yuvp1_iefd_config imgu_css_iefd_defaults = {
.units = {
.cu_1 = { 0, 150, 7, 0 },
.cu_ed = { 7, 110, 244, X, 307, 409, 511, X,
@@ -9436,17 +9436,17 @@ const struct ipu3_uapi_yuvp1_iefd_config ipu3_css_iefd_defaults = {
{ 1, X, 2, X, 8, X } },
};
-const struct ipu3_uapi_yuvp1_yds_config ipu3_css_yds_defaults = {
+const struct ipu3_uapi_yuvp1_yds_config imgu_css_yds_defaults = {
0, 1, 1, 0, 0, 1, 1, 0, 2, X, 0, X
};
-const struct ipu3_uapi_yuvp1_chnr_config ipu3_css_chnr_defaults = {
+const struct ipu3_uapi_yuvp1_chnr_config imgu_css_chnr_defaults = {
.coring = { 0, X, 0, X },
.sense_gain = { 6, 6, 6, X, 4, 4, 4, X },
.iir_fir = { 8, X, 12, X, 0, 256 - 127, X },
};
-const struct ipu3_uapi_yuvp1_y_ee_nr_config ipu3_css_y_ee_nr_defaults = {
+const struct ipu3_uapi_yuvp1_y_ee_nr_config imgu_css_y_ee_nr_defaults = {
.lpf = { 4, X, 8, X, 16, X, 0 },
.sense = { 8191, X, 0, X, 8191, X, 0, X },
.gain = { 8, X, 0, X, 8, X, 0, X },
@@ -9457,7 +9457,7 @@ const struct ipu3_uapi_yuvp1_y_ee_nr_config ipu3_css_y_ee_nr_defaults = {
};
const struct ipu3_uapi_yuvp2_tcc_gain_pcwl_lut_static_config
- ipu3_css_tcc_gain_pcwl_lut = { {
+ imgu_css_tcc_gain_pcwl_lut = { {
1024, 1032, 1040, 1048, 1057, 1065, 1073, 1081, 1089, 1097, 1105, 1113,
1122, 1130, 1138, 1146, 1154, 1162, 1170, 1178, 1187, 1195, 1203, 1211,
1219, 1227, 1235, 1243, 1252, 1260, 1268, 1276, 1284, 1292, 1300, 1308,
@@ -9483,12 +9483,12 @@ const struct ipu3_uapi_yuvp2_tcc_gain_pcwl_lut_static_config
} };
const struct ipu3_uapi_yuvp2_tcc_r_sqr_lut_static_config
- ipu3_css_tcc_r_sqr_lut = { {
+ imgu_css_tcc_r_sqr_lut = { {
32, 44, 64, 92, 128, 180, 256, 364, 512, 628, 724, 808, 888,
956, 1024, 1088, 1144, 1200, 1256, 1304, 1356, 1404, 1448
} };
-const struct imgu_abi_anr_config ipu3_css_anr_defaults = {
+const struct imgu_abi_anr_config imgu_css_anr_defaults = {
.transform = {
.adaptive_treshhold_en = 1,
.alpha = { { 13, 13, 13, 13, 0, 0, 0, 0},
@@ -9545,7 +9545,7 @@ const struct imgu_abi_anr_config ipu3_css_anr_defaults = {
};
/* frame settings for Auto White Balance */
-const struct ipu3_uapi_awb_fr_config_s ipu3_css_awb_fr_defaults = {
+const struct ipu3_uapi_awb_fr_config_s imgu_css_awb_fr_defaults = {
.grid_cfg = {
.width = 16,
.height = 16,
@@ -9560,7 +9560,7 @@ const struct ipu3_uapi_awb_fr_config_s ipu3_css_awb_fr_defaults = {
};
/* settings for Auto Exposure */
-const struct ipu3_uapi_ae_grid_config ipu3_css_ae_grid_defaults = {
+const struct ipu3_uapi_ae_grid_config imgu_css_ae_grid_defaults = {
.width = 16,
.height = 16,
.block_width_log2 = 3,
@@ -9571,13 +9571,13 @@ const struct ipu3_uapi_ae_grid_config ipu3_css_ae_grid_defaults = {
};
/* settings for Auto Exposure color correction matrix */
-const struct ipu3_uapi_ae_ccm ipu3_css_ae_ccm_defaults = {
+const struct ipu3_uapi_ae_ccm imgu_css_ae_ccm_defaults = {
256, 256, 256, 256, /* gain_gr/r/b/gb */
.mat = { 128, 0, 0, 0, 0, 128, 0, 0, 0, 0, 128, 0, 0, 0, 0, 128 },
};
/* settings for Auto Focus */
-const struct ipu3_uapi_af_config_s ipu3_css_af_defaults = {
+const struct ipu3_uapi_af_config_s imgu_css_af_defaults = {
.filter_config = {
{ 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 128 }, 0,
{ 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 128 }, 0,
@@ -9595,7 +9595,7 @@ const struct ipu3_uapi_af_config_s ipu3_css_af_defaults = {
};
/* settings for Auto White Balance */
-const struct ipu3_uapi_awb_config_s ipu3_css_awb_defaults = {
+const struct ipu3_uapi_awb_config_s imgu_css_awb_defaults = {
8191, 8191, 8191, 8191 | /* rgbs_thr_gr/r/gb/b */
IPU3_UAPI_AWB_RGBS_THR_B_EN | IPU3_UAPI_AWB_RGBS_THR_B_INCL_SAT,
.grid = {
diff --git a/drivers/staging/media/ipu3/ipu3-tables.h b/drivers/staging/media/ipu3/ipu3-tables.h
index 6563782cbd22..a1bf3286f380 100644
--- a/drivers/staging/media/ipu3/ipu3-tables.h
+++ b/drivers/staging/media/ipu3/ipu3-tables.h
@@ -19,7 +19,7 @@
#define IMGU_GDC_LUT_UNIT 4
#define IMGU_GDC_LUT_LEN 256
-struct ipu3_css_bds_config {
+struct imgu_css_bds_config {
struct imgu_abi_bds_phase_arr hor_phase_arr;
struct imgu_abi_bds_phase_arr ver_phase_arr;
struct imgu_abi_bds_ptrn_arr ptrn_arr;
@@ -28,39 +28,39 @@ struct ipu3_css_bds_config {
u8 ver_ds_en;
};
-struct ipu3_css_xnr3_vmem_defaults {
+struct imgu_css_xnr3_vmem_defaults {
s16 x[IMGU_XNR3_VMEM_LUT_LEN];
s16 a[IMGU_XNR3_VMEM_LUT_LEN];
s16 b[IMGU_XNR3_VMEM_LUT_LEN];
s16 c[IMGU_XNR3_VMEM_LUT_LEN];
};
-extern const struct ipu3_css_bds_config
- ipu3_css_bds_configs[IMGU_BDS_CONFIG_LEN];
-extern const s32 ipu3_css_downscale_4taps[IMGU_SCALER_DOWNSCALE_4TAPS_LEN];
-extern const s32 ipu3_css_downscale_2taps[IMGU_SCALER_DOWNSCALE_2TAPS_LEN];
-extern const s16 ipu3_css_gdc_lut[IMGU_GDC_LUT_UNIT][IMGU_GDC_LUT_LEN];
-extern const struct ipu3_css_xnr3_vmem_defaults ipu3_css_xnr3_vmem_defaults;
-extern const struct ipu3_uapi_bnr_static_config ipu3_css_bnr_defaults;
-extern const struct ipu3_uapi_dm_config ipu3_css_dm_defaults;
-extern const struct ipu3_uapi_ccm_mat_config ipu3_css_ccm_defaults;
-extern const struct ipu3_uapi_gamma_corr_lut ipu3_css_gamma_lut;
-extern const struct ipu3_uapi_csc_mat_config ipu3_css_csc_defaults;
-extern const struct ipu3_uapi_cds_params ipu3_css_cds_defaults;
-extern const struct ipu3_uapi_shd_config_static ipu3_css_shd_defaults;
-extern const struct ipu3_uapi_yuvp1_iefd_config ipu3_css_iefd_defaults;
-extern const struct ipu3_uapi_yuvp1_yds_config ipu3_css_yds_defaults;
-extern const struct ipu3_uapi_yuvp1_chnr_config ipu3_css_chnr_defaults;
-extern const struct ipu3_uapi_yuvp1_y_ee_nr_config ipu3_css_y_ee_nr_defaults;
+extern const struct imgu_css_bds_config
+ imgu_css_bds_configs[IMGU_BDS_CONFIG_LEN];
+extern const s32 imgu_css_downscale_4taps[IMGU_SCALER_DOWNSCALE_4TAPS_LEN];
+extern const s32 imgu_css_downscale_2taps[IMGU_SCALER_DOWNSCALE_2TAPS_LEN];
+extern const s16 imgu_css_gdc_lut[IMGU_GDC_LUT_UNIT][IMGU_GDC_LUT_LEN];
+extern const struct imgu_css_xnr3_vmem_defaults imgu_css_xnr3_vmem_defaults;
+extern const struct ipu3_uapi_bnr_static_config imgu_css_bnr_defaults;
+extern const struct ipu3_uapi_dm_config imgu_css_dm_defaults;
+extern const struct ipu3_uapi_ccm_mat_config imgu_css_ccm_defaults;
+extern const struct ipu3_uapi_gamma_corr_lut imgu_css_gamma_lut;
+extern const struct ipu3_uapi_csc_mat_config imgu_css_csc_defaults;
+extern const struct ipu3_uapi_cds_params imgu_css_cds_defaults;
+extern const struct ipu3_uapi_shd_config_static imgu_css_shd_defaults;
+extern const struct ipu3_uapi_yuvp1_iefd_config imgu_css_iefd_defaults;
+extern const struct ipu3_uapi_yuvp1_yds_config imgu_css_yds_defaults;
+extern const struct ipu3_uapi_yuvp1_chnr_config imgu_css_chnr_defaults;
+extern const struct ipu3_uapi_yuvp1_y_ee_nr_config imgu_css_y_ee_nr_defaults;
extern const struct ipu3_uapi_yuvp2_tcc_gain_pcwl_lut_static_config
- ipu3_css_tcc_gain_pcwl_lut;
+ imgu_css_tcc_gain_pcwl_lut;
extern const struct ipu3_uapi_yuvp2_tcc_r_sqr_lut_static_config
- ipu3_css_tcc_r_sqr_lut;
-extern const struct imgu_abi_anr_config ipu3_css_anr_defaults;
-extern const struct ipu3_uapi_awb_fr_config_s ipu3_css_awb_fr_defaults;
-extern const struct ipu3_uapi_ae_grid_config ipu3_css_ae_grid_defaults;
-extern const struct ipu3_uapi_ae_ccm ipu3_css_ae_ccm_defaults;
-extern const struct ipu3_uapi_af_config_s ipu3_css_af_defaults;
-extern const struct ipu3_uapi_awb_config_s ipu3_css_awb_defaults;
+ imgu_css_tcc_r_sqr_lut;
+extern const struct imgu_abi_anr_config imgu_css_anr_defaults;
+extern const struct ipu3_uapi_awb_fr_config_s imgu_css_awb_fr_defaults;
+extern const struct ipu3_uapi_ae_grid_config imgu_css_ae_grid_defaults;
+extern const struct ipu3_uapi_ae_ccm imgu_css_ae_ccm_defaults;
+extern const struct ipu3_uapi_af_config_s imgu_css_af_defaults;
+extern const struct ipu3_uapi_awb_config_s imgu_css_awb_defaults;
#endif
diff --git a/drivers/staging/media/ipu3/ipu3-v4l2.c b/drivers/staging/media/ipu3/ipu3-v4l2.c
index c7936032beb9..9c0352b193a7 100644
--- a/drivers/staging/media/ipu3/ipu3-v4l2.c
+++ b/drivers/staging/media/ipu3/ipu3-v4l2.c
@@ -12,7 +12,10 @@
/******************** v4l2_subdev_ops ********************/
-static int ipu3_subdev_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+#define IPU3_RUNNING_MODE_VIDEO 0
+#define IPU3_RUNNING_MODE_STILL 1
+
+static int imgu_subdev_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
{
struct imgu_v4l2_subdev *imgu_sd = container_of(sd,
struct imgu_v4l2_subdev,
@@ -47,7 +50,7 @@ static int ipu3_subdev_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
return 0;
}
-static int ipu3_subdev_s_stream(struct v4l2_subdev *sd, int enable)
+static int imgu_subdev_s_stream(struct v4l2_subdev *sd, int enable)
{
int i;
unsigned int node;
@@ -60,7 +63,7 @@ static int ipu3_subdev_s_stream(struct v4l2_subdev *sd, int enable)
struct device *dev = &imgu->pci_dev->dev;
struct v4l2_pix_format_mplane *fmts[IPU3_CSS_QUEUES] = { NULL };
struct v4l2_rect *rects[IPU3_CSS_RECTS] = { NULL };
- struct ipu3_css_pipe *css_pipe = &imgu->css.pipes[pipe];
+ struct imgu_css_pipe *css_pipe = &imgu->css.pipes[pipe];
struct imgu_media_pipe *imgu_pipe = &imgu->imgu_pipe[pipe];
dev_dbg(dev, "%s %d for pipe %d", __func__, enable, pipe);
@@ -104,7 +107,7 @@ static int ipu3_subdev_s_stream(struct v4l2_subdev *sd, int enable)
rects[IPU3_CSS_RECT_BDS] = &imgu_sd->rect.bds;
rects[IPU3_CSS_RECT_GDC] = &imgu_sd->rect.gdc;
- r = ipu3_css_fmt_set(&imgu->css, fmts, rects, pipe);
+ r = imgu_css_fmt_set(&imgu->css, fmts, rects, pipe);
if (r) {
dev_err(dev, "failed to set initial formats pipe %d with (%d)",
pipe, r);
@@ -116,7 +119,7 @@ static int ipu3_subdev_s_stream(struct v4l2_subdev *sd, int enable)
return 0;
}
-static int ipu3_subdev_get_fmt(struct v4l2_subdev *sd,
+static int imgu_subdev_get_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *fmt)
{
@@ -140,7 +143,7 @@ static int ipu3_subdev_get_fmt(struct v4l2_subdev *sd,
return 0;
}
-static int ipu3_subdev_set_fmt(struct v4l2_subdev *sd,
+static int imgu_subdev_set_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *fmt)
{
@@ -186,7 +189,7 @@ static int ipu3_subdev_set_fmt(struct v4l2_subdev *sd,
return 0;
}
-static int ipu3_subdev_get_selection(struct v4l2_subdev *sd,
+static int imgu_subdev_get_selection(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_selection *sel)
{
@@ -219,7 +222,7 @@ static int ipu3_subdev_get_selection(struct v4l2_subdev *sd,
return 0;
}
-static int ipu3_subdev_set_selection(struct v4l2_subdev *sd,
+static int imgu_subdev_set_selection(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_selection *sel)
{
@@ -260,7 +263,7 @@ static int ipu3_subdev_set_selection(struct v4l2_subdev *sd,
/******************** media_entity_operations ********************/
-static int ipu3_link_setup(struct media_entity *entity,
+static int imgu_link_setup(struct media_entity *entity,
const struct media_pad *local,
const struct media_pad *remote, u32 flags)
{
@@ -299,7 +302,7 @@ static int ipu3_link_setup(struct media_entity *entity,
/******************** vb2_ops ********************/
-static int ipu3_vb2_buf_init(struct vb2_buffer *vb)
+static int imgu_vb2_buf_init(struct vb2_buffer *vb)
{
struct sg_table *sg = vb2_dma_sg_plane_desc(vb, 0);
struct imgu_device *imgu = vb2_get_drv_priv(vb->vb2_queue);
@@ -312,11 +315,11 @@ static int ipu3_vb2_buf_init(struct vb2_buffer *vb)
if (queue == IPU3_CSS_QUEUE_PARAMS)
return 0;
- return ipu3_dmamap_map_sg(imgu, sg->sgl, sg->nents, &buf->map);
+ return imgu_dmamap_map_sg(imgu, sg->sgl, sg->nents, &buf->map);
}
/* Called when each buffer is freed */
-static void ipu3_vb2_buf_cleanup(struct vb2_buffer *vb)
+static void imgu_vb2_buf_cleanup(struct vb2_buffer *vb)
{
struct imgu_device *imgu = vb2_get_drv_priv(vb->vb2_queue);
struct imgu_buffer *buf = container_of(vb,
@@ -328,11 +331,11 @@ static void ipu3_vb2_buf_cleanup(struct vb2_buffer *vb)
if (queue == IPU3_CSS_QUEUE_PARAMS)
return;
- ipu3_dmamap_unmap(imgu, &buf->map);
+ imgu_dmamap_unmap(imgu, &buf->map);
}
/* Transfer buffer ownership to me */
-static void ipu3_vb2_buf_queue(struct vb2_buffer *vb)
+static void imgu_vb2_buf_queue(struct vb2_buffer *vb)
{
struct imgu_device *imgu = vb2_get_drv_priv(vb->vb2_queue);
struct imgu_video_device *node =
@@ -358,7 +361,7 @@ static void ipu3_vb2_buf_queue(struct vb2_buffer *vb)
vb2_set_plane_payload(vb, 0, payload);
}
if (payload >= need_bytes)
- r = ipu3_css_set_parameters(&imgu->css, pipe,
+ r = imgu_css_set_parameters(&imgu->css, pipe,
vb2_plane_vaddr(vb, 0));
buf->flags = V4L2_BUF_FLAG_DONE;
vb2_buffer_done(vb, r == 0 ? VB2_BUF_STATE_DONE
@@ -369,7 +372,7 @@ static void ipu3_vb2_buf_queue(struct vb2_buffer *vb)
vid_buf.vbb.vb2_buf);
mutex_lock(&imgu->lock);
- ipu3_css_buf_init(&buf->css_buf, queue, buf->map.daddr);
+ imgu_css_buf_init(&buf->css_buf, queue, buf->map.daddr);
list_add_tail(&buf->vid_buf.list,
&node->buffers);
mutex_unlock(&imgu->lock);
@@ -385,7 +388,7 @@ static void ipu3_vb2_buf_queue(struct vb2_buffer *vb)
}
-static int ipu3_vb2_queue_setup(struct vb2_queue *vq,
+static int imgu_vb2_queue_setup(struct vb2_queue *vq,
unsigned int *num_buffers,
unsigned int *num_planes,
unsigned int sizes[],
@@ -422,7 +425,7 @@ static int ipu3_vb2_queue_setup(struct vb2_queue *vq,
}
/* Check if all enabled video nodes are streaming, exception ignored */
-static bool ipu3_all_nodes_streaming(struct imgu_device *imgu,
+static bool imgu_all_nodes_streaming(struct imgu_device *imgu,
struct imgu_video_device *except)
{
unsigned int i, pipe, p;
@@ -451,11 +454,11 @@ static bool ipu3_all_nodes_streaming(struct imgu_device *imgu,
return true;
}
-static void ipu3_return_all_buffers(struct imgu_device *imgu,
+static void imgu_return_all_buffers(struct imgu_device *imgu,
struct imgu_video_device *node,
enum vb2_buffer_state state)
{
- struct ipu3_vb2_buffer *b, *b0;
+ struct imgu_vb2_buffer *b, *b0;
/* Return all buffers */
mutex_lock(&imgu->lock);
@@ -466,7 +469,7 @@ static void ipu3_return_all_buffers(struct imgu_device *imgu,
mutex_unlock(&imgu->lock);
}
-static int ipu3_vb2_start_streaming(struct vb2_queue *vq, unsigned int count)
+static int imgu_vb2_start_streaming(struct vb2_queue *vq, unsigned int count)
{
struct imgu_media_pipe *imgu_pipe;
struct imgu_device *imgu = vb2_get_drv_priv(vq);
@@ -497,7 +500,7 @@ static int ipu3_vb2_start_streaming(struct vb2_queue *vq, unsigned int count)
goto fail_return_bufs;
- if (!ipu3_all_nodes_streaming(imgu, node))
+ if (!imgu_all_nodes_streaming(imgu, node))
return 0;
for_each_set_bit(pipe, imgu->css.enabled_pipes, IMGU_MAX_PIPE_NUM) {
@@ -518,12 +521,12 @@ static int ipu3_vb2_start_streaming(struct vb2_queue *vq, unsigned int count)
fail_stop_pipeline:
media_pipeline_stop(&node->vdev.entity);
fail_return_bufs:
- ipu3_return_all_buffers(imgu, node, VB2_BUF_STATE_QUEUED);
+ imgu_return_all_buffers(imgu, node, VB2_BUF_STATE_QUEUED);
return r;
}
-static void ipu3_vb2_stop_streaming(struct vb2_queue *vq)
+static void imgu_vb2_stop_streaming(struct vb2_queue *vq)
{
struct imgu_media_pipe *imgu_pipe;
struct imgu_device *imgu = vb2_get_drv_priv(vq);
@@ -544,7 +547,7 @@ static void ipu3_vb2_stop_streaming(struct vb2_queue *vq)
"failed to stop subdev streaming\n");
/* Was this the first node with streaming disabled? */
- if (imgu->streaming && ipu3_all_nodes_streaming(imgu, node)) {
+ if (imgu->streaming && imgu_all_nodes_streaming(imgu, node)) {
/* Yes, really stop streaming now */
dev_dbg(dev, "IMGU streaming is ready to stop");
r = imgu_s_stream(imgu, false);
@@ -552,7 +555,7 @@ static void ipu3_vb2_stop_streaming(struct vb2_queue *vq)
imgu->streaming = false;
}
- ipu3_return_all_buffers(imgu, node, VB2_BUF_STATE_ERROR);
+ imgu_return_all_buffers(imgu, node, VB2_BUF_STATE_ERROR);
media_pipeline_stop(&node->vdev.entity);
}
@@ -563,13 +566,13 @@ static void ipu3_vb2_stop_streaming(struct vb2_queue *vq)
#define DEF_VID_CAPTURE 0
#define DEF_VID_OUTPUT 1
-struct ipu3_fmt {
+struct imgu_fmt {
u32 fourcc;
u16 type; /* VID_CAPTURE or VID_OUTPUT not both */
};
/* format descriptions for capture and preview */
-static const struct ipu3_fmt formats[] = {
+static const struct imgu_fmt formats[] = {
{ V4L2_PIX_FMT_NV12, VID_CAPTURE },
{ V4L2_PIX_FMT_IPU3_SGRBG10, VID_OUTPUT },
{ V4L2_PIX_FMT_IPU3_SBGGR10, VID_OUTPUT },
@@ -578,7 +581,7 @@ static const struct ipu3_fmt formats[] = {
};
/* Find the first matched format, return default if not found */
-static const struct ipu3_fmt *find_format(struct v4l2_format *f, u32 type)
+static const struct imgu_fmt *find_format(struct v4l2_format *f, u32 type)
{
unsigned int i;
@@ -592,10 +595,10 @@ static const struct ipu3_fmt *find_format(struct v4l2_format *f, u32 type)
&formats[DEF_VID_OUTPUT];
}
-static int ipu3_vidioc_querycap(struct file *file, void *fh,
+static int imgu_vidioc_querycap(struct file *file, void *fh,
struct v4l2_capability *cap)
{
- struct imgu_video_device *node = file_to_intel_ipu3_node(file);
+ struct imgu_video_device *node = file_to_intel_imgu_node(file);
strscpy(cap->driver, IMGU_NAME, sizeof(cap->driver));
strscpy(cap->card, IMGU_NAME, sizeof(cap->card));
@@ -643,10 +646,10 @@ static int vidioc_enum_fmt_vid_out(struct file *file, void *priv,
}
/* Propagate forward always the format from the CIO2 subdev */
-static int ipu3_vidioc_g_fmt(struct file *file, void *fh,
+static int imgu_vidioc_g_fmt(struct file *file, void *fh,
struct v4l2_format *f)
{
- struct imgu_video_device *node = file_to_intel_ipu3_node(file);
+ struct imgu_video_device *node = file_to_intel_imgu_node(file);
f->fmt = node->vdev_fmt.fmt;
@@ -667,7 +670,7 @@ static int imgu_fmt(struct imgu_device *imgu, unsigned int pipe, int node,
struct v4l2_mbus_framefmt pad_fmt;
unsigned int i, css_q;
int r;
- struct ipu3_css_pipe *css_pipe = &imgu->css.pipes[pipe];
+ struct imgu_css_pipe *css_pipe = &imgu->css.pipes[pipe];
struct imgu_media_pipe *imgu_pipe = &imgu->imgu_pipe[pipe];
struct imgu_v4l2_subdev *imgu_sd = &imgu_pipe->imgu_sd;
@@ -733,9 +736,9 @@ static int imgu_fmt(struct imgu_device *imgu, unsigned int pipe, int node,
return -EINVAL;
if (try)
- r = ipu3_css_fmt_try(&imgu->css, fmts, rects, pipe);
+ r = imgu_css_fmt_try(&imgu->css, fmts, rects, pipe);
else
- r = ipu3_css_fmt_set(&imgu->css, fmts, rects, pipe);
+ r = imgu_css_fmt_set(&imgu->css, fmts, rects, pipe);
/* r is the binary number in the firmware blob */
if (r < 0)
@@ -749,10 +752,10 @@ static int imgu_fmt(struct imgu_device *imgu, unsigned int pipe, int node,
return 0;
}
-static int ipu3_try_fmt(struct file *file, void *fh, struct v4l2_format *f)
+static int imgu_try_fmt(struct file *file, void *fh, struct v4l2_format *f)
{
struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp;
- const struct ipu3_fmt *fmt;
+ const struct imgu_fmt *fmt;
if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
fmt = find_format(f, VID_CAPTURE);
@@ -769,58 +772,58 @@ static int ipu3_try_fmt(struct file *file, void *fh, struct v4l2_format *f)
return 0;
}
-static int ipu3_vidioc_try_fmt(struct file *file, void *fh,
+static int imgu_vidioc_try_fmt(struct file *file, void *fh,
struct v4l2_format *f)
{
struct imgu_device *imgu = video_drvdata(file);
struct device *dev = &imgu->pci_dev->dev;
- struct imgu_video_device *node = file_to_intel_ipu3_node(file);
+ struct imgu_video_device *node = file_to_intel_imgu_node(file);
struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
int r;
dev_dbg(dev, "%s [%ux%u] for node %d\n", __func__,
pix_mp->width, pix_mp->height, node->id);
- r = ipu3_try_fmt(file, fh, f);
+ r = imgu_try_fmt(file, fh, f);
if (r)
return r;
return imgu_fmt(imgu, node->pipe, node->id, f, true);
}
-static int ipu3_vidioc_s_fmt(struct file *file, void *fh, struct v4l2_format *f)
+static int imgu_vidioc_s_fmt(struct file *file, void *fh, struct v4l2_format *f)
{
struct imgu_device *imgu = video_drvdata(file);
struct device *dev = &imgu->pci_dev->dev;
- struct imgu_video_device *node = file_to_intel_ipu3_node(file);
+ struct imgu_video_device *node = file_to_intel_imgu_node(file);
struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
int r;
dev_dbg(dev, "%s [%ux%u] for node %d\n", __func__,
pix_mp->width, pix_mp->height, node->id);
- r = ipu3_try_fmt(file, fh, f);
+ r = imgu_try_fmt(file, fh, f);
if (r)
return r;
return imgu_fmt(imgu, node->pipe, node->id, f, false);
}
-struct ipu3_meta_fmt {
+struct imgu_meta_fmt {
__u32 fourcc;
char *name;
};
/* From drivers/media/v4l2-core/v4l2-ioctl.c */
-static const struct ipu3_meta_fmt meta_fmts[] = {
+static const struct imgu_meta_fmt meta_fmts[] = {
{ V4L2_META_FMT_IPU3_PARAMS, "IPU3 processing parameters" },
{ V4L2_META_FMT_IPU3_STAT_3A, "IPU3 3A statistics" },
};
-static int ipu3_meta_enum_format(struct file *file, void *fh,
+static int imgu_meta_enum_format(struct file *file, void *fh,
struct v4l2_fmtdesc *fmt)
{
- struct imgu_video_device *node = file_to_intel_ipu3_node(file);
+ struct imgu_video_device *node = file_to_intel_imgu_node(file);
unsigned int i = fmt->type == V4L2_BUF_TYPE_META_OUTPUT ? 0 : 1;
/* Each node is dedicated to only one meta format */
@@ -833,10 +836,10 @@ static int ipu3_meta_enum_format(struct file *file, void *fh,
return 0;
}
-static int ipu3_vidioc_g_meta_fmt(struct file *file, void *fh,
+static int imgu_vidioc_g_meta_fmt(struct file *file, void *fh,
struct v4l2_format *f)
{
- struct imgu_video_device *node = file_to_intel_ipu3_node(file);
+ struct imgu_video_device *node = file_to_intel_imgu_node(file);
if (f->type != node->vbq.type)
return -EINVAL;
@@ -846,7 +849,7 @@ static int ipu3_vidioc_g_meta_fmt(struct file *file, void *fh,
return 0;
}
-static int ipu3_vidioc_enum_input(struct file *file, void *fh,
+static int imgu_vidioc_enum_input(struct file *file, void *fh,
struct v4l2_input *input)
{
if (input->index > 0)
@@ -857,19 +860,19 @@ static int ipu3_vidioc_enum_input(struct file *file, void *fh,
return 0;
}
-static int ipu3_vidioc_g_input(struct file *file, void *fh, unsigned int *input)
+static int imgu_vidioc_g_input(struct file *file, void *fh, unsigned int *input)
{
*input = 0;
return 0;
}
-static int ipu3_vidioc_s_input(struct file *file, void *fh, unsigned int input)
+static int imgu_vidioc_s_input(struct file *file, void *fh, unsigned int input)
{
return input == 0 ? 0 : -EINVAL;
}
-static int ipu3_vidioc_enum_output(struct file *file, void *fh,
+static int imgu_vidioc_enum_output(struct file *file, void *fh,
struct v4l2_output *output)
{
if (output->index > 0)
@@ -880,7 +883,7 @@ static int ipu3_vidioc_enum_output(struct file *file, void *fh,
return 0;
}
-static int ipu3_vidioc_g_output(struct file *file, void *fh,
+static int imgu_vidioc_g_output(struct file *file, void *fh,
unsigned int *output)
{
*output = 0;
@@ -888,7 +891,7 @@ static int ipu3_vidioc_g_output(struct file *file, void *fh,
return 0;
}
-static int ipu3_vidioc_s_output(struct file *file, void *fh,
+static int imgu_vidioc_s_output(struct file *file, void *fh,
unsigned int output)
{
return output == 0 ? 0 : -EINVAL;
@@ -896,54 +899,54 @@ static int ipu3_vidioc_s_output(struct file *file, void *fh,
/******************** function pointers ********************/
-static struct v4l2_subdev_internal_ops ipu3_subdev_internal_ops = {
- .open = ipu3_subdev_open,
+static struct v4l2_subdev_internal_ops imgu_subdev_internal_ops = {
+ .open = imgu_subdev_open,
};
-static const struct v4l2_subdev_core_ops ipu3_subdev_core_ops = {
+static const struct v4l2_subdev_core_ops imgu_subdev_core_ops = {
.subscribe_event = v4l2_ctrl_subdev_subscribe_event,
.unsubscribe_event = v4l2_event_subdev_unsubscribe,
};
-static const struct v4l2_subdev_video_ops ipu3_subdev_video_ops = {
- .s_stream = ipu3_subdev_s_stream,
+static const struct v4l2_subdev_video_ops imgu_subdev_video_ops = {
+ .s_stream = imgu_subdev_s_stream,
};
-static const struct v4l2_subdev_pad_ops ipu3_subdev_pad_ops = {
+static const struct v4l2_subdev_pad_ops imgu_subdev_pad_ops = {
.link_validate = v4l2_subdev_link_validate_default,
- .get_fmt = ipu3_subdev_get_fmt,
- .set_fmt = ipu3_subdev_set_fmt,
- .get_selection = ipu3_subdev_get_selection,
- .set_selection = ipu3_subdev_set_selection,
+ .get_fmt = imgu_subdev_get_fmt,
+ .set_fmt = imgu_subdev_set_fmt,
+ .get_selection = imgu_subdev_get_selection,
+ .set_selection = imgu_subdev_set_selection,
};
-static const struct v4l2_subdev_ops ipu3_subdev_ops = {
- .core = &ipu3_subdev_core_ops,
- .video = &ipu3_subdev_video_ops,
- .pad = &ipu3_subdev_pad_ops,
+static const struct v4l2_subdev_ops imgu_subdev_ops = {
+ .core = &imgu_subdev_core_ops,
+ .video = &imgu_subdev_video_ops,
+ .pad = &imgu_subdev_pad_ops,
};
-static const struct media_entity_operations ipu3_media_ops = {
- .link_setup = ipu3_link_setup,
+static const struct media_entity_operations imgu_media_ops = {
+ .link_setup = imgu_link_setup,
.link_validate = v4l2_subdev_link_validate,
};
/****************** vb2_ops of the Q ********************/
-static const struct vb2_ops ipu3_vb2_ops = {
- .buf_init = ipu3_vb2_buf_init,
- .buf_cleanup = ipu3_vb2_buf_cleanup,
- .buf_queue = ipu3_vb2_buf_queue,
- .queue_setup = ipu3_vb2_queue_setup,
- .start_streaming = ipu3_vb2_start_streaming,
- .stop_streaming = ipu3_vb2_stop_streaming,
+static const struct vb2_ops imgu_vb2_ops = {
+ .buf_init = imgu_vb2_buf_init,
+ .buf_cleanup = imgu_vb2_buf_cleanup,
+ .buf_queue = imgu_vb2_buf_queue,
+ .queue_setup = imgu_vb2_queue_setup,
+ .start_streaming = imgu_vb2_start_streaming,
+ .stop_streaming = imgu_vb2_stop_streaming,
.wait_prepare = vb2_ops_wait_prepare,
.wait_finish = vb2_ops_wait_finish,
};
/****************** v4l2_file_operations *****************/
-static const struct v4l2_file_operations ipu3_v4l2_fops = {
+static const struct v4l2_file_operations imgu_v4l2_fops = {
.unlocked_ioctl = video_ioctl2,
.open = v4l2_fh_open,
.release = vb2_fop_release,
@@ -953,26 +956,26 @@ static const struct v4l2_file_operations ipu3_v4l2_fops = {
/******************** v4l2_ioctl_ops ********************/
-static const struct v4l2_ioctl_ops ipu3_v4l2_ioctl_ops = {
- .vidioc_querycap = ipu3_vidioc_querycap,
+static const struct v4l2_ioctl_ops imgu_v4l2_ioctl_ops = {
+ .vidioc_querycap = imgu_vidioc_querycap,
.vidioc_enum_fmt_vid_cap_mplane = vidioc_enum_fmt_vid_cap,
- .vidioc_g_fmt_vid_cap_mplane = ipu3_vidioc_g_fmt,
- .vidioc_s_fmt_vid_cap_mplane = ipu3_vidioc_s_fmt,
- .vidioc_try_fmt_vid_cap_mplane = ipu3_vidioc_try_fmt,
+ .vidioc_g_fmt_vid_cap_mplane = imgu_vidioc_g_fmt,
+ .vidioc_s_fmt_vid_cap_mplane = imgu_vidioc_s_fmt,
+ .vidioc_try_fmt_vid_cap_mplane = imgu_vidioc_try_fmt,
.vidioc_enum_fmt_vid_out_mplane = vidioc_enum_fmt_vid_out,
- .vidioc_g_fmt_vid_out_mplane = ipu3_vidioc_g_fmt,
- .vidioc_s_fmt_vid_out_mplane = ipu3_vidioc_s_fmt,
- .vidioc_try_fmt_vid_out_mplane = ipu3_vidioc_try_fmt,
+ .vidioc_g_fmt_vid_out_mplane = imgu_vidioc_g_fmt,
+ .vidioc_s_fmt_vid_out_mplane = imgu_vidioc_s_fmt,
+ .vidioc_try_fmt_vid_out_mplane = imgu_vidioc_try_fmt,
- .vidioc_enum_output = ipu3_vidioc_enum_output,
- .vidioc_g_output = ipu3_vidioc_g_output,
- .vidioc_s_output = ipu3_vidioc_s_output,
+ .vidioc_enum_output = imgu_vidioc_enum_output,
+ .vidioc_g_output = imgu_vidioc_g_output,
+ .vidioc_s_output = imgu_vidioc_s_output,
- .vidioc_enum_input = ipu3_vidioc_enum_input,
- .vidioc_g_input = ipu3_vidioc_g_input,
- .vidioc_s_input = ipu3_vidioc_s_input,
+ .vidioc_enum_input = imgu_vidioc_enum_input,
+ .vidioc_g_input = imgu_vidioc_g_input,
+ .vidioc_s_input = imgu_vidioc_s_input,
/* buffer queue management */
.vidioc_reqbufs = vb2_ioctl_reqbufs,
@@ -986,20 +989,20 @@ static const struct v4l2_ioctl_ops ipu3_v4l2_ioctl_ops = {
.vidioc_expbuf = vb2_ioctl_expbuf,
};
-static const struct v4l2_ioctl_ops ipu3_v4l2_meta_ioctl_ops = {
- .vidioc_querycap = ipu3_vidioc_querycap,
+static const struct v4l2_ioctl_ops imgu_v4l2_meta_ioctl_ops = {
+ .vidioc_querycap = imgu_vidioc_querycap,
/* meta capture */
- .vidioc_enum_fmt_meta_cap = ipu3_meta_enum_format,
- .vidioc_g_fmt_meta_cap = ipu3_vidioc_g_meta_fmt,
- .vidioc_s_fmt_meta_cap = ipu3_vidioc_g_meta_fmt,
- .vidioc_try_fmt_meta_cap = ipu3_vidioc_g_meta_fmt,
+ .vidioc_enum_fmt_meta_cap = imgu_meta_enum_format,
+ .vidioc_g_fmt_meta_cap = imgu_vidioc_g_meta_fmt,
+ .vidioc_s_fmt_meta_cap = imgu_vidioc_g_meta_fmt,
+ .vidioc_try_fmt_meta_cap = imgu_vidioc_g_meta_fmt,
/* meta output */
- .vidioc_enum_fmt_meta_out = ipu3_meta_enum_format,
- .vidioc_g_fmt_meta_out = ipu3_vidioc_g_meta_fmt,
- .vidioc_s_fmt_meta_out = ipu3_vidioc_g_meta_fmt,
- .vidioc_try_fmt_meta_out = ipu3_vidioc_g_meta_fmt,
+ .vidioc_enum_fmt_meta_out = imgu_meta_enum_format,
+ .vidioc_g_fmt_meta_out = imgu_vidioc_g_meta_fmt,
+ .vidioc_s_fmt_meta_out = imgu_vidioc_g_meta_fmt,
+ .vidioc_try_fmt_meta_out = imgu_vidioc_g_meta_fmt,
.vidioc_reqbufs = vb2_ioctl_reqbufs,
.vidioc_create_bufs = vb2_ioctl_create_bufs,
@@ -1012,7 +1015,7 @@ static const struct v4l2_ioctl_ops ipu3_v4l2_meta_ioctl_ops = {
.vidioc_expbuf = vb2_ioctl_expbuf,
};
-static int ipu3_sd_s_ctrl(struct v4l2_ctrl *ctrl)
+static int imgu_sd_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct imgu_v4l2_subdev *imgu_sd =
container_of(ctrl->handler, struct imgu_v4l2_subdev, ctrl_handler);
@@ -1031,25 +1034,29 @@ static int ipu3_sd_s_ctrl(struct v4l2_ctrl *ctrl)
}
}
-static const struct v4l2_ctrl_ops ipu3_subdev_ctrl_ops = {
- .s_ctrl = ipu3_sd_s_ctrl,
+static const struct v4l2_ctrl_ops imgu_subdev_ctrl_ops = {
+ .s_ctrl = imgu_sd_s_ctrl,
+};
+
+static const char * const imgu_ctrl_mode_strings[] = {
+ "Video mode",
+ "Still mode",
};
-static const struct v4l2_ctrl_config ipu3_subdev_ctrl_mode = {
- .ops = &ipu3_subdev_ctrl_ops,
+static const struct v4l2_ctrl_config imgu_subdev_ctrl_mode = {
+ .ops = &imgu_subdev_ctrl_ops,
.id = V4L2_CID_INTEL_IPU3_MODE,
.name = "IPU3 Pipe Mode",
- .type = V4L2_CTRL_TYPE_INTEGER,
- .min = IPU3_RUNNING_MODE_VIDEO,
- .max = IPU3_RUNNING_MODE_STILL,
- .step = 1,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .max = ARRAY_SIZE(imgu_ctrl_mode_strings) - 1,
.def = IPU3_RUNNING_MODE_VIDEO,
+ .qmenu = imgu_ctrl_mode_strings,
};
/******************** Framework registration ********************/
/* helper function to config node's video properties */
-static void ipu3_node_to_v4l2(u32 node, struct video_device *vdev,
+static void imgu_node_to_v4l2(u32 node, struct video_device *vdev,
struct v4l2_format *f)
{
u32 cap;
@@ -1061,32 +1068,32 @@ static void ipu3_node_to_v4l2(u32 node, struct video_device *vdev,
case IMGU_NODE_IN:
cap = V4L2_CAP_VIDEO_OUTPUT_MPLANE;
f->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
- vdev->ioctl_ops = &ipu3_v4l2_ioctl_ops;
+ vdev->ioctl_ops = &imgu_v4l2_ioctl_ops;
break;
case IMGU_NODE_PARAMS:
cap = V4L2_CAP_META_OUTPUT;
f->type = V4L2_BUF_TYPE_META_OUTPUT;
f->fmt.meta.dataformat = V4L2_META_FMT_IPU3_PARAMS;
- vdev->ioctl_ops = &ipu3_v4l2_meta_ioctl_ops;
- ipu3_css_meta_fmt_set(&f->fmt.meta);
+ vdev->ioctl_ops = &imgu_v4l2_meta_ioctl_ops;
+ imgu_css_meta_fmt_set(&f->fmt.meta);
break;
case IMGU_NODE_STAT_3A:
cap = V4L2_CAP_META_CAPTURE;
f->type = V4L2_BUF_TYPE_META_CAPTURE;
f->fmt.meta.dataformat = V4L2_META_FMT_IPU3_STAT_3A;
- vdev->ioctl_ops = &ipu3_v4l2_meta_ioctl_ops;
- ipu3_css_meta_fmt_set(&f->fmt.meta);
+ vdev->ioctl_ops = &imgu_v4l2_meta_ioctl_ops;
+ imgu_css_meta_fmt_set(&f->fmt.meta);
break;
default:
cap = V4L2_CAP_VIDEO_CAPTURE_MPLANE;
f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
- vdev->ioctl_ops = &ipu3_v4l2_ioctl_ops;
+ vdev->ioctl_ops = &imgu_v4l2_ioctl_ops;
}
vdev->device_caps = V4L2_CAP_STREAMING | cap;
}
-static int ipu3_v4l2_subdev_register(struct imgu_device *imgu,
+static int imgu_v4l2_subdev_register(struct imgu_device *imgu,
struct imgu_v4l2_subdev *imgu_sd,
unsigned int pipe)
{
@@ -1102,16 +1109,16 @@ static int ipu3_v4l2_subdev_register(struct imgu_device *imgu,
"failed initialize subdev media entity (%d)\n", r);
return r;
}
- imgu_sd->subdev.entity.ops = &ipu3_media_ops;
+ imgu_sd->subdev.entity.ops = &imgu_media_ops;
for (i = 0; i < IMGU_NODE_NUM; i++) {
imgu_sd->subdev_pads[i].flags = imgu_pipe->nodes[i].output ?
MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE;
}
/* Initialize subdev */
- v4l2_subdev_init(&imgu_sd->subdev, &ipu3_subdev_ops);
+ v4l2_subdev_init(&imgu_sd->subdev, &imgu_subdev_ops);
imgu_sd->subdev.entity.function = MEDIA_ENT_F_PROC_VIDEO_STATISTICS;
- imgu_sd->subdev.internal_ops = &ipu3_subdev_internal_ops;
+ imgu_sd->subdev.internal_ops = &imgu_subdev_internal_ops;
imgu_sd->subdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE |
V4L2_SUBDEV_FL_HAS_EVENTS;
snprintf(imgu_sd->subdev.name, sizeof(imgu_sd->subdev.name),
@@ -1120,7 +1127,7 @@ static int ipu3_v4l2_subdev_register(struct imgu_device *imgu,
atomic_set(&imgu_sd->running_mode, IPU3_RUNNING_MODE_VIDEO);
v4l2_ctrl_handler_init(hdl, 1);
imgu_sd->subdev.ctrl_handler = hdl;
- imgu_sd->ctrl = v4l2_ctrl_new_custom(hdl, &ipu3_subdev_ctrl_mode, NULL);
+ imgu_sd->ctrl = v4l2_ctrl_new_custom(hdl, &imgu_subdev_ctrl_mode, NULL);
if (hdl->error) {
r = hdl->error;
dev_err(&imgu->pci_dev->dev,
@@ -1144,7 +1151,7 @@ fail_subdev:
return r;
}
-static int ipu3_v4l2_node_setup(struct imgu_device *imgu, unsigned int pipe,
+static int imgu_v4l2_node_setup(struct imgu_device *imgu, unsigned int pipe,
int node_num)
{
int r;
@@ -1189,7 +1196,7 @@ static int ipu3_v4l2_node_setup(struct imgu_device *imgu, unsigned int pipe,
node->pad_fmt = def_bus_fmt;
node->id = node_num;
node->pipe = pipe;
- ipu3_node_to_v4l2(node_num, vdev, &node->vdev_fmt);
+ imgu_node_to_v4l2(node_num, vdev, &node->vdev_fmt);
if (node->vdev_fmt.type ==
V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ||
node->vdev_fmt.type ==
@@ -1214,11 +1221,11 @@ static int ipu3_v4l2_node_setup(struct imgu_device *imgu, unsigned int pipe,
/* Initialize vbq */
vbq->type = node->vdev_fmt.type;
vbq->io_modes = VB2_USERPTR | VB2_MMAP | VB2_DMABUF;
- vbq->ops = &ipu3_vb2_ops;
+ vbq->ops = &imgu_vb2_ops;
vbq->mem_ops = &vb2_dma_sg_memops;
if (imgu->buf_struct_size <= 0)
imgu->buf_struct_size =
- sizeof(struct ipu3_vb2_buffer);
+ sizeof(struct imgu_vb2_buffer);
vbq->buf_struct_size = imgu->buf_struct_size;
vbq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
/* can streamon w/o buffers */
@@ -1236,7 +1243,7 @@ static int ipu3_v4l2_node_setup(struct imgu_device *imgu, unsigned int pipe,
snprintf(vdev->name, sizeof(vdev->name), "%s %d %s",
IMGU_NAME, pipe, node->name);
vdev->release = video_device_release_empty;
- vdev->fops = &ipu3_v4l2_fops;
+ vdev->fops = &imgu_v4l2_fops;
vdev->lock = &node->lock;
vdev->v4l2_dev = &imgu->v4l2_dev;
vdev->queue = &node->vbq;
@@ -1269,7 +1276,7 @@ static int ipu3_v4l2_node_setup(struct imgu_device *imgu, unsigned int pipe,
return 0;
}
-static void ipu3_v4l2_nodes_cleanup_pipe(struct imgu_device *imgu,
+static void imgu_v4l2_nodes_cleanup_pipe(struct imgu_device *imgu,
unsigned int pipe, int node)
{
int i;
@@ -1282,12 +1289,12 @@ static void ipu3_v4l2_nodes_cleanup_pipe(struct imgu_device *imgu,
}
}
-static int ipu3_v4l2_nodes_setup_pipe(struct imgu_device *imgu, int pipe)
+static int imgu_v4l2_nodes_setup_pipe(struct imgu_device *imgu, int pipe)
{
int i, r;
for (i = 0; i < IMGU_NODE_NUM; i++) {
- r = ipu3_v4l2_node_setup(imgu, pipe, i);
+ r = imgu_v4l2_node_setup(imgu, pipe, i);
if (r)
goto cleanup;
}
@@ -1295,11 +1302,11 @@ static int ipu3_v4l2_nodes_setup_pipe(struct imgu_device *imgu, int pipe)
return 0;
cleanup:
- ipu3_v4l2_nodes_cleanup_pipe(imgu, pipe, i);
+ imgu_v4l2_nodes_cleanup_pipe(imgu, pipe, i);
return r;
}
-static void ipu3_v4l2_subdev_cleanup(struct imgu_device *imgu, unsigned int i)
+static void imgu_v4l2_subdev_cleanup(struct imgu_device *imgu, unsigned int i)
{
struct imgu_media_pipe *imgu_pipe = &imgu->imgu_pipe[i];
@@ -1308,13 +1315,13 @@ static void ipu3_v4l2_subdev_cleanup(struct imgu_device *imgu, unsigned int i)
media_entity_cleanup(&imgu_pipe->imgu_sd.subdev.entity);
}
-static void ipu3_v4l2_cleanup_pipes(struct imgu_device *imgu, unsigned int pipe)
+static void imgu_v4l2_cleanup_pipes(struct imgu_device *imgu, unsigned int pipe)
{
int i;
for (i = 0; i < pipe; i++) {
- ipu3_v4l2_nodes_cleanup_pipe(imgu, i, IMGU_NODE_NUM);
- ipu3_v4l2_subdev_cleanup(imgu, i);
+ imgu_v4l2_nodes_cleanup_pipe(imgu, i, IMGU_NODE_NUM);
+ imgu_v4l2_subdev_cleanup(imgu, i);
}
}
@@ -1325,15 +1332,15 @@ static int imgu_v4l2_register_pipes(struct imgu_device *imgu)
for (i = 0; i < IMGU_MAX_PIPE_NUM; i++) {
imgu_pipe = &imgu->imgu_pipe[i];
- r = ipu3_v4l2_subdev_register(imgu, &imgu_pipe->imgu_sd, i);
+ r = imgu_v4l2_subdev_register(imgu, &imgu_pipe->imgu_sd, i);
if (r) {
dev_err(&imgu->pci_dev->dev,
"failed to register subdev%d ret (%d)\n", i, r);
goto pipes_cleanup;
}
- r = ipu3_v4l2_nodes_setup_pipe(imgu, i);
+ r = imgu_v4l2_nodes_setup_pipe(imgu, i);
if (r) {
- ipu3_v4l2_subdev_cleanup(imgu, i);
+ imgu_v4l2_subdev_cleanup(imgu, i);
goto pipes_cleanup;
}
}
@@ -1341,7 +1348,7 @@ static int imgu_v4l2_register_pipes(struct imgu_device *imgu)
return 0;
pipes_cleanup:
- ipu3_v4l2_cleanup_pipes(imgu, i);
+ imgu_v4l2_cleanup_pipes(imgu, i);
return r;
}
@@ -1389,7 +1396,7 @@ int imgu_v4l2_register(struct imgu_device *imgu)
return 0;
fail_subdevs:
- ipu3_v4l2_cleanup_pipes(imgu, IMGU_MAX_PIPE_NUM);
+ imgu_v4l2_cleanup_pipes(imgu, IMGU_MAX_PIPE_NUM);
fail_v4l2_pipes:
v4l2_device_unregister(&imgu->v4l2_dev);
fail_v4l2_dev:
@@ -1401,7 +1408,7 @@ fail_v4l2_dev:
int imgu_v4l2_unregister(struct imgu_device *imgu)
{
media_device_unregister(&imgu->media_dev);
- ipu3_v4l2_cleanup_pipes(imgu, IMGU_MAX_PIPE_NUM);
+ imgu_v4l2_cleanup_pipes(imgu, IMGU_MAX_PIPE_NUM);
v4l2_device_unregister(&imgu->v4l2_dev);
media_device_cleanup(&imgu->media_dev);
@@ -1411,8 +1418,8 @@ int imgu_v4l2_unregister(struct imgu_device *imgu)
void imgu_v4l2_buffer_done(struct vb2_buffer *vb,
enum vb2_buffer_state state)
{
- struct ipu3_vb2_buffer *b =
- container_of(vb, struct ipu3_vb2_buffer, vbb.vb2_buf);
+ struct imgu_vb2_buffer *b =
+ container_of(vb, struct imgu_vb2_buffer, vbb.vb2_buf);
list_del(&b->list);
vb2_buffer_done(&b->vbb.vb2_buf, state);
diff --git a/drivers/staging/media/ipu3/ipu3.c b/drivers/staging/media/ipu3/ipu3.c
index d521b3afb8b1..d575ac78c8f0 100644
--- a/drivers/staging/media/ipu3/ipu3.c
+++ b/drivers/staging/media/ipu3/ipu3.c
@@ -72,7 +72,7 @@ static void imgu_dummybufs_cleanup(struct imgu_device *imgu, unsigned int pipe)
struct imgu_media_pipe *imgu_pipe = &imgu->imgu_pipe[pipe];
for (i = 0; i < IPU3_CSS_QUEUES; i++)
- ipu3_dmamap_free(imgu,
+ imgu_dmamap_free(imgu,
&imgu_pipe->queues[i].dmap);
}
@@ -93,7 +93,7 @@ static int imgu_dummybufs_preallocate(struct imgu_device *imgu,
if (i == IMGU_QUEUE_MASTER || size == 0)
continue;
- if (!ipu3_dmamap_alloc(imgu,
+ if (!imgu_dmamap_alloc(imgu,
&imgu_pipe->queues[i].dmap, size)) {
imgu_dummybufs_cleanup(imgu, pipe);
return -ENOMEM;
@@ -133,7 +133,7 @@ static int imgu_dummybufs_init(struct imgu_device *imgu, unsigned int pipe)
else
size = mpix->plane_fmt[0].sizeimage;
- if (ipu3_css_dma_buffer_resize(imgu,
+ if (imgu_css_dma_buffer_resize(imgu,
&imgu_pipe->queues[i].dmap,
size)) {
imgu_dummybufs_cleanup(imgu, pipe);
@@ -141,7 +141,7 @@ static int imgu_dummybufs_init(struct imgu_device *imgu, unsigned int pipe)
}
for (k = 0; k < IMGU_MAX_QUEUE_DEPTH; k++)
- ipu3_css_buf_init(&imgu_pipe->queues[i].dummybufs[k], i,
+ imgu_css_buf_init(&imgu_pipe->queues[i].dummybufs[k], i,
imgu_pipe->queues[i].dmap.daddr);
}
@@ -149,7 +149,7 @@ static int imgu_dummybufs_init(struct imgu_device *imgu, unsigned int pipe)
}
/* May be called from atomic context */
-static struct ipu3_css_buffer *imgu_dummybufs_get(struct imgu_device *imgu,
+static struct imgu_css_buffer *imgu_dummybufs_get(struct imgu_device *imgu,
int queue, unsigned int pipe)
{
unsigned int i;
@@ -164,14 +164,14 @@ static struct ipu3_css_buffer *imgu_dummybufs_get(struct imgu_device *imgu,
return NULL;
for (i = 0; i < IMGU_MAX_QUEUE_DEPTH; i++)
- if (ipu3_css_buf_state(&imgu_pipe->queues[queue].dummybufs[i]) !=
+ if (imgu_css_buf_state(&imgu_pipe->queues[queue].dummybufs[i]) !=
IPU3_CSS_BUFFER_QUEUED)
break;
if (i == IMGU_MAX_QUEUE_DEPTH)
return NULL;
- ipu3_css_buf_init(&imgu_pipe->queues[queue].dummybufs[i], queue,
+ imgu_css_buf_init(&imgu_pipe->queues[queue].dummybufs[i], queue,
imgu_pipe->queues[queue].dmap.daddr);
return &imgu_pipe->queues[queue].dummybufs[i];
@@ -179,7 +179,7 @@ static struct ipu3_css_buffer *imgu_dummybufs_get(struct imgu_device *imgu,
/* Check if given buffer is a dummy buffer */
static bool imgu_dummybufs_check(struct imgu_device *imgu,
- struct ipu3_css_buffer *buf,
+ struct imgu_css_buffer *buf,
unsigned int pipe)
{
unsigned int i;
@@ -200,7 +200,7 @@ static void imgu_buffer_done(struct imgu_device *imgu, struct vb2_buffer *vb,
mutex_unlock(&imgu->lock);
}
-static struct ipu3_css_buffer *imgu_queue_getbuf(struct imgu_device *imgu,
+static struct imgu_css_buffer *imgu_queue_getbuf(struct imgu_device *imgu,
unsigned int node,
unsigned int pipe)
{
@@ -212,7 +212,7 @@ static struct ipu3_css_buffer *imgu_queue_getbuf(struct imgu_device *imgu,
/* Find first free buffer from the node */
list_for_each_entry(buf, &imgu_pipe->nodes[node].buffers, vid_buf.list) {
- if (ipu3_css_buf_state(&buf->css_buf) == IPU3_CSS_BUFFER_NEW)
+ if (imgu_css_buf_state(&buf->css_buf) == IPU3_CSS_BUFFER_NEW)
return &buf->css_buf;
}
@@ -230,7 +230,7 @@ int imgu_queue_buffers(struct imgu_device *imgu, bool initial, unsigned int pipe
int r = 0;
struct imgu_media_pipe *imgu_pipe = &imgu->imgu_pipe[pipe];
- if (!ipu3_css_is_streaming(&imgu->css))
+ if (!imgu_css_is_streaming(&imgu->css))
return 0;
dev_dbg(&imgu->pci_dev->dev, "Queue buffers to pipe %d", pipe);
@@ -247,7 +247,7 @@ int imgu_queue_buffers(struct imgu_device *imgu, bool initial, unsigned int pipe
"Vf not enabled, ignore queue");
continue;
} else if (imgu_pipe->queue_enabled[node]) {
- struct ipu3_css_buffer *buf =
+ struct imgu_css_buffer *buf =
imgu_queue_getbuf(imgu, node, pipe);
struct imgu_buffer *ibuf = NULL;
bool dummy;
@@ -255,7 +255,7 @@ int imgu_queue_buffers(struct imgu_device *imgu, bool initial, unsigned int pipe
if (!buf)
break;
- r = ipu3_css_buf_queue(&imgu->css, pipe, buf);
+ r = imgu_css_buf_queue(&imgu->css, pipe, buf);
if (r)
break;
dummy = imgu_dummybufs_check(imgu, buf, pipe);
@@ -300,7 +300,7 @@ failed:
list_for_each_entry_safe(buf, buf0,
&imgu_pipe->nodes[node].buffers,
vid_buf.list) {
- if (ipu3_css_buf_state(&buf->css_buf) ==
+ if (imgu_css_buf_state(&buf->css_buf) ==
IPU3_CSS_BUFFER_QUEUED)
continue; /* Was already queued, skip */
@@ -317,18 +317,18 @@ static int imgu_powerup(struct imgu_device *imgu)
{
int r;
- r = ipu3_css_set_powerup(&imgu->pci_dev->dev, imgu->base);
+ r = imgu_css_set_powerup(&imgu->pci_dev->dev, imgu->base);
if (r)
return r;
- ipu3_mmu_resume(imgu->mmu);
+ imgu_mmu_resume(imgu->mmu);
return 0;
}
static void imgu_powerdown(struct imgu_device *imgu)
{
- ipu3_mmu_suspend(imgu->mmu);
- ipu3_css_set_powerdown(&imgu->pci_dev->dev, imgu->base);
+ imgu_mmu_suspend(imgu->mmu);
+ imgu_css_set_powerdown(&imgu->pci_dev->dev, imgu->base);
}
int imgu_s_stream(struct imgu_device *imgu, int enable)
@@ -341,7 +341,7 @@ int imgu_s_stream(struct imgu_device *imgu, int enable)
dev_dbg(dev, "stream off\n");
/* Block new buffers to be queued to CSS. */
atomic_set(&imgu->qbuf_barrier, 1);
- ipu3_css_stop_streaming(&imgu->css);
+ imgu_css_stop_streaming(&imgu->css);
synchronize_irq(imgu->pci_dev->irq);
atomic_set(&imgu->qbuf_barrier, 0);
imgu_powerdown(imgu);
@@ -366,7 +366,7 @@ int imgu_s_stream(struct imgu_device *imgu, int enable)
}
/* Start CSS streaming */
- r = ipu3_css_start_streaming(&imgu->css);
+ r = imgu_css_start_streaming(&imgu->css);
if (r) {
dev_err(dev, "failed to start css streaming (%d)", r);
goto fail_start_streaming;
@@ -393,7 +393,7 @@ fail_queueing:
for_each_set_bit(pipe, imgu->css.enabled_pipes, IMGU_MAX_PIPE_NUM)
imgu_dummybufs_cleanup(imgu, pipe);
fail_dummybufs:
- ipu3_css_stop_streaming(&imgu->css);
+ imgu_css_stop_streaming(&imgu->css);
fail_start_streaming:
pm_runtime_put(dev);
@@ -435,7 +435,7 @@ static int imgu_video_nodes_init(struct imgu_device *imgu)
rects[IPU3_CSS_RECT_EFFECTIVE] = &imgu_pipe->imgu_sd.rect.eff;
rects[IPU3_CSS_RECT_BDS] = &imgu_pipe->imgu_sd.rect.bds;
- ipu3_css_fmt_set(&imgu->css, fmts, rects, j);
+ imgu_css_fmt_set(&imgu->css, fmts, rects, j);
/* Pre-allocate dummy buffers */
r = imgu_dummybufs_preallocate(imgu, j);
@@ -478,23 +478,22 @@ static irqreturn_t imgu_isr_threaded(int irq, void *imgu_ptr)
/* Dequeue / queue buffers */
do {
u64 ns = ktime_get_ns();
- struct ipu3_css_buffer *b;
+ struct imgu_css_buffer *b;
struct imgu_buffer *buf = NULL;
unsigned int node, pipe;
bool dummy;
do {
mutex_lock(&imgu->lock);
- b = ipu3_css_buf_dequeue(&imgu->css);
+ b = imgu_css_buf_dequeue(&imgu->css);
mutex_unlock(&imgu->lock);
} while (PTR_ERR(b) == -EAGAIN);
- if (IS_ERR_OR_NULL(b)) {
- if (!b || PTR_ERR(b) == -EBUSY) /* All done */
- break;
- dev_err(&imgu->pci_dev->dev,
- "failed to dequeue buffers (%ld)\n",
- PTR_ERR(b));
+ if (IS_ERR(b)) {
+ if (PTR_ERR(b) != -EBUSY) /* All done */
+ dev_err(&imgu->pci_dev->dev,
+ "failed to dequeue buffers (%ld)\n",
+ PTR_ERR(b));
break;
}
@@ -526,12 +525,12 @@ static irqreturn_t imgu_isr_threaded(int irq, void *imgu_ptr)
buf->vid_buf.vbb.sequence);
}
imgu_buffer_done(imgu, &buf->vid_buf.vbb.vb2_buf,
- ipu3_css_buf_state(&buf->css_buf) ==
+ imgu_css_buf_state(&buf->css_buf) ==
IPU3_CSS_BUFFER_DONE ?
VB2_BUF_STATE_DONE :
VB2_BUF_STATE_ERROR);
mutex_lock(&imgu->lock);
- if (ipu3_css_queue_empty(&imgu->css))
+ if (imgu_css_queue_empty(&imgu->css))
wake_up_all(&imgu->buf_drain_wq);
mutex_unlock(&imgu->lock);
} while (1);
@@ -553,7 +552,7 @@ static irqreturn_t imgu_isr(int irq, void *imgu_ptr)
struct imgu_device *imgu = imgu_ptr;
/* acknowledge interruption */
- if (ipu3_css_irq_ack(&imgu->css) < 0)
+ if (imgu_css_irq_ack(&imgu->css) < 0)
return IRQ_NONE;
return IRQ_WAKE_THREAD;
@@ -638,21 +637,21 @@ static int imgu_pci_probe(struct pci_dev *pci_dev,
atomic_set(&imgu->qbuf_barrier, 0);
init_waitqueue_head(&imgu->buf_drain_wq);
- r = ipu3_css_set_powerup(&pci_dev->dev, imgu->base);
+ r = imgu_css_set_powerup(&pci_dev->dev, imgu->base);
if (r) {
dev_err(&pci_dev->dev,
"failed to power up CSS (%d)\n", r);
goto out_mutex_destroy;
}
- imgu->mmu = ipu3_mmu_init(&pci_dev->dev, imgu->base);
+ imgu->mmu = imgu_mmu_init(&pci_dev->dev, imgu->base);
if (IS_ERR(imgu->mmu)) {
r = PTR_ERR(imgu->mmu);
dev_err(&pci_dev->dev, "failed to initialize MMU (%d)\n", r);
goto out_css_powerdown;
}
- r = ipu3_dmamap_init(imgu);
+ r = imgu_dmamap_init(imgu);
if (r) {
dev_err(&pci_dev->dev,
"failed to initialize DMA mapping (%d)\n", r);
@@ -660,7 +659,7 @@ static int imgu_pci_probe(struct pci_dev *pci_dev,
}
/* ISP programming */
- r = ipu3_css_init(&pci_dev->dev, &imgu->css, imgu->base, phys_len);
+ r = imgu_css_init(&pci_dev->dev, &imgu->css, imgu->base, phys_len);
if (r) {
dev_err(&pci_dev->dev, "failed to initialize CSS (%d)\n", r);
goto out_dmamap_exit;
@@ -690,13 +689,13 @@ static int imgu_pci_probe(struct pci_dev *pci_dev,
out_video_exit:
imgu_video_nodes_exit(imgu);
out_css_cleanup:
- ipu3_css_cleanup(&imgu->css);
+ imgu_css_cleanup(&imgu->css);
out_dmamap_exit:
- ipu3_dmamap_exit(imgu);
+ imgu_dmamap_exit(imgu);
out_mmu_exit:
- ipu3_mmu_exit(imgu->mmu);
+ imgu_mmu_exit(imgu->mmu);
out_css_powerdown:
- ipu3_css_set_powerdown(&pci_dev->dev, imgu->base);
+ imgu_css_set_powerdown(&pci_dev->dev, imgu->base);
out_mutex_destroy:
mutex_destroy(&imgu->lock);
@@ -711,10 +710,10 @@ static void imgu_pci_remove(struct pci_dev *pci_dev)
pm_runtime_get_noresume(&pci_dev->dev);
imgu_video_nodes_exit(imgu);
- ipu3_css_cleanup(&imgu->css);
- ipu3_css_set_powerdown(&pci_dev->dev, imgu->base);
- ipu3_dmamap_exit(imgu);
- ipu3_mmu_exit(imgu->mmu);
+ imgu_css_cleanup(&imgu->css);
+ imgu_css_set_powerdown(&pci_dev->dev, imgu->base);
+ imgu_dmamap_exit(imgu);
+ imgu_mmu_exit(imgu->mmu);
mutex_destroy(&imgu->lock);
}
@@ -724,7 +723,7 @@ static int __maybe_unused imgu_suspend(struct device *dev)
struct imgu_device *imgu = pci_get_drvdata(pci_dev);
dev_dbg(dev, "enter %s\n", __func__);
- imgu->suspend_in_stream = ipu3_css_is_streaming(&imgu->css);
+ imgu->suspend_in_stream = imgu_css_is_streaming(&imgu->css);
if (!imgu->suspend_in_stream)
goto out;
/* Block new buffers to be queued to CSS. */
@@ -736,10 +735,10 @@ static int __maybe_unused imgu_suspend(struct device *dev)
synchronize_irq(pci_dev->irq);
/* Wait until all buffers in CSS are done. */
if (!wait_event_timeout(imgu->buf_drain_wq,
- ipu3_css_queue_empty(&imgu->css), msecs_to_jiffies(1000)))
+ imgu_css_queue_empty(&imgu->css), msecs_to_jiffies(1000)))
dev_err(dev, "wait buffer drain timeout.\n");
- ipu3_css_stop_streaming(&imgu->css);
+ imgu_css_stop_streaming(&imgu->css);
atomic_set(&imgu->qbuf_barrier, 0);
imgu_powerdown(imgu);
pm_runtime_force_suspend(dev);
@@ -769,7 +768,7 @@ static int __maybe_unused imgu_resume(struct device *dev)
}
/* Start CSS streaming */
- r = ipu3_css_start_streaming(&imgu->css);
+ r = imgu_css_start_streaming(&imgu->css);
if (r) {
dev_err(dev, "failed to resume css streaming (%d)", r);
goto out;
diff --git a/drivers/staging/media/ipu3/ipu3.h b/drivers/staging/media/ipu3/ipu3.h
index 04fc99f47ebb..73b123b2b8a2 100644
--- a/drivers/staging/media/ipu3/ipu3.h
+++ b/drivers/staging/media/ipu3/ipu3.h
@@ -32,7 +32,7 @@
#define IMGU_NODE_STAT_3A 4 /* 3A statistics */
#define IMGU_NODE_NUM 5
-#define file_to_intel_ipu3_node(__file) \
+#define file_to_intel_imgu_node(__file) \
container_of(video_devdata(__file), struct imgu_video_device, vdev)
#define IPU3_INPUT_MIN_WIDTH 0U
@@ -44,7 +44,7 @@
#define IPU3_OUTPUT_MAX_WIDTH 4480U
#define IPU3_OUTPUT_MAX_HEIGHT 34004U
-struct ipu3_vb2_buffer {
+struct imgu_vb2_buffer {
/* Public fields */
struct vb2_v4l2_buffer vbb; /* Must be the first field */
@@ -53,9 +53,9 @@ struct ipu3_vb2_buffer {
};
struct imgu_buffer {
- struct ipu3_vb2_buffer vid_buf; /* Must be the first field */
- struct ipu3_css_buffer css_buf;
- struct ipu3_css_map map;
+ struct imgu_vb2_buffer vid_buf; /* Must be the first field */
+ struct imgu_css_buffer css_buf;
+ struct imgu_css_map map;
};
struct imgu_node_mapping {
@@ -107,8 +107,8 @@ struct imgu_media_pipe {
/* Internally enabled queues */
struct {
- struct ipu3_css_map dmap;
- struct ipu3_css_buffer dummybufs[IMGU_MAX_QUEUE_DEPTH];
+ struct imgu_css_map dmap;
+ struct imgu_css_buffer dummybufs[IMGU_MAX_QUEUE_DEPTH];
} queues[IPU3_CSS_QUEUES];
struct imgu_video_device nodes[IMGU_NODE_NUM];
bool queue_enabled[IMGU_NODE_NUM];
@@ -135,18 +135,18 @@ struct imgu_device {
struct v4l2_file_operations v4l2_file_ops;
/* MMU driver for css */
- struct ipu3_mmu_info *mmu;
+ struct imgu_mmu_info *mmu;
struct iova_domain iova_domain;
/* css - Camera Sub-System */
- struct ipu3_css css;
+ struct imgu_css css;
/*
* Coarse-grained lock to protect
* vid_buf.list and css->queue
*/
struct mutex lock;
- /* Forbit streaming and buffer queuing during system suspend. */
+ /* Forbid streaming and buffer queuing during system suspend. */
atomic_t qbuf_barrier;
/* Indicate if system suspend take place while imgu is streaming. */
bool suspend_in_stream;
diff --git a/drivers/staging/media/omap4iss/iss_csi2.c b/drivers/staging/media/omap4iss/iss_csi2.c
index 059cf5bd3c36..a6dc2d2b1228 100644
--- a/drivers/staging/media/omap4iss/iss_csi2.c
+++ b/drivers/staging/media/omap4iss/iss_csi2.c
@@ -712,7 +712,7 @@ static void csi2_isr_ctx(struct iss_csi2_device *csi2,
/* Skip interrupts until we reach the frame skip count. The CSI2 will be
* automatically disabled, as the frame skip count has been programmed
- * in the CSI2_CTx_CTRL1::COUNT field, so reenable it.
+ * in the CSI2_CTx_CTRL1::COUNT field, so re-enable it.
*
* It would have been nice to rely on the FRAME_NUMBER interrupt instead
* but it turned out that the interrupt is only generated when the CSI2
diff --git a/drivers/staging/media/rockchip/vpu/rk3288_vpu_hw_jpeg_enc.c b/drivers/staging/media/rockchip/vpu/rk3288_vpu_hw_jpeg_enc.c
index 5282236d1bb1..06daea66fb49 100644
--- a/drivers/staging/media/rockchip/vpu/rk3288_vpu_hw_jpeg_enc.c
+++ b/drivers/staging/media/rockchip/vpu/rk3288_vpu_hw_jpeg_enc.c
@@ -80,7 +80,7 @@ rk3288_vpu_jpeg_enc_set_qtable(struct rockchip_vpu_dev *vpu,
void rk3288_vpu_jpeg_enc_run(struct rockchip_vpu_ctx *ctx)
{
struct rockchip_vpu_dev *vpu = ctx->dev;
- struct vb2_buffer *src_buf, *dst_buf;
+ struct vb2_v4l2_buffer *src_buf, *dst_buf;
struct rockchip_vpu_jpeg_ctx jpeg_ctx;
u32 reg;
@@ -88,7 +88,7 @@ void rk3288_vpu_jpeg_enc_run(struct rockchip_vpu_ctx *ctx)
dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
memset(&jpeg_ctx, 0, sizeof(jpeg_ctx));
- jpeg_ctx.buffer = vb2_plane_vaddr(dst_buf, 0);
+ jpeg_ctx.buffer = vb2_plane_vaddr(&dst_buf->vb2_buf, 0);
jpeg_ctx.width = ctx->dst_fmt.width;
jpeg_ctx.height = ctx->dst_fmt.height;
jpeg_ctx.quality = ctx->jpeg_quality;
@@ -99,7 +99,7 @@ void rk3288_vpu_jpeg_enc_run(struct rockchip_vpu_ctx *ctx)
VEPU_REG_ENC_CTRL);
rk3288_vpu_set_src_img_ctrl(vpu, ctx);
- rk3288_vpu_jpeg_enc_set_buffers(vpu, ctx, src_buf);
+ rk3288_vpu_jpeg_enc_set_buffers(vpu, ctx, &src_buf->vb2_buf);
rk3288_vpu_jpeg_enc_set_qtable(vpu,
rockchip_vpu_jpeg_get_qtable(&jpeg_ctx, 0),
rockchip_vpu_jpeg_get_qtable(&jpeg_ctx, 1));
diff --git a/drivers/staging/media/rockchip/vpu/rk3399_vpu_hw_jpeg_enc.c b/drivers/staging/media/rockchip/vpu/rk3399_vpu_hw_jpeg_enc.c
index dbc86d95fe3b..3d438797692e 100644
--- a/drivers/staging/media/rockchip/vpu/rk3399_vpu_hw_jpeg_enc.c
+++ b/drivers/staging/media/rockchip/vpu/rk3399_vpu_hw_jpeg_enc.c
@@ -111,7 +111,7 @@ rk3399_vpu_jpeg_enc_set_qtable(struct rockchip_vpu_dev *vpu,
void rk3399_vpu_jpeg_enc_run(struct rockchip_vpu_ctx *ctx)
{
struct rockchip_vpu_dev *vpu = ctx->dev;
- struct vb2_buffer *src_buf, *dst_buf;
+ struct vb2_v4l2_buffer *src_buf, *dst_buf;
struct rockchip_vpu_jpeg_ctx jpeg_ctx;
u32 reg;
@@ -119,7 +119,7 @@ void rk3399_vpu_jpeg_enc_run(struct rockchip_vpu_ctx *ctx)
dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
memset(&jpeg_ctx, 0, sizeof(jpeg_ctx));
- jpeg_ctx.buffer = vb2_plane_vaddr(dst_buf, 0);
+ jpeg_ctx.buffer = vb2_plane_vaddr(&dst_buf->vb2_buf, 0);
jpeg_ctx.width = ctx->dst_fmt.width;
jpeg_ctx.height = ctx->dst_fmt.height;
jpeg_ctx.quality = ctx->jpeg_quality;
@@ -130,7 +130,7 @@ void rk3399_vpu_jpeg_enc_run(struct rockchip_vpu_ctx *ctx)
VEPU_REG_ENCODE_START);
rk3399_vpu_set_src_img_ctrl(vpu, ctx);
- rk3399_vpu_jpeg_enc_set_buffers(vpu, ctx, src_buf);
+ rk3399_vpu_jpeg_enc_set_buffers(vpu, ctx, &src_buf->vb2_buf);
rk3399_vpu_jpeg_enc_set_qtable(vpu,
rockchip_vpu_jpeg_get_qtable(&jpeg_ctx, 0),
rockchip_vpu_jpeg_get_qtable(&jpeg_ctx, 1));
diff --git a/drivers/media/i2c/soc_camera/Kconfig b/drivers/staging/media/soc_camera/Kconfig
index 7c2aabc8a3f6..bacd30f0348d 100644
--- a/drivers/media/i2c/soc_camera/Kconfig
+++ b/drivers/staging/media/soc_camera/Kconfig
@@ -1,11 +1,13 @@
-comment "soc_camera sensor drivers"
-
-config SOC_CAMERA_MT9M001
- tristate "mt9m001 support"
- depends on SOC_CAMERA && I2C
+config SOC_CAMERA
+ tristate "SoC camera support"
+ depends on VIDEO_V4L2 && HAS_DMA && I2C && BROKEN
+ select VIDEOBUF2_CORE
help
- This driver supports MT9M001 cameras from Micron, monochrome
- and colour models.
+ SoC Camera is a common API to several cameras, not connecting
+ over a bus like PCI or USB. For example some i2c camera connected
+ directly to the data bus of an SoC.
+
+comment "soc_camera sensor drivers"
config SOC_CAMERA_MT9M111
tristate "legacy soc_camera mt9m111, mt9m112 and mt9m131 support"
@@ -17,12 +19,6 @@ config SOC_CAMERA_MT9M111
This is the legacy configuration which shouldn't be used anymore,
while VIDEO_MT9M111 should be used instead.
-config SOC_CAMERA_MT9T112
- tristate "mt9t112 support"
- depends on SOC_CAMERA && I2C
- help
- This driver supports MT9T112 cameras from Aptina.
-
config SOC_CAMERA_MT9V022
tristate "mt9v022 and mt9v024 support"
depends on SOC_CAMERA && I2C
@@ -35,32 +31,20 @@ config SOC_CAMERA_OV5642
help
This is a V4L2 camera driver for the OmniVision OV5642 sensor
-config SOC_CAMERA_OV772X
- tristate "ov772x camera support"
- depends on SOC_CAMERA && I2C
- help
- This is a ov772x camera driver
-
-config SOC_CAMERA_OV9640
- tristate "ov9640 camera support"
- depends on SOC_CAMERA && I2C
- help
- This is a ov9640 camera driver
-
config SOC_CAMERA_OV9740
tristate "ov9740 camera support"
depends on SOC_CAMERA && I2C
help
This is a ov9740 camera driver
-config SOC_CAMERA_RJ54N1
- tristate "rj54n1cb0c support"
+config SOC_CAMERA_IMX074
+ tristate "imx074 support (DEPRECATED)"
depends on SOC_CAMERA && I2C
help
- This is a rj54n1cb0c video driver
+ This driver supports IMX074 cameras from Sony
-config SOC_CAMERA_TW9910
- tristate "tw9910 support"
+config SOC_CAMERA_MT9T031
+ tristate "mt9t031 support (DEPRECATED)"
depends on SOC_CAMERA && I2C
help
- This is a tw9910 video driver
+ This driver supports MT9T031 cameras from Micron.
diff --git a/drivers/staging/media/soc_camera/Makefile b/drivers/staging/media/soc_camera/Makefile
new file mode 100644
index 000000000000..3a351bd629f5
--- /dev/null
+++ b/drivers/staging/media/soc_camera/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_SOC_CAMERA) += soc_camera.o soc_mediabus.o
+obj-$(CONFIG_SOC_CAMERA_MT9V022) += soc_mt9v022.o
+obj-$(CONFIG_SOC_CAMERA_OV5642) += soc_ov5642.o
+obj-$(CONFIG_SOC_CAMERA_OV9740) += soc_ov9740.o
+obj-$(CONFIG_SOC_CAMERA_IMX074) += imx074.o
+obj-$(CONFIG_SOC_CAMERA_MT9T031) += mt9t031.o
diff --git a/drivers/staging/media/imx074/imx074.c b/drivers/staging/media/soc_camera/imx074.c
index 1676c166dc83..1676c166dc83 100644
--- a/drivers/staging/media/imx074/imx074.c
+++ b/drivers/staging/media/soc_camera/imx074.c
diff --git a/drivers/staging/media/mt9t031/mt9t031.c b/drivers/staging/media/soc_camera/mt9t031.c
index 4ff179302b4f..4ff179302b4f 100644
--- a/drivers/staging/media/mt9t031/mt9t031.c
+++ b/drivers/staging/media/soc_camera/mt9t031.c
diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/staging/media/soc_camera/soc_camera.c
index 21034339cdcb..1ab86a7499b9 100644
--- a/drivers/media/platform/soc_camera/soc_camera.c
+++ b/drivers/staging/media/soc_camera/soc_camera.c
@@ -4,11 +4,11 @@
* Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
*
* This driver provides an interface between platform-specific camera
- * busses and camera devices. It should be used if the camera is
+ * buses and camera devices. It should be used if the camera is
* connected not over a "proper" bus like PCI or USB, but over a
* special bus, like, for example, the Quick Capture interface on PXA270
* SoCs. Later it should also be used for i.MX31 SoCs from Freescale.
- * It can handle multiple cameras and / or multiple busses, which can
+ * It can handle multiple cameras and / or multiple buses, which can
* be used, e.g., in stereo-vision applications.
*
* This program is free software; you can redistribute it and/or modify
diff --git a/drivers/media/platform/soc_camera/soc_mediabus.c b/drivers/staging/media/soc_camera/soc_mediabus.c
index be74008ec0ca..be74008ec0ca 100644
--- a/drivers/media/platform/soc_camera/soc_mediabus.c
+++ b/drivers/staging/media/soc_camera/soc_mediabus.c
diff --git a/drivers/media/i2c/soc_camera/soc_mt9v022.c b/drivers/staging/media/soc_camera/soc_mt9v022.c
index 6d922b17ea94..6d922b17ea94 100644
--- a/drivers/media/i2c/soc_camera/soc_mt9v022.c
+++ b/drivers/staging/media/soc_camera/soc_mt9v022.c
diff --git a/drivers/media/i2c/soc_camera/soc_ov5642.c b/drivers/staging/media/soc_camera/soc_ov5642.c
index 0931898c79dd..0931898c79dd 100644
--- a/drivers/media/i2c/soc_camera/soc_ov5642.c
+++ b/drivers/staging/media/soc_camera/soc_ov5642.c
diff --git a/drivers/media/i2c/soc_camera/soc_ov9740.c b/drivers/staging/media/soc_camera/soc_ov9740.c
index a07d3145d1b4..a07d3145d1b4 100644
--- a/drivers/media/i2c/soc_camera/soc_ov9740.c
+++ b/drivers/staging/media/soc_camera/soc_ov9740.c
diff --git a/drivers/staging/media/sunxi/cedrus/TODO b/drivers/staging/media/sunxi/cedrus/TODO
index a951b3fd1ea1..ec277ece47af 100644
--- a/drivers/staging/media/sunxi/cedrus/TODO
+++ b/drivers/staging/media/sunxi/cedrus/TODO
@@ -5,8 +5,3 @@ Before this stateless decoder driver can leave the staging area:
* Userspace support for the Request API needs to be reviewed;
* Another stateless decoder driver should be submitted;
* At least one stateless encoder driver should be submitted.
-* When queueing a request containing references to I frames, the
- refcount of the memory for those I frames needs to be incremented
- and decremented when the request is completed. This will likely
- require some help from vb2. The driver should fail the request
- if the memory/buffer is gone.
diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.h b/drivers/staging/media/sunxi/cedrus/cedrus.h
index 3acfdcf83691..4aedd24a9848 100644
--- a/drivers/staging/media/sunxi/cedrus/cedrus.h
+++ b/drivers/staging/media/sunxi/cedrus/cedrus.h
@@ -140,11 +140,14 @@ static inline dma_addr_t cedrus_buf_addr(struct vb2_buffer *buf,
}
static inline dma_addr_t cedrus_dst_buf_addr(struct cedrus_ctx *ctx,
- unsigned int index,
- unsigned int plane)
+ int index, unsigned int plane)
{
- struct vb2_buffer *buf = ctx->dst_bufs[index];
+ struct vb2_buffer *buf;
+ if (index < 0)
+ return 0;
+
+ buf = ctx->dst_bufs[index];
return buf ? cedrus_buf_addr(buf, &ctx->dst_fmt, plane) : 0;
}
diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_dec.c b/drivers/staging/media/sunxi/cedrus/cedrus_dec.c
index 591d191d4286..4d6d602cdde6 100644
--- a/drivers/staging/media/sunxi/cedrus/cedrus_dec.c
+++ b/drivers/staging/media/sunxi/cedrus/cedrus_dec.c
@@ -50,6 +50,8 @@ void cedrus_device_run(void *priv)
break;
}
+ v4l2_m2m_buf_copy_metadata(run.src, run.dst, true);
+
dev->dec_ops[ctx->current_codec]->setup(ctx, &run);
/* Complete request(s) controls if needed. */
diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_dec.h b/drivers/staging/media/sunxi/cedrus/cedrus_dec.h
index 4f423d3a1cad..d1ae7903677b 100644
--- a/drivers/staging/media/sunxi/cedrus/cedrus_dec.h
+++ b/drivers/staging/media/sunxi/cedrus/cedrus_dec.h
@@ -16,12 +16,6 @@
#ifndef _CEDRUS_DEC_H_
#define _CEDRUS_DEC_H_
-extern const struct v4l2_ioctl_ops cedrus_ioctl_ops;
-
-void cedrus_device_work(struct work_struct *work);
void cedrus_device_run(void *priv);
-int cedrus_queue_init(void *priv, struct vb2_queue *src_vq,
- struct vb2_queue *dst_vq);
-
#endif
diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_hw.c b/drivers/staging/media/sunxi/cedrus/cedrus_hw.c
index 300339fee1bc..0acf219a8c91 100644
--- a/drivers/staging/media/sunxi/cedrus/cedrus_hw.c
+++ b/drivers/staging/media/sunxi/cedrus/cedrus_hw.c
@@ -157,14 +157,14 @@ int cedrus_hw_probe(struct cedrus_dev *dev)
irq_dec = platform_get_irq(dev->pdev, 0);
if (irq_dec <= 0) {
- v4l2_err(&dev->v4l2_dev, "Failed to get IRQ\n");
+ dev_err(dev->dev, "Failed to get IRQ\n");
return irq_dec;
}
ret = devm_request_irq(dev->dev, irq_dec, cedrus_irq,
0, dev_name(dev->dev), dev);
if (ret) {
- v4l2_err(&dev->v4l2_dev, "Failed to request IRQ\n");
+ dev_err(dev->dev, "Failed to request IRQ\n");
return ret;
}
@@ -182,21 +182,21 @@ int cedrus_hw_probe(struct cedrus_dev *dev)
ret = of_reserved_mem_device_init(dev->dev);
if (ret && ret != -ENODEV) {
- v4l2_err(&dev->v4l2_dev, "Failed to reserve memory\n");
+ dev_err(dev->dev, "Failed to reserve memory\n");
return ret;
}
ret = sunxi_sram_claim(dev->dev);
if (ret) {
- v4l2_err(&dev->v4l2_dev, "Failed to claim SRAM\n");
+ dev_err(dev->dev, "Failed to claim SRAM\n");
goto err_mem;
}
dev->ahb_clk = devm_clk_get(dev->dev, "ahb");
if (IS_ERR(dev->ahb_clk)) {
- v4l2_err(&dev->v4l2_dev, "Failed to get AHB clock\n");
+ dev_err(dev->dev, "Failed to get AHB clock\n");
ret = PTR_ERR(dev->ahb_clk);
goto err_sram;
@@ -204,7 +204,7 @@ int cedrus_hw_probe(struct cedrus_dev *dev)
dev->mod_clk = devm_clk_get(dev->dev, "mod");
if (IS_ERR(dev->mod_clk)) {
- v4l2_err(&dev->v4l2_dev, "Failed to get MOD clock\n");
+ dev_err(dev->dev, "Failed to get MOD clock\n");
ret = PTR_ERR(dev->mod_clk);
goto err_sram;
@@ -212,7 +212,7 @@ int cedrus_hw_probe(struct cedrus_dev *dev)
dev->ram_clk = devm_clk_get(dev->dev, "ram");
if (IS_ERR(dev->ram_clk)) {
- v4l2_err(&dev->v4l2_dev, "Failed to get RAM clock\n");
+ dev_err(dev->dev, "Failed to get RAM clock\n");
ret = PTR_ERR(dev->ram_clk);
goto err_sram;
@@ -220,7 +220,7 @@ int cedrus_hw_probe(struct cedrus_dev *dev)
dev->rstc = devm_reset_control_get(dev->dev, NULL);
if (IS_ERR(dev->rstc)) {
- v4l2_err(&dev->v4l2_dev, "Failed to get reset control\n");
+ dev_err(dev->dev, "Failed to get reset control\n");
ret = PTR_ERR(dev->rstc);
goto err_sram;
@@ -229,7 +229,7 @@ int cedrus_hw_probe(struct cedrus_dev *dev)
res = platform_get_resource(dev->pdev, IORESOURCE_MEM, 0);
dev->base = devm_ioremap_resource(dev->dev, res);
if (IS_ERR(dev->base)) {
- v4l2_err(&dev->v4l2_dev, "Failed to map registers\n");
+ dev_err(dev->dev, "Failed to map registers\n");
ret = PTR_ERR(dev->base);
goto err_sram;
@@ -237,35 +237,35 @@ int cedrus_hw_probe(struct cedrus_dev *dev)
ret = clk_set_rate(dev->mod_clk, CEDRUS_CLOCK_RATE_DEFAULT);
if (ret) {
- v4l2_err(&dev->v4l2_dev, "Failed to set clock rate\n");
+ dev_err(dev->dev, "Failed to set clock rate\n");
goto err_sram;
}
ret = clk_prepare_enable(dev->ahb_clk);
if (ret) {
- v4l2_err(&dev->v4l2_dev, "Failed to enable AHB clock\n");
+ dev_err(dev->dev, "Failed to enable AHB clock\n");
goto err_sram;
}
ret = clk_prepare_enable(dev->mod_clk);
if (ret) {
- v4l2_err(&dev->v4l2_dev, "Failed to enable MOD clock\n");
+ dev_err(dev->dev, "Failed to enable MOD clock\n");
goto err_ahb_clk;
}
ret = clk_prepare_enable(dev->ram_clk);
if (ret) {
- v4l2_err(&dev->v4l2_dev, "Failed to enable RAM clock\n");
+ dev_err(dev->dev, "Failed to enable RAM clock\n");
goto err_mod_clk;
}
ret = reset_control_reset(dev->rstc);
if (ret) {
- v4l2_err(&dev->v4l2_dev, "Failed to apply reset\n");
+ dev_err(dev->dev, "Failed to apply reset\n");
goto err_ram_clk;
}
diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c b/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c
index 9abd39cae38c..13c34927bad5 100644
--- a/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c
+++ b/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c
@@ -82,7 +82,10 @@ static void cedrus_mpeg2_setup(struct cedrus_ctx *ctx, struct cedrus_run *run)
dma_addr_t fwd_luma_addr, fwd_chroma_addr;
dma_addr_t bwd_luma_addr, bwd_chroma_addr;
struct cedrus_dev *dev = ctx->dev;
+ struct vb2_queue *vq;
const u8 *matrix;
+ int forward_idx;
+ int backward_idx;
unsigned int i;
u32 reg;
@@ -157,22 +160,18 @@ static void cedrus_mpeg2_setup(struct cedrus_ctx *ctx, struct cedrus_run *run)
/* Forward and backward prediction reference buffers. */
- fwd_luma_addr = cedrus_dst_buf_addr(ctx,
- slice_params->forward_ref_index,
- 0);
- fwd_chroma_addr = cedrus_dst_buf_addr(ctx,
- slice_params->forward_ref_index,
- 1);
+ vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+
+ forward_idx = vb2_find_timestamp(vq, slice_params->forward_ref_ts, 0);
+ fwd_luma_addr = cedrus_dst_buf_addr(ctx, forward_idx, 0);
+ fwd_chroma_addr = cedrus_dst_buf_addr(ctx, forward_idx, 1);
cedrus_write(dev, VE_DEC_MPEG_FWD_REF_LUMA_ADDR, fwd_luma_addr);
cedrus_write(dev, VE_DEC_MPEG_FWD_REF_CHROMA_ADDR, fwd_chroma_addr);
- bwd_luma_addr = cedrus_dst_buf_addr(ctx,
- slice_params->backward_ref_index,
- 0);
- bwd_chroma_addr = cedrus_dst_buf_addr(ctx,
- slice_params->backward_ref_index,
- 1);
+ backward_idx = vb2_find_timestamp(vq, slice_params->backward_ref_ts, 0);
+ bwd_luma_addr = cedrus_dst_buf_addr(ctx, backward_idx, 0);
+ bwd_chroma_addr = cedrus_dst_buf_addr(ctx, backward_idx, 1);
cedrus_write(dev, VE_DEC_MPEG_BWD_REF_LUMA_ADDR, bwd_luma_addr);
cedrus_write(dev, VE_DEC_MPEG_BWD_REF_CHROMA_ADDR, bwd_chroma_addr);
diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_video.c b/drivers/staging/media/sunxi/cedrus/cedrus_video.c
index 8721b4a7d496..b47854b3bce4 100644
--- a/drivers/staging/media/sunxi/cedrus/cedrus_video.c
+++ b/drivers/staging/media/sunxi/cedrus/cedrus_video.c
@@ -282,8 +282,13 @@ static int cedrus_s_fmt_vid_cap(struct file *file, void *priv,
{
struct cedrus_ctx *ctx = cedrus_file2ctx(file);
struct cedrus_dev *dev = ctx->dev;
+ struct vb2_queue *vq;
int ret;
+ vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
+ if (vb2_is_busy(vq))
+ return -EBUSY;
+
ret = cedrus_try_fmt_vid_cap(file, priv, f);
if (ret)
return ret;
@@ -299,8 +304,13 @@ static int cedrus_s_fmt_vid_out(struct file *file, void *priv,
struct v4l2_format *f)
{
struct cedrus_ctx *ctx = cedrus_file2ctx(file);
+ struct vb2_queue *vq;
int ret;
+ vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
+ if (vb2_is_busy(vq))
+ return -EBUSY;
+
ret = cedrus_try_fmt_vid_out(file, priv, f);
if (ret)
return ret;
@@ -416,6 +426,14 @@ static void cedrus_buf_cleanup(struct vb2_buffer *vb)
ctx->dst_bufs[vb->index] = NULL;
}
+static int cedrus_buf_out_validate(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+
+ vbuf->field = V4L2_FIELD_NONE;
+ return 0;
+}
+
static int cedrus_buf_prepare(struct vb2_buffer *vb)
{
struct vb2_queue *vq = vb->vb2_queue;
@@ -493,6 +511,7 @@ static struct vb2_ops cedrus_qops = {
.buf_init = cedrus_buf_init,
.buf_cleanup = cedrus_buf_cleanup,
.buf_queue = cedrus_buf_queue,
+ .buf_out_validate = cedrus_buf_out_validate,
.buf_request_complete = cedrus_buf_request_complete,
.start_streaming = cedrus_start_streaming,
.stop_streaming = cedrus_stop_streaming,
diff --git a/drivers/staging/media/zoran/zoran.h b/drivers/staging/media/zoran/zoran.h
index 9bb3c21aa275..e84fb604a689 100644
--- a/drivers/staging/media/zoran/zoran.h
+++ b/drivers/staging/media/zoran/zoran.h
@@ -35,7 +35,7 @@ struct zoran_sync {
unsigned long frame; /* number of buffer that has been free'd */
unsigned long length; /* number of code bytes in buffer (capture only) */
unsigned long seq; /* frame sequence number */
- struct timeval timestamp; /* timestamp */
+ u64 ts; /* timestamp */
};
diff --git a/drivers/staging/media/zoran/zoran_card.c b/drivers/staging/media/zoran/zoran_card.c
index 94dadbba7cd5..ea10523194e8 100644
--- a/drivers/staging/media/zoran/zoran_card.c
+++ b/drivers/staging/media/zoran/zoran_card.c
@@ -1470,7 +1470,7 @@ static int __init zoran_init(void)
v4l_nbufs = 2;
if (v4l_nbufs > VIDEO_MAX_FRAME)
v4l_nbufs = VIDEO_MAX_FRAME;
- /* The user specfies the in KB, we want them in byte
+ /* The user specifies the in KB, we want them in byte
* (and page aligned) */
v4l_bufsize = PAGE_ALIGN(v4l_bufsize * 1024);
if (v4l_bufsize < 32768)
diff --git a/drivers/staging/media/zoran/zoran_device.c b/drivers/staging/media/zoran/zoran_device.c
index 40adceebca7e..22b27632762d 100644
--- a/drivers/staging/media/zoran/zoran_device.c
+++ b/drivers/staging/media/zoran/zoran_device.c
@@ -612,7 +612,7 @@ zr36057_set_memgrab (struct zoran *zr,
zr->v4l_memgrab_active = 0;
zr->v4l_grab_frame = NO_GRAB_ACTIVE;
- /* reenable grabbing to screen if it was running */
+ /* re-enable grabbing to screen if it was running */
if (zr->v4l_overlay_active) {
zr36057_overlay(zr, 1);
} else {
@@ -1151,7 +1151,7 @@ zoran_reap_stat_com (struct zoran *zr)
}
frame = zr->jpg_pend[zr->jpg_dma_tail & BUZ_MASK_FRAME];
buffer = &zr->jpg_buffers.buffer[frame];
- v4l2_get_timestamp(&buffer->bs.timestamp);
+ buffer->bs.ts = ktime_get_ns();
if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) {
buffer->bs.length = (stat_com & 0x7fffff) >> 1;
@@ -1389,7 +1389,7 @@ zoran_irq (int irq,
zr->v4l_buffers.buffer[zr->v4l_grab_frame].state = BUZ_STATE_DONE;
zr->v4l_buffers.buffer[zr->v4l_grab_frame].bs.seq = zr->v4l_grab_seq;
- v4l2_get_timestamp(&zr->v4l_buffers.buffer[zr->v4l_grab_frame].bs.timestamp);
+ zr->v4l_buffers.buffer[zr->v4l_grab_frame].bs.ts = ktime_get_ns();
zr->v4l_grab_frame = NO_GRAB_ACTIVE;
zr->v4l_pend_tail++;
}
diff --git a/drivers/staging/media/zoran/zoran_driver.c b/drivers/staging/media/zoran/zoran_driver.c
index 27c76e2eeb41..04f88f9d6bb4 100644
--- a/drivers/staging/media/zoran/zoran_driver.c
+++ b/drivers/staging/media/zoran/zoran_driver.c
@@ -1354,7 +1354,7 @@ static int zoran_v4l2_buffer_status(struct zoran_fh *fh,
fh->buffers.buffer[num].state == BUZ_STATE_USER) {
buf->sequence = fh->buffers.buffer[num].bs.seq;
buf->flags |= V4L2_BUF_FLAG_DONE;
- buf->timestamp = fh->buffers.buffer[num].bs.timestamp;
+ buf->timestamp = ns_to_timeval(fh->buffers.buffer[num].bs.ts);
} else {
buf->flags |= V4L2_BUF_FLAG_QUEUED;
}
@@ -1388,7 +1388,7 @@ static int zoran_v4l2_buffer_status(struct zoran_fh *fh,
if (fh->buffers.buffer[num].state == BUZ_STATE_DONE ||
fh->buffers.buffer[num].state == BUZ_STATE_USER) {
buf->sequence = fh->buffers.buffer[num].bs.seq;
- buf->timestamp = fh->buffers.buffer[num].bs.timestamp;
+ buf->timestamp = ns_to_timeval(fh->buffers.buffer[num].bs.ts);
buf->bytesused = fh->buffers.buffer[num].bs.length;
buf->flags |= V4L2_BUF_FLAG_DONE;
} else {
diff --git a/drivers/staging/mt7621-dts/gbpc1.dts b/drivers/staging/mt7621-dts/gbpc1.dts
index b73385540216..250c15ace2a7 100644
--- a/drivers/staging/mt7621-dts/gbpc1.dts
+++ b/drivers/staging/mt7621-dts/gbpc1.dts
@@ -117,22 +117,6 @@
status = "okay";
};
-&ethernet {
- //mtd-mac-address = <&factory 0xe000>;
- gmac1: mac@0 {
- compatible = "mediatek,eth-mac";
- reg = <0>;
- phy-handle = <&phy1>;
- };
-
- mdio-bus {
- phy1: ethernet-phy@1 {
- reg = <1>;
- phy-mode = "rgmii";
- };
- };
-};
-
&pinctrl {
state_default: pinctrl0 {
gpio {
@@ -141,3 +125,16 @@
};
};
};
+
+&switch0 {
+ ports {
+ port@0 {
+ label = "ethblack";
+ status = "ok";
+ };
+ port@4 {
+ label = "ethblue";
+ status = "ok";
+ };
+ };
+};
diff --git a/drivers/staging/mt7621-dts/mt7621.dtsi b/drivers/staging/mt7621-dts/mt7621.dtsi
index 6aff3680ce4b..17020e24abd2 100644
--- a/drivers/staging/mt7621-dts/mt7621.dtsi
+++ b/drivers/staging/mt7621-dts/mt7621.dtsi
@@ -372,16 +372,83 @@
mediatek,ethsys = <&ethsys>;
- mediatek,switch = <&gsw>;
+ gmac0: mac@0 {
+ compatible = "mediatek,eth-mac";
+ reg = <0>;
+ phy-mode = "rgmii";
+ fixed-link {
+ speed = <1000>;
+ full-duplex;
+ pause;
+ };
+ };
+ gmac1: mac@1 {
+ compatible = "mediatek,eth-mac";
+ reg = <1>;
+ status = "off";
+ phy-mode = "rgmii";
+ phy-handle = <&phy5>;
+ };
mdio-bus {
#address-cells = <1>;
#size-cells = <0>;
- phy1f: ethernet-phy@1f {
- reg = <0x1f>;
+ phy5: ethernet-phy@5 {
+ reg = <5>;
phy-mode = "rgmii";
};
+
+ switch0: switch0@0 {
+ compatible = "mediatek,mt7621";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0>;
+ mediatek,mcm;
+ resets = <&rstctrl 2>;
+ reset-names = "mcm";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0>;
+ port@0 {
+ status = "off";
+ reg = <0>;
+ label = "lan0";
+ };
+ port@1 {
+ status = "off";
+ reg = <1>;
+ label = "lan1";
+ };
+ port@2 {
+ status = "off";
+ reg = <2>;
+ label = "lan2";
+ };
+ port@3 {
+ status = "off";
+ reg = <3>;
+ label = "lan3";
+ };
+ port@4 {
+ status = "off";
+ reg = <4>;
+ label = "lan4";
+ };
+ port@6 {
+ reg = <6>;
+ label = "cpu";
+ ethernet = <&gmac0>;
+ phy-mode = "trgmii";
+ fixed-link {
+ speed = <1000>;
+ full-duplex;
+ };
+ };
+ };
+ };
};
};
diff --git a/drivers/staging/mt7621-eth/Documentation/devicetree/bindings/net/mediatek-net-gsw.txt b/drivers/staging/mt7621-eth/Documentation/devicetree/bindings/net/mediatek-net-gsw.txt
deleted file mode 100644
index 596b38552697..000000000000
--- a/drivers/staging/mt7621-eth/Documentation/devicetree/bindings/net/mediatek-net-gsw.txt
+++ /dev/null
@@ -1,48 +0,0 @@
-Mediatek Gigabit Switch
-=======================
-
-The mediatek gigabit switch can be found on Mediatek SoCs.
-
-Required properties:
-- compatible: Should be "mediatek,mt7620-gsw", "mediatek,mt7621-gsw",
- "mediatek,mt7623-gsw"
-- reg: Address and length of the register set for the device
-- interrupts: Should contain the gigabit switches interrupt
-
-
-Additional required properties for ARM based SoCs:
-- mediatek,reset-pin: phandle describing the reset GPIO
-- clocks: the clocks used by the switch
-- clock-names: the names of the clocks listed in the clocks property
- these should be "trgpll", "esw", "gp2", "gp1"
-- mt7530-supply: the phandle of the regulator used to power the switch
-- mediatek,pctl-regmap: phandle to the port control regmap. this is used to
- setup the drive current
-
-
-Optional properties:
-- interrupt-parent: Should be the phandle for the interrupt controller
- that services interrupts for this device
-
-Example:
-
-gsw: switch@1b100000 {
- compatible = "mediatek,mt7623-gsw";
- reg = <0 0x1b110000 0 0x300000>;
-
- interrupt-parent = <&pio>;
- interrupts = <168 IRQ_TYPE_EDGE_RISING>;
-
- clocks = <&apmixedsys CLK_APMIXED_TRGPLL>,
- <&ethsys CLK_ETHSYS_ESW>,
- <&ethsys CLK_ETHSYS_GP2>,
- <&ethsys CLK_ETHSYS_GP1>;
- clock-names = "trgpll", "esw", "gp2", "gp1";
-
- mt7530-supply = <&mt6323_vpa_reg>;
-
- mediatek,pctl-regmap = <&syscfg_pctl_a>;
- mediatek,reset-pin = <&pio 15 0>;
-
- status = "okay";
-};
diff --git a/drivers/staging/mt7621-eth/Kconfig b/drivers/staging/mt7621-eth/Kconfig
deleted file mode 100644
index 44ea86c7a96c..000000000000
--- a/drivers/staging/mt7621-eth/Kconfig
+++ /dev/null
@@ -1,39 +0,0 @@
-config NET_VENDOR_MEDIATEK_STAGING
- bool "MediaTek ethernet driver - staging version"
- depends on RALINK
- ---help---
- If you have an MT7621 Mediatek SoC with ethernet, say Y.
-
-if NET_VENDOR_MEDIATEK_STAGING
-choice
- prompt "MAC type"
-
-config NET_MEDIATEK_MT7621
- bool "MT7621"
- depends on MIPS && SOC_MT7621
-
-endchoice
-
-config NET_MEDIATEK_SOC_STAGING
- tristate "MediaTek SoC Gigabit Ethernet support"
- depends on NET_VENDOR_MEDIATEK_STAGING
- select PHYLIB
- ---help---
- This driver supports the gigabit ethernet MACs in the
- MediaTek SoC family.
-
-config NET_MEDIATEK_MDIO
- def_bool NET_MEDIATEK_SOC_STAGING
- depends on NET_MEDIATEK_MT7621
- select PHYLIB
-
-config NET_MEDIATEK_MDIO_MT7620
- def_bool NET_MEDIATEK_SOC_STAGING
- depends on NET_MEDIATEK_MT7621
- select NET_MEDIATEK_MDIO
-
-config NET_MEDIATEK_GSW_MT7621
- def_tristate NET_MEDIATEK_SOC_STAGING
- depends on NET_MEDIATEK_MT7621
-
-endif #NET_VENDOR_MEDIATEK_STAGING
diff --git a/drivers/staging/mt7621-eth/Makefile b/drivers/staging/mt7621-eth/Makefile
deleted file mode 100644
index 018bcc3596b3..000000000000
--- a/drivers/staging/mt7621-eth/Makefile
+++ /dev/null
@@ -1,14 +0,0 @@
-#
-# Makefile for the Ralink SoCs built-in ethernet macs
-#
-
-mtk-eth-soc-y += mtk_eth_soc.o ethtool.o
-
-mtk-eth-soc-$(CONFIG_NET_MEDIATEK_MDIO) += mdio.o
-mtk-eth-soc-$(CONFIG_NET_MEDIATEK_MDIO_MT7620) += mdio_mt7620.o
-
-mtk-eth-soc-$(CONFIG_NET_MEDIATEK_MT7621) += soc_mt7621.o
-
-obj-$(CONFIG_NET_MEDIATEK_GSW_MT7621) += gsw_mt7621.o
-
-obj-$(CONFIG_NET_MEDIATEK_SOC_STAGING) += mtk-eth-soc.o
diff --git a/drivers/staging/mt7621-eth/TODO b/drivers/staging/mt7621-eth/TODO
deleted file mode 100644
index f9e47d4b4cd4..000000000000
--- a/drivers/staging/mt7621-eth/TODO
+++ /dev/null
@@ -1,13 +0,0 @@
-
-- verify devicetree documentation is consistent with code
-- fix ethtool - currently doesn't return valid data.
-- general code review and clean up
-- add support for second MAC on mt7621
-- convert gsw code to use switchdev interfaces
-- md7620_mmi_write etc should probably be wrapped
- in a regmap abstraction.
-- Get soc_mt7621 to work with QDMA TX if possible.
-- Ensure phys are correctly configured when a cable
- is plugged in.
-
-Cc: NeilBrown <neil@brown.name>
diff --git a/drivers/staging/mt7621-eth/ethtool.c b/drivers/staging/mt7621-eth/ethtool.c
deleted file mode 100644
index 8c4228e2c987..000000000000
--- a/drivers/staging/mt7621-eth/ethtool.c
+++ /dev/null
@@ -1,250 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * Copyright (C) 2009-2016 John Crispin <blogic@openwrt.org>
- * Copyright (C) 2009-2016 Felix Fietkau <nbd@openwrt.org>
- * Copyright (C) 2013-2016 Michael Lee <igvtee@gmail.com>
- */
-
-#include "mtk_eth_soc.h"
-#include "ethtool.h"
-
-struct mtk_stat {
- char name[ETH_GSTRING_LEN];
- unsigned int idx;
-};
-
-#define MTK_HW_STAT(stat) { \
- .name = #stat, \
- .idx = offsetof(struct mtk_hw_stats, stat) / sizeof(u64) \
-}
-
-static const struct mtk_stat mtk_ethtool_hw_stats[] = {
- MTK_HW_STAT(tx_bytes),
- MTK_HW_STAT(tx_packets),
- MTK_HW_STAT(tx_skip),
- MTK_HW_STAT(tx_collisions),
- MTK_HW_STAT(rx_bytes),
- MTK_HW_STAT(rx_packets),
- MTK_HW_STAT(rx_overflow),
- MTK_HW_STAT(rx_fcs_errors),
- MTK_HW_STAT(rx_short_errors),
- MTK_HW_STAT(rx_long_errors),
- MTK_HW_STAT(rx_checksum_errors),
- MTK_HW_STAT(rx_flow_control_packets),
-};
-
-#define MTK_HW_STATS_LEN ARRAY_SIZE(mtk_ethtool_hw_stats)
-
-static int mtk_get_link_ksettings(struct net_device *dev,
- struct ethtool_link_ksettings *cmd)
-{
- struct mtk_mac *mac = netdev_priv(dev);
- int err;
-
- if (!mac->phy_dev)
- return -ENODEV;
-
- if (mac->phy_flags == MTK_PHY_FLAG_ATTACH) {
- err = phy_read_status(mac->phy_dev);
- if (err)
- return -ENODEV;
- }
-
- phy_ethtool_ksettings_get(mac->phy_dev, cmd);
- return 0;
-}
-
-static int mtk_set_link_ksettings(struct net_device *dev,
- const struct ethtool_link_ksettings *cmd)
-{
- struct mtk_mac *mac = netdev_priv(dev);
-
- if (!mac->phy_dev)
- return -ENODEV;
-
- if (cmd->base.phy_address != mac->phy_dev->mdio.addr) {
- if (mac->hw->phy->phy_node[cmd->base.phy_address]) {
- mac->phy_dev = mac->hw->phy->phy[cmd->base.phy_address];
- mac->phy_flags = MTK_PHY_FLAG_PORT;
- } else if (mac->hw->mii_bus) {
- mac->phy_dev = mdiobus_get_phy(mac->hw->mii_bus,
- cmd->base.phy_address);
- if (!mac->phy_dev)
- return -ENODEV;
- mac->phy_flags = MTK_PHY_FLAG_ATTACH;
- } else {
- return -ENODEV;
- }
- }
-
- return phy_ethtool_ksettings_set(mac->phy_dev, cmd);
-}
-
-static void mtk_get_drvinfo(struct net_device *dev,
- struct ethtool_drvinfo *info)
-{
- struct mtk_mac *mac = netdev_priv(dev);
- struct mtk_soc_data *soc = mac->hw->soc;
-
- strlcpy(info->driver, mac->hw->dev->driver->name, sizeof(info->driver));
- strlcpy(info->bus_info, dev_name(mac->hw->dev), sizeof(info->bus_info));
-
- if (soc->reg_table[MTK_REG_MTK_COUNTER_BASE])
- info->n_stats = MTK_HW_STATS_LEN;
-}
-
-static u32 mtk_get_msglevel(struct net_device *dev)
-{
- struct mtk_mac *mac = netdev_priv(dev);
-
- return mac->hw->msg_enable;
-}
-
-static void mtk_set_msglevel(struct net_device *dev, u32 value)
-{
- struct mtk_mac *mac = netdev_priv(dev);
-
- mac->hw->msg_enable = value;
-}
-
-static int mtk_nway_reset(struct net_device *dev)
-{
- struct mtk_mac *mac = netdev_priv(dev);
-
- if (!mac->phy_dev)
- return -EOPNOTSUPP;
-
- return genphy_restart_aneg(mac->phy_dev);
-}
-
-static u32 mtk_get_link(struct net_device *dev)
-{
- struct mtk_mac *mac = netdev_priv(dev);
- int err;
-
- if (!mac->phy_dev)
- goto out_get_link;
-
- if (mac->phy_flags == MTK_PHY_FLAG_ATTACH) {
- err = genphy_update_link(mac->phy_dev);
- if (err)
- goto out_get_link;
- }
-
- return mac->phy_dev->link;
-
-out_get_link:
- return ethtool_op_get_link(dev);
-}
-
-static int mtk_set_ringparam(struct net_device *dev,
- struct ethtool_ringparam *ring)
-{
- struct mtk_mac *mac = netdev_priv(dev);
-
- if ((ring->tx_pending < 2) ||
- (ring->rx_pending < 2) ||
- (ring->rx_pending > mac->hw->soc->dma_ring_size) ||
- (ring->tx_pending > mac->hw->soc->dma_ring_size))
- return -EINVAL;
-
- dev->netdev_ops->ndo_stop(dev);
-
- mac->hw->tx_ring.tx_ring_size = BIT(fls(ring->tx_pending) - 1);
- mac->hw->rx_ring[0].rx_ring_size = BIT(fls(ring->rx_pending) - 1);
-
- return dev->netdev_ops->ndo_open(dev);
-}
-
-static void mtk_get_ringparam(struct net_device *dev,
- struct ethtool_ringparam *ring)
-{
- struct mtk_mac *mac = netdev_priv(dev);
-
- ring->rx_max_pending = mac->hw->soc->dma_ring_size;
- ring->tx_max_pending = mac->hw->soc->dma_ring_size;
- ring->rx_pending = mac->hw->rx_ring[0].rx_ring_size;
- ring->tx_pending = mac->hw->tx_ring.tx_ring_size;
-}
-
-static void mtk_get_strings(struct net_device *dev, u32 stringset, u8 *data)
-{
- int i;
-
- switch (stringset) {
- case ETH_SS_STATS:
- for (i = 0; i < MTK_HW_STATS_LEN; i++) {
- memcpy(data, mtk_ethtool_hw_stats[i].name,
- ETH_GSTRING_LEN);
- data += ETH_GSTRING_LEN;
- }
- break;
- }
-}
-
-static int mtk_get_sset_count(struct net_device *dev, int sset)
-{
- switch (sset) {
- case ETH_SS_STATS:
- return MTK_HW_STATS_LEN;
- default:
- return -EOPNOTSUPP;
- }
-}
-
-static void mtk_get_ethtool_stats(struct net_device *dev,
- struct ethtool_stats *stats, u64 *data)
-{
- struct mtk_mac *mac = netdev_priv(dev);
- struct mtk_hw_stats *hwstats = mac->hw_stats;
- unsigned int start;
- int i;
-
- if (netif_running(dev) && netif_device_present(dev)) {
- if (spin_trylock(&hwstats->stats_lock)) {
- mtk_stats_update_mac(mac);
- spin_unlock(&hwstats->stats_lock);
- }
- }
-
- do {
- start = u64_stats_fetch_begin_irq(&hwstats->syncp);
- for (i = 0; i < MTK_HW_STATS_LEN; i++)
- data[i] = ((u64 *)hwstats)[mtk_ethtool_hw_stats[i].idx];
-
- } while (u64_stats_fetch_retry_irq(&hwstats->syncp, start));
-}
-
-static struct ethtool_ops mtk_ethtool_ops = {
- .get_link_ksettings = mtk_get_link_ksettings,
- .set_link_ksettings = mtk_set_link_ksettings,
- .get_drvinfo = mtk_get_drvinfo,
- .get_msglevel = mtk_get_msglevel,
- .set_msglevel = mtk_set_msglevel,
- .nway_reset = mtk_nway_reset,
- .get_link = mtk_get_link,
- .set_ringparam = mtk_set_ringparam,
- .get_ringparam = mtk_get_ringparam,
-};
-
-void mtk_set_ethtool_ops(struct net_device *netdev)
-{
- struct mtk_mac *mac = netdev_priv(netdev);
- struct mtk_soc_data *soc = mac->hw->soc;
-
- if (soc->reg_table[MTK_REG_MTK_COUNTER_BASE]) {
- mtk_ethtool_ops.get_strings = mtk_get_strings;
- mtk_ethtool_ops.get_sset_count = mtk_get_sset_count;
- mtk_ethtool_ops.get_ethtool_stats = mtk_get_ethtool_stats;
- }
-
- netdev->ethtool_ops = &mtk_ethtool_ops;
-}
diff --git a/drivers/staging/mt7621-eth/ethtool.h b/drivers/staging/mt7621-eth/ethtool.h
deleted file mode 100644
index 0071469aea6c..000000000000
--- a/drivers/staging/mt7621-eth/ethtool.h
+++ /dev/null
@@ -1,15 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * Copyright (C) 2009-2016 John Crispin <blogic@openwrt.org>
- * Copyright (C) 2009-2016 Felix Fietkau <nbd@openwrt.org>
- * Copyright (C) 2013-2016 Michael Lee <igvtee@gmail.com>
- */
-
-#ifndef MTK_ETHTOOL_H
-#define MTK_ETHTOOL_H
-
-#include <linux/ethtool.h>
-
-void mtk_set_ethtool_ops(struct net_device *netdev);
-
-#endif /* MTK_ETHTOOL_H */
diff --git a/drivers/staging/mt7621-eth/gsw_mt7620.h b/drivers/staging/mt7621-eth/gsw_mt7620.h
deleted file mode 100644
index 70f7e5481952..000000000000
--- a/drivers/staging/mt7621-eth/gsw_mt7620.h
+++ /dev/null
@@ -1,277 +0,0 @@
-/* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * Copyright (C) 2009-2016 John Crispin <blogic@openwrt.org>
- * Copyright (C) 2009-2016 Felix Fietkau <nbd@openwrt.org>
- * Copyright (C) 2013-2016 Michael Lee <igvtee@gmail.com>
- */
-
-#ifndef _RALINK_GSW_MT7620_H__
-#define _RALINK_GSW_MT7620_H__
-
-#define GSW_REG_PHY_TIMEOUT (5 * HZ)
-
-#define MT7620_GSW_REG_PIAC 0x0004
-
-#define GSW_NUM_VLANS 16
-#define GSW_NUM_VIDS 4096
-#define GSW_NUM_PORTS 7
-#define GSW_PORT6 6
-
-#define GSW_MDIO_ACCESS BIT(31)
-#define GSW_MDIO_READ BIT(19)
-#define GSW_MDIO_WRITE BIT(18)
-#define GSW_MDIO_START BIT(16)
-#define GSW_MDIO_ADDR_SHIFT 20
-#define GSW_MDIO_REG_SHIFT 25
-
-#define GSW_REG_PORT_PMCR(x) (0x3000 + (x * 0x100))
-#define GSW_REG_PORT_STATUS(x) (0x3008 + (x * 0x100))
-#define GSW_REG_SMACCR0 0x3fE4
-#define GSW_REG_SMACCR1 0x3fE8
-#define GSW_REG_CKGCR 0x3ff0
-
-#define GSW_REG_IMR 0x7008
-#define GSW_REG_ISR 0x700c
-#define GSW_REG_GPC1 0x7014
-
-#define SYSC_REG_CHIP_REV_ID 0x0c
-#define SYSC_REG_CFG 0x10
-#define SYSC_REG_CFG1 0x14
-#define RST_CTRL_MCM BIT(2)
-#define SYSC_PAD_RGMII2_MDIO 0x58
-#define SYSC_GPIO_MODE 0x60
-
-#define PORT_IRQ_ST_CHG 0x7f
-
-#define MT7621_ESW_PHY_POLLING 0x0000
-#define MT7620_ESW_PHY_POLLING 0x7000
-
-#define PMCR_IPG BIT(18)
-#define PMCR_MAC_MODE BIT(16)
-#define PMCR_FORCE BIT(15)
-#define PMCR_TX_EN BIT(14)
-#define PMCR_RX_EN BIT(13)
-#define PMCR_BACKOFF BIT(9)
-#define PMCR_BACKPRES BIT(8)
-#define PMCR_RX_FC BIT(5)
-#define PMCR_TX_FC BIT(4)
-#define PMCR_SPEED(_x) (_x << 2)
-#define PMCR_DUPLEX BIT(1)
-#define PMCR_LINK BIT(0)
-
-#define PHY_AN_EN BIT(31)
-#define PHY_PRE_EN BIT(30)
-#define PMY_MDC_CONF(_x) ((_x & 0x3f) << 24)
-
-/* ethernet subsystem config register */
-#define ETHSYS_SYSCFG0 0x14
-/* ethernet subsystem clock register */
-#define ETHSYS_CLKCFG0 0x2c
-#define ETHSYS_TRGMII_CLK_SEL362_5 BIT(11)
-
-/* p5 RGMII wrapper TX clock control register */
-#define MT7530_P5RGMIITXCR 0x7b04
-/* p5 RGMII wrapper RX clock control register */
-#define MT7530_P5RGMIIRXCR 0x7b00
-/* TRGMII TDX ODT registers */
-#define MT7530_TRGMII_TD0_ODT 0x7a54
-#define MT7530_TRGMII_TD1_ODT 0x7a5c
-#define MT7530_TRGMII_TD2_ODT 0x7a64
-#define MT7530_TRGMII_TD3_ODT 0x7a6c
-#define MT7530_TRGMII_TD4_ODT 0x7a74
-#define MT7530_TRGMII_TD5_ODT 0x7a7c
-/* TRGMII TCK ctrl register */
-#define MT7530_TRGMII_TCK_CTRL 0x7a78
-/* TRGMII Tx ctrl register */
-#define MT7530_TRGMII_TXCTRL 0x7a40
-/* port 6 extended control register */
-#define MT7530_P6ECR 0x7830
-/* IO driver control register */
-#define MT7530_IO_DRV_CR 0x7810
-/* top signal control register */
-#define MT7530_TOP_SIG_CTRL 0x7808
-/* modified hwtrap register */
-#define MT7530_MHWTRAP 0x7804
-/* hwtrap status register */
-#define MT7530_HWTRAP 0x7800
-/* status interrupt register */
-#define MT7530_SYS_INT_STS 0x700c
-/* system nterrupt register */
-#define MT7530_SYS_INT_EN 0x7008
-/* system control register */
-#define MT7530_SYS_CTRL 0x7000
-/* port MAC status register */
-#define MT7530_PMSR_P(x) (0x3008 + (x * 0x100))
-/* port MAC control register */
-#define MT7530_PMCR_P(x) (0x3000 + (x * 0x100))
-
-#define MT7621_XTAL_SHIFT 6
-#define MT7621_XTAL_MASK 0x7
-#define MT7621_XTAL_25 6
-#define MT7621_XTAL_40 3
-#define MT7621_MDIO_DRV_MASK (3 << 4)
-#define MT7621_GE1_MODE_MASK (3 << 12)
-
-#define TRGMII_TXCTRL_TXC_INV BIT(30)
-#define P6ECR_INTF_MODE_RGMII BIT(1)
-#define P5RGMIIRXCR_C_ALIGN BIT(8)
-#define P5RGMIIRXCR_DELAY_2 BIT(1)
-#define P5RGMIITXCR_DELAY_2 (BIT(8) | BIT(2))
-
-/* TOP_SIG_CTRL bits */
-#define TOP_SIG_CTRL_NORMAL (BIT(17) | BIT(16))
-
-/* MHWTRAP bits */
-#define MHWTRAP_MANUAL BIT(16)
-#define MHWTRAP_P5_MAC_SEL BIT(13)
-#define MHWTRAP_P6_DIS BIT(8)
-#define MHWTRAP_P5_RGMII_MODE BIT(7)
-#define MHWTRAP_P5_DIS BIT(6)
-#define MHWTRAP_PHY_ACCESS BIT(5)
-
-/* HWTRAP bits */
-#define HWTRAP_XTAL_SHIFT 9
-#define HWTRAP_XTAL_MASK 0x3
-
-/* SYS_CTRL bits */
-#define SYS_CTRL_SW_RST BIT(1)
-#define SYS_CTRL_REG_RST BIT(0)
-
-/* PMCR bits */
-#define PMCR_IFG_XMIT_96 BIT(18)
-#define PMCR_MAC_MODE BIT(16)
-#define PMCR_FORCE_MODE BIT(15)
-#define PMCR_TX_EN BIT(14)
-#define PMCR_RX_EN BIT(13)
-#define PMCR_BACK_PRES_EN BIT(9)
-#define PMCR_BACKOFF_EN BIT(8)
-#define PMCR_TX_FC_EN BIT(5)
-#define PMCR_RX_FC_EN BIT(4)
-#define PMCR_FORCE_SPEED_1000 BIT(3)
-#define PMCR_FORCE_FDX BIT(1)
-#define PMCR_FORCE_LNK BIT(0)
-#define PMCR_FIXED_LINK (PMCR_IFG_XMIT_96 | PMCR_MAC_MODE | \
- PMCR_FORCE_MODE | PMCR_TX_EN | PMCR_RX_EN | \
- PMCR_BACK_PRES_EN | PMCR_BACKOFF_EN | \
- PMCR_FORCE_SPEED_1000 | PMCR_FORCE_FDX | \
- PMCR_FORCE_LNK)
-
-#define PMCR_FIXED_LINK_FC (PMCR_FIXED_LINK | \
- PMCR_TX_FC_EN | PMCR_RX_FC_EN)
-
-/* TRGMII control registers */
-#define GSW_INTF_MODE 0x390
-#define GSW_TRGMII_TD0_ODT 0x354
-#define GSW_TRGMII_TD1_ODT 0x35c
-#define GSW_TRGMII_TD2_ODT 0x364
-#define GSW_TRGMII_TD3_ODT 0x36c
-#define GSW_TRGMII_TXCTL_ODT 0x374
-#define GSW_TRGMII_TCK_ODT 0x37c
-#define GSW_TRGMII_RCK_CTRL 0x300
-
-#define INTF_MODE_TRGMII BIT(1)
-#define TRGMII_RCK_CTRL_RX_RST BIT(31)
-
-/* Mac control registers */
-#define MTK_MAC_P2_MCR 0x200
-#define MTK_MAC_P1_MCR 0x100
-
-#define MAC_MCR_MAX_RX_2K BIT(29)
-#define MAC_MCR_IPG_CFG (BIT(18) | BIT(16))
-#define MAC_MCR_FORCE_MODE BIT(15)
-#define MAC_MCR_TX_EN BIT(14)
-#define MAC_MCR_RX_EN BIT(13)
-#define MAC_MCR_BACKOFF_EN BIT(9)
-#define MAC_MCR_BACKPR_EN BIT(8)
-#define MAC_MCR_FORCE_RX_FC BIT(5)
-#define MAC_MCR_FORCE_TX_FC BIT(4)
-#define MAC_MCR_SPEED_1000 BIT(3)
-#define MAC_MCR_FORCE_DPX BIT(1)
-#define MAC_MCR_FORCE_LINK BIT(0)
-#define MAC_MCR_FIXED_LINK (MAC_MCR_MAX_RX_2K | MAC_MCR_IPG_CFG | \
- MAC_MCR_FORCE_MODE | MAC_MCR_TX_EN | \
- MAC_MCR_RX_EN | MAC_MCR_BACKOFF_EN | \
- MAC_MCR_BACKPR_EN | MAC_MCR_FORCE_RX_FC | \
- MAC_MCR_FORCE_TX_FC | MAC_MCR_SPEED_1000 | \
- MAC_MCR_FORCE_DPX | MAC_MCR_FORCE_LINK)
-#define MAC_MCR_FIXED_LINK_FC (MAC_MCR_MAX_RX_2K | MAC_MCR_IPG_CFG | \
- MAC_MCR_FIXED_LINK)
-
-/* possible XTAL speed */
-#define MT7623_XTAL_40 0
-#define MT7623_XTAL_20 1
-#define MT7623_XTAL_25 3
-
-/* GPIO port control registers */
-#define GPIO_OD33_CTRL8 0x4c0
-#define GPIO_BIAS_CTRL 0xed0
-#define GPIO_DRV_SEL10 0xf00
-
-/* on MT7620 the functio of port 4 can be software configured */
-enum {
- PORT4_EPHY = 0,
- PORT4_EXT,
-};
-
-/* struct mt7620_gsw - the structure that holds the SoC specific data
- * @dev: The Device struct
- * @base: The base address
- * @piac_offset: The PIAC base may change depending on SoC
- * @irq: The IRQ we are using
- * @port4: The port4 mode on MT7620
- * @autopoll: Is MDIO autopolling enabled
- * @ethsys: The ethsys register map
- * @pctl: The pin control register map
- * @clk_gsw: The switch clock
- * @clk_gp1: The gmac1 clock
- * @clk_gp2: The gmac2 clock
- * @clk_trgpll: The trgmii pll clock
- */
-struct mt7620_gsw {
- struct device *dev;
- void __iomem *base;
- u32 piac_offset;
- int irq;
- int port4;
- unsigned long int autopoll;
-
- struct regmap *ethsys;
- struct regmap *pctl;
-
- struct clk *clk_gsw;
- struct clk *clk_gp1;
- struct clk *clk_gp2;
- struct clk *clk_trgpll;
-};
-
-/* switch register I/O wrappers */
-void mtk_switch_w32(struct mt7620_gsw *gsw, u32 val, unsigned int reg);
-u32 mtk_switch_r32(struct mt7620_gsw *gsw, unsigned int reg);
-
-/* the callback used by the driver core to bringup the switch */
-int mtk_gsw_init(struct mtk_eth *eth);
-
-/* MDIO access wrappers */
-int mt7620_mdio_write(struct mii_bus *bus, int phy_addr, int phy_reg, u16 val);
-int mt7620_mdio_read(struct mii_bus *bus, int phy_addr, int phy_reg);
-void mt7620_mdio_link_adjust(struct mtk_eth *eth, int port);
-int mt7620_has_carrier(struct mtk_eth *eth);
-void mt7620_print_link_state(struct mtk_eth *eth, int port, int link,
- int speed, int duplex);
-void mt7530_mdio_w32(struct mt7620_gsw *gsw, u32 reg, u32 val);
-u32 mt7530_mdio_r32(struct mt7620_gsw *gsw, u32 reg);
-void mt7530_mdio_m32(struct mt7620_gsw *gsw, u32 mask, u32 set, u32 reg);
-
-u32 _mt7620_mii_write(struct mt7620_gsw *gsw, u32 phy_addr,
- u32 phy_register, u32 write_data);
-u32 _mt7620_mii_read(struct mt7620_gsw *gsw, int phy_addr, int phy_reg);
-void mt7620_handle_carrier(struct mtk_eth *eth);
-
-#endif
diff --git a/drivers/staging/mt7621-eth/gsw_mt7621.c b/drivers/staging/mt7621-eth/gsw_mt7621.c
deleted file mode 100644
index 53767b17bad9..000000000000
--- a/drivers/staging/mt7621-eth/gsw_mt7621.c
+++ /dev/null
@@ -1,297 +0,0 @@
-/* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * Copyright (C) 2009-2016 John Crispin <blogic@openwrt.org>
- * Copyright (C) 2009-2016 Felix Fietkau <nbd@openwrt.org>
- * Copyright (C) 2013-2016 Michael Lee <igvtee@gmail.com>
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/platform_device.h>
-#include <linux/of_device.h>
-#include <linux/of_irq.h>
-
-#include <ralink_regs.h>
-
-#include "mtk_eth_soc.h"
-#include "gsw_mt7620.h"
-
-void mtk_switch_w32(struct mt7620_gsw *gsw, u32 val, unsigned int reg)
-{
- iowrite32(val, gsw->base + reg);
-}
-EXPORT_SYMBOL_GPL(mtk_switch_w32);
-
-u32 mtk_switch_r32(struct mt7620_gsw *gsw, unsigned int reg)
-{
- return ioread32(gsw->base + reg);
-}
-EXPORT_SYMBOL_GPL(mtk_switch_r32);
-
-static irqreturn_t gsw_interrupt_mt7621(int irq, void *_eth)
-{
- struct mtk_eth *eth = (struct mtk_eth *)_eth;
- struct mt7620_gsw *gsw = (struct mt7620_gsw *)eth->sw_priv;
- u32 reg, i;
-
- reg = mt7530_mdio_r32(gsw, MT7530_SYS_INT_STS);
-
- for (i = 0; i < 5; i++) {
- unsigned int link;
-
- if ((reg & BIT(i)) == 0)
- continue;
-
- link = mt7530_mdio_r32(gsw, MT7530_PMSR_P(i)) & 0x1;
-
- if (link == eth->link[i])
- continue;
-
- eth->link[i] = link;
- if (link)
- netdev_info(*eth->netdev,
- "port %d link up\n", i);
- else
- netdev_info(*eth->netdev,
- "port %d link down\n", i);
- }
-
- mt7530_mdio_w32(gsw, MT7530_SYS_INT_STS, 0x1f);
-
- return IRQ_HANDLED;
-}
-
-static void mt7621_hw_init(struct mtk_eth *eth, struct mt7620_gsw *gsw,
- struct device_node *np)
-{
- u32 i;
- u32 val;
-
- /* hardware reset the switch */
- mtk_reset(eth, RST_CTRL_MCM);
- mdelay(10);
-
- /* reduce RGMII2 PAD driving strength */
- rt_sysc_m32(MT7621_MDIO_DRV_MASK, 0, SYSC_PAD_RGMII2_MDIO);
-
- /* gpio mux - RGMII1=Normal mode */
- rt_sysc_m32(BIT(14), 0, SYSC_GPIO_MODE);
-
- /* set GMAC1 RGMII mode */
- rt_sysc_m32(MT7621_GE1_MODE_MASK, 0, SYSC_REG_CFG1);
-
- /* enable MDIO to control MT7530 */
- rt_sysc_m32(3 << 12, 0, SYSC_GPIO_MODE);
-
- /* turn off all PHYs */
- for (i = 0; i <= 4; i++) {
- val = _mt7620_mii_read(gsw, i, 0x0);
- val |= BIT(11);
- _mt7620_mii_write(gsw, i, 0x0, val);
- }
-
- /* reset the switch */
- mt7530_mdio_w32(gsw, MT7530_SYS_CTRL,
- SYS_CTRL_SW_RST | SYS_CTRL_REG_RST);
- usleep_range(10, 20);
-
- if ((rt_sysc_r32(SYSC_REG_CHIP_REV_ID) & 0xFFFF) == 0x0101) {
- /* GE1, Force 1000M/FD, FC ON, MAX_RX_LENGTH 1536 */
- mtk_switch_w32(gsw, MAC_MCR_FIXED_LINK, MTK_MAC_P2_MCR);
- mt7530_mdio_w32(gsw, MT7530_PMCR_P(6), PMCR_FIXED_LINK);
- } else {
- /* GE1, Force 1000M/FD, FC ON, MAX_RX_LENGTH 1536 */
- mtk_switch_w32(gsw, MAC_MCR_FIXED_LINK_FC, MTK_MAC_P1_MCR);
- mt7530_mdio_w32(gsw, MT7530_PMCR_P(6), PMCR_FIXED_LINK_FC);
- }
-
- /* GE2, Link down */
- mtk_switch_w32(gsw, MAC_MCR_FORCE_MODE, MTK_MAC_P2_MCR);
-
- /* Enable Port 6, P5 as GMAC5, P5 disable */
- val = mt7530_mdio_r32(gsw, MT7530_MHWTRAP);
- /* Enable Port 6 */
- val &= ~MHWTRAP_P6_DIS;
- /* Disable Port 5 */
- val |= MHWTRAP_P5_DIS;
- /* manual override of HW-Trap */
- val |= MHWTRAP_MANUAL;
- mt7530_mdio_w32(gsw, MT7530_MHWTRAP, val);
-
- val = rt_sysc_r32(SYSC_REG_CFG);
- val = (val >> MT7621_XTAL_SHIFT) & MT7621_XTAL_MASK;
- if (val < MT7621_XTAL_25 && val >= MT7621_XTAL_40) {
- /* 40Mhz */
-
- /* disable MT7530 core clock */
- _mt7620_mii_write(gsw, 0, 13, 0x1f);
- _mt7620_mii_write(gsw, 0, 14, 0x410);
- _mt7620_mii_write(gsw, 0, 13, 0x401f);
- _mt7620_mii_write(gsw, 0, 14, 0x0);
-
- /* disable MT7530 PLL */
- _mt7620_mii_write(gsw, 0, 13, 0x1f);
- _mt7620_mii_write(gsw, 0, 14, 0x40d);
- _mt7620_mii_write(gsw, 0, 13, 0x401f);
- _mt7620_mii_write(gsw, 0, 14, 0x2020);
-
- /* for MT7530 core clock = 500Mhz */
- _mt7620_mii_write(gsw, 0, 13, 0x1f);
- _mt7620_mii_write(gsw, 0, 14, 0x40e);
- _mt7620_mii_write(gsw, 0, 13, 0x401f);
- _mt7620_mii_write(gsw, 0, 14, 0x119);
-
- /* enable MT7530 PLL */
- _mt7620_mii_write(gsw, 0, 13, 0x1f);
- _mt7620_mii_write(gsw, 0, 14, 0x40d);
- _mt7620_mii_write(gsw, 0, 13, 0x401f);
- _mt7620_mii_write(gsw, 0, 14, 0x2820);
-
- usleep_range(20, 40);
-
- /* enable MT7530 core clock */
- _mt7620_mii_write(gsw, 0, 13, 0x1f);
- _mt7620_mii_write(gsw, 0, 14, 0x410);
- _mt7620_mii_write(gsw, 0, 13, 0x401f);
- }
-
- /* RGMII */
- _mt7620_mii_write(gsw, 0, 14, 0x1);
-
- /* set MT7530 central align */
- mt7530_mdio_m32(gsw, BIT(0), P6ECR_INTF_MODE_RGMII, MT7530_P6ECR);
- mt7530_mdio_m32(gsw, TRGMII_TXCTRL_TXC_INV, 0,
- MT7530_TRGMII_TXCTRL);
- mt7530_mdio_w32(gsw, MT7530_TRGMII_TCK_CTRL, 0x855);
-
- /* delay setting for 10/1000M */
- mt7530_mdio_w32(gsw, MT7530_P5RGMIIRXCR,
- P5RGMIIRXCR_C_ALIGN | P5RGMIIRXCR_DELAY_2);
- mt7530_mdio_w32(gsw, MT7530_P5RGMIITXCR, 0x14);
-
- /* lower Tx Driving*/
- mt7530_mdio_w32(gsw, MT7530_TRGMII_TD0_ODT, 0x44);
- mt7530_mdio_w32(gsw, MT7530_TRGMII_TD1_ODT, 0x44);
- mt7530_mdio_w32(gsw, MT7530_TRGMII_TD2_ODT, 0x44);
- mt7530_mdio_w32(gsw, MT7530_TRGMII_TD3_ODT, 0x44);
- mt7530_mdio_w32(gsw, MT7530_TRGMII_TD4_ODT, 0x44);
- mt7530_mdio_w32(gsw, MT7530_TRGMII_TD5_ODT, 0x44);
-
- /* turn on all PHYs */
- for (i = 0; i <= 4; i++) {
- val = _mt7620_mii_read(gsw, i, 0);
- val &= ~BIT(11);
- _mt7620_mii_write(gsw, i, 0, val);
- }
-
-#define MT7530_NUM_PORTS 8
-#define REG_ESW_PORT_PCR(x) (0x2004 | ((x) << 8))
-#define REG_ESW_PORT_PVC(x) (0x2010 | ((x) << 8))
-#define REG_ESW_PORT_PPBV1(x) (0x2014 | ((x) << 8))
-#define MT7530_CPU_PORT 6
-
- /* This is copied from mt7530_apply_config in libreCMC driver */
- {
- int i;
-
- for (i = 0; i < MT7530_NUM_PORTS; i++)
- mt7530_mdio_w32(gsw, REG_ESW_PORT_PCR(i), 0x00400000);
-
- mt7530_mdio_w32(gsw, REG_ESW_PORT_PCR(MT7530_CPU_PORT),
- 0x00ff0000);
-
- for (i = 0; i < MT7530_NUM_PORTS; i++)
- mt7530_mdio_w32(gsw, REG_ESW_PORT_PVC(i), 0x810000c0);
- }
-
- /* enable irq */
- mt7530_mdio_m32(gsw, 0, 3 << 16, MT7530_TOP_SIG_CTRL);
- mt7530_mdio_w32(gsw, MT7530_SYS_INT_EN, 0x1f);
-}
-
-static const struct of_device_id mediatek_gsw_match[] = {
- { .compatible = "mediatek,mt7621-gsw" },
- {},
-};
-MODULE_DEVICE_TABLE(of, mediatek_gsw_match);
-
-int mtk_gsw_init(struct mtk_eth *eth)
-{
- struct device_node *np = eth->switch_np;
- struct platform_device *pdev = of_find_device_by_node(np);
- struct mt7620_gsw *gsw;
-
- if (!pdev)
- return -ENODEV;
-
- if (!of_device_is_compatible(np, mediatek_gsw_match->compatible))
- return -EINVAL;
-
- gsw = platform_get_drvdata(pdev);
- eth->sw_priv = gsw;
-
- if (!gsw->irq)
- return -EINVAL;
-
- request_irq(gsw->irq, gsw_interrupt_mt7621, 0,
- "gsw", eth);
- disable_irq(gsw->irq);
-
- mt7621_hw_init(eth, gsw, np);
-
- enable_irq(gsw->irq);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(mtk_gsw_init);
-
-static int mt7621_gsw_probe(struct platform_device *pdev)
-{
- struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- struct mt7620_gsw *gsw;
-
- gsw = devm_kzalloc(&pdev->dev, sizeof(struct mt7620_gsw), GFP_KERNEL);
- if (!gsw)
- return -ENOMEM;
-
- gsw->base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(gsw->base))
- return PTR_ERR(gsw->base);
-
- gsw->dev = &pdev->dev;
- gsw->irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
-
- platform_set_drvdata(pdev, gsw);
-
- return 0;
-}
-
-static int mt7621_gsw_remove(struct platform_device *pdev)
-{
- platform_set_drvdata(pdev, NULL);
-
- return 0;
-}
-
-static struct platform_driver gsw_driver = {
- .probe = mt7621_gsw_probe,
- .remove = mt7621_gsw_remove,
- .driver = {
- .name = "mt7621-gsw",
- .of_match_table = mediatek_gsw_match,
- },
-};
-
-module_platform_driver(gsw_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("John Crispin <blogic@openwrt.org>");
-MODULE_DESCRIPTION("GBit switch driver for Mediatek MT7621 SoC");
diff --git a/drivers/staging/mt7621-eth/mdio.c b/drivers/staging/mt7621-eth/mdio.c
deleted file mode 100644
index 5fea6a447eed..000000000000
--- a/drivers/staging/mt7621-eth/mdio.c
+++ /dev/null
@@ -1,275 +0,0 @@
-/* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License
- *
- * Copyright (C) 2009-2016 John Crispin <blogic@openwrt.org>
- * Copyright (C) 2009-2016 Felix Fietkau <nbd@openwrt.org>
- * Copyright (C) 2013-2016 Michael Lee <igvtee@gmail.com>
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/phy.h>
-#include <linux/of_net.h>
-#include <linux/of_mdio.h>
-
-#include "mtk_eth_soc.h"
-#include "mdio.h"
-
-static int mtk_mdio_reset(struct mii_bus *bus)
-{
- /* TODO */
- return 0;
-}
-
-static void mtk_phy_link_adjust(struct net_device *dev)
-{
- struct mtk_eth *eth = netdev_priv(dev);
- unsigned long flags;
- int i;
-
- spin_lock_irqsave(&eth->phy->lock, flags);
- for (i = 0; i < 8; i++) {
- if (eth->phy->phy_node[i]) {
- struct phy_device *phydev = eth->phy->phy[i];
- int status_change = 0;
-
- if (phydev->link)
- if (eth->phy->duplex[i] != phydev->duplex ||
- eth->phy->speed[i] != phydev->speed)
- status_change = 1;
-
- if (phydev->link != eth->link[i])
- status_change = 1;
-
- switch (phydev->speed) {
- case SPEED_1000:
- case SPEED_100:
- case SPEED_10:
- eth->link[i] = phydev->link;
- eth->phy->duplex[i] = phydev->duplex;
- eth->phy->speed[i] = phydev->speed;
-
- if (status_change &&
- eth->soc->mdio_adjust_link)
- eth->soc->mdio_adjust_link(eth, i);
- break;
- }
- }
- }
- spin_unlock_irqrestore(&eth->phy->lock, flags);
-}
-
-int mtk_connect_phy_node(struct mtk_eth *eth, struct mtk_mac *mac,
- struct device_node *phy_node)
-{
- const __be32 *_port = NULL;
- struct phy_device *phydev;
- int phy_mode, port;
-
- _port = of_get_property(phy_node, "reg", NULL);
-
- if (!_port || (be32_to_cpu(*_port) >= 0x20)) {
- pr_err("%pOFn: invalid port id\n", phy_node);
- return -EINVAL;
- }
- port = be32_to_cpu(*_port);
- phy_mode = of_get_phy_mode(phy_node);
- if (phy_mode < 0) {
- dev_err(eth->dev, "incorrect phy-mode %d\n", phy_mode);
- eth->phy->phy_node[port] = NULL;
- return -EINVAL;
- }
-
- phydev = of_phy_connect(eth->netdev[mac->id], phy_node,
- mtk_phy_link_adjust, 0, phy_mode);
- if (!phydev) {
- dev_err(eth->dev, "could not connect to PHY\n");
- eth->phy->phy_node[port] = NULL;
- return -ENODEV;
- }
-
- phydev->supported &= PHY_1000BT_FEATURES;
- phydev->advertising = phydev->supported;
-
- dev_info(eth->dev,
- "connected port %d to PHY at %s [uid=%08x, driver=%s]\n",
- port, phydev_name(phydev), phydev->phy_id,
- phydev->drv->name);
-
- eth->phy->phy[port] = phydev;
- eth->link[port] = 0;
-
- return 0;
-}
-
-static void phy_init(struct mtk_eth *eth, struct mtk_mac *mac,
- struct phy_device *phy)
-{
- phy_attach(eth->netdev[mac->id], phydev_name(phy),
- PHY_INTERFACE_MODE_MII);
-
- phy->autoneg = AUTONEG_ENABLE;
- phy->speed = 0;
- phy->duplex = 0;
- phy_set_max_speed(phy, SPEED_100);
- phy->advertising = phy->supported | ADVERTISED_Autoneg;
-
- phy_start_aneg(phy);
-}
-
-static int mtk_phy_connect(struct mtk_mac *mac)
-{
- struct mtk_eth *eth = mac->hw;
- int i;
-
- for (i = 0; i < 8; i++) {
- if (eth->phy->phy_node[i]) {
- if (!mac->phy_dev) {
- mac->phy_dev = eth->phy->phy[i];
- mac->phy_flags = MTK_PHY_FLAG_PORT;
- }
- } else if (eth->mii_bus) {
- struct phy_device *phy;
-
- phy = mdiobus_get_phy(eth->mii_bus, i);
- if (phy) {
- phy_init(eth, mac, phy);
- if (!mac->phy_dev) {
- mac->phy_dev = phy;
- mac->phy_flags = MTK_PHY_FLAG_ATTACH;
- }
- }
- }
- }
-
- return 0;
-}
-
-static void mtk_phy_disconnect(struct mtk_mac *mac)
-{
- struct mtk_eth *eth = mac->hw;
- unsigned long flags;
- int i;
-
- for (i = 0; i < 8; i++)
- if (eth->phy->phy_fixed[i]) {
- spin_lock_irqsave(&eth->phy->lock, flags);
- eth->link[i] = 0;
- if (eth->soc->mdio_adjust_link)
- eth->soc->mdio_adjust_link(eth, i);
- spin_unlock_irqrestore(&eth->phy->lock, flags);
- } else if (eth->phy->phy[i]) {
- phy_disconnect(eth->phy->phy[i]);
- } else if (eth->mii_bus) {
- struct phy_device *phy =
- mdiobus_get_phy(eth->mii_bus, i);
-
- if (phy)
- phy_detach(phy);
- }
-}
-
-static void mtk_phy_start(struct mtk_mac *mac)
-{
- struct mtk_eth *eth = mac->hw;
- unsigned long flags;
- int i;
-
- for (i = 0; i < 8; i++) {
- if (eth->phy->phy_fixed[i]) {
- spin_lock_irqsave(&eth->phy->lock, flags);
- eth->link[i] = 1;
- if (eth->soc->mdio_adjust_link)
- eth->soc->mdio_adjust_link(eth, i);
- spin_unlock_irqrestore(&eth->phy->lock, flags);
- } else if (eth->phy->phy[i]) {
- phy_start(eth->phy->phy[i]);
- }
- }
-}
-
-static void mtk_phy_stop(struct mtk_mac *mac)
-{
- struct mtk_eth *eth = mac->hw;
- unsigned long flags;
- int i;
-
- for (i = 0; i < 8; i++)
- if (eth->phy->phy_fixed[i]) {
- spin_lock_irqsave(&eth->phy->lock, flags);
- eth->link[i] = 0;
- if (eth->soc->mdio_adjust_link)
- eth->soc->mdio_adjust_link(eth, i);
- spin_unlock_irqrestore(&eth->phy->lock, flags);
- } else if (eth->phy->phy[i]) {
- phy_stop(eth->phy->phy[i]);
- }
-}
-
-static struct mtk_phy phy_ralink = {
- .connect = mtk_phy_connect,
- .disconnect = mtk_phy_disconnect,
- .start = mtk_phy_start,
- .stop = mtk_phy_stop,
-};
-
-int mtk_mdio_init(struct mtk_eth *eth)
-{
- struct device_node *mii_np;
- int err;
-
- if (!eth->soc->mdio_read || !eth->soc->mdio_write)
- return 0;
-
- spin_lock_init(&phy_ralink.lock);
- eth->phy = &phy_ralink;
-
- mii_np = of_get_child_by_name(eth->dev->of_node, "mdio-bus");
- if (!mii_np) {
- dev_err(eth->dev, "no %s child node found", "mdio-bus");
- return -ENODEV;
- }
-
- if (!of_device_is_available(mii_np)) {
- err = 0;
- goto err_put_node;
- }
-
- eth->mii_bus = mdiobus_alloc();
- if (!eth->mii_bus) {
- err = -ENOMEM;
- goto err_put_node;
- }
-
- eth->mii_bus->name = "mdio";
- eth->mii_bus->read = eth->soc->mdio_read;
- eth->mii_bus->write = eth->soc->mdio_write;
- eth->mii_bus->reset = mtk_mdio_reset;
- eth->mii_bus->priv = eth;
- eth->mii_bus->parent = eth->dev;
-
- snprintf(eth->mii_bus->id, MII_BUS_ID_SIZE, "%pOFn", mii_np);
- err = of_mdiobus_register(eth->mii_bus, mii_np);
- if (err)
- goto err_free_bus;
-
- return 0;
-
-err_free_bus:
- kfree(eth->mii_bus);
-err_put_node:
- of_node_put(mii_np);
- eth->mii_bus = NULL;
- return err;
-}
-
-void mtk_mdio_cleanup(struct mtk_eth *eth)
-{
- if (!eth->mii_bus)
- return;
-
- mdiobus_unregister(eth->mii_bus);
- of_node_put(eth->mii_bus->dev.of_node);
- kfree(eth->mii_bus);
-}
diff --git a/drivers/staging/mt7621-eth/mdio.h b/drivers/staging/mt7621-eth/mdio.h
deleted file mode 100644
index b14e23842a01..000000000000
--- a/drivers/staging/mt7621-eth/mdio.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * Copyright (C) 2009-2016 John Crispin <blogic@openwrt.org>
- * Copyright (C) 2009-2016 Felix Fietkau <nbd@openwrt.org>
- * Copyright (C) 2013-2016 Michael Lee <igvtee@gmail.com>
- */
-
-#ifndef _RALINK_MDIO_H__
-#define _RALINK_MDIO_H__
-
-#ifdef CONFIG_NET_MEDIATEK_MDIO
-int mtk_mdio_init(struct mtk_eth *eth);
-void mtk_mdio_cleanup(struct mtk_eth *eth);
-int mtk_connect_phy_node(struct mtk_eth *eth, struct mtk_mac *mac,
- struct device_node *phy_node);
-#else
-static inline int mtk_mdio_init(struct mtk_eth *eth) { return 0; }
-static inline void mtk_mdio_cleanup(struct mtk_eth *eth) {}
-#endif
-#endif
diff --git a/drivers/staging/mt7621-eth/mdio_mt7620.c b/drivers/staging/mt7621-eth/mdio_mt7620.c
deleted file mode 100644
index ced605c2914e..000000000000
--- a/drivers/staging/mt7621-eth/mdio_mt7620.c
+++ /dev/null
@@ -1,173 +0,0 @@
-/* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * Copyright (C) 2009-2016 John Crispin <blogic@openwrt.org>
- * Copyright (C) 2009-2016 Felix Fietkau <nbd@openwrt.org>
- * Copyright (C) 2013-2016 Michael Lee <igvtee@gmail.com>
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/types.h>
-
-#include "mtk_eth_soc.h"
-#include "gsw_mt7620.h"
-#include "mdio.h"
-
-static int mt7620_mii_busy_wait(struct mt7620_gsw *gsw)
-{
- unsigned long t_start = jiffies;
-
- while (1) {
- if (!(mtk_switch_r32(gsw,
- gsw->piac_offset + MT7620_GSW_REG_PIAC) &
- GSW_MDIO_ACCESS))
- return 0;
- if (time_after(jiffies, t_start + GSW_REG_PHY_TIMEOUT))
- break;
- }
-
- dev_err(gsw->dev, "mdio: MDIO timeout\n");
- return -1;
-}
-
-u32 _mt7620_mii_write(struct mt7620_gsw *gsw, u32 phy_addr,
- u32 phy_register, u32 write_data)
-{
- if (mt7620_mii_busy_wait(gsw))
- return -1;
-
- write_data &= 0xffff;
-
- mtk_switch_w32(gsw, GSW_MDIO_ACCESS | GSW_MDIO_START | GSW_MDIO_WRITE |
- (phy_register << GSW_MDIO_REG_SHIFT) |
- (phy_addr << GSW_MDIO_ADDR_SHIFT) | write_data,
- MT7620_GSW_REG_PIAC);
-
- if (mt7620_mii_busy_wait(gsw))
- return -1;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(_mt7620_mii_write);
-
-u32 _mt7620_mii_read(struct mt7620_gsw *gsw, int phy_addr, int phy_reg)
-{
- u32 d;
-
- if (mt7620_mii_busy_wait(gsw))
- return 0xffff;
-
- mtk_switch_w32(gsw, GSW_MDIO_ACCESS | GSW_MDIO_START | GSW_MDIO_READ |
- (phy_reg << GSW_MDIO_REG_SHIFT) |
- (phy_addr << GSW_MDIO_ADDR_SHIFT),
- MT7620_GSW_REG_PIAC);
-
- if (mt7620_mii_busy_wait(gsw))
- return 0xffff;
-
- d = mtk_switch_r32(gsw, MT7620_GSW_REG_PIAC) & 0xffff;
-
- return d;
-}
-EXPORT_SYMBOL_GPL(_mt7620_mii_read);
-
-int mt7620_mdio_write(struct mii_bus *bus, int phy_addr, int phy_reg, u16 val)
-{
- struct mtk_eth *eth = bus->priv;
- struct mt7620_gsw *gsw = (struct mt7620_gsw *)eth->sw_priv;
-
- return _mt7620_mii_write(gsw, phy_addr, phy_reg, val);
-}
-
-int mt7620_mdio_read(struct mii_bus *bus, int phy_addr, int phy_reg)
-{
- struct mtk_eth *eth = bus->priv;
- struct mt7620_gsw *gsw = (struct mt7620_gsw *)eth->sw_priv;
-
- return _mt7620_mii_read(gsw, phy_addr, phy_reg);
-}
-
-void mt7530_mdio_w32(struct mt7620_gsw *gsw, u32 reg, u32 val)
-{
- _mt7620_mii_write(gsw, 0x1f, 0x1f, (reg >> 6) & 0x3ff);
- _mt7620_mii_write(gsw, 0x1f, (reg >> 2) & 0xf, val & 0xffff);
- _mt7620_mii_write(gsw, 0x1f, 0x10, val >> 16);
-}
-EXPORT_SYMBOL_GPL(mt7530_mdio_w32);
-
-u32 mt7530_mdio_r32(struct mt7620_gsw *gsw, u32 reg)
-{
- u16 high, low;
-
- _mt7620_mii_write(gsw, 0x1f, 0x1f, (reg >> 6) & 0x3ff);
- low = _mt7620_mii_read(gsw, 0x1f, (reg >> 2) & 0xf);
- high = _mt7620_mii_read(gsw, 0x1f, 0x10);
-
- return (high << 16) | (low & 0xffff);
-}
-EXPORT_SYMBOL_GPL(mt7530_mdio_r32);
-
-void mt7530_mdio_m32(struct mt7620_gsw *gsw, u32 mask, u32 set, u32 reg)
-{
- u32 val = mt7530_mdio_r32(gsw, reg);
-
- val &= ~mask;
- val |= set;
- mt7530_mdio_w32(gsw, reg, val);
-}
-EXPORT_SYMBOL_GPL(mt7530_mdio_m32);
-
-static unsigned char *mtk_speed_str(int speed)
-{
- switch (speed) {
- case 2:
- case SPEED_1000:
- return "1000";
- case 1:
- case SPEED_100:
- return "100";
- case 0:
- case SPEED_10:
- return "10";
- }
-
- return "? ";
-}
-
-int mt7620_has_carrier(struct mtk_eth *eth)
-{
- struct mt7620_gsw *gsw = (struct mt7620_gsw *)eth->sw_priv;
- int i;
-
- for (i = 0; i < GSW_PORT6; i++)
- if (mt7530_mdio_r32(gsw, GSW_REG_PORT_STATUS(i)) & 0x1)
- return 1;
- return 0;
-}
-
-void mt7620_print_link_state(struct mtk_eth *eth, int port, int link,
- int speed, int duplex)
-{
- struct mt7620_gsw *gsw = eth->sw_priv;
-
- if (link)
- dev_info(gsw->dev, "port %d link up (%sMbps/%s duplex)\n",
- port, mtk_speed_str(speed),
- (duplex) ? "Full" : "Half");
- else
- dev_info(gsw->dev, "port %d link down\n", port);
-}
-
-void mt7620_mdio_link_adjust(struct mtk_eth *eth, int port)
-{
- mt7620_print_link_state(eth, port, eth->link[port],
- eth->phy->speed[port],
- (eth->phy->duplex[port] == DUPLEX_FULL));
-}
diff --git a/drivers/staging/mt7621-eth/mtk_eth_soc.c b/drivers/staging/mt7621-eth/mtk_eth_soc.c
deleted file mode 100644
index 6027b19f7bc2..000000000000
--- a/drivers/staging/mt7621-eth/mtk_eth_soc.c
+++ /dev/null
@@ -1,2176 +0,0 @@
-/* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * Copyright (C) 2009-2016 John Crispin <blogic@openwrt.org>
- * Copyright (C) 2009-2016 Felix Fietkau <nbd@openwrt.org>
- * Copyright (C) 2013-2016 Michael Lee <igvtee@gmail.com>
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/dma-mapping.h>
-#include <linux/init.h>
-#include <linux/skbuff.h>
-#include <linux/etherdevice.h>
-#include <linux/ethtool.h>
-#include <linux/platform_device.h>
-#include <linux/of_device.h>
-#include <linux/mfd/syscon.h>
-#include <linux/clk.h>
-#include <linux/of_net.h>
-#include <linux/of_mdio.h>
-#include <linux/if_vlan.h>
-#include <linux/reset.h>
-#include <linux/tcp.h>
-#include <linux/io.h>
-#include <linux/bug.h>
-#include <linux/regmap.h>
-
-#include "mtk_eth_soc.h"
-#include "mdio.h"
-#include "ethtool.h"
-
-#define MAX_RX_LENGTH 1536
-#define MTK_RX_ETH_HLEN (VLAN_ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN)
-#define MTK_RX_HLEN (NET_SKB_PAD + MTK_RX_ETH_HLEN + NET_IP_ALIGN)
-#define DMA_DUMMY_DESC 0xffffffff
-#define MTK_DEFAULT_MSG_ENABLE \
- (NETIF_MSG_DRV | \
- NETIF_MSG_PROBE | \
- NETIF_MSG_LINK | \
- NETIF_MSG_TIMER | \
- NETIF_MSG_IFDOWN | \
- NETIF_MSG_IFUP | \
- NETIF_MSG_RX_ERR | \
- NETIF_MSG_TX_ERR)
-
-#define TX_DMA_DESP2_DEF (TX_DMA_LS0 | TX_DMA_DONE)
-#define NEXT_TX_DESP_IDX(X) (((X) + 1) & (ring->tx_ring_size - 1))
-#define NEXT_RX_DESP_IDX(X) (((X) + 1) & (ring->rx_ring_size - 1))
-
-#define SYSC_REG_RSTCTRL 0x34
-
-static int mtk_msg_level = -1;
-module_param_named(msg_level, mtk_msg_level, int, 0);
-MODULE_PARM_DESC(msg_level, "Message level (-1=defaults,0=none,...,16=all)");
-
-static const u16 mtk_reg_table_default[MTK_REG_COUNT] = {
- [MTK_REG_PDMA_GLO_CFG] = MTK_PDMA_GLO_CFG,
- [MTK_REG_PDMA_RST_CFG] = MTK_PDMA_RST_CFG,
- [MTK_REG_DLY_INT_CFG] = MTK_DLY_INT_CFG,
- [MTK_REG_TX_BASE_PTR0] = MTK_TX_BASE_PTR0,
- [MTK_REG_TX_MAX_CNT0] = MTK_TX_MAX_CNT0,
- [MTK_REG_TX_CTX_IDX0] = MTK_TX_CTX_IDX0,
- [MTK_REG_TX_DTX_IDX0] = MTK_TX_DTX_IDX0,
- [MTK_REG_RX_BASE_PTR0] = MTK_RX_BASE_PTR0,
- [MTK_REG_RX_MAX_CNT0] = MTK_RX_MAX_CNT0,
- [MTK_REG_RX_CALC_IDX0] = MTK_RX_CALC_IDX0,
- [MTK_REG_RX_DRX_IDX0] = MTK_RX_DRX_IDX0,
- [MTK_REG_MTK_INT_ENABLE] = MTK_INT_ENABLE,
- [MTK_REG_MTK_INT_STATUS] = MTK_INT_STATUS,
- [MTK_REG_MTK_DMA_VID_BASE] = MTK_DMA_VID0,
- [MTK_REG_MTK_COUNTER_BASE] = MTK_GDMA1_TX_GBCNT,
- [MTK_REG_MTK_RST_GL] = MTK_RST_GL,
-};
-
-static const u16 *mtk_reg_table = mtk_reg_table_default;
-
-void mtk_w32(struct mtk_eth *eth, u32 val, unsigned int reg)
-{
- __raw_writel(val, eth->base + reg);
-}
-
-u32 mtk_r32(struct mtk_eth *eth, unsigned int reg)
-{
- return __raw_readl(eth->base + reg);
-}
-
-static void mtk_reg_w32(struct mtk_eth *eth, u32 val, enum mtk_reg reg)
-{
- mtk_w32(eth, val, mtk_reg_table[reg]);
-}
-
-static u32 mtk_reg_r32(struct mtk_eth *eth, enum mtk_reg reg)
-{
- return mtk_r32(eth, mtk_reg_table[reg]);
-}
-
-/* these bits are also exposed via the reset-controller API. however the switch
- * and FE need to be brought out of reset in the exakt same moemtn and the
- * reset-controller api does not provide this feature yet. Do the reset manually
- * until we fixed the reset-controller api to be able to do this
- */
-void mtk_reset(struct mtk_eth *eth, u32 reset_bits)
-{
- u32 val;
-
- regmap_read(eth->ethsys, SYSC_REG_RSTCTRL, &val);
- val |= reset_bits;
- regmap_write(eth->ethsys, SYSC_REG_RSTCTRL, val);
- usleep_range(10, 20);
- val &= ~reset_bits;
- regmap_write(eth->ethsys, SYSC_REG_RSTCTRL, val);
- usleep_range(10, 20);
-}
-EXPORT_SYMBOL(mtk_reset);
-
-static inline void mtk_irq_ack(struct mtk_eth *eth, u32 mask)
-{
- if (eth->soc->dma_type & MTK_PDMA)
- mtk_reg_w32(eth, mask, MTK_REG_MTK_INT_STATUS);
- if (eth->soc->dma_type & MTK_QDMA)
- mtk_w32(eth, mask, MTK_QMTK_INT_STATUS);
-}
-
-static inline u32 mtk_irq_pending(struct mtk_eth *eth)
-{
- u32 status = 0;
-
- if (eth->soc->dma_type & MTK_PDMA)
- status |= mtk_reg_r32(eth, MTK_REG_MTK_INT_STATUS);
- if (eth->soc->dma_type & MTK_QDMA)
- status |= mtk_r32(eth, MTK_QMTK_INT_STATUS);
-
- return status;
-}
-
-static void mtk_irq_ack_status(struct mtk_eth *eth, u32 mask)
-{
- u32 status_reg = MTK_REG_MTK_INT_STATUS;
-
- if (mtk_reg_table[MTK_REG_MTK_INT_STATUS2])
- status_reg = MTK_REG_MTK_INT_STATUS2;
-
- mtk_reg_w32(eth, mask, status_reg);
-}
-
-static u32 mtk_irq_pending_status(struct mtk_eth *eth)
-{
- u32 status_reg = MTK_REG_MTK_INT_STATUS;
-
- if (mtk_reg_table[MTK_REG_MTK_INT_STATUS2])
- status_reg = MTK_REG_MTK_INT_STATUS2;
-
- return mtk_reg_r32(eth, status_reg);
-}
-
-static inline void mtk_irq_disable(struct mtk_eth *eth, u32 mask)
-{
- u32 val;
-
- if (eth->soc->dma_type & MTK_PDMA) {
- val = mtk_reg_r32(eth, MTK_REG_MTK_INT_ENABLE);
- mtk_reg_w32(eth, val & ~mask, MTK_REG_MTK_INT_ENABLE);
- /* flush write */
- mtk_reg_r32(eth, MTK_REG_MTK_INT_ENABLE);
- }
- if (eth->soc->dma_type & MTK_QDMA) {
- val = mtk_r32(eth, MTK_QMTK_INT_ENABLE);
- mtk_w32(eth, val & ~mask, MTK_QMTK_INT_ENABLE);
- /* flush write */
- mtk_r32(eth, MTK_QMTK_INT_ENABLE);
- }
-}
-
-static inline void mtk_irq_enable(struct mtk_eth *eth, u32 mask)
-{
- u32 val;
-
- if (eth->soc->dma_type & MTK_PDMA) {
- val = mtk_reg_r32(eth, MTK_REG_MTK_INT_ENABLE);
- mtk_reg_w32(eth, val | mask, MTK_REG_MTK_INT_ENABLE);
- /* flush write */
- mtk_reg_r32(eth, MTK_REG_MTK_INT_ENABLE);
- }
- if (eth->soc->dma_type & MTK_QDMA) {
- val = mtk_r32(eth, MTK_QMTK_INT_ENABLE);
- mtk_w32(eth, val | mask, MTK_QMTK_INT_ENABLE);
- /* flush write */
- mtk_r32(eth, MTK_QMTK_INT_ENABLE);
- }
-}
-
-static inline u32 mtk_irq_enabled(struct mtk_eth *eth)
-{
- u32 enabled = 0;
-
- if (eth->soc->dma_type & MTK_PDMA)
- enabled |= mtk_reg_r32(eth, MTK_REG_MTK_INT_ENABLE);
- if (eth->soc->dma_type & MTK_QDMA)
- enabled |= mtk_r32(eth, MTK_QMTK_INT_ENABLE);
-
- return enabled;
-}
-
-static inline void mtk_hw_set_macaddr(struct mtk_mac *mac,
- unsigned char *macaddr)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&mac->hw->page_lock, flags);
- mtk_w32(mac->hw, (macaddr[0] << 8) | macaddr[1], MTK_GDMA1_MAC_ADRH);
- mtk_w32(mac->hw, (macaddr[2] << 24) | (macaddr[3] << 16) |
- (macaddr[4] << 8) | macaddr[5],
- MTK_GDMA1_MAC_ADRL);
- spin_unlock_irqrestore(&mac->hw->page_lock, flags);
-}
-
-static int mtk_set_mac_address(struct net_device *dev, void *p)
-{
- int ret = eth_mac_addr(dev, p);
- struct mtk_mac *mac = netdev_priv(dev);
- struct mtk_eth *eth = mac->hw;
-
- if (ret)
- return ret;
-
- if (eth->soc->set_mac)
- eth->soc->set_mac(mac, dev->dev_addr);
- else
- mtk_hw_set_macaddr(mac, p);
-
- return 0;
-}
-
-static inline int mtk_max_frag_size(int mtu)
-{
- /* make sure buf_size will be at least MAX_RX_LENGTH */
- if (mtu + MTK_RX_ETH_HLEN < MAX_RX_LENGTH)
- mtu = MAX_RX_LENGTH - MTK_RX_ETH_HLEN;
-
- return SKB_DATA_ALIGN(MTK_RX_HLEN + mtu) +
- SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
-}
-
-static inline int mtk_max_buf_size(int frag_size)
-{
- int buf_size = frag_size - NET_SKB_PAD - NET_IP_ALIGN -
- SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
-
- WARN_ON(buf_size < MAX_RX_LENGTH);
-
- return buf_size;
-}
-
-static inline void mtk_get_rxd(struct mtk_rx_dma *rxd,
- struct mtk_rx_dma *dma_rxd)
-{
- rxd->rxd1 = READ_ONCE(dma_rxd->rxd1);
- rxd->rxd2 = READ_ONCE(dma_rxd->rxd2);
- rxd->rxd3 = READ_ONCE(dma_rxd->rxd3);
- rxd->rxd4 = READ_ONCE(dma_rxd->rxd4);
-}
-
-static inline void mtk_set_txd_pdma(struct mtk_tx_dma *txd,
- struct mtk_tx_dma *dma_txd)
-{
- WRITE_ONCE(dma_txd->txd1, txd->txd1);
- WRITE_ONCE(dma_txd->txd3, txd->txd3);
- WRITE_ONCE(dma_txd->txd4, txd->txd4);
- /* clean dma done flag last */
- WRITE_ONCE(dma_txd->txd2, txd->txd2);
-}
-
-static void mtk_clean_rx(struct mtk_eth *eth, struct mtk_rx_ring *ring)
-{
- int i;
-
- if (ring->rx_data && ring->rx_dma) {
- for (i = 0; i < ring->rx_ring_size; i++) {
- if (!ring->rx_data[i])
- continue;
- if (!ring->rx_dma[i].rxd1)
- continue;
- dma_unmap_single(eth->dev,
- ring->rx_dma[i].rxd1,
- ring->rx_buf_size,
- DMA_FROM_DEVICE);
- skb_free_frag(ring->rx_data[i]);
- }
- kfree(ring->rx_data);
- ring->rx_data = NULL;
- }
-
- if (ring->rx_dma) {
- dma_free_coherent(eth->dev,
- ring->rx_ring_size * sizeof(*ring->rx_dma),
- ring->rx_dma,
- ring->rx_phys);
- ring->rx_dma = NULL;
- }
-}
-
-static int mtk_dma_rx_alloc(struct mtk_eth *eth, struct mtk_rx_ring *ring)
-{
- int i, pad = 0;
-
- ring->frag_size = mtk_max_frag_size(ETH_DATA_LEN);
- ring->rx_buf_size = mtk_max_buf_size(ring->frag_size);
- ring->rx_ring_size = eth->soc->dma_ring_size;
- ring->rx_data = kcalloc(ring->rx_ring_size, sizeof(*ring->rx_data),
- GFP_KERNEL);
- if (!ring->rx_data)
- goto no_rx_mem;
-
- for (i = 0; i < ring->rx_ring_size; i++) {
- ring->rx_data[i] = netdev_alloc_frag(ring->frag_size);
- if (!ring->rx_data[i])
- goto no_rx_mem;
- }
-
- ring->rx_dma =
- dma_alloc_coherent(eth->dev,
- ring->rx_ring_size * sizeof(*ring->rx_dma),
- &ring->rx_phys, GFP_ATOMIC | __GFP_ZERO);
- if (!ring->rx_dma)
- goto no_rx_mem;
-
- if (!eth->soc->rx_2b_offset)
- pad = NET_IP_ALIGN;
-
- for (i = 0; i < ring->rx_ring_size; i++) {
- dma_addr_t dma_addr = dma_map_single(eth->dev,
- ring->rx_data[i] + NET_SKB_PAD + pad,
- ring->rx_buf_size,
- DMA_FROM_DEVICE);
- if (unlikely(dma_mapping_error(eth->dev, dma_addr)))
- goto no_rx_mem;
- ring->rx_dma[i].rxd1 = (unsigned int)dma_addr;
-
- if (eth->soc->rx_sg_dma)
- ring->rx_dma[i].rxd2 = RX_DMA_PLEN0(ring->rx_buf_size);
- else
- ring->rx_dma[i].rxd2 = RX_DMA_LSO;
- }
- ring->rx_calc_idx = ring->rx_ring_size - 1;
- /* make sure that all changes to the dma ring are flushed before we
- * continue
- */
- wmb();
-
- return 0;
-
-no_rx_mem:
- return -ENOMEM;
-}
-
-static void mtk_txd_unmap(struct device *dev, struct mtk_tx_buf *tx_buf)
-{
- if (tx_buf->flags & MTK_TX_FLAGS_SINGLE0) {
- dma_unmap_single(dev,
- dma_unmap_addr(tx_buf, dma_addr0),
- dma_unmap_len(tx_buf, dma_len0),
- DMA_TO_DEVICE);
- } else if (tx_buf->flags & MTK_TX_FLAGS_PAGE0) {
- dma_unmap_page(dev,
- dma_unmap_addr(tx_buf, dma_addr0),
- dma_unmap_len(tx_buf, dma_len0),
- DMA_TO_DEVICE);
- }
- if (tx_buf->flags & MTK_TX_FLAGS_PAGE1)
- dma_unmap_page(dev,
- dma_unmap_addr(tx_buf, dma_addr1),
- dma_unmap_len(tx_buf, dma_len1),
- DMA_TO_DEVICE);
-
- tx_buf->flags = 0;
- if (tx_buf->skb && (tx_buf->skb != (struct sk_buff *)DMA_DUMMY_DESC))
- dev_kfree_skb_any(tx_buf->skb);
- tx_buf->skb = NULL;
-}
-
-static void mtk_pdma_tx_clean(struct mtk_eth *eth)
-{
- struct mtk_tx_ring *ring = &eth->tx_ring;
- int i;
-
- if (ring->tx_buf) {
- for (i = 0; i < ring->tx_ring_size; i++)
- mtk_txd_unmap(eth->dev, &ring->tx_buf[i]);
- kfree(ring->tx_buf);
- ring->tx_buf = NULL;
- }
-
- if (ring->tx_dma) {
- dma_free_coherent(eth->dev,
- ring->tx_ring_size * sizeof(*ring->tx_dma),
- ring->tx_dma,
- ring->tx_phys);
- ring->tx_dma = NULL;
- }
-}
-
-static void mtk_qdma_tx_clean(struct mtk_eth *eth)
-{
- struct mtk_tx_ring *ring = &eth->tx_ring;
- int i;
-
- if (ring->tx_buf) {
- for (i = 0; i < ring->tx_ring_size; i++)
- mtk_txd_unmap(eth->dev, &ring->tx_buf[i]);
- kfree(ring->tx_buf);
- ring->tx_buf = NULL;
- }
-
- if (ring->tx_dma) {
- dma_free_coherent(eth->dev,
- ring->tx_ring_size * sizeof(*ring->tx_dma),
- ring->tx_dma,
- ring->tx_phys);
- ring->tx_dma = NULL;
- }
-}
-
-void mtk_stats_update_mac(struct mtk_mac *mac)
-{
- struct mtk_hw_stats *hw_stats = mac->hw_stats;
- unsigned int base = mtk_reg_table[MTK_REG_MTK_COUNTER_BASE];
- u64 stats;
-
- base += hw_stats->reg_offset;
-
- u64_stats_update_begin(&hw_stats->syncp);
-
- if (mac->hw->soc->new_stats) {
- hw_stats->rx_bytes += mtk_r32(mac->hw, base);
- stats = mtk_r32(mac->hw, base + 0x04);
- if (stats)
- hw_stats->rx_bytes += (stats << 32);
- hw_stats->rx_packets += mtk_r32(mac->hw, base + 0x08);
- hw_stats->rx_overflow += mtk_r32(mac->hw, base + 0x10);
- hw_stats->rx_fcs_errors += mtk_r32(mac->hw, base + 0x14);
- hw_stats->rx_short_errors += mtk_r32(mac->hw, base + 0x18);
- hw_stats->rx_long_errors += mtk_r32(mac->hw, base + 0x1c);
- hw_stats->rx_checksum_errors += mtk_r32(mac->hw, base + 0x20);
- hw_stats->rx_flow_control_packets +=
- mtk_r32(mac->hw, base + 0x24);
- hw_stats->tx_skip += mtk_r32(mac->hw, base + 0x28);
- hw_stats->tx_collisions += mtk_r32(mac->hw, base + 0x2c);
- hw_stats->tx_bytes += mtk_r32(mac->hw, base + 0x30);
- stats = mtk_r32(mac->hw, base + 0x34);
- if (stats)
- hw_stats->tx_bytes += (stats << 32);
- hw_stats->tx_packets += mtk_r32(mac->hw, base + 0x38);
- } else {
- hw_stats->tx_bytes += mtk_r32(mac->hw, base);
- hw_stats->tx_packets += mtk_r32(mac->hw, base + 0x04);
- hw_stats->tx_skip += mtk_r32(mac->hw, base + 0x08);
- hw_stats->tx_collisions += mtk_r32(mac->hw, base + 0x0c);
- hw_stats->rx_bytes += mtk_r32(mac->hw, base + 0x20);
- hw_stats->rx_packets += mtk_r32(mac->hw, base + 0x24);
- hw_stats->rx_overflow += mtk_r32(mac->hw, base + 0x28);
- hw_stats->rx_fcs_errors += mtk_r32(mac->hw, base + 0x2c);
- hw_stats->rx_short_errors += mtk_r32(mac->hw, base + 0x30);
- hw_stats->rx_long_errors += mtk_r32(mac->hw, base + 0x34);
- hw_stats->rx_checksum_errors += mtk_r32(mac->hw, base + 0x38);
- hw_stats->rx_flow_control_packets +=
- mtk_r32(mac->hw, base + 0x3c);
- }
-
- u64_stats_update_end(&hw_stats->syncp);
-}
-
-static void mtk_get_stats64(struct net_device *dev,
- struct rtnl_link_stats64 *storage)
-{
- struct mtk_mac *mac = netdev_priv(dev);
- struct mtk_hw_stats *hw_stats = mac->hw_stats;
- unsigned int base = mtk_reg_table[MTK_REG_MTK_COUNTER_BASE];
- unsigned int start;
-
- if (!base) {
- netdev_stats_to_stats64(storage, &dev->stats);
- return;
- }
-
- if (netif_running(dev) && netif_device_present(dev)) {
- if (spin_trylock(&hw_stats->stats_lock)) {
- mtk_stats_update_mac(mac);
- spin_unlock(&hw_stats->stats_lock);
- }
- }
-
- do {
- start = u64_stats_fetch_begin_irq(&hw_stats->syncp);
- storage->rx_packets = hw_stats->rx_packets;
- storage->tx_packets = hw_stats->tx_packets;
- storage->rx_bytes = hw_stats->rx_bytes;
- storage->tx_bytes = hw_stats->tx_bytes;
- storage->collisions = hw_stats->tx_collisions;
- storage->rx_length_errors = hw_stats->rx_short_errors +
- hw_stats->rx_long_errors;
- storage->rx_over_errors = hw_stats->rx_overflow;
- storage->rx_crc_errors = hw_stats->rx_fcs_errors;
- storage->rx_errors = hw_stats->rx_checksum_errors;
- storage->tx_aborted_errors = hw_stats->tx_skip;
- } while (u64_stats_fetch_retry_irq(&hw_stats->syncp, start));
-
- storage->tx_errors = dev->stats.tx_errors;
- storage->rx_dropped = dev->stats.rx_dropped;
- storage->tx_dropped = dev->stats.tx_dropped;
-}
-
-static int mtk_vlan_rx_add_vid(struct net_device *dev,
- __be16 proto, u16 vid)
-{
- struct mtk_mac *mac = netdev_priv(dev);
- struct mtk_eth *eth = mac->hw;
- u32 idx = (vid & 0xf);
- u32 vlan_cfg;
-
- if (!((mtk_reg_table[MTK_REG_MTK_DMA_VID_BASE]) &&
- (dev->features & NETIF_F_HW_VLAN_CTAG_TX)))
- return 0;
-
- if (test_bit(idx, &eth->vlan_map)) {
- netdev_warn(dev, "disable tx vlan offload\n");
- dev->wanted_features &= ~NETIF_F_HW_VLAN_CTAG_TX;
- netdev_update_features(dev);
- } else {
- vlan_cfg = mtk_r32(eth,
- mtk_reg_table[MTK_REG_MTK_DMA_VID_BASE] +
- ((idx >> 1) << 2));
- if (idx & 0x1) {
- vlan_cfg &= 0xffff;
- vlan_cfg |= (vid << 16);
- } else {
- vlan_cfg &= 0xffff0000;
- vlan_cfg |= vid;
- }
- mtk_w32(eth,
- vlan_cfg, mtk_reg_table[MTK_REG_MTK_DMA_VID_BASE] +
- ((idx >> 1) << 2));
- set_bit(idx, &eth->vlan_map);
- }
-
- return 0;
-}
-
-static int mtk_vlan_rx_kill_vid(struct net_device *dev,
- __be16 proto, u16 vid)
-{
- struct mtk_mac *mac = netdev_priv(dev);
- struct mtk_eth *eth = mac->hw;
- u32 idx = (vid & 0xf);
-
- if (!((mtk_reg_table[MTK_REG_MTK_DMA_VID_BASE]) &&
- (dev->features & NETIF_F_HW_VLAN_CTAG_TX)))
- return 0;
-
- clear_bit(idx, &eth->vlan_map);
-
- return 0;
-}
-
-static inline u32 mtk_pdma_empty_txd(struct mtk_tx_ring *ring)
-{
- barrier();
- return (u32)(ring->tx_ring_size -
- ((ring->tx_next_idx - ring->tx_free_idx) &
- (ring->tx_ring_size - 1)));
-}
-
-static int mtk_skb_padto(struct sk_buff *skb, struct mtk_eth *eth)
-{
- unsigned int len;
- int ret;
-
- if (unlikely(skb->len >= VLAN_ETH_ZLEN))
- return 0;
-
- if (eth->soc->padding_64b && !eth->soc->padding_bug)
- return 0;
-
- if (skb_vlan_tag_present(skb))
- len = ETH_ZLEN;
- else if (skb->protocol == cpu_to_be16(ETH_P_8021Q))
- len = VLAN_ETH_ZLEN;
- else if (!eth->soc->padding_64b)
- len = ETH_ZLEN;
- else
- return 0;
-
- if (skb->len >= len)
- return 0;
-
- ret = skb_pad(skb, len - skb->len);
- if (ret < 0)
- return ret;
- skb->len = len;
- skb_set_tail_pointer(skb, len);
-
- return ret;
-}
-
-static int mtk_pdma_tx_map(struct sk_buff *skb, struct net_device *dev,
- int tx_num, struct mtk_tx_ring *ring, bool gso)
-{
- struct mtk_mac *mac = netdev_priv(dev);
- struct mtk_eth *eth = mac->hw;
- struct skb_frag_struct *frag;
- struct mtk_tx_dma txd, *ptxd;
- struct mtk_tx_buf *tx_buf;
- int i, j, k, frag_size, frag_map_size, offset;
- dma_addr_t mapped_addr;
- unsigned int nr_frags;
- u32 def_txd4;
-
- if (mtk_skb_padto(skb, eth)) {
- netif_warn(eth, tx_err, dev, "tx padding failed!\n");
- return -1;
- }
-
- tx_buf = &ring->tx_buf[ring->tx_next_idx];
- memset(tx_buf, 0, sizeof(*tx_buf));
- memset(&txd, 0, sizeof(txd));
- nr_frags = skb_shinfo(skb)->nr_frags;
-
- /* init tx descriptor */
- def_txd4 = eth->soc->txd4;
- txd.txd4 = def_txd4;
-
- if (eth->soc->mac_count > 1)
- txd.txd4 |= (mac->id + 1) << TX_DMA_FPORT_SHIFT;
-
- if (gso)
- txd.txd4 |= TX_DMA_TSO;
-
- /* TX Checksum offload */
- if (skb->ip_summed == CHECKSUM_PARTIAL)
- txd.txd4 |= TX_DMA_CHKSUM;
-
- /* VLAN header offload */
- if (skb_vlan_tag_present(skb)) {
- u16 tag = skb_vlan_tag_get(skb);
-
- txd.txd4 |= TX_DMA_INS_VLAN |
- ((tag >> VLAN_PRIO_SHIFT) << 4) |
- (tag & 0xF);
- }
-
- mapped_addr = dma_map_single(&dev->dev, skb->data,
- skb_headlen(skb), DMA_TO_DEVICE);
- if (unlikely(dma_mapping_error(&dev->dev, mapped_addr)))
- return -1;
-
- txd.txd1 = mapped_addr;
- txd.txd2 = TX_DMA_PLEN0(skb_headlen(skb));
-
- tx_buf->flags |= MTK_TX_FLAGS_SINGLE0;
- dma_unmap_addr_set(tx_buf, dma_addr0, mapped_addr);
- dma_unmap_len_set(tx_buf, dma_len0, skb_headlen(skb));
-
- /* TX SG offload */
- j = ring->tx_next_idx;
- k = 0;
- for (i = 0; i < nr_frags; i++) {
- offset = 0;
- frag = &skb_shinfo(skb)->frags[i];
- frag_size = skb_frag_size(frag);
-
- while (frag_size > 0) {
- frag_map_size = min(frag_size, TX_DMA_BUF_LEN);
- mapped_addr = skb_frag_dma_map(&dev->dev, frag, offset,
- frag_map_size,
- DMA_TO_DEVICE);
- if (unlikely(dma_mapping_error(&dev->dev, mapped_addr)))
- goto err_dma;
-
- if (k & 0x1) {
- j = NEXT_TX_DESP_IDX(j);
- txd.txd1 = mapped_addr;
- txd.txd2 = TX_DMA_PLEN0(frag_map_size);
- txd.txd4 = def_txd4;
-
- tx_buf = &ring->tx_buf[j];
- memset(tx_buf, 0, sizeof(*tx_buf));
-
- tx_buf->flags |= MTK_TX_FLAGS_PAGE0;
- dma_unmap_addr_set(tx_buf, dma_addr0,
- mapped_addr);
- dma_unmap_len_set(tx_buf, dma_len0,
- frag_map_size);
- } else {
- txd.txd3 = mapped_addr;
- txd.txd2 |= TX_DMA_PLEN1(frag_map_size);
-
- tx_buf->skb = (struct sk_buff *)DMA_DUMMY_DESC;
- tx_buf->flags |= MTK_TX_FLAGS_PAGE1;
- dma_unmap_addr_set(tx_buf, dma_addr1,
- mapped_addr);
- dma_unmap_len_set(tx_buf, dma_len1,
- frag_map_size);
-
- if (!((i == (nr_frags - 1)) &&
- (frag_map_size == frag_size))) {
- mtk_set_txd_pdma(&txd,
- &ring->tx_dma[j]);
- memset(&txd, 0, sizeof(txd));
- }
- }
- frag_size -= frag_map_size;
- offset += frag_map_size;
- k++;
- }
- }
-
- /* set last segment */
- if (k & 0x1)
- txd.txd2 |= TX_DMA_LS1;
- else
- txd.txd2 |= TX_DMA_LS0;
- mtk_set_txd_pdma(&txd, &ring->tx_dma[j]);
-
- /* store skb to cleanup */
- tx_buf->skb = skb;
-
- netdev_sent_queue(dev, skb->len);
- skb_tx_timestamp(skb);
-
- ring->tx_next_idx = NEXT_TX_DESP_IDX(j);
- /* make sure that all changes to the dma ring are flushed before we
- * continue
- */
- wmb();
- atomic_set(&ring->tx_free_count, mtk_pdma_empty_txd(ring));
-
- if (netif_xmit_stopped(netdev_get_tx_queue(dev, 0)) || !skb->xmit_more)
- mtk_reg_w32(eth, ring->tx_next_idx, MTK_REG_TX_CTX_IDX0);
-
- return 0;
-
-err_dma:
- j = ring->tx_next_idx;
- for (i = 0; i < tx_num; i++) {
- ptxd = &ring->tx_dma[j];
- tx_buf = &ring->tx_buf[j];
-
- /* unmap dma */
- mtk_txd_unmap(&dev->dev, tx_buf);
-
- ptxd->txd2 = TX_DMA_DESP2_DEF;
- j = NEXT_TX_DESP_IDX(j);
- }
- /* make sure that all changes to the dma ring are flushed before we
- * continue
- */
- wmb();
- return -1;
-}
-
-/* the qdma core needs scratch memory to be setup */
-static int mtk_init_fq_dma(struct mtk_eth *eth)
-{
- dma_addr_t dma_addr, phy_ring_head, phy_ring_tail;
- int cnt = eth->soc->dma_ring_size;
- int i;
-
- eth->scratch_ring = dma_alloc_coherent(eth->dev,
- cnt * sizeof(struct mtk_tx_dma),
- &phy_ring_head,
- GFP_ATOMIC | __GFP_ZERO);
- if (unlikely(!eth->scratch_ring))
- return -ENOMEM;
-
- eth->scratch_head = kcalloc(cnt, QDMA_PAGE_SIZE,
- GFP_KERNEL);
- dma_addr = dma_map_single(eth->dev,
- eth->scratch_head, cnt * QDMA_PAGE_SIZE,
- DMA_FROM_DEVICE);
- if (unlikely(dma_mapping_error(eth->dev, dma_addr)))
- return -ENOMEM;
-
- memset(eth->scratch_ring, 0x0, sizeof(struct mtk_tx_dma) * cnt);
- phy_ring_tail = phy_ring_head + (sizeof(struct mtk_tx_dma) * (cnt - 1));
-
- for (i = 0; i < cnt; i++) {
- eth->scratch_ring[i].txd1 = (dma_addr + (i * QDMA_PAGE_SIZE));
- if (i < cnt - 1)
- eth->scratch_ring[i].txd2 = (phy_ring_head +
- ((i + 1) * sizeof(struct mtk_tx_dma)));
- eth->scratch_ring[i].txd3 = TX_QDMA_SDL(QDMA_PAGE_SIZE);
- }
-
- mtk_w32(eth, phy_ring_head, MTK_QDMA_FQ_HEAD);
- mtk_w32(eth, phy_ring_tail, MTK_QDMA_FQ_TAIL);
- mtk_w32(eth, (cnt << 16) | cnt, MTK_QDMA_FQ_CNT);
- mtk_w32(eth, QDMA_PAGE_SIZE << 16, MTK_QDMA_FQ_BLEN);
-
- return 0;
-}
-
-static void *mtk_qdma_phys_to_virt(struct mtk_tx_ring *ring, u32 desc)
-{
- void *ret = ring->tx_dma;
-
- return ret + (desc - ring->tx_phys);
-}
-
-static struct mtk_tx_dma *mtk_tx_next_qdma(struct mtk_tx_ring *ring,
- struct mtk_tx_dma *txd)
-{
- return mtk_qdma_phys_to_virt(ring, txd->txd2);
-}
-
-static struct mtk_tx_buf *mtk_desc_to_tx_buf(struct mtk_tx_ring *ring,
- struct mtk_tx_dma *txd)
-{
- int idx = txd - ring->tx_dma;
-
- return &ring->tx_buf[idx];
-}
-
-static int mtk_qdma_tx_map(struct sk_buff *skb, struct net_device *dev,
- int tx_num, struct mtk_tx_ring *ring, bool gso)
-{
- struct mtk_mac *mac = netdev_priv(dev);
- struct mtk_eth *eth = mac->hw;
- struct mtk_tx_dma *itxd, *txd;
- struct mtk_tx_buf *tx_buf;
- dma_addr_t mapped_addr;
- unsigned int nr_frags;
- int i, n_desc = 1;
- u32 txd4 = eth->soc->txd4;
-
- itxd = ring->tx_next_free;
- if (itxd == ring->tx_last_free)
- return -ENOMEM;
-
- if (eth->soc->mac_count > 1)
- txd4 |= (mac->id + 1) << TX_DMA_FPORT_SHIFT;
-
- tx_buf = mtk_desc_to_tx_buf(ring, itxd);
- memset(tx_buf, 0, sizeof(*tx_buf));
-
- if (gso)
- txd4 |= TX_DMA_TSO;
-
- /* TX Checksum offload */
- if (skb->ip_summed == CHECKSUM_PARTIAL)
- txd4 |= TX_DMA_CHKSUM;
-
- /* VLAN header offload */
- if (skb_vlan_tag_present(skb))
- txd4 |= TX_DMA_INS_VLAN_MT7621 | skb_vlan_tag_get(skb);
-
- mapped_addr = dma_map_single(&dev->dev, skb->data,
- skb_headlen(skb), DMA_TO_DEVICE);
- if (unlikely(dma_mapping_error(&dev->dev, mapped_addr)))
- return -ENOMEM;
-
- WRITE_ONCE(itxd->txd1, mapped_addr);
- tx_buf->flags |= MTK_TX_FLAGS_SINGLE0;
- dma_unmap_addr_set(tx_buf, dma_addr0, mapped_addr);
- dma_unmap_len_set(tx_buf, dma_len0, skb_headlen(skb));
-
- /* TX SG offload */
- txd = itxd;
- nr_frags = skb_shinfo(skb)->nr_frags;
- for (i = 0; i < nr_frags; i++) {
- struct skb_frag_struct *frag = &skb_shinfo(skb)->frags[i];
- unsigned int offset = 0;
- int frag_size = skb_frag_size(frag);
-
- while (frag_size) {
- bool last_frag = false;
- unsigned int frag_map_size;
-
- txd = mtk_tx_next_qdma(ring, txd);
- if (txd == ring->tx_last_free)
- goto err_dma;
-
- n_desc++;
- frag_map_size = min(frag_size, TX_DMA_BUF_LEN);
- mapped_addr = skb_frag_dma_map(&dev->dev, frag, offset,
- frag_map_size,
- DMA_TO_DEVICE);
- if (unlikely(dma_mapping_error(&dev->dev, mapped_addr)))
- goto err_dma;
-
- if (i == nr_frags - 1 &&
- (frag_size - frag_map_size) == 0)
- last_frag = true;
-
- WRITE_ONCE(txd->txd1, mapped_addr);
- WRITE_ONCE(txd->txd3, (QDMA_TX_SWC |
- TX_DMA_PLEN0(frag_map_size) |
- last_frag * TX_DMA_LS0) |
- mac->id);
- WRITE_ONCE(txd->txd4, 0);
-
- tx_buf->skb = (struct sk_buff *)DMA_DUMMY_DESC;
- tx_buf = mtk_desc_to_tx_buf(ring, txd);
- memset(tx_buf, 0, sizeof(*tx_buf));
-
- tx_buf->flags |= MTK_TX_FLAGS_PAGE0;
- dma_unmap_addr_set(tx_buf, dma_addr0, mapped_addr);
- dma_unmap_len_set(tx_buf, dma_len0, frag_map_size);
- frag_size -= frag_map_size;
- offset += frag_map_size;
- }
- }
-
- /* store skb to cleanup */
- tx_buf->skb = skb;
-
- WRITE_ONCE(itxd->txd4, txd4);
- WRITE_ONCE(itxd->txd3, (QDMA_TX_SWC | TX_DMA_PLEN0(skb_headlen(skb)) |
- (!nr_frags * TX_DMA_LS0)));
-
- netdev_sent_queue(dev, skb->len);
- skb_tx_timestamp(skb);
-
- ring->tx_next_free = mtk_tx_next_qdma(ring, txd);
- atomic_sub(n_desc, &ring->tx_free_count);
-
- /* make sure that all changes to the dma ring are flushed before we
- * continue
- */
- wmb();
-
- if (netif_xmit_stopped(netdev_get_tx_queue(dev, 0)) || !skb->xmit_more)
- mtk_w32(eth, txd->txd2, MTK_QTX_CTX_PTR);
-
- return 0;
-
-err_dma:
- do {
- tx_buf = mtk_desc_to_tx_buf(ring, txd);
-
- /* unmap dma */
- mtk_txd_unmap(&dev->dev, tx_buf);
-
- itxd->txd3 = TX_DMA_DESP2_DEF;
- itxd = mtk_tx_next_qdma(ring, itxd);
- } while (itxd != txd);
-
- return -ENOMEM;
-}
-
-static inline int mtk_cal_txd_req(struct sk_buff *skb)
-{
- int i, nfrags;
- struct skb_frag_struct *frag;
-
- nfrags = 1;
- if (skb_is_gso(skb)) {
- for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
- frag = &skb_shinfo(skb)->frags[i];
- nfrags += DIV_ROUND_UP(frag->size, TX_DMA_BUF_LEN);
- }
- } else {
- nfrags += skb_shinfo(skb)->nr_frags;
- }
-
- return DIV_ROUND_UP(nfrags, 2);
-}
-
-static int mtk_start_xmit(struct sk_buff *skb, struct net_device *dev)
-{
- struct mtk_mac *mac = netdev_priv(dev);
- struct mtk_eth *eth = mac->hw;
- struct mtk_tx_ring *ring = &eth->tx_ring;
- struct net_device_stats *stats = &dev->stats;
- int tx_num;
- int len = skb->len;
- bool gso = false;
-
- tx_num = mtk_cal_txd_req(skb);
- if (unlikely(atomic_read(&ring->tx_free_count) <= tx_num)) {
- netif_stop_queue(dev);
- netif_err(eth, tx_queued, dev,
- "Tx Ring full when queue awake!\n");
- return NETDEV_TX_BUSY;
- }
-
- /* TSO: fill MSS info in tcp checksum field */
- if (skb_is_gso(skb)) {
- if (skb_cow_head(skb, 0)) {
- netif_warn(eth, tx_err, dev,
- "GSO expand head fail.\n");
- goto drop;
- }
-
- if (skb_shinfo(skb)->gso_type &
- (SKB_GSO_TCPV4 | SKB_GSO_TCPV6)) {
- gso = true;
- tcp_hdr(skb)->check = htons(skb_shinfo(skb)->gso_size);
- }
- }
-
- if (ring->tx_map(skb, dev, tx_num, ring, gso) < 0)
- goto drop;
-
- stats->tx_packets++;
- stats->tx_bytes += len;
-
- if (unlikely(atomic_read(&ring->tx_free_count) <= ring->tx_thresh)) {
- netif_stop_queue(dev);
- smp_mb();
- if (unlikely(atomic_read(&ring->tx_free_count) >
- ring->tx_thresh))
- netif_wake_queue(dev);
- }
-
- return NETDEV_TX_OK;
-
-drop:
- stats->tx_dropped++;
- dev_kfree_skb(skb);
- return NETDEV_TX_OK;
-}
-
-static int mtk_poll_rx(struct napi_struct *napi, int budget,
- struct mtk_eth *eth, u32 rx_intr)
-{
- struct mtk_soc_data *soc = eth->soc;
- struct mtk_rx_ring *ring = &eth->rx_ring[0];
- int idx = ring->rx_calc_idx;
- u32 checksum_bit;
- struct sk_buff *skb;
- u8 *data, *new_data;
- struct mtk_rx_dma *rxd, trxd;
- int done = 0, pad;
-
- if (eth->soc->hw_features & NETIF_F_RXCSUM)
- checksum_bit = soc->checksum_bit;
- else
- checksum_bit = 0;
-
- if (eth->soc->rx_2b_offset)
- pad = 0;
- else
- pad = NET_IP_ALIGN;
-
- while (done < budget) {
- struct net_device *netdev;
- unsigned int pktlen;
- dma_addr_t dma_addr;
- int mac = 0;
-
- idx = NEXT_RX_DESP_IDX(idx);
- rxd = &ring->rx_dma[idx];
- data = ring->rx_data[idx];
-
- mtk_get_rxd(&trxd, rxd);
- if (!(trxd.rxd2 & RX_DMA_DONE))
- break;
-
- /* find out which mac the packet come from. values start at 1 */
- if (eth->soc->mac_count > 1) {
- mac = (trxd.rxd4 >> RX_DMA_FPORT_SHIFT) &
- RX_DMA_FPORT_MASK;
- mac--;
- if (mac < 0 || mac >= eth->soc->mac_count)
- goto release_desc;
- }
-
- netdev = eth->netdev[mac];
-
- /* alloc new buffer */
- new_data = napi_alloc_frag(ring->frag_size);
- if (unlikely(!new_data || !netdev)) {
- netdev->stats.rx_dropped++;
- goto release_desc;
- }
- dma_addr = dma_map_single(&netdev->dev,
- new_data + NET_SKB_PAD + pad,
- ring->rx_buf_size,
- DMA_FROM_DEVICE);
- if (unlikely(dma_mapping_error(&netdev->dev, dma_addr))) {
- skb_free_frag(new_data);
- goto release_desc;
- }
-
- /* receive data */
- skb = build_skb(data, ring->frag_size);
- if (unlikely(!skb)) {
- put_page(virt_to_head_page(new_data));
- goto release_desc;
- }
- skb_reserve(skb, NET_SKB_PAD + NET_IP_ALIGN);
-
- dma_unmap_single(&netdev->dev, trxd.rxd1,
- ring->rx_buf_size, DMA_FROM_DEVICE);
- pktlen = RX_DMA_GET_PLEN0(trxd.rxd2);
- skb->dev = netdev;
- skb_put(skb, pktlen);
- if (trxd.rxd4 & checksum_bit)
- skb->ip_summed = CHECKSUM_UNNECESSARY;
- else
- skb_checksum_none_assert(skb);
- skb->protocol = eth_type_trans(skb, netdev);
-
- netdev->stats.rx_packets++;
- netdev->stats.rx_bytes += pktlen;
-
- if (netdev->features & NETIF_F_HW_VLAN_CTAG_RX &&
- RX_DMA_VID(trxd.rxd3))
- __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
- RX_DMA_VID(trxd.rxd3));
- napi_gro_receive(napi, skb);
-
- ring->rx_data[idx] = new_data;
- rxd->rxd1 = (unsigned int)dma_addr;
-
-release_desc:
- if (eth->soc->rx_sg_dma)
- rxd->rxd2 = RX_DMA_PLEN0(ring->rx_buf_size);
- else
- rxd->rxd2 = RX_DMA_LSO;
-
- ring->rx_calc_idx = idx;
- /* make sure that all changes to the dma ring are flushed before
- * we continue
- */
- wmb();
- if (eth->soc->dma_type == MTK_QDMA)
- mtk_w32(eth, ring->rx_calc_idx, MTK_QRX_CRX_IDX0);
- else
- mtk_reg_w32(eth, ring->rx_calc_idx,
- MTK_REG_RX_CALC_IDX0);
- done++;
- }
-
- if (done < budget)
- mtk_irq_ack(eth, rx_intr);
-
- return done;
-}
-
-static int mtk_pdma_tx_poll(struct mtk_eth *eth, int budget, bool *tx_again)
-{
- struct sk_buff *skb;
- struct mtk_tx_buf *tx_buf;
- int done = 0;
- u32 idx, hwidx;
- struct mtk_tx_ring *ring = &eth->tx_ring;
- unsigned int bytes = 0;
-
- idx = ring->tx_free_idx;
- hwidx = mtk_reg_r32(eth, MTK_REG_TX_DTX_IDX0);
-
- while ((idx != hwidx) && budget) {
- tx_buf = &ring->tx_buf[idx];
- skb = tx_buf->skb;
-
- if (!skb)
- break;
-
- if (skb != (struct sk_buff *)DMA_DUMMY_DESC) {
- bytes += skb->len;
- done++;
- budget--;
- }
- mtk_txd_unmap(eth->dev, tx_buf);
- idx = NEXT_TX_DESP_IDX(idx);
- }
- ring->tx_free_idx = idx;
- atomic_set(&ring->tx_free_count, mtk_pdma_empty_txd(ring));
-
- /* read hw index again make sure no new tx packet */
- if (idx != hwidx || idx != mtk_reg_r32(eth, MTK_REG_TX_DTX_IDX0))
- *tx_again = 1;
-
- if (done)
- netdev_completed_queue(*eth->netdev, done, bytes);
-
- return done;
-}
-
-static int mtk_qdma_tx_poll(struct mtk_eth *eth, int budget, bool *tx_again)
-{
- struct mtk_tx_ring *ring = &eth->tx_ring;
- struct mtk_tx_dma *desc;
- struct sk_buff *skb;
- struct mtk_tx_buf *tx_buf;
- int total = 0, done[MTK_MAX_DEVS];
- unsigned int bytes[MTK_MAX_DEVS];
- u32 cpu, dma;
- int i;
-
- memset(done, 0, sizeof(done));
- memset(bytes, 0, sizeof(bytes));
-
- cpu = mtk_r32(eth, MTK_QTX_CRX_PTR);
- dma = mtk_r32(eth, MTK_QTX_DRX_PTR);
-
- desc = mtk_qdma_phys_to_virt(ring, cpu);
-
- while ((cpu != dma) && budget) {
- u32 next_cpu = desc->txd2;
- int mac;
-
- desc = mtk_tx_next_qdma(ring, desc);
- if ((desc->txd3 & QDMA_TX_OWNER_CPU) == 0)
- break;
-
- mac = (desc->txd4 >> TX_DMA_FPORT_SHIFT) &
- TX_DMA_FPORT_MASK;
- mac--;
-
- tx_buf = mtk_desc_to_tx_buf(ring, desc);
- skb = tx_buf->skb;
- if (!skb)
- break;
-
- if (skb != (struct sk_buff *)DMA_DUMMY_DESC) {
- bytes[mac] += skb->len;
- done[mac]++;
- budget--;
- }
- mtk_txd_unmap(eth->dev, tx_buf);
-
- ring->tx_last_free->txd2 = next_cpu;
- ring->tx_last_free = desc;
- atomic_inc(&ring->tx_free_count);
-
- cpu = next_cpu;
- }
-
- mtk_w32(eth, cpu, MTK_QTX_CRX_PTR);
-
- /* read hw index again make sure no new tx packet */
- if (cpu != dma || cpu != mtk_r32(eth, MTK_QTX_DRX_PTR))
- *tx_again = true;
-
- for (i = 0; i < eth->soc->mac_count; i++) {
- if (!done[i])
- continue;
- netdev_completed_queue(eth->netdev[i], done[i], bytes[i]);
- total += done[i];
- }
-
- return total;
-}
-
-static int mtk_poll_tx(struct mtk_eth *eth, int budget, u32 tx_intr,
- bool *tx_again)
-{
- struct mtk_tx_ring *ring = &eth->tx_ring;
- struct net_device *netdev = eth->netdev[0];
- int done;
-
- done = eth->tx_ring.tx_poll(eth, budget, tx_again);
- if (!*tx_again)
- mtk_irq_ack(eth, tx_intr);
-
- if (!done)
- return 0;
-
- smp_mb();
- if (unlikely(!netif_queue_stopped(netdev)))
- return done;
-
- if (atomic_read(&ring->tx_free_count) > ring->tx_thresh)
- netif_wake_queue(netdev);
-
- return done;
-}
-
-static void mtk_stats_update(struct mtk_eth *eth)
-{
- int i;
-
- for (i = 0; i < eth->soc->mac_count; i++) {
- if (!eth->mac[i] || !eth->mac[i]->hw_stats)
- continue;
- if (spin_trylock(&eth->mac[i]->hw_stats->stats_lock)) {
- mtk_stats_update_mac(eth->mac[i]);
- spin_unlock(&eth->mac[i]->hw_stats->stats_lock);
- }
- }
-}
-
-static int mtk_poll(struct napi_struct *napi, int budget)
-{
- struct mtk_eth *eth = container_of(napi, struct mtk_eth, rx_napi);
- u32 status, mtk_status, mask, tx_intr, rx_intr, status_intr;
- int tx_done, rx_done;
- bool tx_again = false;
-
- status = mtk_irq_pending(eth);
- mtk_status = mtk_irq_pending_status(eth);
- tx_intr = eth->soc->tx_int;
- rx_intr = eth->soc->rx_int;
- status_intr = eth->soc->status_int;
- tx_done = 0;
- rx_done = 0;
- tx_again = 0;
-
- if (status & tx_intr)
- tx_done = mtk_poll_tx(eth, budget, tx_intr, &tx_again);
-
- if (status & rx_intr)
- rx_done = mtk_poll_rx(napi, budget, eth, rx_intr);
-
- if (unlikely(mtk_status & status_intr)) {
- mtk_stats_update(eth);
- mtk_irq_ack_status(eth, status_intr);
- }
-
- if (unlikely(netif_msg_intr(eth))) {
- mask = mtk_irq_enabled(eth);
- netdev_info(eth->netdev[0],
- "done tx %d, rx %d, intr 0x%08x/0x%x\n",
- tx_done, rx_done, status, mask);
- }
-
- if (tx_again || rx_done == budget)
- return budget;
-
- status = mtk_irq_pending(eth);
- if (status & (tx_intr | rx_intr))
- return budget;
-
- napi_complete(napi);
- mtk_irq_enable(eth, tx_intr | rx_intr);
-
- return rx_done;
-}
-
-static int mtk_pdma_tx_alloc(struct mtk_eth *eth)
-{
- int i;
- struct mtk_tx_ring *ring = &eth->tx_ring;
-
- ring->tx_ring_size = eth->soc->dma_ring_size;
- ring->tx_free_idx = 0;
- ring->tx_next_idx = 0;
- ring->tx_thresh = max((unsigned long)ring->tx_ring_size >> 2,
- MAX_SKB_FRAGS);
-
- ring->tx_buf = kcalloc(ring->tx_ring_size, sizeof(*ring->tx_buf),
- GFP_KERNEL);
- if (!ring->tx_buf)
- goto no_tx_mem;
-
- ring->tx_dma =
- dma_alloc_coherent(eth->dev,
- ring->tx_ring_size * sizeof(*ring->tx_dma),
- &ring->tx_phys, GFP_ATOMIC | __GFP_ZERO);
- if (!ring->tx_dma)
- goto no_tx_mem;
-
- for (i = 0; i < ring->tx_ring_size; i++) {
- ring->tx_dma[i].txd2 = TX_DMA_DESP2_DEF;
- ring->tx_dma[i].txd4 = eth->soc->txd4;
- }
-
- atomic_set(&ring->tx_free_count, mtk_pdma_empty_txd(ring));
- ring->tx_map = mtk_pdma_tx_map;
- ring->tx_poll = mtk_pdma_tx_poll;
- ring->tx_clean = mtk_pdma_tx_clean;
-
- /* make sure that all changes to the dma ring are flushed before we
- * continue
- */
- wmb();
-
- mtk_reg_w32(eth, ring->tx_phys, MTK_REG_TX_BASE_PTR0);
- mtk_reg_w32(eth, ring->tx_ring_size, MTK_REG_TX_MAX_CNT0);
- mtk_reg_w32(eth, 0, MTK_REG_TX_CTX_IDX0);
- mtk_reg_w32(eth, MTK_PST_DTX_IDX0, MTK_REG_PDMA_RST_CFG);
-
- return 0;
-
-no_tx_mem:
- return -ENOMEM;
-}
-
-static int mtk_qdma_tx_alloc_tx(struct mtk_eth *eth)
-{
- struct mtk_tx_ring *ring = &eth->tx_ring;
- int i, sz = sizeof(*ring->tx_dma);
-
- ring->tx_ring_size = eth->soc->dma_ring_size;
- ring->tx_buf = kcalloc(ring->tx_ring_size, sizeof(*ring->tx_buf),
- GFP_KERNEL);
- if (!ring->tx_buf)
- goto no_tx_mem;
-
- ring->tx_dma = dma_alloc_coherent(eth->dev, ring->tx_ring_size * sz,
- &ring->tx_phys,
- GFP_ATOMIC | __GFP_ZERO);
- if (!ring->tx_dma)
- goto no_tx_mem;
-
- for (i = 0; i < ring->tx_ring_size; i++) {
- int next = (i + 1) % ring->tx_ring_size;
- u32 next_ptr = ring->tx_phys + next * sz;
-
- ring->tx_dma[i].txd2 = next_ptr;
- ring->tx_dma[i].txd3 = TX_DMA_DESP2_DEF;
- }
-
- atomic_set(&ring->tx_free_count, ring->tx_ring_size - 2);
- ring->tx_next_free = &ring->tx_dma[0];
- ring->tx_last_free = &ring->tx_dma[ring->tx_ring_size - 2];
- ring->tx_thresh = max((unsigned long)ring->tx_ring_size >> 2,
- MAX_SKB_FRAGS);
-
- ring->tx_map = mtk_qdma_tx_map;
- ring->tx_poll = mtk_qdma_tx_poll;
- ring->tx_clean = mtk_qdma_tx_clean;
-
- /* make sure that all changes to the dma ring are flushed before we
- * continue
- */
- wmb();
-
- mtk_w32(eth, ring->tx_phys, MTK_QTX_CTX_PTR);
- mtk_w32(eth, ring->tx_phys, MTK_QTX_DTX_PTR);
- mtk_w32(eth,
- ring->tx_phys + ((ring->tx_ring_size - 1) * sz),
- MTK_QTX_CRX_PTR);
- mtk_w32(eth,
- ring->tx_phys + ((ring->tx_ring_size - 1) * sz),
- MTK_QTX_DRX_PTR);
-
- return 0;
-
-no_tx_mem:
- return -ENOMEM;
-}
-
-static int mtk_qdma_init(struct mtk_eth *eth, int ring)
-{
- int err;
-
- err = mtk_init_fq_dma(eth);
- if (err)
- return err;
-
- err = mtk_qdma_tx_alloc_tx(eth);
- if (err)
- return err;
-
- err = mtk_dma_rx_alloc(eth, &eth->rx_ring[ring]);
- if (err)
- return err;
-
- mtk_w32(eth, eth->rx_ring[ring].rx_phys, MTK_QRX_BASE_PTR0);
- mtk_w32(eth, eth->rx_ring[ring].rx_ring_size, MTK_QRX_MAX_CNT0);
- mtk_w32(eth, eth->rx_ring[ring].rx_calc_idx, MTK_QRX_CRX_IDX0);
- mtk_w32(eth, MTK_PST_DRX_IDX0, MTK_QDMA_RST_IDX);
- mtk_w32(eth, (QDMA_RES_THRES << 8) | QDMA_RES_THRES, MTK_QTX_CFG(0));
-
- /* Enable random early drop and set drop threshold automatically */
- mtk_w32(eth, 0x174444, MTK_QDMA_FC_THRES);
- mtk_w32(eth, 0x0, MTK_QDMA_HRED2);
-
- return 0;
-}
-
-static int mtk_pdma_qdma_init(struct mtk_eth *eth)
-{
- int err = mtk_qdma_init(eth, 1);
-
- if (err)
- return err;
-
- err = mtk_dma_rx_alloc(eth, &eth->rx_ring[0]);
- if (err)
- return err;
-
- mtk_reg_w32(eth, eth->rx_ring[0].rx_phys, MTK_REG_RX_BASE_PTR0);
- mtk_reg_w32(eth, eth->rx_ring[0].rx_ring_size, MTK_REG_RX_MAX_CNT0);
- mtk_reg_w32(eth, eth->rx_ring[0].rx_calc_idx, MTK_REG_RX_CALC_IDX0);
- mtk_reg_w32(eth, MTK_PST_DRX_IDX0, MTK_REG_PDMA_RST_CFG);
-
- return 0;
-}
-
-static int mtk_pdma_init(struct mtk_eth *eth)
-{
- struct mtk_rx_ring *ring = &eth->rx_ring[0];
- int err;
-
- err = mtk_pdma_tx_alloc(eth);
- if (err)
- return err;
-
- err = mtk_dma_rx_alloc(eth, ring);
- if (err)
- return err;
-
- mtk_reg_w32(eth, ring->rx_phys, MTK_REG_RX_BASE_PTR0);
- mtk_reg_w32(eth, ring->rx_ring_size, MTK_REG_RX_MAX_CNT0);
- mtk_reg_w32(eth, ring->rx_calc_idx, MTK_REG_RX_CALC_IDX0);
- mtk_reg_w32(eth, MTK_PST_DRX_IDX0, MTK_REG_PDMA_RST_CFG);
-
- return 0;
-}
-
-static void mtk_dma_free(struct mtk_eth *eth)
-{
- int i;
-
- for (i = 0; i < eth->soc->mac_count; i++)
- if (eth->netdev[i])
- netdev_reset_queue(eth->netdev[i]);
- eth->tx_ring.tx_clean(eth);
- mtk_clean_rx(eth, &eth->rx_ring[0]);
- mtk_clean_rx(eth, &eth->rx_ring[1]);
- kfree(eth->scratch_head);
-}
-
-static void mtk_tx_timeout(struct net_device *dev)
-{
- struct mtk_mac *mac = netdev_priv(dev);
- struct mtk_eth *eth = mac->hw;
- struct mtk_tx_ring *ring = &eth->tx_ring;
-
- eth->netdev[mac->id]->stats.tx_errors++;
- netif_err(eth, tx_err, dev,
- "transmit timed out\n");
- if (eth->soc->dma_type & MTK_PDMA) {
- netif_info(eth, drv, dev, "pdma_cfg:%08x\n",
- mtk_reg_r32(eth, MTK_REG_PDMA_GLO_CFG));
- netif_info(eth, drv, dev,
- "tx_ring=%d, base=%08x, max=%u, ctx=%u, dtx=%u, fdx=%hu, next=%hu\n",
- 0, mtk_reg_r32(eth, MTK_REG_TX_BASE_PTR0),
- mtk_reg_r32(eth, MTK_REG_TX_MAX_CNT0),
- mtk_reg_r32(eth, MTK_REG_TX_CTX_IDX0),
- mtk_reg_r32(eth, MTK_REG_TX_DTX_IDX0),
- ring->tx_free_idx,
- ring->tx_next_idx);
- }
- if (eth->soc->dma_type & MTK_QDMA) {
- netif_info(eth, drv, dev, "qdma_cfg:%08x\n",
- mtk_r32(eth, MTK_QDMA_GLO_CFG));
- netif_info(eth, drv, dev,
- "tx_ring=%d, ctx=%08x, dtx=%08x, crx=%08x, drx=%08x, free=%hu\n",
- 0, mtk_r32(eth, MTK_QTX_CTX_PTR),
- mtk_r32(eth, MTK_QTX_DTX_PTR),
- mtk_r32(eth, MTK_QTX_CRX_PTR),
- mtk_r32(eth, MTK_QTX_DRX_PTR),
- atomic_read(&ring->tx_free_count));
- }
- netif_info(eth, drv, dev,
- "rx_ring=%d, base=%08x, max=%u, calc=%u, drx=%u\n",
- 0, mtk_reg_r32(eth, MTK_REG_RX_BASE_PTR0),
- mtk_reg_r32(eth, MTK_REG_RX_MAX_CNT0),
- mtk_reg_r32(eth, MTK_REG_RX_CALC_IDX0),
- mtk_reg_r32(eth, MTK_REG_RX_DRX_IDX0));
-
- schedule_work(&mac->pending_work);
-}
-
-static irqreturn_t mtk_handle_irq(int irq, void *_eth)
-{
- struct mtk_eth *eth = _eth;
- u32 status, int_mask;
-
- status = mtk_irq_pending(eth);
- if (unlikely(!status))
- return IRQ_NONE;
-
- int_mask = (eth->soc->rx_int | eth->soc->tx_int);
- if (likely(status & int_mask)) {
- if (likely(napi_schedule_prep(&eth->rx_napi)))
- __napi_schedule(&eth->rx_napi);
- } else {
- mtk_irq_ack(eth, status);
- }
- mtk_irq_disable(eth, int_mask);
-
- return IRQ_HANDLED;
-}
-
-#ifdef CONFIG_NET_POLL_CONTROLLER
-static void mtk_poll_controller(struct net_device *dev)
-{
- struct mtk_mac *mac = netdev_priv(dev);
- struct mtk_eth *eth = mac->hw;
- u32 int_mask = eth->soc->tx_int | eth->soc->rx_int;
-
- mtk_irq_disable(eth, int_mask);
- mtk_handle_irq(dev->irq, dev);
- mtk_irq_enable(eth, int_mask);
-}
-#endif
-
-int mtk_set_clock_cycle(struct mtk_eth *eth)
-{
- unsigned long sysclk = eth->sysclk;
-
- sysclk /= MTK_US_CYC_CNT_DIVISOR;
- sysclk <<= MTK_US_CYC_CNT_SHIFT;
-
- mtk_w32(eth, (mtk_r32(eth, MTK_GLO_CFG) &
- ~(MTK_US_CYC_CNT_MASK << MTK_US_CYC_CNT_SHIFT)) |
- sysclk,
- MTK_GLO_CFG);
- return 0;
-}
-
-void mtk_fwd_config(struct mtk_eth *eth)
-{
- u32 fwd_cfg;
-
- fwd_cfg = mtk_r32(eth, MTK_GDMA1_FWD_CFG);
-
- /* disable jumbo frame */
- if (eth->soc->jumbo_frame)
- fwd_cfg &= ~MTK_GDM1_JMB_EN;
-
- /* set unicast/multicast/broadcast frame to cpu */
- fwd_cfg &= ~0xffff;
-
- mtk_w32(eth, fwd_cfg, MTK_GDMA1_FWD_CFG);
-}
-
-void mtk_csum_config(struct mtk_eth *eth)
-{
- if (eth->soc->hw_features & NETIF_F_RXCSUM)
- mtk_w32(eth, mtk_r32(eth, MTK_GDMA1_FWD_CFG) |
- (MTK_GDM1_ICS_EN | MTK_GDM1_TCS_EN | MTK_GDM1_UCS_EN),
- MTK_GDMA1_FWD_CFG);
- else
- mtk_w32(eth, mtk_r32(eth, MTK_GDMA1_FWD_CFG) &
- ~(MTK_GDM1_ICS_EN | MTK_GDM1_TCS_EN | MTK_GDM1_UCS_EN),
- MTK_GDMA1_FWD_CFG);
- if (eth->soc->hw_features & NETIF_F_IP_CSUM)
- mtk_w32(eth, mtk_r32(eth, MTK_CDMA_CSG_CFG) |
- (MTK_ICS_GEN_EN | MTK_TCS_GEN_EN | MTK_UCS_GEN_EN),
- MTK_CDMA_CSG_CFG);
- else
- mtk_w32(eth, mtk_r32(eth, MTK_CDMA_CSG_CFG) &
- ~(MTK_ICS_GEN_EN | MTK_TCS_GEN_EN | MTK_UCS_GEN_EN),
- MTK_CDMA_CSG_CFG);
-}
-
-static int mtk_start_dma(struct mtk_eth *eth)
-{
- unsigned long flags;
- u32 val;
- int err;
-
- if (eth->soc->dma_type == MTK_PDMA)
- err = mtk_pdma_init(eth);
- else if (eth->soc->dma_type == MTK_QDMA)
- err = mtk_qdma_init(eth, 0);
- else
- err = mtk_pdma_qdma_init(eth);
- if (err) {
- mtk_dma_free(eth);
- return err;
- }
-
- spin_lock_irqsave(&eth->page_lock, flags);
-
- val = MTK_TX_WB_DDONE | MTK_RX_DMA_EN | MTK_TX_DMA_EN;
- if (eth->soc->rx_2b_offset)
- val |= MTK_RX_2B_OFFSET;
- val |= eth->soc->pdma_glo_cfg;
-
- if (eth->soc->dma_type & MTK_PDMA)
- mtk_reg_w32(eth, val, MTK_REG_PDMA_GLO_CFG);
-
- if (eth->soc->dma_type & MTK_QDMA)
- mtk_w32(eth, val, MTK_QDMA_GLO_CFG);
-
- spin_unlock_irqrestore(&eth->page_lock, flags);
-
- return 0;
-}
-
-static int mtk_open(struct net_device *dev)
-{
- struct mtk_mac *mac = netdev_priv(dev);
- struct mtk_eth *eth = mac->hw;
-
- dma_coerce_mask_and_coherent(&dev->dev, DMA_BIT_MASK(32));
-
- if (!atomic_read(&eth->dma_refcnt)) {
- int err = mtk_start_dma(eth);
-
- if (err)
- return err;
-
- napi_enable(&eth->rx_napi);
- mtk_irq_enable(eth, eth->soc->tx_int | eth->soc->rx_int);
- }
- atomic_inc(&eth->dma_refcnt);
-
- if (eth->phy)
- eth->phy->start(mac);
-
- if (eth->soc->has_carrier && eth->soc->has_carrier(eth))
- netif_carrier_on(dev);
-
- netif_start_queue(dev);
- eth->soc->fwd_config(eth);
-
- return 0;
-}
-
-static void mtk_stop_dma(struct mtk_eth *eth, u32 glo_cfg)
-{
- unsigned long flags;
- u32 val;
- int i;
-
- /* stop the dma enfine */
- spin_lock_irqsave(&eth->page_lock, flags);
- val = mtk_r32(eth, glo_cfg);
- mtk_w32(eth, val & ~(MTK_TX_WB_DDONE | MTK_RX_DMA_EN | MTK_TX_DMA_EN),
- glo_cfg);
- spin_unlock_irqrestore(&eth->page_lock, flags);
-
- /* wait for dma stop */
- for (i = 0; i < 10; i++) {
- val = mtk_r32(eth, glo_cfg);
- if (val & (MTK_TX_DMA_BUSY | MTK_RX_DMA_BUSY)) {
- msleep(20);
- continue;
- }
- break;
- }
-}
-
-static int mtk_stop(struct net_device *dev)
-{
- struct mtk_mac *mac = netdev_priv(dev);
- struct mtk_eth *eth = mac->hw;
-
- netif_tx_disable(dev);
- if (eth->phy)
- eth->phy->stop(mac);
-
- if (!atomic_dec_and_test(&eth->dma_refcnt))
- return 0;
-
- mtk_irq_disable(eth, eth->soc->tx_int | eth->soc->rx_int);
- napi_disable(&eth->rx_napi);
-
- if (eth->soc->dma_type & MTK_PDMA)
- mtk_stop_dma(eth, mtk_reg_table[MTK_REG_PDMA_GLO_CFG]);
-
- if (eth->soc->dma_type & MTK_QDMA)
- mtk_stop_dma(eth, MTK_QDMA_GLO_CFG);
-
- mtk_dma_free(eth);
-
- return 0;
-}
-
-static int __init mtk_init_hw(struct mtk_eth *eth)
-{
- int i, err;
-
- eth->soc->reset_fe(eth);
-
- if (eth->soc->switch_init)
- if (eth->soc->switch_init(eth)) {
- dev_err(eth->dev, "failed to initialize switch core\n");
- return -ENODEV;
- }
-
- err = devm_request_irq(eth->dev, eth->irq, mtk_handle_irq, 0,
- dev_name(eth->dev), eth);
- if (err)
- return err;
-
- err = mtk_mdio_init(eth);
- if (err)
- return err;
-
- /* disable delay and normal interrupt */
- mtk_reg_w32(eth, 0, MTK_REG_DLY_INT_CFG);
- if (eth->soc->dma_type & MTK_QDMA)
- mtk_w32(eth, 0, MTK_QDMA_DELAY_INT);
- mtk_irq_disable(eth, eth->soc->tx_int | eth->soc->rx_int);
-
- /* frame engine will push VLAN tag regarding to VIDX field in Tx desc */
- if (mtk_reg_table[MTK_REG_MTK_DMA_VID_BASE])
- for (i = 0; i < 16; i += 2)
- mtk_w32(eth, ((i + 1) << 16) + i,
- mtk_reg_table[MTK_REG_MTK_DMA_VID_BASE] +
- (i * 2));
-
- if (eth->soc->fwd_config(eth))
- dev_err(eth->dev, "unable to get clock\n");
-
- if (mtk_reg_table[MTK_REG_MTK_RST_GL]) {
- mtk_reg_w32(eth, 1, MTK_REG_MTK_RST_GL);
- mtk_reg_w32(eth, 0, MTK_REG_MTK_RST_GL);
- }
-
- return 0;
-}
-
-static int __init mtk_init(struct net_device *dev)
-{
- struct mtk_mac *mac = netdev_priv(dev);
- struct mtk_eth *eth = mac->hw;
- struct device_node *port;
- const char *mac_addr;
- int err;
-
- mac_addr = of_get_mac_address(mac->of_node);
- if (mac_addr)
- ether_addr_copy(dev->dev_addr, mac_addr);
-
- /* If the mac address is invalid, use random mac address */
- if (!is_valid_ether_addr(dev->dev_addr)) {
- eth_hw_addr_random(dev);
- dev_err(eth->dev, "generated random MAC address %pM\n",
- dev->dev_addr);
- }
- mac->hw->soc->set_mac(mac, dev->dev_addr);
-
- if (eth->soc->port_init)
- for_each_child_of_node(mac->of_node, port)
- if (of_device_is_compatible(port,
- "mediatek,eth-port") &&
- of_device_is_available(port))
- eth->soc->port_init(eth, mac, port);
-
- if (eth->phy) {
- err = eth->phy->connect(mac);
- if (err)
- return err;
- }
-
- return 0;
-}
-
-static void mtk_uninit(struct net_device *dev)
-{
- struct mtk_mac *mac = netdev_priv(dev);
- struct mtk_eth *eth = mac->hw;
-
- if (eth->phy)
- eth->phy->disconnect(mac);
- mtk_mdio_cleanup(eth);
-
- mtk_irq_disable(eth, ~0);
- free_irq(dev->irq, dev);
-}
-
-static int mtk_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
-{
- struct mtk_mac *mac = netdev_priv(dev);
-
- if (!mac->phy_dev)
- return -ENODEV;
-
- switch (cmd) {
- case SIOCGMIIPHY:
- case SIOCGMIIREG:
- case SIOCSMIIREG:
- return phy_mii_ioctl(mac->phy_dev, ifr, cmd);
- default:
- break;
- }
-
- return -EOPNOTSUPP;
-}
-
-static int mtk_change_mtu(struct net_device *dev, int new_mtu)
-{
- struct mtk_mac *mac = netdev_priv(dev);
- struct mtk_eth *eth = mac->hw;
- int frag_size, old_mtu;
- u32 fwd_cfg;
-
- if (!eth->soc->jumbo_frame)
- return eth_change_mtu(dev, new_mtu);
-
- frag_size = mtk_max_frag_size(new_mtu);
- if (new_mtu < 68 || frag_size > PAGE_SIZE)
- return -EINVAL;
-
- old_mtu = dev->mtu;
- dev->mtu = new_mtu;
-
- /* return early if the buffer sizes will not change */
- if (old_mtu <= ETH_DATA_LEN && new_mtu <= ETH_DATA_LEN)
- return 0;
- if (old_mtu > ETH_DATA_LEN && new_mtu > ETH_DATA_LEN)
- return 0;
-
- if (new_mtu <= ETH_DATA_LEN)
- eth->rx_ring[0].frag_size = mtk_max_frag_size(ETH_DATA_LEN);
- else
- eth->rx_ring[0].frag_size = PAGE_SIZE;
- eth->rx_ring[0].rx_buf_size =
- mtk_max_buf_size(eth->rx_ring[0].frag_size);
-
- if (!netif_running(dev))
- return 0;
-
- mtk_stop(dev);
- fwd_cfg = mtk_r32(eth, MTK_GDMA1_FWD_CFG);
- if (new_mtu <= ETH_DATA_LEN) {
- fwd_cfg &= ~MTK_GDM1_JMB_EN;
- } else {
- fwd_cfg &= ~(MTK_GDM1_JMB_LEN_MASK << MTK_GDM1_JMB_LEN_SHIFT);
- fwd_cfg |= (DIV_ROUND_UP(frag_size, 1024) <<
- MTK_GDM1_JMB_LEN_SHIFT) | MTK_GDM1_JMB_EN;
- }
- mtk_w32(eth, fwd_cfg, MTK_GDMA1_FWD_CFG);
-
- return mtk_open(dev);
-}
-
-static void mtk_pending_work(struct work_struct *work)
-{
- struct mtk_mac *mac = container_of(work, struct mtk_mac, pending_work);
- struct mtk_eth *eth = mac->hw;
- struct net_device *dev = eth->netdev[mac->id];
- int err;
-
- rtnl_lock();
- mtk_stop(dev);
-
- err = mtk_open(dev);
- if (err) {
- netif_alert(eth, ifup, dev,
- "Driver up/down cycle failed, closing device.\n");
- dev_close(dev);
- }
- rtnl_unlock();
-}
-
-static int mtk_cleanup(struct mtk_eth *eth)
-{
- int i;
-
- for (i = 0; i < eth->soc->mac_count; i++) {
- struct mtk_mac *mac = netdev_priv(eth->netdev[i]);
-
- if (!eth->netdev[i])
- continue;
-
- unregister_netdev(eth->netdev[i]);
- free_netdev(eth->netdev[i]);
- cancel_work_sync(&mac->pending_work);
- }
-
- return 0;
-}
-
-static const struct net_device_ops mtk_netdev_ops = {
- .ndo_init = mtk_init,
- .ndo_uninit = mtk_uninit,
- .ndo_open = mtk_open,
- .ndo_stop = mtk_stop,
- .ndo_start_xmit = mtk_start_xmit,
- .ndo_set_mac_address = mtk_set_mac_address,
- .ndo_validate_addr = eth_validate_addr,
- .ndo_do_ioctl = mtk_do_ioctl,
- .ndo_change_mtu = mtk_change_mtu,
- .ndo_tx_timeout = mtk_tx_timeout,
- .ndo_get_stats64 = mtk_get_stats64,
- .ndo_vlan_rx_add_vid = mtk_vlan_rx_add_vid,
- .ndo_vlan_rx_kill_vid = mtk_vlan_rx_kill_vid,
-#ifdef CONFIG_NET_POLL_CONTROLLER
- .ndo_poll_controller = mtk_poll_controller,
-#endif
-};
-
-static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np)
-{
- struct mtk_mac *mac;
- const __be32 *_id = of_get_property(np, "reg", NULL);
- int id, err;
-
- if (!_id) {
- dev_err(eth->dev, "missing mac id\n");
- return -EINVAL;
- }
- id = be32_to_cpup(_id);
- if (id >= eth->soc->mac_count || eth->netdev[id]) {
- dev_err(eth->dev, "%d is not a valid mac id\n", id);
- return -EINVAL;
- }
-
- eth->netdev[id] = alloc_etherdev(sizeof(*mac));
- if (!eth->netdev[id]) {
- dev_err(eth->dev, "alloc_etherdev failed\n");
- return -ENOMEM;
- }
- mac = netdev_priv(eth->netdev[id]);
- eth->mac[id] = mac;
- mac->id = id;
- mac->hw = eth;
- mac->of_node = np;
- INIT_WORK(&mac->pending_work, mtk_pending_work);
-
- if (mtk_reg_table[MTK_REG_MTK_COUNTER_BASE]) {
- mac->hw_stats = devm_kzalloc(eth->dev,
- sizeof(*mac->hw_stats),
- GFP_KERNEL);
- if (!mac->hw_stats) {
- err = -ENOMEM;
- goto free_netdev;
- }
- spin_lock_init(&mac->hw_stats->stats_lock);
- mac->hw_stats->reg_offset = id * MTK_STAT_OFFSET;
- }
-
- SET_NETDEV_DEV(eth->netdev[id], eth->dev);
- eth->netdev[id]->netdev_ops = &mtk_netdev_ops;
- eth->netdev[id]->base_addr = (unsigned long)eth->base;
-
- if (eth->soc->init_data)
- eth->soc->init_data(eth->soc, eth->netdev[id]);
-
- eth->netdev[id]->vlan_features = eth->soc->hw_features &
- ~(NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX);
- eth->netdev[id]->features |= eth->soc->hw_features;
-
- if (mtk_reg_table[MTK_REG_MTK_DMA_VID_BASE])
- eth->netdev[id]->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
-
- mtk_set_ethtool_ops(eth->netdev[id]);
-
- err = register_netdev(eth->netdev[id]);
- if (err) {
- dev_err(eth->dev, "error bringing up device\n");
- err = -ENOMEM;
- goto free_netdev;
- }
- eth->netdev[id]->irq = eth->irq;
- netif_info(eth, probe, eth->netdev[id],
- "mediatek frame engine at 0x%08lx, irq %d\n",
- eth->netdev[id]->base_addr, eth->netdev[id]->irq);
-
- return 0;
-
-free_netdev:
- free_netdev(eth->netdev[id]);
- return err;
-}
-
-static int mtk_probe(struct platform_device *pdev)
-{
- struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- const struct of_device_id *match;
- struct device_node *mac_np;
- struct mtk_soc_data *soc;
- struct mtk_eth *eth;
- struct clk *sysclk;
- int err;
-
- device_reset(&pdev->dev);
-
- match = of_match_device(of_mtk_match, &pdev->dev);
- soc = (struct mtk_soc_data *)match->data;
-
- if (soc->reg_table)
- mtk_reg_table = soc->reg_table;
-
- eth = devm_kzalloc(&pdev->dev, sizeof(*eth), GFP_KERNEL);
- if (!eth)
- return -ENOMEM;
-
- eth->base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(eth->base))
- return PTR_ERR(eth->base);
-
- spin_lock_init(&eth->page_lock);
-
- eth->ethsys = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
- "mediatek,ethsys");
- if (IS_ERR(eth->ethsys))
- return PTR_ERR(eth->ethsys);
-
- eth->irq = platform_get_irq(pdev, 0);
- if (eth->irq < 0) {
- dev_err(&pdev->dev, "no IRQ resource found\n");
- return -ENXIO;
- }
-
- sysclk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(sysclk)) {
- dev_err(&pdev->dev,
- "the clock is not defined in the devicetree\n");
- return -ENXIO;
- }
- eth->sysclk = clk_get_rate(sysclk);
-
- eth->switch_np = of_parse_phandle(pdev->dev.of_node,
- "mediatek,switch", 0);
- if (soc->has_switch && !eth->switch_np) {
- dev_err(&pdev->dev, "failed to read switch phandle\n");
- return -ENODEV;
- }
-
- eth->dev = &pdev->dev;
- eth->soc = soc;
- eth->msg_enable = netif_msg_init(mtk_msg_level, MTK_DEFAULT_MSG_ENABLE);
-
- err = mtk_init_hw(eth);
- if (err)
- return err;
-
- if (eth->soc->mac_count > 1) {
- for_each_child_of_node(pdev->dev.of_node, mac_np) {
- if (!of_device_is_compatible(mac_np,
- "mediatek,eth-mac"))
- continue;
-
- if (!of_device_is_available(mac_np))
- continue;
-
- err = mtk_add_mac(eth, mac_np);
- if (err)
- goto err_free_dev;
- }
-
- init_dummy_netdev(&eth->dummy_dev);
- netif_napi_add(&eth->dummy_dev, &eth->rx_napi, mtk_poll,
- soc->napi_weight);
- } else {
- err = mtk_add_mac(eth, pdev->dev.of_node);
- if (err)
- goto err_free_dev;
- netif_napi_add(eth->netdev[0], &eth->rx_napi, mtk_poll,
- soc->napi_weight);
- }
-
- platform_set_drvdata(pdev, eth);
-
- return 0;
-
-err_free_dev:
- mtk_cleanup(eth);
- return err;
-}
-
-static int mtk_remove(struct platform_device *pdev)
-{
- struct mtk_eth *eth = platform_get_drvdata(pdev);
-
- netif_napi_del(&eth->rx_napi);
- mtk_cleanup(eth);
- platform_set_drvdata(pdev, NULL);
-
- return 0;
-}
-
-static struct platform_driver mtk_driver = {
- .probe = mtk_probe,
- .remove = mtk_remove,
- .driver = {
- .name = "mtk_soc_eth",
- .of_match_table = of_mtk_match,
- },
-};
-
-module_platform_driver(mtk_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("John Crispin <blogic@openwrt.org>");
-MODULE_DESCRIPTION("Ethernet driver for MediaTek SoC");
diff --git a/drivers/staging/mt7621-eth/mtk_eth_soc.h b/drivers/staging/mt7621-eth/mtk_eth_soc.h
deleted file mode 100644
index e6ed80433f49..000000000000
--- a/drivers/staging/mt7621-eth/mtk_eth_soc.h
+++ /dev/null
@@ -1,716 +0,0 @@
-/* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * Copyright (C) 2009-2016 John Crispin <blogic@openwrt.org>
- * Copyright (C) 2009-2016 Felix Fietkau <nbd@openwrt.org>
- * Copyright (C) 2013-2016 Michael Lee <igvtee@gmail.com>
- */
-
-#ifndef MTK_ETH_H
-#define MTK_ETH_H
-
-#include <linux/mii.h>
-#include <linux/interrupt.h>
-#include <linux/netdevice.h>
-#include <linux/dma-mapping.h>
-#include <linux/phy.h>
-#include <linux/ethtool.h>
-#include <linux/version.h>
-#include <linux/atomic.h>
-
-/* these registers have different offsets depending on the SoC. we use a lookup
- * table for these
- */
-enum mtk_reg {
- MTK_REG_PDMA_GLO_CFG = 0,
- MTK_REG_PDMA_RST_CFG,
- MTK_REG_DLY_INT_CFG,
- MTK_REG_TX_BASE_PTR0,
- MTK_REG_TX_MAX_CNT0,
- MTK_REG_TX_CTX_IDX0,
- MTK_REG_TX_DTX_IDX0,
- MTK_REG_RX_BASE_PTR0,
- MTK_REG_RX_MAX_CNT0,
- MTK_REG_RX_CALC_IDX0,
- MTK_REG_RX_DRX_IDX0,
- MTK_REG_MTK_INT_ENABLE,
- MTK_REG_MTK_INT_STATUS,
- MTK_REG_MTK_DMA_VID_BASE,
- MTK_REG_MTK_COUNTER_BASE,
- MTK_REG_MTK_RST_GL,
- MTK_REG_MTK_INT_STATUS2,
- MTK_REG_COUNT
-};
-
-/* delayed interrupt bits */
-#define MTK_DELAY_EN_INT 0x80
-#define MTK_DELAY_MAX_INT 0x04
-#define MTK_DELAY_MAX_TOUT 0x04
-#define MTK_DELAY_TIME 20
-#define MTK_DELAY_CHAN (((MTK_DELAY_EN_INT | MTK_DELAY_MAX_INT) << 8) \
- | MTK_DELAY_MAX_TOUT)
-#define MTK_DELAY_INIT ((MTK_DELAY_CHAN << 16) | MTK_DELAY_CHAN)
-#define MTK_PSE_FQFC_CFG_INIT 0x80504000
-#define MTK_PSE_FQFC_CFG_256Q 0xff908000
-
-/* interrupt bits */
-#define MTK_CNT_PPE_AF BIT(31)
-#define MTK_CNT_GDM_AF BIT(29)
-#define MTK_PSE_P2_FC BIT(26)
-#define MTK_PSE_BUF_DROP BIT(24)
-#define MTK_GDM_OTHER_DROP BIT(23)
-#define MTK_PSE_P1_FC BIT(22)
-#define MTK_PSE_P0_FC BIT(21)
-#define MTK_PSE_FQ_EMPTY BIT(20)
-#define MTK_GE1_STA_CHG BIT(18)
-#define MTK_TX_COHERENT BIT(17)
-#define MTK_RX_COHERENT BIT(16)
-#define MTK_TX_DONE_INT3 BIT(11)
-#define MTK_TX_DONE_INT2 BIT(10)
-#define MTK_TX_DONE_INT1 BIT(9)
-#define MTK_TX_DONE_INT0 BIT(8)
-#define MTK_RX_DONE_INT0 BIT(2)
-#define MTK_TX_DLY_INT BIT(1)
-#define MTK_RX_DLY_INT BIT(0)
-
-#define MTK_RX_DONE_INT MTK_RX_DONE_INT0
-#define MTK_TX_DONE_INT (MTK_TX_DONE_INT0 | MTK_TX_DONE_INT1 | \
- MTK_TX_DONE_INT2 | MTK_TX_DONE_INT3)
-
-#define RT5350_RX_DLY_INT BIT(30)
-#define RT5350_TX_DLY_INT BIT(28)
-#define RT5350_RX_DONE_INT1 BIT(17)
-#define RT5350_RX_DONE_INT0 BIT(16)
-#define RT5350_TX_DONE_INT3 BIT(3)
-#define RT5350_TX_DONE_INT2 BIT(2)
-#define RT5350_TX_DONE_INT1 BIT(1)
-#define RT5350_TX_DONE_INT0 BIT(0)
-
-#define RT5350_RX_DONE_INT (RT5350_RX_DONE_INT0 | RT5350_RX_DONE_INT1)
-#define RT5350_TX_DONE_INT (RT5350_TX_DONE_INT0 | RT5350_TX_DONE_INT1 | \
- RT5350_TX_DONE_INT2 | RT5350_TX_DONE_INT3)
-
-/* registers */
-#define MTK_GDMA_OFFSET 0x0020
-#define MTK_PSE_OFFSET 0x0040
-#define MTK_GDMA2_OFFSET 0x0060
-#define MTK_CDMA_OFFSET 0x0080
-#define MTK_DMA_VID0 0x00a8
-#define MTK_PDMA_OFFSET 0x0100
-#define MTK_PPE_OFFSET 0x0200
-#define MTK_CMTABLE_OFFSET 0x0400
-#define MTK_POLICYTABLE_OFFSET 0x1000
-
-#define MT7621_GDMA_OFFSET 0x0500
-#define MT7620_GDMA_OFFSET 0x0600
-
-#define RT5350_PDMA_OFFSET 0x0800
-#define RT5350_SDM_OFFSET 0x0c00
-
-#define MTK_MDIO_ACCESS 0x00
-#define MTK_MDIO_CFG 0x04
-#define MTK_GLO_CFG 0x08
-#define MTK_RST_GL 0x0C
-#define MTK_INT_STATUS 0x10
-#define MTK_INT_ENABLE 0x14
-#define MTK_MDIO_CFG2 0x18
-#define MTK_FOC_TS_T 0x1C
-
-#define MTK_GDMA1_FWD_CFG (MTK_GDMA_OFFSET + 0x00)
-#define MTK_GDMA1_SCH_CFG (MTK_GDMA_OFFSET + 0x04)
-#define MTK_GDMA1_SHPR_CFG (MTK_GDMA_OFFSET + 0x08)
-#define MTK_GDMA1_MAC_ADRL (MTK_GDMA_OFFSET + 0x0C)
-#define MTK_GDMA1_MAC_ADRH (MTK_GDMA_OFFSET + 0x10)
-
-#define MTK_GDMA2_FWD_CFG (MTK_GDMA2_OFFSET + 0x00)
-#define MTK_GDMA2_SCH_CFG (MTK_GDMA2_OFFSET + 0x04)
-#define MTK_GDMA2_SHPR_CFG (MTK_GDMA2_OFFSET + 0x08)
-#define MTK_GDMA2_MAC_ADRL (MTK_GDMA2_OFFSET + 0x0C)
-#define MTK_GDMA2_MAC_ADRH (MTK_GDMA2_OFFSET + 0x10)
-
-#define MTK_PSE_FQ_CFG (MTK_PSE_OFFSET + 0x00)
-#define MTK_CDMA_FC_CFG (MTK_PSE_OFFSET + 0x04)
-#define MTK_GDMA1_FC_CFG (MTK_PSE_OFFSET + 0x08)
-#define MTK_GDMA2_FC_CFG (MTK_PSE_OFFSET + 0x0C)
-
-#define MTK_CDMA_CSG_CFG (MTK_CDMA_OFFSET + 0x00)
-#define MTK_CDMA_SCH_CFG (MTK_CDMA_OFFSET + 0x04)
-
-#define MT7621_GDMA_FWD_CFG(x) (MT7621_GDMA_OFFSET + (x * 0x1000))
-
-/* FIXME this might be different for different SOCs */
-#define MT7620_GDMA1_FWD_CFG (MT7621_GDMA_OFFSET + 0x00)
-
-#define RT5350_TX_BASE_PTR0 (RT5350_PDMA_OFFSET + 0x00)
-#define RT5350_TX_MAX_CNT0 (RT5350_PDMA_OFFSET + 0x04)
-#define RT5350_TX_CTX_IDX0 (RT5350_PDMA_OFFSET + 0x08)
-#define RT5350_TX_DTX_IDX0 (RT5350_PDMA_OFFSET + 0x0C)
-#define RT5350_TX_BASE_PTR1 (RT5350_PDMA_OFFSET + 0x10)
-#define RT5350_TX_MAX_CNT1 (RT5350_PDMA_OFFSET + 0x14)
-#define RT5350_TX_CTX_IDX1 (RT5350_PDMA_OFFSET + 0x18)
-#define RT5350_TX_DTX_IDX1 (RT5350_PDMA_OFFSET + 0x1C)
-#define RT5350_TX_BASE_PTR2 (RT5350_PDMA_OFFSET + 0x20)
-#define RT5350_TX_MAX_CNT2 (RT5350_PDMA_OFFSET + 0x24)
-#define RT5350_TX_CTX_IDX2 (RT5350_PDMA_OFFSET + 0x28)
-#define RT5350_TX_DTX_IDX2 (RT5350_PDMA_OFFSET + 0x2C)
-#define RT5350_TX_BASE_PTR3 (RT5350_PDMA_OFFSET + 0x30)
-#define RT5350_TX_MAX_CNT3 (RT5350_PDMA_OFFSET + 0x34)
-#define RT5350_TX_CTX_IDX3 (RT5350_PDMA_OFFSET + 0x38)
-#define RT5350_TX_DTX_IDX3 (RT5350_PDMA_OFFSET + 0x3C)
-#define RT5350_RX_BASE_PTR0 (RT5350_PDMA_OFFSET + 0x100)
-#define RT5350_RX_MAX_CNT0 (RT5350_PDMA_OFFSET + 0x104)
-#define RT5350_RX_CALC_IDX0 (RT5350_PDMA_OFFSET + 0x108)
-#define RT5350_RX_DRX_IDX0 (RT5350_PDMA_OFFSET + 0x10C)
-#define RT5350_RX_BASE_PTR1 (RT5350_PDMA_OFFSET + 0x110)
-#define RT5350_RX_MAX_CNT1 (RT5350_PDMA_OFFSET + 0x114)
-#define RT5350_RX_CALC_IDX1 (RT5350_PDMA_OFFSET + 0x118)
-#define RT5350_RX_DRX_IDX1 (RT5350_PDMA_OFFSET + 0x11C)
-#define RT5350_PDMA_GLO_CFG (RT5350_PDMA_OFFSET + 0x204)
-#define RT5350_PDMA_RST_CFG (RT5350_PDMA_OFFSET + 0x208)
-#define RT5350_DLY_INT_CFG (RT5350_PDMA_OFFSET + 0x20c)
-#define RT5350_MTK_INT_STATUS (RT5350_PDMA_OFFSET + 0x220)
-#define RT5350_MTK_INT_ENABLE (RT5350_PDMA_OFFSET + 0x228)
-#define RT5350_PDMA_SCH_CFG (RT5350_PDMA_OFFSET + 0x280)
-
-#define MTK_PDMA_GLO_CFG (MTK_PDMA_OFFSET + 0x00)
-#define MTK_PDMA_RST_CFG (MTK_PDMA_OFFSET + 0x04)
-#define MTK_PDMA_SCH_CFG (MTK_PDMA_OFFSET + 0x08)
-#define MTK_DLY_INT_CFG (MTK_PDMA_OFFSET + 0x0C)
-#define MTK_TX_BASE_PTR0 (MTK_PDMA_OFFSET + 0x10)
-#define MTK_TX_MAX_CNT0 (MTK_PDMA_OFFSET + 0x14)
-#define MTK_TX_CTX_IDX0 (MTK_PDMA_OFFSET + 0x18)
-#define MTK_TX_DTX_IDX0 (MTK_PDMA_OFFSET + 0x1C)
-#define MTK_TX_BASE_PTR1 (MTK_PDMA_OFFSET + 0x20)
-#define MTK_TX_MAX_CNT1 (MTK_PDMA_OFFSET + 0x24)
-#define MTK_TX_CTX_IDX1 (MTK_PDMA_OFFSET + 0x28)
-#define MTK_TX_DTX_IDX1 (MTK_PDMA_OFFSET + 0x2C)
-#define MTK_RX_BASE_PTR0 (MTK_PDMA_OFFSET + 0x30)
-#define MTK_RX_MAX_CNT0 (MTK_PDMA_OFFSET + 0x34)
-#define MTK_RX_CALC_IDX0 (MTK_PDMA_OFFSET + 0x38)
-#define MTK_RX_DRX_IDX0 (MTK_PDMA_OFFSET + 0x3C)
-#define MTK_TX_BASE_PTR2 (MTK_PDMA_OFFSET + 0x40)
-#define MTK_TX_MAX_CNT2 (MTK_PDMA_OFFSET + 0x44)
-#define MTK_TX_CTX_IDX2 (MTK_PDMA_OFFSET + 0x48)
-#define MTK_TX_DTX_IDX2 (MTK_PDMA_OFFSET + 0x4C)
-#define MTK_TX_BASE_PTR3 (MTK_PDMA_OFFSET + 0x50)
-#define MTK_TX_MAX_CNT3 (MTK_PDMA_OFFSET + 0x54)
-#define MTK_TX_CTX_IDX3 (MTK_PDMA_OFFSET + 0x58)
-#define MTK_TX_DTX_IDX3 (MTK_PDMA_OFFSET + 0x5C)
-#define MTK_RX_BASE_PTR1 (MTK_PDMA_OFFSET + 0x60)
-#define MTK_RX_MAX_CNT1 (MTK_PDMA_OFFSET + 0x64)
-#define MTK_RX_CALC_IDX1 (MTK_PDMA_OFFSET + 0x68)
-#define MTK_RX_DRX_IDX1 (MTK_PDMA_OFFSET + 0x6C)
-
-/* Switch DMA configuration */
-#define RT5350_SDM_CFG (RT5350_SDM_OFFSET + 0x00)
-#define RT5350_SDM_RRING (RT5350_SDM_OFFSET + 0x04)
-#define RT5350_SDM_TRING (RT5350_SDM_OFFSET + 0x08)
-#define RT5350_SDM_MAC_ADRL (RT5350_SDM_OFFSET + 0x0C)
-#define RT5350_SDM_MAC_ADRH (RT5350_SDM_OFFSET + 0x10)
-#define RT5350_SDM_TPCNT (RT5350_SDM_OFFSET + 0x100)
-#define RT5350_SDM_TBCNT (RT5350_SDM_OFFSET + 0x104)
-#define RT5350_SDM_RPCNT (RT5350_SDM_OFFSET + 0x108)
-#define RT5350_SDM_RBCNT (RT5350_SDM_OFFSET + 0x10C)
-#define RT5350_SDM_CS_ERR (RT5350_SDM_OFFSET + 0x110)
-
-#define RT5350_SDM_ICS_EN BIT(16)
-#define RT5350_SDM_TCS_EN BIT(17)
-#define RT5350_SDM_UCS_EN BIT(18)
-
-/* QDMA registers */
-#define MTK_QTX_CFG(x) (0x1800 + (x * 0x10))
-#define MTK_QTX_SCH(x) (0x1804 + (x * 0x10))
-#define MTK_QRX_BASE_PTR0 0x1900
-#define MTK_QRX_MAX_CNT0 0x1904
-#define MTK_QRX_CRX_IDX0 0x1908
-#define MTK_QRX_DRX_IDX0 0x190C
-#define MTK_QDMA_GLO_CFG 0x1A04
-#define MTK_QDMA_RST_IDX 0x1A08
-#define MTK_QDMA_DELAY_INT 0x1A0C
-#define MTK_QDMA_FC_THRES 0x1A10
-#define MTK_QMTK_INT_STATUS 0x1A18
-#define MTK_QMTK_INT_ENABLE 0x1A1C
-#define MTK_QDMA_HRED2 0x1A44
-
-#define MTK_QTX_CTX_PTR 0x1B00
-#define MTK_QTX_DTX_PTR 0x1B04
-
-#define MTK_QTX_CRX_PTR 0x1B10
-#define MTK_QTX_DRX_PTR 0x1B14
-
-#define MTK_QDMA_FQ_HEAD 0x1B20
-#define MTK_QDMA_FQ_TAIL 0x1B24
-#define MTK_QDMA_FQ_CNT 0x1B28
-#define MTK_QDMA_FQ_BLEN 0x1B2C
-
-#define QDMA_PAGE_SIZE 2048
-#define QDMA_TX_OWNER_CPU BIT(31)
-#define QDMA_TX_SWC BIT(14)
-#define TX_QDMA_SDL(_x) (((_x) & 0x3fff) << 16)
-#define QDMA_RES_THRES 4
-
-/* MDIO_CFG register bits */
-#define MTK_MDIO_CFG_AUTO_POLL_EN BIT(29)
-#define MTK_MDIO_CFG_GP1_BP_EN BIT(16)
-#define MTK_MDIO_CFG_GP1_FRC_EN BIT(15)
-#define MTK_MDIO_CFG_GP1_SPEED_10 (0 << 13)
-#define MTK_MDIO_CFG_GP1_SPEED_100 (1 << 13)
-#define MTK_MDIO_CFG_GP1_SPEED_1000 (2 << 13)
-#define MTK_MDIO_CFG_GP1_DUPLEX BIT(12)
-#define MTK_MDIO_CFG_GP1_FC_TX BIT(11)
-#define MTK_MDIO_CFG_GP1_FC_RX BIT(10)
-#define MTK_MDIO_CFG_GP1_LNK_DWN BIT(9)
-#define MTK_MDIO_CFG_GP1_AN_FAIL BIT(8)
-#define MTK_MDIO_CFG_MDC_CLK_DIV_1 (0 << 6)
-#define MTK_MDIO_CFG_MDC_CLK_DIV_2 (1 << 6)
-#define MTK_MDIO_CFG_MDC_CLK_DIV_4 (2 << 6)
-#define MTK_MDIO_CFG_MDC_CLK_DIV_8 (3 << 6)
-#define MTK_MDIO_CFG_TURBO_MII_FREQ BIT(5)
-#define MTK_MDIO_CFG_TURBO_MII_MODE BIT(4)
-#define MTK_MDIO_CFG_RX_CLK_SKEW_0 (0 << 2)
-#define MTK_MDIO_CFG_RX_CLK_SKEW_200 (1 << 2)
-#define MTK_MDIO_CFG_RX_CLK_SKEW_400 (2 << 2)
-#define MTK_MDIO_CFG_RX_CLK_SKEW_INV (3 << 2)
-#define MTK_MDIO_CFG_TX_CLK_SKEW_0 0
-#define MTK_MDIO_CFG_TX_CLK_SKEW_200 1
-#define MTK_MDIO_CFG_TX_CLK_SKEW_400 2
-#define MTK_MDIO_CFG_TX_CLK_SKEW_INV 3
-
-/* uni-cast port */
-#define MTK_GDM1_JMB_LEN_MASK 0xf
-#define MTK_GDM1_JMB_LEN_SHIFT 28
-#define MTK_GDM1_ICS_EN BIT(22)
-#define MTK_GDM1_TCS_EN BIT(21)
-#define MTK_GDM1_UCS_EN BIT(20)
-#define MTK_GDM1_JMB_EN BIT(19)
-#define MTK_GDM1_STRPCRC BIT(16)
-#define MTK_GDM1_UFRC_P_CPU (0 << 12)
-#define MTK_GDM1_UFRC_P_GDMA1 (1 << 12)
-#define MTK_GDM1_UFRC_P_PPE (6 << 12)
-
-/* checksums */
-#define MTK_ICS_GEN_EN BIT(2)
-#define MTK_UCS_GEN_EN BIT(1)
-#define MTK_TCS_GEN_EN BIT(0)
-
-/* dma mode */
-#define MTK_PDMA BIT(0)
-#define MTK_QDMA BIT(1)
-#define MTK_PDMA_RX_QDMA_TX (MTK_PDMA | MTK_QDMA)
-
-/* dma ring */
-#define MTK_PST_DRX_IDX0 BIT(16)
-#define MTK_PST_DTX_IDX3 BIT(3)
-#define MTK_PST_DTX_IDX2 BIT(2)
-#define MTK_PST_DTX_IDX1 BIT(1)
-#define MTK_PST_DTX_IDX0 BIT(0)
-
-#define MTK_RX_2B_OFFSET BIT(31)
-#define MTK_TX_WB_DDONE BIT(6)
-#define MTK_RX_DMA_BUSY BIT(3)
-#define MTK_TX_DMA_BUSY BIT(1)
-#define MTK_RX_DMA_EN BIT(2)
-#define MTK_TX_DMA_EN BIT(0)
-
-#define MTK_PDMA_SIZE_4DWORDS (0 << 4)
-#define MTK_PDMA_SIZE_8DWORDS (1 << 4)
-#define MTK_PDMA_SIZE_16DWORDS (2 << 4)
-
-#define MTK_US_CYC_CNT_MASK 0xff
-#define MTK_US_CYC_CNT_SHIFT 0x8
-#define MTK_US_CYC_CNT_DIVISOR 1000000
-
-/* PDMA descriptor rxd2 */
-#define RX_DMA_DONE BIT(31)
-#define RX_DMA_LSO BIT(30)
-#define RX_DMA_PLEN0(_x) (((_x) & 0x3fff) << 16)
-#define RX_DMA_GET_PLEN0(_x) (((_x) >> 16) & 0x3fff)
-#define RX_DMA_TAG BIT(15)
-
-/* PDMA descriptor rxd3 */
-#define RX_DMA_TPID(_x) (((_x) >> 16) & 0xffff)
-#define RX_DMA_VID(_x) ((_x) & 0xfff)
-
-/* PDMA descriptor rxd4 */
-#define RX_DMA_L4VALID BIT(30)
-#define RX_DMA_FPORT_SHIFT 19
-#define RX_DMA_FPORT_MASK 0x7
-
-struct mtk_rx_dma {
- unsigned int rxd1;
- unsigned int rxd2;
- unsigned int rxd3;
- unsigned int rxd4;
-} __packed __aligned(4);
-
-/* PDMA tx descriptor bits */
-#define TX_DMA_BUF_LEN 0x3fff
-#define TX_DMA_PLEN0_MASK (TX_DMA_BUF_LEN << 16)
-#define TX_DMA_PLEN0(_x) (((_x) & TX_DMA_BUF_LEN) << 16)
-#define TX_DMA_PLEN1(_x) ((_x) & TX_DMA_BUF_LEN)
-#define TX_DMA_GET_PLEN0(_x) (((_x) >> 16) & TX_DMA_BUF_LEN)
-#define TX_DMA_GET_PLEN1(_x) ((_x) & TX_DMA_BUF_LEN)
-#define TX_DMA_LS1 BIT(14)
-#define TX_DMA_LS0 BIT(30)
-#define TX_DMA_DONE BIT(31)
-#define TX_DMA_FPORT_SHIFT 25
-#define TX_DMA_FPORT_MASK 0x7
-#define TX_DMA_INS_VLAN_MT7621 BIT(16)
-#define TX_DMA_INS_VLAN BIT(7)
-#define TX_DMA_INS_PPPOE BIT(12)
-#define TX_DMA_TAG BIT(15)
-#define TX_DMA_TAG_MASK BIT(15)
-#define TX_DMA_QN(_x) ((_x) << 16)
-#define TX_DMA_PN(_x) ((_x) << 24)
-#define TX_DMA_QN_MASK TX_DMA_QN(0x7)
-#define TX_DMA_PN_MASK TX_DMA_PN(0x7)
-#define TX_DMA_UDF BIT(20)
-#define TX_DMA_CHKSUM (0x7 << 29)
-#define TX_DMA_TSO BIT(28)
-#define TX_DMA_DESP4_DEF (TX_DMA_QN(3) | TX_DMA_PN(1))
-
-/* frame engine counters */
-#define MTK_PPE_AC_BCNT0 (MTK_CMTABLE_OFFSET + 0x00)
-#define MTK_GDMA1_TX_GBCNT (MTK_CMTABLE_OFFSET + 0x300)
-#define MTK_GDMA2_TX_GBCNT (MTK_GDMA1_TX_GBCNT + 0x40)
-
-/* phy device flags */
-#define MTK_PHY_FLAG_PORT BIT(0)
-#define MTK_PHY_FLAG_ATTACH BIT(1)
-
-struct mtk_tx_dma {
- unsigned int txd1;
- unsigned int txd2;
- unsigned int txd3;
- unsigned int txd4;
-} __packed __aligned(4);
-
-struct mtk_eth;
-struct mtk_mac;
-
-/* manage the attached phys */
-struct mtk_phy {
- spinlock_t lock;
-
- struct phy_device *phy[8];
- struct device_node *phy_node[8];
- const __be32 *phy_fixed[8];
- int duplex[8];
- int speed[8];
- int tx_fc[8];
- int rx_fc[8];
- int (*connect)(struct mtk_mac *mac);
- void (*disconnect)(struct mtk_mac *mac);
- void (*start)(struct mtk_mac *mac);
- void (*stop)(struct mtk_mac *mac);
-};
-
-/* struct mtk_soc_data - the structure that holds the SoC specific data
- * @reg_table: Some of the legacy registers changed their location
- * over time. Their offsets are stored in this table
- *
- * @init_data: Some features depend on the silicon revision. This
- * callback allows runtime modification of the content of
- * this struct
- * @reset_fe: This callback is used to trigger the reset of the frame
- * engine
- * @set_mac: This callback is used to set the unicast mac address
- * filter
- * @fwd_config: This callback is used to setup the forward config
- * register of the MAC
- * @switch_init: This callback is used to bring up the switch core
- * @port_init: Some SoCs have ports that can be router to a switch port
- * or an external PHY. This callback is used to setup these
- * ports.
- * @has_carrier: This callback allows driver to check if there is a cable
- * attached.
- * @mdio_init: This callbck is used to setup the MDIO bus if one is
- * present
- * @mdio_cleanup: This callback is used to cleanup the MDIO state.
- * @mdio_write: This callback is used to write data to the MDIO bus.
- * @mdio_read: This callback is used to write data to the MDIO bus.
- * @mdio_adjust_link: This callback is used to apply the PHY settings.
- * @piac_offset: the PIAC register has a different different base offset
- * @hw_features: feature set depends on the SoC type
- * @dma_ring_size: allow GBit SoCs to set bigger rings than FE SoCs
- * @napi_weight: allow GBit SoCs to set bigger napi weight than FE SoCs
- * @dma_type: SoCs is PDMA, QDMA or a mix of the 2
- * @pdma_glo_cfg: the default DMA configuration
- * @rx_int: the TX interrupt bits used by the SoC
- * @tx_int: the TX interrupt bits used by the SoC
- * @status_int: the Status interrupt bits used by the SoC
- * @checksum_bit: the bits used to turn on HW checksumming
- * @txd4: default value of the TXD4 descriptor
- * @mac_count: the number of MACs that the SoC has
- * @new_stats: there is a old and new way to read hardware stats
- * registers
- * @jumbo_frame: does the SoC support jumbo frames ?
- * @rx_2b_offset: tell the rx dma to offset the data by 2 bytes
- * @rx_sg_dma: scatter gather support
- * @padding_64b enable 64 bit padding
- * @padding_bug: rt2880 has a padding bug
- * @has_switch: does the SoC have a built-in switch
- *
- * Although all of the supported SoCs share the same basic functionality, there
- * are several SoC specific functions and features that we need to support. This
- * struct holds the SoC specific data so that the common core can figure out
- * how to setup and use these differences.
- */
-struct mtk_soc_data {
- const u16 *reg_table;
-
- void (*init_data)(struct mtk_soc_data *data, struct net_device *netdev);
- void (*reset_fe)(struct mtk_eth *eth);
- void (*set_mac)(struct mtk_mac *mac, unsigned char *macaddr);
- int (*fwd_config)(struct mtk_eth *eth);
- int (*switch_init)(struct mtk_eth *eth);
- void (*port_init)(struct mtk_eth *eth, struct mtk_mac *mac,
- struct device_node *port);
- int (*has_carrier)(struct mtk_eth *eth);
- int (*mdio_init)(struct mtk_eth *eth);
- void (*mdio_cleanup)(struct mtk_eth *eth);
- int (*mdio_write)(struct mii_bus *bus, int phy_addr, int phy_reg,
- u16 val);
- int (*mdio_read)(struct mii_bus *bus, int phy_addr, int phy_reg);
- void (*mdio_adjust_link)(struct mtk_eth *eth, int port);
- u32 piac_offset;
- netdev_features_t hw_features;
- u32 dma_ring_size;
- u32 napi_weight;
- u32 dma_type;
- u32 pdma_glo_cfg;
- u32 rx_int;
- u32 tx_int;
- u32 status_int;
- u32 checksum_bit;
- u32 txd4;
- u32 mac_count;
-
- u32 new_stats:1;
- u32 jumbo_frame:1;
- u32 rx_2b_offset:1;
- u32 rx_sg_dma:1;
- u32 padding_64b:1;
- u32 padding_bug:1;
- u32 has_switch:1;
-};
-
-#define MTK_STAT_OFFSET 0x40
-
-/* struct mtk_hw_stats - the structure that holds the traffic statistics.
- * @stats_lock: make sure that stats operations are atomic
- * @reg_offset: the status register offset of the SoC
- * @syncp: the refcount
- *
- * All of the supported SoCs have hardware counters for traffic statstics.
- * Whenever the status IRQ triggers we can read the latest stats from these
- * counters and store them in this struct.
- */
-struct mtk_hw_stats {
- spinlock_t stats_lock;
- u32 reg_offset;
- struct u64_stats_sync syncp;
-
- u64 tx_bytes;
- u64 tx_packets;
- u64 tx_skip;
- u64 tx_collisions;
- u64 rx_bytes;
- u64 rx_packets;
- u64 rx_overflow;
- u64 rx_fcs_errors;
- u64 rx_short_errors;
- u64 rx_long_errors;
- u64 rx_checksum_errors;
- u64 rx_flow_control_packets;
-};
-
-/* PDMA descriptor can point at 1-2 segments. This enum allows us to track how
- * memory was allocated so that it can be freed properly
- */
-enum mtk_tx_flags {
- MTK_TX_FLAGS_SINGLE0 = 0x01,
- MTK_TX_FLAGS_PAGE0 = 0x02,
- MTK_TX_FLAGS_PAGE1 = 0x04,
-};
-
-/* struct mtk_tx_buf - This struct holds the pointers to the memory pointed at
- * by the TX descriptor s
- * @skb: The SKB pointer of the packet being sent
- * @dma_addr0: The base addr of the first segment
- * @dma_len0: The length of the first segment
- * @dma_addr1: The base addr of the second segment
- * @dma_len1: The length of the second segment
- */
-struct mtk_tx_buf {
- struct sk_buff *skb;
- u32 flags;
- DEFINE_DMA_UNMAP_ADDR(dma_addr0);
- DEFINE_DMA_UNMAP_LEN(dma_len0);
- DEFINE_DMA_UNMAP_ADDR(dma_addr1);
- DEFINE_DMA_UNMAP_LEN(dma_len1);
-};
-
-/* struct mtk_tx_ring - This struct holds info describing a TX ring
- * @tx_dma: The descriptor ring
- * @tx_buf: The memory pointed at by the ring
- * @tx_phys: The physical addr of tx_buf
- * @tx_next_free: Pointer to the next free descriptor
- * @tx_last_free: Pointer to the last free descriptor
- * @tx_thresh: The threshold of minimum amount of free descriptors
- * @tx_map: Callback to map a new packet into the ring
- * @tx_poll: Callback for the housekeeping function
- * @tx_clean: Callback for the cleanup function
- * @tx_ring_size: How many descriptors are in the ring
- * @tx_free_idx: The index of th next free descriptor
- * @tx_next_idx: QDMA uses a linked list. This element points to the next
- * free descriptor in the list
- * @tx_free_count: QDMA uses a linked list. Track how many free descriptors
- * are present
- */
-struct mtk_tx_ring {
- struct mtk_tx_dma *tx_dma;
- struct mtk_tx_buf *tx_buf;
- dma_addr_t tx_phys;
- struct mtk_tx_dma *tx_next_free;
- struct mtk_tx_dma *tx_last_free;
- u16 tx_thresh;
- int (*tx_map)(struct sk_buff *skb, struct net_device *dev, int tx_num,
- struct mtk_tx_ring *ring, bool gso);
- int (*tx_poll)(struct mtk_eth *eth, int budget, bool *tx_again);
- void (*tx_clean)(struct mtk_eth *eth);
-
- /* PDMA only */
- u16 tx_ring_size;
- u16 tx_free_idx;
-
- /* QDMA only */
- u16 tx_next_idx;
- atomic_t tx_free_count;
-};
-
-/* struct mtk_rx_ring - This struct holds info describing a RX ring
- * @rx_dma: The descriptor ring
- * @rx_data: The memory pointed at by the ring
- * @trx_phys: The physical addr of rx_buf
- * @rx_ring_size: How many descriptors are in the ring
- * @rx_buf_size: The size of each packet buffer
- * @rx_calc_idx: The current head of ring
- */
-struct mtk_rx_ring {
- struct mtk_rx_dma *rx_dma;
- u8 **rx_data;
- dma_addr_t rx_phys;
- u16 rx_ring_size;
- u16 frag_size;
- u16 rx_buf_size;
- u16 rx_calc_idx;
-};
-
-/* currently no SoC has more than 2 macs */
-#define MTK_MAX_DEVS 2
-
-/* struct mtk_eth - This is the main datasructure for holding the state
- * of the driver
- * @dev: The device pointer
- * @base: The mapped register i/o base
- * @page_lock: Make sure that register operations are atomic
- * @soc: pointer to our SoC specific data
- * @dummy_dev: we run 2 netdevs on 1 physical DMA ring and need a
- * dummy for NAPI to work
- * @netdev: The netdev instances
- * @mac: Each netdev is linked to a physical MAC
- * @switch_np: The phandle for the switch
- * @irq: The IRQ that we are using
- * @msg_enable: Ethtool msg level
- * @ysclk: The sysclk rate - neeed for calibration
- * @ethsys: The register map pointing at the range used to setup
- * MII modes
- * @dma_refcnt: track how many netdevs are using the DMA engine
- * @tx_ring: Pointer to the memore holding info about the TX ring
- * @rx_ring: Pointer to the memore holding info about the RX ring
- * @rx_napi: The NAPI struct
- * @scratch_ring: Newer SoCs need memory for a second HW managed TX ring
- * @scratch_head: The scratch memory that scratch_ring points to.
- * @phy: Info about the attached PHYs
- * @mii_bus: If there is a bus we need to create an instance for it
- * @link: Track if the ports have a physical link
- * @sw_priv: Pointer to the switches private data
- * @vlan_map: RX VID tracking
- */
-
-struct mtk_eth {
- struct device *dev;
- void __iomem *base;
- spinlock_t page_lock;
- struct mtk_soc_data *soc;
- struct net_device dummy_dev;
- struct net_device *netdev[MTK_MAX_DEVS];
- struct mtk_mac *mac[MTK_MAX_DEVS];
- struct device_node *switch_np;
- int irq;
- u32 msg_enable;
- unsigned long sysclk;
- struct regmap *ethsys;
- atomic_t dma_refcnt;
- struct mtk_tx_ring tx_ring;
- struct mtk_rx_ring rx_ring[2];
- struct napi_struct rx_napi;
- struct mtk_tx_dma *scratch_ring;
- void *scratch_head;
- struct mtk_phy *phy;
- struct mii_bus *mii_bus;
- int link[8];
- void *sw_priv;
- unsigned long vlan_map;
-};
-
-/* struct mtk_mac - the structure that holds the info about the MACs of the
- * SoC
- * @id: The number of the MAC
- * @of_node: Our devicetree node
- * @hw: Backpointer to our main datastruture
- * @hw_stats: Packet statistics counter
- * @phy_dev: The attached PHY if available
- * @phy_flags: The PHYs flags
- * @pending_work: The workqueue used to reset the dma ring
- */
-struct mtk_mac {
- int id;
- struct device_node *of_node;
- struct mtk_eth *hw;
- struct mtk_hw_stats *hw_stats;
- struct phy_device *phy_dev;
- u32 phy_flags;
- struct work_struct pending_work;
-};
-
-/* the struct describing the SoC. these are declared in the soc_xyz.c files */
-extern const struct of_device_id of_mtk_match[];
-
-/* read the hardware status register */
-void mtk_stats_update_mac(struct mtk_mac *mac);
-
-/* default checksum setup handler */
-void mtk_reset(struct mtk_eth *eth, u32 reset_bits);
-
-/* register i/o wrappers */
-void mtk_w32(struct mtk_eth *eth, u32 val, unsigned int reg);
-u32 mtk_r32(struct mtk_eth *eth, unsigned int reg);
-
-/* default clock calibration handler */
-int mtk_set_clock_cycle(struct mtk_eth *eth);
-
-/* default checksum setup handler */
-void mtk_csum_config(struct mtk_eth *eth);
-
-/* default forward config handler */
-void mtk_fwd_config(struct mtk_eth *eth);
-
-#endif /* MTK_ETH_H */
diff --git a/drivers/staging/mt7621-eth/soc_mt7621.c b/drivers/staging/mt7621-eth/soc_mt7621.c
deleted file mode 100644
index 5d63b5d96f6b..000000000000
--- a/drivers/staging/mt7621-eth/soc_mt7621.c
+++ /dev/null
@@ -1,161 +0,0 @@
-/* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * Copyright (C) 2009-2016 John Crispin <blogic@openwrt.org>
- * Copyright (C) 2009-2016 Felix Fietkau <nbd@openwrt.org>
- * Copyright (C) 2013-2016 Michael Lee <igvtee@gmail.com>
- */
-
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/if_vlan.h>
-#include <linux/of_net.h>
-
-#include <asm/mach-ralink/ralink_regs.h>
-
-#include "mtk_eth_soc.h"
-#include "gsw_mt7620.h"
-#include "mdio.h"
-
-#define MT7620_CDMA_CSG_CFG 0x400
-#define MT7621_CDMP_IG_CTRL (MT7620_CDMA_CSG_CFG + 0x00)
-#define MT7621_CDMP_EG_CTRL (MT7620_CDMA_CSG_CFG + 0x04)
-#define MT7621_RESET_FE BIT(6)
-#define MT7621_L4_VALID BIT(24)
-
-#define MT7621_TX_DMA_UDF BIT(19)
-
-#define CDMA_ICS_EN BIT(2)
-#define CDMA_UCS_EN BIT(1)
-#define CDMA_TCS_EN BIT(0)
-
-#define GDMA_ICS_EN BIT(22)
-#define GDMA_TCS_EN BIT(21)
-#define GDMA_UCS_EN BIT(20)
-
-/* frame engine counters */
-#define MT7621_REG_MIB_OFFSET 0x2000
-#define MT7621_PPE_AC_BCNT0 (MT7621_REG_MIB_OFFSET + 0x00)
-#define MT7621_GDM1_TX_GBCNT (MT7621_REG_MIB_OFFSET + 0x400)
-#define MT7621_GDM2_TX_GBCNT (MT7621_GDM1_TX_GBCNT + 0x40)
-
-#define GSW_REG_GDMA1_MAC_ADRL 0x508
-#define GSW_REG_GDMA1_MAC_ADRH 0x50C
-#define GSW_REG_GDMA2_MAC_ADRL 0x1508
-#define GSW_REG_GDMA2_MAC_ADRH 0x150C
-
-#define MT7621_MTK_RST_GL 0x04
-#define MT7620_MTK_INT_STATUS2 0x08
-
-/* MTK_INT_STATUS reg on mt7620 define CNT_GDM1_AF at BIT(29)
- * but after test it should be BIT(13).
- */
-#define MT7621_MTK_GDM1_AF BIT(28)
-#define MT7621_MTK_GDM2_AF BIT(29)
-
-static const u16 mt7621_reg_table[MTK_REG_COUNT] = {
- [MTK_REG_PDMA_GLO_CFG] = RT5350_PDMA_GLO_CFG,
- [MTK_REG_PDMA_RST_CFG] = RT5350_PDMA_RST_CFG,
- [MTK_REG_DLY_INT_CFG] = RT5350_DLY_INT_CFG,
- [MTK_REG_TX_BASE_PTR0] = RT5350_TX_BASE_PTR0,
- [MTK_REG_TX_MAX_CNT0] = RT5350_TX_MAX_CNT0,
- [MTK_REG_TX_CTX_IDX0] = RT5350_TX_CTX_IDX0,
- [MTK_REG_TX_DTX_IDX0] = RT5350_TX_DTX_IDX0,
- [MTK_REG_RX_BASE_PTR0] = RT5350_RX_BASE_PTR0,
- [MTK_REG_RX_MAX_CNT0] = RT5350_RX_MAX_CNT0,
- [MTK_REG_RX_CALC_IDX0] = RT5350_RX_CALC_IDX0,
- [MTK_REG_RX_DRX_IDX0] = RT5350_RX_DRX_IDX0,
- [MTK_REG_MTK_INT_ENABLE] = RT5350_MTK_INT_ENABLE,
- [MTK_REG_MTK_INT_STATUS] = RT5350_MTK_INT_STATUS,
- [MTK_REG_MTK_DMA_VID_BASE] = 0,
- [MTK_REG_MTK_COUNTER_BASE] = MT7621_GDM1_TX_GBCNT,
- [MTK_REG_MTK_RST_GL] = MT7621_MTK_RST_GL,
- [MTK_REG_MTK_INT_STATUS2] = MT7620_MTK_INT_STATUS2,
-};
-
-static void mt7621_mtk_reset(struct mtk_eth *eth)
-{
- mtk_reset(eth, MT7621_RESET_FE);
-}
-
-static int mt7621_fwd_config(struct mtk_eth *eth)
-{
- /* Setup GMAC1 only, there is no support for GMAC2 yet */
- mtk_w32(eth, mtk_r32(eth, MT7620_GDMA1_FWD_CFG) & ~0xffff,
- MT7620_GDMA1_FWD_CFG);
-
- /* Enable RX checksum */
- mtk_w32(eth, mtk_r32(eth, MT7620_GDMA1_FWD_CFG) | (GDMA_ICS_EN |
- GDMA_TCS_EN | GDMA_UCS_EN),
- MT7620_GDMA1_FWD_CFG);
-
- /* Enable RX VLan Offloading */
- mtk_w32(eth, 0, MT7621_CDMP_EG_CTRL);
-
- return 0;
-}
-
-static void mt7621_set_mac(struct mtk_mac *mac, unsigned char *hwaddr)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&mac->hw->page_lock, flags);
- if (mac->id == 0) {
- mtk_w32(mac->hw, (hwaddr[0] << 8) | hwaddr[1],
- GSW_REG_GDMA1_MAC_ADRH);
- mtk_w32(mac->hw, (hwaddr[2] << 24) | (hwaddr[3] << 16) |
- (hwaddr[4] << 8) | hwaddr[5],
- GSW_REG_GDMA1_MAC_ADRL);
- }
- if (mac->id == 1) {
- mtk_w32(mac->hw, (hwaddr[0] << 8) | hwaddr[1],
- GSW_REG_GDMA2_MAC_ADRH);
- mtk_w32(mac->hw, (hwaddr[2] << 24) | (hwaddr[3] << 16) |
- (hwaddr[4] << 8) | hwaddr[5],
- GSW_REG_GDMA2_MAC_ADRL);
- }
- spin_unlock_irqrestore(&mac->hw->page_lock, flags);
-}
-
-static struct mtk_soc_data mt7621_data = {
- .hw_features = NETIF_F_IP_CSUM | NETIF_F_RXCSUM |
- NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX |
- NETIF_F_SG | NETIF_F_TSO | NETIF_F_TSO6 |
- NETIF_F_IPV6_CSUM,
- .dma_type = MTK_PDMA,
- .dma_ring_size = 256,
- .napi_weight = 64,
- .new_stats = 1,
- .padding_64b = 1,
- .rx_2b_offset = 1,
- .rx_sg_dma = 1,
- .has_switch = 1,
- .mac_count = 2,
- .reset_fe = mt7621_mtk_reset,
- .set_mac = mt7621_set_mac,
- .fwd_config = mt7621_fwd_config,
- .switch_init = mtk_gsw_init,
- .reg_table = mt7621_reg_table,
- .pdma_glo_cfg = MTK_PDMA_SIZE_16DWORDS,
- .rx_int = RT5350_RX_DONE_INT,
- .tx_int = RT5350_TX_DONE_INT,
- .status_int = MT7621_MTK_GDM1_AF | MT7621_MTK_GDM2_AF,
- .checksum_bit = MT7621_L4_VALID,
- .has_carrier = mt7620_has_carrier,
- .mdio_read = mt7620_mdio_read,
- .mdio_write = mt7620_mdio_write,
- .mdio_adjust_link = mt7620_mdio_link_adjust,
-};
-
-const struct of_device_id of_mtk_match[] = {
- { .compatible = "mediatek,mt7621-eth", .data = &mt7621_data },
- {},
-};
-
-MODULE_DEVICE_TABLE(of, of_mtk_match);
diff --git a/drivers/staging/mt7621-pci/Kconfig b/drivers/staging/mt7621-pci/Kconfig
index d33533872a16..c8fa17cfa807 100644
--- a/drivers/staging/mt7621-pci/Kconfig
+++ b/drivers/staging/mt7621-pci/Kconfig
@@ -1,6 +1,7 @@
config PCI_MT7621
tristate "MediaTek MT7621 PCI Controller"
depends on RALINK
+ depends on PCI
select PCI_DRIVERS_GENERIC
help
This selects a driver for the MediaTek MT7621 PCI Controller.
diff --git a/drivers/staging/octeon/ethernet-mdio.c b/drivers/staging/octeon/ethernet-mdio.c
index d6248eecf123..2aee64fdaec5 100644
--- a/drivers/staging/octeon/ethernet-mdio.c
+++ b/drivers/staging/octeon/ethernet-mdio.c
@@ -163,7 +163,7 @@ int cvm_oct_phy_setup_device(struct net_device *dev)
goto no_phy;
phydev = of_phy_connect(dev, phy_node, cvm_oct_adjust_link, 0,
- PHY_INTERFACE_MODE_GMII);
+ priv->phy_mode);
of_node_put(phy_node);
if (!phydev)
diff --git a/drivers/staging/octeon/ethernet.c b/drivers/staging/octeon/ethernet.c
index ce61c5670ef6..986db76705cc 100644
--- a/drivers/staging/octeon/ethernet.c
+++ b/drivers/staging/octeon/ethernet.c
@@ -653,14 +653,37 @@ static struct device_node *cvm_oct_node_for_port(struct device_node *pip,
return np;
}
-static void cvm_set_rgmii_delay(struct device_node *np, int iface, int port)
+static void cvm_set_rgmii_delay(struct octeon_ethernet *priv, int iface,
+ int port)
{
+ struct device_node *np = priv->of_node;
u32 delay_value;
+ bool rx_delay;
+ bool tx_delay;
- if (!of_property_read_u32(np, "rx-delay", &delay_value))
+ /* By default, both RX/TX delay is enabled in
+ * __cvmx_helper_rgmii_enable().
+ */
+ rx_delay = true;
+ tx_delay = true;
+
+ if (!of_property_read_u32(np, "rx-delay", &delay_value)) {
cvmx_write_csr(CVMX_ASXX_RX_CLK_SETX(port, iface), delay_value);
- if (!of_property_read_u32(np, "tx-delay", &delay_value))
+ rx_delay = delay_value > 0;
+ }
+ if (!of_property_read_u32(np, "tx-delay", &delay_value)) {
cvmx_write_csr(CVMX_ASXX_TX_CLK_SETX(port, iface), delay_value);
+ tx_delay = delay_value > 0;
+ }
+
+ if (!rx_delay && !tx_delay)
+ priv->phy_mode = PHY_INTERFACE_MODE_RGMII_ID;
+ else if (!rx_delay)
+ priv->phy_mode = PHY_INTERFACE_MODE_RGMII_RXID;
+ else if (!tx_delay)
+ priv->phy_mode = PHY_INTERFACE_MODE_RGMII_TXID;
+ else
+ priv->phy_mode = PHY_INTERFACE_MODE_RGMII;
}
static int cvm_oct_probe(struct platform_device *pdev)
@@ -825,6 +848,7 @@ static int cvm_oct_probe(struct platform_device *pdev)
priv->port = port;
priv->queue = cvmx_pko_get_base_queue(priv->port);
priv->fau = fau - cvmx_pko_get_num_queues(port) * 4;
+ priv->phy_mode = PHY_INTERFACE_MODE_NA;
for (qos = 0; qos < 16; qos++)
skb_queue_head_init(&priv->tx_free_list[qos]);
for (qos = 0; qos < cvmx_pko_get_num_queues(port);
@@ -856,6 +880,7 @@ static int cvm_oct_probe(struct platform_device *pdev)
break;
case CVMX_HELPER_INTERFACE_MODE_SGMII:
+ priv->phy_mode = PHY_INTERFACE_MODE_SGMII;
dev->netdev_ops = &cvm_oct_sgmii_netdev_ops;
strcpy(dev->name, "eth%d");
break;
@@ -865,11 +890,16 @@ static int cvm_oct_probe(struct platform_device *pdev)
strcpy(dev->name, "spi%d");
break;
- case CVMX_HELPER_INTERFACE_MODE_RGMII:
case CVMX_HELPER_INTERFACE_MODE_GMII:
+ priv->phy_mode = PHY_INTERFACE_MODE_GMII;
+ dev->netdev_ops = &cvm_oct_rgmii_netdev_ops;
+ strcpy(dev->name, "eth%d");
+ break;
+
+ case CVMX_HELPER_INTERFACE_MODE_RGMII:
dev->netdev_ops = &cvm_oct_rgmii_netdev_ops;
strcpy(dev->name, "eth%d");
- cvm_set_rgmii_delay(priv->of_node, interface,
+ cvm_set_rgmii_delay(priv, interface,
port_index);
break;
}
diff --git a/drivers/staging/octeon/octeon-ethernet.h b/drivers/staging/octeon/octeon-ethernet.h
index 4a07e7f43d12..be570d33685a 100644
--- a/drivers/staging/octeon/octeon-ethernet.h
+++ b/drivers/staging/octeon/octeon-ethernet.h
@@ -12,7 +12,7 @@
#define OCTEON_ETHERNET_H
#include <linux/of.h>
-
+#include <linux/phy.h>
#include <asm/octeon/cvmx-helper-board.h>
/**
@@ -33,6 +33,8 @@ struct octeon_ethernet {
* cvmx_helper_interface_mode_t
*/
int imode;
+ /* PHY mode */
+ phy_interface_t phy_mode;
/* List of outstanding tx buffers per queue */
struct sk_buff_head tx_free_list[16];
unsigned int last_speed;
diff --git a/drivers/staging/olpc_dcon/olpc_dcon_xo_1.c b/drivers/staging/olpc_dcon/olpc_dcon_xo_1.c
index 80b8d4153414..a54286498a47 100644
--- a/drivers/staging/olpc_dcon/olpc_dcon_xo_1.c
+++ b/drivers/staging/olpc_dcon/olpc_dcon_xo_1.c
@@ -45,7 +45,7 @@ static int dcon_init_xo_1(struct dcon_priv *dcon)
{
unsigned char lob;
int ret, i;
- struct dcon_gpio *pin = &gpios_asis[0];
+ const struct dcon_gpio *pin = &gpios_asis[0];
for (i = 0; i < ARRAY_SIZE(gpios_asis); i++) {
gpios[i] = devm_gpiod_get(&dcon->client->dev, pin[i].name,
diff --git a/drivers/staging/rtl8188eu/core/rtw_xmit.c b/drivers/staging/rtl8188eu/core/rtw_xmit.c
index 1723a47a96b4..952f2ab51347 100644
--- a/drivers/staging/rtl8188eu/core/rtw_xmit.c
+++ b/drivers/staging/rtl8188eu/core/rtw_xmit.c
@@ -174,7 +174,9 @@ s32 _rtw_init_xmit_priv(struct xmit_priv *pxmitpriv, struct adapter *padapter)
pxmitpriv->free_xmit_extbuf_cnt = num_xmit_extbuf;
- rtw_alloc_hwxmits(padapter);
+ res = rtw_alloc_hwxmits(padapter);
+ if (res == _FAIL)
+ goto exit;
rtw_init_hwxmits(pxmitpriv->hwxmits, pxmitpriv->hwxmit_entry);
for (i = 0; i < 4; i++)
@@ -1503,7 +1505,7 @@ exit:
return res;
}
-void rtw_alloc_hwxmits(struct adapter *padapter)
+s32 rtw_alloc_hwxmits(struct adapter *padapter)
{
struct hw_xmit *hwxmits;
struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
@@ -1512,6 +1514,8 @@ void rtw_alloc_hwxmits(struct adapter *padapter)
pxmitpriv->hwxmits = kcalloc(pxmitpriv->hwxmit_entry,
sizeof(struct hw_xmit), GFP_KERNEL);
+ if (!pxmitpriv->hwxmits)
+ return _FAIL;
hwxmits = pxmitpriv->hwxmits;
@@ -1519,6 +1523,7 @@ void rtw_alloc_hwxmits(struct adapter *padapter)
hwxmits[1] .sta_queue = &pxmitpriv->vi_pending;
hwxmits[2] .sta_queue = &pxmitpriv->be_pending;
hwxmits[3] .sta_queue = &pxmitpriv->bk_pending;
+ return _SUCCESS;
}
void rtw_free_hwxmits(struct adapter *padapter)
diff --git a/drivers/staging/rtl8188eu/include/rtw_xmit.h b/drivers/staging/rtl8188eu/include/rtw_xmit.h
index 788f59c74ea1..ba7e15fbde72 100644
--- a/drivers/staging/rtl8188eu/include/rtw_xmit.h
+++ b/drivers/staging/rtl8188eu/include/rtw_xmit.h
@@ -336,7 +336,7 @@ s32 rtw_txframes_sta_ac_pending(struct adapter *padapter,
void rtw_init_hwxmits(struct hw_xmit *phwxmit, int entry);
s32 _rtw_init_xmit_priv(struct xmit_priv *pxmitpriv, struct adapter *padapter);
void _rtw_free_xmit_priv(struct xmit_priv *pxmitpriv);
-void rtw_alloc_hwxmits(struct adapter *padapter);
+s32 rtw_alloc_hwxmits(struct adapter *padapter);
void rtw_free_hwxmits(struct adapter *padapter);
s32 rtw_xmit(struct adapter *padapter, struct sk_buff **pkt);
diff --git a/drivers/staging/rtl8712/rtl8712_cmd.c b/drivers/staging/rtl8712/rtl8712_cmd.c
index 1920d02f7c9f..8c36acedf507 100644
--- a/drivers/staging/rtl8712/rtl8712_cmd.c
+++ b/drivers/staging/rtl8712/rtl8712_cmd.c
@@ -147,17 +147,9 @@ static u8 write_macreg_hdl(struct _adapter *padapter, u8 *pbuf)
static u8 read_bbreg_hdl(struct _adapter *padapter, u8 *pbuf)
{
- u32 val;
- void (*pcmd_callback)(struct _adapter *dev, struct cmd_obj *pcmd);
struct cmd_obj *pcmd = (struct cmd_obj *)pbuf;
- if (pcmd->rsp && pcmd->rspsz > 0)
- memcpy(pcmd->rsp, (u8 *)&val, pcmd->rspsz);
- pcmd_callback = cmd_callback[pcmd->cmdcode].callback;
- if (!pcmd_callback)
- r8712_free_cmd_obj(pcmd);
- else
- pcmd_callback(padapter, pcmd);
+ r8712_free_cmd_obj(pcmd);
return H2C_SUCCESS;
}
diff --git a/drivers/staging/rtl8712/rtl8712_cmd.h b/drivers/staging/rtl8712/rtl8712_cmd.h
index 92fb77666d44..1ef86b8c592f 100644
--- a/drivers/staging/rtl8712/rtl8712_cmd.h
+++ b/drivers/staging/rtl8712/rtl8712_cmd.h
@@ -140,7 +140,7 @@ enum rtl8712_h2c_cmd {
static struct _cmd_callback cmd_callback[] = {
{GEN_CMD_CODE(_Read_MACREG), NULL}, /*0*/
{GEN_CMD_CODE(_Write_MACREG), NULL},
- {GEN_CMD_CODE(_Read_BBREG), &r8712_getbbrfreg_cmdrsp_callback},
+ {GEN_CMD_CODE(_Read_BBREG), NULL},
{GEN_CMD_CODE(_Write_BBREG), NULL},
{GEN_CMD_CODE(_Read_RFREG), &r8712_getbbrfreg_cmdrsp_callback},
{GEN_CMD_CODE(_Write_RFREG), NULL}, /*5*/
diff --git a/drivers/staging/rtl8723bs/core/rtw_xmit.c b/drivers/staging/rtl8723bs/core/rtw_xmit.c
index 094d61bcb469..b87f13a0b563 100644
--- a/drivers/staging/rtl8723bs/core/rtw_xmit.c
+++ b/drivers/staging/rtl8723bs/core/rtw_xmit.c
@@ -260,7 +260,9 @@ s32 _rtw_init_xmit_priv(struct xmit_priv *pxmitpriv, struct adapter *padapter)
}
}
- rtw_alloc_hwxmits(padapter);
+ res = rtw_alloc_hwxmits(padapter);
+ if (res == _FAIL)
+ goto exit;
rtw_init_hwxmits(pxmitpriv->hwxmits, pxmitpriv->hwxmit_entry);
for (i = 0; i < 4; i++) {
@@ -2144,7 +2146,7 @@ exit:
return res;
}
-void rtw_alloc_hwxmits(struct adapter *padapter)
+s32 rtw_alloc_hwxmits(struct adapter *padapter)
{
struct hw_xmit *hwxmits;
struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
@@ -2155,10 +2157,8 @@ void rtw_alloc_hwxmits(struct adapter *padapter)
pxmitpriv->hwxmits = rtw_zmalloc(sizeof(struct hw_xmit) * pxmitpriv->hwxmit_entry);
- if (pxmitpriv->hwxmits == NULL) {
- DBG_871X("alloc hwxmits fail!...\n");
- return;
- }
+ if (!pxmitpriv->hwxmits)
+ return _FAIL;
hwxmits = pxmitpriv->hwxmits;
@@ -2204,7 +2204,7 @@ void rtw_alloc_hwxmits(struct adapter *padapter)
}
-
+ return _SUCCESS;
}
void rtw_free_hwxmits(struct adapter *padapter)
diff --git a/drivers/staging/rtl8723bs/include/rtw_xmit.h b/drivers/staging/rtl8723bs/include/rtw_xmit.h
index 1b38b9182b31..37f42b2f22f1 100644
--- a/drivers/staging/rtl8723bs/include/rtw_xmit.h
+++ b/drivers/staging/rtl8723bs/include/rtw_xmit.h
@@ -487,7 +487,7 @@ s32 _rtw_init_xmit_priv(struct xmit_priv *pxmitpriv, struct adapter *padapter);
void _rtw_free_xmit_priv (struct xmit_priv *pxmitpriv);
-void rtw_alloc_hwxmits(struct adapter *padapter);
+s32 rtw_alloc_hwxmits(struct adapter *padapter);
void rtw_free_hwxmits(struct adapter *padapter);
diff --git a/drivers/staging/rtlwifi/phydm/rtl_phydm.c b/drivers/staging/rtlwifi/phydm/rtl_phydm.c
index 9930ed954abb..4cc77b2016e1 100644
--- a/drivers/staging/rtlwifi/phydm/rtl_phydm.c
+++ b/drivers/staging/rtlwifi/phydm/rtl_phydm.c
@@ -180,6 +180,8 @@ static int rtl_phydm_init_priv(struct rtl_priv *rtlpriv,
rtlpriv->phydm.internal =
kzalloc(sizeof(struct phy_dm_struct), GFP_KERNEL);
+ if (!rtlpriv->phydm.internal)
+ return 0;
_rtl_phydm_init_com_info(rtlpriv, ic, params);
diff --git a/drivers/staging/rtlwifi/rtl8822be/fw.c b/drivers/staging/rtlwifi/rtl8822be/fw.c
index f061dd1382aa..cf6b7a80b753 100644
--- a/drivers/staging/rtlwifi/rtl8822be/fw.c
+++ b/drivers/staging/rtlwifi/rtl8822be/fw.c
@@ -743,6 +743,8 @@ void rtl8822be_set_fw_rsvdpagepkt(struct ieee80211_hw *hw, bool b_dl_finished)
u1_rsvd_page_loc, 3);
skb = dev_alloc_skb(totalpacketlen);
+ if (!skb)
+ return;
memcpy((u8 *)skb_put(skb, totalpacketlen), &reserved_page_packet,
totalpacketlen);
diff --git a/drivers/staging/speakup/speakup_soft.c b/drivers/staging/speakup/speakup_soft.c
index edff6ce85655..9d85a3a1af4c 100644
--- a/drivers/staging/speakup/speakup_soft.c
+++ b/drivers/staging/speakup/speakup_soft.c
@@ -210,12 +210,15 @@ static ssize_t softsynthx_read(struct file *fp, char __user *buf, size_t count,
return -EINVAL;
spin_lock_irqsave(&speakup_info.spinlock, flags);
+ synth_soft.alive = 1;
while (1) {
prepare_to_wait(&speakup_event, &wait, TASK_INTERRUPTIBLE);
- if (!unicode)
- synth_buffer_skip_nonlatin1();
- if (!synth_buffer_empty() || speakup_info.flushing)
- break;
+ if (synth_current() == &synth_soft) {
+ if (!unicode)
+ synth_buffer_skip_nonlatin1();
+ if (!synth_buffer_empty() || speakup_info.flushing)
+ break;
+ }
spin_unlock_irqrestore(&speakup_info.spinlock, flags);
if (fp->f_flags & O_NONBLOCK) {
finish_wait(&speakup_event, &wait);
@@ -235,6 +238,8 @@ static ssize_t softsynthx_read(struct file *fp, char __user *buf, size_t count,
/* Keep 3 bytes available for a 16bit UTF-8-encoded character */
while (chars_sent <= count - bytes_per_ch) {
+ if (synth_current() != &synth_soft)
+ break;
if (speakup_info.flushing) {
speakup_info.flushing = 0;
ch = '\x18';
@@ -331,7 +336,8 @@ static __poll_t softsynth_poll(struct file *fp, struct poll_table_struct *wait)
poll_wait(fp, &speakup_event, wait);
spin_lock_irqsave(&speakup_info.spinlock, flags);
- if (!synth_buffer_empty() || speakup_info.flushing)
+ if (synth_current() == &synth_soft &&
+ (!synth_buffer_empty() || speakup_info.flushing))
ret = EPOLLIN | EPOLLRDNORM;
spin_unlock_irqrestore(&speakup_info.spinlock, flags);
return ret;
diff --git a/drivers/staging/speakup/spk_priv.h b/drivers/staging/speakup/spk_priv.h
index c8e688878fc7..ac6a74883af4 100644
--- a/drivers/staging/speakup/spk_priv.h
+++ b/drivers/staging/speakup/spk_priv.h
@@ -74,6 +74,7 @@ int synth_request_region(unsigned long start, unsigned long n);
int synth_release_region(unsigned long start, unsigned long n);
int synth_add(struct spk_synth *in_synth);
void synth_remove(struct spk_synth *in_synth);
+struct spk_synth *synth_current(void);
extern struct speakup_info_t speakup_info;
diff --git a/drivers/staging/speakup/synth.c b/drivers/staging/speakup/synth.c
index 25f259ee4ffc..3568bfb89912 100644
--- a/drivers/staging/speakup/synth.c
+++ b/drivers/staging/speakup/synth.c
@@ -481,4 +481,10 @@ void synth_remove(struct spk_synth *in_synth)
}
EXPORT_SYMBOL_GPL(synth_remove);
+struct spk_synth *synth_current(void)
+{
+ return synth;
+}
+EXPORT_SYMBOL_GPL(synth_current);
+
short spk_punc_masks[] = { 0, SOME, MOST, PUNC, PUNC | B_SYM };
diff --git a/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c b/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c
index 611a6ee2943a..7c6cf41645eb 100644
--- a/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c
+++ b/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c
@@ -1370,10 +1370,6 @@ static int vidioc_g_parm(struct file *file, void *priv,
return 0;
}
-#define FRACT_CMP(a, OP, b) \
- ((u64)(a).numerator * (b).denominator OP \
- (u64)(b).numerator * (a).denominator)
-
static int vidioc_s_parm(struct file *file, void *priv,
struct v4l2_streamparm *parm)
{
@@ -1387,8 +1383,8 @@ static int vidioc_s_parm(struct file *file, void *priv,
/* tpf: {*, 0} resets timing; clip to [min, max]*/
tpf = tpf.denominator ? tpf : tpf_default;
- tpf = FRACT_CMP(tpf, <, tpf_min) ? tpf_min : tpf;
- tpf = FRACT_CMP(tpf, >, tpf_max) ? tpf_max : tpf;
+ tpf = V4L2_FRACT_COMPARE(tpf, <, tpf_min) ? tpf_min : tpf;
+ tpf = V4L2_FRACT_COMPARE(tpf, >, tpf_max) ? tpf_max : tpf;
dev->capture.timeperframe = tpf;
parm->parm.capture.timeperframe = tpf;
diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c
index 804daf83be35..064d0db4c51e 100644
--- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c
+++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c
@@ -3513,6 +3513,7 @@ static int vchiq_probe(struct platform_device *pdev)
struct device_node *fw_node;
const struct of_device_id *of_id;
struct vchiq_drvdata *drvdata;
+ struct device *vchiq_dev;
int err;
of_id = of_match_node(vchiq_of_match, pdev->dev.of_node);
@@ -3547,9 +3548,12 @@ static int vchiq_probe(struct platform_device *pdev)
goto failed_platform_init;
}
- if (IS_ERR(device_create(vchiq_class, &pdev->dev, vchiq_devid,
- NULL, "vchiq")))
+ vchiq_dev = device_create(vchiq_class, &pdev->dev, vchiq_devid, NULL,
+ "vchiq");
+ if (IS_ERR(vchiq_dev)) {
+ err = PTR_ERR(vchiq_dev);
goto failed_device_create;
+ }
vchiq_debugfs_init();
diff --git a/drivers/staging/vt6655/device_main.c b/drivers/staging/vt6655/device_main.c
index b370985b58a1..c6bb4aaf9bd0 100644
--- a/drivers/staging/vt6655/device_main.c
+++ b/drivers/staging/vt6655/device_main.c
@@ -1033,8 +1033,6 @@ static void vnt_interrupt_process(struct vnt_private *priv)
return;
}
- MACvIntDisable(priv->PortOffset);
-
spin_lock_irqsave(&priv->lock, flags);
/* Read low level stats */
@@ -1122,8 +1120,6 @@ static void vnt_interrupt_process(struct vnt_private *priv)
}
spin_unlock_irqrestore(&priv->lock, flags);
-
- MACvIntEnable(priv->PortOffset, IMR_MASK_VALUE);
}
static void vnt_interrupt_work(struct work_struct *work)
@@ -1133,14 +1129,17 @@ static void vnt_interrupt_work(struct work_struct *work)
if (priv->vif)
vnt_interrupt_process(priv);
+
+ MACvIntEnable(priv->PortOffset, IMR_MASK_VALUE);
}
static irqreturn_t vnt_interrupt(int irq, void *arg)
{
struct vnt_private *priv = arg;
- if (priv->vif)
- schedule_work(&priv->interrupt_work);
+ schedule_work(&priv->interrupt_work);
+
+ MACvIntDisable(priv->PortOffset);
return IRQ_HANDLED;
}
diff --git a/drivers/target/iscsi/cxgbit/cxgbit.h b/drivers/target/iscsi/cxgbit/cxgbit.h
index 417b9e66b0cd..3cca22e19964 100644
--- a/drivers/target/iscsi/cxgbit/cxgbit.h
+++ b/drivers/target/iscsi/cxgbit/cxgbit.h
@@ -345,7 +345,7 @@ struct cxgbit_device *cxgbit_find_device(struct net_device *, u8 *);
int cxgbit_ddp_init(struct cxgbit_device *);
int cxgbit_setup_conn_pgidx(struct cxgbit_sock *, u32);
int cxgbit_reserve_ttt(struct cxgbit_sock *, struct iscsi_cmd *);
-void cxgbit_release_cmd(struct iscsi_conn *, struct iscsi_cmd *);
+void cxgbit_unmap_cmd(struct iscsi_conn *, struct iscsi_cmd *);
static inline
struct cxgbi_ppm *cdev2ppm(struct cxgbit_device *cdev)
diff --git a/drivers/target/iscsi/cxgbit/cxgbit_ddp.c b/drivers/target/iscsi/cxgbit/cxgbit_ddp.c
index 76a262674c8d..d57fd3ed3fa5 100644
--- a/drivers/target/iscsi/cxgbit/cxgbit_ddp.c
+++ b/drivers/target/iscsi/cxgbit/cxgbit_ddp.c
@@ -263,7 +263,7 @@ out:
r2t->targ_xfer_tag = ttinfo->tag;
}
-void cxgbit_release_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd)
+void cxgbit_unmap_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd)
{
struct cxgbit_cmd *ccmd = iscsit_priv_cmd(cmd);
diff --git a/drivers/target/iscsi/cxgbit/cxgbit_main.c b/drivers/target/iscsi/cxgbit/cxgbit_main.c
index c011c826fc26..4a7bb0b49d17 100644
--- a/drivers/target/iscsi/cxgbit/cxgbit_main.c
+++ b/drivers/target/iscsi/cxgbit/cxgbit_main.c
@@ -678,7 +678,7 @@ static struct iscsit_transport cxgbit_transport = {
.iscsit_get_r2t_ttt = cxgbit_get_r2t_ttt,
.iscsit_get_rx_pdu = cxgbit_get_rx_pdu,
.iscsit_validate_params = cxgbit_validate_params,
- .iscsit_release_cmd = cxgbit_release_cmd,
+ .iscsit_unmap_cmd = cxgbit_unmap_cmd,
.iscsit_aborted_task = iscsit_aborted_task,
.iscsit_get_sup_prot_ops = cxgbit_get_sup_prot_ops,
};
diff --git a/drivers/target/iscsi/cxgbit/cxgbit_target.c b/drivers/target/iscsi/cxgbit/cxgbit_target.c
index 25eb3891e34b..29b350a0b58f 100644
--- a/drivers/target/iscsi/cxgbit/cxgbit_target.c
+++ b/drivers/target/iscsi/cxgbit/cxgbit_target.c
@@ -960,7 +960,7 @@ after_immediate_data:
target_put_sess_cmd(&cmd->se_cmd);
return 0;
} else if (cmd->unsolicited_data) {
- iscsit_set_unsoliticed_dataout(cmd);
+ iscsit_set_unsolicited_dataout(cmd);
}
} else if (immed_ret == IMMEDIATE_DATA_ERL1_CRC_FAILURE) {
diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c
index bd15a564fe24..5ce6e2a40e00 100644
--- a/drivers/target/iscsi/iscsi_target.c
+++ b/drivers/target/iscsi/iscsi_target.c
@@ -308,9 +308,6 @@ bool iscsit_check_np_match(
return false;
}
-/*
- * Called with mutex np_lock held
- */
static struct iscsi_np *iscsit_get_np(
struct sockaddr_storage *sockaddr,
int network_transport)
@@ -318,6 +315,8 @@ static struct iscsi_np *iscsit_get_np(
struct iscsi_np *np;
bool match;
+ lockdep_assert_held(&np_lock);
+
list_for_each_entry(np, &g_np_list, np_list) {
spin_lock_bh(&np->np_thread_lock);
if (np->np_thread_state != ISCSI_NP_THREAD_ACTIVE) {
@@ -1195,7 +1194,7 @@ attach_cmd:
}
EXPORT_SYMBOL(iscsit_setup_scsi_cmd);
-void iscsit_set_unsoliticed_dataout(struct iscsi_cmd *cmd)
+void iscsit_set_unsolicited_dataout(struct iscsi_cmd *cmd)
{
iscsit_set_dataout_sequence_values(cmd);
@@ -1203,7 +1202,7 @@ void iscsit_set_unsoliticed_dataout(struct iscsi_cmd *cmd)
iscsit_start_dataout_timer(cmd, cmd->conn);
spin_unlock_bh(&cmd->dataout_timeout_lock);
}
-EXPORT_SYMBOL(iscsit_set_unsoliticed_dataout);
+EXPORT_SYMBOL(iscsit_set_unsolicited_dataout);
int iscsit_process_scsi_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
struct iscsi_scsi_req *hdr)
@@ -1237,7 +1236,7 @@ int iscsit_process_scsi_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
*/
if (!cmd->immediate_data) {
if (!cmd->sense_reason && cmd->unsolicited_data)
- iscsit_set_unsoliticed_dataout(cmd);
+ iscsit_set_unsolicited_dataout(cmd);
if (!cmd->sense_reason)
return 0;
@@ -1309,7 +1308,7 @@ after_immediate_data:
target_put_sess_cmd(&cmd->se_cmd);
return rc;
} else if (cmd->unsolicited_data)
- iscsit_set_unsoliticed_dataout(cmd);
+ iscsit_set_unsolicited_dataout(cmd);
} else if (immed_ret == IMMEDIATE_DATA_ERL1_CRC_FAILURE) {
/*
@@ -2241,28 +2240,25 @@ iscsit_handle_text_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
rx_size = payload_length;
if (payload_length) {
u32 checksum = 0, data_crc = 0;
- u32 padding = 0, pad_bytes = 0;
+ u32 padding = 0;
int niov = 0, rx_got;
- struct kvec iov[3];
+ struct kvec iov[2];
- text_in = kzalloc(payload_length, GFP_KERNEL);
+ rx_size = ALIGN(payload_length, 4);
+ text_in = kzalloc(rx_size, GFP_KERNEL);
if (!text_in)
goto reject;
cmd->text_in_ptr = text_in;
- memset(iov, 0, 3 * sizeof(struct kvec));
+ memset(iov, 0, sizeof(iov));
iov[niov].iov_base = text_in;
- iov[niov++].iov_len = payload_length;
+ iov[niov++].iov_len = rx_size;
- padding = ((-payload_length) & 3);
- if (padding != 0) {
- iov[niov].iov_base = &pad_bytes;
- iov[niov++].iov_len = padding;
- rx_size += padding;
+ padding = rx_size - payload_length;
+ if (padding)
pr_debug("Receiving %u additional bytes"
" for padding.\n", padding);
- }
if (conn->conn_ops->DataDigest) {
iov[niov].iov_base = &checksum;
iov[niov++].iov_len = ISCSI_CRC_LEN;
@@ -2274,9 +2270,9 @@ iscsit_handle_text_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
goto reject;
if (conn->conn_ops->DataDigest) {
- iscsit_do_crypto_hash_buf(conn->conn_rx_hash, text_in,
- payload_length, padding,
- &pad_bytes, &data_crc);
+ iscsit_do_crypto_hash_buf(conn->conn_rx_hash,
+ text_in, rx_size, 0, NULL,
+ &data_crc);
if (checksum != data_crc) {
pr_err("Text data CRC32C DataDigest"
@@ -2655,9 +2651,6 @@ static int iscsit_handle_immediate_data(
return IMMEDIATE_DATA_NORMAL_OPERATION;
}
-/*
- * Called with sess->conn_lock held.
- */
/* #warning iscsi_build_conn_drop_async_message() only sends out on connections
with active network interface */
static void iscsit_build_conn_drop_async_message(struct iscsi_conn *conn)
@@ -2666,6 +2659,8 @@ static void iscsit_build_conn_drop_async_message(struct iscsi_conn *conn)
struct iscsi_conn *conn_p;
bool found = false;
+ lockdep_assert_held(&conn->sess->conn_lock);
+
/*
* Only send a Asynchronous Message on connections whos network
* interface is still functional.
@@ -4040,9 +4035,9 @@ static void iscsit_release_commands_from_conn(struct iscsi_conn *conn)
struct se_cmd *se_cmd = &cmd->se_cmd;
if (se_cmd->se_tfo != NULL) {
- spin_lock(&se_cmd->t_state_lock);
+ spin_lock_irq(&se_cmd->t_state_lock);
se_cmd->transport_state |= CMD_T_FABRIC_STOP;
- spin_unlock(&se_cmd->t_state_lock);
+ spin_unlock_irq(&se_cmd->t_state_lock);
}
}
spin_unlock_bh(&conn->cmd_lock);
diff --git a/drivers/target/iscsi/iscsi_target.h b/drivers/target/iscsi/iscsi_target.h
index 48bac0acf8c7..c95f56a3ce31 100644
--- a/drivers/target/iscsi/iscsi_target.h
+++ b/drivers/target/iscsi/iscsi_target.h
@@ -31,7 +31,7 @@ extern int iscsit_reset_np_thread(struct iscsi_np *, struct iscsi_tpg_np *,
struct iscsi_portal_group *, bool);
extern int iscsit_del_np(struct iscsi_np *);
extern int iscsit_reject_cmd(struct iscsi_cmd *cmd, u8, unsigned char *);
-extern void iscsit_set_unsoliticed_dataout(struct iscsi_cmd *);
+extern void iscsit_set_unsolicited_dataout(struct iscsi_cmd *);
extern int iscsit_logout_closesession(struct iscsi_cmd *, struct iscsi_conn *);
extern int iscsit_logout_closeconnection(struct iscsi_cmd *, struct iscsi_conn *);
extern int iscsit_logout_removeconnforrecovery(struct iscsi_cmd *, struct iscsi_conn *);
diff --git a/drivers/target/iscsi/iscsi_target_configfs.c b/drivers/target/iscsi/iscsi_target_configfs.c
index a5481dfeae8d..cac94c94ef5d 100644
--- a/drivers/target/iscsi/iscsi_target_configfs.c
+++ b/drivers/target/iscsi/iscsi_target_configfs.c
@@ -1389,18 +1389,6 @@ static int lio_write_pending(struct se_cmd *se_cmd)
return 0;
}
-static int lio_write_pending_status(struct se_cmd *se_cmd)
-{
- struct iscsi_cmd *cmd = container_of(se_cmd, struct iscsi_cmd, se_cmd);
- int ret;
-
- spin_lock_bh(&cmd->istate_lock);
- ret = !(cmd->cmd_flags & ICF_GOT_LAST_DATAOUT);
- spin_unlock_bh(&cmd->istate_lock);
-
- return ret;
-}
-
static int lio_queue_status(struct se_cmd *se_cmd)
{
struct iscsi_cmd *cmd = container_of(se_cmd, struct iscsi_cmd, se_cmd);
@@ -1564,7 +1552,6 @@ const struct target_core_fabric_ops iscsi_ops = {
.sess_get_index = lio_sess_get_index,
.sess_get_initiator_sid = lio_sess_get_initiator_sid,
.write_pending = lio_write_pending,
- .write_pending_status = lio_write_pending_status,
.set_default_node_attributes = lio_set_default_node_attributes,
.get_cmd_state = iscsi_get_cmd_state,
.queue_data_in = lio_queue_data_in,
diff --git a/drivers/target/iscsi/iscsi_target_erl0.c b/drivers/target/iscsi/iscsi_target_erl0.c
index 1193cf884a28..8890c0721053 100644
--- a/drivers/target/iscsi/iscsi_target_erl0.c
+++ b/drivers/target/iscsi/iscsi_target_erl0.c
@@ -802,14 +802,13 @@ void iscsit_start_time2retain_handler(struct iscsi_session *sess)
jiffies + sess->sess_ops->DefaultTime2Retain * HZ);
}
-/*
- * Called with spin_lock_bh(&struct se_portal_group->session_lock) held
- */
int iscsit_stop_time2retain_timer(struct iscsi_session *sess)
{
struct iscsi_portal_group *tpg = sess->tpg;
struct se_portal_group *se_tpg = &tpg->tpg_se_tpg;
+ lockdep_assert_held(&se_tpg->session_lock);
+
if (sess->time2retain_timer_flags & ISCSI_TF_EXPIRED)
return -1;
diff --git a/drivers/target/iscsi/iscsi_target_erl1.c b/drivers/target/iscsi/iscsi_target_erl1.c
index 1b54a9c70851..e02e1aaf63c5 100644
--- a/drivers/target/iscsi/iscsi_target_erl1.c
+++ b/drivers/target/iscsi/iscsi_target_erl1.c
@@ -48,14 +48,20 @@ int iscsit_dump_data_payload(
u32 buf_len,
int dump_padding_digest)
{
- char *buf, pad_bytes[4];
+ char *buf;
int ret = DATAOUT_WITHIN_COMMAND_RECOVERY, rx_got;
- u32 length, padding, offset = 0, size;
+ u32 length, offset = 0, size;
struct kvec iov;
if (conn->sess->sess_ops->RDMAExtensions)
return 0;
+ if (dump_padding_digest) {
+ buf_len = ALIGN(buf_len, 4);
+ if (conn->conn_ops->DataDigest)
+ buf_len += ISCSI_CRC_LEN;
+ }
+
length = min(buf_len, OFFLOAD_BUF_SIZE);
buf = kzalloc(length, GFP_ATOMIC);
@@ -75,41 +81,12 @@ int iscsit_dump_data_payload(
rx_got = rx_data(conn, &iov, 1, size);
if (rx_got != size) {
ret = DATAOUT_CANNOT_RECOVER;
- goto out;
+ break;
}
offset += size;
}
- if (!dump_padding_digest)
- goto out;
-
- padding = ((-buf_len) & 3);
- if (padding != 0) {
- iov.iov_len = padding;
- iov.iov_base = pad_bytes;
-
- rx_got = rx_data(conn, &iov, 1, padding);
- if (rx_got != padding) {
- ret = DATAOUT_CANNOT_RECOVER;
- goto out;
- }
- }
-
- if (conn->conn_ops->DataDigest) {
- u32 data_crc;
-
- iov.iov_len = ISCSI_CRC_LEN;
- iov.iov_base = &data_crc;
-
- rx_got = rx_data(conn, &iov, 1, ISCSI_CRC_LEN);
- if (rx_got != ISCSI_CRC_LEN) {
- ret = DATAOUT_CANNOT_RECOVER;
- goto out;
- }
- }
-
-out:
kfree(buf);
return ret;
}
@@ -797,14 +774,14 @@ static struct iscsi_ooo_cmdsn *iscsit_allocate_ooo_cmdsn(void)
return ooo_cmdsn;
}
-/*
- * Called with sess->cmdsn_mutex held.
- */
static int iscsit_attach_ooo_cmdsn(
struct iscsi_session *sess,
struct iscsi_ooo_cmdsn *ooo_cmdsn)
{
struct iscsi_ooo_cmdsn *ooo_tail, *ooo_tmp;
+
+ lockdep_assert_held(&sess->cmdsn_mutex);
+
/*
* We attach the struct iscsi_ooo_cmdsn entry to the out of order
* list in increasing CmdSN order.
@@ -871,15 +848,14 @@ void iscsit_clear_ooo_cmdsns_for_conn(struct iscsi_conn *conn)
mutex_unlock(&sess->cmdsn_mutex);
}
-/*
- * Called with sess->cmdsn_mutex held.
- */
int iscsit_execute_ooo_cmdsns(struct iscsi_session *sess)
{
int ooo_count = 0;
struct iscsi_cmd *cmd = NULL;
struct iscsi_ooo_cmdsn *ooo_cmdsn, *ooo_cmdsn_tmp;
+ lockdep_assert_held(&sess->cmdsn_mutex);
+
list_for_each_entry_safe(ooo_cmdsn, ooo_cmdsn_tmp,
&sess->sess_ooo_cmdsn_list, ooo_list) {
if (ooo_cmdsn->cmdsn != sess->exp_cmd_sn)
@@ -980,7 +956,7 @@ int iscsit_execute_cmd(struct iscsi_cmd *cmd, int ooo)
if (cmd->se_cmd.transport_state & CMD_T_ABORTED)
return 0;
- iscsit_set_unsoliticed_dataout(cmd);
+ iscsit_set_unsolicited_dataout(cmd);
}
return transport_handle_cdb_direct(&cmd->se_cmd);
@@ -1232,9 +1208,6 @@ void iscsit_mod_dataout_timer(struct iscsi_cmd *cmd)
spin_unlock_bh(&cmd->dataout_timeout_lock);
}
-/*
- * Called with cmd->dataout_timeout_lock held.
- */
void iscsit_start_dataout_timer(
struct iscsi_cmd *cmd,
struct iscsi_conn *conn)
@@ -1242,6 +1215,8 @@ void iscsit_start_dataout_timer(
struct iscsi_session *sess = conn->sess;
struct iscsi_node_attrib *na = iscsit_tpg_get_node_attrib(sess);
+ lockdep_assert_held(&cmd->dataout_timeout_lock);
+
if (cmd->dataout_timer_flags & ISCSI_TF_RUNNING)
return;
diff --git a/drivers/target/iscsi/iscsi_target_util.c b/drivers/target/iscsi/iscsi_target_util.c
index 86987da86dd6..3ac494f63a0b 100644
--- a/drivers/target/iscsi/iscsi_target_util.c
+++ b/drivers/target/iscsi/iscsi_target_util.c
@@ -56,9 +56,6 @@
extern struct list_head g_tiqn_list;
extern spinlock_t tiqn_lock;
-/*
- * Called with cmd->r2t_lock held.
- */
int iscsit_add_r2t_to_list(
struct iscsi_cmd *cmd,
u32 offset,
@@ -68,6 +65,8 @@ int iscsit_add_r2t_to_list(
{
struct iscsi_r2t *r2t;
+ lockdep_assert_held(&cmd->r2t_lock);
+
r2t = kmem_cache_zalloc(lio_r2t_cache, GFP_ATOMIC);
if (!r2t) {
pr_err("Unable to allocate memory for struct iscsi_r2t.\n");
@@ -128,11 +127,10 @@ struct iscsi_r2t *iscsit_get_r2t_from_list(struct iscsi_cmd *cmd)
return NULL;
}
-/*
- * Called with cmd->r2t_lock held.
- */
void iscsit_free_r2t(struct iscsi_r2t *r2t, struct iscsi_cmd *cmd)
{
+ lockdep_assert_held(&cmd->r2t_lock);
+
list_del(&r2t->r2t_list);
kmem_cache_free(lio_r2t_cache, r2t);
}
@@ -762,8 +760,8 @@ void __iscsit_free_cmd(struct iscsi_cmd *cmd, bool check_queues)
iscsit_remove_cmd_from_response_queue(cmd, conn);
}
- if (conn && conn->conn_transport->iscsit_release_cmd)
- conn->conn_transport->iscsit_release_cmd(conn, cmd);
+ if (conn && conn->conn_transport->iscsit_unmap_cmd)
+ conn->conn_transport->iscsit_unmap_cmd(conn, cmd);
}
void iscsit_free_cmd(struct iscsi_cmd *cmd, bool shutdown)
@@ -956,9 +954,6 @@ void iscsit_mod_nopin_response_timer(struct iscsi_conn *conn)
spin_unlock_bh(&conn->nopin_timer_lock);
}
-/*
- * Called with conn->nopin_timer_lock held.
- */
void iscsit_start_nopin_response_timer(struct iscsi_conn *conn)
{
struct iscsi_session *sess = conn->sess;
@@ -1016,13 +1011,13 @@ void iscsit_handle_nopin_timeout(struct timer_list *t)
iscsit_dec_conn_usage_count(conn);
}
-/*
- * Called with conn->nopin_timer_lock held.
- */
void __iscsit_start_nopin_timer(struct iscsi_conn *conn)
{
struct iscsi_session *sess = conn->sess;
struct iscsi_node_attrib *na = iscsit_tpg_get_node_attrib(sess);
+
+ lockdep_assert_held(&conn->nopin_timer_lock);
+
/*
* NOPIN timeout is disabled.
*/
diff --git a/drivers/target/loopback/tcm_loop.c b/drivers/target/loopback/tcm_loop.c
index 7bd7c0c0db6f..3305b47fdf53 100644
--- a/drivers/target/loopback/tcm_loop.c
+++ b/drivers/target/loopback/tcm_loop.c
@@ -128,14 +128,6 @@ static void tcm_loop_submission_work(struct work_struct *work)
set_host_byte(sc, DID_ERROR);
goto out_done;
}
- if (scsi_bidi_cmnd(sc)) {
- struct scsi_data_buffer *sdb = scsi_in(sc);
-
- sgl_bidi = sdb->table.sgl;
- sgl_bidi_count = sdb->table.nents;
- se_cmd->se_cmd_flags |= SCF_BIDI;
-
- }
transfer_length = scsi_transfer_length(sc);
if (!scsi_prot_sg_count(sc) &&
@@ -304,12 +296,6 @@ static int tcm_loop_target_reset(struct scsi_cmnd *sc)
return FAILED;
}
-static int tcm_loop_slave_alloc(struct scsi_device *sd)
-{
- blk_queue_flag_set(QUEUE_FLAG_BIDI, sd->request_queue);
- return 0;
-}
-
static struct scsi_host_template tcm_loop_driver_template = {
.show_info = tcm_loop_show_info,
.proc_name = "tcm_loopback",
@@ -325,7 +311,6 @@ static struct scsi_host_template tcm_loop_driver_template = {
.cmd_per_lun = 1024,
.max_sectors = 0xFFFF,
.dma_boundary = PAGE_SIZE - 1,
- .slave_alloc = tcm_loop_slave_alloc,
.module = THIS_MODULE,
.track_queue_depth = 1,
};
@@ -560,11 +545,6 @@ static int tcm_loop_write_pending(struct se_cmd *se_cmd)
return 0;
}
-static int tcm_loop_write_pending_status(struct se_cmd *se_cmd)
-{
- return 0;
-}
-
static int tcm_loop_queue_data_in(struct se_cmd *se_cmd)
{
struct tcm_loop_cmd *tl_cmd = container_of(se_cmd,
@@ -1159,7 +1139,6 @@ static const struct target_core_fabric_ops loop_ops = {
.release_cmd = tcm_loop_release_cmd,
.sess_get_index = tcm_loop_sess_get_index,
.write_pending = tcm_loop_write_pending,
- .write_pending_status = tcm_loop_write_pending_status,
.set_default_node_attributes = tcm_loop_set_default_node_attributes,
.get_cmd_state = tcm_loop_get_cmd_state,
.queue_data_in = tcm_loop_queue_data_in,
diff --git a/drivers/target/sbp/sbp_target.c b/drivers/target/sbp/sbp_target.c
index 08cee13dfb9a..b0d3583998f0 100644
--- a/drivers/target/sbp/sbp_target.c
+++ b/drivers/target/sbp/sbp_target.c
@@ -1749,11 +1749,6 @@ static int sbp_write_pending(struct se_cmd *se_cmd)
return 0;
}
-static int sbp_write_pending_status(struct se_cmd *se_cmd)
-{
- return 0;
-}
-
static void sbp_set_default_node_attrs(struct se_node_acl *nacl)
{
return;
@@ -2329,7 +2324,6 @@ static const struct target_core_fabric_ops sbp_ops = {
.release_cmd = sbp_release_cmd,
.sess_get_index = sbp_sess_get_index,
.write_pending = sbp_write_pending,
- .write_pending_status = sbp_write_pending_status,
.set_default_node_attributes = sbp_set_default_node_attrs,
.get_cmd_state = sbp_get_cmd_state,
.queue_data_in = sbp_queue_data_in,
diff --git a/drivers/target/target_core_alua.c b/drivers/target/target_core_alua.c
index 6b0d9beacf90..e09f0cf86bed 100644
--- a/drivers/target/target_core_alua.c
+++ b/drivers/target/target_core_alua.c
@@ -910,9 +910,6 @@ static int core_alua_write_tpg_metadata(
return (ret < 0) ? -EIO : 0;
}
-/*
- * Called with tg_pt_gp->tg_pt_gp_transition_mutex held
- */
static int core_alua_update_tpg_primary_metadata(
struct t10_alua_tg_pt_gp *tg_pt_gp)
{
@@ -921,6 +918,8 @@ static int core_alua_update_tpg_primary_metadata(
char *path;
int len, rc;
+ lockdep_assert_held(&tg_pt_gp->tg_pt_gp_transition_mutex);
+
md_buf = kzalloc(ALUA_MD_BUF_LEN, GFP_KERNEL);
if (!md_buf) {
pr_err("Unable to allocate buf for ALUA metadata\n");
diff --git a/drivers/target/target_core_configfs.c b/drivers/target/target_core_configfs.c
index 8e7fffbb8802..fc5ef31f5ba8 100644
--- a/drivers/target/target_core_configfs.c
+++ b/drivers/target/target_core_configfs.c
@@ -401,10 +401,6 @@ static int target_fabric_tf_ops_check(const struct target_core_fabric_ops *tfo)
pr_err("Missing tfo->write_pending()\n");
return -EINVAL;
}
- if (!tfo->write_pending_status) {
- pr_err("Missing tfo->write_pending_status()\n");
- return -EINVAL;
- }
if (!tfo->set_default_node_attributes) {
pr_err("Missing tfo->set_default_node_attributes()\n");
return -EINVAL;
diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c
index 93c56f4a9911..1f8482b6473b 100644
--- a/drivers/target/target_core_device.c
+++ b/drivers/target/target_core_device.c
@@ -404,9 +404,6 @@ int core_enable_device_list_for_node(
return 0;
}
-/*
- * Called with se_node_acl->lun_entry_mutex held.
- */
void core_disable_device_list_for_node(
struct se_lun *lun,
struct se_dev_entry *orig,
@@ -418,6 +415,9 @@ void core_disable_device_list_for_node(
* reference to se_device->dev_group.
*/
struct se_device *dev = rcu_dereference_raw(lun->lun_se_dev);
+
+ lockdep_assert_held(&nacl->lun_entry_mutex);
+
/*
* If the MappedLUN entry is being disabled, the entry in
* lun->lun_deve_list must be removed now before clearing the
diff --git a/drivers/target/target_core_pr.c b/drivers/target/target_core_pr.c
index 397f38cb7f4e..1597a9ebadca 100644
--- a/drivers/target/target_core_pr.c
+++ b/drivers/target/target_core_pr.c
@@ -1290,9 +1290,6 @@ static int core_scsi3_check_implicit_release(
return ret;
}
-/*
- * Called with struct t10_reservation->registration_lock held.
- */
static void __core_scsi3_free_registration(
struct se_device *dev,
struct t10_pr_registration *pr_reg,
@@ -1308,6 +1305,8 @@ static void __core_scsi3_free_registration(
struct se_dev_entry *deve;
char i_buf[PR_REG_ISID_ID_LEN];
+ lockdep_assert_held(&pr_tmpl->registration_lock);
+
memset(i_buf, 0, PR_REG_ISID_ID_LEN);
core_pr_dump_initiator_port(pr_reg, i_buf, PR_REG_ISID_ID_LEN);
@@ -2450,9 +2449,6 @@ core_scsi3_emulate_pro_reserve(struct se_cmd *cmd, int type, int scope,
}
}
-/*
- * Called with struct se_device->dev_reservation_lock held.
- */
static void __core_scsi3_complete_pro_release(
struct se_device *dev,
struct se_node_acl *se_nacl,
@@ -2464,6 +2460,8 @@ static void __core_scsi3_complete_pro_release(
char i_buf[PR_REG_ISID_ID_LEN];
int pr_res_type = 0, pr_res_scope = 0;
+ lockdep_assert_held(&dev->dev_reservation_lock);
+
memset(i_buf, 0, PR_REG_ISID_ID_LEN);
core_pr_dump_initiator_port(pr_reg, i_buf, PR_REG_ISID_ID_LEN);
/*
@@ -2760,9 +2758,6 @@ core_scsi3_emulate_pro_clear(struct se_cmd *cmd, u64 res_key)
return 0;
}
-/*
- * Called with struct se_device->dev_reservation_lock held.
- */
static void __core_scsi3_complete_pro_preempt(
struct se_device *dev,
struct t10_pr_registration *pr_reg,
@@ -2775,6 +2770,8 @@ static void __core_scsi3_complete_pro_preempt(
const struct target_core_fabric_ops *tfo = nacl->se_tpg->se_tpg_tfo;
char i_buf[PR_REG_ISID_ID_LEN];
+ lockdep_assert_held(&dev->dev_reservation_lock);
+
memset(i_buf, 0, PR_REG_ISID_ID_LEN);
core_pr_dump_initiator_port(pr_reg, i_buf, PR_REG_ISID_ID_LEN);
/*
diff --git a/drivers/target/target_core_tmr.c b/drivers/target/target_core_tmr.c
index ad0061e09d4c..3a1bb799a9ab 100644
--- a/drivers/target/target_core_tmr.c
+++ b/drivers/target/target_core_tmr.c
@@ -114,21 +114,6 @@ static bool __target_check_io_state(struct se_cmd *se_cmd,
spin_unlock(&se_cmd->t_state_lock);
return false;
}
- if (se_cmd->transport_state & CMD_T_PRE_EXECUTE) {
- if (se_cmd->scsi_status) {
- pr_debug("Attempted to abort io tag: %llu early failure"
- " status: 0x%02x\n", se_cmd->tag,
- se_cmd->scsi_status);
- spin_unlock(&se_cmd->t_state_lock);
- return false;
- }
- }
- if (sess->sess_tearing_down) {
- pr_debug("Attempted to abort io tag: %llu already shutdown,"
- " skipping\n", se_cmd->tag);
- spin_unlock(&se_cmd->t_state_lock);
- return false;
- }
se_cmd->transport_state |= CMD_T_ABORTED;
if ((tmr_sess != se_cmd->se_sess) && tas)
@@ -232,33 +217,13 @@ static void core_tmr_drain_tmr_list(
continue;
spin_lock(&sess->sess_cmd_lock);
- spin_lock(&cmd->t_state_lock);
- if (!(cmd->transport_state & CMD_T_ACTIVE) ||
- (cmd->transport_state & CMD_T_FABRIC_STOP)) {
- spin_unlock(&cmd->t_state_lock);
- spin_unlock(&sess->sess_cmd_lock);
- continue;
- }
- if (cmd->t_state == TRANSPORT_ISTATE_PROCESSING) {
- spin_unlock(&cmd->t_state_lock);
- spin_unlock(&sess->sess_cmd_lock);
- continue;
- }
- if (sess->sess_tearing_down) {
- spin_unlock(&cmd->t_state_lock);
- spin_unlock(&sess->sess_cmd_lock);
- continue;
- }
- cmd->transport_state |= CMD_T_ABORTED;
- spin_unlock(&cmd->t_state_lock);
+ rc = __target_check_io_state(cmd, sess, 0);
+ spin_unlock(&sess->sess_cmd_lock);
- rc = kref_get_unless_zero(&cmd->cmd_kref);
if (!rc) {
printk("LUN_RESET TMR: non-zero kref_get_unless_zero\n");
- spin_unlock(&sess->sess_cmd_lock);
continue;
}
- spin_unlock(&sess->sess_cmd_lock);
list_move_tail(&tmr_p->tmr_list, &drain_tmr_list);
}
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
index ef9e75b359d4..e3f7e21e6614 100644
--- a/drivers/target/target_core_transport.c
+++ b/drivers/target/target_core_transport.c
@@ -664,11 +664,6 @@ static int transport_cmd_check_stop_to_fabric(struct se_cmd *cmd)
target_remove_from_state_list(cmd);
- /*
- * Clear struct se_cmd->se_lun before the handoff to FE.
- */
- cmd->se_lun = NULL;
-
spin_lock_irqsave(&cmd->t_state_lock, flags);
/*
* Determine if frontend context caller is requesting the stopping of
@@ -696,17 +691,6 @@ static int transport_cmd_check_stop_to_fabric(struct se_cmd *cmd)
return cmd->se_tfo->check_stop_free(cmd);
}
-static void transport_lun_remove_cmd(struct se_cmd *cmd)
-{
- struct se_lun *lun = cmd->se_lun;
-
- if (!lun)
- return;
-
- if (cmpxchg(&cmd->lun_ref_active, true, false))
- percpu_ref_put(&lun->lun_ref);
-}
-
static void target_complete_failure_work(struct work_struct *work)
{
struct se_cmd *cmd = container_of(work, struct se_cmd, work);
@@ -797,8 +781,6 @@ static void target_handle_abort(struct se_cmd *cmd)
WARN_ON_ONCE(kref_read(&cmd->cmd_kref) == 0);
- transport_lun_remove_cmd(cmd);
-
transport_cmd_check_stop_to_fabric(cmd);
}
@@ -1711,7 +1693,6 @@ static void target_complete_tmr_failure(struct work_struct *work)
se_cmd->se_tmr_req->response = TMR_LUN_DOES_NOT_EXIST;
se_cmd->se_tfo->queue_tm_rsp(se_cmd);
- transport_lun_remove_cmd(se_cmd);
transport_cmd_check_stop_to_fabric(se_cmd);
}
@@ -1902,7 +1883,6 @@ void transport_generic_request_failure(struct se_cmd *cmd,
goto queue_full;
check_stop:
- transport_lun_remove_cmd(cmd);
transport_cmd_check_stop_to_fabric(cmd);
return;
@@ -2056,7 +2036,6 @@ void target_execute_cmd(struct se_cmd *cmd)
spin_lock_irq(&cmd->t_state_lock);
cmd->t_state = TRANSPORT_PROCESSING;
- cmd->transport_state &= ~CMD_T_PRE_EXECUTE;
cmd->transport_state |= CMD_T_ACTIVE | CMD_T_SENT;
spin_unlock_irq(&cmd->t_state_lock);
@@ -2201,7 +2180,6 @@ queue_status:
transport_handle_queue_full(cmd, cmd->se_dev, ret, false);
return;
}
- transport_lun_remove_cmd(cmd);
transport_cmd_check_stop_to_fabric(cmd);
}
@@ -2296,7 +2274,6 @@ static void target_complete_ok_work(struct work_struct *work)
if (ret)
goto queue_full;
- transport_lun_remove_cmd(cmd);
transport_cmd_check_stop_to_fabric(cmd);
return;
}
@@ -2322,7 +2299,6 @@ static void target_complete_ok_work(struct work_struct *work)
if (ret)
goto queue_full;
- transport_lun_remove_cmd(cmd);
transport_cmd_check_stop_to_fabric(cmd);
return;
}
@@ -2358,7 +2334,6 @@ queue_rsp:
if (ret)
goto queue_full;
- transport_lun_remove_cmd(cmd);
transport_cmd_check_stop_to_fabric(cmd);
return;
}
@@ -2394,7 +2369,6 @@ queue_status:
break;
}
- transport_lun_remove_cmd(cmd);
transport_cmd_check_stop_to_fabric(cmd);
return;
@@ -2721,9 +2695,6 @@ int transport_generic_free_cmd(struct se_cmd *cmd, int wait_for_tasks)
*/
if (cmd->state_active)
target_remove_from_state_list(cmd);
-
- if (cmd->se_lun)
- transport_lun_remove_cmd(cmd);
}
if (aborted)
cmd->free_compl = &compl;
@@ -2765,7 +2736,6 @@ int target_get_sess_cmd(struct se_cmd *se_cmd, bool ack_kref)
ret = -ESHUTDOWN;
goto out;
}
- se_cmd->transport_state |= CMD_T_PRE_EXECUTE;
list_add_tail(&se_cmd->se_cmd_list, &se_sess->sess_cmd_list);
percpu_ref_get(&se_sess->cmd_count);
out:
@@ -2796,6 +2766,9 @@ static void target_release_cmd_kref(struct kref *kref)
struct completion *abrt_compl = se_cmd->abrt_compl;
unsigned long flags;
+ if (se_cmd->lun_ref_active)
+ percpu_ref_put(&se_cmd->se_lun->lun_ref);
+
if (se_sess) {
spin_lock_irqsave(&se_sess->sess_cmd_lock, flags);
list_del_init(&se_cmd->se_cmd_list);
@@ -3273,6 +3246,22 @@ transport_send_check_condition_and_sense(struct se_cmd *cmd,
}
EXPORT_SYMBOL(transport_send_check_condition_and_sense);
+/**
+ * target_send_busy - Send SCSI BUSY status back to the initiator
+ * @cmd: SCSI command for which to send a BUSY reply.
+ *
+ * Note: Only call this function if target_submit_cmd*() failed.
+ */
+int target_send_busy(struct se_cmd *cmd)
+{
+ WARN_ON_ONCE(cmd->se_cmd_flags & SCF_SCSI_TMR_CDB);
+
+ cmd->scsi_status = SAM_STAT_BUSY;
+ trace_target_cmd_complete(cmd);
+ return cmd->se_tfo->queue_status(cmd);
+}
+EXPORT_SYMBOL(target_send_busy);
+
static void target_tmr_work(struct work_struct *work)
{
struct se_cmd *cmd = container_of(work, struct se_cmd, work);
diff --git a/drivers/target/target_core_user.c b/drivers/target/target_core_user.c
index 5831e0eecea1..9704b135a7bc 100644
--- a/drivers/target/target_core_user.c
+++ b/drivers/target/target_core_user.c
@@ -1663,7 +1663,7 @@ static void tcmu_dev_kref_release(struct kref *kref)
WARN_ON(!all_expired);
tcmu_blocks_release(&udev->data_blocks, 0, udev->dbi_max + 1);
- kfree(udev->data_bitmap);
+ bitmap_free(udev->data_bitmap);
mutex_unlock(&udev->cmdr_lock);
call_rcu(&dev->rcu_head, tcmu_dev_call_rcu);
@@ -1794,11 +1794,12 @@ static int tcmu_netlink_event_send(struct tcmu_dev *udev,
ret = genlmsg_multicast_allns(&tcmu_genl_family, skb, 0,
TCMU_MCGRP_CONFIG, GFP_KERNEL);
- /* We don't care if no one is listening */
- if (ret == -ESRCH)
- ret = 0;
- if (!ret)
- ret = tcmu_wait_genl_cmd_reply(udev);
+
+ /* Wait during an add as the listener may not be up yet */
+ if (ret == 0 ||
+ (ret == -ESRCH && cmd == TCMU_CMD_ADDED_DEVICE))
+ return tcmu_wait_genl_cmd_reply(udev);
+
return ret;
}
@@ -1870,9 +1871,7 @@ static int tcmu_configure_device(struct se_device *dev)
info = &udev->uio_info;
mutex_lock(&udev->cmdr_lock);
- udev->data_bitmap = kcalloc(BITS_TO_LONGS(udev->max_blocks),
- sizeof(unsigned long),
- GFP_KERNEL);
+ udev->data_bitmap = bitmap_zalloc(udev->max_blocks, GFP_KERNEL);
mutex_unlock(&udev->cmdr_lock);
if (!udev->data_bitmap) {
ret = -ENOMEM;
@@ -1959,7 +1958,7 @@ err_register:
vfree(udev->mb_addr);
udev->mb_addr = NULL;
err_vzalloc:
- kfree(udev->data_bitmap);
+ bitmap_free(udev->data_bitmap);
udev->data_bitmap = NULL;
err_bitmap_alloc:
kfree(info->name);
diff --git a/drivers/target/target_core_xcopy.c b/drivers/target/target_core_xcopy.c
index c2e1fc927fdf..9be1418e919f 100644
--- a/drivers/target/target_core_xcopy.c
+++ b/drivers/target/target_core_xcopy.c
@@ -442,11 +442,6 @@ static int xcopy_pt_write_pending(struct se_cmd *se_cmd)
return 0;
}
-static int xcopy_pt_write_pending_status(struct se_cmd *se_cmd)
-{
- return 0;
-}
-
static int xcopy_pt_queue_data_in(struct se_cmd *se_cmd)
{
return 0;
@@ -463,7 +458,6 @@ static const struct target_core_fabric_ops xcopy_pt_tfo = {
.release_cmd = xcopy_pt_release_cmd,
.check_stop_free = xcopy_pt_check_stop_free,
.write_pending = xcopy_pt_write_pending,
- .write_pending_status = xcopy_pt_write_pending_status,
.queue_data_in = xcopy_pt_queue_data_in,
.queue_status = xcopy_pt_queue_status,
};
diff --git a/drivers/target/tcm_fc/tcm_fc.h b/drivers/target/tcm_fc/tcm_fc.h
index 11d27b93b413..b8ced4458118 100644
--- a/drivers/target/tcm_fc/tcm_fc.h
+++ b/drivers/target/tcm_fc/tcm_fc.h
@@ -158,7 +158,6 @@ void ft_release_cmd(struct se_cmd *);
int ft_queue_status(struct se_cmd *);
int ft_queue_data_in(struct se_cmd *);
int ft_write_pending(struct se_cmd *);
-int ft_write_pending_status(struct se_cmd *);
int ft_get_cmd_state(struct se_cmd *);
void ft_queue_tm_resp(struct se_cmd *);
void ft_aborted_task(struct se_cmd *);
diff --git a/drivers/target/tcm_fc/tfc_cmd.c b/drivers/target/tcm_fc/tfc_cmd.c
index a183d4da7db2..f0529ba58f4c 100644
--- a/drivers/target/tcm_fc/tfc_cmd.c
+++ b/drivers/target/tcm_fc/tfc_cmd.c
@@ -184,13 +184,6 @@ int ft_queue_status(struct se_cmd *se_cmd)
return 0;
}
-int ft_write_pending_status(struct se_cmd *se_cmd)
-{
- struct ft_cmd *cmd = container_of(se_cmd, struct ft_cmd, se_cmd);
-
- return cmd->write_data_len != se_cmd->data_length;
-}
-
/*
* Send TX_RDY (transfer ready).
*/
diff --git a/drivers/target/tcm_fc/tfc_conf.c b/drivers/target/tcm_fc/tfc_conf.c
index 1ce49518d440..c873a052fcb0 100644
--- a/drivers/target/tcm_fc/tfc_conf.c
+++ b/drivers/target/tcm_fc/tfc_conf.c
@@ -437,7 +437,6 @@ static const struct target_core_fabric_ops ft_fabric_ops = {
.sess_get_index = ft_sess_get_index,
.sess_get_initiator_sid = NULL,
.write_pending = ft_write_pending,
- .write_pending_status = ft_write_pending_status,
.set_default_node_attributes = ft_set_default_node_attr,
.get_cmd_state = ft_get_cmd_state,
.queue_data_in = ft_queue_data_in,
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index 58bb7d72dc2b..653aa27a25a4 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -344,7 +344,8 @@ source "drivers/thermal/intel/Kconfig"
endmenu
menu "Broadcom thermal drivers"
-depends on ARCH_BCM || ARCH_BRCMSTB || ARCH_BCM2835 || COMPILE_TEST
+depends on ARCH_BCM || ARCH_BRCMSTB || ARCH_BCM2835 || ARCH_BCM_IPROC || \
+ COMPILE_TEST
source "drivers/thermal/broadcom/Kconfig"
endmenu
diff --git a/drivers/thermal/broadcom/Kconfig b/drivers/thermal/broadcom/Kconfig
index c106a15bf7f9..dc9a9bdde3ed 100644
--- a/drivers/thermal/broadcom/Kconfig
+++ b/drivers/thermal/broadcom/Kconfig
@@ -22,3 +22,12 @@ config BCM_NS_THERMAL
BCM4708, BCM4709, BCM5301x, BCM95852X, etc). It contains DMU (Device
Management Unit) block with a thermal sensor that allows checking CPU
temperature.
+
+config BCM_SR_THERMAL
+ tristate "Stingray thermal driver"
+ depends on ARCH_BCM_IPROC || COMPILE_TEST
+ default ARCH_BCM_IPROC
+ help
+ Support for the Stingray family of SoCs. Its different blocks like
+ iHost, CRMU and NITRO has thermal sensor that allows checking its
+ temperature.
diff --git a/drivers/thermal/broadcom/Makefile b/drivers/thermal/broadcom/Makefile
index fae10ecafaef..79df69eb2b8c 100644
--- a/drivers/thermal/broadcom/Makefile
+++ b/drivers/thermal/broadcom/Makefile
@@ -1,3 +1,4 @@
obj-$(CONFIG_BCM2835_THERMAL) += bcm2835_thermal.o
obj-$(CONFIG_BRCMSTB_THERMAL) += brcmstb_thermal.o
obj-$(CONFIG_BCM_NS_THERMAL) += ns-thermal.o
+obj-$(CONFIG_BCM_SR_THERMAL) += sr-thermal.o
diff --git a/drivers/thermal/broadcom/bcm2835_thermal.c b/drivers/thermal/broadcom/bcm2835_thermal.c
index 720760cd493f..ba39647a690c 100644
--- a/drivers/thermal/broadcom/bcm2835_thermal.c
+++ b/drivers/thermal/broadcom/bcm2835_thermal.c
@@ -119,8 +119,7 @@ static const struct debugfs_reg32 bcm2835_thermal_regs[] = {
static void bcm2835_thermal_debugfs(struct platform_device *pdev)
{
- struct thermal_zone_device *tz = platform_get_drvdata(pdev);
- struct bcm2835_thermal_data *data = tz->devdata;
+ struct bcm2835_thermal_data *data = platform_get_drvdata(pdev);
struct debugfs_regset32 *regset;
data->debugfsdir = debugfs_create_dir("bcm2835_thermal", NULL);
@@ -266,7 +265,7 @@ static int bcm2835_thermal_probe(struct platform_device *pdev)
data->tz = tz;
- platform_set_drvdata(pdev, tz);
+ platform_set_drvdata(pdev, data);
/*
* Thermal_zone doesn't enable hwmon as default,
@@ -290,8 +289,8 @@ err_clk:
static int bcm2835_thermal_remove(struct platform_device *pdev)
{
- struct thermal_zone_device *tz = platform_get_drvdata(pdev);
- struct bcm2835_thermal_data *data = tz->devdata;
+ struct bcm2835_thermal_data *data = platform_get_drvdata(pdev);
+ struct thermal_zone_device *tz = data->tz;
debugfs_remove_recursive(data->debugfsdir);
thermal_zone_of_sensor_unregister(&pdev->dev, tz);
diff --git a/drivers/thermal/broadcom/sr-thermal.c b/drivers/thermal/broadcom/sr-thermal.c
new file mode 100644
index 000000000000..2284cbecedf3
--- /dev/null
+++ b/drivers/thermal/broadcom/sr-thermal.c
@@ -0,0 +1,121 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018 Broadcom
+ */
+
+#include <linux/acpi.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/thermal.h>
+
+/*
+ * In stingray thermal IO memory,
+ * Total Number of available TMONs MASK is at offset 0
+ * temperature registers BASE is at 4 byte offset.
+ * Each TMON temperature register size is 4.
+ */
+#define SR_TMON_TEMP_BASE(id) ((id) * 0x4)
+
+#define SR_TMON_MAX_LIST 6
+
+struct sr_tmon {
+ struct thermal_zone_device *tz;
+ unsigned int crit_temp;
+ unsigned int tmon_id;
+ struct sr_thermal *priv;
+};
+
+struct sr_thermal {
+ void __iomem *regs;
+ unsigned int max_crit_temp;
+ struct sr_tmon tmon[SR_TMON_MAX_LIST];
+};
+
+static int sr_get_temp(void *data, int *temp)
+{
+ struct sr_tmon *tmon = data;
+ struct sr_thermal *sr_thermal = tmon->priv;
+
+ *temp = readl(sr_thermal->regs + SR_TMON_TEMP_BASE(tmon->tmon_id));
+
+ return 0;
+}
+
+static const struct thermal_zone_of_device_ops sr_tz_ops = {
+ .get_temp = sr_get_temp,
+};
+
+static int sr_thermal_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct sr_thermal *sr_thermal;
+ struct sr_tmon *tmon;
+ struct resource *res;
+ u32 sr_tmon_list = 0;
+ unsigned int i;
+ int ret;
+
+ sr_thermal = devm_kzalloc(dev, sizeof(*sr_thermal), GFP_KERNEL);
+ if (!sr_thermal)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ sr_thermal->regs = (void __iomem *)devm_memremap(&pdev->dev, res->start,
+ resource_size(res),
+ MEMREMAP_WB);
+ if (IS_ERR(sr_thermal->regs)) {
+ dev_err(dev, "failed to get io address\n");
+ return PTR_ERR(sr_thermal->regs);
+ }
+
+ ret = device_property_read_u32(dev, "brcm,tmon-mask", &sr_tmon_list);
+ if (ret)
+ return ret;
+
+ tmon = sr_thermal->tmon;
+ for (i = 0; i < SR_TMON_MAX_LIST; i++, tmon++) {
+ if (!(sr_tmon_list & BIT(i)))
+ continue;
+
+ /* Flush temperature registers */
+ writel(0, sr_thermal->regs + SR_TMON_TEMP_BASE(i));
+ tmon->tmon_id = i;
+ tmon->priv = sr_thermal;
+ tmon->tz = devm_thermal_zone_of_sensor_register(dev, i, tmon,
+ &sr_tz_ops);
+ if (IS_ERR(tmon->tz))
+ return PTR_ERR(tmon->tz);
+
+ dev_dbg(dev, "thermal sensor %d registered\n", i);
+ }
+ platform_set_drvdata(pdev, sr_thermal);
+
+ return 0;
+}
+
+static const struct of_device_id sr_thermal_of_match[] = {
+ { .compatible = "brcm,sr-thermal", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, sr_thermal_of_match);
+
+static const struct acpi_device_id sr_thermal_acpi_ids[] = {
+ { .id = "BRCM0500" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(acpi, sr_thermal_acpi_ids);
+
+static struct platform_driver sr_thermal_driver = {
+ .probe = sr_thermal_probe,
+ .driver = {
+ .name = "sr-thermal",
+ .of_match_table = sr_thermal_of_match,
+ .acpi_match_table = ACPI_PTR(sr_thermal_acpi_ids),
+ },
+};
+module_platform_driver(sr_thermal_driver);
+
+MODULE_AUTHOR("Pramod Kumar <pramod.kumar@broadcom.com>");
+MODULE_DESCRIPTION("Stingray thermal driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c
index 6fff16113628..f7c1f49ec87f 100644
--- a/drivers/thermal/cpu_cooling.c
+++ b/drivers/thermal/cpu_cooling.c
@@ -536,12 +536,11 @@ static int cpufreq_power2state(struct thermal_cooling_device *cdev,
struct thermal_zone_device *tz, u32 power,
unsigned long *state)
{
- unsigned int cur_freq, target_freq;
+ unsigned int target_freq;
u32 last_load, normalised_power;
struct cpufreq_cooling_device *cpufreq_cdev = cdev->devdata;
struct cpufreq_policy *policy = cpufreq_cdev->policy;
- cur_freq = cpufreq_quick_get(policy->cpu);
power = power > 0 ? power : 0;
last_load = cpufreq_cdev->last_load ?: 1;
normalised_power = (power * 100) / last_load;
diff --git a/drivers/thermal/intel/int340x_thermal/int3400_thermal.c b/drivers/thermal/intel/int340x_thermal/int3400_thermal.c
index 61ca7ce3624e..5f3ed24e26ec 100644
--- a/drivers/thermal/intel/int340x_thermal/int3400_thermal.c
+++ b/drivers/thermal/intel/int340x_thermal/int3400_thermal.c
@@ -22,6 +22,13 @@ enum int3400_thermal_uuid {
INT3400_THERMAL_PASSIVE_1,
INT3400_THERMAL_ACTIVE,
INT3400_THERMAL_CRITICAL,
+ INT3400_THERMAL_ADAPTIVE_PERFORMANCE,
+ INT3400_THERMAL_EMERGENCY_CALL_MODE,
+ INT3400_THERMAL_PASSIVE_2,
+ INT3400_THERMAL_POWER_BOSS,
+ INT3400_THERMAL_VIRTUAL_SENSOR,
+ INT3400_THERMAL_COOLING_MODE,
+ INT3400_THERMAL_HARDWARE_DUTY_CYCLING,
INT3400_THERMAL_MAXIMUM_UUID,
};
@@ -29,6 +36,13 @@ static char *int3400_thermal_uuids[INT3400_THERMAL_MAXIMUM_UUID] = {
"42A441D6-AE6A-462b-A84B-4A8CE79027D3",
"3A95C389-E4B8-4629-A526-C52C88626BAE",
"97C68AE7-15FA-499c-B8C9-5DA81D606E0A",
+ "63BE270F-1C11-48FD-A6F7-3AF253FF3E2D",
+ "5349962F-71E6-431D-9AE8-0A635B710AEE",
+ "9E04115A-AE87-4D1C-9500-0F3E340BFE75",
+ "F5A35014-C209-46A4-993A-EB56DE7530A1",
+ "6ED722A7-9240-48A5-B479-31EEF723D7CF",
+ "16CAF1B7-DD38-40ED-B1C1-1B8A1913D531",
+ "BE84BABF-C4D4-403D-B495-3128FD44dAC1",
};
struct int3400_thermal_priv {
@@ -299,10 +313,9 @@ static int int3400_thermal_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, priv);
- if (priv->uuid_bitmap & 1 << INT3400_THERMAL_PASSIVE_1) {
- int3400_thermal_ops.get_mode = int3400_thermal_get_mode;
- int3400_thermal_ops.set_mode = int3400_thermal_set_mode;
- }
+ int3400_thermal_ops.get_mode = int3400_thermal_get_mode;
+ int3400_thermal_ops.set_mode = int3400_thermal_set_mode;
+
priv->thermal = thermal_zone_device_register("INT3400 Thermal", 0, 0,
priv, &int3400_thermal_ops,
&int3400_thermal_params, 0, 0);
diff --git a/drivers/thermal/intel/intel_powerclamp.c b/drivers/thermal/intel/intel_powerclamp.c
index 7571f7c2e7c9..ac7256b5f020 100644
--- a/drivers/thermal/intel/intel_powerclamp.c
+++ b/drivers/thermal/intel/intel_powerclamp.c
@@ -101,7 +101,7 @@ struct powerclamp_worker_data {
bool clamping;
};
-static struct powerclamp_worker_data * __percpu worker_data;
+static struct powerclamp_worker_data __percpu *worker_data;
static struct thermal_cooling_device *cooling_dev;
static unsigned long *cpu_clamping_mask; /* bit map for tracking per cpu
* clamping kthread worker
@@ -494,7 +494,7 @@ static void start_power_clamp_worker(unsigned long cpu)
struct powerclamp_worker_data *w_data = per_cpu_ptr(worker_data, cpu);
struct kthread_worker *worker;
- worker = kthread_create_worker_on_cpu(cpu, 0, "kidle_inject/%ld", cpu);
+ worker = kthread_create_worker_on_cpu(cpu, 0, "kidle_inj/%ld", cpu);
if (IS_ERR(worker))
return;
diff --git a/drivers/thermal/mtk_thermal.c b/drivers/thermal/mtk_thermal.c
index 0691f260f6ea..e4ea7f6aef20 100644
--- a/drivers/thermal/mtk_thermal.c
+++ b/drivers/thermal/mtk_thermal.c
@@ -71,6 +71,15 @@
#define TEMP_SPARE0 0x0f0
+#define TEMP_ADCPNP0_1 0x148
+#define TEMP_ADCPNP1_1 0x14c
+#define TEMP_ADCPNP2_1 0x150
+#define TEMP_MSR0_1 0x190
+#define TEMP_MSR1_1 0x194
+#define TEMP_MSR2_1 0x198
+#define TEMP_ADCPNP3_1 0x1b4
+#define TEMP_MSR3_1 0x1B8
+
#define PTPCORESEL 0x400
#define TEMP_MONCTL1_PERIOD_UNIT(x) ((x) & 0x3ff)
@@ -105,24 +114,42 @@
/* The number of sensing points per bank */
#define MT8173_NUM_SENSORS_PER_ZONE 4
+/* The number of controller in the MT8173 */
+#define MT8173_NUM_CONTROLLER 1
+
+/* The calibration coefficient of sensor */
+#define MT8173_CALIBRATION 165
+
/*
* Layout of the fuses providing the calibration data
- * These macros could be used for MT8173, MT2701, and MT2712.
+ * These macros could be used for MT8183, MT8173, MT2701, and MT2712.
+ * MT8183 has 6 sensors and needs 6 VTS calibration data.
* MT8173 has 5 sensors and needs 5 VTS calibration data.
* MT2701 has 3 sensors and needs 3 VTS calibration data.
* MT2712 has 4 sensors and needs 4 VTS calibration data.
*/
-#define MT8173_CALIB_BUF0_VALID BIT(0)
-#define MT8173_CALIB_BUF1_ADC_GE(x) (((x) >> 22) & 0x3ff)
-#define MT8173_CALIB_BUF0_VTS_TS1(x) (((x) >> 17) & 0x1ff)
-#define MT8173_CALIB_BUF0_VTS_TS2(x) (((x) >> 8) & 0x1ff)
-#define MT8173_CALIB_BUF1_VTS_TS3(x) (((x) >> 0) & 0x1ff)
-#define MT8173_CALIB_BUF2_VTS_TS4(x) (((x) >> 23) & 0x1ff)
-#define MT8173_CALIB_BUF2_VTS_TSABB(x) (((x) >> 14) & 0x1ff)
-#define MT8173_CALIB_BUF0_DEGC_CALI(x) (((x) >> 1) & 0x3f)
-#define MT8173_CALIB_BUF0_O_SLOPE(x) (((x) >> 26) & 0x3f)
-#define MT8173_CALIB_BUF0_O_SLOPE_SIGN(x) (((x) >> 7) & 0x1)
-#define MT8173_CALIB_BUF1_ID(x) (((x) >> 9) & 0x1)
+#define CALIB_BUF0_VALID BIT(0)
+#define CALIB_BUF1_ADC_GE(x) (((x) >> 22) & 0x3ff)
+#define CALIB_BUF0_VTS_TS1(x) (((x) >> 17) & 0x1ff)
+#define CALIB_BUF0_VTS_TS2(x) (((x) >> 8) & 0x1ff)
+#define CALIB_BUF1_VTS_TS3(x) (((x) >> 0) & 0x1ff)
+#define CALIB_BUF2_VTS_TS4(x) (((x) >> 23) & 0x1ff)
+#define CALIB_BUF2_VTS_TS5(x) (((x) >> 5) & 0x1ff)
+#define CALIB_BUF2_VTS_TSABB(x) (((x) >> 14) & 0x1ff)
+#define CALIB_BUF0_DEGC_CALI(x) (((x) >> 1) & 0x3f)
+#define CALIB_BUF0_O_SLOPE(x) (((x) >> 26) & 0x3f)
+#define CALIB_BUF0_O_SLOPE_SIGN(x) (((x) >> 7) & 0x1)
+#define CALIB_BUF1_ID(x) (((x) >> 9) & 0x1)
+
+enum {
+ VTS1,
+ VTS2,
+ VTS3,
+ VTS4,
+ VTS5,
+ VTSABB,
+ MAX_NUM_VTS,
+};
/* MT2701 thermal sensors */
#define MT2701_TS1 0
@@ -138,6 +165,12 @@
/* The number of sensing points per bank */
#define MT2701_NUM_SENSORS_PER_ZONE 3
+/* The number of controller in the MT2701 */
+#define MT2701_NUM_CONTROLLER 1
+
+/* The calibration coefficient of sensor */
+#define MT2701_CALIBRATION 165
+
/* MT2712 thermal sensors */
#define MT2712_TS1 0
#define MT2712_TS2 1
@@ -153,11 +186,47 @@
/* The number of sensing points per bank */
#define MT2712_NUM_SENSORS_PER_ZONE 4
+/* The number of controller in the MT2712 */
+#define MT2712_NUM_CONTROLLER 1
+
+/* The calibration coefficient of sensor */
+#define MT2712_CALIBRATION 165
+
#define MT7622_TEMP_AUXADC_CHANNEL 11
#define MT7622_NUM_SENSORS 1
#define MT7622_NUM_ZONES 1
#define MT7622_NUM_SENSORS_PER_ZONE 1
#define MT7622_TS1 0
+#define MT7622_NUM_CONTROLLER 1
+
+/* The maximum number of banks */
+#define MAX_NUM_ZONES 8
+
+/* The calibration coefficient of sensor */
+#define MT7622_CALIBRATION 165
+
+/* MT8183 thermal sensors */
+#define MT8183_TS1 0
+#define MT8183_TS2 1
+#define MT8183_TS3 2
+#define MT8183_TS4 3
+#define MT8183_TS5 4
+#define MT8183_TSABB 5
+
+/* AUXADC channel is used for the temperature sensors */
+#define MT8183_TEMP_AUXADC_CHANNEL 11
+
+/* The total number of temperature sensors in the MT8183 */
+#define MT8183_NUM_SENSORS 6
+
+/* The number of sensing points per bank */
+#define MT8183_NUM_SENSORS_PER_ZONE 6
+
+/* The number of controller in the MT8183 */
+#define MT8183_NUM_CONTROLLER 2
+
+/* The calibration coefficient of sensor */
+#define MT8183_CALIBRATION 153
struct mtk_thermal;
@@ -175,10 +244,15 @@ struct mtk_thermal_data {
s32 num_banks;
s32 num_sensors;
s32 auxadc_channel;
+ const int *vts_index;
const int *sensor_mux_values;
const int *msr;
const int *adcpnp;
- struct thermal_bank_cfg bank_data[];
+ const int cali_val;
+ const int num_controller;
+ const int *controller_offset;
+ bool need_switch_bank;
+ struct thermal_bank_cfg bank_data[MAX_NUM_ZONES];
};
struct mtk_thermal {
@@ -194,10 +268,31 @@ struct mtk_thermal {
s32 adc_ge;
s32 degc_cali;
s32 o_slope;
- s32 vts[MT8173_NUM_SENSORS];
+ s32 vts[MAX_NUM_VTS];
const struct mtk_thermal_data *conf;
- struct mtk_thermal_bank banks[];
+ struct mtk_thermal_bank banks[MAX_NUM_ZONES];
+};
+
+/* MT8183 thermal sensor data */
+static const int mt8183_bank_data[MT8183_NUM_SENSORS] = {
+ MT8183_TS1, MT8183_TS2, MT8183_TS3, MT8183_TS4, MT8183_TS5, MT8183_TSABB
+};
+
+static const int mt8183_msr[MT8183_NUM_SENSORS_PER_ZONE] = {
+ TEMP_MSR0_1, TEMP_MSR1_1, TEMP_MSR2_1, TEMP_MSR1, TEMP_MSR0, TEMP_MSR3_1
+};
+
+static const int mt8183_adcpnp[MT8183_NUM_SENSORS_PER_ZONE] = {
+ TEMP_ADCPNP0_1, TEMP_ADCPNP1_1, TEMP_ADCPNP2_1,
+ TEMP_ADCPNP1, TEMP_ADCPNP0, TEMP_ADCPNP3_1
+};
+
+static const int mt8183_mux_values[MT8183_NUM_SENSORS] = { 0, 1, 2, 3, 4, 0 };
+static const int mt8183_tc_offset[MT8183_NUM_CONTROLLER] = {0x0, 0x100};
+
+static const int mt8183_vts_index[MT8183_NUM_SENSORS] = {
+ VTS1, VTS2, VTS3, VTS4, VTS5, VTSABB
};
/* MT8173 thermal sensor data */
@@ -217,6 +312,11 @@ static const int mt8173_adcpnp[MT8173_NUM_SENSORS_PER_ZONE] = {
};
static const int mt8173_mux_values[MT8173_NUM_SENSORS] = { 0, 1, 2, 3, 16 };
+static const int mt8173_tc_offset[MT8173_NUM_CONTROLLER] = { 0x0, };
+
+static const int mt8173_vts_index[MT8173_NUM_SENSORS] = {
+ VTS1, VTS2, VTS3, VTS4, VTSABB
+};
/* MT2701 thermal sensor data */
static const int mt2701_bank_data[MT2701_NUM_SENSORS] = {
@@ -232,6 +332,11 @@ static const int mt2701_adcpnp[MT2701_NUM_SENSORS_PER_ZONE] = {
};
static const int mt2701_mux_values[MT2701_NUM_SENSORS] = { 0, 1, 16 };
+static const int mt2701_tc_offset[MT2701_NUM_CONTROLLER] = { 0x0, };
+
+static const int mt2701_vts_index[MT2701_NUM_SENSORS] = {
+ VTS1, VTS2, VTS3
+};
/* MT2712 thermal sensor data */
static const int mt2712_bank_data[MT2712_NUM_SENSORS] = {
@@ -247,12 +352,19 @@ static const int mt2712_adcpnp[MT2712_NUM_SENSORS_PER_ZONE] = {
};
static const int mt2712_mux_values[MT2712_NUM_SENSORS] = { 0, 1, 2, 3 };
+static const int mt2712_tc_offset[MT2712_NUM_CONTROLLER] = { 0x0, };
+
+static const int mt2712_vts_index[MT2712_NUM_SENSORS] = {
+ VTS1, VTS2, VTS3, VTS4
+};
/* MT7622 thermal sensor data */
static const int mt7622_bank_data[MT7622_NUM_SENSORS] = { MT7622_TS1, };
static const int mt7622_msr[MT7622_NUM_SENSORS_PER_ZONE] = { TEMP_MSR0, };
static const int mt7622_adcpnp[MT7622_NUM_SENSORS_PER_ZONE] = { TEMP_ADCPNP0, };
static const int mt7622_mux_values[MT7622_NUM_SENSORS] = { 0, };
+static const int mt7622_vts_index[MT7622_NUM_SENSORS] = { VTS1 };
+static const int mt7622_tc_offset[MT7622_NUM_CONTROLLER] = { 0x0, };
/**
* The MT8173 thermal controller has four banks. Each bank can read up to
@@ -271,6 +383,11 @@ static const struct mtk_thermal_data mt8173_thermal_data = {
.auxadc_channel = MT8173_TEMP_AUXADC_CHANNEL,
.num_banks = MT8173_NUM_ZONES,
.num_sensors = MT8173_NUM_SENSORS,
+ .vts_index = mt8173_vts_index,
+ .cali_val = MT8173_CALIBRATION,
+ .num_controller = MT8173_NUM_CONTROLLER,
+ .controller_offset = mt8173_tc_offset,
+ .need_switch_bank = true,
.bank_data = {
{
.num_sensors = 2,
@@ -305,6 +422,11 @@ static const struct mtk_thermal_data mt2701_thermal_data = {
.auxadc_channel = MT2701_TEMP_AUXADC_CHANNEL,
.num_banks = 1,
.num_sensors = MT2701_NUM_SENSORS,
+ .vts_index = mt2701_vts_index,
+ .cali_val = MT2701_CALIBRATION,
+ .num_controller = MT2701_NUM_CONTROLLER,
+ .controller_offset = mt2701_tc_offset,
+ .need_switch_bank = true,
.bank_data = {
{
.num_sensors = 3,
@@ -330,6 +452,11 @@ static const struct mtk_thermal_data mt2712_thermal_data = {
.auxadc_channel = MT2712_TEMP_AUXADC_CHANNEL,
.num_banks = 1,
.num_sensors = MT2712_NUM_SENSORS,
+ .vts_index = mt2712_vts_index,
+ .cali_val = MT2712_CALIBRATION,
+ .num_controller = MT2712_NUM_CONTROLLER,
+ .controller_offset = mt2712_tc_offset,
+ .need_switch_bank = true,
.bank_data = {
{
.num_sensors = 4,
@@ -349,6 +476,11 @@ static const struct mtk_thermal_data mt7622_thermal_data = {
.auxadc_channel = MT7622_TEMP_AUXADC_CHANNEL,
.num_banks = MT7622_NUM_ZONES,
.num_sensors = MT7622_NUM_SENSORS,
+ .vts_index = mt7622_vts_index,
+ .cali_val = MT7622_CALIBRATION,
+ .num_controller = MT7622_NUM_CONTROLLER,
+ .controller_offset = mt7622_tc_offset,
+ .need_switch_bank = true,
.bank_data = {
{
.num_sensors = 1,
@@ -361,6 +493,39 @@ static const struct mtk_thermal_data mt7622_thermal_data = {
};
/**
+ * The MT8183 thermal controller has one bank for the current SW framework.
+ * The MT8183 has a total of 6 temperature sensors.
+ * There are two thermal controller to control the six sensor.
+ * The first one bind 2 sensor, and the other bind 4 sensors.
+ * The thermal core only gets the maximum temperature of all sensor, so
+ * the bank concept wouldn't be necessary here. However, the SVS (Smart
+ * Voltage Scaling) unit makes its decisions based on the same bank
+ * data, and this indeed needs the temperatures of the individual banks
+ * for making better decisions.
+ */
+
+static const struct mtk_thermal_data mt8183_thermal_data = {
+ .auxadc_channel = MT8183_TEMP_AUXADC_CHANNEL,
+ .num_banks = MT8183_NUM_SENSORS_PER_ZONE,
+ .num_sensors = MT8183_NUM_SENSORS,
+ .vts_index = mt8183_vts_index,
+ .cali_val = MT8183_CALIBRATION,
+ .num_controller = MT8183_NUM_CONTROLLER,
+ .controller_offset = mt8183_tc_offset,
+ .need_switch_bank = false,
+ .bank_data = {
+ {
+ .num_sensors = 6,
+ .sensors = mt8183_bank_data,
+ },
+ },
+
+ .msr = mt8183_msr,
+ .adcpnp = mt8183_adcpnp,
+ .sensor_mux_values = mt8183_mux_values,
+};
+
+/**
* raw_to_mcelsius - convert a raw ADC value to mcelsius
* @mt: The thermal controller
* @raw: raw ADC value
@@ -375,7 +540,7 @@ static int raw_to_mcelsius(struct mtk_thermal *mt, int sensno, s32 raw)
raw &= 0xfff;
tmp = 203450520 << 3;
- tmp /= 165 + mt->o_slope;
+ tmp /= mt->conf->cali_val + mt->o_slope;
tmp /= 10000 + mt->adc_ge;
tmp *= raw - mt->vts[sensno] - 3350;
tmp >>= 3;
@@ -395,12 +560,14 @@ static void mtk_thermal_get_bank(struct mtk_thermal_bank *bank)
struct mtk_thermal *mt = bank->mt;
u32 val;
- mutex_lock(&mt->lock);
+ if (mt->conf->need_switch_bank) {
+ mutex_lock(&mt->lock);
- val = readl(mt->thermal_base + PTPCORESEL);
- val &= ~0xf;
- val |= bank->id;
- writel(val, mt->thermal_base + PTPCORESEL);
+ val = readl(mt->thermal_base + PTPCORESEL);
+ val &= ~0xf;
+ val |= bank->id;
+ writel(val, mt->thermal_base + PTPCORESEL);
+ }
}
/**
@@ -413,7 +580,8 @@ static void mtk_thermal_put_bank(struct mtk_thermal_bank *bank)
{
struct mtk_thermal *mt = bank->mt;
- mutex_unlock(&mt->lock);
+ if (mt->conf->need_switch_bank)
+ mutex_unlock(&mt->lock);
}
/**
@@ -431,7 +599,8 @@ static int mtk_thermal_bank_temperature(struct mtk_thermal_bank *bank)
u32 raw;
for (i = 0; i < conf->bank_data[bank->id].num_sensors; i++) {
- raw = readl(mt->thermal_base + conf->msr[i]);
+ raw = readl(mt->thermal_base +
+ conf->msr[conf->bank_data[bank->id].sensors[i]]);
temp = raw_to_mcelsius(mt,
conf->bank_data[bank->id].sensors[i],
@@ -478,19 +647,23 @@ static const struct thermal_zone_of_device_ops mtk_thermal_ops = {
};
static void mtk_thermal_init_bank(struct mtk_thermal *mt, int num,
- u32 apmixed_phys_base, u32 auxadc_phys_base)
+ u32 apmixed_phys_base, u32 auxadc_phys_base,
+ int ctrl_id)
{
struct mtk_thermal_bank *bank = &mt->banks[num];
const struct mtk_thermal_data *conf = mt->conf;
int i;
+ int offset = mt->conf->controller_offset[ctrl_id];
+ void __iomem *controller_base = mt->thermal_base + offset;
+
bank->id = num;
bank->mt = mt;
mtk_thermal_get_bank(bank);
/* bus clock 66M counting unit is 12 * 15.15ns * 256 = 46.540us */
- writel(TEMP_MONCTL1_PERIOD_UNIT(12), mt->thermal_base + TEMP_MONCTL1);
+ writel(TEMP_MONCTL1_PERIOD_UNIT(12), controller_base + TEMP_MONCTL1);
/*
* filt interval is 1 * 46.540us = 46.54us,
@@ -498,21 +671,21 @@ static void mtk_thermal_init_bank(struct mtk_thermal *mt, int num,
*/
writel(TEMP_MONCTL2_FILTER_INTERVAL(1) |
TEMP_MONCTL2_SENSOR_INTERVAL(429),
- mt->thermal_base + TEMP_MONCTL2);
+ controller_base + TEMP_MONCTL2);
/* poll is set to 10u */
writel(TEMP_AHBPOLL_ADC_POLL_INTERVAL(768),
- mt->thermal_base + TEMP_AHBPOLL);
+ controller_base + TEMP_AHBPOLL);
/* temperature sampling control, 1 sample */
- writel(0x0, mt->thermal_base + TEMP_MSRCTL0);
+ writel(0x0, controller_base + TEMP_MSRCTL0);
/* exceed this polling time, IRQ would be inserted */
- writel(0xffffffff, mt->thermal_base + TEMP_AHBTO);
+ writel(0xffffffff, controller_base + TEMP_AHBTO);
/* number of interrupts per event, 1 is enough */
- writel(0x0, mt->thermal_base + TEMP_MONIDET0);
- writel(0x0, mt->thermal_base + TEMP_MONIDET1);
+ writel(0x0, controller_base + TEMP_MONIDET0);
+ writel(0x0, controller_base + TEMP_MONIDET1);
/*
* The MT8173 thermal controller does not have its own ADC. Instead it
@@ -527,55 +700,56 @@ static void mtk_thermal_init_bank(struct mtk_thermal *mt, int num,
* this value will be stored to TEMP_PNPMUXADDR (TEMP_SPARE0)
* automatically by hw
*/
- writel(BIT(conf->auxadc_channel), mt->thermal_base + TEMP_ADCMUX);
+ writel(BIT(conf->auxadc_channel), controller_base + TEMP_ADCMUX);
/* AHB address for auxadc mux selection */
writel(auxadc_phys_base + AUXADC_CON1_CLR_V,
- mt->thermal_base + TEMP_ADCMUXADDR);
+ controller_base + TEMP_ADCMUXADDR);
/* AHB address for pnp sensor mux selection */
writel(apmixed_phys_base + APMIXED_SYS_TS_CON1,
- mt->thermal_base + TEMP_PNPMUXADDR);
+ controller_base + TEMP_PNPMUXADDR);
/* AHB value for auxadc enable */
- writel(BIT(conf->auxadc_channel), mt->thermal_base + TEMP_ADCEN);
+ writel(BIT(conf->auxadc_channel), controller_base + TEMP_ADCEN);
/* AHB address for auxadc enable (channel 0 immediate mode selected) */
writel(auxadc_phys_base + AUXADC_CON1_SET_V,
- mt->thermal_base + TEMP_ADCENADDR);
+ controller_base + TEMP_ADCENADDR);
/* AHB address for auxadc valid bit */
writel(auxadc_phys_base + AUXADC_DATA(conf->auxadc_channel),
- mt->thermal_base + TEMP_ADCVALIDADDR);
+ controller_base + TEMP_ADCVALIDADDR);
/* AHB address for auxadc voltage output */
writel(auxadc_phys_base + AUXADC_DATA(conf->auxadc_channel),
- mt->thermal_base + TEMP_ADCVOLTADDR);
+ controller_base + TEMP_ADCVOLTADDR);
/* read valid & voltage are at the same register */
- writel(0x0, mt->thermal_base + TEMP_RDCTRL);
+ writel(0x0, controller_base + TEMP_RDCTRL);
/* indicate where the valid bit is */
writel(TEMP_ADCVALIDMASK_VALID_HIGH | TEMP_ADCVALIDMASK_VALID_POS(12),
- mt->thermal_base + TEMP_ADCVALIDMASK);
+ controller_base + TEMP_ADCVALIDMASK);
/* no shift */
- writel(0x0, mt->thermal_base + TEMP_ADCVOLTAGESHIFT);
+ writel(0x0, controller_base + TEMP_ADCVOLTAGESHIFT);
/* enable auxadc mux write transaction */
writel(TEMP_ADCWRITECTRL_ADC_MUX_WRITE,
- mt->thermal_base + TEMP_ADCWRITECTRL);
+ controller_base + TEMP_ADCWRITECTRL);
for (i = 0; i < conf->bank_data[num].num_sensors; i++)
writel(conf->sensor_mux_values[conf->bank_data[num].sensors[i]],
- mt->thermal_base + conf->adcpnp[i]);
+ mt->thermal_base +
+ conf->adcpnp[conf->bank_data[num].sensors[i]]);
writel((1 << conf->bank_data[num].num_sensors) - 1,
- mt->thermal_base + TEMP_MONCTL0);
+ controller_base + TEMP_MONCTL0);
writel(TEMP_ADCWRITECTRL_ADC_PNP_WRITE |
TEMP_ADCWRITECTRL_ADC_MUX_WRITE,
- mt->thermal_base + TEMP_ADCWRITECTRL);
+ controller_base + TEMP_ADCWRITECTRL);
mtk_thermal_put_bank(bank);
}
@@ -627,19 +801,40 @@ static int mtk_thermal_get_calibration_data(struct device *dev,
goto out;
}
- if (buf[0] & MT8173_CALIB_BUF0_VALID) {
- mt->adc_ge = MT8173_CALIB_BUF1_ADC_GE(buf[1]);
- mt->vts[MT8173_TS1] = MT8173_CALIB_BUF0_VTS_TS1(buf[0]);
- mt->vts[MT8173_TS2] = MT8173_CALIB_BUF0_VTS_TS2(buf[0]);
- mt->vts[MT8173_TS3] = MT8173_CALIB_BUF1_VTS_TS3(buf[1]);
- mt->vts[MT8173_TS4] = MT8173_CALIB_BUF2_VTS_TS4(buf[2]);
- mt->vts[MT8173_TSABB] = MT8173_CALIB_BUF2_VTS_TSABB(buf[2]);
- mt->degc_cali = MT8173_CALIB_BUF0_DEGC_CALI(buf[0]);
- if (MT8173_CALIB_BUF1_ID(buf[1]) &
- MT8173_CALIB_BUF0_O_SLOPE_SIGN(buf[0]))
- mt->o_slope = -MT8173_CALIB_BUF0_O_SLOPE(buf[0]);
+ if (buf[0] & CALIB_BUF0_VALID) {
+ mt->adc_ge = CALIB_BUF1_ADC_GE(buf[1]);
+
+ for (i = 0; i < mt->conf->num_sensors; i++) {
+ switch (mt->conf->vts_index[i]) {
+ case VTS1:
+ mt->vts[VTS1] = CALIB_BUF0_VTS_TS1(buf[0]);
+ break;
+ case VTS2:
+ mt->vts[VTS2] = CALIB_BUF0_VTS_TS2(buf[0]);
+ break;
+ case VTS3:
+ mt->vts[VTS3] = CALIB_BUF1_VTS_TS3(buf[1]);
+ break;
+ case VTS4:
+ mt->vts[VTS4] = CALIB_BUF2_VTS_TS4(buf[2]);
+ break;
+ case VTS5:
+ mt->vts[VTS5] = CALIB_BUF2_VTS_TS5(buf[2]);
+ break;
+ case VTSABB:
+ mt->vts[VTSABB] = CALIB_BUF2_VTS_TSABB(buf[2]);
+ break;
+ default:
+ break;
+ }
+ }
+
+ mt->degc_cali = CALIB_BUF0_DEGC_CALI(buf[0]);
+ if (CALIB_BUF1_ID(buf[1]) &
+ CALIB_BUF0_O_SLOPE_SIGN(buf[0]))
+ mt->o_slope = -CALIB_BUF0_O_SLOPE(buf[0]);
else
- mt->o_slope = MT8173_CALIB_BUF0_O_SLOPE(buf[0]);
+ mt->o_slope = CALIB_BUF0_O_SLOPE(buf[0]);
} else {
dev_info(dev, "Device not calibrated, using default calibration values\n");
}
@@ -666,6 +861,10 @@ static const struct of_device_id mtk_thermal_of_match[] = {
{
.compatible = "mediatek,mt7622-thermal",
.data = (void *)&mt7622_thermal_data,
+ },
+ {
+ .compatible = "mediatek,mt8183-thermal",
+ .data = (void *)&mt8183_thermal_data,
}, {
},
};
@@ -673,7 +872,7 @@ MODULE_DEVICE_TABLE(of, mtk_thermal_of_match);
static int mtk_thermal_probe(struct platform_device *pdev)
{
- int ret, i;
+ int ret, i, ctrl_id;
struct device_node *auxadc, *apmixedsys, *np = pdev->dev.of_node;
struct mtk_thermal *mt;
struct resource *res;
@@ -753,9 +952,10 @@ static int mtk_thermal_probe(struct platform_device *pdev)
goto err_disable_clk_auxadc;
}
- for (i = 0; i < mt->conf->num_banks; i++)
- mtk_thermal_init_bank(mt, i, apmixed_phys_base,
- auxadc_phys_base);
+ for (ctrl_id = 0; ctrl_id < mt->conf->num_controller ; ctrl_id++)
+ for (i = 0; i < mt->conf->num_banks; i++)
+ mtk_thermal_init_bank(mt, i, apmixed_phys_base,
+ auxadc_phys_base, ctrl_id);
platform_set_drvdata(pdev, mt);
@@ -797,6 +997,7 @@ static struct platform_driver mtk_thermal_driver = {
module_platform_driver(mtk_thermal_driver);
+MODULE_AUTHOR("Michael Kao <michael.kao@mediatek.com>");
MODULE_AUTHOR("Louis Yu <louis.yu@mediatek.com>");
MODULE_AUTHOR("Dawei Chien <dawei.chien@mediatek.com>");
MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
diff --git a/drivers/thermal/qcom/tsens-common.c b/drivers/thermal/qcom/tsens-common.c
index 78652cac7f3d..f80c73f11740 100644
--- a/drivers/thermal/qcom/tsens-common.c
+++ b/drivers/thermal/qcom/tsens-common.c
@@ -144,13 +144,17 @@ int __init init_common(struct tsens_device *tmdev)
tmdev->tm_offset = 0;
res = platform_get_resource(op, IORESOURCE_MEM, 1);
srot_base = devm_ioremap_resource(&op->dev, res);
- if (IS_ERR(srot_base))
- return PTR_ERR(srot_base);
+ if (IS_ERR(srot_base)) {
+ ret = PTR_ERR(srot_base);
+ goto err_put_device;
+ }
tmdev->srot_map = devm_regmap_init_mmio(tmdev->dev, srot_base,
&tsens_srot_config);
- if (IS_ERR(tmdev->srot_map))
- return PTR_ERR(tmdev->srot_map);
+ if (IS_ERR(tmdev->srot_map)) {
+ ret = PTR_ERR(tmdev->srot_map);
+ goto err_put_device;
+ }
} else {
/* old DTs where SROT and TM were in a contiguous 2K block */
@@ -159,22 +163,31 @@ int __init init_common(struct tsens_device *tmdev)
res = platform_get_resource(op, IORESOURCE_MEM, 0);
tm_base = devm_ioremap_resource(&op->dev, res);
- if (IS_ERR(tm_base))
- return PTR_ERR(tm_base);
+ if (IS_ERR(tm_base)) {
+ ret = PTR_ERR(tm_base);
+ goto err_put_device;
+ }
tmdev->tm_map = devm_regmap_init_mmio(tmdev->dev, tm_base, &tsens_config);
- if (IS_ERR(tmdev->tm_map))
- return PTR_ERR(tmdev->tm_map);
+ if (IS_ERR(tmdev->tm_map)) {
+ ret = PTR_ERR(tmdev->tm_map);
+ goto err_put_device;
+ }
if (tmdev->srot_map) {
ret = regmap_read(tmdev->srot_map, ctrl_offset, &code);
if (ret)
- return ret;
+ goto err_put_device;
if (!(code & TSENS_EN)) {
dev_err(tmdev->dev, "tsens device is not enabled\n");
- return -ENODEV;
+ ret = -ENODEV;
+ goto err_put_device;
}
}
return 0;
+
+err_put_device:
+ put_device(&op->dev);
+ return ret;
}
diff --git a/drivers/thermal/qoriq_thermal.c b/drivers/thermal/qoriq_thermal.c
index 18c711b19514..3b5f5b3fb1bc 100644
--- a/drivers/thermal/qoriq_thermal.c
+++ b/drivers/thermal/qoriq_thermal.c
@@ -59,14 +59,21 @@ struct qoriq_tmu_regs {
u32 ttr3cr; /* Temperature Range 3 Control Register */
};
+struct qoriq_tmu_data;
+
/*
* Thermal zone data
*/
+struct qoriq_sensor {
+ struct thermal_zone_device *tzd;
+ struct qoriq_tmu_data *qdata;
+ int id;
+};
+
struct qoriq_tmu_data {
- struct thermal_zone_device *tz;
struct qoriq_tmu_regs __iomem *regs;
- int sensor_id;
bool little_endian;
+ struct qoriq_sensor *sensor[SITES_MAX];
};
static void tmu_write(struct qoriq_tmu_data *p, u32 val, void __iomem *addr)
@@ -87,48 +94,50 @@ static u32 tmu_read(struct qoriq_tmu_data *p, void __iomem *addr)
static int tmu_get_temp(void *p, int *temp)
{
+ struct qoriq_sensor *qsensor = p;
+ struct qoriq_tmu_data *qdata = qsensor->qdata;
u32 val;
- struct qoriq_tmu_data *data = p;
- val = tmu_read(data, &data->regs->site[data->sensor_id].tritsr);
+ val = tmu_read(qdata, &qdata->regs->site[qsensor->id].tritsr);
*temp = (val & 0xff) * 1000;
return 0;
}
-static int qoriq_tmu_get_sensor_id(void)
-{
- int ret, id;
- struct of_phandle_args sensor_specs;
- struct device_node *np, *sensor_np;
-
- np = of_find_node_by_name(NULL, "thermal-zones");
- if (!np)
- return -ENODEV;
-
- sensor_np = of_get_next_child(np, NULL);
- ret = of_parse_phandle_with_args(sensor_np, "thermal-sensors",
- "#thermal-sensor-cells",
- 0, &sensor_specs);
- if (ret) {
- of_node_put(np);
- of_node_put(sensor_np);
- return ret;
- }
+static const struct thermal_zone_of_device_ops tmu_tz_ops = {
+ .get_temp = tmu_get_temp,
+};
- if (sensor_specs.args_count >= 1) {
- id = sensor_specs.args[0];
- WARN(sensor_specs.args_count > 1,
- "%pOFn: too many cells in sensor specifier %d\n",
- sensor_specs.np, sensor_specs.args_count);
- } else {
- id = 0;
+static int qoriq_tmu_register_tmu_zone(struct platform_device *pdev)
+{
+ struct qoriq_tmu_data *qdata = platform_get_drvdata(pdev);
+ int id, sites = 0;
+
+ for (id = 0; id < SITES_MAX; id++) {
+ qdata->sensor[id] = devm_kzalloc(&pdev->dev,
+ sizeof(struct qoriq_sensor), GFP_KERNEL);
+ if (!qdata->sensor[id])
+ return -ENOMEM;
+
+ qdata->sensor[id]->id = id;
+ qdata->sensor[id]->qdata = qdata;
+ qdata->sensor[id]->tzd = devm_thermal_zone_of_sensor_register(
+ &pdev->dev, id, qdata->sensor[id], &tmu_tz_ops);
+ if (IS_ERR(qdata->sensor[id]->tzd)) {
+ if (PTR_ERR(qdata->sensor[id]->tzd) == -ENODEV)
+ continue;
+ else
+ return PTR_ERR(qdata->sensor[id]->tzd);
+ }
+
+ sites |= 0x1 << (15 - id);
}
- of_node_put(np);
- of_node_put(sensor_np);
+ /* Enable monitoring */
+ if (sites != 0)
+ tmu_write(qdata, sites | TMR_ME | TMR_ALPF, &qdata->regs->tmr);
- return id;
+ return 0;
}
static int qoriq_tmu_calibration(struct platform_device *pdev)
@@ -178,16 +187,11 @@ static void qoriq_tmu_init_device(struct qoriq_tmu_data *data)
tmu_write(data, TMR_DISABLE, &data->regs->tmr);
}
-static const struct thermal_zone_of_device_ops tmu_tz_ops = {
- .get_temp = tmu_get_temp,
-};
-
static int qoriq_tmu_probe(struct platform_device *pdev)
{
int ret;
struct qoriq_tmu_data *data;
struct device_node *np = pdev->dev.of_node;
- u32 site;
if (!np) {
dev_err(&pdev->dev, "Device OF-Node is NULL");
@@ -203,13 +207,6 @@ static int qoriq_tmu_probe(struct platform_device *pdev)
data->little_endian = of_property_read_bool(np, "little-endian");
- data->sensor_id = qoriq_tmu_get_sensor_id();
- if (data->sensor_id < 0) {
- dev_err(&pdev->dev, "Failed to get sensor id\n");
- ret = -ENODEV;
- goto err_iomap;
- }
-
data->regs = of_iomap(np, 0);
if (!data->regs) {
dev_err(&pdev->dev, "Failed to get memory region\n");
@@ -223,20 +220,13 @@ static int qoriq_tmu_probe(struct platform_device *pdev)
if (ret < 0)
goto err_tmu;
- data->tz = devm_thermal_zone_of_sensor_register(&pdev->dev,
- data->sensor_id,
- data, &tmu_tz_ops);
- if (IS_ERR(data->tz)) {
- ret = PTR_ERR(data->tz);
- dev_err(&pdev->dev,
- "Failed to register thermal zone device %d\n", ret);
- goto err_tmu;
+ ret = qoriq_tmu_register_tmu_zone(pdev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to register sensors\n");
+ ret = -ENODEV;
+ goto err_iomap;
}
- /* Enable monitoring */
- site = 0x1 << (15 - data->sensor_id);
- tmu_write(data, site | TMR_ME | TMR_ALPF, &data->regs->tmr);
-
return 0;
err_tmu:
diff --git a/drivers/thermal/rcar_gen3_thermal.c b/drivers/thermal/rcar_gen3_thermal.c
index 75786cc8e2f9..88fa41cf16e8 100644
--- a/drivers/thermal/rcar_gen3_thermal.c
+++ b/drivers/thermal/rcar_gen3_thermal.c
@@ -19,6 +19,7 @@
#include <linux/thermal.h>
#include "thermal_core.h"
+#include "thermal_hwmon.h"
/* Register offsets */
#define REG_GEN3_IRQSTR 0x04
@@ -337,6 +338,13 @@ static int rcar_gen3_thermal_remove(struct platform_device *pdev)
return 0;
}
+static void rcar_gen3_hwmon_action(void *data)
+{
+ struct thermal_zone_device *zone = data;
+
+ thermal_remove_hwmon_sysfs(zone);
+}
+
static int rcar_gen3_thermal_probe(struct platform_device *pdev)
{
struct rcar_gen3_thermal_priv *priv;
@@ -429,6 +437,17 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev)
if (ret < 0)
goto error_unregister;
+ tsc->zone->tzp->no_hwmon = false;
+ ret = thermal_add_hwmon_sysfs(tsc->zone);
+ if (ret)
+ goto error_unregister;
+
+ ret = devm_add_action(dev, rcar_gen3_hwmon_action, zone);
+ if (ret) {
+ rcar_gen3_hwmon_action(zone);
+ goto error_unregister;
+ }
+
dev_info(dev, "TSC%d: Loaded %d trip points\n", i, ret);
}
diff --git a/drivers/thermal/samsung/exynos_tmu.c b/drivers/thermal/samsung/exynos_tmu.c
index 48eef552cba4..fc9399d9c082 100644
--- a/drivers/thermal/samsung/exynos_tmu.c
+++ b/drivers/thermal/samsung/exynos_tmu.c
@@ -666,7 +666,7 @@ static int exynos_get_temp(void *p, int *temp)
struct exynos_tmu_data *data = p;
int value, ret = 0;
- if (!data || !data->tmu_read || !data->enabled)
+ if (!data || !data->tmu_read)
return -EINVAL;
else if (!data->enabled)
/*
diff --git a/drivers/thermal/tegra/soctherm.c b/drivers/thermal/tegra/soctherm.c
index 45b41b885f49..70043a28eb7a 100644
--- a/drivers/thermal/tegra/soctherm.c
+++ b/drivers/thermal/tegra/soctherm.c
@@ -488,9 +488,41 @@ static int tegra_thermctl_set_trip_temp(void *data, int trip, int temp)
return 0;
}
+static int tegra_thermctl_get_trend(void *data, int trip,
+ enum thermal_trend *trend)
+{
+ struct tegra_thermctl_zone *zone = data;
+ struct thermal_zone_device *tz = zone->tz;
+ int trip_temp, temp, last_temp, ret;
+
+ if (!tz)
+ return -EINVAL;
+
+ ret = tz->ops->get_trip_temp(zone->tz, trip, &trip_temp);
+ if (ret)
+ return ret;
+
+ temp = READ_ONCE(tz->temperature);
+ last_temp = READ_ONCE(tz->last_temperature);
+
+ if (temp > trip_temp) {
+ if (temp >= last_temp)
+ *trend = THERMAL_TREND_RAISING;
+ else
+ *trend = THERMAL_TREND_STABLE;
+ } else if (temp < trip_temp) {
+ *trend = THERMAL_TREND_DROPPING;
+ } else {
+ *trend = THERMAL_TREND_STABLE;
+ }
+
+ return 0;
+}
+
static const struct thermal_zone_of_device_ops tegra_of_thermal_ops = {
.get_temp = tegra_thermctl_get_temp,
.set_trip_temp = tegra_thermctl_set_trip_temp,
+ .get_trend = tegra_thermctl_get_trend,
};
static int get_hot_temp(struct thermal_zone_device *tz, int *trip, int *temp)
@@ -569,7 +601,7 @@ static int tegra_soctherm_set_hwtrips(struct device *dev,
set_throttle:
ret = get_hot_temp(tz, &trip, &temperature);
if (ret) {
- dev_warn(dev, "throttrip: %s: missing hot temperature\n",
+ dev_info(dev, "throttrip: %s: missing hot temperature\n",
sg->name);
return 0;
}
@@ -600,7 +632,7 @@ set_throttle:
}
if (i == THROTTLE_SIZE)
- dev_warn(dev, "throttrip: %s: missing throttle cdev\n",
+ dev_info(dev, "throttrip: %s: missing throttle cdev\n",
sg->name);
return 0;
@@ -1329,7 +1361,7 @@ static int tegra_soctherm_probe(struct platform_device *pdev)
}
tegra->thermctl_tzs = devm_kcalloc(&pdev->dev,
- soc->num_ttgs, sizeof(*z),
+ soc->num_ttgs, sizeof(z),
GFP_KERNEL);
if (!tegra->thermctl_tzs)
return -ENOMEM;
diff --git a/drivers/tty/serial/8250/8250_lpss.c b/drivers/tty/serial/8250/8250_lpss.c
index 98dbc796353f..53ca9ba6ab4b 100644
--- a/drivers/tty/serial/8250/8250_lpss.c
+++ b/drivers/tty/serial/8250/8250_lpss.c
@@ -153,7 +153,6 @@ static int byt_serial_setup(struct lpss8250 *lpss, struct uart_port *port)
#ifdef CONFIG_SERIAL_8250_DMA
static const struct dw_dma_platform_data qrk_serial_dma_pdata = {
.nr_channels = 2,
- .is_private = true,
.chan_allocation_order = CHAN_ALLOCATION_ASCENDING,
.chan_priority = CHAN_PRIORITY_ASCENDING,
.block_size = 4095,
diff --git a/drivers/tty/serial/ar933x_uart.c b/drivers/tty/serial/ar933x_uart.c
index db5df3d54818..3bdd56a1021b 100644
--- a/drivers/tty/serial/ar933x_uart.c
+++ b/drivers/tty/serial/ar933x_uart.c
@@ -49,11 +49,6 @@ struct ar933x_uart_port {
struct clk *clk;
};
-static inline bool ar933x_uart_console_enabled(void)
-{
- return IS_ENABLED(CONFIG_SERIAL_AR933X_CONSOLE);
-}
-
static inline unsigned int ar933x_uart_read(struct ar933x_uart_port *up,
int offset)
{
@@ -508,6 +503,7 @@ static const struct uart_ops ar933x_uart_ops = {
.verify_port = ar933x_uart_verify_port,
};
+#ifdef CONFIG_SERIAL_AR933X_CONSOLE
static struct ar933x_uart_port *
ar933x_console_ports[CONFIG_SERIAL_AR933X_NR_UARTS];
@@ -604,14 +600,7 @@ static struct console ar933x_uart_console = {
.index = -1,
.data = &ar933x_uart_driver,
};
-
-static void ar933x_uart_add_console_port(struct ar933x_uart_port *up)
-{
- if (!ar933x_uart_console_enabled())
- return;
-
- ar933x_console_ports[up->port.line] = up;
-}
+#endif /* CONFIG_SERIAL_AR933X_CONSOLE */
static struct uart_driver ar933x_uart_driver = {
.owner = THIS_MODULE,
@@ -700,7 +689,9 @@ static int ar933x_uart_probe(struct platform_device *pdev)
baud = ar933x_uart_get_baud(port->uartclk, 0, AR933X_UART_MAX_STEP);
up->max_baud = min_t(unsigned int, baud, AR933X_UART_MAX_BAUD);
- ar933x_uart_add_console_port(up);
+#ifdef CONFIG_SERIAL_AR933X_CONSOLE
+ ar933x_console_ports[up->port.line] = up;
+#endif
ret = uart_add_one_port(&ar933x_uart_driver, &up->port);
if (ret)
@@ -749,8 +740,9 @@ static int __init ar933x_uart_init(void)
{
int ret;
- if (ar933x_uart_console_enabled())
- ar933x_uart_driver.cons = &ar933x_uart_console;
+#ifdef CONFIG_SERIAL_AR933X_CONSOLE
+ ar933x_uart_driver.cons = &ar933x_uart_console;
+#endif
ret = uart_register_driver(&ar933x_uart_driver);
if (ret)
diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
index 05147fe24343..0b4f36905321 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -166,6 +166,8 @@ struct atmel_uart_port {
unsigned int pending_status;
spinlock_t lock_suspended;
+ bool hd_start_rx; /* can start RX during half-duplex operation */
+
/* ISO7816 */
unsigned int fidi_min;
unsigned int fidi_max;
@@ -231,6 +233,13 @@ static inline void atmel_uart_write_char(struct uart_port *port, u8 value)
__raw_writeb(value, port->membase + ATMEL_US_THR);
}
+static inline int atmel_uart_is_half_duplex(struct uart_port *port)
+{
+ return ((port->rs485.flags & SER_RS485_ENABLED) &&
+ !(port->rs485.flags & SER_RS485_RX_DURING_TX)) ||
+ (port->iso7816.flags & SER_ISO7816_ENABLED);
+}
+
#ifdef CONFIG_SERIAL_ATMEL_PDC
static bool atmel_use_pdc_rx(struct uart_port *port)
{
@@ -608,10 +617,9 @@ static void atmel_stop_tx(struct uart_port *port)
/* Disable interrupts */
atmel_uart_writel(port, ATMEL_US_IDR, atmel_port->tx_done_mask);
- if (((port->rs485.flags & SER_RS485_ENABLED) &&
- !(port->rs485.flags & SER_RS485_RX_DURING_TX)) ||
- port->iso7816.flags & SER_ISO7816_ENABLED)
+ if (atmel_uart_is_half_duplex(port))
atmel_start_rx(port);
+
}
/*
@@ -628,9 +636,7 @@ static void atmel_start_tx(struct uart_port *port)
return;
if (atmel_use_pdc_tx(port) || atmel_use_dma_tx(port))
- if (((port->rs485.flags & SER_RS485_ENABLED) &&
- !(port->rs485.flags & SER_RS485_RX_DURING_TX)) ||
- port->iso7816.flags & SER_ISO7816_ENABLED)
+ if (atmel_uart_is_half_duplex(port))
atmel_stop_rx(port);
if (atmel_use_pdc_tx(port))
@@ -928,11 +934,14 @@ static void atmel_complete_tx_dma(void *arg)
*/
if (!uart_circ_empty(xmit))
atmel_tasklet_schedule(atmel_port, &atmel_port->tasklet_tx);
- else if (((port->rs485.flags & SER_RS485_ENABLED) &&
- !(port->rs485.flags & SER_RS485_RX_DURING_TX)) ||
- port->iso7816.flags & SER_ISO7816_ENABLED) {
- /* DMA done, stop TX, start RX for RS485 */
- atmel_start_rx(port);
+ else if (atmel_uart_is_half_duplex(port)) {
+ /*
+ * DMA done, re-enable TXEMPTY and signal that we can stop
+ * TX and start RX for RS485
+ */
+ atmel_port->hd_start_rx = true;
+ atmel_uart_writel(port, ATMEL_US_IER,
+ atmel_port->tx_done_mask);
}
spin_unlock_irqrestore(&port->lock, flags);
@@ -1288,6 +1297,10 @@ static int atmel_prepare_rx_dma(struct uart_port *port)
sg_dma_len(&atmel_port->sg_rx)/2,
DMA_DEV_TO_MEM,
DMA_PREP_INTERRUPT);
+ if (!desc) {
+ dev_err(port->dev, "Preparing DMA cyclic failed\n");
+ goto chan_err;
+ }
desc->callback = atmel_complete_rx_dma;
desc->callback_param = port;
atmel_port->desc_rx = desc;
@@ -1376,9 +1389,20 @@ atmel_handle_transmit(struct uart_port *port, unsigned int pending)
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
if (pending & atmel_port->tx_done_mask) {
- /* Either PDC or interrupt transmission */
atmel_uart_writel(port, ATMEL_US_IDR,
atmel_port->tx_done_mask);
+
+ /* Start RX if flag was set and FIFO is empty */
+ if (atmel_port->hd_start_rx) {
+ if (!(atmel_uart_readl(port, ATMEL_US_CSR)
+ & ATMEL_US_TXEMPTY))
+ dev_warn(port->dev, "Should start RX, but TX fifo is not empty\n");
+
+ atmel_port->hd_start_rx = false;
+ atmel_start_rx(port);
+ return;
+ }
+
atmel_tasklet_schedule(atmel_port, &atmel_port->tasklet_tx);
}
}
@@ -1508,9 +1532,7 @@ static void atmel_tx_pdc(struct uart_port *port)
atmel_uart_writel(port, ATMEL_US_IER,
atmel_port->tx_done_mask);
} else {
- if (((port->rs485.flags & SER_RS485_ENABLED) &&
- !(port->rs485.flags & SER_RS485_RX_DURING_TX)) ||
- port->iso7816.flags & SER_ISO7816_ENABLED) {
+ if (atmel_uart_is_half_duplex(port)) {
/* DMA done, stop TX, start RX for RS485 */
atmel_start_rx(port);
}
diff --git a/drivers/tty/serial/kgdboc.c b/drivers/tty/serial/kgdboc.c
index 6fb312e7af71..bfe5e9e034ec 100644
--- a/drivers/tty/serial/kgdboc.c
+++ b/drivers/tty/serial/kgdboc.c
@@ -148,8 +148,10 @@ static int configure_kgdboc(void)
char *cptr = config;
struct console *cons;
- if (!strlen(config) || isspace(config[0]))
+ if (!strlen(config) || isspace(config[0])) {
+ err = 0;
goto noconfig;
+ }
kgdboc_io_ops.is_console = 0;
kgdb_tty_driver = NULL;
diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c
index f5bdde405627..450ba6d7996c 100644
--- a/drivers/tty/serial/max310x.c
+++ b/drivers/tty/serial/max310x.c
@@ -1415,6 +1415,8 @@ static int max310x_spi_probe(struct spi_device *spi)
if (spi->dev.of_node) {
const struct of_device_id *of_id =
of_match_device(max310x_dt_ids, &spi->dev);
+ if (!of_id)
+ return -ENODEV;
devtype = (struct max310x_devtype *)of_id->data;
} else {
diff --git a/drivers/tty/serial/mvebu-uart.c b/drivers/tty/serial/mvebu-uart.c
index 231f751d1ef4..7e7b1559fa36 100644
--- a/drivers/tty/serial/mvebu-uart.c
+++ b/drivers/tty/serial/mvebu-uart.c
@@ -810,6 +810,9 @@ static int mvebu_uart_probe(struct platform_device *pdev)
return -EINVAL;
}
+ if (!match)
+ return -ENODEV;
+
/* Assume that all UART ports have a DT alias or none has */
id = of_alias_get_id(pdev->dev.of_node, "serial");
if (!pdev->dev.of_node || id < 0)
diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c
index 27235a526cce..4c188f4079b3 100644
--- a/drivers/tty/serial/mxs-auart.c
+++ b/drivers/tty/serial/mxs-auart.c
@@ -1686,6 +1686,10 @@ static int mxs_auart_probe(struct platform_device *pdev)
s->port.mapbase = r->start;
s->port.membase = ioremap(r->start, resource_size(r));
+ if (!s->port.membase) {
+ ret = -ENOMEM;
+ goto out_disable_clks;
+ }
s->port.ops = &mxs_auart_ops;
s->port.iotype = UPIO_MEM;
s->port.fifosize = MXS_AUART_FIFO_SIZE;
diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c
index 9ed121f08a54..6157213a8359 100644
--- a/drivers/tty/serial/pch_uart.c
+++ b/drivers/tty/serial/pch_uart.c
@@ -192,8 +192,6 @@ enum {
#define PCH_UART_HAL_LOOP (PCH_UART_MCR_LOOP)
#define PCH_UART_HAL_AFE (PCH_UART_MCR_AFE)
-#define PCI_VENDOR_ID_ROHM 0x10DB
-
#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
#define DEFAULT_UARTCLK 1843200 /* 1.8432 MHz */
diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c
index 3bcec1c20219..35e5f9c5d5be 100644
--- a/drivers/tty/serial/qcom_geni_serial.c
+++ b/drivers/tty/serial/qcom_geni_serial.c
@@ -1050,7 +1050,7 @@ static int __init qcom_geni_console_setup(struct console *co, char *options)
{
struct uart_port *uport;
struct qcom_geni_serial_port *port;
- int baud;
+ int baud = 9600;
int bits = 8;
int parity = 'n';
int flow = 'n';
diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c
index 635178cf3eed..09a183dfc526 100644
--- a/drivers/tty/serial/sc16is7xx.c
+++ b/drivers/tty/serial/sc16is7xx.c
@@ -1507,7 +1507,7 @@ static int __init sc16is7xx_init(void)
ret = i2c_add_driver(&sc16is7xx_i2c_uart_driver);
if (ret < 0) {
pr_err("failed to init sc16is7xx i2c --> %d\n", ret);
- return ret;
+ goto err_i2c;
}
#endif
@@ -1515,10 +1515,18 @@ static int __init sc16is7xx_init(void)
ret = spi_register_driver(&sc16is7xx_spi_uart_driver);
if (ret < 0) {
pr_err("failed to init sc16is7xx spi --> %d\n", ret);
- return ret;
+ goto err_spi;
}
#endif
return ret;
+
+err_spi:
+#ifdef CONFIG_SERIAL_SC16IS7XX_I2C
+ i2c_del_driver(&sc16is7xx_i2c_uart_driver);
+#endif
+err_i2c:
+ uart_unregister_driver(&sc16is7xx_uart);
+ return ret;
}
module_init(sc16is7xx_init);
diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c
index 060fcd42b6d5..2d1c626312cd 100644
--- a/drivers/tty/serial/sh-sci.c
+++ b/drivers/tty/serial/sh-sci.c
@@ -838,19 +838,9 @@ static void sci_transmit_chars(struct uart_port *port)
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(port);
- if (uart_circ_empty(xmit)) {
+ if (uart_circ_empty(xmit))
sci_stop_tx(port);
- } else {
- ctrl = serial_port_in(port, SCSCR);
-
- if (port->type != PORT_SCI) {
- serial_port_in(port, SCxSR); /* Dummy read */
- sci_clear_SCxSR(port, SCxSR_TDxE_CLEAR(port));
- }
- ctrl |= SCSCR_TIE;
- serial_port_out(port, SCSCR, ctrl);
- }
}
/* On SH3, SCIF may read end-of-break as a space->mark char */
diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c
index 044c3cbdcfa4..a9e12b3bc31d 100644
--- a/drivers/tty/tty_port.c
+++ b/drivers/tty/tty_port.c
@@ -325,7 +325,7 @@ static void tty_port_shutdown(struct tty_port *port, struct tty_struct *tty)
if (tty && C_HUPCL(tty))
tty_port_lower_dtr_rts(port);
- if (port->ops->shutdown)
+ if (port->ops && port->ops->shutdown)
port->ops->shutdown(port);
}
out:
@@ -398,7 +398,7 @@ EXPORT_SYMBOL_GPL(tty_port_tty_wakeup);
*/
int tty_port_carrier_raised(struct tty_port *port)
{
- if (port->ops->carrier_raised == NULL)
+ if (!port->ops || !port->ops->carrier_raised)
return 1;
return port->ops->carrier_raised(port);
}
@@ -414,7 +414,7 @@ EXPORT_SYMBOL(tty_port_carrier_raised);
*/
void tty_port_raise_dtr_rts(struct tty_port *port)
{
- if (port->ops->dtr_rts)
+ if (port->ops && port->ops->dtr_rts)
port->ops->dtr_rts(port, 1);
}
EXPORT_SYMBOL(tty_port_raise_dtr_rts);
@@ -429,7 +429,7 @@ EXPORT_SYMBOL(tty_port_raise_dtr_rts);
*/
void tty_port_lower_dtr_rts(struct tty_port *port)
{
- if (port->ops->dtr_rts)
+ if (port->ops && port->ops->dtr_rts)
port->ops->dtr_rts(port, 0);
}
EXPORT_SYMBOL(tty_port_lower_dtr_rts);
@@ -684,7 +684,7 @@ int tty_port_open(struct tty_port *port, struct tty_struct *tty,
if (!tty_port_initialized(port)) {
clear_bit(TTY_IO_ERROR, &tty->flags);
- if (port->ops->activate) {
+ if (port->ops && port->ops->activate) {
int retval = port->ops->activate(port, tty);
if (retval) {
mutex_unlock(&port->mutex);
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index 739f8960811a..ec666eb4b7b4 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -558,10 +558,8 @@ static void acm_softint(struct work_struct *work)
clear_bit(EVENT_RX_STALL, &acm->flags);
}
- if (test_bit(EVENT_TTY_WAKEUP, &acm->flags)) {
+ if (test_and_clear_bit(EVENT_TTY_WAKEUP, &acm->flags))
tty_port_tty_wakeup(&acm->port);
- clear_bit(EVENT_TTY_WAKEUP, &acm->flags);
- }
}
/*
diff --git a/drivers/usb/common/common.c b/drivers/usb/common/common.c
index 48277bbc15e4..73c8e6591746 100644
--- a/drivers/usb/common/common.c
+++ b/drivers/usb/common/common.c
@@ -145,6 +145,8 @@ enum usb_dr_mode of_usb_get_dr_mode_by_phy(struct device_node *np, int arg0)
do {
controller = of_find_node_with_property(controller, "phys");
+ if (!of_device_is_available(controller))
+ continue;
index = 0;
do {
if (arg0 == -1) {
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index 3189181bb628..975d7c1288e3 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -2742,6 +2742,9 @@ int usb_add_hcd(struct usb_hcd *hcd,
retval = usb_phy_roothub_set_mode(hcd->phy_roothub,
PHY_MODE_USB_HOST_SS);
if (retval)
+ retval = usb_phy_roothub_set_mode(hcd->phy_roothub,
+ PHY_MODE_USB_HOST);
+ if (retval)
goto err_usb_phy_roothub_power_on;
retval = usb_phy_roothub_power_on(hcd->phy_roothub);
diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c
index fdc6e4e403e8..8cced3609e24 100644
--- a/drivers/usb/dwc3/dwc3-pci.c
+++ b/drivers/usb/dwc3/dwc3-pci.c
@@ -29,6 +29,7 @@
#define PCI_DEVICE_ID_INTEL_BXT_M 0x1aaa
#define PCI_DEVICE_ID_INTEL_APL 0x5aaa
#define PCI_DEVICE_ID_INTEL_KBP 0xa2b0
+#define PCI_DEVICE_ID_INTEL_CMLH 0x02ee
#define PCI_DEVICE_ID_INTEL_GLK 0x31aa
#define PCI_DEVICE_ID_INTEL_CNPLP 0x9dee
#define PCI_DEVICE_ID_INTEL_CNPH 0xa36e
@@ -305,6 +306,9 @@ static const struct pci_device_id dwc3_pci_id_table[] = {
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_MRFLD),
(kernel_ulong_t) &dwc3_pci_mrfld_properties, },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_CMLH),
+ (kernel_ulong_t) &dwc3_pci_intel_properties, },
+
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_SPTLP),
(kernel_ulong_t) &dwc3_pci_intel_properties, },
diff --git a/drivers/usb/early/xhci-dbc.c b/drivers/usb/early/xhci-dbc.c
index d2652dccc699..c9cfb100ecdc 100644
--- a/drivers/usb/early/xhci-dbc.c
+++ b/drivers/usb/early/xhci-dbc.c
@@ -94,7 +94,7 @@ static void * __init xdbc_get_page(dma_addr_t *dma_addr)
{
void *virt;
- virt = memblock_alloc_nopanic(PAGE_SIZE, PAGE_SIZE);
+ virt = memblock_alloc(PAGE_SIZE, PAGE_SIZE);
if (!virt)
return NULL;
diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c
index 75b113a5b25c..f3816a5c861e 100644
--- a/drivers/usb/gadget/function/f_hid.c
+++ b/drivers/usb/gadget/function/f_hid.c
@@ -391,20 +391,20 @@ try_again:
req->complete = f_hidg_req_complete;
req->context = hidg;
+ spin_unlock_irqrestore(&hidg->write_spinlock, flags);
+
status = usb_ep_queue(hidg->in_ep, req, GFP_ATOMIC);
if (status < 0) {
ERROR(hidg->func.config->cdev,
"usb_ep_queue error on int endpoint %zd\n", status);
- goto release_write_pending_unlocked;
+ goto release_write_pending;
} else {
status = count;
}
- spin_unlock_irqrestore(&hidg->write_spinlock, flags);
return status;
release_write_pending:
spin_lock_irqsave(&hidg->write_spinlock, flags);
-release_write_pending_unlocked:
hidg->write_pending = 0;
spin_unlock_irqrestore(&hidg->write_spinlock, flags);
diff --git a/drivers/usb/gadget/function/f_tcm.c b/drivers/usb/gadget/function/f_tcm.c
index 34f5982cab78..7f01f78b1d23 100644
--- a/drivers/usb/gadget/function/f_tcm.c
+++ b/drivers/usb/gadget/function/f_tcm.c
@@ -1292,14 +1292,6 @@ static u32 usbg_sess_get_index(struct se_session *se_sess)
return 0;
}
-/*
- * XXX Error recovery: return != 0 if we expect writes. Dunno when that could be
- */
-static int usbg_write_pending_status(struct se_cmd *se_cmd)
-{
- return 0;
-}
-
static void usbg_set_default_node_attrs(struct se_node_acl *nacl)
{
}
@@ -1725,7 +1717,6 @@ static const struct target_core_fabric_ops usbg_ops = {
.sess_get_index = usbg_sess_get_index,
.sess_get_initiator_sid = NULL,
.write_pending = usbg_send_write_request,
- .write_pending_status = usbg_write_pending_status,
.set_default_node_attributes = usbg_set_default_node_attrs,
.get_cmd_state = usbg_get_cmd_state,
.queue_data_in = usbg_send_read_response,
diff --git a/drivers/usb/gadget/udc/net2272.c b/drivers/usb/gadget/udc/net2272.c
index b77f3126580e..c2011cd7df8c 100644
--- a/drivers/usb/gadget/udc/net2272.c
+++ b/drivers/usb/gadget/udc/net2272.c
@@ -945,6 +945,7 @@ net2272_dequeue(struct usb_ep *_ep, struct usb_request *_req)
break;
}
if (&req->req != _req) {
+ ep->stopped = stopped;
spin_unlock_irqrestore(&ep->dev->lock, flags);
return -EINVAL;
}
diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c
index f63f82450bf4..898339e5df10 100644
--- a/drivers/usb/gadget/udc/net2280.c
+++ b/drivers/usb/gadget/udc/net2280.c
@@ -866,9 +866,6 @@ static void start_queue(struct net2280_ep *ep, u32 dmactl, u32 td_dma)
(void) readl(&ep->dev->pci->pcimstctl);
writel(BIT(DMA_START), &dma->dmastat);
-
- if (!ep->is_in)
- stop_out_naking(ep);
}
static void start_dma(struct net2280_ep *ep, struct net2280_request *req)
@@ -907,6 +904,7 @@ static void start_dma(struct net2280_ep *ep, struct net2280_request *req)
writel(BIT(DMA_START), &dma->dmastat);
return;
}
+ stop_out_naking(ep);
}
tmp = dmactl_default;
@@ -1275,9 +1273,9 @@ static int net2280_dequeue(struct usb_ep *_ep, struct usb_request *_req)
break;
}
if (&req->req != _req) {
+ ep->stopped = stopped;
spin_unlock_irqrestore(&ep->dev->lock, flags);
- dev_err(&ep->dev->pdev->dev, "%s: Request mismatch\n",
- __func__);
+ ep_dbg(ep->dev, "%s: Request mismatch\n", __func__);
return -EINVAL;
}
diff --git a/drivers/usb/gadget/udc/pch_udc.c b/drivers/usb/gadget/udc/pch_udc.c
index 55c8c8abeacd..cded51f36fc1 100644
--- a/drivers/usb/gadget/udc/pch_udc.c
+++ b/drivers/usb/gadget/udc/pch_udc.c
@@ -368,7 +368,6 @@ struct pch_udc_dev {
#define PCI_DEVICE_ID_INTEL_QUARK_X1000_UDC 0x0939
#define PCI_DEVICE_ID_INTEL_EG20T_UDC 0x8808
-#define PCI_VENDOR_ID_ROHM 0x10DB
#define PCI_DEVICE_ID_ML7213_IOH_UDC 0x801D
#define PCI_DEVICE_ID_ML7831_IOH_UDC 0x8808
diff --git a/drivers/usb/host/ohci-sm501.c b/drivers/usb/host/ohci-sm501.c
index c9233cddf9a2..c26228c25f99 100644
--- a/drivers/usb/host/ohci-sm501.c
+++ b/drivers/usb/host/ohci-sm501.c
@@ -126,8 +126,7 @@ static int ohci_hcd_sm501_drv_probe(struct platform_device *pdev)
retval = dma_declare_coherent_memory(dev, mem->start,
mem->start - mem->parent->start,
- resource_size(mem),
- DMA_MEMORY_EXCLUSIVE);
+ resource_size(mem));
if (retval) {
dev_err(dev, "cannot declare coherent memory\n");
goto err1;
diff --git a/drivers/usb/host/ohci-tmio.c b/drivers/usb/host/ohci-tmio.c
index a631dbb369d7..f88a0370659f 100644
--- a/drivers/usb/host/ohci-tmio.c
+++ b/drivers/usb/host/ohci-tmio.c
@@ -225,7 +225,7 @@ static int ohci_hcd_tmio_drv_probe(struct platform_device *dev)
}
ret = dma_declare_coherent_memory(&dev->dev, sram->start, sram->start,
- resource_size(sram), DMA_MEMORY_EXCLUSIVE);
+ resource_size(sram));
if (ret)
goto err_dma_declare;
diff --git a/drivers/usb/host/u132-hcd.c b/drivers/usb/host/u132-hcd.c
index 934584f0a20a..6343fbacd244 100644
--- a/drivers/usb/host/u132-hcd.c
+++ b/drivers/usb/host/u132-hcd.c
@@ -3204,6 +3204,9 @@ static int __init u132_hcd_init(void)
printk(KERN_INFO "driver %s\n", hcd_name);
workqueue = create_singlethread_workqueue("u132");
retval = platform_driver_register(&u132_platform_driver);
+ if (retval)
+ destroy_workqueue(workqueue);
+
return retval;
}
diff --git a/drivers/usb/host/xhci-dbgcap.c b/drivers/usb/host/xhci-dbgcap.c
index c78be578abb0..d932cc31711e 100644
--- a/drivers/usb/host/xhci-dbgcap.c
+++ b/drivers/usb/host/xhci-dbgcap.c
@@ -516,7 +516,6 @@ static int xhci_do_dbc_stop(struct xhci_hcd *xhci)
return -1;
writel(0, &dbc->regs->control);
- xhci_dbc_mem_cleanup(xhci);
dbc->state = DS_DISABLED;
return 0;
@@ -562,8 +561,10 @@ static void xhci_dbc_stop(struct xhci_hcd *xhci)
ret = xhci_do_dbc_stop(xhci);
spin_unlock_irqrestore(&dbc->lock, flags);
- if (!ret)
+ if (!ret) {
+ xhci_dbc_mem_cleanup(xhci);
pm_runtime_put_sync(xhci_to_hcd(xhci)->self.controller);
+ }
}
static void
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index e2eece693655..96a740543183 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -1545,20 +1545,25 @@ int xhci_bus_suspend(struct usb_hcd *hcd)
port_index = max_ports;
while (port_index--) {
u32 t1, t2;
-
+ int retries = 10;
+retry:
t1 = readl(ports[port_index]->addr);
t2 = xhci_port_state_to_neutral(t1);
portsc_buf[port_index] = 0;
- /* Bail out if a USB3 port has a new device in link training */
- if ((hcd->speed >= HCD_USB3) &&
+ /*
+ * Give a USB3 port in link training time to finish, but don't
+ * prevent suspend as port might be stuck
+ */
+ if ((hcd->speed >= HCD_USB3) && retries-- &&
(t1 & PORT_PLS_MASK) == XDEV_POLLING) {
- bus_state->bus_suspended = 0;
spin_unlock_irqrestore(&xhci->lock, flags);
- xhci_dbg(xhci, "Bus suspend bailout, port in polling\n");
- return -EBUSY;
+ msleep(XHCI_PORT_POLLING_LFPS_TIME);
+ spin_lock_irqsave(&xhci->lock, flags);
+ xhci_dbg(xhci, "port %d polling in bus suspend, waiting\n",
+ port_index);
+ goto retry;
}
-
/* suspend ports in U0, or bail out for new connect changes */
if ((t1 & PORT_PE) && (t1 & PORT_PLS_MASK) == XDEV_U0) {
if ((t1 & PORT_CSC) && wake_enabled) {
diff --git a/drivers/usb/host/xhci-rcar.c b/drivers/usb/host/xhci-rcar.c
index a6e463715779..671bce18782c 100644
--- a/drivers/usb/host/xhci-rcar.c
+++ b/drivers/usb/host/xhci-rcar.c
@@ -246,6 +246,7 @@ int xhci_rcar_init_quirk(struct usb_hcd *hcd)
if (!xhci_rcar_wait_for_pll_active(hcd))
return -ETIMEDOUT;
+ xhci->quirks |= XHCI_TRUST_TX_LENGTH;
return xhci_rcar_download_firmware(hcd);
}
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 40fa25c4d041..9215a28dad40 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -1647,10 +1647,13 @@ static void handle_port_status(struct xhci_hcd *xhci,
}
}
- if ((portsc & PORT_PLC) && (portsc & PORT_PLS_MASK) == XDEV_U0 &&
- DEV_SUPERSPEED_ANY(portsc)) {
+ if ((portsc & PORT_PLC) &&
+ DEV_SUPERSPEED_ANY(portsc) &&
+ ((portsc & PORT_PLS_MASK) == XDEV_U0 ||
+ (portsc & PORT_PLS_MASK) == XDEV_U1 ||
+ (portsc & PORT_PLS_MASK) == XDEV_U2)) {
xhci_dbg(xhci, "resume SS port %d finished\n", port_id);
- /* We've just brought the device into U0 through either the
+ /* We've just brought the device into U0/1/2 through either the
* Resume state after a device remote wakeup, or through the
* U3Exit state after a host-initiated resume. If it's a device
* initiated remote wake, don't pass up the link state change,
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 652dc36e3012..9334cdee382a 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -452,6 +452,14 @@ struct xhci_op_regs {
*/
#define XHCI_DEFAULT_BESL 4
+/*
+ * USB3 specification define a 360ms tPollingLFPSTiemout for USB3 ports
+ * to complete link training. usually link trainig completes much faster
+ * so check status 10 times with 36ms sleep in places we need to wait for
+ * polling to complete.
+ */
+#define XHCI_PORT_POLLING_LFPS_TIME 36
+
/**
* struct xhci_intr_reg - Interrupt Register Set
* @irq_pending: IMAN - Interrupt Management Register. Used to enable
diff --git a/drivers/usb/misc/usb251xb.c b/drivers/usb/misc/usb251xb.c
index 4d72b7d1d383..04684849d683 100644
--- a/drivers/usb/misc/usb251xb.c
+++ b/drivers/usb/misc/usb251xb.c
@@ -547,7 +547,7 @@ static int usb251xb_get_ofdata(struct usb251xb *hub,
*/
hub->port_swap = USB251XB_DEF_PORT_SWAP;
of_property_for_each_u32(np, "swap-dx-lanes", prop, p, port) {
- if ((port >= 0) && (port <= data->port_cnt))
+ if (port <= data->port_cnt)
hub->port_swap |= BIT(port);
}
@@ -612,7 +612,7 @@ static int usb251xb_probe(struct usb251xb *hub)
dev);
int err;
- if (np) {
+ if (np && of_id) {
err = usb251xb_get_ofdata(hub,
(struct usb251xb_data *)of_id->data);
if (err) {
diff --git a/drivers/usb/mtu3/Kconfig b/drivers/usb/mtu3/Kconfig
index bcc23486c4ed..928c2cd6fc00 100644
--- a/drivers/usb/mtu3/Kconfig
+++ b/drivers/usb/mtu3/Kconfig
@@ -6,6 +6,7 @@ config USB_MTU3
tristate "MediaTek USB3 Dual Role controller"
depends on USB || USB_GADGET
depends on ARCH_MEDIATEK || COMPILE_TEST
+ depends on EXTCON || !EXTCON
select USB_XHCI_MTK if USB_SUPPORT && USB_XHCI_HCD
help
Say Y or M here if your system runs on MediaTek SoCs with
diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c
index fffe23ab0189..979bef9bfb6b 100644
--- a/drivers/usb/serial/cp210x.c
+++ b/drivers/usb/serial/cp210x.c
@@ -80,6 +80,7 @@ static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x10C4, 0x804E) }, /* Software Bisque Paramount ME build-in converter */
{ USB_DEVICE(0x10C4, 0x8053) }, /* Enfora EDG1228 */
{ USB_DEVICE(0x10C4, 0x8054) }, /* Enfora GSM2228 */
+ { USB_DEVICE(0x10C4, 0x8056) }, /* Lorenz Messtechnik devices */
{ USB_DEVICE(0x10C4, 0x8066) }, /* Argussoft In-System Programmer */
{ USB_DEVICE(0x10C4, 0x806F) }, /* IMS USB to RS422 Converter Cable */
{ USB_DEVICE(0x10C4, 0x807A) }, /* Crumb128 board */
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index 8f5b17471759..1d8461ae2c34 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -609,6 +609,8 @@ static const struct usb_device_id id_table_combined[] = {
.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
{ USB_DEVICE(FTDI_VID, FTDI_NT_ORIONLXM_PID),
.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
+ { USB_DEVICE(FTDI_VID, FTDI_NT_ORIONLX_PLUS_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_NT_ORION_IO_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_SYNAPSE_SS200_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_CUSTOMWARE_MINIPLEX_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_CUSTOMWARE_MINIPLEX2_PID) },
diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h
index b863bedb55a1..5755f0df0025 100644
--- a/drivers/usb/serial/ftdi_sio_ids.h
+++ b/drivers/usb/serial/ftdi_sio_ids.h
@@ -567,7 +567,9 @@
/*
* NovaTech product ids (FTDI_VID)
*/
-#define FTDI_NT_ORIONLXM_PID 0x7c90 /* OrionLXm Substation Automation Platform */
+#define FTDI_NT_ORIONLXM_PID 0x7c90 /* OrionLXm Substation Automation Platform */
+#define FTDI_NT_ORIONLX_PLUS_PID 0x7c91 /* OrionLX+ Substation Automation Platform */
+#define FTDI_NT_ORION_IO_PID 0x7c92 /* Orion I/O */
/*
* Synapse Wireless product ids (FTDI_VID)
diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c
index fc52ac75fbf6..18110225d506 100644
--- a/drivers/usb/serial/mos7720.c
+++ b/drivers/usb/serial/mos7720.c
@@ -366,8 +366,6 @@ static int write_parport_reg_nonblock(struct mos7715_parport *mos_parport,
if (!urbtrack)
return -ENOMEM;
- kref_get(&mos_parport->ref_count);
- urbtrack->mos_parport = mos_parport;
urbtrack->urb = usb_alloc_urb(0, GFP_ATOMIC);
if (!urbtrack->urb) {
kfree(urbtrack);
@@ -388,6 +386,8 @@ static int write_parport_reg_nonblock(struct mos7715_parport *mos_parport,
usb_sndctrlpipe(usbdev, 0),
(unsigned char *)urbtrack->setup,
NULL, 0, async_complete, urbtrack);
+ kref_get(&mos_parport->ref_count);
+ urbtrack->mos_parport = mos_parport;
kref_init(&urbtrack->ref_count);
INIT_LIST_HEAD(&urbtrack->urblist_entry);
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index 11b21d9410f3..83869065b802 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -246,6 +246,7 @@ static void option_instat_callback(struct urb *urb);
#define QUECTEL_PRODUCT_EC25 0x0125
#define QUECTEL_PRODUCT_BG96 0x0296
#define QUECTEL_PRODUCT_EP06 0x0306
+#define QUECTEL_PRODUCT_EM12 0x0512
#define CMOTECH_VENDOR_ID 0x16d8
#define CMOTECH_PRODUCT_6001 0x6001
@@ -1066,7 +1067,8 @@ static const struct usb_device_id option_ids[] = {
.driver_info = RSVD(3) },
{ USB_DEVICE(QUALCOMM_VENDOR_ID, 0x6613)}, /* Onda H600/ZTE MF330 */
{ USB_DEVICE(QUALCOMM_VENDOR_ID, 0x0023)}, /* ONYX 3G device */
- { USB_DEVICE(QUALCOMM_VENDOR_ID, 0x9000)}, /* SIMCom SIM5218 */
+ { USB_DEVICE(QUALCOMM_VENDOR_ID, 0x9000), /* SIMCom SIM5218 */
+ .driver_info = NCTRL(0) | NCTRL(1) | NCTRL(2) | NCTRL(3) | RSVD(4) },
/* Quectel products using Qualcomm vendor ID */
{ USB_DEVICE(QUALCOMM_VENDOR_ID, QUECTEL_PRODUCT_UC15)},
{ USB_DEVICE(QUALCOMM_VENDOR_ID, QUECTEL_PRODUCT_UC20),
@@ -1087,6 +1089,9 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EP06, 0xff, 0xff, 0xff),
.driver_info = RSVD(1) | RSVD(2) | RSVD(3) | RSVD(4) | NUMEP2 },
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EP06, 0xff, 0, 0) },
+ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM12, 0xff, 0xff, 0xff),
+ .driver_info = RSVD(1) | RSVD(2) | RSVD(3) | RSVD(4) | NUMEP2 },
+ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM12, 0xff, 0, 0) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_6001) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CMU_300) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_6003),
@@ -1940,10 +1945,12 @@ static const struct usb_device_id option_ids[] = {
.driver_info = RSVD(4) },
{ USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7e35, 0xff), /* D-Link DWM-222 */
.driver_info = RSVD(4) },
- { USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x3e01, 0xff, 0xff, 0xff) }, /* D-Link DWM-152/C1 */
- { USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x3e02, 0xff, 0xff, 0xff) }, /* D-Link DWM-156/C1 */
- { USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x7e11, 0xff, 0xff, 0xff) }, /* D-Link DWM-156/A3 */
- { USB_DEVICE_INTERFACE_CLASS(0x2020, 0x4000, 0xff) }, /* OLICARD300 - MT6225 */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x3e01, 0xff, 0xff, 0xff) }, /* D-Link DWM-152/C1 */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x3e02, 0xff, 0xff, 0xff) }, /* D-Link DWM-156/C1 */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x7e11, 0xff, 0xff, 0xff) }, /* D-Link DWM-156/A3 */
+ { USB_DEVICE_INTERFACE_CLASS(0x2020, 0x2031, 0xff), /* Olicard 600 */
+ .driver_info = RSVD(4) },
+ { USB_DEVICE_INTERFACE_CLASS(0x2020, 0x4000, 0xff) }, /* OLICARD300 - MT6225 */
{ USB_DEVICE(INOVIA_VENDOR_ID, INOVIA_SEW858) },
{ USB_DEVICE(VIATELECOM_VENDOR_ID, VIATELECOM_PRODUCT_CDS7) },
{ USB_DEVICE_AND_INTERFACE_INFO(WETELECOM_VENDOR_ID, WETELECOM_PRODUCT_WMD200, 0xff, 0xff, 0xff) },
diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c
index 36742e8e7edc..a6d68191c861 100644
--- a/drivers/usb/storage/uas.c
+++ b/drivers/usb/storage/uas.c
@@ -368,25 +368,19 @@ static void uas_data_cmplt(struct urb *urb)
struct scsi_cmnd *cmnd = urb->context;
struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
struct uas_dev_info *devinfo = (void *)cmnd->device->hostdata;
- struct scsi_data_buffer *sdb = NULL;
+ struct scsi_data_buffer *sdb = &cmnd->sdb;
unsigned long flags;
int status = urb->status;
spin_lock_irqsave(&devinfo->lock, flags);
if (cmdinfo->data_in_urb == urb) {
- sdb = scsi_in(cmnd);
cmdinfo->state &= ~DATA_IN_URB_INFLIGHT;
cmdinfo->data_in_urb = NULL;
} else if (cmdinfo->data_out_urb == urb) {
- sdb = scsi_out(cmnd);
cmdinfo->state &= ~DATA_OUT_URB_INFLIGHT;
cmdinfo->data_out_urb = NULL;
}
- if (sdb == NULL) {
- WARN_ON_ONCE(1);
- goto out;
- }
if (devinfo->resetting)
goto out;
@@ -401,9 +395,9 @@ static void uas_data_cmplt(struct urb *urb)
if (status != -ENOENT && status != -ECONNRESET && status != -ESHUTDOWN)
uas_log_cmd_state(cmnd, "data cmplt err", status);
/* error: no data transfered */
- sdb->resid = sdb->length;
+ scsi_set_resid(cmnd, sdb->length);
} else {
- sdb->resid = sdb->length - urb->actual_length;
+ scsi_set_resid(cmnd, sdb->length - urb->actual_length);
}
uas_try_complete(cmnd, __func__);
out:
@@ -426,8 +420,7 @@ static struct urb *uas_alloc_data_urb(struct uas_dev_info *devinfo, gfp_t gfp,
struct usb_device *udev = devinfo->udev;
struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
struct urb *urb = usb_alloc_urb(0, gfp);
- struct scsi_data_buffer *sdb = (dir == DMA_FROM_DEVICE)
- ? scsi_in(cmnd) : scsi_out(cmnd);
+ struct scsi_data_buffer *sdb = &cmnd->sdb;
unsigned int pipe = (dir == DMA_FROM_DEVICE)
? devinfo->data_in_pipe : devinfo->data_out_pipe;
diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
index 0f62db091d8d..a2233d72ae7c 100644
--- a/drivers/usb/typec/tcpm/tcpm.c
+++ b/drivers/usb/typec/tcpm/tcpm.c
@@ -37,6 +37,7 @@
S(SRC_ATTACHED), \
S(SRC_STARTUP), \
S(SRC_SEND_CAPABILITIES), \
+ S(SRC_SEND_CAPABILITIES_TIMEOUT), \
S(SRC_NEGOTIATE_CAPABILITIES), \
S(SRC_TRANSITION_SUPPLY), \
S(SRC_READY), \
@@ -2966,10 +2967,34 @@ static void run_state_machine(struct tcpm_port *port)
/* port->hard_reset_count = 0; */
port->caps_count = 0;
port->pd_capable = true;
- tcpm_set_state_cond(port, hard_reset_state(port),
+ tcpm_set_state_cond(port, SRC_SEND_CAPABILITIES_TIMEOUT,
PD_T_SEND_SOURCE_CAP);
}
break;
+ case SRC_SEND_CAPABILITIES_TIMEOUT:
+ /*
+ * Error recovery for a PD_DATA_SOURCE_CAP reply timeout.
+ *
+ * PD 2.0 sinks are supposed to accept src-capabilities with a
+ * 3.0 header and simply ignore any src PDOs which the sink does
+ * not understand such as PPS but some 2.0 sinks instead ignore
+ * the entire PD_DATA_SOURCE_CAP message, causing contract
+ * negotiation to fail.
+ *
+ * After PD_N_HARD_RESET_COUNT hard-reset attempts, we try
+ * sending src-capabilities with a lower PD revision to
+ * make these broken sinks work.
+ */
+ if (port->hard_reset_count < PD_N_HARD_RESET_COUNT) {
+ tcpm_set_state(port, HARD_RESET_SEND, 0);
+ } else if (port->negotiated_rev > PD_REV20) {
+ port->negotiated_rev--;
+ port->hard_reset_count = 0;
+ tcpm_set_state(port, SRC_SEND_CAPABILITIES, 0);
+ } else {
+ tcpm_set_state(port, hard_reset_state(port), 0);
+ }
+ break;
case SRC_NEGOTIATE_CAPABILITIES:
ret = tcpm_pd_check_request(port);
if (ret < 0) {
diff --git a/drivers/usb/typec/tcpm/wcove.c b/drivers/usb/typec/tcpm/wcove.c
index 423208e19383..6770afd40765 100644
--- a/drivers/usb/typec/tcpm/wcove.c
+++ b/drivers/usb/typec/tcpm/wcove.c
@@ -615,8 +615,13 @@ static int wcove_typec_probe(struct platform_device *pdev)
wcove->dev = &pdev->dev;
wcove->regmap = pmic->regmap;
- irq = regmap_irq_get_virq(pmic->irq_chip_data_chgr,
- platform_get_irq(pdev, 0));
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "Failed to get IRQ: %d\n", irq);
+ return irq;
+ }
+
+ irq = regmap_irq_get_virq(pmic->irq_chip_data_chgr, irq);
if (irq < 0)
return irq;
diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c
index 23593cb23dd0..618fb6461017 100644
--- a/drivers/vhost/scsi.c
+++ b/drivers/vhost/scsi.c
@@ -346,11 +346,6 @@ static int vhost_scsi_write_pending(struct se_cmd *se_cmd)
return 0;
}
-static int vhost_scsi_write_pending_status(struct se_cmd *se_cmd)
-{
- return 0;
-}
-
static void vhost_scsi_set_default_node_attrs(struct se_node_acl *nacl)
{
return;
@@ -2302,7 +2297,6 @@ static const struct target_core_fabric_ops vhost_scsi_ops = {
.sess_get_index = vhost_scsi_sess_get_index,
.sess_get_initiator_sid = NULL,
.write_pending = vhost_scsi_write_pending,
- .write_pending_status = vhost_scsi_write_pending_status,
.set_default_node_attributes = vhost_scsi_set_default_node_attrs,
.get_cmd_state = vhost_scsi_get_cmd_state,
.queue_data_in = vhost_scsi_queue_data_in,
diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c
index a2e5dc7716e2..5ace833de746 100644
--- a/drivers/vhost/vhost.c
+++ b/drivers/vhost/vhost.c
@@ -1188,7 +1188,7 @@ static bool vq_access_ok(struct vhost_virtqueue *vq, unsigned int num,
struct vring_used __user *used)
{
- size_t s = vhost_has_feature(vq, VIRTIO_RING_F_EVENT_IDX) ? 2 : 0;
+ size_t s __maybe_unused = vhost_has_feature(vq, VIRTIO_RING_F_EVENT_IDX) ? 2 : 0;
return access_ok(desc, num * sizeof *desc) &&
access_ok(avail,
diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c
index feb90764a811..53b8ceea9bde 100644
--- a/drivers/video/backlight/pwm_bl.c
+++ b/drivers/video/backlight/pwm_bl.c
@@ -435,7 +435,7 @@ static int pwm_backlight_initial_power_state(const struct pwm_bl_data *pb)
*/
/* if the enable GPIO is disabled, do not enable the backlight */
- if (pb->enable_gpio && gpiod_get_value(pb->enable_gpio) == 0)
+ if (pb->enable_gpio && gpiod_get_value_cansleep(pb->enable_gpio) == 0)
return FB_BLANK_POWERDOWN;
/* The regulator is disabled, do not enable the backlight */
diff --git a/drivers/video/fbdev/aty/radeon_pm.c b/drivers/video/fbdev/aty/radeon_pm.c
index e695adb0e573..2dc5703eac51 100644
--- a/drivers/video/fbdev/aty/radeon_pm.c
+++ b/drivers/video/fbdev/aty/radeon_pm.c
@@ -2844,8 +2844,8 @@ void radeonfb_pm_init(struct radeonfb_info *rinfo, int dynclk, int ignore_devlis
* in some desktop G4s), Via (M9+ chip on iBook G4) and
* Snowy (M11 chip on iBook G4 manufactured after July 2005)
*/
- if (!strcmp(rinfo->of_node->name, "ATY,JasperParent") ||
- !strcmp(rinfo->of_node->name, "ATY,SnowyParent")) {
+ if (of_node_name_eq(rinfo->of_node, "ATY,JasperParent") ||
+ of_node_name_eq(rinfo->of_node, "ATY,SnowyParent")) {
rinfo->reinit_func = radeon_reinitialize_M10;
rinfo->pm_mode |= radeon_pm_off;
}
@@ -2855,7 +2855,7 @@ void radeonfb_pm_init(struct radeonfb_info *rinfo, int dynclk, int ignore_devlis
rinfo->pm_mode |= radeon_pm_off;
}
#endif
- if (!strcmp(rinfo->of_node->name, "ATY,ViaParent")) {
+ if (of_node_name_eq(rinfo->of_node, "ATY,ViaParent")) {
rinfo->reinit_func = radeon_reinitialize_M9P;
rinfo->pm_mode |= radeon_pm_off;
}
diff --git a/drivers/video/fbdev/cg14.c b/drivers/video/fbdev/cg14.c
index 9af54c2368fd..a6dce1a78490 100644
--- a/drivers/video/fbdev/cg14.c
+++ b/drivers/video/fbdev/cg14.c
@@ -486,8 +486,8 @@ static int cg14_probe(struct platform_device *op)
info->var.xres);
info->fix.smem_len = PAGE_ALIGN(linebytes * info->var.yres);
- if (!strcmp(dp->parent->name, "sbus") ||
- !strcmp(dp->parent->name, "sbi")) {
+ if (of_node_name_eq(dp->parent, "sbus") ||
+ of_node_name_eq(dp->parent, "sbi")) {
info->fix.smem_start = op->resource[0].start;
par->iospace = op->resource[0].flags & IORESOURCE_BITS;
} else {
diff --git a/drivers/video/fbdev/cg3.c b/drivers/video/fbdev/cg3.c
index 1bd95b02f3aa..6d42def8436b 100644
--- a/drivers/video/fbdev/cg3.c
+++ b/drivers/video/fbdev/cg3.c
@@ -369,7 +369,7 @@ static int cg3_probe(struct platform_device *op)
info->var.red.length = 8;
info->var.green.length = 8;
info->var.blue.length = 8;
- if (!strcmp(dp->name, "cgRDI"))
+ if (of_node_name_eq(dp, "cgRDI"))
par->flags |= CG3_FLAG_RDI;
if (par->flags & CG3_FLAG_RDI)
cg3_rdi_maybe_fixup_var(&info->var, dp);
diff --git a/drivers/video/fbdev/chipsfb.c b/drivers/video/fbdev/chipsfb.c
index 40182ed85648..ca549e1532e6 100644
--- a/drivers/video/fbdev/chipsfb.c
+++ b/drivers/video/fbdev/chipsfb.c
@@ -349,7 +349,7 @@ static void init_chips(struct fb_info *p, unsigned long addr)
static int chipsfb_pci_init(struct pci_dev *dp, const struct pci_device_id *ent)
{
struct fb_info *p;
- unsigned long addr, size;
+ unsigned long addr;
unsigned short cmd;
int rc = -ENODEV;
@@ -361,7 +361,6 @@ static int chipsfb_pci_init(struct pci_dev *dp, const struct pci_device_id *ent)
if ((dp->resource[0].flags & IORESOURCE_MEM) == 0)
goto err_disable;
addr = pci_resource_start(dp, 0);
- size = pci_resource_len(dp, 0);
if (addr == 0)
goto err_disable;
diff --git a/drivers/video/fbdev/core/fb_cmdline.c b/drivers/video/fbdev/core/fb_cmdline.c
index 39509ccd92f1..3b5bd666b952 100644
--- a/drivers/video/fbdev/core/fb_cmdline.c
+++ b/drivers/video/fbdev/core/fb_cmdline.c
@@ -75,36 +75,33 @@ EXPORT_SYMBOL(fb_get_options);
* NOTE: This function is a __setup and __init function.
* It only stores the options. Drivers have to call
* fb_get_options() as necessary.
- *
- * Returns zero.
- *
*/
static int __init video_setup(char *options)
{
- int i, global = 0;
-
if (!options || !*options)
- global = 1;
+ goto out;
- if (!global && !strncmp(options, "ofonly", 6)) {
+ if (!strncmp(options, "ofonly", 6)) {
ofonly = 1;
- global = 1;
+ goto out;
}
- if (!global && !strchr(options, ':')) {
- fb_mode_option = options;
- global = 1;
- }
+ if (strchr(options, ':')) {
+ /* named */
+ int i;
- if (!global) {
for (i = 0; i < FB_MAX; i++) {
if (video_options[i] == NULL) {
video_options[i] = options;
break;
}
}
+ } else {
+ /* global */
+ fb_mode_option = options;
}
+out:
return 1;
}
__setup("video=", video_setup);
diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c
index bfa1360ec750..cd059a801662 100644
--- a/drivers/video/fbdev/core/fbcon.c
+++ b/drivers/video/fbdev/core/fbcon.c
@@ -656,11 +656,14 @@ static void fbcon_prepare_logo(struct vc_data *vc, struct fb_info *info,
kfree(save);
}
+ if (logo_shown == FBCON_LOGO_DONTSHOW)
+ return;
+
if (logo_lines > vc->vc_bottom) {
logo_shown = FBCON_LOGO_CANSHOW;
printk(KERN_INFO
"fbcon_init: disable boot-logo (boot-logo bigger than screen).\n");
- } else if (logo_shown != FBCON_LOGO_DONTSHOW) {
+ } else {
logo_shown = FBCON_LOGO_DRAW;
vc->vc_top = logo_lines;
}
@@ -999,7 +1002,7 @@ static const char *fbcon_startup(void)
if (!softback_buf) {
softback_buf =
(unsigned long)
- kmalloc(fbcon_softback_size,
+ kvmalloc(fbcon_softback_size,
GFP_KERNEL);
if (!softback_buf) {
fbcon_softback_size = 0;
@@ -1008,7 +1011,7 @@ static const char *fbcon_startup(void)
}
} else {
if (softback_buf) {
- kfree((void *) softback_buf);
+ kvfree((void *) softback_buf);
softback_buf = 0;
softback_top = 0;
}
@@ -1066,6 +1069,9 @@ static void fbcon_init(struct vc_data *vc, int init)
cap = info->flags;
+ if (console_loglevel <= CONSOLE_LOGLEVEL_QUIET)
+ logo_shown = FBCON_LOGO_DONTSHOW;
+
if (vc != svc || logo_shown == FBCON_LOGO_DONTSHOW ||
(info->fix.type == FB_TYPE_TEXT))
logo = 0;
@@ -3672,7 +3678,7 @@ static void fbcon_exit(void)
}
#endif
- kfree((void *)softback_buf);
+ kvfree((void *)softback_buf);
softback_buf = 0UL;
for_each_registered_fb(i) {
diff --git a/drivers/video/fbdev/core/fbmem.c b/drivers/video/fbdev/core/fbmem.c
index cb43a2258c51..4721491e6c8c 100644
--- a/drivers/video/fbdev/core/fbmem.c
+++ b/drivers/video/fbdev/core/fbmem.c
@@ -431,6 +431,9 @@ static void fb_do_show_logo(struct fb_info *info, struct fb_image *image,
{
unsigned int x;
+ if (image->width > info->var.xres || image->height > info->var.yres)
+ return;
+
if (rotate == FB_ROTATE_UR) {
for (x = 0;
x < num && image->dx + image->width <= info->var.xres;
diff --git a/drivers/video/fbdev/core/fbmon.c b/drivers/video/fbdev/core/fbmon.c
index dd3128990776..3558a70a6664 100644
--- a/drivers/video/fbdev/core/fbmon.c
+++ b/drivers/video/fbdev/core/fbmon.c
@@ -978,6 +978,8 @@ void fb_edid_to_monspecs(unsigned char *edid, struct fb_monspecs *specs)
get_monspecs(edid, specs);
specs->modedb = fb_create_modedb(edid, &specs->modedb_len, specs);
+ if (!specs->modedb)
+ return;
/*
* Workaround for buggy EDIDs that sets that the first
diff --git a/drivers/video/fbdev/ffb.c b/drivers/video/fbdev/ffb.c
index 6b1915872af1..b7aee0c427a8 100644
--- a/drivers/video/fbdev/ffb.c
+++ b/drivers/video/fbdev/ffb.c
@@ -944,7 +944,7 @@ static int ffb_probe(struct platform_device *op)
info->var.accel_flags = FB_ACCELF_TEXT;
- if (!strcmp(dp->name, "SUNW,afb"))
+ if (of_node_name_eq(dp, "SUNW,afb"))
par->flags |= FFB_FLAG_AFB;
par->board_type = of_getintprop_default(dp, "board_type", 0);
diff --git a/drivers/video/fbdev/geode/gxfb_core.c b/drivers/video/fbdev/geode/gxfb_core.c
index f4f76373b2a8..b1906cf5a8f0 100644
--- a/drivers/video/fbdev/geode/gxfb_core.c
+++ b/drivers/video/fbdev/geode/gxfb_core.c
@@ -33,6 +33,8 @@
#include <linux/pci.h>
#include <linux/cs5535.h>
+#include <asm/olpc.h>
+
#include "gxfb.h"
static char *mode_option;
@@ -107,9 +109,6 @@ static struct fb_videomode gx_modedb[] = {
FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
};
-#ifdef CONFIG_OLPC
-#include <asm/olpc.h>
-
static struct fb_videomode gx_dcon_modedb[] = {
/* The only mode the DCON has is 1200x900 */
{ NULL, 50, 1200, 900, 17460, 24, 8, 4, 5, 8, 3,
@@ -128,14 +127,6 @@ static void get_modedb(struct fb_videomode **modedb, unsigned int *size)
}
}
-#else
-static void get_modedb(struct fb_videomode **modedb, unsigned int *size)
-{
- *modedb = (struct fb_videomode *) gx_modedb;
- *size = ARRAY_SIZE(gx_modedb);
-}
-#endif
-
static int gxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
{
if (var->xres > 1600 || var->yres > 1200)
diff --git a/drivers/video/fbdev/geode/lxfb_core.c b/drivers/video/fbdev/geode/lxfb_core.c
index 138da6cb6cbc..17ab905811b1 100644
--- a/drivers/video/fbdev/geode/lxfb_core.c
+++ b/drivers/video/fbdev/geode/lxfb_core.c
@@ -23,6 +23,8 @@
#include <linux/pci.h>
#include <linux/uaccess.h>
+#include <asm/olpc.h>
+
#include "lxfb.h"
static char *mode_option;
@@ -216,9 +218,6 @@ static struct fb_videomode geode_modedb[] = {
0, FB_VMODE_NONINTERLACED, 0 },
};
-#ifdef CONFIG_OLPC
-#include <asm/olpc.h>
-
static struct fb_videomode olpc_dcon_modedb[] = {
/* The only mode the DCON has is 1200x900 */
{ NULL, 50, 1200, 900, 17460, 24, 8, 4, 5, 8, 3,
@@ -237,14 +236,6 @@ static void get_modedb(struct fb_videomode **modedb, unsigned int *size)
}
}
-#else
-static void get_modedb(struct fb_videomode **modedb, unsigned int *size)
-{
- *modedb = (struct fb_videomode *) geode_modedb;
- *size = ARRAY_SIZE(geode_modedb);
-}
-#endif
-
static int lxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
{
if (var->xres > 1920 || var->yres > 1440)
diff --git a/drivers/video/fbdev/imsttfb.c b/drivers/video/fbdev/imsttfb.c
index 5d9670daf60e..4b9615e4ce74 100644
--- a/drivers/video/fbdev/imsttfb.c
+++ b/drivers/video/fbdev/imsttfb.c
@@ -1497,8 +1497,8 @@ static int imsttfb_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
switch (pdev->device) {
case PCI_DEVICE_ID_IMS_TT128: /* IMS,tt128mbA */
par->ramdac = IBM;
- if (dp && ((strcmp(dp->name, "IMS,tt128mb8") == 0) ||
- (strcmp(dp->name, "IMS,tt128mb8A") == 0)))
+ if (of_node_name_eq(dp, "IMS,tt128mb8") ||
+ of_node_name_eq(dp, "IMS,tt128mb8A"))
par->ramdac = TVP;
break;
case PCI_DEVICE_ID_IMS_TT3D: /* IMS,tt3d */
diff --git a/drivers/video/fbdev/mbx/mbxdebugfs.c b/drivers/video/fbdev/mbx/mbxdebugfs.c
index 2bd328883178..09af721638fb 100644
--- a/drivers/video/fbdev/mbx/mbxdebugfs.c
+++ b/drivers/video/fbdev/mbx/mbxdebugfs.c
@@ -211,36 +211,22 @@ static const struct file_operations misc_fops = {
static void mbxfb_debugfs_init(struct fb_info *fbi)
{
struct mbxfb_info *mfbi = fbi->par;
- struct mbxfb_debugfs_data *dbg;
-
- dbg = kzalloc(sizeof(struct mbxfb_debugfs_data), GFP_KERNEL);
- mfbi->debugfs_data = dbg;
-
- dbg->dir = debugfs_create_dir("mbxfb", NULL);
- dbg->sysconf = debugfs_create_file("sysconf", 0444, dbg->dir,
- fbi, &sysconf_fops);
- dbg->clock = debugfs_create_file("clock", 0444, dbg->dir,
- fbi, &clock_fops);
- dbg->display = debugfs_create_file("display", 0444, dbg->dir,
- fbi, &display_fops);
- dbg->gsctl = debugfs_create_file("gsctl", 0444, dbg->dir,
- fbi, &gsctl_fops);
- dbg->sdram = debugfs_create_file("sdram", 0444, dbg->dir,
- fbi, &sdram_fops);
- dbg->misc = debugfs_create_file("misc", 0444, dbg->dir,
- fbi, &misc_fops);
+ struct dentry *dir;
+
+ dir = debugfs_create_dir("mbxfb", NULL);
+ mfbi->debugfs_dir = dir;
+
+ debugfs_create_file("sysconf", 0444, dir, fbi, &sysconf_fops);
+ debugfs_create_file("clock", 0444, dir, fbi, &clock_fops);
+ debugfs_create_file("display", 0444, dir, fbi, &display_fops);
+ debugfs_create_file("gsctl", 0444, dir, fbi, &gsctl_fops);
+ debugfs_create_file("sdram", 0444, dir, fbi, &sdram_fops);
+ debugfs_create_file("misc", 0444, dir, fbi, &misc_fops);
}
static void mbxfb_debugfs_remove(struct fb_info *fbi)
{
struct mbxfb_info *mfbi = fbi->par;
- struct mbxfb_debugfs_data *dbg = mfbi->debugfs_data;
-
- debugfs_remove(dbg->misc);
- debugfs_remove(dbg->sdram);
- debugfs_remove(dbg->gsctl);
- debugfs_remove(dbg->display);
- debugfs_remove(dbg->clock);
- debugfs_remove(dbg->sysconf);
- debugfs_remove(dbg->dir);
+
+ debugfs_remove_recursive(mfbi->debugfs_dir);
}
diff --git a/drivers/video/fbdev/mbx/mbxfb.c b/drivers/video/fbdev/mbx/mbxfb.c
index 539b85da0897..6ded480a69b4 100644
--- a/drivers/video/fbdev/mbx/mbxfb.c
+++ b/drivers/video/fbdev/mbx/mbxfb.c
@@ -74,7 +74,7 @@ struct mbxfb_info {
u32 pseudo_palette[MAX_PALETTES];
#ifdef CONFIG_FB_MBX_DEBUG
- void *debugfs_data;
+ struct dentry *debugfs_dir;
#endif
};
diff --git a/drivers/video/fbdev/offb.c b/drivers/video/fbdev/offb.c
index 057d3cdef92e..fbc6eafb63c7 100644
--- a/drivers/video/fbdev/offb.c
+++ b/drivers/video/fbdev/offb.c
@@ -141,6 +141,7 @@ static int offb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
/* Clear PALETTE_ACCESS_CNTL in DAC_CNTL */
out_le32(par->cmap_adr + 0x58,
in_le32(par->cmap_adr + 0x58) & ~0x20);
+ /* fall through */
case cmap_r128:
/* Set palette index & data */
out_8(par->cmap_adr + 0xb0, regno);
@@ -210,6 +211,7 @@ static int offb_blank(int blank, struct fb_info *info)
/* Clear PALETTE_ACCESS_CNTL in DAC_CNTL */
out_le32(par->cmap_adr + 0x58,
in_le32(par->cmap_adr + 0x58) & ~0x20);
+ /* fall through */
case cmap_r128:
/* Set palette index & data */
out_8(par->cmap_adr + 0xb0, i);
@@ -646,7 +648,7 @@ static void __init offb_init_nodriver(struct device_node *dp, int no_real_node)
}
#endif
/* kludge for valkyrie */
- if (strcmp(dp->name, "valkyrie") == 0)
+ if (of_node_name_eq(dp, "valkyrie"))
address += 0x1000;
offb_init_fb(no_real_node ? "bootx" : NULL,
width, height, depth, pitch, address,
diff --git a/drivers/video/fbdev/omap2/omapfb/dss/core.c b/drivers/video/fbdev/omap2/omapfb/dss/core.c
index b4bcf3a4a647..b5956a1a30d4 100644
--- a/drivers/video/fbdev/omap2/omapfb/dss/core.c
+++ b/drivers/video/fbdev/omap2/omapfb/dss/core.c
@@ -110,19 +110,12 @@ DEFINE_SHOW_ATTRIBUTE(dss);
static struct dentry *dss_debugfs_dir;
-static int dss_initialize_debugfs(void)
+static void dss_initialize_debugfs(void)
{
dss_debugfs_dir = debugfs_create_dir("omapdss", NULL);
- if (IS_ERR(dss_debugfs_dir)) {
- int err = PTR_ERR(dss_debugfs_dir);
- dss_debugfs_dir = NULL;
- return err;
- }
debugfs_create_file("clk", S_IRUGO, dss_debugfs_dir,
&dss_debug_dump_clocks, &dss_fops);
-
- return 0;
}
static void dss_uninitialize_debugfs(void)
@@ -130,26 +123,19 @@ static void dss_uninitialize_debugfs(void)
debugfs_remove_recursive(dss_debugfs_dir);
}
-int dss_debugfs_create_file(const char *name, void (*write)(struct seq_file *))
+void dss_debugfs_create_file(const char *name, void (*write)(struct seq_file *))
{
- struct dentry *d;
-
- d = debugfs_create_file(name, S_IRUGO, dss_debugfs_dir,
- write, &dss_fops);
-
- return PTR_ERR_OR_ZERO(d);
+ debugfs_create_file(name, S_IRUGO, dss_debugfs_dir, write, &dss_fops);
}
#else /* CONFIG_FB_OMAP2_DSS_DEBUGFS */
-static inline int dss_initialize_debugfs(void)
+static inline void dss_initialize_debugfs(void)
{
- return 0;
}
static inline void dss_uninitialize_debugfs(void)
{
}
-int dss_debugfs_create_file(const char *name, void (*write)(struct seq_file *))
+void dss_debugfs_create_file(const char *name, void (*write)(struct seq_file *))
{
- return 0;
}
#endif /* CONFIG_FB_OMAP2_DSS_DEBUGFS */
@@ -182,15 +168,11 @@ static struct notifier_block omap_dss_pm_notif_block = {
static int __init omap_dss_probe(struct platform_device *pdev)
{
- int r;
-
core.pdev = pdev;
dss_features_init(omapdss_get_version());
- r = dss_initialize_debugfs();
- if (r)
- goto err_debugfs;
+ dss_initialize_debugfs();
if (def_disp_name)
core.default_display_name = def_disp_name;
@@ -198,10 +180,6 @@ static int __init omap_dss_probe(struct platform_device *pdev)
register_pm_notifier(&omap_dss_pm_notif_block);
return 0;
-
-err_debugfs:
-
- return r;
}
static int omap_dss_remove(struct platform_device *pdev)
diff --git a/drivers/video/fbdev/omap2/omapfb/dss/dss-of.c b/drivers/video/fbdev/omap2/omapfb/dss/dss-of.c
index f1eb8b0f8a2a..5ce893c1923d 100644
--- a/drivers/video/fbdev/omap2/omapfb/dss/dss-of.c
+++ b/drivers/video/fbdev/omap2/omapfb/dss/dss-of.c
@@ -60,7 +60,7 @@ omapdss_of_get_next_port(const struct device_node *parent,
return NULL;
}
prev = port;
- } while (of_node_cmp(port->name, "port") != 0);
+ } while (!of_node_name_eq(port, "port"));
of_node_put(ports);
}
@@ -83,7 +83,7 @@ omapdss_of_get_next_endpoint(const struct device_node *parent,
if (!ep)
return NULL;
prev = ep;
- } while (of_node_cmp(ep->name, "endpoint") != 0);
+ } while (!of_node_name_eq(ep, "endpoint"));
return ep;
}
diff --git a/drivers/video/fbdev/omap2/omapfb/dss/dss.h b/drivers/video/fbdev/omap2/omapfb/dss/dss.h
index a3cc0ca8f9d2..b1a354494144 100644
--- a/drivers/video/fbdev/omap2/omapfb/dss/dss.h
+++ b/drivers/video/fbdev/omap2/omapfb/dss/dss.h
@@ -214,7 +214,7 @@ struct platform_device *dss_get_core_pdev(void);
int dss_dsi_enable_pads(int dsi_id, unsigned lane_mask);
void dss_dsi_disable_pads(int dsi_id, unsigned lane_mask);
int dss_set_min_bus_tput(struct device *dev, unsigned long tput);
-int dss_debugfs_create_file(const char *name, void (*write)(struct seq_file *));
+void dss_debugfs_create_file(const char *name, void (*write)(struct seq_file *));
/* display */
int dss_suspend_all_devices(void);
diff --git a/drivers/video/fbdev/omap2/omapfb/dss/hdmi4_core.c b/drivers/video/fbdev/omap2/omapfb/dss/hdmi4_core.c
index fa72e735dad2..d146793dd044 100644
--- a/drivers/video/fbdev/omap2/omapfb/dss/hdmi4_core.c
+++ b/drivers/video/fbdev/omap2/omapfb/dss/hdmi4_core.c
@@ -712,7 +712,7 @@ int hdmi4_audio_config(struct hdmi_core_data *core, struct hdmi_wp_data *wp,
else
acore.i2s_cfg.justification = HDMI_AUDIO_JUSTIFY_RIGHT;
/*
- * The I2S input word length is twice the lenght given in the IEC-60958
+ * The I2S input word length is twice the length given in the IEC-60958
* status word. If the word size is greater than
* 20 bits, increment by one.
*/
diff --git a/drivers/video/fbdev/ssd1307fb.c b/drivers/video/fbdev/ssd1307fb.c
index 4061a20cfe24..3b361bc9feb8 100644
--- a/drivers/video/fbdev/ssd1307fb.c
+++ b/drivers/video/fbdev/ssd1307fb.c
@@ -667,10 +667,10 @@ static int ssd1307fb_probe(struct i2c_client *client,
if (par->reset) {
/* Reset the screen */
- gpiod_set_value_cansleep(par->reset, 0);
- udelay(4);
gpiod_set_value_cansleep(par->reset, 1);
udelay(4);
+ gpiod_set_value_cansleep(par->reset, 0);
+ udelay(4);
}
if (par->vbat_reg) {
diff --git a/drivers/video/fbdev/via/viafbdev.c b/drivers/video/fbdev/via/viafbdev.c
index 7bb7e90b8f00..bdf5a0ea876d 100644
--- a/drivers/video/fbdev/via/viafbdev.c
+++ b/drivers/video/fbdev/via/viafbdev.c
@@ -2110,7 +2110,7 @@ MODULE_PARM_DESC(viafb_lcd_panel_id,
module_param(viafb_lcd_dsp_method, int, S_IRUSR);
MODULE_PARM_DESC(viafb_lcd_dsp_method,
- "Set Flat Panel display scaling method.(Default=Expandsion)");
+ "Set Flat Panel display scaling method.(Default=Expansion)");
module_param(viafb_SAMM_ON, int, S_IRUSR);
MODULE_PARM_DESC(viafb_SAMM_ON,
diff --git a/drivers/virt/vboxguest/vboxguest_core.c b/drivers/virt/vboxguest/vboxguest_core.c
index df7d09409efe..8ca333f21292 100644
--- a/drivers/virt/vboxguest/vboxguest_core.c
+++ b/drivers/virt/vboxguest/vboxguest_core.c
@@ -27,6 +27,10 @@
#define GUEST_MAPPINGS_TRIES 5
+#define VBG_KERNEL_REQUEST \
+ (VMMDEV_REQUESTOR_KERNEL | VMMDEV_REQUESTOR_USR_DRV | \
+ VMMDEV_REQUESTOR_CON_DONT_KNOW | VMMDEV_REQUESTOR_TRUST_NOT_GIVEN)
+
/**
* Reserves memory in which the VMM can relocate any guest mappings
* that are floating around.
@@ -48,7 +52,8 @@ static void vbg_guest_mappings_init(struct vbg_dev *gdev)
int i, rc;
/* Query the required space. */
- req = vbg_req_alloc(sizeof(*req), VMMDEVREQ_GET_HYPERVISOR_INFO);
+ req = vbg_req_alloc(sizeof(*req), VMMDEVREQ_GET_HYPERVISOR_INFO,
+ VBG_KERNEL_REQUEST);
if (!req)
return;
@@ -135,7 +140,8 @@ static void vbg_guest_mappings_exit(struct vbg_dev *gdev)
* Tell the host that we're going to free the memory we reserved for
* it, the free it up. (Leak the memory if anything goes wrong here.)
*/
- req = vbg_req_alloc(sizeof(*req), VMMDEVREQ_SET_HYPERVISOR_INFO);
+ req = vbg_req_alloc(sizeof(*req), VMMDEVREQ_SET_HYPERVISOR_INFO,
+ VBG_KERNEL_REQUEST);
if (!req)
return;
@@ -172,8 +178,10 @@ static int vbg_report_guest_info(struct vbg_dev *gdev)
struct vmmdev_guest_info2 *req2 = NULL;
int rc, ret = -ENOMEM;
- req1 = vbg_req_alloc(sizeof(*req1), VMMDEVREQ_REPORT_GUEST_INFO);
- req2 = vbg_req_alloc(sizeof(*req2), VMMDEVREQ_REPORT_GUEST_INFO2);
+ req1 = vbg_req_alloc(sizeof(*req1), VMMDEVREQ_REPORT_GUEST_INFO,
+ VBG_KERNEL_REQUEST);
+ req2 = vbg_req_alloc(sizeof(*req2), VMMDEVREQ_REPORT_GUEST_INFO2,
+ VBG_KERNEL_REQUEST);
if (!req1 || !req2)
goto out_free;
@@ -187,8 +195,8 @@ static int vbg_report_guest_info(struct vbg_dev *gdev)
req2->additions_minor = VBG_VERSION_MINOR;
req2->additions_build = VBG_VERSION_BUILD;
req2->additions_revision = VBG_SVN_REV;
- /* (no features defined yet) */
- req2->additions_features = 0;
+ req2->additions_features =
+ VMMDEV_GUEST_INFO2_ADDITIONS_FEATURES_REQUESTOR_INFO;
strlcpy(req2->name, VBG_VERSION_STRING,
sizeof(req2->name));
@@ -230,7 +238,8 @@ static int vbg_report_driver_status(struct vbg_dev *gdev, bool active)
struct vmmdev_guest_status *req;
int rc;
- req = vbg_req_alloc(sizeof(*req), VMMDEVREQ_REPORT_GUEST_STATUS);
+ req = vbg_req_alloc(sizeof(*req), VMMDEVREQ_REPORT_GUEST_STATUS,
+ VBG_KERNEL_REQUEST);
if (!req)
return -ENOMEM;
@@ -423,7 +432,8 @@ static int vbg_heartbeat_host_config(struct vbg_dev *gdev, bool enabled)
struct vmmdev_heartbeat *req;
int rc;
- req = vbg_req_alloc(sizeof(*req), VMMDEVREQ_HEARTBEAT_CONFIGURE);
+ req = vbg_req_alloc(sizeof(*req), VMMDEVREQ_HEARTBEAT_CONFIGURE,
+ VBG_KERNEL_REQUEST);
if (!req)
return -ENOMEM;
@@ -457,7 +467,8 @@ static int vbg_heartbeat_init(struct vbg_dev *gdev)
gdev->guest_heartbeat_req = vbg_req_alloc(
sizeof(*gdev->guest_heartbeat_req),
- VMMDEVREQ_GUEST_HEARTBEAT);
+ VMMDEVREQ_GUEST_HEARTBEAT,
+ VBG_KERNEL_REQUEST);
if (!gdev->guest_heartbeat_req)
return -ENOMEM;
@@ -528,7 +539,8 @@ static int vbg_reset_host_event_filter(struct vbg_dev *gdev,
struct vmmdev_mask *req;
int rc;
- req = vbg_req_alloc(sizeof(*req), VMMDEVREQ_CTL_GUEST_FILTER_MASK);
+ req = vbg_req_alloc(sizeof(*req), VMMDEVREQ_CTL_GUEST_FILTER_MASK,
+ VBG_KERNEL_REQUEST);
if (!req)
return -ENOMEM;
@@ -567,8 +579,14 @@ static int vbg_set_session_event_filter(struct vbg_dev *gdev,
u32 changed, previous;
int rc, ret = 0;
- /* Allocate a request buffer before taking the spinlock */
- req = vbg_req_alloc(sizeof(*req), VMMDEVREQ_CTL_GUEST_FILTER_MASK);
+ /*
+ * Allocate a request buffer before taking the spinlock, when
+ * the session is being terminated the requestor is the kernel,
+ * as we're cleaning up.
+ */
+ req = vbg_req_alloc(sizeof(*req), VMMDEVREQ_CTL_GUEST_FILTER_MASK,
+ session_termination ? VBG_KERNEL_REQUEST :
+ session->requestor);
if (!req) {
if (!session_termination)
return -ENOMEM;
@@ -627,7 +645,8 @@ static int vbg_reset_host_capabilities(struct vbg_dev *gdev)
struct vmmdev_mask *req;
int rc;
- req = vbg_req_alloc(sizeof(*req), VMMDEVREQ_SET_GUEST_CAPABILITIES);
+ req = vbg_req_alloc(sizeof(*req), VMMDEVREQ_SET_GUEST_CAPABILITIES,
+ VBG_KERNEL_REQUEST);
if (!req)
return -ENOMEM;
@@ -662,8 +681,14 @@ static int vbg_set_session_capabilities(struct vbg_dev *gdev,
u32 changed, previous;
int rc, ret = 0;
- /* Allocate a request buffer before taking the spinlock */
- req = vbg_req_alloc(sizeof(*req), VMMDEVREQ_SET_GUEST_CAPABILITIES);
+ /*
+ * Allocate a request buffer before taking the spinlock, when
+ * the session is being terminated the requestor is the kernel,
+ * as we're cleaning up.
+ */
+ req = vbg_req_alloc(sizeof(*req), VMMDEVREQ_SET_GUEST_CAPABILITIES,
+ session_termination ? VBG_KERNEL_REQUEST :
+ session->requestor);
if (!req) {
if (!session_termination)
return -ENOMEM;
@@ -722,7 +747,8 @@ static int vbg_query_host_version(struct vbg_dev *gdev)
struct vmmdev_host_version *req;
int rc, ret;
- req = vbg_req_alloc(sizeof(*req), VMMDEVREQ_GET_HOST_VERSION);
+ req = vbg_req_alloc(sizeof(*req), VMMDEVREQ_GET_HOST_VERSION,
+ VBG_KERNEL_REQUEST);
if (!req)
return -ENOMEM;
@@ -783,19 +809,24 @@ int vbg_core_init(struct vbg_dev *gdev, u32 fixed_events)
gdev->mem_balloon.get_req =
vbg_req_alloc(sizeof(*gdev->mem_balloon.get_req),
- VMMDEVREQ_GET_MEMBALLOON_CHANGE_REQ);
+ VMMDEVREQ_GET_MEMBALLOON_CHANGE_REQ,
+ VBG_KERNEL_REQUEST);
gdev->mem_balloon.change_req =
vbg_req_alloc(sizeof(*gdev->mem_balloon.change_req),
- VMMDEVREQ_CHANGE_MEMBALLOON);
+ VMMDEVREQ_CHANGE_MEMBALLOON,
+ VBG_KERNEL_REQUEST);
gdev->cancel_req =
vbg_req_alloc(sizeof(*(gdev->cancel_req)),
- VMMDEVREQ_HGCM_CANCEL2);
+ VMMDEVREQ_HGCM_CANCEL2,
+ VBG_KERNEL_REQUEST);
gdev->ack_events_req =
vbg_req_alloc(sizeof(*gdev->ack_events_req),
- VMMDEVREQ_ACKNOWLEDGE_EVENTS);
+ VMMDEVREQ_ACKNOWLEDGE_EVENTS,
+ VBG_KERNEL_REQUEST);
gdev->mouse_status_req =
vbg_req_alloc(sizeof(*gdev->mouse_status_req),
- VMMDEVREQ_GET_MOUSE_STATUS);
+ VMMDEVREQ_GET_MOUSE_STATUS,
+ VBG_KERNEL_REQUEST);
if (!gdev->mem_balloon.get_req || !gdev->mem_balloon.change_req ||
!gdev->cancel_req || !gdev->ack_events_req ||
@@ -892,9 +923,9 @@ void vbg_core_exit(struct vbg_dev *gdev)
* vboxguest_linux.c calls this when userspace opens the char-device.
* Return: A pointer to the new session or an ERR_PTR on error.
* @gdev: The Guest extension device.
- * @user: Set if this is a session for the vboxuser device.
+ * @requestor: VMMDEV_REQUESTOR_* flags
*/
-struct vbg_session *vbg_core_open_session(struct vbg_dev *gdev, bool user)
+struct vbg_session *vbg_core_open_session(struct vbg_dev *gdev, u32 requestor)
{
struct vbg_session *session;
@@ -903,7 +934,7 @@ struct vbg_session *vbg_core_open_session(struct vbg_dev *gdev, bool user)
return ERR_PTR(-ENOMEM);
session->gdev = gdev;
- session->user_session = user;
+ session->requestor = requestor;
return session;
}
@@ -924,7 +955,9 @@ void vbg_core_close_session(struct vbg_session *session)
if (!session->hgcm_client_ids[i])
continue;
- vbg_hgcm_disconnect(gdev, session->hgcm_client_ids[i], &rc);
+ /* requestor is kernel here, as we're cleaning up. */
+ vbg_hgcm_disconnect(gdev, VBG_KERNEL_REQUEST,
+ session->hgcm_client_ids[i], &rc);
}
kfree(session);
@@ -1152,7 +1185,8 @@ static int vbg_req_allowed(struct vbg_dev *gdev, struct vbg_session *session,
return -EPERM;
}
- if (trusted_apps_only && session->user_session) {
+ if (trusted_apps_only &&
+ (session->requestor & VMMDEV_REQUESTOR_USER_DEVICE)) {
vbg_err("Denying userspace vmm call type %#08x through vboxuser device node\n",
req->request_type);
return -EPERM;
@@ -1209,8 +1243,8 @@ static int vbg_ioctl_hgcm_connect(struct vbg_dev *gdev,
if (i >= ARRAY_SIZE(session->hgcm_client_ids))
return -EMFILE;
- ret = vbg_hgcm_connect(gdev, &conn->u.in.loc, &client_id,
- &conn->hdr.rc);
+ ret = vbg_hgcm_connect(gdev, session->requestor, &conn->u.in.loc,
+ &client_id, &conn->hdr.rc);
mutex_lock(&gdev->session_mutex);
if (ret == 0 && conn->hdr.rc >= 0) {
@@ -1251,7 +1285,8 @@ static int vbg_ioctl_hgcm_disconnect(struct vbg_dev *gdev,
if (i >= ARRAY_SIZE(session->hgcm_client_ids))
return -EINVAL;
- ret = vbg_hgcm_disconnect(gdev, client_id, &disconn->hdr.rc);
+ ret = vbg_hgcm_disconnect(gdev, session->requestor, client_id,
+ &disconn->hdr.rc);
mutex_lock(&gdev->session_mutex);
if (ret == 0 && disconn->hdr.rc >= 0)
@@ -1313,12 +1348,12 @@ static int vbg_ioctl_hgcm_call(struct vbg_dev *gdev,
}
if (IS_ENABLED(CONFIG_COMPAT) && f32bit)
- ret = vbg_hgcm_call32(gdev, client_id,
+ ret = vbg_hgcm_call32(gdev, session->requestor, client_id,
call->function, call->timeout_ms,
VBG_IOCTL_HGCM_CALL_PARMS32(call),
call->parm_count, &call->hdr.rc);
else
- ret = vbg_hgcm_call(gdev, client_id,
+ ret = vbg_hgcm_call(gdev, session->requestor, client_id,
call->function, call->timeout_ms,
VBG_IOCTL_HGCM_CALL_PARMS(call),
call->parm_count, &call->hdr.rc);
@@ -1408,6 +1443,7 @@ static int vbg_ioctl_check_balloon(struct vbg_dev *gdev,
}
static int vbg_ioctl_write_core_dump(struct vbg_dev *gdev,
+ struct vbg_session *session,
struct vbg_ioctl_write_coredump *dump)
{
struct vmmdev_write_core_dump *req;
@@ -1415,7 +1451,8 @@ static int vbg_ioctl_write_core_dump(struct vbg_dev *gdev,
if (vbg_ioctl_chk(&dump->hdr, sizeof(dump->u.in), 0))
return -EINVAL;
- req = vbg_req_alloc(sizeof(*req), VMMDEVREQ_WRITE_COREDUMP);
+ req = vbg_req_alloc(sizeof(*req), VMMDEVREQ_WRITE_COREDUMP,
+ session->requestor);
if (!req)
return -ENOMEM;
@@ -1476,7 +1513,7 @@ int vbg_core_ioctl(struct vbg_session *session, unsigned int req, void *data)
case VBG_IOCTL_CHECK_BALLOON:
return vbg_ioctl_check_balloon(gdev, data);
case VBG_IOCTL_WRITE_CORE_DUMP:
- return vbg_ioctl_write_core_dump(gdev, data);
+ return vbg_ioctl_write_core_dump(gdev, session, data);
}
/* Variable sized requests. */
@@ -1508,7 +1545,8 @@ int vbg_core_set_mouse_status(struct vbg_dev *gdev, u32 features)
struct vmmdev_mouse_status *req;
int rc;
- req = vbg_req_alloc(sizeof(*req), VMMDEVREQ_SET_MOUSE_STATUS);
+ req = vbg_req_alloc(sizeof(*req), VMMDEVREQ_SET_MOUSE_STATUS,
+ VBG_KERNEL_REQUEST);
if (!req)
return -ENOMEM;
diff --git a/drivers/virt/vboxguest/vboxguest_core.h b/drivers/virt/vboxguest/vboxguest_core.h
index 7ad9ec45bfa9..4188c12b839f 100644
--- a/drivers/virt/vboxguest/vboxguest_core.h
+++ b/drivers/virt/vboxguest/vboxguest_core.h
@@ -154,15 +154,15 @@ struct vbg_session {
* host. Protected by vbg_gdev.session_mutex.
*/
u32 guest_caps;
- /** Does this session belong to a root process or a user one? */
- bool user_session;
+ /** VMMDEV_REQUESTOR_* flags */
+ u32 requestor;
/** Set on CANCEL_ALL_WAITEVENTS, protected by vbg_devevent_spinlock. */
bool cancel_waiters;
};
int vbg_core_init(struct vbg_dev *gdev, u32 fixed_events);
void vbg_core_exit(struct vbg_dev *gdev);
-struct vbg_session *vbg_core_open_session(struct vbg_dev *gdev, bool user);
+struct vbg_session *vbg_core_open_session(struct vbg_dev *gdev, u32 requestor);
void vbg_core_close_session(struct vbg_session *session);
int vbg_core_ioctl(struct vbg_session *session, unsigned int req, void *data);
int vbg_core_set_mouse_status(struct vbg_dev *gdev, u32 features);
@@ -172,12 +172,13 @@ irqreturn_t vbg_core_isr(int irq, void *dev_id);
void vbg_linux_mouse_event(struct vbg_dev *gdev);
/* Private (non exported) functions form vboxguest_utils.c */
-void *vbg_req_alloc(size_t len, enum vmmdev_request_type req_type);
+void *vbg_req_alloc(size_t len, enum vmmdev_request_type req_type,
+ u32 requestor);
void vbg_req_free(void *req, size_t len);
int vbg_req_perform(struct vbg_dev *gdev, void *req);
int vbg_hgcm_call32(
- struct vbg_dev *gdev, u32 client_id, u32 function, u32 timeout_ms,
- struct vmmdev_hgcm_function_parameter32 *parm32, u32 parm_count,
- int *vbox_status);
+ struct vbg_dev *gdev, u32 requestor, u32 client_id, u32 function,
+ u32 timeout_ms, struct vmmdev_hgcm_function_parameter32 *parm32,
+ u32 parm_count, int *vbox_status);
#endif
diff --git a/drivers/virt/vboxguest/vboxguest_linux.c b/drivers/virt/vboxguest/vboxguest_linux.c
index 6e2a9619192d..6e8c0f1c1056 100644
--- a/drivers/virt/vboxguest/vboxguest_linux.c
+++ b/drivers/virt/vboxguest/vboxguest_linux.c
@@ -5,6 +5,7 @@
* Copyright (C) 2006-2016 Oracle Corporation
*/
+#include <linux/cred.h>
#include <linux/input.h>
#include <linux/kernel.h>
#include <linux/miscdevice.h>
@@ -28,6 +29,23 @@ static DEFINE_MUTEX(vbg_gdev_mutex);
/** Global vbg_gdev pointer used by vbg_get/put_gdev. */
static struct vbg_dev *vbg_gdev;
+static u32 vbg_misc_device_requestor(struct inode *inode)
+{
+ u32 requestor = VMMDEV_REQUESTOR_USERMODE |
+ VMMDEV_REQUESTOR_CON_DONT_KNOW |
+ VMMDEV_REQUESTOR_TRUST_NOT_GIVEN;
+
+ if (from_kuid(current_user_ns(), current->cred->uid) == 0)
+ requestor |= VMMDEV_REQUESTOR_USR_ROOT;
+ else
+ requestor |= VMMDEV_REQUESTOR_USR_USER;
+
+ if (in_egroup_p(inode->i_gid))
+ requestor |= VMMDEV_REQUESTOR_GRP_VBOX;
+
+ return requestor;
+}
+
static int vbg_misc_device_open(struct inode *inode, struct file *filp)
{
struct vbg_session *session;
@@ -36,7 +54,7 @@ static int vbg_misc_device_open(struct inode *inode, struct file *filp)
/* misc_open sets filp->private_data to our misc device */
gdev = container_of(filp->private_data, struct vbg_dev, misc_device);
- session = vbg_core_open_session(gdev, false);
+ session = vbg_core_open_session(gdev, vbg_misc_device_requestor(inode));
if (IS_ERR(session))
return PTR_ERR(session);
@@ -53,7 +71,8 @@ static int vbg_misc_device_user_open(struct inode *inode, struct file *filp)
gdev = container_of(filp->private_data, struct vbg_dev,
misc_device_user);
- session = vbg_core_open_session(gdev, false);
+ session = vbg_core_open_session(gdev, vbg_misc_device_requestor(inode) |
+ VMMDEV_REQUESTOR_USER_DEVICE);
if (IS_ERR(session))
return PTR_ERR(session);
@@ -115,7 +134,8 @@ static long vbg_misc_device_ioctl(struct file *filp, unsigned int req,
req == VBG_IOCTL_VMMDEV_REQUEST_BIG;
if (is_vmmdev_req)
- buf = vbg_req_alloc(size, VBG_IOCTL_HDR_TYPE_DEFAULT);
+ buf = vbg_req_alloc(size, VBG_IOCTL_HDR_TYPE_DEFAULT,
+ session->requestor);
else
buf = kmalloc(size, GFP_KERNEL);
if (!buf)
diff --git a/drivers/virt/vboxguest/vboxguest_utils.c b/drivers/virt/vboxguest/vboxguest_utils.c
index bf4474214b4d..75fd140b02ff 100644
--- a/drivers/virt/vboxguest/vboxguest_utils.c
+++ b/drivers/virt/vboxguest/vboxguest_utils.c
@@ -62,7 +62,8 @@ VBG_LOG(vbg_err, pr_err);
VBG_LOG(vbg_debug, pr_debug);
#endif
-void *vbg_req_alloc(size_t len, enum vmmdev_request_type req_type)
+void *vbg_req_alloc(size_t len, enum vmmdev_request_type req_type,
+ u32 requestor)
{
struct vmmdev_request_header *req;
int order = get_order(PAGE_ALIGN(len));
@@ -78,7 +79,7 @@ void *vbg_req_alloc(size_t len, enum vmmdev_request_type req_type)
req->request_type = req_type;
req->rc = VERR_GENERAL_FAILURE;
req->reserved1 = 0;
- req->reserved2 = 0;
+ req->requestor = requestor;
return req;
}
@@ -119,7 +120,7 @@ static bool hgcm_req_done(struct vbg_dev *gdev,
return done;
}
-int vbg_hgcm_connect(struct vbg_dev *gdev,
+int vbg_hgcm_connect(struct vbg_dev *gdev, u32 requestor,
struct vmmdev_hgcm_service_location *loc,
u32 *client_id, int *vbox_status)
{
@@ -127,7 +128,7 @@ int vbg_hgcm_connect(struct vbg_dev *gdev,
int rc;
hgcm_connect = vbg_req_alloc(sizeof(*hgcm_connect),
- VMMDEVREQ_HGCM_CONNECT);
+ VMMDEVREQ_HGCM_CONNECT, requestor);
if (!hgcm_connect)
return -ENOMEM;
@@ -153,13 +154,15 @@ int vbg_hgcm_connect(struct vbg_dev *gdev,
}
EXPORT_SYMBOL(vbg_hgcm_connect);
-int vbg_hgcm_disconnect(struct vbg_dev *gdev, u32 client_id, int *vbox_status)
+int vbg_hgcm_disconnect(struct vbg_dev *gdev, u32 requestor,
+ u32 client_id, int *vbox_status)
{
struct vmmdev_hgcm_disconnect *hgcm_disconnect = NULL;
int rc;
hgcm_disconnect = vbg_req_alloc(sizeof(*hgcm_disconnect),
- VMMDEVREQ_HGCM_DISCONNECT);
+ VMMDEVREQ_HGCM_DISCONNECT,
+ requestor);
if (!hgcm_disconnect)
return -ENOMEM;
@@ -593,9 +596,10 @@ static int hgcm_call_copy_back_result(
return 0;
}
-int vbg_hgcm_call(struct vbg_dev *gdev, u32 client_id, u32 function,
- u32 timeout_ms, struct vmmdev_hgcm_function_parameter *parms,
- u32 parm_count, int *vbox_status)
+int vbg_hgcm_call(struct vbg_dev *gdev, u32 requestor, u32 client_id,
+ u32 function, u32 timeout_ms,
+ struct vmmdev_hgcm_function_parameter *parms, u32 parm_count,
+ int *vbox_status)
{
struct vmmdev_hgcm_call *call;
void **bounce_bufs = NULL;
@@ -615,7 +619,7 @@ int vbg_hgcm_call(struct vbg_dev *gdev, u32 client_id, u32 function,
goto free_bounce_bufs;
}
- call = vbg_req_alloc(size, VMMDEVREQ_HGCM_CALL);
+ call = vbg_req_alloc(size, VMMDEVREQ_HGCM_CALL, requestor);
if (!call) {
ret = -ENOMEM;
goto free_bounce_bufs;
@@ -647,9 +651,9 @@ EXPORT_SYMBOL(vbg_hgcm_call);
#ifdef CONFIG_COMPAT
int vbg_hgcm_call32(
- struct vbg_dev *gdev, u32 client_id, u32 function, u32 timeout_ms,
- struct vmmdev_hgcm_function_parameter32 *parm32, u32 parm_count,
- int *vbox_status)
+ struct vbg_dev *gdev, u32 requestor, u32 client_id, u32 function,
+ u32 timeout_ms, struct vmmdev_hgcm_function_parameter32 *parm32,
+ u32 parm_count, int *vbox_status)
{
struct vmmdev_hgcm_function_parameter *parm64 = NULL;
u32 i, size;
@@ -689,7 +693,7 @@ int vbg_hgcm_call32(
goto out_free;
}
- ret = vbg_hgcm_call(gdev, client_id, function, timeout_ms,
+ ret = vbg_hgcm_call(gdev, requestor, client_id, function, timeout_ms,
parm64, parm_count, vbox_status);
if (ret < 0)
goto out_free;
diff --git a/drivers/virt/vboxguest/vboxguest_version.h b/drivers/virt/vboxguest/vboxguest_version.h
index 77f0c8f8a231..84834dad38d5 100644
--- a/drivers/virt/vboxguest/vboxguest_version.h
+++ b/drivers/virt/vboxguest/vboxguest_version.h
@@ -9,11 +9,10 @@
#ifndef __VBOX_VERSION_H__
#define __VBOX_VERSION_H__
-/* Last synced October 4th 2017 */
-#define VBG_VERSION_MAJOR 5
-#define VBG_VERSION_MINOR 2
+#define VBG_VERSION_MAJOR 6
+#define VBG_VERSION_MINOR 0
#define VBG_VERSION_BUILD 0
-#define VBG_SVN_REV 68940
-#define VBG_VERSION_STRING "5.2.0"
+#define VBG_SVN_REV 127566
+#define VBG_VERSION_STRING "6.0.0"
#endif
diff --git a/drivers/virt/vboxguest/vmmdev.h b/drivers/virt/vboxguest/vmmdev.h
index 5e2ae978935d..6337b8d75d96 100644
--- a/drivers/virt/vboxguest/vmmdev.h
+++ b/drivers/virt/vboxguest/vmmdev.h
@@ -98,8 +98,8 @@ struct vmmdev_request_header {
s32 rc;
/** Reserved field no.1. MBZ. */
u32 reserved1;
- /** Reserved field no.2. MBZ. */
- u32 reserved2;
+ /** IN: Requestor information (VMMDEV_REQUESTOR_*) */
+ u32 requestor;
};
VMMDEV_ASSERT_SIZE(vmmdev_request_header, 24);
@@ -247,6 +247,8 @@ struct vmmdev_guest_info {
};
VMMDEV_ASSERT_SIZE(vmmdev_guest_info, 24 + 8);
+#define VMMDEV_GUEST_INFO2_ADDITIONS_FEATURES_REQUESTOR_INFO BIT(0)
+
/** struct vmmdev_guestinfo2 - Guest information report, version 2. */
struct vmmdev_guest_info2 {
/** Header. */
@@ -259,7 +261,7 @@ struct vmmdev_guest_info2 {
u32 additions_build;
/** SVN revision. */
u32 additions_revision;
- /** Feature mask, currently unused. */
+ /** Feature mask. */
u32 additions_features;
/**
* The intentional meaning of this field was:
diff --git a/drivers/virtio/virtio.c b/drivers/virtio/virtio.c
index 59e36ef4920f..98b30f54342c 100644
--- a/drivers/virtio/virtio.c
+++ b/drivers/virtio/virtio.c
@@ -161,6 +161,7 @@ EXPORT_SYMBOL_GPL(virtio_config_enable);
void virtio_add_status(struct virtio_device *dev, unsigned int status)
{
+ might_sleep();
dev->config->set_status(dev, dev->config->get_status(dev) | status);
}
EXPORT_SYMBOL_GPL(virtio_add_status);
@@ -170,6 +171,7 @@ int virtio_finalize_features(struct virtio_device *dev)
int ret = dev->config->finalize_features(dev);
unsigned status;
+ might_sleep();
if (ret)
return ret;
diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c
index fb12fe205f86..f19061b585a4 100644
--- a/drivers/virtio/virtio_balloon.c
+++ b/drivers/virtio/virtio_balloon.c
@@ -457,9 +457,12 @@ static void update_balloon_size_func(struct work_struct *work)
update_balloon_size_work);
diff = towards_target(vb);
+ if (!diff)
+ return;
+
if (diff > 0)
diff -= fill_balloon(vb, diff);
- else if (diff < 0)
+ else
diff += leak_balloon(vb, -diff);
update_balloon_size(vb);
@@ -922,7 +925,6 @@ static int virtballoon_probe(struct virtio_device *vdev)
VIRTIO_BALLOON_CMD_ID_STOP);
vb->cmd_id_stop = cpu_to_virtio32(vb->vdev,
VIRTIO_BALLOON_CMD_ID_STOP);
- vb->num_free_page_blocks = 0;
spin_lock_init(&vb->free_page_list_lock);
INIT_LIST_HEAD(&vb->free_page_list);
if (virtio_has_feature(vdev, VIRTIO_BALLOON_F_PAGE_POISON)) {
diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c
index a0b07c331255..18846afb39da 100644
--- a/drivers/virtio/virtio_ring.c
+++ b/drivers/virtio/virtio_ring.c
@@ -271,6 +271,17 @@ static bool vring_use_dma_api(struct virtio_device *vdev)
return false;
}
+size_t virtio_max_dma_size(struct virtio_device *vdev)
+{
+ size_t max_segment_size = SIZE_MAX;
+
+ if (vring_use_dma_api(vdev))
+ max_segment_size = dma_max_mapping_size(&vdev->dev);
+
+ return max_segment_size;
+}
+EXPORT_SYMBOL_GPL(virtio_max_dma_size);
+
static void *vring_alloc_queue(struct virtio_device *vdev, size_t size,
dma_addr_t *dma_handle, gfp_t flag)
{
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 57f017d74a97..242eea859637 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -241,6 +241,22 @@ config RAVE_SP_WATCHDOG
help
Support for the watchdog on RAVE SP device.
+config MLX_WDT
+ tristate "Mellanox Watchdog"
+ depends on MELLANOX_PLATFORM
+ select WATCHDOG_CORE
+ select REGMAP
+ help
+ This is the driver for the hardware watchdog on Mellanox systems.
+ If you are going to use it, say Y here, otherwise N.
+ This driver can be used together with the watchdog daemon.
+ It can also watch your kernel to make sure it doesn't freeze,
+ and if it does, it reboots your system after a certain amount of
+ time.
+
+ To compile this driver as a module, choose M here: the
+ module will be called mlx-wdt.
+
# ALPHA Architecture
# ARM Architecture
@@ -817,6 +833,18 @@ config STM32_WATCHDOG
To compile this driver as a module, choose M here: the
module will be called stm32_iwdg.
+config STPMIC1_WATCHDOG
+ tristate "STPMIC1 PMIC watchdog support"
+ depends on MFD_STPMIC1
+ select WATCHDOG_CORE
+ help
+ Say Y here to include watchdog support embedded into STPMIC1 PMIC.
+ If the watchdog timer expires, stpmic1 will shut down all its power
+ supplies.
+
+ To compile this driver as a module, choose M here: the
+ module will be called spmic1_wdt.
+
config UNIPHIER_WATCHDOG
tristate "UniPhier watchdog support"
depends on ARCH_UNIPHIER || COMPILE_TEST
@@ -1145,7 +1173,7 @@ config HP_WATCHDOG
select WATCHDOG_CORE
depends on X86 && PCI
help
- A software monitoring watchdog and NMI sourcing driver. This driver
+ A software monitoring watchdog and NMI handling driver. This driver
will detect lockups and provide a stack trace. This is a driver that
will only load on an HP ProLiant system with a minimum of iLO2 support.
To compile this driver as a module, choose M here: the module will be
@@ -1163,12 +1191,13 @@ config KEMPLD_WDT
called kempld_wdt.
config HPWDT_NMI_DECODING
- bool "NMI decoding support for the HP ProLiant iLO2+ Hardware Watchdog Timer"
+ bool "NMI support for the HP ProLiant iLO2+ Hardware Watchdog Timer"
depends on HP_WATCHDOG
default y
help
- When an NMI occurs this feature will make the necessary BIOS calls to
- log the cause of the NMI.
+ Enables the NMI handler for the watchdog pretimeout NMI and the iLO
+ "Generate NMI to System" virtual button. When an NMI is claimed
+ by the driver, panic is called.
config SC1200_WDT
tristate "National Semiconductor PC87307/PC97307 (ala SC1200) Watchdog"
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index a0917ef28e07..ba930e464657 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -142,6 +142,7 @@ obj-$(CONFIG_INTEL_MID_WATCHDOG) += intel-mid_wdt.o
obj-$(CONFIG_INTEL_MEI_WDT) += mei_wdt.o
obj-$(CONFIG_NI903X_WDT) += ni903x_wdt.o
obj-$(CONFIG_NIC7018_WDT) += nic7018_wdt.o
+obj-$(CONFIG_MLX_WDT) += mlx_wdt.o
# M68K Architecture
obj-$(CONFIG_M54xx_WATCHDOG) += m54xx_wdt.o
@@ -220,3 +221,4 @@ obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o
obj-$(CONFIG_MENF21BMC_WATCHDOG) += menf21bmc_wdt.o
obj-$(CONFIG_MENZ069_WATCHDOG) += menz69_wdt.o
obj-$(CONFIG_RAVE_SP_WATCHDOG) += rave-sp-wdt.o
+obj-$(CONFIG_STPMIC1_WATCHDOG) += stpmic1_wdt.o
diff --git a/drivers/watchdog/dw_wdt.c b/drivers/watchdog/dw_wdt.c
index 501aebb5b81f..aa95f57cc1c3 100644
--- a/drivers/watchdog/dw_wdt.c
+++ b/drivers/watchdog/dw_wdt.c
@@ -16,8 +16,6 @@
* heartbeat requests after the watchdog device has been closed.
*/
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
#include <linux/bitops.h>
#include <linux/clk.h>
#include <linux/delay.h>
diff --git a/drivers/watchdog/mlx_wdt.c b/drivers/watchdog/mlx_wdt.c
new file mode 100644
index 000000000000..70c2cbf9c993
--- /dev/null
+++ b/drivers/watchdog/mlx_wdt.c
@@ -0,0 +1,290 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Mellanox watchdog driver
+ *
+ * Copyright (C) 2019 Mellanox Technologies
+ * Copyright (C) 2019 Michael Shych <mshych@mellanox.com>
+ */
+
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/log2.h>
+#include <linux/module.h>
+#include <linux/platform_data/mlxreg.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/watchdog.h>
+
+#define MLXREG_WDT_CLOCK_SCALE 1000
+#define MLXREG_WDT_MAX_TIMEOUT_TYPE1 32
+#define MLXREG_WDT_MAX_TIMEOUT_TYPE2 255
+#define MLXREG_WDT_MIN_TIMEOUT 1
+#define MLXREG_WDT_OPTIONS_BASE (WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE | \
+ WDIOF_SETTIMEOUT)
+
+/**
+ * struct mlxreg_wdt - wd private data:
+ *
+ * @wdd: watchdog device;
+ * @device: basic device;
+ * @pdata: data received from platform driver;
+ * @regmap: register map of parent device;
+ * @timeout: defined timeout in sec.;
+ * @action_idx: index for direct access to action register;
+ * @timeout_idx:index for direct access to TO register;
+ * @tleft_idx: index for direct access to time left register;
+ * @ping_idx: index for direct access to ping register;
+ * @reset_idx: index for direct access to reset cause register;
+ * @wd_type: watchdog HW type;
+ */
+struct mlxreg_wdt {
+ struct watchdog_device wdd;
+ struct mlxreg_core_platform_data *pdata;
+ void *regmap;
+ int action_idx;
+ int timeout_idx;
+ int tleft_idx;
+ int ping_idx;
+ int reset_idx;
+ enum mlxreg_wdt_type wdt_type;
+};
+
+static void mlxreg_wdt_check_card_reset(struct mlxreg_wdt *wdt)
+{
+ struct mlxreg_core_data *reg_data;
+ u32 regval;
+ int rc;
+
+ if (wdt->reset_idx == -EINVAL)
+ return;
+
+ if (!(wdt->wdd.info->options & WDIOF_CARDRESET))
+ return;
+
+ reg_data = &wdt->pdata->data[wdt->reset_idx];
+ rc = regmap_read(wdt->regmap, reg_data->reg, &regval);
+ if (!rc) {
+ if (regval & ~reg_data->mask) {
+ wdt->wdd.bootstatus = WDIOF_CARDRESET;
+ dev_info(wdt->wdd.parent,
+ "watchdog previously reset the CPU\n");
+ }
+ }
+}
+
+static int mlxreg_wdt_start(struct watchdog_device *wdd)
+{
+ struct mlxreg_wdt *wdt = watchdog_get_drvdata(wdd);
+ struct mlxreg_core_data *reg_data = &wdt->pdata->data[wdt->action_idx];
+
+ return regmap_update_bits(wdt->regmap, reg_data->reg, ~reg_data->mask,
+ BIT(reg_data->bit));
+}
+
+static int mlxreg_wdt_stop(struct watchdog_device *wdd)
+{
+ struct mlxreg_wdt *wdt = watchdog_get_drvdata(wdd);
+ struct mlxreg_core_data *reg_data = &wdt->pdata->data[wdt->action_idx];
+
+ return regmap_update_bits(wdt->regmap, reg_data->reg, ~reg_data->mask,
+ ~BIT(reg_data->bit));
+}
+
+static int mlxreg_wdt_ping(struct watchdog_device *wdd)
+{
+ struct mlxreg_wdt *wdt = watchdog_get_drvdata(wdd);
+ struct mlxreg_core_data *reg_data = &wdt->pdata->data[wdt->ping_idx];
+
+ return regmap_update_bits_base(wdt->regmap, reg_data->reg,
+ ~reg_data->mask, BIT(reg_data->bit),
+ NULL, false, true);
+}
+
+static int mlxreg_wdt_set_timeout(struct watchdog_device *wdd,
+ unsigned int timeout)
+{
+ struct mlxreg_wdt *wdt = watchdog_get_drvdata(wdd);
+ struct mlxreg_core_data *reg_data = &wdt->pdata->data[wdt->timeout_idx];
+ u32 regval, set_time, hw_timeout;
+ int rc;
+
+ if (wdt->wdt_type == MLX_WDT_TYPE1) {
+ rc = regmap_read(wdt->regmap, reg_data->reg, &regval);
+ if (rc)
+ return rc;
+
+ hw_timeout = order_base_2(timeout * MLXREG_WDT_CLOCK_SCALE);
+ regval = (regval & reg_data->mask) | hw_timeout;
+ /* Rowndown to actual closest number of sec. */
+ set_time = BIT(hw_timeout) / MLXREG_WDT_CLOCK_SCALE;
+ } else {
+ set_time = timeout;
+ regval = timeout;
+ }
+
+ wdd->timeout = set_time;
+ rc = regmap_write(wdt->regmap, reg_data->reg, regval);
+
+ if (!rc) {
+ /*
+ * Restart watchdog with new timeout period
+ * if watchdog is already started.
+ */
+ if (watchdog_active(wdd)) {
+ rc = mlxreg_wdt_stop(wdd);
+ if (!rc)
+ rc = mlxreg_wdt_start(wdd);
+ }
+ }
+
+ return rc;
+}
+
+static unsigned int mlxreg_wdt_get_timeleft(struct watchdog_device *wdd)
+{
+ struct mlxreg_wdt *wdt = watchdog_get_drvdata(wdd);
+ struct mlxreg_core_data *reg_data = &wdt->pdata->data[wdt->tleft_idx];
+ u32 regval;
+ int rc;
+
+ rc = regmap_read(wdt->regmap, reg_data->reg, &regval);
+ /* Return 0 timeleft in case of failure register read. */
+ return rc == 0 ? regval : 0;
+}
+
+static const struct watchdog_ops mlxreg_wdt_ops_type1 = {
+ .start = mlxreg_wdt_start,
+ .stop = mlxreg_wdt_stop,
+ .ping = mlxreg_wdt_ping,
+ .set_timeout = mlxreg_wdt_set_timeout,
+ .owner = THIS_MODULE,
+};
+
+static const struct watchdog_ops mlxreg_wdt_ops_type2 = {
+ .start = mlxreg_wdt_start,
+ .stop = mlxreg_wdt_stop,
+ .ping = mlxreg_wdt_ping,
+ .set_timeout = mlxreg_wdt_set_timeout,
+ .get_timeleft = mlxreg_wdt_get_timeleft,
+ .owner = THIS_MODULE,
+};
+
+static const struct watchdog_info mlxreg_wdt_main_info = {
+ .options = MLXREG_WDT_OPTIONS_BASE
+ | WDIOF_CARDRESET,
+ .identity = "mlx-wdt-main",
+};
+
+static const struct watchdog_info mlxreg_wdt_aux_info = {
+ .options = MLXREG_WDT_OPTIONS_BASE
+ | WDIOF_ALARMONLY,
+ .identity = "mlx-wdt-aux",
+};
+
+static void mlxreg_wdt_config(struct mlxreg_wdt *wdt,
+ struct mlxreg_core_platform_data *pdata)
+{
+ struct mlxreg_core_data *data = pdata->data;
+ int i;
+
+ wdt->reset_idx = -EINVAL;
+ for (i = 0; i < pdata->counter; i++, data++) {
+ if (strnstr(data->label, "action", sizeof(data->label)))
+ wdt->action_idx = i;
+ else if (strnstr(data->label, "timeout", sizeof(data->label)))
+ wdt->timeout_idx = i;
+ else if (strnstr(data->label, "timeleft", sizeof(data->label)))
+ wdt->tleft_idx = i;
+ else if (strnstr(data->label, "ping", sizeof(data->label)))
+ wdt->ping_idx = i;
+ else if (strnstr(data->label, "reset", sizeof(data->label)))
+ wdt->reset_idx = i;
+ }
+
+ wdt->pdata = pdata;
+ if (strnstr(pdata->identity, mlxreg_wdt_main_info.identity,
+ sizeof(mlxreg_wdt_main_info.identity)))
+ wdt->wdd.info = &mlxreg_wdt_main_info;
+ else
+ wdt->wdd.info = &mlxreg_wdt_aux_info;
+
+ wdt->wdt_type = pdata->version;
+ if (wdt->wdt_type == MLX_WDT_TYPE2) {
+ wdt->wdd.ops = &mlxreg_wdt_ops_type2;
+ wdt->wdd.max_timeout = MLXREG_WDT_MAX_TIMEOUT_TYPE2;
+ } else {
+ wdt->wdd.ops = &mlxreg_wdt_ops_type1;
+ wdt->wdd.max_timeout = MLXREG_WDT_MAX_TIMEOUT_TYPE1;
+ }
+ wdt->wdd.min_timeout = MLXREG_WDT_MIN_TIMEOUT;
+}
+
+static int mlxreg_wdt_init_timeout(struct mlxreg_wdt *wdt,
+ struct mlxreg_core_platform_data *pdata)
+{
+ u32 timeout;
+
+ timeout = pdata->data[wdt->timeout_idx].health_cntr;
+ return mlxreg_wdt_set_timeout(&wdt->wdd, timeout);
+}
+
+static int mlxreg_wdt_probe(struct platform_device *pdev)
+{
+ struct mlxreg_core_platform_data *pdata;
+ struct mlxreg_wdt *wdt;
+ int rc;
+
+ pdata = dev_get_platdata(&pdev->dev);
+ if (!pdata) {
+ dev_err(&pdev->dev, "Failed to get platform data.\n");
+ return -EINVAL;
+ }
+ wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
+ if (!wdt)
+ return -ENOMEM;
+
+ wdt->wdd.parent = &pdev->dev;
+ wdt->regmap = pdata->regmap;
+ mlxreg_wdt_config(wdt, pdata);
+
+ if ((pdata->features & MLXREG_CORE_WD_FEATURE_NOWAYOUT))
+ watchdog_set_nowayout(&wdt->wdd, WATCHDOG_NOWAYOUT);
+ watchdog_stop_on_reboot(&wdt->wdd);
+ watchdog_stop_on_unregister(&wdt->wdd);
+ watchdog_set_drvdata(&wdt->wdd, wdt);
+ rc = mlxreg_wdt_init_timeout(wdt, pdata);
+ if (rc)
+ goto register_error;
+
+ if ((pdata->features & MLXREG_CORE_WD_FEATURE_START_AT_BOOT)) {
+ rc = mlxreg_wdt_start(&wdt->wdd);
+ if (rc)
+ goto register_error;
+ set_bit(WDOG_HW_RUNNING, &wdt->wdd.status);
+ }
+ mlxreg_wdt_check_card_reset(wdt);
+ rc = devm_watchdog_register_device(&pdev->dev, &wdt->wdd);
+
+register_error:
+ if (rc)
+ dev_err(&pdev->dev,
+ "Cannot register watchdog device (err=%d)\n", rc);
+ return rc;
+}
+
+static struct platform_driver mlxreg_wdt_driver = {
+ .probe = mlxreg_wdt_probe,
+ .driver = {
+ .name = "mlx-wdt",
+ },
+};
+
+module_platform_driver(mlxreg_wdt_driver);
+
+MODULE_AUTHOR("Michael Shych <michaelsh@mellanox.com>");
+MODULE_DESCRIPTION("Mellanox watchdog driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:mlx-wdt");
diff --git a/drivers/watchdog/pc87413_wdt.c b/drivers/watchdog/pc87413_wdt.c
index 06a892e36a8d..2ffa39b46970 100644
--- a/drivers/watchdog/pc87413_wdt.c
+++ b/drivers/watchdog/pc87413_wdt.c
@@ -437,7 +437,7 @@ static long pc87413_ioctl(struct file *file, unsigned int cmd,
return -EINVAL;
timeout = new_timeout;
pc87413_refresh();
- /* fall through and return the new timeout... */
+ /* fall through - and return the new timeout... */
case WDIOC_GETTIMEOUT:
new_timeout = timeout * 60;
return put_user(new_timeout, uarg.i);
diff --git a/drivers/watchdog/pika_wdt.c b/drivers/watchdog/pika_wdt.c
index e0a6f8c0f03c..bb97f5b2f7eb 100644
--- a/drivers/watchdog/pika_wdt.c
+++ b/drivers/watchdog/pika_wdt.c
@@ -225,7 +225,7 @@ static int __init pikawdt_init(void)
{
struct device_node *np;
void __iomem *fpga;
- static u32 post1;
+ u32 post1;
int ret;
np = of_find_compatible_node(NULL, NULL, "pika,fpga");
diff --git a/drivers/watchdog/qcom-wdt.c b/drivers/watchdog/qcom-wdt.c
index 780971318810..5dfd604477a4 100644
--- a/drivers/watchdog/qcom-wdt.c
+++ b/drivers/watchdog/qcom-wdt.c
@@ -245,6 +245,28 @@ static int qcom_wdt_remove(struct platform_device *pdev)
return 0;
}
+static int __maybe_unused qcom_wdt_suspend(struct device *dev)
+{
+ struct qcom_wdt *wdt = dev_get_drvdata(dev);
+
+ if (watchdog_active(&wdt->wdd))
+ qcom_wdt_stop(&wdt->wdd);
+
+ return 0;
+}
+
+static int __maybe_unused qcom_wdt_resume(struct device *dev)
+{
+ struct qcom_wdt *wdt = dev_get_drvdata(dev);
+
+ if (watchdog_active(&wdt->wdd))
+ qcom_wdt_start(&wdt->wdd);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(qcom_wdt_pm_ops, qcom_wdt_suspend, qcom_wdt_resume);
+
static const struct of_device_id qcom_wdt_of_table[] = {
{ .compatible = "qcom,kpss-timer", .data = reg_offset_data_apcs_tmr },
{ .compatible = "qcom,scss-timer", .data = reg_offset_data_apcs_tmr },
@@ -259,6 +281,7 @@ static struct platform_driver qcom_watchdog_driver = {
.driver = {
.name = KBUILD_MODNAME,
.of_match_table = qcom_wdt_of_table,
+ .pm = &qcom_wdt_pm_ops,
},
};
module_platform_driver(qcom_watchdog_driver);
diff --git a/drivers/watchdog/sbc60xxwdt.c b/drivers/watchdog/sbc60xxwdt.c
index 87333a41f753..72d15fd1f183 100644
--- a/drivers/watchdog/sbc60xxwdt.c
+++ b/drivers/watchdog/sbc60xxwdt.c
@@ -270,8 +270,8 @@ static long fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
timeout = new_timeout;
wdt_keepalive();
- /* Fall through */
}
+ /* Fall through */
case WDIOC_GETTIMEOUT:
return put_user(timeout, p);
default:
diff --git a/drivers/watchdog/sc1200wdt.c b/drivers/watchdog/sc1200wdt.c
index 8e4e2fc13f87..e035a4d4b299 100644
--- a/drivers/watchdog/sc1200wdt.c
+++ b/drivers/watchdog/sc1200wdt.c
@@ -239,7 +239,7 @@ static long sc1200wdt_ioctl(struct file *file, unsigned int cmd,
return -EINVAL;
timeout = new_timeout;
sc1200wdt_write_data(WDTO, timeout);
- /* fall through and return the new timeout */
+ /* fall through - and return the new timeout */
case WDIOC_GETTIMEOUT:
return put_user(timeout * 60, p);
diff --git a/drivers/watchdog/sc520_wdt.c b/drivers/watchdog/sc520_wdt.c
index 6aadb56e7faa..403542f9ed8d 100644
--- a/drivers/watchdog/sc520_wdt.c
+++ b/drivers/watchdog/sc520_wdt.c
@@ -324,8 +324,8 @@ static long fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
return -EINVAL;
wdt_keepalive();
- /* Fall through */
}
+ /* Fall through */
case WDIOC_GETTIMEOUT:
return put_user(timeout, p);
default:
diff --git a/drivers/watchdog/smsc37b787_wdt.c b/drivers/watchdog/smsc37b787_wdt.c
index 445ea1ad1fa9..c768dcd53034 100644
--- a/drivers/watchdog/smsc37b787_wdt.c
+++ b/drivers/watchdog/smsc37b787_wdt.c
@@ -478,7 +478,7 @@ static long wb_smsc_wdt_ioctl(struct file *file,
return -EINVAL;
timeout = new_timeout;
wb_smsc_wdt_set_timeout(timeout);
- /* fall through and return the new timeout... */
+ /* fall through - and return the new timeout... */
case WDIOC_GETTIMEOUT:
new_timeout = timeout;
if (unit == UNIT_MINUTE)
diff --git a/drivers/watchdog/stpmic1_wdt.c b/drivers/watchdog/stpmic1_wdt.c
new file mode 100644
index 000000000000..ad431d8ad95f
--- /dev/null
+++ b/drivers/watchdog/stpmic1_wdt.c
@@ -0,0 +1,139 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) STMicroelectronics 2018
+// Author: Pascal Paillet <p.paillet@st.com> for STMicroelectronics.
+
+#include <linux/kernel.h>
+#include <linux/mfd/stpmic1.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/watchdog.h>
+
+/* WATCHDOG CONTROL REGISTER bit */
+#define WDT_START BIT(0)
+#define WDT_PING BIT(1)
+#define WDT_START_MASK BIT(0)
+#define WDT_PING_MASK BIT(1)
+#define WDT_STOP 0
+
+#define PMIC_WDT_MIN_TIMEOUT 1
+#define PMIC_WDT_MAX_TIMEOUT 256
+#define PMIC_WDT_DEFAULT_TIMEOUT 30
+
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+struct stpmic1_wdt {
+ struct stpmic1 *pmic;
+ struct watchdog_device wdtdev;
+};
+
+static int pmic_wdt_start(struct watchdog_device *wdd)
+{
+ struct stpmic1_wdt *wdt = watchdog_get_drvdata(wdd);
+
+ return regmap_update_bits(wdt->pmic->regmap,
+ WCHDG_CR, WDT_START_MASK, WDT_START);
+}
+
+static int pmic_wdt_stop(struct watchdog_device *wdd)
+{
+ struct stpmic1_wdt *wdt = watchdog_get_drvdata(wdd);
+
+ return regmap_update_bits(wdt->pmic->regmap,
+ WCHDG_CR, WDT_START_MASK, WDT_STOP);
+}
+
+static int pmic_wdt_ping(struct watchdog_device *wdd)
+{
+ struct stpmic1_wdt *wdt = watchdog_get_drvdata(wdd);
+
+ return regmap_update_bits(wdt->pmic->regmap,
+ WCHDG_CR, WDT_PING_MASK, WDT_PING);
+}
+
+static int pmic_wdt_set_timeout(struct watchdog_device *wdd,
+ unsigned int timeout)
+{
+ struct stpmic1_wdt *wdt = watchdog_get_drvdata(wdd);
+
+ wdd->timeout = timeout;
+ /* timeout is equal to register value + 1 */
+ return regmap_write(wdt->pmic->regmap, WCHDG_TIMER_CR, timeout - 1);
+}
+
+static const struct watchdog_info pmic_watchdog_info = {
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+ .identity = "STPMIC1 PMIC Watchdog",
+};
+
+static const struct watchdog_ops pmic_watchdog_ops = {
+ .owner = THIS_MODULE,
+ .start = pmic_wdt_start,
+ .stop = pmic_wdt_stop,
+ .ping = pmic_wdt_ping,
+ .set_timeout = pmic_wdt_set_timeout,
+};
+
+static int pmic_wdt_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct stpmic1 *pmic;
+ struct stpmic1_wdt *wdt;
+
+ if (!pdev->dev.parent)
+ return -EINVAL;
+
+ pmic = dev_get_drvdata(pdev->dev.parent);
+ if (!pmic)
+ return -EINVAL;
+
+ wdt = devm_kzalloc(&pdev->dev, sizeof(struct stpmic1_wdt), GFP_KERNEL);
+ if (!wdt)
+ return -ENOMEM;
+
+ wdt->pmic = pmic;
+
+ wdt->wdtdev.info = &pmic_watchdog_info;
+ wdt->wdtdev.ops = &pmic_watchdog_ops;
+ wdt->wdtdev.min_timeout = PMIC_WDT_MIN_TIMEOUT;
+ wdt->wdtdev.max_timeout = PMIC_WDT_MAX_TIMEOUT;
+ wdt->wdtdev.parent = &pdev->dev;
+
+ wdt->wdtdev.timeout = PMIC_WDT_DEFAULT_TIMEOUT;
+ watchdog_init_timeout(&wdt->wdtdev, 0, &pdev->dev);
+
+ watchdog_set_nowayout(&wdt->wdtdev, nowayout);
+ watchdog_set_drvdata(&wdt->wdtdev, wdt);
+
+ ret = devm_watchdog_register_device(&pdev->dev, &wdt->wdtdev);
+ if (ret)
+ return ret;
+
+ dev_dbg(wdt->pmic->dev, "PMIC Watchdog driver probed\n");
+ return 0;
+}
+
+static const struct of_device_id of_pmic_wdt_match[] = {
+ { .compatible = "st,stpmic1-wdt" },
+ { },
+};
+
+MODULE_DEVICE_TABLE(of, of_pmic_wdt_match);
+
+static struct platform_driver stpmic1_wdt_driver = {
+ .probe = pmic_wdt_probe,
+ .driver = {
+ .name = "stpmic1-wdt",
+ .of_match_table = of_pmic_wdt_match,
+ },
+};
+module_platform_driver(stpmic1_wdt_driver);
+
+MODULE_DESCRIPTION("Watchdog driver for STPMIC1 device");
+MODULE_AUTHOR("Pascal Paillet <p.paillet@st.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/watchdog/w83877f_wdt.c b/drivers/watchdog/w83877f_wdt.c
index 05658ecc0aa4..db9b6488e388 100644
--- a/drivers/watchdog/w83877f_wdt.c
+++ b/drivers/watchdog/w83877f_wdt.c
@@ -292,8 +292,8 @@ static long fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
timeout = new_timeout;
wdt_keepalive();
- /* Fall through */
}
+ /* Fall through */
case WDIOC_GETTIMEOUT:
return put_user(timeout, p);
default:
diff --git a/drivers/xen/Makefile b/drivers/xen/Makefile
index c48927a58e10..ad3844d9f876 100644
--- a/drivers/xen/Makefile
+++ b/drivers/xen/Makefile
@@ -1,6 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_HOTPLUG_CPU) += cpu_hotplug.o
-obj-$(CONFIG_X86) += fallback.o
obj-y += grant-table.o features.o balloon.o manage.o preempt.o time.o
obj-y += mem-reservation.o
obj-y += events/
diff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c
index 39b229f9e256..d37dd5bb7a8f 100644
--- a/drivers/xen/balloon.c
+++ b/drivers/xen/balloon.c
@@ -604,6 +604,7 @@ int alloc_xenballooned_pages(int nr_pages, struct page **pages)
while (pgno < nr_pages) {
page = balloon_retrieve(true);
if (page) {
+ __ClearPageOffline(page);
pages[pgno++] = page;
#ifdef CONFIG_XEN_HAVE_PVMMU
/*
@@ -645,8 +646,10 @@ void free_xenballooned_pages(int nr_pages, struct page **pages)
mutex_lock(&balloon_mutex);
for (i = 0; i < nr_pages; i++) {
- if (pages[i])
+ if (pages[i]) {
+ __SetPageOffline(pages[i]);
balloon_append(pages[i]);
+ }
}
balloon_stats.target_unpopulated -= nr_pages;
diff --git a/drivers/xen/cpu_hotplug.c b/drivers/xen/cpu_hotplug.c
index b1357aa4bc55..f192b6f42da9 100644
--- a/drivers/xen/cpu_hotplug.c
+++ b/drivers/xen/cpu_hotplug.c
@@ -54,7 +54,7 @@ static int vcpu_online(unsigned int cpu)
}
static void vcpu_hotplug(unsigned int cpu)
{
- if (!cpu_possible(cpu))
+ if (cpu >= nr_cpu_ids || !cpu_possible(cpu))
return;
switch (vcpu_online(cpu)) {
diff --git a/drivers/xen/fallback.c b/drivers/xen/fallback.c
deleted file mode 100644
index b04fb64c5a91..000000000000
--- a/drivers/xen/fallback.c
+++ /dev/null
@@ -1,81 +0,0 @@
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/bug.h>
-#include <linux/export.h>
-#include <asm/hypervisor.h>
-#include <asm/xen/hypercall.h>
-
-int xen_event_channel_op_compat(int cmd, void *arg)
-{
- struct evtchn_op op;
- int rc;
-
- op.cmd = cmd;
- memcpy(&op.u, arg, sizeof(op.u));
- rc = _hypercall1(int, event_channel_op_compat, &op);
-
- switch (cmd) {
- case EVTCHNOP_close:
- case EVTCHNOP_send:
- case EVTCHNOP_bind_vcpu:
- case EVTCHNOP_unmask:
- /* no output */
- break;
-
-#define COPY_BACK(eop) \
- case EVTCHNOP_##eop: \
- memcpy(arg, &op.u.eop, sizeof(op.u.eop)); \
- break
-
- COPY_BACK(bind_interdomain);
- COPY_BACK(bind_virq);
- COPY_BACK(bind_pirq);
- COPY_BACK(status);
- COPY_BACK(alloc_unbound);
- COPY_BACK(bind_ipi);
-#undef COPY_BACK
-
- default:
- WARN_ON(rc != -ENOSYS);
- break;
- }
-
- return rc;
-}
-EXPORT_SYMBOL_GPL(xen_event_channel_op_compat);
-
-int xen_physdev_op_compat(int cmd, void *arg)
-{
- struct physdev_op op;
- int rc;
-
- op.cmd = cmd;
- memcpy(&op.u, arg, sizeof(op.u));
- rc = _hypercall1(int, physdev_op_compat, &op);
-
- switch (cmd) {
- case PHYSDEVOP_IRQ_UNMASK_NOTIFY:
- case PHYSDEVOP_set_iopl:
- case PHYSDEVOP_set_iobitmap:
- case PHYSDEVOP_apic_write:
- /* no output */
- break;
-
-#define COPY_BACK(pop, fld) \
- case PHYSDEVOP_##pop: \
- memcpy(arg, &op.u.fld, sizeof(op.u.fld)); \
- break
-
- COPY_BACK(irq_status_query, irq_status_query);
- COPY_BACK(apic_read, apic_op);
- COPY_BACK(ASSIGN_VECTOR, irq_op);
-#undef COPY_BACK
-
- default:
- WARN_ON(rc != -ENOSYS);
- break;
- }
-
- return rc;
-}
-EXPORT_SYMBOL_GPL(xen_physdev_op_compat);
diff --git a/drivers/xen/gntdev-dmabuf.c b/drivers/xen/gntdev-dmabuf.c
index cba6b586bfbd..2c4f324f8626 100644
--- a/drivers/xen/gntdev-dmabuf.c
+++ b/drivers/xen/gntdev-dmabuf.c
@@ -80,6 +80,12 @@ struct gntdev_dmabuf_priv {
struct list_head imp_list;
/* This is the lock which protects dma_buf_xxx lists. */
struct mutex lock;
+ /*
+ * We reference this file while exporting dma-bufs, so
+ * the grant device context is not destroyed while there are
+ * external users alive.
+ */
+ struct file *filp;
};
/* DMA buffer export support. */
@@ -311,6 +317,7 @@ static void dmabuf_exp_release(struct kref *kref)
dmabuf_exp_wait_obj_signal(gntdev_dmabuf->priv, gntdev_dmabuf);
list_del(&gntdev_dmabuf->next);
+ fput(gntdev_dmabuf->priv->filp);
kfree(gntdev_dmabuf);
}
@@ -423,6 +430,7 @@ static int dmabuf_exp_from_pages(struct gntdev_dmabuf_export_args *args)
mutex_lock(&args->dmabuf_priv->lock);
list_add(&gntdev_dmabuf->next, &args->dmabuf_priv->exp_list);
mutex_unlock(&args->dmabuf_priv->lock);
+ get_file(gntdev_dmabuf->priv->filp);
return 0;
fail:
@@ -737,6 +745,14 @@ static int dmabuf_imp_release(struct gntdev_dmabuf_priv *priv, u32 fd)
return 0;
}
+static void dmabuf_imp_release_all(struct gntdev_dmabuf_priv *priv)
+{
+ struct gntdev_dmabuf *q, *gntdev_dmabuf;
+
+ list_for_each_entry_safe(gntdev_dmabuf, q, &priv->imp_list, next)
+ dmabuf_imp_release(priv, gntdev_dmabuf->fd);
+}
+
/* DMA buffer IOCTL support. */
long gntdev_ioctl_dmabuf_exp_from_refs(struct gntdev_priv *priv, int use_ptemod,
@@ -834,7 +850,7 @@ long gntdev_ioctl_dmabuf_imp_release(struct gntdev_priv *priv,
return dmabuf_imp_release(priv->dmabuf_priv, op.fd);
}
-struct gntdev_dmabuf_priv *gntdev_dmabuf_init(void)
+struct gntdev_dmabuf_priv *gntdev_dmabuf_init(struct file *filp)
{
struct gntdev_dmabuf_priv *priv;
@@ -847,10 +863,13 @@ struct gntdev_dmabuf_priv *gntdev_dmabuf_init(void)
INIT_LIST_HEAD(&priv->exp_wait_list);
INIT_LIST_HEAD(&priv->imp_list);
+ priv->filp = filp;
+
return priv;
}
void gntdev_dmabuf_fini(struct gntdev_dmabuf_priv *priv)
{
+ dmabuf_imp_release_all(priv);
kfree(priv);
}
diff --git a/drivers/xen/gntdev-dmabuf.h b/drivers/xen/gntdev-dmabuf.h
index 7220a53d0fc5..3d9b9cf9d5a1 100644
--- a/drivers/xen/gntdev-dmabuf.h
+++ b/drivers/xen/gntdev-dmabuf.h
@@ -14,7 +14,7 @@
struct gntdev_dmabuf_priv;
struct gntdev_priv;
-struct gntdev_dmabuf_priv *gntdev_dmabuf_init(void);
+struct gntdev_dmabuf_priv *gntdev_dmabuf_init(struct file *filp);
void gntdev_dmabuf_fini(struct gntdev_dmabuf_priv *priv);
diff --git a/drivers/xen/gntdev.c b/drivers/xen/gntdev.c
index 5efc5eee9544..7cf9c51318aa 100644
--- a/drivers/xen/gntdev.c
+++ b/drivers/xen/gntdev.c
@@ -600,7 +600,7 @@ static int gntdev_open(struct inode *inode, struct file *flip)
mutex_init(&priv->lock);
#ifdef CONFIG_XEN_GNTDEV_DMABUF
- priv->dmabuf_priv = gntdev_dmabuf_init();
+ priv->dmabuf_priv = gntdev_dmabuf_init(flip);
if (IS_ERR(priv->dmabuf_priv)) {
ret = PTR_ERR(priv->dmabuf_priv);
kfree(priv);
diff --git a/drivers/xen/swiotlb-xen.c b/drivers/xen/swiotlb-xen.c
index bb7888429be6..877baf2a94f4 100644
--- a/drivers/xen/swiotlb-xen.c
+++ b/drivers/xen/swiotlb-xen.c
@@ -214,10 +214,13 @@ retry:
/*
* Get IO TLB memory from any location.
*/
- if (early)
+ if (early) {
xen_io_tlb_start = memblock_alloc(PAGE_ALIGN(bytes),
PAGE_SIZE);
- else {
+ if (!xen_io_tlb_start)
+ panic("%s: Failed to allocate %lu bytes align=0x%lx\n",
+ __func__, PAGE_ALIGN(bytes), PAGE_SIZE);
+ } else {
#define SLABS_PER_PAGE (1 << (PAGE_SHIFT - IO_TLB_SHIFT))
#define IO_TLB_MIN_SLABS ((1<<20) >> IO_TLB_SHIFT)
while ((SLABS_PER_PAGE << order) > IO_TLB_MIN_SLABS) {
diff --git a/drivers/xen/xen-acpi-processor.c b/drivers/xen/xen-acpi-processor.c
index fbb9137c7d02..98e35644fda7 100644
--- a/drivers/xen/xen-acpi-processor.c
+++ b/drivers/xen/xen-acpi-processor.c
@@ -410,21 +410,21 @@ static int check_acpi_ids(struct acpi_processor *pr_backup)
/* All online CPUs have been processed at this stage. Now verify
* whether in fact "online CPUs" == physical CPUs.
*/
- acpi_id_present = kcalloc(BITS_TO_LONGS(nr_acpi_bits), sizeof(unsigned long), GFP_KERNEL);
+ acpi_id_present = bitmap_zalloc(nr_acpi_bits, GFP_KERNEL);
if (!acpi_id_present)
return -ENOMEM;
- acpi_id_cst_present = kcalloc(BITS_TO_LONGS(nr_acpi_bits), sizeof(unsigned long), GFP_KERNEL);
+ acpi_id_cst_present = bitmap_zalloc(nr_acpi_bits, GFP_KERNEL);
if (!acpi_id_cst_present) {
- kfree(acpi_id_present);
+ bitmap_free(acpi_id_present);
return -ENOMEM;
}
acpi_psd = kcalloc(nr_acpi_bits, sizeof(struct acpi_psd_package),
GFP_KERNEL);
if (!acpi_psd) {
- kfree(acpi_id_present);
- kfree(acpi_id_cst_present);
+ bitmap_free(acpi_id_present);
+ bitmap_free(acpi_id_cst_present);
return -ENOMEM;
}
@@ -533,14 +533,14 @@ static int __init xen_acpi_processor_init(void)
return -ENODEV;
nr_acpi_bits = get_max_acpi_id() + 1;
- acpi_ids_done = kcalloc(BITS_TO_LONGS(nr_acpi_bits), sizeof(unsigned long), GFP_KERNEL);
+ acpi_ids_done = bitmap_zalloc(nr_acpi_bits, GFP_KERNEL);
if (!acpi_ids_done)
return -ENOMEM;
acpi_perf_data = alloc_percpu(struct acpi_processor_performance);
if (!acpi_perf_data) {
pr_debug("Memory allocation error for acpi_perf_data\n");
- kfree(acpi_ids_done);
+ bitmap_free(acpi_ids_done);
return -ENOMEM;
}
for_each_possible_cpu(i) {
@@ -584,7 +584,7 @@ err_unregister:
err_out:
/* Freeing a NULL pointer is OK: alloc_percpu zeroes. */
free_acpi_perf_data();
- kfree(acpi_ids_done);
+ bitmap_free(acpi_ids_done);
return rc;
}
static void __exit xen_acpi_processor_exit(void)
@@ -592,9 +592,9 @@ static void __exit xen_acpi_processor_exit(void)
int i;
unregister_syscore_ops(&xap_syscore_ops);
- kfree(acpi_ids_done);
- kfree(acpi_id_present);
- kfree(acpi_id_cst_present);
+ bitmap_free(acpi_ids_done);
+ bitmap_free(acpi_id_present);
+ bitmap_free(acpi_id_cst_present);
kfree(acpi_psd);
for_each_possible_cpu(i)
acpi_processor_unregister_performance(i);
diff --git a/drivers/xen/xen-balloon.c b/drivers/xen/xen-balloon.c
index 2acbfe104e46..a67236b02452 100644
--- a/drivers/xen/xen-balloon.c
+++ b/drivers/xen/xen-balloon.c
@@ -37,6 +37,7 @@
#include <linux/mm_types.h>
#include <linux/init.h>
#include <linux/capability.h>
+#include <linux/memory_hotplug.h>
#include <xen/xen.h>
#include <xen/interface/xen.h>
@@ -50,6 +51,10 @@
#define BALLOON_CLASS_NAME "xen_memory"
+#ifdef CONFIG_MEMORY_HOTPLUG
+u64 xen_saved_max_mem_size = 0;
+#endif
+
static struct device balloon_dev;
static int register_balloon(struct device *dev);
@@ -63,6 +68,12 @@ static void watch_target(struct xenbus_watch *watch,
static bool watch_fired;
static long target_diff;
+#ifdef CONFIG_MEMORY_HOTPLUG
+ /* The balloon driver will take care of adding memory now. */
+ if (xen_saved_max_mem_size)
+ max_mem_size = xen_saved_max_mem_size;
+#endif
+
err = xenbus_scanf(XBT_NIL, "memory", "target", "%llu", &new_target);
if (err != 1) {
/* This is ok (for domain0 at least) - so just return */
diff --git a/drivers/xen/xen-pciback/pciback_ops.c b/drivers/xen/xen-pciback/pciback_ops.c
index ea4a08b83fa0..787966f44589 100644
--- a/drivers/xen/xen-pciback/pciback_ops.c
+++ b/drivers/xen/xen-pciback/pciback_ops.c
@@ -127,8 +127,6 @@ void xen_pcibk_reset_device(struct pci_dev *dev)
if (pci_is_enabled(dev))
pci_disable_device(dev);
- pci_write_config_word(dev, PCI_COMMAND, 0);
-
dev->is_busmaster = 0;
} else {
pci_read_config_word(dev, PCI_COMMAND, &cmd);
diff --git a/drivers/xen/xen-pciback/xenbus.c b/drivers/xen/xen-pciback/xenbus.c
index 581c4e1a8b82..23f7f6ec7d1f 100644
--- a/drivers/xen/xen-pciback/xenbus.c
+++ b/drivers/xen/xen-pciback/xenbus.c
@@ -544,7 +544,7 @@ static void xen_pcibk_frontend_changed(struct xenbus_device *xdev,
xenbus_switch_state(xdev, XenbusStateClosed);
if (xenbus_dev_is_online(xdev))
break;
- /* fall through if not online */
+ /* fall through - if not online */
case XenbusStateUnknown:
dev_dbg(&xdev->dev, "frontend is gone! unregister device\n");
device_unregister(&xdev->dev);
diff --git a/drivers/xen/xen-scsiback.c b/drivers/xen/xen-scsiback.c
index c9e23a126218..ba0942e481bc 100644
--- a/drivers/xen/xen-scsiback.c
+++ b/drivers/xen/xen-scsiback.c
@@ -1184,7 +1184,7 @@ static void scsiback_frontend_changed(struct xenbus_device *dev,
xenbus_switch_state(dev, XenbusStateClosed);
if (xenbus_dev_is_online(dev))
break;
- /* fall through if not online */
+ /* fall through - if not online */
case XenbusStateUnknown:
device_unregister(&dev->dev);
break;
@@ -1404,11 +1404,6 @@ static int scsiback_write_pending(struct se_cmd *se_cmd)
return 0;
}
-static int scsiback_write_pending_status(struct se_cmd *se_cmd)
-{
- return 0;
-}
-
static void scsiback_set_default_node_attrs(struct se_node_acl *nacl)
{
}
@@ -1818,7 +1813,6 @@ static const struct target_core_fabric_ops scsiback_ops = {
.sess_get_index = scsiback_sess_get_index,
.sess_get_initiator_sid = NULL,
.write_pending = scsiback_write_pending,
- .write_pending_status = scsiback_write_pending_status,
.set_default_node_attributes = scsiback_set_default_node_attrs,
.get_cmd_state = scsiback_get_cmd_state,
.queue_data_in = scsiback_queue_data_in,
OpenPOWER on IntegriCloud